Merge "Allow clipping individual direction of TaskView" into sc-v2-dev
diff --git a/Android.bp b/Android.bp
index 45d022f..43d28c9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -224,7 +224,6 @@
srcs: ["proguard.flags"],
}
-
// Library with all the dependencies for building Launcher Go
android_library {
name: "LauncherGoResLib",
@@ -253,3 +252,24 @@
},
}
+// Build rule for Quickstep library
+android_library {
+ name: "Launcher3QuickStepLib",
+ srcs: [
+ ":launcher-src-no-build-config",
+ ],
+ resource_dirs: [
+ "quickstep/res",
+ ],
+ static_libs: [
+ "SystemUI-statsd",
+ "SystemUISharedLib",
+ "Launcher3CommonDepsLib"
+ ],
+ manifest: "quickstep/AndroidManifest.xml",
+ platform_apis: true,
+ min_sdk_version: "current",
+ lint: {
+ baseline_filename: "lint-baseline-launcher3.xml",
+ },
+}
diff --git a/Android.mk b/Android.mk
index c222f24..c1dbc53 100644
--- a/Android.mk
+++ b/Android.mk
@@ -53,42 +53,6 @@
include $(BUILD_PACKAGE)
#
-# Build rule for Quickstep library.
-#
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_AAPT2_ONLY := true
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- SystemUI-statsd \
- SystemUISharedLib
-ifneq (,$(wildcard frameworks/base))
- LOCAL_PRIVATE_PLATFORM_APIS := true
-else
- LOCAL_SDK_VERSION := system_current
- LOCAL_MIN_SDK_VERSION := 26
-endif
-LOCAL_MODULE := Launcher3QuickStepLib
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3CommonDepsLib
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
- $(call all-java-files-under, quickstep/src) \
- $(call all-java-files-under, src_shortcuts_overrides)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/quickstep/res
-LOCAL_PROGUARD_ENABLED := disabled
-
-
-LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-#
# Build rule for Quickstep app.
#
include $(CLEAR_VARS)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 8066816..c72f62d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -31,6 +31,7 @@
android:fullBackupOnly="true"
android:fullBackupContent="@xml/backupscheme"
android:hardwareAccelerated="true"
+ android:debuggable="true"
android:icon="@drawable/ic_launcher_home"
android:label="@string/derived_app_name"
android:theme="@style/AppTheme"
diff --git a/quickstep/Android.bp b/quickstep/Android.bp
index 38c9919..7b3e6c4 100644
--- a/quickstep/Android.bp
+++ b/quickstep/Android.bp
@@ -26,3 +26,19 @@
path: "robolectric_tests",
srcs: ["robolectric_tests/src/**/*.java"],
}
+
+filegroup {
+ name: "launcher3-quickstep-tests-src",
+ path: "tests",
+ srcs: ["tests/src/**/*.java"],
+}
+
+filegroup {
+ name: "launcher3-quickstep-oop-tests-src",
+ path: "tests",
+ srcs: [
+ "tests/src/com/android/quickstep/NavigationModeSwitchRule.java",
+ "tests/src/com/android/quickstep/AbstractQuickStepTest.java",
+ "tests/src/com/android/quickstep/TaplTestsQuickstep.java",
+ ]
+}
diff --git a/quickstep/res/interpolator/app_open_x.xml b/quickstep/res/interpolator/app_open_x.xml
new file mode 100644
index 0000000..5fa0bcb
--- /dev/null
+++ b/quickstep/res/interpolator/app_open_x.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2021, 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.
+*/
+-->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:pathData="M 0, 0 C 0.1217, 0.0462, 0.15, 0.4686, 0.1667, 0.66 C 0.1834, 0.8878, 0.1667, 1, 1, 1"/>
diff --git a/quickstep/res/interpolator/three_point_fast_out_extra_slow_in.xml b/quickstep/res/interpolator/three_point_fast_out_extra_slow_in.xml
new file mode 100644
index 0000000..70c4231
--- /dev/null
+++ b/quickstep/res/interpolator/three_point_fast_out_extra_slow_in.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2021, 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.
+*/
+-->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
diff --git a/quickstep/res/layout/taskbar.xml b/quickstep/res/layout/taskbar.xml
index c0e0862..b4c168c 100644
--- a/quickstep/res/layout/taskbar.xml
+++ b/quickstep/res/layout/taskbar.xml
@@ -65,13 +65,12 @@
android:layout_gravity="end"/>
</FrameLayout>
- <View
+ <com.android.launcher3.taskbar.StashedHandleView
android:id="@+id/stashed_handle"
tools:comment1="The actual size and shape will be set as a ViewOutlineProvider at runtime"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- tools:comment2="TODO: Tint dynamically"
- android:background="?android:attr/textColorPrimary"
+ android:background="@color/taskbar_stashed_handle_dark_color"
android:clipToOutline="true"
android:layout_gravity="bottom"/>
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index 2f24441..17980f0 100644
--- a/quickstep/res/values/colors.xml
+++ b/quickstep/res/values/colors.xml
@@ -28,4 +28,7 @@
<!-- Taskbar -->
<color name="taskbar_background">@color/overview_scrim_dark</color>
<color name="taskbar_icon_selection_ripple">#E0E0E0</color>
+
+ <color name="taskbar_stashed_handle_light_color">#EBffffff</color>
+ <color name="taskbar_stashed_handle_dark_color">#99000000</color>
</resources>
\ No newline at end of file
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
deleted file mode 100644
index 82e8903..0000000
--- a/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2020 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.LauncherUIHelper.doLayout;
-
-import android.app.ActivityManager.RunningTaskInfo;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-
-import com.android.quickstep.fallback.FallbackRecentsView;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.android.controller.ActivityController;
-import org.robolectric.annotation.LooperMode;
-import org.robolectric.annotation.LooperMode.Mode;
-import org.robolectric.shadows.ShadowLooper;
-import org.robolectric.util.ReflectionHelpers;
-
-
-@RunWith(RobolectricTestRunner.class)
-@LooperMode(Mode.PAUSED)
-@org.junit.Ignore
-public class RecentsActivityTest {
-
- @Test
- public void testRecentsActivityCreates() {
- ActivityController<RecentsActivity> controller =
- Robolectric.buildActivity(RecentsActivity.class);
-
- RecentsActivity launcher = controller.setup().get();
- doLayout(launcher);
-
- // TODO: Ensure that LauncherAppState is not created
- }
-
- @Test
- public void testRecents_showCurrentTask() {
- ActivityController<RecentsActivity> controller =
- Robolectric.buildActivity(RecentsActivity.class);
-
- RecentsActivity activity = controller.setup().get();
- doLayout(activity);
-
- FallbackRecentsView frv = activity.getOverviewPanel();
-
- RunningTaskInfo placeholderTask = new RunningTaskInfo();
- placeholderTask.taskId = 22;
- frv.showCurrentTask(new RunningTaskInfo[]{placeholderTask});
- doLayout(activity);
-
- ThumbnailData thumbnailData = new ThumbnailData();
- ReflectionHelpers.setField(thumbnailData, "thumbnail",
- Bitmap.createBitmap(300, 500, Config.ARGB_8888));
- frv.switchToScreenshot(thumbnailData, () -> { });
- ShadowLooper.idleMainLooper();
- }
-}
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 433ae3c..f06447b 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -32,7 +32,6 @@
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
-import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SCRIM_FOR_APP_LAUNCH;
import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
@@ -71,6 +70,7 @@
import android.view.View;
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
+import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
@@ -142,21 +142,11 @@
private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION =
"android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS";
- private static final long APP_LAUNCH_DURATION = 450;
- // Use a shorter duration for x or y translation to create a curve effect
- private static final long APP_LAUNCH_CURVED_DURATION = 250;
+ private static final long APP_LAUNCH_DURATION = 500;
+
private static final long APP_LAUNCH_ALPHA_DURATION = 50;
private static final long APP_LAUNCH_ALPHA_START_DELAY = 25;
- // We scale the durations for the downward app launch animations (minus the scale animation).
- private static final float APP_LAUNCH_DOWN_DUR_SCALE_FACTOR = 0.8f;
- private static final long APP_LAUNCH_DOWN_DURATION =
- (long) (APP_LAUNCH_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
- private static final long APP_LAUNCH_DOWN_CURVED_DURATION =
- (long) (APP_LAUNCH_CURVED_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
- private static final long APP_LAUNCH_ALPHA_DOWN_DURATION =
- (long) (APP_LAUNCH_ALPHA_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
-
public static final int ANIMATION_NAV_FADE_IN_DURATION = 266;
public static final int ANIMATION_NAV_FADE_OUT_DURATION = 133;
public static final long ANIMATION_DELAY_NAV_FADE_IN =
@@ -166,9 +156,6 @@
public static final Interpolator NAV_FADE_OUT_INTERPOLATOR =
new PathInterpolator(0.2f, 0f, 1f, 1f);
- private static final long CROP_DURATION = 375;
- private static final long RADIUS_DURATION = 375;
-
public static final int RECENTS_LAUNCH_DURATION = 336;
private static final int LAUNCHER_RESUME_START_DELAY = 100;
private static final int CLOSING_TRANSITION_DURATION_MS = 250;
@@ -222,6 +209,9 @@
// Will never be larger than MAX_NUM_TASKS
private LinkedHashMap<Integer, Pair<Integer, Integer>> mTaskStartParams;
+ private final Interpolator mOpeningXInterpolator;
+ private final Interpolator mOpeningInterpolator;
+
public QuickstepTransitionManager(Context context) {
mLauncher = Launcher.cast(Launcher.getLauncher(context));
mDragLayer = mLauncher.getDragLayer();
@@ -248,6 +238,10 @@
SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(
mStartingWindowListener);
}
+
+ mOpeningXInterpolator = AnimationUtils.loadInterpolator(context, R.interpolator.app_open_x);
+ mOpeningInterpolator = AnimationUtils.loadInterpolator(context,
+ R.interpolator.three_point_fast_out_extra_slow_in);
}
@Override
@@ -686,27 +680,29 @@
final float finalShadowRadius = appTargetsAreTranslucent ? 0 : mMaxShadowRadius;
MultiValueUpdateListener listener = new MultiValueUpdateListener() {
- FloatProp mDx = new FloatProp(0, prop.dX, 0, prop.xDuration, AGGRESSIVE_EASE);
- FloatProp mDy = new FloatProp(0, prop.dY, 0, prop.yDuration, AGGRESSIVE_EASE);
+ FloatProp mDx = new FloatProp(0, prop.dX, 0, APP_LAUNCH_DURATION,
+ mOpeningXInterpolator);
+ FloatProp mDy = new FloatProp(0, prop.dY, 0, APP_LAUNCH_DURATION,
+ mOpeningInterpolator);
FloatProp mIconScaleToFitScreen = new FloatProp(prop.initialAppIconScale,
- prop.finalAppIconScale, 0, APP_LAUNCH_DURATION, EXAGGERATED_EASE);
+ prop.finalAppIconScale, 0, APP_LAUNCH_DURATION, mOpeningInterpolator);
FloatProp mIconAlpha = new FloatProp(prop.iconAlphaStart, 0f,
- APP_LAUNCH_ALPHA_START_DELAY, prop.alphaDuration, LINEAR);
+ APP_LAUNCH_ALPHA_START_DELAY, APP_LAUNCH_ALPHA_DURATION, LINEAR);
FloatProp mWindowRadius = new FloatProp(initialWindowRadius, finalWindowRadius, 0,
- RADIUS_DURATION, EXAGGERATED_EASE);
+ APP_LAUNCH_DURATION, mOpeningInterpolator);
FloatProp mShadowRadius = new FloatProp(0, finalShadowRadius, 0,
- APP_LAUNCH_DURATION, EXAGGERATED_EASE);
+ APP_LAUNCH_DURATION, mOpeningInterpolator);
FloatProp mCropRectCenterX = new FloatProp(prop.cropCenterXStart, prop.cropCenterXEnd,
- 0, CROP_DURATION, EXAGGERATED_EASE);
+ 0, APP_LAUNCH_DURATION, mOpeningInterpolator);
FloatProp mCropRectCenterY = new FloatProp(prop.cropCenterYStart, prop.cropCenterYEnd,
- 0, CROP_DURATION, EXAGGERATED_EASE);
+ 0, APP_LAUNCH_DURATION, mOpeningInterpolator);
FloatProp mCropRectWidth = new FloatProp(prop.cropWidthStart, prop.cropWidthEnd, 0,
- CROP_DURATION, EXAGGERATED_EASE);
+ APP_LAUNCH_DURATION, mOpeningInterpolator);
FloatProp mCropRectHeight = new FloatProp(prop.cropHeightStart, prop.cropHeightEnd, 0,
- CROP_DURATION, EXAGGERATED_EASE);
+ APP_LAUNCH_DURATION, mOpeningInterpolator);
FloatProp mNavFadeOut = new FloatProp(1f, 0f, 0, ANIMATION_NAV_FADE_OUT_DURATION,
NAV_FADE_OUT_INTERPOLATOR);
@@ -908,22 +904,23 @@
WIDGET_CROSSFADE_DURATION_MILLIS / 2 /* delay */,
WIDGET_CROSSFADE_DURATION_MILLIS / 2 /* duration */, LINEAR);
final FloatProp mWindowRadius = new FloatProp(initialWindowRadius, finalWindowRadius,
- 0 /* start */, RADIUS_DURATION, LINEAR);
- final FloatProp mCornerRadiusProgress = new FloatProp(0, 1, 0, RADIUS_DURATION, LINEAR);
+ 0 /* start */, APP_LAUNCH_DURATION, mOpeningInterpolator);
+ final FloatProp mCornerRadiusProgress = new FloatProp(0, 1, 0, APP_LAUNCH_DURATION,
+ mOpeningInterpolator);
// Window & widget background positioning bounds
final FloatProp mDx = new FloatProp(widgetBackgroundBounds.centerX(),
- windowTargetBounds.centerX(), 0 /* delay */, APP_LAUNCH_CURVED_DURATION,
- EXAGGERATED_EASE);
+ windowTargetBounds.centerX(), 0 /* delay */, APP_LAUNCH_DURATION,
+ mOpeningXInterpolator);
final FloatProp mDy = new FloatProp(widgetBackgroundBounds.centerY(),
windowTargetBounds.centerY(), 0 /* delay */, APP_LAUNCH_DURATION,
- EXAGGERATED_EASE);
+ mOpeningInterpolator);
final FloatProp mWidth = new FloatProp(widgetBackgroundBounds.width(),
windowTargetBounds.width(), 0 /* delay */, APP_LAUNCH_DURATION,
- EXAGGERATED_EASE);
+ mOpeningInterpolator);
final FloatProp mHeight = new FloatProp(widgetBackgroundBounds.height(),
windowTargetBounds.height(), 0 /* delay */, APP_LAUNCH_DURATION,
- EXAGGERATED_EASE);
+ mOpeningInterpolator);
final FloatProp mNavFadeOut = new FloatProp(1f, 0f, 0, ANIMATION_NAV_FADE_OUT_DURATION,
NAV_FADE_OUT_INTERPOLATOR);
@@ -1467,10 +1464,6 @@
public final float dX;
public final float dY;
- public final long xDuration;
- public final long yDuration;
- public final long alphaDuration;
-
public final float initialAppIconScale;
public final float finalAppIconScale;
@@ -1502,14 +1495,6 @@
dX = centerX - launcherIconBounds.centerX();
dY = centerY - launcherIconBounds.centerY();
- boolean useUpwardAnimation = launcherIconBounds.top > centerY
- || Math.abs(dY) < dp.cellHeightPx;
- xDuration = useUpwardAnimation ? APP_LAUNCH_CURVED_DURATION
- : APP_LAUNCH_DOWN_DURATION;
- yDuration = useUpwardAnimation ? APP_LAUNCH_DURATION
- : APP_LAUNCH_DOWN_CURVED_DURATION;
- alphaDuration = useUpwardAnimation ? APP_LAUNCH_ALPHA_DURATION
- : APP_LAUNCH_ALPHA_DOWN_DURATION;
iconAlphaStart = hasSplashScreen && !hasDifferentAppIcon ? 0 : 1f;
final int windowIconSize = ResourceUtils.getDimenByName("starting_surface_icon_size",
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 08300e2..e871c25 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -25,8 +25,11 @@
import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_KEYGUARD;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import android.animation.ObjectAnimator;
import android.annotation.DrawableRes;
@@ -69,6 +72,9 @@
private static final int FLAG_A11Y_VISIBLE = 1 << 3;
private static final int FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE = 1 << 4;
private static final int FLAG_KEYGUARD_VISIBLE = 1 << 5;
+ private static final int FLAG_DISABLE_HOME = 1 << 6;
+ private static final int FLAG_DISABLE_RECENTS = 1 << 7;
+ private static final int FLAG_DISABLE_BACK = 1 << 8;
private static final int MASK_IME_SWITCHER_VISIBLE = FLAG_SWITCHER_SUPPORTED | FLAG_IME_VISIBLE;
@@ -169,7 +175,8 @@
mBackButton = addButton(R.drawable.ic_sysbar_back, BUTTON_BACK,
mNavButtonContainer, mControllers.navButtonController, R.id.back);
mPropertyHolders.add(new StatePropertyHolder(mBackButton,
- flags -> (flags & FLAG_IME_VISIBLE) == 0));
+ flags -> (flags & FLAG_IME_VISIBLE) == 0 &&
+ (flags & FLAG_DISABLE_BACK) == 0));
// Hide when keyguard is showing, show when bouncer is showing
mPropertyHolders.add(new StatePropertyHolder(mBackButton,
flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 ||
@@ -180,12 +187,14 @@
navButtonController, R.id.home);
mPropertyHolders.add(new StatePropertyHolder(homeButton,
flags -> (flags & FLAG_IME_VISIBLE) == 0 &&
- (flags & FLAG_KEYGUARD_VISIBLE) == 0));
+ (flags & FLAG_KEYGUARD_VISIBLE) == 0 &&
+ (flags & FLAG_DISABLE_HOME) == 0));
View recentsButton = addButton(R.drawable.ic_sysbar_recent, BUTTON_RECENTS,
navContainer, navButtonController, R.id.recent_apps);
mPropertyHolders.add(new StatePropertyHolder(recentsButton,
flags -> (flags & FLAG_IME_VISIBLE) == 0 &&
- (flags & FLAG_KEYGUARD_VISIBLE) == 0));
+ (flags & FLAG_KEYGUARD_VISIBLE) == 0 &&
+ (flags & FLAG_DISABLE_RECENTS) == 0));
// A11y button
mA11yButton = addButton(R.drawable.ic_sysbar_accessibility_button, BUTTON_A11Y,
@@ -202,6 +211,12 @@
boolean a11yVisible = (systemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean a11yLongClickable =
(systemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
+ boolean isHomeDisabled =
+ (systemUiStateFlags & SYSUI_STATE_HOME_DISABLED) != 0;
+ boolean isRecentsDisabled =
+ (systemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0;
+ boolean isBackDisabled =
+ (systemUiStateFlags & SYSUI_STATE_BACK_DISABLED) != 0;
if (!forceUpdate && systemUiStateFlags == mSysuiStateFlags) {
return;
@@ -211,6 +226,10 @@
updateStateForFlag(FLAG_IME_VISIBLE, isImeVisible);
updateStateForFlag(FLAG_SWITCHER_SUPPORTED, isImeSwitcherShowing);
updateStateForFlag(FLAG_A11Y_VISIBLE, a11yVisible);
+ updateStateForFlag(FLAG_DISABLE_HOME, isHomeDisabled);
+ updateStateForFlag(FLAG_DISABLE_RECENTS, isRecentsDisabled);
+ updateStateForFlag(FLAG_DISABLE_BACK, isBackDisabled);
+
if (mA11yButton != null) {
// Only used in 3 button
mA11yButton.setLongClickable(a11yLongClickable);
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java
new file mode 100644
index 0000000..0224bc4
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 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.taskbar;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.ColorInt;
+import androidx.core.content.ContextCompat;
+
+import com.android.launcher3.R;
+
+public class StashedHandleView extends View {
+
+ private final @ColorInt int mStashedHandleLightColor;
+ private final @ColorInt int mStashedHandleDarkColor;
+ private final Rect mSampledRegion = new Rect();
+ private final int[] mTmpArr = new int[2];
+
+ public StashedHandleView(Context context) {
+ this(context, null);
+ }
+
+ public StashedHandleView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public StashedHandleView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public StashedHandleView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ mStashedHandleLightColor = ContextCompat.getColor(context,
+ R.color.taskbar_stashed_handle_light_color);
+ mStashedHandleDarkColor = ContextCompat.getColor(context,
+ R.color.taskbar_stashed_handle_dark_color);
+ }
+
+ public void updateSampledRegion() {
+ getLocationOnScreen(mTmpArr);
+ mSampledRegion.set(mTmpArr[0], mTmpArr[1], mTmpArr[0] + getWidth(),
+ mTmpArr[1] + getHeight());
+ }
+
+ public Rect getSampledRegion() {
+ return mSampledRegion;
+ }
+
+ public void updateHandleColor(boolean isRegionDark) {
+ setBackgroundColor(isRegionDark ? mStashedHandleLightColor : mStashedHandleDarkColor);
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index df37261..2858d7c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -16,6 +16,7 @@
package com.android.launcher3.taskbar;
import android.animation.Animator;
+import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Outline;
import android.graphics.Rect;
@@ -25,19 +26,30 @@
import androidx.annotation.Nullable;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.anim.RevealOutlineAnimation;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+import com.android.launcher3.util.Executors;
import com.android.quickstep.AnimatedFloat;
+import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
/**
* Handles properties/data collection, then passes the results to our stashed handle View to render.
*/
public class StashedHandleViewController {
+ /**
+ * The SharedPreferences key for whether the stashed handle region is dark.
+ */
+ private static final String SHARED_PREFS_STASHED_HANDLE_REGION_DARK_KEY =
+ "stashed_handle_region_is_dark";
+
private final TaskbarActivityContext mActivity;
- private final View mStashedHandleView;
+ private final SharedPreferences mPrefs;
+ private final StashedHandleView mStashedHandleView;
private final int mStashedHandleWidth;
private final int mStashedHandleHeight;
+ private final RegionSamplingHelper mRegionSamplingHelper;
private final AnimatedFloat mTaskbarStashedHandleAlpha = new AnimatedFloat(
this::updateStashedHandleAlpha);
private final AnimatedFloat mTaskbarStashedHandleHintScale = new AnimatedFloat(
@@ -52,13 +64,31 @@
private boolean mIsAtStashedRevealBounds = true;
- public StashedHandleViewController(TaskbarActivityContext activity, View stashedHandleView) {
+ public StashedHandleViewController(TaskbarActivityContext activity,
+ StashedHandleView stashedHandleView) {
mActivity = activity;
+ mPrefs = Utilities.getPrefs(mActivity);
mStashedHandleView = stashedHandleView;
+ mStashedHandleView.updateHandleColor(
+ mPrefs.getBoolean(SHARED_PREFS_STASHED_HANDLE_REGION_DARK_KEY, false));
final Resources resources = mActivity.getResources();
mStashedHandleWidth = resources.getDimensionPixelSize(R.dimen.taskbar_stashed_handle_width);
mStashedHandleHeight = resources.getDimensionPixelSize(
R.dimen.taskbar_stashed_handle_height);
+ mRegionSamplingHelper = new RegionSamplingHelper(mStashedHandleView,
+ new RegionSamplingHelper.SamplingCallback() {
+ @Override
+ public void onRegionDarknessChanged(boolean isRegionDark) {
+ mStashedHandleView.updateHandleColor(isRegionDark);
+ mPrefs.edit().putBoolean(SHARED_PREFS_STASHED_HANDLE_REGION_DARK_KEY,
+ isRegionDark).apply();
+ }
+
+ @Override
+ public Rect getSampledRegion(View sampledView) {
+ return mStashedHandleView.getSampledRegion();
+ }
+ }, Executors.UI_HELPER_EXECUTOR);
}
public void init(TaskbarControllers controllers) {
@@ -93,6 +123,10 @@
});
}
+ public void onDestroy() {
+ mRegionSamplingHelper.stopAndDestroy();
+ }
+
public AnimatedFloat getStashedHandleAlpha() {
return mTaskbarStashedHandleAlpha;
}
@@ -117,6 +151,16 @@
return handleRevealProvider.createRevealAnimator(mStashedHandleView, !isStashed);
}
+ public void onIsStashed(boolean isStashed) {
+ mRegionSamplingHelper.setWindowVisible(isStashed);
+ if (isStashed) {
+ mStashedHandleView.updateSampledRegion();
+ mRegionSamplingHelper.start(mStashedHandleView.getSampledRegion());
+ } else {
+ mRegionSamplingHelper.stop();
+ }
+ }
+
protected void updateStashedHandleAlpha() {
mStashedHandleView.setAlpha(mTaskbarStashedHandleAlpha.value);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index a90f17d..ca574e6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -36,7 +36,6 @@
import android.view.Display;
import android.view.Gravity;
import android.view.LayoutInflater;
-import android.view.RoundedCorner;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
@@ -84,7 +83,6 @@
private final TaskbarControllers mControllers;
private final WindowManager mWindowManager;
- private final RoundedCorner mLeftCorner, mRightCorner;
private WindowManager.LayoutParams mWindowLayoutParams;
private boolean mIsFullscreen;
// The size we should return to when we call setTaskbarWindowFullscreen(false)
@@ -117,7 +115,7 @@
R.layout.taskbar, null, false);
TaskbarView taskbarView = mDragLayer.findViewById(R.id.taskbar_view);
FrameLayout navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view);
- View stashedHandleView = mDragLayer.findViewById(R.id.stashed_handle);
+ StashedHandleView stashedHandleView = mDragLayer.findViewById(R.id.stashed_handle);
// Construct controllers.
mControllers = new TaskbarControllers(this,
@@ -138,12 +136,10 @@
? windowContext.getApplicationContext()
: windowContext.getApplicationContext().createDisplayContext(display);
mWindowManager = c.getSystemService(WindowManager.class);
- mLeftCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
- mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
}
public void init() {
- mLastRequestedNonFullscreenHeight = getDefaultTaskbarWindowHeight();
+ mLastRequestedNonFullscreenHeight = mDeviceProfile.taskbarSize;
mWindowLayoutParams = new WindowManager.LayoutParams(
MATCH_PARENT,
mLastRequestedNonFullscreenHeight,
@@ -173,14 +169,6 @@
return mNavMode == Mode.THREE_BUTTONS;
}
- public RoundedCorner getLeftCorner() {
- return mLeftCorner;
- }
-
- public RoundedCorner getRightCorner() {
- return mRightCorner;
- }
-
@Override
public LayoutInflater getLayoutInflater() {
return mLayoutInflater;
@@ -247,7 +235,6 @@
return;
}
mControllers.rotationButtonController.onDisable2FlagChanged(state2);
- mControllers.taskbarKeyguardController.disableNavbarElements(state1, state2);
}
public void onSystemBarAttributesChanged(int displayId, int behavior) {
@@ -262,12 +249,8 @@
setTaskbarWindowHeight(fullscreen ? MATCH_PARENT : mLastRequestedNonFullscreenHeight);
}
- public boolean isTaskbarWindowFullscreen() {
- return mIsFullscreen;
- }
-
/**
- * Updates the TaskbarContainer height (pass {@link #getDefaultTaskbarWindowHeight()} to reset).
+ * Updates the TaskbarContainer height (pass deviceProfile.taskbarSize to reset).
*/
public void setTaskbarWindowHeight(int height) {
if (mWindowLayoutParams.height == height || mIsDestroyed) {
@@ -287,14 +270,6 @@
mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
}
- /**
- * Returns the default height of the window, including the static corner radii above taskbar.
- */
- public int getDefaultTaskbarWindowHeight() {
- return mDeviceProfile.taskbarSize
- + Math.max(mLeftCorner.getRadius(), mRightCorner.getRadius());
- }
-
protected void onTaskbarIconClicked(View view) {
Object tag = view.getTag();
if (tag instanceof Task) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index 2927a63..b32a41e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -89,5 +89,6 @@
taskbarDragLayerController.onDestroy();
taskbarKeyguardController.onDestroy();
taskbarViewController.onDestroy();
+ stashedHandleViewController.onDestroy();
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index 0848c35..cd1baf7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
-import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
@@ -41,12 +40,9 @@
public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> {
private final Paint mTaskbarBackgroundPaint;
- private final Path mInvertedLeftCornerPath, mInvertedRightCornerPath;
private final OnComputeInsetsListener mTaskbarInsetsComputer = this::onComputeTaskbarInsets;
- // Initialized in init.
private TaskbarDragLayerController.TaskbarDragLayerCallbacks mControllerCallbacks;
- private float mLeftCornerRadius, mRightCornerRadius;
private float mTaskbarBackgroundOffset;
@@ -69,32 +65,10 @@
mTaskbarBackgroundPaint = new Paint();
mTaskbarBackgroundPaint.setColor(getResources().getColor(R.color.taskbar_background));
mTaskbarBackgroundPaint.setAlpha(0);
- mTaskbarBackgroundPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
- mTaskbarBackgroundPaint.setStyle(Paint.Style.FILL);
-
- // Will be set in init(), but this ensures they are always non-null.
- mInvertedLeftCornerPath = new Path();
- mInvertedRightCornerPath = new Path();
}
public void init(TaskbarDragLayerController.TaskbarDragLayerCallbacks callbacks) {
mControllerCallbacks = callbacks;
-
- // Create the paths for the inverted rounded corners above the taskbar. Start with a filled
- // square, and then subtracting out a circle from the appropriate corner.
- mLeftCornerRadius = mActivity.getLeftCorner().getRadius();
- mRightCornerRadius = mActivity.getRightCorner().getRadius();
- Path square = new Path();
- square.addRect(0, 0, mLeftCornerRadius, mLeftCornerRadius, Path.Direction.CW);
- Path circle = new Path();
- circle.addCircle(mLeftCornerRadius, 0, mLeftCornerRadius, Path.Direction.CW);
- mInvertedLeftCornerPath.op(square, circle, Path.Op.DIFFERENCE);
- square.reset();
- square.addRect(0, 0, mRightCornerRadius, mRightCornerRadius, Path.Direction.CW);
- circle.reset();
- circle.addCircle(0, 0, mRightCornerRadius, Path.Direction.CW);
- mInvertedRightCornerPath.op(square, circle, Path.Op.DIFFERENCE);
-
recreateControllers();
}
@@ -147,20 +121,8 @@
protected void dispatchDraw(Canvas canvas) {
float backgroundHeight = mControllerCallbacks.getTaskbarBackgroundHeight()
* (1f - mTaskbarBackgroundOffset);
- canvas.save();
- canvas.translate(0, canvas.getHeight() - backgroundHeight);
-
- // Draw the background behind taskbar content.
- canvas.drawRect(0, 0, canvas.getWidth(), backgroundHeight, mTaskbarBackgroundPaint);
-
- // Draw the inverted rounded corners above the taskbar.
- canvas.translate(0, -mLeftCornerRadius);
- canvas.drawPath(mInvertedLeftCornerPath, mTaskbarBackgroundPaint);
- canvas.translate(0, mLeftCornerRadius);
- canvas.translate(canvas.getWidth() - mRightCornerRadius, -mRightCornerRadius);
- canvas.drawPath(mInvertedRightCornerPath, mTaskbarBackgroundPaint);
-
- canvas.restore();
+ canvas.drawRect(0, canvas.getHeight() - backgroundHeight, canvas.getWidth(),
+ canvas.getHeight(), mTaskbarBackgroundPaint);
super.dispatchDraw(canvas);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 0afa480..14150b9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -16,7 +16,6 @@
package com.android.launcher3.taskbar;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
-import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_CONTENT;
import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_FRAME;
import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
@@ -133,14 +132,13 @@
// Let touches pass through us.
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
} else if (mControllers.navbarButtonsViewController.isImeVisible()) {
- insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_CONTENT);
+ insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME);
} else if (!mControllers.uiController.isTaskbarTouchable()) {
// Let touches pass through us.
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
} else if (mControllers.taskbarViewController.areIconsVisible()) {
// Buttons are visible, take over the full taskbar area
- insetsInfo.setTouchableInsets(mActivity.isTaskbarWindowFullscreen()
- ? TOUCHABLE_INSETS_FRAME : TOUCHABLE_INSETS_CONTENT);
+ insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME);
} else {
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
index a2039b6..834cd9c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
@@ -1,7 +1,10 @@
package com.android.launcher3.taskbar;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
import android.app.KeyguardManager;
@@ -9,7 +12,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.view.View;
/**
* Controller for managing keyguard state for taskbar
@@ -17,10 +19,11 @@
public class TaskbarKeyguardController {
private static final int KEYGUARD_SYSUI_FLAGS = SYSUI_STATE_BOUNCER_SHOWING |
- SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING | SYSUI_STATE_DEVICE_DOZING;
+ SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING | SYSUI_STATE_DEVICE_DOZING |
+ SYSUI_STATE_OVERVIEW_DISABLED | SYSUI_STATE_HOME_DISABLED |
+ SYSUI_STATE_BACK_DISABLED;
private final TaskbarActivityContext mContext;
- private int mDisabledNavIcons;
private int mKeyguardSysuiFlags;
private boolean mBouncerShowing;
private NavbarButtonsViewController mNavbarButtonsViewController;
@@ -70,22 +73,13 @@
mIsScreenOff = false;
}
- public void disableNavbarElements(int state1, int state2) {
- if (mDisabledNavIcons == state1) {
- // no change
- return;
- }
- mDisabledNavIcons = state1;
- updateIconsForBouncer();
- }
-
/**
* Hides/shows taskbar when keyguard is up
*/
private void updateIconsForBouncer() {
- boolean disableBack = (mDisabledNavIcons & View.STATUS_BAR_DISABLE_BACK) != 0;
- boolean disableRecent = (mDisabledNavIcons & View.STATUS_BAR_DISABLE_RECENT) != 0;
- boolean disableHome = (mDisabledNavIcons & View.STATUS_BAR_DISABLE_HOME) != 0;
+ boolean disableBack = (mKeyguardSysuiFlags & SYSUI_STATE_BACK_DISABLED) != 0;
+ boolean disableRecent = (mKeyguardSysuiFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0;
+ boolean disableHome = (mKeyguardSysuiFlags & SYSUI_STATE_HOME_DISABLED) != 0;
boolean onlyBackEnabled = !disableBack && disableRecent && disableHome;
boolean showBackForBouncer = onlyBackEnabled &&
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 949df82..1f5ff02 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -285,6 +285,7 @@
@Override
public void onAnimationStart(Animator animation) {
mIsStashed = isStashed;
+ onIsStashed(mIsStashed);
}
@Override
@@ -326,4 +327,8 @@
animateForward ? UNSTASHED_TASKBAR_HANDLE_HINT_SCALE : 1)
.setDuration(TASKBAR_HINT_STASH_DURATION).start();
}
+
+ private void onIsStashed(boolean isStashed) {
+ mControllers.stashedHandleViewController.onIsStashed(isStashed);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 1882762..6b95f08 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -165,9 +165,8 @@
int offsetY = launcherDp.getTaskbarOffsetY();
setter.setFloat(mTaskbarIconTranslationYForHome, VALUE, -offsetY, LINEAR);
- int collapsedHeight = mActivity.getDefaultTaskbarWindowHeight();
- int expandedHeight = Math.max(collapsedHeight,
- mActivity.getDeviceProfile().taskbarSize + offsetY);
+ int collapsedHeight = mActivity.getDeviceProfile().taskbarSize;
+ int expandedHeight = collapsedHeight + offsetY;
setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowHeight(
anim.getAnimatedFraction() > 0 ? expandedHeight : collapsedHeight));
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 069af96..14bc380 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -84,9 +84,9 @@
import com.android.quickstep.util.QuickstepOnboardingPrefs;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
-import com.android.unfold.UnfoldTransitionFactory;
-import com.android.unfold.UnfoldTransitionProgressProvider;
-import com.android.unfold.config.UnfoldTransitionConfig;
+import com.android.systemui.unfold.UnfoldTransitionFactory;
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
+import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index ec6032d..773817f 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -45,6 +45,7 @@
import android.os.Messenger;
import android.os.ParcelUuid;
import android.os.UserHandle;
+import android.util.Log;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
@@ -57,6 +58,7 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.SpringAnimationBuilder;
+import com.android.launcher3.testing.TestProtocol;
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.fallback.RecentsState;
import com.android.quickstep.util.AppCloseConfig;
@@ -138,6 +140,10 @@
mActiveAnimationFactory = new FallbackHomeAnimationFactory(duration);
ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
Intent intent = new Intent(mGestureState.getHomeIntent());
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d(TestProtocol.L3_SWIPE_TO_HOME,
+ "createHomeAnimationFactory: " + intent.toShortString(true, true, true, false));
+ }
mActiveAnimationFactory.addGestureContract(intent);
try {
mContext.startActivity(intent, options.toBundle());
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index e948221..3c05a3e 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -20,12 +20,14 @@
import android.graphics.Rect;
import android.util.ArraySet;
+import android.util.Log;
import android.view.RemoteAnimationTarget;
import androidx.annotation.BinderThread;
import androidx.annotation.UiThread;
import com.android.launcher3.Utilities;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.Preconditions;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
@@ -95,6 +97,9 @@
RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
Rect homeContentInsets, Rect minimizedHomeBounds) {
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d(TestProtocol.L3_SWIPE_TO_HOME, "RecentsAnimationCallbacks.onAnimationStart");
+ }
// Convert appTargets to type RemoteAnimationTarget for all apps except Home app
RemoteAnimationTarget[] nonHomeApps = Arrays.stream(appTargets)
.filter(remoteAnimationTarget ->
@@ -116,6 +121,10 @@
mController::finishAnimationToApp);
} else {
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d(TestProtocol.L3_SWIPE_TO_HOME,
+ "RecentsAnimationCallbacks.onAnimationStart callback");
+ }
for (RecentsAnimationListener listener : getListeners()) {
listener.onRecentsAnimationStart(mController, targets);
}
diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
index d0fb9e5..c5ab84d 100644
--- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
+++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
@@ -24,8 +24,8 @@
import com.android.launcher3.Hotseat;
import com.android.launcher3.Launcher;
import com.android.launcher3.util.HorizontalInsettableView;
-import com.android.unfold.UnfoldTransitionProgressProvider;
-import com.android.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
/**
* Controls animations that are happening during unfolding foldable devices
diff --git a/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java b/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java
index 9eda8f4..3777c65 100644
--- a/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java
+++ b/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java
@@ -17,7 +17,7 @@
import androidx.annotation.NonNull;
-import com.android.unfold.updates.screen.ScreenStatusProvider;
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider;
import java.util.ArrayList;
import java.util.List;
diff --git a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
index 126e044..482092d 100644
--- a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
@@ -29,7 +29,7 @@
import com.android.launcher3.widget.NavigableAppWidgetHostView;
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator;
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator.TranslationApplier;
-import com.android.unfold.UnfoldTransitionProgressProvider;
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
import java.util.HashMap;
import java.util.Map;
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index ef52b41..618d9aa 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -2807,14 +2807,49 @@
resetTaskVisuals();
int pageToSnapTo = mCurrentPage;
- if ((dismissedIndex < pageToSnapTo && !showAsGrid)
- || pageToSnapTo == taskCount - 1) {
- pageToSnapTo -= 1;
- }
+ int taskViewIdToSnapTo = -1;
if (showAsGrid) {
+ // Get the id of the task view we will snap to based on the current
+ // page's relative position as the order of indices change over time due
+ // to dismissals.
+ TaskView snappedTaskView = getTaskViewAtByAbsoluteIndex(mCurrentPage);
+ if (snappedTaskView != null) {
+ if (snappedTaskView.getTaskViewId() == mFocusedTaskViewId) {
+ if (finalNextFocusedTaskView != null) {
+ taskViewIdToSnapTo = finalNextFocusedTaskView.getTaskViewId();
+ } else {
+ taskViewIdToSnapTo = mFocusedTaskViewId;
+ }
+ } else {
+ int snappedTaskViewId = snappedTaskView.getTaskViewId();
+ boolean isSnappedTaskInTopRow = mTopRowIdSet.contains(
+ snappedTaskViewId);
+ IntArray taskViewIdArray =
+ isSnappedTaskInTopRow ? getTopRowIdArray()
+ : getBottomRowIdArray();
+ int snappedIndex = taskViewIdArray.indexOf(snappedTaskViewId);
+ taskViewIdArray.removeValue(dismissedTaskViewId);
+ if (snappedIndex < taskViewIdArray.size()) {
+ taskViewIdToSnapTo = taskViewIdArray.get(snappedIndex);
+ } else if (snappedIndex == taskViewIdArray.size()) {
+ // If the snapped task is the last item from the dismissed row,
+ // snap to the same column in the other grid row
+ IntArray inverseRowTaskViewIdArray =
+ isSnappedTaskInTopRow ? getBottomRowIdArray()
+ : getTopRowIdArray();
+ if (snappedIndex < inverseRowTaskViewIdArray.size()) {
+ taskViewIdToSnapTo = inverseRowTaskViewIdArray.get(
+ snappedIndex);
+ }
+ }
+ }
+ }
+
int primaryScroll = mOrientationHandler.getPrimaryScroll(RecentsView.this);
int currentPageScroll = getScrollForPage(pageToSnapTo);
mCurrentPageScrollDiff = primaryScroll - currentPageScroll;
+ } else if (dismissedIndex < pageToSnapTo || pageToSnapTo == taskCount - 1) {
+ pageToSnapTo -= 1;
}
removeViewInLayout(dismissedTaskView);
mTopRowIdSet.remove(dismissedTaskViewId);
@@ -2834,40 +2869,50 @@
// Update scroll and snap to page.
updateScrollSynchronously();
- int highestVisibleTaskIndex = getHighestVisibleTaskIndex();
- if (highestVisibleTaskIndex < Integer.MAX_VALUE) {
- TaskView taskView = getTaskViewAt(highestVisibleTaskIndex);
+ if (showAsGrid) {
+ // Rebalance tasks in the grid
+ int highestVisibleTaskIndex = getHighestVisibleTaskIndex();
+ if (highestVisibleTaskIndex < Integer.MAX_VALUE) {
+ TaskView taskView = getTaskViewAt(highestVisibleTaskIndex);
- boolean shouldRebalance = false;
- int screenStart = mOrientationHandler.getPrimaryScroll(
- RecentsView.this);
- int taskStart = mOrientationHandler.getChildStart(taskView)
- + (int) taskView.getOffsetAdjustment(
- /*fullscreenEnabled=*/ false,
- /*gridEnabled=*/ true);
-
- // Rebalance only if there is a maximum gap between the task and the
- // screen's edge; this ensures that rebalanced tasks are outside the
- // visible screen.
- if (mIsRtl) {
- shouldRebalance = taskStart <= screenStart + mPageSpacing;
- } else {
- int screenEnd = screenStart + mOrientationHandler.getMeasuredSize(
+ boolean shouldRebalance = false;
+ int screenStart = mOrientationHandler.getPrimaryScroll(
RecentsView.this);
- int taskSize = (int) (mOrientationHandler.getMeasuredSize(taskView)
- * taskView.getSizeAdjustment(/*fullscreenEnabled=*/ false));
- int taskEnd = taskStart + taskSize;
+ int taskStart = mOrientationHandler.getChildStart(taskView)
+ + (int) taskView.getOffsetAdjustment(/*fullscreenEnabled=*/
+ false, /*gridEnabled=*/ true);
- shouldRebalance = taskEnd >= screenEnd - mPageSpacing;
+ // Rebalance only if there is a maximum gap between the task and the
+ // screen's edge; this ensures that rebalanced tasks are outside the
+ // visible screen.
+ if (mIsRtl) {
+ shouldRebalance = taskStart <= screenStart + mPageSpacing;
+ } else {
+ int screenEnd =
+ screenStart + mOrientationHandler.getMeasuredSize(
+ RecentsView.this);
+ int taskSize = (int) (mOrientationHandler.getMeasuredSize(
+ taskView) * taskView
+ .getSizeAdjustment(/*fullscreenEnabled=*/false));
+ int taskEnd = taskStart + taskSize;
+
+ shouldRebalance = taskEnd >= screenEnd - mPageSpacing;
+ }
+
+ if (shouldRebalance) {
+ updateGridProperties(/*isTaskDismissal=*/ true,
+ highestVisibleTaskIndex);
+ updateScrollSynchronously();
+ }
}
- if (shouldRebalance) {
- updateGridProperties(/*isTaskDismissal=*/ true,
- highestVisibleTaskIndex);
- updateScrollSynchronously();
+ // If snapping to another page due to indices rearranging, find the new
+ // index after dismissal & rearrange using the task view id.
+ if (taskViewIdToSnapTo != -1) {
+ pageToSnapTo = indexOfChild(
+ getTaskViewFromTaskViewId(taskViewIdToSnapTo));
}
}
-
setCurrentPage(pageToSnapTo);
dispatchScrollChanged();
}
@@ -2880,10 +2925,32 @@
}
/**
+ * Returns all the tasks in the top row, without the focused task
+ */
+ private IntArray getTopRowIdArray() {
+ if (mTopRowIdSet.isEmpty()) {
+ return new IntArray(0);
+ }
+ IntArray topArray = new IntArray(mTopRowIdSet.size());
+ int taskViewCount = getTaskViewCount();
+ for (int i = 0; i < taskViewCount; i++) {
+ int taskViewId = getTaskViewAt(i).getTaskViewId();
+ if (mTopRowIdSet.contains(taskViewId)) {
+ topArray.add(taskViewId);
+ }
+ }
+ return topArray;
+ }
+
+ /**
* Returns all the tasks in the bottom row, without the focused task
*/
private IntArray getBottomRowIdArray() {
- IntArray bottomArray = new IntArray();
+ int bottomRowIdArraySize = getTaskViewCount() - mTopRowIdSet.size() - 1;
+ if (bottomRowIdArraySize <= 0) {
+ return new IntArray(0);
+ }
+ IntArray bottomArray = new IntArray(bottomRowIdArraySize);
int taskViewCount = getTaskViewCount();
for (int i = 0; i < taskViewCount; i++) {
int taskViewId = getTaskViewAt(i).getTaskViewId();
@@ -2904,7 +2971,7 @@
if (mTopRowIdSet.isEmpty()) return Integer.MAX_VALUE; // return earlier
int lastVisibleIndex = Integer.MAX_VALUE;
- IntArray topRowIdArray = mTopRowIdSet.getArray();
+ IntArray topRowIdArray = getTopRowIdArray();
IntArray bottomRowIdArray = getBottomRowIdArray();
int balancedColumns = Math.min(bottomRowIdArray.size(), topRowIdArray.size());
@@ -4062,6 +4129,7 @@
setCurrentTask(-1);
mRecentsAnimationController = null;
executeSideTaskLaunchCallback();
+ mRemoteTargetHandles = null;
}
public void setDisallowScrollToClearAll(boolean disallowScrollToClearAll) {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 5986649..f84a05c 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -674,6 +674,7 @@
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
ActivityOptionsWrapper opts = mActivity.getActivityLaunchOptions(this, null);
+ opts.options.setLaunchDisplayId(getRootViewDisplayId());
if (ActivityManagerWrapper.getInstance()
.startActivityFromRecents(mTask.key, opts.options)) {
RecentsView recentsView = getRecentsView();
@@ -714,6 +715,7 @@
// Indicate success once the system has indicated that the transition has started
ActivityOptions opts = ActivityOptionsCompat.makeCustomAnimation(
getContext(), 0, 0, () -> callback.accept(true), MAIN_EXECUTOR.getHandler());
+ opts.setLaunchDisplayId(getRootViewDisplayId());
if (freezeTaskList) {
ActivityOptionsCompat.setFreezeRecentTasksList(opts);
}
@@ -1526,6 +1528,10 @@
mDigitalWellBeingToast.setBannerColorTint(tintColor, amount);
}
+ private int getRootViewDisplayId() {
+ return getRootView().getDisplay().getDisplayId();
+ }
+
/**
* We update and subsequently draw these in {@link #setFullscreenProgress(float)}.
*/
diff --git a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
index f33abb0..0f5a1c8 100644
--- a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
+++ b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
@@ -19,9 +19,9 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static androidx.test.InstrumentationRegistry.getTargetContext;
-import static com.android.launcher3.common.WidgetUtils.createWidgetInfo;
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.util.WidgetUtils.createWidgetInfo;
import static com.android.quickstep.NavigationModeSwitchRule.Mode.ZERO_BUTTON;
import static org.junit.Assert.assertEquals;
diff --git a/robolectric_tests/Android.bp b/robolectric_tests/Android.bp
index 9ed26ff..6473d00 100644
--- a/robolectric_tests/Android.bp
+++ b/robolectric_tests/Android.bp
@@ -40,7 +40,6 @@
name: "LauncherRoboTests",
srcs: [
":launcher3-robolectric-src",
- ":launcher3-test-src-common",
],
java_resources: [":launcher3-robolectric-resources"],
static_libs: [
diff --git a/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java b/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
deleted file mode 100644
index ea75548..0000000
--- a/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2020 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.ui;
-
-import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
-import static com.android.launcher3.util.LauncherUIHelper.buildAndBindLauncher;
-import static com.android.launcher3.util.LauncherUIHelper.doLayout;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-
-import android.content.Context;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.MotionEvent.PointerProperties;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.folder.Folder;
-import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.folder.FolderPagedView;
-import com.android.launcher3.util.LauncherLayoutBuilder;
-import com.android.launcher3.util.LauncherLayoutBuilder.FolderBuilder;
-import com.android.launcher3.util.LauncherModelHelper;
-import com.android.launcher3.widget.picker.WidgetsFullSheet;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.LooperMode;
-import org.robolectric.annotation.LooperMode.Mode;
-import org.robolectric.shadows.ShadowLooper;
-
-/**
- * Tests scroll behavior at various Launcher UI components
- */
-@RunWith(RobolectricTestRunner.class)
-@LooperMode(Mode.PAUSED)
-@org.junit.Ignore
-public class LauncherUIScrollTest {
-
- private Context mTargetContext;
- private InvariantDeviceProfile mIdp;
- private LauncherModelHelper mModelHelper;
-
- private LauncherLayoutBuilder mLayoutBuilder;
-
- @Before
- public void setup() throws Exception {
- mModelHelper = new LauncherModelHelper();
- mTargetContext = RuntimeEnvironment.application;
- mIdp = InvariantDeviceProfile.INSTANCE.get(mTargetContext);
-
- Settings.Global.putFloat(mTargetContext.getContentResolver(),
- Settings.Global.WINDOW_ANIMATION_SCALE, 0);
-
- mModelHelper.installApp(TEST_PACKAGE);
- // LayoutBuilder with 3 workspace pages
- mLayoutBuilder = new LauncherLayoutBuilder()
- .atWorkspace(0, mIdp.numRows - 1, 0).putApp(TEST_PACKAGE, TEST_PACKAGE)
- .atWorkspace(0, mIdp.numRows - 1, 1).putApp(TEST_PACKAGE, TEST_PACKAGE)
- .atWorkspace(0, mIdp.numRows - 1, 2).putApp(TEST_PACKAGE, TEST_PACKAGE);
- }
-
- @Test
- public void testWorkspacePagesBound() throws Exception {
- // Verify that the workspace if bound synchronously
- Launcher launcher = loadLauncher();
- assertEquals(3, launcher.getWorkspace().getPageCount());
- assertEquals(0, launcher.getWorkspace().getCurrentPage());
-
- launcher.dispatchGenericMotionEvent(createScrollEvent(-1));
- assertNotEquals("Workspace was not scrolled",
- 0, launcher.getWorkspace().getNextPage());
- }
-
- @Test
- public void testAllAppsScroll() throws Exception {
- // Install 100 apps
- for (int i = 0; i < 100; i++) {
- mModelHelper.installApp(TEST_PACKAGE + i);
- }
-
- // Bind and open all-apps
- Launcher launcher = loadLauncher();
- launcher.getStateManager().goToState(LauncherState.ALL_APPS, false);
- doLayout(launcher);
-
- int currentScroll = launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
- launcher.dispatchGenericMotionEvent(createScrollEvent(-1));
- int newScroll = launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
-
- assertNotEquals("All Apps was not scrolled", currentScroll, newScroll);
- assertEquals("Workspace was scrolled", 0, launcher.getWorkspace().getNextPage());
- }
-
- @Test
- public void testWidgetsListScroll() throws Exception {
- // Install 100 widgets
- for (int i = 0; i < 100; i++) {
- mModelHelper.installCustomShortcut(TEST_PACKAGE + i, "shortcutProvider");
- }
-
- // Bind and open widgets
- Launcher launcher = loadLauncher();
- WidgetsFullSheet widgets = WidgetsFullSheet.show(launcher, false);
- doLayout(launcher);
-
- int currentScroll = widgets.getRecyclerView().getCurrentScrollY();
- launcher.dispatchGenericMotionEvent(createScrollEvent(-1));
- int newScroll = widgets.getRecyclerView().getCurrentScrollY();
- assertNotEquals("Widgets was not scrolled", currentScroll, newScroll);
- assertEquals("Workspace was scrolled", 0, launcher.getWorkspace().getNextPage());
- }
-
- @Test
- public void testFolderPageScroll() throws Exception {
- // Add a folder with multiple icons
- FolderBuilder fb = mLayoutBuilder.atWorkspace(mIdp.numColumns / 2, mIdp.numRows / 2, 0)
- .putFolder(0);
- for (int i = 0; i < 100; i++) {
- fb.addApp(TEST_PACKAGE, TEST_PACKAGE);
- }
-
- // Bind and open folder
- Launcher launcher = loadLauncher();
- doLayout(launcher);
- launcher.getWorkspace().getFirstMatch((i, v) -> v instanceof FolderIcon).performClick();
- ShadowLooper.idleMainLooper();
- doLayout(launcher);
- FolderPagedView folderPages = Folder.getOpen(launcher).getContent();
-
- assertEquals(0, folderPages.getNextPage());
- launcher.dispatchGenericMotionEvent(createScrollEvent(-1));
- assertNotEquals("Folder page was not scrolled", 0, folderPages.getNextPage());
- assertEquals("Workspace was scrolled", 0, launcher.getWorkspace().getNextPage());
- }
-
- private Launcher loadLauncher() throws Exception {
- mModelHelper.setupDefaultLayoutProvider(mLayoutBuilder).loadModelSync();
- return buildAndBindLauncher();
- }
-
- private static MotionEvent createScrollEvent(int scroll) {
- DeviceProfile dp = InvariantDeviceProfile.INSTANCE
- .get(RuntimeEnvironment.application).supportedProfiles.get(0);
-
- final PointerProperties[] pointerProperties = new PointerProperties[1];
- pointerProperties[0] = new PointerProperties();
- pointerProperties[0].id = 0;
- final MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[1];
- coords[0] = new MotionEvent.PointerCoords();
- coords[0].setAxisValue(MotionEvent.AXIS_VSCROLL, scroll);
- coords[0].x = dp.widthPx / 2;
- coords[0].y = dp.heightPx / 2;
-
- final long time = SystemClock.uptimeMillis();
- return MotionEvent.obtain(time, time, MotionEvent.ACTION_SCROLL, 1,
- pointerProperties, coords, 0, 0, 1.0f, 1.0f, 0, 0,
- InputDevice.SOURCE_CLASS_POINTER, 0);
- }
-
-}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/CachingWidgetPreviewLoaderTest.java b/robolectric_tests/src/com/android/launcher3/widget/CachingWidgetPreviewLoaderTest.java
deleted file mode 100644
index 1090d1e..0000000
--- a/robolectric_tests/src/com/android/launcher3/widget/CachingWidgetPreviewLoaderTest.java
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.widget;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-import android.graphics.Bitmap;
-import android.os.CancellationSignal;
-import android.os.UserHandle;
-import android.util.Size;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.testing.TestActivity;
-import com.android.launcher3.widget.WidgetPreviewLoader.WidgetPreviewLoadedCallback;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-
-import java.util.Arrays;
-import java.util.Collections;
-
-@RunWith(RobolectricTestRunner.class)
-public class CachingWidgetPreviewLoaderTest {
- private final Size SIZE_10_10 = new Size(10, 10);
- private final Size SIZE_20_20 = new Size(20, 20);
- private static final String TEST_PACKAGE = "com.example.test";
- private final ComponentName TEST_PROVIDER =
- new ComponentName(TEST_PACKAGE, ".WidgetProvider");
- private final ComponentName TEST_PROVIDER2 =
- new ComponentName(TEST_PACKAGE, ".WidgetProvider2");
- private final Bitmap BITMAP = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
- private final Bitmap BITMAP2 = Bitmap.createBitmap(20, 20, Bitmap.Config.ARGB_8888);
-
-
- @Mock private CancellationSignal mCancellationSignal;
- @Mock private WidgetPreviewLoader mDelegate;
- @Mock private IconCache mIconCache;
- @Mock private DeviceProfile mDeviceProfile;
- @Mock private LauncherAppWidgetProviderInfo mProviderInfo;
- @Mock private LauncherAppWidgetProviderInfo mProviderInfo2;
- @Mock private WidgetPreviewLoadedCallback mPreviewLoadedCallback;
- @Mock private WidgetPreviewLoadedCallback mPreviewLoadedCallback2;
- @Captor private ArgumentCaptor<WidgetPreviewLoadedCallback> mCallbackCaptor;
-
- private TestActivity mTestActivity;
- private CachingWidgetPreviewLoader mLoader;
- private WidgetItem mWidgetItem;
- private WidgetItem mWidgetItem2;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mLoader = new CachingWidgetPreviewLoader(mDelegate);
-
- mTestActivity = Robolectric.buildActivity(TestActivity.class).setup().get();
- mTestActivity.setDeviceProfile(mDeviceProfile);
-
- when(mDelegate.loadPreview(any(), any(), any(), any())).thenReturn(mCancellationSignal);
-
- mProviderInfo.provider = TEST_PROVIDER;
- when(mProviderInfo.getProfile()).thenReturn(new UserHandle(0));
-
- mProviderInfo2.provider = TEST_PROVIDER2;
- when(mProviderInfo2.getProfile()).thenReturn(new UserHandle(0));
-
- InvariantDeviceProfile testProfile = new InvariantDeviceProfile();
- testProfile.numRows = 5;
- testProfile.numColumns = 5;
-
- mWidgetItem = new WidgetItem(mProviderInfo, testProfile, mIconCache);
- mWidgetItem2 = new WidgetItem(mProviderInfo2, testProfile, mIconCache);
- }
-
- @Test
- public void getPreview_notInCache_shouldReturnNull() {
- assertThat(mLoader.getPreview(mWidgetItem, SIZE_10_10)).isNull();
- }
-
- @Test
- public void getPreview_notInCache_shouldNotCallDelegate() {
- mLoader.getPreview(mWidgetItem, SIZE_10_10);
-
- verifyZeroInteractions(mDelegate);
- }
-
- @Test
- public void getPreview_inCache_shouldReturnCachedBitmap() {
- loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
-
- assertThat(mLoader.getPreview(mWidgetItem, SIZE_10_10)).isEqualTo(BITMAP);
- }
-
- @Test
- public void getPreview_otherSizeInCache_shouldReturnNull() {
- loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
-
- assertThat(mLoader.getPreview(mWidgetItem, SIZE_20_20)).isNull();
- }
-
- @Test
- public void getPreview_otherItemInCache_shouldReturnNull() {
- loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
-
- assertThat(mLoader.getPreview(mWidgetItem2, SIZE_10_10)).isNull();
- }
-
- @Test
- public void getPreview_shouldStoreMultipleSizesPerItem() {
- loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
- loadPreviewIntoCache(mWidgetItem, SIZE_20_20, BITMAP2);
-
- assertThat(mLoader.getPreview(mWidgetItem, SIZE_10_10)).isEqualTo(BITMAP);
- assertThat(mLoader.getPreview(mWidgetItem, SIZE_20_20)).isEqualTo(BITMAP2);
- }
-
- @Test
- public void loadPreview_notInCache_shouldStartLoading() {
- mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
-
- verify(mDelegate).loadPreview(eq(mTestActivity), eq(mWidgetItem), eq(SIZE_10_10), any());
- verifyZeroInteractions(mPreviewLoadedCallback);
- }
-
- @Test
- public void loadPreview_thenLoaded_shouldCallBack() {
- mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
- verify(mDelegate).loadPreview(any(), any(), any(), mCallbackCaptor.capture());
- WidgetPreviewLoadedCallback loaderCallback = mCallbackCaptor.getValue();
-
- loaderCallback.onPreviewLoaded(BITMAP);
-
- verify(mPreviewLoadedCallback).onPreviewLoaded(BITMAP);
- }
-
- @Test
- public void loadPreview_thenCancelled_shouldCancelDelegateRequest() {
- CancellationSignal cancellationSignal =
- mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
-
- cancellationSignal.cancel();
-
- verify(mCancellationSignal).cancel();
- verifyZeroInteractions(mPreviewLoadedCallback);
- assertThat(mLoader.getPreview(mWidgetItem, SIZE_10_10)).isNull();
- }
-
- @Test
- public void loadPreview_thenCancelled_otherCallListening_shouldNotCancelDelegateRequest() {
- CancellationSignal cancellationSignal1 =
- mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
- mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback2);
-
- cancellationSignal1.cancel();
-
- verifyZeroInteractions(mCancellationSignal);
- }
-
- @Test
- public void loadPreview_thenCancelled_otherCallListening_loaded_shouldCallBackToNonCancelled() {
- CancellationSignal cancellationSignal1 =
- mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
- mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback2);
- verify(mDelegate).loadPreview(any(), any(), any(), mCallbackCaptor.capture());
- WidgetPreviewLoadedCallback loaderCallback = mCallbackCaptor.getValue();
-
- cancellationSignal1.cancel();
- loaderCallback.onPreviewLoaded(BITMAP);
-
- verifyZeroInteractions(mPreviewLoadedCallback);
- verify(mPreviewLoadedCallback2).onPreviewLoaded(BITMAP);
- assertThat(mLoader.getPreview(mWidgetItem, SIZE_10_10)).isEqualTo(BITMAP);
- }
-
- @Test
- public void loadPreview_thenCancelled_bothCallsCancelled_shouldCancelDelegateRequest() {
- CancellationSignal cancellationSignal1 =
- mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
- CancellationSignal cancellationSignal2 =
- mLoader.loadPreview(
- mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback2);
-
- cancellationSignal1.cancel();
- cancellationSignal2.cancel();
-
- verify(mCancellationSignal).cancel();
- verifyZeroInteractions(mPreviewLoadedCallback);
- verifyZeroInteractions(mPreviewLoadedCallback2);
- assertThat(mLoader.getPreview(mWidgetItem, SIZE_10_10)).isNull();
- }
-
- @Test
- public void loadPreview_multipleCallbacks_shouldOnlyCallDelegateOnce() {
- mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
- mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback2);
-
- verify(mDelegate).loadPreview(any(), any(), any(), any());
- }
-
- @Test
- public void loadPreview_multipleCallbacks_shouldForwardResultToEachCallback() {
- mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
- mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback2);
-
- verify(mDelegate).loadPreview(any(), any(), any(), mCallbackCaptor.capture());
- WidgetPreviewLoadedCallback loaderCallback = mCallbackCaptor.getValue();
-
- loaderCallback.onPreviewLoaded(BITMAP);
-
- verify(mPreviewLoadedCallback).onPreviewLoaded(BITMAP);
- verify(mPreviewLoadedCallback2).onPreviewLoaded(BITMAP);
- }
-
- @Test
- public void loadPreview_inCache_shouldCallBackImmediately() {
- loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
- reset(mDelegate);
-
- mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
-
- verify(mPreviewLoadedCallback).onPreviewLoaded(BITMAP);
- verifyZeroInteractions(mDelegate);
- }
-
- @Test
- public void loadPreview_thenLoaded_thenCancelled_shouldNotRemovePreviewFromCache() {
- CancellationSignal cancellationSignal =
- mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
- verify(mDelegate).loadPreview(any(), any(), any(), mCallbackCaptor.capture());
- WidgetPreviewLoadedCallback loaderCallback = mCallbackCaptor.getValue();
- loaderCallback.onPreviewLoaded(BITMAP);
-
- cancellationSignal.cancel();
-
- assertThat(mLoader.getPreview(mWidgetItem, SIZE_10_10)).isEqualTo(BITMAP);
- }
-
- @Test
- public void isPreviewLoaded_notLoaded_shouldReturnFalse() {
- assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
- }
-
- @Test
- public void isPreviewLoaded_otherSizeLoaded_shouldReturnFalse() {
- loadPreviewIntoCache(mWidgetItem, SIZE_20_20, BITMAP);
-
- assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
- }
-
- @Test
- public void isPreviewLoaded_otherItemLoaded_shouldReturnFalse() {
- loadPreviewIntoCache(mWidgetItem2, SIZE_10_10, BITMAP);
-
- assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
- }
-
- @Test
- public void isPreviewLoaded_loaded_shouldReturnTrue() {
- loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
-
- assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isTrue();
- }
-
- @Test
- public void clearPreviews_notInCache_shouldBeNoOp() {
- mLoader.clearPreviews(Collections.singletonList(mWidgetItem));
-
- assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
- }
-
- @Test
- public void clearPreviews_inCache_shouldRemovePreview() {
- loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
-
- mLoader.clearPreviews(Collections.singletonList(mWidgetItem));
-
- assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
- }
-
- @Test
- public void clearPreviews_inCache_multipleSizes_shouldRemoveAllSizes() {
- loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
- loadPreviewIntoCache(mWidgetItem, SIZE_20_20, BITMAP);
-
- mLoader.clearPreviews(Collections.singletonList(mWidgetItem));
-
- assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
- assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_20_20)).isFalse();
- }
-
- @Test
- public void clearPreviews_inCache_otherItems_shouldOnlyRemoveSpecifiedItems() {
- loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
- loadPreviewIntoCache(mWidgetItem2, SIZE_10_10, BITMAP);
-
- mLoader.clearPreviews(Collections.singletonList(mWidgetItem));
-
- assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
- assertThat(mLoader.isPreviewLoaded(mWidgetItem2, SIZE_10_10)).isTrue();
- }
-
- @Test
- public void clearPreviews_inCache_otherItems_shouldRemoveAllSpecifiedItems() {
- loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
- loadPreviewIntoCache(mWidgetItem2, SIZE_10_10, BITMAP);
-
- mLoader.clearPreviews(Arrays.asList(mWidgetItem, mWidgetItem2));
-
- assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
- assertThat(mLoader.isPreviewLoaded(mWidgetItem2, SIZE_10_10)).isFalse();
- }
-
- @Test
- public void clearPreviews_loading_shouldCancelLoad() {
- mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
-
- mLoader.clearPreviews(Collections.singletonList(mWidgetItem));
-
- verify(mCancellationSignal).cancel();
- }
-
- @Test
- public void clearAll_cacheEmpty_shouldBeNoOp() {
- mLoader.clearAll();
-
- assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
- }
-
- @Test
- public void clearAll_inCache_shouldRemovePreview() {
- loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
-
- mLoader.clearAll();
-
- assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
- }
-
- @Test
- public void clearAll_inCache_multipleSizes_shouldRemoveAllSizes() {
- loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
- loadPreviewIntoCache(mWidgetItem, SIZE_20_20, BITMAP);
-
- mLoader.clearAll();
-
- assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
- assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_20_20)).isFalse();
- }
-
- @Test
- public void clearAll_inCache_multipleItems_shouldRemoveAll() {
- loadPreviewIntoCache(mWidgetItem, SIZE_10_10, BITMAP);
- loadPreviewIntoCache(mWidgetItem, SIZE_20_20, BITMAP);
- loadPreviewIntoCache(mWidgetItem2, SIZE_20_20, BITMAP);
-
- mLoader.clearAll();
-
- assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_10_10)).isFalse();
- assertThat(mLoader.isPreviewLoaded(mWidgetItem, SIZE_20_20)).isFalse();
- assertThat(mLoader.isPreviewLoaded(mWidgetItem2, SIZE_20_20)).isFalse();
- }
-
- @Test
- public void clearAll_loading_shouldCancelLoad() {
- mLoader.loadPreview(mTestActivity, mWidgetItem, SIZE_10_10, mPreviewLoadedCallback);
-
- mLoader.clearAll();
-
- verify(mCancellationSignal).cancel();
- }
-
- private void loadPreviewIntoCache(WidgetItem widgetItem, Size size, Bitmap bitmap) {
- reset(mDelegate);
- mLoader.loadPreview(mTestActivity, widgetItem, size, ignored -> {});
- verify(mDelegate).loadPreview(any(), any(), any(), mCallbackCaptor.capture());
- WidgetPreviewLoadedCallback loaderCallback = mCallbackCaptor.getValue();
-
- loaderCallback.onPreviewLoaded(bitmap);
- }
-}
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 9778b61..ec96c6d 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -145,6 +145,7 @@
/**
* Returns {@link StatsLogManager} for user event logging.
*/
+ @Override
public StatsLogManager getStatsLogManager() {
if (mStatsLogManager == null) {
mStatsLogManager = StatsLogManager.newInstance(this);
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 3d6be69..702b73a 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -48,7 +48,6 @@
import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.launcher3.util.Themes;
-import com.android.launcher3.widget.DatabaseWidgetPreviewLoader;
import com.android.launcher3.widget.custom.CustomWidgetManager;
public class LauncherAppState {
@@ -64,7 +63,6 @@
private final LauncherModel mModel;
private final IconProvider mIconProvider;
private final IconCache mIconCache;
- private final DatabaseWidgetPreviewLoader mWidgetCache;
private final InvariantDeviceProfile mInvariantDeviceProfile;
private final RunnableList mOnTerminateCallback = new RunnableList();
@@ -139,7 +137,6 @@
mIconProvider = new IconProvider(context, Themes.isThemedIconEnabled(context));
mIconCache = new IconCache(mContext, mInvariantDeviceProfile,
iconCacheFileName, mIconProvider);
- mWidgetCache = new DatabaseWidgetPreviewLoader(mContext, mIconCache);
mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext));
mOnTerminateCallback.add(mIconCache::close);
}
@@ -155,7 +152,6 @@
LauncherIcons.clearPool();
mIconCache.updateIconParams(
mInvariantDeviceProfile.fillResIconDpi, mInvariantDeviceProfile.iconBitmapSize);
- mWidgetCache.refresh();
mModel.forceReload();
}
@@ -181,10 +177,6 @@
return mModel;
}
- public DatabaseWidgetPreviewLoader getWidgetCache() {
- return mWidgetCache;
- }
-
public InvariantDeviceProfile getInvariantDeviceProfile() {
return mInvariantDeviceProfile;
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index d162abd..38beeea 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -683,8 +683,9 @@
CellLayout finalScreen = mWorkspaceScreens.get(finalScreenId);
// If the final screen is empty, convert it to the extra empty screen
- if (finalScreen.getShortcutsAndWidgets().getChildCount() == 0 &&
- !finalScreen.isDropPending()) {
+ if (finalScreen != null
+ && finalScreen.getShortcutsAndWidgets().getChildCount() == 0
+ && !finalScreen.isDropPending()) {
mWorkspaceScreens.remove(finalScreenId);
mScreenOrder.removeValue(finalScreenId);
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 92ed18a..466b268 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -286,9 +286,7 @@
@Override
protected void onPostExecute(WidgetItem item) {
- mWidgetCell.setPreviewSize(item);
- mWidgetCell.applyFromCellItem(item, mApp.getWidgetCache());
- mWidgetCell.ensurePreview();
+ mWidgetCell.applyFromCellItem(item);
}
}.executeOnExecutor(MODEL_EXECUTOR);
// TODO: Create a worker looper executor and reuse that everywhere.
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 82b0f7c..83fb3d1 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -123,7 +123,6 @@
iconCache.updateIconsForPkg(packages[i], mUser);
activitiesLists.put(
packages[i], appsList.updatePackage(context, packages[i], mUser));
- app.getWidgetCache().removePackage(packages[i], mUser);
// The update may have changed which shortcuts/widgets are available.
// Refresh the widgets for the package if we have an activity running.
@@ -148,7 +147,6 @@
for (int i = 0; i < N; i++) {
if (DEBUG) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
appsList.removePackage(packages[i], mUser);
- app.getWidgetCache().removePackage(packages[i], mUser);
}
flagOp = FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
break;
diff --git a/src/com/android/launcher3/recyclerview/ViewHolderBinder.java b/src/com/android/launcher3/recyclerview/ViewHolderBinder.java
index 6215827..31436c4 100644
--- a/src/com/android/launcher3/recyclerview/ViewHolderBinder.java
+++ b/src/com/android/launcher3/recyclerview/ViewHolderBinder.java
@@ -22,6 +22,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.List;
/**
* Creates and populates views with data
@@ -46,7 +47,7 @@
V newViewHolder(ViewGroup parent);
/** Populate UI references in {@link ViewHolder} with data. */
- void bindViewHolder(V viewHolder, T data, @ListPosition int position);
+ void bindViewHolder(V viewHolder, T data, @ListPosition int position, List<Object> payloads);
/**
* Called when the view is recycled. Views are recycled in batches once they are sufficiently
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index b34af97..24d3fd4 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -18,6 +18,7 @@
import static android.animation.ValueAnimator.areAnimatorsEnabled;
+import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.AnimatorPlaybackController.callListenerCommandRecursively;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_ALL_ANIMATIONS;
@@ -27,12 +28,14 @@
import android.animation.AnimatorSet;
import android.os.Handler;
import android.os.Looper;
+import android.util.Log;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
+import com.android.launcher3.testing.TestProtocol;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -253,6 +256,9 @@
if (listener != null) {
animation.addListener(listener);
}
+ if (TestProtocol.sDebugTracing && state == NORMAL) {
+ Log.d(TestProtocol.L3_SWIPE_TO_HOME, "goToStateAnimated: " + state);
+ }
mUiHandler.post(new StartAnimRunnable(animation));
}
@@ -328,11 +334,17 @@
@Override
public void onAnimationStart(Animator animation) {
// Change the internal state only when the transition actually starts
+ if (TestProtocol.sDebugTracing && state == NORMAL) {
+ Log.d(TestProtocol.L3_SWIPE_TO_HOME, "onAnimationStart: " + state);
+ }
onStateTransitionStart(state);
}
@Override
public void onAnimationSuccess(Animator animator) {
+ if (TestProtocol.sDebugTracing && state == NORMAL) {
+ Log.d(TestProtocol.L3_SWIPE_TO_HOME, "onAnimationEnd: " + state);
+ }
onStateTransitionEnd(state);
}
};
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 1c5b31b..c484811 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -119,4 +119,5 @@
public static final String FALLBACK_ACTIVITY_NO_SET = "b/181019015";
public static final String THIRD_PARTY_LAUNCHER_NOT_SET = "b/187080582";
public static final String TASK_VIEW_ID_CRASH = "b/195430732";
+ public static final String L3_SWIPE_TO_HOME = "b/192018189";
}
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index b95904e..c822213 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -27,6 +27,7 @@
import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.ViewCache;
@@ -109,6 +110,10 @@
return null;
}
+ default StatsLogManager getStatsLogManager() {
+ return StatsLogManager.newInstance((Context) this);
+ }
+
/**
* Returns the ActivityContext associated with the given Context.
*/
diff --git a/src/com/android/launcher3/widget/CachingWidgetPreviewLoader.java b/src/com/android/launcher3/widget/CachingWidgetPreviewLoader.java
deleted file mode 100644
index afceadd..0000000
--- a/src/com/android/launcher3/widget/CachingWidgetPreviewLoader.java
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.widget;
-
-import android.graphics.Bitmap;
-import android.os.CancellationSignal;
-import android.util.Size;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
-import androidx.collection.ArrayMap;
-import androidx.collection.ArraySet;
-
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.util.ComponentKey;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-/** Wrapper around {@link DatabaseWidgetPreviewLoader} that contains caching logic. */
-public class CachingWidgetPreviewLoader implements WidgetPreviewLoader {
-
- @NonNull private final WidgetPreviewLoader mDelegate;
- @NonNull private final Map<ComponentKey, Map<Size, CacheResult>> mCache = new ArrayMap<>();
-
- public CachingWidgetPreviewLoader(@NonNull WidgetPreviewLoader delegate) {
- mDelegate = delegate;
- }
-
- /** Returns whether the preview is loaded for the item and size. */
- public boolean isPreviewLoaded(@NonNull WidgetItem item, @NonNull Size previewSize) {
- return getPreview(item, previewSize) != null;
- }
-
- /** Returns the cached preview for the item and size, or null if there is none. */
- @Nullable
- public Bitmap getPreview(@NonNull WidgetItem item, @NonNull Size previewSize) {
- CacheResult cacheResult = getCacheResult(item, previewSize);
- if (cacheResult instanceof CacheResult.Loaded) {
- return ((CacheResult.Loaded) cacheResult).mBitmap;
- } else {
- return null;
- }
- }
-
- @NonNull
- private CacheResult getCacheResult(@NonNull WidgetItem item, @NonNull Size previewSize) {
- synchronized (mCache) {
- Map<Size, CacheResult> cacheResults = mCache.get(toComponentKey(item));
- if (cacheResults == null) {
- return CacheResult.MISS;
- }
-
- return cacheResults.getOrDefault(previewSize, CacheResult.MISS);
- }
- }
-
- /**
- * Puts the result in the cache for the item and size. Returns the value previously in the
- * cache, or null if there was none.
- */
- @Nullable
- private CacheResult putCacheResult(
- @NonNull WidgetItem item,
- @NonNull Size previewSize,
- @Nullable CacheResult cacheResult) {
- ComponentKey key = toComponentKey(item);
- synchronized (mCache) {
- Map<Size, CacheResult> cacheResults = mCache.getOrDefault(key, new ArrayMap<>());
- CacheResult previous;
- if (cacheResult == null) {
- previous = cacheResults.remove(previewSize);
- if (cacheResults.isEmpty()) {
- mCache.remove(key);
- } else {
- previous = cacheResults.put(previewSize, cacheResult);
- mCache.put(key, cacheResults);
- }
- } else {
- previous = cacheResults.put(previewSize, cacheResult);
- mCache.put(key, cacheResults);
- }
- return previous;
- }
- }
-
- private void removeCacheResult(@NonNull WidgetItem item, @NonNull Size previewSize) {
- ComponentKey key = toComponentKey(item);
- synchronized (mCache) {
- Map<Size, CacheResult> cacheResults = mCache.getOrDefault(key, new ArrayMap<>());
- cacheResults.remove(previewSize);
- mCache.put(key, cacheResults);
- }
- }
-
- /**
- * Gets the preview for the widget item and size, using the value in the cache if stored.
- *
- * @return a {@link CancellationSignal}, which can cancel the request before it loads
- */
- @Override
- @UiThread
- @NonNull
- public CancellationSignal loadPreview(
- @NonNull BaseActivity activity, @NonNull WidgetItem item, @NonNull Size previewSize,
- @NonNull WidgetPreviewLoadedCallback callback) {
- CancellationSignal signal = new CancellationSignal();
- signal.setOnCancelListener(() -> {
- synchronized (mCache) {
- CacheResult cacheResult = getCacheResult(item, previewSize);
- if (!(cacheResult instanceof CacheResult.Loading)) {
- // If the key isn't actively loading, then this is a no-op. Cancelling loading
- // shouldn't clear the cache if we've already loaded.
- return;
- }
-
- CacheResult.Loading prev = (CacheResult.Loading) cacheResult;
- CacheResult.Loading updated = prev.withoutCallback(callback);
-
- if (updated.mCallbacks.isEmpty()) {
- // If the last callback was removed, then cancel the underlying request in the
- // delegate.
- prev.mCancellationSignal.cancel();
- removeCacheResult(item, previewSize);
- } else {
- // If there are other callbacks still active, then don't cancel the delegate's
- // request, just remove this callback from the set.
- putCacheResult(item, previewSize, updated);
- }
- }
- });
-
- synchronized (mCache) {
- CacheResult cacheResult = getCacheResult(item, previewSize);
- if (cacheResult instanceof CacheResult.Loaded) {
- // If the bitmap is already present in the cache, invoke the callback immediately.
- callback.onPreviewLoaded(((CacheResult.Loaded) cacheResult).mBitmap);
- return signal;
- }
-
- if (cacheResult instanceof CacheResult.Loading) {
- // If we're already loading the preview for this key, then just add the callback
- // to the set we'll call after it loads.
- CacheResult.Loading prev = (CacheResult.Loading) cacheResult;
- putCacheResult(item, previewSize, prev.withCallback(callback));
- return signal;
- }
-
- CancellationSignal delegateCancellationSignal =
- mDelegate.loadPreview(
- activity,
- item,
- previewSize,
- preview -> {
- CacheResult prev;
- synchronized (mCache) {
- prev = putCacheResult(
- item, previewSize, new CacheResult.Loaded(preview));
- }
- if (prev instanceof CacheResult.Loading) {
- // Notify each stored callback that the preview has loaded.
- ((CacheResult.Loading) prev).mCallbacks
- .forEach(c -> c.onPreviewLoaded(preview));
- } else {
- // If there isn't a loading object in the cache, then we were
- // notified before adding this signal to the cache. Just
- // call back to the provided callback, there can't be others.
- callback.onPreviewLoaded(preview);
- }
- });
- ArraySet<WidgetPreviewLoadedCallback> callbacks = new ArraySet<>();
- callbacks.add(callback);
- putCacheResult(
- item,
- previewSize,
- new CacheResult.Loading(delegateCancellationSignal, callbacks));
- }
-
- return signal;
- }
-
- /** Clears all cached previews for {@code items}, cancelling any in-progress preview loading. */
- public void clearPreviews(Iterable<WidgetItem> items) {
- List<CacheResult> previousCacheResults = new ArrayList<>();
- synchronized (mCache) {
- for (WidgetItem item : items) {
- Map<Size, CacheResult> previousMap = mCache.remove(toComponentKey(item));
- if (previousMap != null) {
- previousCacheResults.addAll(previousMap.values());
- }
- }
- }
-
- for (CacheResult previousCacheResult : previousCacheResults) {
- if (previousCacheResult instanceof CacheResult.Loading) {
- ((CacheResult.Loading) previousCacheResult).mCancellationSignal.cancel();
- }
- }
- }
-
- /** Clears all cached previews, cancelling any in-progress preview loading. */
- public void clearAll() {
- List<CacheResult> previousCacheResults;
- synchronized (mCache) {
- previousCacheResults =
- mCache
- .values()
- .stream()
- .flatMap(sizeToResult -> sizeToResult.values().stream())
- .collect(Collectors.toList());
- mCache.clear();
- }
-
- for (CacheResult previousCacheResult : previousCacheResults) {
- if (previousCacheResult instanceof CacheResult.Loading) {
- ((CacheResult.Loading) previousCacheResult).mCancellationSignal.cancel();
- }
- }
- }
-
- private abstract static class CacheResult {
- static final CacheResult MISS = new CacheResult() {};
-
- static final class Loading extends CacheResult {
- @NonNull final CancellationSignal mCancellationSignal;
- @NonNull final Set<WidgetPreviewLoadedCallback> mCallbacks;
-
- Loading(@NonNull CancellationSignal cancellationSignal,
- @NonNull Set<WidgetPreviewLoadedCallback> callbacks) {
- mCancellationSignal = cancellationSignal;
- mCallbacks = callbacks;
- }
-
- @NonNull
- Loading withCallback(@NonNull WidgetPreviewLoadedCallback callback) {
- if (mCallbacks.contains(callback)) return this;
- Set<WidgetPreviewLoadedCallback> newCallbacks =
- new ArraySet<>(mCallbacks.size() + 1);
- newCallbacks.addAll(mCallbacks);
- newCallbacks.add(callback);
- return new Loading(mCancellationSignal, newCallbacks);
- }
-
- @NonNull
- Loading withoutCallback(@NonNull WidgetPreviewLoadedCallback callback) {
- if (!mCallbacks.contains(callback)) return this;
- Set<WidgetPreviewLoadedCallback> newCallbacks =
- new ArraySet<>(mCallbacks.size() - 1);
- for (WidgetPreviewLoadedCallback existingCallback : mCallbacks) {
- if (!existingCallback.equals(callback)) {
- newCallbacks.add(existingCallback);
- }
- }
- return new Loading(mCancellationSignal, newCallbacks);
- }
- }
-
- static final class Loaded extends CacheResult {
- @NonNull final Bitmap mBitmap;
-
- Loaded(@NonNull Bitmap bitmap) {
- mBitmap = bitmap;
- }
- }
- }
-
- @NonNull
- private static ComponentKey toComponentKey(@NonNull WidgetItem item) {
- return new ComponentKey(item.componentName, item.user);
- }
-}
diff --git a/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java b/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java
index 4ec7e60..aacb9c5 100644
--- a/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java
+++ b/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java
@@ -16,21 +16,10 @@
package com.android.launcher3.widget;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import android.content.ComponentName;
-import android.content.ContentValues;
import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
-import android.database.Cursor;
-import android.database.SQLException;
-import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
@@ -39,72 +28,40 @@
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
-import android.os.CancellationSignal;
+import android.os.Handler;
import android.os.Process;
-import android.os.UserHandle;
import android.util.Log;
-import android.util.LongSparseArray;
-import android.util.Pair;
import android.util.Size;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.LauncherFiles;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.icons.GraphicsUtils;
-import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.ShadowGenerator;
+import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
-import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.Executors;
-import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.SQLiteCacheHelper;
-import com.android.launcher3.util.Thunk;
+import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.widget.util.WidgetSizes;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.WeakHashMap;
import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
-/** {@link WidgetPreviewLoader} that loads preview images from a {@link CacheDb}. */
-public class DatabaseWidgetPreviewLoader implements WidgetPreviewLoader {
+/** Utility class to load widget previews */
+public class DatabaseWidgetPreviewLoader {
private static final String TAG = "WidgetPreviewLoader";
- private static final boolean DEBUG = false;
-
- private final HashMap<String, long[]> mPackageVersions = new HashMap<>();
-
- /**
- * Weak reference objects, do not prevent their referents from being made finalizable,
- * finalized, and then reclaimed.
- * Note: synchronized block used for this variable is expensive and the block should always
- * be posted to a background thread.
- */
- @Thunk final Set<Bitmap> mUnusedBitmaps = Collections.newSetFromMap(new WeakHashMap<>());
private final Context mContext;
- private final IconCache mIconCache;
- private final UserCache mUserCache;
- private final CacheDb mDb;
private final float mPreviewBoxCornerRadius;
- public DatabaseWidgetPreviewLoader(Context context, IconCache iconCache) {
+ public DatabaseWidgetPreviewLoader(Context context) {
mContext = context;
- mIconCache = iconCache;
- mUserCache = UserCache.INSTANCE.get(context);
- mDb = new CacheDb(context);
float previewCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(context);
mPreviewBoxCornerRadius = previewCornerRadius > 0
? previewCornerRadius
@@ -117,251 +74,29 @@
*
* @return a request id which can be used to cancel the request.
*/
- @Override
@NonNull
- public CancellationSignal loadPreview(
- @NonNull BaseActivity activity,
+ public HandlerRunnable loadPreview(
@NonNull WidgetItem item,
@NonNull Size previewSize,
- @NonNull WidgetPreviewLoadedCallback callback) {
- int previewWidth = previewSize.getWidth();
- int previewHeight = previewSize.getHeight();
- String size = previewWidth + "x" + previewHeight;
- WidgetCacheKey key = new WidgetCacheKey(item.componentName, item.user, size);
-
- PreviewLoadTask task =
- new PreviewLoadTask(activity, key, item, previewWidth, previewHeight, callback);
- task.executeOnExecutor(Executors.THREAD_POOL_EXECUTOR);
-
- CancellationSignal signal = new CancellationSignal();
- signal.setOnCancelListener(task);
- return signal;
- }
-
- /** Clears the database storing previews. */
- public void refresh() {
- mDb.clear();
- }
-
- /**
- * The DB holds the generated previews for various components. Previews can also have different
- * sizes (landscape vs portrait).
- */
- private static class CacheDb extends SQLiteCacheHelper {
- private static final int DB_VERSION = 9;
-
- private static final String TABLE_NAME = "shortcut_and_widget_previews";
- private static final String COLUMN_COMPONENT = "componentName";
- private static final String COLUMN_USER = "profileId";
- private static final String COLUMN_SIZE = "size";
- private static final String COLUMN_PACKAGE = "packageName";
- private static final String COLUMN_LAST_UPDATED = "lastUpdated";
- private static final String COLUMN_VERSION = "version";
- private static final String COLUMN_PREVIEW_BITMAP = "preview_bitmap";
-
- CacheDb(Context context) {
- super(context, LauncherFiles.WIDGET_PREVIEWS_DB, DB_VERSION, TABLE_NAME);
- }
-
- @Override
- public void onCreateTable(SQLiteDatabase database) {
- database.execSQL("CREATE TABLE IF NOT EXISTS "
- + TABLE_NAME
- + " ("
- + COLUMN_COMPONENT
- + " TEXT NOT NULL, "
- + COLUMN_USER
- + " INTEGER NOT NULL, "
- + COLUMN_SIZE
- + " TEXT NOT NULL, "
- + COLUMN_PACKAGE
- + " TEXT NOT NULL, "
- + COLUMN_LAST_UPDATED
- + " INTEGER NOT NULL DEFAULT 0, "
- + COLUMN_VERSION
- + " INTEGER NOT NULL DEFAULT 0, "
- + COLUMN_PREVIEW_BITMAP
- + " BLOB, "
- + "PRIMARY KEY ("
- + COLUMN_COMPONENT
- + ", "
- + COLUMN_USER
- + ", "
- + COLUMN_SIZE
- + ") "
- +
- ");");
- }
- }
-
- @Thunk void writeToDb(WidgetCacheKey key, long[] versions, Bitmap preview) {
- ContentValues values = new ContentValues();
- values.put(CacheDb.COLUMN_COMPONENT, key.componentName.flattenToShortString());
- values.put(CacheDb.COLUMN_USER, mUserCache.getSerialNumberForUser(key.user));
- values.put(CacheDb.COLUMN_SIZE, key.mSize);
- values.put(CacheDb.COLUMN_PACKAGE, key.componentName.getPackageName());
- values.put(CacheDb.COLUMN_VERSION, versions[0]);
- values.put(CacheDb.COLUMN_LAST_UPDATED, versions[1]);
- values.put(CacheDb.COLUMN_PREVIEW_BITMAP, GraphicsUtils.flattenBitmap(preview));
- mDb.insertOrReplace(values);
- }
-
- /** Removes the package from the preview database. */
- public void removePackage(String packageName, UserHandle user) {
- removePackage(packageName, user, mUserCache.getSerialNumberForUser(user));
- }
-
- /** Removes the package from the preview database. */
- public void removePackage(String packageName, UserHandle user, long userSerial) {
- synchronized (mPackageVersions) {
- mPackageVersions.remove(packageName);
- }
-
- mDb.delete(
- CacheDb.COLUMN_PACKAGE + " = ? AND " + CacheDb.COLUMN_USER + " = ?",
- new String[]{packageName, Long.toString(userSerial)});
- }
-
- /**
- * Updates the persistent DB:
- * 1. Any preview generated for an old package version is removed
- * 2. Any preview for an absent package is removed
- * This ensures that we remove entries for packages which changed while the launcher was dead.
- *
- * @param packageUser if provided, specifies that list only contains previews for the
- * given package/user, otherwise the list contains all previews
- */
- public void removeObsoletePreviews(ArrayList<? extends ComponentKey> list,
- @Nullable PackageUserKey packageUser) {
- Preconditions.assertWorkerThread();
-
- LongSparseArray<HashSet<String>> validPackages = new LongSparseArray<>();
-
- for (ComponentKey key : list) {
- final long userId = mUserCache.getSerialNumberForUser(key.user);
- HashSet<String> packages = validPackages.get(userId);
- if (packages == null) {
- packages = new HashSet<>();
- validPackages.put(userId, packages);
- }
- packages.add(key.componentName.getPackageName());
- }
-
- LongSparseArray<HashSet<String>> packagesToDelete = new LongSparseArray<>();
- long passedUserId = packageUser == null ? 0
- : mUserCache.getSerialNumberForUser(packageUser.mUser);
- Cursor c = null;
- try {
- c = mDb.query(
- new String[]{CacheDb.COLUMN_USER, CacheDb.COLUMN_PACKAGE,
- CacheDb.COLUMN_LAST_UPDATED, CacheDb.COLUMN_VERSION},
- null, null);
- while (c.moveToNext()) {
- long userId = c.getLong(0);
- String pkg = c.getString(1);
- long lastUpdated = c.getLong(2);
- long version = c.getLong(3);
-
- if (packageUser != null && (!pkg.equals(packageUser.mPackageName)
- || userId != passedUserId)) {
- // This preview is associated with a different package/user, no need to remove.
- continue;
- }
-
- HashSet<String> packages = validPackages.get(userId);
- if (packages != null && packages.contains(pkg)) {
- long[] versions = getPackageVersion(pkg);
- if (versions[0] == version && versions[1] == lastUpdated) {
- // Every thing checks out
- continue;
- }
- }
-
- // We need to delete this package.
- packages = packagesToDelete.get(userId);
- if (packages == null) {
- packages = new HashSet<>();
- packagesToDelete.put(userId, packages);
- }
- packages.add(pkg);
- }
-
- for (int i = 0; i < packagesToDelete.size(); i++) {
- long userId = packagesToDelete.keyAt(i);
- UserHandle user = mUserCache.getUserForSerialNumber(userId);
- for (String pkg : packagesToDelete.valueAt(i)) {
- removePackage(pkg, user, userId);
- }
- }
- } catch (SQLException e) {
- Log.e(TAG, "Error updating widget previews", e);
- } finally {
- if (c != null) {
- c.close();
- }
- }
- }
-
- /**
- * Reads the preview bitmap from the DB or null if the preview is not in the DB.
- */
- @Thunk Bitmap readFromDb(WidgetCacheKey key, Bitmap recycle, PreviewLoadTask loadTask) {
- Cursor cursor = null;
- try {
- cursor = mDb.query(
- new String[]{CacheDb.COLUMN_PREVIEW_BITMAP},
- CacheDb.COLUMN_COMPONENT + " = ? AND " + CacheDb.COLUMN_USER + " = ? AND "
- + CacheDb.COLUMN_SIZE + " = ?",
- new String[]{
- key.componentName.flattenToShortString(),
- Long.toString(mUserCache.getSerialNumberForUser(key.user)),
- key.mSize
- });
- // If cancelled, skip getting the blob and decoding it into a bitmap
- if (loadTask.isCancelled()) {
- return null;
- }
- if (cursor.moveToNext()) {
- byte[] blob = cursor.getBlob(0);
- BitmapFactory.Options opts = new BitmapFactory.Options();
- opts.inBitmap = recycle;
- try {
- if (!loadTask.isCancelled()) {
- return BitmapFactory.decodeByteArray(blob, 0, blob.length, opts);
- }
- } catch (Exception e) {
- return null;
- }
- }
- } catch (SQLException e) {
- Log.w(TAG, "Error loading preview from DB", e);
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- return null;
+ @NonNull Consumer<Bitmap> callback) {
+ Handler handler = Executors.UI_HELPER_EXECUTOR.getHandler();
+ HandlerRunnable<Bitmap> request = new HandlerRunnable<>(handler,
+ () -> generatePreview(item, previewSize.getWidth(), previewSize.getHeight()),
+ MAIN_EXECUTOR,
+ callback);
+ Utilities.postAsyncCallback(handler, request);
+ return request;
}
/**
* Returns a generated preview 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) {
+ private Bitmap generatePreview(WidgetItem item, int previewWidth, int previewHeight) {
if (item.widgetInfo != null) {
- return generateWidgetPreview(launcher, item.widgetInfo,
- previewWidth, recycle, null);
+ return generateWidgetPreview(item.widgetInfo, previewWidth, null);
} else {
- return new Pair<>(generateShortcutPreview(launcher, item.activityInfo,
- previewWidth, previewHeight, recycle), false);
+ return generateShortcutPreview(item.activityInfo, previewWidth, previewHeight);
}
}
@@ -369,16 +104,12 @@
* Generates the widget preview from either the {@link WidgetManagerHelper} or cache
* and add badge at the bottom right corner.
*
- * @param launcher
* @param info information about the widget
* @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 Pair<Bitmap (the preview) , Boolean (should be stored in db)>
*/
- public Pair<Bitmap, Boolean> generateWidgetPreview(BaseActivity launcher,
- LauncherAppWidgetProviderInfo info,
- int maxPreviewWidth, Bitmap preview, int[] preScaledWidthOut) {
+ public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info,
+ int maxPreviewWidth, int[] preScaledWidthOut) {
// Load the preview image if possible
if (maxPreviewWidth < 0) maxPreviewWidth = Integer.MAX_VALUE;
@@ -409,117 +140,97 @@
int previewWidth;
int previewHeight;
- boolean savePreviewImage = widgetPreviewExists || info.previewImage == 0;
+ DeviceProfile dp = ActivityContext.lookupContext(mContext).getDeviceProfile();
if (widgetPreviewExists && drawable.getIntrinsicWidth() > 0
&& drawable.getIntrinsicHeight() > 0) {
previewWidth = drawable.getIntrinsicWidth();
previewHeight = drawable.getIntrinsicHeight();
} else {
- DeviceProfile dp = launcher.getDeviceProfile();
Size widgetSize = WidgetSizes.getWidgetPaddedSizePx(mContext, info.provider, dp, spanX,
spanY);
previewWidth = widgetSize.getWidth();
previewHeight = widgetSize.getHeight();
}
- // Scale to fit width only - let the widget preview be clipped in the
- // vertical dimension
- float scale = 1f;
if (preScaledWidthOut != null) {
preScaledWidthOut[0] = previewWidth;
}
- if (previewWidth > maxPreviewWidth) {
- scale = maxPreviewWidth / (float) (previewWidth);
- }
+ // Scale to fit width only - let the widget preview be clipped in the
+ // vertical dimension
+ final float scale = previewWidth > maxPreviewWidth
+ ? (maxPreviewWidth / (float) (previewWidth)) : 1f;
if (scale != 1f) {
previewWidth = Math.max((int) (scale * previewWidth), 1);
previewHeight = Math.max((int) (scale * previewHeight), 1);
}
- final Canvas c = new Canvas();
- if (preview == null) {
- // If no bitmap was provided, then allocate a new one with the right size.
- preview = Bitmap.createBitmap(previewWidth, previewHeight, Config.ARGB_8888);
- c.setBitmap(preview);
- } else {
- // If a bitmap was passed in, attempt to reconfigure the bitmap to the same dimensions
- // as the preview.
- try {
- preview.reconfigure(previewWidth, previewHeight, preview.getConfig());
- } catch (IllegalArgumentException e) {
- // This occurs if the preview can't be reconfigured for any reason. In this case,
- // allocate a new bitmap with the right size.
- preview = Bitmap.createBitmap(previewWidth, previewHeight, Config.ARGB_8888);
- }
+ final int previewWidthF = previewWidth;
+ final int previewHeightF = previewHeight;
+ final Drawable drawableF = drawable;
- c.setBitmap(preview);
- c.drawColor(0, PorterDuff.Mode.CLEAR);
- }
-
- // Draw the scaled preview into the final bitmap
- if (widgetPreviewExists) {
- drawable.setBounds(0, 0, previewWidth, previewHeight);
- drawable.draw(c);
- } else {
- RectF boxRect;
-
- // Draw horizontal and vertical lines to represent individual columns.
- final Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
-
- if (Utilities.ATLEAST_S) {
- boxRect = new RectF(/* left= */ 0, /* top= */ 0, /* right= */
- previewWidth, /* bottom= */ previewHeight);
-
- p.setStyle(Paint.Style.FILL);
- p.setColor(Color.WHITE);
- float roundedCorner = mContext.getResources().getDimension(
- android.R.dimen.system_app_widget_background_radius);
- c.drawRoundRect(boxRect, roundedCorner, roundedCorner, p);
+ return BitmapRenderer.createHardwareBitmap(previewWidth, previewHeight, c -> {
+ // Draw the scaled preview into the final bitmap
+ if (widgetPreviewExists) {
+ drawableF.setBounds(0, 0, previewWidthF, previewHeightF);
+ drawableF.draw(c);
} else {
- boxRect = drawBoxWithShadow(c, previewWidth, previewHeight);
- }
+ RectF boxRect;
- p.setStyle(Paint.Style.STROKE);
- p.setStrokeWidth(mContext.getResources()
- .getDimension(R.dimen.widget_preview_cell_divider_width));
- p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+ // Draw horizontal and vertical lines to represent individual columns.
+ final Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
- float t = boxRect.left;
- float tileSize = boxRect.width() / spanX;
- for (int i = 1; i < spanX; i++) {
- t += tileSize;
- c.drawLine(t, 0, t, previewHeight, p);
- }
+ if (Utilities.ATLEAST_S) {
+ boxRect = new RectF(/* left= */ 0, /* top= */ 0, /* right= */
+ previewWidthF, /* bottom= */ previewHeightF);
- t = boxRect.top;
- tileSize = boxRect.height() / spanY;
- for (int i = 1; i < spanY; i++) {
- t += tileSize;
- c.drawLine(0, t, previewWidth, t, p);
- }
-
- // Draw icon in the center.
- try {
- Drawable icon =
- mIconCache.getFullResIcon(info.provider.getPackageName(), info.icon);
- if (icon != null) {
- int appIconSize = launcher.getDeviceProfile().iconSizePx;
- int iconSize = (int) Math.min(appIconSize * scale,
- Math.min(boxRect.width(), boxRect.height()));
-
- icon = mutateOnMainThread(icon);
- int hoffset = (previewWidth - iconSize) / 2;
- int yoffset = (previewHeight - iconSize) / 2;
- icon.setBounds(hoffset, yoffset, hoffset + iconSize, yoffset + iconSize);
- icon.draw(c);
+ p.setStyle(Paint.Style.FILL);
+ p.setColor(Color.WHITE);
+ float roundedCorner = mContext.getResources().getDimension(
+ android.R.dimen.system_app_widget_background_radius);
+ c.drawRoundRect(boxRect, roundedCorner, roundedCorner, p);
+ } else {
+ boxRect = drawBoxWithShadow(c, previewWidthF, previewHeightF);
}
- } catch (Resources.NotFoundException e) {
- savePreviewImage = false;
+
+ p.setStyle(Paint.Style.STROKE);
+ p.setStrokeWidth(mContext.getResources()
+ .getDimension(R.dimen.widget_preview_cell_divider_width));
+ p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+
+ float t = boxRect.left;
+ float tileSize = boxRect.width() / spanX;
+ for (int i = 1; i < spanX; i++) {
+ t += tileSize;
+ c.drawLine(t, 0, t, previewHeightF, p);
+ }
+
+ t = boxRect.top;
+ tileSize = boxRect.height() / spanY;
+ for (int i = 1; i < spanY; i++) {
+ t += tileSize;
+ c.drawLine(0, t, previewWidthF, t, p);
+ }
+
+ // Draw icon in the center.
+ try {
+ Drawable icon = LauncherAppState.getInstance(mContext).getIconCache()
+ .getFullResIcon(info.provider.getPackageName(), info.icon);
+ if (icon != null) {
+ int appIconSize = dp.iconSizePx;
+ int iconSize = (int) Math.min(appIconSize * scale,
+ Math.min(boxRect.width(), boxRect.height()));
+
+ icon = mutateOnMainThread(icon);
+ int hoffset = (previewWidthF - iconSize) / 2;
+ int yoffset = (previewHeightF - iconSize) / 2;
+ icon.setBounds(hoffset, yoffset, hoffset + iconSize, yoffset + iconSize);
+ icon.draw(c);
+ }
+ } catch (Resources.NotFoundException e) {
+ }
}
- c.setBitmap(null);
- }
- return new Pair<>(preview, savePreviewImage);
+ });
}
private RectF drawBoxWithShadow(Canvas c, int width, int height) {
@@ -537,42 +248,29 @@
return builder.bounds;
}
- private Bitmap generateShortcutPreview(BaseActivity launcher, ShortcutConfigActivityInfo info,
- int maxWidth, int maxHeight, Bitmap preview) {
- int iconSize = launcher.getDeviceProfile().allAppsIconSizePx;
- int padding = launcher.getResources()
+ private Bitmap generateShortcutPreview(
+ ShortcutConfigActivityInfo info, int maxWidth, int maxHeight) {
+ int iconSize = ActivityContext.lookupContext(mContext).getDeviceProfile().allAppsIconSizePx;
+ int padding = mContext.getResources()
.getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
int size = iconSize + 2 * padding;
if (maxHeight < size || maxWidth < size) {
throw new RuntimeException("Max size is too small for preview");
}
- final Canvas c = new Canvas();
- if (preview == null || preview.getWidth() < size || preview.getHeight() < size) {
- preview = Bitmap.createBitmap(size, size, Config.ARGB_8888);
- c.setBitmap(preview);
- } else {
- if (preview.getWidth() > size || preview.getHeight() > size) {
- preview.reconfigure(size, size, preview.getConfig());
- }
+ return BitmapRenderer.createHardwareBitmap(size, size, c -> {
+ drawBoxWithShadow(c, size, size);
- // Reusing bitmap. Clear it.
- c.setBitmap(preview);
- c.drawColor(0, PorterDuff.Mode.CLEAR);
- }
+ LauncherIcons li = LauncherIcons.obtain(mContext);
+ Drawable icon = li.createBadgedIconBitmap(
+ mutateOnMainThread(info.getFullResIcon(
+ LauncherAppState.getInstance(mContext).getIconCache())),
+ Process.myUserHandle(), 0).newIcon(mContext);
+ li.recycle();
- drawBoxWithShadow(c, size, size);
-
- LauncherIcons li = LauncherIcons.obtain(mContext);
- Drawable icon = li.createBadgedIconBitmap(
- mutateOnMainThread(info.getFullResIcon(mIconCache)),
- Process.myUserHandle(), 0).newIcon(launcher);
- li.recycle();
-
- icon.setBounds(padding, padding, padding + iconSize, padding + iconSize);
- icon.draw(c);
- c.setBitmap(null);
- return preview;
+ icon.setBounds(padding, padding, padding + iconSize, padding + iconSize);
+ icon.draw(c);
+ });
}
private Drawable mutateOnMainThread(final Drawable drawable) {
@@ -585,206 +283,4 @@
throw new RuntimeException(e);
}
}
-
- /**
- * @return an array of containing versionCode and lastUpdatedTime for the package.
- */
- @Thunk long[] getPackageVersion(String packageName) {
- synchronized (mPackageVersions) {
- long[] versions = mPackageVersions.get(packageName);
- if (versions == null) {
- versions = new long[2];
- try {
- PackageInfo info = mContext.getPackageManager().getPackageInfo(packageName,
- PackageManager.GET_UNINSTALLED_PACKAGES);
- versions[0] = info.versionCode;
- versions[1] = info.lastUpdateTime;
- } catch (NameNotFoundException e) {
- Log.e(TAG, "PackageInfo not found", e);
- }
- mPackageVersions.put(packageName, versions);
- }
- return versions;
- }
- }
-
- private class PreviewLoadTask extends AsyncTask<Void, Void, Bitmap>
- implements CancellationSignal.OnCancelListener {
- @Thunk final WidgetCacheKey mKey;
- private final WidgetItem mInfo;
- private final int mPreviewHeight;
- private final int mPreviewWidth;
- private final WidgetPreviewLoadedCallback mCallback;
- private final BaseActivity mActivity;
- @Thunk long[] mVersions;
- @Thunk Bitmap mBitmapToRecycle;
-
- @Nullable private Bitmap mUnusedPreviewBitmap;
- private boolean mSaveToDB = false;
-
- PreviewLoadTask(BaseActivity activity, WidgetCacheKey key, WidgetItem info,
- int previewWidth, int previewHeight, WidgetPreviewLoadedCallback callback) {
- mActivity = activity;
- mKey = key;
- mInfo = info;
- mPreviewHeight = previewHeight;
- mPreviewWidth = previewWidth;
- mCallback = callback;
- if (DEBUG) {
- Log.d(TAG, String.format("%s, %s, %d, %d",
- mKey, mInfo, mPreviewHeight, mPreviewWidth));
- }
- }
-
- @Override
- protected Bitmap doInBackground(Void... params) {
- Bitmap unusedBitmap = null;
-
- // If already cancelled before this gets to run in the background, then return early
- if (isCancelled()) {
- return null;
- }
- synchronized (mUnusedBitmaps) {
- // Check if we can re-use a bitmap
- for (Bitmap candidate : mUnusedBitmaps) {
- if (candidate != null && candidate.isMutable()
- && candidate.getWidth() == mPreviewWidth
- && candidate.getHeight() == mPreviewHeight) {
- unusedBitmap = candidate;
- mUnusedBitmaps.remove(unusedBitmap);
- break;
- }
- }
- }
-
- // creating a bitmap is expensive. Do not do this inside synchronized block.
- if (unusedBitmap == null) {
- unusedBitmap = Bitmap.createBitmap(mPreviewWidth, mPreviewHeight, Config.ARGB_8888);
- }
- // If cancelled now, don't bother reading the preview from the DB
- if (isCancelled()) {
- return unusedBitmap;
- }
- Bitmap preview = readFromDb(mKey, unusedBitmap, this);
- // Only consider generating the preview if we have not cancelled the task already
- if (!isCancelled() && preview == null) {
- // Fetch the version info before we generate the preview, so that, in-case the
- // app was updated while we are generating the preview, we use the old version info,
- // which would gets re-written next time.
- boolean persistable = mInfo.activityInfo == null
- || mInfo.activityInfo.isPersistable();
- mVersions = persistable ? getPackageVersion(mKey.componentName.getPackageName())
- : null;
-
- // it's not in the db... we need to generate it
- Pair<Bitmap, Boolean> pair = generatePreview(mActivity, mInfo, unusedBitmap,
- mPreviewWidth, mPreviewHeight);
- preview = pair.first;
-
- if (preview != unusedBitmap) {
- mUnusedPreviewBitmap = unusedBitmap;
- }
-
- this.mSaveToDB = pair.second;
- }
- return preview;
- }
-
- @Override
- protected void onPostExecute(final Bitmap preview) {
- mCallback.onPreviewLoaded(preview);
-
- // Write the generated preview to the DB in the worker thread
- if (mVersions != null) {
- MODEL_EXECUTOR.post(new Runnable() {
- @Override
- public void run() {
- if (mUnusedPreviewBitmap != null) {
- // If we didn't end up using the bitmap, it can be added back into the
- // recycled set.
- synchronized (mUnusedBitmaps) {
- mUnusedBitmaps.add(mUnusedPreviewBitmap);
- }
- }
-
- 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);
- mBitmapToRecycle = preview;
- } else {
- // If we've already cancelled, then skip writing the bitmap to the DB
- // and manually add the bitmap back to the recycled set
- synchronized (mUnusedBitmaps) {
- mUnusedBitmaps.add(preview);
- }
- }
- }
- });
- } else {
- // If we don't need to write to disk, then ensure the preview gets recycled by
- // the normal clear mechanism
- mBitmapToRecycle = preview;
- }
- }
-
- @Override
- protected void onCancelled(final Bitmap preview) {
- // If we've cancelled while the task is running, then can return the bitmap to the
- // recycled set immediately. Otherwise, it will be recycled after the preview is written
- // to disk.
- if (preview != null) {
- MODEL_EXECUTOR.post(new Runnable() {
- @Override
- public void run() {
- synchronized (mUnusedBitmaps) {
- mUnusedBitmaps.add(preview);
- }
- }
- });
- }
- }
-
- @Override
- public void onCancel() {
- cancel(true);
-
- // This only handles the case where the PreviewLoadTask is cancelled after the task has
- // successfully completed (including having written to disk when necessary). In the
- // other cases where it is cancelled while the task is running, it will be cleaned up
- // in the tasks's onCancelled() call, and if cancelled while the task is writing to
- // disk, it will be cancelled in the task's onPostExecute() call.
- if (mBitmapToRecycle != null) {
- MODEL_EXECUTOR.post(new Runnable() {
- @Override
- public void run() {
- synchronized (mUnusedBitmaps) {
- mUnusedBitmaps.add(mBitmapToRecycle);
- }
- mBitmapToRecycle = null;
- }
- });
- }
- }
- }
-
- private static final class WidgetCacheKey extends ComponentKey {
-
- @Thunk final String mSize;
-
- WidgetCacheKey(ComponentName componentName, UserHandle user, String size) {
- super(componentName, user);
- this.mSize = size;
- }
-
- @Override
- public int hashCode() {
- return super.hashCode() ^ mSize.hashCode();
- }
-
- @Override
- public boolean equals(Object o) {
- return super.equals(o) && ((WidgetCacheKey) o).mSize.equals(mSize);
- }
- }
}
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index 991910d..2347d28 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -140,10 +140,9 @@
.addDragListener(new AppWidgetHostViewDragListener(launcher));
}
if (preview == null && mAppWidgetHostViewPreview == null) {
- Drawable p = new FastBitmapDrawable(
- app.getWidgetCache().generateWidgetPreview(launcher,
- createWidgetInfo.info, maxWidth, null,
- previewSizeBeforeScale).first);
+ Drawable p = new FastBitmapDrawable(new DatabaseWidgetPreviewLoader(launcher)
+ .generateWidgetPreview(
+ createWidgetInfo.info, maxWidth, previewSizeBeforeScale));
if (RoundedCornerEnforcement.isRoundedCornerEnabled()) {
p = new RoundDrawableWrapper(p, mEnforcedRoundedCornersForWidget);
}
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index bd444db..f1ac656 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -26,14 +26,12 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
-import android.os.CancellationSignal;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Size;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
-import android.view.View.OnLayoutChangeListener;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -42,18 +40,22 @@
import android.widget.RemoteViews;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.BaseActivity;
import com.android.launcher3.CheckLongPressHelper;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.RoundDrawableWrapper;
+import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.widget.util.WidgetSizes;
+import java.util.function.Consumer;
+
/**
* Represents the individual cell of the widget inside the widget tray. The preview is drawn
* horizontally centered, and scaled down if needed.
@@ -63,7 +65,7 @@
* transition from the view to drag view, so when adding padding support, DnD would need to
* consider the appropriate scaling factor.
*/
-public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
+public class WidgetCell extends LinearLayout {
private static final String TAG = "WidgetCell";
private static final boolean DEBUG = false;
@@ -115,15 +117,12 @@
protected WidgetItem mItem;
- private WidgetPreviewLoader mWidgetPreviewLoader;
+ private final DatabaseWidgetPreviewLoader mWidgetPreviewLoader;
- protected CancellationSignal mActiveRequest;
+ protected HandlerRunnable mActiveRequest;
private boolean mAnimatePreview = true;
- private boolean mApplyBitmapDeferred = false;
- private Drawable mDeferredDrawable;
-
- protected final BaseActivity mActivity;
+ protected final ActivityContext mActivity;
private final CheckLongPressHelper mLongPressHelper;
private final float mEnforcedCornerRadius;
@@ -143,7 +142,8 @@
public WidgetCell(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mActivity = BaseActivity.fromContext(context);
+ mActivity = ActivityContext.lookupContext(context);
+ mWidgetPreviewLoader = new DatabaseWidgetPreviewLoader(context);
mLongPressHelper = new CheckLongPressHelper(this);
mLongPressHelper.setLongPressTimeoutFactor(1);
@@ -218,7 +218,36 @@
this.mSourceContainer = sourceContainer;
}
- public void applyFromCellItem(WidgetItem item, WidgetPreviewLoader loader) {
+ /**
+ * Applies the item to this view
+ */
+ public void applyFromCellItem(WidgetItem item) {
+ applyFromCellItem(item, 1f);
+ }
+
+ /**
+ * Applies the item to this view
+ */
+ public void applyFromCellItem(WidgetItem item, float previewScale) {
+ applyFromCellItem(item, previewScale, this::applyPreview, null);
+ }
+
+ /**
+ * Applies the item to this view
+ * @param item item to apply
+ * @param previewScale factor to scale the preview
+ * @param callback callback when preview is loaded in case the preview is being loaded or cached
+ * @param cachedPreview previously cached preview bitmap is present
+ */
+ public void applyFromCellItem(WidgetItem item, float previewScale,
+ @NonNull Consumer<Bitmap> callback, @Nullable Bitmap cachedPreview) {
+ // setPreviewSize
+ DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+ Size widgetSize = WidgetSizes.getWidgetItemSizePx(getContext(), deviceProfile, item);
+ mTargetPreviewWidth = widgetSize.getWidth();
+ mTargetPreviewHeight = widgetSize.getHeight();
+ mPreviewContainerScale = previewScale;
+
applyPreviewOnAppWidgetHostView(item);
Context context = getContext();
@@ -240,14 +269,14 @@
}
}
- mWidgetPreviewLoader = loader;
if (item.activityInfo != null) {
setTag(new PendingAddShortcutInfo(item.activityInfo));
} else {
setTag(new PendingAddWidgetInfo(item.widgetInfo, mSourceContainer));
}
- }
+ ensurePreviewWithCallback(callback, cachedPreview);
+ }
private void applyPreviewOnAppWidgetHostView(WidgetItem item) {
if (mRemoteViewsPreview != null) {
@@ -294,37 +323,15 @@
return mAppWidgetHostViewPreview;
}
- /**
- * Sets if applying bitmap preview should be deferred. The UI will still load the bitmap, but
- * will not cause invalidate, so that when deferring is disabled later, all the bitmaps are
- * ready.
- * This prevents invalidates while the animation is running.
- */
- public void setApplyBitmapDeferred(boolean isDeferred) {
- if (mApplyBitmapDeferred != isDeferred) {
- mApplyBitmapDeferred = isDeferred;
- if (!mApplyBitmapDeferred && mDeferredDrawable != null) {
- applyPreview(mDeferredDrawable);
- mDeferredDrawable = null;
- }
- }
- }
-
public void setAnimatePreview(boolean shouldAnimate) {
mAnimatePreview = shouldAnimate;
}
- public void applyPreview(Bitmap bitmap) {
- FastBitmapDrawable drawable = new FastBitmapDrawable(bitmap);
- applyPreview(new RoundDrawableWrapper(drawable, mEnforcedCornerRadius));
- }
+ private void applyPreview(Bitmap bitmap) {
+ if (bitmap != null) {
+ Drawable drawable = new RoundDrawableWrapper(
+ new FastBitmapDrawable(bitmap), mEnforcedCornerRadius);
- private void applyPreview(Drawable drawable) {
- if (mApplyBitmapDeferred) {
- mDeferredDrawable = drawable;
- return;
- }
- if (drawable != null) {
// Scale down the preview size if it's wider than the cell.
float scale = 1f;
if (mTargetPreviewWidth > 0) {
@@ -349,6 +356,10 @@
} else {
mWidgetImageContainer.setAlpha(1f);
}
+ if (mActiveRequest != null) {
+ mActiveRequest.cancel();
+ mActiveRequest = null;
+ }
}
private void setContainerSize(int width, int height) {
@@ -358,7 +369,13 @@
mWidgetImageContainer.setLayoutParams(layoutParams);
}
- public void ensurePreview() {
+ /**
+ * Ensures that the preview is already loaded or being loaded. If the preview is not loaded,
+ * it applies the provided cachedPreview. If that is null, it starts a loader and notifies the
+ * callback on successful load.
+ */
+ private void ensurePreviewWithCallback(Consumer<Bitmap> callback,
+ @Nullable Bitmap cachedPreview) {
if (mAppWidgetHostViewPreview != null) {
int containerWidth = (int) (mTargetPreviewWidth * mPreviewContainerScale);
int containerHeight = (int) (mTargetPreviewHeight * mPreviewContainerScale);
@@ -382,38 +399,18 @@
mAppWidgetHostViewPreview.setLayoutParams(params);
mWidgetImageContainer.addView(mAppWidgetHostViewPreview, /* index= */ 0);
mWidgetImage.setVisibility(View.GONE);
- applyPreview((Drawable) null);
+ applyPreview(null);
+ return;
+ }
+ if (cachedPreview != null) {
+ applyPreview(cachedPreview);
return;
}
if (mActiveRequest != null) {
return;
}
mActiveRequest = mWidgetPreviewLoader.loadPreview(
- BaseActivity.fromContext(getContext()), mItem,
- new Size(mTargetPreviewWidth, mTargetPreviewHeight),
- this::applyPreview);
- }
-
- /** Sets the widget preview image size in number of cells. */
- public Size setPreviewSize(WidgetItem widgetItem) {
- return setPreviewSize(widgetItem, 1f);
- }
-
- /** Sets the widget preview image size, in number of cells, and preview scale. */
- public Size setPreviewSize(WidgetItem widgetItem, float previewScale) {
- DeviceProfile deviceProfile = mActivity.getDeviceProfile();
- Size widgetSize = WidgetSizes.getWidgetItemSizePx(getContext(), deviceProfile, widgetItem);
- mTargetPreviewWidth = widgetSize.getWidth();
- mTargetPreviewHeight = widgetSize.getHeight();
- mPreviewContainerScale = previewScale;
- return widgetSize;
- }
-
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
- int oldTop, int oldRight, int oldBottom) {
- removeOnLayoutChangeListener(this);
- ensurePreview();
+ mItem, new Size(mTargetPreviewWidth, mTargetPreviewHeight), callback);
}
@Override
@@ -429,17 +426,6 @@
mLongPressHelper.cancelLongPress();
}
- /**
- * Helper method to get the string info of the tag.
- */
- private String getTagToString() {
- if (getTag() instanceof PendingAddWidgetInfo ||
- getTag() instanceof PendingAddShortcutInfo) {
- return getTag().toString();
- }
- return "";
- }
-
private static NavigableAppWidgetHostView createAppWidgetHostView(Context context) {
return new NavigableAppWidgetHostView(context) {
@Override
@@ -450,12 +436,7 @@
}
private static boolean isLauncherContext(Context context) {
- try {
- Launcher.getLauncher(context);
- return true;
- } catch (Exception e) {
- return false;
- }
+ return ActivityContext.lookupContext(context) instanceof Launcher;
}
@Override
diff --git a/src/com/android/launcher3/widget/WidgetPreviewLoader.java b/src/com/android/launcher3/widget/WidgetPreviewLoader.java
deleted file mode 100644
index ff5c82f..0000000
--- a/src/com/android/launcher3/widget/WidgetPreviewLoader.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.widget;
-
-import android.graphics.Bitmap;
-import android.os.CancellationSignal;
-import android.util.Size;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.UiThread;
-
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.model.WidgetItem;
-
-/** Asynchronous loader of preview bitmaps for {@link WidgetItem}s. */
-public interface WidgetPreviewLoader {
- /**
- * Loads a widget preview and calls back to {@code callback} when complete.
- *
- * @return a {@link CancellationSignal} which can be used to cancel the request.
- */
- @NonNull
- @UiThread
- CancellationSignal loadPreview(
- @NonNull BaseActivity activity,
- @NonNull WidgetItem item,
- @NonNull Size previewSize,
- @NonNull WidgetPreviewLoadedCallback callback);
-
- /** Callback class for requests to {@link WidgetPreviewLoader}. */
- interface WidgetPreviewLoadedCallback {
- void onPreviewLoaded(@NonNull Bitmap preview);
- }
-}
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 6beff3a..bb4638a 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -37,7 +37,6 @@
import android.widget.TextView;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.model.WidgetItem;
@@ -199,11 +198,7 @@
tableRow.setGravity(Gravity.TOP);
row.forEach(widgetItem -> {
WidgetCell widget = addItemCell(tableRow);
- widget.setPreviewSize(widgetItem);
- widget.applyFromCellItem(widgetItem, LauncherAppState.getInstance(mActivityContext)
- .getWidgetCache());
- widget.ensurePreview();
- widget.setVisibility(View.VISIBLE);
+ widget.applyFromCellItem(widgetItem);
});
widgetsTable.addView(tableRow);
});
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 9dbfa87..09f0299 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -687,7 +687,7 @@
.findFirst()
.orElse(null);
if (viewHolderForTip != null) {
- return ((ViewGroup) viewHolderForTip.mTableContainer.getChildAt(0)).getChildAt(0);
+ return ((ViewGroup) viewHolderForTip.tableContainer.getChildAt(0)).getChildAt(0);
}
return null;
@@ -745,7 +745,6 @@
mWidgetsListAdapter = new WidgetsListAdapter(
context,
LayoutInflater.from(context),
- apps.getWidgetCache(),
apps.getIconCache(),
this::getEmptySpaceHeight,
/* iconClickListener= */ WidgetsFullSheet.this,
@@ -784,7 +783,6 @@
if (mAdapterType == PRIMARY || mAdapterType == WORK) {
mWidgetsRecyclerView.addOnAttachStateChangeListener(mBindScrollbarInSearchMode);
}
- mWidgetsListAdapter.setApplyBitmapDeferred(false, mWidgetsRecyclerView);
mWidgetsListAdapter.setMaxHorizontalSpansPerRow(mMaxSpansPerRow);
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
index 1ad1f7a..d52134c 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -24,14 +24,12 @@
import android.graphics.Rect;
import android.os.Process;
import android.util.Log;
-import android.util.Size;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
-import android.widget.TableRow;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -41,29 +39,22 @@
import androidx.recyclerview.widget.RecyclerView.LayoutParams;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.recyclerview.ViewHolderBinder;
import com.android.launcher3.util.LabelComparator;
import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.widget.CachingWidgetPreviewLoader;
-import com.android.launcher3.widget.DatabaseWidgetPreviewLoader;
-import com.android.launcher3.widget.WidgetCell;
-import com.android.launcher3.widget.WidgetPreviewLoader.WidgetPreviewLoadedCallback;
+import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.widget.model.WidgetListSpaceEntry;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import com.android.launcher3.widget.model.WidgetsListContentEntry;
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
-import com.android.launcher3.widget.util.WidgetSizes;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
@@ -95,11 +86,8 @@
private static final int VIEW_TYPE_WIDGETS_SEARCH_HEADER = R.id.view_type_widgets_search_header;
private final Context mContext;
- private final Launcher mLauncher;
- private final CachingWidgetPreviewLoader mCachingPreviewLoader;
private final WidgetsDiffReporter mDiffReporter;
private final SparseArray<ViewHolderBinder> mViewHolderBinders = new SparseArray<>();
- private final WidgetsListTableViewHolderBinder mWidgetsListTableViewHolderBinder;
private final WidgetListBaseRowEntryComparator mRowComparator =
new WidgetListBaseRowEntryComparator();
@@ -115,26 +103,21 @@
@Nullable private Predicate<WidgetsListBaseEntry> mFilter = null;
@Nullable private RecyclerView mRecyclerView;
@Nullable private PackageUserKey mPendingClickHeader;
- private final int mShortcutPreviewPadding;
private final int mSpacingBetweenEntries;
private int mMaxSpanSize = 4;
- private final WidgetPreviewLoadedCallback mPreviewLoadedCallback =
- ignored -> updateVisibleEntries();
-
public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
- DatabaseWidgetPreviewLoader widgetPreviewLoader, IconCache iconCache,
- IntSupplier emptySpaceHeightProvider,
+ IconCache iconCache, IntSupplier emptySpaceHeightProvider,
OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) {
mContext = context;
- mLauncher = Launcher.getLauncher(context);
- mCachingPreviewLoader = new CachingWidgetPreviewLoader(widgetPreviewLoader);
mDiffReporter = new WidgetsDiffReporter(iconCache, this);
WidgetsListDrawableFactory listDrawableFactory = new WidgetsListDrawableFactory(context);
- mWidgetsListTableViewHolderBinder = new WidgetsListTableViewHolderBinder(
- layoutInflater, iconClickListener, iconLongClickListener,
- mCachingPreviewLoader, listDrawableFactory);
- mViewHolderBinders.put(VIEW_TYPE_WIDGETS_LIST, mWidgetsListTableViewHolderBinder);
+
+ mViewHolderBinders.put(
+ VIEW_TYPE_WIDGETS_LIST,
+ new WidgetsListTableViewHolderBinder(
+ layoutInflater, iconClickListener, iconLongClickListener,
+ listDrawableFactory));
mViewHolderBinders.put(
VIEW_TYPE_WIDGETS_HEADER,
new WidgetsListHeaderViewHolderBinder(
@@ -150,9 +133,6 @@
mViewHolderBinders.put(
VIEW_TYPE_WIDGETS_SPACE,
new WidgetsSpaceViewHolderBinder(emptySpaceHeightProvider));
- mShortcutPreviewPadding =
- 2 * context.getResources()
- .getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
mSpacingBetweenEntries =
context.getResources().getDimensionPixelSize(R.dimen.widget_list_entry_spacing);
}
@@ -186,28 +166,6 @@
mFilter = filter;
}
- /**
- * Defers applying bitmap on all the {@link WidgetCell} in the {@param rv}.
- *
- * @see WidgetCell#setApplyBitmapDeferred(boolean)
- */
- public void setApplyBitmapDeferred(boolean isDeferred, RecyclerView rv) {
- mWidgetsListTableViewHolderBinder.setApplyBitmapDeferred(isDeferred);
-
- for (int i = rv.getChildCount() - 1; i >= 0; i--) {
- ViewHolder viewHolder = rv.getChildViewHolder(rv.getChildAt(i));
- if (viewHolder.getItemViewType() == VIEW_TYPE_WIDGETS_LIST) {
- WidgetsRowViewHolder holder = (WidgetsRowViewHolder) viewHolder;
- for (int j = holder.mTableContainer.getChildCount() - 1; j >= 0; j--) {
- TableRow row = (TableRow) holder.mTableContainer.getChildAt(j);
- for (int k = row.getChildCount() - 1; k >= 0; k--) {
- ((WidgetCell) row.getChildAt(k)).setApplyBitmapDeferred(isDeferred);
- }
- }
- }
- }
- }
-
@Override
public int getItemCount() {
return mVisibleEntries.size();
@@ -233,7 +191,6 @@
/** Updates the widget list based on {@code tempEntries}. */
public void setWidgets(List<WidgetsListBaseEntry> tempEntries) {
- mCachingPreviewLoader.clearAll();
mAllEntries.clear();
mAllEntries.add(new WidgetListSpaceEntry());
tempEntries.stream().sorted(mRowComparator).forEach(mAllEntries::add);
@@ -247,15 +204,10 @@
public void setWidgetsOnSearch(List<WidgetsListBaseEntry> searchResults) {
// Forget the expanded package every time widget list is refreshed in search mode.
mWidgetsContentVisiblePackageUserKey = null;
- cancelLoadingPreviews();
setWidgets(searchResults);
}
private void updateVisibleEntries() {
- // If not all previews are ready, then defer this update and try again after the preview
- // loads.
- if (!ensureAllPreviewsReady()) return;
-
// Get the current top of the header with the matching key before adjusting the visible
// entries.
OptionalInt previousPositionForPackageUserKey =
@@ -293,54 +245,6 @@
}
}
- /**
- * Checks that all preview images are loaded and starts loading for those that aren't ready.
- *
- * @return true if all previews are ready and the data can be updated, false otherwise.
- */
- private boolean ensureAllPreviewsReady() {
- boolean allReady = true;
- BaseActivity activity = BaseActivity.fromContext(mContext);
- for (WidgetsListBaseEntry entry : mAllEntries) {
- if (!(entry instanceof WidgetsListContentEntry)) continue;
-
- WidgetsListContentEntry contentEntry = (WidgetsListContentEntry) entry;
- if (!matchesKey(entry, mWidgetsContentVisiblePackageUserKey)) {
- // If the entry isn't visible, clear any loaded previews.
- mCachingPreviewLoader.clearPreviews(contentEntry.mWidgets);
- continue;
- }
-
- for (int i = 0; i < entry.mWidgets.size(); i++) {
- WidgetItem widgetItem = entry.mWidgets.get(i);
- DeviceProfile deviceProfile = activity.getDeviceProfile();
- Size widgetSize = WidgetSizes.getWidgetItemSizePx(mContext, deviceProfile,
- widgetItem);
- if (widgetItem.isShortcut()) {
- widgetSize =
- new Size(
- widgetSize.getWidth() + mShortcutPreviewPadding,
- widgetSize.getHeight() + mShortcutPreviewPadding);
- }
-
- if (widgetItem.hasPreviewLayout()
- || mCachingPreviewLoader.isPreviewLoaded(widgetItem, widgetSize)) {
- // The widget is ready if it can be rendered with a preview layout or if its
- // preview bitmap is in the cache.
- continue;
- }
-
- // If we've reached this point, we should load the preview for the widget.
- allReady = false;
- mCachingPreviewLoader.loadPreview(
- activity,
- widgetItem,
- widgetSize,
- mPreviewLoadedCallback);
- }
- }
- return allReady;
- }
/** Returns whether {@code entry} matches {@code key}. */
private static boolean isHeaderForPackageUserKey(
@@ -361,13 +265,17 @@
public void resetExpandedHeader() {
if (mWidgetsContentVisiblePackageUserKey != null) {
mWidgetsContentVisiblePackageUserKey = null;
- cancelLoadingPreviews();
updateVisibleEntries();
}
}
@Override
- public void onBindViewHolder(ViewHolder holder, int pos) {
+ public void onBindViewHolder(ViewHolder holder, int position) {
+ onBindViewHolder(holder, position, Collections.EMPTY_LIST);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int pos, List<Object> payloads) {
ViewHolderBinder viewHolderBinder = mViewHolderBinders.get(getItemViewType(pos));
WidgetsListBaseEntry entry = mVisibleEntries.get(pos);
@@ -376,7 +284,7 @@
if (pos == (getItemCount() - 1)) {
listPos |= POSITION_LAST;
}
- viewHolderBinder.bindViewHolder(holder, mVisibleEntries.get(pos), listPos);
+ viewHolderBinder.bindViewHolder(holder, mVisibleEntries.get(pos), listPos, payloads);
holder.itemView.setTag(R.id.tag_widget_entry, entry);
}
@@ -430,11 +338,10 @@
// Ignore invalid clicks, such as collapsing a package that isn't currently expanded.
if (!showWidgets && !packageUserKey.equals(mWidgetsContentVisiblePackageUserKey)) return;
- cancelLoadingPreviews();
-
if (showWidgets) {
mWidgetsContentVisiblePackageUserKey = packageUserKey;
- mLauncher.getStatsLogManager().logger().log(LAUNCHER_WIDGETSTRAY_APP_EXPANDED);
+ ActivityContext.lookupContext(mContext)
+ .getStatsLogManager().logger().log(LAUNCHER_WIDGETSTRAY_APP_EXPANDED);
} else {
mWidgetsContentVisiblePackageUserKey = null;
}
@@ -446,16 +353,6 @@
updateVisibleEntries();
}
- private void cancelLoadingPreviews() {
- mCachingPreviewLoader.clearAll();
- }
-
- /** Returns the position of the currently expanded header, or empty if it's not present. */
- public OptionalInt getSelectedHeaderPosition() {
- if (mWidgetsContentVisiblePackageUserKey == null) return OptionalInt.empty();
- return getPositionForPackageUserKey(mWidgetsContentVisiblePackageUserKey);
- }
-
/**
* Returns the position of {@code key} in {@link #mVisibleEntries}, or empty if it's not
* present.
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
index 00750bd..fadb637 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
@@ -23,6 +23,8 @@
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
+import java.util.List;
+
/**
* Binds data from {@link WidgetsListHeaderEntry} to UI elements in {@link WidgetsListHeaderHolder}.
*/
@@ -50,7 +52,7 @@
@Override
public void bindViewHolder(WidgetsListHeaderHolder viewHolder, WidgetsListHeaderEntry data,
- @ListPosition int position) {
+ @ListPosition int position, List<Object> payloads) {
WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
widgetsListHeader.applyFromItemInfoWithIcon(data);
widgetsListHeader.setExpanded(data.isWidgetListShown());
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
index 1e2a3bf..bff43c1 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
@@ -24,6 +24,8 @@
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
+import java.util.List;
+
/**
* Binds data from {@link WidgetsListHeaderEntry} to UI elements in {@link WidgetsListHeaderHolder}.
*/
@@ -51,7 +53,7 @@
@Override
public void bindViewHolder(WidgetsListSearchHeaderHolder viewHolder,
- WidgetsListSearchHeaderEntry data, @ListPosition int position) {
+ WidgetsListSearchHeaderEntry data, @ListPosition int position, List<Object> payloads) {
WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
widgetsListHeader.applyFromItemInfoWithIcon(data);
widgetsListHeader.setExpanded(data.isWidgetListShown());
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index 804b0ae..8c9ff09 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -20,7 +20,7 @@
import android.graphics.Bitmap;
import android.util.Log;
-import android.util.Size;
+import android.util.Pair;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -33,7 +33,6 @@
import com.android.launcher3.R;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.recyclerview.ViewHolderBinder;
-import com.android.launcher3.widget.CachingWidgetPreviewLoader;
import com.android.launcher3.widget.WidgetCell;
import com.android.launcher3.widget.model.WidgetsListContentEntry;
import com.android.launcher3.widget.util.WidgetsTableUtils;
@@ -53,31 +52,18 @@
private final OnClickListener mIconClickListener;
private final OnLongClickListener mIconLongClickListener;
private final WidgetsListDrawableFactory mListDrawableFactory;
- private final CachingWidgetPreviewLoader mWidgetPreviewLoader;
- private boolean mApplyBitmapDeferred = false;
public WidgetsListTableViewHolderBinder(
LayoutInflater layoutInflater,
OnClickListener iconClickListener,
OnLongClickListener iconLongClickListener,
- CachingWidgetPreviewLoader widgetPreviewLoader,
WidgetsListDrawableFactory listDrawableFactory) {
mLayoutInflater = layoutInflater;
mIconClickListener = iconClickListener;
mIconLongClickListener = iconLongClickListener;
- mWidgetPreviewLoader = widgetPreviewLoader;
mListDrawableFactory = listDrawableFactory;
}
- /**
- * Defers applying bitmap on all the {@link WidgetCell} at
- * {@link #bindViewHolder(WidgetsRowViewHolder, WidgetsListContentEntry, int)} if
- * {@code applyBitmapDeferred} is {@code true}.
- */
- public void setApplyBitmapDeferred(boolean applyBitmapDeferred) {
- mApplyBitmapDeferred = applyBitmapDeferred;
- }
-
@Override
public WidgetsRowViewHolder newViewHolder(ViewGroup parent) {
if (DEBUG) {
@@ -87,25 +73,30 @@
WidgetsRowViewHolder viewHolder =
new WidgetsRowViewHolder(mLayoutInflater.inflate(
R.layout.widgets_table_container, parent, false));
- viewHolder.mTableContainer.setBackgroundDrawable(
+ viewHolder.tableContainer.setBackgroundDrawable(
mListDrawableFactory.createContentBackgroundDrawable());
return viewHolder;
}
@Override
public void bindViewHolder(WidgetsRowViewHolder holder, WidgetsListContentEntry entry,
- @ListPosition int position) {
- WidgetsListTableView table = holder.mTableContainer;
+ @ListPosition int position, List<Object> payloads) {
+ for (Object payload : payloads) {
+ Pair<WidgetItem, Bitmap> pair = (Pair) payload;
+ holder.previewCache.put(pair.first, pair.second);
+ }
+
+ WidgetsListTableView table = holder.tableContainer;
if (DEBUG) {
Log.d(TAG, String.format("onBindViewHolder [widget#=%d, table.getChildCount=%d]",
entry.mWidgets.size(), table.getChildCount()));
}
table.setListDrawableState(((position & POSITION_LAST) != 0) ? LAST : MIDDLE);
-
List<ArrayList<WidgetItem>> widgetItemsTable =
WidgetsTableUtils.groupWidgetItemsIntoTable(
entry.mWidgets, entry.getMaxSpanSizeInCells());
recycleTableBeforeBinding(table, widgetItemsTable);
+
// Bind the widget items.
for (int i = 0; i < widgetItemsTable.size(); i++) {
List<WidgetItem> widgetItemsPerRow = widgetItemsTable.get(i);
@@ -115,16 +106,17 @@
WidgetCell widget = (WidgetCell) row.getChildAt(j);
widget.clear();
WidgetItem widgetItem = widgetItemsPerRow.get(j);
- Size previewSize = widget.setPreviewSize(widgetItem);
- widget.applyFromCellItem(widgetItem, mWidgetPreviewLoader);
- widget.setApplyBitmapDeferred(mApplyBitmapDeferred);
- Bitmap preview = mWidgetPreviewLoader.getPreview(widgetItem, previewSize);
- if (preview == null) {
- widget.ensurePreview();
- } else {
- widget.applyPreview(preview);
- }
widget.setVisibility(View.VISIBLE);
+
+ // When preview loads, notify adapter to rebind the item and possibly animate
+ widget.applyFromCellItem(widgetItem, 1f,
+ bitmap -> {
+ if (holder.getBindingAdapter() != null) {
+ holder.getBindingAdapter().notifyItemChanged(
+ holder.getBindingAdapterPosition(),
+ Pair.create(widgetItem, bitmap));
+ }
+ }, holder.previewCache.get(widgetItem));
}
}
}
@@ -165,6 +157,7 @@
View preview = widget.findViewById(R.id.widget_preview_container);
preview.setOnClickListener(mIconClickListener);
preview.setOnLongClickListener(mIconLongClickListener);
+ widget.setAnimatePreview(false);
tableRow.addView(widget);
}
}
@@ -173,9 +166,10 @@
@Override
public void unbindViewHolder(WidgetsRowViewHolder holder) {
- int numOfRows = holder.mTableContainer.getChildCount();
+ int numOfRows = holder.tableContainer.getChildCount();
+ holder.previewCache.clear();
for (int i = 0; i < numOfRows; i++) {
- TableRow tableRow = (TableRow) holder.mTableContainer.getChildAt(i);
+ TableRow tableRow = (TableRow) holder.tableContainer.getChildAt(i);
int numOfCols = tableRow.getChildCount();
for (int j = 0; j < numOfCols; j++) {
WidgetCell widget = (WidgetCell) tableRow.getChildAt(j);
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
index 60dfebe..c986007 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
@@ -32,7 +32,6 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.widget.WidgetCell;
@@ -109,10 +108,7 @@
for (WidgetItem widgetItem : widgetItems) {
WidgetCell widgetCell = addItemCell(tableRow);
- widgetCell.setPreviewSize(widgetItem, data.mPreviewScale);
- widgetCell.applyFromCellItem(widgetItem,
- LauncherAppState.getInstance(getContext()).getWidgetCache());
- widgetCell.ensurePreview();
+ widgetCell.applyFromCellItem(widgetItem, data.mPreviewScale);
}
addView(tableRow);
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java b/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java
index 618e2cb..fe2d84b 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java
@@ -15,20 +15,26 @@
*/
package com.android.launcher3.widget.picker;
+import android.graphics.Bitmap;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.android.launcher3.R;
+import com.android.launcher3.model.WidgetItem;
+
+import java.util.HashMap;
+import java.util.Map;
/** A {@link ViewHolder} for showing widgets of an app in the full widget picker. */
public final class WidgetsRowViewHolder extends ViewHolder {
- public final WidgetsListTableView mTableContainer;
+ public final WidgetsListTableView tableContainer;
+ public final Map<WidgetItem, Bitmap> previewCache = new HashMap<>();
public WidgetsRowViewHolder(View v) {
super(v);
- mTableContainer = v.findViewById(R.id.widgets_table);
+ tableContainer = v.findViewById(R.id.widgets_table);
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsSpaceViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsSpaceViewHolderBinder.java
index f33c2fa..1aa5753 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsSpaceViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsSpaceViewHolderBinder.java
@@ -27,6 +27,7 @@
import com.android.launcher3.recyclerview.ViewHolderBinder;
import com.android.launcher3.widget.model.WidgetListSpaceEntry;
+import java.util.List;
import java.util.function.IntSupplier;
/**
@@ -47,7 +48,8 @@
}
@Override
- public void bindViewHolder(ViewHolder holder, WidgetListSpaceEntry data, int position) {
+ public void bindViewHolder(ViewHolder holder, WidgetListSpaceEntry data,
+ @ListPosition int position, List<Object> payloads) {
((EmptySpaceView) holder.itemView).setFixedHeight(mEmptySpaceHeightProvider.getAsInt());
}
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index 631067b..12e9e1e 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -150,7 +150,6 @@
}
}
- app.getWidgetCache().removeObsoletePreviews(widgetsAndShortcuts, packageUser);
return updatedItems;
}
diff --git a/tests/Android.bp b/tests/Android.bp
index da55c28..aeddc4c 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -20,7 +20,78 @@
default_applicable_licenses: ["packages_apps_Launcher3_license"],
}
+// Source code used for test
filegroup {
- name: "launcher3-test-src-common",
- srcs: ["src_common/**/*.java"],
+ name: "launcher-tests-src",
+ srcs: ["src/**/*.java"],
+}
+
+// Source code used for oop test helpers
+filegroup {
+ name: "launcher-oop-tests-src",
+ srcs: [
+ "src/com/android/launcher3/ui/AbstractLauncherUiTest.java",
+ "src/com/android/launcher3/ui/ActivityLeakTracker.java",
+ "src/com/android/launcher3/ui/PortraitLandscapeRunner.java",
+ "src/com/android/launcher3/util/Wait.java",
+ "src/com/android/launcher3/util/WidgetUtils.java",
+ "src/com/android/launcher3/util/rule/FailureWatcher.java",
+ "src/com/android/launcher3/util/rule/LauncherActivityRule.java",
+ "src/com/android/launcher3/util/rule/ScreenRecordRule.java",
+ "src/com/android/launcher3/util/rule/ShellCommandRule.java",
+ "src/com/android/launcher3/util/rule/SimpleActivityRule.java",
+ "src/com/android/launcher3/util/rule/TestStabilityRule.java",
+ "src/com/android/launcher3/ui/TaplTestsLauncher3.java",
+ "src/com/android/launcher3/testcomponent/BaseTestingActivity.java",
+ "src/com/android/launcher3/testcomponent/CustomShortcutConfigActivity.java",
+ "src/com/android/launcher3/testcomponent/TestCommandReceiver.java",
+ "src/com/android/launcher3/testcomponent/TestLauncherActivity.java",
+ ],
+}
+
+// Library with all the dependencies for building quickstep
+android_library {
+ name: "Launcher3TestLib",
+ srcs: [ ],
+ resource_dirs: ["res"],
+ static_libs: [
+ "launcher-aosp-tapl",
+ "androidx.test.core",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "androidx.test.espresso.core",
+ "androidx.test.espresso.contrib",
+ "androidx.test.espresso.intents",
+ "androidx.test.uiautomator_uiautomator",
+ "mockito-target-inline-minus-junit4",
+ "launcher_log_protos_lite",
+ "truth-prebuilt"
+ ],
+ manifest: "AndroidManifest-common.xml",
+ platform_apis: true,
+}
+
+android_test {
+ name: "Launcher3Tests",
+ srcs: [
+ ":launcher-tests-src",
+ ],
+ static_libs: ["Launcher3TestLib"],
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ "android.test.mock",
+ ],
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+ use_embedded_native_libs: false,
+ compile_multilib: "both",
+ instrumentation_for: "Launcher3",
+ manifest: "AndroidManifest.xml",
+ platform_apis: true,
+ test_config: "Launcher3Tests.xml",
+ data: [":Launcher3"]
}
diff --git a/tests/Android.mk b/tests/Android.mk
deleted file mode 100644
index 6adc685..0000000
--- a/tests/Android.mk
+++ /dev/null
@@ -1,54 +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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-#
-# Build rule for Launcher3Tests
-#
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_STATIC_JAVA_LIBRARIES := \
- androidx.test.runner \
- androidx.test.rules \
- androidx.test.uiautomator_uiautomator \
- mockito-target-minus-junit4 \
- launcher_log_protos_lite
-
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_STATIC_JAVA_LIBRARIES += launcher-aosp-tapl
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
- $(call all-java-files-under, src_common)
-
-
-LOCAL_FULL_LIBS_MANIFEST_FILES := $(LOCAL_PATH)/AndroidManifest-common.xml
-
-LOCAL_PACKAGE_NAME := Launcher3Tests
-
-LOCAL_INSTRUMENTATION_FOR := Launcher3
-
-LOCAL_TEST_CONFIG := Launcher3Tests.xml
-
-LOCAL_COMPATIBILITY_SUPPORT_FILES := $(call intermediates-dir-for,APPS,Launcher3)/package.apk:Launcher3.apk
-
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../NOTICE
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 918ec4a..8222f75 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -24,7 +24,7 @@
<uses-permission android:name="android.permission.READ_LOGS"/>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
- <application android:debuggable="true">
+ <application android:debuggable="true" android:extractNativeLibs="true">
<uses-library android:name="android.test.runner"/>
<receiver
diff --git a/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java b/tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
similarity index 90%
rename from robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
rename to tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
index 2a94d9b..23e6235 100644
--- a/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
+++ b/tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.folder;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -23,6 +25,9 @@
import android.content.Intent;
import android.os.UserHandle;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.Executors;
@@ -30,15 +35,11 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.LooperMode;
-import org.robolectric.annotation.LooperMode.Mode;
import java.util.ArrayList;
-@RunWith(RobolectricTestRunner.class)
-@LooperMode(Mode.PAUSED)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public final class FolderNameProviderTest {
private Context mContext;
private WorkspaceItemInfo mItem1;
@@ -46,7 +47,7 @@
@Before
public void setUp() {
- mContext = RuntimeEnvironment.application;
+ mContext = getApplicationContext();
mItem1 = new WorkspaceItemInfo(new AppInfo(
new ComponentName("a.b.c", "a.b.c/a.b.c.d"),
"title1",
diff --git a/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java b/tests/src/com/android/launcher3/logging/FileLogTest.java
similarity index 89%
rename from robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
rename to tests/src/com/android/launcher3/logging/FileLogTest.java
index 01b23ba..e5f8cec 100644
--- a/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
+++ b/tests/src/com/android/launcher3/logging/FileLogTest.java
@@ -1,16 +1,17 @@
package com.android.launcher3.logging;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
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.annotation.LooperMode;
-import org.robolectric.annotation.LooperMode.Mode;
import java.io.File;
import java.io.PrintWriter;
@@ -20,8 +21,8 @@
/**
* Tests for {@link FileLog}
*/
-@RunWith(RobolectricTestRunner.class)
-@LooperMode(Mode.PAUSED)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public class FileLogTest {
private File mTempDir;
@@ -29,7 +30,7 @@
public void setUp() {
int count = 0;
do {
- mTempDir = new File(RuntimeEnvironment.application.getCacheDir(),
+ mTempDir = new File(getApplicationContext().getCacheDir(),
"log-test-" + (count++));
} while (!mTempDir.mkdir());
diff --git a/robolectric_tests/src/com/android/launcher3/popup/PopupPopulatorTest.java b/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
similarity index 95%
rename from robolectric_tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
rename to tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
index 83bf7da..6764e09 100644
--- a/robolectric_tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
+++ b/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
@@ -16,6 +16,8 @@
package com.android.launcher3.popup;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
import static com.android.launcher3.popup.PopupPopulator.NUM_DYNAMIC;
@@ -27,10 +29,11 @@
import android.content.pm.ShortcutInfo;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Collections;
@@ -39,7 +42,8 @@
/**
* Tests the sorting and filtering of shortcuts in {@link PopupPopulator}.
*/
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public class PopupPopulatorTest {
@Test
@@ -137,7 +141,7 @@
private ShortcutInfo createInfo(boolean isStatic, int rank) {
ShortcutInfo info = spy(new ShortcutInfo.Builder(
- RuntimeEnvironment.application, generateId(isStatic, rank))
+ getApplicationContext(), generateId(isStatic, rank))
.setRank(rank)
.build());
doReturn(isStatic).when(info).isDeclaredInManifest();
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 0d5b9ad..1a6ce8c 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -52,7 +52,6 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
-import com.android.launcher3.common.WidgetUtils;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.tapl.LauncherInstrumentation;
@@ -63,6 +62,7 @@
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Wait;
+import com.android.launcher3.util.WidgetUtils;
import com.android.launcher3.util.rule.FailureWatcher;
import com.android.launcher3.util.rule.LauncherActivityRule;
import com.android.launcher3.util.rule.ScreenRecordRule;
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index 9c6c317..fa39ce0 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -17,7 +17,7 @@
import static androidx.test.InstrumentationRegistry.getTargetContext;
-import static com.android.launcher3.common.WidgetUtils.createWidgetInfo;
+import static com.android.launcher3.util.WidgetUtils.createWidgetInfo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
diff --git a/tests/src/com/android/launcher3/util/ActivityContextWrapper.java b/tests/src/com/android/launcher3/util/ActivityContextWrapper.java
new file mode 100644
index 0000000..2618a2e
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/ActivityContextWrapper.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.view.ContextThemeWrapper;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.BaseDragLayer;
+
+/**
+ * {@link ContextWrapper} with internal Launcher interface for testing
+ */
+public class ActivityContextWrapper extends ContextThemeWrapper implements ActivityContext {
+
+ private final DeviceProfile mProfile;
+ private final MyDragLayer mMyDragLayer;
+
+ public ActivityContextWrapper(Context base) {
+ super(base, android.R.style.Theme_DeviceDefault);
+ mProfile = InvariantDeviceProfile.INSTANCE.get(base).getDeviceProfile(base).copy(base);
+ mMyDragLayer = new MyDragLayer(this);
+ }
+
+ @Override
+ public BaseDragLayer getDragLayer() {
+ return mMyDragLayer;
+ }
+
+ @Override
+ public DeviceProfile getDeviceProfile() {
+ return mProfile;
+ }
+
+ private static class MyDragLayer extends BaseDragLayer<ActivityContextWrapper> {
+
+ MyDragLayer(Context context) {
+ super(context, null, 1);
+ }
+
+ @Override
+ public void recreateControllers() {
+ mControllers = new TouchController[0];
+ }
+ }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/util/GridOccupancyTest.java b/tests/src/com/android/launcher3/util/GridOccupancyTest.java
similarity index 92%
rename from robolectric_tests/src/com/android/launcher3/util/GridOccupancyTest.java
rename to tests/src/com/android/launcher3/util/GridOccupancyTest.java
index 2f3fc37..b498077 100644
--- a/robolectric_tests/src/com/android/launcher3/util/GridOccupancyTest.java
+++ b/tests/src/com/android/launcher3/util/GridOccupancyTest.java
@@ -4,14 +4,17 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
/**
* Unit tests for {@link GridOccupancy}
*/
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public class GridOccupancyTest {
@Test
diff --git a/robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java b/tests/src/com/android/launcher3/util/IntArrayTest.java
similarity index 86%
rename from robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java
rename to tests/src/com/android/launcher3/util/IntArrayTest.java
index c08e198..a3c7007 100644
--- a/robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java
+++ b/tests/src/com/android/launcher3/util/IntArrayTest.java
@@ -17,14 +17,17 @@
import static com.google.common.truth.Truth.assertThat;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
/**
- * Robolectric unit tests for {@link IntArray}
+ * Unit tests for {@link IntArray}
*/
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public class IntArrayTest {
@Test
diff --git a/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java b/tests/src/com/android/launcher3/util/IntSetTest.java
similarity index 91%
rename from robolectric_tests/src/com/android/launcher3/util/IntSetTest.java
rename to tests/src/com/android/launcher3/util/IntSetTest.java
index 7a8c00b..cdb2891 100644
--- a/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java
+++ b/tests/src/com/android/launcher3/util/IntSetTest.java
@@ -21,14 +21,17 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
/**
- * Robolectric unit tests for {@link IntSet}
+ * Unit tests for {@link IntSet}
*/
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public class IntSetTest {
@Test
diff --git a/tests/src_common/com/android/launcher3/common/WidgetUtils.java b/tests/src/com/android/launcher3/util/WidgetUtils.java
similarity index 82%
rename from tests/src_common/com/android/launcher3/common/WidgetUtils.java
rename to tests/src/com/android/launcher3/util/WidgetUtils.java
index 97500e3..6fc8491 100644
--- a/tests/src_common/com/android/launcher3/common/WidgetUtils.java
+++ b/tests/src/com/android/launcher3/util/WidgetUtils.java
@@ -13,19 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.launcher3.common;
+package com.android.launcher3.util;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.os.Bundle;
+import android.os.Process;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.widget.LauncherAppWidgetHost;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -102,4 +108,17 @@
resolver.insert(LauncherSettings.Favorites.CONTENT_URI,
writer.getValues(targetContext));
}
+
+
+ /**
+ * Creates a {@link AppWidgetProviderInfo} for the provided component name
+ */
+ public static AppWidgetProviderInfo createAppWidgetProviderInfo(ComponentName cn) {
+ AppWidgetProviderInfo info = AppWidgetManager.getInstance(getApplicationContext())
+ .getInstalledProvidersForPackage(
+ getInstrumentation().getContext().getPackageName(), Process.myUserHandle())
+ .get(0);
+ info.provider = cn;
+ return info;
+ }
}
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index 0b60ffc..fae55cc 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -30,6 +30,7 @@
public FailureWatcher(UiDevice device, LauncherInstrumentation launcher) {
mDevice = device;
mLauncher = launcher;
+ Log.d("b/196820244", "FailureWatcher.ctor", new Exception());
}
@Override
@@ -44,7 +45,9 @@
@Override
public void evaluate() throws Throwable {
try {
+ Log.d("b/196820244", "Before evaluate");
FailureWatcher.super.apply(base, description).evaluate();
+ Log.d("b/196820244", "After evaluate");
} finally {
if (mLauncher.hadNontestEvents()) {
throw new AssertionError(
@@ -64,7 +67,9 @@
}
public static void onError(UiDevice device, Description description, Throwable e) {
+ Log.d("b/196820244", "onError 1");
if (device == null) return;
+ Log.d("b/196820244", "onError 2");
final File parentFile = getInstrumentation().getTargetContext().getFilesDir();
final File sceenshot = new File(parentFile,
"TestScreenshot-" + description.getMethodName() + ".png");
diff --git a/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java b/tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
similarity index 96%
rename from robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
rename to tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
index a6f892c..24ae583 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
+++ b/tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.widget;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -25,6 +27,9 @@
import android.graphics.Point;
import android.graphics.Rect;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
@@ -32,15 +37,13 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public final class LauncherAppWidgetProviderInfoTest {
private static final int CELL_SIZE = 50;
@@ -51,8 +54,7 @@
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
+ mContext = getApplicationContext();
}
@Test
@@ -260,6 +262,7 @@
return null;
}).when(profile).getCellSize(any(Point.class));
Mockito.when(profile.getCellSize()).thenReturn(new Point(CELL_SIZE, CELL_SIZE));
+ Mockito.when(profile.shouldInsetWidgets()).thenReturn(true);
InvariantDeviceProfile idp = new InvariantDeviceProfile();
List<DeviceProfile> supportedProfiles = new ArrayList<>(idp.supportedProfiles);
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java
similarity index 95%
rename from robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java
rename to tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java
index b9f183c..6232938 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java
@@ -15,13 +15,16 @@
*/
package com.android.launcher3.widget.picker;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.robolectric.Shadows.shadowOf;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
@@ -30,6 +33,8 @@
import android.os.UserHandle;
import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.icons.BitmapInfo;
@@ -48,15 +53,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public final class WidgetsDiffReporterTest {
private static final String TEST_PACKAGE_PREFIX = "com.android.test";
private static final WidgetListBaseRowEntryComparator COMPARATOR =
@@ -87,7 +89,7 @@
.getComponent().getPackageName())
.when(mIconCache).getTitleNoCache(any());
- mContext = RuntimeEnvironment.application;
+ mContext = getApplicationContext();
mWidgetsDiffReporter = new WidgetsDiffReporter(mIconCache, mAdapter);
mHeaderA = createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "A",
/* appName= */ "A", /* numOfWidgets= */ 3);
@@ -294,14 +296,10 @@
}
private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
- ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
ArrayList<WidgetItem> widgetItems = new ArrayList<>();
for (int i = 0; i < numOfWidgets; i++) {
ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
- AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
- widgetInfo.provider = cn;
- ReflectionHelpers.setField(widgetInfo, "providerInfo",
- packageManager.addReceiverIfNotPresent(cn));
+ AppWidgetProviderInfo widgetInfo = createAppWidgetProviderInfo(cn);
WidgetItem widgetItem = new WidgetItem(
LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
similarity index 91%
rename from robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
rename to tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
index fb44ca1..44d6964 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
@@ -15,12 +15,13 @@
*/
package com.android.launcher3.widget.picker;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
-import static org.robolectric.Shadows.shadowOf;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
@@ -31,6 +32,8 @@
import android.view.LayoutInflater;
import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.icons.BitmapInfo;
@@ -38,8 +41,9 @@
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.util.ActivityContextWrapper;
import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.widget.DatabaseWidgetPreviewLoader;
+import com.android.launcher3.util.WidgetUtils;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import com.android.launcher3.widget.model.WidgetsListContentEntry;
@@ -51,20 +55,20 @@
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
-@RunWith(RobolectricTestRunner.class)
+/**
+ * Unit tests for WidgetsListAdapter
+ * Note that all indices matching are shifted by 1 to account for the empty space at the start.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public final class WidgetsListAdapterTest {
private static final String TEST_PACKAGE_PLACEHOLDER = "com.google.test";
@Mock private LayoutInflater mMockLayoutInflater;
- @Mock private DatabaseWidgetPreviewLoader mMockWidgetCache;
@Mock private RecyclerView.AdapterDataObserver mListener;
@Mock private IconCache mIconCache;
@@ -76,12 +80,12 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
+ mContext = new ActivityContextWrapper(getApplicationContext());
mTestProfile = new InvariantDeviceProfile();
mTestProfile.numRows = 5;
mTestProfile.numColumns = 5;
mUserHandle = Process.myUserHandle();
- mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater, mMockWidgetCache,
+ mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater,
mIconCache, () -> 0, null, null);
mAdapter.registerAdapterDataObserver(mListener);
@@ -102,7 +106,7 @@
mAdapter.setWidgets(generateSampleMap(1));
mAdapter.setWidgets(generateSampleMap(2));
- verify(mListener).onItemRangeInserted(eq(1), eq(1));
+ verify(mListener).onItemRangeInserted(eq(2), eq(1));
}
@Test
@@ -110,7 +114,7 @@
mAdapter.setWidgets(generateSampleMap(2));
mAdapter.setWidgets(generateSampleMap(1));
- verify(mListener).onItemRangeRemoved(eq(1), eq(1));
+ verify(mListener).onItemRangeRemoved(eq(2), eq(1));
}
@Test
@@ -118,7 +122,7 @@
mAdapter.setWidgets(generateSampleMap(1));
mAdapter.setWidgets(generateSampleMap(1));
- verify(mListener).onItemRangeChanged(eq(0), eq(1), isNull());
+ verify(mListener).onItemRangeChanged(eq(1), eq(1), isNull());
}
@Test
@@ -137,7 +141,7 @@
// THEN the visible entries list becomes:
// [com.google.test0, com.google.test1, com.google.test1 content, com.google.test2]
// com.google.test.1 content is inserted into position 2.
- verify(mListener).onItemRangeInserted(eq(2), eq(1));
+ verify(mListener).onItemRangeInserted(eq(3), eq(1));
}
@Test
@@ -165,7 +169,7 @@
mAdapter.setWidgets(allEntries);
// THEN the onItemRangeChanged is invoked for "com.google.test1 content" at index 2.
- verify(mListener).onItemRangeChanged(eq(2), eq(1), isNull());
+ verify(mListener).onItemRangeChanged(eq(3), eq(1), isNull());
}
@Test
@@ -196,15 +200,16 @@
allAppsWithWidgets.get(6), allAppsWithWidgets.get(7));
mAdapter.setWidgets(newList);
+ // Account for 1st items as empty space
// Computation logic | [Intermediate list during computation]
// THEN B <> C < 0, removed B from index 1 | [A, E]
- verify(mListener).onItemRangeRemoved(/* positionStart= */ 1, /* itemCount= */ 1);
+ verify(mListener).onItemRangeRemoved(/* positionStart= */ 2, /* itemCount= */ 1);
// THEN E <> C > 0, C inserted to index 1 | [A, C, E]
- verify(mListener).onItemRangeInserted(/* positionStart= */ 1, /* itemCount= */ 1);
- // THEN E <> D > 0, D inserted to index 2 | [A, C, D, E]
verify(mListener).onItemRangeInserted(/* positionStart= */ 2, /* itemCount= */ 1);
+ // THEN E <> D > 0, D inserted to index 2 | [A, C, D, E]
+ verify(mListener).onItemRangeInserted(/* positionStart= */ 3, /* itemCount= */ 1);
// THEN E <> null = -1, E deleted from index 3 | [A, C, D]
- verify(mListener).onItemRangeRemoved(/* positionStart= */ 3, /* itemCount= */ 1);
+ verify(mListener).onItemRangeRemoved(/* positionStart= */ 4, /* itemCount= */ 1);
}
@Test
@@ -227,8 +232,8 @@
// THEN expanded app is reset and the visible entries list becomes:
// [com.google.test0, com.google.test1, com.google.test2]
- verify(mListener).onItemRangeChanged(eq(1), eq(1), isNull());
- verify(mListener).onItemRangeRemoved(/* positionStart= */ 2, /* itemCount= */ 1);
+ verify(mListener).onItemRangeChanged(eq(2), eq(1), isNull());
+ verify(mListener).onItemRangeRemoved(/* positionStart= */ 3, /* itemCount= */ 1);
}
/**
@@ -265,14 +270,10 @@
}
private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
- ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
ArrayList<WidgetItem> widgetItems = new ArrayList<>();
for (int i = 0; i < numOfWidgets; i++) {
ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
- AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
- widgetInfo.provider = cn;
- ReflectionHelpers.setField(widgetInfo, "providerInfo",
- packageManager.addReceiverIfNotPresent(cn));
+ AppWidgetProviderInfo widgetInfo = WidgetUtils.createAppWidgetProviderInfo(cn);
widgetItems.add(new WidgetItem(
LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
similarity index 76%
rename from robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
rename to tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
index b7d7788..969c12a 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
@@ -15,13 +15,16 @@
*/
package com.android.launcher3.widget.picker;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
-import static org.robolectric.Shadows.shadowOf;
+
+import static java.util.Collections.EMPTY_LIST;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
@@ -31,7 +34,9 @@
import android.widget.FrameLayout;
import android.widget.TextView;
-import com.android.launcher3.DeviceProfile;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.icons.BitmapInfo;
@@ -39,28 +44,23 @@
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.PackageItemInfo;
-import com.android.launcher3.testing.TestActivity;
+import com.android.launcher3.util.ActivityContextWrapper;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.WidgetUtils;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.android.controller.ActivityController;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public final class WidgetsListHeaderViewHolderBinderTest {
private static final String TEST_PACKAGE = "com.google.test";
private static final String APP_NAME = "Test app";
@@ -68,55 +68,41 @@
private Context mContext;
private WidgetsListHeaderViewHolderBinder mViewHolderBinder;
private InvariantDeviceProfile mTestProfile;
- // Replace ActivityController with ActivityScenario, which is the recommended way for activity
- // testing.
- private ActivityController<TestActivity> mActivityController;
- private TestActivity mTestActivity;
@Mock
private IconCache mIconCache;
@Mock
- private DeviceProfile mDeviceProfile;
- @Mock
private OnHeaderClickListener mOnHeaderClickListener;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
+
+ mContext = new ActivityContextWrapper(getApplicationContext());
mTestProfile = new InvariantDeviceProfile();
mTestProfile.numRows = 5;
mTestProfile.numColumns = 5;
- mActivityController = Robolectric.buildActivity(TestActivity.class);
- mTestActivity = mActivityController.setup().get();
- mTestActivity.setDeviceProfile(mDeviceProfile);
-
doAnswer(invocation -> {
ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
return componentWithLabel.getComponent().getShortClassName();
}).when(mIconCache).getTitleNoCache(any());
mViewHolderBinder = new WidgetsListHeaderViewHolderBinder(
- LayoutInflater.from(mTestActivity),
+ LayoutInflater.from(mContext),
mOnHeaderClickListener,
- new WidgetsListDrawableFactory(mTestActivity));
- }
-
- @After
- public void tearDown() {
- mActivityController.destroy();
+ new WidgetsListDrawableFactory(mContext));
}
@Test
public void bindViewHolder_appWith3Widgets_shouldShowTheCorrectAppNameAndSubtitle() {
WidgetsListHeaderHolder viewHolder = mViewHolderBinder.newViewHolder(
- new FrameLayout(mTestActivity));
+ new FrameLayout(mContext));
WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
WidgetsListHeaderEntry entry = generateSampleAppHeader(
APP_NAME,
TEST_PACKAGE,
/* numOfWidgets= */ 3);
- mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
+ mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0, EMPTY_LIST);
TextView appTitle = widgetsListHeader.findViewById(R.id.app_title);
TextView appSubtitle = widgetsListHeader.findViewById(R.id.app_subtitle);
@@ -127,14 +113,14 @@
@Test
public void bindViewHolder_shouldAttachOnHeaderClickListener() {
WidgetsListHeaderHolder viewHolder = mViewHolderBinder.newViewHolder(
- new FrameLayout(mTestActivity));
+ new FrameLayout(mContext));
WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
WidgetsListHeaderEntry entry = generateSampleAppHeader(
APP_NAME,
TEST_PACKAGE,
/* numOfWidgets= */ 3);
- mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
+ mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0, EMPTY_LIST);
widgetsListHeader.callOnClick();
verify(mOnHeaderClickListener).onHeaderClicked(eq(true),
@@ -153,14 +139,10 @@
}
private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
- ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
ArrayList<WidgetItem> widgetItems = new ArrayList<>();
for (int i = 0; i < numOfWidgets; i++) {
ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
- AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
- widgetInfo.provider = cn;
- ReflectionHelpers.setField(widgetInfo, "providerInfo",
- packageManager.addReceiverIfNotPresent(cn));
+ AppWidgetProviderInfo widgetInfo = WidgetUtils.createAppWidgetProviderInfo(cn);
widgetItems.add(new WidgetItem(
LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
similarity index 76%
rename from robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
rename to tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
index 2b4cea0..453f4fb 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
@@ -15,13 +15,16 @@
*/
package com.android.launcher3.widget.picker;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
-import static org.robolectric.Shadows.shadowOf;
+
+import static java.util.Collections.EMPTY_LIST;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
@@ -31,7 +34,9 @@
import android.widget.FrameLayout;
import android.widget.TextView;
-import com.android.launcher3.DeviceProfile;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.icons.BitmapInfo;
@@ -39,28 +44,23 @@
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.PackageItemInfo;
-import com.android.launcher3.testing.TestActivity;
+import com.android.launcher3.util.ActivityContextWrapper;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.WidgetUtils;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.android.controller.ActivityController;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public final class WidgetsListSearchHeaderViewHolderBinderTest {
private static final String TEST_PACKAGE = "com.google.test";
private static final String APP_NAME = "Test app";
@@ -68,55 +68,40 @@
private Context mContext;
private WidgetsListSearchHeaderViewHolderBinder mViewHolderBinder;
private InvariantDeviceProfile mTestProfile;
- // Replace ActivityController with ActivityScenario, which is the recommended way for activity
- // testing.
- private ActivityController<TestActivity> mActivityController;
- private TestActivity mTestActivity;
@Mock
private IconCache mIconCache;
@Mock
- private DeviceProfile mDeviceProfile;
- @Mock
private OnHeaderClickListener mOnHeaderClickListener;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
+ mContext = new ActivityContextWrapper(getApplicationContext());
mTestProfile = new InvariantDeviceProfile();
mTestProfile.numRows = 5;
mTestProfile.numColumns = 5;
- mActivityController = Robolectric.buildActivity(TestActivity.class);
- mTestActivity = mActivityController.setup().get();
- mTestActivity.setDeviceProfile(mDeviceProfile);
-
doAnswer(invocation -> {
ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
return componentWithLabel.getComponent().getShortClassName();
}).when(mIconCache).getTitleNoCache(any());
mViewHolderBinder = new WidgetsListSearchHeaderViewHolderBinder(
- LayoutInflater.from(mTestActivity),
+ LayoutInflater.from(mContext),
mOnHeaderClickListener,
- new WidgetsListDrawableFactory(mTestActivity));
- }
-
- @After
- public void tearDown() {
- mActivityController.destroy();
+ new WidgetsListDrawableFactory(mContext));
}
@Test
public void bindViewHolder_appWith3Widgets_shouldShowTheCorrectAppNameAndSubtitle() {
WidgetsListSearchHeaderHolder viewHolder = mViewHolderBinder.newViewHolder(
- new FrameLayout(mTestActivity));
+ new FrameLayout(mContext));
WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
WidgetsListSearchHeaderEntry entry = generateSampleSearchHeader(
APP_NAME,
TEST_PACKAGE,
/* numOfWidgets= */ 3);
- mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
+ mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0, EMPTY_LIST);
TextView appTitle = widgetsListHeader.findViewById(R.id.app_title);
TextView appSubtitle = widgetsListHeader.findViewById(R.id.app_subtitle);
@@ -128,14 +113,14 @@
@Test
public void bindViewHolder_shouldAttachOnHeaderClickListener() {
WidgetsListSearchHeaderHolder viewHolder = mViewHolderBinder.newViewHolder(
- new FrameLayout(mTestActivity));
+ new FrameLayout(mContext));
WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
WidgetsListSearchHeaderEntry entry = generateSampleSearchHeader(
APP_NAME,
TEST_PACKAGE,
/* numOfWidgets= */ 3);
- mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
+ mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0, EMPTY_LIST);
widgetsListHeader.callOnClick();
verify(mOnHeaderClickListener).onHeaderClicked(eq(true),
@@ -154,14 +139,10 @@
}
private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
- ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
ArrayList<WidgetItem> widgetItems = new ArrayList<>();
for (int i = 0; i < numOfWidgets; i++) {
ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
- AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
- widgetInfo.provider = cn;
- ReflectionHelpers.setField(widgetInfo, "providerInfo",
- packageManager.addReceiverIfNotPresent(cn));
+ AppWidgetProviderInfo widgetInfo = WidgetUtils.createAppWidgetProviderInfo(cn);
widgetItems.add(new WidgetItem(
LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
similarity index 70%
rename from robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
rename to tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
index 9f66fb7..5816b77 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
@@ -15,13 +15,14 @@
*/
package com.android.launcher3.widget.picker;
-import static android.os.Looper.getMainLooper;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
-import static org.robolectric.Shadows.shadowOf;
+
+import static java.util.Collections.EMPTY_LIST;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
@@ -35,7 +36,9 @@
import android.widget.TableRow;
import android.widget.TextView;
-import com.android.launcher3.DeviceProfile;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.icons.BitmapInfo;
@@ -43,30 +46,24 @@
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.PackageItemInfo;
-import com.android.launcher3.testing.TestActivity;
-import com.android.launcher3.widget.CachingWidgetPreviewLoader;
-import com.android.launcher3.widget.DatabaseWidgetPreviewLoader;
+import com.android.launcher3.util.ActivityContextWrapper;
+import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.WidgetUtils;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.WidgetCell;
import com.android.launcher3.widget.model.WidgetsListContentEntry;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.android.controller.ActivityController;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public final class WidgetsListTableViewHolderBinderTest {
private static final String TEST_PACKAGE = "com.google.test";
private static final String APP_NAME = "Test app";
@@ -74,10 +71,6 @@
private Context mContext;
private WidgetsListTableViewHolderBinder mViewHolderBinder;
private InvariantDeviceProfile mTestProfile;
- // Replace ActivityController with ActivityScenario, which is the recommended way for activity
- // testing.
- private ActivityController<TestActivity> mActivityController;
- private TestActivity mTestActivity;
@Mock
private OnLongClickListener mOnLongClickListener;
@@ -85,56 +78,42 @@
private OnClickListener mOnIconClickListener;
@Mock
private IconCache mIconCache;
- @Mock
- private DatabaseWidgetPreviewLoader mWidgetPreviewLoader;
- @Mock
- private DeviceProfile mDeviceProfile;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
+ mContext = new ActivityContextWrapper(getApplicationContext());
mTestProfile = new InvariantDeviceProfile();
mTestProfile.numRows = 5;
mTestProfile.numColumns = 5;
- mActivityController = Robolectric.buildActivity(TestActivity.class);
- mTestActivity = mActivityController.setup().get();
- mTestActivity.setDeviceProfile(mDeviceProfile);
-
doAnswer(invocation -> {
ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
return componentWithLabel.getComponent().getShortClassName();
}).when(mIconCache).getTitleNoCache(any());
mViewHolderBinder = new WidgetsListTableViewHolderBinder(
- LayoutInflater.from(mTestActivity),
+ LayoutInflater.from(mContext),
mOnIconClickListener,
mOnLongClickListener,
- new CachingWidgetPreviewLoader(mWidgetPreviewLoader),
- new WidgetsListDrawableFactory(mTestActivity));
- }
-
- @After
- public void tearDown() {
- mActivityController.destroy();
+ new WidgetsListDrawableFactory(mContext));
}
@Test
- public void bindViewHolder_appWith3Widgets_shouldHave3Widgets() {
+ public void bindViewHolder_appWith3Widgets_shouldHave3Widgets() throws Exception {
WidgetsRowViewHolder viewHolder = mViewHolderBinder.newViewHolder(
- new FrameLayout(mTestActivity));
+ new FrameLayout(mContext));
WidgetsListContentEntry entry = generateSampleAppWithWidgets(
APP_NAME,
TEST_PACKAGE,
/* numOfWidgets= */ 3);
- mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
- shadowOf(getMainLooper()).idle();
+ mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0, EMPTY_LIST);
+ Executors.MAIN_EXECUTOR.submit(() -> { }).get();
// THEN the table container has one row, which contains 3 widgets.
// View: .SampleWidget0 | .SampleWidget1 | .SampleWidget2
- assertThat(viewHolder.mTableContainer.getChildCount()).isEqualTo(1);
- TableRow row = (TableRow) viewHolder.mTableContainer.getChildAt(0);
+ assertThat(viewHolder.tableContainer.getChildCount()).isEqualTo(1);
+ TableRow row = (TableRow) viewHolder.tableContainer.getChildAt(0);
assertThat(row.getChildCount()).isEqualTo(3);
// Widget 0 label is .SampleWidget0.
assertWidgetCellWithLabel(row.getChildAt(0), ".SampleWidget0");
@@ -152,18 +131,15 @@
return new WidgetsListContentEntry(appInfo,
/* titleSectionName= */ "",
- generateWidgetItems(packageName, numOfWidgets));
+ generateWidgetItems(packageName, numOfWidgets),
+ Integer.MAX_VALUE);
}
private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
- ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
ArrayList<WidgetItem> widgetItems = new ArrayList<>();
for (int i = 0; i < numOfWidgets; i++) {
ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
- AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
- widgetInfo.provider = cn;
- ReflectionHelpers.setField(widgetInfo, "providerInfo",
- packageManager.addReceiverIfNotPresent(cn));
+ AppWidgetProviderInfo widgetInfo = WidgetUtils.createAppWidgetProviderInfo(cn);
widgetItems.add(new WidgetItem(
LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java b/tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
similarity index 92%
rename from robolectric_tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
rename to tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
index 106cac0..4b61b2c 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
@@ -15,15 +15,20 @@
*/
package com.android.launcher3.widget.picker.model;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
-import static org.robolectric.Shadows.shadowOf;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
-import android.content.Context;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.icons.ComponentWithLabel;
@@ -38,16 +43,13 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public final class WidgetsListContentEntryTest {
private static final String PACKAGE_NAME = "com.android.test";
private static final String PACKAGE_NAME_2 = "com.android.test2";
@@ -60,7 +62,6 @@
@Mock private IconCache mIconCache;
- private Context mContext;
private InvariantDeviceProfile mTestProfile;
@Before
@@ -71,7 +72,6 @@
mWidgetsToLabels.put(mWidget2, "Dog");
mWidgetsToLabels.put(mWidget3, "Bird");
- mContext = RuntimeEnvironment.application;
mTestProfile = new InvariantDeviceProfile();
mTestProfile.numRows = 5;
mTestProfile.numColumns = 5;
@@ -242,17 +242,12 @@
assertThat(widgetsListRowEntry1.equals(widgetsListRowEntry2)).isTrue();
}
-
private WidgetItem createWidgetItem(ComponentName componentName, int spanX, int spanY) {
String label = mWidgetsToLabels.get(componentName);
- ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
- AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
- widgetInfo.provider = componentName;
- ReflectionHelpers.setField(widgetInfo, "providerInfo",
- packageManager.addReceiverIfNotPresent(componentName));
+ AppWidgetProviderInfo widgetInfo = createAppWidgetProviderInfo(componentName);
LauncherAppWidgetProviderInfo launcherAppWidgetProviderInfo =
- LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo);
+ LauncherAppWidgetProviderInfo.fromProviderInfo(getApplicationContext(), widgetInfo);
launcherAppWidgetProviderInfo.spanX = spanX;
launcherAppWidgetProviderInfo.spanY = spanY;
launcherAppWidgetProviderInfo.label = label;
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java b/tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
similarity index 90%
rename from robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
rename to tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
index 36b6f01..c862d6b 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
@@ -16,7 +16,10 @@
package com.android.launcher3.widget.picker.search;
-import static android.os.Looper.getMainLooper;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
@@ -25,7 +28,6 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
-import static org.robolectric.Shadows.shadowOf;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
@@ -33,6 +35,9 @@
import android.graphics.Bitmap;
import android.os.UserHandle;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.ComponentWithLabel;
@@ -52,16 +57,13 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public class SimpleWidgetsSearchAlgorithmTest {
@Mock private IconCache mIconCache;
@@ -82,7 +84,7 @@
private SearchCallback<WidgetsListBaseEntry> mSearchCallback;
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
doAnswer(invocation -> {
ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
@@ -91,7 +93,7 @@
mTestProfile = new InvariantDeviceProfile();
mTestProfile.numRows = 5;
mTestProfile.numColumns = 5;
- mContext = RuntimeEnvironment.application;
+ mContext = getApplicationContext();
mCalendarHeaderEntry =
createWidgetsHeaderEntry("com.example.android.Calendar", "Calendar", 2);
@@ -102,8 +104,8 @@
mClockHeaderEntry = createWidgetsHeaderEntry("com.example.android.Clock", "Clock", 3);
mClockContentEntry = createWidgetsContentEntry("com.example.android.Clock", "Clock", 3);
-
- mSimpleWidgetsSearchAlgorithm = new SimpleWidgetsSearchAlgorithm(mDataProvider);
+ mSimpleWidgetsSearchAlgorithm = MAIN_EXECUTOR.submit(
+ () -> new SimpleWidgetsSearchAlgorithm(mDataProvider)).get();
doReturn(Collections.EMPTY_LIST).when(mDataProvider).getAllWidgets();
}
@@ -156,13 +158,13 @@
}
@Test
- public void doSearch_shouldInformCallback() {
+ public void doSearch_shouldInformCallback() throws Exception {
doReturn(List.of(mCalendarHeaderEntry, mCalendarContentEntry, mCameraHeaderEntry,
mCameraContentEntry, mClockHeaderEntry, mClockContentEntry))
.when(mDataProvider)
.getAllWidgets();
mSimpleWidgetsSearchAlgorithm.doSearch("Ca", mSearchCallback);
- shadowOf(getMainLooper()).idle();
+ MAIN_EXECUTOR.submit(() -> { }).get();
verify(mSearchCallback).onSearchResult(
matches("Ca"), argThat(a -> a != null && !a.isEmpty()));
}
@@ -195,14 +197,10 @@
}
private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
- ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
ArrayList<WidgetItem> widgetItems = new ArrayList<>();
for (int i = 0; i < numOfWidgets; i++) {
ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
- AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
- widgetInfo.provider = cn;
- ReflectionHelpers.setField(widgetInfo, "providerInfo",
- packageManager.addReceiverIfNotPresent(cn));
+ AppWidgetProviderInfo widgetInfo = createAppWidgetProviderInfo(cn);
WidgetItem widgetItem = new WidgetItem(
LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java b/tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
similarity index 83%
rename from robolectric_tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
rename to tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
index 7ac879a..583d37f 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
@@ -16,39 +16,38 @@
package com.android.launcher3.widget.picker.search;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import android.content.Context;
import android.view.View;
import android.widget.ImageButton;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.search.SearchAlgorithm;
-import com.android.launcher3.testing.TestActivity;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.android.controller.ActivityController;
import java.util.ArrayList;
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public class WidgetsSearchBarControllerTest {
private WidgetsSearchBarController mController;
- // TODO: Replace ActivityController with ActivityScenario, which is the recommended way for
- // activity testing.
- private ActivityController<TestActivity> mActivityController;
private ExtendedEditText mEditText;
private ImageButton mCancelButton;
@Mock
@@ -59,20 +58,14 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mActivityController = Robolectric.buildActivity(TestActivity.class);
- TestActivity testActivity = mActivityController.setup().get();
+ Context context = getApplicationContext();
- mEditText = new ExtendedEditText(testActivity);
- mCancelButton = new ImageButton(testActivity);
+ mEditText = new ExtendedEditText(context);
+ mCancelButton = new ImageButton(context);
mController = new WidgetsSearchBarController(
mSearchAlgorithm, mEditText, mCancelButton, mSearchModeListener);
}
- @After
- public void tearDown() {
- mActivityController.destroy();
- }
-
@Test
public void onSearchResult_shouldInformSearchModeListener() {
ArrayList<WidgetsListBaseEntry> entries = new ArrayList<>();
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java b/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
similarity index 89%
rename from robolectric_tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
rename to tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
index 56d7d68..d6da776 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
@@ -15,11 +15,14 @@
*/
package com.android.launcher3.widget.picker.util;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
-import static org.robolectric.Shadows.shadowOf;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
@@ -29,6 +32,9 @@
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.IconCache;
@@ -42,15 +48,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
-@RunWith(RobolectricTestRunner.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public final class WidgetsTableUtilsTest {
private static final String TEST_PACKAGE = "com.google.test";
@@ -73,7 +76,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
+ mContext = getApplicationContext();
mTestProfile = new InvariantDeviceProfile();
mTestProfile.numRows = 5;
@@ -152,16 +155,14 @@
ArrayList<WidgetItem> widgetItems = new ArrayList<>();
widgetSizes.stream().forEach(
widgetSize -> {
- ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
- AppWidgetProviderInfo info = new AppWidgetProviderInfo();
- info.provider = ComponentName.createRelative(TEST_PACKAGE,
- ".WidgetProvider_" + widgetSize.x + "x" + widgetSize.y);
+ AppWidgetProviderInfo info = createAppWidgetProviderInfo(
+ ComponentName.createRelative(
+ TEST_PACKAGE,
+ ".WidgetProvider_" + widgetSize.x + "x" + widgetSize.y));
LauncherAppWidgetProviderInfo widgetInfo =
LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
widgetInfo.spanX = widgetSize.x;
widgetInfo.spanY = widgetSize.y;
- ReflectionHelpers.setField(widgetInfo, "providerInfo",
- packageManager.addReceiverIfNotPresent(widgetInfo.provider));
widgetItems.add(new WidgetItem(widgetInfo, mTestProfile, mIconCache));
}
);
diff --git a/tests/src_common/README.md b/tests/src_common/README.md
deleted file mode 100644
index 2bc9e73..0000000
--- a/tests/src_common/README.md
+++ /dev/null
@@ -1 +0,0 @@
-Common source code used by both android tests and robolectric tests
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 2ca40d8..23aaa25 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -251,8 +251,19 @@
} else {
try {
final int userId = ContextUtils.getUserId(getContext());
+ final String launcherPidCommand = "pidof " + pi.packageName;
+ final String initialPid = mDevice.executeShellCommand(launcherPidCommand)
+ .replaceAll("\\s", "");
mDevice.executeShellCommand(
"pm enable --user " + userId + " " + cn.flattenToString());
+ // Wait for Launcher restart after enabling test provider.
+ for (int i = 0; i < 100; ++i) {
+ final String currentPid = mDevice.executeShellCommand(launcherPidCommand)
+ .replaceAll("\\s", "");
+ if (!currentPid.isEmpty() && !currentPid.equals(initialPid)) break;
+ if (i == 99) fail("Launcher didn't restart after enabling test provider");
+ SystemClock.sleep(100);
+ }
} catch (IOException e) {
fail(e.toString());
}
@@ -305,7 +316,7 @@
public boolean isTwoPanels() {
return getTestInfo(TestProtocol.REQUEST_IS_TWO_PANELS)
- .getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ .getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
private void setForcePauseTimeout(long timeout) {