Add nullcheck for VelocityTracker
am: 0063204dbf
Change-Id: I46066ce3cb2810a743043a5a85012b70cf45de59
diff --git a/Android.mk b/Android.mk
index 78ea02a..3d1d996 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 := \
@@ -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
@@ -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
diff --git a/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java b/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java
index bcb1f5c..3953fd0 100644
--- a/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java
+++ b/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java
@@ -40,7 +40,9 @@
@Override
protected void composeRecentsLaunchAnimator(AnimatorSet anim, View v,
- RemoteAnimationTargetCompat[] targets, boolean launcherClosing) {
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
+ boolean launcherClosing) {
// Stubbed. Recents launch animation will come from the recents view itself and will not
// use remote animations.
}
@@ -74,21 +76,23 @@
}
@Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+ public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
AnimationResult result) {
boolean isGoingToRecents =
- taskIsATargetWithMode(targetCompats, mLauncher.getTaskId(), MODE_OPENING)
+ taskIsATargetWithMode(appTargets, mLauncher.getTaskId(), MODE_OPENING)
&& (mLauncher.getStateManager().getState() == LauncherState.OVERVIEW);
if (isGoingToRecents) {
IconRecentsView recentsView = mLauncher.getOverviewPanel();
if (!recentsView.isReadyForRemoteAnim()) {
recentsView.setOnReadyForRemoteAnimCallback(() ->
- postAsyncCallback(mHandler, () -> onCreateAnimation(targetCompats, result))
+ postAsyncCallback(mHandler, () -> onCreateAnimation(appTargets,
+ wallpaperTargets, result))
);
return;
}
}
- super.onCreateAnimation(targetCompats, result);
+ super.onCreateAnimation(appTargets, wallpaperTargets, result);
}
}
}
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index cbc77d2..ae8fd82 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -20,7 +20,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
import com.android.launcher3.uioverrides.touchcontrollers.LandscapeStatesTouchController;
@@ -51,7 +50,7 @@
.getMode().hasGestures;
list.add(new PortraitStatesTouchController(launcher, allowDragToOverview));
}
- if (FeatureFlags.PULL_DOWN_STATUS_BAR && Utilities.IS_DEBUG_DEVICE
+ if (Utilities.IS_DEBUG_DEVICE
&& !launcher.getDeviceProfile().isMultiWindowMode
&& !launcher.getDeviceProfile().isVerticalBarLayout()) {
list.add(new StatusBarTouchController(launcher));
diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index 92900f2..ddf0fff 100644
--- a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -95,11 +95,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();
}
@@ -113,13 +114,13 @@
}
RemoteAnimationTargetSet targetSet =
- new RemoteAnimationTargetSet(targetCompats, MODE_CLOSING);
+ new RemoteAnimationTargetSet(appTargets, wallpaperTargets, MODE_CLOSING);
mRecentsView.setTransitionedFromApp(!targetSet.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 +158,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/FallbackActivityControllerHelper.java
index 057b48b..4994526 100644
--- a/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
+++ b/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
@@ -26,6 +26,7 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.RemoteAnimationTargetSet;
import com.android.quickstep.views.IconRecentsView;
@@ -85,13 +86,13 @@
@Override
public ActivityInitListener createActivityInitListener(
BiPredicate<RecentsActivity, Boolean> onInitListener) {
- return new RecentsActivityTracker(onInitListener);
+ return new ActivityInitListener(onInitListener, RecentsActivity.ACTIVITY_TRACKER);
}
@Nullable
@Override
public RecentsActivity getCreatedActivity() {
- return RecentsActivityTracker.getCurrentActivity();
+ return RecentsActivity.ACTIVITY_TRACKER.getCreatedActivity();
}
@Nullable
diff --git a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
index b0d9cda..8993b67 100644
--- a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -55,7 +55,7 @@
}
@Override
- public ActivityInitListener createActivityInitListener(
+ public LauncherInitListener createActivityInitListener(
BiPredicate<Launcher, Boolean> onInitListener) {
return new LauncherInitListener(onInitListener);
}
diff --git a/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 216972c..9c409cc 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;
@@ -104,7 +104,7 @@
private final long mToggleClickedTime = SystemClock.uptimeMillis();
private boolean mUserEventLogged;
- private ActivityInitListener mListener;
+ private ActivityInitListener<T> mListener;
public RecentsActivityCommand() {
mHelper = mOverviewComponentObserver.getActivityControlHelper();
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/cache/BaseIconCache.java b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
index a886c0a..93f0538 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
@@ -374,7 +374,7 @@
* Adds a default package entry in the cache. This entry is not persisted and will be removed
* when the cache is flushed.
*/
- public synchronized void cachePackageInstallInfo(String packageName, UserHandle user,
+ protected synchronized void cachePackageInstallInfo(String packageName, UserHandle user,
Bitmap icon, CharSequence title) {
removeFromMemCacheLocked(packageName, user);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 7115943..3384397 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -30,6 +30,9 @@
import android.content.Context;
import android.view.View;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.SpringAnimationBuilder;
@@ -38,9 +41,6 @@
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
/**
* A {@link QuickstepAppTransitionManagerImpl} that also implements recents transitions from
* {@link RecentsView}.
@@ -64,15 +64,16 @@
@Override
protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
- @NonNull RemoteAnimationTargetCompat[] targets, boolean launcherClosing) {
+ @NonNull RemoteAnimationTargetCompat[] appTargets,
+ @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing) {
RecentsView recentsView = mLauncher.getOverviewPanel();
boolean skipLauncherChanges = !launcherClosing;
- TaskView taskView = findTaskViewToLaunch(mLauncher, v, targets);
+ TaskView taskView = findTaskViewToLaunch(mLauncher, v, appTargets);
ClipAnimationHelper helper = new ClipAnimationHelper(mLauncher);
- anim.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, targets, helper)
- .setDuration(RECENTS_LAUNCH_DURATION));
+ anim.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets,
+ wallpaperTargets, helper).setDuration(RECENTS_LAUNCH_DURATION));
Animator childStateAnimation = null;
// Found a visible recents task that matches the opening app, lets launch the app from there
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java
index c5c4add..76050d5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java
@@ -27,7 +27,7 @@
}
@Override
- protected boolean init(Launcher launcher, boolean alreadyOnHome) {
+ public boolean init(Launcher launcher, boolean alreadyOnHome) {
PredictionUiStateManager.INSTANCE.get(launcher).switchClient(Client.OVERVIEW);
return super.init(launcher, alreadyOnHome);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index 596bc4f..46e883a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -22,33 +22,35 @@
import android.content.Context;
import android.graphics.Rect;
+import android.os.RemoteException;
+import android.util.Log;
import android.view.Gravity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.OverviewToAllAppsTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
-import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
+import com.android.quickstep.RecentsModel;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.TouchInteractionService;
+import com.android.quickstep.util.SharedApiCompat;
import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.shared.recents.ISystemUiProxy;
import java.util.ArrayList;
@@ -58,8 +60,20 @@
public abstract class RecentsUiFactory {
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);
+
+ private static final String TAG = RecentsUiFactory.class.getSimpleName();
+
+ private static AsyncCommand newSetShelfHeightCmd(Context context) {
+ return (visible, height) -> {
+ ISystemUiProxy sysUiProxy = RecentsModel.INSTANCE.get(context).getSystemUiProxy();
+ if (sysUiProxy == null) return;
+ try {
+ SharedApiCompat.setShelfHeight(sysUiProxy, visible != 0, height);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error setShelfHeight", e);
+ }
+ };
+ }
public static RotationMode ROTATION_LANDSCAPE = new RotationMode(-90) {
@Override
@@ -164,8 +178,7 @@
}
}
- if (FeatureFlags.PULL_DOWN_STATUS_BAR
- && !launcher.getDeviceProfile().isMultiWindowMode) {
+ if (!launcher.getDeviceProfile().isMultiWindowMode) {
list.add(new StatusBarTouchController(launcher));
}
@@ -200,7 +213,7 @@
DeviceProfile profile = launcher.getDeviceProfile();
boolean visible = (state == NORMAL || state == OVERVIEW) && launcher.isUserActive()
&& !profile.isVerticalBarLayout();
- UiThreadHelper.runAsyncCommand(launcher, SET_SHELF_HEIGHT_CMD,
+ UiThreadHelper.runAsyncCommand(launcher, newSetShelfHeightCmd(launcher),
visible ? 1 : 0, profile.hotseatBarSizePx);
if (state == NORMAL) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index ad90e16..b939898 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -90,11 +90,12 @@
/**
* Create remote window animation from the currently running app to the overview panel.
*
- * @param targetCompats the target apps
+ * @param appTargets the target apps
* @return animation from app to overview
*/
@Override
- public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) {
+ public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets) {
if (mRecentsView != null) {
mRecentsView.setRunningTaskIconScaledDown(true);
}
@@ -114,8 +115,8 @@
return anim;
}
- RemoteAnimationTargetSet targetSet =
- new RemoteAnimationTargetSet(targetCompats, MODE_CLOSING);
+ RemoteAnimationTargetSet targetSet = new RemoteAnimationTargetSet(appTargets,
+ wallpaperTargets, MODE_CLOSING);
// Use the top closing app to determine the insets for the animation
RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mTargetTaskId);
@@ -153,8 +154,8 @@
if (targetSet.isAnimatingHome()) {
// If we are animating home, fade in the opening targets
- RemoteAnimationTargetSet openingSet =
- new RemoteAnimationTargetSet(targetCompats, MODE_OPENING);
+ RemoteAnimationTargetSet openingSet = new RemoteAnimationTargetSet(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 7196f7c..d524b5d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -57,10 +57,10 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.views.FloatingIconView;
-import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.inputconsumers.InputConsumer;
+import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.ClipAnimationHelper;
import com.android.quickstep.util.ClipAnimationHelper.TransformParams;
import com.android.quickstep.util.RectFSpringAnim;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
index 8c5a788..33180a9 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
@@ -35,6 +35,7 @@
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;
@@ -177,13 +178,13 @@
@Override
public ActivityInitListener createActivityInitListener(
BiPredicate<RecentsActivity, Boolean> onInitListener) {
- return new RecentsActivityTracker(onInitListener);
+ return new ActivityInitListener(onInitListener, RecentsActivity.ACTIVITY_TRACKER);
}
@Nullable
@Override
public RecentsActivity getCreatedActivity() {
- return RecentsActivityTracker.getCurrentActivity();
+ return BaseRecentsActivity.ACTIVITY_TRACKER.getCreatedActivity();
}
@Nullable
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
index 57e6d96..e7005a6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -50,7 +50,6 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherInitListenerEx;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
@@ -61,11 +60,13 @@
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.views.LauncherRecentsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
+import com.android.systemui.plugins.shared.LauncherOverlayManager;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.function.BiPredicate;
@@ -108,6 +109,14 @@
// Ensure recents is at the correct position for NORMAL state. For example, when we detach
// recents, we assume the first task is invisible, making translation off by one task.
activity.getStateManager().reapplyState();
+ setLauncherHideBackArrow(false);
+ }
+
+ private void setLauncherHideBackArrow(boolean hideBackArrow) {
+ Launcher launcher = getCreatedActivity();
+ if (launcher != null) {
+ launcher.getRootView().setForceHideBackArrow(hideBackArrow);
+ }
}
@Override
@@ -138,7 +147,7 @@
? FloatingIconView.getFloatingIconView(activity, workspaceView,
true /* hideOriginal */, iconLocation, false /* isOpening */)
: null;
-
+ setLauncherHideBackArrow(true);
return new HomeAnimationFactory() {
@Nullable
@Override
@@ -395,11 +404,7 @@
@Nullable
@Override
public Launcher getCreatedActivity() {
- LauncherAppState app = LauncherAppState.getInstanceNoCreate();
- if (app == null) {
- return null;
- }
- return (Launcher) app.getModel().getCallback();
+ return Launcher.ACTIVITY_TRACKER.getCreatedActivity();
}
@Nullable
@@ -472,4 +477,18 @@
public void onLaunchTaskSuccess(Launcher launcher) {
launcher.getStateManager().moveToRestState();
}
+
+ @Override
+ public void closeOverlay() {
+ Launcher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return;
+ }
+ LauncherOverlayManager om = launcher.getOverlayManager();
+ if (!launcher.isStarted() || launcher.isForceInvisible()) {
+ om.hideOverlay(false /* animate */);
+ } else {
+ om.hideOverlay(150);
+ }
+ }
}
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
index a8d402e..6df9ce5 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,12 @@
import android.content.Context;
import android.os.Build;
import android.os.SystemClock;
-import android.util.Log;
import android.view.ViewConfiguration;
import com.android.launcher3.BaseDraggingActivity;
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;
@@ -209,7 +207,8 @@
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 +216,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 72a14b5..c4f375a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -16,7 +16,9 @@
public class QuickstepTestInformationHandler extends TestInformationHandler {
+ private final Context mContext;
public QuickstepTestInformationHandler(Context context) {
+ mContext = context;
}
@Override
@@ -37,12 +39,6 @@
return response;
}
- case TestProtocol.REQUEST_IS_LAUNCHER_INITIALIZED: {
- response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
- TouchInteractionService.isInitialized());
- return response;
- }
-
case TestProtocol.REQUEST_HOTSEAT_TOP: {
if (mLauncher == null) return null;
@@ -54,7 +50,7 @@
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();
@@ -67,8 +63,7 @@
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();
@@ -81,4 +76,18 @@
return super.call(method);
}
+
+ private RecentsView getRecentsView() {
+ OverviewComponentObserver observer = new OverviewComponentObserver(mContext);
+ try {
+ return observer.getActivityControlHelper().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..4a9aa2d 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.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityOptionsCompat;
@@ -152,9 +150,10 @@
true /* startAtFrontOfQueue */) {
@Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
- AnimationResult result) {
- AnimatorSet anim = composeRecentsLaunchAnimator(taskView, targetCompats);
+ public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
+ AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
+ wallpaperTargets);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -174,12 +173,13 @@
* Composes the animations for a launch from the recents list if possible.
*/
private AnimatorSet composeRecentsLaunchAnimator(TaskView taskView,
- RemoteAnimationTargetCompat[] targets) {
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets) {
AnimatorSet target = new AnimatorSet();
- boolean activityClosing = taskIsATargetWithMode(targets, getTaskId(), MODE_CLOSING);
+ boolean activityClosing = taskIsATargetWithMode(appTargets, getTaskId(), MODE_CLOSING);
ClipAnimationHelper helper = new ClipAnimationHelper(this);
- target.play(getRecentsWindowAnimator(taskView, !activityClosing, targets, helper)
- .setDuration(RECENTS_LAUNCH_DURATION));
+ 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/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
index 6897c1e..00fa0f2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
@@ -111,14 +111,15 @@
* animation.
*/
public static ValueAnimator getRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
- RemoteAnimationTargetCompat[] targets, final ClipAnimationHelper inOutHelper) {
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets, final ClipAnimationHelper inOutHelper) {
SyncRtSurfaceTransactionApplierCompat applier =
new SyncRtSurfaceTransactionApplierCompat(v);
ClipAnimationHelper.TransformParams params = new ClipAnimationHelper.TransformParams()
.setSyncTransactionApplier(applier);
final RemoteAnimationTargetSet targetSet =
- new RemoteAnimationTargetSet(targets, MODE_OPENING);
+ new RemoteAnimationTargetSet(appTargets, wallpaperTargets, MODE_OPENING);
targetSet.addDependentTransactionApplier(applier);
final RecentsView recentsView = v.getRecentsView();
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 729287c..7c1bc4e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -79,6 +79,7 @@
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.uioverrides.DejankBinderTracker;
import com.android.launcher3.util.DefaultDisplay;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
@@ -271,7 +272,6 @@
private OverviewCommandHelper mOverviewCommandHelper;
private OverviewComponentObserver mOverviewComponentObserver;
private OverviewInteractionState mOverviewInteractionState;
- private OverviewCallbacks mOverviewCallbacks;
private TaskOverlayFactory mTaskOverlayFactory;
private InputConsumerController mInputConsumer;
private boolean mAssistantAvailable;
@@ -468,7 +468,6 @@
mOverviewCommandHelper = new OverviewCommandHelper(this, mOverviewComponentObserver);
mOverviewInteractionState = OverviewInteractionState.INSTANCE.get(this);
- mOverviewCallbacks = OverviewCallbacks.get(this);
mTaskOverlayFactory = TaskOverlayFactory.INSTANCE.get(this);
mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
mIsUserUnlocked = true;
@@ -538,6 +537,7 @@
}
private void onInputEvent(InputEvent ev) {
+ DejankBinderTracker.allowBinderTrackingInTests();
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "onInputEvent " + ev);
}
@@ -573,6 +573,7 @@
TOUCH_INTERACTION_LOG.addLog("onMotionEvent", event.getActionMasked());
mUncheckedConsumer.onMotionEvent(event);
+ DejankBinderTracker.disallowBinderTrackingInTests();
}
private boolean validSystemUiFlags() {
@@ -636,7 +637,8 @@
}
private InputConsumer newBaseConsumer(boolean useSharedState, MotionEvent event) {
- RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0);
+ RunningTaskInfo runningTaskInfo = DejankBinderTracker.whitelistIpcs(
+ () -> mAM.getRunningTask(0));
if (!useSharedState) {
sSwipeSharedState.clearAllState(false /* finishAnimation */);
}
@@ -652,7 +654,8 @@
if (isExcludedAssistant(runningTaskInfo)) {
// In the case where we are in the excluded assistant state, ignore it and treat the
// running activity as the task behind the assistant
- runningTaskInfo = mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT);
+ runningTaskInfo = DejankBinderTracker.whitelistIpcs(
+ () -> mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT));
if (!ActivityManagerWrapper.isHomeTask(runningTaskInfo)) {
final ComponentName homeComponent =
mOverviewComponentObserver.getHomeIntent().getComponent();
@@ -701,20 +704,20 @@
final boolean shouldDefer;
final BaseSwipeUpHandler.Factory factory;
+ ActivityControlHelper activityControlHelper =
+ mOverviewComponentObserver.getActivityControlHelper();
if (mMode == Mode.NO_BUTTON && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
shouldDefer = !sSwipeSharedState.recentsAnimationFinishInterrupted;
factory = mFallbackNoButtonFactory;
} else {
- shouldDefer = mOverviewComponentObserver.getActivityControlHelper()
- .deferStartingActivity(mActiveNavBarRegion, event);
+ shouldDefer = activityControlHelper.deferStartingActivity(mActiveNavBarRegion, event);
factory = mWindowTreansformFactory;
}
- return new OtherActivityInputConsumer(this, runningTaskInfo,
- shouldDefer, mOverviewCallbacks, this::onConsumerInactive,
- sSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion,
- disableHorizontalSwipe(event), factory, mLogId);
+ return new OtherActivityInputConsumer(this, runningTaskInfo, shouldDefer,
+ this::onConsumerInactive, sSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion,
+ disableHorizontalSwipe(event), activityControlHelper, factory, mLogId);
}
private InputConsumer createDeviceLockedInputConsumer(RunningTaskInfo taskInfo) {
@@ -736,10 +739,10 @@
if (activity.getRootView().hasWindowFocus() || sSwipeSharedState.goingToLauncher) {
return new OverviewInputConsumer(activity, mInputMonitorCompat,
- false /* startingInActivityBounds */);
+ false /* startingInActivityBounds */, activityControl);
} else {
return new OverviewWithoutFocusInputConsumer(activity, mInputMonitorCompat,
- disableHorizontalSwipe(event));
+ activityControl, disableHorizontalSwipe(event));
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 411a808..db8eb27 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -68,6 +68,7 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.uioverrides.DejankBinderTracker;
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;
@@ -423,9 +424,13 @@
private void initializeLauncherAnimationController() {
buildAnimationController();
- if (LatencyTrackerCompat.isEnabled(mContext)) {
- LatencyTrackerCompat.logToggleRecents((int) (mLauncherFrameDrawnTime - mTouchTimeMs));
- }
+ DejankBinderTracker.whitelistIpcs(() -> {
+ // Only used in debug builds
+ if (LatencyTrackerCompat.isEnabled(mContext)) {
+ LatencyTrackerCompat.logToggleRecents(
+ (int) (mLauncherFrameDrawnTime - mTouchTimeMs));
+ }
+ });
// 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
@@ -684,8 +689,8 @@
}
BaseDraggingActivity activity = mActivityControlHelper.getCreatedActivity();
- return activity == null
- ? InputConsumer.NO_OP : new OverviewInputConsumer(activity, null, true);
+ return activity == null ? InputConsumer.NO_OP
+ : new OverviewInputConsumer(activity, null, true, mActivityControlHelper);
}
private void endRunningWindowAnim(boolean cancel) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
index e0ff8af..6275d21 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
@@ -38,6 +38,7 @@
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.util.ObjectWrapper;
import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.BaseSwipeUpHandler;
@@ -47,7 +48,6 @@
import com.android.quickstep.RecentsModel;
import com.android.quickstep.SwipeSharedState;
import com.android.quickstep.fallback.FallbackRecentsView;
-import com.android.quickstep.util.ObjectWrapper;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.SwipeAnimationTargetSet;
import com.android.quickstep.views.TaskView;
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..f06702d 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,6 +22,7 @@
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;
@@ -44,14 +45,16 @@
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.ActivityControlHelper;
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;
@@ -61,6 +64,7 @@
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;
/**
@@ -77,11 +81,11 @@
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 ActivityControlHelper mActivityControlHelper;
private final BaseSwipeUpHandler.Factory mHandlerFactory;
@@ -121,10 +125,10 @@
private int mLogId;
public OtherActivityInputConsumer(Context base, RunningTaskInfo runningTaskInfo,
- boolean isDeferredDownTarget, OverviewCallbacks overviewCallbacks,
- Consumer<OtherActivityInputConsumer> onCompleteCallback,
+ boolean isDeferredDownTarget, Consumer<OtherActivityInputConsumer> onCompleteCallback,
SwipeSharedState swipeSharedState, InputMonitorCompat inputMonitorCompat,
RectF swipeTouchRegion, boolean disableHorizontalSwipe,
+ ActivityControlHelper activityControlHelper,
Factory handlerFactory, int logId) {
super(base);
mLogId = logId;
@@ -134,6 +138,7 @@
mMode = SysUINavigationMode.getMode(base);
mSwipeTouchRegion = swipeTouchRegion;
mHandlerFactory = handlerFactory;
+ mActivityControlHelper = activityControlHelper;
mMotionPauseDetector = new MotionPauseDetector(base);
mMotionPauseMinDisplacement = base.getResources().getDimension(
@@ -144,7 +149,6 @@
boolean continuingPreviousGesture = swipeSharedState.getActiveListener() != null;
mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget;
- mOverviewCallbacks = overviewCallbacks;
mSwipeSharedState = swipeSharedState;
mNavBarPosition = new NavBarPosition(base);
@@ -313,7 +317,7 @@
}
mInputMonitorCompat.pilferPointers();
- mOverviewCallbacks.closeAllWindows();
+ mActivityControlHelper.closeOverlay();
ActivityManagerWrapper.getInstance().closeSystemWindows(
CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
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..4857d00 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
@@ -19,7 +19,6 @@
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 +26,8 @@
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.ActivityControlHelper;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -42,6 +40,7 @@
implements InputConsumer {
private final T mActivity;
+ private final ActivityControlHelper<T> mActivityControlHelper;
private final BaseDragLayer mTarget;
private final InputMonitorCompat mInputMonitor;
@@ -53,10 +52,12 @@
private boolean mTargetHandledTouch;
public OverviewInputConsumer(T activity, @Nullable InputMonitorCompat inputMonitor,
- boolean startingInActivityBounds) {
+ boolean startingInActivityBounds,
+ ActivityControlHelper<T> activityControlHelper) {
mActivity = activity;
mInputMonitor = inputMonitor;
mStartingInActivityBounds = startingInActivityBounds;
+ mActivityControlHelper = activityControlHelper;
mTarget = activity.getDragLayer();
if (startingInActivityBounds) {
@@ -98,7 +99,7 @@
if (!mTargetHandledTouch && handled) {
mTargetHandledTouch = true;
if (!mStartingInActivityBounds) {
- OverviewCallbacks.get(mActivity).closeAllWindows();
+ mActivityControlHelper.closeOverlay();
ActivityManagerWrapper.getInstance()
.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
TOUCH_INTERACTION_LOG.addLog("startQuickstep");
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index 05cbb78..e11d492 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
@@ -34,10 +34,9 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Utilities;
import com.android.launcher3.logging.StatsLogUtils;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.quickstep.OverviewCallbacks;
+import com.android.quickstep.ActivityControlHelper;
import com.android.quickstep.util.NavBarPosition;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -50,16 +49,17 @@
private final float mSquaredTouchSlop;
private final Context mContext;
private final NavBarPosition mNavBarPosition;
+ private final ActivityControlHelper mActivityControlHelper;
private boolean mInterceptedTouch;
private VelocityTracker mVelocityTracker;
-
public OverviewWithoutFocusInputConsumer(Context context, InputMonitorCompat inputMonitor,
- boolean disableHorizontalSwipe) {
+ ActivityControlHelper activityControlHelper, boolean disableHorizontalSwipe) {
mInputMonitor = inputMonitor;
mDisableHorizontalSwipe = disableHorizontalSwipe;
mContext = context;
+ mActivityControlHelper = activityControlHelper;
mSquaredTouchSlop = Utilities.squaredTouchSlop(context);
mNavBarPosition = new NavBarPosition(context);
@@ -148,7 +148,7 @@
}
if (triggerQuickstep) {
- OverviewCallbacks.get(mContext).closeAllWindows();
+ mActivityControlHelper.closeOverlay();
ActivityManagerWrapper.getInstance()
.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
TOUCH_INTERACTION_LOG.addLog("startQuickstep");
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
index b1999d7..71ad8ba 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java
@@ -20,6 +20,7 @@
import android.graphics.Rect;
import android.util.ArraySet;
+import androidx.annotation.BinderThread;
import androidx.annotation.UiThread;
import com.android.launcher3.Utilities;
@@ -70,14 +71,16 @@
mListeners.remove(listener);
}
- @Override
+ // Called only in R+ platform
+ @BinderThread
public final void onAnimationStart(RecentsAnimationControllerCompat controller,
- RemoteAnimationTargetCompat[] targets, Rect homeContentInsets,
- Rect minimizedHomeBounds) {
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
+ Rect homeContentInsets, Rect minimizedHomeBounds) {
mController = controller;
- SwipeAnimationTargetSet targetSet = new SwipeAnimationTargetSet(controller, targets,
- homeContentInsets, minimizedHomeBounds, mShouldMinimizeSplitScreen,
- mOnFinishListener);
+ SwipeAnimationTargetSet targetSet = new SwipeAnimationTargetSet(controller, appTargets,
+ wallpaperTargets, homeContentInsets, minimizedHomeBounds,
+ mShouldMinimizeSplitScreen, mOnFinishListener);
if (mCancelled) {
targetSet.cancelAnimation();
@@ -90,6 +93,17 @@
}
}
+ // 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);
+ }
+
+ @BinderThread
@Override
public final void onAnimationCanceled(ThumbnailData thumbnailData) {
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
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
index 3619d3a..3da6b78 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java
@@ -41,10 +41,10 @@
public final Rect minimizedHomeBounds;
public SwipeAnimationTargetSet(RecentsAnimationControllerCompat controller,
- RemoteAnimationTargetCompat[] targets, Rect homeContentInsets,
- Rect minimizedHomeBounds, boolean shouldMinimizeSplitScreen,
+ RemoteAnimationTargetCompat[] apps, RemoteAnimationTargetCompat[] wallpapers,
+ Rect homeContentInsets, Rect minimizedHomeBounds, boolean shouldMinimizeSplitScreen,
Consumer<SwipeAnimationTargetSet> onFinishListener) {
- super(targets, MODE_CLOSING);
+ super(apps, wallpapers, MODE_CLOSING);
this.controller = controller;
this.homeContentInsets = homeContentInsets;
this.minimizedHomeBounds = minimizedHomeBounds;
@@ -62,8 +62,8 @@
*/
public SwipeAnimationTargetSet cloneWithoutTargets() {
return new SwipeAnimationTargetSet(controller, new RemoteAnimationTargetCompat[0],
- homeContentInsets, minimizedHomeBounds, mShouldMinimizeSplitScreen,
- mOnFinishListener);
+ new RemoteAnimationTargetCompat[0], homeContentInsets, minimizedHomeBounds,
+ mShouldMinimizeSplitScreen, mOnFinishListener);
}
public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint) {
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 b601834..0f9fc17 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
@@ -45,6 +45,7 @@
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
+import com.android.launcher3.uioverrides.DejankBinderTracker;
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.util.ClipAnimationHelper;
@@ -193,7 +194,7 @@
@Override
public boolean shouldUseMultiWindowTaskSizeStrategy() {
- return mActivity.isInMultiWindowMode();
+ return DejankBinderTracker.whitelistIpcs(() -> mActivity.isInMultiWindowMode());
}
@Override
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..5799c01 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;
@@ -152,7 +151,6 @@
mPaint.setShader(null);
mOverlay.reset();
}
-
if (mOverviewScreenshotActionsPlugin != null) {
mOverviewScreenshotActionsPlugin
.setupActions((ViewGroup) getTaskView(), getThumbnail(), mActivity);
@@ -345,8 +343,7 @@
final Configuration configuration =
getContext().getResources().getConfiguration();
// Rotate the screenshot if not in multi-window mode
- isRotated = FeatureFlags.OVERVIEW_USE_SCREENSHOT_ORIENTATION &&
- configuration.orientation != mThumbnailData.orientation &&
+ isRotated = configuration.orientation != mThumbnailData.orientation &&
!mActivity.isInMultiWindowMode() &&
mThumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN;
// Scale the screenshot to always fit the width of the card.
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 5c4d6d8..98aaceb 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -16,8 +16,6 @@
<resources>
<string name="task_overlay_factory_class" translatable="false"></string>
- <string name="overview_callbacks_class" translatable="false"></string>
-
<!-- Activity which blocks home gesture -->
<string name="gesture_blocking_activity" translatable="false"></string>
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index a8e2956..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..663b125 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -19,30 +19,25 @@
import android.content.Context;
import android.content.Intent;
import android.os.Build;
-import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
-import com.android.launcher3.states.InternalStateHandler;
-import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
-import com.android.quickstep.OverviewCallbacks;
+import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.RemoteAnimationProvider;
import java.util.function.BiPredicate;
@TargetApi(Build.VERSION_CODES.P)
-public class LauncherInitListener extends InternalStateHandler implements ActivityInitListener {
-
- private final BiPredicate<Launcher, Boolean> mOnInitListener;
+public class LauncherInitListener extends ActivityInitListener<Launcher> {
private RemoteAnimationProvider mRemoteAnimationProvider;
public LauncherInitListener(BiPredicate<Launcher, Boolean> onInitListener) {
- mOnInitListener = onInitListener;
+ super(onInitListener, Launcher.ACTIVITY_TRACKER);
}
@Override
- protected boolean init(Launcher launcher, boolean alreadyOnHome) {
+ public boolean init(Launcher launcher, boolean alreadyOnHome) {
if (mRemoteAnimationProvider != null) {
QuickstepAppTransitionManagerImpl appTransitionManager =
(QuickstepAppTransitionManagerImpl) launcher.getAppTransitionManager();
@@ -50,7 +45,7 @@
// Set a one-time animation provider. After the first call, this will get cleared.
// TODO: Probably also check the intended target id.
CancellationSignal cancellationSignal = new CancellationSignal();
- appTransitionManager.setRemoteAnimationProvider((targets) -> {
+ appTransitionManager.setRemoteAnimationProvider((appTargets, wallpaperTargets) -> {
// On the first call clear the reference.
cancellationSignal.cancel();
@@ -58,34 +53,25 @@
mRemoteAnimationProvider = null;
if (provider != null && launcher.getStateManager().getState().overviewUi) {
- return provider.createWindowAnimation(targets);
+ return provider.createWindowAnimation(appTargets, wallpaperTargets);
}
return null;
}, cancellationSignal);
}
- OverviewCallbacks.get(launcher).onInitOverviewTransition();
- return mOnInitListener.test(launcher, alreadyOnHome);
- }
-
- @Override
- public void register() {
- initWhenReady();
+ launcher.deferOverlayCallbacksUntilNextResumeOrStop();
+ return super.init(launcher, alreadyOnHome);
}
@Override
public void unregister() {
mRemoteAnimationProvider = null;
- clearReference();
+ super.unregister();
}
@Override
public void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
Context context, Handler handler, long duration) {
mRemoteAnimationProvider = animProvider;
-
- register();
-
- Bundle options = animProvider.toActivityOptions(handler, duration, context).toBundle();
- context.startActivity(addToIntent(new Intent((intent))), options);
+ super.registerAndStartActivity(intent, animProvider, context, handler, duration);
}
}
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index 991408c..7cd8786 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -57,6 +57,9 @@
import android.util.Pair;
import android.view.View;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.Interpolators;
@@ -80,9 +83,6 @@
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.systemui.shared.system.WindowManagerWrapper;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
/**
* {@link LauncherAppTransitionManager} with Quickstep-specific app transitions for launching from
* home and/or all-apps.
@@ -202,17 +202,19 @@
true /* startAtFrontOfQueue */) {
@Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
- AnimationResult result) {
+ public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
AnimatorSet anim = new AnimatorSet();
boolean launcherClosing =
- launcherIsATargetWithMode(targetCompats, MODE_CLOSING);
+ launcherIsATargetWithMode(appTargets, MODE_CLOSING);
- if (isLaunchingFromRecents(v, targetCompats)) {
- composeRecentsLaunchAnimator(anim, v, targetCompats, launcherClosing);
+ if (isLaunchingFromRecents(v, appTargets)) {
+ composeRecentsLaunchAnimator(anim, v, appTargets, wallpaperTargets,
+ launcherClosing);
} else {
- composeIconLaunchAnimator(anim, v, targetCompats, launcherClosing);
+ composeIconLaunchAnimator(anim, v, appTargets, wallpaperTargets,
+ launcherClosing);
}
if (launcherClosing) {
@@ -255,36 +257,39 @@
*
* @param anim the animator set to add to
* @param v the launching view
- * @param targets the apps that are opening/closing
+ * @param appTargets the apps that are opening/closing
* @param launcherClosing true if the launcher app is closing
*/
protected abstract void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
- @NonNull RemoteAnimationTargetCompat[] targets, boolean launcherClosing);
+ @NonNull RemoteAnimationTargetCompat[] appTargets,
+ @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing);
/**
* Compose the animations for a launch from the app icon.
*
* @param anim the animation to add to
* @param v the launching view with the icon
- * @param targets the list of opening/closing apps
+ * @param appTargets the list of opening/closing apps
* @param launcherClosing true if launcher is closing
*/
private void composeIconLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
- @NonNull RemoteAnimationTargetCompat[] targets, boolean launcherClosing) {
+ @NonNull RemoteAnimationTargetCompat[] appTargets,
+ @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
+ boolean launcherClosing) {
// Set the state animation first so that any state listeners are called
// before our internal listeners.
mLauncher.getStateManager().setCurrentAnimation(anim);
- Rect windowTargetBounds = getWindowTargetBounds(targets);
+ Rect windowTargetBounds = getWindowTargetBounds(appTargets);
boolean isAllOpeningTargetTrs = true;
- for (int i = 0; i < targets.length; i++) {
- RemoteAnimationTargetCompat target = targets[i];
+ for (int i = 0; i < appTargets.length; i++) {
+ RemoteAnimationTargetCompat target = appTargets[i];
if (target.mode == MODE_OPENING) {
isAllOpeningTargetTrs &= target.isTranslucent;
}
if (!isAllOpeningTargetTrs) break;
}
- anim.play(getOpeningWindowAnimators(v, targets, windowTargetBounds,
+ anim.play(getOpeningWindowAnimators(v, appTargets, wallpaperTargets, windowTargetBounds,
!isAllOpeningTargetTrs));
if (launcherClosing) {
Pair<AnimatorSet, Runnable> launcherContentAnimator =
@@ -305,10 +310,10 @@
* In multiwindow mode, we need to get the final size of the opening app window target to help
* figure out where the floating view should animate to.
*/
- private Rect getWindowTargetBounds(RemoteAnimationTargetCompat[] targets) {
+ private Rect getWindowTargetBounds(RemoteAnimationTargetCompat[] appTargets) {
Rect bounds = new Rect(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx);
if (mLauncher.isInMultiWindowMode()) {
- for (RemoteAnimationTargetCompat target : targets) {
+ for (RemoteAnimationTargetCompat target : appTargets) {
if (target.mode == MODE_OPENING) {
bounds.set(target.sourceContainerBounds);
bounds.offsetTo(target.position.x, target.position.y);
@@ -418,7 +423,9 @@
/**
* @return Animator that controls the window of the opening targets.
*/
- private ValueAnimator getOpeningWindowAnimators(View v, RemoteAnimationTargetCompat[] targets,
+ private ValueAnimator getOpeningWindowAnimators(View v,
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
Rect windowTargetBounds, boolean toggleVisibility) {
RectF bounds = new RectF();
FloatingIconView floatingView = FloatingIconView.getFloatingIconView(mLauncher, v,
@@ -426,8 +433,8 @@
Rect crop = new Rect();
Matrix matrix = new Matrix();
- RemoteAnimationTargetSet openingTargets = new RemoteAnimationTargetSet(targets,
- MODE_OPENING);
+ RemoteAnimationTargetSet openingTargets = new RemoteAnimationTargetSet(appTargets,
+ wallpaperTargets, MODE_OPENING);
SyncRtSurfaceTransactionApplierCompat surfaceApplier =
new SyncRtSurfaceTransactionApplierCompat(floatingView);
openingTargets.addDependentTransactionApplier(surfaceApplier);
@@ -551,9 +558,9 @@
float croppedHeight = (windowTargetBounds.height() - crop.height()) * scale;
float croppedWidth = (windowTargetBounds.width() - crop.width()) * scale;
- SurfaceParams[] params = new SurfaceParams[targets.length];
- for (int i = targets.length - 1; i >= 0; i--) {
- RemoteAnimationTargetCompat target = targets[i];
+ SurfaceParams[] params = new SurfaceParams[appTargets.length];
+ for (int i = appTargets.length - 1; i >= 0; i--) {
+ RemoteAnimationTargetCompat target = appTargets[i];
Rect targetCrop;
final float alpha;
final float cornerRadius;
@@ -619,7 +626,8 @@
/**
* Animator that controls the transformations of the windows when unlocking the device.
*/
- private Animator getUnlockWindowAnimator(RemoteAnimationTargetCompat[] targets) {
+ private Animator getUnlockWindowAnimator(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets) {
SyncRtSurfaceTransactionApplierCompat surfaceApplier =
new SyncRtSurfaceTransactionApplierCompat(mDragLayer);
ValueAnimator unlockAnimator = ValueAnimator.ofFloat(0, 1);
@@ -629,9 +637,9 @@
unlockAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- SurfaceParams[] params = new SurfaceParams[targets.length];
- for (int i = targets.length - 1; i >= 0; i--) {
- RemoteAnimationTargetCompat target = targets[i];
+ SurfaceParams[] params = new SurfaceParams[appTargets.length];
+ for (int i = appTargets.length - 1; i >= 0; i--) {
+ RemoteAnimationTargetCompat target = appTargets[i];
params[i] = new SurfaceParams(target.leash, 1f, null,
target.sourceContainerBounds,
RemoteAnimationProvider.getLayer(target, MODE_OPENING), cornerRadius);
@@ -645,7 +653,8 @@
/**
* Animator that controls the transformations of the windows the targets that are closing.
*/
- private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] targets) {
+ private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets) {
SyncRtSurfaceTransactionApplierCompat surfaceApplier =
new SyncRtSurfaceTransactionApplierCompat(mDragLayer);
Matrix matrix = new Matrix();
@@ -661,9 +670,9 @@
@Override
public void onUpdate(float percent) {
- SurfaceParams[] params = new SurfaceParams[targets.length];
- for (int i = targets.length - 1; i >= 0; i--) {
- RemoteAnimationTargetCompat target = targets[i];
+ SurfaceParams[] params = new SurfaceParams[appTargets.length];
+ for (int i = appTargets.length - 1; i >= 0; i--) {
+ RemoteAnimationTargetCompat target = appTargets[i];
final float alpha;
final float cornerRadius;
if (target.mode == MODE_CLOSING) {
@@ -764,13 +773,21 @@
}
@Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+ public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
LauncherAnimationRunner.AnimationResult result) {
+ if (mLauncher.isDestroyed()) {
+ AnimatorSet anim = new AnimatorSet();
+ anim.play(getClosingWindowAnimators(appTargets, wallpaperTargets));
+ result.setAnimation(anim, mLauncher.getApplicationContext());
+ return;
+ }
+
if (!mLauncher.hasBeenResumed()) {
// If launcher is not resumed, wait until new async-frame after resume
mLauncher.addOnResumeCallback(() ->
postAsyncCallback(mHandler, () ->
- onCreateAnimation(targetCompats, result)));
+ onCreateAnimation(appTargets, wallpaperTargets, result)));
return;
}
@@ -782,14 +799,14 @@
AnimatorSet anim = null;
RemoteAnimationProvider provider = mRemoteAnimationProvider;
if (provider != null) {
- anim = provider.createWindowAnimation(targetCompats);
+ anim = provider.createWindowAnimation(appTargets, wallpaperTargets);
}
if (anim == null) {
anim = new AnimatorSet();
anim.play(mFromUnlock
- ? getUnlockWindowAnimator(targetCompats)
- : getClosingWindowAnimators(targetCompats));
+ ? getUnlockWindowAnimator(appTargets, wallpaperTargets)
+ : getClosingWindowAnimators(appTargets, wallpaperTargets));
// Normally, we run the launcher content animation when we are transitioning
// home, but if home is already visible, then we don't want to animate the
@@ -799,7 +816,7 @@
// targets list because it is already visible). In that case, we force
// invisibility on touch down, and only reset it after the animation to home
// is initialized.
- if (launcherIsATargetWithMode(targetCompats, MODE_OPENING)
+ if (launcherIsATargetWithMode(appTargets, MODE_OPENING)
|| mLauncher.isForceInvisible()) {
// Only register the content animation for cancellation when state changes
mLauncher.getStateManager().setCurrentAnimation(anim);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java b/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java
new file mode 100644
index 0000000..d8aa235
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java
@@ -0,0 +1,159 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides;
+
+import static android.os.IBinder.FLAG_ONEWAY;
+
+import android.os.Binder;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+
+import androidx.annotation.MainThread;
+
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.function.BiConsumer;
+import java.util.function.Supplier;
+
+/**
+ * A binder proxy transaction listener for tracking non-whitelisted binder calls.
+ */
+public class DejankBinderTracker implements Binder.ProxyTransactListener {
+ private static final String TAG = "DejankBinderTracker";
+
+ private static final Object sLock = new Object();
+ private static final HashSet<String> sWhitelistedFrameworkClasses = new HashSet<>();
+ static {
+ // Common IPCs that are ok to block the main thread.
+ sWhitelistedFrameworkClasses.add("android.view.IWindowSession");
+ sWhitelistedFrameworkClasses.add("android.os.IPowerManager");
+ }
+ private static boolean sTemporarilyIgnoreTracking = false;
+
+ // Used by the client to limit binder tracking to specific regions
+ private static boolean sTrackingAllowed = false;
+
+ private BiConsumer<String, Integer> mUnexpectedTransactionCallback;
+ private boolean mIsTracking = false;
+
+ /**
+ * Temporarily ignore blocking binder calls for the duration of this {@link Runnable}.
+ */
+ @MainThread
+ public static void whitelistIpcs(Runnable runnable) {
+ sTemporarilyIgnoreTracking = true;
+ runnable.run();
+ sTemporarilyIgnoreTracking = false;
+ }
+
+ /**
+ * Temporarily ignore blocking binder calls for the duration of this {@link Supplier}.
+ */
+ @MainThread
+ public static <T> T whitelistIpcs(Supplier<T> supplier) {
+ sTemporarilyIgnoreTracking = true;
+ T value = supplier.get();
+ sTemporarilyIgnoreTracking = false;
+ return value;
+ }
+
+ /**
+ * Enables binder tracking during a test.
+ */
+ @MainThread
+ public static void allowBinderTrackingInTests() {
+ sTrackingAllowed = true;
+ }
+
+ /**
+ * Disables binder tracking during a test.
+ */
+ @MainThread
+ public static void disallowBinderTrackingInTests() {
+ sTrackingAllowed = false;
+ }
+
+ public DejankBinderTracker(BiConsumer<String, Integer> unexpectedTransactionCallback) {
+ mUnexpectedTransactionCallback = unexpectedTransactionCallback;
+ }
+
+ @MainThread
+ public void startTracking() {
+ if (!Build.TYPE.toLowerCase(Locale.ROOT).contains("debug")
+ && !Build.TYPE.toLowerCase(Locale.ROOT).equals("eng")) {
+ Log.wtf(TAG, "Unexpected use of binder tracker in non-debug build", new Exception());
+ return;
+ }
+ if (mIsTracking) {
+ return;
+ }
+ mIsTracking = true;
+ Binder.setProxyTransactListener(this);
+ }
+
+ @MainThread
+ public void stopTracking() {
+ if (!mIsTracking) {
+ return;
+ }
+ mIsTracking = false;
+ Binder.setProxyTransactListener(null);
+ }
+
+ // Override the hidden Binder#onTransactStarted method
+ public synchronized Object onTransactStarted(IBinder binder, int transactionCode, int flags) {
+ if (!mIsTracking
+ || !sTrackingAllowed
+ || sTemporarilyIgnoreTracking
+ || (flags & FLAG_ONEWAY) == FLAG_ONEWAY
+ || !isMainThread()) {
+ return null;
+ }
+
+ String descriptor;
+ try {
+ descriptor = binder.getInterfaceDescriptor();
+ if (sWhitelistedFrameworkClasses.contains(descriptor)) {
+ return null;
+ }
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ descriptor = binder.getClass().getSimpleName();
+ }
+
+ mUnexpectedTransactionCallback.accept(descriptor, transactionCode);
+ return null;
+ }
+
+ @Override
+ public Object onTransactStarted(IBinder binder, int transactionCode) {
+ // Do nothing
+ return null;
+ }
+
+ @Override
+ public void onTransactEnded(Object session) {
+ // Do nothing
+ }
+
+ public static boolean isMainThread() {
+ return Thread.currentThread() == Looper.getMainLooper().getThread();
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java b/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java
index 853a1c6..27d81ef 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java
@@ -18,7 +18,8 @@
import android.content.Context;
import android.provider.DeviceConfig;
-import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
+
+import com.android.launcher3.config.FeatureFlags.BaseTogglableFlag;
public class TogglableFlag extends BaseTogglableFlag {
public static final String NAMESPACE_LAUNCHER = "launcher";
@@ -36,14 +37,14 @@
@Override
public void addChangeListener(Context context, Runnable r) {
DeviceConfig.addOnPropertiesChangedListener(
- NAMESPACE_LAUNCHER,
- context.getMainExecutor(),
- (properties) -> {
- if (!NAMESPACE_LAUNCHER.equals(properties.getNamespace())) {
- return;
- }
- initialize(context);
- r.run();
- });
+ NAMESPACE_LAUNCHER,
+ context.getMainExecutor(),
+ (properties) -> {
+ if (!NAMESPACE_LAUNCHER.equals(properties.getNamespace())) {
+ return;
+ }
+ initialize(context);
+ r.run();
+ });
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index c02df93..b0b5dcf 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -165,13 +165,14 @@
CancellationSignal cancellationSignal) {
QuickstepAppTransitionManagerImpl appTransitionManager =
(QuickstepAppTransitionManagerImpl) launcher.getAppTransitionManager();
- appTransitionManager.setRemoteAnimationProvider((targets) -> {
+ appTransitionManager.setRemoteAnimationProvider((appTargets, wallpaperTargets) -> {
// On the first call clear the reference.
cancellationSignal.cancel();
ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
- fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(targets));
+ fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(appTargets,
+ wallpaperTargets));
AnimatorSet anim = new AnimatorSet();
anim.play(fadeAnimation);
return anim;
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index 5c9c7d4..2d4b136 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -17,12 +17,10 @@
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,7 +32,7 @@
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.ActivityInitListener;
import com.android.quickstep.util.RemoteAnimationTargetSet;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -97,15 +95,7 @@
void onLaunchTaskSuccess(T activity);
- interface ActivityInitListener {
-
- void register();
-
- void unregister();
-
- void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
- Context context, Handler handler, long duration);
- }
+ default void closeOverlay() { }
interface AnimationFactory {
diff --git a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
index 1ac7ed4..71833ad 100644
--- a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
@@ -28,6 +28,7 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.uioverrides.UiFactory;
+import com.android.launcher3.util.ActivityTracker;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
@@ -43,6 +44,7 @@
*/
public abstract class BaseRecentsActivity extends BaseDraggingActivity {
+ public static ActivityTracker<BaseRecentsActivity> ACTIVITY_TRACKER = new ActivityTracker<>();
private Configuration mOldConfig;
@Override
@@ -55,7 +57,7 @@
getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
- RecentsActivityTracker.onRecentsActivityCreate(this);
+ ACTIVITY_TRACKER.handleCreate((RecentsActivity) this);
}
/**
@@ -132,13 +134,13 @@
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
- RecentsActivityTracker.onRecentsActivityNewIntent(this);
+ ACTIVITY_TRACKER.handleNewIntent(this, intent);
}
@Override
protected void onDestroy() {
super.onDestroy();
- RecentsActivityTracker.onRecentsActivityDestroy(this);
+ ACTIVITY_TRACKER.onActivityDestroyed(this);
}
@Override
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/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/util/ActivityInitListener.java b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
new file mode 100644
index 0000000..fe37d60
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.util.ActivityTracker;
+import com.android.launcher3.util.ActivityTracker.SchedulerCallback;
+
+import java.util.function.BiPredicate;
+
+public class ActivityInitListener<T extends BaseActivity> implements SchedulerCallback<T> {
+
+ private final BiPredicate<T, Boolean> mOnInitListener;
+ private final ActivityTracker<T> mActivityTracker;
+
+ public ActivityInitListener(BiPredicate<T, Boolean> onInitListener,
+ ActivityTracker<T> tracker) {
+ mOnInitListener = onInitListener;
+ mActivityTracker = tracker;
+ }
+
+ @Override
+ public boolean init(T activity, boolean alreadyOnHome) {
+ return mOnInitListener.test(activity, alreadyOnHome);
+ }
+
+ public void register() {
+ mActivityTracker.schedule(this);
+ }
+
+ public void unregister() {
+ mActivityTracker.clearReference(this);
+ }
+
+ public void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
+ Context context, Handler handler, long duration) {
+ register();
+
+ Bundle options = animProvider.toActivityOptions(handler, duration, context).toBundle();
+ context.startActivity(addToIntent(new Intent((intent))), options);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index 4503a43..6210fc2 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -31,16 +31,17 @@
static final int Z_BOOST_BASE = 800570000;
- AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targets);
+ AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets);
default ActivityOptions toActivityOptions(Handler handler, long duration, Context context) {
LauncherAnimationRunner runner = new LauncherAnimationRunner(handler,
false /* startAtFrontOfQueue */) {
@Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
- AnimationResult result) {
- result.setAnimation(createWindowAnimation(targetCompats), context);
+ public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
+ result.setAnimation(createWindowAnimation(appTargets, wallpaperTargets), context);
}
};
return ActivityOptionsCompat.makeRemoteAnimation(
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java
index 1229293..d769248 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java
@@ -32,10 +32,12 @@
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 RemoteAnimationTargetSet(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;
}
diff --git a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
index 40dd74b..1d0851c 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
@@ -32,8 +32,9 @@
private final RemoteAnimationTargetSet mTarget;
private boolean mFirstFrame = true;
- public RemoteFadeOutAnimationListener(RemoteAnimationTargetCompat[] targets) {
- mTarget = new RemoteAnimationTargetSet(targets, MODE_CLOSING);
+ public RemoteFadeOutAnimationListener(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets) {
+ mTarget = new RemoteAnimationTargetSet(appTargets, wallpaperTargets, MODE_CLOSING);
}
@Override
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 8c11c1c..fcbc314 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -22,11 +22,17 @@
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_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;
@@ -41,11 +47,17 @@
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.launcher3.util.rule.SimpleActivityRule;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
+import com.android.quickstep.views.RecentsView;
import org.junit.Rule;
import org.junit.Test;
@@ -54,11 +66,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;
@@ -74,6 +86,10 @@
@Rule
public final TestRule mOrderSensitiveRules;
+ @Rule
+ public final SimpleActivityRule<RecentsActivity> mActivityMonitor =
+ new SimpleActivityRule(RecentsActivity.class);
+
public FallbackRecentsTest() throws RemoteException {
Instrumentation instrumentation = getInstrumentation();
Context context = instrumentation.getContext();
@@ -81,9 +97,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,7 +130,7 @@
}
}
- @NavigationModeSwitch(mode = THREE_BUTTON)
+ @NavigationModeSwitch
@Test
public void goToOverviewFromHome() {
mDevice.pressHome();
@@ -121,7 +140,7 @@
mLauncher.getBackground().switchToOverview();
}
- @NavigationModeSwitch(mode = THREE_BUTTON)
+ @NavigationModeSwitch
@Test
public void goToOverviewFromApp() {
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
@@ -129,15 +148,85 @@
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 = mActivityMonitor.getActivity();
+ if (activity == null) {
+ return false;
+ }
+ result[0] = f.apply(activity);
+ return true;
+ }).get(), DEFAULT_UI_TIMEOUT);
+ return (T) result[0];
+ }
+
+ private BaseOverview pressHomeAndGoToOverview() {
+ mDevice.pressHome();
+ return mLauncher.getBackground().switchToOverview();
+ }
+
+ @NavigationModeSwitch
+ @Test
+ public void testOverview() {
+ startAppFast(getAppPackageName());
+ startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+ startTestActivity(2);
+
+ 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();
+ }
}
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..6726179
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
@@ -0,0 +1,268 @@
+/*
+ * 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.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
+ */
+@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();
+ 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-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-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-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-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 cc8d664..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>
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-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/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..32eb2ec 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);
@@ -216,5 +218,10 @@
public Bitmap newIcon() {
return Bitmap.createBitmap(1, 1, Config.ARGB_8888);
}
+
+ @Override
+ public synchronized BitmapInfo getDefaultIcon(UserHandle user) {
+ return BitmapInfo.fromBitmap(newIcon());
+ }
}
}
diff --git a/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
index 42848f4..81b9043 100644
--- a/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -11,7 +11,6 @@
import com.android.launcher3.WorkspaceItemInfo;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@@ -41,7 +40,6 @@
}
@Test
- @Ignore("This test fails with resource errors") // b/131115553
public void testCacheUpdate_update_apps() throws Exception {
// Clear all icons from apps list so that its easy to check what was updated
for (AppInfo info : allAppsList.data) {
@@ -66,7 +64,6 @@
}
@Test
- @Ignore("This test fails with resource errors") // b/131115553
public void testSessionUpdate_ignores_normal_apps() throws Exception {
executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app1"));
@@ -75,7 +72,6 @@
}
@Test
- @Ignore("This test fails with resource errors") // b/131115553
public void testSessionUpdate_updates_pending_apps() throws Exception {
executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app3"));
diff --git a/robolectric_tests/src/com/android/launcher3/model/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..9f833b1 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,9 +27,6 @@
return mOpenHelper.getWritableDatabase();
}
- @Override
- protected void notifyListeners() { }
-
private static class MyDatabaseHelper extends DatabaseHelper {
public MyDatabaseHelper(Context context) {
super(context, null, null);
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/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 994ba65..e2ef337 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -36,6 +36,7 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.uioverrides.DejankBinderTracker;
import com.android.launcher3.uioverrides.DisplayRotationListener;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
import com.android.launcher3.util.PackageManagerHelper;
@@ -65,7 +66,8 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mIsSafeModeEnabled = getPackageManager().isSafeMode();
+ mIsSafeModeEnabled = DejankBinderTracker.whitelistIpcs(() ->
+ getPackageManager().isSafeMode());
mRotationListener = new DisplayRotationListener(this, this::onDeviceRotationChanged);
// Update theme
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 17fbb79..aa975bd 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -133,7 +133,8 @@
String pkg = getIntentPackage(info.launchIntent);
if (!TextUtils.isEmpty(pkg)
- && !launcherApps.isPackageEnabledForProfile(pkg, info.user)) {
+ && !launcherApps.isPackageEnabledForProfile(pkg, info.user)
+ && !info.isActivity) {
if (DBG) {
Log.d(TAG, "Ignoring shortcut for absent package: " + info.launchIntent);
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 7bb618d..9326e95 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -27,6 +27,7 @@
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;
@@ -112,15 +113,17 @@
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.qsb.QsbContainerView;
-import com.android.launcher3.states.InternalStateHandler;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.touch.ItemClickHandler;
+import com.android.launcher3.uioverrides.DejankBinderTracker;
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;
@@ -149,6 +152,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;
@@ -158,14 +167,18 @@
import java.util.HashSet;
import java.util.List;
import java.util.function.Predicate;
+import java.util.function.Supplier;
/**
* Default launcher application.
*/
public class Launcher extends BaseDraggingActivity implements LauncherExterns,
Callbacks, LauncherProviderChangeListener, UserEventDelegate,
- InvariantDeviceProfile.OnIDPChangeListener {
+ 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;
@@ -293,8 +306,14 @@
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) {
+ DejankBinderTracker.allowBinderTrackingInTests();
RaceConditionTracker.onEvent(ON_CREATE_EVT, ENTER);
if (DEBUG_STRICT_MODE) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
@@ -343,7 +362,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.
@@ -389,6 +408,10 @@
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onCreate(savedInstanceState);
}
+ mOverlayManager = getDefaultOverlay();
+ PluginManagerWrapper.INSTANCE.get(this).addPluginListener(this,
+ OverlayPlugin.class, false /* allowedMultiple */);
+
mRotationHelper.initialize();
TraceHelper.endSection("Launcher-onCreate");
@@ -412,6 +435,39 @@
}
}
});
+ DejankBinderTracker.disallowBinderTrackingInTests();
+ }
+
+ 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
@@ -570,6 +626,7 @@
/**
* Call this after onCreate to set or clear overlay.
*/
+ @Override
public void setLauncherOverlay(LauncherOverlay overlay) {
if (overlay != null) {
overlay.setOverlayCallbacks(new LauncherOverlayCallbacksImpl());
@@ -577,18 +634,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().
@@ -787,9 +842,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
@@ -816,10 +868,6 @@
getString(R.string.derived_app_name)), Toast.LENGTH_SHORT).show();
}
}
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onRequestPermissionsResult(requestCode, permissions,
- grantResults);
- }
}
/**
@@ -878,9 +926,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);
@@ -896,13 +947,16 @@
@Override
protected void onStart() {
+ DejankBinderTracker.allowBinderTrackingInTests();
RaceConditionTracker.onEvent(ON_START_EVT, ENTER);
super.onStart();
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onStart();
+ if (!mDeferOverlayCallbacks) {
+ mOverlayManager.onActivityStarted(this);
}
+
mAppWidgetHost.setListenIfResumed(true);
RaceConditionTracker.onEvent(ON_START_EVT, EXIT);
+ DejankBinderTracker.disallowBinderTrackingInTests();
}
private void handleDeferredResume() {
@@ -942,15 +996,50 @@
} 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();
}
}
@@ -962,6 +1051,7 @@
@Override
protected void onResume() {
+ DejankBinderTracker.allowBinderTrackingInTests();
RaceConditionTracker.onEvent(ON_RESUME_EVT, ENTER);
TraceHelper.beginSection("ON_RESUME");
super.onResume();
@@ -979,12 +1069,15 @@
resumeCallbacks.clear();
}
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onResume();
+ if (mDeferOverlayCallbacks) {
+ scheduleDeferredCheck();
+ } else {
+ mOverlayManager.onActivityResumed(this);
}
TraceHelper.endSection("ON_RESUME");
RaceConditionTracker.onEvent(ON_RESUME_EVT, EXIT);
+ DejankBinderTracker.disallowBinderTrackingInTests();
}
@Override
@@ -996,8 +1089,9 @@
mDragController.cancelDrag();
mDragController.resetLastGestureUpTime();
mDropTargetBar.animateToVisibility(false);
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onPause();
+
+ if (!mDeferOverlayCallbacks) {
+ mOverlayManager.onActivityPaused(this);
}
}
@@ -1013,35 +1107,6 @@
mStateManager.onWindowFocusChanged();
}
- public interface LauncherOverlay {
-
- /**
- * Touch interaction leading to overscroll has begun
- */
- void onScrollInteractionBegin();
-
- /**
- * Touch interaction related to overscroll has ended
- */
- void onScrollInteractionEnd();
-
- /**
- * Scroll progress, between 0 and 100, when the user scrolls beyond the leftmost
- * screen (or in the case of RTL, the rightmost screen).
- */
- void onScrollChange(float progress, boolean rtl);
-
- /**
- * Called when the launcher is ready to use the overlay
- * @param callbacks A set of callbacks provided by Launcher in relation to the overlay
- */
- void setOverlayCallbacks(LauncherOverlayCallbacks callbacks);
- }
-
- public interface LauncherOverlayCallbacks {
- void onScrollChanged(float progress);
- }
-
class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks {
public void onScrollChanged(float progress) {
@@ -1299,19 +1364,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() {
@@ -1360,10 +1420,16 @@
return mModelWriter;
}
+ @Override
public SharedPreferences getSharedPrefs() {
return mSharedPrefs;
}
+ @Override
+ public SharedPreferences getDevicePrefs() {
+ return Utilities.getDevicePrefs(this);
+ }
+
public int getOrientation() {
return mOldConfig.orientation;
}
@@ -1381,8 +1447,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) {
@@ -1420,6 +1485,7 @@
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onHomeIntent(internalStateHandled);
}
+ mOverlayManager.hideOverlay(isStarted() && !isForceInvisible());
}
TraceHelper.endSection("NEW_INTENT");
@@ -1465,18 +1531,17 @@
}
super.onSaveInstanceState(outState);
-
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onSaveInstanceState(outState);
- }
+ mOverlayManager.onActivitySaveInstanceState(this, outState);
}
@Override
public void onDestroy() {
super.onDestroy();
+ ACTIVITY_TRACKER.onActivityDestroyed(this);
unregisterReceiver(mScreenOffReceiver);
mWorkspace.removeFolderListeners();
+ PluginManagerWrapper.INSTANCE.get(this).removePluginListener(this);
if (mCancelTouchController != null) {
mCancelTouchController.run();
@@ -1501,9 +1566,8 @@
TextKeyListener.getInstance().release();
clearPendingBinds();
LauncherAppState.getIDP(this).removeOnChangeListener(this);
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onDestroy();
- }
+
+ mOverlayManager.onActivityDestroyed(this);
}
public LauncherAccessibilityDelegate getAccessibilityDelegate() {
@@ -1749,9 +1813,6 @@
if (finishAutoCancelActionMode()) {
return;
}
- if (mLauncherCallbacks != null && mLauncherCallbacks.handleBackPressed()) {
- return;
- }
if (mDragController.isDragging()) {
mDragController.cancelDrag();
@@ -1876,9 +1937,6 @@
// This clears all widget bitmaps from the widget tray
// TODO(hyunyoungs)
}
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onTrimMemory(level);
- }
UiFactory.onTrimMemory(this, level);
}
@@ -2498,6 +2556,7 @@
if (mLauncherCallbacks != null) {
mLauncherCallbacks.dump(prefix, fd, writer, args);
}
+ mOverlayManager.dump(prefix, writer);
}
@Override
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index d70abc2..efb3d36 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -17,6 +17,7 @@
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;
@@ -28,12 +29,13 @@
import android.util.Log;
import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.notification.NotificationListener;
+import com.android.launcher3.pm.InstallSessionTracker;
+import com.android.launcher3.pm.PackageInstallerCompat;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.SecureSettingsObserver;
@@ -44,7 +46,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 +56,8 @@
private final InvariantDeviceProfile mInvariantDeviceProfile;
private final SecureSettingsObserver mNotificationDotsObserver;
+ private final InstallSessionTracker mInstallSessionTracker;
+
public static LauncherAppState getInstance(final Context context) {
return INSTANCE.get(context);
}
@@ -102,6 +106,9 @@
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 {
@@ -141,7 +148,7 @@
mContext.unregisterReceiver(mModel);
final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext);
launcherApps.removeOnAppsChangedCallback(mModel);
- PackageInstallerCompat.getInstance(mContext).onStop();
+ mInstallSessionTracker.unregister();
if (mNotificationDotsObserver != null) {
mNotificationDotsObserver.unregister();
}
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 c0cf135..f360325 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -24,8 +24,8 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+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;
@@ -34,8 +34,8 @@
import androidx.annotation.Nullable;
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;
@@ -75,7 +77,7 @@
* for the Launcher.
*/
public class LauncherModel extends BroadcastReceiver
- implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
+ implements LauncherAppsCompat.OnAppsChangedCallbackCompat, InstallSessionTracker.Callback {
private static final boolean DEBUG_RECEIVER = false;
static final String TAG = "Launcher.Model";
@@ -127,20 +129,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) {
- HashSet<String> packages = new HashSet<>();
- packages.add(packageName);
- enqueueModelUpdateTask(new CacheDataUpdatedTask(
- CacheDataUpdatedTask.OP_SESSION_UPDATE, Process.myUserHandle(), packages));
- }
-
/**
* Adds the provided items to the workspace.
*/
@@ -173,30 +161,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);
@@ -392,16 +356,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;
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 6081300..d78c1b3 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -223,7 +223,6 @@
mOpenHelper.onAddOrDeleteOp(db);
uri = ContentUris.withAppendedId(uri, rowId);
- notifyListeners();
reloadLauncherIfExternal();
return uri;
}
@@ -283,7 +282,6 @@
t.commit();
}
- notifyListeners();
reloadLauncherIfExternal();
return values.length;
}
@@ -329,7 +327,6 @@
int count = db.delete(args.table, args.where, args.args);
if (count > 0) {
mOpenHelper.onAddOrDeleteOp(db);
- notifyListeners();
reloadLauncherIfExternal();
}
return count;
@@ -343,8 +340,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 +433,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());
}
@@ -1042,7 +1030,6 @@
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;
@@ -1051,9 +1038,6 @@
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;
diff --git a/src/com/android/launcher3/LauncherProviderChangeListener.java b/src/com/android/launcher3/LauncherProviderChangeListener.java
index 0243088..6afe885 100644
--- a/src/com/android/launcher3/LauncherProviderChangeListener.java
+++ b/src/com/android/launcher3/LauncherProviderChangeListener.java
@@ -7,7 +7,5 @@
*/
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/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/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index 6853bf6..55402dd 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;
@@ -38,13 +40,11 @@
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.
*/
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 003bcc1..abf6cbd 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -30,6 +30,7 @@
import android.os.UserHandle;
import android.util.Log;
import android.util.LongSparseArray;
+import android.util.Pair;
import androidx.annotation.Nullable;
@@ -289,14 +290,25 @@
return null;
}
- private Bitmap generatePreview(BaseActivity launcher, WidgetItem item, Bitmap recycle,
+ /**
+ * Returns generatedPreview for a widget and if the preview should be saved in persistent
+ * storage.
+ * @param launcher
+ * @param item
+ * @param recycle
+ * @param previewWidth
+ * @param previewHeight
+ * @return Pair<Bitmap, Boolean>
+ */
+ private Pair<Bitmap, Boolean> generatePreview(BaseActivity launcher, WidgetItem item,
+ Bitmap recycle,
int previewWidth, int previewHeight) {
if (item.widgetInfo != null) {
return generateWidgetPreview(launcher, item.widgetInfo,
previewWidth, recycle, null);
} else {
- return generateShortcutPreview(launcher, item.activityInfo,
- previewWidth, previewHeight, recycle);
+ return new Pair<>(generateShortcutPreview(launcher, item.activityInfo,
+ previewWidth, previewHeight, recycle), false);
}
}
@@ -309,9 +321,10 @@
* @param maxPreviewWidth width of the preview on either workspace or tray
* @param preview bitmap that can be recycled
* @param preScaledWidthOut return the width of the returned bitmap
- * @return
+ * @return Pair<Bitmap (the preview) , Boolean (should be stored in db)>
*/
- public Bitmap generateWidgetPreview(BaseActivity launcher, LauncherAppWidgetProviderInfo info,
+ public Pair<Bitmap, Boolean> generateWidgetPreview(BaseActivity launcher,
+ LauncherAppWidgetProviderInfo info,
int maxPreviewWidth, Bitmap preview, int[] preScaledWidthOut) {
// Load the preview image if possible
if (maxPreviewWidth < 0) maxPreviewWidth = Integer.MAX_VALUE;
@@ -341,6 +354,8 @@
int previewWidth;
int previewHeight;
+ boolean savePreviewImage = widgetPreviewExists || info.previewImage == 0;
+
if (widgetPreviewExists && drawable.getIntrinsicWidth() > 0
&& drawable.getIntrinsicHeight() > 0) {
previewWidth = drawable.getIntrinsicWidth();
@@ -427,10 +442,12 @@
icon.setBounds(hoffset, yoffset, hoffset + iconSize, yoffset + iconSize);
icon.draw(c);
}
- } catch (Resources.NotFoundException e) { }
+ } catch (Resources.NotFoundException e) {
+ savePreviewImage = false;
+ }
c.setBitmap(null);
}
- return preview;
+ return new Pair<>(preview, savePreviewImage);
}
private RectF drawBoxWithShadow(Canvas c, int width, int height) {
@@ -533,6 +550,8 @@
@Thunk long[] mVersions;
@Thunk Bitmap mBitmapToRecycle;
+ private boolean mSaveToDB = false;
+
PreviewLoadTask(WidgetCacheKey key, WidgetItem info, int previewWidth,
int previewHeight, WidgetCell caller) {
mKey = key;
@@ -588,7 +607,10 @@
: null;
// it's not in the db... we need to generate it
- preview = generatePreview(mActivity, mInfo, unusedBitmap, mPreviewWidth, mPreviewHeight);
+ Pair<Bitmap, Boolean> pair = generatePreview(mActivity, mInfo, unusedBitmap,
+ mPreviewWidth, mPreviewHeight);
+ preview = pair.first;
+ this.mSaveToDB = pair.second;
}
return preview;
}
@@ -602,7 +624,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 474c59d..eca5d12 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 &&
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
index cdb5c4d..047346d 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -21,7 +21,6 @@
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
-import android.content.pm.PackageInstaller;
import android.content.pm.ShortcutInfo;
import android.graphics.Rect;
import android.os.Bundle;
@@ -59,9 +58,7 @@
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) {
+ if (Utilities.ATLEAST_OREO) {
sInstance = new LauncherAppsCompatVO(context.getApplicationContext());
} else {
sInstance = new LauncherAppsCompatVL(context.getApplicationContext());
@@ -88,6 +85,4 @@
UserHandle user);
public abstract List<ShortcutConfigActivityInfo> getCustomShortcutActivityList(
@Nullable PackageUserKey packageUser);
-
- public abstract List<PackageInstaller.SessionInfo> getAllPackageInstallerSessions();
}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
index 1885d8f..f1b9756 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
@@ -22,7 +22,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
-import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
@@ -33,6 +32,9 @@
import android.util.ArrayMap;
import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVL;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.PackageUserKey;
@@ -41,9 +43,6 @@
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;
@@ -207,10 +206,5 @@
}
return result;
}
-
- @Override
- public List<PackageInstaller.SessionInfo> getAllPackageInstallerSessions() {
- return mContext.getPackageManager().getPackageInstaller().getAllSessions();
- }
}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVQ.java b/src/com/android/launcher3/compat/LauncherAppsCompatVQ.java
deleted file mode 100644
index 0a1811e..0000000
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVQ.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.compat;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageInstaller;
-
-import java.util.List;
-
-@TargetApi(29)
-public class LauncherAppsCompatVQ extends LauncherAppsCompatVO {
-
- LauncherAppsCompatVQ(Context context) {
- super(context);
- }
-
- public List<PackageInstaller.SessionInfo> getAllPackageInstallerSessions() {
- return mLauncherApps.getAllPackageInstallerSessions();
- }
-}
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java
deleted file mode 100644
index 55df98b..0000000
--- a/src/com/android/launcher3/compat/PackageInstallerCompat.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.compat;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageInstaller.SessionInfo;
-import android.os.Process;
-import android.os.UserHandle;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.Utilities;
-import com.android.launcher3.util.PackageUserKey;
-
-public abstract class PackageInstallerCompat {
-
- // Set<String> of session ids of promise icons that have been added to the home screen
- // as FLAG_PROMISE_NEW_INSTALLS.
- protected static final String PROMISE_ICON_IDS = "promise_icon_ids";
-
- public static final int STATUS_INSTALLED = 0;
- public static final int STATUS_INSTALLING = 1;
- public static final int STATUS_FAILED = 2;
-
- private static final Object sInstanceLock = new Object();
- private static PackageInstallerCompat sInstance;
-
- public static PackageInstallerCompat getInstance(Context context) {
- synchronized (sInstanceLock) {
- if (sInstance == null) {
- sInstance = new PackageInstallerCompatVL(context);
- }
- return sInstance;
- }
- }
-
- public static UserHandle getUserHandle(SessionInfo info) {
- return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle();
- }
-
- /**
- * @return a map of active installs to their progress
- */
- public abstract HashMap<PackageUserKey, SessionInfo> updateAndGetActiveSessionCache();
-
- /**
- * @return an active SessionInfo for {@param pkg} or null if none exists.
- */
- public abstract SessionInfo getActiveSessionInfo(UserHandle user, String pkg);
-
- public abstract void onStop();
-
- public static final class PackageInstallInfo {
- public final ComponentName componentName;
- public final String packageName;
- public final int state;
- public final int progress;
- public final UserHandle user;
-
- private PackageInstallInfo(@NonNull SessionInfo info) {
- this.state = STATUS_INSTALLING;
- this.packageName = info.getAppPackageName();
- this.componentName = new ComponentName(packageName, "");
- this.progress = (int) (info.getProgress() * 100f);
- this.user = getUserHandle(info);
- }
-
- public PackageInstallInfo(String packageName, int state, int progress, UserHandle user) {
- this.state = state;
- this.packageName = packageName;
- this.componentName = new ComponentName(packageName, "");
- this.progress = progress;
- this.user = user;
- }
-
- public static PackageInstallInfo fromInstallingState(SessionInfo info) {
- return new PackageInstallInfo(info);
- }
-
- public static PackageInstallInfo fromState(int state, String packageName, UserHandle user) {
- return new PackageInstallInfo(packageName, state, 0 /* progress */, user);
- }
-
- }
-
- public abstract List<SessionInfo> getAllVerifiedSessions();
-
- /**
- * Returns true if a promise icon was already added to the home screen for {@param sessionId}.
- * Applicable only for icons with flag FLAG_PROMISE_NEW_INSTALLS.
- */
- public abstract boolean promiseIconAddedForId(int sessionId);
-
- /**
- * Applicable only for icons with flag FLAG_PROMISE_NEW_INSTALLS.
- */
- public abstract void removePromiseIconId(int sessionId);
-}
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
deleted file mode 100644
index 9c57e8a..0000000
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ /dev/null
@@ -1,288 +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.LauncherModel;
-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();
- mInstaller.registerSessionCallback(mCallback, MODEL_EXECUTOR.getHandler());
- mLauncherApps = LauncherAppsCompat.getInstance(context);
- 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() {
- mInstaller.unregisterSessionCallback(mCallback);
- }
-
- @Thunk void sendUpdate(PackageInstallInfo info) {
- LauncherAppState app = LauncherAppState.getInstanceNoCreate();
- if (app != null) {
- app.getModel().setPackageState(info);
- }
- }
-
- /**
- * Add a promise app icon to the workspace iff:
- * - The settings for it are enabled
- * - The user installed the app
- * - There is an app icon and label (For apps with no launching activity, no icon is provided).
- * - The app is not already installed
- * - A promise icon for the session has not already been created
- */
- private void tryQueuePromiseAppIcon(SessionInfo sessionInfo) {
- if (Utilities.ATLEAST_OREO && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
- && SessionCommitReceiver.isEnabled(mAppContext)
- && verify(sessionInfo) != null
- && sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER
- && sessionInfo.getAppIcon() != null
- && !TextUtils.isEmpty(sessionInfo.getAppLabel())
- && !mPromiseIconIds.contains(sessionInfo.getSessionId())
- && mLauncherApps.getApplicationInfo(sessionInfo.getAppPackageName(), 0,
- getUserHandle(sessionInfo)) == null) {
- SessionCommitReceiver.queuePromiseAppIconAddition(mAppContext, sessionInfo);
- mPromiseIconIds.add(sessionInfo.getSessionId());
- updatePromiseIconPrefs();
- }
- }
-
- private final SessionCallback mCallback = new SessionCallback() {
-
- @Override
- public void onCreated(int sessionId) {
- SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId);
- if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS && sessionInfo != null) {
- LauncherAppState app = LauncherAppState.getInstanceNoCreate();
- if (app != null) {
- app.getModel().onInstallSessionCreated(
- PackageInstallInfo.fromInstallingState(sessionInfo));
- }
- }
-
- tryQueuePromiseAppIcon(sessionInfo);
- }
-
- @Override
- public void onFinished(int sessionId, boolean success) {
- // For a finished session, we can't get the session info. So use the
- // packageName from our local cache.
- PackageUserKey key = mActiveSessions.get(sessionId);
- mActiveSessions.remove(sessionId);
-
- if (key != null && key.mPackageName != null) {
- String packageName = key.mPackageName;
- sendUpdate(PackageInstallInfo.fromState(success ? STATUS_INSTALLED : STATUS_FAILED,
- packageName, key.mUser));
-
- if (!success && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
- && mPromiseIconIds.contains(sessionId)) {
- LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
- if (appState != null) {
- appState.getModel().onSessionFailure(packageName, key.mUser);
- }
- // If it is successful, the id is removed in the the package added flow.
- removePromiseIconId(sessionId);
- }
- }
- }
-
- @Override
- public void onProgressChanged(int sessionId, float progress) {
- SessionInfo session = verify(mInstaller.getSessionInfo(sessionId));
- if (session != null && session.getAppPackageName() != null) {
- sendUpdate(PackageInstallInfo.fromInstallingState(session));
- }
- }
-
- @Override
- public void onActiveChanged(int sessionId, boolean active) { }
-
- @Override
- public void onBadgingChanged(int sessionId) {
- SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId);
- if (sessionInfo != null) {
- tryQueuePromiseAppIcon(sessionInfo);
- }
- }
-
- private SessionInfo pushSessionDisplayToLauncher(int sessionId) {
- SessionInfo session = verify(mInstaller.getSessionInfo(sessionId));
- if (session != null && session.getAppPackageName() != null) {
- mActiveSessions.put(session.getSessionId(),
- new PackageUserKey(session.getAppPackageName(), getUserHandle(session)));
- addSessionInfoToCache(session, getUserHandle(session));
- LauncherAppState app = LauncherAppState.getInstanceNoCreate();
- if (app != null) {
- app.getModel().updateSessionDisplayInfo(session.getAppPackageName());
- }
- return session;
- }
- return null;
- }
- };
-
- private PackageInstaller.SessionInfo verify(PackageInstaller.SessionInfo sessionInfo) {
- if (sessionInfo == null
- || sessionInfo.getInstallerPackageName() == null
- || TextUtils.isEmpty(sessionInfo.getAppPackageName())) {
- return null;
- }
- String pkg = sessionInfo.getInstallerPackageName();
- synchronized (mSessionVerifiedMap) {
- if (!mSessionVerifiedMap.containsKey(pkg)) {
- LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mAppContext);
- boolean hasSystemFlag = launcherApps.getApplicationInfo(pkg,
- ApplicationInfo.FLAG_SYSTEM, getUserHandle(sessionInfo)) != null;
- mSessionVerifiedMap.put(pkg, DEBUG || hasSystemFlag);
- }
- }
- return mSessionVerifiedMap.get(pkg) ? sessionInfo : null;
- }
-
- @Override
- public List<SessionInfo> getAllVerifiedSessions() {
- List<SessionInfo> list = new ArrayList<>(Utilities.ATLEAST_Q
- ? mLauncherApps.getAllPackageInstallerSessions()
- : mInstaller.getAllSessions());
- Iterator<SessionInfo> it = list.iterator();
- while (it.hasNext()) {
- if (verify(it.next()) == null) {
- it.remove();
- }
- }
- return list;
- }
-
- @Override
- public boolean promiseIconAddedForId(int sessionId) {
- return mPromiseIconIds.contains(sessionId);
- }
-
- @Override
- public void removePromiseIconId(int sessionId) {
- if (mPromiseIconIds.contains(sessionId)) {
- mPromiseIconIds.getArray().removeValue(sessionId);
- updatePromiseIconPrefs();
- }
- }
-
- private void updatePromiseIconPrefs() {
- getPrefs(mAppContext).edit()
- .putString(PROMISE_ICON_IDS, mPromiseIconIds.getArray().toConcatString())
- .apply();
- }
-}
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
similarity index 83%
rename from src/com/android/launcher3/config/BaseFlags.java
rename to src/com/android/launcher3/config/FeatureFlags.java
index 7c8fe16..c502dd7 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");
@@ -130,13 +132,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());
@@ -236,14 +238,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..b8981b6 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -45,6 +45,7 @@
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;
@@ -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/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 9c46260..b1818a0 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -24,6 +24,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;
@@ -50,6 +51,7 @@
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.util.InstantAppResolver;
+import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
import java.util.function.Supplier;
@@ -235,6 +237,10 @@
return mIconProvider.getIcon(info, mIconDpi, flattenDrawable);
}
+ public void updateSessionCache(PackageUserKey key, PackageInstaller.SessionInfo info) {
+ cachePackageInstallInfo(key.mPackageName, key.mUser, info.getAppIcon(), info.getAppLabel());
+ }
+
@Override
protected String getIconSystemState(String packageName) {
return mIconProvider.getSystemStateForPackage(mSystemState, packageName)
diff --git a/src/com/android/launcher3/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 dfd5a70..844a2a6 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -29,11 +29,11 @@
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;
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index 3873a17..1e1df88 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -28,14 +28,16 @@
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.SafeCloseable;
@@ -46,9 +48,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,8 +109,7 @@
mDataChanged = true;
}
- public void addPromiseApp(Context context,
- PackageInstallerCompat.PackageInstallInfo installInfo) {
+ public void addPromiseApp(Context context, PackageInstallInfo installInfo) {
ApplicationInfo applicationInfo = LauncherAppsCompat.getInstance(context)
.getApplicationInfo(installInfo.packageName, 0, installInfo.user);
// only if not yet installed
@@ -134,10 +132,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);
}
}
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/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 50e1d56..97cf846 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -53,7 +53,6 @@
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;
@@ -65,6 +64,8 @@
import com.android.launcher3.icons.LauncherIcons;
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;
@@ -286,7 +287,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);
@@ -576,7 +579,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 +842,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));
}
}
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..7ea310c 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -107,7 +107,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);
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..520c207
--- /dev/null
+++ b/src/com/android/launcher3/pm/PackageInstallerCompat.java
@@ -0,0 +1,220 @@
+/*
+ * 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.compat.LauncherAppsCompat;
+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.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)) {
+ 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;
+ }
+
+ 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())
+ && LauncherAppsCompat.getInstance(mAppContext).getApplicationInfo(
+ sessionInfo.getAppPackageName(), 0, getUserHandle(sessionInfo)) == 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/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 d3ec83b..619293b 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -29,6 +29,7 @@
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;
@@ -55,8 +56,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) {
@@ -83,7 +83,7 @@
}
case TestProtocol.REQUEST_IS_LAUNCHER_INITIALIZED: {
- response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, true);
+ response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, isLauncherInitialized());
break;
}
@@ -121,6 +121,21 @@
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 e) {
+ throw new RuntimeException(e);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ break;
+ }
+
case TestProtocol.REQUEST_TOTAL_PSS_KB: {
Debug.MemoryInfo mem = new Debug.MemoryInfo();
Debug.getMemoryInfo(mem);
@@ -153,4 +168,9 @@
}
return response;
}
+
+ protected boolean isLauncherInitialized() {
+ final LauncherModel model = LauncherAppState.getInstance(mContext).getModel();
+ return model.getCallback() == null || model.isModelLoaded();
+ }
}
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 62bb564..ac080c2 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -20,13 +20,10 @@
* Protocol for custom accessibility events for communication with UI Automation tests.
*/
public final class TestProtocol {
- public static final String GET_SCROLL_MESSAGE = "TAPL_GET_SCROLL";
- public static final String SCROLL_Y_FIELD = "scrollY";
public static final String STATE_FIELD = "state";
public static final String SWITCHED_TO_STATE_MESSAGE = "TAPL_SWITCHED_TO_STATE";
public static final String SCROLL_FINISHED_MESSAGE = "TAPL_SCROLL_FINISHED";
public static final String PAUSE_DETECTED_MESSAGE = "TAPL_PAUSE_DETECTED";
- public static final String RESPONSE_MESSAGE_POSTFIX = "_RESPONSE";
public static final int NORMAL_STATE_ORDINAL = 0;
public static final int SPRING_LOADED_STATE_ORDINAL = 1;
public static final int OVERVIEW_STATE_ORDINAL = 2;
@@ -71,6 +68,7 @@
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";
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/util/ActivityTracker.java b/src/com/android/launcher3/util/ActivityTracker.java
new file mode 100644
index 0000000..e85a4e8
--- /dev/null
+++ b/src/com/android/launcher3/util/ActivityTracker.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.util;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseActivity;
+
+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();
+ }
+ }
+
+ public void schedule(SchedulerCallback<? extends T> callback) {
+ synchronized (this) {
+ mPendingCallback = new WeakReference<>((SchedulerCallback<T>) callback);
+ }
+ MAIN_EXECUTOR.execute(this);
+ }
+
+ @Override
+ public void run() {
+ T activity = mCurrentActivity.get();
+ if (activity != null) {
+ initIfPending(activity, activity.isStarted());
+ }
+ }
+
+ public boolean initIfPending(T activity, boolean alreadyOnHome) {
+ SchedulerCallback<T> pendingCallback = mPendingCallback.get();
+ if (pendingCallback != null) {
+ if (!pendingCallback.init(activity, alreadyOnHome)) {
+ clearReference(pendingCallback);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public boolean clearReference(SchedulerCallback<? extends T> handler) {
+ synchronized (this) {
+ if (mPendingCallback.get() == handler) {
+ mPendingCallback.clear();
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public boolean hasPending() {
+ return mPendingCallback.get() != null;
+ }
+
+ public boolean handleCreate(T activity) {
+ mCurrentActivity = new WeakReference<>(activity);
+ return handleIntent(activity, activity.getIntent(), false, false);
+ }
+
+ public boolean handleNewIntent(T activity, Intent intent) {
+ return handleIntent(activity, intent, activity.isStarted(), true);
+ }
+
+ private boolean handleIntent(
+ T activity, Intent intent, boolean alreadyOnHome, boolean explicitIntent) {
+ boolean result = false;
+ if (intent != null && intent.getExtras() != null) {
+ IBinder stateBinder = intent.getExtras().getBinder(EXTRA_SCHEDULER_CALLBACK);
+ if (stateBinder instanceof ObjectWrapper) {
+ SchedulerCallback<T> handler =
+ ((ObjectWrapper<SchedulerCallback>) stateBinder).get();
+ if (!handler.init(activity, alreadyOnHome)) {
+ intent.getExtras().remove(EXTRA_SCHEDULER_CALLBACK);
+ }
+ result = true;
+ }
+ }
+ if (!result && !explicitIntent) {
+ result = initIfPending(activity, alreadyOnHome);
+ }
+ return result;
+ }
+
+ public interface SchedulerCallback<T extends BaseActivity> {
+
+ boolean init(T activity, boolean alreadyOnHome);
+
+ default Intent addToIntent(Intent intent) {
+ Bundle extras = new Bundle();
+ extras.putBinder(EXTRA_SCHEDULER_CALLBACK, ObjectWrapper.wrap(this));
+ intent.putExtras(extras);
+ return intent;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
index fe9c2c4..cf4e8c7 100644
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -22,6 +22,7 @@
import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.uioverrides.DejankBinderTracker;
import com.android.launcher3.util.ResourceBasedOverride.Overrides;
import java.util.concurrent.ExecutionException;
@@ -41,7 +42,8 @@
public T get(Context context) {
if (mValue == null) {
if (Looper.myLooper() == Looper.getMainLooper()) {
- mValue = mProvider.get(context.getApplicationContext());
+ mValue = DejankBinderTracker.whitelistIpcs(() ->
+ 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 96%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/ObjectWrapper.java
rename to src/com/android/launcher3/util/ObjectWrapper.java
index abfe3ad..b692431 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ObjectWrapper.java
+++ b/src/com/android/launcher3/util/ObjectWrapper.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.quickstep.util;
+package com.android.launcher3.util;
import android.os.Binder;
import android.os.IBinder;
diff --git a/src/com/android/launcher3/util/ShortcutUtil.java b/src/com/android/launcher3/util/ShortcutUtil.java
index af99713..49c97da 100644
--- a/src/com/android/launcher3/util/ShortcutUtil.java
+++ b/src/com/android/launcher3/util/ShortcutUtil.java
@@ -19,7 +19,7 @@
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.model.WidgetsModel;
import com.android.launcher3.shortcuts.ShortcutKey;
public class ShortcutUtil {
@@ -64,7 +64,7 @@
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 +76,4 @@
&& info.container != ItemInfo.NO_ID
&& info instanceof WorkspaceItemInfo;
}
-}
\ No newline at end of file
+}
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/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/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/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..0f6ad27 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -11,8 +11,6 @@
import android.util.Log;
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;
@@ -22,6 +20,8 @@
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.util.MultiHashMap;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
@@ -46,6 +46,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;
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/DejankBinderTracker.java b/src_ui_overrides/com/android/launcher3/uioverrides/DejankBinderTracker.java
new file mode 100644
index 0000000..47f6ac6
--- /dev/null
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/DejankBinderTracker.java
@@ -0,0 +1,57 @@
+/**
+ * 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 android.os.IBinder;
+
+import java.util.function.BiConsumer;
+import java.util.function.Supplier;
+
+/**
+ * A binder proxy transaction listener for tracking non-whitelisted binder calls.
+ */
+public class DejankBinderTracker {
+ public static void whitelistIpcs(Runnable runnable) {}
+
+ public static <T> T whitelistIpcs(Supplier<T> supplier) {
+ return null;
+ }
+
+ public static void allowBinderTrackingInTests() {}
+
+ public static void disallowBinderTrackingInTests() {}
+
+ public DejankBinderTracker(BiConsumer<String, Integer> unexpectedTransactionCallback) { }
+
+ public void startTracking() {}
+
+ public void stopTracking() {}
+
+ public Object onTransactStarted(IBinder binder, int transactionCode, int flags) {
+ return null;
+ }
+
+ public Object onTransactStarted(IBinder binder, int transactionCode) {
+ return null;
+ }
+
+ public void onTransactEnded(Object session) {}
+
+ public static boolean isMainThread() {
+ return true;
+ }
+}
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/tests/Android.mk b/tests/Android.mk
index 02ead4e..b5c1dae 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -30,13 +30,14 @@
LOCAL_STATIC_JAVA_LIBRARIES += SystemUISharedLib
LOCAL_SRC_FILES := $(call all-java-files-under, tapl) \
+ ../quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java \
../src/com/android/launcher3/ResourceUtils.java \
../src/com/android/launcher3/util/SecureSettingsObserver.java \
../src/com/android/launcher3/testing/TestProtocol.java
endif
LOCAL_MODULE := ub-launcher-aosp-tapl
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index c6f55a7..0b74dc4 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -73,8 +73,12 @@
</intent-filter>
</activity>
+ <service
+ android:name="com.android.launcher3.testcomponent.ListViewService"
+ android:permission="android.permission.BIND_REMOTEVIEWS" />
+
<provider
- android:name="com.android.launcher3.testcomponent.TestCommandReceiver"
+ android:name="com.android.launcher3.testcomponent.TestCommandProvider"
android:authorities="${packageName}.commands"
android:exported="true"/>
diff --git a/tests/res/layout/test_layout_widget_list.xml b/tests/res/layout/test_layout_widget_list.xml
new file mode 100644
index 0000000..0152040
--- /dev/null
+++ b/tests/res/layout/test_layout_widget_list.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#FFFFFF">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="#FF0000FF"
+ android:id="@android:id/text1"
+ android:padding="10dp" />
+
+ <ListView
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:id="@android:id/list" />
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
index efbd9c9..7ef946d 100644
--- a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
+++ b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
@@ -48,7 +48,6 @@
public void setUp() throws Exception {
super.setUp();
mDevice.pressHome();
- waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
mSessionId = -1;
}
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 0f50009..63657dd 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -17,6 +17,7 @@
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;
@@ -27,9 +28,11 @@
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.PackageManager;
import android.os.Process;
@@ -42,6 +45,7 @@
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;
@@ -55,6 +59,7 @@
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;
@@ -228,6 +233,35 @@
}
/**
+ * 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();
+ waitForModelLoaded();
+ }
+
+ /**
* Runs the callback on the UI thread and returns the result.
*/
protected <T> T getOnUiThread(final Callable<T> callback) {
@@ -344,14 +378,14 @@
}
}
- protected void startAppFast(String packageName) {
+ public static void startAppFast(String packageName) {
startIntent(
getInstrumentation().getContext().getPackageManager().getLaunchIntentForPackage(
packageName),
By.pkg(packageName).depth(0));
}
- protected void startTestActivity(int activityNumber) {
+ public static void startTestActivity(int activityNumber) {
final String packageName = getAppPackageName();
final Intent intent = getInstrumentation().getContext().getPackageManager().
getLaunchIntentForPackage(packageName);
@@ -360,19 +394,25 @@
startIntent(intent, By.pkg(packageName).text("TestActivity" + activityNumber));
}
- private void startIntent(Intent intent, BySelector selector) {
+ private static void startIntent(Intent intent, BySelector selector) {
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
getInstrumentation().getTargetContext().startActivity(intent);
assertTrue("App didn't start: " + selector,
- mDevice.wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT));
+ UiDevice.getInstance(getInstrumentation())
+ .wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT));
}
- public static String resolveSystemApp(String category) {
+ public static ActivityInfo resolveSystemAppInfo(String category) {
return getInstrumentation().getContext().getPackageManager().resolveActivity(
new Intent(Intent.ACTION_MAIN).addCategory(category),
PackageManager.MATCH_SYSTEM_ONLY).
- activityInfo.packageName;
+ activityInfo;
+ }
+
+
+ public static String resolveSystemApp(String category) {
+ return resolveSystemAppInfo(category).packageName;
}
protected void closeLauncherActivity() {
diff --git a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
index a76b4a4..1d89d6e 100644
--- a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
+++ b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
@@ -30,7 +30,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.testcomponent.TestCommandReceiver;
+import com.android.launcher3.testcomponent.TestCommandProvider;
import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.rule.ShellCommandRule;
@@ -63,7 +63,7 @@
PackageManager pm = mTargetContext.getPackageManager();
ProviderInfo pi = pm.getProviderInfo(new ComponentName(mContext,
- TestCommandReceiver.class), 0);
+ TestCommandProvider.class), 0);
mAuthority = pi.authority;
}
@@ -73,7 +73,6 @@
// Launch the home activity
mDevice.pressHome();
- waitForModelLoaded();
mLauncher.getWorkspace().getHotseatAppIcon(getSettingsApp().getLabel().toString());
}
@@ -89,7 +88,6 @@
// Launch the home activity
mDevice.pressHome();
- waitForModelLoaded();
// Verify widget present
assertTrue("Widget is not present",
@@ -106,7 +104,6 @@
// Launch the home activity
mDevice.pressHome();
- waitForModelLoaded();
mLauncher.getWorkspace().getHotseatFolder("Folder: Copy");
}
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index e6348d9..3d691da 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -15,7 +15,9 @@
*/
package com.android.launcher3.ui.widget;
-import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
+import static com.android.launcher3.widget.WidgetHostViewLoader.getDefaultOptionsForWidget;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -25,6 +27,7 @@
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
@@ -39,15 +42,12 @@
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.pm.PackageInstallerCompat;
import com.android.launcher3.tapl.Workspace;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TestViewHelpers;
-import com.android.launcher3.util.ContentWriter;
-import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.rule.ShellCommandRule;
import com.android.launcher3.widget.PendingAddWidgetInfo;
-import com.android.launcher3.widget.WidgetHostViewLoader;
import org.junit.After;
import org.junit.Before;
@@ -57,7 +57,6 @@
import java.util.HashSet;
import java.util.Set;
-import java.util.function.Consumer;
/**
* Tests for bind widget flow.
@@ -72,7 +71,6 @@
public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
private ContentResolver mResolver;
- private AppWidgetManagerCompat mWidgetManager;
// Objects created during test, which should be cleaned up in the end.
private Cursor mCursor;
@@ -85,7 +83,6 @@
super.setUp();
mResolver = mTargetContext.getContentResolver();
- mWidgetManager = AppWidgetManagerCompat.getInstance(mTargetContext);
// Clear all existing data
LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
@@ -108,7 +105,7 @@
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, true);
LauncherAppWidgetInfo item = createWidgetInfo(info, true);
- setupContents(item);
+ addItemToScreen(item);
verifyWidgetPresent(info);
}
@@ -117,7 +114,7 @@
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
LauncherAppWidgetInfo item = createWidgetInfo(info, true);
- setupContents(item);
+ addItemToScreen(item);
verifyWidgetPresent(info);
}
@@ -127,7 +124,7 @@
LauncherAppWidgetInfo item = createWidgetInfo(info, false);
item.appWidgetId = -33;
- setupContents(item);
+ addItemToScreen(item);
final Workspace workspace = mLauncher.getWorkspace();
// Item deleted from db
@@ -148,7 +145,7 @@
LauncherAppWidgetInfo item = createWidgetInfo(info, false);
item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID;
- setupContents(item);
+ addItemToScreen(item);
verifyWidgetPresent(info);
}
@@ -161,7 +158,7 @@
LauncherAppWidgetInfo item = createWidgetInfo(info, false);
item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID;
- setupContents(item);
+ addItemToScreen(item);
verifyPendingWidgetPresent();
// Item deleted from db
@@ -183,7 +180,7 @@
item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
| LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
- setupContents(item);
+ addItemToScreen(item);
assertTrue("Pending widget exists",
mLauncher.getWorkspace().tryGetPendingWidget(0) == null);
@@ -202,7 +199,7 @@
| LauncherAppWidgetInfo.FLAG_RESTORE_STARTED
| LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
- setupContents(item);
+ addItemToScreen(item);
verifyPendingWidgetPresent();
// Verify item still exists in db
@@ -230,7 +227,7 @@
PackageInstaller installer = mTargetContext.getPackageManager().getPackageInstaller();
mSessionId = installer.createSession(params);
- setupContents(item);
+ addItemToScreen(item);
verifyPendingWidgetPresent();
// Verify item still exists in db
@@ -245,35 +242,6 @@
& LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
}
- /**
- * Adds {@param item} on the homescreen on the 0th screen at 0,0, and verifies that the
- * widget class is displayed on the homescreen.
- */
- private void setupContents(LauncherAppWidgetInfo item) {
- int screenId = FIRST_SCREEN_ID;
- // Update the screen id counter for the provider.
- LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
-
- if (screenId > FIRST_SCREEN_ID) {
- screenId = FIRST_SCREEN_ID;
- }
-
- // Insert the item
- ContentWriter writer = new ContentWriter(mTargetContext);
- item.id = LauncherSettings.Settings.call(
- mResolver, LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
- .getInt(LauncherSettings.Settings.EXTRA_VALUE);
- item.screenId = screenId;
- item.onAddToDatabase(writer);
- writer.put(LauncherSettings.Favorites._ID, item.id);
- mResolver.insert(LauncherSettings.Favorites.CONTENT_URI, writer.getValues(mTargetContext));
- resetLoaderState();
-
- // Launch the home activity
- mDevice.pressHome();
- waitForModelLoaded();
- }
-
private void verifyWidgetPresent(LauncherAppWidgetProviderInfo info) {
assertTrue("Widget is not present",
mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
@@ -289,8 +257,10 @@
* @param bindWidget if true the info is bound and a valid widgetId is assigned to
* the LauncherAppWidgetInfo
*/
- private LauncherAppWidgetInfo createWidgetInfo(
+ public static LauncherAppWidgetInfo createWidgetInfo(
LauncherAppWidgetProviderInfo info, boolean bindWidget) {
+ Context targetContext = getTargetContext();
+
LauncherAppWidgetInfo item = new LauncherAppWidgetInfo(
LauncherAppWidgetInfo.NO_ID, info.provider);
item.spanX = info.minSpanX;
@@ -308,11 +278,12 @@
pendingInfo.spanY = item.spanY;
pendingInfo.minSpanX = item.minSpanX;
pendingInfo.minSpanY = item.minSpanY;
- Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(mTargetContext, pendingInfo);
+ Bundle options = getDefaultOptionsForWidget(targetContext, pendingInfo);
- AppWidgetHost host = new LauncherAppWidgetHost(mTargetContext);
+ AppWidgetHost host = new LauncherAppWidgetHost(targetContext);
int widgetId = host.allocateAppWidgetId();
- if (!mWidgetManager.bindAppWidgetIdIfAllowed(widgetId, info, options)) {
+ if (!AppWidgetManagerCompat.getInstance(targetContext)
+ .bindAppWidgetIdIfAllowed(widgetId, info, options)) {
host.deleteAppWidgetId(widgetId);
throw new IllegalArgumentException("Unable to bind widget id");
}
@@ -331,7 +302,7 @@
Set<String> activePackage = getOnUiThread(() -> {
Set<String> packages = new HashSet<>();
- PackageInstallerCompat.getInstance(mTargetContext).updateAndGetActiveSessionCache()
+ PackageInstallerCompat.getInstance(mTargetContext).getActiveSessions()
.keySet().forEach(packageUserKey -> packages.add(packageUserKey.mPackageName));
return packages;
});
diff --git a/tests/src/com/android/launcher3/util/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/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 6dced8c..e1e9b8d 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -53,8 +53,8 @@
return LauncherInstrumentation.ContainerType.ALL_APPS;
}
- private boolean hasClickableIcon(
- UiObject2 allAppsContainer, UiObject2 appListRecycler, BySelector appIconSelector) {
+ private boolean hasClickableIcon(UiObject2 allAppsContainer, UiObject2 appListRecycler,
+ BySelector appIconSelector, int displayBottom) {
final UiObject2 icon = appListRecycler.findObject(appIconSelector);
if (icon == null) {
LauncherInstrumentation.log("hasClickableIcon: icon not visible");
@@ -66,6 +66,10 @@
LauncherInstrumentation.log("hasClickableIcon: icon center is under search box");
return false;
}
+ if (iconBounds.bottom > displayBottom) {
+ LauncherInstrumentation.log("hasClickableIcon: icon center bellow bottom offset");
+ return false;
+ }
LauncherInstrumentation.log("hasClickableIcon: icon is clickable");
return true;
}
@@ -91,25 +95,31 @@
final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer,
"apps_list_view");
final UiObject2 searchBox = getSearchBox(allAppsContainer);
+
+ int bottomGestureMargin = ResourceUtils.getNavbarSize(
+ ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1;
+ int deviceHeight = mLauncher.getDevice().getDisplayHeight();
+ int displayBottom = deviceHeight - bottomGestureMargin;
allAppsContainer.setGestureMargins(
0,
getSearchBox(allAppsContainer).getVisibleBounds().bottom + 1,
0,
- ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
- mLauncher.getResources()) + 1);
+ bottomGestureMargin);
final BySelector appIconSelector = AppIcon.getAppIconSelector(appName, mLauncher);
- if (!hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector)) {
+ if (!hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector,
+ displayBottom)) {
scrollBackToBeginning();
int attempts = 0;
- int scroll = getScroll(allAppsContainer);
+ int scroll = getAllAppsScroll();
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled")) {
- while (!hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector)) {
+ while (!hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector,
+ displayBottom)) {
mLauncher.scrollToLastVisibleRow(
allAppsContainer,
mLauncher.getObjectsInContainer(allAppsContainer, "icon"),
- searchBox.getVisibleBounds().bottom -
- allAppsContainer.getVisibleBounds().top);
- final int newScroll = getScroll(allAppsContainer);
+ searchBox.getVisibleBounds().bottom
+ - allAppsContainer.getVisibleBounds().top);
+ final int newScroll = getAllAppsScroll();
if (newScroll == scroll) break;
mLauncher.assertTrue(
@@ -122,8 +132,11 @@
verifyActiveContainer();
}
+ // Ignore bottom offset selection here as there might not be any scroll more scroll
+ // region available.
mLauncher.assertTrue("Unable to scroll to a clickable icon: " + appName,
- hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector));
+ hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector,
+ deviceHeight));
final UiObject2 appIcon = mLauncher.waitForObjectInContainer(appListRecycler,
appIconSelector);
@@ -141,9 +154,9 @@
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(
@@ -159,9 +172,10 @@
}
}
- private int getScroll(UiObject2 allAppsContainer) {
- return mLauncher.getAnswerFromLauncher(allAppsContainer, TestProtocol.GET_SCROLL_MESSAGE).
- getInt(TestProtocol.SCROLL_Y_FIELD, -1);
+ private int getAllAppsScroll() {
+ return mLauncher.getTestInfo(
+ TestProtocol.REQUEST_APPS_LIST_SCROLL_Y)
+ .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
private UiObject2 getSearchBox(UiObject2 allAppsContainer) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index bcce8ef..9e66740 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -104,7 +104,12 @@
startY = endY = mLauncher.getDevice().getDisplayHeight() / 2;
}
- mLauncher.swipeToState(startX, startY, endX, endY, 10, expectedState);
+ if (mLauncher.isFallbackOverview()) {
+ mLauncher.linearGesture(startX, startY, endX, endY, 10);
+ new BaseOverview(mLauncher);
+ } else {
+ mLauncher.swipeToState(startX, startY, endX, endY, 10, expectedState);
+ }
break;
}
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 49c0c89..338f714 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -63,10 +63,10 @@
/**
* Dismissed all tasks by scrolling to Clear-all button and pressing it.
*/
- public Workspace dismissAllTasks() {
+ public void dismissAllTasks() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"dismissing all tasks")) {
- final BySelector clearAllSelector = mLauncher.getLauncherObjectSelector("clear_all");
+ final BySelector clearAllSelector = mLauncher.getOverviewObjectSelector("clear_all");
for (int i = 0;
i < FLINGS_FOR_DISMISS_LIMIT
&& !verifyActiveContainer().hasObject(clearAllSelector);
@@ -75,10 +75,6 @@
}
mLauncher.waitForObjectInContainer(verifyActiveContainer(), clearAllSelector).click();
- try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
- "dismissed all tasks")) {
- return new Workspace(mLauncher);
- }
}
}
@@ -109,7 +105,7 @@
"want to get current task")) {
verifyActiveContainer();
final List<UiObject2> taskViews = mLauncher.getDevice().findObjects(
- mLauncher.getLauncherObjectSelector("snapshot"));
+ mLauncher.getOverviewObjectSelector("snapshot"));
mLauncher.assertNotEquals("Unable to find a task", 0, taskViews.size());
// taskViews contains up to 3 task views: the 'main' (having the widest visible
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 52971d9..a03f8ab 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -521,15 +521,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.
*
@@ -734,7 +725,7 @@
@NonNull
UiObject2 waitForFallbackLauncherObject(String resName) {
- return waitForObjectBySelector(getFallbackLauncherObjectSelector(resName));
+ return waitForObjectBySelector(getOverviewObjectSelector(resName));
}
private UiObject2 waitForObjectBySelector(BySelector selector) {
@@ -751,7 +742,7 @@
return By.res(getLauncherPackageName(), resName);
}
- BySelector getFallbackLauncherObjectSelector(String resName) {
+ BySelector getOverviewObjectSelector(String resName) {
return By.res(getOverviewPackageName(), resName);
}
@@ -784,8 +775,8 @@
}
int getBottomGestureMargin(UiObject2 container) {
- return container.getVisibleBounds().bottom - getRealDisplaySize().y +
- getBottomGestureSize();
+ return container.getVisibleBounds().bottom - getRealDisplaySize().y
+ + getBottomGestureSize();
}
void scrollToLastVisibleRow(UiObject2 container, Collection<UiObject2> items, int topPadding) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Overview.java b/tests/tapl/com/android/launcher3/tapl/Overview.java
index da68da3..4f8aeb1 100644
--- a/tests/tapl/com/android/launcher3/tapl/Overview.java
+++ b/tests/tapl/com/android/launcher3/tapl/Overview.java
@@ -67,4 +67,13 @@
}
}
}
+
+ @Override
+ public void dismissAllTasks() {
+ super.dismissAllTasks();
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "dismissed all tasks")) {
+ new Workspace(mLauncher);
+ }
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/TestHelpers.java b/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
index 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..0195693 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -102,8 +102,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);
}
}