[automerger skipped] Fix bug where icon remains invisible after returning home. am: a3ea27de39 -s ours
am skip reason: Change-Id I082291ca9b288f57701cc00d61a9b3a84da8b084 with SHA-1 ad7e30f5d7 is in history
Change-Id: Ia0b4a222f49ffab92533873134b358df1780ddf2
diff --git a/Android.bp b/Android.bp
index 4c38205..fc99880 100644
--- a/Android.bp
+++ b/Android.bp
@@ -30,3 +30,19 @@
manifest: "tests/tapl/AndroidManifest.xml",
platform_apis: true,
}
+
+java_library_static {
+ name: "launcher-log-protos-lite",
+ srcs: [
+ "protos/*.proto",
+ "proto_overrides/*.proto",
+ ],
+ sdk_version: "current",
+ proto: {
+ type: "lite",
+ local_include_dirs:[
+ "protos",
+ "proto_overrides",
+ ],
+ },
+}
diff --git a/Android.mk b/Android.mk
index 78ea02a..5def65f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -84,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
@@ -146,7 +145,7 @@
LOCAL_AAPT2_ONLY := true
LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
+LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLibLauncherWrapper launcherprotosnano
ifneq (,$(wildcard frameworks/base))
LOCAL_PRIVATE_PLATFORM_APIS := true
else
@@ -163,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 := \
@@ -202,7 +200,7 @@
$(LOCAL_PATH)/quickstep/recents_ui_overrides/res
LOCAL_FULL_LIBS_MANIFEST_FILES := \
- $(LOCAL_PATH)/AndroidManifest.xml \
+ $(LOCAL_PATH)/quickstep/AndroidManifest-launcher.xml \
$(LOCAL_PATH)/AndroidManifest-common.xml
LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
@@ -218,7 +216,7 @@
LOCAL_USE_AAPT2 := true
LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
+LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLibLauncherWrapper launcherprotosnano
ifneq (,$(wildcard frameworks/base))
LOCAL_PRIVATE_PLATFORM_APIS := true
else
@@ -249,7 +247,7 @@
LOCAL_FULL_LIBS_MANIFEST_FILES := \
$(LOCAL_PATH)/go/AndroidManifest.xml \
- $(LOCAL_PATH)/AndroidManifest.xml \
+ $(LOCAL_PATH)/quickstep/AndroidManifest-launcher.xml \
$(LOCAL_PATH)/AndroidManifest-common.xml
LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
@@ -264,7 +262,7 @@
LOCAL_USE_AAPT2 := true
LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
+LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLibLauncherWrapper launcherprotosnano
ifneq (,$(wildcard frameworks/base))
LOCAL_PRIVATE_PLATFORM_APIS := true
else
@@ -295,7 +293,7 @@
LOCAL_FULL_LIBS_MANIFEST_FILES := \
$(LOCAL_PATH)/go/AndroidManifest.xml \
- $(LOCAL_PATH)/AndroidManifest.xml \
+ $(LOCAL_PATH)/quickstep/AndroidManifest-launcher.xml \
$(LOCAL_PATH)/AndroidManifest-common.xml
LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
diff --git a/OWNERS b/OWNERS
index 538ca33..7340e84 100644
--- a/OWNERS
+++ b/OWNERS
@@ -10,6 +10,7 @@
sunnygoyal@google.com
twickham@google.com
winsonc@google.com
+zakcohen@google.com
per-file FeatureFlags.java = sunnygoyal@google.com, adamcohen@google.com
per-file BaseFlags.java = sunnygoyal@google.com, adamcohen@google.com
diff --git a/SecondaryDisplayLauncher/res/values-ar/strings.xml b/SecondaryDisplayLauncher/res/values-ar/strings.xml
index aa34c7d..ba81c11 100644
--- a/SecondaryDisplayLauncher/res/values-ar/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-ar/strings.xml
@@ -21,5 +21,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="couldnt_launch" msgid="7873588052226763866">"تعذَّر تشغيل النشاط."</string>
<string name="add_app_shortcut" msgid="2756755330707509435">"إضافة اختصار التطبيق"</string>
- <string name="set_wallpaper" msgid="6475195450505435904">"ضبط الخلفية"</string>
+ <string name="set_wallpaper" msgid="6475195450505435904">"تعيين الخلفية"</string>
</resources>
diff --git a/SecondaryDisplayLauncher/res/values-en-rCA/strings.xml b/SecondaryDisplayLauncher/res/values-en-rCA/strings.xml
deleted file mode 100644
index 8d8c419..0000000
--- a/SecondaryDisplayLauncher/res/values-en-rCA/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="couldnt_launch" msgid="7873588052226763866">"Couldn\'t launch the activity"</string>
- <string name="add_app_shortcut" msgid="2756755330707509435">"Add app shortcut"</string>
- <string name="set_wallpaper" msgid="6475195450505435904">"Set wallpaper"</string>
-</resources>
diff --git a/SecondaryDisplayLauncher/res/values-en-rXC/strings.xml b/SecondaryDisplayLauncher/res/values-en-rXC/strings.xml
deleted file mode 100644
index da69193..0000000
--- a/SecondaryDisplayLauncher/res/values-en-rXC/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="couldnt_launch" msgid="7873588052226763866">"Couldn\'t launch the activity"</string>
- <string name="add_app_shortcut" msgid="2756755330707509435">"Add app shortcut"</string>
- <string name="set_wallpaper" msgid="6475195450505435904">"Set wallpaper"</string>
-</resources>
diff --git a/go/AndroidManifest.xml b/go/AndroidManifest.xml
index fae1eff..f84a82e 100644
--- a/go/AndroidManifest.xml
+++ b/go/AndroidManifest.xml
@@ -22,7 +22,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.android.launcher3" >
- <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="25"/>
+ <uses-sdk android:targetSdkVersion="29" android:minSdkVersion="25"/>
<application
android:backupAgent="com.android.launcher3.LauncherBackupAgent"
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/QuickstepLauncher.java b/go/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
new file mode 100644
index 0000000..0c60468
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.uioverrides;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
+import com.android.launcher3.uioverrides.touchcontrollers.LandscapeStatesTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
+import com.android.launcher3.util.TouchController;
+import com.android.quickstep.SysUINavigationMode;
+
+import java.util.ArrayList;
+
+public class QuickstepLauncher extends BaseQuickstepLauncher {
+
+ public static final boolean GO_LOW_RAM_RECENTS_ENABLED = true;
+
+ @Override
+ public TouchController[] createTouchControllers() {
+ ArrayList<TouchController> list = new ArrayList<>();
+ list.add(getDragController());
+
+ if (getDeviceProfile().isVerticalBarLayout()) {
+ list.add(new LandscapeStatesTouchController(this));
+ list.add(new LandscapeEdgeSwipeController(this));
+ } else {
+ boolean allowDragToOverview = SysUINavigationMode.INSTANCE.get(this)
+ .getMode().hasGestures;
+ list.add(new PortraitStatesTouchController(this, allowDragToOverview));
+ }
+ return list.toArray(new TouchController[list.size()]);
+ }
+}
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
deleted file mode 100644
index cbc77d2..0000000
--- a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.uioverrides;
-
-import com.android.launcher3.DeviceProfile;
-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;
-import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
-import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
-import com.android.launcher3.util.TouchController;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.views.IconRecentsView;
-
-import java.util.ArrayList;
-
-/**
- * Provides recents-related {@link UiFactory} logic and classes.
- */
-public abstract class RecentsUiFactory {
-
- public static final boolean GO_LOW_RAM_RECENTS_ENABLED = true;
-
- public static TouchController[] createTouchControllers(Launcher launcher) {
- ArrayList<TouchController> list = new ArrayList<>();
- list.add(launcher.getDragController());
-
- if (launcher.getDeviceProfile().isVerticalBarLayout()) {
- list.add(new LandscapeStatesTouchController(launcher));
- list.add(new LandscapeEdgeSwipeController(launcher));
- } else {
- boolean allowDragToOverview = SysUINavigationMode.INSTANCE.get(launcher)
- .getMode().hasGestures;
- list.add(new PortraitStatesTouchController(launcher, allowDragToOverview));
- }
- if (FeatureFlags.PULL_DOWN_STATUS_BAR && Utilities.IS_DEBUG_DEVICE
- && !launcher.getDeviceProfile().isMultiWindowMode
- && !launcher.getDeviceProfile().isVerticalBarLayout()) {
- list.add(new StatusBarTouchController(launcher));
- }
- return list.toArray(new TouchController[list.size()]);
- }
-
- /**
- * Creates and returns the controller responsible for recents view state transitions.
- *
- * @param launcher the launcher activity
- * @return state handler for recents
- */
- public static StateHandler createRecentsViewStateController(Launcher launcher) {
- return new RecentsViewStateController(launcher);
- }
-
- /**
- * Clean-up logic that occurs when recents is no longer in use/visible.
- *
- * @param launcher the launcher activity
- */
- public static void resetOverview(Launcher launcher) {
- IconRecentsView recentsView = launcher.getOverviewPanel();
- recentsView.setTransitionedFromApp(false);
- }
-
- /**
- * Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
- *
- * @param launcher the launcher activity
- */
- public static void onLauncherStateOrResumeChanged(Launcher launcher) {}
-
- public static RotationMode getRotationMode(DeviceProfile dp) {
- return RotationMode.NORMAL;
- }
-
- public static void clearSwipeSharedState(boolean finishAnimation) {}
-}
diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index 92900f2..04753d2 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,13 +46,13 @@
RemoteAnimationProvider {
private static final String TAG = "AppToOverviewAnimationProvider";
- private final ActivityControlHelper<T> mHelper;
+ private final BaseActivityInterface<T> mActivityInterface;
private final int mTargetTaskId;
private IconRecentsView mRecentsView;
private AppToOverviewAnimationListener mAnimationReadyListener;
- AppToOverviewAnimationProvider(ActivityControlHelper<T> helper, int targetTaskId) {
- mHelper = helper;
+ AppToOverviewAnimationProvider(BaseActivityInterface<T> activityInterface, int targetTaskId) {
+ mActivityInterface = activityInterface;
mTargetTaskId = targetTaskId;
}
@@ -69,15 +68,15 @@
/**
* Callback for when the activity is ready/initialized.
*
- * @param activity the activity that is ready
* @param wasVisible true if it was visible before
*/
- boolean onActivityReady(T activity, Boolean wasVisible) {
+ boolean onActivityReady(Boolean wasVisible) {
+ T activity = mActivityInterface.getCreatedActivity();
if (mAnimationReadyListener != null) {
mAnimationReadyListener.onActivityReady(activity);
}
- ActivityControlHelper.AnimationFactory factory =
- mHelper.prepareRecentsUI(activity, wasVisible,
+ BaseActivityInterface.AnimationFactory factory =
+ mActivityInterface.prepareRecentsUI(wasVisible,
false /* animate activity */, (controller) -> {
controller.dispatchOnStart();
ValueAnimator anim = controller.getAnimationPlayer()
@@ -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 78%
rename from go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
rename to go/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index 057b48b..ecb9472 100644
--- a/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
+++ b/go/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -26,29 +26,30 @@
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;
+import java.util.function.Predicate;
/**
- * {@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,
+ public AnimationFactory prepareRecentsUI(boolean activityVisible,
boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
if (activityVisible) {
return (transitionLength) -> { };
}
+ RecentsActivity activity = getCreatedActivity();
IconRecentsView rv = activity.getOverviewPanel();
rv.setUsingRemoteAnimation(true);
rv.setAlpha(0);
@@ -58,17 +59,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;
}
@@ -84,14 +85,15 @@
@Override
public ActivityInitListener createActivityInitListener(
- BiPredicate<RecentsActivity, Boolean> onInitListener) {
- return new RecentsActivityTracker(onInitListener);
+ Predicate<Boolean> onInitListener) {
+ return new ActivityInitListener<>((activity, alreadyOnHome) ->
+ onInitListener.test(alreadyOnHome), RecentsActivity.ACTIVITY_TRACKER);
}
@Nullable
@Override
public RecentsActivity getCreatedActivity() {
- return RecentsActivityTracker.getCurrentActivity();
+ return RecentsActivity.ACTIVITY_TRACKER.getCreatedActivity();
}
@Nullable
@@ -115,5 +117,5 @@
}
@Override
- public void onLaunchTaskSuccess(RecentsActivity activity) { }
+ public void onLaunchTaskSuccess() { }
}
diff --git a/go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java b/go/quickstep/src/com/android/quickstep/GoActivityInterface.java
similarity index 81%
rename from go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java
rename to go/quickstep/src/com/android/quickstep/GoActivityInterface.java
index 274a347..b62d17c 100644
--- a/go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java
+++ b/go/quickstep/src/com/android/quickstep/GoActivityInterface.java
@@ -13,11 +13,11 @@
*
* @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) {
+ public void onTransitionCancelled(boolean activityVisible) {
// Go transitions to overview are all atomic.
}
@@ -29,7 +29,7 @@
}
@Override
- public void onSwipeUpToRecentsComplete(T activity) {
+ public void onSwipeUpToRecentsComplete() {
// Go does not support swipe up gesture.
}
@@ -39,7 +39,7 @@
}
@Override
- public HomeAnimationFactory prepareHomeUI(T activity) {
+ public HomeAnimationFactory prepareHomeUI() {
// Go does not support gestures from app to home.
return null;
}
@@ -63,7 +63,7 @@
}
@Override
- public void onLaunchTaskFailed(T activity) {
+ public void onLaunchTaskFailed() {
// Go does not support gestures from one task to another.
}
}
diff --git a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java b/go/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
similarity index 78%
rename from go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
rename to go/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index b0d9cda..3e93480 100644
--- a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/go/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -26,26 +26,25 @@
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.views.IconRecentsView;
-import java.util.function.BiPredicate;
import java.util.function.Consumer;
+import java.util.function.Predicate;
/**
- * {@link 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,
- boolean activityVisible, boolean animateActivity,
+ public AnimationFactory prepareRecentsUI(boolean activityVisible, boolean animateActivity,
Consumer<AnimatorPlaybackController> callback) {
- LauncherState fromState = activity.getStateManager().getState();
- activity.<IconRecentsView>getOverviewPanel().setUsingRemoteAnimation(true);
+ Launcher launcher = getCreatedActivity();
+ LauncherState fromState = launcher.getStateManager().getState();
+ launcher.<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) {
- callback.accept(activity.getStateManager().createAnimationToNewWorkspace(
+ public void createActivityInterface(long transitionLength) {
+ callback.accept(launcher.getStateManager().createAnimationToNewWorkspace(
fromState, OVERVIEW, transitionLength));
}
@@ -55,9 +54,9 @@
}
@Override
- public ActivityInitListener createActivityInitListener(
- BiPredicate<Launcher, Boolean> onInitListener) {
- return new LauncherInitListener(onInitListener);
+ public LauncherInitListener createActivityInitListener(Predicate<Boolean> onInitListener) {
+ return new LauncherInitListener((activity, alreadyOnHome) ->
+ onInitListener.test(alreadyOnHome));
}
@Override
@@ -106,7 +105,8 @@
}
@Override
- public void onLaunchTaskSuccess(Launcher launcher) {
+ public void onLaunchTaskSuccess() {
+ Launcher launcher = getCreatedActivity();
launcher.getStateManager().moveToRestState();
}
}
diff --git a/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 216972c..a436ce7 100644
--- a/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -27,8 +27,8 @@
import com.android.launcher3.BaseDraggingActivity;
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;
@@ -40,26 +40,28 @@
public class OverviewCommandHelper {
private final Context mContext;
- private final ActivityManagerWrapper mAM;
+ private final RecentsAnimationDeviceState mDeviceState;
private final RecentsModel mRecentsModel;
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();
+ 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);
+ ActivityManagerWrapper.getInstance()
+ .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
MAIN_EXECUTOR.execute(new RecentsActivityCommand<>());
}
@@ -99,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
diff --git a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 19dd82f..f743663 100644
--- a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -34,6 +34,9 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.UserManagerCompat;
+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;
@@ -44,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() {
@@ -63,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();
}
@@ -90,7 +84,7 @@
@Override
public void onTip(int actionType, int viewType) {
- if (mIsUserUnlocked) {
+ if (mDeviceState.isUserUnlocked()) {
mOverviewCommandHelper.onTip(actionType, viewType);
}
}
@@ -127,7 +121,7 @@
public void onMotionEvent(MotionEvent ev) { }
public void onBind(ISystemUiProxy iSystemUiProxy) {
- mRecentsModel.setSystemUiProxy(iSystemUiProxy);
+ SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(iSystemUiProxy);
}
};
@@ -140,35 +134,30 @@
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/quickstep/src/com/android/quickstep/util/ShelfPeekAnim.java b/go/quickstep/src/com/android/quickstep/util/ShelfPeekAnim.java
index fb89013..3842efb 100644
--- a/go/quickstep/src/com/android/quickstep/util/ShelfPeekAnim.java
+++ b/go/quickstep/src/com/android/quickstep/util/ShelfPeekAnim.java
@@ -17,7 +17,7 @@
import com.android.launcher3.Launcher;
-/** Empty class, only exists so that l3goWithQuickstepIconRecentsDebug compiles. */
+/** Empty class, only exists so that lowRamWithQuickstepIconRecentsDebug compiles. */
public class ShelfPeekAnim {
public ShelfPeekAnim(Launcher launcher) {
}
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index 87b4d4e..e380698 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -40,6 +40,7 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.FloatProperty;
@@ -66,6 +67,7 @@
import com.android.launcher3.util.Themes;
import com.android.quickstep.ContentFillItemAnimator;
import com.android.quickstep.RecentsModel;
+import com.android.quickstep.RecentsModel.TaskVisualsChangeListener;
import com.android.quickstep.RecentsToActivityHelper;
import com.android.quickstep.TaskActionController;
import com.android.quickstep.TaskAdapter;
@@ -74,6 +76,7 @@
import com.android.quickstep.TaskSwipeCallback;
import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
@@ -87,7 +90,8 @@
* Root view for the icon recents view. Acts as the main interface to the rest of the Launcher code
* base.
*/
-public final class IconRecentsView extends FrameLayout implements Insettable {
+public final class IconRecentsView extends FrameLayout
+ implements Insettable, TaskVisualsChangeListener {
public static final FloatProperty<IconRecentsView> CONTENT_ALPHA =
new FloatProperty<IconRecentsView>("contentAlpha") {
@@ -159,22 +163,6 @@
private AnimatorSet mLayoutAnimation;
private final ArraySet<View> mLayingOutViews = new ArraySet<>();
private Rect mInsets;
- private final RecentsModel.TaskThumbnailChangeListener listener = (taskId, thumbnailData) -> {
- ArrayList<TaskItemView> itemViews = getTaskViews();
- for (int i = 0, size = itemViews.size(); i < size; i++) {
- TaskItemView taskView = itemViews.get(i);
- TaskHolder taskHolder = (TaskHolder) mTaskRecyclerView.getChildViewHolder(taskView);
- Optional<Task> optTask = taskHolder.getTask();
- if (optTask.filter(task -> task.key.id == taskId).isPresent()) {
- Task task = optTask.get();
- // Update thumbnail on the task.
- task.thumbnail = thumbnailData;
- taskView.setThumbnail(thumbnailData);
- return task;
- }
- }
- return null;
- };
public IconRecentsView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -189,10 +177,30 @@
mActivity.getStatsLogManager());
mTaskAdapter.setActionController(mTaskActionController);
mTaskLayoutManager = new LinearLayoutManager(mContext, VERTICAL, true /* reverseLayout */);
- RecentsModel.INSTANCE.get(context).addThumbnailChangeListener(listener);
}
@Override
+ public Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {
+ ArrayList<TaskItemView> itemViews = getTaskViews();
+ for (int i = 0, size = itemViews.size(); i < size; i++) {
+ TaskItemView taskView = itemViews.get(i);
+ TaskHolder taskHolder = (TaskHolder) mTaskRecyclerView.getChildViewHolder(taskView);
+ Optional<Task> optTask = taskHolder.getTask();
+ if (optTask.filter(task -> task.key.id == taskId).isPresent()) {
+ Task task = optTask.get();
+ // Update thumbnail on the task.
+ task.thumbnail = thumbnailData;
+ taskView.setThumbnail(thumbnailData);
+ return task;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void onTaskIconChanged(String pkg, UserHandle user) { }
+
+ @Override
protected void onFinishInflate() {
super.onFinishInflate();
if (mTaskRecyclerView == null) {
@@ -275,6 +283,18 @@
}
@Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
+ }
+
+ @Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
int childCount = mTaskRecyclerView.getChildCount();
diff --git a/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/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/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 f491ed7..8bd9dba 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
@@ -44,6 +44,7 @@
private final PackageManager mPm;
private final ColorExtractor mColorExtractor;
private boolean mDisableColorExtractor;
+ private boolean mBadgeOnLeft = false;
protected final int mFillResIconDpi;
protected final int mIconBitmapSize;
@@ -77,6 +78,7 @@
protected void clear() {
mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND;
mDisableColorExtractor = false;
+ mBadgeOnLeft = false;
}
public ShadowGenerator getShadowGenerator() {
@@ -116,7 +118,7 @@
icon = createIconBitmap(new BitmapDrawable(mContext.getResources(), icon), 1f);
}
- return BitmapInfo.fromBitmap(icon, mDisableColorExtractor ? null : mColorExtractor);
+ return BitmapInfo.of(icon, extractColor(icon));
}
public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user,
@@ -186,7 +188,10 @@
} else {
result = bitmap;
}
- return BitmapInfo.fromBitmap(result, mDisableColorExtractor ? null : mColorExtractor);
+ int color = extractColor(result);
+ return icon instanceof BitmapInfo.Extender
+ ? ((BitmapInfo.Extender) icon).getExtendedInfo(result, color, this)
+ : BitmapInfo.of(result, color);
}
public Bitmap createScaledBitmapWithoutShadow(Drawable icon, boolean shrinkNonAdaptiveIcons) {
@@ -198,6 +203,13 @@
}
/**
+ * Switches badging to left/right
+ */
+ public void setBadgeOnLeft(boolean badgeOnLeft) {
+ mBadgeOnLeft = badgeOnLeft;
+ }
+
+ /**
* Sets the background color used for wrapped adaptive icon
*/
public void setWrapperBackgroundColor(int color) {
@@ -258,8 +270,12 @@
*/
public void badgeWithDrawable(Canvas target, Drawable badge) {
int badgeSize = getBadgeSizeForIconSize(mIconBitmapSize);
- badge.setBounds(mIconBitmapSize - badgeSize, mIconBitmapSize - badgeSize,
- mIconBitmapSize, mIconBitmapSize);
+ if (mBadgeOnLeft) {
+ badge.setBounds(0, mIconBitmapSize - badgeSize, badgeSize, mIconBitmapSize);
+ } else {
+ badge.setBounds(mIconBitmapSize - badgeSize, mIconBitmapSize - badgeSize,
+ mIconBitmapSize, mIconBitmapSize);
+ }
badge.draw(target);
}
@@ -337,6 +353,10 @@
iconDpi);
}
+ private int extractColor(Bitmap bitmap) {
+ return mDisableColorExtractor ? 0 : mColorExtractor.findDominantColorByHue(bitmap);
+ }
+
/**
* Returns the correct badge size given an icon size
*/
diff --git a/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java b/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java
index 245561e..d33f9b1 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java
@@ -18,32 +18,55 @@
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
+import androidx.annotation.NonNull;
+
public class BitmapInfo {
public static final Bitmap LOW_RES_ICON = Bitmap.createBitmap(1, 1, Config.ALPHA_8);
+ public static final BitmapInfo LOW_RES_INFO = fromBitmap(LOW_RES_ICON);
- public Bitmap icon;
- public int color;
+ public final Bitmap icon;
+ public final int color;
- public void applyTo(BitmapInfo info) {
- info.icon = icon;
- info.color = color;
+ public BitmapInfo(Bitmap icon, int color) {
+ this.icon = icon;
+ this.color = color;
+ }
+
+ /**
+ * Ideally icon should not be null, except in cases when generating hardware bitmap failed
+ */
+ public final boolean isNullOrLowRes() {
+ return icon == null || icon == LOW_RES_ICON;
}
public final boolean isLowRes() {
return LOW_RES_ICON == icon;
}
- public static BitmapInfo fromBitmap(Bitmap bitmap) {
- return fromBitmap(bitmap, null);
+ public static BitmapInfo fromBitmap(@NonNull Bitmap bitmap) {
+ return of(bitmap, 0);
}
- public static BitmapInfo fromBitmap(Bitmap bitmap, ColorExtractor dominantColorExtractor) {
- BitmapInfo info = new BitmapInfo();
- info.icon = bitmap;
- info.color = dominantColorExtractor != null
- ? dominantColorExtractor.findDominantColorByHue(bitmap)
- : 0;
- return info;
+ public static BitmapInfo of(@NonNull Bitmap bitmap, int color) {
+ return new BitmapInfo(bitmap, color);
+ }
+
+ /**
+ * Interface to be implemented by drawables to provide a custom BitmapInfo
+ */
+ public interface Extender {
+
+ /**
+ * Called for creating a custom BitmapInfo
+ */
+ default BitmapInfo getExtendedInfo(Bitmap bitmap, int color, BaseIconFactory iconFactory) {
+ return BitmapInfo.of(bitmap, color);
+ }
+
+ /**
+ * Notifies the drawable that it will be drawn directly in the UI, without any preprocessing
+ */
+ default void prepareToDrawOnUi() { }
}
}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
index a886c0a..6f63d88 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
@@ -71,7 +71,10 @@
// Empty class name is used for storing package default entry.
public static final String EMPTY_CLASS_NAME = ".";
- public static class CacheEntry extends BitmapInfo {
+ public static class CacheEntry {
+
+ @NonNull
+ public BitmapInfo bitmap = BitmapInfo.LOW_RES_INFO;
public CharSequence title = "";
public CharSequence contentDescription = "";
}
@@ -259,23 +262,23 @@
if (!replaceExisting) {
entry = mCache.get(key);
// We can't reuse the entry if the high-res icon is not present.
- if (entry == null || entry.icon == null || entry.isLowRes()) {
+ if (entry == null || entry.bitmap.isNullOrLowRes()) {
entry = null;
}
}
if (entry == null) {
entry = new CacheEntry();
- cachingLogic.loadIcon(mContext, object, entry);
+ entry.bitmap = cachingLogic.loadIcon(mContext, object);
}
// 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;
+ if (entry.bitmap.isNullOrLowRes()) return;
entry.title = cachingLogic.getLabel(object);
entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user);
if (cachingLogic.addToMemCache()) mCache.put(key, entry);
- ContentValues values = newContentValues(entry, entry.title.toString(),
+ ContentValues values = newContentValues(entry.bitmap, entry.title.toString(),
componentName.getPackageName(), cachingLogic.getKeywords(object, mLocaleList));
addIconToDB(values, componentName, info, userSerial);
}
@@ -300,8 +303,8 @@
return mDefaultIcons.get(user);
}
- public boolean isDefaultIcon(Bitmap icon, UserHandle user) {
- return getDefaultIcon(user).icon == icon;
+ public boolean isDefaultIcon(BitmapInfo icon, UserHandle user) {
+ return getDefaultIcon(user).icon == icon.icon;
}
/**
@@ -315,7 +318,7 @@
assertWorkerThread();
ComponentKey cacheKey = new ComponentKey(componentName, user);
CacheEntry entry = mCache.get(cacheKey);
- if (entry == null || (entry.isLowRes() && !useLowResIcon)) {
+ if (entry == null || (entry.bitmap.isLowRes() && !useLowResIcon)) {
entry = new CacheEntry();
if (cachingLogic.addToMemCache()) {
mCache.put(cacheKey, entry);
@@ -330,7 +333,7 @@
providerFetchedOnce = true;
if (object != null) {
- cachingLogic.loadIcon(mContext, object, entry);
+ entry.bitmap = cachingLogic.loadIcon(mContext, object);
} else {
if (usePackageIcon) {
CacheEntry packageEntry = getEntryForPackageLocked(
@@ -338,15 +341,15 @@
if (packageEntry != null) {
if (DEBUG) Log.d(TAG, "using package default icon for " +
componentName.toShortString());
- packageEntry.applyTo(entry);
+ entry.bitmap = packageEntry.bitmap;
entry.title = packageEntry.title;
entry.contentDescription = packageEntry.contentDescription;
}
}
- if (entry.icon == null) {
+ if (entry.bitmap == null) {
if (DEBUG) Log.d(TAG, "using default icon for " +
componentName.toShortString());
- getDefaultIcon(user).applyTo(entry);
+ entry.bitmap = getDefaultIcon(user);
}
}
}
@@ -374,7 +377,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);
@@ -390,10 +393,10 @@
}
if (icon != null) {
BaseIconFactory li = getIconFactory();
- li.createIconBitmap(icon).applyTo(entry);
+ entry.bitmap = li.createIconBitmap(icon);
li.close();
}
- if (!TextUtils.isEmpty(title) && entry.icon != null) {
+ if (!TextUtils.isEmpty(title) && entry.bitmap.icon != null) {
mCache.put(cacheKey, entry);
}
}
@@ -413,7 +416,7 @@
ComponentKey cacheKey = getPackageKey(packageName, user);
CacheEntry entry = mCache.get(cacheKey);
- if (entry == null || (entry.isLowRes() && !useLowResIcon)) {
+ if (entry == null || (entry.bitmap.isLowRes() && !useLowResIcon)) {
entry = new CacheEntry();
boolean entryUpdated = true;
@@ -438,8 +441,8 @@
entry.title = appInfo.loadLabel(mPackageManager);
entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user);
- entry.icon = useLowResIcon ? LOW_RES_ICON : iconInfo.icon;
- entry.color = iconInfo.color;
+ entry.bitmap = BitmapInfo.of(
+ useLowResIcon ? LOW_RES_ICON : iconInfo.icon, iconInfo.color);
// Add the icon in the DB here, since these do not get written during
// package updates.
@@ -461,7 +464,7 @@
return entry;
}
- private boolean getEntryFromDB(ComponentKey cacheKey, CacheEntry entry, boolean lowRes) {
+ protected boolean getEntryFromDB(ComponentKey cacheKey, CacheEntry entry, boolean lowRes) {
Cursor c = null;
try {
c = mIconDb.query(
@@ -472,7 +475,7 @@
Long.toString(getSerialNumberForUser(cacheKey.user))});
if (c.moveToNext()) {
// Set the alpha to be 255, so that we never have a wrong color
- entry.color = setColorAlphaBound(c.getInt(0), 255);
+ entry.bitmap = BitmapInfo.of(LOW_RES_ICON, setColorAlphaBound(c.getInt(0), 255));
entry.title = c.getString(1);
if (entry.title == null) {
entry.title = "";
@@ -482,13 +485,12 @@
entry.title, cacheKey.user);
}
- if (lowRes) {
- entry.icon = LOW_RES_ICON;
- } else {
+ if (!lowRes) {
byte[] data = c.getBlob(2);
try {
- entry.icon = BitmapFactory.decodeByteArray(data, 0, data.length,
- mDecodeOptions);
+ entry.bitmap = BitmapInfo.of(
+ BitmapFactory.decodeByteArray(data, 0, data.length, mDecodeOptions),
+ entry.bitmap.color);
} catch (Exception e) { }
}
return true;
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java b/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
index e40a9c2..a89ede7 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
@@ -17,9 +17,11 @@
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageInfo;
import android.os.LocaleList;
import android.os.UserHandle;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.icons.BitmapInfo;
@@ -32,7 +34,8 @@
CharSequence getLabel(T object);
- void loadIcon(Context context, T object, BitmapInfo target);
+ @NonNull
+ BitmapInfo loadIcon(Context context, T object);
/**
* Provides a option list of keywords to associate with this object
@@ -43,6 +46,13 @@
}
/**
+ * 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() {
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java b/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java
index 8224966..bcdbce5 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java
@@ -171,7 +171,8 @@
long updateTime = c.getLong(indexLastUpdate);
int version = c.getInt(indexVersion);
T app = componentMap.remove(component);
- if (version == info.versionCode && updateTime == info.lastUpdateTime
+ if (version == info.versionCode
+ && updateTime == cachingLogic.getLastUpdatedTime(app, info)
&& TextUtils.equals(c.getString(systemStateIndex),
mIconCache.getIconSystemState(info.packageName))) {
@@ -231,7 +232,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/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml
new file mode 100644
index 0000000..60afddb
--- /dev/null
+++ b/quickstep/AndroidManifest-launcher.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.launcher3">
+ <uses-sdk android:targetSdkVersion="29" android:minSdkVersion="25"/>
+ <!--
+ Manifest entries specific to Launcher3. This is merged with AndroidManifest-common.xml.
+ Refer comments around specific entries on how to extend individual components.
+ -->
+
+ <application
+ android:backupAgent="com.android.launcher3.LauncherBackupAgent"
+ android:fullBackupOnly="true"
+ android:fullBackupContent="@xml/backupscheme"
+ android:hardwareAccelerated="true"
+ android:icon="@drawable/ic_launcher_home"
+ android:label="@string/derived_app_name"
+ android:theme="@style/AppTheme"
+ android:largeHeap="@bool/config_largeHeap"
+ android:restoreAnyVersion="true"
+ android:supportsRtl="true" >
+
+ <!--
+ Main launcher activity. When extending only change the name, and keep all the
+ attributes and intent filters the same
+ -->
+ <activity
+ android:name="com.android.launcher3.uioverrides.QuickstepLauncher"
+ android:launchMode="singleTask"
+ android:clearTaskOnLaunch="true"
+ android:stateNotNeeded="true"
+ android:windowSoftInputMode="adjustPan"
+ android:screenOrientation="unspecified"
+ android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
+ android:resizeableActivity="true"
+ android:resumeWhilePausing="true"
+ android:taskAffinity=""
+ android:enabled="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.HOME" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.MONKEY"/>
+ <category android:name="android.intent.category.LAUNCHER_APP" />
+ </intent-filter>
+ <meta-data
+ android:name="com.android.launcher3.grid.control"
+ android:value="${packageName}.grid_control" />
+ </activity>
+
+ </application>
+</manifest>
diff --git a/quickstep/recents_ui_overrides/res/drawable/predicted_icon_background.xml b/quickstep/recents_ui_overrides/res/drawable/predicted_icon_background.xml
new file mode 100644
index 0000000..cfc6d48
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/drawable/predicted_icon_background.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:inset="@dimen/predicted_icon_background_inset">
+ <shape>
+ <solid android:color="?attr/folderFillColor" />
+ <corners android:radius="@dimen/predicted_icon_background_corner_radius" />
+ </shape>
+</inset>
diff --git a/quickstep/recents_ui_overrides/res/values/dimens.xml b/quickstep/recents_ui_overrides/res/values/dimens.xml
index 863a8ba..ee672d4 100644
--- a/quickstep/recents_ui_overrides/res/values/dimens.xml
+++ b/quickstep/recents_ui_overrides/res/values/dimens.xml
@@ -28,4 +28,9 @@
<dimen name="swipe_up_fling_min_visible_change">18dp</dimen>
<dimen name="swipe_up_y_overshoot">10dp</dimen>
<dimen name="swipe_up_max_workspace_trans_y">-60dp</dimen>
+
+ <!-- Predicted icon related -->
+ <dimen name="predicted_icon_background_corner_radius">15dp</dimen>
+ <dimen name="predicted_icon_background_inset">8dp</dimen>
+
</resources>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java
new file mode 100644
index 0000000..fe9fd60
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3;
+
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionManager;
+import android.app.prediction.AppPredictor;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.AppTargetId;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.allapps.AllAppsStore;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.appprediction.ComponentKeyMapper;
+import com.android.launcher3.appprediction.DynamicItemCache;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.touch.ItemLongClickListener;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.ComponentKey;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Provides prediction ability for the hotseat. Fills gaps in hotseat with predicted items, allows
+ * pinning of predicted apps and manages replacement of predicted apps with user drag.
+ */
+public class HotseatPredictionController implements DragController.DragListener,
+ View.OnAttachStateChangeListener, SystemShortcut.Factory<QuickstepLauncher>,
+ InvariantDeviceProfile.OnIDPChangeListener, AllAppsStore.OnUpdateListener,
+ IconCache.ItemInfoUpdateReceiver {
+
+ private static final String TAG = "PredictiveHotseat";
+ private static final boolean DEBUG = false;
+
+ //TODO: replace this with AppTargetEvent.ACTION_UNPIN (b/144119543)
+ private static final int APPTARGET_ACTION_UNPIN = 4;
+
+ private static final String PREDICTION_CLIENT = "hotseat";
+
+ private DropTarget.DragObject mDragObject;
+ private int mHotSeatItemsCount;
+
+ private Launcher mLauncher;
+ private Hotseat mHotseat;
+
+ private List<ComponentKeyMapper> mComponentKeyMappers = new ArrayList<>();
+
+ private DynamicItemCache mDynamicItemCache;
+
+ private AppPredictor mAppPredictor;
+ private AllAppsStore mAllAppsStore;
+
+ public HotseatPredictionController(Launcher launcher) {
+ mLauncher = launcher;
+ mHotseat = launcher.getHotseat();
+ mAllAppsStore = mLauncher.getAppsView().getAppsStore();
+ mAllAppsStore.addUpdateListener(this);
+ mDynamicItemCache = new DynamicItemCache(mLauncher, () -> fillGapsWithPrediction(false));
+ mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons;
+ launcher.getDeviceProfile().inv.addOnChangeListener(this);
+ mHotseat.addOnAttachStateChangeListener(this);
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View view) {
+ mLauncher.getDragController().addDragListener(this);
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View view) {
+ mLauncher.getDragController().removeDragListener(this);
+ }
+
+ /**
+ * Fills gaps in the hotseat with predictions
+ */
+ public void fillGapsWithPrediction(boolean animate) {
+ if (mDragObject != null) {
+ return;
+ }
+ List<WorkspaceItemInfo> predictedApps = mapToWorkspaceItemInfo(mComponentKeyMappers);
+ int predictionIndex = 0;
+ ArrayList<ItemInfo> newItemsToAdd = new ArrayList<>();
+ for (int rank = 0; rank < mHotSeatItemsCount; rank++) {
+ View child = mHotseat.getChildAt(
+ mHotseat.getCellXFromOrder(rank),
+ mHotseat.getCellYFromOrder(rank));
+
+ if (child != null && !isPredictedIcon(child)) {
+ continue;
+ }
+ if (predictedApps.size() <= predictionIndex) {
+ // Remove predicted apps from the past
+ if (isPredictedIcon(child)) {
+ mHotseat.removeView(child);
+ }
+ continue;
+ }
+
+ WorkspaceItemInfo predictedItem = predictedApps.get(predictionIndex++);
+ if (isPredictedIcon(child)) {
+ BubbleTextView icon = (BubbleTextView) child;
+ icon.applyFromWorkspaceItem(predictedItem);
+ } else {
+ newItemsToAdd.add(predictedItem);
+ }
+ preparePredictionInfo(predictedItem, rank);
+ }
+ mLauncher.bindItems(newItemsToAdd, animate);
+ for (BubbleTextView icon : getPredictedIcons()) {
+ icon.verifyHighRes();
+ icon.setOnLongClickListener((v) -> {
+ PopupContainerWithArrow.showForIcon((BubbleTextView) v);
+ return true;
+ });
+ icon.setBackgroundResource(R.drawable.predicted_icon_background);
+ }
+ }
+
+ /**
+ * Unregisters callbacks and frees resources
+ */
+ public void destroy() {
+ mAllAppsStore.removeUpdateListener(this);
+ mLauncher.getDeviceProfile().inv.removeOnChangeListener(this);
+ mHotseat.removeOnAttachStateChangeListener(this);
+ if (mAppPredictor != null) {
+ mAppPredictor.destroy();
+ }
+ }
+
+ /**
+ * Creates App Predictor with all the current apps pinned on the hotseat
+ */
+ public void createPredictor() {
+ AppPredictionManager apm = mLauncher.getSystemService(AppPredictionManager.class);
+ if (apm == null) {
+ return;
+ }
+ if (mAppPredictor != null) {
+ mAppPredictor.destroy();
+ }
+ mAppPredictor = apm.createAppPredictionSession(
+ new AppPredictionContext.Builder(mLauncher)
+ .setUiSurface(PREDICTION_CLIENT)
+ .setPredictedTargetCount(mHotSeatItemsCount)
+ .setExtras(getAppPredictionContextExtra())
+ .build());
+ mAppPredictor.registerPredictionUpdates(mLauncher.getMainExecutor(),
+ this::setPredictedApps);
+ mAppPredictor.requestPredictionUpdate();
+ }
+
+ private Bundle getAppPredictionContextExtra() {
+ Bundle bundle = new Bundle();
+ ViewGroup vg = mHotseat.getShortcutsAndWidgets();
+ ArrayList<AppTarget> pinnedApps = new ArrayList<>();
+ for (int i = 0; i < vg.getChildCount(); i++) {
+ View child = vg.getChildAt(i);
+ if (isPinnedIcon(child)) {
+ WorkspaceItemInfo itemInfo = (WorkspaceItemInfo) child.getTag();
+ pinnedApps.add(getAppTargetFromItemInfo(itemInfo));
+ }
+ }
+ bundle.putParcelableArrayList("pinned_apps", pinnedApps);
+ return bundle;
+ }
+
+ private void setPredictedApps(List<AppTarget> appTargets) {
+ mComponentKeyMappers.clear();
+ for (AppTarget appTarget : appTargets) {
+ ComponentKey key;
+ if (appTarget.getShortcutInfo() != null) {
+ key = ShortcutKey.fromInfo(appTarget.getShortcutInfo());
+ } else {
+ key = new ComponentKey(new ComponentName(appTarget.getPackageName(),
+ appTarget.getClassName()), appTarget.getUser());
+ }
+ mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache));
+ }
+ updateDependencies();
+ fillGapsWithPrediction(false);
+ }
+
+ private void updateDependencies() {
+ mDynamicItemCache.updateDependencies(mComponentKeyMappers, mAllAppsStore, this,
+ mHotSeatItemsCount);
+ }
+
+ private void pinPrediction(ItemInfo info) {
+ BubbleTextView icon = (BubbleTextView) mHotseat.getChildAt(
+ mHotseat.getCellXFromOrder(info.rank),
+ mHotseat.getCellYFromOrder(info.rank));
+ if (icon == null) {
+ return;
+ }
+ WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo((WorkspaceItemInfo) info);
+ mLauncher.getModelWriter().addItemToDatabase(workspaceItemInfo,
+ LauncherSettings.Favorites.CONTAINER_HOTSEAT, workspaceItemInfo.screenId,
+ workspaceItemInfo.cellX, workspaceItemInfo.cellY);
+ ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 1, 0.8f, 1).start();
+ icon.reset();
+ icon.applyFromWorkspaceItem(workspaceItemInfo);
+ icon.setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE);
+ AppTarget appTarget = getAppTargetFromItemInfo(workspaceItemInfo);
+ notifyItemAction(appTarget, AppTargetEvent.ACTION_PIN);
+ }
+
+ private List<WorkspaceItemInfo> mapToWorkspaceItemInfo(
+ List<ComponentKeyMapper> components) {
+ AllAppsStore allAppsStore = mLauncher.getAppsView().getAppsStore();
+ if (allAppsStore.getApps().length == 0) {
+ return Collections.emptyList();
+ }
+
+ List<WorkspaceItemInfo> predictedApps = new ArrayList<>();
+ for (ComponentKeyMapper mapper : components) {
+ ItemInfoWithIcon info = mapper.getApp(allAppsStore);
+ if (info instanceof AppInfo) {
+ WorkspaceItemInfo predictedApp = new WorkspaceItemInfo((AppInfo) info);
+ predictedApps.add(predictedApp);
+ } else if (info instanceof WorkspaceItemInfo) {
+ predictedApps.add(new WorkspaceItemInfo((WorkspaceItemInfo) info));
+ } else {
+ if (DEBUG) {
+ Log.e(TAG, "Predicted app not found: " + mapper);
+ }
+ }
+ // Stop at the number of hotseat items
+ if (predictedApps.size() == mHotSeatItemsCount) {
+ break;
+ }
+ }
+ return predictedApps;
+ }
+
+ private List<BubbleTextView> getPredictedIcons() {
+ List<BubbleTextView> icons = new ArrayList<>();
+ ViewGroup vg = mHotseat.getShortcutsAndWidgets();
+ for (int i = 0; i < vg.getChildCount(); i++) {
+ View child = vg.getChildAt(i);
+ if (isPredictedIcon(child)) {
+ icons.add((BubbleTextView) child);
+ }
+ }
+ return icons;
+ }
+
+ private void removePredictedApps(boolean animate) {
+ for (BubbleTextView icon : getPredictedIcons()) {
+ if (animate) {
+ icon.animate().scaleY(0).scaleX(0).setListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ if (icon.getParent() != null) {
+ mHotseat.removeView(icon);
+ }
+ }
+ });
+ } else {
+ if (icon.getParent() != null) {
+ mHotseat.removeView(icon);
+ }
+ }
+ }
+ }
+
+ private void notifyItemAction(AppTarget target, int action) {
+ if (mAppPredictor != null) {
+ mAppPredictor.notifyAppTargetEvent(new AppTargetEvent.Builder(target, action).build());
+ }
+ }
+
+ @Override
+ public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+ removePredictedApps(true);
+ mDragObject = dragObject;
+ }
+
+ @Override
+ public void onDragEnd() {
+ if (mDragObject == null) {
+ return;
+ }
+ ItemInfo dragInfo = mDragObject.dragInfo;
+ if (dragInfo instanceof WorkspaceItemInfo && dragInfo.getTargetComponent() != null) {
+ if (isInHotseat(dragInfo) && !isInHotseat(mDragObject.originalDragInfo)) {
+ notifyItemAction(getAppTargetFromItemInfo(dragInfo), AppTargetEvent.ACTION_PIN);
+ } else if (!isInHotseat(dragInfo) && isInHotseat(mDragObject.originalDragInfo)) {
+ notifyItemAction(getAppTargetFromItemInfo(dragInfo), APPTARGET_ACTION_UNPIN);
+ }
+ }
+ mDragObject = null;
+ fillGapsWithPrediction(true);
+ }
+
+ @Nullable
+ @Override
+ public SystemShortcut<QuickstepLauncher> getShortcut(QuickstepLauncher activity,
+ ItemInfo itemInfo) {
+ if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
+ return null;
+ }
+ return new PinPrediction(activity, itemInfo);
+ }
+
+ private void preparePredictionInfo(WorkspaceItemInfo itemInfo, int rank) {
+ itemInfo.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
+ itemInfo.rank = rank;
+ itemInfo.cellX = rank;
+ itemInfo.cellY = mHotSeatItemsCount - rank - 1;
+ itemInfo.screenId = rank;
+ }
+
+ @Override
+ public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
+ this.mHotSeatItemsCount = profile.numHotseatIcons;
+ createPredictor();
+ }
+
+ @Override
+ public void onAppsUpdated() {
+ updateDependencies();
+ fillGapsWithPrediction(false);
+ }
+
+ @Override
+ public void reapplyItemInfo(ItemInfoWithIcon info) {
+
+ }
+
+ private class PinPrediction extends SystemShortcut<QuickstepLauncher> {
+
+ private PinPrediction(QuickstepLauncher target, ItemInfo itemInfo) {
+ super(R.drawable.ic_pin, R.string.pin_prediction, target,
+ itemInfo);
+ }
+
+ @Override
+ public void onClick(View view) {
+ dismissTaskMenuView(mTarget);
+ pinPrediction(mItemInfo);
+ }
+ }
+
+ private static boolean isPredictedIcon(View view) {
+ return view instanceof BubbleTextView && view.getTag() instanceof WorkspaceItemInfo
+ && ((WorkspaceItemInfo) view.getTag()).container
+ == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
+ }
+
+ private static boolean isPinnedIcon(View view) {
+ if (!(view instanceof BubbleTextView && view.getTag() instanceof WorkspaceItemInfo)) {
+ return false;
+ }
+ ItemInfo info = (ItemInfo) view.getTag();
+ return info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT && (
+ info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+ || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT);
+ }
+
+ private static boolean isInHotseat(ItemInfo itemInfo) {
+ return itemInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+ }
+
+ private static AppTarget getAppTargetFromItemInfo(ItemInfo info) {
+ if (info.getTargetComponent() == null) return null;
+ return new AppTarget.Builder(
+ new AppTargetId("app:" + info.getTargetComponent().getPackageName()),
+ info.getTargetComponent().getPackageName(), info.user).setClassName(
+ info.getTargetComponent().getClassName()).build();
+ }
+}
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 114fd8e..51ee216 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -50,7 +50,7 @@
import com.android.launcher3.anim.AnimatorSetBuilder;
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;
@@ -81,15 +81,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
deleted file mode 100644
index c5c4add..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3;
-
-import com.android.launcher3.appprediction.PredictionUiStateManager;
-import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
-
-import java.util.function.BiPredicate;
-
-public class LauncherInitListenerEx extends LauncherInitListener {
-
- public LauncherInitListenerEx(BiPredicate<Launcher, Boolean> onInitListener) {
- super(onInitListener);
- }
-
- @Override
- protected boolean init(Launcher launcher, boolean alreadyOnHome) {
- PredictionUiStateManager.INSTANCE.get(launcher).switchClient(Client.OVERVIEW);
- return super.init(launcher, alreadyOnHome);
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java
index b9f4147..0712285 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java
@@ -18,8 +18,6 @@
import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
-import android.content.Context;
-
import com.android.launcher3.AppInfo;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.allapps.AllAppsStore;
@@ -29,11 +27,9 @@
public class ComponentKeyMapper {
protected final ComponentKey componentKey;
- private final Context mContext;
private final DynamicItemCache mCache;
- public ComponentKeyMapper(Context context, ComponentKey key, DynamicItemCache cache) {
- mContext = context;
+ public ComponentKeyMapper(ComponentKey key, DynamicItemCache cache) {
componentKey = key;
mCache = cache;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
index 65e69b6..38bb180 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
@@ -18,6 +18,7 @@
import static android.content.pm.PackageManager.MATCH_INSTANT;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
import android.content.Context;
import android.content.Intent;
@@ -37,8 +38,10 @@
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
+import com.android.launcher3.AppInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.shortcuts.DeepShortcutManager;
@@ -72,6 +75,7 @@
private final Handler mUiHandler;
private final InstantAppResolver mInstantAppResolver;
private final Runnable mOnUpdateCallback;
+ private final IconCache mIconCache;
private final Map<ShortcutKey, WorkspaceItemInfo> mShortcuts;
private final Map<String, InstantAppItemInfo> mInstantApps;
@@ -82,6 +86,7 @@
mUiHandler = new Handler(Looper.getMainLooper(), this::handleUiMessage);
mInstantAppResolver = InstantAppResolver.newInstance(context);
mOnUpdateCallback = onUpdateCallback;
+ mIconCache = LauncherAppState.getInstance(mContext).getIconCache();
mShortcuts = new HashMap<>();
mInstantApps = new HashMap<>();
@@ -170,7 +175,7 @@
if (!details.isEmpty()) {
WorkspaceItemInfo si = new WorkspaceItemInfo(details.get(0), mContext);
try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
- si.applyFrom(li.createShortcutIcon(details.get(0), true /* badged */, null));
+ si.bitmap = li.createShortcutIcon(details.get(0), true /* badged */, null);
} catch (Exception e) {
if (DEBUG) {
Log.e(TAG, "Error loading shortcut icon for " + shortcutKey.toString());
@@ -209,7 +214,7 @@
InstantAppItemInfo info = new InstantAppItemInfo(intent, pkgName);
IconCache iconCache = LauncherAppState.getInstance(mContext).getIconCache();
iconCache.getTitleAndIcon(info, false);
- if (info.iconBitmap == null || iconCache.isDefaultIcon(info.iconBitmap, info.user)) {
+ if (info.bitmap.icon == null || iconCache.isDefaultIcon(info.bitmap, info.user)) {
return null;
}
return info;
@@ -240,4 +245,35 @@
public WorkspaceItemInfo getShortcutInfo(ShortcutKey key) {
return mShortcuts.get(key);
}
+
+ /**
+ * requests and caches icons for app targets
+ */
+ public void updateDependencies(List<ComponentKeyMapper> componentKeyMappers,
+ AllAppsStore appsStore, IconCache.ItemInfoUpdateReceiver callback, int itemCount) {
+ List<String> instantAppsToLoad = new ArrayList<>();
+ List<ShortcutKey> shortcutsToLoad = new ArrayList<>();
+ int total = componentKeyMappers.size();
+ for (int i = 0, count = 0; i < total && count < itemCount; i++) {
+ ComponentKeyMapper mapper = componentKeyMappers.get(i);
+ // Update instant apps
+ if (COMPONENT_CLASS_MARKER.equals(mapper.getComponentClass())) {
+ instantAppsToLoad.add(mapper.getPackage());
+ count++;
+ } else if (mapper.getComponentKey() instanceof ShortcutKey) {
+ shortcutsToLoad.add((ShortcutKey) mapper.getComponentKey());
+ count++;
+ } else {
+ // Reload high res icon
+ AppInfo info = (AppInfo) mapper.getApp(appsStore);
+ if (info != null) {
+ if (info.usingLowResIcon()) {
+ mIconCache.updateIconInBackground(callback, info);
+ }
+ count++;
+ }
+ }
+ }
+ cacheItems(shortcutsToLoad, instantAppsToLoad);
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
index 9c66107..f9ee701 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,7 +18,6 @@
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
import android.app.prediction.AppPredictor;
import android.app.prediction.AppTarget;
@@ -27,7 +26,6 @@
import androidx.annotation.NonNull;
-import com.android.launcher3.AppInfo;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
import com.android.launcher3.ItemInfo;
@@ -40,7 +38,6 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener;
-import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -245,7 +242,7 @@
key = new ComponentKey(new ComponentName(appTarget.getPackageName(),
appTarget.getClassName()), appTarget.getUser());
}
- state.apps.add(new ComponentKeyMapper(mContext, key, mDynamicItemCache));
+ state.apps.add(new ComponentKeyMapper(key, mDynamicItemCache));
}
}
updateDependencies(state);
@@ -256,33 +253,8 @@
if (!state.isEnabled || mAppsView == null) {
return;
}
-
- IconCache iconCache = LauncherAppState.getInstance(mContext).getIconCache();
- List<String> instantAppsToLoad = new ArrayList<>();
- List<ShortcutKey> shortcutsToLoad = new ArrayList<>();
- int total = state.apps.size();
- for (int i = 0, count = 0; i < total && count < mMaxIconsPerRow; i++) {
- ComponentKeyMapper mapper = state.apps.get(i);
- // Update instant apps
- if (COMPONENT_CLASS_MARKER.equals(mapper.getComponentClass())) {
- instantAppsToLoad.add(mapper.getPackage());
- count++;
- } else if (mapper.getComponentKey() instanceof ShortcutKey) {
- shortcutsToLoad.add((ShortcutKey) mapper.getComponentKey());
- count++;
- } else {
- // Reload high res icon
- AppInfo info = (AppInfo) mapper.getApp(mAppsView.getAppsStore());
- if (info != null) {
- if (info.usingLowResIcon()) {
- // TODO: Update icon cache to support null callbacks.
- iconCache.updateIconInBackground(this, info);
- }
- count++;
- }
- }
- }
- mDynamicItemCache.cacheItems(shortcutsToLoad, instantAppsToLoad);
+ mDynamicItemCache.updateDependencies(state.apps, mAppsView.getAppsStore(), this,
+ mMaxIconsPerRow);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
similarity index 65%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
rename to quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index cac170c..6aaae4c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherState.NORMAL;
@@ -21,16 +20,20 @@
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.Bundle;
import android.view.Gravity;
+import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.HotseatPredictionController;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.RotationMode;
+import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
@@ -46,21 +49,20 @@
import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
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;
+import java.util.stream.Stream;
-/**
- * Provides recents-related {@link UiFactory} logic and classes.
- */
-public abstract class RecentsUiFactory {
+public class QuickstepLauncher extends BaseQuickstepLauncher {
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
public void mapRect(int left, int top, int right, int bottom, Rect out) {
@@ -87,7 +89,6 @@
}
}
};
-
public static RotationMode ROTATION_SEASCAPE = new RotationMode(90) {
@Override
public void mapRect(int left, int top, int right, int bottom, Rect out) {
@@ -133,81 +134,114 @@
| horizontalGravity | verticalGravity;
}
};
+ private HotseatPredictionController mHotseatPredictionController;
- public static RotationMode getRotationMode(DeviceProfile dp) {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) {
+ mHotseatPredictionController = new HotseatPredictionController(this);
+ }
+ }
+
+ @Override
+ protected RotationMode getFakeRotationMode(DeviceProfile dp) {
return !dp.isVerticalBarLayout() ? RotationMode.NORMAL
: (dp.isSeascape() ? ROTATION_SEASCAPE : ROTATION_LANDSCAPE);
}
- public static TouchController[] createTouchControllers(Launcher launcher) {
- Mode mode = SysUINavigationMode.getMode(launcher);
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ onStateOrResumeChanged();
+ }
- ArrayList<TouchController> list = new ArrayList<>();
- list.add(launcher.getDragController());
- if (mode == NO_BUTTON) {
- list.add(new NoButtonQuickSwitchTouchController(launcher));
- list.add(new NavBarToHomeTouchController(launcher));
- list.add(new FlingAndHoldTouchController(launcher));
+ @Override
+ protected void onActivityFlagsChanged(int changeBits) {
+ super.onActivityFlagsChanged(changeBits);
+
+ if ((changeBits & (ACTIVITY_STATE_DEFERRED_RESUMED | ACTIVITY_STATE_STARTED
+ | ACTIVITY_STATE_USER_ACTIVE | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0
+ && (getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0) {
+ onStateOrResumeChanged();
+ }
+ }
+
+ @Override
+ public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
+ if (mHotseatPredictionController != null) {
+ return Stream.concat(super.getSupportedShortcuts(),
+ Stream.of(mHotseatPredictionController));
} else {
- if (launcher.getDeviceProfile().isVerticalBarLayout()) {
- list.add(new OverviewToAllAppsTouchController(launcher));
- list.add(new LandscapeEdgeSwipeController(launcher));
- if (mode.hasGestures) {
- list.add(new TransposedQuickSwitchTouchController(launcher));
- }
- } else {
- list.add(new PortraitStatesTouchController(launcher,
- mode.hasGestures /* allowDragToOverview */));
- if (mode.hasGestures) {
- list.add(new QuickSwitchTouchController(launcher));
- }
- }
+ return super.getSupportedShortcuts();
}
-
- if (FeatureFlags.PULL_DOWN_STATUS_BAR
- && !launcher.getDeviceProfile().isMultiWindowMode) {
- list.add(new StatusBarTouchController(launcher));
- }
-
- list.add(new LauncherTaskViewController(launcher));
- return list.toArray(new TouchController[list.size()]);
- }
-
- /**
- * Creates and returns the controller responsible for recents view state transitions.
- *
- * @param launcher the launcher activity
- * @return state handler for recents
- */
- public static StateHandler createRecentsViewStateController(Launcher launcher) {
- return new RecentsViewStateController(launcher);
- }
-
- /**
- * Clears the swipe shared state for the current swipe gesture.
- */
- public static void clearSwipeSharedState(boolean finishAnimation) {
- TouchInteractionService.getSwipeSharedState().clearAllState(finishAnimation);
}
/**
* Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
- *
- * @param launcher the launcher activity
*/
- public static void onLauncherStateOrResumeChanged(Launcher launcher) {
- LauncherState state = launcher.getStateManager().getState();
- DeviceProfile profile = launcher.getDeviceProfile();
- boolean visible = (state == NORMAL || state == OVERVIEW) && launcher.isUserActive()
+ private void onStateOrResumeChanged() {
+ LauncherState state = getStateManager().getState();
+ DeviceProfile profile = getDeviceProfile();
+ boolean visible = (state == NORMAL || state == OVERVIEW) && isUserActive()
&& !profile.isVerticalBarLayout();
- UiThreadHelper.runAsyncCommand(launcher, SET_SHELF_HEIGHT_CMD,
- visible ? 1 : 0, profile.hotseatBarSizePx);
-
+ UiThreadHelper.runAsyncCommand(this, SET_SHELF_HEIGHT, visible ? 1 : 0,
+ profile.hotseatBarSizePx);
if (state == NORMAL) {
- launcher.<RecentsView>getOverviewPanel().setSwipeDownShouldLaunchApp(false);
+ ((RecentsView) getOverviewPanel()).setSwipeDownShouldLaunchApp(false);
}
}
+ @Override
+ public void finishBindingItems(int pageBoundFirst) {
+ super.finishBindingItems(pageBoundFirst);
+ if (mHotseatPredictionController != null) {
+ mHotseatPredictionController.createPredictor();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mHotseatPredictionController != null) {
+ mHotseatPredictionController.destroy();
+ }
+ }
+
+ @Override
+ public TouchController[] createTouchControllers() {
+ Mode mode = SysUINavigationMode.getMode(this);
+
+ ArrayList<TouchController> list = new ArrayList<>();
+ list.add(getDragController());
+ if (mode == NO_BUTTON) {
+ list.add(new NoButtonQuickSwitchTouchController(this));
+ list.add(new NavBarToHomeTouchController(this));
+ list.add(new FlingAndHoldTouchController(this));
+ } else {
+ if (getDeviceProfile().isVerticalBarLayout()) {
+ list.add(new OverviewToAllAppsTouchController(this));
+ list.add(new LandscapeEdgeSwipeController(this));
+ if (mode.hasGestures) {
+ list.add(new TransposedQuickSwitchTouchController(this));
+ }
+ } else {
+ list.add(new PortraitStatesTouchController(this,
+ mode.hasGestures /* allowDragToOverview */));
+ if (mode.hasGestures) {
+ list.add(new QuickSwitchTouchController(this));
+ }
+ }
+ }
+
+ if (!getDeviceProfile().isMultiWindowMode) {
+ list.add(new StatusBarTouchController(this));
+ }
+
+ list.add(new LauncherTaskViewController(this));
+ return list.toArray(new TouchController[list.size()]);
+ }
+
private static final class LauncherTaskViewController extends
TaskViewTouchController<Launcher> {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 63ac528..bb66ae1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -69,7 +69,7 @@
return super.getOverviewScaleAndTranslation(launcher);
}
TaskView dummyTask;
- if (recentsView.getCurrentPage() >= 0) {
+ if (recentsView.getCurrentPage() >= recentsView.getTaskViewStartIndex()) {
if (recentsView.getCurrentPage() <= taskCount - 1) {
dummyTask = recentsView.getCurrentPageTaskView();
} else {
@@ -78,7 +78,7 @@
} else {
dummyTask = recentsView.getTaskViewAt(0);
}
- return recentsView.getTempClipAnimationHelper().updateForFullscreenOverview(dummyTask)
+ return recentsView.getTempAppWindowAnimationHelper().updateForFullscreenOverview(dummyTask)
.getScaleAndTranslation();
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
index 6c9f46f..7b4bb02 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -20,13 +20,14 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.quickstep.GestureState;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
/**
* State to indicate we are about to launch a recent task. Note that this state is only used when
- * quick switching from launcher; quick switching from an app uses WindowTransformSwipeHelper.
- * @see com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget#NEW_TASK
+ * quick switching from launcher; quick switching from an app uses LauncherSwipeHandler.
+ * @see GestureState.GestureEndTarget#NEW_TASK
*/
public class QuickSwitchState extends BackgroundAppState {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
index 3231f37..d388f49 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
@@ -47,7 +47,7 @@
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.VibratorWrapper;
-import com.android.quickstep.OverviewInteractionState;
+import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.views.RecentsView;
@@ -116,7 +116,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;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index ef50c7b..ad4a343 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.touch.AbstractStateChangeTouchController.SUCCESS_TRANSITION_PROGRESS;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -44,7 +45,7 @@
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.config.BaseFlags;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -108,7 +109,7 @@
if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
return true;
}
- if (BaseFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS.get()
+ if (FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS.get()
&& AssistantUtilities.isExcludedAssistantRunning()) {
return true;
}
@@ -138,8 +139,13 @@
if (!recentsView.isRtl()) {
pullbackDist = -pullbackDist;
}
- Animator pullback = ObjectAnimator.ofFloat(recentsView, TRANSLATION_X, pullbackDist);
+ ObjectAnimator pullback = ObjectAnimator.ofFloat(recentsView, TRANSLATION_X,
+ pullbackDist);
pullback.setInterpolator(PULLBACK_INTERPOLATOR);
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ pullback.addUpdateListener(
+ valueAnimator -> recentsView.redrawLiveTile(false /* mightNeedToRefill */));
+ }
anim.play(pullback);
} else if (mStartState == ALL_APPS) {
AnimatorSetBuilder builder = new AnimatorSetBuilder();
@@ -192,6 +198,11 @@
boolean success = interpolatedProgress >= SUCCESS_TRANSITION_PROGRESS
|| (velocity < 0 && fling);
if (success) {
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ RecentsView recentsView = mLauncher.getOverviewPanel();
+ recentsView.switchToScreenshot(null,
+ () -> recentsView.finishRecentsAnimation(true /* toRecents */, null));
+ }
mLauncher.getStateManager().goToState(mEndState, true,
() -> onSwipeInteractionCompleted(mEndState));
if (mStartState != mEndState) {
@@ -203,7 +214,7 @@
logStateChange(topOpenView.getLogContainerType(), logAction);
}
ActivityManagerWrapper.getInstance()
- .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+ .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
} else {
// Quickly return to the state we came from (we didn't move far).
ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 86aa430..9ad0638 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -53,11 +53,10 @@
import android.view.View;
import android.view.animation.Interpolator;
-import com.android.launcher3.Launcher;
+import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
-import com.android.launcher3.QuickstepAppTransitionManagerImpl;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsTransitionController;
@@ -71,7 +70,7 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.VibratorWrapper;
-import com.android.quickstep.OverviewInteractionState;
+import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.ShelfPeekAnim;
@@ -92,8 +91,9 @@
private static final Interpolator TRANSLATE_OUT_INTERPOLATOR = ACCEL_0_75;
private static final Interpolator SCALE_DOWN_INTERPOLATOR = DEACCEL;
- private final Launcher mLauncher;
+ private final BaseQuickstepLauncher mLauncher;
private final BothAxesSwipeDetector mSwipeDetector;
+ private final ShelfPeekAnim mShelfPeekAnim;
private final float mXRange;
private final float mYRange;
private final MotionPauseDetector mMotionPauseDetector;
@@ -102,7 +102,6 @@
private boolean mNoIntercept;
private LauncherState mStartState;
- private ShelfPeekAnim mShelfPeekAnim;
private boolean mIsHomeScreenVisible = true;
// As we drag, we control 3 animations: one to get non-overview components out of the way,
@@ -111,9 +110,10 @@
private AnimatorPlaybackController mXOverviewAnim;
private AnimatorPlaybackController mYOverviewAnim;
- public NoButtonQuickSwitchTouchController(Launcher launcher) {
+ public NoButtonQuickSwitchTouchController(BaseQuickstepLauncher launcher) {
mLauncher = launcher;
mSwipeDetector = new BothAxesSwipeDetector(mLauncher, this);
+ mShelfPeekAnim = mLauncher.getShelfPeekAnim();
mXRange = mLauncher.getDeviceProfile().widthPx / 2f;
mYRange = LayoutUtils.getShelfTrackingDistance(mLauncher, mLauncher.getDeviceProfile());
mMotionPauseDetector = new MotionPauseDetector(mLauncher);
@@ -154,7 +154,7 @@
if ((ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) == 0) {
return false;
}
- int stateFlags = OverviewInteractionState.INSTANCE.get(mLauncher).getSystemUiStateFlags();
+ int stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags();
if ((stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0) {
return false;
}
@@ -165,9 +165,6 @@
public void onDragStart(boolean start) {
mMotionPauseDetector.clear();
if (start) {
- mShelfPeekAnim = ((QuickstepAppTransitionManagerImpl) mLauncher
- .getAppTransitionManager()).getShelfPeekAnim();
-
mStartState = mLauncher.getStateManager().getState();
mMotionPauseDetector.setOnMotionPauseListener(this);
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 14216ff..912be98 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
@@ -46,9 +46,9 @@
import com.android.launcher3.touch.SingleAxisSwipeDetector;
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.ActivityManagerWrapper;
@@ -84,7 +84,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;
}
@@ -97,7 +97,7 @@
mStartContainerType = LauncherLogProto.ContainerType.NAVBAR;
mTaskToLaunch = mLauncher.<RecentsView>getOverviewPanel().getTaskViewAt(0);
ActivityManagerWrapper.getInstance()
- .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+ .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index ad90e16..59b117f 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,8 +69,8 @@
boolean onActivityReady(T activity, Boolean wasVisible) {
activity.<RecentsView>getOverviewPanel().showCurrentTask(mTargetTaskId);
AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
- ActivityControlHelper.AnimationFactory factory =
- mHelper.prepareRecentsUI(activity, wasVisible,
+ BaseActivityInterface.AnimationFactory factory =
+ mHelper.prepareRecentsUI(wasVisible,
false /* animate activity */, (controller) -> {
controller.dispatchOnStart();
ValueAnimator anim = controller.getAnimationPlayer()
@@ -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);
}
@@ -102,7 +102,7 @@
anim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
- mHelper.onSwipeUpToRecentsComplete(mActivity);
+ mHelper.onSwipeUpToRecentsComplete();
if (mRecentsView != null) {
mRecentsView.animateUpRunningTaskIconScale();
}
@@ -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 5cce53e..b14da5c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -15,18 +15,15 @@
*/
package com.android.quickstep;
-import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
-import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
import android.animation.Animator;
import android.annotation.TargetApi;
-import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.content.Intent;
import android.graphics.Point;
@@ -34,8 +31,6 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
@@ -52,22 +47,21 @@
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.views.FloatingIconView;
-import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
-import com.android.quickstep.ActivityControlHelper.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.BaseActivityInterface.HomeAnimationFactory;
+import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
+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.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;
/**
@@ -75,7 +69,7 @@
*/
@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();
@@ -92,16 +86,14 @@
protected float mDragLengthFactor = 1;
protected final Context mContext;
- protected final OverviewComponentObserver mOverviewComponentObserver;
- protected final ActivityControlHelper<T> mActivityControlHelper;
- protected final RecentsModel mRecentsModel;
- protected final int mRunningTaskId;
+ protected final RecentsAnimationDeviceState mDeviceState;
+ protected final GestureState mGestureState;
+ protected final BaseActivityInterface<T> mActivityInterface;
+ protected final InputConsumerController mInputConsumer;
- protected final ClipAnimationHelper mClipAnimationHelper;
+ protected final AppWindowAnimationHelper mAppWindowAnimationHelper;
protected final TransformParams mTransformParams = new TransformParams();
- protected final Mode mMode;
-
// Shift in the range of [0, 1].
// 0 => preview snapShot is completely visible, and hotseat is completely translated down
// 1 => preview snapShot is completely aligned with the recents view and hotseat is completely
@@ -109,7 +101,12 @@
protected final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
protected final ActivityInitListener mActivityInitListener;
- protected final RecentsAnimationWrapper mRecentsAnimationWrapper;
+
+ 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;
@@ -118,40 +115,28 @@
protected Runnable mGestureEndCallback;
- protected final Handler mMainThreadHandler = MAIN_EXECUTOR.getHandler();
protected MultiStateCallback mStateCallback;
protected boolean mCanceled;
protected int mFinishingRecentsAnimationForNewTaskId = -1;
- protected BaseSwipeUpHandler(Context context,
- OverviewComponentObserver overviewComponentObserver,
- RecentsModel recentsModel, InputConsumerController inputConsumer, int runningTaskId) {
+ protected BaseSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
+ GestureState gestureState, InputConsumerController inputConsumer) {
mContext = context;
- mOverviewComponentObserver = overviewComponentObserver;
- mActivityControlHelper = overviewComponentObserver.getActivityControlHelper();
- mRecentsModel = recentsModel;
+ mDeviceState = deviceState;
+ mGestureState = gestureState;
+ mActivityInterface = gestureState.getActivityInterface();
mActivityInitListener =
- mActivityControlHelper.createActivityInitListener(this::onActivityInit);
- mRunningTaskId = runningTaskId;
- mRecentsAnimationWrapper = new RecentsAnimationWrapper(inputConsumer,
- this::createNewInputProxyHandler);
- mMode = SysUINavigationMode.getMode(context);
+ mActivityInterface.createActivityInitListener(this::onActivityInit);
+ mInputConsumer = inputConsumer;
- mClipAnimationHelper = new ClipAnimationHelper(context);
+ mAppWindowAnimationHelper = new AppWindowAnimationHelper(context);
mPageSpacing = context.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
+
initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext)
.getDeviceProfile(mContext));
}
- protected void setStateOnUiThread(int stateFlag) {
- if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
- mStateCallback.setState(stateFlag);
- } else {
- postAsyncCallback(mMainThreadHandler, () -> mStateCallback.setState(stateFlag));
- }
- }
-
protected void performHapticFeedback() {
VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
}
@@ -191,8 +176,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) -> {
@@ -200,8 +185,10 @@
updateFinalShift();
}
});
- mRecentsView.setRecentsAnimationWrapper(mRecentsAnimationWrapper);
- mRecentsView.setClipAnimationHelper(mClipAnimationHelper);
+ mRecentsView.setAppWindowAnimationHelper(mAppWindowAnimationHelper);
+ runOnRecentsAnimationStart(() ->
+ mRecentsView.setRecentsAnimationTargets(mRecentsAnimationController,
+ mRecentsAnimationTargets));
}
protected void startNewTask(int successStateFlag, Consumer<Boolean> resultCallback) {
@@ -213,7 +200,7 @@
} else {
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) {
@@ -221,31 +208,54 @@
success -> {
resultCallback.accept(success);
if (!success) {
- mActivityControlHelper.onLaunchTaskFailed(mActivity);
+ mActivityInterface.onLaunchTaskFailed();
nextTask.notifyTaskLaunchFailed(TAG);
} else {
- mActivityControlHelper.onLaunchTaskSuccess(mActivity);
+ mActivityInterface.onLaunchTaskSuccess();
}
- }, mMainThreadHandler);
+ }, MAIN_EXECUTOR.getHandler());
}
- setStateOnUiThread(successStateFlag);
+ mStateCallback.setStateOnUiThread(successStateFlag);
}
mCanceled = false;
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(
+ mGestureState.getRunningTaskId());
- 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 {
@@ -253,16 +263,40 @@
dp = dp.copy(mContext);
overviewStackBounds = getStackBounds(dp);
}
- dp.updateInsets(targetSet.homeContentInsets);
+ 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;
+ if (mRecentsView != null) {
+ mRecentsView.setRecentsAnimationTargets(null, null);
+ }
+ }
+
+ @Override
+ public void onRecentsAnimationFinished(RecentsAnimationController controller) {
+ mRecentsAnimationController = null;
+ mRecentsAnimationTargets = null;
+ if (mRecentsView != null) {
+ mRecentsView.setRecentsAnimationTargets(null, null);
+ }
}
private Rect getStackBounds(DeviceProfile dp) {
@@ -280,17 +314,17 @@
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);
- if (mMode == Mode.NO_BUTTON) {
+ mAppWindowAnimationHelper.updateTargetRect(TEMP_RECT);
+ if (mDeviceState.isFullyGesturalNavMode()) {
// We can drag all the way to the top of the screen.
mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
}
@@ -301,7 +335,7 @@
*/
protected abstract boolean moveWindowWithRecentsScroll();
- protected abstract boolean onActivityInit(final T activity, Boolean alreadyOnHome);
+ protected abstract boolean onActivityInit(Boolean alreadyOnHome);
/**
* Called to create a input proxy for the running task
@@ -329,13 +363,13 @@
@UiThread
public abstract void onGestureEnded(float endVelocity, PointF velocity, PointF downPos);
- public abstract void onConsumerAboutToBeSwitched(SwipeSharedState sharedState);
+ public abstract void onConsumerAboutToBeSwitched();
public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) { }
public void initWhenReady() {
// Preload the plan
- mRecentsModel.getTasks(null);
+ RecentsModel.INSTANCE.get(mContext).getTasks(null);
mActivityInitListener.register();
}
@@ -347,10 +381,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) {
@@ -366,9 +403,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();
@@ -384,7 +423,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.
@@ -415,12 +454,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);
}
}
@@ -448,8 +486,8 @@
public interface Factory {
- BaseSwipeUpHandler newHandler(RunningTaskInfo runningTask,
- long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask);
+ BaseSwipeUpHandler newHandler(GestureState gestureState, long touchTimeMs,
+ boolean continuingLastGesture, boolean isLikelyToStartNewTask);
}
protected interface RunningWindowAnim {
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 82%
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..f889bc1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
@@ -30,31 +30,32 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
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;
-import java.util.function.BiPredicate;
import java.util.function.Consumer;
+import java.util.function.Predicate;
/**
- * {@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) {
+ public void onTransitionCancelled(boolean activityVisible) {
// TODO:
}
@@ -72,7 +73,11 @@
}
@Override
- public void onSwipeUpToRecentsComplete(RecentsActivity activity) {
+ public void onSwipeUpToRecentsComplete() {
+ RecentsActivity activity = getCreatedActivity();
+ if (activity == null) {
+ return;
+ }
RecentsView recentsView = activity.getOverviewPanel();
recentsView.getClearAllButton().setVisibilityAlpha(1);
recentsView.setDisallowScrollToClearAll(false);
@@ -87,7 +92,8 @@
@NonNull
@Override
- public HomeAnimationFactory prepareHomeUI(RecentsActivity activity) {
+ public HomeAnimationFactory prepareHomeUI() {
+ RecentsActivity activity = getCreatedActivity();
RecentsView recentsView = activity.getOverviewPanel();
return new HomeAnimationFactory() {
@@ -118,8 +124,9 @@
}
@Override
- public AnimationFactory prepareRecentsUI(RecentsActivity activity, boolean activityVisible,
+ public AnimationFactory prepareRecentsUI(boolean activityVisible,
boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
+ RecentsActivity activity = getCreatedActivity();
if (activityVisible) {
return (transitionLength) -> { };
}
@@ -137,17 +144,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);
@@ -176,14 +183,15 @@
@Override
public ActivityInitListener createActivityInitListener(
- BiPredicate<RecentsActivity, Boolean> onInitListener) {
- return new RecentsActivityTracker(onInitListener);
+ Predicate<Boolean> onInitListener) {
+ return new ActivityInitListener<>((activity, alreadyOnHome) ->
+ onInitListener.test(alreadyOnHome), RecentsActivity.ACTIVITY_TRACKER);
}
@Nullable
@Override
public RecentsActivity getCreatedActivity() {
- return RecentsActivityTracker.getCurrentActivity();
+ return BaseRecentsActivity.ACTIVITY_TRACKER.getCreatedActivity();
}
@Nullable
@@ -228,13 +236,21 @@
}
@Override
- public void onLaunchTaskFailed(RecentsActivity activity) {
+ public void onLaunchTaskFailed() {
// TODO: probably go back to overview instead.
+ RecentsActivity activity = getCreatedActivity();
+ if (activity == null) {
+ return;
+ }
activity.<RecentsView>getOverviewPanel().startHome();
}
@Override
- public void onLaunchTaskSuccess(RecentsActivity activity) {
+ public void onLaunchTaskSuccess() {
+ RecentsActivity activity = getCreatedActivity();
+ if (activity == null) {
+ return;
+ }
activity.onTaskLaunched();
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
similarity index 64%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
index e0ff8af..c939de8 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -13,51 +13,46 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.quickstep.inputconsumers;
+package com.android.quickstep;
+import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
+import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
+import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
+import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
+import static com.android.quickstep.LauncherSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.RecentsActivity.EXTRA_TASK_ID;
import static com.android.quickstep.RecentsActivity.EXTRA_THUMBNAIL;
-import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
-import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.HOME;
-import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.LAST_TASK;
-import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.NEW_TASK;
-import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.RECENTS;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import android.animation.Animator;
import android.animation.AnimatorSet;
-import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Bundle;
+import android.util.ArrayMap;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory;
-import com.android.quickstep.AnimatedFloat;
-import com.android.quickstep.BaseSwipeUpHandler;
-import com.android.quickstep.MultiStateCallback;
-import com.android.quickstep.OverviewComponentObserver;
-import com.android.quickstep.RecentsActivity;
-import com.android.quickstep.RecentsModel;
-import com.android.quickstep.SwipeSharedState;
+import com.android.launcher3.util.ObjectWrapper;
+import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
+import com.android.quickstep.GestureState.GestureEndTarget;
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.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.InputConsumerController;
-public class FallbackNoButtonInputConsumer extends
- BaseSwipeUpHandler<RecentsActivity, FallbackRecentsView> {
+/**
+ * Handles the navigation gestures when a 3rd party launcher is the default home activity.
+ */
+public class FallbackSwipeHandler extends BaseSwipeUpHandler<RecentsActivity, FallbackRecentsView> {
private static final String[] STATE_NAMES = DEBUG_STATES ? new String[5] : null;
@@ -80,92 +75,89 @@
private static final int STATE_APP_CONTROLLER_RECEIVED =
getFlagForIndex(4, "STATE_APP_CONTROLLER_RECEIVED");
- public enum GestureEndTarget {
- HOME(3, 100, 1),
- RECENTS(1, 300, 0),
- LAST_TASK(0, 150, 1),
- NEW_TASK(0, 150, 1);
-
+ public static class EndTargetAnimationParams {
private final float mEndProgress;
private final long mDurationMultiplier;
private final float mLauncherAlpha;
- GestureEndTarget(float endProgress, long durationMultiplier, float launcherAlpha) {
+ EndTargetAnimationParams(float endProgress, long durationMultiplier, float launcherAlpha) {
mEndProgress = endProgress;
mDurationMultiplier = durationMultiplier;
mLauncherAlpha = launcherAlpha;
}
}
+ private static ArrayMap<GestureEndTarget, EndTargetAnimationParams>
+ mEndTargetAnimationParams = new ArrayMap();
private final AnimatedFloat mLauncherAlpha = new AnimatedFloat(this::onLauncherAlphaChanged);
private boolean mIsMotionPaused = false;
- private GestureEndTarget mEndTarget;
private final boolean mInQuickSwitchMode;
private final boolean mContinuingLastGesture;
private final boolean mRunningOverHome;
private final boolean mSwipeUpOverHome;
- private final RunningTaskInfo mRunningTaskInfo;
-
private final PointF mEndVelocityPxPerMs = new PointF(0, 0.5f);
private RunningWindowAnim mFinishAnimation;
- public FallbackNoButtonInputConsumer(Context context,
- OverviewComponentObserver overviewComponentObserver,
- RunningTaskInfo runningTaskInfo, RecentsModel recentsModel,
- InputConsumerController inputConsumer,
+ public FallbackSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
+ GestureState gestureState, InputConsumerController inputConsumer,
boolean isLikelyToStartNewTask, boolean continuingLastGesture) {
- super(context, overviewComponentObserver, recentsModel, inputConsumer, runningTaskInfo.id);
+ super(context, deviceState, gestureState, inputConsumer);
mLauncherAlpha.value = 1;
- mRunningTaskInfo = runningTaskInfo;
mInQuickSwitchMode = isLikelyToStartNewTask || continuingLastGesture;
mContinuingLastGesture = continuingLastGesture;
- mRunningOverHome = ActivityManagerWrapper.isHomeTask(runningTaskInfo);
+ mRunningOverHome = ActivityManagerWrapper.isHomeTask(mGestureState.getRunningTask());
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);
}
+ // Going home has an extra long progress to ensure that it animates into the screen
+ mEndTargetAnimationParams.put(HOME, new EndTargetAnimationParams(3, 100, 1));
+ mEndTargetAnimationParams.put(RECENTS, new EndTargetAnimationParams(1, 300, 0));
+ mEndTargetAnimationParams.put(LAST_TASK, new EndTargetAnimationParams(0, 150, 1));
+ mEndTargetAnimationParams.put(NEW_TASK, new EndTargetAnimationParams(0, 150, 1));
+
initStateCallbacks();
}
private void initStateCallbacks() {
mStateCallback = new MultiStateCallback(STATE_NAMES);
- mStateCallback.addCallback(STATE_HANDLER_INVALIDATED,
+ mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED,
this::onHandlerInvalidated);
- mStateCallback.addCallback(STATE_RECENTS_PRESENT | STATE_HANDLER_INVALIDATED,
+ mStateCallback.runOnceAtState(STATE_RECENTS_PRESENT | STATE_HANDLER_INVALIDATED,
this::onHandlerInvalidatedWithRecents);
- mStateCallback.addCallback(STATE_GESTURE_CANCELLED | STATE_APP_CONTROLLER_RECEIVED,
+ mStateCallback.runOnceAtState(STATE_GESTURE_CANCELLED | STATE_APP_CONTROLLER_RECEIVED,
this::finishAnimationTargetSetAnimationComplete);
if (mInQuickSwitchMode) {
- mStateCallback.addCallback(STATE_GESTURE_COMPLETED | STATE_APP_CONTROLLER_RECEIVED
+ mStateCallback.runOnceAtState(STATE_GESTURE_COMPLETED | STATE_APP_CONTROLLER_RECEIVED
| STATE_RECENTS_PRESENT,
this::finishAnimationTargetSet);
} else {
- mStateCallback.addCallback(STATE_GESTURE_COMPLETED | STATE_APP_CONTROLLER_RECEIVED,
+ mStateCallback.runOnceAtState(STATE_GESTURE_COMPLETED | STATE_APP_CONTROLLER_RECEIVED,
this::finishAnimationTargetSet);
}
}
private void onLauncherAlphaChanged() {
- if (mRecentsAnimationWrapper.targetSet != null && mEndTarget == null) {
+ if (mRecentsAnimationTargets != null && mGestureState.getEndTarget() == null) {
applyTransformUnchecked();
}
}
@Override
- protected boolean onActivityInit(final RecentsActivity activity, Boolean alreadyOnHome) {
- mActivity = activity;
- mRecentsView = activity.getOverviewPanel();
+ protected boolean onActivityInit(Boolean alreadyOnHome) {
+ mActivity = mActivityInterface.getCreatedActivity();
+ mRecentsView = mActivity.getOverviewPanel();
linkRecentsViewScroll();
mRecentsView.setDisallowScrollToClearAll(true);
mRecentsView.getClearAllButton().setVisibilityAlpha(0);
@@ -174,12 +166,12 @@
if (!mContinuingLastGesture) {
if (mRunningOverHome) {
- mRecentsView.onGestureAnimationStart(mRunningTaskInfo);
+ mRecentsView.onGestureAnimationStart(mGestureState.getRunningTask());
} else {
- mRecentsView.onGestureAnimationStart(mRunningTaskId);
+ mRecentsView.onGestureAnimationStart(mGestureState.getRunningTaskId());
}
}
- setStateOnUiThread(STATE_RECENTS_PRESENT);
+ mStateCallback.setStateOnUiThread(STATE_RECENTS_PRESENT);
return true;
}
@@ -222,18 +214,20 @@
@Override
public Intent getLaunchIntent() {
if (mInQuickSwitchMode || mSwipeUpOverHome) {
- return mOverviewComponentObserver.getOverviewIntent();
+ return mGestureState.getOverviewIntent();
} else {
- return mOverviewComponentObserver.getHomeIntent();
+ return mGestureState.getHomeIntent();
}
}
@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();
}
}
@@ -241,8 +235,8 @@
@Override
public void onGestureCancelled() {
updateDisplacement(0);
- mEndTarget = LAST_TASK;
- setStateOnUiThread(STATE_GESTURE_CANCELLED);
+ mGestureState.setEndTarget(LAST_TASK);
+ mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED);
}
@Override
@@ -250,28 +244,29 @@
mEndVelocityPxPerMs.set(0, velocity.y / 1000);
if (mInQuickSwitchMode) {
// For now set it to non-null, it will be reset before starting the animation
- mEndTarget = LAST_TASK;
+ mGestureState.setEndTarget(LAST_TASK);
} else {
float flingThreshold = mContext.getResources()
.getDimension(R.dimen.quickstep_fling_threshold_velocity);
boolean isFling = Math.abs(endVelocity) > flingThreshold;
if (isFling) {
- mEndTarget = endVelocity < 0 ? HOME : LAST_TASK;
+ mGestureState.setEndTarget(endVelocity < 0 ? HOME : LAST_TASK);
} else if (mIsMotionPaused) {
- mEndTarget = RECENTS;
+ mGestureState.setEndTarget(RECENTS);
} else {
- mEndTarget = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW ? HOME : LAST_TASK;
+ mGestureState.setEndTarget(mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW
+ ? HOME
+ : LAST_TASK);
}
}
- setStateOnUiThread(STATE_GESTURE_COMPLETED);
+ mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
}
@Override
- public void onConsumerAboutToBeSwitched(SwipeSharedState sharedState) {
- if (mInQuickSwitchMode && mEndTarget != null) {
- sharedState.canGestureBeContinued = true;
- sharedState.goingToLauncher = false;
+ public void onConsumerAboutToBeSwitched() {
+ if (mInQuickSwitchMode && mGestureState.getEndTarget() != null) {
+ mGestureState.setEndTarget(HOME);
mCanceled = true;
mCurrentShift.cancelAnimation();
@@ -287,12 +282,12 @@
? newRunningTaskView.getTask().key.id
: -1;
mRecentsView.setCurrentTask(newRunningTaskId);
- sharedState.setRecentsAnimationFinishInterrupted(newRunningTaskId);
+ mGestureState.setFinishingRecentsAnimationTaskId(newRunningTaskId);
}
mRecentsView.setOnScrollChangeListener(null);
}
} else {
- setStateOnUiThread(STATE_HANDLER_INVALIDATED);
+ mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED);
}
}
@@ -313,29 +308,29 @@
}
private void finishAnimationTargetSetAnimationComplete() {
- switch (mEndTarget) {
+ switch (mGestureState.getEndTarget()) {
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());
+ mContext.startActivity(mGestureState.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 */,
+ final int runningTaskId = mGestureState.getRunningTaskId();
+ ThumbnailData thumbnail = mRecentsAnimationController.screenshotTask(runningTaskId);
+ mRecentsAnimationController.setDeferCancelUntilNextTransition(true /* defer */,
false /* screenshot */);
ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
@@ -343,12 +338,12 @@
Bundle extras = new Bundle();
extras.putBinder(EXTRA_THUMBNAIL, new ObjectWrapper<>(thumbnail));
- extras.putInt(EXTRA_TASK_ID, mRunningTaskId);
+ extras.putInt(EXTRA_TASK_ID, runningTaskId);
- Intent intent = new Intent(mOverviewComponentObserver.getOverviewIntent())
+ Intent intent = new Intent(mGestureState.getOverviewIntent())
.putExtras(extras);
mContext.startActivity(intent, options.toBundle());
- mRecentsAnimationWrapper.targetSet.controller.cleanupScreenshot();
+ mRecentsAnimationController.cleanupScreenshot();
break;
}
case NEW_TASK: {
@@ -357,25 +352,28 @@
}
}
- setStateOnUiThread(STATE_HANDLER_INVALIDATED);
+ mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED);
}
private void finishAnimationTargetSet() {
if (mInQuickSwitchMode) {
// Recalculate the end target, some views might have been initialized after
// gesture has ended.
- if (mRecentsView == null || !mRecentsAnimationWrapper.hasTargets()) {
- mEndTarget = LAST_TASK;
+ if (mRecentsView == null || !hasTargets()) {
+ mGestureState.setEndTarget(LAST_TASK);
} else {
final int runningTaskIndex = mRecentsView.getRunningTaskIndex();
final int taskToLaunch = mRecentsView.getNextPage();
- mEndTarget = (runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex)
- ? NEW_TASK : LAST_TASK;
+ mGestureState.setEndTarget(
+ (runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex)
+ ? NEW_TASK
+ : LAST_TASK);
}
}
- float endProgress = mEndTarget.mEndProgress;
- long duration = (long) (mEndTarget.mDurationMultiplier *
+ EndTargetAnimationParams params = mEndTargetAnimationParams.get(mGestureState.getEndTarget());
+ float endProgress = params.mEndProgress;
+ long duration = (long) (params.mDurationMultiplier *
Math.abs(endProgress - mCurrentShift.value));
if (mRecentsView != null) {
duration = Math.max(duration, mRecentsView.getScroller().getDuration());
@@ -390,7 +388,7 @@
}
};
- if (mEndTarget == HOME && !mRunningOverHome) {
+ if (mGestureState.getEndTarget() == HOME && !mRunningOverHome) {
RectFSpringAnim anim = createWindowAnimationToHome(mCurrentShift.value, duration);
anim.addAnimatorListener(endListener);
anim.start(mEndVelocityPxPerMs);
@@ -399,7 +397,7 @@
AnimatorSet anim = new AnimatorSet();
anim.play(mLauncherAlpha.animateToValue(
- mLauncherAlpha.value, mEndTarget.mLauncherAlpha));
+ mLauncherAlpha.value, params.mLauncherAlpha));
anim.play(mCurrentShift.animateToValue(mCurrentShift.value, endProgress));
anim.setDuration(duration);
@@ -414,22 +412,23 @@
}
@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();
- setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
+ mStateCallback.setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
}
@Override
- public void onRecentsAnimationCanceled() {
- mRecentsAnimationWrapper.setController(null);
- setStateOnUiThread(STATE_HANDLER_INVALIDATED);
+ public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+ super.onRecentsAnimationCanceled(thumbnailData);
+ mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED);
}
/**
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 73%
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 c0be9ec..28fc3da 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -28,7 +28,7 @@
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.WindowTransformSwipeHandler.RECENTS_ATTACH_DURATION;
+import static com.android.quickstep.LauncherSwipeHandler.RECENTS_ATTACH_DURATION;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -37,7 +37,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;
@@ -47,17 +46,18 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
+import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherInitListenerEx;
+import com.android.launcher3.LauncherInitListener;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.QuickstepAppTransitionManagerImpl;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.ShelfPeekAnim;
import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
@@ -65,15 +65,17 @@
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;
+import java.util.function.Predicate;
/**
- * {@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;
@@ -90,42 +92,65 @@
}
@Override
- public void onTransitionCancelled(Launcher activity, boolean activityVisible) {
- LauncherState startState = activity.getStateManager().getRestState();
- activity.getStateManager().goToState(startState, activityVisible);
+ public void onTransitionCancelled(boolean activityVisible) {
+ Launcher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return;
+ }
+ LauncherState startState = launcher.getStateManager().getRestState();
+ launcher.getStateManager().goToState(startState, activityVisible);
}
@Override
- public void onSwipeUpToRecentsComplete(Launcher activity) {
+ public void onSwipeUpToRecentsComplete() {
// Re apply state in case we did something funky during the transition.
- activity.getStateManager().reapplyState();
- DiscoveryBounce.showForOverviewIfNeeded(activity);
+ Launcher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return;
+ }
+ launcher.getStateManager().reapplyState();
+ DiscoveryBounce.showForOverviewIfNeeded(launcher);
}
@Override
- public void onSwipeUpToHomeComplete(Launcher activity) {
+ public void onSwipeUpToHomeComplete() {
+ Launcher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return;
+ }
// 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();
+ launcher.getStateManager().reapplyState();
+ setLauncherHideBackArrow(false);
+ }
+
+ private void setLauncherHideBackArrow(boolean hideBackArrow) {
+ Launcher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return;
+ }
+ launcher.getRootView().setForceHideBackArrow(hideBackArrow);
}
@Override
public void onAssistantVisibilityChanged(float visibility) {
Launcher launcher = getCreatedActivity();
- if (launcher != null) {
- launcher.onAssistantVisibilityChanged(visibility);
+ if (launcher == null) {
+ return;
}
+ launcher.onAssistantVisibilityChanged(visibility);
}
@NonNull
@Override
- public HomeAnimationFactory prepareHomeUI(Launcher activity) {
- final DeviceProfile dp = activity.getDeviceProfile();
- final RecentsView recentsView = activity.getOverviewPanel();
+ public HomeAnimationFactory prepareHomeUI() {
+ Launcher launcher = getCreatedActivity();
+ final DeviceProfile dp = launcher.getDeviceProfile();
+ final RecentsView recentsView = launcher.getOverviewPanel();
final TaskView runningTaskView = recentsView.getRunningTaskView();
final View workspaceView;
if (runningTaskView != null && runningTaskView.getTask().key.getComponent() != null) {
- workspaceView = activity.getWorkspace().getFirstMatchForAppClose(
+ workspaceView = launcher.getWorkspace().getFirstMatchForAppClose(
runningTaskView.getTask().key.getComponent().getPackageName(),
UserHandle.of(runningTaskView.getTask().key.userId));
} else {
@@ -134,10 +159,10 @@
final RectF iconLocation = new RectF();
boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow();
FloatingIconView floatingIconView = canUseWorkspaceView
- ? FloatingIconView.getFloatingIconView(activity, workspaceView,
+ ? FloatingIconView.getFloatingIconView(launcher, workspaceView,
true /* hideOriginal */, iconLocation, false /* isOpening */)
: null;
-
+ setLauncherHideBackArrow(true);
return new HomeAnimationFactory() {
@Nullable
@Override
@@ -160,48 +185,47 @@
public AnimatorPlaybackController createActivityAnimationToHome() {
// Return an empty APC here since we have an non-user controlled animation to home.
long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx);
- return activity.getStateManager().createAnimationToNewWorkspace(NORMAL, accuracy,
+ return launcher.getStateManager().createAnimationToNewWorkspace(NORMAL, accuracy,
0 /* animComponents */);
}
@Override
public void playAtomicAnimation(float velocity) {
- new StaggeredWorkspaceAnim(activity, velocity, true /* animateOverviewScrim */)
+ new StaggeredWorkspaceAnim(launcher, velocity, true /* animateOverviewScrim */)
.start();
}
};
}
@Override
- public AnimationFactory prepareRecentsUI(Launcher activity, boolean activityVisible,
+ public AnimationFactory prepareRecentsUI(boolean activityVisible,
boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
- final LauncherState startState = activity.getStateManager().getState();
+ BaseQuickstepLauncher launcher = getCreatedActivity();
+ final LauncherState startState = launcher.getStateManager().getState();
LauncherState resetState = startState;
if (startState.disableRestore) {
- resetState = activity.getStateManager().getRestState();
+ resetState = launcher.getStateManager().getRestState();
}
- activity.getStateManager().setRestState(resetState);
+ launcher.getStateManager().setRestState(resetState);
final LauncherState fromState = animateActivity ? BACKGROUND_APP : OVERVIEW;
- activity.getStateManager().goToState(fromState, false);
+ launcher.getStateManager().goToState(fromState, false);
// Since all apps is not visible, we can safely reset the scroll position.
// This ensures then the next swipe up to all-apps starts from scroll 0.
- activity.getAppsView().reset(false /* animate */);
+ launcher.getAppsView().reset(false /* animate */);
return new AnimationFactory() {
- private final ShelfPeekAnim mShelfAnim =
- ((QuickstepAppTransitionManagerImpl) activity.getAppTransitionManager())
- .getShelfPeekAnim();
+ private final ShelfPeekAnim mShelfAnim = launcher.getShelfPeekAnim();
private boolean mIsAttachedToWindow;
@Override
- public void createActivityController(long transitionLength) {
- createActivityControllerInternal(activity, fromState, transitionLength, callback);
+ public void createActivityInterface(long transitionLength) {
+ createActivityInterfaceInternal(launcher, fromState, transitionLength, callback);
// Creating the activity controller animation sometimes reapplies the launcher state
// (because we set the animation as the current state animation), so we reapply the
// attached state here as well to ensure recents is shown/hidden appropriately.
- if (SysUINavigationMode.getMode(activity) == Mode.NO_BUTTON) {
+ if (SysUINavigationMode.getMode(launcher) == Mode.NO_BUTTON) {
setRecentsAttachedToAppWindow(mIsAttachedToWindow, false);
}
}
@@ -215,7 +239,7 @@
@Override
public void onTransitionCancelled() {
- activity.getStateManager().goToState(startState, false /* animate */);
+ launcher.getStateManager().goToState(startState, false /* animate */);
}
@Override
@@ -230,8 +254,8 @@
return;
}
mIsAttachedToWindow = attached;
- LauncherRecentsView recentsView = activity.getOverviewPanel();
- Animator fadeAnim = activity.getStateManager()
+ LauncherRecentsView recentsView = launcher.getOverviewPanel();
+ Animator fadeAnim = launcher.getStateManager()
.createStateElementAnimation(
INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0);
@@ -245,7 +269,7 @@
float fromTranslationX = attached ? offscreenX - scrollOffsetX : 0;
float toTranslationX = attached ? 0 : offscreenX - scrollOffsetX;
- activity.getStateManager()
+ launcher.getStateManager()
.cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_X_ANIM);
if (!recentsView.isShown() && animate) {
@@ -257,7 +281,7 @@
if (!animate) {
recentsView.setTranslationX(toTranslationX);
} else {
- activity.getStateManager().createStateElementAnimation(
+ launcher.getStateManager().createStateElementAnimation(
INDEX_RECENTS_TRANSLATE_X_ANIM,
fromTranslationX, toTranslationX).start();
}
@@ -271,7 +295,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) {
@@ -355,19 +379,15 @@
}
@Override
- public ActivityInitListener createActivityInitListener(
- BiPredicate<Launcher, Boolean> onInitListener) {
- return new LauncherInitListenerEx(onInitListener);
+ public ActivityInitListener createActivityInitListener(Predicate<Boolean> onInitListener) {
+ return new LauncherInitListener((activity, alreadyOnHome) ->
+ onInitListener.test(alreadyOnHome));
}
@Nullable
@Override
- public Launcher getCreatedActivity() {
- LauncherAppState app = LauncherAppState.getInstanceNoCreate();
- if (app == null) {
- return null;
- }
- return (Launcher) app.getModel().getCallback();
+ public BaseQuickstepLauncher getCreatedActivity() {
+ return BaseQuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity();
}
@Nullable
@@ -403,8 +423,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
@@ -432,12 +452,70 @@
}
@Override
- public void onLaunchTaskFailed(Launcher launcher) {
+ public void onLaunchTaskFailed() {
+ Launcher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return;
+ }
launcher.getStateManager().goToState(OVERVIEW);
}
@Override
- public void onLaunchTaskSuccess(Launcher launcher) {
+ public void onLaunchTaskSuccess() {
+ Launcher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return;
+ }
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 switchRunningTaskViewToScreenshot(ThumbnailData thumbnailData,
+ Runnable onFinishRunnable) {
+ Launcher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return;
+ }
+ RecentsView recentsView = launcher.getOverviewPanel();
+ if (recentsView == null) {
+ if (onFinishRunnable != null) {
+ onFinishRunnable.run();
+ }
+ return;
+ }
+ recentsView.switchToScreenshot(thumbnailData, onFinishRunnable);
+ }
+
+ @Override
+ public void setOnDeferredActivityLaunchCallback(Runnable r) {
+ Launcher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return;
+ }
+ launcher.setOnDeferredActivityLaunchCallback(r);
+ }
+
+ @Override
+ public void updateOverviewPredictionState() {
+ Launcher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return;
+ }
+ PredictionUiStateManager.INSTANCE.get(launcher).switchClient(
+ PredictionUiStateManager.Client.OVERVIEW);
+ }
}
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
similarity index 71%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
index 2fa4feb..c28bf28 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -23,29 +23,25 @@
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.GestureState.GestureEndTarget.HOME;
+import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
+import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
+import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
+import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINISHED;
import static com.android.quickstep.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.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE;
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK;
import static com.android.quickstep.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;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
-import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.content.Intent;
-import android.graphics.Canvas;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Build;
@@ -71,18 +67,17 @@
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.HomeAnimationFactory;
-import com.android.quickstep.SysUINavigationMode.Mode;
-import com.android.quickstep.inputconsumers.InputConsumer;
+import com.android.quickstep.BaseActivityInterface.AnimationFactory;
+import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
+import com.android.quickstep.GestureState.GestureEndTarget;
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.SharedApiCompat;
import com.android.quickstep.util.ShelfPeekAnim;
import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
-import com.android.quickstep.util.SwipeAnimationTargetSet;
import com.android.quickstep.views.LiveTileOverlay;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -90,13 +85,14 @@
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;
+/**
+ * Handles the navigation gestures when Launcher is the default home activity.
+ */
@TargetApi(Build.VERSION_CODES.O)
-public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
- extends BaseSwipeUpHandler<T, RecentsView>
- implements OnApplyWindowInsetsListener {
- private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName();
+public class LauncherSwipeHandler<T extends BaseDraggingActivity>
+ extends BaseSwipeUpHandler<T, RecentsView> implements OnApplyWindowInsetsListener {
+ private static final String TAG = LauncherSwipeHandler.class.getSimpleName();
private static final String[] STATE_NAMES = DEBUG_STATES ? new String[16] : null;
@@ -148,42 +144,6 @@
private static final int LAUNCHER_UI_STATES =
STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED;
- public enum GestureEndTarget {
- HOME(1, STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT, true, false,
- ContainerType.WORKSPACE, false),
-
- RECENTS(1, STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
- | STATE_SCREENSHOT_VIEW_SHOWN, true, false, ContainerType.TASKSWITCHER, true),
-
- NEW_TASK(0, STATE_START_NEW_TASK | STATE_CAPTURE_SCREENSHOT, false, true,
- ContainerType.APP, true),
-
- LAST_TASK(0, STATE_RESUME_LAST_TASK, false, true, ContainerType.APP, false);
-
- GestureEndTarget(float endShift, int endState, boolean isLauncher, boolean canBeContinued,
- int containerType, boolean recentsAttachedToAppWindow) {
- this.endShift = endShift;
- this.endState = endState;
- this.isLauncher = isLauncher;
- this.canBeContinued = canBeContinued;
- this.containerType = containerType;
- this.recentsAttachedToAppWindow = recentsAttachedToAppWindow;
- }
-
- /** 0 is app, 1 is overview */
- public final float endShift;
- /** The state to apply when we reach this final target */
- public final int endState;
- /** Whether the target is in the launcher activity */
- public final boolean isLauncher;
- /** Whether the user can start a new gesture while this one is finishing */
- public final boolean canBeContinued;
- /** Used to log where the user ended up after the gesture ends */
- public final int containerType;
- /** Whether RecentsView should be attached to the window as we animate to this target */
- public final boolean recentsAttachedToAppWindow;
- }
-
public static final long MAX_SWIPE_DURATION = 350;
public static final long MIN_SWIPE_DURATION = 80;
public static final long MIN_OVERSHOOT_DURATION = 120;
@@ -200,7 +160,8 @@
*/
private static final int LOG_NO_OP_PAGE_INDEX = -1;
- private GestureEndTarget mGestureEndTarget;
+ private final TaskAnimationManager mTaskAnimationManager;
+
// Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise
private RunningWindowAnim mRunningWindowAnim;
private boolean mIsShelfPeeking;
@@ -214,8 +175,6 @@
private boolean mHasLauncherTransitionControllerStarted;
private AnimationFactory mAnimationFactory = (t) -> { };
- private LiveTileOverlay mLiveTileOverlay = new LiveTileOverlay();
- private boolean mLiveTileOverlayAttached = false;
private boolean mWasLauncherAlreadyVisible;
@@ -229,11 +188,14 @@
private final long mTouchTimeMs;
private long mLauncherFrameDrawnTime;
- public WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context,
- long touchTimeMs, OverviewComponentObserver overviewComponentObserver,
- boolean continuingLastGesture,
- InputConsumerController inputConsumer, RecentsModel recentsModel) {
- super(context, overviewComponentObserver, recentsModel, inputConsumer, runningTaskInfo.id);
+ private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch;
+
+ public LauncherSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
+ TaskAnimationManager taskAnimationManager, GestureState gestureState,
+ long touchTimeMs, boolean continuingLastGesture,
+ InputConsumerController inputConsumer) {
+ super(context, deviceState, gestureState, inputConsumer);
+ mTaskAnimationManager = taskAnimationManager;
mTouchTimeMs = touchTimeMs;
mContinuingLastGesture = continuingLastGesture;
initStateCallbacks();
@@ -242,65 +204,65 @@
private void initStateCallbacks() {
mStateCallback = new MultiStateCallback(STATE_NAMES);
- mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED,
+ mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED,
this::onLauncherPresentAndGestureStarted);
- mStateCallback.addCallback(STATE_LAUNCHER_DRAWN | STATE_GESTURE_STARTED,
+ mStateCallback.runOnceAtState(STATE_LAUNCHER_DRAWN | STATE_GESTURE_STARTED,
this::initializeLauncherAnimationController);
- mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN,
+ mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN,
this::launcherFrameDrawn);
- mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_STARTED
+ mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_STARTED
| STATE_GESTURE_CANCELLED,
this::resetStateForAnimationCancel);
- mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_APP_CONTROLLER_RECEIVED,
+ mStateCallback.runOnceAtState(STATE_LAUNCHER_STARTED | STATE_APP_CONTROLLER_RECEIVED,
this::sendRemoteAnimationsToAnimationFactory);
- mStateCallback.addCallback(STATE_RESUME_LAST_TASK | STATE_APP_CONTROLLER_RECEIVED,
+ mStateCallback.runOnceAtState(STATE_RESUME_LAST_TASK | STATE_APP_CONTROLLER_RECEIVED,
this::resumeLastTask);
- mStateCallback.addCallback(STATE_START_NEW_TASK | STATE_SCREENSHOT_CAPTURED,
+ mStateCallback.runOnceAtState(STATE_START_NEW_TASK | STATE_SCREENSHOT_CAPTURED,
this::startNewTask);
- mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
+ mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
| STATE_LAUNCHER_DRAWN | STATE_CAPTURE_SCREENSHOT,
this::switchToScreenshot);
- mStateCallback.addCallback(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED
+ mStateCallback.runOnceAtState(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED
| STATE_SCALED_CONTROLLER_RECENTS,
this::finishCurrentTransitionToRecents);
- mStateCallback.addCallback(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED
+ mStateCallback.runOnceAtState(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED
| STATE_SCALED_CONTROLLER_HOME,
this::finishCurrentTransitionToHome);
- mStateCallback.addCallback(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED,
+ mStateCallback.runOnceAtState(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED,
this::reset);
- mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
+ mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
| STATE_LAUNCHER_DRAWN | STATE_SCALED_CONTROLLER_RECENTS
| STATE_CURRENT_TASK_FINISHED | STATE_GESTURE_COMPLETED
| STATE_GESTURE_STARTED,
this::setupLauncherUiAfterSwipeUpToRecentsAnimation);
- mStateCallback.addCallback(STATE_HANDLER_INVALIDATED, this::invalidateHandler);
- mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
+ mGestureState.runOnceAtState(STATE_END_TARGET_ANIMATION_FINISHED, this::onEndTargetSet);
+
+ mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED, this::invalidateHandler);
+ mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
this::invalidateHandlerWithLauncher);
- mStateCallback.addCallback(STATE_HANDLER_INVALIDATED | STATE_RESUME_LAST_TASK,
+ mStateCallback.runOnceAtState(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
+ mStateCallback.addChangeListener(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT
| STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT,
(b) -> mRecentsView.setRunningTaskHidden(!b));
}
}
@Override
- protected boolean onActivityInit(final T activity, Boolean alreadyOnHome) {
+ protected boolean onActivityInit(Boolean alreadyOnHome) {
+ final T activity = mActivityInterface.getCreatedActivity();
if (mActivity == activity) {
return true;
}
@@ -326,21 +288,28 @@
mStateCallback.setState(STATE_LAUNCHER_PRESENT);
if (alreadyOnHome) {
- onLauncherStart(activity);
+ onLauncherStart();
} else {
- activity.setOnStartCallback(this::onLauncherStart);
+ activity.runOnceOnStart(this::onLauncherStart);
}
setupRecentsViewUi();
+
+ if (mDeviceState.getNavMode() == TWO_BUTTONS) {
+ // If the device is in two button mode, swiping up will show overview with predictions
+ // so we need to kick off switching to the overview predictions as soon as possible
+ mActivityInterface.updateOverviewPredictionState();
+ }
return true;
}
@Override
protected boolean moveWindowWithRecentsScroll() {
- return mGestureEndTarget != HOME;
+ return mGestureState.getEndTarget() != HOME;
}
- private void onLauncherStart(final T activity) {
+ private void onLauncherStart() {
+ final T activity = mActivityInterface.getCreatedActivity();
if (mActivity != activity) {
return;
}
@@ -350,9 +319,9 @@
// If we've already ended the gesture and are going home, don't prepare recents UI,
// as that will set the state as BACKGROUND_APP, overriding the animation to NORMAL.
- if (mGestureEndTarget != HOME) {
+ if (mGestureState.getEndTarget() != HOME) {
Runnable initAnimFactory = () -> {
- mAnimationFactory = mActivityControlHelper.prepareRecentsUI(mActivity,
+ mAnimationFactory = mActivityInterface.prepareRecentsUI(
mWasLauncherAlreadyVisible, true,
this::onAnimatorPlaybackControllerCreated);
maybeUpdateRecentsAttachedState(false /* animate */);
@@ -361,7 +330,7 @@
// Launcher is visible, but might be about to stop. Thus, if we prepare recents
// now, it might get overridden by moveToRestState() in onStop(). To avoid this,
// wait until the next gesture (and possibly launcher) starts.
- mStateCallback.addCallback(STATE_GESTURE_STARTED, initAnimFactory);
+ mStateCallback.runOnceAtState(STATE_GESTURE_STARTED, initAnimFactory);
} else {
initAnimFactory.run();
}
@@ -372,13 +341,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) {
@@ -399,15 +374,31 @@
// that time by a previous window transition.
setupRecentsViewUi();
+ // For the duration of the gesture, in cases where an activity is launched while the
+ // activity is not yet resumed, finish the animation to ensure we get resumed
+ mGestureState.getActivityInterface().setOnDeferredActivityLaunchCallback(
+ mOnDeferredActivityLaunch);
+
notifyGestureStartedAsync();
}
+ private void onDeferredActivityLaunch() {
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ mActivityInterface.switchRunningTaskViewToScreenshot(
+ null, () -> {
+ mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
+ });
+ } else {
+ mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
+ }
+ }
+
private void setupRecentsViewUi() {
if (mContinuingLastGesture) {
updateSysUiFlags(mCurrentShift.value);
return;
}
- mRecentsView.onGestureAnimationStart(mRunningTaskId);
+ mRecentsView.onGestureAnimationStart(mGestureState.getRunningTaskId());
}
private void launcherFrameDrawn() {
@@ -415,15 +406,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
@@ -431,16 +427,15 @@
.getHighResLoadingState().setVisible(true);
}
- private float getTaskCurveScaleForOffsetX(float offsetX, float taskWidth) {
- float distanceToReachEdge = mDp.widthPx / 2 + taskWidth / 2 +
- mContext.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
- float interpolation = Math.min(1, offsetX / distanceToReachEdge);
- return TaskView.getCurveScaleForInterpolation(interpolation);
- }
-
@Override
public void onMotionPauseChanged(boolean isPaused) {
setShelfState(isPaused ? PEEK : HIDE, ShelfPeekAnim.INTERPOLATOR, ShelfPeekAnim.DURATION);
+
+ if (mDeviceState.isFullyGesturalNavMode() && isPaused) {
+ // In fully gestural nav mode, switch to overview predictions once the user has paused
+ // (this is a no-op if the predictions are already in that state)
+ mActivityInterface.updateOverviewPredictionState();
+ }
}
public void maybeUpdateRecentsAttachedState() {
@@ -455,16 +450,15 @@
* Note this method has no effect unless the navigation mode is NO_BUTTON.
*/
private void maybeUpdateRecentsAttachedState(boolean animate) {
- if (mMode != Mode.NO_BUTTON || mRecentsView == null) {
+ if (!mDeviceState.isFullyGesturalNavMode() || mRecentsView == null) {
return;
}
- RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationWrapper.targetSet == null
- ? null
- : mRecentsAnimationWrapper.targetSet.findTask(mRunningTaskId);
+ RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null
+ ? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
+ : null;
final boolean recentsAttachedToAppWindow;
- int runningTaskIndex = mRecentsView.getRunningTaskIndex();
- if (mGestureEndTarget != null) {
- recentsAttachedToAppWindow = mGestureEndTarget.recentsAttachedToAppWindow;
+ if (mGestureState.getEndTarget() != null) {
+ recentsAttachedToAppWindow = mGestureState.getEndTarget().recentsAttachedToAppWindow;
} else if (mContinuingLastGesture
&& mRecentsView.getRunningTaskIndex() != mRecentsView.getNextPage()) {
recentsAttachedToAppWindow = true;
@@ -511,13 +505,14 @@
}
private void buildAnimationController() {
- if (mGestureEndTarget == HOME || mHasLauncherTransitionControllerStarted) {
- // We don't want a new mLauncherTransitionController if mGestureEndTarget == HOME (it
- // has its own animation) or if we're already animating the current controller.
+ if (mGestureState.getEndTarget() == HOME || mHasLauncherTransitionControllerStarted) {
+ // We don't want a new mLauncherTransitionController if
+ // mGestureState.getEndTarget() == HOME (it has its own animation) or if we're already
+ // animating the current controller.
return;
}
initTransitionEndpoints(mActivity.getDeviceProfile());
- mAnimationFactory.createActivityController(mTransitionDragLength);
+ mAnimationFactory.createActivityInterface(mTransitionDragLength);
}
@Override
@@ -537,29 +532,28 @@
@Override
public Intent getLaunchIntent() {
- return mOverviewComponentObserver.getOverviewIntent();
+ return mGestureState.getOverviewIntent();
}
@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) {
+ LiveTileOverlay.getInstance().update(
+ mAppWindowAnimationHelper.getCurrentRectWithInsets(),
+ mAppWindowAnimationHelper.getCurrentCornerRadius());
}
}
final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
if (passed != mPassedOverviewThreshold) {
mPassedOverviewThreshold = passed;
- if (mMode != Mode.NO_BUTTON) {
+ if (!mDeviceState.isFullyGesturalNavMode()) {
performHapticFeedback();
}
}
@@ -572,7 +566,7 @@
}
private void updateLauncherTransitionProgress() {
- if (mGestureEndTarget == HOME) {
+ if (mGestureState.getEndTarget() == HOME) {
return;
}
// Normalize the progress to 0 to 1, as the animation controller will clamp it to that
@@ -591,34 +585,41 @@
: 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);
- setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
+ 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.runOnceAtState(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED,
+ mRecentsAnimationController::enableInputConsumer);
+ mStateCallback.setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
mPassedOverviewThreshold = false;
}
@Override
- public void onRecentsAnimationCanceled() {
- mRecentsAnimationWrapper.setController(null);
+ public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+ super.onRecentsAnimationCanceled(thumbnailData);
mActivityInitListener.unregister();
- setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
- TOUCH_INTERACTION_LOG.addLog("cancelRecentsAnimation");
+ mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
+ ActiveGestureLog.INSTANCE.addLog("cancelRecentsAnimation");
}
@Override
public void onGestureStarted() {
notifyGestureStartedAsync();
- setStateOnUiThread(STATE_GESTURE_STARTED);
+ mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);
mGestureStarted = true;
}
@@ -641,7 +642,7 @@
@Override
public void onGestureCancelled() {
updateDisplacement(0);
- setStateOnUiThread(STATE_GESTURE_COMPLETED);
+ mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
mLogAction = Touch.SWIPE_NOOP;
handleNormalGestureEnd(0, false, new PointF(), true /* isCancel */);
}
@@ -656,7 +657,7 @@
float flingThreshold = mContext.getResources()
.getDimension(R.dimen.quickstep_fling_threshold_velocity);
boolean isFling = mGestureStarted && Math.abs(endVelocity) > flingThreshold;
- setStateOnUiThread(STATE_GESTURE_COMPLETED);
+ mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
mLogAction = isFling ? Touch.FLING : Touch.SWIPE;
boolean isVelocityVertical = Math.abs(velocity.y) > Math.abs(velocity.x);
@@ -671,16 +672,16 @@
@Override
protected InputConsumer createNewInputProxyHandler() {
- endRunningWindowAnim(mGestureEndTarget == HOME /* cancel */);
+ endRunningWindowAnim(mGestureState.getEndTarget() == HOME /* cancel */);
endLauncherTransitionController();
if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
// Hide the task view, if not already hidden
- setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha);
+ setTargetAlphaProvider(LauncherSwipeHandler::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) {
@@ -693,12 +694,30 @@
}
}
+ private void onEndTargetSet() {
+ switch (mGestureState.getEndTarget()) {
+ case HOME:
+ mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
+ break;
+ case RECENTS:
+ mStateCallback.setState(STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
+ | STATE_SCREENSHOT_VIEW_SHOWN);
+ break;
+ case NEW_TASK:
+ mStateCallback.setState(STATE_START_NEW_TASK | STATE_CAPTURE_SCREENSHOT);
+ break;
+ case LAST_TASK:
+ mStateCallback.setState(STATE_RESUME_LAST_TASK);
+ break;
+ }
+ }
+
private GestureEndTarget calculateEndTarget(PointF velocity, float endVelocity, boolean isFling,
boolean isCancel) {
final GestureEndTarget endTarget;
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;
@@ -714,7 +733,7 @@
if (!isFling) {
if (isCancel) {
endTarget = LAST_TASK;
- } else if (mMode == Mode.NO_BUTTON) {
+ } else if (mDeviceState.isFullyGesturalNavMode()) {
if (mIsShelfPeeking) {
endTarget = RECENTS;
} else if (goingToNewTask) {
@@ -735,9 +754,9 @@
boolean willGoToNewTaskOnSwipeUp =
goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity);
- if (mMode == Mode.NO_BUTTON && isSwipeUp && !willGoToNewTaskOnSwipeUp) {
+ if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !willGoToNewTaskOnSwipeUp) {
endTarget = HOME;
- } else if (mMode == Mode.NO_BUTTON && isSwipeUp && !mIsShelfPeeking) {
+ } else if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !mIsShelfPeeking) {
// If swiping at a diagonal, base end target on the faster velocity.
endTarget = NEW_TASK;
} else if (isSwipeUp) {
@@ -748,9 +767,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;
@@ -764,7 +781,7 @@
float currentShift = mCurrentShift.value;
final GestureEndTarget endTarget = calculateEndTarget(velocity, endVelocity,
isFling, isCancel);
- float endShift = endTarget.endShift;
+ float endShift = endTarget.isLauncher ? 1 : 0;
final float startShift;
Interpolator interpolator = DEACCEL;
if (!isFling) {
@@ -779,7 +796,7 @@
float minFlingVelocity = mContext.getResources()
.getDimension(R.dimen.quickstep_fling_min_velocity);
if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) {
- if (endTarget == RECENTS && mMode != Mode.NO_BUTTON) {
+ if (endTarget == RECENTS && !mDeviceState.isFullyGesturalNavMode()) {
Interpolators.OvershootParams overshoot = new Interpolators.OvershootParams(
startShift, endShift, endShift, endVelocity / 1000,
mTransitionDragLength, mContext);
@@ -803,15 +820,16 @@
}
}
- if (endTarget.isLauncher) {
- mRecentsAnimationWrapper.enableInputProxy();
+ if (endTarget.isLauncher && mRecentsAnimationController != null) {
+ mRecentsAnimationController.enableInputProxy(mInputConsumer,
+ this::createNewInputProxyHandler);
}
if (endTarget == HOME) {
setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
} else if (endTarget == RECENTS) {
- mLiveTileOverlay.startIconAnimation();
+ LiveTileOverlay.getInstance().startIconAnimation();
if (mRecentsView != null) {
int nearestPage = mRecentsView.getPageNearestToCenterOfScreen();
if (mRecentsView.getNextPage() != nearestPage) {
@@ -824,7 +842,7 @@
}
duration = Math.max(duration, mRecentsView.getScroller().getDuration());
}
- if (mMode == Mode.NO_BUTTON) {
+ if (mDeviceState.isFullyGesturalNavMode()) {
setShelfState(ShelfAnimState.OVERVIEW, interpolator, duration);
}
} else if (endTarget == NEW_TASK || endTarget == LAST_TASK) {
@@ -859,27 +877,28 @@
@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));
}
@UiThread
private void animateToProgressInternal(float start, float end, long duration,
Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) {
- mGestureEndTarget = target;
+ // Set the state, but don't notify until the animation completes
+ mGestureState.setEndTarget(target, false /* isAtomic */);
maybeUpdateRecentsAttachedState();
- if (mGestureEndTarget == HOME) {
+ if (mGestureState.getEndTarget() == HOME) {
HomeAnimationFactory homeAnimFactory;
if (mActivity != null) {
- homeAnimFactory = mActivityControlHelper.prepareHomeUI(mActivity);
+ homeAnimFactory = mActivityInterface.prepareHomeUI();
} 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;
}
@@ -890,14 +909,15 @@
return AnimatorPlaybackController.wrap(new AnimatorSet(), duration);
}
};
- mStateCallback.addChangeHandler(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
+ mStateCallback.addChangeListener(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
isPresent -> mRecentsView.startHome());
}
RectFSpringAnim windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
windowAnim.addAnimatorListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
- setStateOnUiThread(target.endState);
+ // Finalize the state and notify of the change
+ mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
}
});
windowAnim.start(velocityPxPerMs);
@@ -922,10 +942,9 @@
// We are about to launch the current running task, so use LAST_TASK state
// instead of NEW_TASK. This could happen, for example, if our scroll is
// aborted after we determined the target to be NEW_TASK.
- setStateOnUiThread(LAST_TASK.endState);
- } else {
- setStateOnUiThread(target.endState);
+ mGestureState.setEndTarget(LAST_TASK);
}
+ mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
}
});
windowAnim.start();
@@ -933,7 +952,7 @@
}
// Always play the entire launcher animation when going home, since it is separate from
// the animation that has been controlled thus far.
- if (mGestureEndTarget == HOME) {
+ if (mGestureState.getEndTarget() == HOME) {
start = 0;
}
@@ -985,36 +1004,50 @@
}
// Make sure recents is in its final state
maybeUpdateRecentsAttachedState(false);
- mActivityControlHelper.onSwipeUpToHomeComplete(mActivity);
+ mActivityInterface.onSwipeUpToHomeComplete();
}
});
return anim;
}
@Override
- public void onConsumerAboutToBeSwitched(SwipeSharedState sharedState) {
- if (mGestureEndTarget != null) {
- sharedState.canGestureBeContinued = mGestureEndTarget.canBeContinued;
- sharedState.goingToLauncher = mGestureEndTarget.isLauncher;
+ public void onConsumerAboutToBeSwitched() {
+ if (mActivity != null) {
+ // In the off chance that the gesture ends before Launcher is started, we should clear
+ // the callback here so that it doesn't update with the wrong state
+ mActivity.clearRunOnceOnStartCallback();
+ resetLauncherListenersAndOverlays();
}
-
- if (sharedState.canGestureBeContinued) {
- cancelCurrentAnimation(sharedState);
+ if (mGestureState.getEndTarget() != null && !mGestureState.isRunningAnimationToLauncher()) {
+ cancelCurrentAnimation();
} else {
reset();
}
}
+ 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();
}
@UiThread
private void startNewTask() {
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ mRecentsAnimationController.finish(true /* toRecents */, this::startNewTaskInternal);
+ } else {
+ startNewTaskInternal();
+ }
+ }
+
+ @UiThread
+ private void startNewTaskInternal() {
startNewTask(STATE_HANDLER_INVALIDATED, success -> {
if (!success) {
// We couldn't launch the task, so take user to overview so they can
@@ -1027,14 +1060,14 @@
}
private void reset() {
- setStateOnUiThread(STATE_HANDLER_INVALIDATED);
+ mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED);
}
/**
* Cancels any running animation so that the active target can be overriden by a new swipe
* handle (in case of quick switch).
*/
- private void cancelCurrentAnimation(SwipeSharedState sharedState) {
+ private void cancelCurrentAnimation() {
mCanceled = true;
mCurrentShift.cancelAnimation();
if (mLauncherTransitionController != null && mLauncherTransitionController
@@ -1052,7 +1085,7 @@
? newRunningTaskView.getTask().key.id
: -1;
mRecentsView.setCurrentTask(newRunningTaskId);
- sharedState.setRecentsAnimationFinishInterrupted(newRunningTaskId);
+ mGestureState.setFinishingRecentsAnimationTaskId(newRunningTaskId);
}
}
@@ -1071,9 +1104,7 @@
endLauncherTransitionController();
mRecentsView.onGestureAnimationEnd();
-
- mActivity.getRootView().setOnApplyWindowInsetsListener(null);
- removeLiveTileOverlay();
+ resetLauncherListenersAndOverlays();
}
private void endLauncherTransitionController() {
@@ -1084,139 +1115,130 @@
}
}
+ private void resetLauncherListenersAndOverlays() {
+ // Reset the callback for deferred activity launches
+ if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ mActivityInterface.setOnDeferredActivityLaunchCallback(null);
+ }
+ mActivity.getRootView().setOnApplyWindowInsetsListener(null);
+ removeLiveTileOverlay();
+ }
+
private void notifyTransitionCancelled() {
mAnimationFactory.onTransitionCancelled();
}
private void resetStateForAnimationCancel() {
boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted;
- mActivityControlHelper.onTransitionCancelled(mActivity, wasVisible);
+ mActivityInterface.onTransitionCancelled(wasVisible);
// Leave the pending invisible flag, as it may be used by wallpaper open animation.
mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
}
private void switchToScreenshot() {
+ final int runningTaskId = mGestureState.getRunningTaskId();
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
- } else if (!mRecentsAnimationWrapper.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) {
+ SharedApiCompat.setWillFinishToHome(mRecentsAnimationController.getController(),
+ true /* willFinishToHome */);
// Update the screenshot of the task
if (mTaskSnapshot == null) {
- mTaskSnapshot = controller.screenshotTask(mRunningTaskId);
+ mTaskSnapshot = mRecentsAnimationController.screenshotTask(runningTaskId);
+ }
+ mRecentsView.updateThumbnail(runningTaskId, mTaskSnapshot, false /* refreshNow */);
+ }
+ mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+ } else if (!hasTargets()) {
+ // If there are no targets, then we don't need to capture anything
+ mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+ } else {
+ boolean finishTransitionPosted = false;
+ if (mRecentsAnimationController != null) {
+ // Update the screenshot of the task
+ if (mTaskSnapshot == null) {
+ mTaskSnapshot = mRecentsAnimationController.screenshotTask(runningTaskId);
}
final TaskView taskView;
- if (mGestureEndTarget == HOME) {
+ if (mGestureState.getEndTarget() == HOME) {
// Capture the screenshot before finishing the transition to home to ensure it's
// taken in the correct orientation, but no need to update the thumbnail.
taskView = null;
} else {
- taskView = mRecentsView.updateThumbnail(mRunningTaskId, mTaskSnapshot);
+ taskView = mRecentsView.updateThumbnail(runningTaskId, mTaskSnapshot);
}
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,
+ () -> mStateCallback.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);
- setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
- RaceConditionTracker.onEvent(SCREENSHOT_CAPTURED_EVT, EXIT);
+ Object traceToken = TraceHelper.INSTANCE.beginSection(SCREENSHOT_CAPTURED_EVT,
+ TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS);
+ mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+ TraceHelper.INSTANCE.endSection(traceToken);
}
}
}
private void finishCurrentTransitionToRecents() {
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
- } else if (!mRecentsAnimationWrapper.hasTargets()) {
- // If there are no targets, then there is nothing to finish
- setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
+ mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
+ } else if (!hasTargets() || mRecentsAnimationController == null) {
+ // If there are no targets or the animation not started, then there is nothing to finish
+ mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
} else {
- synchronized (mRecentsAnimationWrapper) {
- mRecentsAnimationWrapper.finish(true /* toRecents */,
- () -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
- }
+ mRecentsAnimationController.finish(true /* toRecents */,
+ () -> mStateCallback.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 */,
- () -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED),
+ if (!hasTargets() || mRecentsAnimationController == null) {
+ // If there are no targets or the animation not started, then there is nothing to finish
+ mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
+ } else {
+ mRecentsAnimationController.finish(true /* toRecents */,
+ () -> mStateCallback.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();
+ if (mRecentsAnimationController != null) {
+ mRecentsAnimationController.setDeferCancelUntilNextTransition(true /* defer */,
+ true /* screenshot */);
+ }
mRecentsView.onSwipeUpAnimationSuccess();
- RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG);
-
+ SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
doLogGesture(RECENTS);
reset();
}
private void setTargetAlphaProvider(TargetAlphaProvider provider) {
- mClipAnimationHelper.setTaskAlphaCallback(provider);
+ mAppWindowAnimationHelper.setTaskAlphaCallback(provider);
updateFinalShift();
}
- private synchronized void addLiveTileOverlay() {
- if (!mLiveTileOverlayAttached) {
- mActivity.getRootView().getOverlay().add(mLiveTileOverlay);
- mRecentsView.setLiveTileOverlay(mLiveTileOverlay);
- mLiveTileOverlayAttached = true;
+ private void addLiveTileOverlay() {
+ if (LiveTileOverlay.getInstance().attach(mActivity.getRootView().getOverlay())) {
+ mRecentsView.setLiveTileOverlayAttached(true);
}
}
- private synchronized void removeLiveTileOverlay() {
- if (mLiveTileOverlayAttached) {
- mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
- mRecentsView.setLiveTileOverlay(null);
- mLiveTileOverlayAttached = false;
- }
+ private void removeLiveTileOverlay() {
+ LiveTileOverlay.getInstance().detach(mActivity.getRootView().getOverlay());
+ mRecentsView.setLiveTileOverlayAttached(false);
}
public static float getHiddenTargetAlpha(RemoteAnimationTargetCompat app, float expectedAlpha) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/MultiStateCallback.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/MultiStateCallback.java
deleted file mode 100644
index 357c9fc..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/MultiStateCallback.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.launcher3.config.FeatureFlags;
-
-import java.util.StringJoiner;
-import java.util.function.Consumer;
-
-/**
- * Utility class to help manage multiple callbacks based on different states.
- */
-public class MultiStateCallback {
-
- private static final String TAG = "MultiStateCallback";
- public static final boolean DEBUG_STATES = false;
-
- private final SparseArray<Runnable> mCallbacks = new SparseArray<>();
- private final SparseArray<Consumer<Boolean>> mStateChangeHandlers = new SparseArray<>();
-
- private final String[] mStateNames;
-
- public MultiStateCallback(String[] stateNames) {
- mStateNames = DEBUG_STATES ? stateNames : null;
- }
-
- private int mState = 0;
-
- /**
- * Adds the provided state flags to the global state and executes any callbacks as a result.
- */
- public void setState(int stateFlag) {
- if (DEBUG_STATES) {
- Log.d(TAG, "[" + System.identityHashCode(this) + "] Adding "
- + convertToFlagNames(stateFlag) + " to " + convertToFlagNames(mState));
- }
-
- int oldState = mState;
- mState = mState | stateFlag;
-
- int count = mCallbacks.size();
- for (int i = 0; i < count; i++) {
- int state = mCallbacks.keyAt(i);
-
- if ((mState & state) == state) {
- Runnable callback = mCallbacks.valueAt(i);
- if (callback != null) {
- // Set the callback to null, so that it does not run again.
- mCallbacks.setValueAt(i, null);
- callback.run();
- }
- }
- }
- notifyStateChangeHandlers(oldState);
- }
-
- /**
- * Adds the provided state flags to the global state and executes any change handlers
- * as a result.
- */
- public void clearState(int stateFlag) {
- if (DEBUG_STATES) {
- Log.d(TAG, "[" + System.identityHashCode(this) + "] Removing "
- + convertToFlagNames(stateFlag) + " from " + convertToFlagNames(mState));
- }
-
- int oldState = mState;
- mState = mState & ~stateFlag;
- notifyStateChangeHandlers(oldState);
- }
-
- private void notifyStateChangeHandlers(int oldState) {
- int count = mStateChangeHandlers.size();
- for (int i = 0; i < count; i++) {
- int state = mStateChangeHandlers.keyAt(i);
- boolean wasOn = (state & oldState) == state;
- boolean isOn = (state & mState) == state;
-
- if (wasOn != isOn) {
- mStateChangeHandlers.valueAt(i).accept(isOn);
- }
- }
- }
-
- /**
- * Sets the callbacks to be run when the provided states are enabled.
- * The callback is only run once.
- */
- public void addCallback(int stateMask, Runnable callback) {
- if (FeatureFlags.IS_DOGFOOD_BUILD && mCallbacks.get(stateMask) != null) {
- throw new IllegalStateException("Multiple callbacks on same state");
- }
- mCallbacks.put(stateMask, callback);
- }
-
- /**
- * Sets the handler to be called when the provided states are enabled or disabled.
- */
- public void addChangeHandler(int stateMask, Consumer<Boolean> handler) {
- mStateChangeHandlers.put(stateMask, handler);
- }
-
- public int getState() {
- return mState;
- }
-
- public boolean hasStates(int stateMask) {
- return (mState & stateMask) == stateMask;
- }
-
- private String convertToFlagNames(int flags) {
- StringJoiner joiner = new StringJoiner(", ", "[", " (" + flags + ")]");
- for (int i = 0; i < mStateNames.length; i++) {
- if ((flags & (1 << i)) != 0) {
- joiner.add(mStateNames[i]);
- }
- }
- return joiner.toString();
- }
-
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
index a8d402e..c4733bd 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
@@ -25,14 +25,14 @@
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.appprediction.PredictionUiStateManager;
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;
@@ -46,37 +46,43 @@
public class OverviewCommandHelper {
private final Context mContext;
- private final ActivityManagerWrapper mAM;
+ private final RecentsAnimationDeviceState mDeviceState;
private final RecentsModel mRecentsModel;
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();
+ 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);
+ ActivityManagerWrapper.getInstance()
+ .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
MAIN_EXECUTOR.execute(new RecentsActivityCommand<>());
}
+ @BinderThread
public void onOverviewShown(boolean triggeredFromAltTab) {
MAIN_EXECUTOR.execute(new ShowRecentsCommand(triggeredFromAltTab));
}
+ @BinderThread
public void onOverviewHidden() {
MAIN_EXECUTOR.execute(new HideRecentsCommand());
}
+ @BinderThread
public void onTip(int actionType, int viewType) {
MAIN_EXECUTOR.execute(() ->
UserEventDispatcher.newInstance(mContext).logActionTip(actionType, viewType));
@@ -93,14 +99,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;
}
@@ -125,7 +131,7 @@
@Override
protected boolean handleCommand(long elapsedTime) {
- RecentsView recents = (RecentsView) mHelper.getVisibleRecentsView();
+ RecentsView recents = (RecentsView) mActivityInterface.getVisibleRecentsView();
if (recents == null) {
return false;
}
@@ -141,7 +147,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;
@@ -150,10 +156,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);
@@ -169,13 +175,13 @@
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, MAIN_EXECUTOR.getHandler(),
mAnimationProvider.getRecentsLaunchDuration());
@@ -185,7 +191,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();
@@ -198,18 +204,24 @@
return false;
}
- private boolean onActivityReady(T activity, Boolean wasVisible) {
+ private boolean onActivityReady(Boolean wasVisible) {
+ final T activity = mActivityInterface.getCreatedActivity();
if (!mUserEventLogged) {
activity.getUserEventDispatcher().logActionCommand(
LauncherLogProto.Action.Command.RECENTS_BUTTON,
- mHelper.getContainerType(),
+ mActivityInterface.getContainerType(),
LauncherLogProto.ContainerType.TASKSWITCHER);
mUserEventLogged = true;
}
+
+ // Switch prediction client to overview
+ PredictionUiStateManager.INSTANCE.get(activity).switchClient(
+ PredictionUiStateManager.Client.OVERVIEW);
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));
@@ -217,7 +229,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 3c78dd8..92c55da 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -4,18 +4,27 @@
import android.content.Context;
import android.os.Bundle;
+import android.util.Log;
+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.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
@@ -47,12 +56,10 @@
case TestProtocol.REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN: {
try {
final int leftMargin = MAIN_EXECUTOR.submit(() ->
- mLauncher.<RecentsView>getOverviewPanel().getLeftGestureMargin()).get();
+ 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;
}
@@ -60,21 +67,48 @@
case TestProtocol.REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN: {
try {
final int rightMargin = MAIN_EXECUTOR.submit(() ->
- mLauncher.<RecentsView>getOverviewPanel().getRightGestureMargin()).
- get();
+ 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() {
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 9bdc98b..e0e20ee 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;
@@ -91,9 +89,13 @@
int taskID = intent.getIntExtra(EXTRA_TASK_ID, 0);
IBinder thumbnail = intent.getExtras().getBinder(EXTRA_THUMBNAIL);
if (taskID != 0 && thumbnail instanceof ObjectWrapper) {
- ThumbnailData thumbnailData = ((ObjectWrapper<ThumbnailData>) thumbnail).get();
+ ObjectWrapper<ThumbnailData> obj = (ObjectWrapper<ThumbnailData>) thumbnail;
+ ThumbnailData thumbnailData = obj.get();
mFallbackRecentsView.showCurrentTask(taskID);
mFallbackRecentsView.updateThumbnail(taskID, thumbnailData);
+ // Clear the ref since any reference to the extras on the system side will still
+ // hold a reference to the wrapper
+ obj.clear();
}
}
intent.removeExtra(EXTRA_TASK_ID);
@@ -152,9 +154,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) {
@@ -174,12 +177,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
deleted file mode 100644
index 8783ee3..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
-import 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 java.io.PrintWriter;
-
-/**
- * Utility class used to store state information shared across multiple transitions.
- */
-public class SwipeSharedState implements SwipeAnimationListener {
-
- private OverviewComponentObserver mOverviewComponentObserver;
-
- private RecentsAnimationListenerSet mRecentsAnimationListener;
- private SwipeAnimationTargetSet mLastAnimationTarget;
-
- private boolean mLastAnimationCancelled = false;
- private boolean mLastAnimationRunning = false;
-
- public boolean canGestureBeContinued;
- public boolean goingToLauncher;
- public boolean recentsAnimationFinishInterrupted;
- public int nextRunningTaskId = -1;
- private int mLogId;
-
- public void setOverviewComponentObserver(OverviewComponentObserver observer) {
- mOverviewComponentObserver = observer;
- }
-
- @Override
- public final void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) {
- mLastAnimationTarget = targetSet;
-
- mLastAnimationCancelled = false;
- mLastAnimationRunning = true;
- }
-
- private void clearAnimationTarget() {
- if (mLastAnimationTarget != null) {
- mLastAnimationTarget.release();
- mLastAnimationTarget = null;
- }
- }
-
- @Override
- public final void onRecentsAnimationCanceled() {
- clearAnimationTarget();
-
- mLastAnimationCancelled = true;
- mLastAnimationRunning = false;
- }
-
- private void clearListenerState(boolean finishAnimation) {
- if (mRecentsAnimationListener != null) {
- mRecentsAnimationListener.removeListener(this);
- mRecentsAnimationListener.cancelListener();
- if (mLastAnimationRunning && mLastAnimationTarget != null) {
- Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(),
- finishAnimation
- ? mLastAnimationTarget::finishAnimation
- : mLastAnimationTarget::cancelAnimation);
- mLastAnimationTarget = null;
- }
- }
- mRecentsAnimationListener = null;
- clearAnimationTarget();
- mLastAnimationCancelled = false;
- mLastAnimationRunning = false;
- }
-
- private void onSwipeAnimationFinished(SwipeAnimationTargetSet targetSet) {
- if (mLastAnimationTarget == targetSet) {
- mLastAnimationRunning = false;
- }
- }
-
- public RecentsAnimationListenerSet newRecentsAnimationListenerSet() {
- Preconditions.assertUIThread();
-
- if (mLastAnimationRunning) {
- String msg = "New animation started before completing old animation";
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
- throw new IllegalArgumentException(msg);
- } else {
- Log.e("SwipeSharedState", msg, new Exception());
- }
- }
-
- clearListenerState(false /* finishAnimation */);
- boolean shouldMinimiseSplitScreen = mOverviewComponentObserver == null ? false
- : mOverviewComponentObserver.getActivityControlHelper().shouldMinimizeSplitScreen();
- mRecentsAnimationListener = new RecentsAnimationListenerSet(
- shouldMinimiseSplitScreen, this::onSwipeAnimationFinished);
- mRecentsAnimationListener.addListener(this);
- return mRecentsAnimationListener;
- }
-
- public RecentsAnimationListenerSet getActiveListener() {
- return mRecentsAnimationListener;
- }
-
- public void applyActiveRecentsAnimationState(SwipeAnimationListener listener) {
- if (mLastAnimationTarget != null) {
- listener.onRecentsAnimationStart(mLastAnimationTarget);
- } else if (mLastAnimationCancelled) {
- listener.onRecentsAnimationCanceled();
- }
- }
-
- /**
- * Called when a recents animation has finished, but was interrupted before the next task was
- * launched. The given {@param runningTaskId} should be used as the running task for the
- * continuing input consumer.
- */
- public void setRecentsAnimationFinishInterrupted(int runningTaskId) {
- recentsAnimationFinishInterrupted = true;
- nextRunningTaskId = runningTaskId;
- mLastAnimationTarget = mLastAnimationTarget.cloneWithoutTargets();
- }
-
- public void clearAllState(boolean finishAnimation) {
- clearListenerState(finishAnimation);
- canGestureBeContinued = false;
- recentsAnimationFinishInterrupted = false;
- nextRunningTaskId = -1;
- goingToLauncher = false;
- }
-
- public void dump(String prefix, PrintWriter pw) {
- pw.println(prefix + "goingToLauncher=" + goingToLauncher);
- pw.println(prefix + "canGestureBeContinued=" + canGestureBeContinued);
- pw.println(prefix + "recentsAnimationFinishInterrupted=" + recentsAnimationFinishInterrupted);
- pw.println(prefix + "nextRunningTaskId=" + nextRunningTaskId);
- pw.println(prefix + "lastAnimationCancelled=" + mLastAnimationCancelled);
- pw.println(prefix + "lastAnimationRunning=" + mLastAnimationRunning);
- pw.println(prefix + "logTraceId=" + mLogId);
- }
-
- public void setLogTraceId(int logId) {
- this.mLogId = logId;
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
index 17457aa..b5441df 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -19,11 +19,11 @@
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import android.graphics.Matrix;
-import android.view.View;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
+import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.quickstep.views.TaskThumbnailView;
@@ -40,30 +40,30 @@
public class TaskOverlayFactory implements ResourceBasedOverride {
/** Note that these will be shown in order from top to bottom, if available for the task. */
- private static final TaskSystemShortcut[] MENU_OPTIONS = new TaskSystemShortcut[]{
- new TaskSystemShortcut.AppInfo(),
- new TaskSystemShortcut.SplitScreen(),
- new TaskSystemShortcut.Pin(),
- new TaskSystemShortcut.Install(),
- new TaskSystemShortcut.Freeform()
+ private static final TaskShortcutFactory[] MENU_OPTIONS = new TaskShortcutFactory[]{
+ TaskShortcutFactory.APP_INFO,
+ TaskShortcutFactory.SPLIT_SCREEN,
+ TaskShortcutFactory.PIN,
+ TaskShortcutFactory.INSTALL,
+ TaskShortcutFactory.FREE_FORM,
+ TaskShortcutFactory.WELLBEING
};
- public static final MainThreadInitializedObject<TaskOverlayFactory> INSTANCE =
- forOverride(TaskOverlayFactory.class, R.string.task_overlay_factory_class);
-
- public List<TaskSystemShortcut> getEnabledShortcuts(TaskView taskView) {
- final ArrayList<TaskSystemShortcut> shortcuts = new ArrayList<>();
+ public static List<SystemShortcut> getEnabledShortcuts(TaskView taskView) {
+ final ArrayList<SystemShortcut> shortcuts = new ArrayList<>();
final BaseDraggingActivity activity = BaseActivity.fromContext(taskView.getContext());
- for (TaskSystemShortcut menuOption : MENU_OPTIONS) {
- View.OnClickListener onClickListener =
- menuOption.getOnClickListener(activity, taskView);
- if (onClickListener != null) {
- shortcuts.add(menuOption);
+ for (TaskShortcutFactory menuOption : MENU_OPTIONS) {
+ SystemShortcut shortcut = menuOption.getShortcut(activity, taskView);
+ if (shortcut != null) {
+ shortcuts.add(shortcut);
}
}
return shortcuts;
}
+ public static final MainThreadInitializedObject<TaskOverlayFactory> INSTANCE =
+ forOverride(TaskOverlayFactory.class, R.string.task_overlay_factory_class);
+
public TaskOverlay createOverlay(TaskThumbnailView thumbnailView) {
return new TaskOverlay();
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
new file mode 100644
index 0000000..9ba2e5a
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.view.View;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.model.WellbeingModel;
+import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.popup.SystemShortcut.AppInfo;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.InstantAppResolver;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskThumbnailView;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
+import com.android.systemui.shared.recents.view.RecentsTransition;
+import com.android.systemui.shared.system.ActivityCompat;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.ActivityOptionsCompat;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Represents a system shortcut that can be shown for a recent task.
+ */
+public interface TaskShortcutFactory {
+
+ SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView view);
+
+ static WorkspaceItemInfo dummyInfo(TaskView view) {
+ Task task = view.getTask();
+
+ WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo();
+ dummyInfo.intent = new Intent();
+ ComponentName component = task.getTopComponent();
+ dummyInfo.intent.setComponent(component);
+ dummyInfo.user = UserHandle.of(task.key.userId);
+ dummyInfo.title = TaskUtils.getTitle(view.getContext(), task);
+ return dummyInfo;
+ }
+
+ TaskShortcutFactory APP_INFO = (activity, view) -> new AppInfo(activity, dummyInfo(view));
+
+ abstract class MultiWindowFactory implements TaskShortcutFactory {
+
+ private final int mIconRes;
+ private final int mTextRes;
+
+ MultiWindowFactory(int iconRes, int textRes) {
+ mIconRes = iconRes;
+ mTextRes = textRes;
+ }
+
+ protected abstract boolean isAvailable(BaseDraggingActivity activity, int displayId);
+ protected abstract ActivityOptions makeLaunchOptions(Activity activity);
+ protected abstract boolean onActivityStarted(BaseDraggingActivity activity);
+
+ @Override
+ public SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView taskView) {
+ final Task task = taskView.getTask();
+ if (!task.isDockable) {
+ return null;
+ }
+ if (!isAvailable(activity, task.key.displayId)) {
+ return null;
+ }
+ return new MultiWindowSystemShortcut(mIconRes, mTextRes, activity, taskView, this);
+ }
+ }
+
+ class MultiWindowSystemShortcut extends SystemShortcut {
+
+ private Handler mHandler;
+
+ private final RecentsView mRecentsView;
+ private final TaskThumbnailView mThumbnailView;
+ private final TaskView mTaskView;
+ private final MultiWindowFactory mFactory;
+
+ public MultiWindowSystemShortcut(int iconRes, int textRes,
+ BaseDraggingActivity activity, TaskView taskView, MultiWindowFactory factory) {
+ super(iconRes, textRes, activity, dummyInfo(taskView));
+
+ mHandler = new Handler(Looper.getMainLooper());
+ mTaskView = taskView;
+ mRecentsView = activity.getOverviewPanel();
+ mThumbnailView = taskView.getThumbnail();
+ mFactory = factory;
+ }
+
+ @Override
+ public void onClick(View view) {
+ Task.TaskKey taskKey = mTaskView.getTask().key;
+ final int taskId = taskKey.id;
+
+ final View.OnLayoutChangeListener onLayoutChangeListener =
+ new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int l, int t, int r, int b,
+ int oldL, int oldT, int oldR, int oldB) {
+ mTaskView.getRootView().removeOnLayoutChangeListener(this);
+ mRecentsView.clearIgnoreResetTask(taskId);
+
+ // Start animating in the side pages once launcher has been resized
+ mRecentsView.dismissTask(mTaskView, false, false);
+ }
+ };
+
+ final DeviceProfile.OnDeviceProfileChangeListener onDeviceProfileChangeListener =
+ new DeviceProfile.OnDeviceProfileChangeListener() {
+ @Override
+ public void onDeviceProfileChanged(DeviceProfile dp) {
+ mTarget.removeOnDeviceProfileChangeListener(this);
+ if (dp.isMultiWindowMode) {
+ mTaskView.getRootView().addOnLayoutChangeListener(
+ onLayoutChangeListener);
+ }
+ }
+ };
+
+ dismissTaskMenuView(mTarget);
+
+ ActivityOptions options = mFactory.makeLaunchOptions(mTarget);
+ if (options != null
+ && ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
+ options)) {
+ if (!mFactory.onActivityStarted(mTarget)) {
+ return;
+ }
+ // Add a device profile change listener to kick off animating the side tasks
+ // once we enter multiwindow mode and relayout
+ mTarget.addOnDeviceProfileChangeListener(onDeviceProfileChangeListener);
+
+ final Runnable animStartedListener = () -> {
+ // Hide the task view and wait for the window to be resized
+ // TODO: Consider animating in launcher and do an in-place start activity
+ // afterwards
+ mRecentsView.setIgnoreResetTask(taskId);
+ mTaskView.setAlpha(0f);
+ };
+
+ final int[] position = new int[2];
+ mThumbnailView.getLocationOnScreen(position);
+ final int width = (int) (mThumbnailView.getWidth() * mTaskView.getScaleX());
+ final int height = (int) (mThumbnailView.getHeight() * mTaskView.getScaleY());
+ final Rect taskBounds = new Rect(position[0], position[1],
+ position[0] + width, position[1] + height);
+
+ // Take the thumbnail of the task without a scrim and apply it back after
+ float alpha = mThumbnailView.getDimAlpha();
+ mThumbnailView.setDimAlpha(0);
+ Bitmap thumbnail = RecentsTransition.drawViewIntoHardwareBitmap(
+ taskBounds.width(), taskBounds.height(), mThumbnailView, 1f,
+ Color.BLACK);
+ mThumbnailView.setDimAlpha(alpha);
+
+ AppTransitionAnimationSpecsFuture future =
+ new AppTransitionAnimationSpecsFuture(mHandler) {
+ @Override
+ public List<AppTransitionAnimationSpecCompat> composeSpecs() {
+ return Collections.singletonList(new AppTransitionAnimationSpecCompat(
+ taskId, thumbnail, taskBounds));
+ }
+ };
+ WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
+ future, animStartedListener, mHandler, true /* scaleUp */,
+ taskKey.displayId);
+ }
+ }
+ }
+
+ TaskShortcutFactory SPLIT_SCREEN = new MultiWindowFactory(
+ R.drawable.ic_split_screen, R.string.recent_task_option_split_screen) {
+
+ @Override
+ protected boolean isAvailable(BaseDraggingActivity activity, int displayId) {
+ // Don't show menu-item if already in multi-window and the task is from
+ // the secondary display.
+ // TODO(b/118266305): Temporarily disable splitscreen for secondary display while new
+ // implementation is enabled
+ return !activity.getDeviceProfile().isMultiWindowMode
+ && (displayId == -1 || displayId == DEFAULT_DISPLAY);
+ }
+
+ @Override
+ protected ActivityOptions makeLaunchOptions(Activity activity) {
+ final ActivityCompat act = new ActivityCompat(activity);
+ final int navBarPosition = WindowManagerWrapper.getInstance().getNavBarPosition(
+ act.getDisplayId());
+ if (navBarPosition == WindowManagerWrapper.NAV_BAR_POS_INVALID) {
+ return null;
+ }
+ boolean dockTopOrLeft = navBarPosition != WindowManagerWrapper.NAV_BAR_POS_LEFT;
+ return ActivityOptionsCompat.makeSplitScreenOptions(dockTopOrLeft);
+ }
+
+ @Override
+ protected boolean onActivityStarted(BaseDraggingActivity activity) {
+ SystemUiProxy.INSTANCE.get(activity).onSplitScreenInvoked();
+ activity.getUserEventDispatcher().logActionOnControl(TAP,
+ LauncherLogProto.ControlType.SPLIT_SCREEN_TARGET);
+ return true;
+ }
+ };
+
+ TaskShortcutFactory FREE_FORM = new MultiWindowFactory(
+ R.drawable.ic_split_screen, R.string.recent_task_option_freeform) {
+
+ @Override
+ protected boolean isAvailable(BaseDraggingActivity activity, int displayId) {
+ return ActivityManagerWrapper.getInstance().supportsFreeformMultiWindow(activity);
+ }
+
+ @Override
+ protected ActivityOptions makeLaunchOptions(Activity activity) {
+ ActivityOptions activityOptions = ActivityOptionsCompat.makeFreeformOptions();
+ // Arbitrary bounds only because freeform is in dev mode right now
+ Rect r = new Rect(50, 50, 200, 200);
+ activityOptions.setLaunchBounds(r);
+ return activityOptions;
+ }
+
+ @Override
+ protected boolean onActivityStarted(BaseDraggingActivity activity) {
+ activity.returnToHomescreen();
+ return true;
+ }
+ };
+
+ TaskShortcutFactory PIN = (activity, tv) -> {
+ if (!SystemUiProxy.INSTANCE.get(activity).isActive()) {
+ return null;
+ }
+ if (!ActivityManagerWrapper.getInstance().isScreenPinningEnabled()) {
+ return null;
+ }
+ if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
+ // We shouldn't be able to pin while an app is locked.
+ return null;
+ }
+ return new PinSystemShortcut(activity, tv);
+ };
+
+ class PinSystemShortcut extends SystemShortcut {
+
+ private static final String TAG = "PinSystemShortcut";
+
+ private final TaskView mTaskView;
+
+ public PinSystemShortcut(BaseDraggingActivity target, TaskView tv) {
+ super(R.drawable.ic_pin, R.string.recent_task_option_pin, target, dummyInfo(tv));
+ mTaskView = tv;
+ }
+
+ @Override
+ public void onClick(View view) {
+ Consumer<Boolean> resultCallback = success -> {
+ if (success) {
+ SystemUiProxy.INSTANCE.get(mTarget).startScreenPinning(
+ mTaskView.getTask().key.id);
+ } else {
+ mTaskView.notifyTaskLaunchFailed(TAG);
+ }
+ };
+ mTaskView.launchTask(true, resultCallback, Executors.MAIN_EXECUTOR.getHandler());
+ dismissTaskMenuView(mTarget);
+ }
+ }
+
+ TaskShortcutFactory INSTALL = (activity, view) ->
+ InstantAppResolver.newInstance(activity).isInstantApp(activity,
+ view.getTask().getTopComponent().getPackageName())
+ ? new SystemShortcut.Install(activity, dummyInfo(view)) : null;
+
+ TaskShortcutFactory WELLBEING = (activity, view) ->
+ WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, dummyInfo(view));
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
deleted file mode 100644
index 1af0db0..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
+++ /dev/null
@@ -1,340 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
-
-import android.app.Activity;
-import android.app.ActivityOptions;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.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.R;
-import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.popup.SystemShortcut;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.util.InstantAppResolver;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskThumbnailView;
-import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.recents.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;
-import com.android.systemui.shared.recents.view.RecentsTransition;
-import com.android.systemui.shared.system.ActivityCompat;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
-import com.android.systemui.shared.system.WindowManagerWrapper;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.function.Consumer;
-
-/**
- * Represents a system shortcut that can be shown for a recent task.
- */
-public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut {
-
- private static final String TAG = "TaskSystemShortcut";
-
- protected T mSystemShortcut;
-
- public TaskSystemShortcut(T systemShortcut) {
- super(systemShortcut);
- mSystemShortcut = systemShortcut;
- }
-
- protected TaskSystemShortcut(int iconResId, int labelResId) {
- super(iconResId, labelResId);
- }
-
- @Override
- public View.OnClickListener getOnClickListener(
- BaseDraggingActivity activity, ItemInfo itemInfo) {
- return null;
- }
-
- public View.OnClickListener getOnClickListener(BaseDraggingActivity activity, TaskView view) {
- Task task = view.getTask();
-
- WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo();
- dummyInfo.intent = new Intent();
- ComponentName component = task.getTopComponent();
- dummyInfo.intent.setComponent(component);
- dummyInfo.user = UserHandle.of(task.key.userId);
- dummyInfo.title = TaskUtils.getTitle(activity, task);
-
- return getOnClickListenerForTask(activity, task, dummyInfo);
- }
-
- protected View.OnClickListener getOnClickListenerForTask(
- BaseDraggingActivity activity, Task task, ItemInfo dummyInfo) {
- return mSystemShortcut.getOnClickListener(activity, dummyInfo);
- }
-
- public static class AppInfo extends TaskSystemShortcut<SystemShortcut.AppInfo> {
- public AppInfo() {
- super(new SystemShortcut.AppInfo());
- }
- }
-
- public static abstract class MultiWindow extends TaskSystemShortcut {
-
- private Handler mHandler;
-
- public MultiWindow(int iconRes, int textRes) {
- super(iconRes, textRes);
- mHandler = new Handler(Looper.getMainLooper());
- }
-
- protected abstract boolean isAvailable(BaseDraggingActivity activity, int displayId);
- protected abstract ActivityOptions makeLaunchOptions(Activity activity);
- protected abstract boolean onActivityStarted(BaseDraggingActivity activity);
-
- @Override
- public View.OnClickListener getOnClickListener(
- BaseDraggingActivity activity, TaskView taskView) {
- final Task task = taskView.getTask();
- final int taskId = task.key.id;
- final int displayId = task.key.displayId;
- if (!task.isDockable) {
- return null;
- }
- if (!isAvailable(activity, displayId)) {
- return null;
- }
- final RecentsView recentsView = activity.getOverviewPanel();
-
- final TaskThumbnailView thumbnailView = taskView.getThumbnail();
- return (v -> {
- final View.OnLayoutChangeListener onLayoutChangeListener =
- new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int l, int t, int r, int b,
- int oldL, int oldT, int oldR, int oldB) {
- taskView.getRootView().removeOnLayoutChangeListener(this);
- recentsView.clearIgnoreResetTask(taskId);
-
- // Start animating in the side pages once launcher has been resized
- recentsView.dismissTask(taskView, false, false);
- }
- };
-
- final DeviceProfile.OnDeviceProfileChangeListener onDeviceProfileChangeListener =
- new DeviceProfile.OnDeviceProfileChangeListener() {
- @Override
- public void onDeviceProfileChanged(DeviceProfile dp) {
- activity.removeOnDeviceProfileChangeListener(this);
- if (dp.isMultiWindowMode) {
- taskView.getRootView().addOnLayoutChangeListener(
- onLayoutChangeListener);
- }
- }
- };
-
- dismissTaskMenuView(activity);
-
- ActivityOptions options = makeLaunchOptions(activity);
- if (options != null
- && ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
- options)) {
- if (!onActivityStarted(activity)) {
- return;
- }
- // Add a device profile change listener to kick off animating the side tasks
- // once we enter multiwindow mode and relayout
- activity.addOnDeviceProfileChangeListener(onDeviceProfileChangeListener);
-
- final Runnable animStartedListener = () -> {
- // Hide the task view and wait for the window to be resized
- // TODO: Consider animating in launcher and do an in-place start activity
- // afterwards
- recentsView.setIgnoreResetTask(taskId);
- taskView.setAlpha(0f);
- };
-
- final int[] position = new int[2];
- thumbnailView.getLocationOnScreen(position);
- final int width = (int) (thumbnailView.getWidth() * taskView.getScaleX());
- final int height = (int) (thumbnailView.getHeight() * taskView.getScaleY());
- final Rect taskBounds = new Rect(position[0], position[1],
- position[0] + width, position[1] + height);
-
- // Take the thumbnail of the task without a scrim and apply it back after
- float alpha = thumbnailView.getDimAlpha();
- thumbnailView.setDimAlpha(0);
- Bitmap thumbnail = RecentsTransition.drawViewIntoHardwareBitmap(
- taskBounds.width(), taskBounds.height(), thumbnailView, 1f,
- Color.BLACK);
- thumbnailView.setDimAlpha(alpha);
-
- AppTransitionAnimationSpecsFuture future =
- new AppTransitionAnimationSpecsFuture(mHandler) {
- @Override
- public List<AppTransitionAnimationSpecCompat> composeSpecs() {
- return Collections.singletonList(new AppTransitionAnimationSpecCompat(
- taskId, thumbnail, taskBounds));
- }
- };
- WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
- future, animStartedListener, mHandler, true /* scaleUp */, displayId);
- }
- });
- }
- }
-
- public static class SplitScreen extends MultiWindow {
- public SplitScreen() {
- super(R.drawable.ic_split_screen, R.string.recent_task_option_split_screen);
- }
-
- @Override
- protected boolean isAvailable(BaseDraggingActivity activity, int displayId) {
- // Don't show menu-item if already in multi-window and the task is from
- // the secondary display.
- // TODO(b/118266305): Temporarily disable splitscreen for secondary display while new
- // implementation is enabled
- return !activity.getDeviceProfile().isMultiWindowMode
- && (displayId == -1 || displayId == DEFAULT_DISPLAY);
- }
-
- @Override
- protected ActivityOptions makeLaunchOptions(Activity activity) {
- final ActivityCompat act = new ActivityCompat(activity);
- final int navBarPosition = WindowManagerWrapper.getInstance().getNavBarPosition(
- act.getDisplayId());
- if (navBarPosition == WindowManagerWrapper.NAV_BAR_POS_INVALID) {
- return null;
- }
- boolean dockTopOrLeft = navBarPosition != WindowManagerWrapper.NAV_BAR_POS_LEFT;
- return ActivityOptionsCompat.makeSplitScreenOptions(dockTopOrLeft);
- }
-
- @Override
- protected boolean onActivityStarted(BaseDraggingActivity activity) {
- 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;
- }
- activity.getUserEventDispatcher().logActionOnControl(TAP,
- LauncherLogProto.ControlType.SPLIT_SCREEN_TARGET);
- return true;
- }
- }
-
- public static class Freeform extends MultiWindow {
- public Freeform() {
- super(R.drawable.ic_split_screen, R.string.recent_task_option_freeform);
- }
-
- @Override
- protected boolean isAvailable(BaseDraggingActivity activity, int displayId) {
- return ActivityManagerWrapper.getInstance().supportsFreeformMultiWindow(activity);
- }
-
- @Override
- protected ActivityOptions makeLaunchOptions(Activity activity) {
- ActivityOptions activityOptions = ActivityOptionsCompat.makeFreeformOptions();
- // Arbitrary bounds only because freeform is in dev mode right now
- Rect r = new Rect(50, 50, 200, 200);
- activityOptions.setLaunchBounds(r);
- return activityOptions;
- }
-
- @Override
- protected boolean onActivityStarted(BaseDraggingActivity activity) {
- activity.returnToHomescreen();
- return true;
- }
- }
-
- public static class Pin extends TaskSystemShortcut {
-
- private static final String TAG = Pin.class.getSimpleName();
-
- private Handler mHandler;
-
- public Pin() {
- super(R.drawable.ic_pin, R.string.recent_task_option_pin);
- mHandler = new Handler(Looper.getMainLooper());
- }
-
- @Override
- public View.OnClickListener getOnClickListener(
- BaseDraggingActivity activity, TaskView taskView) {
- ISystemUiProxy sysUiProxy = RecentsModel.INSTANCE.get(activity).getSystemUiProxy();
- if (sysUiProxy == null) {
- return null;
- }
- if (!ActivityManagerWrapper.getInstance().isScreenPinningEnabled()) {
- return null;
- }
- if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
- // We shouldn't be able to pin while an app is locked.
- return null;
- }
- return view -> {
- Consumer<Boolean> resultCallback = success -> {
- if (success) {
- try {
- sysUiProxy.startScreenPinning(taskView.getTask().key.id);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to start screen pinning: ", e);
- }
- } else {
- taskView.notifyTaskLaunchFailed(TAG);
- }
- };
- taskView.launchTask(true, resultCallback, mHandler);
- dismissTaskMenuView(activity);
- };
- }
- }
-
- public static class Install extends TaskSystemShortcut<SystemShortcut.Install> {
- public Install() {
- super(new SystemShortcut.Install());
- }
-
- @Override
- protected View.OnClickListener getOnClickListenerForTask(
- BaseDraggingActivity activity, Task task, ItemInfo itemInfo) {
- if (InstantAppResolver.newInstance(activity).isInstantApp(activity,
- task.getTopComponent().getPackageName())) {
- return mSystemShortcut.createOnClickListener(activity, itemInfo);
- }
- return null;
- }
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
index 6897c1e..bfe5738 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,36 @@
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) {
+ SurfaceParams[] liveTileSurfaceParams =
+ liveTileAnimationHelper.getSurfaceParams(liveTileParams);
+ if (liveTileSurfaceParams != null) {
+ Collections.addAll(surfaceParamsList, liveTileSurfaceParams);
+ }
+ }
+ }
+ // 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 +205,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 f321826..71f8ba4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -27,82 +27,62 @@
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;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.Service;
-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.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.InputEvent;
import android.view.MotionEvent;
-import android.view.Surface;
import androidx.annotation.BinderThread;
+import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.R;
-import com.android.launcher3.ResourceUtils;
import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.BaseFlags;
-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.DefaultDisplay;
-import com.android.quickstep.SysUINavigationMode.Mode;
-import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
+import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
+import com.android.launcher3.util.TraceHelper;
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.OverscrollInputConsumer;
import com.android.quickstep.inputconsumers.OverviewInputConsumer;
import com.android.quickstep.inputconsumers.OverviewWithoutFocusInputConsumer;
import com.android.quickstep.inputconsumers.ResetGestureInputConsumer;
import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer;
+import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.AssistantUtilities;
+import com.android.systemui.plugins.OverscrollPlugin;
+import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
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 java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -131,51 +111,45 @@
* Service connected by system-UI for handling touch interaction.
*/
@TargetApi(Build.VERSION_CODES.Q)
-public class TouchInteractionService extends Service implements
- NavigationModeChangeListener, DefaultDisplay.DisplayInfoChangeListener {
-
- /**
- * 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 static final EventLogArray TOUCH_INTERACTION_LOG =
- new EventLogArray("touch_interaction_log", 40);
+public class TouchInteractionService extends Service implements PluginListener<OverscrollPlugin> {
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;
+ @Nullable
+ private OverscrollPlugin mOverscrollPlugin;
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_EXECUTOR.execute(TouchInteractionService.this::initInputMonitor);
- MAIN_EXECUTOR.execute(TouchInteractionService.this::onSystemUiProxySet);
- MAIN_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);
+ TouchInteractionService.this.initInputMonitor();
+ preloadOverview(true /* fromInit */);
+ });
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) {
@@ -184,42 +158,58 @@
}
}
+ @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);
+ TouchInteractionService.this.onAssistantVisibilityChanged();
+ });
}
+ @BinderThread
@Override
public void onAssistantVisibilityChanged(float visibility) {
- mLastAssistantVisibility = visibility;
- MAIN_EXECUTOR.execute(
- TouchInteractionService.this::onAssistantVisibilityChanged);
+ MAIN_EXECUTOR.execute(() -> {
+ mDeviceState.setAssistantVisibility(visibility);
+ 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()) {
UI_HELPER_EXECUTOR.execute(TouchInteractionService.this::tryNotifyBackGesture);
}
}
+ @BinderThread
public void onSystemUiStateChanged(int stateFlags) {
- mSystemUiStateFlags = stateFlags;
- MAIN_EXECUTOR.execute(TouchInteractionService.this::onSystemUiFlagsChanged);
+ MAIN_EXECUTOR.execute(() -> {
+ mDeviceState.setSystemUiFlags(stateFlags);
+ TouchInteractionService.this.onSystemUiFlagsChanged();
+ });
+ }
+
+ @BinderThread
+ public void onActiveNavBarRegionChanges(Region region) {
+ MAIN_EXECUTOR.execute(() -> mDeviceState.setDeferredGestureRegion(region));
}
/** Deprecated methods **/
@@ -242,8 +232,6 @@
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;
@@ -253,91 +241,38 @@
return sIsInitialized;
}
- public static SwipeSharedState getSwipeSharedState() {
- return sSwipeSharedState;
- }
-
- private final InputConsumer mResetGestureInputConsumer =
- new ResetGestureInputConsumer(sSwipeSharedState);
-
- private final BaseSwipeUpHandler.Factory mWindowTreansformFactory =
- this::createWindowTransformSwipeHandler;
- private final BaseSwipeUpHandler.Factory mFallbackNoButtonFactory =
- this::createFallbackNoButtonSwipeHandler;
+ private final BaseSwipeUpHandler.Factory mLauncherSwipeHandlerFactory =
+ this::createLauncherSwipeHandler;
+ private final BaseSwipeUpHandler.Factory mFallbackSwipeHandlerFactory =
+ this::createFallbackSwipeHandler;
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 TaskAnimationManager mTaskAnimationManager;
private InputConsumer mUncheckedConsumer = InputConsumer.NO_OP;
private InputConsumer mConsumer = InputConsumer.NO_OP;
private Choreographer mMainChoreographer;
-
- private Region mActiveNavBarRegion = new Region();
+ private InputConsumer mResetGestureInputConsumer;
+ private GestureState mGestureState = new GestureState();
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();
-
// 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();
+ mDeviceState = new RecentsAnimationDeviceState(this);
+ mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
+ mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
- if (UserManagerCompat.getInstance(this).isUserUnlocked(Process.myUserHandle())) {
- initWhenUserUnlocked();
- } else {
- mIsUserUnlocked = false;
- registerReceiver(mUserUnlockedReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
- }
-
- mDefaultDisplayId = DefaultDisplay.INSTANCE.get(this).getInfo().id;
- 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;
}
@@ -359,123 +294,43 @@
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "initInputMonitor 1");
}
- if (!mMode.hasGestures || mISystemUiProxy == null) {
+ disposeEventHandlers();
+ if (mDeviceState.isButtonNavMode() || !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;
- }
-
- DefaultDisplay.Info displayInfo = DefaultDisplay.INSTANCE.get(this).getInfo();
- Point realSize = new Point(displayInfo.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 (displayInfo.rotation) {
- 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);
- }
- }
- }
-
- @Override
- public void onNavigationModeChanged(Mode newMode) {
+ 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, "onNavigationModeChanged " + newMode);
+ Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "initInputMonitor 3");
}
- if (mMode.hasGestures != newMode.hasGestures) {
- if (newMode.hasGestures) {
- DefaultDisplay.INSTANCE.get(this).addChangeListener(this);
- } else {
- DefaultDisplay.INSTANCE.get(this).removeChangeListener(this);
- }
- }
- mMode = newMode;
- disposeEventHandlers();
+ mDeviceState.updateGestureTouchRegions();
+ }
+
+ /**
+ * Called when the navigation mode changes, guaranteed to be after the device state has updated.
+ */
+ private void onNavigationModeChanged(SysUINavigationMode.Mode mode) {
initInputMonitor();
-
- if (mMode == Mode.NO_BUTTON) {
- mExclusionListener.register();
- } else {
- mExclusionListener.unregister();
- }
+ resetHomeBounceSeenOnQuickstepEnabledFirstTime();
}
- @Override
- public void onDisplayInfoChanged(DefaultDisplay.Info info, int flags) {
- if (info.id != mDefaultDisplayId) {
- return;
- }
-
- initTouchBounds();
- }
-
- private void initWhenUserUnlocked() {
- 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);
+ @UiThread
+ public void onUserUnlocked() {
+ mTaskAnimationManager = new TaskAnimationManager();
+ mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
+ mOverviewCommandHelper = new OverviewCommandHelper(this, mDeviceState,
+ mOverviewComponentObserver);
+ mResetGestureInputConsumer = new ResetGestureInputConsumer(mTaskAnimationManager);
mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
- mIsUserUnlocked = true;
-
- sSwipeSharedState.setOverviewComponentObserver(mOverviewComponentObserver);
mInputConsumer.registerInputConsumer();
- onSystemUiProxySet();
onSystemUiFlagsChanged();
onAssistantVisibilityChanged();
@@ -483,51 +338,71 @@
// new ModelPreload().start(this);
mBackGestureNotificationCounter = Math.max(0, Utilities.getDevicePrefs(this)
.getInt(KEY_BACK_NOTIFICATION_COUNT, MAX_BACK_NOTIFICATION_COUNT));
+ resetHomeBounceSeenOnQuickstepEnabledFirstTime();
- Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver);
+ PluginManagerWrapper.INSTANCE.get(getBaseContext()).addPluginListener(this,
+ OverscrollPlugin.class, false /* allowMultiple */);
}
- @UiThread
- private void onSystemUiProxySet() {
- if (mIsUserUnlocked) {
- mRecentsModel.setSystemUiProxy(mISystemUiProxy);
- mOverviewInteractionState.setSystemUiProxy(mISystemUiProxy);
+ private void onDeferredActivityLaunch() {
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ mOverviewComponentObserver.getActivityInterface().switchRunningTaskViewToScreenshot(
+ null, () -> {
+ mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
+ });
+ } else {
+ mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
+ }
+ }
+
+ private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
+ if (!mDeviceState.isUserUnlocked() || mDeviceState.isButtonNavMode()) {
+ // 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() {
+ PluginManagerWrapper.INSTANCE.get(getBaseContext()).removePluginListener(this);
+
sIsInitialized = false;
- if (mIsUserUnlocked) {
+ if (mDeviceState.isUserUnlocked()) {
mInputConsumer.unregisterInputConsumer();
mOverviewComponentObserver.onDestroy();
}
disposeEventHandlers();
- if (mMode.hasGestures) {
- DefaultDisplay.INSTANCE.get(this).removeChangeListener(this);
- }
+ mDeviceState.destroy();
+ SystemUiProxy.INSTANCE.get(this).setProxy(null);
sConnected = false;
- Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver);
- SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
- mExclusionListener.unregister();
-
super.onDestroy();
}
@@ -545,197 +420,189 @@
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;
if (event.getAction() == ACTION_DOWN) {
- mLogId = TOUCH_INTERACTION_LOG.generateAndSetLogId();
- sSwipeSharedState.setLogTraceId(mLogId);
+ GestureState newGestureState = new GestureState(mOverviewComponentObserver,
+ ActiveGestureLog.INSTANCE.generateAndSetLogId());
+ newGestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
+ () -> mAM.getRunningTask(0)));
- if (mSwipeTouchRegion.contains(event.getX(), event.getY())) {
- boolean useSharedState = mConsumer.useSharedSwipeState();
+ if (mDeviceState.isInSwipeUpTouchRegion(event)) {
mConsumer.onConsumerAboutToBeSwitched();
- mConsumer = newConsumer(useSharedState, event);
- TOUCH_INTERACTION_LOG.addLog("setInputConsumer", mConsumer.getType());
+ mConsumer = newConsumer(mGestureState, newGestureState, event);
+
+ ActiveGestureLog.INSTANCE.addLog("setInputConsumer", mConsumer.getType());
mUncheckedConsumer = mConsumer;
- } else if (mIsUserUnlocked && mMode == Mode.NO_BUTTON
- && canTriggerAssistantAction(event)) {
+ } else if (mDeviceState.isUserUnlocked()
+ && mDeviceState.isFullyGesturalNavMode()
+ && 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;
}
+
+ // Save the current gesture state
+ mGestureState = newGestureState;
}
- TOUCH_INTERACTION_LOG.addLog("onMotionEvent", event.getActionMasked());
+ 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 previousGestureState,
+ GestureState newGestureState, 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(newGestureState);
} else {
return mResetGestureInputConsumer;
}
}
- // 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;
- if (mMode == Mode.NO_BUTTON) {
- final ActivityControlHelper activityControl =
- mOverviewComponentObserver.getActivityControlHelper();
- if (canTriggerAssistantAction(event)) {
- base = new AssistantTouchConsumer(this, mISystemUiProxy, activityControl, base,
- mInputMonitorCompat);
+ // When there is an existing recents animation running, bypass systemState check as this is
+ // a followup gesture and the first gesture started in a valid system state.
+ InputConsumer base = canStartSystemGesture
+ || previousGestureState.isRecentsAnimationRunning()
+ ? newBaseConsumer(previousGestureState, newGestureState, event)
+ : mResetGestureInputConsumer;
+ if (mDeviceState.isFullyGesturalNavMode()) {
+ if (mDeviceState.canTriggerAssistantAction(event)) {
+ base = new AssistantInputConsumer(this, newGestureState, base, mInputMonitorCompat);
}
- if ((mSystemUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 0) {
+ if (mOverscrollPlugin != null) {
+ // Put the overscroll gesture as higher priority than the Assistant or base gestures
+ base = new OverscrollInputConsumer(this, newGestureState, base, mInputMonitorCompat,
+ mOverscrollPlugin);
+ }
+
+ if (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, newGestureState);
}
- 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);
- if (!useSharedState) {
- sSwipeSharedState.clearAllState(false /* finishAnimation */);
- }
- if ((mSystemUiStateFlags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) != 0) {
+ private InputConsumer newBaseConsumer(GestureState previousGestureState,
+ GestureState gestureState, MotionEvent event) {
+ if (mDeviceState.isKeyguardShowingOccluded()) {
// This handles apps showing over the lockscreen (e.g. camera)
- return createDeviceLockedInputConsumer(runningTaskInfo);
+ return createDeviceLockedInputConsumer(gestureState);
}
- final ActivityControlHelper activityControl =
- mOverviewComponentObserver.getActivityControlHelper();
-
boolean forceOverviewInputConsumer = false;
- if (AssistantUtilities.isExcludedAssistant(runningTaskInfo)) {
+ if (AssistantUtilities.isExcludedAssistant(gestureState.getRunningTask())) {
// 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 /* ignoreActivityType */);
+ gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.assistant",
+ () -> mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT /* ignoreActivityType */)));
ComponentName homeComponent = mOverviewComponentObserver.getHomeIntent().getComponent();
- ComponentName runningComponent = runningTaskInfo.baseIntent.getComponent();
+ ComponentName runningComponent =
+ gestureState.getRunningTask().baseIntent.getComponent();
forceOverviewInputConsumer =
- runningComponent != null && runningComponent.equals(homeComponent);
+ runningComponent != null && runningComponent.equals(homeComponent);
}
- if (runningTaskInfo == null && !sSwipeSharedState.goingToLauncher
- && !sSwipeSharedState.recentsAnimationFinishInterrupted) {
- return mResetGestureInputConsumer;
- } else if (sSwipeSharedState.recentsAnimationFinishInterrupted) {
+ if (previousGestureState.getFinishingRecentsAnimationTaskId() > 0) {
// If the finish animation was interrupted, then continue using the other activity input
// consumer but with the next task as the running task
RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
- info.id = sSwipeSharedState.nextRunningTaskId;
- return createOtherActivityInputConsumer(event, info);
- } else if (sSwipeSharedState.goingToLauncher || activityControl.isResumed()
+ info.id = previousGestureState.getFinishingRecentsAnimationTaskId();
+ gestureState.updateRunningTask(info);
+ return createOtherActivityInputConsumer(previousGestureState, gestureState, event);
+ } else if (gestureState.getRunningTask() == null) {
+ return mResetGestureInputConsumer;
+ } else if (previousGestureState.isRunningAnimationToLauncher()
+ || gestureState.getActivityInterface().isResumed()
|| forceOverviewInputConsumer) {
- return createOverviewInputConsumer(event, forceOverviewInputConsumer);
- } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityControl.isInLiveTileMode()) {
- return createOverviewInputConsumer(event, forceOverviewInputConsumer);
- } else if (mGestureBlockingActivity != null && runningTaskInfo != null
- && mGestureBlockingActivity.equals(runningTaskInfo.topActivity)) {
+ return createOverviewInputConsumer(
+ previousGestureState, gestureState, event, forceOverviewInputConsumer);
+ } else if (ENABLE_QUICKSTEP_LIVE_TILE.get()
+ && gestureState.getActivityInterface().isInLiveTileMode()) {
+ return createOverviewInputConsumer(
+ previousGestureState, gestureState, event, forceOverviewInputConsumer);
+ } else if (mDeviceState.isGestureBlockedActivity(gestureState.getRunningTask())) {
return mResetGestureInputConsumer;
} else {
- return createOtherActivityInputConsumer(event, runningTaskInfo);
+ return createOtherActivityInputConsumer(previousGestureState, gestureState, event);
}
}
- 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 previousGestureState,
+ GestureState gestureState, MotionEvent event) {
final boolean shouldDefer;
final BaseSwipeUpHandler.Factory factory;
- if (mMode == Mode.NO_BUTTON && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
- shouldDefer = !sSwipeSharedState.recentsAnimationFinishInterrupted;
- factory = mFallbackNoButtonFactory;
+ if (mDeviceState.isFullyGesturalNavMode()
+ && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
+ shouldDefer = previousGestureState.getFinishingRecentsAnimationTaskId() < 0;
+ factory = mFallbackSwipeHandlerFactory;
} else {
- shouldDefer = mOverviewComponentObserver.getActivityControlHelper()
- .deferStartingActivity(mActiveNavBarRegion, event);
- factory = mWindowTreansformFactory;
+ shouldDefer = gestureState.getActivityInterface().deferStartingActivity(mDeviceState,
+ event);
+ factory = mLauncherSwipeHandlerFactory;
}
- return new OtherActivityInputConsumer(this, runningTaskInfo,
- shouldDefer, mOverviewCallbacks, this::onConsumerInactive,
- sSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion,
- disableHorizontalSwipe(event), factory, mLogId);
+ final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
+ return new OtherActivityInputConsumer(this, mDeviceState, mTaskAnimationManager,
+ gestureState, shouldDefer, this::onConsumerInactive,
+ mInputMonitorCompat, disableHorizontalSwipe, factory);
}
- private InputConsumer createDeviceLockedInputConsumer(RunningTaskInfo taskInfo) {
- if (mMode == Mode.NO_BUTTON && taskInfo != null) {
- return new DeviceLockedInputConsumer(this, sSwipeSharedState, mInputMonitorCompat,
- mSwipeTouchRegion, taskInfo.taskId, mLogId);
+ private InputConsumer createDeviceLockedInputConsumer(GestureState gestureState) {
+ if (mDeviceState.isFullyGesturalNavMode() && gestureState.getRunningTask() != null) {
+ return new DeviceLockedInputConsumer(this, mDeviceState, mTaskAnimationManager,
+ gestureState, mInputMonitorCompat);
} else {
return mResetGestureInputConsumer;
}
}
- public InputConsumer createOverviewInputConsumer(MotionEvent event,
+ public InputConsumer createOverviewInputConsumer(GestureState previousGestureState,
+ GestureState gestureState, MotionEvent event,
boolean forceOverviewInputConsumer) {
- final ActivityControlHelper activityControl =
- mOverviewComponentObserver.getActivityControlHelper();
- BaseDraggingActivity activity = activityControl.getCreatedActivity();
+ BaseDraggingActivity activity = gestureState.getActivityInterface().getCreatedActivity();
if (activity == null) {
return mResetGestureInputConsumer;
}
if (activity.getRootView().hasWindowFocus()
- || sSwipeSharedState.goingToLauncher
- || (BaseFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS.get()
+ || previousGestureState.isRunningAnimationToLauncher()
+ || (FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS.get()
&& forceOverviewInputConsumer)) {
- 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, mDeviceState, gestureState,
+ mInputMonitorCompat, disableHorizontalSwipe);
}
}
@@ -750,10 +617,10 @@
}
private void preloadOverview(boolean fromInit) {
- if (!mIsUserUnlocked) {
+ if (!mDeviceState.isUserUnlocked()) {
return;
}
- if (!mMode.hasGestures && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
+ if (mDeviceState.isButtonNavMode() && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
// Prevent the overview from being started before the real home on first boot.
return;
}
@@ -764,12 +631,12 @@
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) -> {
- AppLaunchTracker.INSTANCE.get(activity);
+ activityInterface.createActivityInitListener((wasVisible) -> {
+ AppLaunchTracker.INSTANCE.get(TouchInteractionService.this);
return false;
}).register();
} else if (fromInit) {
@@ -779,19 +646,18 @@
return;
}
- // Pass null animation handler to indicate this start is preload.
- startRecentsActivityAsync(mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState(),
- null);
+ mTaskAnimationManager.preloadRecentsAnimation(
+ mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState());
}
@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;
@@ -819,22 +685,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()) {
- sSwipeSharedState.dump(" ", pw);
- }
pw.println(" mConsumer=" + mConsumer.getName());
pw.println("FeatureFlags:");
pw.println(" APPLY_CONFIG_AT_RUNTIME=" + APPLY_CONFIG_AT_RUNTIME.get());
@@ -843,8 +698,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);
}
}
@@ -856,26 +710,26 @@
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,
+ private BaseSwipeUpHandler createLauncherSwipeHandler(GestureState gestureState,
long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask) {
- return new WindowTransformSwipeHandler(runningTask, this, touchTimeMs,
- mOverviewComponentObserver, continuingLastGesture, mInputConsumer, mRecentsModel);
+ return new LauncherSwipeHandler(this, mDeviceState, mTaskAnimationManager,
+ gestureState, touchTimeMs, continuingLastGesture, mInputConsumer);
}
- private BaseSwipeUpHandler createFallbackNoButtonSwipeHandler(RunningTaskInfo runningTask,
+ private BaseSwipeUpHandler createFallbackSwipeHandler(GestureState gestureState,
long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask) {
- return new FallbackNoButtonInputConsumer(this, mOverviewComponentObserver, runningTask,
- mRecentsModel, mInputConsumer, isLikelyToStartNewTask, continuingLastGesture);
+ return new FallbackSwipeHandler(this, mDeviceState, gestureState,
+ mInputConsumer, isLikelyToStartNewTask, continuingLastGesture);
}
protected boolean shouldNotifyBackGesture() {
return mBackGestureNotificationCounter > 0 &&
- mGestureBlockingActivity != null;
+ mDeviceState.getGestureBlockedActivityPackage() != null;
}
@WorkerThread
@@ -885,7 +739,7 @@
Utilities.getDevicePrefs(this).edit()
.putInt(KEY_BACK_NOTIFICATION_COUNT, mBackGestureNotificationCounter).apply();
sendBroadcast(new Intent(NOTIFY_ACTION_BACK).setPackage(
- mGestureBlockingActivity.getPackageName()));
+ mDeviceState.getGestureBlockedActivityPackage()));
}
}
@@ -893,4 +747,14 @@
UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
.startRecentsActivity(intent, null, listener, null, null));
}
+
+ @Override
+ public void onPluginConnected(OverscrollPlugin overscrollPlugin, Context context) {
+ mOverscrollPlugin = overscrollPlugin;
+ }
+
+ @Override
+ public void onPluginDisconnected(OverscrollPlugin overscrollPlugin) {
+ mOverscrollPlugin = null;
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/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/fallback/FallbackRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 7f1aae5..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;
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..2f73fc1 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 {
@@ -22,11 +23,6 @@
}
@Override
- public boolean useSharedSwipeState() {
- return mDelegate.useSharedSwipeState();
- }
-
- @Override
public boolean allowInterceptByParent() {
return mDelegate.allowInterceptByParent() && mState != STATE_ACTIVE;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index b24c788..5a34520 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,9 +22,8 @@
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
-import static com.android.quickstep.TouchInteractionService.INTENT_EXTRA_LOG_TRACE_ID;
-import static com.android.quickstep.TouchInteractionService.startRecentsActivityAsync;
-import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
+import static com.android.quickstep.LauncherSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
+import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
import android.content.ComponentName;
import android.content.Context;
@@ -32,7 +31,6 @@
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;
@@ -40,12 +38,17 @@
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.SwipeSharedState;
-import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.util.RecentsAnimationListenerSet;
-import com.android.quickstep.util.SwipeAnimationTargetSet;
+import com.android.quickstep.RecentsAnimationController;
+import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.RecentsAnimationCallbacks;
+import com.android.quickstep.RecentsAnimationTargets;
+import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.util.AppWindowAnimationHelper;
+import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -53,7 +56,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;
@@ -71,45 +74,44 @@
getFlagForIndex(1, "STATE_HANDLER_INVALIDATED");
private final Context mContext;
+ private final RecentsAnimationDeviceState mDeviceState;
+ private final TaskAnimationManager mTaskAnimationManager;
+ private final GestureState mGestureState;
private final float mTouchSlopSquared;
- private final SwipeSharedState mSwipeSharedState;
private final InputMonitorCompat mInputMonitorCompat;
private final PointF mTouchDown = new PointF();
- private final ClipAnimationHelper mClipAnimationHelper;
- private int mLogId;
- private final ClipAnimationHelper.TransformParams mTransformParams;
+ private final AppWindowAnimationHelper mAppWindowAnimationHelper;
+ private final AppWindowAnimationHelper.TransformParams mTransformParams;
private final Point mDisplaySize;
private final MultiStateCallback mStateCallback;
- private final RectF mSwipeTouchRegion;
- public final int mRunningTaskId;
private VelocityTracker mVelocityTracker;
private float mProgress;
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,
- int logId) {
+ public DeviceLockedInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
+ TaskAnimationManager taskAnimationManager, GestureState gestureState,
+ InputMonitorCompat inputMonitorCompat) {
mContext = context;
+ mDeviceState = deviceState;
+ mTaskAnimationManager = taskAnimationManager;
+ mGestureState = gestureState;
mTouchSlopSquared = squaredTouchSlop(context);
- mSwipeSharedState = swipeSharedState;
- mClipAnimationHelper = new ClipAnimationHelper(context);
- mLogId = logId;
- mTransformParams = new ClipAnimationHelper.TransformParams();
+ mAppWindowAnimationHelper = new AppWindowAnimationHelper(context);
+ mTransformParams = new AppWindowAnimationHelper.TransformParams();
mInputMonitorCompat = inputMonitorCompat;
- mSwipeTouchRegion = swipeTouchRegion;
- mRunningTaskId = runningTaskId;
// Do not use DeviceProfile as the user data might be locked
mDisplaySize = DefaultDisplay.INSTANCE.get(context).getInfo().realSize;
// Init states
mStateCallback = new MultiStateCallback(STATE_NAMES);
- mStateCallback.addCallback(STATE_TARGET_RECEIVED | STATE_HANDLER_INVALIDATED,
+ mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_HANDLER_INVALIDATED,
this::endRemoteAnimation);
mVelocityTracker = VelocityTracker.obtain();
@@ -137,7 +139,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);
@@ -155,9 +157,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;
}
@@ -202,45 +202,48 @@
private void startRecentsTransition() {
mThresholdCrossed = true;
- RecentsAnimationListenerSet newListenerSet =
- mSwipeSharedState.newRecentsAnimationListenerSet();
- newListenerSet.addListener(this);
+ mInputMonitorCompat.pilferPointers();
+
Intent intent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_DEFAULT)
.setComponent(new ComponentName(mContext, LockScreenRecentsActivity.class))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
- .putExtra(INTENT_EXTRA_LOG_TRACE_ID, mLogId);
-
- mInputMonitorCompat.pilferPointers();
- startRecentsActivityAsync(intent, newListenerSet);
+ .putExtra(INTENT_EXTRA_LOG_TRACE_ID, mGestureState.getGestureId());
+ mTaskAnimationManager.startRecentsAnimation(mGestureState, intent, this);
}
@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(
+ mGestureState.getRunningTaskId());
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/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index e41880d..bf2128d 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
@@ -22,45 +22,44 @@
import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.MotionEvent.INVALID_POINTER_ID;
+
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.INTENT_EXTRA_LOG_TRACE_ID;
-import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
-import static com.android.quickstep.TouchInteractionService.startRecentsActivityAsync;
+import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS;
+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;
import android.view.MotionEvent;
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.BaseSwipeUpHandler.Factory;
-import com.android.quickstep.OverviewCallbacks;
-import com.android.quickstep.SwipeSharedState;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.GestureState;
+import com.android.quickstep.InputConsumer;
+import com.android.quickstep.RecentsAnimationCallbacks;
+import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.TaskAnimationManager;
+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;
/**
@@ -75,21 +74,20 @@
// TODO: Move to quickstep contract
public static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3;
+ private final RecentsAnimationDeviceState mDeviceState;
+ private final TaskAnimationManager mTaskAnimationManager;
+ private final GestureState mGestureState;
+ private RecentsAnimationCallbacks mActiveCallbacks;
private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher();
- private final RunningTaskInfo mRunningTask;
- private final 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;
- private final NavBarPosition mNavBarPosition;
-
private final Consumer<OtherActivityInputConsumer> mOnCompleteCallback;
private final MotionPauseDetector mMotionPauseDetector;
private final float mMotionPauseMinDisplacement;
+
private VelocityTracker mVelocityTracker;
private BaseSwipeUpHandler mInteractionHandler;
@@ -118,22 +116,19 @@
ActivityManagerWrapper.getInstance().cancelRecentsAnimation(
true /* restoreHomeStackPosition */);
};
- private int mLogId;
- public OtherActivityInputConsumer(Context base, RunningTaskInfo runningTaskInfo,
- boolean isDeferredDownTarget, OverviewCallbacks overviewCallbacks,
- Consumer<OtherActivityInputConsumer> onCompleteCallback,
- SwipeSharedState swipeSharedState, InputMonitorCompat inputMonitorCompat,
- RectF swipeTouchRegion, boolean disableHorizontalSwipe,
- Factory handlerFactory, int logId) {
+ public OtherActivityInputConsumer(Context base, RecentsAnimationDeviceState deviceState,
+ TaskAnimationManager taskAnimationManager, GestureState gestureState,
+ boolean isDeferredDownTarget, Consumer<OtherActivityInputConsumer> onCompleteCallback,
+ InputMonitorCompat inputMonitorCompat, boolean disableHorizontalSwipe,
+ Factory handlerFactory) {
super(base);
- mLogId = logId;
-
+ mDeviceState = deviceState;
+ mTaskAnimationManager = taskAnimationManager;
+ 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(
@@ -142,12 +137,8 @@
mVelocityTracker = VelocityTracker.obtain();
mInputMonitorCompat = inputMonitorCompat;
- boolean continuingPreviousGesture = swipeSharedState.getActiveListener() != null;
+ boolean continuingPreviousGesture = mTaskAnimationManager.isRecentsAnimationRunning();
mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget;
- mOverviewCallbacks = overviewCallbacks;
- mSwipeSharedState = swipeSharedState;
-
- mNavBarPosition = new NavBarPosition(base);
mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
float slop = QUICKSTEP_TOUCH_SLOP_RATIO * mTouchSlop;
@@ -179,7 +170,7 @@
if (mPassedWindowMoveSlop && mInteractionHandler != null
&& !mRecentsViewDispatcher.hasConsumer()) {
mRecentsViewDispatcher.setConsumer(mInteractionHandler.getRecentsViewDispatcher(
- mNavBarPosition.getRotationMode()));
+ mDeviceState.getNavBarPosition().getRotationMode()));
}
int edgeFlags = ev.getEdgeFlags();
ev.setEdgeFlags(edgeFlags | EDGE_NAV_BAR);
@@ -194,8 +185,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);
@@ -206,14 +197,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);
}
}
@@ -289,7 +280,7 @@
mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
}
- if (mMode == Mode.NO_BUTTON) {
+ if (mDeviceState.isFullyGesturalNavMode()) {
mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement
|| isLikelyToStartNewTask);
mMotionPauseDetector.addPosition(displacement, ev.getEventTime());
@@ -307,13 +298,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);
@@ -323,28 +314,24 @@
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);
+ mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs,
+ mTaskAnimationManager.isRecentsAnimationRunning(), isLikelyToStartNewTask);
+ mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished);
+ mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler::onMotionPauseChanged);
+ mInteractionHandler.initWhenReady();
- mInteractionHandler = handler;
- handler.setGestureEndCallback(this::onInteractionGestureFinished);
- mMotionPauseDetector.setOnMotionPauseListener(handler::onMotionPauseChanged);
- handler.initWhenReady();
-
- if (listenerSet != null) {
- listenerSet.addListener(handler);
- mSwipeSharedState.applyActiveRecentsAnimationState(handler);
+ if (mTaskAnimationManager.isRecentsAnimationRunning()) {
+ mActiveCallbacks = mTaskAnimationManager.continueRecentsAnimation(mGestureState);
+ mActiveCallbacks.addListener(mInteractionHandler);
+ mTaskAnimationManager.notifyRecentsAnimationState(mInteractionHandler);
notifyGestureStarted();
} else {
- RecentsAnimationListenerSet newListenerSet =
- mSwipeSharedState.newRecentsAnimationListenerSet();
- newListenerSet.addListener(handler);
- Intent intent = handler.getLaunchIntent();
- intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mLogId);
- startRecentsActivityAsync(intent, newListenerSet);
+ Intent intent = mInteractionHandler.getLaunchIntent();
+ intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mGestureState.getGestureId());
+ mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(mGestureState, intent,
+ mInteractionHandler);
}
}
@@ -353,8 +340,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) {
@@ -364,8 +351,10 @@
ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
float velocityY = mVelocityTracker.getYVelocity(mActivePointerId);
- float velocity = mNavBarPosition.isRightEdge() ? velocityX
- : mNavBarPosition.isLeftEdge() ? -velocityX
+ float velocity = mDeviceState.getNavBarPosition().isRightEdge()
+ ? velocityX
+ : mDeviceState.getNavBarPosition().isLeftEdge()
+ ? -velocityX
: velocityY;
mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement);
@@ -388,7 +377,7 @@
mVelocityTracker.recycle();
mVelocityTracker = null;
mMotionPauseDetector.clear();
- RaceConditionTracker.onEvent(UP_EVT, EXIT);
+ TraceHelper.INSTANCE.endSection(traceToken);
}
@Override
@@ -399,7 +388,7 @@
// The consumer is being switched while we are active. Set up the shared state to be
// used by the next animation
removeListener();
- mInteractionHandler.onConsumerAboutToBeSwitched(mSwipeSharedState);
+ mInteractionHandler.onConsumerAboutToBeSwitched();
}
}
@@ -412,16 +401,15 @@
}
private void removeListener() {
- RecentsAnimationListenerSet listenerSet = mSwipeSharedState.getActiveListener();
- if (listenerSet != null) {
- listenerSet.removeListener(mInteractionHandler);
+ if (mActiveCallbacks != null) {
+ mActiveCallbacks.removeListener(mInteractionHandler);
}
}
private float getDisplacement(MotionEvent ev) {
- if (mNavBarPosition.isRightEdge()) {
+ if (mDeviceState.getNavBarPosition().isRightEdge()) {
return ev.getX() - mDownPos.x;
- } else if (mNavBarPosition.isLeftEdge()) {
+ } else if (mDeviceState.getNavBarPosition().isLeftEdge()) {
return mDownPos.x - ev.getX();
} else {
return ev.getY() - mDownPos.y;
@@ -429,11 +417,6 @@
}
@Override
- public boolean useSharedSwipeState() {
- return mInteractionHandler != null;
- }
-
- @Override
public boolean allowInterceptByParent() {
return !mPassedPilferInputSlop;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java
new file mode 100644
index 0000000..e3da98b
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.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.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.content.Context;
+import android.graphics.PointF;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.quickstep.GestureState;
+import com.android.quickstep.InputConsumer;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.plugins.OverscrollPlugin;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+/**
+ * Input consumer for handling events to pass to an {@code OverscrollPlugin}.
+ *
+ * @param <T> Draggable activity subclass used by RecentsView
+ */
+public class OverscrollInputConsumer<T extends BaseDraggingActivity> extends DelegateInputConsumer {
+
+ private static final String TAG = "OverscrollInputConsumer";
+
+ 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 final Context mContext;
+ private final GestureState mGestureState;
+ @Nullable private final OverscrollPlugin mPlugin;
+
+ private RecentsView mRecentsView;
+
+ public OverscrollInputConsumer(Context context, GestureState gestureState,
+ InputConsumer delegate, InputMonitorCompat inputMonitor, OverscrollPlugin plugin) {
+ super(delegate, inputMonitor);
+ mContext = context;
+ mGestureState = gestureState;
+ mPlugin = plugin;
+
+ float slop = ViewConfiguration.get(context).getScaledTouchSlop();
+ mSquaredSlop = slop * slop;
+
+ gestureState.getActivityInterface().createActivityInitListener(this::onActivityInit)
+ .register();
+ }
+
+ @Override
+ public int getType() {
+ return TYPE_OVERSCROLL | mDelegate.getType();
+ }
+
+ private boolean onActivityInit(Boolean alreadyOnHome) {
+ mRecentsView = mGestureState.getActivityInterface().getCreatedActivity().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 (isOverscrolled()) {
+ setActive(ev);
+ } else {
+ mState = STATE_DELEGATE_ACTIVE;
+ }
+ }
+ }
+
+ break;
+ }
+ case ACTION_CANCEL:
+ case ACTION_UP:
+ if (mState != STATE_DELEGATE_ACTIVE && mPassedSlop && mPlugin != null) {
+ mPlugin.onOverscroll(getDeviceState());
+ }
+
+ mPassedSlop = false;
+ mState = STATE_INACTIVE;
+ break;
+ }
+
+ if (mState != STATE_ACTIVE) {
+ mDelegate.onMotionEvent(ev);
+ }
+ }
+
+ private boolean isOverscrolled() {
+ // 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 String getDeviceState() {
+ String deviceState = OverscrollPlugin.DEVICE_STATE_UNKNOWN;
+ int consumerType = mDelegate.getType();
+ if (((consumerType & InputConsumer.TYPE_OVERVIEW) > 0)
+ || ((consumerType & InputConsumer.TYPE_OVERVIEW_WITHOUT_FOCUS)) > 0) {
+ deviceState = OverscrollPlugin.DEVICE_STATE_LAUNCHER;
+ } else if ((consumerType & InputConsumer.TYPE_OTHER_ACTIVITY) > 0) {
+ deviceState = OverscrollPlugin.DEVICE_STATE_APP;
+ } else if (((consumerType & InputConsumer.TYPE_RESET_GESTURE) > 0)
+ || ((consumerType & InputConsumer.TYPE_DEVICE_LOCKED) > 0)) {
+ deviceState = OverscrollPlugin.DEVICE_STATE_LOCKED;
+ }
+
+ return deviceState;
+ }
+}
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 e553891..c19754f 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 f40d552..875ec29 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 android.content.Context;
import android.content.Intent;
@@ -36,30 +35,34 @@
import com.android.launcher3.logging.StatsLogUtils;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.quickstep.util.NavBarPosition;
+import com.android.quickstep.GestureState;
+import com.android.quickstep.InputConsumer;
+import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.util.ActiveGestureLog;
import com.android.systemui.shared.system.InputMonitorCompat;
public class OverviewWithoutFocusInputConsumer implements InputConsumer {
+ private final Context mContext;
+ private final RecentsAnimationDeviceState mDeviceState;
+ private final GestureState mGestureState;
private final InputMonitorCompat mInputMonitor;
private final boolean mDisableHorizontalSwipe;
private final PointF mDownPos = new PointF();
private final float mSquaredTouchSlop;
- private final Context mContext;
- private final NavBarPosition mNavBarPosition;
private boolean mInterceptedTouch;
private VelocityTracker mVelocityTracker;
-
- public OverviewWithoutFocusInputConsumer(Context context, InputMonitorCompat inputMonitor,
- boolean disableHorizontalSwipe) {
+ public OverviewWithoutFocusInputConsumer(Context context,
+ RecentsAnimationDeviceState deviceState, GestureState gestureState,
+ InputMonitorCompat inputMonitor, boolean disableHorizontalSwipe) {
+ mContext = context;
+ mDeviceState = deviceState;
+ mGestureState = gestureState;
mInputMonitor = inputMonitor;
mDisableHorizontalSwipe = disableHorizontalSwipe;
- mContext = context;
mSquaredTouchSlop = Utilities.squaredTouchSlop(context);
- mNavBarPosition = new NavBarPosition(context);
-
mVelocityTracker = VelocityTracker.obtain();
}
@@ -130,8 +133,11 @@
mVelocityTracker.computeCurrentVelocity(100);
float velocityX = mVelocityTracker.getXVelocity();
float velocityY = mVelocityTracker.getYVelocity();
- float velocity = mNavBarPosition.isRightEdge()
- ? -velocityX : (mNavBarPosition.isLeftEdge() ? velocityX : -velocityY);
+ float velocity = mDeviceState.getNavBarPosition().isRightEdge()
+ ? -velocityX
+ : mDeviceState.getNavBarPosition().isLeftEdge()
+ ? velocityX
+ : -velocityY;
final boolean triggerQuickstep;
int touch = Touch.FLING;
@@ -148,7 +154,7 @@
mContext.startActivity(new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- 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/ResetGestureInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
index 8eede81..d34b40b 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,17 +17,18 @@
import android.view.MotionEvent;
-import com.android.quickstep.SwipeSharedState;
+import com.android.quickstep.InputConsumer;
+import com.android.quickstep.TaskAnimationManager;
/**
* A NO_OP input consumer which also resets any pending gesture
*/
public class ResetGestureInputConsumer implements InputConsumer {
- private final SwipeSharedState mSwipeSharedState;
+ private final TaskAnimationManager mTaskAnimationManager;
- public ResetGestureInputConsumer(SwipeSharedState swipeSharedState) {
- mSwipeSharedState = swipeSharedState;
+ public ResetGestureInputConsumer(TaskAnimationManager taskAnimationManager) {
+ mTaskAnimationManager = taskAnimationManager;
}
@Override
@@ -38,8 +39,8 @@
@Override
public void onMotionEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN
- && mSwipeSharedState.getActiveListener() != null) {
- mSwipeSharedState.clearAllState(false /* finishAnimation */);
+ && mTaskAnimationManager.isRecentsAnimationRunning()) {
+ mTaskAnimationManager.finishRunningRecentsAnimation(false /* toHome */);
}
}
}
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..fabfc4b
--- /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";
+
+ private 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 88%
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 fa3be9c..4a39e73 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;
@@ -39,11 +38,11 @@
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;
@@ -55,7 +54,7 @@
* 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();
@@ -101,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);
@@ -156,36 +155,26 @@
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) {
- float progress = params.progress;
- if (params.currentRect == null) {
- RectF currentRect;
- mTmpRectF.set(mTargetRect);
- Utilities.scaleRectFAboutCenter(mTmpRectF, params.offsetScale);
- currentRect = mRectFEvaluator.evaluate(progress, mSourceRect, mTmpRectF);
- currentRect.offset(params.offsetX, 0);
-
- // Don't clip past progress > 1.
- progress = Math.min(1, progress);
- final RectF sourceWindowClipInsets = params.forLiveTile
- ? mSourceWindowClipInsetsForLiveTile : mSourceWindowClipInsets;
- mClipRectF.left = sourceWindowClipInsets.left * progress;
- mClipRectF.top = sourceWindowClipInsets.top * progress;
- mClipRectF.right =
- mSourceStackBounds.width() - (sourceWindowClipInsets.right * progress);
- mClipRectF.bottom =
- mSourceStackBounds.height() - (sourceWindowClipInsets.bottom * progress);
- params.setCurrentRectAndTargetAlpha(currentRect, 1);
+ public SurfaceParams[] getSurfaceParams(TransformParams params) {
+ if (params.targetSet == null) {
+ return null;
}
- SurfaceParams[] surfaceParams = new SurfaceParams[targetSet.unfilteredApps.length];
- for (int i = 0; i < targetSet.unfilteredApps.length; i++) {
- RemoteAnimationTargetCompat app = targetSet.unfilteredApps[i];
+ float progress = Utilities.boundToRange(params.progress, 0, 1);
+ 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);
@@ -194,7 +183,7 @@
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) {
+ 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);
@@ -217,14 +206,14 @@
&& app.isNotInRecents) {
alpha = 1 - Interpolators.DEACCEL_2_5.getInterpolation(progress);
}
- } else if (targetSet.hasRecents) {
+ } 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() && launcherOnTop) {
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get() && params.launcherOnTop) {
crop = null;
layer = Integer.MAX_VALUE;
}
@@ -236,6 +225,30 @@
cornerRadius / scale);
}
applySurfaceParams(params.syncTransactionApplier, surfaceParams);
+ return surfaceParams;
+ }
+
+ public RectF updateCurrentRect(TransformParams params) {
+ float progress = params.progress;
+ if (params.currentRect == null) {
+ RectF currentRect;
+ mTmpRectF.set(mTargetRect);
+ Utilities.scaleRectFAboutCenter(mTmpRectF, params.offsetScale);
+ currentRect = mRectFEvaluator.evaluate(progress, mSourceRect, mTmpRectF);
+ currentRect.offset(params.offsetX, 0);
+
+ // Don't clip past progress > 1.
+ progress = Math.min(1, progress);
+ final RectF sourceWindowClipInsets = params.forLiveTile
+ ? mSourceWindowClipInsetsForLiveTile : mSourceWindowClipInsets;
+ mClipRectF.left = sourceWindowClipInsets.left * progress;
+ mClipRectF.top = sourceWindowClipInsets.top * progress;
+ mClipRectF.right =
+ mSourceStackBounds.width() - (sourceWindowClipInsets.right * progress);
+ mClipRectF.bottom =
+ mSourceStackBounds.height() - (sourceWindowClipInsets.bottom * progress);
+ params.setCurrentRectAndTargetAlpha(currentRect, 1);
+ }
return params.currentRect;
}
@@ -244,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);
@@ -309,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);
@@ -329,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)
@@ -379,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;
@@ -394,6 +405,7 @@
targetAlpha = 0;
forLiveTile = false;
cornerRadius = -1;
+ launcherOnTop = false;
}
public TransformParams setProgress(float progress) {
@@ -428,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;
diff --git a/quickstep/src/com/android/quickstep/util/AssistantUtilities.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AssistantUtilities.java
similarity index 100%
rename from quickstep/src/com/android/quickstep/util/AssistantUtilities.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/util/AssistantUtilities.java
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
deleted file mode 100644
index bbb318a..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.util;
-
-import static com.android.launcher3.uioverrides.RecentsUiFactory.ROTATION_LANDSCAPE;
-import static com.android.launcher3.uioverrides.RecentsUiFactory.ROTATION_SEASCAPE;
-import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
-
-import android.content.Context;
-import android.view.Surface;
-
-import com.android.launcher3.graphics.RotationMode;
-import com.android.launcher3.util.DefaultDisplay;
-import com.android.quickstep.SysUINavigationMode;
-
-/**
- * Utility class to check nav bar position
- */
-public class NavBarPosition {
-
- private final SysUINavigationMode.Mode mMode;
- private final int mDisplayRotation;
-
- public NavBarPosition(Context context) {
- mMode = SysUINavigationMode.getMode(context);
- mDisplayRotation = DefaultDisplay.INSTANCE.get(context).getInfo().rotation;
- }
-
- public boolean isRightEdge() {
- return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_90;
- }
-
- public boolean isLeftEdge() {
- return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_270;
- }
-
- public RotationMode getRotationMode() {
- return isLeftEdge() ? ROTATION_SEASCAPE
- : (isRightEdge() ? ROTATION_LANDSCAPE : RotationMode.NORMAL);
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java
deleted file mode 100644
index b1999d7..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.launcher3.util.Executors.MAIN_EXECUTOR;
-
-import android.graphics.Rect;
-import android.util.ArraySet;
-
-import androidx.annotation.UiThread;
-
-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;
-
-/**
- * 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_EXECUTOR.getHandler(), () -> {
- for (SwipeAnimationListener listener : getListeners()) {
- listener.onRecentsAnimationStart(targetSet);
- }
- });
- }
- }
-
- @Override
- public final void onAnimationCanceled(ThumbnailData thumbnailData) {
- Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
- for (SwipeAnimationListener listener : getListeners()) {
- listener.onRecentsAnimationCanceled();
- }
- });
- // TODO: handle the transition better instead of simply using a transition delay.
- if (thumbnailData != null) {
- MAIN_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/ShelfPeekAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java
index 83bc416..41be683 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java
@@ -30,7 +30,6 @@
/**
* Animates the shelf between states HIDE, PEEK, and OVERVIEW.
*/
-
public class ShelfPeekAnim {
public static final Interpolator INTERPOLATOR = OVERSHOOT_1_2;
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 958ef7d..babf13e 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
@@ -22,8 +22,8 @@
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;
@@ -39,13 +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.anim.SpringAnimationBuilder;
import com.android.launcher3.graphics.OverviewScrim;
import com.android.quickstep.views.RecentsView;
-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.
@@ -64,7 +61,7 @@
private final float mVelocity;
private final float mSpringTransY;
- private final List<Animator> mAnimators = new ArrayList<>();
+ private final AnimatorSet mAnimators = new AnimatorSet();
public StaggeredWorkspaceAnim(Launcher launcher, float velocity, boolean animateOverviewScrim) {
prepareToAnimate(launcher);
@@ -130,16 +127,9 @@
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);
@@ -147,11 +137,7 @@
hotseat.setClipChildren(hotseatClipChildren);
hotseat.setClipToPadding(hotseatClipToPadding);
}
- };
-
- for (Animator a : mAnimators) {
- a.addListener(resetClipListener);
- }
+ });
}
/**
@@ -173,13 +159,7 @@
* 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();
}
/**
@@ -196,17 +176,23 @@
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);
v.setAlpha(0);
ObjectAnimator alpha = ObjectAnimator.ofFloat(v, View.ALPHA, 0f, 1f);
alpha.setInterpolator(LINEAR);
alpha.setDuration(ALPHA_DURATION_MS);
alpha.setStartDelay(startDelay);
- mAnimators.add(alpha);
+ mAnimators.play(alpha);
}
private void addScrimAnimationForState(Launcher launcher, LauncherState state, long duration) {
@@ -215,11 +201,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 3619d3a..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.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.UI_HELPER_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);
- UI_HELPER_EXECUTOR.execute(() -> {
- controller.setInputConsumerEnabled(false);
- controller.finish(toRecents, sendUserLeaveHint);
-
- if (callback != null) {
- MAIN_EXECUTOR.execute(callback);
- }
- });
- }
-
- public void enableInputConsumer() {
- UI_HELPER_EXECUTOR.submit(() -> {
- controller.hideCurrentInputMethod();
- controller.setInputConsumerEnabled(true);
- });
- }
-
- public void setWindowThresholdCrossed(boolean thresholdCrossed) {
- UI_HELPER_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/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index c2cb720..82fbbc6 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
@@ -47,10 +47,11 @@
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;
@@ -100,7 +101,13 @@
@Override
public void startHome() {
- mActivity.getStateManager().goToState(NORMAL);
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ switchToScreenshot(null,
+ () -> finishRecentsAnimation(true /* toRecents */,
+ () -> mActivity.getStateManager().goToState(NORMAL)));
+ } else {
+ mActivity.getStateManager().goToState(NORMAL);
+ }
}
@Override
@@ -137,7 +144,7 @@
*/
@Override
public AnimatorSet createAdjacentPageAnimForTaskLaunch(TaskView tv,
- ClipAnimationHelper helper) {
+ AppWindowAnimationHelper helper) {
AnimatorSet anim = super.createAdjacentPageAnimForTaskLaunch(tv, helper);
if (!SysUINavigationMode.getMode(mActivity).hasGestures) {
@@ -191,12 +198,11 @@
@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);
}
@@ -216,7 +222,7 @@
@Override
public boolean shouldUseMultiWindowTaskSizeStrategy() {
- return mActivity.isInMultiWindowMode();
+ return TraceHelper.whitelistIpcs("isInMultiWindowMode", mActivity::isInMultiWindowMode);
}
@Override
@@ -229,9 +235,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) {
@@ -253,12 +268,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
@@ -312,8 +326,8 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- PluginManagerWrapper.INSTANCE.get(getContext())
- .addPluginListener(mRecentsExtraCardPluginListener, RecentsExtraCard.class);
+ PluginManagerWrapper.INSTANCE.get(getContext()).addPluginListener(
+ mRecentsExtraCardPluginListener, RecentsExtraCard.class);
}
@Override
@@ -364,10 +378,10 @@
}
@Override
- public void resetTaskVisuals() {
- super.resetTaskVisuals();
+ public void setContentAlpha(float alpha) {
+ super.setContentAlpha(alpha);
if (mRecentsExtraViewContainer != null) {
- mRecentsExtraViewContainer.setAlpha(mContentAlpha);
+ mRecentsExtraViewContainer.setAlpha(alpha);
}
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
index a838797..18eda60 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
@@ -16,6 +16,7 @@
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.FloatProperty;
+import android.view.ViewOverlay;
import com.android.launcher3.anim.Interpolators;
@@ -36,6 +37,15 @@
}
};
+ private static LiveTileOverlay sInstance;
+
+ public static LiveTileOverlay getInstance() {
+ if (sInstance == null) {
+ sInstance = new LiveTileOverlay();
+ }
+ return sInstance;
+ }
+
private final Paint mPaint = new Paint();
private Rect mBoundsRect = new Rect();
@@ -46,8 +56,9 @@
private boolean mDrawEnabled = true;
private float mIconAnimationProgress = 0f;
+ private boolean mIsAttached;
- public LiveTileOverlay() {
+ private LiveTileOverlay() {
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
@@ -124,6 +135,23 @@
return PixelFormat.TRANSLUCENT;
}
+ public boolean attach(ViewOverlay overlay) {
+ if (overlay != null && !mIsAttached) {
+ overlay.add(this);
+ mIsAttached = true;
+ return true;
+ }
+
+ return false;
+ }
+
+ public void detach(ViewOverlay overlay) {
+ if (overlay != null) {
+ overlay.remove(this);
+ mIsAttached = false;
+ }
+ }
+
private void setIconAnimationProgress(float progress) {
mIconAnimationProgress = progress;
invalidateSelf();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 6ad3cc6..6e7214e 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
@@ -60,6 +60,7 @@
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
+import android.os.UserHandle;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
@@ -102,12 +103,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.RecentsModel.TaskVisualsChangeListener;
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;
@@ -125,7 +128,7 @@
@TargetApi(Build.VERSION_CODES.P)
public abstract class RecentsView<T extends BaseActivity> extends PagedView implements Insettable,
TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
- InvariantDeviceProfile.OnIDPChangeListener, TaskThumbnailChangeListener {
+ InvariantDeviceProfile.OnIDPChangeListener, TaskVisualsChangeListener {
private static final String TAG = RecentsView.class.getSimpleName();
@@ -155,8 +158,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;
@@ -176,7 +180,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
@@ -304,7 +308,7 @@
private final int mEmptyMessagePadding;
private boolean mShowEmptyMessage;
private Layout mEmptyTextLayout;
- private LiveTileOverlay mLiveTileOverlay;
+ private boolean mLiveTileOverlayAttached;
// Keeps track of the index where the first TaskView should be
private int mTaskViewStartIndex = 0;
@@ -327,7 +331,7 @@
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);
@@ -380,14 +384,38 @@
return null;
}
- public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData) {
+ @Override
+ public void onTaskIconChanged(String pkg, UserHandle user) {
+ for (int i = 0; i < getTaskViewCount(); i++) {
+ TaskView tv = getTaskViewAt(i);
+ Task task = tv.getTask();
+ if (task != null && task.key != null && pkg.equals(task.key.getPackageName())
+ && task.key.userId == user.getIdentifier()) {
+ task.icon = null;
+ if (tv.getIconView().getDrawable() != null) {
+ tv.onTaskListVisibilityChanged(true /* visible */);
+ }
+ }
+ }
+ }
+
+ /**
+ * Update the thumbnail of the task.
+ * @param refreshNow Refresh immediately if it's true.
+ */
+ 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);
@@ -457,6 +485,11 @@
public void setOverviewStateEnabled(boolean enabled) {
mOverviewStateEnabled = enabled;
updateTaskStackListenerState();
+ if (!enabled) {
+ // Reset the running task when leaving overview since it can still have a reference to
+ // its thumbnail
+ mTmpRunningTask = null;
+ }
}
public void onDigitalWellbeingToastShown() {
@@ -798,8 +831,9 @@
mIgnoreResetTaskId = -1;
mTaskListChangeId = -1;
- mRecentsAnimationWrapper = null;
- mClipAnimationHelper = null;
+ mRecentsAnimationController = null;
+ mRecentsAnimationTargets = null;
+ mAppWindowAnimationHelper = null;
unloadVisibleTaskData();
setCurrentPage(0);
@@ -847,8 +881,8 @@
*/
public void onSwipeUpAnimationSuccess() {
if (getRunningTaskView() != null) {
- float startProgress = ENABLE_QUICKSTEP_LIVE_TILE.get() && mLiveTileOverlay != null
- ? mLiveTileOverlay.cancelIconAnimation()
+ float startProgress = ENABLE_QUICKSTEP_LIVE_TILE.get() && mLiveTileOverlayAttached
+ ? LiveTileOverlay.getInstance().cancelIconAnimation()
: 0f;
animateUpRunningTaskIconScale(startProgress);
}
@@ -862,7 +896,9 @@
setEnableFreeScroll(true);
setEnableDrawingLiveTile(true);
setOnScrollChangeListener(null);
- setRunningTaskViewShowScreenshot(true);
+ if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ setRunningTaskViewShowScreenshot(true);
+ }
setRunningTaskHidden(false);
animateUpRunningTaskIconScale();
}
@@ -1508,14 +1544,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;
@@ -1575,10 +1611,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);
@@ -1658,8 +1694,8 @@
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
final int[] visibleTasks = getVisibleChildrenRange();
- event.setFromIndex(taskViewCount - visibleTasks[1] - 1);
- event.setToIndex(taskViewCount - visibleTasks[0] - 1);
+ event.setFromIndex(taskViewCount - visibleTasks[1]);
+ event.setToIndex(taskViewCount - visibleTasks[0]);
event.setItemCount(taskViewCount);
}
}
@@ -1681,33 +1717,47 @@
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) {
- mLiveTileOverlay = liveTileOverlay;
+ public void setLiveTileOverlayAttached(boolean liveTileOverlayAttached) {
+ mLiveTileOverlayAttached = liveTileOverlayAttached;
}
public void updateLiveTileIcon(Drawable icon) {
- if (mLiveTileOverlay != null) {
- mLiveTileOverlay.setIcon(icon);
+ if (mLiveTileOverlayAttached) {
+ LiveTileOverlay.getInstance().setIcon(icon);
}
}
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, () -> {
+ if (onFinishComplete != null) {
+ onFinishComplete.run();
+ // After we finish the recents animation, the current task id should be correctly
+ // reset so that when the task is launched from Overview later, it goes through the
+ // flow of starting a new task instead of finishing recents animation to app. A
+ // typical example of this is (1) user swipes up from app to Overview (2) user
+ // taps on QSB (3) user goes back to Overview and launch the most recent task.
+ setCurrentTask(-1);
+ }
+ });
}
public void setDisallowScrollToClearAll(boolean disallowScrollToClearAll) {
@@ -1794,15 +1844,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++) {
- getTaskViewAt(i).setOverlayEnabled(i == overlayEnabledPage);
+ for (int i = mTaskViewStartIndex; i < mTaskViewStartIndex + taskCount; i++) {
+ getTaskViewAtByAbsoluteIndex(i).setOverlayEnabled(i == overlayEnabledPage);
}
}
@@ -1823,6 +1882,22 @@
return Math.max(insets.getSystemGestureInsets().right, insets.getSystemWindowInsetRight());
}
+ /** If it's in the live tile mode, switch the running task into screenshot mode. */
+ public void switchToScreenshot(ThumbnailData thumbnailData, Runnable onFinishRunnable) {
+ TaskView taskView = getRunningTaskView();
+ if (taskView != null) {
+ taskView.setShowScreenshot(true);
+ if (thumbnailData != null) {
+ taskView.getThumbnail().setThumbnail(taskView.getTask(), thumbnailData);
+ } else {
+ taskView.getThumbnail().refresh();
+ }
+ ViewUtils.postDraw(taskView, onFinishRunnable);
+ } else {
+ onFinishRunnable.run();
+ }
+ }
+
@Override
public void addView(View child, int index) {
super.addView(child, index);
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 07d0796..80022b4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
@@ -16,7 +16,6 @@
package com.android.quickstep.views;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
import android.animation.Animator;
@@ -26,7 +25,6 @@
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;
@@ -41,16 +39,13 @@
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.popup.SystemShortcut;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.TaskOverlayFactory;
-import com.android.quickstep.TaskSystemShortcut;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.views.IconView.OnScaleUpdateListener;
-import java.util.List;
-
/**
* Contains options for a recent task when long-pressing its icon.
*/
@@ -197,22 +192,15 @@
params.topMargin = (int) -mThumbnailTopMargin;
mTaskIcon.setLayoutParams(params);
- final BaseDraggingActivity activity = BaseDraggingActivity.fromContext(getContext());
- final List<TaskSystemShortcut> shortcuts =
- TaskOverlayFactory.INSTANCE.get(getContext()).getEnabledShortcuts(taskView);
- final int count = shortcuts.size();
- for (int i = 0; i < count; ++i) {
- final TaskSystemShortcut menuOption = shortcuts.get(i);
- addMenuOption(menuOption, menuOption.getOnClickListener(activity, taskView));
- }
+ TaskOverlayFactory.getEnabledShortcuts(taskView).forEach(this::addMenuOption);
}
- private void addMenuOption(TaskSystemShortcut menuOption, OnClickListener onClickListener) {
+ private void addMenuOption(SystemShortcut menuOption) {
ViewGroup menuOptionView = (ViewGroup) mActivity.getLayoutInflater().inflate(
R.layout.task_view_menu_option, this, false);
menuOption.setIconAndLabelFor(
menuOptionView.findViewById(R.id.icon), menuOptionView.findViewById(R.id.text));
- menuOptionView.setOnClickListener(onClickListener);
+ menuOptionView.setOnClickListener(menuOption);
mOptionLayout.addView(menuOptionView);
}
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 044292a..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
@@ -45,7 +45,6 @@
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;
@@ -135,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;
@@ -152,7 +170,6 @@
mPaint.setShader(null);
mOverlay.reset();
}
-
if (mOverviewScreenshotActionsPlugin != null) {
mOverviewScreenshotActionsPlugin
.setupActions((ViewGroup) getTaskView(), getThumbnail(), mActivity);
@@ -345,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/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 51802df..a1775f4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -53,7 +53,7 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -62,7 +62,6 @@
import com.android.quickstep.RecentsModel;
import com.android.quickstep.TaskIconCache;
import com.android.quickstep.TaskOverlayFactory;
-import com.android.quickstep.TaskSystemShortcut;
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.util.TaskCornerRadius;
@@ -288,11 +287,19 @@
public void launchTask(boolean animate, boolean freezeTaskList, Consumer<Boolean> resultCallback,
Handler resultCallbackHandler) {
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ RecentsView recentsView = getRecentsView();
if (isRunningTask()) {
- getRecentsView().finishRecentsAnimation(false /* toRecents */,
+ recentsView.finishRecentsAnimation(false /* toRecents */,
() -> resultCallbackHandler.post(() -> resultCallback.accept(true)));
} else {
- launchTaskInternal(animate, freezeTaskList, resultCallback, resultCallbackHandler);
+ // This is a workaround against the WM issue that app open is not correctly animated
+ // when recents animation is being cleaned up (b/143774568). When that's possible,
+ // we should rely on the framework side to cancel the recents animation, and we will
+ // clean up the screenshot on the launcher side while we launch the next task.
+ recentsView.switchToScreenshot(null,
+ () -> recentsView.finishRecentsAnimation(true /* toRecents */,
+ () -> launchTaskInternal(animate, freezeTaskList, resultCallback,
+ resultCallbackHandler)));
}
} else {
launchTaskInternal(animate, freezeTaskList, resultCallback, resultCallbackHandler);
@@ -714,15 +721,8 @@
getContext().getText(R.string.accessibility_close_task)));
final Context context = getContext();
- final List<TaskSystemShortcut> shortcuts =
- TaskOverlayFactory.INSTANCE.get(getContext()).getEnabledShortcuts(this);
- final int count = shortcuts.size();
- for (int i = 0; i < count; ++i) {
- final TaskSystemShortcut menuOption = shortcuts.get(i);
- OnClickListener onClickListener = menuOption.getOnClickListener(mActivity, this);
- if (onClickListener != null) {
- info.addAction(menuOption.createAccessibilityAction(context));
- }
+ for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this)) {
+ info.addAction(s.createAccessibilityAction(context));
}
if (mDigitalWellBeingToast.hasLimit()) {
@@ -735,8 +735,8 @@
final RecentsView recentsView = getRecentsView();
final AccessibilityNodeInfo.CollectionItemInfo itemInfo =
AccessibilityNodeInfo.CollectionItemInfo.obtain(
- 0, 1, recentsView.getChildCount() - recentsView.indexOfChild(this) - 1, 1,
- false);
+ 0, 1, recentsView.getTaskViewCount() - recentsView.indexOfChild(this) - 1,
+ 1, false);
info.setCollectionItemInfo(itemInfo);
}
@@ -753,16 +753,9 @@
return true;
}
- final List<TaskSystemShortcut> shortcuts =
- TaskOverlayFactory.INSTANCE.get(getContext()).getEnabledShortcuts(this);
- final int count = shortcuts.size();
- for (int i = 0; i < count; ++i) {
- final TaskSystemShortcut menuOption = shortcuts.get(i);
- if (menuOption.hasHandlerForAction(action)) {
- OnClickListener onClickListener = menuOption.getOnClickListener(mActivity, this);
- if (onClickListener != null) {
- onClickListener.onClick(this);
- }
+ for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this)) {
+ if (s.hasHandlerForAction(action)) {
+ s.onClick(this);
return true;
}
}
diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml
deleted file mode 100644
index 2d1418e..0000000
--- a/quickstep/res/values-en-rCA/strings.xml
+++ /dev/null
@@ -1,37 +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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Split screen"</string>
- <string name="recent_task_option_pin" msgid="7929860679018978258">"Pin"</string>
- <string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
- <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string>
- <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
- <string name="accessibility_close_task" msgid="5354563209433803643">"Close"</string>
- <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string>
- <string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
- <string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
- <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
- <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 minute"</string>
- <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> left today"</string>
- <string name="title_app_suggestions" msgid="4185902664111965088">"App suggestions"</string>
- <string name="all_apps_label" msgid="8542784161730910663">"All apps"</string>
- <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Your predicted apps"</string>
-</resources>
diff --git a/quickstep/res/values-en-rXC/strings.xml b/quickstep/res/values-en-rXC/strings.xml
deleted file mode 100644
index bb186db..0000000
--- a/quickstep/res/values-en-rXC/strings.xml
+++ /dev/null
@@ -1,37 +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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Split screen"</string>
- <string name="recent_task_option_pin" msgid="7929860679018978258">"Pin"</string>
- <string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
- <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string>
- <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
- <string name="accessibility_close_task" msgid="5354563209433803643">"Close"</string>
- <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string>
- <string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
- <string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
- <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
- <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 minute"</string>
- <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> left today"</string>
- <string name="title_app_suggestions" msgid="4185902664111965088">"App suggestions"</string>
- <string name="all_apps_label" msgid="8542784161730910663">"All apps"</string>
- <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Your predicted apps"</string>
-</resources>
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 292eaaa..327bb14 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>
@@ -33,4 +31,6 @@
<!-- Assistant Gesture -->
<integer name="assistant_gesture_min_time_threshold">200</integer>
<integer name="assistant_gesture_corner_deg_threshold">20</integer>
+
+ <string name="wellbeing_provider_pkg" translatable="false"></string>
</resources>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
new file mode 100644
index 0000000..9ea13c6
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.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.launcher3;
+
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.allapps.DiscoveryBounce.BOUNCE_MAX_COUNT;
+import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_COUNT;
+import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
+import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_COUNT;
+import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
+
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+
+import com.android.launcher3.LauncherState.ScaleAndTranslation;
+import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.model.WellbeingModel;
+import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.proxy.ProxyActivityStarter;
+import com.android.launcher3.proxy.StartActivityParams;
+import com.android.launcher3.uioverrides.BackButtonAlphaHandler;
+import com.android.launcher3.uioverrides.RecentsViewStateController;
+import com.android.launcher3.util.UiThreadHelper;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.RemoteFadeOutAnimationListener;
+import com.android.quickstep.util.ShelfPeekAnim;
+
+import java.util.stream.Stream;
+
+/**
+ * Extension of Launcher activity to provide quickstep specific functionality
+ */
+public abstract class BaseQuickstepLauncher extends Launcher
+ implements NavigationModeChangeListener {
+
+ /**
+ * Reusable command for applying the back button alpha on the background thread.
+ */
+ public static final UiThreadHelper.AsyncCommand SET_BACK_BUTTON_ALPHA =
+ (context, arg1, arg2) -> SystemUiProxy.INSTANCE.get(context).setBackButtonAlpha(
+ Float.intBitsToFloat(arg1), arg2 != 0);
+
+ private final ShelfPeekAnim mShelfPeekAnim = new ShelfPeekAnim(this);
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ SysUINavigationMode.Mode mode = SysUINavigationMode.INSTANCE.get(this)
+ .addModeChangeListener(this);
+ getRotationHelper().setRotationHadDifferentUI(mode != Mode.NO_BUTTON);
+
+ if (!getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
+ getStateManager().addStateListener(new LauncherStateManager.StateListener() {
+ @Override
+ public void onStateTransitionStart(LauncherState toState) { }
+
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ boolean swipeUpEnabled = SysUINavigationMode.INSTANCE
+ .get(BaseQuickstepLauncher.this).getMode().hasGestures;
+ LauncherState prevState = getStateManager().getLastState();
+
+ if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
+ && finalState == ALL_APPS && prevState == NORMAL) || BOUNCE_MAX_COUNT
+ <= getSharedPrefs().getInt(HOME_BOUNCE_COUNT, 0))) {
+ getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
+ getStateManager().removeStateListener(this);
+ }
+ }
+ });
+ }
+
+ if (!getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)) {
+ getStateManager().addStateListener(new LauncherStateManager.StateListener() {
+ @Override
+ public void onStateTransitionStart(LauncherState toState) { }
+
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ LauncherState prevState = getStateManager().getLastState();
+
+ if ((finalState == ALL_APPS && prevState == OVERVIEW) || BOUNCE_MAX_COUNT
+ <= getSharedPrefs().getInt(SHELF_BOUNCE_COUNT, 0)) {
+ getSharedPrefs().edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
+ getStateManager().removeStateListener(this);
+ }
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
+ super.onDestroy();
+ }
+
+ @Override
+ public void onNavigationModeChanged(Mode newMode) {
+ getDragLayer().recreateControllers();
+ getRotationHelper().setRotationHadDifferentUI(newMode != Mode.NO_BUTTON);
+ }
+
+ @Override
+ public void onEnterAnimationComplete() {
+ super.onEnterAnimationComplete();
+ // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
+ // as a part of quickstep, so that high-res thumbnails can load the next time we enter
+ // overview
+ RecentsModel.INSTANCE.get(this).getThumbnailCache()
+ .getHighResLoadingState().setVisible(true);
+ }
+
+ @Override
+ public void onTrimMemory(int level) {
+ super.onTrimMemory(level);
+ RecentsModel.INSTANCE.get(this).onTrimMemory(level);
+ }
+
+ @Override
+ public void startIntentSenderForResult(IntentSender intent, int requestCode,
+ Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) {
+ if (requestCode != -1) {
+ mPendingActivityRequestCode = requestCode;
+ StartActivityParams params = new StartActivityParams(this, requestCode);
+ params.intentSender = intent;
+ params.fillInIntent = fillInIntent;
+ params.flagsMask = flagsMask;
+ params.flagsValues = flagsValues;
+ params.extraFlags = extraFlags;
+ params.options = options;
+ startActivity(ProxyActivityStarter.getLaunchIntent(this, params));
+ } else {
+ super.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask,
+ flagsValues, extraFlags, options);
+ }
+ }
+
+ @Override
+ public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
+ if (requestCode != -1) {
+ mPendingActivityRequestCode = -1;
+ StartActivityParams params = new StartActivityParams(this, requestCode);
+ params.intent = intent;
+ params.options = options;
+ startActivity(ProxyActivityStarter.getLaunchIntent(this, params));
+ } else {
+ super.startActivityForResult(intent, requestCode, options);
+ }
+ }
+
+ @Override
+ protected void onDeferredResumed() {
+ if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
+ // Remove any active ProxyActivityStarter task and send RESULT_CANCELED to Launcher.
+ onActivityResult(mPendingActivityRequestCode, RESULT_CANCELED, null);
+ // ProxyActivityStarter is started with clear task to reset the task after which it
+ // removes the task itself.
+ startActivity(ProxyActivityStarter.getLaunchIntent(this, null));
+ }
+ }
+
+ @Override
+ protected StateHandler[] createStateHandlers() {
+ return new StateHandler[] {
+ getAllAppsController(),
+ getWorkspace(),
+ new RecentsViewStateController(this),
+ new BackButtonAlphaHandler(this)};
+ }
+
+ @Override
+ protected ScaleAndTranslation getOverviewScaleAndTranslationForNormalState() {
+ if (SysUINavigationMode.getMode(this) == Mode.NO_BUTTON) {
+ float offscreenTranslationX = getDeviceProfile().widthPx
+ - getOverviewPanel().getPaddingStart();
+ return new ScaleAndTranslation(1f, offscreenTranslationX, 0f);
+ }
+ return super.getOverviewScaleAndTranslationForNormalState();
+ }
+
+ @Override
+ public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) {
+ QuickstepAppTransitionManagerImpl appTransitionManager =
+ (QuickstepAppTransitionManagerImpl) getAppTransitionManager();
+ appTransitionManager.setRemoteAnimationProvider((appTargets, wallpaperTargets) -> {
+
+ // On the first call clear the reference.
+ signal.cancel();
+
+ ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
+ fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(appTargets,
+ wallpaperTargets));
+ AnimatorSet anim = new AnimatorSet();
+ anim.play(fadeAnimation);
+ return anim;
+ }, signal);
+ }
+
+ @Override
+ public void onDragLayerHierarchyChanged() {
+ onLauncherStateOrFocusChanged();
+ }
+
+ @Override
+ protected void onActivityFlagsChanged(int changeBits) {
+ if ((changeBits
+ & (ACTIVITY_STATE_WINDOW_FOCUSED | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0) {
+ onLauncherStateOrFocusChanged();
+ }
+
+ super.onActivityFlagsChanged(changeBits);
+ }
+
+ /**
+ * Sets the back button visibility based on the current state/window focus.
+ */
+ private void onLauncherStateOrFocusChanged() {
+ Mode mode = SysUINavigationMode.getMode(this);
+ boolean shouldBackButtonBeHidden = mode.hasGestures
+ && getStateManager().getState().hideBackButton
+ && hasWindowFocus()
+ && (getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0;
+ if (shouldBackButtonBeHidden) {
+ // Show the back button if there is a floating view visible.
+ shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenViewWithType(this,
+ TYPE_ALL & ~TYPE_HIDE_BACK_BUTTON) == null;
+ }
+ UiThreadHelper.setBackButtonAlphaAsync(this, SET_BACK_BUTTON_ALPHA,
+ shouldBackButtonBeHidden ? 0f : 1f, true /* animate */);
+ if (getDragLayer() != null) {
+ getRootView().setDisallowBackGesture(shouldBackButtonBeHidden);
+ }
+ }
+
+ @Override
+ public void finishBindingItems(int pageBoundFirst) {
+ super.finishBindingItems(pageBoundFirst);
+ // Instantiate and initialize WellbeingModel now that its loading won't interfere with
+ // populating workspace.
+ // TODO: Find a better place for this
+ WellbeingModel.get(this);
+ }
+
+ @Override
+ public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
+ return Stream.concat(super.getSupportedShortcuts(),
+ Stream.of(WellbeingModel.SHORTCUT_FACTORY));
+ }
+
+ public ShelfPeekAnim getShelfPeekAnim() {
+ return mShelfPeekAnim;
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index a8e2956..96ac489 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() {
diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java
index b9ce1ce..96340b2 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -19,30 +19,31 @@
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.launcher3.util.ActivityTracker;
+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;
+ /**
+ * @param onInitListener a callback made when the activity is initialized. The callback should
+ * return true to continue receiving callbacks (ie. for if the activity is
+ * recreated).
+ */
public LauncherInitListener(BiPredicate<Launcher, Boolean> onInitListener) {
- 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 +51,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 +59,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 a91410c..d4db05a 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -71,8 +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.util.ShelfPeekAnim;
+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;
@@ -151,8 +150,6 @@
private RemoteAnimationProvider mRemoteAnimationProvider;
- private final ShelfPeekAnim mShelfPeekAnim;
-
private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
@@ -180,12 +177,6 @@
mLauncher.addOnDeviceProfileChangeListener(this);
registerRemoteAnimations();
-
- mShelfPeekAnim = new ShelfPeekAnim(mLauncher);
- }
-
- public ShelfPeekAnim getShelfPeekAnim() {
- return mShelfPeekAnim;
}
@Override
@@ -211,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) {
@@ -264,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 =
@@ -314,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);
@@ -427,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,
@@ -435,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);
@@ -560,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;
@@ -628,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);
@@ -638,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);
@@ -654,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();
@@ -670,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) {
@@ -773,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;
}
@@ -791,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
@@ -808,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/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
new file mode 100644
index 0000000..5aa4388
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.model;
+
+import static android.content.ContentResolver.SCHEME_CONTENT;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.createAndStartNewLooper;
+
+import android.annotation.TargetApi;
+import android.app.RemoteAction;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.LauncherApps;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Process;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import androidx.annotation.MainThread;
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.popup.RemoteActionShortcut;
+import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.SimpleBroadcastReceiver;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Data model for digital wellbeing status of apps.
+ */
+@TargetApi(Build.VERSION_CODES.Q)
+public final class WellbeingModel {
+ private static final String TAG = "WellbeingModel";
+ private static final int[] RETRY_TIMES_MS = {5000, 15000, 30000};
+ private static final boolean DEBUG = false;
+
+ private static final int MSG_PACKAGE_ADDED = 1;
+ private static final int MSG_PACKAGE_REMOVED = 2;
+ private static final int MSG_FULL_REFRESH = 3;
+
+ // Welbeing contract
+ private static final String METHOD_GET_ACTIONS = "get_actions";
+ private static final String EXTRA_ACTIONS = "actions";
+ private static final String EXTRA_ACTION = "action";
+ private static final String EXTRA_MAX_NUM_ACTIONS_SHOWN = "max_num_actions_shown";
+ private static final String EXTRA_PACKAGES = "packages";
+
+ private static WellbeingModel sInstance;
+
+ private final Context mContext;
+ private final String mWellbeingProviderPkg;
+ private final Handler mWorkerHandler;
+
+ private final ContentObserver mContentObserver;
+
+ private final Object mModelLock = new Object();
+ // Maps the action Id to the corresponding RemoteAction
+ private final Map<String, RemoteAction> mActionIdMap = new ArrayMap<>();
+ private final Map<String, String> mPackageToActionId = new HashMap<>();
+
+ private boolean mIsInTest;
+
+ private WellbeingModel(final Context context) {
+ mContext = context;
+ mWorkerHandler =
+ new Handler(createAndStartNewLooper("WellbeingHandler"), this::handleMessage);
+
+ mWellbeingProviderPkg = mContext.getString(R.string.wellbeing_provider_pkg);
+ mContentObserver = new ContentObserver(MAIN_EXECUTOR.getHandler()) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ // Wellbeing reports that app actions have changed.
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG, "ContentObserver.onChange() called with: selfChange = [" + selfChange
+ + "], uri = [" + uri + "]");
+ }
+ Preconditions.assertUIThread();
+ updateWellbeingData();
+ }
+ };
+
+ if (!TextUtils.isEmpty(mWellbeingProviderPkg)) {
+ context.registerReceiver(
+ new SimpleBroadcastReceiver(this::onWellbeingProviderChanged),
+ PackageManagerHelper.getPackageFilter(mWellbeingProviderPkg,
+ Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_CHANGED,
+ Intent.ACTION_PACKAGE_REMOVED, Intent.ACTION_PACKAGE_DATA_CLEARED,
+ Intent.ACTION_PACKAGE_RESTARTED));
+
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ context.registerReceiver(new SimpleBroadcastReceiver(this::onAppPackageChanged),
+ filter);
+
+ restartObserver();
+ }
+ }
+
+ public void setInTest(boolean inTest) {
+ mIsInTest = inTest;
+ }
+
+ protected void onWellbeingProviderChanged(Intent intent) {
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG, "Changes to Wellbeing package: intent = [" + intent + "]");
+ }
+ restartObserver();
+ }
+
+ private void restartObserver() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ resolver.unregisterContentObserver(mContentObserver);
+ Uri actionsUri = apiBuilder().path("actions").build();
+ try {
+ resolver.registerContentObserver(
+ actionsUri, true /* notifyForDescendants */, mContentObserver);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to register content observer for " + actionsUri + ": " + e);
+ if (mIsInTest) throw new RuntimeException(e);
+ }
+ updateWellbeingData();
+ }
+
+ @MainThread
+ public static WellbeingModel get(@NonNull Context context) {
+ Preconditions.assertUIThread();
+ if (sInstance == null) {
+ sInstance = new WellbeingModel(context.getApplicationContext());
+ }
+ return sInstance;
+ }
+
+ @MainThread
+ private SystemShortcut getShortcutForApp(String packageName, int userId,
+ BaseDraggingActivity activity, ItemInfo info) {
+ Preconditions.assertUIThread();
+ // Work profile apps are not recognized by digital wellbeing.
+ if (userId != UserHandle.myUserId()) {
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG, "getShortcutForApp [" + packageName + "]: not current user");
+ }
+ return null;
+ }
+
+ synchronized (mModelLock) {
+ String actionId = mPackageToActionId.get(packageName);
+ final RemoteAction action = actionId != null ? mActionIdMap.get(actionId) : null;
+ if (action == null) {
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG, "getShortcutForApp [" + packageName + "]: no action");
+ }
+ return null;
+ }
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG,
+ "getShortcutForApp [" + packageName + "]: action: '" + action.getTitle()
+ + "'");
+ }
+ return new RemoteActionShortcut(action, activity, info);
+ }
+ }
+
+ private void updateWellbeingData() {
+ mWorkerHandler.sendEmptyMessage(MSG_FULL_REFRESH);
+ }
+
+ private Uri.Builder apiBuilder() {
+ return new Uri.Builder()
+ .scheme(SCHEME_CONTENT)
+ .authority(mWellbeingProviderPkg + ".api");
+ }
+
+ private boolean updateActions(String... packageNames) {
+ if (packageNames.length == 0) {
+ return true;
+ }
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG, "retrieveActions() called with: packageNames = [" + String.join(", ",
+ packageNames) + "]");
+ }
+ Preconditions.assertNonUiThread();
+
+ Uri contentUri = apiBuilder().build();
+ final Bundle remoteActionBundle;
+ try (ContentProviderClient client = mContext.getContentResolver()
+ .acquireUnstableContentProviderClient(contentUri)) {
+ if (client == null) {
+ if (DEBUG || mIsInTest) Log.i(TAG, "retrieveActions(): null provider");
+ return false;
+ }
+
+ // Prepare wellbeing call parameters.
+ final Bundle params = new Bundle();
+ params.putStringArray(EXTRA_PACKAGES, packageNames);
+ params.putInt(EXTRA_MAX_NUM_ACTIONS_SHOWN, 1);
+ // Perform wellbeing call .
+ remoteActionBundle = client.call(METHOD_GET_ACTIONS, null, params);
+ } catch (DeadObjectException e) {
+ Log.i(TAG, "retrieveActions(): DeadObjectException");
+ return false;
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to retrieve data from " + contentUri + ": " + e);
+ if (mIsInTest) throw new RuntimeException(e);
+ return true;
+ }
+
+ synchronized (mModelLock) {
+ // Remove the entries for requested packages, and then update the fist with what we
+ // got from service
+ Arrays.stream(packageNames).forEach(mPackageToActionId::remove);
+
+ // The result consists of sub-bundles, each one is per a remote action. Each sub-bundle
+ // has a RemoteAction and a list of packages to which the action applies.
+ for (String actionId :
+ remoteActionBundle.getStringArray(EXTRA_ACTIONS)) {
+ final Bundle actionBundle = remoteActionBundle.getBundle(actionId);
+ mActionIdMap.put(actionId,
+ actionBundle.getParcelable(EXTRA_ACTION));
+
+ final String[] packagesForAction =
+ actionBundle.getStringArray(EXTRA_PACKAGES);
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG, "....actionId: " + actionId + ", packages: " + String.join(", ",
+ packagesForAction));
+ }
+ for (String packageName : packagesForAction) {
+ mPackageToActionId.put(packageName, actionId);
+ }
+ }
+ }
+ if (DEBUG || mIsInTest) Log.i(TAG, "retrieveActions(): finished");
+ return true;
+ }
+
+ private boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_PACKAGE_REMOVED: {
+ String packageName = (String) msg.obj;
+ mWorkerHandler.removeCallbacksAndMessages(packageName);
+ synchronized (mModelLock) {
+ mPackageToActionId.remove(packageName);
+ }
+ return true;
+ }
+ case MSG_PACKAGE_ADDED: {
+ String packageName = (String) msg.obj;
+ mWorkerHandler.removeCallbacksAndMessages(packageName);
+ if (!updateActions(packageName)) {
+ scheduleRefreshRetry(msg);
+ }
+ return true;
+ }
+
+ case MSG_FULL_REFRESH: {
+ // Remove all existing messages
+ mWorkerHandler.removeCallbacksAndMessages(null);
+ final String[] packageNames = mContext.getSystemService(LauncherApps.class)
+ .getActivityList(null, Process.myUserHandle()).stream()
+ .map(li -> li.getApplicationInfo().packageName).distinct()
+ .toArray(String[]::new);
+ if (!updateActions(packageNames)) {
+ scheduleRefreshRetry(msg);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void scheduleRefreshRetry(Message originalMsg) {
+ int retryCount = originalMsg.arg1;
+ if (retryCount >= RETRY_TIMES_MS.length) {
+ // To many retries, skip
+ return;
+ }
+
+ Message msg = Message.obtain(originalMsg);
+ msg.arg1 = retryCount + 1;
+ mWorkerHandler.sendMessageDelayed(msg, RETRY_TIMES_MS[retryCount]);
+ }
+
+ private void onAppPackageChanged(Intent intent) {
+ if (DEBUG || mIsInTest) Log.d(TAG, "Changes in apps: intent = [" + intent + "]");
+ Preconditions.assertUIThread();
+
+ final String packageName = intent.getData().getSchemeSpecificPart();
+ if (packageName == null || packageName.length() == 0) {
+ // they sent us a bad intent
+ return;
+ }
+
+ final String action = intent.getAction();
+ if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+ Message.obtain(mWorkerHandler, MSG_PACKAGE_REMOVED, packageName).sendToTarget();
+ } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+ Message.obtain(mWorkerHandler, MSG_PACKAGE_ADDED, packageName).sendToTarget();
+ }
+ }
+
+ /**
+ * Shortcut factory for generating wellbeing action
+ */
+ public static final SystemShortcut.Factory SHORTCUT_FACTORY = (activity, info) ->
+ (info.getTargetComponent() == null) ? null : WellbeingModel.get(activity)
+ .getShortcutForApp(
+ info.getTargetComponent().getPackageName(), info.user.getIdentifier(),
+ activity, info);
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
new file mode 100644
index 0000000..965b5f0
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides;
+
+import android.app.Activity;
+import android.app.Person;
+import android.content.pm.ShortcutInfo;
+import android.util.Base64;
+
+import com.android.launcher3.Utilities;
+import com.android.systemui.shared.system.ActivityCompat;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.util.zip.Deflater;
+
+public class ApiWrapper {
+
+ public static boolean dumpActivity(Activity activity, PrintWriter writer) {
+ if (!Utilities.IS_DEBUG_DEVICE) {
+ return false;
+ }
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ if (!(new ActivityCompat(activity).encodeViewHierarchy(out))) {
+ return false;
+ }
+
+ Deflater deflater = new Deflater();
+ deflater.setInput(out.toByteArray());
+ deflater.finish();
+
+ out.reset();
+ byte[] buffer = new byte[1024];
+ while (!deflater.finished()) {
+ int count = deflater.deflate(buffer); // returns the generated code... index
+ out.write(buffer, 0, count);
+ }
+
+ writer.println("--encoded-view-dump-v0--");
+ writer.println(Base64.encodeToString(
+ out.toByteArray(), Base64.NO_WRAP | Base64.NO_PADDING));
+ return true;
+ }
+
+ public static Person[] getPersons(ShortcutInfo si) {
+ Person[] persons = si.getPersons();
+ return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java b/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java
index 693ae60..43dc882 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java
@@ -16,32 +16,26 @@
package com.android.launcher3.uioverrides;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
-import com.android.launcher3.Launcher;
+import com.android.launcher3.BaseQuickstepLauncher;
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 BaseQuickstepLauncher mLauncher;
- private final Launcher mLauncher;
- private final OverviewInteractionState mOverviewInteractionState;
-
- public BackButtonAlphaHandler(Launcher launcher) {
+ public BackButtonAlphaHandler(BaseQuickstepLauncher launcher) {
mLauncher = launcher;
- mOverviewInteractionState = OverviewInteractionState.INSTANCE.get(mLauncher);
}
@Override
- public void setState(LauncherState state) {
- UiFactory.onLauncherStateOrFocusChanged(mLauncher);
- }
+ public void setState(LauncherState state) { }
@Override
public void setStateWithAnimation(LauncherState toState,
@@ -49,21 +43,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,
+ BaseQuickstepLauncher.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);
- });
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Reapply the final alpha in case some state (e.g. window focus) changed.
- UiFactory.onLauncherStateOrFocusChanged(mLauncher);
- }
+ UiThreadHelper.setBackButtonAlphaAsync(mLauncher,
+ BaseQuickstepLauncher.SET_BACK_BUTTON_ALPHA, alpha, false /* animate */);
});
builder.play(anim);
}
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
deleted file mode 100644
index c02df93..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.uioverrides;
-
-import static android.app.Activity.RESULT_CANCELED;
-
-import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
-import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
-import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.allapps.DiscoveryBounce.BOUNCE_MAX_COUNT;
-import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_COUNT;
-import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
-import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_COUNT;
-import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
-
-import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
-import android.app.Activity;
-import android.app.Person;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.content.pm.ShortcutInfo;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.util.Base64;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherState.ScaleAndTranslation;
-import com.android.launcher3.LauncherStateManager;
-import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.QuickstepAppTransitionManagerImpl;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.proxy.ProxyActivityStarter;
-import com.android.launcher3.proxy.StartActivityParams;
-import com.android.quickstep.OverviewInteractionState;
-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.util.RemoteFadeOutAnimationListener;
-import com.android.systemui.shared.system.ActivityCompat;
-
-import java.io.ByteArrayOutputStream;
-import java.io.PrintWriter;
-import java.util.zip.Deflater;
-
-public class UiFactory extends RecentsUiFactory {
-
- public static Runnable enableLiveUIChanges(Launcher launcher) {
- NavigationModeChangeListener listener = m -> {
- launcher.getDragLayer().recreateControllers();
- launcher.getRotationHelper().setRotationHadDifferentUI(m != Mode.NO_BUTTON);
- };
- SysUINavigationMode mode = SysUINavigationMode.INSTANCE.get(launcher);
- SysUINavigationMode.Mode m = mode.addModeChangeListener(listener);
- launcher.getRotationHelper().setRotationHadDifferentUI(m != Mode.NO_BUTTON);
- return () -> mode.removeModeChangeListener(listener);
- }
-
- public static StateHandler[] getStateHandler(Launcher launcher) {
- return new StateHandler[] {
- launcher.getAllAppsController(),
- launcher.getWorkspace(),
- createRecentsViewStateController(launcher),
- new BackButtonAlphaHandler(launcher)};
- }
-
- /**
- * Sets the back button visibility based on the current state/window focus.
- */
- public static void onLauncherStateOrFocusChanged(Launcher launcher) {
- boolean shouldBackButtonBeHidden = launcher != null
- && launcher.getStateManager().getState().hideBackButton
- && launcher.hasWindowFocus();
- if (shouldBackButtonBeHidden) {
- // Show the back button if there is a floating view visible.
- shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenViewWithType(launcher,
- TYPE_ALL & ~TYPE_HIDE_BACK_BUTTON) == null;
- }
- OverviewInteractionState.INSTANCE.get(launcher)
- .setBackButtonAlpha(shouldBackButtonBeHidden ? 0 : 1, true /* animate */);
- if (launcher != null && launcher.getDragLayer() != null) {
- launcher.getRootView().setDisallowBackGesture(shouldBackButtonBeHidden);
- }
- }
-
- public static void onCreate(Launcher launcher) {
- if (!launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
- launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() {
- @Override
- public void onStateTransitionStart(LauncherState toState) {
- }
-
- @Override
- public void onStateTransitionComplete(LauncherState finalState) {
- boolean swipeUpEnabled = SysUINavigationMode.INSTANCE.get(launcher).getMode()
- .hasGestures;
- LauncherState prevState = launcher.getStateManager().getLastState();
-
- if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
- && finalState == ALL_APPS && prevState == NORMAL) || BOUNCE_MAX_COUNT <=
- launcher.getSharedPrefs().getInt(HOME_BOUNCE_COUNT, 0))) {
- launcher.getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
- launcher.getStateManager().removeStateListener(this);
- }
- }
- });
- }
-
- if (!launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)) {
- launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() {
- @Override
- public void onStateTransitionStart(LauncherState toState) {
- }
-
- @Override
- public void onStateTransitionComplete(LauncherState finalState) {
- LauncherState prevState = launcher.getStateManager().getLastState();
-
- if ((finalState == ALL_APPS && prevState == OVERVIEW) || BOUNCE_MAX_COUNT <=
- launcher.getSharedPrefs().getInt(SHELF_BOUNCE_COUNT, 0)) {
- launcher.getSharedPrefs().edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
- launcher.getStateManager().removeStateListener(this);
- }
- }
- });
- }
- }
-
- public static void onEnterAnimationComplete(Context context) {
- // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
- // as a part of quickstep, so that high-res thumbnails can load the next time we enter
- // overview
- RecentsModel.INSTANCE.get(context).getThumbnailCache()
- .getHighResLoadingState().setVisible(true);
- }
-
- public static void onTrimMemory(Context context, int level) {
- RecentsModel model = RecentsModel.INSTANCE.get(context);
- if (model != null) {
- model.onTrimMemory(level);
- }
- }
-
- public static void useFadeOutAnimationForLauncherStart(Launcher launcher,
- CancellationSignal cancellationSignal) {
- QuickstepAppTransitionManagerImpl appTransitionManager =
- (QuickstepAppTransitionManagerImpl) launcher.getAppTransitionManager();
- appTransitionManager.setRemoteAnimationProvider((targets) -> {
-
- // On the first call clear the reference.
- cancellationSignal.cancel();
-
- ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
- fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(targets));
- AnimatorSet anim = new AnimatorSet();
- anim.play(fadeAnimation);
- return anim;
- }, cancellationSignal);
- }
-
- public static boolean dumpActivity(Activity activity, PrintWriter writer) {
- if (!Utilities.IS_DEBUG_DEVICE) {
- return false;
- }
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- if (!(new ActivityCompat(activity).encodeViewHierarchy(out))) {
- return false;
- }
-
- Deflater deflater = new Deflater();
- deflater.setInput(out.toByteArray());
- deflater.finish();
-
- out.reset();
- byte[] buffer = new byte[1024];
- while (!deflater.finished()) {
- int count = deflater.deflate(buffer); // returns the generated code... index
- out.write(buffer, 0, count);
- }
-
- writer.println("--encoded-view-dump-v0--");
- writer.println(Base64.encodeToString(
- out.toByteArray(), Base64.NO_WRAP | Base64.NO_PADDING));
- return true;
- }
-
- public static boolean startIntentSenderForResult(Activity activity, IntentSender intent,
- int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
- Bundle options) {
- StartActivityParams params = new StartActivityParams(activity, requestCode);
- params.intentSender = intent;
- params.fillInIntent = fillInIntent;
- params.flagsMask = flagsMask;
- params.flagsValues = flagsValues;
- params.extraFlags = extraFlags;
- params.options = options;
- ((Context) activity).startActivity(ProxyActivityStarter.getLaunchIntent(activity, params));
- return true;
- }
-
- public static boolean startActivityForResult(Activity activity, Intent intent, int requestCode,
- Bundle options) {
- StartActivityParams params = new StartActivityParams(activity, requestCode);
- params.intent = intent;
- params.options = options;
- activity.startActivity(ProxyActivityStarter.getLaunchIntent(activity, params));
- return true;
- }
-
- /**
- * Removes any active ProxyActivityStarter task and sends RESULT_CANCELED to Launcher.
- *
- * ProxyActivityStarter is started with clear task to reset the task after which it removes the
- * task itself.
- */
- public static void resetPendingActivityResults(Launcher launcher, int requestCode) {
- launcher.onActivityResult(requestCode, RESULT_CANCELED, null);
- launcher.startActivity(ProxyActivityStarter.getLaunchIntent(launcher, null));
- }
-
- public static ScaleAndTranslation getOverviewScaleAndTranslationForNormalState(Launcher l) {
- if (SysUINavigationMode.getMode(l) == Mode.NO_BUTTON) {
- float offscreenTranslationX = l.getDeviceProfile().widthPx
- - l.getOverviewPanel().getPaddingStart();
- return new ScaleAndTranslation(1f, offscreenTranslationX, 0f);
- }
- return new ScaleAndTranslation(1.1f, 0f, 0f);
- }
-
- public static Person[] getPersons(ShortcutInfo si) {
- Person[] persons = si.getPersons();
- return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
index 39b0f8d..3cb0088 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
@@ -14,7 +14,7 @@
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.quickstep.RecentsModel;
+import com.android.quickstep.SystemUiProxy;
/**
* Touch controller for handling edge swipes in landscape/seascape UI
@@ -73,7 +73,7 @@
protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
super.onSwipeInteractionCompleted(targetState, logAction);
if (mStartState == NORMAL && targetState == OVERVIEW) {
- RecentsModel.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
+ SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
}
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index db6a40f..99b2a81 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -47,8 +47,7 @@
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 +136,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;
@@ -301,7 +299,7 @@
protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
super.onSwipeInteractionCompleted(targetState, logAction);
if (mStartState == NORMAL && targetState == OVERVIEW) {
- RecentsModel.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
+ SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
}
}
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 73%
rename from quickstep/src/com/android/quickstep/ActivityControlHelper.java
rename to quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 110cc23..fd55e07 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,35 +31,40 @@
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.quickstep.util.ShelfPeekAnim;
+import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import java.util.function.BiPredicate;
import java.util.function.Consumer;
+import java.util.function.Predicate;
/**
* Utility class which abstracts out the logical differences between Launcher and RecentsActivity.
*/
@TargetApi(Build.VERSION_CODES.P)
-public interface ActivityControlHelper<T extends BaseDraggingActivity> {
+public interface BaseActivityInterface<T extends BaseDraggingActivity> {
- void onTransitionCancelled(T activity, boolean activityVisible);
+ void onTransitionCancelled(boolean activityVisible);
int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect);
- void onSwipeUpToRecentsComplete(T activity);
+ void onSwipeUpToRecentsComplete();
- default void onSwipeUpToHomeComplete(T activity) { }
+ default void onSwipeUpToHomeComplete() { }
void onAssistantVisibilityChanged(float visibility);
- @NonNull HomeAnimationFactory prepareHomeUI(T activity);
+ @NonNull HomeAnimationFactory prepareHomeUI();
- AnimationFactory prepareRecentsUI(T activity, boolean activityVisible,
- boolean animateActivity, Consumer<AnimatorPlaybackController> callback);
+ AnimationFactory prepareRecentsUI(boolean activityVisible, boolean animateActivity,
+ Consumer<AnimatorPlaybackController> callback);
- ActivityInitListener createActivityInitListener(BiPredicate<T, Boolean> onInitListener);
+ ActivityInitListener createActivityInitListener(Predicate<Boolean> onInitListener);
+
+ /**
+ * Sets a callback to be run when an activity launch happens while launcher is not yet resumed.
+ */
+ default void setOnDeferredActivityLaunchCallback(Runnable r) {}
@Nullable
T getCreatedActivity();
@@ -83,36 +85,38 @@
boolean shouldMinimizeSplitScreen();
- default boolean deferStartingActivity(Region activeNavBarRegion, MotionEvent ev) {
+ default boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
return true;
}
/**
+ * Updates the prediction state to the overview state.
+ */
+ default void updateOverviewPredictionState() {
+ // By default overview predictions are not supported
+ }
+
+ /**
* Used for containerType in {@link com.android.launcher3.logging.UserEventDispatcher}
*/
int getContainerType();
boolean isInLiveTileMode();
- void onLaunchTaskFailed(T activity);
+ void onLaunchTaskFailed();
- void onLaunchTaskSuccess(T activity);
+ void onLaunchTaskSuccess();
- interface ActivityInitListener {
+ default void closeOverlay() { }
- void register();
-
- void unregister();
-
- void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
- Context context, Handler handler, long duration);
- }
+ default void switchRunningTaskViewToScreenshot(ThumbnailData thumbnailData,
+ Runnable runnable) {}
interface AnimationFactory {
- 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 1ac7ed4..5fcdc19 100644
--- a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
@@ -27,7 +27,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
-import com.android.launcher3.uioverrides.UiFactory;
+import com.android.launcher3.util.ActivityTracker;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
@@ -43,6 +43,7 @@
*/
public abstract class BaseRecentsActivity extends BaseDraggingActivity {
+ public static ActivityTracker<BaseRecentsActivity> ACTIVITY_TRACKER = new ActivityTracker<>();
private Configuration mOldConfig;
@Override
@@ -55,7 +56,7 @@
getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
- RecentsActivityTracker.onRecentsActivityCreate(this);
+ ACTIVITY_TRACKER.handleCreate((RecentsActivity) this);
}
/**
@@ -120,25 +121,29 @@
@Override
public void onEnterAnimationComplete() {
super.onEnterAnimationComplete();
- UiFactory.onEnterAnimationComplete(this);
+ // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
+ // as a part of quickstep, so that high-res thumbnails can load the next time we enter
+ // overview
+ RecentsModel.INSTANCE.get(this).getThumbnailCache()
+ .getHighResLoadingState().setVisible(true);
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
- UiFactory.onTrimMemory(this, level);
+ RecentsModel.INSTANCE.get(this).onTrimMemory(level);
}
@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
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
new file mode 100644
index 0000000..ae0886b
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -0,0 +1,284 @@
+/*
+ * 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.quickstep.MultiStateCallback.DEBUG_STATES;
+
+import android.app.ActivityManager;
+import android.content.Intent;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+import java.util.ArrayList;
+
+/**
+ * Manages the state for an active system gesture, listens for events from the system and Launcher,
+ * and fires events when the states change.
+ */
+public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationListener {
+
+ /**
+ * Defines the end targets of a gesture and the associated state.
+ */
+ public enum GestureEndTarget {
+ HOME(true, ContainerType.WORKSPACE, false),
+
+ RECENTS(true, ContainerType.TASKSWITCHER, true),
+
+ NEW_TASK(false, ContainerType.APP, true),
+
+ LAST_TASK(false, ContainerType.APP, false);
+
+ GestureEndTarget(boolean isLauncher, int containerType,
+ boolean recentsAttachedToAppWindow) {
+ this.isLauncher = isLauncher;
+ this.containerType = containerType;
+ this.recentsAttachedToAppWindow = recentsAttachedToAppWindow;
+ }
+
+ /** Whether the target is in the launcher activity. Implicitly, if the end target is going
+ to Launcher, then we can not interrupt the animation to start another gesture. */
+ public final boolean isLauncher;
+ /** Used to log where the user ended up after the gesture ends */
+ public final int containerType;
+ /** Whether RecentsView should be attached to the window as we animate to this target */
+ public final boolean recentsAttachedToAppWindow;
+ }
+
+ private static final String TAG = "GestureState";
+
+ private static final ArrayList<String> STATE_NAMES = new ArrayList<>();
+ private static int FLAG_COUNT = 0;
+ private static int getFlagForIndex(String name) {
+ if (DEBUG_STATES) {
+ STATE_NAMES.add(name);
+ }
+ int index = 1 << FLAG_COUNT;
+ FLAG_COUNT++;
+ return index;
+ }
+
+ // Called when the end target as been set
+ public static final int STATE_END_TARGET_SET =
+ getFlagForIndex("STATE_END_TARGET_SET");
+
+ // Called when the end target animation has finished
+ public static final int STATE_END_TARGET_ANIMATION_FINISHED =
+ getFlagForIndex("STATE_END_TARGET_ANIMATION_FINISHED");
+
+ // Called when the recents animation has been requested to start
+ public static final int STATE_RECENTS_ANIMATION_INITIALIZED =
+ getFlagForIndex("STATE_RECENTS_ANIMATION_INITIALIZED");
+
+ // Called when the recents animation is started and the TaskAnimationManager has been updated
+ // with the controller and targets
+ public static final int STATE_RECENTS_ANIMATION_STARTED =
+ getFlagForIndex("STATE_RECENTS_ANIMATION_STARTED");
+
+ // Called when the recents animation is canceled
+ public static final int STATE_RECENTS_ANIMATION_CANCELED =
+ getFlagForIndex("STATE_RECENTS_ANIMATION_CANCELED");
+
+ // Called when the recents animation finishes
+ public static final int STATE_RECENTS_ANIMATION_FINISHED =
+ getFlagForIndex("STATE_RECENTS_ANIMATION_FINISHED");
+
+ // Always called when the recents animation ends (regardless of cancel or finish)
+ public static final int STATE_RECENTS_ANIMATION_ENDED =
+ getFlagForIndex("STATE_RECENTS_ANIMATION_ENDED");
+
+
+ // Needed to interact with the current activity
+ private final Intent mHomeIntent;
+ private final Intent mOverviewIntent;
+ private final BaseActivityInterface mActivityInterface;
+ private final MultiStateCallback mStateCallback;
+ private final int mGestureId;
+
+ private ActivityManager.RunningTaskInfo mRunningTask;
+ private GestureEndTarget mEndTarget;
+ // TODO: This can be removed once we stop finishing the animation when starting a new task
+ private int mFinishingRecentsAnimationTaskId = -1;
+
+ public GestureState(OverviewComponentObserver componentObserver, int gestureId) {
+ mHomeIntent = componentObserver.getHomeIntent();
+ mOverviewIntent = componentObserver.getOverviewIntent();
+ mActivityInterface = componentObserver.getActivityInterface();
+ mStateCallback = new MultiStateCallback(STATE_NAMES.toArray(new String[0]));
+ mGestureId = gestureId;
+ }
+
+ public GestureState() {
+ // Do nothing, only used for initializing the gesture state prior to user unlock
+ mHomeIntent = new Intent();
+ mOverviewIntent = new Intent();
+ mActivityInterface = null;
+ mStateCallback = new MultiStateCallback(STATE_NAMES.toArray(new String[0]));
+ mGestureId = -1;
+ }
+
+ /**
+ * @return whether the gesture state has the provided {@param stateMask} flags set.
+ */
+ public boolean hasState(int stateMask) {
+ return mStateCallback.hasStates(stateMask);
+ }
+
+ /**
+ * Sets the given {@param stateFlag}s.
+ */
+ public void setState(int stateFlag) {
+ mStateCallback.setState(stateFlag);
+ }
+
+ /**
+ * Adds a callback for when the states matching the given {@param stateMask} is set.
+ */
+ public void runOnceAtState(int stateMask, Runnable callback) {
+ mStateCallback.runOnceAtState(stateMask, callback);
+ }
+
+ /**
+ * @return the intent for the Home component.
+ */
+ public Intent getHomeIntent() {
+ return mHomeIntent;
+ }
+
+ /**
+ * @return the intent for the Overview component.
+ */
+ public Intent getOverviewIntent() {
+ return mOverviewIntent;
+ }
+
+ /**
+ * @return the interface to the activity handing the UI updates for this gesture.
+ */
+ public <T extends BaseDraggingActivity> BaseActivityInterface<T> getActivityInterface() {
+ return mActivityInterface;
+ }
+
+ /**
+ * @return the id for this particular gesture.
+ */
+ public int getGestureId() {
+ return mGestureId;
+ }
+
+ /**
+ * @return the running task for this gesture.
+ */
+ public ActivityManager.RunningTaskInfo getRunningTask() {
+ return mRunningTask;
+ }
+
+ /**
+ * @return the running task id for this gesture.
+ */
+ public int getRunningTaskId() {
+ return mRunningTask != null ? mRunningTask.taskId : -1;
+ }
+
+ /**
+ * Updates the running task for the gesture to be the given {@param runningTask}.
+ */
+ public void updateRunningTask(ActivityManager.RunningTaskInfo runningTask) {
+ mRunningTask = runningTask;
+ }
+
+ /**
+ * @return the end target for this gesture (if known).
+ */
+ public GestureEndTarget getEndTarget() {
+ return mEndTarget;
+ }
+
+ /**
+ * Sets the end target of this gesture and immediately notifies the state changes.
+ */
+ public void setEndTarget(GestureEndTarget target) {
+ setEndTarget(target, true /* isAtomic */);
+ }
+
+ /**
+ * Sets the end target of this gesture, but if {@param isAtomic} is {@code false}, then the
+ * caller must explicitly set {@link #STATE_END_TARGET_ANIMATION_FINISHED} themselves.
+ */
+ public void setEndTarget(GestureEndTarget target, boolean isAtomic) {
+ mEndTarget = target;
+ mStateCallback.setState(STATE_END_TARGET_SET);
+ if (isAtomic) {
+ mStateCallback.setState(STATE_END_TARGET_ANIMATION_FINISHED);
+ }
+ }
+
+ /**
+ * @return the id for the task that was about to be launched following the finish of the recents
+ * animation. Only defined between when the finish-recents call was made and the launch
+ * activity call is made.
+ */
+ public int getFinishingRecentsAnimationTaskId() {
+ return mFinishingRecentsAnimationTaskId;
+ }
+
+ /**
+ * Sets the id for the task will be launched after the recents animation is finished. Once the
+ * animation has finished then the id will be reset to -1.
+ */
+ public void setFinishingRecentsAnimationTaskId(int taskId) {
+ mFinishingRecentsAnimationTaskId = taskId;
+ mStateCallback.runOnceAtState(STATE_RECENTS_ANIMATION_FINISHED, () -> {
+ mFinishingRecentsAnimationTaskId = -1;
+ });
+ }
+
+ /**
+ * @return whether the current gesture is still running a recents animation to a state in the
+ * Launcher or Recents activity.
+ * Updates the running task for the gesture to be the given {@param runningTask}.
+ */
+ public boolean isRunningAnimationToLauncher() {
+ return isRecentsAnimationRunning() && mEndTarget != null && mEndTarget.isLauncher;
+ }
+
+ /**
+ * @return whether the recents animation is started but not yet ended
+ */
+ public boolean isRecentsAnimationRunning() {
+ return mStateCallback.hasStates(STATE_RECENTS_ANIMATION_INITIALIZED) &&
+ !mStateCallback.hasStates(STATE_RECENTS_ANIMATION_ENDED);
+ }
+
+ @Override
+ public void onRecentsAnimationStart(RecentsAnimationController controller,
+ RecentsAnimationTargets targets) {
+ mStateCallback.setState(STATE_RECENTS_ANIMATION_STARTED);
+ }
+
+ @Override
+ public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+ mStateCallback.setState(STATE_RECENTS_ANIMATION_CANCELED);
+ mStateCallback.setState(STATE_RECENTS_ANIMATION_ENDED);
+ }
+
+ @Override
+ public void onRecentsAnimationFinished(RecentsAnimationController controller) {
+ mStateCallback.setState(STATE_RECENTS_ANIMATION_FINISHED);
+ mStateCallback.setState(STATE_RECENTS_ANIMATION_ENDED);
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java
similarity index 91%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java
rename to quickstep/src/com/android/quickstep/InputConsumer.java
index a1e5d47..3e84e7d 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_OVERSCROLL = 1 << 9;
String[] NAMES = new String[] {
"TYPE_NO_OP", // 0
@@ -44,16 +45,13 @@
"TYPE_SCREEN_PINNED", // 6
"TYPE_OVERVIEW_WITHOUT_FOCUS", // 7
"TYPE_RESET_GESTURE", // 8
+ "TYPE_OVERSCROLL", // 9
};
InputConsumer NO_OP = () -> TYPE_NO_OP;
int getType();
- default boolean useSharedSwipeState() {
- return false;
- }
-
/**
* Returns true if the user has crossed the threshold for it to be an explicit action.
*/
@@ -63,6 +61,8 @@
/**
* Called by the event queue when the consumer is about to be switched to a new consumer.
+ * Consumers should update the state accordingly here before the state is passed to the new
+ * consumer.
*/
default void onConsumerAboutToBeSwitched() { }
diff --git a/quickstep/src/com/android/quickstep/MultiStateCallback.java b/quickstep/src/com/android/quickstep/MultiStateCallback.java
new file mode 100644
index 0000000..6c65e01
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/MultiStateCallback.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import static com.android.launcher3.Utilities.postAsyncCallback;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.os.Looper;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.launcher3.config.FeatureFlags;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.StringJoiner;
+import java.util.function.Consumer;
+
+/**
+ * Utility class to help manage multiple callbacks based on different states.
+ */
+public class MultiStateCallback {
+
+ private static final String TAG = "MultiStateCallback";
+ public static final boolean DEBUG_STATES = false;
+
+ private final SparseArray<LinkedList<Runnable>> mCallbacks = new SparseArray<>();
+ private final SparseArray<ArrayList<Consumer<Boolean>>> mStateChangeListeners =
+ new SparseArray<>();
+
+ private final String[] mStateNames;
+
+ private int mState = 0;
+
+ public MultiStateCallback(String[] stateNames) {
+ mStateNames = DEBUG_STATES ? stateNames : null;
+ }
+
+ /**
+ * Adds the provided state flags to the global state on the UI thread and executes any callbacks
+ * as a result.
+ */
+ public void setStateOnUiThread(int stateFlag) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ setState(stateFlag);
+ } else {
+ postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> setState(stateFlag));
+ }
+ }
+
+ /**
+ * Adds the provided state flags to the global state and executes any callbacks as a result.
+ */
+ public void setState(int stateFlag) {
+ if (DEBUG_STATES) {
+ Log.d(TAG, "[" + System.identityHashCode(this) + "] Adding "
+ + convertToFlagNames(stateFlag) + " to " + convertToFlagNames(mState));
+ }
+
+ final int oldState = mState;
+ mState = mState | stateFlag;
+
+ int count = mCallbacks.size();
+ for (int i = 0; i < count; i++) {
+ int state = mCallbacks.keyAt(i);
+
+ if ((mState & state) == state) {
+ LinkedList<Runnable> callbacks = mCallbacks.valueAt(i);
+ while (!callbacks.isEmpty()) {
+ callbacks.pollFirst().run();
+ }
+ }
+ }
+ notifyStateChangeListeners(oldState);
+ }
+
+ /**
+ * Adds the provided state flags to the global state and executes any change handlers
+ * as a result.
+ */
+ public void clearState(int stateFlag) {
+ if (DEBUG_STATES) {
+ Log.d(TAG, "[" + System.identityHashCode(this) + "] Removing "
+ + convertToFlagNames(stateFlag) + " from " + convertToFlagNames(mState));
+ }
+
+ int oldState = mState;
+ mState = mState & ~stateFlag;
+ notifyStateChangeListeners(oldState);
+ }
+
+ private void notifyStateChangeListeners(int oldState) {
+ int count = mStateChangeListeners.size();
+ for (int i = 0; i < count; i++) {
+ int state = mStateChangeListeners.keyAt(i);
+ boolean wasOn = (state & oldState) == state;
+ boolean isOn = (state & mState) == state;
+
+ if (wasOn != isOn) {
+ ArrayList<Consumer<Boolean>> listeners = mStateChangeListeners.valueAt(i);
+ for (Consumer<Boolean> listener : listeners) {
+ listener.accept(isOn);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets a callback to be run when the provided states in the given {@param stateMask} is
+ * enabled. The callback is only run *once*, and if the states are already set at the time of
+ * this call then the callback will be made immediately.
+ */
+ public void runOnceAtState(int stateMask, Runnable callback) {
+ if ((mState & stateMask) == stateMask) {
+ callback.run();
+ } else {
+ final LinkedList<Runnable> callbacks;
+ if (mCallbacks.indexOfKey(stateMask) >= 0) {
+ callbacks = mCallbacks.get(stateMask);
+ if (FeatureFlags.IS_DOGFOOD_BUILD && callbacks.contains(callback)) {
+ throw new IllegalStateException("Existing callback for state found");
+ }
+ } else {
+ callbacks = new LinkedList<>();
+ mCallbacks.put(stateMask, callbacks);
+ }
+ callbacks.add(callback);
+ }
+ }
+
+ /**
+ * Adds a persistent listener to be called states in the given {@param stateMask} are enabled
+ * or disabled.
+ */
+ public void addChangeListener(int stateMask, Consumer<Boolean> listener) {
+ final ArrayList<Consumer<Boolean>> listeners;
+ if (mStateChangeListeners.indexOfKey(stateMask) >= 0) {
+ listeners = mStateChangeListeners.get(stateMask);
+ } else {
+ listeners = new ArrayList<>();
+ mStateChangeListeners.put(stateMask, listeners);
+ }
+ listeners.add(listener);
+ }
+
+ public int getState() {
+ return mState;
+ }
+
+ public boolean hasStates(int stateMask) {
+ return (mState & stateMask) == stateMask;
+ }
+
+ private String convertToFlagNames(int flags) {
+ StringJoiner joiner = new StringJoiner(", ", "[", " (" + flags + ")]");
+ for (int i = 0; i < mStateNames.length; i++) {
+ if ((flags & (1 << i)) != 0) {
+ joiner.add(mStateNames[i]);
+ }
+ }
+ return joiner.toString();
+ }
+
+}
diff --git a/quickstep/src/com/android/quickstep/NormalizedIconLoader.java b/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
deleted file mode 100644
index bd6204a..0000000
--- a/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
+++ /dev/null
@@ -1,99 +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.app.ActivityManager.TaskDescription;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.UserHandle;
-import android.util.LruCache;
-import android.util.SparseArray;
-
-import com.android.launcher3.FastBitmapDrawable;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.graphics.DrawableFactory;
-import com.android.launcher3.icons.LauncherIcons;
-import com.android.systemui.shared.recents.model.IconLoader;
-import com.android.systemui.shared.recents.model.TaskKeyLruCache;
-
-/**
- * Extension of {@link IconLoader} with icon normalization support
- */
-@TargetApi(Build.VERSION_CODES.O)
-public class NormalizedIconLoader extends IconLoader {
-
- private final SparseArray<BitmapInfo> mDefaultIcons = new SparseArray<>();
- private final DrawableFactory mDrawableFactory;
- private final boolean mDisableColorExtraction;
-
- public NormalizedIconLoader(Context context, TaskKeyLruCache<Drawable> iconCache,
- LruCache<ComponentName, ActivityInfo> activityInfoCache,
- boolean disableColorExtraction) {
- super(context, iconCache, activityInfoCache);
- mDrawableFactory = DrawableFactory.INSTANCE.get(context);
- mDisableColorExtraction = disableColorExtraction;
- }
-
- @Override
- public Drawable getDefaultIcon(int userId) {
- synchronized (mDefaultIcons) {
- BitmapInfo info = mDefaultIcons.get(userId);
- if (info == null) {
- info = getBitmapInfo(Resources.getSystem()
- .getDrawable(android.R.drawable.sym_def_app_icon), userId, 0, false);
- mDefaultIcons.put(userId, info);
- }
-
- return new FastBitmapDrawable(info);
- }
- }
-
- @Override
- protected Drawable createBadgedDrawable(Drawable drawable, int userId, TaskDescription desc) {
- return new FastBitmapDrawable(getBitmapInfo(drawable, userId, desc.getPrimaryColor(),
- false));
- }
-
- private BitmapInfo getBitmapInfo(Drawable drawable, int userId,
- int primaryColor, boolean isInstantApp) {
- try (LauncherIcons la = LauncherIcons.obtain(mContext)) {
- if (mDisableColorExtraction) {
- la.disableColorExtraction();
- }
- la.setWrapperBackgroundColor(primaryColor);
-
- // User version code O, so that the icon is always wrapped in an adaptive icon container
- return la.createBadgedIconBitmap(drawable, UserHandle.of(userId),
- Build.VERSION_CODES.O, isInstantApp);
- }
- }
-
- @Override
- protected Drawable getBadgedActivityIcon(ActivityInfo activityInfo, int userId,
- TaskDescription desc) {
- BitmapInfo bitmapInfo = getBitmapInfo(
- activityInfo.loadUnbadgedIcon(mContext.getPackageManager()),
- userId,
- desc.getPrimaryColor(),
- activityInfo.applicationInfo.isInstantApp());
- return mDrawableFactory.newIcon(mContext, bitmapInfo, activityInfo);
- }
-}
diff --git a/quickstep/src/com/android/quickstep/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 858c3b6..0000000
--- a/quickstep/src/com/android/quickstep/OverviewInteractionState.java
+++ /dev/null
@@ -1,146 +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 com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.DiscoveryBounce;
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.systemui.shared.recents.ISystemUiProxy;
-
-/**
- * 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(UI_HELPER_EXECUTOR.getLooper(), 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/RecentsActivityTracker.java b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java
deleted file mode 100644
index 4d1d9ef..0000000
--- a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java
+++ /dev/null
@@ -1,130 +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 com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
-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.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);
-
- public synchronized void schedule(RecentsActivityTracker tracker) {
- mPendingTracker = new WeakReference<>(tracker);
- MAIN_EXECUTOR.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..acf61b4
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.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.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 targets) {}
+
+ /**
+ * 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) {}
+
+ /**
+ * Callback made whenever the recents animation is finished.
+ */
+ default void onRecentsAnimationFinished(RecentsAnimationController controller) {}
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
new file mode 100644
index 0000000..46af8bf
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -0,0 +1,252 @@
+/*
+ * 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 InputConsumerController mInputConsumerController;
+ private Supplier<InputConsumer> mInputProxySupplier;
+ private InputConsumer mInputConsumer;
+ private boolean mWindowThresholdCrossed = false;
+ private boolean mTouchInProgress;
+ private boolean mFinishPending;
+
+ public RecentsAnimationController(RecentsAnimationControllerCompat controller,
+ boolean shouldMinimizeSplitScreen,
+ Consumer<RecentsAnimationController> onFinishedListener) {
+ mController = controller;
+ mOnFinishedListener = onFinishedListener;
+ mShouldMinimizeSplitScreen = shouldMinimizeSplitScreen;
+ }
+
+ /**
+ * 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.screenshotTask(taskId);
+ }
+
+ /**
+ * 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);
+ }
+
+ /** @return wrapper controller. */
+ public RecentsAnimationControllerCompat getController() {
+ return mController;
+ }
+
+ 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..81f411e
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -0,0 +1,502 @@
+/*
+ * 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 android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED;
+import static com.android.launcher3.ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE;
+import static com.android.launcher3.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
+import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
+import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
+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.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.graphics.Point;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.os.Process;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.Surface;
+
+import androidx.annotation.BinderThread;
+
+import com.android.launcher3.R;
+import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.util.DefaultDisplay;
+import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
+import com.android.quickstep.util.NavBarPosition;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
+import com.android.systemui.shared.system.SystemGestureExclusionListenerCompat;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Manages the state of the system during a swipe up gesture.
+ */
+public class RecentsAnimationDeviceState implements
+ NavigationModeChangeListener,
+ DefaultDisplay.DisplayInfoChangeListener {
+
+ private final Context mContext;
+ private final UserManagerCompat mUserManager;
+ private final SysUINavigationMode mSysUiNavMode;
+ private final DefaultDisplay mDefaultDisplay;
+ private final int mDisplayId;
+
+ private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>();
+
+ private @SystemUiStateFlags int mSystemUiStateFlags;
+ private SysUINavigationMode.Mode mMode = THREE_BUTTONS;
+ private NavBarPosition mNavBarPosition;
+
+ 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) {
+ final ContentResolver resolver = context.getContentResolver();
+ mContext = context;
+ mUserManager = UserManagerCompat.getInstance(context);
+ mSysUiNavMode = SysUINavigationMode.INSTANCE.get(context);
+ mDefaultDisplay = DefaultDisplay.INSTANCE.get(context);
+ mDisplayId = mDefaultDisplay.getInfo().id;
+ runOnDestroy(() -> mDefaultDisplay.removeChangeListener(this));
+
+ // Register for user unlocked if necessary
+ mIsUserUnlocked = mUserManager.isUserUnlocked(Process.myUserHandle());
+ if (!mIsUserUnlocked) {
+ mContext.registerReceiver(mUserUnlockedReceiver,
+ new IntentFilter(ACTION_USER_UNLOCKED));
+ }
+ runOnDestroy(() -> Utilities.unregisterReceiverSafely(mContext, mUserUnlockedReceiver));
+
+ // 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;
+ }
+ };
+ runOnDestroy(mExclusionListener::unregister);
+
+ // Register for navigation mode changes
+ onNavigationModeChanged(mSysUiNavMode.addModeChangeListener(this));
+ runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(this));
+
+ // Add any blocked activities
+ String blockingActivity = context.getString(R.string.gesture_blocking_activity);
+ if (!TextUtils.isEmpty(blockingActivity)) {
+ mGestureBlockedActivity = ComponentName.unflattenFromString(blockingActivity);
+ }
+ }
+
+ private void runOnDestroy(Runnable action) {
+ mOnDestroyActions.add(action);
+ }
+
+ /**
+ * Cleans up all the registered listeners and receivers.
+ */
+ public void destroy() {
+ for (Runnable r : mOnDestroyActions) {
+ r.run();
+ }
+ }
+
+ /**
+ * Adds a listener for the nav mode change, guaranteed to be called after the device state's
+ * mode has changed.
+ */
+ public void addNavigationModeChangedCallback(NavigationModeChangeListener listener) {
+ listener.onNavigationModeChanged(mSysUiNavMode.addModeChangeListener(listener));
+ runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(listener));
+ }
+
+ @Override
+ public void onNavigationModeChanged(SysUINavigationMode.Mode newMode) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "onNavigationModeChanged " + newMode);
+ }
+ mDefaultDisplay.removeChangeListener(this);
+ if (newMode.hasGestures) {
+ mDefaultDisplay.addChangeListener(this);
+ }
+
+ if (mMode == NO_BUTTON) {
+ mExclusionListener.register();
+ } else {
+ mExclusionListener.unregister();
+ }
+ mMode = newMode;
+ mNavBarPosition = new NavBarPosition(mMode, mDefaultDisplay.getInfo());
+ }
+
+ @Override
+ public void onDisplayInfoChanged(DefaultDisplay.Info info, int flags) {
+ if (info.id != getDisplayId()) {
+ return;
+ }
+
+ mNavBarPosition = new NavBarPosition(mMode, info);
+ updateGestureTouchRegions();
+ }
+
+ /**
+ * @return the current navigation mode for the device.
+ */
+ public SysUINavigationMode.Mode getNavMode() {
+ return mMode;
+ }
+
+ /**
+ * @return the nav bar position for the current nav bar mode and display rotation.
+ */
+ public NavBarPosition getNavBarPosition() {
+ return mNavBarPosition;
+ }
+
+ /**
+ * @return whether the current nav mode is fully gestural.
+ */
+ public boolean isFullyGesturalNavMode() {
+ return mMode == NO_BUTTON;
+ }
+
+ /**
+ * @return whether the current nav mode has some gestures (either 2 or 0 button mode).
+ */
+ public boolean isGesturalNavMode() {
+ return mMode == TWO_BUTTONS || mMode == NO_BUTTON;
+ }
+
+ /**
+ * @return whether the current nav mode is button-based.
+ */
+ public boolean isButtonNavMode() {
+ return mMode == THREE_BUTTONS;
+ }
+
+ /**
+ * @return the display id for the display that Launcher is running on.
+ */
+ public int getDisplayId() {
+ 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 != 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..718c5ba
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -0,0 +1,44 @@
+/*
+ * 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;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 2e59ed5..517501a 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -28,14 +28,10 @@
import android.os.Build;
import android.os.Looper;
import android.os.Process;
-import android.os.RemoteException;
import android.os.UserHandle;
-import android.util.Log;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.LauncherAppsCompat.OnAppsChangedCallbackCompat;
+import com.android.launcher3.icons.IconProvider;
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;
@@ -52,17 +48,13 @@
@TargetApi(Build.VERSION_CODES.O)
public class RecentsModel extends TaskStackChangeListener {
- private static final String TAG = "RecentsModel";
-
// We do not need any synchronization for this variable as its only written on UI thread.
public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
new MainThreadInitializedObject<>(RecentsModel::new);
- private final List<TaskThumbnailChangeListener> mThumbnailChangeListeners = new ArrayList<>();
+ private final List<TaskVisualsChangeListener> mThumbnailChangeListeners = new ArrayList<>();
private final Context mContext;
- private ISystemUiProxy mSystemUiProxy;
-
private final RecentTasksList mTaskList;
private final TaskIconCache mIconCache;
private final TaskThumbnailCache mThumbnailCache;
@@ -75,8 +67,10 @@
new KeyguardManagerCompat(context), ActivityManagerWrapper.getInstance());
mIconCache = new TaskIconCache(context, looper);
mThumbnailCache = new TaskThumbnailCache(context, looper);
+
ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
- setupPackageListener();
+ IconProvider.registerIconChangeListener(context,
+ this::onPackageIconChanged, MAIN_EXECUTOR.getHandler());
}
public TaskIconCache getIconCache() {
@@ -178,14 +172,6 @@
mIconCache.onTaskRemoved(dummyKey);
}
- public void setSystemUiProxy(ISystemUiProxy systemUiProxy) {
- mSystemUiProxy = systemUiProxy;
- }
-
- public ISystemUiProxy getSystemUiProxy() {
- return mSystemUiProxy;
- }
-
public void onTrimMemory(int level) {
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
mThumbnailCache.getHighResLoadingState().setVisible(false);
@@ -197,44 +183,40 @@
}
}
- 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);
+ private void onPackageIconChanged(String pkg, UserHandle user) {
+ mIconCache.invalidateCacheEntries(pkg, user);
+ for (int i = mThumbnailChangeListeners.size() - 1; i >= 0; i--) {
+ mThumbnailChangeListeners.get(i).onTaskIconChanged(pkg, user);
}
}
- private void setupPackageListener() {
- LauncherAppsCompat.getInstance(mContext)
- .addOnAppsChangedCallback(new OnAppsChangedCallbackCompat() {
- @Override
- public void onPackageRemoved(String packageName, UserHandle user) {
- mIconCache.invalidatePackage(packageName);
- }
-
- @Override
- public void onPackageChanged(String packageName, UserHandle user) {
- mIconCache.invalidatePackage(packageName);
- }
- });
- }
-
- public void addThumbnailChangeListener(TaskThumbnailChangeListener listener) {
+ /**
+ * Adds a listener for visuals changes
+ */
+ public void addThumbnailChangeListener(TaskVisualsChangeListener listener) {
mThumbnailChangeListeners.add(listener);
}
- public void removeThumbnailChangeListener(TaskThumbnailChangeListener listener) {
+ /**
+ * Removes a previously added listener
+ */
+ public void removeThumbnailChangeListener(TaskVisualsChangeListener listener) {
mThumbnailChangeListeners.remove(listener);
}
- public interface TaskThumbnailChangeListener {
+ /**
+ * Listener for receiving various task properties changes
+ */
+ public interface TaskVisualsChangeListener {
+ /**
+ * Called whn the task thumbnail changes
+ */
Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData);
+
+ /**
+ * Called when the icon for a task changes
+ */
+ void onTaskIconChanged(String pkg, UserHandle user);
}
}
diff --git a/quickstep/src/com/android/quickstep/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/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
new file mode 100644
index 0000000..e3e8ace
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
+
+import android.content.Intent;
+import android.util.Log;
+
+import androidx.annotation.UiThread;
+
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+
+public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
+
+ private RecentsAnimationController mController;
+ private RecentsAnimationCallbacks mCallbacks;
+ private RecentsAnimationTargets mTargets;
+ // Temporary until we can hook into gesture state events
+ private GestureState mLastGestureState;
+
+ /**
+ * Preloads the recents animation.
+ */
+ public void preloadRecentsAnimation(Intent intent) {
+ // Pass null animation handler to indicate this start is for preloading
+ UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
+ .startRecentsActivity(intent, null, null, null, null));
+ }
+
+ /**
+ * Starts a new recents animation for the activity with the given {@param intent}.
+ */
+ @UiThread
+ public RecentsAnimationCallbacks startRecentsAnimation(GestureState gestureState,
+ Intent intent, RecentsAnimationCallbacks.RecentsAnimationListener listener) {
+ // Notify if recents animation is still running
+ if (mController != null) {
+ String msg = "New recents animation started before old animation completed";
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ throw new IllegalArgumentException(msg);
+ } else {
+ Log.e("TaskAnimationManager", msg, new Exception());
+ }
+ }
+ // But force-finish it anyways
+ finishRunningRecentsAnimation(false /* toHome */);
+
+ final BaseActivityInterface activityInterface = gestureState.getActivityInterface();
+ mLastGestureState = gestureState;
+ mCallbacks = new RecentsAnimationCallbacks(activityInterface.shouldMinimizeSplitScreen());
+ mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
+ @Override
+ public void onRecentsAnimationStart(RecentsAnimationController controller,
+ RecentsAnimationTargets targets) {
+ mController = controller;
+ mTargets = targets;
+ }
+
+ @Override
+ public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+ if (thumbnailData != null) {
+ // If a screenshot is provided, switch to the screenshot before cleaning up
+ activityInterface.switchRunningTaskViewToScreenshot(thumbnailData,
+ () -> cleanUpRecentsAnimation(thumbnailData));
+ } else {
+ cleanUpRecentsAnimation(null /* canceledThumbnail */);
+ }
+ }
+
+ @Override
+ public void onRecentsAnimationFinished(RecentsAnimationController controller) {
+ cleanUpRecentsAnimation(null /* canceledThumbnail */);
+ }
+ });
+ mCallbacks.addListener(gestureState);
+ mCallbacks.addListener(listener);
+ UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
+ .startRecentsActivity(intent, null, mCallbacks, null, null));
+ gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED);
+ return mCallbacks;
+ }
+
+ /**
+ * Continues the existing running recents animation for a new gesture.
+ */
+ public RecentsAnimationCallbacks continueRecentsAnimation(GestureState gestureState) {
+ mCallbacks.removeListener(mLastGestureState);
+ mLastGestureState = gestureState;
+ mCallbacks.addListener(gestureState);
+ return mCallbacks;
+ }
+
+ /**
+ * Finishes the running recents animation.
+ */
+ public void finishRunningRecentsAnimation(boolean toHome) {
+ if (mController != null) {
+ mCallbacks.notifyAnimationCanceled();
+ Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome
+ ? mController::finishAnimationToHome
+ : mController::finishAnimationToApp);
+ cleanUpRecentsAnimation(null /* canceledThumbnail */);
+ }
+ }
+
+ /**
+ * Used to notify a listener of the current recents animation state (used if the listener was
+ * not yet added to the callbacks at the point that the listener callbacks would have been
+ * made).
+ */
+ public void notifyRecentsAnimationState(
+ RecentsAnimationCallbacks.RecentsAnimationListener listener) {
+ if (isRecentsAnimationRunning()) {
+ listener.onRecentsAnimationStart(mController, mTargets);
+ }
+ // TODO: Do we actually need to report canceled/finished?
+ }
+
+ /**
+ * @return whether there is a recents animation running.
+ */
+ public boolean isRecentsAnimationRunning() {
+ return mController != null;
+ }
+
+ /**
+ * Cleans up the recents animation entirely.
+ */
+ private void cleanUpRecentsAnimation(ThumbnailData canceledThumbnail) {
+ // Clean up the screenshot if necessary
+ if (mController != null && canceledThumbnail != null) {
+ mController.cleanupScreenshot();
+ }
+
+ // Release all the target leashes
+ if (mTargets != null) {
+ mTargets.release();
+ }
+
+ // Remove gesture state from callbacks
+ if (mCallbacks != null && mLastGestureState != null) {
+ mCallbacks.removeListener(mLastGestureState);
+ }
+
+ mController = null;
+ mCallbacks = null;
+ mTargets = null;
+ mLastGestureState = null;
+ }
+
+ public void dump() {
+ // TODO
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index 289a129..e590aea 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -15,67 +15,64 @@
*/
package com.android.quickstep;
-import static com.android.launcher3.uioverrides.RecentsUiFactory.GO_LOW_RAM_RECENTS_ENABLED;
+import static com.android.launcher3.FastBitmapDrawable.newIcon;
+import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_RECENTS_ENABLED;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import android.content.ComponentName;
+import android.app.ActivityManager.TaskDescription;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
-import android.util.LruCache;
+import android.os.UserHandle;
+import android.util.SparseArray;
import android.view.accessibility.AccessibilityManager;
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.IconProvider;
+import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.util.TaskKeyLruCache;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.recents.model.TaskKeyLruCache;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.PackageManagerWrapper;
-import java.util.Map;
import java.util.function.Consumer;
/**
* Manages the caching of task icons and related data.
- * TODO(b/138944598): This class should later be merged into IconCache.
*/
public class TaskIconCache {
private final Handler mBackgroundHandler;
private final AccessibilityManager mAccessibilityManager;
- private final NormalizedIconLoader mIconLoader;
-
- private final TaskKeyLruCache<Drawable> mIconCache;
- private final TaskKeyLruCache<String> mContentDescriptionCache;
- private final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
-
- private TaskKeyLruCache.EvictionCallback mClearActivityInfoOnEviction =
- new TaskKeyLruCache.EvictionCallback() {
- @Override
- public void onEntryEvicted(Task.TaskKey key) {
- if (key != null) {
- mActivityInfoCache.remove(key.getComponent());
- }
- }
- };
+ private final Context mContext;
+ private final TaskKeyLruCache<TaskCacheEntry> mIconCache;
+ private final SparseArray<BitmapInfo> mDefaultIcons = new SparseArray<>();
+ private final IconProvider mIconProvider;
public TaskIconCache(Context context, Looper backgroundLooper) {
+ mContext = context;
mBackgroundHandler = new Handler(backgroundLooper);
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
Resources res = context.getResources();
int cacheSize = res.getInteger(R.integer.recentsIconCacheSize);
- mIconCache = new TaskKeyLruCache<>(cacheSize, mClearActivityInfoOnEviction);
- mContentDescriptionCache = new TaskKeyLruCache<>(cacheSize, mClearActivityInfoOnEviction);
- mActivityInfoCache = new LruCache<>(cacheSize);
- mIconLoader = new NormalizedIconLoader(context, mIconCache, mActivityInfoCache,
- true /* disableColorExtraction */);
+ mIconCache = new TaskKeyLruCache<>(cacheSize);
+ mIconProvider = new IconProvider(context);
}
/**
@@ -96,15 +93,14 @@
IconLoadRequest request = new IconLoadRequest(mBackgroundHandler) {
@Override
public void run() {
- Drawable icon = mIconLoader.getIcon(task);
- String contentDescription = loadContentDescriptionInBackground(task);
+ TaskCacheEntry entry = getCacheEntry(task);
if (isCanceled()) {
// We don't call back to the provided callback in this case
return;
}
MAIN_EXECUTOR.execute(() -> {
- task.icon = icon;
- task.titleDescription = contentDescription;
+ task.icon = entry.icon;
+ task.titleDescription = entry.contentDescription;
callback.accept(task);
onEnd();
});
@@ -116,51 +112,99 @@
public void clear() {
mIconCache.evictAll();
- mContentDescriptionCache.evictAll();
}
- /**
- * Loads the content description for the given {@param task}.
- */
- private String loadContentDescriptionInBackground(Task task) {
- // Return the cached content description if it exists
- String label = mContentDescriptionCache.getAndInvalidateIfModified(task.key);
- if (label != null) {
- return label;
- }
-
- // Skip loading content descriptions if accessibility is disabled unless low RAM recents
- // is enabled.
- if (!GO_LOW_RAM_RECENTS_ENABLED && !mAccessibilityManager.isEnabled()) {
- return "";
- }
-
- // Skip loading the content description if the activity no longer exists
- ActivityInfo activityInfo = mIconLoader.getAndUpdateActivityInfo(task.key);
- if (activityInfo == null) {
- return "";
- }
-
- // Load the label otherwise
- label = ActivityManagerWrapper.getInstance().getBadgedContentDescription(activityInfo,
- task.key.userId, task.taskDescription);
- mContentDescriptionCache.put(task.key, label);
- return label;
- }
-
-
void onTaskRemoved(TaskKey taskKey) {
mIconCache.remove(taskKey);
}
- void 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);
+ void invalidateCacheEntries(String pkg, UserHandle handle) {
+ Utilities.postAsyncCallback(mBackgroundHandler,
+ () -> mIconCache.removeAll(key ->
+ pkg.equals(key.getPackageName()) && handle.getIdentifier() == key.userId));
+ }
+
+ @WorkerThread
+ private TaskCacheEntry getCacheEntry(Task task) {
+ TaskCacheEntry entry = mIconCache.getAndInvalidateIfModified(task.key);
+ if (entry != null) {
+ return entry;
+ }
+
+ TaskDescription desc = task.taskDescription;
+ TaskKey key = task.key;
+ ActivityInfo activityInfo = null;
+
+ // Create new cache entry
+ entry = new TaskCacheEntry();
+
+ // Load icon
+ // TODO: Load icon resource (b/143363444)
+ Bitmap icon = desc.getIcon();
+ if (icon != null) {
+ entry.icon = new FastBitmapDrawable(getBitmapInfo(
+ new BitmapDrawable(mContext.getResources(), icon),
+ key.userId,
+ desc.getPrimaryColor(),
+ false /* isInstantApp */));
+ } else {
+ activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(
+ key.getComponent(), key.userId);
+ if (activityInfo != null) {
+ BitmapInfo bitmapInfo = getBitmapInfo(
+ mIconProvider.getIcon(activityInfo, UserHandle.of(key.userId)),
+ key.userId,
+ desc.getPrimaryColor(),
+ activityInfo.applicationInfo.isInstantApp());
+ entry.icon = newIcon(mContext, bitmapInfo);
+ } else {
+ entry.icon = getDefaultIcon(key.userId);
}
}
+
+ // Loading content descriptions if accessibility or low RAM recents is enabled.
+ if (GO_LOW_RAM_RECENTS_ENABLED || mAccessibilityManager.isEnabled()) {
+ // Skip loading the content description if the activity no longer exists
+ if (activityInfo == null) {
+ activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(
+ key.getComponent(), key.userId);
+ }
+ if (activityInfo != null) {
+ entry.contentDescription = ActivityManagerWrapper.getInstance()
+ .getBadgedContentDescription(activityInfo, task.key.userId,
+ task.taskDescription);
+ }
+ }
+
+ mIconCache.put(task.key, entry);
+ return entry;
+ }
+
+ @WorkerThread
+ private Drawable getDefaultIcon(int userId) {
+ synchronized (mDefaultIcons) {
+ BitmapInfo info = mDefaultIcons.get(userId);
+ if (info == null) {
+ try (LauncherIcons la = LauncherIcons.obtain(mContext)) {
+ info = la.makeDefaultIcon(UserHandle.of(userId));
+ }
+ mDefaultIcons.put(userId, info);
+ }
+ return new FastBitmapDrawable(info);
+ }
+ }
+
+ @WorkerThread
+ private BitmapInfo getBitmapInfo(Drawable drawable, int userId,
+ int primaryColor, boolean isInstantApp) {
+ try (LauncherIcons la = LauncherIcons.obtain(mContext)) {
+ la.disableColorExtraction();
+ la.setWrapperBackgroundColor(primaryColor);
+
+ // User version code O, so that the icon is always wrapped in an adaptive icon container
+ return la.createBadgedIconBitmap(drawable, UserHandle.of(userId),
+ Build.VERSION_CODES.O, isInstantApp);
+ }
}
public static abstract class IconLoadRequest extends HandlerRunnable {
@@ -168,4 +212,9 @@
super(handler, null);
}
}
+
+ private static class TaskCacheEntry {
+ public Drawable icon;
+ public String contentDescription = "";
+ }
}
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index 3b50c26..e47df6c 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -27,9 +27,9 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.util.TaskKeyLruCache;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.recents.model.TaskKeyLruCache;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -41,7 +41,7 @@
private final Handler mBackgroundHandler;
private final int mCacheSize;
- private final ThumbnailCache mCache;
+ private final TaskKeyLruCache<ThumbnailData> mCache;
private final HighResLoadingState mHighResLoadingState;
public static class HighResLoadingState {
@@ -100,7 +100,7 @@
Resources res = context.getResources();
mCacheSize = res.getInteger(R.integer.recentsThumbnailCacheSize);
- mCache = new ThumbnailCache(mCacheSize);
+ mCache = new TaskKeyLruCache<>(mCacheSize);
}
/**
@@ -223,21 +223,4 @@
this.reducedResolution = reducedResolution;
}
}
-
- private static class ThumbnailCache extends TaskKeyLruCache<ThumbnailData> {
-
- public ThumbnailCache(int cacheSize) {
- super(cacheSize);
- }
-
- /**
- * Updates the cache entry if it is already present in the cache
- */
- public void updateIfAlreadyInCache(int taskId, ThumbnailData thumbnailData) {
- ThumbnailData oldData = getCacheEntry(taskId);
- if (oldData != null) {
- putCacheEntry(taskId, thumbnailData);
- }
- }
- }
}
diff --git a/quickstep/src/com/android/quickstep/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/util/ActivityInitListener.java b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
new file mode 100644
index 0000000..b1c72ce
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
@@ -0,0 +1,69 @@
+/*
+ * 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;
+
+ /**
+ * @param onInitListener a callback made when the activity is initialized. The callback should
+ * return true to continue receiving callbacks (ie. for if the activity is
+ * recreated).
+ */
+ public ActivityInitListener(BiPredicate<T, Boolean> onInitListener,
+ ActivityTracker<T> tracker) {
+ mOnInitListener = onInitListener;
+ mActivityTracker = tracker;
+ }
+
+ @Override
+ public boolean init(T activity, boolean alreadyOnHome) {
+ return mOnInitListener.test(activity, alreadyOnHome);
+ }
+
+ /**
+ * Registers the activity-created listener. If the activity is already created, then the
+ * callback provided in the constructor will be called synchronously.
+ */
+ public void register() {
+ mActivityTracker.schedule(this);
+ }
+
+ 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/NavBarPosition.java b/quickstep/src/com/android/quickstep/util/NavBarPosition.java
new file mode 100644
index 0000000..a4614de
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/NavBarPosition.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.view.Gravity;
+import android.view.Surface;
+
+import com.android.launcher3.graphics.RotationMode;
+import com.android.launcher3.util.DefaultDisplay;
+import com.android.quickstep.SysUINavigationMode;
+
+/**
+ * Utility class to check nav bar position.
+ */
+public class NavBarPosition {
+
+ public static RotationMode ROTATION_LANDSCAPE = new RotationMode(-90) {
+ @Override
+ public void mapRect(int left, int top, int right, int bottom, Rect out) {
+ out.left = top;
+ out.top = right;
+ out.right = bottom;
+ out.bottom = left;
+ }
+
+ @Override
+ public void mapInsets(Context context, Rect insets, Rect out) {
+ // If there is a display cutout, the top insets in portrait would also include the
+ // cutout, which we will get as the left inset in landscape. Using the max of left and
+ // top allows us to cover both cases (with or without cutout).
+ if (SysUINavigationMode.getMode(context) == NO_BUTTON) {
+ out.top = Math.max(insets.top, insets.left);
+ out.bottom = Math.max(insets.right, insets.bottom);
+ out.left = out.right = 0;
+ } else {
+ out.top = Math.max(insets.top, insets.left);
+ out.bottom = insets.right;
+ out.left = insets.bottom;
+ out.right = 0;
+ }
+ }
+ };
+
+ public static RotationMode ROTATION_SEASCAPE = new RotationMode(90) {
+ @Override
+ public void mapRect(int left, int top, int right, int bottom, Rect out) {
+ out.left = bottom;
+ out.top = left;
+ out.right = top;
+ out.bottom = right;
+ }
+
+ @Override
+ public void mapInsets(Context context, Rect insets, Rect out) {
+ if (SysUINavigationMode.getMode(context) == NO_BUTTON) {
+ out.top = Math.max(insets.top, insets.right);
+ out.bottom = Math.max(insets.left, insets.bottom);
+ out.left = out.right = 0;
+ } else {
+ out.top = Math.max(insets.top, insets.right);
+ out.bottom = insets.left;
+ out.right = insets.bottom;
+ out.left = 0;
+ }
+ }
+
+ @Override
+ public int toNaturalGravity(int absoluteGravity) {
+ int horizontalGravity = absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+ int verticalGravity = absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK;
+
+ if (horizontalGravity == Gravity.RIGHT) {
+ horizontalGravity = Gravity.LEFT;
+ } else if (horizontalGravity == Gravity.LEFT) {
+ horizontalGravity = Gravity.RIGHT;
+ }
+
+ if (verticalGravity == Gravity.TOP) {
+ verticalGravity = Gravity.BOTTOM;
+ } else if (verticalGravity == Gravity.BOTTOM) {
+ verticalGravity = Gravity.TOP;
+ }
+
+ return ((absoluteGravity & ~Gravity.HORIZONTAL_GRAVITY_MASK)
+ & ~Gravity.VERTICAL_GRAVITY_MASK)
+ | horizontalGravity | verticalGravity;
+ }
+ };
+
+ private final SysUINavigationMode.Mode mMode;
+ private final int mDisplayRotation;
+
+ public NavBarPosition(SysUINavigationMode.Mode mode, DefaultDisplay.Info info) {
+ mMode = mode;
+ mDisplayRotation = info.rotation;
+ }
+
+ public boolean isRightEdge() {
+ return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_90;
+ }
+
+ public boolean isLeftEdge() {
+ return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_270;
+ }
+
+ public RotationMode getRotationMode() {
+ return isLeftEdge() ? ROTATION_SEASCAPE
+ : (isRightEdge() ? ROTATION_LANDSCAPE : RotationMode.NORMAL);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/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/util/TaskKeyLruCache.java b/quickstep/src/com/android/quickstep/util/TaskKeyLruCache.java
new file mode 100644
index 0000000..d87feec
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/TaskKeyLruCache.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import android.util.Log;
+
+import com.android.systemui.shared.recents.model.Task.TaskKey;
+
+import java.util.LinkedHashMap;
+import java.util.function.Predicate;
+
+/**
+ * A simple LRU cache for task key entries
+ * @param <V> The type of the value
+ */
+public class TaskKeyLruCache<V> {
+
+ private final MyLinkedHashMap<V> mMap;
+
+ public TaskKeyLruCache(int maxSize) {
+ mMap = new MyLinkedHashMap<>(maxSize);
+ }
+
+ /**
+ * Removes all entries from the cache
+ */
+ public synchronized void evictAll() {
+ mMap.clear();
+ }
+
+ /**
+ * Removes a particular entry from the cache
+ */
+ public synchronized void remove(TaskKey key) {
+ mMap.remove(key.id);
+ }
+
+ /**
+ * Removes all entries matching keyCheck
+ */
+ public synchronized void removeAll(Predicate<TaskKey> keyCheck) {
+ mMap.entrySet().removeIf(e -> keyCheck.test(e.getValue().mKey));
+ }
+
+ /**
+ * Gets the entry if it is still valid
+ */
+ public synchronized V getAndInvalidateIfModified(TaskKey key) {
+ Entry<V> entry = mMap.get(key.id);
+
+ if (entry != null && entry.mKey.windowingMode == key.windowingMode
+ && entry.mKey.lastActiveTime == key.lastActiveTime) {
+ return entry.mValue;
+ } else {
+ remove(key);
+ return null;
+ }
+ }
+
+ /**
+ * Adds an entry to the cache, optionally evicting the last accessed entry
+ */
+ public final synchronized void put(TaskKey key, V value) {
+ if (key != null && value != null) {
+ mMap.put(key.id, new Entry<>(key, value));
+ } else {
+ Log.e("TaskKeyCache", "Unexpected null key or value: " + key + ", " + value);
+ }
+ }
+
+ /**
+ * Updates the cache entry if it is already present in the cache
+ */
+ public synchronized void updateIfAlreadyInCache(int taskId, V data) {
+ Entry<V> entry = mMap.get(taskId);
+ if (entry != null) {
+ entry.mValue = data;
+ }
+ }
+
+ private static class Entry<V> {
+
+ final TaskKey mKey;
+ V mValue;
+
+ Entry(TaskKey key, V value) {
+ mKey = key;
+ mValue = value;
+ }
+
+ @Override
+ public int hashCode() {
+ return mKey.id;
+ }
+ }
+
+ private static class MyLinkedHashMap<V> extends LinkedHashMap<Integer, Entry<V>> {
+
+ private final int mMaxSize;
+
+ MyLinkedHashMap(int maxSize) {
+ super(0, 0.75f, true /* accessOrder */);
+ mMaxSize = maxSize;
+ }
+
+ @Override
+ protected boolean removeEldestEntry(Entry<Integer, TaskKeyLruCache.Entry<V>> eldest) {
+ return size() > mMaxSize;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index 3320dae..7885f5c 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -35,9 +35,9 @@
import android.util.AttributeSet;
import android.view.animation.Interpolator;
+import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.QuickstepAppTransitionManagerImpl;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
@@ -47,7 +47,6 @@
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
import com.android.quickstep.util.LayoutUtils;
-import com.android.quickstep.util.ShelfPeekAnim;
/**
* Scrim used for all-apps and shelf in Overview
@@ -56,7 +55,8 @@
* From normal state to overview state, the shelf just fades in and does not move
* From overview state to all-apps state the shelf moves up and fades in to cover the screen
*/
-public class ShelfScrimView extends ScrimView implements NavigationModeChangeListener {
+public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
+ implements NavigationModeChangeListener {
// If the progress is more than this, shelf follows the finger, otherwise it moves faster to
// cover the whole screen
@@ -197,12 +197,10 @@
if (mProgress >= 1) {
mRemainingScreenColor = 0;
mShelfColor = 0;
- ShelfPeekAnim shelfPeekAnim = ((QuickstepAppTransitionManagerImpl)
- mLauncher.getAppTransitionManager()).getShelfPeekAnim();
LauncherState state = mLauncher.getStateManager().getState();
if (mSysUINavigationMode == Mode.NO_BUTTON
&& (state == BACKGROUND_APP || state == QUICK_SWITCH)
- && shelfPeekAnim.isPeeking()) {
+ && mLauncher.getShelfPeekAnim().isPeeking()) {
// Show the shelf background when peeking during swipe up.
mShelfColor = setColorAlphaBound(mEndScrim, mMidAlpha);
}
diff --git a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
index 7801775..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);
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 8c11c1c..85cffaa 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,12 +49,18 @@
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.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
@@ -54,11 +68,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;
@@ -81,9 +95,12 @@
mDevice.setOrientationNatural();
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),
@@ -111,8 +128,9 @@
}
}
- @NavigationModeSwitch(mode = THREE_BUTTON)
+ @NavigationModeSwitch
@Test
+ @Ignore // b/143488140
public void goToOverviewFromHome() {
mDevice.pressHome();
assertTrue("Fallback Launcher not visible", mDevice.wait(Until.hasObject(By.pkg(
@@ -121,23 +139,118 @@
mLauncher.getBackground().switchToOverview();
}
- @NavigationModeSwitch(mode = THREE_BUTTON)
+ @NavigationModeSwitch
@Test
+ @Ignore // b/143488140
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, mLauncher);
+ return (T) result[0];
+ }
+
+ private BaseOverview pressHomeAndGoToOverview() {
+ mDevice.pressHome();
+ return mLauncher.getBackground().switchToOverview();
+ }
+
+ @NavigationModeSwitch
+ @Test
+ @Ignore // b/143488140
+ 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, mLauncher);
+
+ 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,
+ mLauncher);
+ }
+
+ 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 c2197ab..5606ac2 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -30,6 +30,7 @@
import android.content.pm.PackageManager;
import android.util.Log;
+import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiDevice;
import com.android.launcher3.tapl.LauncherInstrumentation;
@@ -80,7 +81,13 @@
Mode mode = description.getAnnotation(NavigationModeSwitch.class).mode();
return new Statement() {
private void assertTrue(String message, boolean condition) {
- if(!condition) {
+ if (mLauncher.getDevice().hasObject(By.textStartsWith(""))) {
+ // The condition above is "screen is not empty". We are not treating
+ // "Screen is empty" as an anomaly here. It's an acceptable state when
+ // Launcher just starts under instrumentation.
+ mLauncher.checkForAnomaly();
+ }
+ if (!condition) {
final AssertionError assertionError = new AssertionError(message);
FailureWatcher.onError(mLauncher.getDevice(), description, assertionError);
throw assertionError;
@@ -202,7 +209,6 @@
final String error = mLauncher.getNavigationModeMismatchError();
assertTrue("Switching nav mode: " + error, error == null);
- Thread.sleep(5000);
return true;
}
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index 2111e2c..891bf24 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -16,8 +16,11 @@
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 static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_PRESUBMIT;
+import static com.android.launcher3.util.rule.TestStabilityRule.RUN_FLAFOR;
+import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_PRESUBMIT;
import android.content.Intent;
@@ -25,6 +28,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.Launcher;
+import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.util.RaceConditionReproducer;
import com.android.quickstep.NavigationModeSwitchRule.Mode;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 8cd3bb6..428e647 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -248,6 +248,7 @@
@Test
@NavigationModeSwitch
@PortraitLandscape
+ @Ignore // b/143285809
public void testQuickSwitchFromApp() throws Exception {
startTestActivity(2);
startTestActivity(3);
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/values-af/strings.xml b/res/values-af/strings.xml
index ba16dd3..ca59afa 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -20,6 +20,7 @@
<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">"Werk"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Program is nie geïnstalleer nie."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Program is nie beskikbaar nie"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 2fbdae1..a80ecb0 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -20,6 +20,7 @@
<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-as/strings.xml b/res/values-as/strings.xml
index 221921f..2984603 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -20,6 +20,7 @@
<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-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index e6fe3bd..883003c 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -20,6 +20,7 @@
<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">"Aplikacija nije instalirana."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikacija nije dostupna"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 2ccd34f..b4cf913 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -20,6 +20,7 @@
<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-bg/strings.xml b/res/values-bg/strings.xml
index f7d1d0f..408f205 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -20,6 +20,7 @@
<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-bn/strings.xml b/res/values-bn/strings.xml
index 0a72632..09608b5 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -92,7 +92,7 @@
<string name="title_change_settings" msgid="1376365968844349552">"সেটিংস পরিবর্তন করুন"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"বিজ্ঞপ্তির ডট দেখুন"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"হোম স্ক্রিনে আইকন যোগ করুন"</string>
- <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"নতুন অ্যাপের জন্য"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"নতুন অ্যাপ্লিকেশানগুলির জন্যে"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"অজানা"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"সরান"</string>
<string name="abandoned_search" msgid="891119232568284442">"সার্চ"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 57c5072..692b57d 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -20,6 +20,7 @@
<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">"Práce"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikace není nainstalována."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikace není k dispozici."</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 9097ffd..dc17516 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -20,6 +20,7 @@
<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">"Arbejde"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Appen er ikke installeret."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Appen er ikke tilgængelig"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 1602a53..a345bab 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -20,6 +20,7 @@
<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">"Arbeit"</string>
<string name="activity_not_found" msgid="8071924732094499514">"App ist nicht installiert."</string>
<string name="activity_not_available" msgid="7456344436509528827">"App nicht verfügbar"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
deleted file mode 100644
index 7adc218..0000000
--- a/res/values-en-rCA/strings.xml
+++ /dev/null
@@ -1,142 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2008 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<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="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>
- <string name="safemode_shortcut_error" msgid="9160126848219158407">"Downloaded app disabled in Safe mode"</string>
- <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets disabled in Safe mode"</string>
- <string name="shortcut_not_available" msgid="2536503539825726397">"Shortcut isn\'t available"</string>
- <string name="home_screen" msgid="806512411299847073">"Home screen"</string>
- <string name="custom_actions" msgid="3747508247759093328">"Customised actions"</string>
- <string name="long_press_widget_to_add" msgid="7699152356777458215">"Touch & hold to pick up a widget."</string>
- <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Double-tap & hold to pick up a widget or use customised actions."</string>
- <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
- <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Touch & hold to place manually"</string>
- <string name="place_automatically" msgid="8064208734425456485">"Add automatically"</string>
- <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Search apps"</string>
- <string name="all_apps_loading_message" msgid="5813968043155271636">"Loading apps…"</string>
- <string name="all_apps_no_search_results" msgid="3200346862396363786">"No apps found matching \'<xliff:g id="QUERY">%1$s</xliff:g>\'"</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Search for more apps"</string>
- <string name="label_application" msgid="8531721983832654978">"App"</string>
- <string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
- <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Touch & hold to pick up a shortcut."</string>
- <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Double-tap & hold to pick up a shortcut or use custom actions."</string>
- <string name="out_of_space" msgid="4691004494942118364">"No more room on this Home screen."</string>
- <string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favourites tray"</string>
- <string name="all_apps_button_label" msgid="8130441508702294465">"Apps list"</string>
- <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Personal apps list"</string>
- <string name="all_apps_button_work_label" msgid="7270707118948892488">"Work apps list"</string>
- <string name="all_apps_home_button_label" msgid="252062713717058851">"Home"</string>
- <string name="remove_drop_target_label" msgid="7812859488053230776">"Remove"</string>
- <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
- <string name="app_info_drop_target_label" msgid="692894985365717661">"App info"</string>
- <string name="install_drop_target_label" msgid="2539096853673231757">"Install"</string>
- <string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
- <string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
- <string name="permlab_read_settings" msgid="1941457408239617576">"read Home settings and shortcuts"</string>
- <string name="permdesc_read_settings" msgid="5833423719057558387">"Allows the app to read the settings and shortcuts in Home."</string>
- <string name="permlab_write_settings" msgid="3574213698004620587">"write Home settings and shortcuts"</string>
- <string name="permdesc_write_settings" msgid="5440712911516509985">"Allows the app to change the settings and shortcuts in Home."</string>
- <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> is not allowed to make phone calls"</string>
- <string name="gadget_error_text" msgid="6081085226050792095">"Problem loading widget"</string>
- <string name="gadget_setup_text" msgid="8274003207686040488">"Setup"</string>
- <string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
- <string name="folder_hint_text" msgid="6617836969016293992">"Unnamed Folder"</string>
- <string name="disabled_app_label" msgid="6673129024321402780">"Disabled <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
- <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifications</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notification</item>
- </plurals>
- <string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d of %2$d"</string>
- <string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d of %2$d"</string>
- <string name="workspace_new_page" msgid="257366611030256142">"New home screen page"</string>
- <string name="folder_opened" msgid="94695026776264709">"Folder opened, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
- <string name="folder_tap_to_close" msgid="4625795376335528256">"Tap to close folder"</string>
- <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tap to save rename"</string>
- <string name="folder_closed" msgid="4100806530910930934">"Folder closed"</string>
- <string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
- <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles & wallpapers"</string>
- <string name="settings_button_text" msgid="8873672322605444408">"Home settings"</string>
- <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Allow Home screen rotation"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
- <string name="notification_dots_title" msgid="9062440428204120317">"Notification dots"</string>
- <string name="notification_dots_desc_on" msgid="1679848116452218908">"On"</string>
- <string name="notification_dots_desc_off" msgid="1760796511504341095">"Off"</string>
- <string name="title_missing_notification_access" msgid="7503287056163941064">"Notification access needed"</string>
- <string name="msg_missing_notification_access" msgid="281113995110910548">"To show Notification Dots, turn on app notifications for <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="title_change_settings" msgid="1376365968844349552">"Change settings"</string>
- <string name="notification_dots_service_title" msgid="4284221181793592871">"Show notification dots"</string>
- <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Add icon to Home screen"</string>
- <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"For new apps"</string>
- <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
- <string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
- <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
- <string name="abandoned_promises_title" msgid="7096178467971716750">"This app is not installed"</string>
- <string name="abandoned_promise_explanation" msgid="3990027586878167529">"The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually."</string>
- <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloading, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
- <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> waiting to install"</string>
- <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> widgets"</string>
- <string name="widgets_list" msgid="796804551140113767">"Widgets list"</string>
- <string name="widgets_list_closed" msgid="6141506579418771922">"Widgets list closed"</string>
- <string name="action_add_to_workspace" msgid="8902165848117513641">"Add to Home screen"</string>
- <string name="action_move_here" msgid="2170188780612570250">"Move item here"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Item added to home screen"</string>
- <string name="item_removed" msgid="851119963877842327">"Item removed"</string>
- <string name="undo" msgid="4151576204245173321">"Undo"</string>
- <string name="action_move" msgid="4339390619886385032">"Move item"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
- <string name="move_to_position" msgid="6750008980455459790">"Move to position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="move_to_hotseat_position" msgid="6295412897075147808">"Move to favourites position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="item_moved" msgid="4606538322571412879">"Item moved"</string>
- <string name="add_to_folder" msgid="9040534766770853243">"Add to folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="add_to_folder_with_app" msgid="4534929978967147231">"Add to folder with <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="added_to_folder" msgid="4793259502305558003">"Item added to folder"</string>
- <string name="create_folder_with" msgid="4050141361160214248">"Create folder with: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_created" msgid="6409794597405184510">"Folder created"</string>
- <string name="action_move_to_workspace" msgid="1603837886334246317">"Move to Home screen"</string>
- <string name="action_resize" msgid="1802976324781771067">"Re-size"</string>
- <string name="action_increase_width" msgid="8773715375078513326">"Increase width"</string>
- <string name="action_increase_height" msgid="459390020612501122">"Increase height"</string>
- <string name="action_decrease_width" msgid="1374549771083094654">"Decrease width"</string>
- <string name="action_decrease_height" msgid="282377193880900022">"Decrease height"</string>
- <string name="widget_resized" msgid="9130327887929620">"Widget re-sized to width <xliff:g id="NUMBER_0">%1$s</xliff:g> height <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
- <string name="action_deep_shortcut" msgid="2864038805849372848">"Short cuts"</string>
- <string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Shortcuts and notifications"</string>
- <string name="action_dismiss_notification" msgid="5909461085055959187">"Dismiss"</string>
- <string name="notification_dismissed" msgid="6002233469409822874">"Notification dismissed"</string>
- <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
- <string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
- <string name="work_profile_toggle_label" msgid="3081029915775481146">"Work profile"</string>
- <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Find work apps here"</string>
- <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Each work app has a badge and is kept secure by your organisation. Move apps to your Home screen for easier access."</string>
- <string name="work_mode_on_label" msgid="4781128097185272916">"Managed by your organisation"</string>
- <string name="work_mode_off_label" msgid="3194894777601421047">"Notifications and apps are off"</string>
- <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Close"</string>
- <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Closed"</string>
- <string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
-</resources>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
deleted file mode 100644
index c741482..0000000
--- a/res/values-en-rXC/strings.xml
+++ /dev/null
@@ -1,142 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2008 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<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="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>
- <string name="safemode_shortcut_error" msgid="9160126848219158407">"Downloaded app disabled in Safe mode"</string>
- <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets disabled in Safe mode"</string>
- <string name="shortcut_not_available" msgid="2536503539825726397">"Shortcut isn\'t available"</string>
- <string name="home_screen" msgid="806512411299847073">"Home screen"</string>
- <string name="custom_actions" msgid="3747508247759093328">"Custom actions"</string>
- <string name="long_press_widget_to_add" msgid="7699152356777458215">"Touch & hold to pick up a widget."</string>
- <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Double-tap & hold to pick up a widget or use custom actions."</string>
- <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
- <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Touch & hold to place manually"</string>
- <string name="place_automatically" msgid="8064208734425456485">"Add automatically"</string>
- <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Search apps"</string>
- <string name="all_apps_loading_message" msgid="5813968043155271636">"Loading apps…"</string>
- <string name="all_apps_no_search_results" msgid="3200346862396363786">"No apps found matching \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="1366263386197059176">"Search for more apps"</string>
- <string name="label_application" msgid="8531721983832654978">"App"</string>
- <string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
- <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Touch & hold to pick up a shortcut."</string>
- <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Double-tap & hold to pick up a shortcut or use custom actions."</string>
- <string name="out_of_space" msgid="4691004494942118364">"No more room on this Home screen."</string>
- <string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favorites tray"</string>
- <string name="all_apps_button_label" msgid="8130441508702294465">"Apps list"</string>
- <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Personal apps list"</string>
- <string name="all_apps_button_work_label" msgid="7270707118948892488">"Work apps list"</string>
- <string name="all_apps_home_button_label" msgid="252062713717058851">"Home"</string>
- <string name="remove_drop_target_label" msgid="7812859488053230776">"Remove"</string>
- <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
- <string name="app_info_drop_target_label" msgid="692894985365717661">"App info"</string>
- <string name="install_drop_target_label" msgid="2539096853673231757">"Install"</string>
- <string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
- <string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
- <string name="permlab_read_settings" msgid="1941457408239617576">"read Home settings and shortcuts"</string>
- <string name="permdesc_read_settings" msgid="5833423719057558387">"Allows the app to read the settings and shortcuts in Home."</string>
- <string name="permlab_write_settings" msgid="3574213698004620587">"write Home settings and shortcuts"</string>
- <string name="permdesc_write_settings" msgid="5440712911516509985">"Allows the app to change the settings and shortcuts in Home."</string>
- <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> is not allowed to make phone calls"</string>
- <string name="gadget_error_text" msgid="6081085226050792095">"Problem loading widget"</string>
- <string name="gadget_setup_text" msgid="8274003207686040488">"Setup"</string>
- <string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
- <string name="folder_hint_text" msgid="6617836969016293992">"Unnamed Folder"</string>
- <string name="disabled_app_label" msgid="6673129024321402780">"Disabled <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
- <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifications</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notification</item>
- </plurals>
- <string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d of %2$d"</string>
- <string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d of %2$d"</string>
- <string name="workspace_new_page" msgid="257366611030256142">"New home screen page"</string>
- <string name="folder_opened" msgid="94695026776264709">"Folder opened, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
- <string name="folder_tap_to_close" msgid="4625795376335528256">"Tap to close folder"</string>
- <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tap to save rename"</string>
- <string name="folder_closed" msgid="4100806530910930934">"Folder closed"</string>
- <string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
- <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles & wallpapers"</string>
- <string name="settings_button_text" msgid="8873672322605444408">"Home settings"</string>
- <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Allow Home screen rotation"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
- <string name="notification_dots_title" msgid="9062440428204120317">"Notification dots"</string>
- <string name="notification_dots_desc_on" msgid="1679848116452218908">"On"</string>
- <string name="notification_dots_desc_off" msgid="1760796511504341095">"Off"</string>
- <string name="title_missing_notification_access" msgid="7503287056163941064">"Notification access needed"</string>
- <string name="msg_missing_notification_access" msgid="281113995110910548">"To show Notification Dots, turn on app notifications for <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="title_change_settings" msgid="1376365968844349552">"Change settings"</string>
- <string name="notification_dots_service_title" msgid="4284221181793592871">"Show notification dots"</string>
- <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Add icon to Home screen"</string>
- <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"For new apps"</string>
- <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
- <string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
- <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
- <string name="abandoned_promises_title" msgid="7096178467971716750">"This app is not installed"</string>
- <string name="abandoned_promise_explanation" msgid="3990027586878167529">"The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually."</string>
- <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloading, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
- <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> waiting to install"</string>
- <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> widgets"</string>
- <string name="widgets_list" msgid="796804551140113767">"Widgets list"</string>
- <string name="widgets_list_closed" msgid="6141506579418771922">"Widgets list closed"</string>
- <string name="action_add_to_workspace" msgid="8902165848117513641">"Add to Home screen"</string>
- <string name="action_move_here" msgid="2170188780612570250">"Move item here"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Item added to home screen"</string>
- <string name="item_removed" msgid="851119963877842327">"Item removed"</string>
- <string name="undo" msgid="4151576204245173321">"Undo"</string>
- <string name="action_move" msgid="4339390619886385032">"Move item"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
- <string name="move_to_position" msgid="6750008980455459790">"Move to position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="move_to_hotseat_position" msgid="6295412897075147808">"Move to favorites position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="item_moved" msgid="4606538322571412879">"Item moved"</string>
- <string name="add_to_folder" msgid="9040534766770853243">"Add to folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="add_to_folder_with_app" msgid="4534929978967147231">"Add to folder with <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="added_to_folder" msgid="4793259502305558003">"Item added to folder"</string>
- <string name="create_folder_with" msgid="4050141361160214248">"Create folder with: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_created" msgid="6409794597405184510">"Folder created"</string>
- <string name="action_move_to_workspace" msgid="1603837886334246317">"Move to Home screen"</string>
- <string name="action_resize" msgid="1802976324781771067">"Resize"</string>
- <string name="action_increase_width" msgid="8773715375078513326">"Increase width"</string>
- <string name="action_increase_height" msgid="459390020612501122">"Increase height"</string>
- <string name="action_decrease_width" msgid="1374549771083094654">"Decrease width"</string>
- <string name="action_decrease_height" msgid="282377193880900022">"Decrease height"</string>
- <string name="widget_resized" msgid="9130327887929620">"Widget resized to width <xliff:g id="NUMBER_0">%1$s</xliff:g> height <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
- <string name="action_deep_shortcut" msgid="2864038805849372848">"Shortcuts"</string>
- <string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Shortcuts and notifications"</string>
- <string name="action_dismiss_notification" msgid="5909461085055959187">"Dismiss"</string>
- <string name="notification_dismissed" msgid="6002233469409822874">"Notification dismissed"</string>
- <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
- <string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
- <string name="work_profile_toggle_label" msgid="3081029915775481146">"Work profile"</string>
- <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Find work apps here"</string>
- <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Each work app has a badge and is kept secure by your organization. Move apps to your Home screen for easier access."</string>
- <string name="work_mode_on_label" msgid="4781128097185272916">"Managed by your organization"</string>
- <string name="work_mode_off_label" msgid="3194894777601421047">"Notifications and apps are off"</string>
- <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Close"</string>
- <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Closed"</string>
- <string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
-</resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 960ef93..d10c84b 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -20,6 +20,7 @@
<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">"Trabajo"</string>
<string name="activity_not_found" msgid="8071924732094499514">"No se instaló la aplicación."</string>
<string name="activity_not_available" msgid="7456344436509528827">"La aplicación no está disponible."</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 01f60e4..09b1239 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -20,6 +20,7 @@
<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">"Trabajo"</string>
<string name="activity_not_found" msgid="8071924732094499514">"La aplicación no está instalada."</string>
<string name="activity_not_available" msgid="7456344436509528827">"La aplicación no está disponible"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index ff121fa..1e470e1 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -20,6 +20,7 @@
<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">"Töö"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Rakendus pole installitud."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Rakendus ei ole saadaval"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 913ff48..926cdb9 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -20,6 +20,7 @@
<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-fi/strings.xml b/res/values-fi/strings.xml
index e0930b6..f87441f 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -20,6 +20,7 @@
<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">"Työ"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Sovellusta ei ole asennettu."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Sovellus ei ole käytettävissä"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 50d6a06..5ac514d 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -20,6 +20,7 @@
<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">"Lanceur3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Travail"</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>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index da9da38..3228dea 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -80,7 +80,7 @@
<string name="widget_button_text" msgid="2880537293434387943">"વિજેટ્સ"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"વૉલપેપર્સ"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"શૈલીઓ અને વૉલપેપર"</string>
- <string name="settings_button_text" msgid="8873672322605444408">"હોમ સેટિંગ"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"હોમ સેટિંગ્સ"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"તમારા વ્યવસ્થાપક દ્વારા અક્ષમ કરેલ"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"હોમ સ્ક્રીનને ફેરવવાની મંજૂરી આપો"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"જ્યારે ફોન ફેરવવામાં આવે ત્યારે"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 7e26ca4..3327590 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -20,6 +20,7 @@
<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">"Munka"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Az alkalmazás nincs telepítve."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Az alkalmazás nem érhető el"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 9d26c8c..d66ca32 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -20,6 +20,7 @@
<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">"Vinna"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Forritið er ekki uppsett."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Forritið er ekki í boði"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index aaceb53..8c4e3c5 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -20,6 +20,7 @@
<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">"Lavoro"</string>
<string name="activity_not_found" msgid="8071924732094499514">"App non installata."</string>
<string name="activity_not_available" msgid="7456344436509528827">"App non disponibile"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 3525699..c25e879 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -20,6 +20,7 @@
<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-ja/strings.xml b/res/values-ja/strings.xml
index dad4879..354f020 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -20,6 +20,7 @@
<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-ka/strings.xml b/res/values-ka/strings.xml
index b348afb..e9afef9 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -20,6 +20,7 @@
<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-kk/strings.xml b/res/values-kk/strings.xml
index bd25ac9..1cd9045 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -20,6 +20,7 @@
<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-ko/strings.xml b/res/values-ko/strings.xml
index e2767f9..58b65da 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -20,6 +20,7 @@
<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-lo/strings.xml b/res/values-lo/strings.xml
index e764347..afe7664 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -20,6 +20,7 @@
<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-lt/strings.xml b/res/values-lt/strings.xml
index 7f93ac8..6571582 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -20,6 +20,7 @@
<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">"Darbas"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Programa neįdiegta."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Programa nepasiekiama"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 13d88fe..3da0fbb 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -20,6 +20,7 @@
<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">"Darbs"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Lietotne nav instalēta."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Lietotne nav pieejama."</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 458fb73..ab14e89 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -80,9 +80,9 @@
<string name="widget_button_text" msgid="2880537293434387943">"Виџети"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Тапети"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стилови и тапети"</string>
- <string name="settings_button_text" msgid="8873672322605444408">"Поставки за почетен екран"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Поставки за Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Оневозможено од администраторот"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Дозволете ротација на почетниот екран"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"Дозволете ротација на Почетниот екран"</string>
<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>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index a118368..4362e7c 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -20,6 +20,7 @@
<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-mn/strings.xml b/res/values-mn/strings.xml
index 1b0e753..ab02ac6 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -20,6 +20,7 @@
<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-mr/strings.xml b/res/values-mr/strings.xml
index f71bbd9..49e3898 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -20,6 +20,7 @@
<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>
@@ -82,7 +83,7 @@
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"शैली आणि वॉलपेपर"</string>
<string name="settings_button_text" msgid="8873672322605444408">"होम सेटिंग्ज"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"आपल्या प्रशासकाने अक्षम केले"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"मुख्य स्क्रीन फिरविण्यास अनुमती द्या"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"मुख्यस्क्रीन फिरविण्यास अनुमती द्या"</string>
<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>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 492e6b6..7d05412 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -20,6 +20,7 @@
<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">"Kerja"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Apl tidak dipasang."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Apl tidak tersedia"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 356ed03..2257367 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -20,6 +20,7 @@
<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">"Jobb"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Appen er ikke installert."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Appen er ikke tilgjengelig"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 58d61f9..1e7aee9 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -20,6 +20,7 @@
<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-nl/strings.xml b/res/values-nl/strings.xml
index c2dfb71..ec30d8c 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -20,6 +20,7 @@
<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">"Werk"</string>
<string name="activity_not_found" msgid="8071924732094499514">"App is niet geïnstalleerd."</string>
<string name="activity_not_available" msgid="7456344436509528827">"App is niet beschikbaar"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 37bd2d5..4ddc903 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -20,6 +20,7 @@
<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>
@@ -84,7 +85,7 @@
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ଆପଣଙ୍କ ଆଡମିନଙ୍କ ଦ୍ୱାରା ଅକ୍ଷମ କରାଯାଇଛି"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"ହୋମ୍ ସ୍କ୍ରୀନ୍ ବୁଲାଇବା ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"ଯେତେବେଳେ ଫୋନକୁ ବୁଲାଯାଇଥାଏ"</string>
- <string name="notification_dots_title" msgid="9062440428204120317">"ବିଜ୍ଞପ୍ତି ଡଟ୍ସ"</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="title_missing_notification_access" msgid="7503287056163941064">"ବିଜ୍ଞପ୍ତି ଆକ୍ସେସ୍ ଆବଶ୍ୟକ ଅଟେ"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 24686c4..d4cd5be 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -20,6 +20,7 @@
<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-pl/strings.xml b/res/values-pl/strings.xml
index 5d9b50e..6885960 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -20,6 +20,7 @@
<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">"Praca"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikacja nie jest zainstalowana."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikacja niedostępna"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 1cc699f..23b00d0 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -20,6 +20,7 @@
<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-si/strings.xml b/res/values-si/strings.xml
index 2f9dc42..ef99a59 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -20,6 +20,7 @@
<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-sl/strings.xml b/res/values-sl/strings.xml
index 417151b..044a4b4 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -20,6 +20,7 @@
<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">"Služba"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikacija ni nameščena."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikacija ni na voljo"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 5e2c954..4e52592 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -20,6 +20,7 @@
<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">"Апликација није инсталирана."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Апликација није доступна"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 7df507b..e7400a6 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -20,6 +20,7 @@
<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">"Arbete"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Appen är inte installerad."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Appen är inte tillgänglig"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 799b6b8..6e933a1 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -36,7 +36,7 @@
<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_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" உடன் பொருந்தும் ஆப்ஸ் இல்லை"</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>
<string name="notifications_header" msgid="1404149926117359025">"அறிவிப்புகள்"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index de4863a..0df94c7 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -20,6 +20,7 @@
<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">"Trabaho"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Hindi naka-install ang app."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Hindi available ang app"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 1b2b3d1..56f8447 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -20,6 +20,7 @@
<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">"Uygulama yüklü değil."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Uygulama kullanılamıyor"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 622cdcc..13ba701 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -20,6 +20,7 @@
<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-ur/strings.xml b/res/values-ur/strings.xml
index 74b5eaa..4f77670 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -20,6 +20,7 @@
<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-uz/strings.xml b/res/values-uz/strings.xml
index dac1ac9..69084d7 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -20,6 +20,7 @@
<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">"Ishga oid"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Ilova o‘rnatilmadi."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Ilova mavjud emas"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 4dc58b9..71decfc 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -20,6 +20,7 @@
<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">"Trình chạy 3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Ứng dụng chưa được cài đặt."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Ứng dụng không có sẵn"</string>
@@ -85,7 +86,7 @@
<string name="allow_rotation_title" msgid="7728578836261442095">"Cho phép xoay Màn hình chính"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Khi xoay điện thoại"</string>
<string name="notification_dots_title" msgid="9062440428204120317">"Dấu chấm thông báo"</string>
- <string name="notification_dots_desc_on" msgid="1679848116452218908">"Đang bật"</string>
+ <string name="notification_dots_desc_on" msgid="1679848116452218908">"Bật"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Tắt"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Cần quyền truy cập thông báo"</string>
<string name="msg_missing_notification_access" msgid="281113995110910548">"Để hiển thị Dấu chấm thông báo, hãy bật thông báo ứng dụng cho <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 0478627..9804af1 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -20,6 +20,7 @@
<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">"未安装该应用。"</string>
<string name="activity_not_available" msgid="7456344436509528827">"应用不可用"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index ed53c32..e737744 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -20,6 +20,7 @@
<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-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 14f2e06..e971b69 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -20,6 +20,7 @@
<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/config.xml b/res/values/config.xml
index 0387184..2a1f6f7 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -1,5 +1,5 @@
<resources>
-<!-- Miscellaneous -->
+ <!-- Miscellaneous -->
<bool name="config_largeHeap">false</bool>
<bool name="is_tablet">false</bool>
<bool name="is_large_tablet">false</bool>
@@ -21,10 +21,10 @@
<!-- String representing the fragment class for settings activity.-->
<string name="settings_fragment_name" translatable="false">com.android.launcher3.settings.SettingsActivity$LauncherSettingsFragment</string>
-<!-- DragController -->
+ <!-- DragController -->
<item type="id" name="drag_event_parity" />
-<!-- AllApps & Launcher transitions -->
+ <!-- AllApps & Launcher transitions -->
<!-- Out of 100, the percent to shrink the workspace during spring loaded mode. -->
<integer name="config_workspaceSpringLoadShrinkPercentage">90</integer>
@@ -34,7 +34,7 @@
<!-- View tag key used to store SpringAnimation data. -->
<item type="id" name="spring_animation_tag" />
-<!-- Workspace -->
+ <!-- Workspace -->
<!-- The duration (in ms) of the fade animation on the object outlines, used when
we are dragging objects around on the home screen. -->
<integer name="config_dragOutlineFadeTime">900</integer>
@@ -57,29 +57,20 @@
<!-- The duration of the caret animation -->
<integer name="config_caretAnimationDuration">200</integer>
-<!-- Hotseat -->
+ <!-- Hotseat -->
<bool name="hotseat_transpose_layout_with_orientation">true</bool>
<!-- Various classes overriden by projects/build flavors. -->
<string name="app_filter_class" translatable="false"></string>
- <string name="icon_provider_class" translatable="false"></string>
- <string name="drawable_factory_class" translatable="false"></string>
<string name="user_event_dispatcher_class" translatable="false"></string>
<string name="stats_log_manager_class" translatable="false"></string>
<string name="app_transition_manager_class" translatable="false"></string>
<string name="instant_app_resolver_class" translatable="false"></string>
<string name="main_process_initializer_class" translatable="false"></string>
- <string name="system_shortcut_factory_class" translatable="false"></string>
<string name="app_launch_tracker_class" translatable="false"></string>
<string name="test_information_handler_class" translatable="false"></string>
<string name="launcher_activity_logic_class" translatable="false"></string>
- <!-- Package name of the default wallpaper picker. -->
- <string name="wallpaper_picker_package" translatable="false"></string>
-
- <!-- Whitelisted package to retrieve packagename for badge. Can be empty. -->
- <string name="shortcutinfo_badgepkg_whitelist" translatable="false"></string>
-
<!-- View ID to use for QSB widget -->
<item type="id" name="qsb_widget" />
@@ -97,7 +88,12 @@
<integer name="config_popupArrowOpenCloseDuration">40</integer>
<integer name="config_removeNotificationViewDuration">300</integer>
-<!-- Accessibility actions -->
+ <!-- Default packages -->
+ <string name="wallpaper_picker_package" translatable="false"></string>
+ <string name="calendar_component_name" translatable="false"></string>
+ <string name="clock_component_name" translatable="false"></string>
+
+ <!-- Accessibility actions -->
<item type="id" name="action_remove" />
<item type="id" name="action_uninstall" />
<item type="id" name="action_reconfigure" />
@@ -112,10 +108,10 @@
<item type="id" name="action_dismiss_notification" />
<item type="id" name="action_remote_action_shortcut" />
-<!-- QSB IDs. DO not change -->
+ <!-- QSB IDs. DO not change -->
<item type="id" name="search_container_workspace" />
<item type="id" name="search_container_all_apps" />
-<!-- Recents -->
+ <!-- Recents -->
<item type="id" name="overview_panel"/>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9d9c2e8..dec8939 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -102,9 +102,11 @@
<string name="app_info_drop_target_label">App info</string>
<!-- Label for install drop target. [CHAR_LIMIT=20] -->
<string name="install_drop_target_label">Install</string>
-
<!-- Label for install dismiss prediction. -->
<string translatable="false" name="dismiss_prediction_label">Dismiss prediction</string>
+ <!-- Label for pinning predicted app. -->
+ <string name="pin_prediction" translatable="false">Pin Prediction</string>
+
<!-- Permissions: -->
<skip />
diff --git a/robolectric_tests/src/com/android/launcher3/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 bc936b7..5b6d94d 100644
--- a/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
+++ b/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
@@ -15,17 +15,20 @@
import android.os.Process;
import android.os.UserHandle;
+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.model.BgDataModel.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;
@@ -44,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.
*/
@@ -79,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);
@@ -200,15 +202,14 @@
CacheEntry entry = mCache.get(new ComponentKey(componentName, user));
if (entry == null) {
entry = new CacheEntry();
- getDefaultIcon(user).applyTo(entry);
+ entry.bitmap = getDefaultIcon(user);
}
return entry;
}
public void addCache(ComponentName key, String title) {
CacheEntry entry = new CacheEntry();
- entry.icon = newIcon();
- entry.color = Color.RED;
+ entry.bitmap = BitmapInfo.of(newIcon(), Color.RED);
entry.title = title;
mCache.put(new ComponentKey(key, Process.myUserHandle()), entry);
}
@@ -216,5 +217,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..69c5b00 100644
--- a/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -2,16 +2,15 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import com.android.launcher3.AppInfo;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.icons.BitmapInfo;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@@ -41,11 +40,10 @@
}
@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) {
- info.iconBitmap = null;
+ info.bitmap = BitmapInfo.LOW_RES_INFO;
}
executeTaskForTest(newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, "app1"));
@@ -58,15 +56,14 @@
assertFalse(allAppsList.data.isEmpty());
for (AppInfo info : allAppsList.data) {
if (info.componentName.getPackageName().equals("app1")) {
- assertNotNull(info.iconBitmap);
+ assertFalse(info.bitmap.isNullOrLowRes());
} else {
- assertNull(info.iconBitmap);
+ assertTrue(info.bitmap.isNullOrLowRes());
}
}
}
@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"));
@@ -89,10 +85,10 @@
for (ItemInfo info : bgDataModel.itemsIdMap) {
if (updates.contains(info.id)) {
assertEquals(NEW_LABEL_PREFIX + info.id, info.title);
- assertNotNull(((WorkspaceItemInfo) info).iconBitmap);
+ assertFalse(((WorkspaceItemInfo) info).bitmap.isNullOrLowRes());
} else {
assertNotSame(NEW_LABEL_PREFIX + info.id, info.title);
- assertNull(((WorkspaceItemInfo) info).iconBitmap);
+ assertTrue(((WorkspaceItemInfo) info).bitmap.isNullOrLowRes());
}
}
}
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/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/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/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index b28077f..382bfdf 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -35,7 +35,7 @@
import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate;
-import com.android.launcher3.uioverrides.UiFactory;
+import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.ViewCache;
@@ -81,19 +81,39 @@
protected StatsLogManager mStatsLogManager;
protected SystemUiController mSystemUiController;
- private static final int ACTIVITY_STATE_STARTED = 1 << 0;
- private static final int ACTIVITY_STATE_RESUMED = 1 << 1;
+
+ public static final int ACTIVITY_STATE_STARTED = 1 << 0;
+ public static final int ACTIVITY_STATE_RESUMED = 1 << 1;
+
/**
- * State flag indicating if the user is active or the actitvity when to background as a result
+ * State flags indicating that the activity has received one frame after resume, and was
+ * not immediately paused.
+ */
+ public static final int ACTIVITY_STATE_DEFERRED_RESUMED = 1 << 2;
+
+ public static final int ACTIVITY_STATE_WINDOW_FOCUSED = 1 << 3;
+
+ /**
+ * State flag indicating if the user is active or the activity when to background as a result
* of user action.
* @see #isUserActive()
*/
- private static final int ACTIVITY_STATE_USER_ACTIVE = 1 << 2;
+ public static final int ACTIVITY_STATE_USER_ACTIVE = 1 << 4;
+
+ /**
+ * State flag indicating that a state transition is in progress
+ */
+ public static final int ACTIVITY_STATE_TRANSITION_ACTIVE = 1 << 5;
@Retention(SOURCE)
@IntDef(
flag = true,
- value = {ACTIVITY_STATE_STARTED, ACTIVITY_STATE_RESUMED, ACTIVITY_STATE_USER_ACTIVE})
+ value = {ACTIVITY_STATE_STARTED,
+ ACTIVITY_STATE_RESUMED,
+ ACTIVITY_STATE_DEFERRED_RESUMED,
+ ACTIVITY_STATE_WINDOW_FOCUSED,
+ ACTIVITY_STATE_USER_ACTIVE,
+ ACTIVITY_STATE_TRANSITION_ACTIVE})
public @interface ActivityFlags{}
@ActivityFlags
@@ -146,19 +166,19 @@
@Override
protected void onStart() {
- mActivityFlags |= ACTIVITY_STATE_STARTED;
+ addActivityFlags(ACTIVITY_STATE_STARTED);
super.onStart();
}
@Override
protected void onResume() {
- mActivityFlags |= ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE;
+ addActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE);
super.onResume();
}
@Override
protected void onUserLeaveHint() {
- mActivityFlags &= ~ACTIVITY_STATE_USER_ACTIVE;
+ removeActivityFlags(ACTIVITY_STATE_USER_ACTIVE);
super.onUserLeaveHint();
}
@@ -172,7 +192,7 @@
@Override
protected void onStop() {
- mActivityFlags &= ~ACTIVITY_STATE_STARTED & ~ACTIVITY_STATE_USER_ACTIVE;
+ removeActivityFlags(ACTIVITY_STATE_STARTED | ACTIVITY_STATE_USER_ACTIVE);
mForceInvisible = 0;
super.onStop();
@@ -183,7 +203,7 @@
@Override
protected void onPause() {
- mActivityFlags &= ~ACTIVITY_STATE_RESUMED;
+ removeActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_DEFERRED_RESUMED);
super.onPause();
// Reset the overridden sysui flags used for the task-swipe launch animation, we do this
@@ -193,6 +213,17 @@
getSystemUiController().updateUiState(UI_STATE_OVERVIEW, 0);
}
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ if (hasFocus) {
+ addActivityFlags(ACTIVITY_STATE_WINDOW_FOCUSED);
+ } else {
+ removeActivityFlags(ACTIVITY_STATE_WINDOW_FOCUSED);
+ }
+
+ }
+
public boolean isStarted() {
return (mActivityFlags & ACTIVITY_STATE_STARTED) != 0;
}
@@ -208,6 +239,22 @@
return (mActivityFlags & ACTIVITY_STATE_USER_ACTIVE) != 0;
}
+ public int getActivityFlags() {
+ return mActivityFlags;
+ }
+
+ protected void addActivityFlags(int flags) {
+ mActivityFlags |= flags;
+ onActivityFlagsChanged(flags);
+ }
+
+ protected void removeActivityFlags(int flags) {
+ mActivityFlags &= ~flags;
+ onActivityFlagsChanged(flags);
+ }
+
+ protected void onActivityFlagsChanged(int changeBits) { }
+
public void addOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
mDPChangeListeners.add(listener);
}
@@ -233,7 +280,7 @@
/**
* Used to set the override visibility state, used only to handle the transition home with the
* recents animation.
- * @see QuickstepAppTransitionManagerImpl#getWallpaperOpenRunner()
+ * @see QuickstepAppTransitionManagerImpl#getWallpaperOpenRunner
*/
public void addForceInvisibleFlag(@InvisibilityFlags int flag) {
mForceInvisible |= flag;
@@ -260,7 +307,7 @@
@Override
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
- if (!UiFactory.dumpActivity(this, writer)) {
+ if (!ApiWrapper.dumpActivity(this, writer)) {
super.dump(prefix, fd, writer, args);
}
}
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 994ba65..772eb00 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;
@@ -33,13 +34,13 @@
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.uioverrides.DisplayRotationListener;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.util.TraceHelper;
/**
* Extension of BaseActivity allowing support for drag-n-drop
@@ -56,7 +57,7 @@
private ActionMode mCurrentActionMode;
protected boolean mIsSafeModeEnabled;
- private OnStartCallback mOnStartCallback;
+ private Runnable mOnStartCallback;
private int mThemeRes = R.style.AppTheme;
@@ -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
@@ -166,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);
@@ -222,7 +226,7 @@
super.onStart();
if (mOnStartCallback != null) {
- mOnStartCallback.onActivityStart(this);
+ mOnStartCallback.run();
mOnStartCallback = null;
}
}
@@ -234,8 +238,12 @@
mRotationListener.disable();
}
- public <T extends BaseDraggingActivity> void setOnStartCallback(OnStartCallback<T> callback) {
- mOnStartCallback = callback;
+ public void runOnceOnStart(Runnable action) {
+ mOnStartCallback = action;
+ }
+
+ public void clearRunOnceOnStartCallback() {
+ mOnStartCallback = null;
}
protected void onDeviceProfileInitiated() {
@@ -254,12 +262,4 @@
}
protected abstract void reapplyUi();
-
- /**
- * Callback for listening for onStart
- */
- public interface OnStartCallback<T extends BaseDraggingActivity> {
-
- void onActivityStart(T activity);
- }
}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 7adb6a4..e6f8a85 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -16,6 +16,8 @@
package com.android.launcher3;
+import static com.android.launcher3.FastBitmapDrawable.newIcon;
+import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import android.animation.Animator;
@@ -45,7 +47,6 @@
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.graphics.PreloadIconDrawable;
@@ -215,6 +216,7 @@
cancelDotScaleAnim();
mDotParams.scale = 0f;
mForceHideDot = false;
+ setBackground(null);
}
private void cancelDotScaleAnim() {
@@ -287,9 +289,8 @@
}
private void applyIconAndLabel(ItemInfoWithIcon info) {
- FastBitmapDrawable iconDrawable = DrawableFactory.INSTANCE.get(getContext())
- .newIcon(getContext(), info);
- mDotParams.color = IconPalette.getMutedColor(info.iconColor, 0.54f);
+ FastBitmapDrawable iconDrawable = newIcon(getContext(), info);
+ mDotParams.color = IconPalette.getMutedColor(info.bitmap.color, 0.54f);
setIcon(iconDrawable);
setText(info.title);
@@ -496,7 +497,8 @@
// Text should be visible everywhere but the hotseat.
Object tag = getParent() instanceof FolderIcon ? ((View) getParent()).getTag() : getTag();
ItemInfo info = tag instanceof ItemInfo ? (ItemInfo) tag : null;
- return info == null || info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+ return info == null || (info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT
+ && info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION);
}
public void setTextVisibility(boolean visible) {
@@ -567,8 +569,7 @@
preloadDrawable = (PreloadIconDrawable) mIcon;
preloadDrawable.setLevel(progressLevel);
} else {
- preloadDrawable = DrawableFactory.INSTANCE.get(getContext())
- .newPendingIcon(getContext(), info);
+ preloadDrawable = newPendingIcon(getContext(), info);
preloadDrawable.setLevel(progressLevel);
setIcon(preloadDrawable);
}
@@ -665,7 +666,7 @@
mDisableRelayout = true;
// Optimization: Starting in N, pre-uploads the bitmap to RenderThread.
- info.iconBitmap.prepareToDraw();
+ info.bitmap.icon.prepareToDraw();
if (info instanceof AppInfo) {
applyFromApplicationInfo((AppInfo) info);
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index a90025e..5091684 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import android.animation.ObjectAnimator;
+import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -35,6 +36,7 @@
import android.util.Property;
import android.util.SparseArray;
+import com.android.launcher3.graphics.PlaceHolderIconDrawable;
import com.android.launcher3.icons.BitmapInfo;
public class FastBitmapDrawable extends Drawable {
@@ -98,10 +100,6 @@
this(info.icon, info.color);
}
- public FastBitmapDrawable(ItemInfoWithIcon info) {
- this(info.iconBitmap, info.iconColor);
- }
-
protected FastBitmapDrawable(Bitmap b, int iconColor) {
this(b, iconColor, false);
}
@@ -365,7 +363,7 @@
}
@Override
- public Drawable newDrawable() {
+ public FastBitmapDrawable newDrawable() {
return new FastBitmapDrawable(mBitmap, mIconColor, mIsDisabled);
}
@@ -374,4 +372,37 @@
return 0;
}
}
+
+ /**
+ * Interface to be implemented by custom {@link BitmapInfo} to handle drawable construction
+ */
+ public interface Factory {
+
+ /**
+ * Called to create a new drawable
+ */
+ FastBitmapDrawable newDrawable();
+ }
+
+ /**
+ * Returns a FastBitmapDrawable with the icon.
+ */
+ public static FastBitmapDrawable newIcon(Context context, ItemInfoWithIcon info) {
+ FastBitmapDrawable drawable = newIcon(context, info.bitmap);
+ drawable.setIsDisabled(info.isDisabled());
+ return drawable;
+ }
+
+ /**
+ * Creates a drawable for the provided BitmapInfo
+ */
+ public static FastBitmapDrawable newIcon(Context context, BitmapInfo info) {
+ if (info instanceof Factory) {
+ return ((Factory) info).newDrawable();
+ } else if (info.isLowRes()) {
+ return new PlaceHolderIconDrawable(info, context);
+ } else {
+ return new FastBitmapDrawable(info);
+ }
+ }
}
diff --git a/src/com/android/launcher3/IconProvider.java b/src/com/android/launcher3/IconProvider.java
deleted file mode 100644
index 0f006f7..0000000
--- a/src/com/android/launcher3/IconProvider.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.android.launcher3;
-
-import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
-
-import android.content.pm.LauncherActivityInfo;
-import android.graphics.drawable.Drawable;
-
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.ResourceBasedOverride;
-
-public class IconProvider implements ResourceBasedOverride {
-
- public static MainThreadInitializedObject<IconProvider> INSTANCE =
- forOverride(IconProvider.class, R.string.icon_provider_class);
-
- public IconProvider() { }
-
- public String getSystemStateForPackage(String systemState, String packageName) {
- return systemState;
- }
-
- /**
- * @param flattenDrawable true if the caller does not care about the specification of the
- * original icon as long as the flattened version looks the same.
- */
- public Drawable getIcon(LauncherActivityInfo info, int iconDpi, boolean flattenDrawable) {
- return info.getIcon(iconDpi);
- }
-}
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index fe91602..21359f1 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -17,6 +17,7 @@
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;
@@ -27,6 +28,7 @@
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;
@@ -42,8 +44,8 @@
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
-import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.LauncherIcons;
@@ -71,7 +73,6 @@
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.
@@ -112,8 +113,7 @@
@WorkerThread
private static void flushQueueInBackground(Context context) {
- LauncherModel model = LauncherAppState.getInstance(context).getModel();
- if (model.getCallback() == null) {
+ if (Launcher.ACTIVITY_TRACKER.getCreatedActivity() == null) {
// Launcher not loaded
return;
}
@@ -126,7 +126,7 @@
return;
}
- LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
+ LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
for (String encoded : strings) {
PendingInstallShortcutInfo info = decode(encoded, context);
if (info == null) {
@@ -135,7 +135,7 @@
String pkg = getIntentPackage(info.launchIntent);
if (!TextUtils.isEmpty(pkg)
- && !launcherApps.isPackageEnabledForProfile(pkg, info.user)
+ && !launcherApps.isPackageEnabled(pkg, info.user)
&& !info.isActivity) {
if (DBG) {
Log.d(TAG, "Ignoring shortcut for absent package: " + info.launchIntent);
@@ -148,7 +148,8 @@
}
prefs.edit().remove(APPS_PENDING_INSTALL).apply();
if (!installQueue.isEmpty()) {
- model.addAndBindAddedWorkspaceItems(installQueue);
+ LauncherAppState.getInstance(context).getModel()
+ .addAndBindAddedWorkspaceItems(installQueue);
}
}
@@ -490,9 +491,13 @@
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();
+ if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
+ fetchAndUpdateShortcutIconAsync(mContext, itemInfo, shortcutInfo, true);
+ } else {
+ LauncherIcons li = LauncherIcons.obtain(mContext);
+ itemInfo.bitmap = li.createShortcutIcon(shortcutInfo);
+ li.recycle();
+ }
return Pair.create(itemInfo, shortcutInfo);
} else if (providerInfo != null) {
LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
@@ -527,7 +532,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,7 +622,7 @@
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;
@@ -662,7 +667,7 @@
if (iconInfo == null) {
iconInfo = app.getIconCache().getDefaultIcon(info.user);
}
- info.applyFrom(iconInfo);
+ info.bitmap = iconInfo;
info.title = Utilities.trim(name);
info.contentDescription = app.getContext().getPackageManager()
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index d66ba73..9d87152 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -104,8 +104,6 @@
public int iconBitmapSize;
public int fillResIconDpi;
public float iconTextSize;
- public float allAppsIconSize;
- public float allAppsIconTextSize;
private SparseArray<TypedValue> mExtraAttrs;
@@ -146,8 +144,6 @@
iconTextSize = p.iconTextSize;
numHotseatIcons = p.numHotseatIcons;
numAllAppsColumns = p.numAllAppsColumns;
- allAppsIconSize = p.allAppsIconSize;
- allAppsIconTextSize = p.allAppsIconTextSize;
defaultLayoutId = p.defaultLayoutId;
demoModeLayoutId = p.demoModeLayoutId;
mExtraAttrs = p.mExtraAttrs;
diff --git a/src/com/android/launcher3/ItemInfoWithIcon.java b/src/com/android/launcher3/ItemInfoWithIcon.java
index 1550bb0..1941455 100644
--- a/src/com/android/launcher3/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/ItemInfoWithIcon.java
@@ -16,10 +16,6 @@
package com.android.launcher3;
-import static com.android.launcher3.icons.BitmapInfo.LOW_RES_ICON;
-
-import android.graphics.Bitmap;
-
import com.android.launcher3.icons.BitmapInfo;
/**
@@ -30,14 +26,9 @@
public static final String TAG = "ItemInfoDebug";
/**
- * A bitmap version of the application icon.
+ * The bitmap for the application icon
*/
- public Bitmap iconBitmap;
-
- /**
- * Dominant color in the {@link #iconBitmap}.
- */
- public int iconColor;
+ public BitmapInfo bitmap = BitmapInfo.LOW_RES_INFO;
/**
* Indicates that the icon is disabled due to safe mode restrictions.
@@ -106,8 +97,7 @@
protected ItemInfoWithIcon(ItemInfoWithIcon info) {
super(info);
- iconBitmap = info.iconBitmap;
- iconColor = info.iconColor;
+ bitmap = info.bitmap;
runtimeStatusFlags = info.runtimeStatusFlags;
}
@@ -120,12 +110,7 @@
* Indicates whether we're using a low res icon
*/
public boolean usingLowResIcon() {
- return iconBitmap == LOW_RES_ICON;
- }
-
- public void applyFrom(BitmapInfo info) {
- iconBitmap = info.icon;
- iconColor = info.color;
+ return bitmap.isLowRes();
}
/**
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 8086045..cb8fe05 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -27,12 +27,15 @@
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.popup.SystemShortcut.APP_INFO;
+import static com.android.launcher3.popup.SystemShortcut.DISMISS_PREDICTION;
+import static com.android.launcher3.popup.SystemShortcut.INSTALL;
+import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
-import static com.android.launcher3.util.RaceConditionTracker.ENTER;
-import static com.android.launcher3.util.RaceConditionTracker.EXIT;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -58,6 +61,7 @@
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Parcelable;
import android.os.Process;
@@ -79,9 +83,12 @@
import android.widget.Toast;
import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.LauncherState.ScaleAndTranslation;
+import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsStore;
@@ -89,7 +96,6 @@
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;
@@ -110,18 +116,21 @@
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.popup.SystemShortcut;
import com.android.launcher3.qsb.QsbContainerView;
-import com.android.launcher3.states.InternalStateHandler;
import com.android.launcher3.states.RotationHelper;
+import com.android.launcher3.touch.AllAppsSwipeController;
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;
@@ -130,11 +139,11 @@
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;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.util.ViewOnDrawExecutor;
@@ -150,6 +159,12 @@
import com.android.launcher3.widget.WidgetListRowEntry;
import com.android.launcher3.widget.WidgetsFullSheet;
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;
@@ -159,14 +174,19 @@
import java.util.HashSet;
import java.util.List;
import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
/**
* Default launcher application.
*/
public class Launcher extends BaseDraggingActivity implements LauncherExterns,
- 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;
@@ -202,9 +222,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;
@@ -255,6 +277,10 @@
private ArrayList<OnResumeCallback> mOnResumeCallbacks = new ArrayList<>();
+ // Used to notify when an activity launch has been deferred because launcher is not yet resumed
+ // TODO: See if we can remove this later
+ private Runnable mOnDeferredActivityLaunchCallback;
+
private ViewOnDrawExecutor mPendingExecutor;
private LauncherModel mModel;
@@ -278,12 +304,11 @@
*/
private PendingRequestArgs mPendingRequestArgs;
// Request id for any pending activity result
- private int mPendingActivityRequestCode = -1;
+ protected int mPendingActivityRequestCode = -1;
public ViewGroupFocusHelper mFocusHandler;
private RotationHelper mRotationHelper;
- private Runnable mCancelTouchController;
final Handler mHandler = new Handler();
private final Runnable mHandleDeferredResume = this::handleDeferredResume;
@@ -294,9 +319,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()
@@ -311,10 +342,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());
@@ -330,7 +359,6 @@
mDragController = new DragController(this);
mAllAppsController = new AllAppsTransitionController(this);
mStateManager = new LauncherStateManager(this);
- UiFactory.onCreate(this);
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
mAppWidgetHost = new LauncherAppWidgetHost(this,
@@ -344,7 +372,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.
@@ -390,10 +418,12 @@
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) {
@@ -413,12 +443,45 @@
}
}
});
+
+ 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
public void onEnterAnimationComplete() {
super.onEnterAnimationComplete();
- UiFactory.onEnterAnimationComplete(this);
mAllAppsController.highlightWorkTabIfNecessary();
mRotationHelper.setCurrentTransitionRequest(REQUEST_NONE);
}
@@ -432,7 +495,6 @@
}
mOldConfig.setTo(newConfig);
- UiFactory.onLauncherStateOrResumeChanged(this);
super.onConfigurationChanged(newConfig);
}
@@ -452,7 +514,7 @@
public void reapplyUi(boolean cancelCurrentAnimation) {
if (supportsFakeLandscapeUI()) {
mRotationMode = mStableDeviceProfile == null
- ? RotationMode.NORMAL : UiFactory.getRotationMode(mDeviceProfile);
+ ? RotationMode.NORMAL : getFakeRotationMode(mDeviceProfile);
}
getRootView().dispatchInsets();
getStateManager().reapplyState(cancelCurrentAnimation);
@@ -515,7 +577,7 @@
if (supportsFakeLandscapeUI() && mDeviceProfile.isVerticalBarLayout()) {
mStableDeviceProfile = mDeviceProfile.inv.portraitProfile;
- mRotationMode = UiFactory.getRotationMode(mDeviceProfile);
+ mRotationMode = getFakeRotationMode(mDeviceProfile);
} else {
mStableDeviceProfile = null;
mRotationMode = RotationMode.NORMAL;
@@ -567,18 +629,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());
@@ -586,18 +642,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().
@@ -643,6 +697,7 @@
switch (requestCode) {
case REQUEST_CREATE_SHORTCUT:
completeAddShortcut(intent, info.container, screenId, info.cellX, info.cellY, info);
+ announceForAccessibility(R.string.item_added_to_workspace);
break;
case REQUEST_CREATE_APPWIDGET:
completeAddAppWidget(appWidgetId, info, null, null);
@@ -667,7 +722,6 @@
break;
}
}
-
return screenId;
}
@@ -796,9 +850,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
@@ -825,10 +876,6 @@
getString(R.string.derived_app_name)), Toast.LENGTH_SHORT).show();
}
}
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onRequestPermissionsResult(requestCode, permissions,
- grantResults);
- }
}
/**
@@ -887,9 +934,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);
@@ -897,21 +947,21 @@
NotificationListener.removeNotificationsChangedListener();
getStateManager().moveToRestState();
- UiFactory.onLauncherStateOrResumeChanged(this);
-
// Workaround for b/78520668, explicitly trim memory once UI is hidden
onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
}
@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() {
@@ -919,7 +969,6 @@
logStopAndResume(Action.Command.RESUME);
getUserEventDispatcher().startSession();
- UiFactory.onLauncherStateOrResumeChanged(this);
AppLaunchTracker.INSTANCE.get(this).onReturnedToHome();
// Process any items that were added while Launcher was away.
@@ -934,15 +983,17 @@
DiscoveryBounce.showForHomeIfNeeded(this);
- if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
- UiFactory.resetPendingActivityResults(this, mPendingActivityRequestCode);
- }
+ onDeferredResumed();
+ addActivityFlags(ACTIVITY_STATE_DEFERRED_RESUMED);
+
mDeferredResumePending = false;
} else {
mDeferredResumePending = true;
}
}
+ protected void onDeferredResumed() { }
+
private void logStopAndResume(int command) {
int containerType = mStateManager.getState().containerType;
if (containerType == ContainerType.WORKSPACE && mWorkspace != null) {
@@ -951,30 +1002,66 @@
} else {
getUserEventDispatcher().logActionCommand(command, containerType, -1);
}
+ }
+ 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();
}
+ addActivityFlags(ACTIVITY_STATE_TRANSITION_ACTIVE);
}
public void onStateSetEnd(LauncherState state) {
getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
getWorkspace().setClipChildren(!state.disablePageClipping);
finishAutoCancelActionMode();
+ removeActivityFlags(ACTIVITY_STATE_TRANSITION_ACTIVE);
}
@Override
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);
@@ -988,12 +1075,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
@@ -1005,52 +1093,12 @@
mDragController.cancelDrag();
mDragController.resetLastGestureUpTime();
mDropTargetBar.animateToVisibility(false);
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onPause();
+
+ if (!mDeferOverlayCallbacks) {
+ mOverlayManager.onActivityPaused(this);
}
}
- @Override
- protected void onUserLeaveHint() {
- super.onUserLeaveHint();
- UiFactory.onLauncherStateOrResumeChanged(this);
- }
-
- @Override
- public void onWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
- 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) {
@@ -1114,7 +1162,6 @@
// Setup the drag layer
mDragLayer.setup(mDragController, mWorkspace);
- mCancelTouchController = UiFactory.enableLiveUIChanges(this);
mWorkspace.setup(mDragController);
// Until the workspace is bound, ensure that we keep the wallpaper offset locked to the
@@ -1181,8 +1228,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) {
@@ -1280,6 +1327,7 @@
hostView.setVisibility(View.VISIBLE);
prepareAppWidget(hostView, launcherInfo);
mWorkspace.addInScreen(hostView, launcherInfo);
+ announceForAccessibility(R.string.item_added_to_workspace);
}
private void prepareAppWidget(AppWidgetHostView hostView, LauncherAppWidgetInfo item) {
@@ -1308,19 +1356,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() {
@@ -1369,17 +1412,23 @@
return mModelWriter;
}
+ @Override
public SharedPreferences getSharedPrefs() {
return mSharedPrefs;
}
+ @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() &
@@ -1390,8 +1439,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) {
@@ -1429,9 +1477,10 @@
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onHomeIntent(internalStateHandled);
}
+ mOverlayManager.hideOverlay(isStarted() && !isForceInvisible());
}
- TraceHelper.endSection("NEW_INTENT");
+ TraceHelper.INSTANCE.endSection(traceToken);
}
@Override
@@ -1474,23 +1523,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();
-
- if (mCancelTouchController != null) {
- mCancelTouchController.run();
- mCancelTouchController = null;
- }
+ PluginManagerWrapper.INSTANCE.get(this).removePluginListener(this);
// Stop callbacks from LauncherModel
// It's possible to receive onDestroy after a new Launcher activity has
@@ -1510,9 +1553,8 @@
TextKeyListener.getInstance().release();
clearPendingBinds();
LauncherAppState.getIDP(this).removeOnChangeListener(this);
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onDestroy();
- }
+
+ mOverlayManager.onActivityDestroyed(this);
}
public LauncherAccessibilityDelegate getAccessibilityDelegate() {
@@ -1528,10 +1570,7 @@
if (requestCode != -1) {
mPendingActivityRequestCode = requestCode;
}
- if (requestCode == -1
- || !UiFactory.startActivityForResult(this, intent, requestCode, options)) {
- super.startActivityForResult(intent, requestCode, options);
- }
+ super.startActivityForResult(intent, requestCode, options);
}
@Override
@@ -1540,14 +1579,11 @@
if (requestCode != -1) {
mPendingActivityRequestCode = requestCode;
}
- if (requestCode == -1 || !UiFactory.startIntentSenderForResult(this, intent, requestCode,
- fillInIntent, flagsMask, flagsValues, extraFlags, options)) {
- try {
- super.startIntentSenderForResult(intent, requestCode,
- fillInIntent, flagsMask, flagsValues, extraFlags, options);
- } catch (IntentSender.SendIntentException e) {
- throw new ActivityNotFoundException();
- }
+ try {
+ super.startIntentSenderForResult(intent, requestCode,
+ fillInIntent, flagsMask, flagsValues, extraFlags, options);
+ } catch (IntentSender.SendIntentException e) {
+ throw new ActivityNotFoundException();
}
}
@@ -1758,9 +1794,6 @@
if (finishAutoCancelActionMode()) {
return;
}
- if (mLauncherCallbacks != null && mLauncherCallbacks.handleBackPressed()) {
- return;
- }
if (mDragController.isDragging()) {
mDragController.cancelDrag();
@@ -1844,7 +1877,10 @@
// recents animation into launcher. Defer launching the activity until Launcher is
// next resumed.
addOnResumeCallback(() -> startActivitySafely(v, intent, item, sourceContainer));
- UiFactory.clearSwipeSharedState(true /* finishAnimation */);
+ if (mOnDeferredActivityLaunchCallback != null) {
+ mOnDeferredActivityLaunchCallback.run();
+ mOnDeferredActivityLaunchCallback = null;
+ }
return true;
}
@@ -1885,10 +1921,6 @@
// This clears all widget bitmaps from the widget tray
// TODO(hyunyoungs)
}
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onTrimMemory(level);
- }
- UiFactory.onTrimMemory(this, level);
}
@Override
@@ -1909,6 +1941,14 @@
}
/**
+ * Persistant callback which notifies when an activity launch is deferred because the activity
+ * was not yet resumed.
+ */
+ public void setOnDeferredActivityLaunchCallback(Runnable callback) {
+ mOnDeferredActivityLaunchCallback = callback;
+ }
+
+ /**
* Implementation of the method from LauncherModel.Callbacks.
*/
@Override
@@ -1942,7 +1982,7 @@
* 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.
@@ -1960,7 +2000,7 @@
if (mHotseat != null) {
mHotseat.resetLayout(getWallpaperDeviceProfile().isVerticalBarLayout());
}
- TraceHelper.endSection("startBinding");
+ TraceHelper.INSTANCE.endSection(traceToken);
}
@Override
@@ -2153,112 +2193,113 @@
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;
}
@@ -2336,7 +2377,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);
@@ -2360,7 +2401,7 @@
mDeviceProfile.inv.numFolderColumns * mDeviceProfile.inv.numFolderRows);
getViewCache().setCacheSize(R.layout.folder_page, 2);
- TraceHelper.endSection("finishBindingItems");
+ TraceHelper.INSTANCE.endSection(traceToken);
}
private boolean canRunNewAppsAnimation() {
@@ -2376,6 +2417,10 @@
return bounceAnim;
}
+ private void announceForAccessibility(@StringRes int stringResId) {
+ getDragLayer().announceForAccessibility(getString(stringResId));
+ }
+
/**
* Add the icons for all apps.
*
@@ -2507,6 +2552,7 @@
if (mLauncherCallbacks != null) {
mLauncherCallbacks.dump(prefix, fd, writer, args);
}
+ mOverlayManager.dump(prefix, writer);
}
@Override
@@ -2597,16 +2643,40 @@
return super.onKeyUp(keyCode, event);
}
- public static Launcher getLauncher(Context context) {
- return (Launcher) fromContext(context);
+ protected StateHandler[] createStateHandlers() {
+ return new StateHandler[] { getAllAppsController(), getWorkspace() };
}
+ public TouchController[] createTouchControllers() {
+ return new TouchController[] {getDragController(), new AllAppsSwipeController(this)};
+ }
+
+ protected RotationMode getFakeRotationMode(DeviceProfile deviceProfile) {
+ return RotationMode.NORMAL;
+ }
+
+ protected ScaleAndTranslation getOverviewScaleAndTranslationForNormalState() {
+ return new ScaleAndTranslation(1.1f, 0f, 0f);
+ }
+
+ public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) { }
+
+ public void onDragLayerHierarchyChanged() { }
+
@Override
public void returnToHomescreen() {
super.returnToHomescreen();
getStateManager().goToState(LauncherState.NORMAL);
}
+ public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
+ return Stream.of(APP_INFO, WIDGETS, INSTALL, DISMISS_PREDICTION);
+ }
+
+ public static Launcher getLauncher(Context context) {
+ return (Launcher) fromContext(context);
+ }
+
/**
* Just a wrapper around the type cast to allow easier tracking of calls.
*/
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index d70abc2..79f4821 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -17,26 +17,29 @@
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.IconProvider;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.notification.NotificationListener;
+import com.android.launcher3.pm.InstallSessionTracker;
+import com.android.launcher3.pm.PackageInstallerCompat;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.SecureSettingsObserver;
+import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.launcher3.widget.custom.CustomWidgetManager;
public class LauncherAppState {
@@ -44,7 +47,7 @@
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;
@@ -54,6 +57,10 @@
private final InvariantDeviceProfile mInvariantDeviceProfile;
private final SecureSettingsObserver mNotificationDotsObserver;
+ private final InstallSessionTracker mInstallSessionTracker;
+ private final SimpleBroadcastReceiver mModelChangeReceiver;
+ private final SafeCloseable mCalendarChangeTracker;
+
public static LauncherAppState getInstance(final Context context) {
return INSTANCE.get(context);
}
@@ -67,10 +74,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;
@@ -80,28 +83,34 @@
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);
}
- FeatureFlags.APP_SEARCH_IMPROVEMENTS.addChangeListener(context, mModel::forceReload);
- mContext.registerReceiver(mModel, filter);
+ mCalendarChangeTracker = IconProvider.registerIconChangeListener(mContext,
+ mModel::onAppIconChanged, MODEL_EXECUTOR.getHandler());
+
+ // TODO: remove listener on terminate
+ FeatureFlags.APP_SEARCH_IMPROVEMENTS.addChangeListener(context, mModel::forceReload);
+ CustomWidgetManager.INSTANCE.get(mContext)
+ .setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts);
+
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 {
@@ -138,20 +147,19 @@
* 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();
+ mCalendarChangeTracker.close();
+ CustomWidgetManager.INSTANCE.get(mContext).setWidgetRefreshCallback(null);
+
if (mNotificationDotsObserver != null) {
mNotificationDotsObserver.unregister();
}
}
LauncherModel setLauncher(Launcher launcher) {
- getLocalProvider(mContext).setLauncherProviderChangeListener(launcher);
mModel.initialize(launcher);
- CustomWidgetManager.INSTANCE.get(launcher)
- .setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts);
return mModel;
}
@@ -177,11 +185,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 1215d43..4e29a95 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -29,7 +29,7 @@
import android.util.SparseArray;
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;
@@ -80,7 +80,7 @@
@Override
public void startListening() {
- if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
return;
}
mFlags |= FLAG_LISTENING;
@@ -107,7 +107,7 @@
@Override
public void stopListening() {
- if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
return;
}
mFlags &= ~FLAG_LISTENING;
@@ -166,7 +166,7 @@
@Override
public int allocateAppWidgetId() {
- if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
return AppWidgetManager.INVALID_APPWIDGET_ID;
}
@@ -263,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;
}
@@ -279,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/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 a012412..1e25c0c 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -21,21 +21,21 @@
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.Context;
import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.PackageInstaller;
import android.content.pm.ShortcutInfo;
-import android.os.Process;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
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;
@@ -52,6 +52,8 @@
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.IntSparseArrayMap;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -74,8 +76,7 @@
* 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";
@@ -127,20 +128,6 @@
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, final UserHandle user) {
- HashSet<String> packages = new HashSet<>();
- packages.add(packageName);
- enqueueModelUpdateTask(new CacheDataUpdatedTask(
- CacheDataUpdatedTask.OP_SESSION_UPDATE, user, packages));
- }
-
/**
* Adds the provided items to the workspace.
*/
@@ -173,30 +160,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);
@@ -248,17 +211,24 @@
enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, true));
}
- public void updatePinnedShortcuts(String packageName, List<ShortcutInfo> shortcuts,
- UserHandle user) {
- enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, false));
+ /**
+ * Called when the icon for an app changes, outside of package event
+ */
+ @WorkerThread
+ public void onAppIconChanged(String packageName, UserHandle user) {
+ // Update the icon for the calendar package
+ Context context = mApp.getContext();
+ onPackageChanged(packageName, user);
+
+ List<ShortcutInfo> pinnedShortcuts = DeepShortcutManager.getInstance(context)
+ .queryForPinnedShortcuts(packageName, user);
+ if (!pinnedShortcuts.isEmpty()) {
+ enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, pinnedShortcuts, user,
+ false));
+ }
}
- /**
- * 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)) {
@@ -266,7 +236,7 @@
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) ||
@@ -392,16 +362,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);
- bindApplicationsIfNeeded();
+ 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;
@@ -504,7 +523,7 @@
updateAndBindWorkspaceItem(() -> {
si.updateFromDeepShortcutInfo(info, mApp.getContext());
LauncherIcons li = LauncherIcons.obtain(mApp.getContext());
- si.applyFrom(li.createShortcutIcon(info));
+ si.bitmap = li.createShortcutIcon(info);
li.recycle();
return si;
});
@@ -540,7 +559,8 @@
if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
writer.println(prefix + "All apps list: size=" + mBgAllAppsList.data.size());
for (AppInfo info : mBgAllAppsList.data) {
- writer.println(prefix + " title=\"" + info.title + "\" iconBitmap=" + info.iconBitmap
+ writer.println(prefix + " title=\"" + info.title
+ + "\" bitmapIcon=" + info.bitmap.icon
+ " componentName=" + info.componentName.getPackageName());
}
}
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 6081300..42927ea 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -45,8 +45,6 @@
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.provider.BaseColumns;
@@ -69,7 +67,6 @@
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.NoLocaleSQLiteHelper;
import com.android.launcher3.util.PackageManagerHelper;
-import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Thunk;
import org.xmlpull.v1.XmlPullParser;
@@ -100,9 +97,6 @@
static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
- private final ChangeListenerWrapper mListenerWrapper = new ChangeListenerWrapper();
- private Handler mListenerHandler;
-
protected DatabaseHelper mOpenHelper;
/**
@@ -122,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.
@@ -130,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);
@@ -153,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,
@@ -223,7 +208,6 @@
mOpenHelper.onAddOrDeleteOp(db);
uri = ContentUris.withAppendedId(uri, rowId);
- notifyListeners();
reloadLauncherIfExternal();
return uri;
}
@@ -283,7 +267,6 @@
t.commit();
}
- notifyListeners();
reloadLauncherIfExternal();
return values.length;
}
@@ -329,7 +312,6 @@
int count = db.delete(args.table, args.where, args.args);
if (count > 0) {
mOpenHelper.onAddOrDeleteOp(db);
- notifyListeners();
reloadLauncherIfExternal();
}
return count;
@@ -343,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;
}
@@ -438,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());
}
@@ -564,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.
@@ -589,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() {
@@ -633,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();
}
@@ -1039,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 c509680..ec307db 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -127,6 +127,7 @@
public static final int CONTAINER_DESKTOP = -100;
public static final int CONTAINER_HOTSEAT = -101;
public static final int CONTAINER_PREDICTION = -102;
+ public static final int CONTAINER_HOTSEAT_PREDICTION = -103;
static final String containerToString(int container) {
switch (container) {
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 6e2626b..d2b447b 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -26,9 +26,11 @@
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
import static com.android.launcher3.anim.Interpolators.clampToProgress;
+import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
@@ -36,14 +38,11 @@
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
-import static com.android.launcher3.anim.Interpolators.ACCEL_2;
-import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import android.view.animation.Interpolator;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.states.SpringLoadedState;
-import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.uioverrides.states.AllAppsState;
import com.android.launcher3.uioverrides.states.OverviewState;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -210,7 +209,7 @@
}
public ScaleAndTranslation getOverviewScaleAndTranslation(Launcher launcher) {
- return UiFactory.getOverviewScaleAndTranslationForNormalState(launcher);
+ return launcher.getOverviewScaleAndTranslationForNormalState();
}
public float getOverviewFullscreenProgress() {
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index f673508..daf270b 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -33,7 +33,6 @@
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.anim.PropertySetter.AnimatedPropertySetter;
import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.uioverrides.UiFactory;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -144,7 +143,7 @@
public StateHandler[] getStateHandlers() {
if (mStateHandlers == null) {
- mStateHandlers = UiFactory.getStateHandler(mLauncher);
+ mStateHandlers = mLauncher.createStateHandlers();
}
return mStateHandlers;
}
@@ -412,7 +411,6 @@
// Only disable clipping if needed, otherwise leave it as previous value.
mLauncher.getWorkspace().setClipChildren(false);
}
- UiFactory.onLauncherStateOrResumeChanged(mLauncher);
for (int i = mListeners.size() - 1; i >= 0; i--) {
mListeners.get(i).onStateTransitionStart(state);
@@ -433,8 +431,6 @@
setRestState(null);
}
- UiFactory.onLauncherStateOrResumeChanged(mLauncher);
-
for (int i = mListeners.size() - 1; i >= 0; i--) {
mListeners.get(i).onStateTransitionComplete(state);
}
@@ -442,10 +438,6 @@
AccessibilityManagerCompat.sendStateEventToTest(mLauncher, state.ordinal);
}
- public void onWindowFocusChanged() {
- UiFactory.onLauncherStateOrFocusChanged(mLauncher);
- }
-
public LauncherState getLastState() {
return mLastStableState;
}
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 c8c590d..3c2ed72 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -16,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;
@@ -28,12 +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;
@@ -157,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) {
@@ -287,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 a87c446..e0c50e2 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -16,6 +16,8 @@
package com.android.launcher3;
+import static com.android.launcher3.pm.PackageInstallerCompat.getUserHandle;
+
import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -23,6 +25,7 @@
import android.content.Intent;
import android.content.SharedPreferences;
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;
@@ -37,14 +40,11 @@
import android.text.TextUtils;
import android.util.Log;
-import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.pm.PackageInstallerCompat;
import com.android.launcher3.util.Executors;
-import com.android.launcher3.compat.PackageInstallerCompat;
import java.util.List;
-import static com.android.launcher3.compat.PackageInstallerCompat.getUserHandle;
-
/**
* BroadcastReceiver to handle session commit intent.
*/
@@ -90,9 +90,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));
@@ -100,9 +99,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;
}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 5d0effa..2bec0ba 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -27,6 +27,7 @@
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.content.res.Resources;
@@ -59,12 +60,12 @@
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.dragndrop.FolderAdaptiveIcon;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.graphics.TintedDrawableSpan;
+import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.pm.ShortcutConfigActivityInfo;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.IntArray;
@@ -518,19 +519,20 @@
}
/**
- * Returns the full drawable for {@param info}.
+ * Returns the full drawable for info without any flattening or pre-processing.
+ *
* @param outObj this is set to the internal data associated with {@param info},
* eg {@link LauncherActivityInfo} or {@link ShortcutInfo}.
*/
public static Drawable getFullDrawable(Launcher launcher, ItemInfo info, int width, int height,
- boolean flattenDrawable, Object[] outObj) {
+ 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()
- .getFullResIcon(activityInfo, flattenDrawable) : null;
+ return activityInfo == null ? null : new IconProvider(launcher).getIconForUI(
+ activityInfo, launcher.getDeviceProfile().inv.fillResIconDpi);
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
if (info instanceof PendingAddShortcutInfo) {
ShortcutConfigActivityInfo activityInfo =
@@ -582,7 +584,7 @@
}
ShortcutInfo si = (ShortcutInfo) obj;
LauncherIcons li = LauncherIcons.obtain(appState.getContext());
- Bitmap badge = li.getShortcutInfoBadge(si, appState.getIconCache()).iconBitmap;
+ Bitmap badge = li.getShortcutInfoBadge(si, appState.getIconCache()).bitmap.icon;
li.recycle();
float badgeSize = LauncherIcons.getBadgeSizeForIconSize(iconSize);
float insetFraction = (iconSize - badgeSize) / iconSize;
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index c6381b0..37b58d3 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -23,24 +23,28 @@
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.CancellationSignal;
import android.os.Process;
import android.os.UserHandle;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.LongSparseArray;
+import android.util.Pair;
import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.compat.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.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;
@@ -77,6 +81,9 @@
private final UserManagerCompat mUserManager;
private final CacheDb mDb;
+ private final UserHandle mMyUser = Process.myUserHandle();
+ private final ArrayMap<UserHandle, Bitmap> mUserBadges = new ArrayMap<>();
+
public WidgetPreviewLoader(Context context, IconCache iconCache) {
mContext = context;
mIconCache = iconCache;
@@ -85,6 +92,51 @@
}
/**
+ * Returns a drawable that can be used as a badge for the user or null.
+ */
+ @UiThread
+ public Drawable getBadgeForUser(UserHandle user, int badgeSize) {
+ if (mMyUser.equals(user)) {
+ return null;
+ }
+
+ Bitmap badgeBitmap = getUserBadge(user, badgeSize);
+ FastBitmapDrawable d = new FastBitmapDrawable(badgeBitmap);
+ d.setFilterBitmap(true);
+ d.setBounds(0, 0, badgeBitmap.getWidth(), badgeBitmap.getHeight());
+ return d;
+ }
+
+ private Bitmap getUserBadge(UserHandle user, int badgeSize) {
+ synchronized (mUserBadges) {
+ Bitmap badgeBitmap = mUserBadges.get(user);
+ if (badgeBitmap != null) {
+ return badgeBitmap;
+ }
+
+ final Resources res = mContext.getResources();
+ badgeBitmap = Bitmap.createBitmap(badgeSize, badgeSize, Bitmap.Config.ARGB_8888);
+
+ Drawable drawable = mContext.getPackageManager().getUserBadgedDrawableForDensity(
+ new BitmapDrawable(res, badgeBitmap), user,
+ new Rect(0, 0, badgeSize, badgeSize),
+ 0);
+ if (drawable instanceof BitmapDrawable) {
+ badgeBitmap = ((BitmapDrawable) drawable).getBitmap();
+ } else {
+ badgeBitmap.eraseColor(Color.TRANSPARENT);
+ Canvas c = new Canvas(badgeBitmap);
+ drawable.setBounds(0, 0, badgeSize, badgeSize);
+ drawable.draw(c);
+ c.setBitmap(null);
+ }
+
+ mUserBadges.put(user, badgeBitmap);
+ return badgeBitmap;
+ }
+ }
+
+ /**
* Generates the widget preview on {@link AsyncTask#THREAD_POOL_EXECUTOR}. Must be
* called on UI thread
*
@@ -105,8 +157,8 @@
public void refresh() {
mDb.clear();
-
}
+
/**
* The DB holds the generated previews for various components. Previews can also have different
* sizes (landscape vs portrait).
@@ -289,14 +341,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);
}
}
@@ -309,9 +372,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;
@@ -341,6 +405,8 @@
int previewWidth;
int previewHeight;
+ boolean savePreviewImage = widgetPreviewExists || info.previewImage == 0;
+
if (widgetPreviewExists && drawable.getIntrinsicWidth() > 0
&& drawable.getIntrinsicHeight() > 0) {
previewWidth = drawable.getIntrinsicWidth();
@@ -427,10 +493,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) {
@@ -533,6 +601,8 @@
@Thunk long[] mVersions;
@Thunk Bitmap mBitmapToRecycle;
+ private boolean mSaveToDB = false;
+
PreviewLoadTask(WidgetCacheKey key, WidgetItem info, int previewWidth,
int previewHeight, WidgetCell caller) {
mKey = key;
@@ -588,7 +658,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;
}
@@ -602,7 +675,7 @@
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);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 9eeb286..d445bc9 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -60,7 +60,6 @@
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;
@@ -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;
@@ -965,6 +965,9 @@
onOverlayScrollChanged(0);
}
+ public boolean hasOverlay() {
+ return mLauncherOverlay != null;
+ }
private boolean isScrollingOverlay() {
return mLauncherOverlay != null &&
@@ -1472,9 +1475,6 @@
public DragView beginDragShared(View child, DragSource source, ItemInfo dragObject,
DragPreviewProvider previewProvider, DragOptions dragOptions) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_CONTEXT_MENU, "beginDragShared");
- }
float iconScale = 1f;
if (child instanceof BubbleTextView) {
Drawable icon = ((BubbleTextView) child).getIcon();
diff --git a/src/com/android/launcher3/WorkspaceItemInfo.java b/src/com/android/launcher3/WorkspaceItemInfo.java
index 23795c5..be907e5 100644
--- a/src/com/android/launcher3/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/WorkspaceItemInfo.java
@@ -28,7 +28,7 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.uioverrides.UiFactory;
+import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.util.ContentWriter;
import java.util.Arrays;
@@ -140,7 +140,7 @@
.put(Favorites.RESTORED, status);
if (!usingLowResIcon()) {
- writer.putIcon(iconBitmap, user);
+ writer.putIcon(bitmap, user);
}
if (iconResource != null) {
writer.put(Favorites.ICON_PACKAGE, iconResource.packageName)
@@ -192,7 +192,7 @@
}
disabledMessage = shortcutInfo.getDisabledMessage();
- Person[] persons = UiFactory.getPersons(shortcutInfo);
+ Person[] persons = ApiWrapper.getPersons(shortcutInfo);
personKeys = persons.length == 0 ? Utilities.EMPTY_STRING_ARRAY
: Arrays.stream(persons).map(Person::getKey).sorted().toArray(String[]::new);
}
diff --git a/src/com/android/launcher3/WorkspaceLayoutManager.java b/src/com/android/launcher3/WorkspaceLayoutManager.java
index ea2d4d0..0b9d602 100644
--- a/src/com/android/launcher3/WorkspaceLayoutManager.java
+++ b/src/com/android/launcher3/WorkspaceLayoutManager.java
@@ -39,7 +39,8 @@
default void addInScreenFromBind(View child, ItemInfo info) {
int x = info.cellX;
int y = info.cellY;
- if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+ if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
+ || info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
int screenId = info.screenId;
x = getHotseat().getCellXFromOrder(screenId);
y = getHotseat().getCellYFromOrder(screenId);
@@ -83,7 +84,8 @@
}
final CellLayout layout;
- if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+ if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
+ || container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
layout = getHotseat();
// Hide folder title in the hotseat
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 0c12c60..a7ef9ef 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -191,6 +191,7 @@
ArrayList<ItemInfo> itemList = new ArrayList<>();
itemList.add(info);
mLauncher.bindItems(itemList, true);
+ announceConfirmation(R.string.item_added_to_workspace);
} else if (item instanceof PendingAddItemInfo) {
PendingAddItemInfo info = (PendingAddItemInfo) item;
Workspace workspace = mLauncher.getWorkspace();
@@ -198,7 +199,6 @@
mLauncher.addPendingItem(info, Favorites.CONTAINER_DESKTOP,
screenId, coordinates, info.spanX, info.spanY);
}
- announceConfirmation(R.string.item_added_to_workspace);
}
});
return true;
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 37ee248..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);
}
/**
@@ -616,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/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/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
index c8b1f67..8f6500b 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
@@ -28,7 +28,7 @@
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.CustomAppWidgetProviderInfo;
@@ -51,7 +51,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) {
@@ -82,7 +82,7 @@
@Override
public boolean bindAppWidgetIdIfAllowed(int appWidgetId, AppWidgetProviderInfo info,
Bundle options) {
- if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
return false;
}
if (appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) {
@@ -94,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 :
@@ -117,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()) {
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java
index 11ec333..2814afc 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java
@@ -21,7 +21,7 @@
import androidx.annotation.Nullable;
-import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.util.PackageUserKey;
import java.util.Collections;
@@ -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 39f6949..0000000
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ /dev/null
@@ -1,99 +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.PackageInstaller.SessionCallback;
-import android.content.pm.ShortcutInfo;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.UserHandle;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.Utilities;
-import com.android.launcher3.util.LooperExecutor;
-import com.android.launcher3.util.PackageUserKey;
-
-import java.util.List;
-
-public abstract class LauncherAppsCompat {
-
- public interface OnAppsChangedCallbackCompat {
- default void onPackageRemoved(String packageName, UserHandle user) { }
- default void onPackageAdded(String packageName, UserHandle user) { }
- default void onPackageChanged(String packageName, UserHandle user) { }
- default void onPackagesAvailable(String[] packageNames, UserHandle user,
- boolean replacing) { }
- default void onPackagesUnavailable(String[] packageNames, UserHandle user,
- boolean replacing) { }
- default void onPackagesSuspended(String[] packageNames, UserHandle user) { }
- default void onPackagesUnsuspended(String[] packageNames, UserHandle user) { }
- default 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();
-
- public abstract void registerSessionCallback(LooperExecutor executor,
- SessionCallback sessionCallback);
- public abstract void unregisterSessionCallback(SessionCallback sessionCallback);
-}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
deleted file mode 100644
index 281274c..0000000
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
+++ /dev/null
@@ -1,230 +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.PackageInstaller.SessionCallback;
-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.LooperExecutor;
-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();
- }
-
- @Override
- public void registerSessionCallback(LooperExecutor executor, SessionCallback sessionCallback) {
- mContext.getPackageManager().getPackageInstaller().registerSessionCallback(sessionCallback,
- executor.getHandler());
- }
-
- @Override
- public void unregisterSessionCallback(SessionCallback sessionCallback) {
- mContext.getPackageManager().getPackageInstaller()
- .unregisterSessionCallback(sessionCallback);
- }
-}
-
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
deleted file mode 100644
index 5e13d00..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 static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-
-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 androidx.annotation.Nullable;
-
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVO;
-import com.android.launcher3.icons.LauncherIcons;
-import com.android.launcher3.util.PackageUserKey;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@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.
- 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.
- 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 48805af..0000000
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVQ.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.compat;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageInstaller.SessionCallback;
-
-import com.android.launcher3.util.LooperExecutor;
-
-import java.util.List;
-
-@TargetApi(29)
-public class LauncherAppsCompatVQ extends LauncherAppsCompatVO {
-
- LauncherAppsCompatVQ(Context context) {
- super(context);
- }
-
- public List<PackageInstaller.SessionInfo> getAllPackageInstallerSessions() {
- return mLauncherApps.getAllPackageInstallerSessions();
- }
-
- @Override
- public void registerSessionCallback(LooperExecutor executor, SessionCallback sessionCallback) {
- mLauncherApps.registerPackageInstallerSessionCallback(executor, sessionCallback);
- }
-
- @Override
- public void unregisterSessionCallback(SessionCallback sessionCallback) {
- mLauncherApps.unregisterPackageInstallerSessionCallback(sessionCallback);
- }
-}
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 409b21d..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 static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-
-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.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.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 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();
- mLauncherApps = LauncherAppsCompat.getInstance(context);
- mLauncherApps.registerSessionCallback(MODEL_EXECUTOR, mCallback);
- mPromiseIconIds = IntSet.wrap(IntArray.fromConcatString(
- 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() {
- mLauncherApps.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) {
- UserHandle user = getUserHandle(session);
- mActiveSessions.put(session.getSessionId(),
- new PackageUserKey(session.getAppPackageName(), user));
- addSessionInfoToCache(session, user);
- LauncherAppState app = LauncherAppState.getInstanceNoCreate();
- if (app != null) {
- app.getModel().updateSessionDisplayInfo(session.getAppPackageName(),
- user);
- }
- 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 64d236f..f4b705e 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -25,6 +25,7 @@
import androidx.annotation.Keep;
import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.BuildConfig;
import com.android.launcher3.Utilities;
import com.android.launcher3.uioverrides.TogglableFlag;
@@ -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,46 +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;
-
- // 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");
@@ -119,10 +121,19 @@
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 final TogglableFlag ASSISTANT_GIVES_LAUNCHER_FOCUS = new TogglableFlag(
"ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
"Allow Launcher to handle nav bar gestures while Assistant is running over it");
+ public static final TogglableFlag ENABLE_HYBRID_HOTSEAT = new TogglableFlag(
+ "ENABLE_HYBRID_HOTSEAT", false, "Fill gaps in hotseat with predicted apps");
+
+ public static final TogglableFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = new TogglableFlag(
+ "ENABLE_DEEP_SHORTCUT_ICON_CACHE", true, "R/W deep shortcut in IconCache");
+
public static void initialize(Context context) {
// Avoid the disk read for user builds
if (Utilities.IS_DEBUG_DEVICE) {
@@ -137,13 +148,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());
@@ -243,14 +254,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 9fb1090..1b0567f 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -45,13 +45,14 @@
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.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;
@@ -92,7 +93,7 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mRequest = LauncherAppsCompatVO.getPinItemRequest(getIntent());
+ mRequest = PinRequestHelper.getPinItemRequest(getIntent());
if (mRequest == null) {
finish();
return;
@@ -176,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;
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 cdc7061..8823bde 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -57,7 +57,6 @@
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
-import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.views.Transposable;
@@ -121,7 +120,7 @@
}
public void recreateControllers() {
- mControllers = UiFactory.createTouchControllers(mActivity);
+ mControllers = mActivity.createTouchControllers();
}
public ViewGroupFocusHelper getFocusIndicatorHelper() {
@@ -477,14 +476,14 @@
public void onViewAdded(View child) {
super.onViewAdded(child);
updateChildIndices();
- UiFactory.onLauncherStateOrFocusChanged(mActivity);
+ mActivity.onDragLayerHierarchyChanged();
}
@Override
public void onViewRemoved(View child) {
super.onViewRemoved(child);
updateChildIndices();
- UiFactory.onLauncherStateOrFocusChanged(mActivity);
+ mActivity.onDragLayerHierarchyChanged();
}
@Override
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index f66d07e..145885a 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -216,8 +216,7 @@
Object[] outObj = new Object[1];
int w = mBitmap.getWidth();
int h = mBitmap.getHeight();
- Drawable dr = Utilities.getFullDrawable(mLauncher, info, w, h,
- false /* flattenDrawable */, outObj);
+ Drawable dr = Utilities.getFullDrawable(mLauncher, info, w, h, outObj);
if (dr instanceof AdaptiveIconDrawable) {
int blurMargin = (int) mLauncher.getResources()
diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
index 07eb0d6..869dd94 100644
--- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
@@ -32,7 +32,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.PendingAddItemInfo;
-import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -68,7 +67,7 @@
public boolean init(Launcher launcher, boolean alreadyOnHome) {
super.init(launcher, alreadyOnHome);
if (!alreadyOnHome) {
- UiFactory.useFadeOutAnimationForLauncherStart(launcher, mCancelSignal);
+ launcher.useFadeOutAnimationForLauncherStart(mCancelSignal);
}
return false;
}
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/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 2d817e6..5b3a05e 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -16,10 +16,12 @@
package com.android.launcher3.folder;
+import static com.android.launcher3.FastBitmapDrawable.newIcon;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ENTER_INDEX;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.EXIT_INDEX;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
import static com.android.launcher3.folder.FolderIcon.DROP_IN_ANIMATION_DURATION;
+import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -38,7 +40,6 @@
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;
@@ -66,7 +67,6 @@
private final Context mContext;
private final FolderIcon mIcon;
- private final DrawableFactory mDrawableFactory;
private final int mIconSize;
// These variables are all associated with the drawing of the preview; they are stored
@@ -94,7 +94,6 @@
public PreviewItemManager(FolderIcon icon) {
mContext = icon.getContext();
mIcon = icon;
- mDrawableFactory = DrawableFactory.INSTANCE.get(mContext);
mIconSize = Launcher.getLauncher(mContext).getDeviceProfile().folderChildIconSizePx;
}
@@ -395,11 +394,11 @@
private void setDrawable(PreviewItemDrawingParams p, WorkspaceItemInfo item) {
if (item.hasPromiseIconUi()) {
- PreloadIconDrawable drawable = mDrawableFactory.newPendingIcon(mContext, item);
+ PreloadIconDrawable drawable = newPendingIcon(mContext, item);
drawable.setLevel(item.getInstallProgress());
p.drawable = drawable;
} else {
- p.drawable = mDrawableFactory.newIcon(mContext, item);
+ p.drawable = newIcon(mContext, item);
}
p.drawable.setBounds(0, 0, mIconSize, mIconSize);
p.item = item;
diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java
deleted file mode 100644
index 837301f..0000000
--- a/src/com/android/launcher3/graphics/DrawableFactory.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.graphics;
-
-import static com.android.launcher3.graphics.IconShape.getShapePath;
-import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
-
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Process;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-
-import androidx.annotation.UiThread;
-
-import com.android.launcher3.FastBitmapDrawable;
-import com.android.launcher3.ItemInfoWithIcon;
-import com.android.launcher3.R;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.ResourceBasedOverride;
-
-/**
- * Factory for creating new drawables.
- */
-public class DrawableFactory implements ResourceBasedOverride {
-
- public static final MainThreadInitializedObject<DrawableFactory> INSTANCE =
- forOverride(DrawableFactory.class, R.string.drawable_factory_class);
-
- protected final UserHandle mMyUser = Process.myUserHandle();
- protected final ArrayMap<UserHandle, Bitmap> mUserBadges = new ArrayMap<>();
-
- /**
- * Returns a FastBitmapDrawable with the icon.
- */
- public FastBitmapDrawable newIcon(Context context, ItemInfoWithIcon info) {
- FastBitmapDrawable drawable = info.usingLowResIcon()
- ? new PlaceHolderIconDrawable(info, getShapePath(), context)
- : new FastBitmapDrawable(info);
- drawable.setIsDisabled(info.isDisabled());
- return drawable;
- }
-
- public FastBitmapDrawable newIcon(Context context, BitmapInfo info, ActivityInfo target) {
- return info.isLowRes()
- ? new PlaceHolderIconDrawable(info, getShapePath(), context)
- : new FastBitmapDrawable(info);
- }
-
- /**
- * Returns a FastBitmapDrawable with the icon.
- */
- public PreloadIconDrawable newPendingIcon(Context context, ItemInfoWithIcon info) {
- return new PreloadIconDrawable(info, getShapePath(), context);
- }
-
- /**
- * Returns a drawable that can be used as a badge for the user or null.
- */
- @UiThread
- public Drawable getBadgeForUser(UserHandle user, Context context, int badgeSize) {
- if (mMyUser.equals(user)) {
- return null;
- }
-
- Bitmap badgeBitmap = getUserBadge(user, context, badgeSize);
- FastBitmapDrawable d = new FastBitmapDrawable(badgeBitmap);
- d.setFilterBitmap(true);
- d.setBounds(0, 0, badgeBitmap.getWidth(), badgeBitmap.getHeight());
- return d;
- }
-
- protected synchronized Bitmap getUserBadge(UserHandle user, Context context, int badgeSize) {
- Bitmap badgeBitmap = mUserBadges.get(user);
- if (badgeBitmap != null) {
- return badgeBitmap;
- }
-
- final Resources res = context.getApplicationContext().getResources();
- badgeBitmap = Bitmap.createBitmap(badgeSize, badgeSize, Bitmap.Config.ARGB_8888);
-
- Drawable drawable = context.getPackageManager().getUserBadgedDrawableForDensity(
- new BitmapDrawable(res, badgeBitmap), user, new Rect(0, 0, badgeSize, badgeSize),
- 0);
- if (drawable instanceof BitmapDrawable) {
- badgeBitmap = ((BitmapDrawable) drawable).getBitmap();
- } else {
- badgeBitmap.eraseColor(Color.TRANSPARENT);
- Canvas c = new Canvas(badgeBitmap);
- drawable.setBounds(0, 0, badgeSize, badgeSize);
- drawable.draw(c);
- c.setBitmap(null);
- }
-
- mUserBadges.put(user, badgeBitmap);
- return badgeBitmap;
- }
-}
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index d7b845b..2badb6e 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -50,8 +50,8 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
-import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.Utilities;
+import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.WorkspaceLayoutManager;
import com.android.launcher3.allapps.SearchUiManager;
import com.android.launcher3.config.FeatureFlags;
@@ -105,7 +105,7 @@
Build.VERSION.SDK_INT);
mWorkspaceItemInfo = new WorkspaceItemInfo();
- mWorkspaceItemInfo.applyFrom(iconInfo);
+ mWorkspaceItemInfo.bitmap = iconInfo;
mWorkspaceItemInfo.intent = new Intent();
mWorkspaceItemInfo.contentDescription = mWorkspaceItemInfo.title =
context.getString(R.string.label_application);
diff --git a/src/com/android/launcher3/graphics/PlaceHolderIconDrawable.java b/src/com/android/launcher3/graphics/PlaceHolderIconDrawable.java
index 23745cb..d347e8f 100644
--- a/src/com/android/launcher3/graphics/PlaceHolderIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PlaceHolderIconDrawable.java
@@ -17,14 +17,14 @@
import static androidx.core.graphics.ColorUtils.compositeColors;
+import static com.android.launcher3.graphics.IconShape.getShapePath;
+
import android.content.Context;
-import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.Rect;
import com.android.launcher3.FastBitmapDrawable;
-import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.R;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.util.Themes;
@@ -37,20 +37,12 @@
// Path in [0, 100] bounds.
private final Path mProgressPath;
- public PlaceHolderIconDrawable(BitmapInfo info, Path progressPath, Context context) {
- this(info.icon, info.color, progressPath, context);
- }
+ public PlaceHolderIconDrawable(BitmapInfo info, Context context) {
+ super(info);
- public PlaceHolderIconDrawable(ItemInfoWithIcon info, Path progressPath, Context context) {
- this(info.iconBitmap, info.iconColor, progressPath, context);
- }
-
- protected PlaceHolderIconDrawable(Bitmap b, int iconColor, Path progressPath, Context context) {
- super(b, iconColor);
-
- mProgressPath = progressPath;
+ mProgressPath = getShapePath();
mPaint.setColor(compositeColors(
- Themes.getAttrColor(context, R.attr.loadingIconColor), iconColor));
+ Themes.getAttrColor(context, R.attr.loadingIconColor), info.color));
}
@Override
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index cc4c2ef..b0e1db1 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -18,6 +18,7 @@
package com.android.launcher3.graphics;
import static com.android.launcher3.graphics.IconShape.DEFAULT_PATH_SIZE;
+import static com.android.launcher3.graphics.IconShape.getShapePath;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -101,13 +102,10 @@
private ObjectAnimator mCurrentAnim;
- /**
- * @param progressPath fixed path in the bounds [0, 0, 100, 100] representing a progress bar.
- */
- public PreloadIconDrawable(ItemInfoWithIcon info, Path progressPath, Context context) {
- super(info);
+ public PreloadIconDrawable(ItemInfoWithIcon info, Context context) {
+ super(info.bitmap);
mItem = info;
- mProgressPath = progressPath;
+ mProgressPath = getShapePath();
mScaledTrackPath = new Path();
mScaledProgressPath = new Path();
@@ -289,4 +287,11 @@
}
invalidateSelf();
}
+
+ /**
+ * Returns a FastBitmapDrawable with the icon.
+ */
+ public static PreloadIconDrawable newPendingIcon(Context context, ItemInfoWithIcon info) {
+ return new PreloadIconDrawable(info, context);
+ }
}
diff --git a/src/com/android/launcher3/icons/ClockDrawableWrapper.java b/src/com/android/launcher3/icons/ClockDrawableWrapper.java
new file mode 100644
index 0000000..b7dd092
--- /dev/null
+++ b/src/com/android/launcher3/icons/ClockDrawableWrapper.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.icons;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.android.launcher3.FastBitmapDrawable;
+
+import java.util.Calendar;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Wrapper over {@link AdaptiveIconDrawable} to intercept icon flattening logic for dynamic
+ * clock icons
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class ClockDrawableWrapper extends AdaptiveIconDrawable implements BitmapInfo.Extender {
+
+ private static final String TAG = "ClockDrawableWrapper";
+
+ private static final boolean DISABLE_SECONDS = true;
+
+ // Time after which the clock icon should check for an update. The actual invalidate
+ // will only happen in case of any change.
+ public static final long TICK_MS = DISABLE_SECONDS ? TimeUnit.MINUTES.toMillis(1) : 200L;
+
+ private static final String LAUNCHER_PACKAGE = "com.android.launcher3";
+ private static final String ROUND_ICON_METADATA_KEY = LAUNCHER_PACKAGE
+ + ".LEVEL_PER_TICK_ICON_ROUND";
+ private static final String HOUR_INDEX_METADATA_KEY = LAUNCHER_PACKAGE + ".HOUR_LAYER_INDEX";
+ private static final String MINUTE_INDEX_METADATA_KEY = LAUNCHER_PACKAGE
+ + ".MINUTE_LAYER_INDEX";
+ private static final String SECOND_INDEX_METADATA_KEY = LAUNCHER_PACKAGE
+ + ".SECOND_LAYER_INDEX";
+ private static final String DEFAULT_HOUR_METADATA_KEY = LAUNCHER_PACKAGE
+ + ".DEFAULT_HOUR";
+ private static final String DEFAULT_MINUTE_METADATA_KEY = LAUNCHER_PACKAGE
+ + ".DEFAULT_MINUTE";
+ private static final String DEFAULT_SECOND_METADATA_KEY = LAUNCHER_PACKAGE
+ + ".DEFAULT_SECOND";
+
+ /* Number of levels to jump per second for the second hand */
+ private static final int LEVELS_PER_SECOND = 10;
+
+ public static final int INVALID_VALUE = -1;
+
+ private final AnimationInfo mAnimationInfo = new AnimationInfo();
+ private int mTargetSdkVersion;
+
+ public ClockDrawableWrapper(AdaptiveIconDrawable base) {
+ super(base.getBackground(), base.getForeground());
+ }
+
+ /**
+ * Loads and returns the wrapper from the provided package, or returns null
+ * if it is unable to load.
+ */
+ public static ClockDrawableWrapper forPackage(Context context, String pkg, int iconDpi) {
+ try {
+ PackageManager pm = context.getPackageManager();
+ ApplicationInfo appInfo = pm.getApplicationInfo(pkg,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA);
+ final Bundle metadata = appInfo.metaData;
+ if (metadata == null) {
+ return null;
+ }
+ int drawableId = metadata.getInt(ROUND_ICON_METADATA_KEY, 0);
+ if (drawableId == 0) {
+ return null;
+ }
+
+ Drawable drawable = pm.getResourcesForApplication(appInfo).getDrawableForDensity(
+ drawableId, iconDpi).mutate();
+ if (!(drawable instanceof AdaptiveIconDrawable)) {
+ return null;
+ }
+
+ ClockDrawableWrapper wrapper =
+ new ClockDrawableWrapper((AdaptiveIconDrawable) drawable);
+ wrapper.mTargetSdkVersion = appInfo.targetSdkVersion;
+ AnimationInfo info = wrapper.mAnimationInfo;
+
+ info.baseDrawableState = drawable.getConstantState();
+
+ info.hourLayerIndex = metadata.getInt(HOUR_INDEX_METADATA_KEY, INVALID_VALUE);
+ info.minuteLayerIndex = metadata.getInt(MINUTE_INDEX_METADATA_KEY, INVALID_VALUE);
+ info.secondLayerIndex = metadata.getInt(SECOND_INDEX_METADATA_KEY, INVALID_VALUE);
+
+ info.defaultHour = metadata.getInt(DEFAULT_HOUR_METADATA_KEY, 0);
+ info.defaultMinute = metadata.getInt(DEFAULT_MINUTE_METADATA_KEY, 0);
+ info.defaultSecond = metadata.getInt(DEFAULT_SECOND_METADATA_KEY, 0);
+
+ LayerDrawable foreground = (LayerDrawable) wrapper.getForeground();
+ int layerCount = foreground.getNumberOfLayers();
+ if (info.hourLayerIndex < 0 || info.hourLayerIndex >= layerCount) {
+ info.hourLayerIndex = INVALID_VALUE;
+ }
+ if (info.minuteLayerIndex < 0 || info.minuteLayerIndex >= layerCount) {
+ info.minuteLayerIndex = INVALID_VALUE;
+ }
+ if (info.secondLayerIndex < 0 || info.secondLayerIndex >= layerCount) {
+ info.secondLayerIndex = INVALID_VALUE;
+ } else if (DISABLE_SECONDS) {
+ foreground.setDrawable(info.secondLayerIndex, null);
+ info.secondLayerIndex = INVALID_VALUE;
+ }
+ return wrapper;
+ } catch (Exception e) {
+ Log.d(TAG, "Unable to load clock drawable info", e);
+ }
+ return null;
+ }
+
+ @Override
+ public BitmapInfo getExtendedInfo(Bitmap bitmap, int color, BaseIconFactory iconFactory) {
+ iconFactory.disableColorExtraction();
+ float [] scale = new float[1];
+ AdaptiveIconDrawable background = new AdaptiveIconDrawable(
+ getBackground().getConstantState().newDrawable(), null);
+ BitmapInfo bitmapInfo = iconFactory.createBadgedIconBitmap(background,
+ Process.myUserHandle(), mTargetSdkVersion, false, scale);
+
+ return new ClockBitmapInfo(bitmap, color, scale[0], mAnimationInfo, bitmapInfo.icon);
+ }
+
+ @Override
+ public void prepareToDrawOnUi() {
+ mAnimationInfo.applyTime(Calendar.getInstance(), (LayerDrawable) getForeground());
+ }
+
+ private static class AnimationInfo {
+
+ public ConstantState baseDrawableState;
+
+ public int hourLayerIndex;
+ public int minuteLayerIndex;
+ public int secondLayerIndex;
+ public int defaultHour;
+ public int defaultMinute;
+ public int defaultSecond;
+
+ boolean applyTime(Calendar time, LayerDrawable foregroundDrawable) {
+ time.setTimeInMillis(System.currentTimeMillis());
+
+ // We need to rotate by the difference from the default time if one is specified.
+ int convertedHour = (time.get(Calendar.HOUR) + (12 - defaultHour)) % 12;
+ int convertedMinute = (time.get(Calendar.MINUTE) + (60 - defaultMinute)) % 60;
+ int convertedSecond = (time.get(Calendar.SECOND) + (60 - defaultSecond)) % 60;
+
+ boolean invalidate = false;
+ if (hourLayerIndex != INVALID_VALUE) {
+ final Drawable hour = foregroundDrawable.getDrawable(hourLayerIndex);
+ if (hour.setLevel(convertedHour * 60 + time.get(Calendar.MINUTE))) {
+ invalidate = true;
+ }
+ }
+
+ if (minuteLayerIndex != INVALID_VALUE) {
+ final Drawable minute = foregroundDrawable.getDrawable(minuteLayerIndex);
+ if (minute.setLevel(time.get(Calendar.HOUR) * 60 + convertedMinute)) {
+ invalidate = true;
+ }
+ }
+
+ if (secondLayerIndex != INVALID_VALUE) {
+ final Drawable second = foregroundDrawable.getDrawable(secondLayerIndex);
+ if (second.setLevel(convertedSecond * LEVELS_PER_SECOND)) {
+ invalidate = true;
+ }
+ }
+
+ return invalidate;
+ }
+ }
+
+ private static class ClockBitmapInfo extends BitmapInfo implements FastBitmapDrawable.Factory {
+
+ public final float scale;
+ public final int offset;
+ public final AnimationInfo animInfo;
+ public final Bitmap mFlattenedBackground;
+
+ ClockBitmapInfo(Bitmap icon, int color, float scale, AnimationInfo animInfo,
+ Bitmap background) {
+ super(icon, color);
+ this.scale = scale;
+ this.animInfo = animInfo;
+ this.offset = (int) Math.ceil(ShadowGenerator.BLUR_FACTOR * icon.getWidth());
+ this.mFlattenedBackground = background;
+ }
+
+ @Override
+ public FastBitmapDrawable newDrawable() {
+ return new ClockIconDrawable(this);
+ }
+ }
+
+ private static class ClockIconDrawable extends FastBitmapDrawable implements Runnable {
+
+ private final Calendar mTime = Calendar.getInstance();
+
+ private final ClockBitmapInfo mInfo;
+
+ private final AdaptiveIconDrawable mFullDrawable;
+ private final LayerDrawable mForeground;
+
+ ClockIconDrawable(ClockBitmapInfo clockInfo) {
+ super(clockInfo);
+
+ mInfo = clockInfo;
+
+ mFullDrawable = (AdaptiveIconDrawable) mInfo.animInfo.baseDrawableState.newDrawable();
+ mForeground = (LayerDrawable) mFullDrawable.getForeground();
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+ mFullDrawable.setBounds(bounds);
+ }
+
+ @Override
+ public void drawInternal(Canvas canvas, Rect bounds) {
+ if (mInfo == null) {
+ super.drawInternal(canvas, bounds);
+ return;
+ }
+ // draw the background that is already flattened to a bitmap
+ canvas.drawBitmap(mInfo.mFlattenedBackground, null, bounds, mPaint);
+
+ // prepare and draw the foreground
+ mInfo.animInfo.applyTime(mTime, mForeground);
+
+ canvas.scale(mInfo.scale, mInfo.scale,
+ bounds.exactCenterX() + mInfo.offset, bounds.exactCenterY() + mInfo.offset);
+ canvas.clipPath(mFullDrawable.getIconMask());
+ mForeground.draw(canvas);
+
+ reschedule();
+ }
+
+ @Override
+ protected void updateFilter() {
+ super.updateFilter();
+ mFullDrawable.setColorFilter(mPaint.getColorFilter());
+ }
+
+ @Override
+ public void run() {
+ if (mInfo.animInfo.applyTime(mTime, mForeground)) {
+ invalidateSelf();
+ } else {
+ reschedule();
+ }
+ }
+
+ @Override
+ public boolean setVisible(boolean visible, boolean restart) {
+ boolean result = super.setVisible(visible, restart);
+ if (visible) {
+ reschedule();
+ } else {
+ unscheduleSelf(this);
+ }
+ return result;
+ }
+
+ private void reschedule() {
+ if (!isVisible()) {
+ return;
+ }
+
+ unscheduleSelf(this);
+ final long upTime = SystemClock.uptimeMillis();
+ final long step = TICK_MS; /* tick every 200 ms */
+ scheduleSelf(this, upTime - ((upTime % step)) + step);
+ }
+
+ @Override
+ public ConstantState getConstantState() {
+ return new ClockConstantState(mInfo, isDisabled());
+ }
+
+ private static class ClockConstantState extends MyConstantState {
+
+ private final ClockBitmapInfo mInfo;
+
+ ClockConstantState(ClockBitmapInfo info, boolean isDisabled) {
+ super(info.icon, info.color, isDisabled);
+ mInfo = info;
+ }
+
+ @Override
+ public FastBitmapDrawable newDrawable() {
+ ClockIconDrawable drawable = new ClockIconDrawable(mInfo);
+ drawable.setIsDisabled(mIsDisabled);
+ return drawable;
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher3/icons/ComponentWithLabel.java b/src/com/android/launcher3/icons/ComponentWithLabel.java
index 832956d..f7ee5f9 100644
--- a/src/com/android/launcher3/icons/ComponentWithLabel.java
+++ b/src/com/android/launcher3/icons/ComponentWithLabel.java
@@ -57,10 +57,8 @@
}
@Override
- public void loadIcon(Context context,
- ComponentWithLabel object, BitmapInfo target) {
- // Do not load icon.
- target.icon = BitmapInfo.LOW_RES_ICON;
+ public BitmapInfo loadIcon(Context context, ComponentWithLabel object) {
+ return BitmapInfo.LOW_RES_INFO;
}
@Override
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 9c46260..4ac6ff4 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -23,9 +23,12 @@
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;
@@ -35,13 +38,11 @@
import androidx.annotation.NonNull;
import com.android.launcher3.AppInfo;
-import com.android.launcher3.IconProvider;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherFiles;
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;
@@ -49,7 +50,10 @@
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.ComponentKey;
import com.android.launcher3.util.InstantAppResolver;
+import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
import java.util.function.Supplier;
@@ -63,8 +67,9 @@
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;
@@ -76,10 +81,11 @@
inv.fillResIconDpi, inv.iconBitmapSize, true /* inMemoryCache */);
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);
+ mIconProvider = new IconProvider(context);
}
@Override
@@ -159,7 +165,7 @@
CacheEntry entry = cacheLocked(application.componentName,
application.user, () -> null, mLauncherActivityInfoCachingLogic,
false, application.usingLowResIcon());
- if (entry.icon != null && !isDefaultIcon(entry.icon, application.user)) {
+ if (entry.bitmap != null && !isDefaultIcon(entry.bitmap, application.user)) {
applyCacheEntry(entry, application);
}
}
@@ -174,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.
*/
@@ -181,7 +195,7 @@
// null info means not installed, but if we have a component from the intent then
// we should still look in the cache for restored app icons.
if (info.getTargetComponent() == null) {
- info.applyFrom(getDefaultIcon(info.user));
+ info.bitmap = getDefaultIcon(info.user);
info.title = "";
info.contentDescription = "";
} else {
@@ -224,15 +238,15 @@
protected void applyCacheEntry(CacheEntry entry, ItemInfoWithIcon info) {
info.title = Utilities.trim(entry.title);
info.contentDescription = entry.contentDescription;
- info.applyFrom((entry.icon == null) ? getDefaultIcon(info.user) : entry);
+ info.bitmap = (entry.bitmap == null) ? getDefaultIcon(info.user) : entry.bitmap;
}
public Drawable getFullResIcon(LauncherActivityInfo info) {
- return getFullResIcon(info, true);
+ return mIconProvider.getIcon(info, mIconDpi);
}
- public Drawable getFullResIcon(LauncherActivityInfo info, boolean flattenDrawable) {
- return mIconProvider.getIcon(info, mIconDpi, flattenDrawable);
+ public void updateSessionCache(PackageUserKey key, PackageInstaller.SessionInfo info) {
+ cachePackageInstallInfo(key.mPackageName, key.mUser, info.getAppIcon(), info.getAppLabel());
}
@Override
@@ -241,6 +255,15 @@
+ ",flags_asi:" + FeatureFlags.APP_SEARCH_IMPROVEMENTS.get();
}
+ @Override
+ protected boolean getEntryFromDB(ComponentKey cacheKey, CacheEntry entry, boolean lowRes) {
+ if (mIconProvider.isClockIcon(cacheKey)) {
+ // For clock icon, we always load the dynamic icon
+ return false;
+ }
+ return super.getEntryFromDB(cacheKey, entry, lowRes);
+ }
+
public static abstract class IconLoadRequest extends HandlerRunnable {
IconLoadRequest(Handler handler, Runnable endRunnable) {
super(handler, endRunnable);
diff --git a/src/com/android/launcher3/icons/IconProvider.java b/src/com/android/launcher3/icons/IconProvider.java
new file mode 100644
index 0000000..26b7eae
--- /dev/null
+++ b/src/com/android/launcher3/icons/IconProvider.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.icons;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Process;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.launcher3.R;
+import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.icons.BitmapInfo.Extender;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.SafeCloseable;
+
+import java.util.Calendar;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+
+/**
+ * Class to handle icon loading from different packages
+ */
+public class IconProvider {
+
+ private static final String TAG = "IconProvider";
+ private static final boolean DEBUG = false;
+
+ private static final String ICON_METADATA_KEY_PREFIX = ".dynamic_icons";
+
+ private static final String SYSTEM_STATE_SEPARATOR = " ";
+
+ // Default value returned if there are problems getting resources.
+ private static final int NO_ID = 0;
+
+ private static final BiFunction<LauncherActivityInfo, Integer, Drawable> LAI_LOADER =
+ LauncherActivityInfo::getIcon;
+
+ private static final BiFunction<ActivityInfo, PackageManager, Drawable> AI_LOADER =
+ ActivityInfo::loadUnbadgedIcon;
+
+
+ private final Context mContext;
+ private final ComponentName mCalendar;
+ private final ComponentName mClock;
+
+ public IconProvider(Context context) {
+ mContext = context;
+ mCalendar = parseComponentOrNull(context, R.string.calendar_component_name);
+ mClock = parseComponentOrNull(context, R.string.clock_component_name);
+ }
+
+ /**
+ * Adds any modification to the provided systemState for dynamic icons. This system state
+ * is used by caches to check for icon invalidation.
+ */
+ public String getSystemStateForPackage(String systemState, String packageName) {
+ if (mCalendar != null && mCalendar.getPackageName().equals(packageName)) {
+ return systemState + SYSTEM_STATE_SEPARATOR + getDay();
+ } else {
+ return systemState;
+ }
+ }
+
+ /**
+ * Loads the icon for the provided LauncherActivityInfo such that it can be drawn directly
+ * on the UI
+ */
+ public Drawable getIconForUI(LauncherActivityInfo info, int iconDpi) {
+ Drawable icon = getIcon(info, iconDpi);
+ if (icon instanceof BitmapInfo.Extender) {
+ ((Extender) icon).prepareToDrawOnUi();
+ }
+ return icon;
+ }
+
+ /**
+ * Loads the icon for the provided LauncherActivityInfo
+ */
+ public Drawable getIcon(LauncherActivityInfo info, int iconDpi) {
+ return getIcon(info.getApplicationInfo().packageName, info.getUser(),
+ info, iconDpi, LAI_LOADER);
+ }
+
+ /**
+ * Loads the icon for the provided activity info
+ */
+ public Drawable getIcon(ActivityInfo info, UserHandle user) {
+ return getIcon(info.applicationInfo.packageName, user, info, mContext.getPackageManager(),
+ AI_LOADER);
+ }
+
+ private <T, P> Drawable getIcon(String packageName, UserHandle user, T obj, P param,
+ BiFunction<T, P, Drawable> loader) {
+ Drawable icon = null;
+ if (mCalendar != null && mCalendar.getPackageName().equals(packageName)) {
+ icon = loadCalendarDrawable(0);
+ } else if (mClock != null
+ && mClock.getPackageName().equals(packageName)
+ && Process.myUserHandle().equals(user)) {
+ icon = loadClockDrawable(0);
+ }
+ return icon == null ? loader.apply(obj, param) : icon;
+ }
+
+ private Drawable loadCalendarDrawable(int iconDpi) {
+ PackageManager pm = mContext.getPackageManager();
+ try {
+ final Bundle metadata = pm.getActivityInfo(
+ mCalendar,
+ PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA)
+ .metaData;
+ final Resources resources = pm.getResourcesForApplication(mCalendar.getPackageName());
+ final int id = getDynamicIconId(metadata, resources);
+ if (id != NO_ID) {
+ if (DEBUG) Log.d(TAG, "Got icon #" + id);
+ return resources.getDrawableForDensity(id, iconDpi, null /* theme */);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ if (DEBUG) {
+ Log.d(TAG, "Could not get activityinfo or resources for package: "
+ + mCalendar.getPackageName());
+ }
+ }
+ return null;
+ }
+
+ private Drawable loadClockDrawable(int iconDpi) {
+ return ClockDrawableWrapper.forPackage(mContext, mClock.getPackageName(), iconDpi);
+ }
+
+ protected boolean isClockIcon(ComponentKey key) {
+ return mClock != null && mClock.equals(key.componentName)
+ && Process.myUserHandle().equals(key.user);
+ }
+
+ /**
+ * @param metadata metadata of the default activity of Calendar
+ * @param resources from the Calendar package
+ * @return the resource id for today's Calendar icon; 0 if resources cannot be found.
+ */
+ private int getDynamicIconId(Bundle metadata, Resources resources) {
+ if (metadata == null) {
+ return NO_ID;
+ }
+ String key = mCalendar.getPackageName() + ICON_METADATA_KEY_PREFIX;
+ final int arrayId = metadata.getInt(key, NO_ID);
+ if (arrayId == NO_ID) {
+ return NO_ID;
+ }
+ try {
+ return resources.obtainTypedArray(arrayId).getResourceId(getDay(), NO_ID);
+ } catch (Resources.NotFoundException e) {
+ if (DEBUG) {
+ Log.d(TAG, "package defines '" + key + "' but corresponding array not found");
+ }
+ return NO_ID;
+ }
+ }
+
+ /**
+ * @return Today's day of the month, zero-indexed.
+ */
+ private int getDay() {
+ return Calendar.getInstance().get(Calendar.DAY_OF_MONTH) - 1;
+ }
+
+
+ /**
+ * Registers a callback to listen for calendar icon changes.
+ * The callback receives the packageName for the calendar icon
+ */
+ public static SafeCloseable registerIconChangeListener(Context context,
+ BiConsumer<String, UserHandle> callback, Handler handler) {
+ ComponentName calendar = parseComponentOrNull(context, R.string.calendar_component_name);
+ ComponentName clock = parseComponentOrNull(context, R.string.clock_component_name);
+
+ if (calendar == null && clock == null) {
+ return () -> { };
+ }
+
+ BroadcastReceiver receiver = new DateTimeChangeReceiver(callback);
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
+ if (calendar != null) {
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
+ filter.addAction(Intent.ACTION_DATE_CHANGED);
+ }
+ context.registerReceiver(receiver, filter, null, handler);
+
+ return () -> context.unregisterReceiver(receiver);
+ }
+
+ private static class DateTimeChangeReceiver extends BroadcastReceiver {
+
+ private final BiConsumer<String, UserHandle> mCallback;
+
+ DateTimeChangeReceiver(BiConsumer<String, UserHandle> callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
+ ComponentName clock = parseComponentOrNull(context, R.string.clock_component_name);
+ if (clock != null) {
+ mCallback.accept(clock.getPackageName(), Process.myUserHandle());
+ }
+ }
+
+ ComponentName calendar =
+ parseComponentOrNull(context, R.string.calendar_component_name);
+ if (calendar != null) {
+ for (UserHandle user : UserManagerCompat.getInstance(context).getUserProfiles()) {
+ mCallback.accept(calendar.getPackageName(), user);
+ }
+ }
+
+ }
+ }
+
+ private static ComponentName parseComponentOrNull(Context context, int resId) {
+ String cn = context.getString(resId);
+ return TextUtils.isEmpty(cn) ? null : ComponentName.unflattenFromString(cn);
+
+ }
+}
diff --git a/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
index f9a94da..93de35a 100644
--- a/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
+++ b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
@@ -20,7 +20,6 @@
import android.content.pm.LauncherActivityInfo;
import android.os.UserHandle;
-import com.android.launcher3.IconProvider;
import com.android.launcher3.R;
import com.android.launcher3.icons.cache.CachingLogic;
import com.android.launcher3.util.ResourceBasedOverride;
@@ -55,13 +54,11 @@
}
@Override
- public void loadIcon(Context context, LauncherActivityInfo object,
- BitmapInfo target) {
- LauncherIcons li = LauncherIcons.obtain(context);
- li.createBadgedIconBitmap(
- IconProvider.INSTANCE.get(context)
- .getIcon(object, li.mFillResIconDpi, true /* flattenDrawable */),
- object.getUser(), object.getApplicationInfo().targetSdkVersion).applyTo(target);
- li.recycle();
+ public BitmapInfo loadIcon(Context context, LauncherActivityInfo object) {
+ try (LauncherIcons li = LauncherIcons.obtain(context)) {
+ return li.createBadgedIconBitmap(new IconProvider(context)
+ .getIcon(object, li.mFillResIconDpi),
+ object.getUser(), object.getApplicationInfo().targetSdkVersion);
+ }
}
}
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index adc92c4..4d3599e 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -25,13 +25,14 @@
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;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.shortcuts.DeepShortcutManager;
@@ -45,8 +46,6 @@
*/
public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
- private static final String EXTRA_BADGEPKG = "badge_package";
-
private static final Object sPoolSync = new Object();
private static LauncherIcons sPool;
private static int sPoolId = 0;
@@ -115,7 +114,6 @@
}
// below methods should also migrate to BaseIconFactory
-
public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo) {
return createShortcutIcon(shortcutInfo, true /* badged */);
}
@@ -124,12 +122,20 @@
return createShortcutIcon(shortcutInfo, badged, null);
}
- public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo,
- boolean badged, @Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
+ public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo, boolean badged,
+ @Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
+ if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
+ return createShortcutIconCached(shortcutInfo, badged, true, fallbackIconProvider);
+ } else {
+ return createShortcutIconLegacy(shortcutInfo, badged, fallbackIconProvider);
+ }
+ }
+
+ public BitmapInfo createShortcutIconLegacy(ShortcutInfo shortcutInfo, boolean badged,
+ @Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
Drawable unbadgedDrawable = DeepShortcutManager.getInstance(mContext)
.getShortcutIconDrawable(shortcutInfo, mFillResIconDpi);
IconCache cache = LauncherAppState.getInstance(mContext).getIconCache();
-
final Bitmap unbadgedBitmap;
if (unbadgedDrawable != null) {
unbadgedBitmap = createScaledBitmapWithoutShadow(unbadgedDrawable, 0);
@@ -137,37 +143,68 @@
if (fallbackIconProvider != null) {
// Fallback icons are already badged and with appropriate shadow
ItemInfoWithIcon fullIcon = fallbackIconProvider.get();
- if (fullIcon != null && fullIcon.iconBitmap != null) {
- BitmapInfo result = new BitmapInfo();
- result.icon = fullIcon.iconBitmap;
- result.color = fullIcon.iconColor;
- return result;
+ if (fullIcon != null && fullIcon.bitmap != null) {
+ return fullIcon.bitmap;
}
}
unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle()).icon;
}
- BitmapInfo result = new BitmapInfo();
if (!badged) {
- result.color = Themes.getColorAccent(mContext);
- result.icon = unbadgedBitmap;
- return result;
+ return BitmapInfo.of(unbadgedBitmap, Themes.getColorAccent(mContext));
}
final Bitmap unbadgedfinal = unbadgedBitmap;
final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache);
- result.color = badge.iconColor;
- result.icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
+ Bitmap icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
getShadowGenerator().recreateIcon(unbadgedfinal, c);
- badgeWithDrawable(c, new FastBitmapDrawable(badge));
+ badgeWithDrawable(c, new FastBitmapDrawable(badge.bitmap));
});
- return result;
+ return BitmapInfo.of(icon, badge.bitmap.color);
+ }
+
+ @WorkerThread
+ public BitmapInfo createShortcutIconCached(ShortcutInfo shortcutInfo, boolean badged,
+ boolean useCache, @Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
+ IconCache cache = LauncherAppState.getInstance(mContext).getIconCache();
+ final BitmapInfo bitmapInfo;
+ if (useCache) {
+ bitmapInfo = cache.getDeepShortcutTitleAndIcon(shortcutInfo).bitmap;
+ } else {
+ bitmapInfo = new ShortcutCachingLogic().loadIcon(mContext, shortcutInfo);
+ }
+ final Bitmap unbadgedBitmap;
+ if (bitmapInfo.icon != null) {
+ unbadgedBitmap = bitmapInfo.icon;
+ } else {
+ if (fallbackIconProvider != null) {
+ // Fallback icons are already badged and with appropriate shadow
+ ItemInfoWithIcon fullIcon = fallbackIconProvider.get();
+ if (fullIcon != null && fullIcon.bitmap != null) {
+ return fullIcon.bitmap;
+ }
+ }
+ unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle()).icon;
+ }
+
+ if (!badged) {
+ return BitmapInfo.of(unbadgedBitmap, Themes.getColorAccent(mContext));
+ }
+
+ final Bitmap unbadgedfinal = unbadgedBitmap;
+ final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache);
+
+ Bitmap icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
+ getShadowGenerator().recreateIcon(unbadgedfinal, c);
+ badgeWithDrawable(c, new FastBitmapDrawable(badge.bitmap));
+ });
+ return BitmapInfo.of(icon, badge.bitmap.color);
}
public ItemInfoWithIcon getShortcutInfoBadge(ShortcutInfo shortcutInfo, IconCache cache) {
ComponentName cn = shortcutInfo.getActivity();
- String badgePkg = getBadgePackage(shortcutInfo);
+ String badgePkg = shortcutInfo.getPackage();
boolean hasBadgePkgSet = !badgePkg.equals(shortcutInfo.getPackage());
if (cn != null && !hasBadgePkgSet) {
// Get the app info for the source activity.
@@ -185,14 +222,4 @@
return pkgInfo;
}
}
-
- private String getBadgePackage(ShortcutInfo si) {
- String whitelistedPkg = mContext.getString(R.string.shortcutinfo_badgepkg_whitelist);
- if (whitelistedPkg.equals(si.getPackage())
- && si.getExtras() != null
- && si.getExtras().containsKey(EXTRA_BADGEPKG)) {
- return si.getExtras().getString(EXTRA_BADGEPKG);
- }
- return si.getPackage();
- }
}
diff --git a/src/com/android/launcher3/icons/ShortcutCachingLogic.java b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
new file mode 100644
index 0000000..5c21470
--- /dev/null
+++ b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
@@ -0,0 +1,79 @@
+/*
+ * 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 androidx.annotation.NonNull;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.icons.cache.CachingLogic;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.Themes;
+
+/**
+ * Caching logic for shortcuts.
+ */
+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();
+ }
+
+ @NonNull
+ @Override
+ public BitmapInfo loadIcon(Context context, ShortcutInfo info) {
+ try (LauncherIcons li = LauncherIcons.obtain(context)) {
+ Drawable unbadgedDrawable = DeepShortcutManager.getInstance(context)
+ .getShortcutIconDrawable(info, LauncherAppState.getIDP(context).fillResIconDpi);
+ if (unbadgedDrawable == null) return BitmapInfo.LOW_RES_INFO;
+ return new BitmapInfo(li.createScaledBitmapWithoutShadow(
+ unbadgedDrawable, 0), Themes.getColorAccent(context));
+ }
+ }
+
+ @Override
+ public long getLastUpdatedTime(ShortcutInfo shortcutInfo, PackageInfo info) {
+ if (shortcutInfo == null || !FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
+ return info.lastUpdateTime;
+ }
+ return Math.max(shortcutInfo.getLastChangedTimestamp(), info.lastUpdateTime);
+ }
+
+ @Override
+ public boolean addToMemCache() {
+ return false;
+ }
+}
diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java
index 923a89b..04cf20a 100644
--- a/src/com/android/launcher3/logging/FileLog.java
+++ b/src/com/android/launcher3/logging/FileLog.java
@@ -8,6 +8,8 @@
import android.util.Log;
import android.util.Pair;
+import androidx.annotation.VisibleForTesting;
+
import com.android.launcher3.util.IOUtils;
import java.io.BufferedReader;
@@ -88,7 +90,8 @@
Message.obtain(getHandler(), LogWriterCallback.MSG_WRITE, out).sendToTarget();
}
- private static Handler getHandler() {
+ @VisibleForTesting
+ static Handler getHandler() {
synchronized (DATE_FORMAT) {
if (sHandler == null) {
sHandler = new Handler(createAndStartNewLooper("file-logger"),
@@ -102,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;
}
/**
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index b8c583c..fa0fe1b 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -17,6 +17,7 @@
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;
@@ -29,11 +30,10 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.WorkspaceItemInfo;
-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;
@@ -95,7 +95,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.
@@ -151,7 +151,7 @@
WorkspaceItemInfo wii = (WorkspaceItemInfo) itemInfo;
wii.title = "";
- wii.applyFrom(app.getIconCache().getDefaultIcon(item.user));
+ wii.bitmap = app.getIconCache().getDefaultIcon(item.user);
app.getIconCache().getTitleAndIcon(wii,
((WorkspaceItemInfo) itemInfo).usingLowResIcon());
}
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index 3873a17..9f1843f 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -23,21 +23,24 @@
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 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.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.PackageInstallerCompat;
-import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
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;
@@ -46,9 +49,6 @@
import java.util.List;
import java.util.function.Consumer;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
/**
* Stores the list of all applications for the all apps view.
@@ -110,10 +110,9 @@
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);
@@ -134,10 +133,10 @@
&& appInfo.user.equals(user)
&& appInfo instanceof PromiseAppInfo) {
final PromiseAppInfo promiseAppInfo = (PromiseAppInfo) appInfo;
- if (installInfo.state == PackageInstallerCompat.STATUS_INSTALLING) {
+ if (installInfo.state == PackageInstallInfo.STATUS_INSTALLING) {
promiseAppInfo.level = installInfo.progress;
return promiseAppInfo;
- } else if (installInfo.state == PackageInstallerCompat.STATUS_FAILED) {
+ } else if (installInfo.state == PackageInstallInfo.STATUS_FAILED) {
removeApp(i);
}
}
@@ -164,11 +163,8 @@
* 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);
}
}
@@ -214,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.
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index 0a4f005..a00a6bd 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -300,8 +300,8 @@
public LooperIdleLock newIdleLock(Object lock) {
LooperIdleLock idleLock = new LooperIdleLock(lock, Looper.getMainLooper());
- // If we are not binding, there is no reason to wait for idle.
- if (mCallbacks.get() == null) {
+ // If we are not binding or if the main looper is already idle, there is no reason to wait
+ if (mCallbacks.get() == null || Looper.getMainLooper().getQueue().isIdle()) {
idleLock.queueIdle();
}
return idleLock;
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..95268d0 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;
@@ -153,7 +153,7 @@
info.title = getTitle();
// the fallback icon
if (!loadIcon(info)) {
- info.applyFrom(mIconCache.getDefaultIcon(info.user));
+ info.bitmap = mIconCache.getDefaultIcon(info.user);
}
// TODO: If there's an explicit component and we can't install that, delete it.
@@ -183,7 +183,7 @@
info.iconResource.resourceName = resourceName;
BitmapInfo iconInfo = li.createIconBitmap(info.iconResource);
if (iconInfo != null) {
- info.applyFrom(iconInfo);
+ info.bitmap = iconInfo;
return true;
}
}
@@ -192,7 +192,7 @@
// Failed to load from resource, try loading from DB.
byte[] data = getBlob(iconIndex);
try {
- info.applyFrom(li.createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length)));
+ info.bitmap = li.createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length));
return true;
} catch (Exception e) {
Log.e(TAG, "Failed to decode byte array for info " + info, e);
@@ -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);
@@ -273,7 +273,7 @@
info.intent = newIntent;
mIconCache.getTitleAndIcon(info, lai, useLowResIcon);
- if (mIconCache.isDefaultIcon(info.iconBitmap, user)) {
+ if (mIconCache.isDefaultIcon(info.bitmap, user)) {
loadIcon(info);
}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 50e1d56..3a4085c 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -30,6 +30,7 @@
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;
@@ -39,6 +40,7 @@
import android.util.Log;
import android.util.LongSparseArray;
import android.util.MutableInt;
+import android.util.TimingLogger;
import com.android.launcher3.AppInfo;
import com.android.launcher3.FolderInfo;
@@ -52,8 +54,6 @@
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;
@@ -63,8 +63,11 @@
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;
@@ -104,7 +107,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;
@@ -120,7 +123,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());
@@ -168,82 +171,99 @@
}
}
- TraceHelper.beginSection(TAG);
+ Object traceToken = TraceHelper.INSTANCE.beginSection(TAG);
+ TimingLogger logger = 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");
+
+ if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
+ 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");
+
+ if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
+ 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();
- TraceHelper.partitionSection(TAG, "step 4.3: save widgets in icon cache");
updateHandler.updateIcons(allWidgetsList, new ComponentCachingLogic(
mApp.getContext(), true), mApp.getModel()::onWidgetLabelsUpdated);
+ logger.addSplit("save widgets in icon cache");
verifyNotStopped();
- TraceHelper.partitionSection(TAG, "step 5: Finish icon cache update");
updateHandler.finish();
+ logger.addSplit("finish icon update");
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() {
@@ -251,7 +271,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);
@@ -286,7 +306,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);
@@ -387,7 +409,7 @@
// 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 it's a deep shortcut, we'll use pinned shortcuts to restore it
if (cn != null && validTarget && c.itemType
@@ -396,7 +418,7 @@
// 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 {
@@ -496,14 +518,16 @@
// use the last saved icon instead of the default.
Supplier<ItemInfoWithIcon> fallbackIconProvider = () ->
c.loadIcon(finalInfo, li) ? finalInfo : null;
- info.applyFrom(li.createShortcutIcon(pinnedShortcut,
- true /* badged */, fallbackIconProvider));
+ info.bitmap = li.createShortcutIcon(
+ pinnedShortcut, true /* badged */,
+ fallbackIconProvider);
li.recycle();
if (pmHelper.isAppSuspended(
pinnedShortcut.getPackage(), info.user)) {
info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
}
intent = info.intent;
+ allDeepShortcuts.add(pinnedShortcut);
} else {
// Create a shortcut info in disabled mode for now.
info = c.loadSimpleWorkspaceItem();
@@ -576,7 +600,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;
}
@@ -839,12 +863,12 @@
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));
}
}
@@ -852,7 +876,8 @@
return allActivityList;
}
- private void loadDeepShortcuts() {
+ private List<ShortcutInfo> loadDeepShortcuts() {
+ List<ShortcutInfo> allShortcuts = new ArrayList<>();
mBgDataModel.deepShortcutMap.clear();
mBgDataModel.hasShortcutHostPermission = mShortcutManager.hasHostPermission();
if (mBgDataModel.hasShortcutHostPermission) {
@@ -860,10 +885,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/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index 802cbc7..2832150 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -19,16 +19,14 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-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.model.BgDataModel.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.HashSet;
@@ -46,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.
@@ -79,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/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index d6ebaaf..1e614bd 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -16,10 +16,12 @@
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;
@@ -33,7 +35,6 @@
import com.android.launcher3.SessionCommitReceiver;
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.BitmapInfo;
@@ -55,9 +56,6 @@
import java.util.HashSet;
import java.util.List;
-import static com.android.launcher3.WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
-import static com.android.launcher3.WorkspaceItemInfo.FLAG_RESTORED_ICON;
-
/**
* Handles updates due to changes in package manager (app installed/updated/removed)
* or when a user availability changes.
@@ -107,7 +105,7 @@
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);
@@ -191,7 +189,7 @@
BitmapInfo iconInfo = li.createIconBitmap(si.iconResource);
li.recycle();
if (iconInfo != null) {
- si.applyFrom(iconInfo);
+ si.bitmap = iconInfo;
infoUpdated = true;
}
}
@@ -221,8 +219,8 @@
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_RESTORED_ICON | FLAG_AUTOINSTALL_ICON)) {
if (updateWorkspaceItemIntent(context, si, packageName)) {
@@ -305,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]);
}
}
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 6c358b1..05225d4 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -93,8 +93,8 @@
// If the shortcut is pinned but no longer has an icon in the system,
// keep the current icon instead of reverting to the default icon.
LauncherIcons li = LauncherIcons.obtain(context);
- workspaceItemInfo.applyFrom(li.createShortcutIcon(fullDetails, true,
- () -> workspaceItemInfo));
+ workspaceItemInfo.bitmap = li.createShortcutIcon(
+ fullDetails, true, () -> workspaceItemInfo);
li.recycle();
updatedWorkspaceItemInfos.add(workspaceItemInfo);
}
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 4b773d7..db1c307 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -92,7 +92,7 @@
// If the shortcut is pinned but no longer has an icon in the system,
// keep the current icon instead of reverting to the default icon.
LauncherIcons li = LauncherIcons.obtain(context);
- si.applyFrom(li.createShortcutIcon(shortcut, true, () -> si));
+ si.bitmap = li.createShortcutIcon(shortcut, true, () -> si);
li.recycle();
} else {
si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
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/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index 10378ee..059ad18 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.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
@@ -32,9 +33,10 @@
import android.util.Log;
import android.util.Pair;
+import androidx.annotation.AnyThread;
import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
-import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.SecureSettingsObserver;
@@ -44,6 +46,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
/**
* A {@link NotificationListenerService} that sends updates to its
@@ -59,16 +62,17 @@
private static final int MSG_NOTIFICATION_POSTED = 1;
private static final int MSG_NOTIFICATION_REMOVED = 2;
private static final int MSG_NOTIFICATION_FULL_REFRESH = 3;
+ private static final int MSG_CANCEL_NOTIFICATION = 4;
+ private static final int MSG_RANKING_UPDATE = 5;
private static NotificationListener sNotificationListenerInstance = null;
private static NotificationsChangedListener sNotificationsChangedListener;
- private static StatusBarNotificationsChangedListener sStatusBarNotificationsChangedListener;
private static boolean sIsConnected;
- private static boolean sIsCreated;
private final Handler mWorkerHandler;
private final Handler mUiHandler;
private final Ranking mTempRanking = new Ranking();
+
/** Maps groupKey's to the corresponding group of notifications. */
private final Map<String, NotificationGroup> mNotificationGroupMap = new HashMap<>();
/** Maps keys to their corresponding current group key */
@@ -79,85 +83,12 @@
private SecureSettingsObserver mNotificationDotsObserver;
- private final Handler.Callback mWorkerCallback = new Handler.Callback() {
- @Override
- public boolean handleMessage(Message message) {
- switch (message.what) {
- case MSG_NOTIFICATION_POSTED:
- mUiHandler.obtainMessage(message.what, message.obj).sendToTarget();
- break;
- case MSG_NOTIFICATION_REMOVED:
- mUiHandler.obtainMessage(message.what, message.obj).sendToTarget();
- break;
- case MSG_NOTIFICATION_FULL_REFRESH:
- List<StatusBarNotification> activeNotifications;
- if (sIsConnected) {
- try {
- activeNotifications = filterNotifications(getActiveNotifications());
- } catch (SecurityException ex) {
- Log.e(TAG, "SecurityException: failed to fetch notifications");
- activeNotifications = new ArrayList<StatusBarNotification>();
-
- }
- } else {
- activeNotifications = new ArrayList<StatusBarNotification>();
- }
-
- mUiHandler.obtainMessage(message.what, activeNotifications).sendToTarget();
- break;
- }
- return true;
- }
- };
-
- private final Handler.Callback mUiCallback = new Handler.Callback() {
- @Override
- public boolean handleMessage(Message message) {
- switch (message.what) {
- case MSG_NOTIFICATION_POSTED:
- if (sNotificationsChangedListener != null) {
- NotificationPostedMsg msg = (NotificationPostedMsg) message.obj;
- sNotificationsChangedListener.onNotificationPosted(msg.packageUserKey,
- msg.notificationKey, msg.shouldBeFilteredOut);
- }
- break;
- case MSG_NOTIFICATION_REMOVED:
- if (sNotificationsChangedListener != null) {
- Pair<PackageUserKey, NotificationKeyData> pair
- = (Pair<PackageUserKey, NotificationKeyData>) message.obj;
- sNotificationsChangedListener.onNotificationRemoved(pair.first, pair.second);
- }
- break;
- case MSG_NOTIFICATION_FULL_REFRESH:
- if (sNotificationsChangedListener != null) {
- sNotificationsChangedListener.onNotificationFullRefresh(
- (List<StatusBarNotification>) message.obj);
- }
- break;
- }
- return true;
- }
- };
-
public NotificationListener() {
- super();
- mWorkerHandler = new Handler(MODEL_EXECUTOR.getLooper(), mWorkerCallback);
- mUiHandler = new Handler(Looper.getMainLooper(), mUiCallback);
+ mWorkerHandler = new Handler(MODEL_EXECUTOR.getLooper(), this::handleWorkerMessage);
+ mUiHandler = new Handler(Looper.getMainLooper(), this::handleUiMessage);
sNotificationListenerInstance = this;
}
- @Override
- public void onCreate() {
- super.onCreate();
- sIsCreated = true;
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- sIsCreated = false;
- }
-
public static @Nullable NotificationListener getInstanceIfConnected() {
return sIsConnected ? sNotificationListenerInstance : null;
}
@@ -168,25 +99,107 @@
NotificationListener notificationListener = getInstanceIfConnected();
if (notificationListener != null) {
notificationListener.onNotificationFullRefresh();
- } else if (!sIsCreated && sNotificationsChangedListener != null) {
+ } else {
// User turned off dots globally, so we unbound this service;
// tell the listener that there are no notifications to remove dots.
- sNotificationsChangedListener.onNotificationFullRefresh(
- Collections.<StatusBarNotification>emptyList());
+ MODEL_EXECUTOR.submit(() -> MAIN_EXECUTOR.submit(() ->
+ listener.onNotificationFullRefresh(Collections.emptyList())));
}
}
- public static void setStatusBarNotificationsChangedListener
- (StatusBarNotificationsChangedListener listener) {
- sStatusBarNotificationsChangedListener = listener;
- }
-
public static void removeNotificationsChangedListener() {
sNotificationsChangedListener = null;
}
- public static void removeStatusBarNotificationsChangedListener() {
- sStatusBarNotificationsChangedListener = null;
+ private boolean handleWorkerMessage(Message message) {
+ switch (message.what) {
+ case MSG_NOTIFICATION_POSTED: {
+ StatusBarNotification sbn = (StatusBarNotification) message.obj;
+ mUiHandler.obtainMessage(notificationIsValidForUI(sbn)
+ ? MSG_NOTIFICATION_POSTED : MSG_NOTIFICATION_REMOVED,
+ toKeyPair(sbn)).sendToTarget();
+ return true;
+ }
+ case MSG_NOTIFICATION_REMOVED: {
+ StatusBarNotification sbn = (StatusBarNotification) message.obj;
+ mUiHandler.obtainMessage(MSG_NOTIFICATION_REMOVED,
+ toKeyPair(sbn)).sendToTarget();
+
+ NotificationGroup notificationGroup = mNotificationGroupMap.get(sbn.getGroupKey());
+ String key = sbn.getKey();
+ if (notificationGroup != null) {
+ notificationGroup.removeChildKey(key);
+ if (notificationGroup.isEmpty()) {
+ if (key.equals(mLastKeyDismissedByLauncher)) {
+ // Only cancel the group notification if launcher dismissed the
+ // last child.
+ cancelNotification(notificationGroup.getGroupSummaryKey());
+ }
+ mNotificationGroupMap.remove(sbn.getGroupKey());
+ }
+ }
+ if (key.equals(mLastKeyDismissedByLauncher)) {
+ mLastKeyDismissedByLauncher = null;
+ }
+ return true;
+ }
+ case MSG_NOTIFICATION_FULL_REFRESH:
+ List<StatusBarNotification> activeNotifications = null;
+ if (sIsConnected) {
+ try {
+ activeNotifications = Arrays.stream(getActiveNotifications())
+ .filter(this::notificationIsValidForUI)
+ .collect(Collectors.toList());
+ } catch (SecurityException ex) {
+ Log.e(TAG, "SecurityException: failed to fetch notifications");
+ activeNotifications = new ArrayList<>();
+ }
+ } else {
+ activeNotifications = new ArrayList<>();
+ }
+
+ mUiHandler.obtainMessage(message.what, activeNotifications).sendToTarget();
+ return true;
+ case MSG_CANCEL_NOTIFICATION: {
+ mLastKeyDismissedByLauncher = (String) message.obj;
+ cancelNotification(mLastKeyDismissedByLauncher);
+ return true;
+ }
+ case MSG_RANKING_UPDATE: {
+ String[] keys = ((RankingMap) message.obj).getOrderedKeys();
+ for (StatusBarNotification sbn : getActiveNotifications(keys)) {
+ updateGroupKeyIfNecessary(sbn);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean handleUiMessage(Message message) {
+ switch (message.what) {
+ case MSG_NOTIFICATION_POSTED:
+ if (sNotificationsChangedListener != null) {
+ Pair<PackageUserKey, NotificationKeyData> msg = (Pair) message.obj;
+ sNotificationsChangedListener.onNotificationPosted(
+ msg.first, msg.second);
+ }
+ break;
+ case MSG_NOTIFICATION_REMOVED:
+ if (sNotificationsChangedListener != null) {
+ Pair<PackageUserKey, NotificationKeyData> msg = (Pair) message.obj;
+ sNotificationsChangedListener.onNotificationRemoved(
+ msg.first, msg.second);
+ }
+ break;
+ case MSG_NOTIFICATION_FULL_REFRESH:
+ if (sNotificationsChangedListener != null) {
+ sNotificationsChangedListener.onNotificationFullRefresh(
+ (List<StatusBarNotification>) message.obj);
+ }
+ break;
+ }
+ return true;
}
@Override
@@ -217,84 +230,37 @@
super.onListenerDisconnected();
sIsConnected = false;
mNotificationDotsObserver.unregister();
+ onNotificationFullRefresh();
}
@Override
public void onNotificationPosted(final StatusBarNotification sbn) {
- super.onNotificationPosted(sbn);
- if (sbn == null) {
- // There is a bug in platform where we can get a null notification; just ignore it.
- return;
- }
- mWorkerHandler.obtainMessage(MSG_NOTIFICATION_POSTED, new NotificationPostedMsg(sbn))
- .sendToTarget();
- if (sStatusBarNotificationsChangedListener != null) {
- sStatusBarNotificationsChangedListener.onNotificationPosted(sbn);
- }
- }
-
- /**
- * An object containing data to send to MSG_NOTIFICATION_POSTED targets.
- */
- private class NotificationPostedMsg {
- final PackageUserKey packageUserKey;
- final NotificationKeyData notificationKey;
- final boolean shouldBeFilteredOut;
-
- NotificationPostedMsg(StatusBarNotification sbn) {
- packageUserKey = PackageUserKey.fromNotification(sbn);
- notificationKey = NotificationKeyData.fromNotification(sbn);
- shouldBeFilteredOut = shouldBeFilteredOut(sbn);
+ if (sbn != null) {
+ mWorkerHandler.obtainMessage(MSG_NOTIFICATION_POSTED, sbn).sendToTarget();
}
}
@Override
public void onNotificationRemoved(final StatusBarNotification sbn) {
- super.onNotificationRemoved(sbn);
- if (sbn == null) {
- // There is a bug in platform where we can get a null notification; just ignore it.
- return;
+ if (sbn != null) {
+ mWorkerHandler.obtainMessage(MSG_NOTIFICATION_REMOVED, sbn).sendToTarget();
}
- Pair<PackageUserKey, NotificationKeyData> packageUserKeyAndNotificationKey
- = new Pair<>(PackageUserKey.fromNotification(sbn),
- NotificationKeyData.fromNotification(sbn));
- mWorkerHandler.obtainMessage(MSG_NOTIFICATION_REMOVED, packageUserKeyAndNotificationKey)
- .sendToTarget();
- if (sStatusBarNotificationsChangedListener != null) {
- sStatusBarNotificationsChangedListener.onNotificationRemoved(sbn);
- }
-
- NotificationGroup notificationGroup = mNotificationGroupMap.get(sbn.getGroupKey());
- String key = sbn.getKey();
- if (notificationGroup != null) {
- notificationGroup.removeChildKey(key);
- if (notificationGroup.isEmpty()) {
- if (key.equals(mLastKeyDismissedByLauncher)) {
- // Only cancel the group notification if launcher dismissed the last child.
- cancelNotification(notificationGroup.getGroupSummaryKey());
- }
- mNotificationGroupMap.remove(sbn.getGroupKey());
- }
- }
- if (key.equals(mLastKeyDismissedByLauncher)) {
- mLastKeyDismissedByLauncher = null;
- }
- }
-
- public void cancelNotificationFromLauncher(String key) {
- mLastKeyDismissedByLauncher = key;
- cancelNotification(key);
}
@Override
public void onNotificationRankingUpdate(RankingMap rankingMap) {
- super.onNotificationRankingUpdate(rankingMap);
- String[] keys = rankingMap.getOrderedKeys();
- for (StatusBarNotification sbn : getActiveNotifications(keys)) {
- updateGroupKeyIfNecessary(sbn);
- }
+ mWorkerHandler.obtainMessage(MSG_RANKING_UPDATE, rankingMap).sendToTarget();
}
+ /**
+ * Cancels a notification
+ */
+ @AnyThread
+ public void cancelNotificationFromLauncher(String key) {
+ mWorkerHandler.obtainMessage(MSG_CANCEL_NOTIFICATION, key).sendToTarget();
+ }
+
+ @WorkerThread
private void updateGroupKeyIfNecessary(StatusBarNotification sbn) {
String childKey = sbn.getKey();
String oldGroupKey = mNotificationGroupKeyMap.get(childKey);
@@ -328,53 +294,33 @@
}
}
- /** This makes a potentially expensive binder call and should be run on a background thread. */
+ /**
+ * This makes a potentially expensive binder call and should be run on a background thread.
+ */
+ @WorkerThread
public List<StatusBarNotification> getNotificationsForKeys(List<NotificationKeyData> keys) {
- StatusBarNotification[] notifications = NotificationListener.this
- .getActiveNotifications(NotificationKeyData.extractKeysOnly(keys)
- .toArray(new String[keys.size()]));
- return notifications == null
- ? Collections.<StatusBarNotification>emptyList() : Arrays.asList(notifications);
+ StatusBarNotification[] notifications = getActiveNotifications(
+ keys.stream().map(n -> n.notificationKey).toArray(String[]::new));
+ return notifications == null ? Collections.emptyList() : Arrays.asList(notifications);
}
/**
- * Filter out notifications that don't have an intent
- * or are headers for grouped notifications.
- *
- * @see #shouldBeFilteredOut(StatusBarNotification)
+ * Returns true for notifications that have an intent and are not headers for grouped
+ * notifications and should be shown in the notification popup.
*/
- private List<StatusBarNotification> filterNotifications(
- StatusBarNotification[] notifications) {
- if (notifications == null) return null;
- IntSet removedNotifications = new IntSet();
- for (int i = 0; i < notifications.length; i++) {
- if (shouldBeFilteredOut(notifications[i])) {
- removedNotifications.add(i);
- }
- }
- List<StatusBarNotification> filteredNotifications = new ArrayList<>(
- notifications.length - removedNotifications.size());
- for (int i = 0; i < notifications.length; i++) {
- if (!removedNotifications.contains(i)) {
- filteredNotifications.add(notifications[i]);
- }
- }
- return filteredNotifications;
- }
-
- private boolean shouldBeFilteredOut(StatusBarNotification sbn) {
+ @WorkerThread
+ private boolean notificationIsValidForUI(StatusBarNotification sbn) {
Notification notification = sbn.getNotification();
-
updateGroupKeyIfNecessary(sbn);
getCurrentRanking().getRanking(sbn.getKey(), mTempRanking);
if (!mTempRanking.canShowBadge()) {
- return true;
+ return false;
}
if (mTempRanking.getChannel().getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
// Special filtering for the default, legacy "Miscellaneous" channel.
if ((notification.flags & Notification.FLAG_ONGOING_EVENT) != 0) {
- return true;
+ return false;
}
}
@@ -382,19 +328,19 @@
CharSequence text = notification.extras.getCharSequence(Notification.EXTRA_TEXT);
boolean missingTitleAndText = TextUtils.isEmpty(title) && TextUtils.isEmpty(text);
boolean isGroupHeader = (notification.flags & Notification.FLAG_GROUP_SUMMARY) != 0;
- return (isGroupHeader || missingTitleAndText);
+ return !isGroupHeader && !missingTitleAndText;
+ }
+
+ private static Pair<PackageUserKey, NotificationKeyData> toKeyPair(StatusBarNotification sbn) {
+ return Pair.create(PackageUserKey.fromNotification(sbn),
+ NotificationKeyData.fromNotification(sbn));
}
public interface NotificationsChangedListener {
void onNotificationPosted(PackageUserKey postedPackageUserKey,
- NotificationKeyData notificationKey, boolean shouldBeFilteredOut);
+ NotificationKeyData notificationKey);
void onNotificationRemoved(PackageUserKey removedPackageUserKey,
NotificationKeyData notificationKey);
void onNotificationFullRefresh(List<StatusBarNotification> activeNotifications);
}
-
- public interface StatusBarNotificationsChangedListener {
- void onNotificationPosted(StatusBarNotification sbn);
- void onNotificationRemoved(StatusBarNotification sbn);
- }
}
diff --git a/src/com/android/launcher3/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..a15399d
--- /dev/null
+++ b/src/com/android/launcher3/pm/PinRequestHelper.java
@@ -0,0 +1,106 @@
+/*
+ * 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.LauncherAppState;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.icons.LauncherIcons;
+
+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.
+ if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
+ fetchAndUpdateShortcutIconAsync(context, info, si, false);
+ } else {
+ LauncherIcons li = LauncherIcons.obtain(context);
+ info.bitmap = li.createShortcutIcon(si, false /* badged */);
+ li.recycle();
+ LauncherAppState.getInstance(context).getModel()
+ .updateAndBindWorkspaceItem(info, si);
+ }
+ 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 e8ac1d4..e70673a 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -77,6 +77,7 @@
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
/**
* A container for shortcuts to deep links and notifications associated with an app.
@@ -196,9 +197,6 @@
* @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.
@@ -213,7 +211,7 @@
final PopupContainerWithArrow container =
(PopupContainerWithArrow) launcher.getLayoutInflater().inflate(
R.layout.popup_container, launcher.getDragLayer(), false);
- container.populateAndShow(icon, itemInfo, SystemShortcutFactory.INSTANCE.get(launcher));
+ container.populateAndShow(icon, itemInfo);
return container;
}
@@ -238,16 +236,15 @@
}
}
- protected void populateAndShow(
- BubbleTextView icon, ItemInfo item, SystemShortcutFactory factory) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_CONTEXT_MENU, "populateAndShow");
- }
+ protected void populateAndShow(BubbleTextView icon, ItemInfo item) {
PopupDataProvider popupDataProvider = mLauncher.getPopupDataProvider();
populateAndShow(icon,
popupDataProvider.getShortcutCountForItem(item),
popupDataProvider.getNotificationKeysForItem(item),
- factory.getEnabledShortcuts(mLauncher, item));
+ mLauncher.getSupportedShortcuts()
+ .map(s -> s.getShortcut(mLauncher, item))
+ .filter(s -> s != null)
+ .collect(Collectors.toList()));
}
public ViewGroup getSystemShortcutContainerForTesting() {
@@ -382,8 +379,7 @@
@Override
public void onWidgetsBound() {
ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
- SystemShortcut widgetInfo = new SystemShortcut.Widgets();
- View.OnClickListener onClickListener = widgetInfo.getOnClickListener(mLauncher, itemInfo);
+ SystemShortcut widgetInfo = SystemShortcut.WIDGETS.getShortcut(mLauncher, itemInfo);
View widgetsView = null;
int count = mSystemShortcutContainer.getChildCount();
for (int i = 0; i < count; i++) {
@@ -394,7 +390,7 @@
}
}
- if (onClickListener != null && widgetsView == null) {
+ if (widgetInfo != null && widgetsView == null) {
// We didn't have any widgets cached but now there are some, so enable the shortcut.
if (mSystemShortcutContainer != this) {
initializeSystemShortcut(
@@ -407,7 +403,7 @@
close(false);
PopupContainerWithArrow.showForIcon(mOriginalIcon);
}
- } else if (onClickListener == null && widgetsView != null) {
+ } else if (widgetInfo == null && widgetsView != null) {
// No widgets exist, but we previously added the shortcut so remove it.
if (mSystemShortcutContainer != this) {
mSystemShortcutContainer.removeView(widgetsView);
@@ -430,8 +426,7 @@
info.setIconAndContentDescriptionFor((ImageView) view);
}
view.setTag(info);
- view.setOnClickListener(info.getOnClickListener(mLauncher,
- (ItemInfo) mOriginalIcon.getTag()));
+ view.setOnClickListener(info);
}
/**
@@ -506,7 +501,7 @@
DotInfo dotInfo = mLauncher.getDotInfoForItem(itemInfo);
if (mNotificationItemView != null && dotInfo != null) {
mNotificationItemView.updateHeader(
- dotInfo.getNotificationCount(), itemInfo.iconColor);
+ dotInfo.getNotificationCount(), itemInfo.bitmap.color);
}
}
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index 4612b2a..c5aa836 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -20,13 +20,15 @@
import android.service.notification.StatusBarNotification;
import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.notification.NotificationKeyData;
import com.android.launcher3.notification.NotificationListener;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.ShortcutUtil;
@@ -39,13 +41,9 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
/**
* Provides data for the popup menu that appears after long-clicking on apps.
*/
@@ -76,28 +74,14 @@
@Override
public void onNotificationPosted(PackageUserKey postedPackageUserKey,
- NotificationKeyData notificationKey, boolean shouldBeFilteredOut) {
+ NotificationKeyData notificationKey) {
DotInfo dotInfo = mPackageUserToDotInfos.get(postedPackageUserKey);
- boolean dotShouldBeRefreshed;
if (dotInfo == null) {
- if (!shouldBeFilteredOut) {
- DotInfo newDotInfo = new DotInfo();
- newDotInfo.addOrUpdateNotificationKey(notificationKey);
- mPackageUserToDotInfos.put(postedPackageUserKey, newDotInfo);
- dotShouldBeRefreshed = true;
- } else {
- dotShouldBeRefreshed = false;
- }
- } else {
- dotShouldBeRefreshed = shouldBeFilteredOut
- ? dotInfo.removeNotificationKey(notificationKey)
- : dotInfo.addOrUpdateNotificationKey(notificationKey);
- if (dotInfo.getNotificationKeys().size() == 0) {
- mPackageUserToDotInfos.remove(postedPackageUserKey);
- }
+ dotInfo = new DotInfo();
+ mPackageUserToDotInfos.put(postedPackageUserKey, dotInfo);
}
- if (dotShouldBeRefreshed) {
- updateNotificationDots(t -> postedPackageUserKey.equals(t));
+ if (dotInfo.addOrUpdateNotificationKey(notificationKey)) {
+ updateNotificationDots(postedPackageUserKey::equals);
}
}
@@ -109,7 +93,7 @@
if (oldDotInfo.getNotificationKeys().size() == 0) {
mPackageUserToDotInfos.remove(removedPackageUserKey);
}
- updateNotificationDots(t -> removedPackageUserKey.equals(t));
+ updateNotificationDots(removedPackageUserKey::equals);
trimNotifications(mPackageUserToDotInfos);
}
}
@@ -195,14 +179,6 @@
: getNotificationsForItem(info, dotInfo.getNotificationKeys());
}
- /** This makes a potentially expensive binder call and should be run on a background thread. */
- public @NonNull List<StatusBarNotification> getStatusBarNotificationsForKeys(
- List<NotificationKeyData> notificationKeys) {
- NotificationListener notificationListener = NotificationListener.getInstanceIfConnected();
- return notificationListener == null ? Collections.EMPTY_LIST
- : notificationListener.getNotificationsForKeys(notificationKeys);
- }
-
public void cancelNotification(String notificationKey) {
NotificationListener notificationListener = NotificationListener.getInstanceIfConnected();
if (notificationListener == null) {
diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java
index dbfe988..80c6683 100644
--- a/src/com/android/launcher3/popup/PopupPopulator.java
+++ b/src/com/android/launcher3/popup/PopupPopulator.java
@@ -20,7 +20,9 @@
import android.content.pm.ShortcutInfo;
import android.os.Handler;
import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
@@ -28,6 +30,7 @@
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.notification.NotificationInfo;
import com.android.launcher3.notification.NotificationKeyData;
+import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.util.PackageUserKey;
@@ -37,9 +40,7 @@
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
+import java.util.stream.Collectors;
/**
* Contains logic relevant to populating a {@link PopupContainerWithArrow}. In particular,
@@ -130,12 +131,15 @@
final UserHandle user = originalInfo.user;
return () -> {
if (!notificationKeys.isEmpty()) {
- List<StatusBarNotification> notifications = launcher.getPopupDataProvider()
- .getStatusBarNotificationsForKeys(notificationKeys);
- List<NotificationInfo> infos = new ArrayList<>(notifications.size());
- for (int i = 0; i < notifications.size(); i++) {
- StatusBarNotification notification = notifications.get(i);
- infos.add(new NotificationInfo(launcher, notification));
+ NotificationListener notificationListener =
+ NotificationListener.getInstanceIfConnected();
+ final List<NotificationInfo> infos;
+ if (notificationListener == null) {
+ infos = Collections.emptyList();
+ } else {
+ infos = notificationListener.getNotificationsForKeys(notificationKeys).stream()
+ .map(sbn -> new NotificationInfo(launcher, sbn))
+ .collect(Collectors.toList());
}
uiHandler.post(() -> container.applyNotificationInfos(infos));
}
@@ -150,7 +154,7 @@
final WorkspaceItemInfo si = new WorkspaceItemInfo(shortcut, launcher);
// Use unbadged icon for the menu.
LauncherIcons li = LauncherIcons.obtain(launcher);
- si.applyFrom(li.createShortcutIcon(shortcut, false /* badged */));
+ si.bitmap = li.createShortcutIcon(shortcut, false /* badged */);
li.recycle();
si.rank = i;
diff --git a/src/com/android/launcher3/popup/RemoteActionShortcut.java b/src/com/android/launcher3/popup/RemoteActionShortcut.java
index 5a5fbab..8751202 100644
--- a/src/com/android/launcher3/popup/RemoteActionShortcut.java
+++ b/src/com/android/launcher3/popup/RemoteActionShortcut.java
@@ -16,13 +16,19 @@
package com.android.launcher3.popup;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.app.RemoteAction;
+import android.content.Context;
import android.content.Intent;
-import android.os.Handler;
-import android.os.Looper;
+import android.os.Build;
import android.util.Log;
import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.ImageView;
+import android.widget.TextView;
import android.widget.Toast;
import com.android.launcher3.AbstractFloatingView;
@@ -32,55 +38,75 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.userevent.nano.LauncherLogProto;
+@TargetApi(Build.VERSION_CODES.Q)
public class RemoteActionShortcut extends SystemShortcut<BaseDraggingActivity> {
private static final String TAG = "RemoteActionShortcut";
private static final boolean DEBUG = Utilities.IS_DEBUG_DEVICE;
private final RemoteAction mAction;
- public RemoteActionShortcut(RemoteAction action) {
- super(action.getIcon(), action.getTitle(), action.getContentDescription(),
- R.id.action_remote_action_shortcut);
+ public RemoteActionShortcut(RemoteAction action,
+ BaseDraggingActivity activity, ItemInfo itemInfo) {
+ super(0, R.id.action_remote_action_shortcut, activity, itemInfo);
mAction = action;
}
@Override
- public View.OnClickListener getOnClickListener(
- final BaseDraggingActivity activity, final ItemInfo itemInfo) {
- return view -> {
- AbstractFloatingView.closeAllOpenViews(activity);
+ public void setIconAndLabelFor(View iconView, TextView labelView) {
+ mAction.getIcon().loadDrawableAsync(iconView.getContext(),
+ iconView::setBackground,
+ MAIN_EXECUTOR.getHandler());
+ labelView.setText(mAction.getTitle());
+ }
- final String actionIdentity = mAction.getTitle() + ", " +
- itemInfo.getTargetComponent().getPackageName();
- try {
- if (DEBUG) Log.d(TAG, "Sending action: " + actionIdentity);
- mAction.getActionIntent().send(
- activity,
- 0,
- new Intent().putExtra(
- Intent.EXTRA_PACKAGE_NAME,
- itemInfo.getTargetComponent().getPackageName()),
- (pendingIntent, intent, resultCode, resultData, resultExtras) -> {
- if (DEBUG) Log.d(TAG, "Action is complete: " + actionIdentity);
- if (resultData != null && !resultData.isEmpty()) {
- Log.e(TAG, "Remote action returned result: " + actionIdentity
- + " : " + resultData);
- Toast.makeText(activity, resultData, Toast.LENGTH_SHORT).show();
- }
- },
- new Handler(Looper.getMainLooper()));
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Remote action canceled: " + actionIdentity, e);
- Toast.makeText(activity, activity.getString(
- R.string.remote_action_failed,
- mAction.getTitle()),
- Toast.LENGTH_SHORT)
- .show();
- }
+ @Override
+ public void setIconAndContentDescriptionFor(ImageView view) {
+ mAction.getIcon().loadDrawableAsync(view.getContext(),
+ view::setImageDrawable,
+ MAIN_EXECUTOR.getHandler());
+ view.setContentDescription(mAction.getContentDescription());
+ }
- activity.getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.Touch.TAP,
- LauncherLogProto.ControlType.REMOTE_ACTION_SHORTCUT, view);
- };
+ @Override
+ public AccessibilityNodeInfo.AccessibilityAction createAccessibilityAction(Context context) {
+ return new AccessibilityNodeInfo.AccessibilityAction(
+ R.id.action_remote_action_shortcut, mAction.getContentDescription());
+ }
+
+ @Override
+ public void onClick(View view) {
+ AbstractFloatingView.closeAllOpenViews(mTarget);
+
+ final String actionIdentity = mAction.getTitle() + ", "
+ + mItemInfo.getTargetComponent().getPackageName();
+ try {
+ if (DEBUG) Log.d(TAG, "Sending action: " + actionIdentity);
+ mAction.getActionIntent().send(
+ mTarget,
+ 0,
+ new Intent().putExtra(
+ Intent.EXTRA_PACKAGE_NAME,
+ mItemInfo.getTargetComponent().getPackageName()),
+ (pendingIntent, intent, resultCode, resultData, resultExtras) -> {
+ if (DEBUG) Log.d(TAG, "Action is complete: " + actionIdentity);
+ if (resultData != null && !resultData.isEmpty()) {
+ Log.e(TAG, "Remote action returned result: " + actionIdentity
+ + " : " + resultData);
+ Toast.makeText(mTarget, resultData, Toast.LENGTH_SHORT).show();
+ }
+ },
+ MAIN_EXECUTOR.getHandler());
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Remote action canceled: " + actionIdentity, e);
+ Toast.makeText(mTarget, mTarget.getString(
+ R.string.remote_action_failed,
+ mAction.getTitle()),
+ Toast.LENGTH_SHORT)
+ .show();
+ }
+
+ mTarget.getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.Touch.TAP,
+ LauncherLogProto.ControlType.REMOTE_ACTION_SHORTCUT, view);
}
@Override
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index a87b7b8..222c6c9 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -5,14 +5,13 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
-import android.graphics.drawable.Icon;
-import android.os.Handler;
-import android.os.Looper;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ItemInfo;
@@ -39,41 +38,30 @@
* 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
+ implements View.OnClickListener {
+
private final int mIconResId;
private final int mLabelResId;
- private final Icon mIcon;
- private final CharSequence mLabel;
- private final CharSequence mContentDescription;
private final int mAccessibilityActionId;
- public SystemShortcut(int iconResId, int labelResId) {
+ protected final T mTarget;
+ protected final ItemInfo mItemInfo;
+
+ public SystemShortcut(int iconResId, int labelResId, T target, ItemInfo itemInfo) {
mIconResId = iconResId;
mLabelResId = labelResId;
mAccessibilityActionId = labelResId;
- mIcon = null;
- mLabel = null;
- mContentDescription = null;
+ mTarget = target;
+ mItemInfo = itemInfo;
}
- public SystemShortcut(Icon icon, CharSequence label, CharSequence contentDescription,
- int accessibilityActionId) {
- mIcon = icon;
- mLabel = label;
- mContentDescription = contentDescription;
- mAccessibilityActionId = accessibilityActionId;
- mIconResId = 0;
- mLabelResId = 0;
- }
-
- public SystemShortcut(SystemShortcut other) {
+ public SystemShortcut(SystemShortcut<T> other) {
mIconResId = other.mIconResId;
mLabelResId = other.mLabelResId;
- mIcon = other.mIcon;
- mLabel = other.mLabel;
- mContentDescription = other.mContentDescription;
mAccessibilityActionId = other.mAccessibilityActionId;
+ mTarget = other.mTarget;
+ mItemInfo = other.mItemInfo;
}
/**
@@ -84,150 +72,135 @@
}
public void setIconAndLabelFor(View iconView, TextView labelView) {
- if (mIcon != null) {
- mIcon.loadDrawableAsync(iconView.getContext(),
- iconView::setBackground,
- new Handler(Looper.getMainLooper()));
- } else {
- iconView.setBackgroundResource(mIconResId);
- }
-
- if (mLabel != null) {
- labelView.setText(mLabel);
- } else {
- labelView.setText(mLabelResId);
- }
+ iconView.setBackgroundResource(mIconResId);
+ labelView.setText(mLabelResId);
}
public void setIconAndContentDescriptionFor(ImageView view) {
- if (mIcon != null) {
- mIcon.loadDrawableAsync(view.getContext(),
- view::setImageDrawable,
- new Handler(Looper.getMainLooper()));
- } else {
- view.setImageResource(mIconResId);
- }
-
- view.setContentDescription(getContentDescription(view.getContext()));
- }
-
- private CharSequence getContentDescription(Context context) {
- return mContentDescription != null ? mContentDescription : context.getText(mLabelResId);
+ view.setImageResource(mIconResId);
+ view.setContentDescription(view.getContext().getText(mLabelResId));
}
public AccessibilityNodeInfo.AccessibilityAction createAccessibilityAction(Context context) {
- return new AccessibilityNodeInfo.AccessibilityAction(mAccessibilityActionId,
- getContentDescription(context));
+ return new AccessibilityNodeInfo.AccessibilityAction(
+ mAccessibilityActionId, context.getText(mLabelResId));
}
public boolean hasHandlerForAction(int action) {
return mAccessibilityActionId == action;
}
- public abstract View.OnClickListener getOnClickListener(T activity, ItemInfo itemInfo);
+ public interface Factory<T extends BaseDraggingActivity> {
+
+ @Nullable SystemShortcut<T> getShortcut(T activity, ItemInfo itemInfo);
+ }
+
+ public static final Factory<Launcher> WIDGETS = (launcher, itemInfo) -> {
+ if (itemInfo.getTargetComponent() == null) return null;
+ final List<WidgetItem> widgets =
+ launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
+ itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
+ if (widgets == null) {
+ return null;
+ }
+ return new Widgets(launcher, itemInfo);
+ };
public static class Widgets extends SystemShortcut<Launcher> {
- public Widgets() {
- super(R.drawable.ic_widget, R.string.widget_button_text);
+ public Widgets(Launcher target, ItemInfo itemInfo) {
+ super(R.drawable.ic_widget, R.string.widget_button_text, target, itemInfo);
}
@Override
- public View.OnClickListener getOnClickListener(final Launcher launcher,
- final ItemInfo itemInfo) {
- if (itemInfo.getTargetComponent() == null) return null;
- final List<WidgetItem> widgets =
- launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
- itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
- if (widgets == null) {
- return null;
- }
- return (view) -> {
- AbstractFloatingView.closeAllOpenViews(launcher);
- WidgetsBottomSheet widgetsBottomSheet =
- (WidgetsBottomSheet) launcher.getLayoutInflater().inflate(
- R.layout.widgets_bottom_sheet, launcher.getDragLayer(), false);
- widgetsBottomSheet.populateAndShow(itemInfo);
- launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
- ControlType.WIDGETS_BUTTON, view);
- };
+ public void onClick(View view) {
+ AbstractFloatingView.closeAllOpenViews(mTarget);
+ WidgetsBottomSheet widgetsBottomSheet =
+ (WidgetsBottomSheet) mTarget.getLayoutInflater().inflate(
+ R.layout.widgets_bottom_sheet, mTarget.getDragLayer(), false);
+ widgetsBottomSheet.populateAndShow(mItemInfo);
+ mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+ ControlType.WIDGETS_BUTTON, view);
}
}
+ public static final Factory<BaseDraggingActivity> APP_INFO = AppInfo::new;
+
public static class AppInfo extends SystemShortcut {
- public AppInfo() {
- super(R.drawable.ic_info_no_shadow, R.string.app_info_drop_target_label);
+
+ public AppInfo(BaseDraggingActivity target, ItemInfo itemInfo) {
+ super(R.drawable.ic_info_no_shadow, R.string.app_info_drop_target_label, target,
+ itemInfo);
}
@Override
- public View.OnClickListener getOnClickListener(
- BaseDraggingActivity activity, ItemInfo itemInfo) {
- return (view) -> {
- dismissTaskMenuView(activity);
- Rect sourceBounds = activity.getViewBounds(view);
- new PackageManagerHelper(activity).startDetailsActivityForInfo(
- itemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
- activity.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
- ControlType.APPINFO_TARGET, view);
- };
+ public void onClick(View view) {
+ dismissTaskMenuView(mTarget);
+ Rect sourceBounds = mTarget.getViewBounds(view);
+ new PackageManagerHelper(mTarget).startDetailsActivityForInfo(
+ mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
+ mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+ ControlType.APPINFO_TARGET, view);
}
}
+ public static Factory<BaseDraggingActivity> INSTALL = (activity, itemInfo) -> {
+ boolean supportsWebUI = (itemInfo instanceof WorkspaceItemInfo)
+ && ((WorkspaceItemInfo) itemInfo).hasStatusFlag(
+ WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI);
+ boolean isInstantApp = false;
+ if (itemInfo instanceof com.android.launcher3.AppInfo) {
+ com.android.launcher3.AppInfo appInfo = (com.android.launcher3.AppInfo) itemInfo;
+ isInstantApp = InstantAppResolver.newInstance(activity).isInstantApp(appInfo);
+ }
+ boolean enabled = supportsWebUI || isInstantApp;
+ if (!enabled) {
+ return null;
+ }
+ return new Install(activity, itemInfo);
+ };
+
public static class Install extends SystemShortcut {
- public Install() {
- super(R.drawable.ic_install_no_shadow, R.string.install_drop_target_label);
+
+ public Install(BaseDraggingActivity target, ItemInfo itemInfo) {
+ super(R.drawable.ic_install_no_shadow, R.string.install_drop_target_label,
+ target, itemInfo);
}
@Override
- public View.OnClickListener getOnClickListener(
- BaseDraggingActivity activity, ItemInfo itemInfo) {
- boolean supportsWebUI = (itemInfo instanceof WorkspaceItemInfo) &&
- ((WorkspaceItemInfo) itemInfo).hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI);
- boolean isInstantApp = false;
- if (itemInfo instanceof com.android.launcher3.AppInfo) {
- com.android.launcher3.AppInfo appInfo = (com.android.launcher3.AppInfo) itemInfo;
- isInstantApp = InstantAppResolver.newInstance(activity).isInstantApp(appInfo);
- }
- boolean enabled = supportsWebUI || isInstantApp;
- if (!enabled) {
- return null;
- }
- return createOnClickListener(activity, itemInfo);
- }
-
- public View.OnClickListener createOnClickListener(
- BaseDraggingActivity activity, ItemInfo itemInfo) {
- return view -> {
- Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent(
- itemInfo.getTargetComponent().getPackageName());
- activity.startActivitySafely(view, intent, itemInfo, null);
- AbstractFloatingView.closeAllOpenViews(activity);
- };
+ public void onClick(View view) {
+ Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent(
+ mItemInfo.getTargetComponent().getPackageName());
+ mTarget.startActivitySafely(view, intent, mItemInfo, null);
+ AbstractFloatingView.closeAllOpenViews(mTarget);
}
}
+ public static Factory<Launcher> DISMISS_PREDICTION = (launcher, itemInfo) -> {
+ if (!FeatureFlags.ENABLE_PREDICTION_DISMISS.get()) return null;
+ if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_PREDICTION) return null;
+ return new DismissPrediction(launcher, itemInfo);
+ };
+
public static class DismissPrediction extends SystemShortcut<Launcher> {
- public DismissPrediction() {
- super(R.drawable.ic_remove_no_shadow, R.string.dismiss_prediction_label);
+ public DismissPrediction(Launcher launcher, ItemInfo itemInfo) {
+ super(R.drawable.ic_remove_no_shadow, R.string.dismiss_prediction_label, launcher,
+ itemInfo);
}
@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);
- };
+ public void onClick(View view) {
+ PopupContainerWithArrow.closeAllOpenViews(mTarget);
+ mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+ ControlType.DISMISS_PREDICTION, ContainerType.DEEPSHORTCUTS);
+ AppLaunchTracker.INSTANCE.get(view.getContext()).onDismissApp(
+ mItemInfo.getTargetComponent(),
+ mItemInfo.user,
+ AppLaunchTracker.CONTAINER_PREDICTIONS);
}
}
- protected static void dismissTaskMenuView(BaseDraggingActivity activity) {
+ public 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
deleted file mode 100644
index dfcc2f8..0000000
--- a/src/com/android/launcher3/popup/SystemShortcutFactory.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.popup;
-
-import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.ResourceBasedOverride;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class SystemShortcutFactory implements ResourceBasedOverride {
-
- public static final MainThreadInitializedObject<SystemShortcutFactory> INSTANCE =
- forOverride(SystemShortcutFactory.class, R.string.system_shortcut_factory_class);
-
- /** Note that these are in order of priority. */
- private final SystemShortcut[] mAllShortcuts;
-
- @SuppressWarnings("unused")
- public SystemShortcutFactory() {
- this(new SystemShortcut.AppInfo(),
- new SystemShortcut.Widgets(),
- new SystemShortcut.Install(),
- new SystemShortcut.DismissPrediction());
- }
-
- protected SystemShortcutFactory(SystemShortcut... shortcuts) {
- mAllShortcuts = shortcuts;
- }
-
- public @NonNull List<SystemShortcut> getEnabledShortcuts(Launcher launcher, ItemInfo info) {
- List<SystemShortcut> systemShortcuts = new ArrayList<>();
- for (SystemShortcut systemShortcut : mAllShortcuts) {
- if (systemShortcut.getOnClickListener(launcher, info) != null) {
- systemShortcuts.add(systemShortcut);
- }
- }
-
- return systemShortcuts;
- }
-}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
index ee97641..3e59b61 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
@@ -25,7 +25,9 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.icons.BitmapRenderer;
/**
* Extension of {@link DragPreviewProvider} which generates bitmaps scaled to the default icon size.
@@ -39,16 +41,27 @@
mPositionShift = shift;
}
+ @Override
public Bitmap createDragBitmap() {
+ if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
+ int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx;
+ return BitmapRenderer.createHardwareBitmap(
+ size + blurSizeOutline,
+ size + blurSizeOutline,
+ (c) -> drawDragViewOnBackground(c, size));
+ } else {
+ return createDragBitmapLegacy();
+ }
+ }
+
+ private Bitmap createDragBitmapLegacy() {
Drawable d = mView.getBackground();
Rect bounds = getDrawableBounds(d);
-
int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx;
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);
@@ -57,6 +70,16 @@
return b;
}
+ private void drawDragViewOnBackground(Canvas canvas, float size) {
+ Drawable d = mView.getBackground();
+ Rect bounds = getDrawableBounds(d);
+
+ canvas.translate(blurSizeOutline / 2, blurSizeOutline / 2);
+ canvas.scale(size / bounds.width(), size / bounds.height(), 0, 0);
+ canvas.translate(bounds.left, bounds.top);
+ d.draw(canvas);
+ }
+
@Override
public float getScaleAndPosition(Bitmap preview, int[] outPos) {
Launcher launcher = Launcher.getLauncher(mView.getContext());
diff --git a/src/com/android/launcher3/states/InternalStateHandler.java b/src/com/android/launcher3/states/InternalStateHandler.java
deleted file mode 100644
index a23cd6d..0000000
--- a/src/com/android/launcher3/states/InternalStateHandler.java
+++ /dev/null
@@ -1,145 +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 static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
-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.model.BgDataModel.Callbacks;
-
-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);
-
- public void schedule(InternalStateHandler handler) {
- synchronized (this) {
- mPendingHandler = new WeakReference<>(handler);
- }
- MAIN_EXECUTOR.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 c6de9ca..40e267b 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -24,19 +24,23 @@
import android.graphics.Color;
import android.os.Bundle;
import android.os.Debug;
+import android.view.View;
+
+import androidx.annotation.Keep;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherState;
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 {
@@ -56,8 +60,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) {
@@ -114,15 +117,27 @@
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());
@@ -152,6 +167,13 @@
break;
}
+ case TestProtocol.REQUEST_VIEW_LEAK: {
+ if (mLeaks == null) mLeaks = new LinkedList();
+
+ mLeaks.add(new View(mContext));
+ break;
+ }
+
case TestProtocol.REQUEST_ICON_HEIGHT: {
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
mDeviceProfile.allAppsCellHeightPx);
@@ -162,8 +184,40 @@
}
protected boolean isLauncherInitialized() {
- final LauncherModel model = LauncherAppState.getInstance(mContext).getModel();
- return model.getCallback() == null || model.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);
+ createFinalizationObserver(fence);
+ try {
+ do {
+ Runtime.getRuntime().gc();
+ Runtime.getRuntime().runFinalization();
+ } while (!fence.await(100, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ // Create the observer in the scope of a method to minimize the chance that
+ // it remains live in a DEX/machine register at the point of the fence guard.
+ // This must be kept to avoid R8 inlining it.
+ @Keep
+ private static void createFinalizationObserver(CountDownLatch fence) {
+ new Object() {
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ fence.countDown();
+ } finally {
+ super.finalize();
+ }
+ }
+ };
}
}
-
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 07ddbdc..832f7f0 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;
@@ -73,11 +70,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";
@@ -86,5 +86,4 @@
public static final String NO_BACKGROUND_TO_OVERVIEW_TAG = "b/138251824";
public static final String NO_DRAG_TO_WORKSPACE = "b/138729456";
public static final String APP_NOT_DISABLED = "b/139891609";
- public static final String NO_CONTEXT_MENU = "b/141770616";
}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
similarity index 77%
rename from src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
rename to src/com/android/launcher3/touch/AllAppsSwipeController.java
index 23f21a1..31a5d79 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -1,4 +1,19 @@
-package com.android.launcher3.uioverrides;
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.touch;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
@@ -9,8 +24,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationComponents;
-import com.android.launcher3.touch.AbstractStateChangeTouchController;
-import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
/**
@@ -58,8 +71,8 @@
@Override
protected int getLogContainerTypeForNormalState(MotionEvent ev) {
- return mLauncher.getDragLayer().isEventOverView(mLauncher.getHotseat(), mTouchDownEvent) ?
- ContainerType.HOTSEAT : ContainerType.WORKSPACE;
+ return mLauncher.getDragLayer().isEventOverView(mLauncher.getHotseat(), mTouchDownEvent)
+ ? ContainerType.HOTSEAT : ContainerType.WORKSPACE;
}
@Override
diff --git a/src/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 86d2b39..aa02d0a 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -79,19 +79,10 @@
}
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/util/ActivityTracker.java b/src/com/android/launcher3/util/ActivityTracker.java
new file mode 100644
index 0000000..499f655
--- /dev/null
+++ b/src/com/android/launcher3/util/ActivityTracker.java
@@ -0,0 +1,152 @@
+/*
+ * 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 (mCurrentActivity.get() == activity) {
+ mCurrentActivity.clear();
+ }
+ }
+
+ /**
+ * Schedules the callback to be notified when the activity is created.
+ * @return true if the activity is already created, false otherwise
+ */
+ public boolean schedule(SchedulerCallback<? extends T> callback) {
+ synchronized (this) {
+ mPendingCallback = new WeakReference<>((SchedulerCallback<T>) callback);
+ }
+ if (!notifyInitIfPending()) {
+ // If the activity doesn't already exist, then post and wait for the activity to start
+ MAIN_EXECUTOR.execute(this);
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void run() {
+ notifyInitIfPending();
+ }
+
+ /**
+ * Notifies the pending callback if the activity is now created.
+ * @return true if the activity is now created.
+ */
+ private boolean notifyInitIfPending() {
+ T activity = mCurrentActivity.get();
+ if (activity != null) {
+ notifyInitIfPending(activity, activity.isStarted());
+ return true;
+ }
+ return false;
+ }
+
+ public boolean notifyInitIfPending(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) {
+ 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 = notifyInitIfPending(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/ContentWriter.java b/src/com/android/launcher3/util/ContentWriter.java
index 00adf10..2d64353 100644
--- a/src/com/android/launcher3/util/ContentWriter.java
+++ b/src/com/android/launcher3/util/ContentWriter.java
@@ -26,6 +26,7 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.GraphicsUtils;
/**
@@ -37,7 +38,7 @@
private final Context mContext;
private CommitParams mCommitParams;
- private Bitmap mIcon;
+ private BitmapInfo mIcon;
private UserHandle mUser;
public ContentWriter(Context context, CommitParams commitParams) {
@@ -79,7 +80,7 @@
return this;
}
- public ContentWriter putIcon(Bitmap value, UserHandle user) {
+ public ContentWriter putIcon(BitmapInfo value, UserHandle user) {
mIcon = value;
mUser = user;
return this;
@@ -97,7 +98,7 @@
Preconditions.assertNonUiThread();
if (mIcon != null && !LauncherAppState.getInstance(context).getIconCache()
.isDefaultIcon(mIcon, mUser)) {
- mValues.put(LauncherSettings.Favorites.ICON, GraphicsUtils.flattenBitmap(mIcon));
+ mValues.put(LauncherSettings.Favorites.ICON, GraphicsUtils.flattenBitmap(mIcon.icon));
mIcon = null;
}
return mValues;
diff --git a/src/com/android/launcher3/util/LooperIdleLock.java b/src/com/android/launcher3/util/LooperIdleLock.java
index 2896535..f4ccf42 100644
--- a/src/com/android/launcher3/util/LooperIdleLock.java
+++ b/src/com/android/launcher3/util/LooperIdleLock.java
@@ -22,29 +22,30 @@
/**
* Utility class to block execution until the UI looper is idle.
*/
-public class LooperIdleLock implements MessageQueue.IdleHandler, Runnable {
+public class LooperIdleLock implements MessageQueue.IdleHandler {
private final Object mLock;
private boolean mIsLocked;
+ private Looper mLooper;
public LooperIdleLock(Object lock, Looper looper) {
mLock = lock;
+ mLooper = looper;
mIsLocked = true;
looper.getQueue().addIdleHandler(this);
}
@Override
- public void run() {
- Looper.myQueue().addIdleHandler(this);
- }
-
- @Override
public boolean queueIdle() {
synchronized (mLock) {
mIsLocked = false;
mLock.notify();
}
+ // Manually remove from the list in case we're calling this outside of the idle callbacks
+ // (this is Ok in the normal flow as well because MessageQueue makes a copy of all handlers
+ // before calling back)
+ mLooper.getQueue().removeIdleHandler(this);
return false;
}
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
index fe9c2c4..520a9ed 100644
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -41,7 +41,8 @@
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 MAIN_EXECUTOR.submit(() -> get(context)).get();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ObjectWrapper.java b/src/com/android/launcher3/util/ObjectWrapper.java
similarity index 90%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/ObjectWrapper.java
rename to src/com/android/launcher3/util/ObjectWrapper.java
index abfe3ad..e5b4707 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;
@@ -25,7 +25,7 @@
*/
public class ObjectWrapper<T> extends Binder {
- private final T mObject;
+ private T mObject;
public ObjectWrapper(T object) {
mObject = object;
@@ -35,6 +35,10 @@
return mObject;
}
+ public void clear() {
+ mObject = null;
+ }
+
public static IBinder wrap(Object obj) {
return new ObjectWrapper<>(obj);
}
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index e97adb5..91f687e 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -24,6 +24,7 @@
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;
@@ -34,6 +35,7 @@
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;
@@ -46,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;
@@ -61,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);
}
/**
@@ -74,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;
}
@@ -84,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();
}
@@ -202,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);
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/com/android/launcher3/util/ShortcutUtil.java b/src/com/android/launcher3/util/ShortcutUtil.java
index af99713..a03b743 100644
--- a/src/com/android/launcher3/util/ShortcutUtil.java
+++ b/src/com/android/launcher3/util/ShortcutUtil.java
@@ -15,11 +15,20 @@
*/
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.LauncherIcons;
+import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.shortcuts.ShortcutKey;
public class ShortcutUtil {
@@ -61,10 +70,25 @@
&& 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) {
+ MODEL_EXECUTOR.execute(() -> {
+ try (LauncherIcons li = LauncherIcons.obtain(context)) {
+ info.bitmap = li.createShortcutIcon(si, badged, null);
+ 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 +100,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 f8d1632..ec87e79 100644
--- a/src/com/android/launcher3/util/UiThreadHelper.java
+++ b/src/com/android/launcher3/util/UiThreadHelper.java
@@ -52,15 +52,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);
}
@@ -74,7 +81,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;
@@ -82,7 +89,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/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 49d94f0..0c9a28b 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -415,7 +415,7 @@
int width = isFolderIcon ? originalView.getWidth() : (int) pos.width();
int height = isFolderIcon ? originalView.getHeight() : (int) pos.height();
if (supportsAdaptiveIcons) {
- drawable = getFullDrawable(l, info, width, height, false, sTmpObjArray);
+ drawable = getFullDrawable(l, info, width, height, sTmpObjArray);
if (drawable instanceof AdaptiveIconDrawable) {
badge = getBadge(l, info, sTmpObjArray[0]);
} else {
@@ -428,7 +428,7 @@
// Similar to DragView, we simply use the BubbleTextView icon here.
drawable = btvIcon;
} else {
- drawable = getFullDrawable(l, info, width, height, false, sTmpObjArray);
+ drawable = getFullDrawable(l, info, width, height, sTmpObjArray);
}
}
}
@@ -581,6 +581,7 @@
setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge,
mIconLoadResult.iconOffset);
+
setVisibility(VISIBLE);
hideOriginalView(originalView);
};
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 9f59d78..e64b2fb 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -77,7 +77,7 @@
/**
* Simple scrim which draws a flat color
*/
-public class ScrimView extends View implements Insettable, OnChangeListener,
+public class ScrimView<T extends Launcher> extends View implements Insettable, OnChangeListener,
AccessibilityStateChangeListener, StateListener {
public static final Property<ScrimView, Integer> DRAG_HANDLE_ALPHA =
@@ -101,7 +101,7 @@
private final Rect mTempRect = new Rect();
private final int[] mTempPos = new int[2];
- protected final Launcher mLauncher;
+ protected final T mLauncher;
private final WallpaperColorInfo mWallpaperColorInfo;
private final AccessibilityManager mAM;
protected final int mEndScrim;
@@ -130,7 +130,7 @@
public ScrimView(Context context, AttributeSet attrs) {
super(context, attrs);
- mLauncher = Launcher.getLauncher(context);
+ mLauncher = Launcher.cast(Launcher.getLauncher(context));
mWallpaperColorInfo = WallpaperColorInfo.getInstance(context);
mEndScrim = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
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/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index 50db40f..6038873 100644
--- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -16,6 +16,9 @@
package com.android.launcher3.widget;
+import static com.android.launcher3.FastBitmapDrawable.newIcon;
+import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
+
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -33,12 +36,11 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.FastBitmapDrawable;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.R;
-import com.android.launcher3.graphics.DrawableFactory;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.Themes;
@@ -128,24 +130,22 @@
mCenterDrawable.setCallback(null);
mCenterDrawable = null;
}
- if (info.iconBitmap != null) {
+ if (info.bitmap.icon != null) {
// The view displays three modes,
// 1) App icon in the center
// 2) Preload icon in the center
// 3) Setup icon in the center and app icon in the top right corner.
- DrawableFactory drawableFactory = DrawableFactory.INSTANCE.get(getContext());
if (mDisabledForSafeMode) {
- FastBitmapDrawable disabledIcon = drawableFactory.newIcon(getContext(), info);
+ FastBitmapDrawable disabledIcon = newIcon(getContext(), info);
disabledIcon.setIsDisabled(true);
mCenterDrawable = disabledIcon;
mSettingIconDrawable = null;
} else if (isReadyForClickSetup()) {
- mCenterDrawable = drawableFactory.newIcon(getContext(), info);
+ mCenterDrawable = newIcon(getContext(), info);
mSettingIconDrawable = getResources().getDrawable(R.drawable.ic_setting).mutate();
- updateSettingColor(info.iconColor);
+ updateSettingColor(info.bitmap.color);
} else {
- mCenterDrawable = DrawableFactory.INSTANCE.get(getContext())
- .newPendingIcon(getContext(), info);
+ mCenterDrawable = newPendingIcon(getContext(), info);
mSettingIconDrawable = null;
applyState();
}
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 6944879..f713b33 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -36,7 +36,6 @@
import com.android.launcher3.SimpleOnStylusPressListener;
import com.android.launcher3.StylusEventHelper;
import com.android.launcher3.WidgetPreviewLoader;
-import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.model.WidgetItem;
@@ -182,10 +181,8 @@
return;
}
if (bitmap != null) {
- mWidgetImage.setBitmap(bitmap,
- DrawableFactory.INSTANCE.get(getContext()).getBadgeForUser(mItem.user,
- getContext(), BaseIconFactory.getBadgeSizeForIconSize(
- mDeviceProfile.allAppsIconSizePx)));
+ mWidgetImage.setBitmap(bitmap, mWidgetPreviewLoader.getBadgeForUser(mItem.user,
+ BaseIconFactory.getBadgeSizeForIconSize(mDeviceProfile.allAppsIconSizePx)));
if (mAnimatePreview) {
mWidgetImage.setAlpha(0f);
ViewPropertyAnimator anim = mWidgetImage.animate();
diff --git a/src/com/android/launcher3/widget/WidgetsDiffReporter.java b/src/com/android/launcher3/widget/WidgetsDiffReporter.java
index 435125b..f3b325d 100644
--- a/src/com/android/launcher3/widget/WidgetsDiffReporter.java
+++ b/src/com/android/launcher3/widget/WidgetsDiffReporter.java
@@ -18,6 +18,8 @@
import android.util.Log;
+import androidx.recyclerview.widget.RecyclerView;
+
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.widget.WidgetsListAdapter.WidgetListRowEntryComparator;
@@ -25,8 +27,6 @@
import java.util.ArrayList;
import java.util.Iterator;
-import androidx.recyclerview.widget.RecyclerView;
-
/**
* Do diff on widget's tray list items and call the {@link RecyclerView.Adapter}
* methods accordingly.
@@ -137,7 +137,7 @@
}
private boolean isSamePackageItemInfo(PackageItemInfo curInfo, PackageItemInfo newInfo) {
- return curInfo.iconBitmap.equals(newInfo.iconBitmap) &&
- !mIconCache.isDefaultIcon(curInfo.iconBitmap, curInfo.user);
+ return curInfo.bitmap.icon.equals(newInfo.bitmap.icon)
+ && !mIconCache.isDefaultIcon(curInfo.bitmap, curInfo.user);
}
}
diff --git a/src/com/android/launcher3/widget/custom/CustomWidgetManager.java b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
index f20c83d..b3569f2 100644
--- a/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
+++ b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
@@ -38,7 +38,6 @@
import com.android.systemui.plugins.CustomWidgetPlugin;
import com.android.systemui.plugins.PluginListener;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
@@ -57,14 +56,12 @@
*/
private int mAutoProviderId = 0;
private final SparseArray<CustomWidgetPlugin> mPlugins;
- private final SparseArray<WeakReference<Context>> mContexts;
private final List<CustomAppWidgetProviderInfo> mCustomWidgets;
private final SparseArray<ComponentName> mWidgetsIdMap;
private Consumer<PackageUserKey> mWidgetRefreshCallback;
private CustomWidgetManager(Context context) {
mPlugins = new SparseArray<>();
- mContexts = new SparseArray<>();
mCustomWidgets = new ArrayList<>();
mWidgetsIdMap = new SparseArray<>();
PluginManagerWrapper.INSTANCE.get(context)
@@ -74,7 +71,6 @@
@Override
public void onPluginConnected(CustomWidgetPlugin plugin, Context context) {
mPlugins.put(mAutoProviderId, plugin);
- mContexts.put(mAutoProviderId, new WeakReference<>(context));
List<AppWidgetProviderInfo> providers = AppWidgetManager.getInstance(context)
.getInstalledProvidersForProfile(Process.myUserHandle());
if (providers.isEmpty()) return;
@@ -94,7 +90,6 @@
int providerId = findProviderId(plugin);
if (providerId == -1) return;
mPlugins.remove(providerId);
- mContexts.remove(providerId);
mCustomWidgets.remove(getWidgetProvider(providerId));
mWidgetsIdMap.remove(providerId);
}
@@ -112,9 +107,8 @@
public void onViewCreated(LauncherAppWidgetHostView view) {
CustomAppWidgetProviderInfo info = (CustomAppWidgetProviderInfo) view.getAppWidgetInfo();
CustomWidgetPlugin plugin = mPlugins.get(info.providerId);
- WeakReference<Context> context = mContexts.get(info.providerId);
if (plugin == null) return;
- plugin.onViewCreated(context == null ? null : context.get(), view);
+ plugin.onViewCreated(view);
}
/**
@@ -156,13 +150,13 @@
info.provider = new ComponentName(
context.getPackageName(), CLS_CUSTOM_WIDGET_PREFIX + providerId);
- info.label = plugin.getLabel(context);
- info.resizeMode = plugin.getResizeMode(context);
+ info.label = plugin.getLabel();
+ info.resizeMode = plugin.getResizeMode();
- info.spanX = plugin.getSpanX(context);
- info.spanY = plugin.getSpanY(context);
- info.minSpanX = plugin.getMinSpanX(context);
- info.minSpanY = plugin.getMinSpanY(context);
+ info.spanX = plugin.getSpanX();
+ info.spanY = plugin.getSpanY();
+ info.minSpanX = plugin.getMinSpanX();
+ info.minSpanY = plugin.getMinSpanY();
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_flags/com/android/launcher3/config/FeatureFlags.java b/src_flags/com/android/launcher3/config/FeatureFlags.java
deleted file mode 100644
index 73c6996..0000000
--- a/src_flags/com/android/launcher3/config/FeatureFlags.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * 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
- }
-}
diff --git a/src_plugins/com/android/systemui/plugins/CustomWidgetPlugin.java b/src_plugins/com/android/systemui/plugins/CustomWidgetPlugin.java
index 47aa94b..56ebcc5 100644
--- a/src_plugins/com/android/systemui/plugins/CustomWidgetPlugin.java
+++ b/src_plugins/com/android/systemui/plugins/CustomWidgetPlugin.java
@@ -17,7 +17,6 @@
package com.android.systemui.plugins;
import android.appwidget.AppWidgetHostView;
-import android.content.Context;
import com.android.systemui.plugins.annotations.ProvidesInterface;
@@ -33,40 +32,40 @@
/**
* The label to display to the user in the AppWidget picker.
*/
- String getLabel(Context context);
+ 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(Context context);
+ 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(Context context);
+ 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(Context context);
+ 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(Context context);
+ int getMinSpanY();
/**
* The rules by which a widget can be resized.
*/
- int getResizeMode(Context context);
+ int getResizeMode();
/**
* Notify the plugin that container of the widget has been rendered, where the custom widget
* can be attached to.
*/
- void onViewCreated(Context context, AppWidgetHostView parent);
+ void onViewCreated(AppWidgetHostView 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/OverscrollPlugin.java b/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java
new file mode 100644
index 0000000..60eb304
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.plugins;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Implement this interface to receive a callback when the user swipes right
+ * to left on the gesture area. It won't fire if the user has quick switched to a previous app
+ * (swiped right) and the current app isn't yet the active one (i.e., if swiping left would take
+ * the user to a more recent app).
+ */
+@ProvidesInterface(action = com.android.systemui.plugins.OverscrollPlugin.ACTION,
+ version = com.android.systemui.plugins.OverlayPlugin.VERSION)
+public interface OverscrollPlugin extends Plugin {
+
+ String ACTION = "com.android.systemui.action.PLUGIN_LAUNCHER_OVERSCROLL";
+ int VERSION = 1;
+
+ String DEVICE_STATE_LOCKED = "Locked";
+ String DEVICE_STATE_LAUNCHER = "Launcher";
+ String DEVICE_STATE_APP = "App";
+ String DEVICE_STATE_UNKNOWN = "Unknown";
+
+ /**
+ * Called when the user completed a right to left swipe in the gesture area.
+ *
+ * @param deviceState One of the DEVICE_STATE_* constants.
+ */
+ void onOverscroll(String deviceState);
+}
diff --git a/src_plugins/com/android/systemui/plugins/RecentsExtraCard.java b/src_plugins/com/android/systemui/plugins/RecentsExtraCard.java
index 0ebea3d..cd9f33d 100644
--- a/src_plugins/com/android/systemui/plugins/RecentsExtraCard.java
+++ b/src_plugins/com/android/systemui/plugins/RecentsExtraCard.java
@@ -34,9 +34,9 @@
/**
* Sets up the recents overview extra card and fills in data.
*
- * @param context Plugin context
+ * @param context Plugin context
* @param frameLayout PlaceholderView
- * @param activity Recents activity to hold extra view
+ * @param activity Recents activity to hold extra view
*/
void setupView(Context context, FrameLayout frameLayout, Activity activity);
}
diff --git a/src/com/android/launcher3/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/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_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
new file mode 100644
index 0000000..5407ea3
--- /dev/null
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides;
+
+import android.app.Activity;
+import android.app.Person;
+import android.content.pm.ShortcutInfo;
+
+import com.android.launcher3.Utilities;
+
+import java.io.PrintWriter;
+
+public class ApiWrapper {
+
+ public static boolean dumpActivity(Activity activity, PrintWriter writer) {
+ return false;
+ }
+
+ public static Person[] getPersons(ShortcutInfo si) {
+ return Utilities.EMPTY_PERSON_ARRAY;
+ }
+}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/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
deleted file mode 100644
index 467ae02..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.uioverrides;
-
-import android.app.Activity;
-import android.app.Person;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.content.pm.ShortcutInfo;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState.ScaleAndTranslation;
-import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.graphics.RotationMode;
-import com.android.launcher3.util.TouchController;
-
-import java.io.PrintWriter;
-
-public class UiFactory {
-
- public static TouchController[] createTouchControllers(Launcher launcher) {
- return new TouchController[] {
- launcher.getDragController(), new AllAppsSwipeController(launcher)};
- }
-
- public static Runnable enableLiveUIChanges(Launcher l) {
- return null;
- }
-
- public static StateHandler[] getStateHandler(Launcher launcher) {
- return new StateHandler[] {
- launcher.getAllAppsController(), launcher.getWorkspace() };
- }
-
- public static void resetOverview(Launcher launcher) { }
-
- public static void onLauncherStateOrFocusChanged(Launcher launcher) { }
-
- public static void onCreate(Launcher launcher) { }
-
- public static void onStart(Launcher launcher) { }
-
- public static void onEnterAnimationComplete(Context context) {}
-
- public static void onLauncherStateOrResumeChanged(Launcher launcher) { }
-
- public static void onTrimMemory(Launcher launcher, int level) { }
-
- public static void useFadeOutAnimationForLauncherStart(Launcher launcher,
- CancellationSignal cancellationSignal) { }
-
- public static boolean dumpActivity(Activity activity, PrintWriter writer) {
- return false;
- }
-
- public static void setBackButtonAlpha(Launcher launcher, float alpha, boolean animate) { }
-
-
- public static ScaleAndTranslation getOverviewScaleAndTranslationForNormalState(Launcher l) {
- return new ScaleAndTranslation(1.1f, 0f, 0f);
- }
-
- public static RotationMode getRotationMode(DeviceProfile dp) {
- return RotationMode.NORMAL;
- }
-
- public static boolean startIntentSenderForResult(Activity activity, IntentSender intent,
- int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
- Bundle options) {
- return false;
- }
-
- public static boolean startActivityForResult(Activity activity, Intent intent, int requestCode,
- Bundle options) {
- return false;
- }
-
- public static void resetPendingActivityResults(Launcher launcher, int requestCode) { }
-
- public static void clearSwipeSharedState(boolean finishAnimation) {}
-
- public static Person[] getPersons(ShortcutInfo si) {
- return Utilities.EMPTY_PERSON_ARRAY;
- }
-}
diff --git a/tests/Android.mk b/tests/Android.mk
index 02ead4e..83fdddc 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -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)
@@ -50,7 +50,8 @@
androidx.test.runner \
androidx.test.rules \
androidx.test.uiautomator_uiautomator \
- mockito-target-minus-junit4
+ mockito-target-minus-junit4 \
+ launcher-log-protos-lite
ifneq (,$(wildcard frameworks/base))
LOCAL_PRIVATE_PLATFORM_APIS := true
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index ffa90b9..56eca6d 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -79,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/AndroidManifest.xml b/tests/AndroidManifest.xml
index 24b5b02..5cf96c8 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -18,7 +18,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.android.launcher3.tests">
- <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="25"
+ <uses-sdk android:targetSdkVersion="29" android:minSdkVersion="25"
tools:overrideLibrary="android.support.test.uiautomator.v18"/>
<application android:debuggable="true">
diff --git a/tests/dummy_app/AndroidManifest.xml b/tests/dummy_app/AndroidManifest.xml
index 9d0a74a..f00138c 100644
--- a/tests/dummy_app/AndroidManifest.xml
+++ b/tests/dummy_app/AndroidManifest.xml
@@ -21,7 +21,7 @@
to come from a domain that you own or have control over. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.aardwolf">
- <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="21"/>
+ <uses-sdk android:targetSdkVersion="29" android:minSdkVersion="21"/>
<application android:label="Aardwolf">
<activity
android:name="Activity1"
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 64df8e0..0dcfaa8 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.compat.LauncherAppsCompat;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.util.PackageManagerHelper;
-
-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());
@@ -139,7 +142,7 @@
when(mMockIconCache.getDefaultIcon(eq(mLoaderCursor.user)))
.thenReturn(BitmapInfo.fromBitmap(icon));
WorkspaceItemInfo info = mLoaderCursor.loadSimpleWorkspaceItem();
- assertEquals(icon, info.iconBitmap);
+ assertEquals(icon, info.bitmap.icon);
assertEquals("my-shortcut", info.title);
assertEquals(ITEM_TYPE_SHORTCUT, info.itemType);
}
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/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 4246096..eb6c3ed 100644
--- a/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java
+++ b/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java
@@ -15,126 +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(), Long.parseLong(arg)));
- 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/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 9a894b1..4243ed0 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.ui;
+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;
@@ -23,14 +26,15 @@
import static java.lang.System.exit;
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
-
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;
@@ -42,19 +46,19 @@
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.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;
@@ -119,10 +123,6 @@
protected final LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
@Rule
- public ShellCommandRule mDefaultLauncherRule =
- TestHelpers.isInLauncherProcess() ? ShellCommandRule.setDefaultLauncher() : null;
-
- @Rule
public ShellCommandRule mDisableHeadsUpNotification =
ShellCommandRule.disableHeadsUpNotification();
@@ -150,9 +150,13 @@
}
protected TestRule getRulesInsideActivityMonitor() {
- return RuleChain.
- outerRule(new PortraitLandscapeRunner(this)).
- around(new FailureWatcher(mDevice));
+ final RuleChain inner = RuleChain.outerRule(new PortraitLandscapeRunner(this))
+ .around(new FailureWatcher(mDevice));
+
+ return TestHelpers.isInLauncherProcess()
+ ? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher())
+ .around(inner) :
+ inner;
}
@Rule
@@ -178,7 +182,7 @@
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 "
@@ -217,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();
}
/**
@@ -278,7 +304,7 @@
protected void waitForLauncherCondition(
String message, Function<Launcher, Boolean> condition, long timeout) {
if (!TestHelpers.isInLauncherProcess()) return;
- Wait.atMost(message, () -> getFromLauncher(condition), timeout);
+ Wait.atMost(message, () -> getFromLauncher(condition), timeout, mLauncher);
}
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
@@ -291,7 +317,7 @@
final Object fromLauncher = getFromLauncher(f);
output[0] = fromLauncher;
return fromLauncher != null;
- }, timeout);
+ }, timeout, mLauncher);
return (T) output[0];
}
@@ -305,13 +331,12 @@
Wait.atMost(message, () -> {
testThreadAction.run();
return getFromLauncher(condition);
- }, timeout);
+ }, timeout, mLauncher);
}
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);
}
/**
@@ -344,7 +369,7 @@
}
}
- protected void startAppFast(String packageName) {
+ public static void startAppFast(String packageName) {
startIntent(
getInstrumentation().getContext().getPackageManager().getLaunchIntentForPackage(
packageName),
@@ -352,7 +377,7 @@
true /* newTask */);
}
- protected void startTestActivity(int activityNumber) {
+ public static void startTestActivity(int activityNumber) {
final String packageName = getAppPackageName();
final Intent intent = getInstrumentation().getContext().getPackageManager().
getLaunchIntentForPackage(packageName);
@@ -362,7 +387,7 @@
false /* newTask */);
}
- private void startIntent(Intent intent, BySelector selector, boolean newTask) {
+ private static void startIntent(Intent intent, BySelector selector, boolean newTask) {
intent.addCategory(Intent.CATEGORY_LAUNCHER);
if (newTask) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
@@ -371,14 +396,20 @@
}
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 ff21880..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;
}
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 2cf6c2b..d7096b0 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -24,8 +24,6 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import android.util.Log;
-
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -38,7 +36,6 @@
import com.android.launcher3.tapl.AppIconMenuItem;
import com.android.launcher3.tapl.Widgets;
import com.android.launcher3.tapl.Workspace;
-import com.android.launcher3.util.rule.TestStabilityRule.Stability;
import com.android.launcher3.views.OptionsPopupView;
import com.android.launcher3.widget.WidgetsFullSheet;
import com.android.launcher3.widget.WidgetsRecyclerView;
@@ -345,10 +342,4 @@
public static String getAppPackageName() {
return getInstrumentation().getContext().getPackageName();
}
-
- @Test
- @Stability
- public void testTestStabilityAttribute() {
- Log.d("TestStabilityRule", "Hello world!");
- }
}
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index e1b3ede..0472ce1 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -103,12 +103,12 @@
setResult(acceptConfig);
if (acceptConfig) {
- Wait.atMost(null, new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT);
+ Wait.atMost(null, new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
} else {
// Verify that the widget id is deleted.
Wait.atMost(null, () -> mAppWidgetManager.getAppWidgetInfo(mWidgetId) == null,
- DEFAULT_ACTIVITY_TIMEOUT);
+ DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
}
}
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index f42bf1f..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,34 +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();
- }
-
private void verifyWidgetPresent(LauncherAppWidgetProviderInfo info) {
assertTrue("Widget is not present",
mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
@@ -288,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;
@@ -307,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");
}
@@ -330,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/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 07129dd..d909ad7 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -59,7 +59,8 @@
@RunWith(AndroidJUnit4.class)
public class RequestPinItemTest extends AbstractLauncherUiTest {
- @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
+ @Rule
+ public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
private String mCallbackAction;
private String mShortcutId;
@@ -84,10 +85,10 @@
.equals(AppWidgetNoConfig.class.getName()));
}
- @Test
+ @Test
public void testPinWidgetNoConfig_customPreview() throws Throwable {
// Command to set custom preview
- Intent command = RequestPinItemActivity.getCommandIntent(
+ Intent command = RequestPinItemActivity.getCommandIntent(
RequestPinItemActivity.class, "setRemoteViewColor").putExtra(
RequestPinItemActivity.EXTRA_PARAM + "0", Color.RED);
@@ -169,7 +170,8 @@
// Go back to home
mLauncher.pressHome();
- Wait.atMost(null, new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT);
+ Wait.atMost(null, new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT,
+ mLauncher);
}
/**
diff --git a/tests/src/com/android/launcher3/util/RaceConditionReproducer.java b/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
index 8f89173..ed2ec7b 100644
--- a/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
+++ b/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
@@ -17,8 +17,6 @@
package com.android.launcher3.util;
import static com.android.launcher3.util.Executors.createAndStartNewLooper;
-import static com.android.launcher3.util.RaceConditionTracker.ENTER_POSTFIX;
-import static com.android.launcher3.util.RaceConditionTracker.EXIT_POSTFIX;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -64,15 +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() {
- return new Handler(createAndStartNewLooper("RaceConditionEventResumer"));
+ 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);
}
/**
@@ -209,7 +222,8 @@
parseReproString(mReproString) : generateSequenceToFollowLocked();
Log.e(TAG, "---- Start of iteration; state:\n" + dumpStateLocked());
checkIfCompletedSequenceToFollowLocked();
- RaceConditionTracker.setEventProcessor(this);
+
+ TraceHelperForTest.setRaceConditionReproducer(this);
}
/**
@@ -218,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);
@@ -243,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/Wait.java b/tests/src/com/android/launcher3/util/Wait.java
index 899686b..2663d02 100644
--- a/tests/src/com/android/launcher3/util/Wait.java
+++ b/tests/src/com/android/launcher3/util/Wait.java
@@ -3,6 +3,8 @@
import android.os.SystemClock;
import android.util.Log;
+import com.android.launcher3.tapl.LauncherInstrumentation;
+
import org.junit.Assert;
/**
@@ -12,11 +14,13 @@
private static final long DEFAULT_SLEEP_MS = 200;
- public static void atMost(String message, Condition condition, long timeout) {
- atMost(message, condition, timeout, DEFAULT_SLEEP_MS);
+ public static void atMost(String message, Condition condition, long timeout,
+ LauncherInstrumentation launcher) {
+ atMost(message, condition, timeout, DEFAULT_SLEEP_MS, launcher);
}
- public static void atMost(String message, Condition condition, long timeout, long sleepMillis) {
+ public static void atMost(String message, Condition condition, long timeout, long sleepMillis,
+ LauncherInstrumentation launcher) {
final long startTime = SystemClock.uptimeMillis();
long endTime = startTime + timeout;
Log.d("Wait", "atMost: " + startTime + " - " + endTime);
@@ -40,6 +44,7 @@
throw new RuntimeException(t);
}
Log.d("Wait", "atMost: timed out: " + SystemClock.uptimeMillis());
+ launcher.checkForAnomaly();
Assert.fail(message);
}
}
diff --git a/tests/src/com/android/launcher3/util/rule/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
index d7f41bf..858cb38 100644
--- a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
@@ -17,9 +17,11 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
import androidx.test.uiautomator.UiDevice;
import org.junit.rules.TestRule;
@@ -50,20 +52,33 @@
+ "(?<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;
+
+ public 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) {
- if (description.getAnnotation(Stability.class) != null) {
+ final Stability stability = description.getAnnotation(Stability.class);
+ if (stability != null) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
- getRunFlavor();
-
- base.evaluate();
+ if ((stability.flavors() & RUN_FLAFOR) != 0) {
+ Log.d(TAG, "Running " + description.getDisplayName());
+ base.evaluate();
+ } else {
+ Log.d(TAG, "Skipping " + description.getDisplayName());
+ }
}
};
} else {
@@ -71,49 +86,76 @@
}
}
- private static void getRunFlavor() throws Exception {
- final String launcherVersion = getInstrumentation().
- getContext().
- getPackageManager().
- getPackageInfo(
- UiDevice.getInstance(getInstrumentation()).
- getLauncherPackageName(),
- 0).
- versionName;
+ private static int getRunFlavor() {
+ final String flavorOverride = InstrumentationRegistry.getArguments().getString("flavor");
+
+ if (flavorOverride != null) {
+ Log.d(TAG, "Flavor override: " + flavorOverride);
+ try {
+ return (int) TestStabilityRule.class.getField(flavorOverride).get(null);
+ } catch (NoSuchFieldException e) {
+ throw new AssertionError("Unrecognized run flavor override: " + flavorOverride);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ final String launcherVersion;
+ try {
+ launcherVersion = getInstrumentation().
+ 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()) {
- Log.e(TAG, "Match not found");
+ throw new AssertionError("Launcher build match not found");
}
final String platformVersion = Build.VERSION.INCREMENTAL;
final Matcher platformBuildMatcher = PLATFORM_BUILD.matcher(platformVersion);
if (!platformBuildMatcher.find()) {
- Log.e(TAG, "Match not found");
+ 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 {
- Log.e(TAG, "ERROR3");
+ throw new AssertionError("Unrecognized run flavor");
}
+
+ return runFlavor;
}
}
diff --git a/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java b/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
index a31d8a6..c7f7cd6 100644
--- a/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
+++ b/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
@@ -23,16 +23,19 @@
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.graphics.Bitmap;
+import android.view.LayoutInflater;
+
+import androidx.recyclerview.widget.RecyclerView;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import android.view.LayoutInflater;
-import com.android.launcher3.icons.IconCache;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.WidgetPreviewLoader;
import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.util.MultiHashMap;
@@ -46,8 +49,6 @@
import java.util.ArrayList;
import java.util.Map;
-import androidx.recyclerview.widget.RecyclerView;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class WidgetsListAdapterTest {
@@ -136,7 +137,7 @@
PackageItemInfo pInfo = new PackageItemInfo(wi.componentName.getPackageName());
pInfo.title = pInfo.packageName;
pInfo.user = wi.user;
- pInfo.iconBitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8);
+ pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
newMap.addToList(pInfo, wi);
if (newMap.size() == num) {
break;
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 96e4b8c..3bdeb14 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -29,6 +29,8 @@
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.testing.TestProtocol;
+import java.util.stream.Collectors;
+
/**
* Operations on AllApps opened from Home. Also a parent for All Apps opened from Overview.
*/
@@ -48,9 +50,8 @@
mLauncher.waitForObjectInContainer(appListRecycler, By.clazz(TextView.class));
verifyNotFrozen("All apps freeze flags upon opening all apps");
mIconHeight = mLauncher.getTestInfo(
- TestProtocol.REQUEST_ICON_HEIGHT)
- .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
-
+ TestProtocol.REQUEST_ICON_HEIGHT).
+ getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
@Override
@@ -58,8 +59,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");
@@ -75,6 +76,10 @@
LauncherInstrumentation.log("hasClickableIcon: icon center is under search box");
return false;
}
+ if (iconBounds.bottom > displayBottom) {
+ LauncherInstrumentation.log("hasClickableIcon: icon bottom below bottom offset");
+ return false;
+ }
LauncherInstrumentation.log("hasClickableIcon: icon is clickable");
return true;
}
@@ -100,25 +105,31 @@
final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer,
"apps_list_view");
final UiObject2 searchBox = getSearchBox(allAppsContainer);
- allAppsContainer.setGestureMargins(
- 0,
- getSearchBox(allAppsContainer).getVisibleBounds().bottom + 1,
- 0,
- ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
- mLauncher.getResources()) + 1);
+
+ int bottomGestureMargin = ResourceUtils.getNavbarSize(
+ ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1;
+ int deviceHeight = mLauncher.getDevice().getDisplayHeight();
+ int displayBottom = deviceHeight - 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)) {
+ while (!hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector,
+ displayBottom)) {
mLauncher.scrollToLastVisibleRow(
allAppsContainer,
- mLauncher.getObjectsInContainer(allAppsContainer, "icon"),
- searchBox.getVisibleBounds().bottom -
- allAppsContainer.getVisibleBounds().top);
- final int newScroll = getScroll(allAppsContainer);
+ mLauncher.getObjectsInContainer(allAppsContainer, "icon")
+ .stream()
+ .filter(icon ->
+ icon.getVisibleBounds().bottom
+ <= displayBottom)
+ .collect(Collectors.toList()),
+ searchBox.getVisibleBounds().bottom
+ - allAppsContainer.getVisibleBounds().top);
+ final int newScroll = getAllAppsScroll();
if (newScroll == scroll) break;
mLauncher.assertTrue(
@@ -131,8 +142,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);
@@ -150,16 +164,17 @@
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, margins, 50);
+ mLauncher.scroll(
+ allAppsContainer, Direction.UP, margins, 12, false);
}
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled up")) {
@@ -168,9 +183,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) {
@@ -186,7 +202,7 @@
final UiObject2 allAppsContainer = verifyActiveContainer();
// Start the gesture in the center to avoid starting at elements near the top.
mLauncher.scroll(
- allAppsContainer, Direction.DOWN, new Rect(0, 0, 0, mHeight / 2), 10);
+ allAppsContainer, Direction.DOWN, new Rect(0, 0, 0, mHeight / 2), 10, false);
verifyActiveContainer();
}
}
@@ -200,7 +216,7 @@
final UiObject2 allAppsContainer = verifyActiveContainer();
// Start the gesture in the center, for symmetry with forward.
mLauncher.scroll(
- allAppsContainer, Direction.UP, new Rect(0, mHeight / 2, 0, 0), 10);
+ allAppsContainer, Direction.UP, new Rect(0, mHeight / 2, 0, 0), 10, false);
verifyActiveContainer();
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index db3d846..6583d32 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -105,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, false);
+ new BaseOverview(mLauncher);
+ } else {
+ mLauncher.swipeToState(startX, startY, endX, endY, 10, expectedState);
+ }
break;
}
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 49c0c89..339e14f 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -55,7 +55,8 @@
final int leftMargin = mLauncher.getTestInfo(
TestProtocol.REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN).
getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
- mLauncher.scroll(overview, Direction.LEFT, new Rect(leftMargin, 0, 0, 0), 20);
+ mLauncher.scroll(
+ overview, Direction.LEFT, new Rect(leftMargin + 1, 0, 0, 0), 20, false);
verifyActiveContainer();
}
}
@@ -63,10 +64,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 +76,6 @@
}
mLauncher.waitForObjectInContainer(verifyActiveContainer(), clearAllSelector).click();
- try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
- "dismissed all tasks")) {
- return new Workspace(mLauncher);
- }
}
}
@@ -93,7 +90,8 @@
final int rightMargin = mLauncher.getTestInfo(
TestProtocol.REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN).
getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
- mLauncher.scroll(overview, Direction.RIGHT, new Rect(0, 0, rightMargin, 0), 20);
+ mLauncher.scroll(
+ overview, Direction.RIGHT, new Rect(0, 0, rightMargin + 1, 0), 20, false);
verifyActiveContainer();
}
}
@@ -109,7 +107,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/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index c52650d..ad67f1b 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -69,6 +69,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
@@ -297,6 +298,14 @@
return null;
}
+ public void checkForAnomaly() {
+ final String anomalyMessage = getAnomalyMessage();
+ if (anomalyMessage != null) {
+ failWithSystemHealth(
+ "Tests are broken by a non-Launcher system error: " + anomalyMessage);
+ }
+ }
+
private String getVisibleStateMessage() {
if (hasLauncherObject(WIDGETS_RES_ID)) return "Widgets";
if (hasLauncherObject(OVERVIEW_RES_ID)) return "Overview";
@@ -330,20 +339,17 @@
}
private void fail(String message) {
- message = "http://go/tapl : " + getContextDescription() + message;
+ checkForAnomaly();
- final String anomaly = getAnomalyMessage();
- if (anomaly != null) {
- message = anomaly + ", which causes:\n" + message;
- } else {
- message = message + " (visible state: " + getVisibleStateMessage() + ")";
- }
+ failWithSystemHealth("http://go/tapl : " + getContextDescription() + message +
+ " (visible state: " + getVisibleStateMessage() + ")");
+ }
+ private void failWithSystemHealth(String message) {
final String systemHealth = getSystemHealthMessage();
if (systemHealth != null) {
message = message
- + ", which might be a consequence of system health "
- + "problems:\n<<<<<<<<<<<<<<<<<<\n"
+ + ", perhaps because of system health problems:\n<<<<<<<<<<<<<<<<<<\n"
+ systemHealth + "\n>>>>>>>>>>>>>>>>>>";
}
@@ -423,11 +429,7 @@
// b/136278866
for (int i = 0; i != 100; ++i) {
if (getNavigationModeMismatchError() == null) break;
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
+ sleep(100);
}
final String error = getNavigationModeMismatchError();
@@ -495,7 +497,7 @@
}
}
- private void waitForLauncherInitialized() {
+ public void waitForLauncherInitialized() {
for (int i = 0; i < 100; ++i) {
if (getTestInfo(
TestProtocol.REQUEST_IS_LAUNCHER_INITIALIZED).
@@ -521,15 +523,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.
*
@@ -543,8 +536,7 @@
// 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);
+ checkForAnomaly();
final Point displaySize = getRealDisplaySize();
@@ -552,7 +544,8 @@
linearGesture(
displaySize.x / 2, displaySize.y - 1,
displaySize.x / 2, 0,
- ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME);
+ ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME,
+ false);
try (LauncherInstrumentation.Closable c = addContextLayer(
"Swiped up from context menu to home")) {
waitUntilGone("deep_shortcuts_container");
@@ -704,8 +697,8 @@
final UiObject2 object = container.wait(
Until.findObject(getLauncherObjectSelector(resName)),
WAIT_TIME_MS);
- assertNotNull("Can't find a launcher object id: " + resName + " in container: " +
- container.getResourceName(), object);
+ assertNotNull("Can't find a view in Launcher, id: " + resName + " in container: "
+ + container.getResourceName(), object);
return object;
}
@@ -714,8 +707,8 @@
final UiObject2 object = container.wait(
Until.findObject(selector),
WAIT_TIME_MS);
- assertNotNull("Can't find a launcher object id: " + selector + " in container: " +
- container.getResourceName(), object);
+ assertNotNull("Can't find a view in Launcher, id: " + selector + " in container: "
+ + container.getResourceName(), object);
return object;
}
@@ -741,12 +734,12 @@
@NonNull
UiObject2 waitForFallbackLauncherObject(String resName) {
- return waitForObjectBySelector(getFallbackLauncherObjectSelector(resName));
+ return waitForObjectBySelector(getOverviewObjectSelector(resName));
}
private UiObject2 waitForObjectBySelector(BySelector selector) {
final UiObject2 object = mDevice.wait(Until.findObject(selector), WAIT_TIME_MS);
- assertNotNull("Can't find a launcher object; selector: " + selector, object);
+ assertNotNull("Can't find a view in Launcher, selector: " + selector, object);
return object;
}
@@ -758,7 +751,7 @@
return By.res(getLauncherPackageName(), resName);
}
- BySelector getFallbackLauncherObjectSelector(String resName) {
+ BySelector getOverviewObjectSelector(String resName) {
return By.res(getOverviewPackageName(), resName);
}
@@ -777,7 +770,7 @@
void swipeToState(int startX, int startY, int endX, int endY, int steps, int expectedState) {
final Bundle parcel = (Bundle) executeAndWaitForEvent(
- () -> linearGesture(startX, startY, endX, endY, steps),
+ () -> linearGesture(startX, startY, endX, endY, steps, false),
event -> TestProtocol.SWITCHED_TO_STATE_MESSAGE.equals(event.getClassName()),
"Swipe failed to receive an event for the swipe end");
assertEquals("Swipe switched launcher to a wrong state;",
@@ -790,31 +783,38 @@
ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, getResources()) + 1;
}
- int getBottomGestureMargin(UiObject2 container) {
- return container.getVisibleBounds().bottom - getRealDisplaySize().y +
- getBottomGestureSize();
+ int getBottomGestureMarginInContainer(UiObject2 container) {
+ final int bottomGestureStartOnScreen = getRealDisplaySize().y - getBottomGestureSize();
+ return container.getVisibleBounds().bottom - bottomGestureStartOnScreen;
}
- void scrollToLastVisibleRow(UiObject2 container, Collection<UiObject2> items, int topPadding) {
+ void scrollToLastVisibleRow(
+ UiObject2 container,
+ Collection<UiObject2> items,
+ int topPaddingInContainer) {
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;
+ final int itemRowCurrentTopOnScreen = lowestItem.getVisibleBounds().top;
+ final Rect containerRect = container.getVisibleBounds();
+ final int itemRowNewTopOnScreen = containerRect.top + topPaddingInContainer;
+ final int distance = itemRowCurrentTopOnScreen - itemRowNewTopOnScreen + getTouchSlop();
+ final int bottomGestureMarginInContainer = getBottomGestureMarginInContainer(container);
scroll(
container,
Direction.DOWN,
new Rect(
0,
+ containerRect.height() - distance - bottomGestureMarginInContainer,
0,
- 0,
- Math.max(bottomMargin, getBottomGestureMargin(container))),
- 150);
+ bottomGestureMarginInContainer),
+ 10,
+ true);
}
- void scroll(UiObject2 container, Direction direction, Rect margins, int steps) {
+ void scroll(
+ UiObject2 container, Direction direction, Rect margins, int steps, boolean slowDown) {
final Rect rect = container.getVisibleBounds();
if (margins != null) {
rect.left += margins.left;
@@ -831,34 +831,26 @@
switch (direction) {
case UP: {
startX = endX = rect.centerX();
- final int vertCenter = rect.centerY();
- final float halfGestureHeight = rect.height() / 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() / 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() / 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() / 2.0f;
- startX = (int) (horizCenter + halfGestureWidth) - 1;
- endX = (int) (horizCenter - halfGestureWidth);
+ startX = rect.right - 1;
+ endX = rect.left;
}
break;
default:
@@ -867,7 +859,7 @@
}
executeAndWaitForEvent(
- () -> linearGesture(startX, startY, endX, endY, steps),
+ () -> linearGesture(startX, startY, endX, endY, steps, slowDown),
event -> TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName()),
"Didn't receive a scroll end message: " + startX + ", " + startY
+ ", " + endX + ", " + endY);
@@ -875,16 +867,25 @@
// Inject a swipe gesture. Inject exactly 'steps' motion points, incrementing event time by a
// fixed interval each time.
- void linearGesture(int startX, int startY, int endX, int endY, int steps) {
+ void linearGesture(int startX, int startY, int endX, int endY, int steps, boolean slowDown) {
log("linearGesture: " + startX + ", " + startY + " -> " + endX + ", " + endY);
final long downTime = SystemClock.uptimeMillis();
final Point start = new Point(startX, startY);
final Point end = new Point(endX, endY);
sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start);
- final long endTime = movePointer(downTime, downTime, steps * GESTURE_STEP_MS, start, end);
+ final long endTime = movePointer(start, end, steps, downTime, slowDown);
sendPointer(downTime, endTime, MotionEvent.ACTION_UP, end);
}
+ long movePointer(Point start, Point end, int steps, long downTime, boolean slowDown) {
+ long endTime = movePointer(downTime, downTime, steps * GESTURE_STEP_MS, start, end);
+ if (slowDown) {
+ endTime = movePointer(downTime, endTime + GESTURE_STEP_MS, 5 * GESTURE_STEP_MS, end,
+ end);
+ }
+ return endTime;
+ }
+
void waitForIdle() {
mDevice.waitForIdle();
}
@@ -1010,4 +1011,18 @@
public void produceNativeLeak() {
getTestInfo(TestProtocol.REQUEST_NATIVE_LEAK);
}
+
+ 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;
+ }
}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/Overview.java b/tests/tapl/com/android/launcher3/tapl/Overview.java
index da68da3..16a64a7 100644
--- a/tests/tapl/com/android/launcher3/tapl/Overview.java
+++ b/tests/tapl/com/android/launcher3/tapl/Overview.java
@@ -58,7 +58,7 @@
getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD),
mLauncher.getDevice().getDisplayWidth() / 2,
0,
- 50,
+ 12,
ALL_APPS_STATE_ORDINAL);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
@@ -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/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 91f0fc4..2ee424b 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -52,7 +52,7 @@
final Rect taskBounds = mTask.getVisibleBounds();
final int centerX = taskBounds.centerX();
final int centerY = taskBounds.centerY();
- mLauncher.linearGesture(centerX, centerY, centerX, 0, 10);
+ mLauncher.linearGesture(centerX, centerY, centerX, 0, 10, false);
mLauncher.waitForIdle();
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/TestHelpers.java b/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
index 0c9fda3..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) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 51239c9..d208c66 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -48,8 +48,9 @@
mLauncher.scroll(
widgetsContainer,
Direction.DOWN,
- new Rect(0, 0, 0, mLauncher.getBottomGestureMargin(widgetsContainer)),
- FLING_STEPS);
+ new Rect(0, 0, 0,
+ mLauncher.getBottomGestureMarginInContainer(widgetsContainer) + 1),
+ FLING_STEPS, false);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung forward")) {
verifyActiveContainer();
}
@@ -69,7 +70,7 @@
widgetsContainer,
Direction.UP,
new Rect(0, 0, widgetsContainer.getVisibleBounds().width(), 0),
- FLING_STEPS);
+ FLING_STEPS, false);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) {
verifyActiveContainer();
}
@@ -102,8 +103,8 @@
"com.android.launcher3.widget.WidgetCell",
widget.getClassName());
- if (widget.getVisibleBounds().bottom <=
- displaySize.y - mLauncher.getBottomGestureSize()) {
+ if (widget.getVisibleBounds().bottom
+ <= displaySize.y - mLauncher.getBottomGestureSize()) {
return new Widget(mLauncher, widget);
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 0aa36dd..81d343d 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -32,13 +32,13 @@
import androidx.test.uiautomator.Direction;
import androidx.test.uiautomator.UiObject2;
+import com.android.launcher3.ResourceUtils;
import com.android.launcher3.testing.TestProtocol;
/**
* Operations on the workspace screen.
*/
public final class Workspace extends Home {
- private static final int DRAG_DURACTION = 2000;
private static final int FLING_STEPS = 10;
private final UiObject2 mHotseat;
@@ -59,7 +59,11 @@
verifyActiveContainer();
final UiObject2 hotseat = mHotseat;
final Point start = hotseat.getVisibleCenter();
- start.y = hotseat.getVisibleBounds().bottom - 1;
+ int deviceHeight = mLauncher.getDevice().getDisplayHeight();
+ int bottomGestureMargin = ResourceUtils.getNavbarSize(
+ ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources());
+ int displayBottom = deviceHeight - bottomGestureMargin;
+ start.y = displayBottom - 1;
final int swipeHeight = mLauncher.getTestInfo(
TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT).
getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
@@ -72,7 +76,7 @@
start.y,
start.x,
start.y - swipeHeight - mLauncher.getTouchSlop(),
- 60,
+ 12,
ALL_APPS_STATE_ORDINAL);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
@@ -165,8 +169,7 @@
LauncherInstrumentation.log("dragIconToWorkspace: sent down");
launcher.waitForLauncherObject(longPressIndicator);
LauncherInstrumentation.log("dragIconToWorkspace: indicator");
- launcher.movePointer(
- downTime, SystemClock.uptimeMillis(), DRAG_DURACTION, launchableCenter, dest);
+ launcher.movePointer(launchableCenter, dest, 10, downTime, true);
LauncherInstrumentation.log("dragIconToWorkspace: moved pointer");
launcher.sendPointer(
downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest);
@@ -181,8 +184,8 @@
public void flingForward() {
final UiObject2 workspace = verifyActiveContainer();
mLauncher.scroll(workspace, Direction.RIGHT,
- new Rect(0, 0, mLauncher.getEdgeSensitivityWidth(), 0),
- FLING_STEPS);
+ new Rect(0, 0, mLauncher.getEdgeSensitivityWidth() + 1, 0),
+ FLING_STEPS, false);
verifyActiveContainer();
}
@@ -193,8 +196,8 @@
public void flingBackward() {
final UiObject2 workspace = verifyActiveContainer();
mLauncher.scroll(workspace, Direction.LEFT,
- new Rect(mLauncher.getEdgeSensitivityWidth(), 0, 0, 0),
- FLING_STEPS);
+ new Rect(mLauncher.getEdgeSensitivityWidth() + 1, 0, 0, 0),
+ FLING_STEPS, false);
verifyActiveContainer();
}