• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.launcher3.widget.util;
17 
18 import com.android.launcher3.model.WidgetItem;
19 
20 import java.util.ArrayList;
21 import java.util.Comparator;
22 import java.util.List;
23 import java.util.stream.Collectors;
24 
25 /** An utility class which groups {@link WidgetItem}s into a table. */
26 public final class WidgetsTableUtils {
27 
28     /**
29      * Groups widgets in the following order:
30      * 1. Widgets always go before shortcuts.
31      * 2. Widgets with smaller horizontal spans will be shown first.
32      * 3. If widgets have the same horizontal spans, then widgets with a smaller vertical spans will
33      *    go first.
34      * 4. If both widgets have the same horizontal and vertical spans, they will use the same order
35      *    from the given {@code widgetItems}.
36      */
37     private static final Comparator<WidgetItem> WIDGET_SHORTCUT_COMPARATOR = (item, otherItem) -> {
38         if (item.widgetInfo != null && otherItem.widgetInfo == null) return -1;
39 
40         if (item.widgetInfo == null && otherItem.widgetInfo != null) return 1;
41         if (item.spanX == otherItem.spanX) {
42             if (item.spanY == otherItem.spanY) return 0;
43             return item.spanY > otherItem.spanY ? 1 : -1;
44         }
45         return item.spanX > otherItem.spanX ? 1 : -1;
46     };
47 
48 
49     /**
50      * Groups widgets items into a 2D array which matches their appearance in a UI table.
51      *
52      * <p>Grouping:
53      * 1. Widgets and shortcuts never group together in the same row.
54      * 2. The ordered widgets are grouped together in the same row until their total horizontal
55      *    spans exceed the {@code maxSpansPerRow} - 1.
56      * 3. The order shortcuts are grouped together in the same row until their total horizontal
57      *    spans exceed the {@code maxSpansPerRow} - 1.
58      * 4. If there is only one widget in a row, its width may exceed the {@code maxSpansPerRow}.
59      *
60      * <p>Let's say the {@code maxSpansPerRow} is set to 6. Widgets can be grouped in the same row
61      * if their total horizontal spans added don't exceed 5.
62      * Example 1: Row 1: 2x2, 2x3, 1x1. Total horizontal spans is 5. This is okay.
63      * Example 2: Row 1: 2x2, 4x3, 1x1. the total horizontal spans is 7. This is wrong. 4x3 and 1x1
64      * should be moved to a new row.
65      * Example 3: Row 1: 6x4. This is okay because this is the only item in the row.
66      */
groupWidgetItemsIntoTable( List<WidgetItem> widgetItems, final int maxSpansPerRow)67     public static List<ArrayList<WidgetItem>> groupWidgetItemsIntoTable(
68             List<WidgetItem> widgetItems, final int maxSpansPerRow) {
69         List<WidgetItem> sortedWidgetItems = widgetItems.stream().sorted(WIDGET_SHORTCUT_COMPARATOR)
70                 .collect(Collectors.toList());
71         List<ArrayList<WidgetItem>> widgetItemsTable = new ArrayList<>();
72         ArrayList<WidgetItem> widgetItemsAtRow = null;
73         for (WidgetItem widgetItem : sortedWidgetItems) {
74             if (widgetItemsAtRow == null) {
75                 widgetItemsAtRow = new ArrayList<>();
76                 widgetItemsTable.add(widgetItemsAtRow);
77             }
78             int numOfWidgetItems = widgetItemsAtRow.size();
79             int totalHorizontalSpan = widgetItemsAtRow.stream().map(item -> item.spanX)
80                     .reduce(/* default= */ 0, Integer::sum);
81             int totalHorizontalSpanAfterAddingWidget = widgetItem.spanX + totalHorizontalSpan;
82             if (numOfWidgetItems == 0) {
83                 widgetItemsAtRow.add(widgetItem);
84             } else if (
85                     // The max spans per row is reduced by 1 to ensure we don't pack too many
86                     // 1xn widgets on the same row, which may reduce the space for rendering a
87                     // widget's description.
88                     totalHorizontalSpanAfterAddingWidget <= maxSpansPerRow - 1
89                             && widgetItem.hasSameType(widgetItemsAtRow.get(numOfWidgetItems - 1))) {
90                 // Group items in the same row if
91                 // 1. they are with the same type, i.e. a row can only have widgets or shortcuts but
92                 //    never a mix of both.
93                 // 2. the total number of horizontal spans are smaller than or equal to
94                 //    MAX_SPAN_PER_ROW. If an item has a horizontal span > MAX_SPAN_PER_ROW, we just
95                 //    place it in its own row regardless of the horizontal span limit.
96                 widgetItemsAtRow.add(widgetItem);
97             } else {
98                 widgetItemsAtRow = new ArrayList<>();
99                 widgetItemsTable.add(widgetItemsAtRow);
100                 widgetItemsAtRow.add(widgetItem);
101             }
102         }
103         return widgetItemsTable;
104     }
105 }
106