Merge "Import translations. DO NOT MERGE ANYWHERE" into main
diff --git a/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml
index c6e2d8c..80d8154 100644
--- a/quickstep/AndroidManifest-launcher.xml
+++ b/quickstep/AndroidManifest-launcher.xml
@@ -48,7 +48,7 @@
android:stateNotNeeded="true"
android:windowSoftInputMode="adjustPan"
android:screenOrientation="unspecified"
- android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
+ android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
android:resizeableActivity="true"
android:resumeWhilePausing="true"
android:taskAffinity=""
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index bf198b6..4abf6e1 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -80,7 +80,7 @@
android:stateNotNeeded="true"
android:theme="@style/LauncherTheme"
android:screenOrientation="behind"
- android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
+ android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
android:resizeableActivity="true"
android:resumeWhilePausing="true"
android:enableOnBackInvokedCallback="false"
diff --git a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
index 23cb8e9..943c08c 100644
--- a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
+++ b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
@@ -194,6 +194,11 @@
return false;
}
+ View dragView = widgetCell.getDragAndDropView();
+ if (dragView == null) {
+ return false;
+ }
+
ClipData clipData = new ClipData(
new ClipDescription(
/* label= */ "", // not displayed anywhere; so, set to empty.
@@ -209,9 +214,9 @@
.putExtra(EXTRA_IS_PENDING_WIDGET_DRAG, true));
// DRAG_FLAG_GLOBAL permits dragging data beyond app window.
- return view.startDragAndDrop(
+ return dragView.startDragAndDrop(
clipData,
- new View.DragShadowBuilder(view),
+ new View.DragShadowBuilder(dragView),
/* myLocalState= */ null,
View.DRAG_FLAG_GLOBAL
);
diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
index 39f2c00..64bb05e 100644
--- a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
@@ -19,6 +19,7 @@
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
import android.app.prediction.AppTarget;
+import android.content.ComponentName;
import android.content.Context;
import android.text.TextUtils;
@@ -29,7 +30,6 @@
import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.picker.WidgetRecommendationCategoryProvider;
@@ -64,36 +64,24 @@
widget -> new ComponentKey(widget.providerName, widget.user)).collect(
Collectors.toSet());
Predicate<WidgetItem> notOnWorkspace = w -> !widgetsInWorkspace.contains(w);
- Map<PackageUserKey, List<WidgetItem>> allWidgets =
- dataModel.widgetsModel.getAllWidgetsWithoutShortcuts();
+ Map<ComponentKey, WidgetItem> allWidgets =
+ dataModel.widgetsModel.getAllWidgetComponentsWithoutShortcuts();
List<WidgetItem> servicePredictedItems = new ArrayList<>();
- List<WidgetItem> localFilteredWidgets = new ArrayList<>();
for (AppTarget app : mTargets) {
- PackageUserKey packageUserKey = new PackageUserKey(app.getPackageName(), app.getUser());
- List<WidgetItem> widgets = allWidgets.get(packageUserKey);
- if (widgets == null || widgets.isEmpty()) {
+ ComponentKey componentKey = new ComponentKey(
+ new ComponentName(app.getPackageName(), app.getClassName()), app.getUser());
+ WidgetItem widget = allWidgets.get(componentKey);
+ if (widget == null) {
continue;
}
String className = app.getClassName();
if (!TextUtils.isEmpty(className)) {
- WidgetItem item = widgets.stream()
- .filter(w -> className.equals(w.componentName.getClassName()))
- .filter(notOnWorkspace)
- .findFirst()
- .orElse(null);
- if (item != null) {
- servicePredictedItems.add(item);
- continue;
+ if (notOnWorkspace.test(widget)) {
+ servicePredictedItems.add(widget);
}
}
- // No widget was added by the service, try local filtering
- widgets.stream().filter(notOnWorkspace).findFirst()
- .ifPresent(localFilteredWidgets::add);
- }
- if (servicePredictedItems.isEmpty()) {
- servicePredictedItems.addAll(localFilteredWidgets);
}
List<ItemInfo> items;
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index 7eeea84..85ea5fd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -719,6 +719,8 @@
} else if (i == MAX_VISIBLE_BUBBLES_COLLAPSED - 1
&& bubbleCount == MAX_VISIBLE_BUBBLES_COLLAPSED) {
bv.setAlpha(0);
+ } else {
+ bv.setAlpha(1);
}
}
}
diff --git a/quickstep/src/com/android/quickstep/BaseContainerInterface.java b/quickstep/src/com/android/quickstep/BaseContainerInterface.java
index 9955183..b1d511c 100644
--- a/quickstep/src/com/android/quickstep/BaseContainerInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseContainerInterface.java
@@ -41,7 +41,6 @@
import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.taskbar.TaskbarUIController;
-import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
@@ -207,10 +206,10 @@
}
public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect,
- PagedOrientationHandler orientedState) {
+ RecentsPagedOrientationHandler orientationHandler) {
if (dp.isTablet) {
if (Flags.enableGridOnlyOverview()) {
- calculateGridTaskSize(context, dp, outRect, orientedState);
+ calculateGridTaskSize(context, dp, outRect, orientationHandler);
} else {
calculateFocusTaskSize(context, dp, outRect);
}
@@ -218,15 +217,19 @@
Resources res = context.getResources();
float maxScale = res.getFloat(R.dimen.overview_max_scale);
int taskMargin = dp.overviewTaskMarginPx;
+ // In fake orientation, OverviewActions is hidden and we only leave a margin there.
+ int overviewActionsClaimedSpace = orientationHandler.isLayoutNaturalToLauncher()
+ ? dp.getOverviewActionsClaimedSpace() : dp.overviewActionsTopMarginPx;
calculateTaskSizeInternal(
context,
dp,
dp.overviewTaskThumbnailTopMarginPx,
- dp.getOverviewActionsClaimedSpace(),
+ overviewActionsClaimedSpace,
res.getDimensionPixelSize(R.dimen.overview_minimum_next_prev_size) + taskMargin,
maxScale,
Gravity.CENTER,
- outRect);
+ outRect,
+ orientationHandler);
}
}
@@ -234,7 +237,7 @@
* Calculates the taskView size for carousel during app to overview animation on tablets.
*/
public final void calculateCarouselTaskSize(Context context, DeviceProfile dp, Rect outRect,
- PagedOrientationHandler orientedState) {
+ RecentsPagedOrientationHandler orientationHandler) {
if (dp.isTablet && dp.isGestureMode) {
Resources res = context.getResources();
float minScale = res.getFloat(R.dimen.overview_carousel_min_scale);
@@ -243,7 +246,7 @@
calculateTaskSizeInternal(context, dp, gridRect, minScale, Gravity.CENTER | Gravity.TOP,
outRect);
} else {
- calculateTaskSize(context, dp, outRect, orientedState);
+ calculateTaskSize(context, dp, outRect, orientationHandler);
}
}
@@ -257,16 +260,39 @@
private void calculateTaskSizeInternal(Context context, DeviceProfile dp, int claimedSpaceAbove,
int claimedSpaceBelow, int minimumHorizontalPadding, float maxScale, int gravity,
- Rect outRect) {
- Rect insets = dp.getInsets();
-
+ Rect outRect, RecentsPagedOrientationHandler orientationHandler) {
Rect potentialTaskRect = new Rect(0, 0, dp.widthPx, dp.heightPx);
- potentialTaskRect.inset(insets.left, insets.top, insets.right, insets.bottom);
- potentialTaskRect.inset(
+
+ Rect insets;
+ if (orientationHandler.isLayoutNaturalToLauncher()) {
+ insets = dp.getInsets();
+ } else {
+ Rect portraitInsets = dp.getInsets();
+ DisplayController displayController = DisplayController.INSTANCE.get(context);
+ Rect deviceRotationInsets = displayController.getInfo().getCurrentBounds().get(
+ orientationHandler.getRotation()).insets;
+ // Obtain the landscape/seascape insets, and rotate it to portrait perspective.
+ orientationHandler.rotateInsets(deviceRotationInsets, outRect);
+ // Then combine with portrait's insets to leave space for status bar/nav bar in
+ // either orientations.
+ outRect.set(
+ Math.max(outRect.left, portraitInsets.left),
+ Math.max(outRect.top, portraitInsets.top),
+ Math.max(outRect.right, portraitInsets.right),
+ Math.max(outRect.bottom, portraitInsets.bottom)
+ );
+ insets = outRect;
+ }
+ potentialTaskRect.inset(insets);
+
+ outRect.set(
minimumHorizontalPadding,
claimedSpaceAbove,
minimumHorizontalPadding,
claimedSpaceBelow);
+ // Rotate the paddings to portrait perspective,
+ orientationHandler.rotateInsets(outRect, outRect);
+ potentialTaskRect.inset(outRect);
calculateTaskSizeInternal(context, dp, potentialTaskRect, maxScale, gravity, outRect);
}
@@ -326,7 +352,7 @@
* Calculates the overview grid non-focused task size for the provided device configuration.
*/
public final void calculateGridTaskSize(Context context, DeviceProfile dp, Rect outRect,
- PagedOrientationHandler orientedState) {
+ RecentsPagedOrientationHandler orientationHandler) {
Resources res = context.getResources();
Rect potentialTaskRect = new Rect();
if (Flags.enableGridOnlyOverview()) {
@@ -344,7 +370,7 @@
int outHeight = Math.round(scale * taskDimension.y);
int gravity = Gravity.TOP;
- gravity |= orientedState.getRecentsRtlSetting(res) ? Gravity.RIGHT : Gravity.LEFT;
+ gravity |= orientationHandler.getRecentsRtlSetting(res) ? Gravity.RIGHT : Gravity.LEFT;
Gravity.apply(gravity, outWidth, outHeight, potentialTaskRect, outRect);
}
@@ -352,8 +378,8 @@
* Calculates the modal taskView size for the provided device configuration
*/
public final void calculateModalTaskSize(Context context, DeviceProfile dp, Rect outRect,
- PagedOrientationHandler orientedState) {
- calculateTaskSize(context, dp, outRect, orientedState);
+ RecentsPagedOrientationHandler orientationHandler) {
+ calculateTaskSize(context, dp, outRect, orientationHandler);
boolean isGridOnlyOverview = dp.isTablet && Flags.enableGridOnlyOverview();
int claimedSpaceBelow = isGridOnlyOverview
? dp.overviewActionsTopMarginPx + dp.overviewActionsHeight + dp.stashedTaskbarHeight
@@ -372,6 +398,7 @@
minimumHorizontalPadding,
1f /*maxScale*/,
Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM,
- outRect);
+ outRect,
+ orientationHandler);
}
}
diff --git a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
index 8f8cc6e..ec04cb7 100644
--- a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
@@ -121,6 +121,10 @@
override fun getEnd(rect: RectF): Float = rect.bottom
+ override fun rotateInsets(insets: Rect, outInsets: Rect) {
+ outInsets.set(insets.bottom, insets.left, insets.top, insets.right)
+ }
+
override fun getClearAllSidePadding(view: View, isRtl: Boolean): Int =
if (isRtl) view.paddingBottom / 2 else -view.paddingTop / 2
diff --git a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
index f6284d5..758a737 100644
--- a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
+++ b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
@@ -130,6 +130,11 @@
}
@Override
+ public void rotateInsets(@NonNull Rect insets, @NonNull Rect outInsets) {
+ outInsets.set(insets);
+ }
+
+ @Override
public int getClearAllSidePadding(View view, boolean isRtl) {
return (isRtl ? view.getPaddingRight() : - view.getPaddingLeft()) / 2;
}
diff --git a/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt b/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
index 5bc1be8..df4b030 100644
--- a/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
@@ -70,6 +70,9 @@
fun getEnd(rect: RectF): Float
+ /** Rotate the provided insets to portrait perspective. */
+ fun rotateInsets(insets: Rect, outInsets: Rect)
+
fun getClearAllSidePadding(view: View, isRtl: Boolean): Int
fun getSecondaryDimension(view: View): Int
diff --git a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
index 46e4b0c..333359f 100644
--- a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
@@ -44,6 +44,10 @@
import com.android.quickstep.views.IconAppChipView
class SeascapePagedViewHandler : LandscapePagedViewHandler() {
+ override fun rotateInsets(insets: Rect, outInsets: Rect) {
+ outInsets.set(insets.top, insets.right, insets.bottom, insets.left)
+ }
+
override val secondaryTranslationDirectionFactor: Int = -1
override fun getSplitTranslationDirectionFactor(
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
index 0e451b5..acbb2ec 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -187,7 +187,13 @@
viewBounds, false /* ignoreTransform */, null /* recycle */,
mStartingPosition);
}
-
+ // In some cases originalView is off-screen so we don't get a valid starting position
+ // ex. on rotation
+ // TODO(b/345556328) We shouldn't be animating if starting position of view isn't ready
+ if (mStartingPosition.isEmpty()) {
+ // Set to non empty for now so calculations in #update() don't break
+ mStartingPosition.set(0, 0, 1, 1);
+ }
final BaseDragLayer.LayoutParams lp = new BaseDragLayer.LayoutParams(
Math.round(mStartingPosition.width()),
Math.round(mStartingPosition.height()));
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index 4fd686d..5e79743 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -825,7 +825,10 @@
taskContainers.forEach {
val thumbnailBounds = Rect()
if (relativeToDragLayer) {
- container.dragLayer.getDescendantRectRelativeToSelf(it.snapshotView, bounds)
+ container.dragLayer.getDescendantRectRelativeToSelf(
+ it.snapshotView,
+ thumbnailBounds
+ )
} else {
thumbnailBounds.set(it.snapshotView)
}
diff --git a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
index 8702f70..7b57c81 100644
--- a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
+++ b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
@@ -184,7 +184,7 @@
}
@Test
- public void widgetsRecommendationRan_shouldReturnPackageWidgetsWhenEmpty() {
+ public void widgetsRecommendationRan_shouldReturnEmptyWidgetsWhenEmpty() {
runOnExecutorSync(MODEL_EXECUTOR, () -> {
// Not installed widget
@@ -204,19 +204,12 @@
newWidgetsPredicationTask(List.of(widget5, widget3, widget4, widget1)));
runOnExecutorSync(MAIN_EXECUTOR, () -> { });
- // THEN only 2 widgets are returned because the launcher only filters out
- // non-exist widgets.
+ // Only widgets suggested by prediction system are returned.
List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
.stream()
.map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
.collect(Collectors.toList());
- assertThat(recommendedWidgets).hasSize(2);
- recommendedWidgets.forEach(pendingAddWidgetInfo ->
- assertThat(pendingAddWidgetInfo.recommendationCategory).isNotNull()
- );
- // Another widget from the same package
- assertWidgetInfo(recommendedWidgets.get(0).info, mApp4Provider2);
- assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1);
+ assertThat(recommendedWidgets).hasSize(0);
});
}
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index 5e0edb3..4cba0b5 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -32,6 +32,7 @@
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
+import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
@@ -130,6 +131,22 @@
}
/**
+ * Returns a map of widget component keys to corresponding widget items. Excludes the
+ * shortcuts.
+ */
+ public synchronized Map<ComponentKey, WidgetItem> getAllWidgetComponentsWithoutShortcuts() {
+ if (!WIDGETS_ENABLED) {
+ return Collections.emptyMap();
+ }
+ Map<ComponentKey, WidgetItem> widgetsMap = new HashMap<>();
+ mWidgetsList.forEach((packageItemInfo, widgetsAndShortcuts) ->
+ widgetsAndShortcuts.stream().filter(item -> item.widgetInfo != null).forEach(
+ item -> widgetsMap.put(new ComponentKey(item.componentName, item.user),
+ item)));
+ return widgetsMap;
+ }
+
+ /**
* @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
* only widgets and shortcuts associated with the package/user are.
*/
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 21eee55..96b3457 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -405,7 +405,7 @@
navigationMode = wmProxy.getNavigationMode(displayInfoContext);
mPerDisplayBounds.putAll(perDisplayBoundsCache);
- List<WindowBounds> cachedValue = mPerDisplayBounds.get(normalizedDisplayInfo);
+ List<WindowBounds> cachedValue = getCurrentBounds();
realBounds = wmProxy.getRealBounds(displayInfoContext, displayInfo);
if (cachedValue == null) {
@@ -415,7 +415,7 @@
FileLog.e(TAG, "(Invalid Cache) perDisplayBounds : " + mPerDisplayBounds);
mPerDisplayBounds.clear();
mPerDisplayBounds.putAll(wmProxy.estimateInternalDisplayBounds(displayInfoContext));
- cachedValue = mPerDisplayBounds.get(normalizedDisplayInfo);
+ cachedValue = getCurrentBounds();
if (cachedValue == null) {
FileLog.e(TAG, "normalizedDisplayInfo not found in estimation: "
+ normalizedDisplayInfo);
@@ -505,6 +505,13 @@
return Collections.unmodifiableSet(mPerDisplayBounds.keySet());
}
+ /**
+ * Returns all {@link WindowBounds}s for the current display.
+ */
+ public List<WindowBounds> getCurrentBounds() {
+ return mPerDisplayBounds.get(normalizedDisplayInfo);
+ }
+
public int getDensityDpi() {
return densityDpi;
}
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 2bb485a..35372d3 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -372,6 +372,13 @@
}
}
+ /**
+ * Returns a view (holding the previews) that can be dragged and dropped.
+ */
+ public View getDragAndDropView() {
+ return mWidgetImageContainer;
+ }
+
public WidgetImageView getWidgetView() {
return mWidgetImage;
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/icons/UserBadgeDrawableTest.kt b/tests/multivalentTests/src/com/android/launcher3/icons/UserBadgeDrawableTest.kt
new file mode 100644
index 0000000..d611ae8
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/icons/UserBadgeDrawableTest.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.icons
+
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.icons.UserBadgeDrawable.SHADOW_COLOR
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+/** Test for [UserBadgeDrawable] */
+@RunWith(AndroidJUnit4::class)
+class UserBadgeDrawableTest {
+ private val context = InstrumentationRegistry.getInstrumentation().targetContext
+ private val canvas = mock<Canvas>()
+ private val systemUnderTest =
+ UserBadgeDrawable(context, R.drawable.ic_work_app_badge, R.color.badge_tint_work, false)
+
+ @Test
+ fun draw_opaque() {
+ val colorList = mutableListOf<Int>()
+ whenever(
+ canvas.drawCircle(
+ any(),
+ any(),
+ any(),
+ any()
+ )
+ ).then { colorList.add(it.getArgument<Paint>(3).color) }
+
+ systemUnderTest.alpha = 255
+ systemUnderTest.draw(canvas)
+
+ assertThat(colorList).containsExactly(SHADOW_COLOR, Color.WHITE)
+ }
+
+ @Test
+ fun draw_transparent() {
+ val colorList = mutableListOf<Int>()
+ whenever(
+ canvas.drawCircle(
+ any(),
+ any(),
+ any(),
+ any()
+ )
+ ).then { colorList.add(it.getArgument<Paint>(3).color) }
+
+ systemUnderTest.alpha = 0
+ systemUnderTest.draw(canvas)
+
+ assertThat(colorList).hasSize(2)
+ assertThat(Color.valueOf(colorList[0]).alpha()).isEqualTo(0)
+ assertThat(Color.valueOf(colorList[1]).alpha()).isEqualTo(0)
+ }
+}