Refactoring before adding a new view type in the WidgetsListAdapter
Changes made: 1. Model: added an abstract class for storing common information for entries shown in the full page widgets picker. 2. Introduced a ViewHolderBinder interface to split the logic of binding data to ViewHolder into separate classes. 3. Move the view holder binding of WidgetsListRow from WidgetListAdapter to its new class. 4. Move some widgets picker classes into a new picker package. Test: Auto: Run WidgetsListAdapterTest, WidgetsListRowEntryTest and WidgetsListRowViewHolderBinderTest. Manual: open the all apps widgets tray and navigate the list. Bug: 179797520 Change-Id: Iab29557842bb79156cad84d00a4c5d0db0c5aa06
This commit is contained in:
parent
823c5f8bf6
commit
2f5648a911
|
@ -25,7 +25,7 @@ import androidx.annotation.Nullable;
|
|||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.icons.ComponentWithLabelAndIcon;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
import com.android.launcher3.widget.WidgetListRowEntry;
|
||||
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -43,17 +43,17 @@ public class WidgetsModel {
|
|||
public static final boolean GO_DISABLE_WIDGETS = true;
|
||||
public static final boolean GO_DISABLE_NOTIFICATION_DOTS = true;
|
||||
|
||||
private static final ArrayList<WidgetListRowEntry> EMPTY_WIDGET_LIST = new ArrayList<>();
|
||||
private static final ArrayList<WidgetsListBaseEntry> EMPTY_WIDGET_LIST = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Returns a list of {@link WidgetListRowEntry}. All {@link WidgetItem} in a single row
|
||||
* are sorted (based on label and user), but the overall list of {@link WidgetListRowEntry}s
|
||||
* is not sorted. This list is sorted at the UI when using
|
||||
* {@link com.android.launcher3.widget.WidgetsDiffReporter}
|
||||
* Returns a list of {@link WidgetsListBaseEntry}. All {@link WidgetItem} in a single row are
|
||||
* sorted (based on label and user), but the overall list of {@link WidgetsListBaseEntry}s is
|
||||
* not sorted. This list is sorted at the UI when using
|
||||
* {@link com.android.launcher3.widget.picker.WidgetsDiffReporter}
|
||||
*
|
||||
* @see com.android.launcher3.widget.WidgetsListAdapter#setWidgets(ArrayList)
|
||||
* @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List)
|
||||
*/
|
||||
public synchronized ArrayList<WidgetListRowEntry> getWidgetsList(Context context) {
|
||||
public synchronized ArrayList<WidgetsListBaseEntry> getWidgetsList(Context context) {
|
||||
return EMPTY_WIDGET_LIST;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<com.android.launcher3.widget.WidgetsFullSheet
|
||||
<com.android.launcher3.widget.picker.WidgetsFullSheet
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
@ -27,7 +27,7 @@
|
|||
android:background="?android:attr/colorPrimary"
|
||||
android:elevation="4dp">
|
||||
|
||||
<com.android.launcher3.widget.WidgetsRecyclerView
|
||||
<com.android.launcher3.widget.picker.WidgetsRecyclerView
|
||||
android:id="@+id/widgets_list_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
@ -49,4 +49,4 @@
|
|||
android:layout_alignParentTop="true"
|
||||
android:layout_marginEnd="@dimen/fastscroll_end_margin" />
|
||||
</com.android.launcher3.views.TopRoundedCornerView>
|
||||
</com.android.launcher3.widget.WidgetsFullSheet>
|
||||
</com.android.launcher3.widget.picker.WidgetsFullSheet>
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.testing;
|
||||
|
||||
import com.android.launcher3.BaseActivity;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.views.ActivityContext;
|
||||
import com.android.launcher3.views.BaseDragLayer;
|
||||
|
||||
/** An empty activity for {@link android.app.Fragment}s, {@link android.view.View}s testing. */
|
||||
public class TestActivity extends BaseActivity implements ActivityContext {
|
||||
|
||||
private DeviceProfile mDeviceProfile;
|
||||
|
||||
@Override
|
||||
public BaseDragLayer getDragLayer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceProfile getDeviceProfile() {
|
||||
return mDeviceProfile;
|
||||
}
|
||||
|
||||
public void setDeviceProfile(DeviceProfile deviceProfile) {
|
||||
mDeviceProfile = deviceProfile;
|
||||
}
|
||||
}
|
|
@ -38,7 +38,7 @@ 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.WidgetsFullSheet;
|
||||
import com.android.launcher3.widget.picker.WidgetsFullSheet;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
|
|
@ -1,151 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.launcher3.widget;
|
||||
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Matchers.isNull;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
import android.appwidget.AppWidgetProviderInfo;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.view.LayoutInflater;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.LauncherAppWidgetProviderInfo;
|
||||
import com.android.launcher3.WidgetPreviewLoader;
|
||||
import com.android.launcher3.icons.BitmapInfo;
|
||||
import com.android.launcher3.icons.IconCache;
|
||||
import com.android.launcher3.model.WidgetItem;
|
||||
import com.android.launcher3.model.data.PackageItemInfo;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
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;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class WidgetsListAdapterTest {
|
||||
|
||||
@Mock private LayoutInflater mMockLayoutInflater;
|
||||
@Mock private WidgetPreviewLoader mMockWidgetCache;
|
||||
@Mock private RecyclerView.AdapterDataObserver mListener;
|
||||
@Mock private IconCache mIconCache;
|
||||
|
||||
private WidgetsListAdapter mAdapter;
|
||||
private InvariantDeviceProfile mTestProfile;
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mTestProfile = new InvariantDeviceProfile();
|
||||
mTestProfile.numRows = 5;
|
||||
mTestProfile.numColumns = 5;
|
||||
mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater, mMockWidgetCache,
|
||||
mIconCache, null, null);
|
||||
mAdapter.registerAdapterDataObserver(mListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_notifyDataSetChanged() throws Exception {
|
||||
mAdapter.setWidgets(generateSampleMap(1));
|
||||
verify(mListener, times(1)).onChanged();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_notifyItemInserted() throws Exception {
|
||||
mAdapter.setWidgets(generateSampleMap(1));
|
||||
mAdapter.setWidgets(generateSampleMap(2));
|
||||
verify(mListener, times(1)).onChanged();
|
||||
verify(mListener, times(1)).onItemRangeInserted(eq(1), eq(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_notifyItemRemoved() throws Exception {
|
||||
mAdapter.setWidgets(generateSampleMap(2));
|
||||
mAdapter.setWidgets(generateSampleMap(1));
|
||||
verify(mListener, times(1)).onChanged();
|
||||
verify(mListener, times(1)).onItemRangeRemoved(eq(1), eq(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyItemChanged_PackageIconDiff() throws Exception {
|
||||
mAdapter.setWidgets(generateSampleMap(1));
|
||||
mAdapter.setWidgets(generateSampleMap(1));
|
||||
verify(mListener, times(1)).onChanged();
|
||||
verify(mListener, times(1)).onItemRangeChanged(eq(0), eq(1), isNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyItemChanged_widgetItemInfoDiff() throws Exception {
|
||||
// TODO: same package name but item number changed
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyItemInsertedRemoved_hodgepodge() throws Exception {
|
||||
// TODO: insert and remove combined. curMap
|
||||
// newMap [A, C, D] [A, B, E]
|
||||
// B - C < 0, removed B from index 1 [A, E]
|
||||
// E - C > 0, C inserted to index 1 [A, C, E]
|
||||
// E - D > 0, D inserted to index 2 [A, C, D, E]
|
||||
// E - null = -1, E deleted from index 3 [A, C, D]
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to generate the sample widget model map that can be used for the tests
|
||||
* @param num the number of WidgetItem the map should contain
|
||||
*/
|
||||
private ArrayList<WidgetListRowEntry> generateSampleMap(int num) {
|
||||
ArrayList<WidgetListRowEntry> result = new ArrayList<>();
|
||||
if (num <= 0) return result;
|
||||
ShadowPackageManager spm = shadowOf(mContext.getPackageManager());
|
||||
|
||||
for (int i = 0; i < num; i++) {
|
||||
ComponentName cn = new ComponentName("com.placeholder.apk" + i, "PlaceholderWidet");
|
||||
|
||||
AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
|
||||
widgetInfo.provider = cn;
|
||||
ReflectionHelpers.setField(widgetInfo, "providerInfo", spm.addReceiverIfNotPresent(cn));
|
||||
|
||||
WidgetItem wi = new WidgetItem(LauncherAppWidgetProviderInfo
|
||||
.fromProviderInfo(mContext, widgetInfo), mTestProfile, mIconCache);
|
||||
|
||||
PackageItemInfo pInfo = new PackageItemInfo(wi.componentName.getPackageName());
|
||||
pInfo.title = pInfo.packageName;
|
||||
pInfo.user = wi.user;
|
||||
pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
|
||||
|
||||
result.add(new WidgetListRowEntry(pInfo, new ArrayList<>(Collections.singleton(wi))));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.launcher3.widget.picker;
|
||||
|
||||
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;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.view.LayoutInflater;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.LauncherAppWidgetProviderInfo;
|
||||
import com.android.launcher3.WidgetPreviewLoader;
|
||||
import com.android.launcher3.icons.BitmapInfo;
|
||||
import com.android.launcher3.icons.ComponentWithLabel;
|
||||
import com.android.launcher3.icons.IconCache;
|
||||
import com.android.launcher3.model.WidgetItem;
|
||||
import com.android.launcher3.model.data.PackageItemInfo;
|
||||
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
||||
import com.android.launcher3.widget.model.WidgetsListContentEntry;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
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)
|
||||
public final class WidgetsListAdapterTest {
|
||||
|
||||
private static final String TEST_PACKAGE_1 = "com.google.test.1";
|
||||
private static final String TEST_PACKAGE_2 = "com.google.test.2";
|
||||
|
||||
@Mock private LayoutInflater mMockLayoutInflater;
|
||||
@Mock private WidgetPreviewLoader mMockWidgetCache;
|
||||
@Mock private RecyclerView.AdapterDataObserver mListener;
|
||||
@Mock private IconCache mIconCache;
|
||||
|
||||
private WidgetsListAdapter mAdapter;
|
||||
private InvariantDeviceProfile mTestProfile;
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mTestProfile = new InvariantDeviceProfile();
|
||||
mTestProfile.numRows = 5;
|
||||
mTestProfile.numColumns = 5;
|
||||
mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater, mMockWidgetCache,
|
||||
mIconCache, null, null);
|
||||
mAdapter.registerAdapterDataObserver(mListener);
|
||||
|
||||
doAnswer(invocation -> ((ComponentWithLabel) invocation.getArgument(0))
|
||||
.getComponent().getPackageName())
|
||||
.when(mIconCache).getTitleNoCache(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setWidgets_shouldNotifyDataSetChanged() {
|
||||
mAdapter.setWidgets(generateSampleMap(1));
|
||||
|
||||
verify(mListener).onChanged();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setWidgets_withItemInserted_shouldNotifyItemInserted() {
|
||||
mAdapter.setWidgets(generateSampleMap(1));
|
||||
mAdapter.setWidgets(generateSampleMap(2));
|
||||
|
||||
verify(mListener).onItemRangeInserted(eq(1), eq(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setWidgets_withItemRemoved_shouldNotifyItemRemoved() {
|
||||
mAdapter.setWidgets(generateSampleMap(2));
|
||||
mAdapter.setWidgets(generateSampleMap(1));
|
||||
|
||||
verify(mListener).onItemRangeRemoved(eq(1), eq(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setWidgets_appIconChanged_shouldNotifyItemChanged() {
|
||||
mAdapter.setWidgets(generateSampleMap(1));
|
||||
mAdapter.setWidgets(generateSampleMap(1));
|
||||
|
||||
verify(mListener).onItemRangeChanged(eq(0), eq(1), isNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setWidgets_sameApp_moreWidgets_shouldNotifyItemChangedWithWidgetItemInfoDiff() {
|
||||
// GIVEN the adapter was first populated with test package 1 & test package 2.
|
||||
WidgetsListBaseEntry testPackage1With2WidgetsListEntry =
|
||||
generateSampleAppWithWidgets(TEST_PACKAGE_1, /* numOfWidgets= */ 2);
|
||||
WidgetsListBaseEntry testPackage2With2WidgetsListEntry =
|
||||
generateSampleAppWithWidgets(TEST_PACKAGE_2, /* numOfWidgets= */ 2);
|
||||
mAdapter.setWidgets(
|
||||
List.of(testPackage1With2WidgetsListEntry, testPackage2With2WidgetsListEntry));
|
||||
|
||||
// WHEN the adapter is updated with the same list of apps but test package 2 has 3 widgets
|
||||
// now.
|
||||
WidgetsListBaseEntry testPackage1With3WidgetsListEntry =
|
||||
generateSampleAppWithWidgets(TEST_PACKAGE_2, /* numOfWidgets= */ 2);
|
||||
mAdapter.setWidgets(
|
||||
List.of(testPackage1With2WidgetsListEntry, testPackage1With3WidgetsListEntry));
|
||||
|
||||
// THEN the onItemRangeChanged is invoked.
|
||||
verify(mListener).onItemRangeChanged(eq(1), eq(1), isNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setWidgets_hodgepodge_shouldInvokeExpectedDataObserverCallbacks() {
|
||||
List<WidgetsListBaseEntry> allAppsWithWidgets = generateSampleMap(5);
|
||||
// GIVEN the current widgets list consist of [A, B, E].
|
||||
List<WidgetsListBaseEntry> currentList = List.of(
|
||||
allAppsWithWidgets.get(0), allAppsWithWidgets.get(1), allAppsWithWidgets.get(4));
|
||||
mAdapter.setWidgets(currentList);
|
||||
|
||||
// WHEN the widgets list is updated to [A, C, D].
|
||||
List<WidgetsListBaseEntry> newList = List.of(
|
||||
allAppsWithWidgets.get(0), allAppsWithWidgets.get(2), allAppsWithWidgets.get(3));
|
||||
mAdapter.setWidgets(newList);
|
||||
|
||||
// Computation logic | [Intermediate list during computation]
|
||||
// THEN B <> C < 0, removed B from index 1 | [A, E]
|
||||
verify(mListener).onItemRangeRemoved(/* positionStart= */ 1, /* 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 <> null = -1, E deleted from index 3 | [A, C, D]
|
||||
verify(mListener).onItemRangeRemoved(/* positionStart= */ 3, /* itemCount= */ 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to generate the sample widget model map that can be used for the tests
|
||||
* @param num the number of WidgetItem the map should contain
|
||||
*/
|
||||
private ArrayList<WidgetsListBaseEntry> generateSampleMap(int num) {
|
||||
ArrayList<WidgetsListBaseEntry> result = new ArrayList<>();
|
||||
if (num <= 0) return result;
|
||||
|
||||
for (int i = 0; i < num; i++) {
|
||||
String packageName = "com.placeholder.apk" + i;
|
||||
|
||||
List<WidgetItem> widgetItems = generateWidgetItems(packageName, /* numOfWidgets= */ 1);
|
||||
|
||||
PackageItemInfo pInfo = new PackageItemInfo(packageName);
|
||||
pInfo.title = pInfo.packageName;
|
||||
pInfo.user = widgetItems.get(0).user;
|
||||
pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
|
||||
|
||||
result.add(new WidgetsListContentEntry(pInfo, /* titleSectionName= */ "", widgetItems));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private WidgetsListBaseEntry generateSampleAppWithWidgets(String packageName,
|
||||
int numOfWidgets) {
|
||||
PackageItemInfo appInfo = new PackageItemInfo(packageName);
|
||||
appInfo.title = appInfo.packageName;
|
||||
appInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
|
||||
|
||||
return new WidgetsListContentEntry(appInfo,
|
||||
/* titleSectionName= */ "",
|
||||
generateWidgetItems(packageName, numOfWidgets));
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
widgetItems.add(new WidgetItem(
|
||||
LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
|
||||
mTestProfile, mIconCache));
|
||||
}
|
||||
return widgetItems;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* 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.picker;
|
||||
|
||||
import static android.os.Looper.getMainLooper;
|
||||
|
||||
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 android.graphics.Bitmap;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnLongClickListener;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.LauncherAppWidgetProviderInfo;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.WidgetPreviewLoader;
|
||||
import com.android.launcher3.icons.BitmapInfo;
|
||||
import com.android.launcher3.icons.ComponentWithLabel;
|
||||
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.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)
|
||||
public final class WidgetsListRowViewHolderBinderTest {
|
||||
private static final String TEST_PACKAGE = "com.google.test";
|
||||
private static final String APP_NAME = "Test app";
|
||||
|
||||
private Context mContext;
|
||||
private WidgetsListRowViewHolderBinder 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;
|
||||
@Mock
|
||||
private OnClickListener mOnIconClickListener;
|
||||
@Mock
|
||||
private IconCache mIconCache;
|
||||
@Mock
|
||||
private WidgetPreviewLoader mWidgetPreviewLoader;
|
||||
@Mock
|
||||
private DeviceProfile mDeviceProfile;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
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 WidgetsListRowViewHolderBinder(
|
||||
mContext,
|
||||
LayoutInflater.from(mTestActivity),
|
||||
mOnIconClickListener,
|
||||
mOnLongClickListener,
|
||||
mWidgetPreviewLoader);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
mActivityController.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindViewHolder_appWith3Widgets_shouldMatchAppTitle() {
|
||||
WidgetsRowViewHolder viewHolder = mViewHolderBinder.newViewHolder(
|
||||
new FrameLayout(mTestActivity));
|
||||
WidgetsListContentEntry entry = generateSampleAppWithWidgets(
|
||||
APP_NAME,
|
||||
TEST_PACKAGE,
|
||||
/* numOfWidgets= */ 3);
|
||||
mViewHolderBinder.bindViewHolder(viewHolder, entry);
|
||||
|
||||
assertThat(viewHolder.title.getText()).isEqualTo(APP_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindViewHolder_appWith3Widgets_shouldHave3Widgets() {
|
||||
WidgetsRowViewHolder viewHolder = mViewHolderBinder.newViewHolder(
|
||||
new FrameLayout(mTestActivity));
|
||||
WidgetsListContentEntry entry = generateSampleAppWithWidgets(
|
||||
APP_NAME,
|
||||
TEST_PACKAGE,
|
||||
/* numOfWidgets= */ 3);
|
||||
mViewHolderBinder.bindViewHolder(viewHolder, entry);
|
||||
shadowOf(getMainLooper()).idle();
|
||||
|
||||
// THEN the cell container has 5 children: 3 widgets + 2 separators
|
||||
// Index: 0 1 2 3 4
|
||||
// View: .SampleWidget0 | .SampleWidget1 | .SampleWidget2
|
||||
assertThat(viewHolder.cellContainer.getChildCount()).isEqualTo(5);
|
||||
// Widget 0 label is .SampleWidget0.
|
||||
assertWidgetCellWithLabel(viewHolder.cellContainer.getChildAt(0), ".SampleWidget0");
|
||||
// Widget 1 label is .SampleWidget1.
|
||||
assertWidgetCellWithLabel(viewHolder.cellContainer.getChildAt(2), ".SampleWidget1");
|
||||
// Widget 2 label is .SampleWidget2.
|
||||
assertWidgetCellWithLabel(viewHolder.cellContainer.getChildAt(4), ".SampleWidget2");
|
||||
}
|
||||
|
||||
private WidgetsListContentEntry generateSampleAppWithWidgets(String appName, String packageName,
|
||||
int numOfWidgets) {
|
||||
PackageItemInfo appInfo = new PackageItemInfo(packageName);
|
||||
appInfo.title = appName;
|
||||
appInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
|
||||
|
||||
return new WidgetsListContentEntry(appInfo,
|
||||
/* titleSectionName= */ "",
|
||||
generateWidgetItems(packageName, numOfWidgets));
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
widgetItems.add(new WidgetItem(
|
||||
LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
|
||||
mTestProfile, mIconCache));
|
||||
}
|
||||
return widgetItems;
|
||||
}
|
||||
|
||||
private void assertWidgetCellWithLabel(View view, String label) {
|
||||
assertThat(view).isInstanceOf(WidgetCell.class);
|
||||
TextView widgetLabel = (TextView) view.findViewById(R.id.widget_name);
|
||||
assertThat(widgetLabel.getText()).isEqualTo(label);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* 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.picker.model;
|
||||
|
||||
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 com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.LauncherAppWidgetProviderInfo;
|
||||
import com.android.launcher3.icons.ComponentWithLabel;
|
||||
import com.android.launcher3.icons.IconCache;
|
||||
import com.android.launcher3.model.WidgetItem;
|
||||
import com.android.launcher3.model.data.PackageItemInfo;
|
||||
import com.android.launcher3.widget.model.WidgetsListContentEntry;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
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)
|
||||
public final class WidgetsListContentEntryTest {
|
||||
private static final String PACKAGE_NAME = "com.google.test";
|
||||
private static final PackageItemInfo PACKAGE_ITEM_INFO = new PackageItemInfo(PACKAGE_NAME);
|
||||
private static final ComponentName WIDGET_1 = ComponentName.createRelative(PACKAGE_NAME,
|
||||
".widget1");
|
||||
private static final ComponentName WIDGET_2 = ComponentName.createRelative(PACKAGE_NAME,
|
||||
".widget2");
|
||||
private static final ComponentName WIDGET_3 = ComponentName.createRelative(PACKAGE_NAME,
|
||||
".widget3");
|
||||
private static final Map<ComponentName, String> WIDGETS_TO_LABELS = new HashMap();
|
||||
|
||||
static {
|
||||
WIDGETS_TO_LABELS.put(WIDGET_1, "Cat");
|
||||
WIDGETS_TO_LABELS.put(WIDGET_2, "Dog");
|
||||
WIDGETS_TO_LABELS.put(WIDGET_3, "Bird");
|
||||
}
|
||||
|
||||
@Mock private IconCache mIconCache;
|
||||
|
||||
private Context mContext;
|
||||
private InvariantDeviceProfile mTestProfile;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mTestProfile = new InvariantDeviceProfile();
|
||||
mTestProfile.numRows = 5;
|
||||
mTestProfile.numColumns = 5;
|
||||
|
||||
doAnswer(invocation -> {
|
||||
ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
|
||||
return WIDGETS_TO_LABELS.get(componentWithLabel.getComponent());
|
||||
}).when(mIconCache).getTitleNoCache(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unsortedWidgets_diffLabels_shouldSortWidgetItems() {
|
||||
// GIVEN a list of widgets in unsorted order.
|
||||
// Cat 2x3
|
||||
WidgetItem widgetItem1 = createWidgetItem(WIDGET_1, /* spanX= */ 2, /* spanY= */ 3);
|
||||
// Dog 2x3
|
||||
WidgetItem widgetItem2 = createWidgetItem(WIDGET_2, /* spanX= */ 2, /* spanY= */ 3);
|
||||
// Bird 2x3
|
||||
WidgetItem widgetItem3 = createWidgetItem(WIDGET_3, /* spanX= */ 2, /* spanY= */ 3);
|
||||
|
||||
// WHEN creates a WidgetsListRowEntry with the unsorted widgets.
|
||||
WidgetsListContentEntry widgetsListRowEntry = new WidgetsListContentEntry(PACKAGE_ITEM_INFO,
|
||||
/* titleSectionName= */ "T",
|
||||
List.of(widgetItem1, widgetItem2, widgetItem3));
|
||||
|
||||
// THEN the widgets list is sorted by their labels alphabetically: [Bird, Cat, Dog].
|
||||
assertThat(widgetsListRowEntry.mWidgets)
|
||||
.containsExactly(widgetItem3, widgetItem1, widgetItem2)
|
||||
.inOrder();
|
||||
assertThat(widgetsListRowEntry.mTitleSectionName).isEqualTo("T");
|
||||
assertThat(widgetsListRowEntry.mPkgItem).isEqualTo(PACKAGE_ITEM_INFO);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unsortedWidgets_sameLabels_differentSize_shouldSortWidgetItems() {
|
||||
// GIVEN a list of widgets in unsorted order.
|
||||
// Cat 3x3
|
||||
WidgetItem widgetItem1 = createWidgetItem(WIDGET_1, /* spanX= */ 3, /* spanY= */ 3);
|
||||
// Cat 1x2
|
||||
WidgetItem widgetItem2 = createWidgetItem(WIDGET_1, /* spanX= */ 1, /* spanY= */ 2);
|
||||
// Cat 2x2
|
||||
WidgetItem widgetItem3 = createWidgetItem(WIDGET_1, /* spanX= */ 2, /* spanY= */ 2);
|
||||
|
||||
// WHEN creates a WidgetsListRowEntry with the unsorted widgets.
|
||||
WidgetsListContentEntry widgetsListRowEntry = new WidgetsListContentEntry(PACKAGE_ITEM_INFO,
|
||||
/* titleSectionName= */ "T",
|
||||
List.of(widgetItem1, widgetItem2, widgetItem3));
|
||||
|
||||
// THEN the widgets list is sorted by their gird sizes in an ascending order:
|
||||
// [1x2, 2x2, 3x3].
|
||||
assertThat(widgetsListRowEntry.mWidgets)
|
||||
.containsExactly(widgetItem2, widgetItem3, widgetItem1)
|
||||
.inOrder();
|
||||
assertThat(widgetsListRowEntry.mTitleSectionName).isEqualTo("T");
|
||||
assertThat(widgetsListRowEntry.mPkgItem).isEqualTo(PACKAGE_ITEM_INFO);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unsortedWidgets_hodgepodge_shouldSortWidgetItems() {
|
||||
// GIVEN a list of widgets in unsorted order.
|
||||
// Cat 3x3
|
||||
WidgetItem widgetItem1 = createWidgetItem(WIDGET_1, /* spanX= */ 3, /* spanY= */ 3);
|
||||
// Cat 1x2
|
||||
WidgetItem widgetItem2 = createWidgetItem(WIDGET_1, /* spanX= */ 1, /* spanY= */ 2);
|
||||
// Dog 2x2
|
||||
WidgetItem widgetItem3 = createWidgetItem(WIDGET_2, /* spanX= */ 2, /* spanY= */ 2);
|
||||
// Bird 2x2
|
||||
WidgetItem widgetItem4 = createWidgetItem(WIDGET_3, /* spanX= */ 2, /* spanY= */ 2);
|
||||
|
||||
// WHEN creates a WidgetsListRowEntry with the unsorted widgets.
|
||||
WidgetsListContentEntry widgetsListRowEntry = new WidgetsListContentEntry(PACKAGE_ITEM_INFO,
|
||||
/* titleSectionName= */ "T",
|
||||
List.of(widgetItem1, widgetItem2, widgetItem3, widgetItem4));
|
||||
|
||||
// THEN the widgets list is first sorted by labels alphabetically. Then, for widgets with
|
||||
// same labels, they are sorted by their gird sizes in an ascending order:
|
||||
// [Bird 2x2, Cat 1x2, Cat 3x3, Dog 2x2]
|
||||
assertThat(widgetsListRowEntry.mWidgets)
|
||||
.containsExactly(widgetItem4, widgetItem2, widgetItem1, widgetItem3)
|
||||
.inOrder();
|
||||
assertThat(widgetsListRowEntry.mTitleSectionName).isEqualTo("T");
|
||||
assertThat(widgetsListRowEntry.mPkgItem).isEqualTo(PACKAGE_ITEM_INFO);
|
||||
}
|
||||
|
||||
private WidgetItem createWidgetItem(ComponentName componentName, int spanX, int spanY) {
|
||||
String label = WIDGETS_TO_LABELS.get(componentName);
|
||||
ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
|
||||
AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
|
||||
widgetInfo.provider = componentName;
|
||||
ReflectionHelpers.setField(widgetInfo, "providerInfo",
|
||||
packageManager.addReceiverIfNotPresent(componentName));
|
||||
|
||||
LauncherAppWidgetProviderInfo launcherAppWidgetProviderInfo =
|
||||
LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo);
|
||||
launcherAppWidgetProviderInfo.spanX = spanX;
|
||||
launcherAppWidgetProviderInfo.spanY = spanY;
|
||||
launcherAppWidgetProviderInfo.label = label;
|
||||
|
||||
return new WidgetItem(launcherAppWidgetProviderInfo, mTestProfile, mIconCache);
|
||||
}
|
||||
}
|
|
@ -181,10 +181,10 @@ import com.android.launcher3.widget.PendingAddWidgetInfo;
|
|||
import com.android.launcher3.widget.PendingAppWidgetHostView;
|
||||
import com.android.launcher3.widget.WidgetAddFlowHandler;
|
||||
import com.android.launcher3.widget.WidgetHostViewLoader;
|
||||
import com.android.launcher3.widget.WidgetListRowEntry;
|
||||
import com.android.launcher3.widget.WidgetManagerHelper;
|
||||
import com.android.launcher3.widget.WidgetsFullSheet;
|
||||
import com.android.launcher3.widget.custom.CustomWidgetManager;
|
||||
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
||||
import com.android.launcher3.widget.picker.WidgetsFullSheet;
|
||||
import com.android.systemui.plugins.OverlayPlugin;
|
||||
import com.android.systemui.plugins.PluginListener;
|
||||
import com.android.systemui.plugins.shared.LauncherExterns;
|
||||
|
@ -2552,7 +2552,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
|
|||
}
|
||||
|
||||
@Override
|
||||
public void bindAllWidgets(final ArrayList<WidgetListRowEntry> allWidgets) {
|
||||
public void bindAllWidgets(final List<WidgetsListBaseEntry> allWidgets) {
|
||||
mPopupDataProvider.setAllWidgets(allWidgets);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ import com.android.launcher3.model.data.ItemInfo;
|
|||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.ItemInfoMatcher;
|
||||
import com.android.launcher3.widget.WidgetListRowEntry;
|
||||
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
@ -123,7 +123,7 @@ public abstract class BaseModelUpdateTask implements ModelUpdateTask {
|
|||
}
|
||||
|
||||
public void bindUpdatedWidgets(BgDataModel dataModel) {
|
||||
final ArrayList<WidgetListRowEntry> widgets =
|
||||
final ArrayList<WidgetsListBaseEntry> widgets =
|
||||
dataModel.widgetsModel.getWidgetsList(mApp.getContext());
|
||||
scheduleCallbackTask(c -> c.bindAllWidgets(widgets));
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ import com.android.launcher3.util.IntSet;
|
|||
import com.android.launcher3.util.IntSparseArrayMap;
|
||||
import com.android.launcher3.util.ItemInfoMatcher;
|
||||
import com.android.launcher3.util.ViewOnDrawExecutor;
|
||||
import com.android.launcher3.widget.WidgetListRowEntry;
|
||||
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
|
@ -467,7 +467,7 @@ public class BgDataModel {
|
|||
void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
|
||||
void bindRestoreItemsChange(HashSet<ItemInfo> updates);
|
||||
void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher);
|
||||
void bindAllWidgets(ArrayList<WidgetListRowEntry> widgets);
|
||||
void bindAllWidgets(List<WidgetsListBaseEntry> widgets);
|
||||
void onPageBoundSynchronously(int page);
|
||||
void executeOnNextDraw(ViewOnDrawExecutor executor);
|
||||
void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap);
|
||||
|
|
|
@ -31,10 +31,10 @@ import com.android.launcher3.notification.NotificationListener;
|
|||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
import com.android.launcher3.util.ShortcutUtil;
|
||||
import com.android.launcher3.widget.WidgetListRowEntry;
|
||||
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
||||
import com.android.launcher3.widget.model.WidgetsListContentEntry;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -59,7 +59,7 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan
|
|||
/** Maps packages to their DotInfo's . */
|
||||
private Map<PackageUserKey, DotInfo> mPackageUserToDotInfos = new HashMap<>();
|
||||
/** Maps packages to their Widgets */
|
||||
private ArrayList<WidgetListRowEntry> mAllWidgets = new ArrayList<>();
|
||||
private List<WidgetsListBaseEntry> mAllWidgets = List.of();
|
||||
|
||||
private PopupDataChangeListener mChangeListener = PopupDataChangeListener.INSTANCE;
|
||||
|
||||
|
@ -187,7 +187,7 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan
|
|||
notificationListener.cancelNotificationFromLauncher(notificationKey);
|
||||
}
|
||||
|
||||
public void setAllWidgets(ArrayList<WidgetListRowEntry> allWidgets) {
|
||||
public void setAllWidgets(List<WidgetsListBaseEntry> allWidgets) {
|
||||
mAllWidgets = allWidgets;
|
||||
mChangeListener.onWidgetsBound();
|
||||
}
|
||||
|
@ -196,14 +196,15 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan
|
|||
mChangeListener = listener == null ? PopupDataChangeListener.INSTANCE : listener;
|
||||
}
|
||||
|
||||
public ArrayList<WidgetListRowEntry> getAllWidgets() {
|
||||
public List<WidgetsListBaseEntry> getAllWidgets() {
|
||||
return mAllWidgets;
|
||||
}
|
||||
|
||||
public List<WidgetItem> getWidgetsForPackageUser(PackageUserKey packageUserKey) {
|
||||
return mAllWidgets.stream()
|
||||
.filter(row -> row.pkgItem.packageName.equals(packageUserKey.mPackageName))
|
||||
.flatMap(row -> row.widgets.stream())
|
||||
.filter(row -> row instanceof WidgetsListContentEntry
|
||||
&& row.mPkgItem.packageName.equals(packageUserKey.mPackageName))
|
||||
.flatMap(row -> ((WidgetsListContentEntry) row).mWidgets.stream())
|
||||
.filter(widget -> packageUserKey.mUser.equals(widget.user))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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.recyclerview;
|
||||
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
||||
|
||||
/**
|
||||
* Creates and populates views with data
|
||||
*
|
||||
* @param <T> A data model which is used to populate the {@link ViewHolder}.
|
||||
* @param <V> A subclass of {@link ViewHolder} which holds references to views.
|
||||
*/
|
||||
public interface ViewHolderBinder<T, V extends ViewHolder> {
|
||||
/**
|
||||
* Creates a new view, and attach it to the parent {@link ViewGroup}. Then, populates UI
|
||||
* references in a {@link ViewHolder}.
|
||||
*/
|
||||
V newViewHolder(ViewGroup parent);
|
||||
|
||||
/** Populate UI references in {@link ViewHolder} with data. */
|
||||
void bindViewHolder(V viewHolder, T data);
|
||||
|
||||
/**
|
||||
* Called when the view is recycled. Views are recycled in batches once they are sufficiently
|
||||
* far off screen that it is unlikely the user will scroll back to them soon. Optionally
|
||||
* override this to free expensive resources.
|
||||
*/
|
||||
default void unbindViewHolder(V viewHolder) {}
|
||||
}
|
|
@ -46,7 +46,7 @@ import com.android.launcher3.util.ItemInfoMatcher;
|
|||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.util.ViewOnDrawExecutor;
|
||||
import com.android.launcher3.views.BaseDragLayer;
|
||||
import com.android.launcher3.widget.WidgetListRowEntry;
|
||||
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
@ -232,7 +232,7 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity
|
|||
public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher) { }
|
||||
|
||||
@Override
|
||||
public void bindAllWidgets(ArrayList<WidgetListRowEntry> widgets) { }
|
||||
public void bindAllWidgets(List<WidgetsListBaseEntry> widgets) { }
|
||||
|
||||
@Override
|
||||
public void onPageBoundSynchronously(int page) { }
|
||||
|
|
|
@ -33,7 +33,7 @@ import com.android.launcher3.LauncherAppState;
|
|||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.util.ResourceBasedOverride;
|
||||
import com.android.launcher3.widget.WidgetsFullSheet;
|
||||
import com.android.launcher3.widget.picker.WidgetsFullSheet;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Function;
|
||||
|
|
|
@ -49,7 +49,7 @@ import com.android.launcher3.popup.ArrowPopup;
|
|||
import com.android.launcher3.shortcuts.DeepShortcutView;
|
||||
import com.android.launcher3.testing.TestLogging;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.widget.WidgetsFullSheet;
|
||||
import com.android.launcher3.widget.picker.WidgetsFullSheet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
* 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.
|
||||
|
@ -13,36 +13,24 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.launcher3.widget;
|
||||
|
||||
import com.android.launcher3.model.WidgetItem;
|
||||
package com.android.launcher3.widget.model;
|
||||
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.PackageItemInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Holder class to store all the information related to a single row in the widget list
|
||||
*/
|
||||
public class WidgetListRowEntry {
|
||||
|
||||
public final PackageItemInfo pkgItem;
|
||||
|
||||
public final ArrayList<WidgetItem> widgets;
|
||||
/** Holder class to store the package information of an entry shown in the widgets list. */
|
||||
public abstract class WidgetsListBaseEntry {
|
||||
public final PackageItemInfo mPkgItem;
|
||||
|
||||
/**
|
||||
* Character that is used as a section name for the {@link ItemInfo#title}.
|
||||
* (e.g., "G" will be stored if title is "Google")
|
||||
*/
|
||||
public String titleSectionName;
|
||||
public final String mTitleSectionName;
|
||||
|
||||
public WidgetListRowEntry(PackageItemInfo pkgItem, ArrayList<WidgetItem> items) {
|
||||
this.pkgItem = pkgItem;
|
||||
this.widgets = items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return pkgItem.packageName + ":" + widgets.size();
|
||||
public WidgetsListBaseEntry(PackageItemInfo pkgItem, String titleSectionName) {
|
||||
mPkgItem = pkgItem;
|
||||
mTitleSectionName = titleSectionName;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.model;
|
||||
|
||||
import com.android.launcher3.model.WidgetItem;
|
||||
import com.android.launcher3.model.data.PackageItemInfo;
|
||||
import com.android.launcher3.widget.WidgetItemComparator;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Holder class to store all the information related to a list of widgets from the same app which is
|
||||
* shown in the {@link com.android.launcher3.widget.picker.WidgetsFullSheet}.
|
||||
*/
|
||||
public final class WidgetsListContentEntry extends WidgetsListBaseEntry {
|
||||
|
||||
public final List<WidgetItem> mWidgets;
|
||||
|
||||
public WidgetsListContentEntry(PackageItemInfo pkgItem, String titleSectionName,
|
||||
List<WidgetItem> items) {
|
||||
super(pkgItem, titleSectionName);
|
||||
this.mWidgets =
|
||||
items.stream().sorted(new WidgetItemComparator()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mPkgItem.packageName + ":" + mWidgets.size();
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.widget;
|
||||
package com.android.launcher3.widget.picker;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
|
@ -22,7 +22,9 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||
|
||||
import com.android.launcher3.icons.IconCache;
|
||||
import com.android.launcher3.model.data.PackageItemInfo;
|
||||
import com.android.launcher3.widget.WidgetsListAdapter.WidgetListRowEntryComparator;
|
||||
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
||||
import com.android.launcher3.widget.model.WidgetsListContentEntry;
|
||||
import com.android.launcher3.widget.picker.WidgetsListAdapter.WidgetListBaseRowEntryComparator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
@ -43,8 +45,13 @@ public class WidgetsDiffReporter {
|
|||
mListener = listener;
|
||||
}
|
||||
|
||||
public void process(ArrayList<WidgetListRowEntry> currentEntries,
|
||||
ArrayList<WidgetListRowEntry> newEntries, WidgetListRowEntryComparator comparator) {
|
||||
/**
|
||||
* Notifies the difference between {@code currentEntries} & {@code newEntries} by calling the
|
||||
* relevant {@link androidx.recyclerview.widget.RecyclerView.RecyclerViewDataObserver} methods.
|
||||
*/
|
||||
public void process(ArrayList<WidgetsListBaseEntry> currentEntries,
|
||||
ArrayList<WidgetsListBaseEntry> newEntries,
|
||||
WidgetListBaseRowEntryComparator comparator) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "process oldEntries#=" + currentEntries.size()
|
||||
+ " newEntries#=" + newEntries.size());
|
||||
|
@ -62,20 +69,20 @@ public class WidgetsDiffReporter {
|
|||
}
|
||||
return;
|
||||
}
|
||||
ArrayList<WidgetListRowEntry> orgEntries =
|
||||
(ArrayList<WidgetListRowEntry>) currentEntries.clone();
|
||||
Iterator<WidgetListRowEntry> orgIter = orgEntries.iterator();
|
||||
Iterator<WidgetListRowEntry> newIter = newEntries.iterator();
|
||||
ArrayList<WidgetsListBaseEntry> orgEntries =
|
||||
(ArrayList<WidgetsListBaseEntry>) currentEntries.clone();
|
||||
Iterator<WidgetsListBaseEntry> orgIter = orgEntries.iterator();
|
||||
Iterator<WidgetsListBaseEntry> newIter = newEntries.iterator();
|
||||
|
||||
WidgetListRowEntry orgRowEntry = orgIter.next();
|
||||
WidgetListRowEntry newRowEntry = newIter.next();
|
||||
WidgetsListBaseEntry orgRowEntry = orgIter.next();
|
||||
WidgetsListBaseEntry newRowEntry = newIter.next();
|
||||
|
||||
do {
|
||||
int diff = comparePackageName(orgRowEntry, newRowEntry, comparator);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, String.format("diff=%d orgRowEntry (%s) newRowEntry (%s)",
|
||||
diff, orgRowEntry != null? orgRowEntry.toString() : null,
|
||||
newRowEntry != null? newRowEntry.toString() : null));
|
||||
diff, orgRowEntry != null ? orgRowEntry.toString() : null,
|
||||
newRowEntry != null ? newRowEntry.toString() : null));
|
||||
}
|
||||
int index = -1;
|
||||
if (diff < 0) {
|
||||
|
@ -83,17 +90,17 @@ public class WidgetsDiffReporter {
|
|||
mListener.notifyItemRemoved(index);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, String.format("notifyItemRemoved called (%d)%s", index,
|
||||
orgRowEntry.titleSectionName));
|
||||
orgRowEntry.mTitleSectionName));
|
||||
}
|
||||
currentEntries.remove(index);
|
||||
orgRowEntry = orgIter.hasNext() ? orgIter.next() : null;
|
||||
} else if (diff > 0) {
|
||||
index = orgRowEntry != null? currentEntries.indexOf(orgRowEntry):
|
||||
currentEntries.size();
|
||||
index = orgRowEntry != null ? currentEntries.indexOf(orgRowEntry)
|
||||
: currentEntries.size();
|
||||
currentEntries.add(index, newRowEntry);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, String.format("notifyItemInserted called (%d)%s", index,
|
||||
newRowEntry.titleSectionName));
|
||||
newRowEntry.mTitleSectionName));
|
||||
}
|
||||
newRowEntry = newIter.hasNext() ? newIter.next() : null;
|
||||
mListener.notifyItemInserted(index);
|
||||
|
@ -102,14 +109,14 @@ public class WidgetsDiffReporter {
|
|||
// same package name but,
|
||||
// did the icon, title, etc, change?
|
||||
// or did the widget size and desc, span, etc change?
|
||||
if (!isSamePackageItemInfo(orgRowEntry.pkgItem, newRowEntry.pkgItem) ||
|
||||
!orgRowEntry.widgets.equals(newRowEntry.widgets)) {
|
||||
if (!isSamePackageItemInfo(orgRowEntry.mPkgItem, newRowEntry.mPkgItem)
|
||||
|| !areWidgetsEqual(orgRowEntry, newRowEntry)) {
|
||||
index = currentEntries.indexOf(orgRowEntry);
|
||||
currentEntries.set(index, newRowEntry);
|
||||
mListener.notifyItemChanged(index);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, String.format("notifyItemChanged called (%d)%s", index,
|
||||
newRowEntry.titleSectionName));
|
||||
newRowEntry.mTitleSectionName));
|
||||
}
|
||||
}
|
||||
orgRowEntry = orgIter.hasNext() ? orgIter.next() : null;
|
||||
|
@ -122,10 +129,11 @@ public class WidgetsDiffReporter {
|
|||
* Compare package name using the same comparator as in {@link WidgetsListAdapter}.
|
||||
* Also handle null row pointers.
|
||||
*/
|
||||
private int comparePackageName(WidgetListRowEntry curRow, WidgetListRowEntry newRow,
|
||||
WidgetListRowEntryComparator comparator) {
|
||||
private int comparePackageName(WidgetsListBaseEntry curRow, WidgetsListBaseEntry newRow,
|
||||
WidgetListBaseRowEntryComparator comparator) {
|
||||
if (curRow == null && newRow == null) {
|
||||
throw new IllegalStateException("Cannot compare PackageItemInfo if both rows are null.");
|
||||
throw new IllegalStateException(
|
||||
"Cannot compare PackageItemInfo if both rows are null.");
|
||||
}
|
||||
|
||||
if (curRow == null && newRow != null) {
|
||||
|
@ -136,6 +144,17 @@ public class WidgetsDiffReporter {
|
|||
return comparator.compare(curRow, newRow);
|
||||
}
|
||||
|
||||
private boolean areWidgetsEqual(WidgetsListBaseEntry curRow,
|
||||
WidgetsListBaseEntry newRow) {
|
||||
if (!(curRow instanceof WidgetsListContentEntry)
|
||||
|| !(newRow instanceof WidgetsListContentEntry)) {
|
||||
return false;
|
||||
}
|
||||
WidgetsListContentEntry orgRowEntry = (WidgetsListContentEntry) curRow;
|
||||
WidgetsListContentEntry newRowEntry = (WidgetsListContentEntry) newRow;
|
||||
return orgRowEntry.mWidgets.equals(newRowEntry.mWidgets);
|
||||
}
|
||||
|
||||
private boolean isSamePackageItemInfo(PackageItemInfo curInfo, PackageItemInfo newInfo) {
|
||||
return curInfo.bitmap.icon.equals(newInfo.bitmap.icon)
|
||||
&& !mIconCache.isDefaultIcon(curInfo.bitmap, curInfo.user);
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.launcher3.widget;
|
||||
package com.android.launcher3.widget.picker;
|
||||
|
||||
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
|
||||
import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
|
||||
|
@ -42,6 +42,7 @@ import com.android.launcher3.anim.PendingAnimation;
|
|||
import com.android.launcher3.compat.AccessibilityManagerCompat;
|
||||
import com.android.launcher3.views.RecyclerViewFastScroller;
|
||||
import com.android.launcher3.views.TopRoundedCornerView;
|
||||
import com.android.launcher3.widget.BaseWidgetSheet;
|
||||
|
||||
/**
|
||||
* Popup for showing the full list of available widgets
|
||||
|
@ -218,8 +219,8 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
mNoIntercept = false;
|
||||
RecyclerViewFastScroller scroller = mRecyclerView.getScrollbar();
|
||||
if (scroller.getThumbOffsetY() >= 0 &&
|
||||
getPopupContainer().isEventOverView(scroller, ev)) {
|
||||
if (scroller.getThumbOffsetY() >= 0
|
||||
&& getPopupContainer().isEventOverView(scroller, ev)) {
|
||||
mNoIntercept = true;
|
||||
} else if (getPopupContainer().isEventOverView(mContent, ev)) {
|
||||
mNoIntercept = !mRecyclerView.shouldContainerScroll(ev, getPopupContainer());
|
||||
|
@ -228,6 +229,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||
return super.onControllerInterceptTouchEvent(ev);
|
||||
}
|
||||
|
||||
/** Shows the {@link WidgetsFullSheet} on the launcher. */
|
||||
public static WidgetsFullSheet show(Launcher launcher, boolean animate) {
|
||||
WidgetsFullSheet sheet = (WidgetsFullSheet) launcher.getLayoutInflater()
|
||||
.inflate(R.layout.widgets_full_sheet, launcher.getDragLayer(), false);
|
||||
|
@ -237,6 +239,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||
return sheet;
|
||||
}
|
||||
|
||||
/** Gets the {@link WidgetsRecyclerView} which shows all widgets in {@link WidgetsFullSheet}. */
|
||||
@VisibleForTesting
|
||||
public static WidgetsRecyclerView getWidgetsView(Launcher launcher) {
|
||||
return launcher.findViewById(R.id.widgets_list_view);
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.launcher3.widget.picker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
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 androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.RecyclerView.Adapter;
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.WidgetPreviewLoader;
|
||||
import com.android.launcher3.icons.IconCache;
|
||||
import com.android.launcher3.recyclerview.ViewHolderBinder;
|
||||
import com.android.launcher3.util.LabelComparator;
|
||||
import com.android.launcher3.widget.WidgetCell;
|
||||
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
||||
import com.android.launcher3.widget.model.WidgetsListContentEntry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* List view adapter for the widget tray.
|
||||
*
|
||||
* <p>Memory vs. Performance:
|
||||
* The less number of types of views are inserted into a {@link RecyclerView}, the more recycling
|
||||
* happens and less memory is consumed.
|
||||
*/
|
||||
public class WidgetsListAdapter extends Adapter<ViewHolder> {
|
||||
|
||||
private static final String TAG = "WidgetsListAdapter";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
/** Uniquely identifies widgets list view type within the app. */
|
||||
private static final int VIEW_TYPE_WIDGETS_LIST = R.layout.widgets_list_row_view;
|
||||
|
||||
private final WidgetsDiffReporter mDiffReporter;
|
||||
private final SparseArray<ViewHolderBinder> mViewHolderBinders = new SparseArray<>();
|
||||
private final WidgetsListRowViewHolderBinder mWidgetsListRowViewHolderBinder;
|
||||
|
||||
private ArrayList<WidgetsListBaseEntry> mEntries = new ArrayList<>();
|
||||
|
||||
public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
|
||||
WidgetPreviewLoader widgetPreviewLoader, IconCache iconCache,
|
||||
OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) {
|
||||
mDiffReporter = new WidgetsDiffReporter(iconCache, this);
|
||||
mWidgetsListRowViewHolderBinder = new WidgetsListRowViewHolderBinder(context,
|
||||
layoutInflater, iconClickListener, iconLongClickListener, widgetPreviewLoader);
|
||||
mViewHolderBinders.put(VIEW_TYPE_WIDGETS_LIST, mWidgetsListRowViewHolderBinder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defers applying bitmap on all the {@link WidgetCell} in the {@param rv}.
|
||||
*
|
||||
* @see WidgetCell#setApplyBitmapDeferred(boolean)
|
||||
*/
|
||||
public void setApplyBitmapDeferred(boolean isDeferred, RecyclerView rv) {
|
||||
mWidgetsListRowViewHolderBinder.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.cellContainer.getChildCount() - 1; j >= 0; j--) {
|
||||
View v = holder.cellContainer.getChildAt(j);
|
||||
if (v instanceof WidgetCell) {
|
||||
((WidgetCell) v).setApplyBitmapDeferred(isDeferred);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mEntries.size();
|
||||
}
|
||||
|
||||
/** Gets the section name for {@link com.android.launcher3.views.RecyclerViewFastScroller}. */
|
||||
public String getSectionName(int pos) {
|
||||
return mEntries.get(pos).mTitleSectionName;
|
||||
}
|
||||
|
||||
/** Updates the widget list. */
|
||||
public void setWidgets(List<WidgetsListBaseEntry> tempEntries) {
|
||||
ArrayList<WidgetsListBaseEntry> newEntries = new ArrayList<>(tempEntries);
|
||||
WidgetListBaseRowEntryComparator rowComparator = new WidgetListBaseRowEntryComparator();
|
||||
Collections.sort(newEntries, rowComparator);
|
||||
mDiffReporter.process(mEntries, newEntries, rowComparator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int pos) {
|
||||
ViewHolderBinder viewHolderBinder = mViewHolderBinders.get(getItemViewType(pos));
|
||||
viewHolderBinder.bindViewHolder(holder, mEntries.get(pos));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "\nonCreateViewHolder");
|
||||
}
|
||||
|
||||
return mViewHolderBinders.get(viewType).newViewHolder(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewRecycled(ViewHolder holder) {
|
||||
mViewHolderBinders.get(holder.getItemViewType()).unbindViewHolder(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFailedToRecycleView(ViewHolder holder) {
|
||||
// If child views are animating, then the RecyclerView may choose not to recycle the view,
|
||||
// causing extraneous onCreateViewHolder() calls. It is safe in this case to continue
|
||||
// recycling this view, and take care in onViewRecycled() to cancel any existing
|
||||
// animations.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int pos) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int pos) {
|
||||
WidgetsListBaseEntry entry = mEntries.get(pos);
|
||||
if (entry instanceof WidgetsListContentEntry) {
|
||||
return VIEW_TYPE_WIDGETS_LIST;
|
||||
}
|
||||
throw new UnsupportedOperationException("ViewHolderBinder not found for " + entry);
|
||||
}
|
||||
|
||||
/** Comparator for sorting WidgetListRowEntry based on package title. */
|
||||
public static class WidgetListBaseRowEntryComparator implements
|
||||
Comparator<WidgetsListBaseEntry> {
|
||||
|
||||
private final LabelComparator mComparator = new LabelComparator();
|
||||
|
||||
@Override
|
||||
public int compare(WidgetsListBaseEntry a, WidgetsListBaseEntry b) {
|
||||
return mComparator.compare(a.mPkgItem.title.toString(), b.mPkgItem.title.toString());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* 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.
|
||||
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.launcher3.widget;
|
||||
package com.android.launcher3.widget.picker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
@ -23,103 +23,76 @@ import android.view.View.OnClickListener;
|
|||
import android.view.View.OnLongClickListener;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.RecyclerView.Adapter;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.WidgetPreviewLoader;
|
||||
import com.android.launcher3.icons.IconCache;
|
||||
import com.android.launcher3.model.WidgetItem;
|
||||
import com.android.launcher3.util.LabelComparator;
|
||||
import com.android.launcher3.recyclerview.ViewHolderBinder;
|
||||
import com.android.launcher3.widget.WidgetCell;
|
||||
import com.android.launcher3.widget.model.WidgetsListContentEntry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* List view adapter for the widget tray.
|
||||
*
|
||||
* <p>Memory vs. Performance:
|
||||
* The less number of types of views are inserted into a {@link RecyclerView}, the more recycling
|
||||
* happens and less memory is consumed. {@link #getItemViewType} was not overridden as there is
|
||||
* only a single type of view.
|
||||
* Binds data from {@link WidgetsListContentEntry} to UI elements in {@link WidgetsRowViewHolder}.
|
||||
*/
|
||||
public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> {
|
||||
|
||||
private static final String TAG = "WidgetsListAdapter";
|
||||
public class WidgetsListRowViewHolderBinder
|
||||
implements ViewHolderBinder<WidgetsListContentEntry, WidgetsRowViewHolder> {
|
||||
private static final boolean DEBUG = false;
|
||||
private static final String TAG = "WidgetsListRowViewHolderBinder";
|
||||
|
||||
private final WidgetPreviewLoader mWidgetPreviewLoader;
|
||||
private final LayoutInflater mLayoutInflater;
|
||||
|
||||
private final int mIndent;
|
||||
private final OnClickListener mIconClickListener;
|
||||
private final OnLongClickListener mIconLongClickListener;
|
||||
private final int mIndent;
|
||||
private ArrayList<WidgetListRowEntry> mEntries = new ArrayList<>();
|
||||
private final WidgetsDiffReporter mDiffReporter;
|
||||
private final WidgetPreviewLoader mWidgetPreviewLoader;
|
||||
private boolean mApplyBitmapDeferred = false;
|
||||
|
||||
private boolean mApplyBitmapDeferred;
|
||||
|
||||
public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
|
||||
WidgetPreviewLoader widgetPreviewLoader, IconCache iconCache,
|
||||
OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) {
|
||||
public WidgetsListRowViewHolderBinder(
|
||||
Context context,
|
||||
LayoutInflater layoutInflater,
|
||||
OnClickListener iconClickListener,
|
||||
OnLongClickListener iconLongClickListener,
|
||||
WidgetPreviewLoader widgetPreviewLoader) {
|
||||
mLayoutInflater = layoutInflater;
|
||||
mWidgetPreviewLoader = widgetPreviewLoader;
|
||||
mIndent = context.getResources().getDimensionPixelSize(R.dimen.widget_section_indent);
|
||||
mIconClickListener = iconClickListener;
|
||||
mIconLongClickListener = iconLongClickListener;
|
||||
mIndent = context.getResources().getDimensionPixelSize(R.dimen.widget_section_indent);
|
||||
mDiffReporter = new WidgetsDiffReporter(iconCache, this);
|
||||
mWidgetPreviewLoader = widgetPreviewLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defers applying bitmap on all the {@link WidgetCell} in the {@param rv}
|
||||
*
|
||||
* @see WidgetCell#setApplyBitmapDeferred(boolean)
|
||||
* Defers applying bitmap on all the {@link WidgetCell} at
|
||||
* {@link #bindViewHolder(WidgetsRowViewHolder, WidgetsListContentEntry)} if
|
||||
* {@code applyBitmapDeferred} is {@code true}.
|
||||
*/
|
||||
public void setApplyBitmapDeferred(boolean isDeferred, RecyclerView rv) {
|
||||
mApplyBitmapDeferred = isDeferred;
|
||||
public void setApplyBitmapDeferred(boolean applyBitmapDeferred) {
|
||||
mApplyBitmapDeferred = applyBitmapDeferred;
|
||||
}
|
||||
|
||||
for (int i = rv.getChildCount() - 1; i >= 0; i--) {
|
||||
WidgetsRowViewHolder holder = (WidgetsRowViewHolder)
|
||||
rv.getChildViewHolder(rv.getChildAt(i));
|
||||
for (int j = holder.cellContainer.getChildCount() - 1; j >= 0; j--) {
|
||||
View v = holder.cellContainer.getChildAt(j);
|
||||
if (v instanceof WidgetCell) {
|
||||
((WidgetCell) v).setApplyBitmapDeferred(mApplyBitmapDeferred);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public WidgetsRowViewHolder newViewHolder(ViewGroup parent) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "\nonCreateViewHolder");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the widget list.
|
||||
*/
|
||||
public void setWidgets(ArrayList<WidgetListRowEntry> tempEntries) {
|
||||
WidgetListRowEntryComparator rowComparator = new WidgetListRowEntryComparator();
|
||||
Collections.sort(tempEntries, rowComparator);
|
||||
mDiffReporter.process(mEntries, tempEntries, rowComparator);
|
||||
ViewGroup container = (ViewGroup) mLayoutInflater.inflate(
|
||||
R.layout.widgets_list_row_view, parent, false);
|
||||
|
||||
// if the end padding is 0, then container view (horizontal scroll view) doesn't respect
|
||||
// the end of the linear layout width + the start padding and doesn't allow scrolling.
|
||||
container.findViewById(R.id.widgets_cell_list).setPaddingRelative(mIndent, 0, 1, 0);
|
||||
|
||||
return new WidgetsRowViewHolder(container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mEntries.size();
|
||||
}
|
||||
|
||||
public String getSectionName(int pos) {
|
||||
return mEntries.get(pos).titleSectionName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(WidgetsRowViewHolder holder, int pos) {
|
||||
WidgetListRowEntry entry = mEntries.get(pos);
|
||||
List<WidgetItem> infoList = entry.widgets;
|
||||
public void bindViewHolder(WidgetsRowViewHolder holder, WidgetsListContentEntry entry) {
|
||||
List<WidgetItem> infoList = entry.mWidgets;
|
||||
|
||||
ViewGroup row = holder.cellContainer;
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, String.format(
|
||||
"onBindViewHolder [pos=%d, widget#=%d, row.getChildCount=%d]",
|
||||
pos, infoList.size(), row.getChildCount()));
|
||||
Log.d(TAG, String.format("onBindViewHolder [widget#=%d, row.getChildCount=%d]",
|
||||
infoList.size(), row.getChildCount()));
|
||||
}
|
||||
|
||||
// Add more views.
|
||||
|
@ -150,7 +123,7 @@ public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> {
|
|||
}
|
||||
|
||||
// Bind the views in the application info section.
|
||||
holder.title.applyFromItemInfoWithIcon(entry.pkgItem);
|
||||
holder.title.applyFromItemInfoWithIcon(entry.mPkgItem);
|
||||
|
||||
// Bind the view in the widget horizontal tray region.
|
||||
for (int i = 0; i < infoList.size(); i++) {
|
||||
|
@ -167,53 +140,11 @@ public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public WidgetsRowViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "\nonCreateViewHolder");
|
||||
}
|
||||
|
||||
ViewGroup container = (ViewGroup) mLayoutInflater.inflate(
|
||||
R.layout.widgets_list_row_view, parent, false);
|
||||
|
||||
// if the end padding is 0, then container view (horizontal scroll view) doesn't respect
|
||||
// the end of the linear layout width + the start padding and doesn't allow scrolling.
|
||||
container.findViewById(R.id.widgets_cell_list).setPaddingRelative(mIndent, 0, 1, 0);
|
||||
|
||||
return new WidgetsRowViewHolder(container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewRecycled(WidgetsRowViewHolder holder) {
|
||||
public void unbindViewHolder(WidgetsRowViewHolder holder) {
|
||||
int total = holder.cellContainer.getChildCount();
|
||||
for (int i = 0; i < total; i += 2) {
|
||||
WidgetCell widget = (WidgetCell) holder.cellContainer.getChildAt(i);
|
||||
widget.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onFailedToRecycleView(WidgetsRowViewHolder holder) {
|
||||
// If child views are animating, then the RecyclerView may choose not to recycle the view,
|
||||
// causing extraneous onCreateViewHolder() calls. It is safe in this case to continue
|
||||
// recycling this view, and take care in onViewRecycled() to cancel any existing
|
||||
// animations.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int pos) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparator for sorting WidgetListRowEntry based on package title
|
||||
*/
|
||||
public static class WidgetListRowEntryComparator implements Comparator<WidgetListRowEntry> {
|
||||
|
||||
private final LabelComparator mComparator = new LabelComparator();
|
||||
|
||||
@Override
|
||||
public int compare(WidgetListRowEntry a, WidgetListRowEntry b) {
|
||||
return mComparator.compare(a.pkgItem.title.toString(), b.pkgItem.title.toString());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.widget;
|
||||
package com.android.launcher3.widget.picker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
|
@ -22,13 +22,13 @@ import android.util.AttributeSet;
|
|||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.launcher3.BaseRecyclerView;
|
||||
import com.android.launcher3.R;
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener;
|
||||
|
||||
import com.android.launcher3.BaseRecyclerView;
|
||||
import com.android.launcher3.R;
|
||||
|
||||
/**
|
||||
* The widgets recycler view.
|
||||
*/
|
||||
|
@ -89,7 +89,7 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
|
|||
LinearLayoutManager layoutManager = ((LinearLayoutManager) getLayoutManager());
|
||||
layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction));
|
||||
|
||||
int posInt = (int) ((touchFraction == 1)? pos -1 : pos);
|
||||
int posInt = (int) ((touchFraction == 1) ? pos - 1 : pos);
|
||||
return mAdapter.getSectionName(posInt);
|
||||
}
|
||||
|
||||
|
@ -171,4 +171,4 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
|
|||
@Override
|
||||
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,16 +13,17 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.launcher3.widget;
|
||||
package com.android.launcher3.widget.picker;
|
||||
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
||||
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.R;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
||||
|
||||
public class WidgetsRowViewHolder extends ViewHolder {
|
||||
/** A {@link ViewHolder} for a row in the full widget picker. */
|
||||
public final class WidgetsRowViewHolder extends ViewHolder {
|
||||
|
||||
public final ViewGroup cellContainer;
|
||||
public final BubbleTextView title;
|
|
@ -21,10 +21,10 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
|||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.model.BgDataModel.Callbacks;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.widget.WidgetListRowEntry;
|
||||
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}.
|
||||
|
@ -47,7 +47,7 @@ public class LoaderResults extends BaseLoaderResults {
|
|||
|
||||
@Override
|
||||
public void bindWidgets() {
|
||||
final ArrayList<WidgetListRowEntry> widgets =
|
||||
final List<WidgetsListBaseEntry> widgets =
|
||||
mBgDataModel.widgetsModel.getWidgetsList(mApp.getContext());
|
||||
executeCallbacksTask(c -> c.bindAllWidgets(widgets), mUiExecutor);
|
||||
}
|
||||
|
|
|
@ -28,12 +28,12 @@ import com.android.launcher3.model.data.PackageItemInfo;
|
|||
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
import com.android.launcher3.widget.WidgetItemComparator;
|
||||
import com.android.launcher3.widget.WidgetListRowEntry;
|
||||
import com.android.launcher3.widget.WidgetManagerHelper;
|
||||
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
||||
import com.android.launcher3.widget.model.WidgetsListContentEntry;
|
||||
import com.android.launcher3.widget.picker.WidgetsDiffReporter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -60,24 +60,23 @@ public class WidgetsModel {
|
|||
private final Map<PackageItemInfo, List<WidgetItem>> mWidgetsList = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Returns a list of {@link WidgetListRowEntry}. All {@link WidgetItem} in a single row
|
||||
* are sorted (based on label and user), but the overall list of {@link WidgetListRowEntry}s
|
||||
* is not sorted. This list is sorted at the UI when using
|
||||
* {@link com.android.launcher3.widget.WidgetsDiffReporter}
|
||||
* Returns a list of {@link WidgetsListBaseEntry}. All {@link WidgetItem} in a single row
|
||||
* are sorted (based on label and user), but the overall list of
|
||||
* {@link WidgetsListBaseEntry}s is not sorted. This list is sorted at the UI when using
|
||||
* {@link WidgetsDiffReporter}
|
||||
*
|
||||
* @see com.android.launcher3.widget.WidgetsListAdapter#setWidgets(ArrayList)
|
||||
* @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List)
|
||||
*/
|
||||
public synchronized ArrayList<WidgetListRowEntry> getWidgetsList(Context context) {
|
||||
ArrayList<WidgetListRowEntry> result = new ArrayList<>();
|
||||
public synchronized ArrayList<WidgetsListBaseEntry> getWidgetsList(Context context) {
|
||||
ArrayList<WidgetsListBaseEntry> result = new ArrayList<>();
|
||||
AlphabeticIndexCompat indexer = new AlphabeticIndexCompat(context);
|
||||
|
||||
WidgetItemComparator widgetComparator = new WidgetItemComparator();
|
||||
for (Map.Entry<PackageItemInfo, List<WidgetItem>> entry : mWidgetsList.entrySet()) {
|
||||
WidgetListRowEntry row = new WidgetListRowEntry(
|
||||
entry.getKey(), new ArrayList<>(entry.getValue()));
|
||||
row.titleSectionName = (row.pkgItem.title == null) ? "" :
|
||||
indexer.computeSectionName(row.pkgItem.title);
|
||||
Collections.sort(row.widgets, widgetComparator);
|
||||
PackageItemInfo pkgItem = entry.getKey();
|
||||
String sectionName = (pkgItem.title == null) ? "" :
|
||||
indexer.computeSectionName(pkgItem.title);
|
||||
WidgetsListContentEntry row =
|
||||
new WidgetsListContentEntry(pkgItem, sectionName, entry.getValue());
|
||||
result.add(row);
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -37,8 +37,8 @@ import com.android.launcher3.tapl.AppIconMenuItem;
|
|||
import com.android.launcher3.tapl.Widgets;
|
||||
import com.android.launcher3.tapl.Workspace;
|
||||
import com.android.launcher3.views.OptionsPopupView;
|
||||
import com.android.launcher3.widget.WidgetsFullSheet;
|
||||
import com.android.launcher3.widget.WidgetsRecyclerView;
|
||||
import com.android.launcher3.widget.picker.WidgetsFullSheet;
|
||||
import com.android.launcher3.widget.picker.WidgetsRecyclerView;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
|
|
Loading…
Reference in New Issue