Merge "Add null check for recents when tapping on Taskbar icons" into tm-qpr-dev
diff --git a/OWNERS b/OWNERS
index dd9fbf9..2d7a014 100644
--- a/OWNERS
+++ b/OWNERS
@@ -29,6 +29,7 @@
sihua@google.com
sunnygoyal@google.com
tracyzhou@google.com
+tsuharesu@google.com
twickham@google.com
vadimt@google.com
victortulias@google.com
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index a91507c..d581582 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -49,4 +49,6 @@
<item name="config_wallpaperMaxScale" format="float" type="dimen">
@*android:dimen/config_wallpaperMaxScale
</item>
+
+ <string name="setup_wizard_pkg" translatable="false" />
</resources>
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index b021671..2fa22f1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -45,6 +45,7 @@
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
import android.os.Process;
import android.os.SystemProperties;
import android.os.Trace;
@@ -218,8 +219,8 @@
new TaskbarScrimViewController(this, taskbarScrimView),
new TaskbarUnfoldAnimationController(this, unfoldTransitionProgressProvider,
mWindowManager,
- new RotationChangeProvider(WindowManagerGlobal.getWindowManagerService(), this,
- getMainExecutor())),
+ new RotationChangeProvider(c.getSystemService(DisplayManager.class), this,
+ getMainThreadHandler())),
new TaskbarKeyguardController(this),
new StashedHandleViewController(this, stashedHandleView),
new TaskbarStashController(this),
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 93baf5b..a56300a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -68,6 +68,7 @@
import android.graphics.RectF;
import android.hardware.SensorManager;
import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.display.DisplayManager;
import android.media.permission.SafeCloseable;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -882,7 +883,7 @@
getMainExecutor(),
/* backgroundExecutor= */ UI_HELPER_EXECUTOR,
/* tracingTagPrefix= */ "launcher",
- WindowManagerGlobal.getWindowManagerService()
+ getSystemService(DisplayManager.class)
);
mUnfoldTransitionProgressProvider = unfoldComponent.getUnfoldTransitionProvider()
@@ -901,9 +902,10 @@
/* context= */ this,
config,
getMainExecutor(),
+ getMainThreadHandler(),
/* backgroundExecutor= */ UI_HELPER_EXECUTOR,
/* tracingTagPrefix= */ "launcher",
- WindowManagerGlobal.getWindowManagerService()
+ getSystemService(DisplayManager.class)
);
final RemoteUnfoldTransitionReceiver remoteUnfoldTransitionProgressProvider =
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index e264a7f..e9385d9 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -878,7 +878,7 @@
if (DesktopTaskView.DESKTOP_MODE_SUPPORTED && targets.hasDesktopTasks()) {
mRemoteTargetHandles = mTargetGluer.assignTargetsForDesktop(targets);
} else {
- mRemoteTargetHandles = mTargetGluer.assignTargetsForSplitScreen(mContext, targets);
+ mRemoteTargetHandles = mTargetGluer.assignTargetsForSplitScreen(targets);
}
mRecentsAnimationController = controller;
mRecentsAnimationTargets = targets;
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 589459f..a8f3c3a 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -37,6 +37,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.launcher3.R;
import com.android.launcher3.tracing.OverviewComponentObserverProto;
import com.android.launcher3.tracing.TouchInteractionServiceProto;
import com.android.launcher3.util.SimpleBroadcastReceiver;
@@ -65,6 +66,7 @@
private final Intent mMyHomeIntent;
private final Intent mFallbackIntent;
private final SparseIntArray mConfigChangesMap = new SparseIntArray();
+ private final String mSetupWizardPkg;
private Consumer<Boolean> mOverviewChangeListener = b -> { };
@@ -86,6 +88,7 @@
new ComponentName(context.getPackageName(), info.activityInfo.name);
mMyHomeIntent.setComponent(myHomeComponent);
mConfigChangesMap.append(myHomeComponent.hashCode(), info.activityInfo.configChanges);
+ mSetupWizardPkg = context.getString(R.string.setup_wizard_pkg);
ComponentName fallbackComponent = new ComponentName(mContext, RecentsActivity.class);
mFallbackIntent = new Intent(Intent.ACTION_MAIN)
@@ -127,6 +130,12 @@
private void updateOverviewTargets() {
ComponentName defaultHome = PackageManagerWrapper.getInstance()
.getHomeActivities(new ArrayList<>());
+ if (defaultHome != null && defaultHome.getPackageName().equals(mSetupWizardPkg)) {
+ // Treat setup wizard as null default home, because there is a period between setup and
+ // launcher being default home where it is briefly null. Otherwise, it would appear as
+ // if overview targets are changing twice, giving the listener an incorrect signal.
+ defaultHome = null;
+ }
mIsHomeDisabled = mDeviceState.isHomeDisabled();
mIsDefaultHome = Objects.equals(mMyHomeIntent.getComponent(), defaultHome);
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index 9b00dcf..f30d3f1 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -89,7 +89,7 @@
* Length of targets.apps should match that of {@link #mRemoteTargetHandles}.
*
* If split screen may be active when this is called, you might want to use
- * {@link #assignTargetsForSplitScreen(Context, RemoteAnimationTargets)}
+ * {@link #assignTargetsForSplitScreen(RemoteAnimationTargets)}
*/
public RemoteTargetHandle[] assignTargets(RemoteAnimationTargets targets) {
for (int i = 0; i < mRemoteTargetHandles.length; i++) {
@@ -102,43 +102,45 @@
}
/**
- * Similar to {@link #assignTargets(RemoteAnimationTargets)}, except this matches the
- * apps in targets.apps to that of the _active_ split screened tasks.
- * See {@link #assignTargetsForSplitScreen(RemoteAnimationTargets, int[])}
+ * Similar to {@link #assignTargets(RemoteAnimationTargets)}, except this assigns the
+ * apps in {@code targets.apps} to the {@link #mRemoteTargetHandles} with index 0 will being
+ * the left/top task, index 1 right/bottom.
*/
- public RemoteTargetHandle[] assignTargetsForSplitScreen(
- Context context, RemoteAnimationTargets targets) {
- int[] splitIds = TopTaskTracker.INSTANCE.get(context).getRunningSplitTaskIds();
- return assignTargetsForSplitScreen(targets, splitIds);
- }
-
- /**
- * Assigns the provided splitIDs to the {@link #mRemoteTargetHandles}, with index 0 will being
- * the left/top task, index 1 right/bottom
- */
- public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets,
- int[] splitIds) {
- RemoteAnimationTarget topLeftTarget; // only one set if single/fullscreen task
- RemoteAnimationTarget bottomRightTarget;
+ public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets) {
if (mRemoteTargetHandles.length == 1) {
// If we're not in split screen, the splitIds count doesn't really matter since we
// should always hit this case.
mRemoteTargetHandles[0].mTransformParams.setTargetSet(targets);
if (targets.apps.length > 0) {
// Unclear why/when target.apps length == 0, but it sure does happen :(
- topLeftTarget = targets.apps[0];
- mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget, null);
+ mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(targets.apps[0], null);
}
} else {
- // split screen
- topLeftTarget = targets.findTask(splitIds[0]);
- bottomRightTarget = targets.findTask(splitIds[1]);
+ RemoteAnimationTarget topLeftTarget = targets.apps[0];
+
+ // Fetch the adjacent target for split screen.
+ RemoteAnimationTarget bottomRightTarget = null;
+ for (int i = 1; i < targets.apps.length; i++) {
+ final RemoteAnimationTarget target = targets.apps[i];
+ Rect topLeftBounds = getStartBounds(topLeftTarget);
+ Rect bounds = getStartBounds(target);
+ if (topLeftBounds.left > bounds.right || topLeftBounds.top > bounds.bottom) {
+ bottomRightTarget = topLeftTarget;
+ topLeftTarget = target;
+ break;
+ } else if (topLeftBounds.right < bounds.left || topLeftBounds.bottom < bounds.top) {
+ bottomRightTarget = target;
+ break;
+ }
+ }
// remoteTargetHandle[0] denotes topLeft task, so we pass in the bottomRight to exclude,
// vice versa
mSplitBounds = new SplitBounds(
getStartBounds(topLeftTarget),
- getStartBounds(bottomRightTarget), splitIds[0], splitIds[1]);
+ getStartBounds(bottomRightTarget),
+ topLeftTarget.taskId,
+ bottomRightTarget.taskId);
mRemoteTargetHandles[0].mTransformParams.setTargetSet(
createRemoteAnimationTargetsForTarget(targets, bottomRightTarget));
mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget,
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 1a72e3f..da97df6 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -190,7 +190,7 @@
if (forDesktop) {
remoteTargetHandles = gluer.assignTargetsForDesktop(targets);
} else if (v.containsMultipleTasks()) {
- remoteTargetHandles = gluer.assignTargetsForSplitScreen(targets, v.getTaskIds());
+ remoteTargetHandles = gluer.assignTargetsForSplitScreen(targets);
} else {
remoteTargetHandles = gluer.assignTargets(targets);
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index accab38..4680608 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -1132,6 +1132,10 @@
return;
}
+ // TODO(b/258022658): Remove temporary logging.
+ Log.i(TAG, "preloadOverview: forSUWAllSet=" + forSUWAllSet
+ + ", isHomeAndOverviewSame=" + mOverviewComponentObserver.isHomeAndOverviewSame());
+
mTaskAnimationManager.preloadRecentsAnimation(overviewIntent);
}
diff --git a/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java b/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java
index ac0c17d..bd0ce34 100644
--- a/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java
+++ b/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java
@@ -15,17 +15,30 @@
*/
package com.android.quickstep.interaction;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL;
+
import android.content.Context;
import android.graphics.Insets;
import android.util.AttributeSet;
import android.view.MotionEvent;
+import android.view.View;
import android.view.WindowInsets;
import android.widget.RelativeLayout;
+import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentManager;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+
/** Root layout that TutorialFragment uses to intercept motion events. */
public class RootSandboxLayout extends RelativeLayout {
+
+ private View mFeedbackView;
+ private View mTutorialStepView;
+ private View mSkipButton;
+ private View mDoneButton;
+
public RootSandboxLayout(Context context) {
super(context);
}
@@ -52,4 +65,51 @@
return getHeight() + insets.top + insets.bottom;
}
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ if (ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
+ return;
+ }
+ mFeedbackView = findViewById(R.id.gesture_tutorial_fragment_feedback_view);
+ mTutorialStepView =
+ mFeedbackView.findViewById(R.id.gesture_tutorial_fragment_feedback_tutorial_step);
+ mSkipButton = mFeedbackView.findViewById(R.id.gesture_tutorial_fragment_close_button);
+ mDoneButton = mFeedbackView.findViewById(R.id.gesture_tutorial_fragment_action_button);
+
+ mFeedbackView.addOnLayoutChangeListener(
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ if (mSkipButton.getVisibility() != VISIBLE
+ && mDoneButton.getVisibility() != VISIBLE) {
+ return;
+ }
+ // Either the skip or the done button is ever shown at once, never both.
+ boolean showingSkipButton = mSkipButton.getVisibility() == VISIBLE;
+ boolean isRTL = Utilities.isRtl(getContext().getResources());
+ updateTutorialStepViewTranslation(
+ showingSkipButton ? mSkipButton : mDoneButton,
+ // Translate the step indicator away from whichever button is being
+ // shown. The skip button in on the left in LTR or on the right in RTL.
+ // The done button is on the right in LTR or left in RTL.
+ (showingSkipButton && !isRTL) || (!showingSkipButton && isRTL));
+ });
+ }
+
+ private void updateTutorialStepViewTranslation(
+ @NonNull View anchorView, boolean translateToRight) {
+ mTutorialStepView.setTranslationX(translateToRight
+ ? Math.min(
+ // Translate to the right if the views are overlapping on large fonts and
+ // display sizes.
+ Math.max(0, anchorView.getRight() - mTutorialStepView.getLeft()),
+ // Do not translate beyond the bounds of the container view.
+ mFeedbackView.getWidth() - mTutorialStepView.getRight())
+ : Math.max(
+ // Translate to the left if the views are overlapping on large fonts and
+ // display sizes.
+ Math.min(0, anchorView.getLeft() - mTutorialStepView.getRight()),
+ // Do not translate beyond the bounds of the container view.
+ -mTutorialStepView.getLeft()));
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
index 89177b6..14898b1 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -381,6 +381,7 @@
}
setOverlayEnabled(false);
onTaskListVisibilityChanged(false);
+ setVisibility(VISIBLE);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 0284d29..16935c1 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -706,6 +706,12 @@
private ObjectAnimator mActionsViewAlphaAnimator;
private float mActionsViewAlphaAnimatorFinalValue;
+ /**
+ * Keeps track of the desktop task. Optional and only present when the feature flag is enabled.
+ */
+ @Nullable
+ private DesktopTaskView mDesktopTaskView;
+
private MultiWindowModeChangedListener mMultiWindowModeChangedListener =
new MultiWindowModeChangedListener() {
@Override
@@ -1586,6 +1592,8 @@
// update the map of instance counts
mFilterState.updateInstanceCountMap(taskGroups);
+ // Clear out desktop view if it is set
+ mDesktopTaskView = null;
DesktopTask desktopTask = null;
// Add views as children based on whether it's grouped or single task. Looping through
@@ -1644,12 +1652,13 @@
if (!taskGroups.isEmpty()) {
addView(mClearAllButton);
- if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
- TaskView taskView = getTaskViewFromPool(TaskView.Type.DESKTOP);
+ if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED
+ && !getSplitSelectController().isSplitSelectActive()) {
+ mDesktopTaskView = (DesktopTaskView) getTaskViewFromPool(TaskView.Type.DESKTOP);
// Always add a desktop task to the first position. Even if it is empty
- addView(taskView, 0);
+ addView(mDesktopTaskView, 0);
ArrayList<Task> tasks = desktopTask != null ? desktopTask.tasks : new ArrayList<>();
- ((DesktopTaskView) taskView).bind(tasks, mOrientationState);
+ mDesktopTaskView.bind(tasks, mOrientationState);
}
}
@@ -2774,6 +2783,10 @@
} else if (taskView.isDesktopTask()) {
// Desktop task was not focused. Pin it to the right of focused
desktopTaskIndex = i;
+ if (taskView.getVisibility() == View.GONE) {
+ // Desktop task view is hidden, skip it from grid calculations
+ continue;
+ }
if (!ENABLE_GRID_ONLY_OVERVIEW.get()) {
// Only apply x-translation when using legacy overview grid
gridTranslations[i] += mIsRtl ? taskWidthAndSpacing : -taskWidthAndSpacing;
@@ -4467,6 +4480,9 @@
mSplitSelectStateController.setAnimateCurrentTaskDismissal(
true /*animateCurrentTaskDismissal*/);
mSplitHiddenTaskViewIndex = indexOfChild(taskView);
+ if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ updateDesktopTaskVisibility(false /* visible */);
+ }
}
/**
@@ -4483,6 +4499,15 @@
mSplitSelectStateController.setInitialTaskSelect(splitSelectSource.intent,
splitSelectSource.position.stagePosition, splitSelectSource.itemInfo,
splitSelectSource.splitEvent, splitSelectSource.alreadyRunningTaskId);
+ if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ updateDesktopTaskVisibility(false /* visible */);
+ }
+ }
+
+ private void updateDesktopTaskVisibility(boolean visible) {
+ if (mDesktopTaskView != null) {
+ mDesktopTaskView.setVisibility(visible ? VISIBLE : GONE);
+ }
}
/**
@@ -4633,6 +4658,9 @@
mSplitHiddenTaskView.setThumbnailVisibility(VISIBLE);
mSplitHiddenTaskView = null;
}
+ if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ updateDesktopTaskVisibility(true /* visible */);
+ }
}
private void safeRemoveDragLayerView(@Nullable View viewToRemove) {
@@ -5004,8 +5032,7 @@
mRemoteTargetHandles = gluer.assignTargetsForDesktop(recentsAnimationTargets);
} else {
gluer = new RemoteTargetGluer(getContext(), getSizeStrategy());
- mRemoteTargetHandles = gluer.assignTargetsForSplitScreen(
- getContext(), recentsAnimationTargets);
+ mRemoteTargetHandles = gluer.assignTargetsForSplitScreen(recentsAnimationTargets);
}
mSplitBoundsConfig = gluer.getSplitBounds();
// Add release check to the targets from the RemoteTargetGluer and not the targets
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index c98b60f..98b61d1 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -21,9 +21,14 @@
import android.content.Context;
+import androidx.annotation.VisibleForTesting;
+
import com.android.launcher3.BuildConfig;
import com.android.launcher3.Utilities;
+import java.util.function.Predicate;
+import java.util.function.ToIntFunction;
+
/**
* Defines a set of flags used to control various launcher behaviors.
*
@@ -33,6 +38,11 @@
public static final String FLAGS_PREF_NAME = "featureFlags";
+ @VisibleForTesting
+ public static Predicate<BooleanFlag> sBooleanReader = f -> f.mCurrentValue;
+ @VisibleForTesting
+ public static ToIntFunction<IntFlag> sIntReader = f -> f.mCurrentValue;
+
private FeatureFlags() { }
public static boolean showFlagTogglerUi(Context context) {
@@ -385,7 +395,7 @@
}
public boolean get() {
- return mCurrentValue;
+ return sBooleanReader.test(this);
}
}
@@ -401,7 +411,7 @@
}
public int get() {
- return mCurrentValue;
+ return sIntReader.applyAsInt(this);
}
}
}
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index 8efd12a..5a50569 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -92,7 +92,7 @@
private static final int PRELOAD_BACKGROUND_COLOR_INDEX = 1;
private static final int ALPHA_DURATION_MILLIS = 3000;
- private static final int OVERLAY_ALPHA_RANGE = 127;
+ private static final int OVERLAY_ALPHA_RANGE = 191;
private static final long WAVE_MOTION_DELAY_FACTOR_MILLIS = 100;
private static final WeakHashMap<Integer, PorterDuffColorFilter> COLOR_FILTER_MAP =
new WeakHashMap<>();
diff --git a/tests/src/com/android/launcher3/util/TestUtil.java b/tests/src/com/android/launcher3/util/TestUtil.java
index d7c6c4f..433fd31 100644
--- a/tests/src/com/android/launcher3/util/TestUtil.java
+++ b/tests/src/com/android/launcher3/util/TestUtil.java
@@ -27,12 +27,18 @@
import androidx.test.uiautomator.UiDevice;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.config.FeatureFlags.BooleanFlag;
+import com.android.launcher3.config.FeatureFlags.IntFlag;
+
import org.junit.Assert;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.CountDownLatch;
+import java.util.function.Predicate;
+import java.util.function.ToIntFunction;
public class TestUtil {
public static final String DUMMY_PACKAGE = "com.example.android.aardwolf";
@@ -68,6 +74,36 @@
}
}
+ /**
+ * Utility class to override a boolean flag during test. Note that the returned SafeCloseable
+ * must be closed to restore the original state
+ */
+ public static SafeCloseable overrideFlag(BooleanFlag flag, boolean value) {
+ Predicate<BooleanFlag> originalProxy = FeatureFlags.sBooleanReader;
+ Predicate<BooleanFlag> testProxy = f -> f == flag ? value : originalProxy.test(f);
+ FeatureFlags.sBooleanReader = testProxy;
+ return () -> {
+ if (FeatureFlags.sBooleanReader == testProxy) {
+ FeatureFlags.sBooleanReader = originalProxy;
+ }
+ };
+ }
+
+ /**
+ * Utility class to override a int flag during test. Note that the returned SafeCloseable
+ * must be closed to restore the original state
+ */
+ public static SafeCloseable overrideFlag(IntFlag flag, int value) {
+ ToIntFunction<IntFlag> originalProxy = FeatureFlags.sIntReader;
+ ToIntFunction<IntFlag> testProxy = f -> f == flag ? value : originalProxy.applyAsInt(f);
+ FeatureFlags.sIntReader = testProxy;
+ return () -> {
+ if (FeatureFlags.sIntReader == testProxy) {
+ FeatureFlags.sIntReader = originalProxy;
+ }
+ };
+ }
+
public static void uninstallDummyApp() throws IOException {
UiDevice.getInstance(getInstrumentation()).executeShellCommand(
"pm uninstall " + DUMMY_PACKAGE);