Alina Zaidi | c6fe0dd | 2021-03-03 18:40:01 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2021 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package com.android.launcher3.widget.picker.search; |
| 18 | |
| 19 | import static android.os.Looper.getMainLooper; |
| 20 | |
Sunny Goyal | ce6cc7e | 2021-03-30 16:51:08 -0700 | [diff] [blame] | 21 | import static org.junit.Assert.assertEquals; |
Alina Zaidi | c6fe0dd | 2021-03-03 18:40:01 +0000 | [diff] [blame] | 22 | import static org.mockito.ArgumentMatchers.any; |
Sunny Goyal | ce6cc7e | 2021-03-30 16:51:08 -0700 | [diff] [blame] | 23 | import static org.mockito.ArgumentMatchers.argThat; |
Alina Zaidi | c6fe0dd | 2021-03-03 18:40:01 +0000 | [diff] [blame] | 24 | import static org.mockito.ArgumentMatchers.matches; |
Sunny Goyal | ce6cc7e | 2021-03-30 16:51:08 -0700 | [diff] [blame] | 25 | import static org.mockito.Mockito.doAnswer; |
| 26 | import static org.mockito.Mockito.doReturn; |
Alina Zaidi | c6fe0dd | 2021-03-03 18:40:01 +0000 | [diff] [blame] | 27 | import static org.mockito.Mockito.verify; |
| 28 | import static org.robolectric.Shadows.shadowOf; |
| 29 | |
Sunny Goyal | ce6cc7e | 2021-03-30 16:51:08 -0700 | [diff] [blame] | 30 | import android.appwidget.AppWidgetProviderInfo; |
| 31 | import android.content.ComponentName; |
| 32 | import android.content.Context; |
| 33 | import android.graphics.Bitmap; |
| 34 | import android.os.UserHandle; |
| 35 | |
| 36 | import com.android.launcher3.InvariantDeviceProfile; |
| 37 | import com.android.launcher3.icons.BitmapInfo; |
| 38 | import com.android.launcher3.icons.ComponentWithLabel; |
| 39 | import com.android.launcher3.icons.IconCache; |
| 40 | import com.android.launcher3.model.WidgetItem; |
| 41 | import com.android.launcher3.model.data.PackageItemInfo; |
| 42 | import com.android.launcher3.popup.PopupDataProvider; |
Alina Zaidi | c6fe0dd | 2021-03-03 18:40:01 +0000 | [diff] [blame] | 43 | import com.android.launcher3.search.SearchCallback; |
Sunny Goyal | ce6cc7e | 2021-03-30 16:51:08 -0700 | [diff] [blame] | 44 | import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; |
Alina Zaidi | c6fe0dd | 2021-03-03 18:40:01 +0000 | [diff] [blame] | 45 | import com.android.launcher3.widget.model.WidgetsListBaseEntry; |
Sunny Goyal | ce6cc7e | 2021-03-30 16:51:08 -0700 | [diff] [blame] | 46 | import com.android.launcher3.widget.model.WidgetsListContentEntry; |
| 47 | import com.android.launcher3.widget.model.WidgetsListHeaderEntry; |
| 48 | import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry; |
Alina Zaidi | c6fe0dd | 2021-03-03 18:40:01 +0000 | [diff] [blame] | 49 | |
| 50 | import org.junit.Before; |
| 51 | import org.junit.Test; |
| 52 | import org.junit.runner.RunWith; |
Alina Zaidi | c6fe0dd | 2021-03-03 18:40:01 +0000 | [diff] [blame] | 53 | import org.mockito.Mock; |
| 54 | import org.mockito.MockitoAnnotations; |
| 55 | import org.robolectric.RobolectricTestRunner; |
Sunny Goyal | ce6cc7e | 2021-03-30 16:51:08 -0700 | [diff] [blame] | 56 | import org.robolectric.RuntimeEnvironment; |
| 57 | import org.robolectric.shadows.ShadowPackageManager; |
| 58 | import org.robolectric.util.ReflectionHelpers; |
Alina Zaidi | c6fe0dd | 2021-03-03 18:40:01 +0000 | [diff] [blame] | 59 | |
| 60 | import java.util.ArrayList; |
Sunny Goyal | ce6cc7e | 2021-03-30 16:51:08 -0700 | [diff] [blame] | 61 | import java.util.Collections; |
Alina Zaidi | c6fe0dd | 2021-03-03 18:40:01 +0000 | [diff] [blame] | 62 | import java.util.List; |
Alina Zaidi | c6fe0dd | 2021-03-03 18:40:01 +0000 | [diff] [blame] | 63 | |
| 64 | @RunWith(RobolectricTestRunner.class) |
| 65 | public class SimpleWidgetsSearchAlgorithmTest { |
| 66 | |
Sunny Goyal | ce6cc7e | 2021-03-30 16:51:08 -0700 | [diff] [blame] | 67 | @Mock private IconCache mIconCache; |
| 68 | |
| 69 | private InvariantDeviceProfile mTestProfile; |
| 70 | private WidgetsListHeaderEntry mCalendarHeaderEntry; |
| 71 | private WidgetsListContentEntry mCalendarContentEntry; |
| 72 | private WidgetsListHeaderEntry mCameraHeaderEntry; |
| 73 | private WidgetsListContentEntry mCameraContentEntry; |
| 74 | private WidgetsListHeaderEntry mClockHeaderEntry; |
| 75 | private WidgetsListContentEntry mClockContentEntry; |
| 76 | private Context mContext; |
| 77 | |
Alina Zaidi | c6fe0dd | 2021-03-03 18:40:01 +0000 | [diff] [blame] | 78 | private SimpleWidgetsSearchAlgorithm mSimpleWidgetsSearchAlgorithm; |
| 79 | @Mock |
Sunny Goyal | ce6cc7e | 2021-03-30 16:51:08 -0700 | [diff] [blame] | 80 | private PopupDataProvider mDataProvider; |
Alina Zaidi | c6fe0dd | 2021-03-03 18:40:01 +0000 | [diff] [blame] | 81 | @Mock |
| 82 | private SearchCallback<WidgetsListBaseEntry> mSearchCallback; |
Alina Zaidi | c6fe0dd | 2021-03-03 18:40:01 +0000 | [diff] [blame] | 83 | |
| 84 | @Before |
| 85 | public void setUp() { |
| 86 | MockitoAnnotations.initMocks(this); |
Sunny Goyal | ce6cc7e | 2021-03-30 16:51:08 -0700 | [diff] [blame] | 87 | doAnswer(invocation -> { |
| 88 | ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0); |
| 89 | return componentWithLabel.getComponent().getShortClassName(); |
| 90 | }).when(mIconCache).getTitleNoCache(any()); |
| 91 | mTestProfile = new InvariantDeviceProfile(); |
| 92 | mTestProfile.numRows = 5; |
| 93 | mTestProfile.numColumns = 5; |
| 94 | mContext = RuntimeEnvironment.application; |
| 95 | |
| 96 | mCalendarHeaderEntry = |
| 97 | createWidgetsHeaderEntry("com.example.android.Calendar", "Calendar", 2); |
| 98 | mCalendarContentEntry = |
| 99 | createWidgetsContentEntry("com.example.android.Calendar", "Calendar", 2); |
| 100 | mCameraHeaderEntry = createWidgetsHeaderEntry("com.example.android.Camera", "Camera", 11); |
| 101 | mCameraContentEntry = createWidgetsContentEntry("com.example.android.Camera", "Camera", 11); |
| 102 | mClockHeaderEntry = createWidgetsHeaderEntry("com.example.android.Clock", "Clock", 3); |
| 103 | mClockContentEntry = createWidgetsContentEntry("com.example.android.Clock", "Clock", 3); |
| 104 | |
| 105 | |
| 106 | mSimpleWidgetsSearchAlgorithm = new SimpleWidgetsSearchAlgorithm(mDataProvider); |
| 107 | doReturn(Collections.EMPTY_LIST).when(mDataProvider).getAllWidgets(); |
Alina Zaidi | c6fe0dd | 2021-03-03 18:40:01 +0000 | [diff] [blame] | 108 | } |
| 109 | |
| 110 | @Test |
Sunny Goyal | ce6cc7e | 2021-03-30 16:51:08 -0700 | [diff] [blame] | 111 | public void filter_shouldMatchOnAppName() { |
| 112 | doReturn(List.of(mCalendarHeaderEntry, mCalendarContentEntry, mCameraHeaderEntry, |
| 113 | mCameraContentEntry, mClockHeaderEntry, mClockContentEntry)) |
| 114 | .when(mDataProvider) |
| 115 | .getAllWidgets(); |
Alina Zaidi | c6fe0dd | 2021-03-03 18:40:01 +0000 | [diff] [blame] | 116 | |
Sunny Goyal | ce6cc7e | 2021-03-30 16:51:08 -0700 | [diff] [blame] | 117 | assertEquals(List.of( |
| 118 | new WidgetsListSearchHeaderEntry( |
| 119 | mCalendarHeaderEntry.mPkgItem, |
| 120 | mCalendarHeaderEntry.mTitleSectionName, |
| 121 | mCalendarHeaderEntry.mWidgets), |
| 122 | mCalendarContentEntry, |
| 123 | new WidgetsListSearchHeaderEntry( |
| 124 | mCameraHeaderEntry.mPkgItem, |
| 125 | mCameraHeaderEntry.mTitleSectionName, |
| 126 | mCameraHeaderEntry.mWidgets), |
| 127 | mCameraContentEntry), |
| 128 | SimpleWidgetsSearchAlgorithm.getFilteredWidgets(mDataProvider, "Ca")); |
Alina Zaidi | c6fe0dd | 2021-03-03 18:40:01 +0000 | [diff] [blame] | 129 | } |
| 130 | |
| 131 | @Test |
Sunny Goyal | ce6cc7e | 2021-03-30 16:51:08 -0700 | [diff] [blame] | 132 | public void filter_shouldMatchOnWidgetLabel() { |
| 133 | doReturn(List.of(mCalendarHeaderEntry, mCalendarContentEntry, mCameraHeaderEntry, |
| 134 | mCameraContentEntry)) |
| 135 | .when(mDataProvider) |
| 136 | .getAllWidgets(); |
Alina Zaidi | c6fe0dd | 2021-03-03 18:40:01 +0000 | [diff] [blame] | 137 | |
Sunny Goyal | ce6cc7e | 2021-03-30 16:51:08 -0700 | [diff] [blame] | 138 | assertEquals(List.of( |
| 139 | new WidgetsListSearchHeaderEntry( |
| 140 | mCalendarHeaderEntry.mPkgItem, |
| 141 | mCalendarHeaderEntry.mTitleSectionName, |
| 142 | mCalendarHeaderEntry.mWidgets.subList(1, 2)), |
| 143 | new WidgetsListContentEntry( |
| 144 | mCalendarHeaderEntry.mPkgItem, |
| 145 | mCalendarHeaderEntry.mTitleSectionName, |
| 146 | mCalendarHeaderEntry.mWidgets.subList(1, 2)), |
| 147 | new WidgetsListSearchHeaderEntry( |
| 148 | mCameraHeaderEntry.mPkgItem, |
| 149 | mCameraHeaderEntry.mTitleSectionName, |
| 150 | mCameraHeaderEntry.mWidgets.subList(1, 3)), |
| 151 | new WidgetsListContentEntry( |
| 152 | mCameraHeaderEntry.mPkgItem, |
| 153 | mCameraHeaderEntry.mTitleSectionName, |
| 154 | mCameraHeaderEntry.mWidgets.subList(1, 3))), |
| 155 | SimpleWidgetsSearchAlgorithm.getFilteredWidgets(mDataProvider, "Widget1")); |
| 156 | } |
Alina Zaidi | c6fe0dd | 2021-03-03 18:40:01 +0000 | [diff] [blame] | 157 | |
Sunny Goyal | ce6cc7e | 2021-03-30 16:51:08 -0700 | [diff] [blame] | 158 | @Test |
| 159 | public void doSearch_shouldInformCallback() { |
| 160 | doReturn(List.of(mCalendarHeaderEntry, mCalendarContentEntry, mCameraHeaderEntry, |
| 161 | mCameraContentEntry, mClockHeaderEntry, mClockContentEntry)) |
| 162 | .when(mDataProvider) |
| 163 | .getAllWidgets(); |
| 164 | mSimpleWidgetsSearchAlgorithm.doSearch("Ca", mSearchCallback); |
Alina Zaidi | c6fe0dd | 2021-03-03 18:40:01 +0000 | [diff] [blame] | 165 | shadowOf(getMainLooper()).idle(); |
Sunny Goyal | ce6cc7e | 2021-03-30 16:51:08 -0700 | [diff] [blame] | 166 | verify(mSearchCallback).onSearchResult( |
| 167 | matches("Ca"), argThat(a -> a != null && !a.isEmpty())); |
| 168 | } |
| 169 | |
| 170 | private WidgetsListHeaderEntry createWidgetsHeaderEntry(String packageName, String appName, |
| 171 | int numOfWidgets) { |
| 172 | List<WidgetItem> widgetItems = generateWidgetItems(packageName, numOfWidgets); |
| 173 | PackageItemInfo pInfo = createPackageItemInfo(packageName, appName, |
| 174 | widgetItems.get(0).user); |
| 175 | |
| 176 | return new WidgetsListHeaderEntry(pInfo, /* titleSectionName= */ "", widgetItems); |
| 177 | } |
| 178 | |
| 179 | private WidgetsListContentEntry createWidgetsContentEntry(String packageName, String appName, |
| 180 | int numOfWidgets) { |
| 181 | List<WidgetItem> widgetItems = generateWidgetItems(packageName, numOfWidgets); |
| 182 | PackageItemInfo pInfo = createPackageItemInfo(packageName, appName, |
| 183 | widgetItems.get(0).user); |
| 184 | |
| 185 | return new WidgetsListContentEntry(pInfo, /* titleSectionName= */ "", widgetItems); |
| 186 | } |
| 187 | |
| 188 | private PackageItemInfo createPackageItemInfo(String packageName, String appName, |
| 189 | UserHandle userHandle) { |
| 190 | PackageItemInfo pInfo = new PackageItemInfo(packageName); |
| 191 | pInfo.title = appName; |
| 192 | pInfo.user = userHandle; |
| 193 | pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0); |
| 194 | return pInfo; |
| 195 | } |
| 196 | |
| 197 | private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) { |
| 198 | ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager()); |
| 199 | ArrayList<WidgetItem> widgetItems = new ArrayList<>(); |
| 200 | for (int i = 0; i < numOfWidgets; i++) { |
| 201 | ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i); |
| 202 | AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo(); |
| 203 | widgetInfo.provider = cn; |
| 204 | ReflectionHelpers.setField(widgetInfo, "providerInfo", |
| 205 | packageManager.addReceiverIfNotPresent(cn)); |
| 206 | |
| 207 | WidgetItem widgetItem = new WidgetItem( |
| 208 | LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo), |
| 209 | mTestProfile, mIconCache); |
| 210 | widgetItems.add(widgetItem); |
| 211 | } |
| 212 | return widgetItems; |
Alina Zaidi | c6fe0dd | 2021-03-03 18:40:01 +0000 | [diff] [blame] | 213 | } |
| 214 | } |