OSDN Git Service

Dashboard search bar polish
[android-x86/packages-apps-Settings.git] / src / com / android / settings / dashboard / DashboardData.java
1 /*
2  * Copyright (C) 2016 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.settings.dashboard;
17
18 import android.annotation.IntDef;
19 import android.graphics.drawable.Icon;
20 import android.support.annotation.Nullable;
21 import android.support.annotation.VisibleForTesting;
22 import android.support.v7.util.DiffUtil;
23 import android.text.TextUtils;
24
25 import com.android.settings.R;
26 import com.android.settings.dashboard.conditional.Condition;
27 import com.android.settingslib.drawer.DashboardCategory;
28 import com.android.settingslib.drawer.Tile;
29
30 import java.lang.annotation.Retention;
31 import java.lang.annotation.RetentionPolicy;
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.Objects;
35
36 /**
37  * Description about data list used in the DashboardAdapter. In the data list each item can be
38  * Condition, suggestion or category tile.
39  * <p>
40  * ItemsData has inner class Item, which represents the Item in data list.
41  */
42 public class DashboardData {
43     @Deprecated
44     public static final int SUGGESTION_MODE_DEFAULT = 0;
45     @Deprecated
46     public static final int SUGGESTION_MODE_COLLAPSED = 1;
47     @Deprecated
48     public static final int SUGGESTION_MODE_EXPANDED = 2;
49
50     public static final int HEADER_MODE_DEFAULT = 0;
51     public static final int HEADER_MODE_SUGGESTION_EXPANDED = 1;
52     public static final int HEADER_MODE_FULLY_EXPANDED = 2;
53     public static final int HEADER_MODE_COLLAPSED = 3;
54     @Retention(RetentionPolicy.SOURCE)
55     @IntDef({HEADER_MODE_DEFAULT, HEADER_MODE_SUGGESTION_EXPANDED, HEADER_MODE_FULLY_EXPANDED,
56         HEADER_MODE_COLLAPSED})
57     public @interface HeaderMode{}
58
59     public static final int POSITION_NOT_FOUND = -1;
60     public static final int DEFAULT_SUGGESTION_COUNT = 2;
61
62     // id namespace for different type of items.
63     private static final int NS_SPACER = 0;
64     private static final int NS_ITEMS = 2000;
65     private static final int NS_CONDITION = 3000;
66     private static final int NS_SUGGESTION_CONDITION = 4000;
67
68     private final List<Item> mItems;
69     private final List<DashboardCategory> mCategories;
70     private final List<Condition> mConditions;
71     private final List<Tile> mSuggestions;
72     @Deprecated
73     private final int mSuggestionMode;
74     @Deprecated
75     private final Condition mExpandedCondition;
76     private final @HeaderMode int mSuggestionConditionMode;
77     private int mId;
78     private boolean mCombineSuggestionAndCondition;
79
80     private DashboardData(Builder builder) {
81         mCategories = builder.mCategories;
82         mConditions = builder.mConditions;
83         mSuggestions = builder.mSuggestions;
84         mSuggestionMode = builder.mSuggestionMode;
85         mExpandedCondition = builder.mExpandedCondition;
86         mSuggestionConditionMode = builder.mSuggestionConditionMode;
87         mCombineSuggestionAndCondition = builder.mCombineSuggestionAndCondition;
88
89         mItems = new ArrayList<>();
90         mId = 0;
91
92         buildItemsData();
93     }
94
95     public int getItemIdByPosition(int position) {
96         return mItems.get(position).id;
97     }
98
99     public int getItemTypeByPosition(int position) {
100         return mItems.get(position).type;
101     }
102
103     public Object getItemEntityByPosition(int position) {
104         return mItems.get(position).entity;
105     }
106
107     public List<Item> getItemList() {
108         return mItems;
109     }
110
111     public int size() {
112         return mItems.size();
113     }
114
115     public Object getItemEntityById(long id) {
116         for (final Item item : mItems) {
117             if (item.id == id) {
118                 return item.entity;
119             }
120         }
121         return null;
122     }
123
124     public List<DashboardCategory> getCategories() {
125         return mCategories;
126     }
127
128     public List<Condition> getConditions() {
129         return mConditions;
130     }
131
132     public List<Tile> getSuggestions() {
133         return mSuggestions;
134     }
135
136     public int getSuggestionMode() {
137         return mSuggestionMode;
138     }
139
140     public int getSuggestionConditionMode() {
141         return mSuggestionConditionMode;
142     }
143
144     @Deprecated
145     public Condition getExpandedCondition() {
146         return mExpandedCondition;
147     }
148
149     /**
150      * Find the position of the object in mItems list, using the equals method to compare
151      *
152      * @param entity the object that need to be found in list
153      * @return position of the object, return POSITION_NOT_FOUND if object isn't in the list
154      */
155     public int getPositionByEntity(Object entity) {
156         if (entity == null) return POSITION_NOT_FOUND;
157
158         final int size = mItems.size();
159         for (int i = 0; i < size; i++) {
160             final Object item = mItems.get(i).entity;
161             if (entity.equals(item)) {
162                 return i;
163             }
164         }
165
166         return POSITION_NOT_FOUND;
167     }
168
169     /**
170      * Find the position of the Tile object.
171      * <p>
172      * First, try to find the exact identical instance of the tile object, if not found,
173      * then try to find a tile has the same title.
174      *
175      * @param tile tile that need to be found
176      * @return position of the object, return INDEX_NOT_FOUND if object isn't in the list
177      */
178     public int getPositionByTile(Tile tile) {
179         final int size = mItems.size();
180         for (int i = 0; i < size; i++) {
181             final Object entity = mItems.get(i).entity;
182             if (entity == tile) {
183                 return i;
184             } else if (entity instanceof Tile && tile.title.equals(((Tile) entity).title)) {
185                 return i;
186             }
187         }
188
189         return POSITION_NOT_FOUND;
190     }
191
192     /**
193      * Get the count of suggestions to display
194      *
195      * The displayable count mainly depends on the {@link #mSuggestionMode}
196      * and the size of suggestions list.
197      *
198      * When in default mode, displayable count couldn't larger than
199      * {@link #DEFAULT_SUGGESTION_COUNT}.
200      *
201      * When in expanded mode, display all the suggestions.
202      *
203      * @return the count of suggestions to display
204      */
205     public int getDisplayableSuggestionCount() {
206         final int suggestionSize = sizeOf(mSuggestions);
207         if (mCombineSuggestionAndCondition) {
208             if (mSuggestionConditionMode == HEADER_MODE_COLLAPSED) {
209                 return 0;
210             }
211             if (mSuggestionConditionMode == HEADER_MODE_DEFAULT) {
212                 return Math.min(DEFAULT_SUGGESTION_COUNT, suggestionSize);
213             }
214             return suggestionSize;
215         }
216         if (mSuggestionMode == SUGGESTION_MODE_DEFAULT) {
217             return Math.min(DEFAULT_SUGGESTION_COUNT, suggestionSize);
218         }
219         if (mSuggestionMode == SUGGESTION_MODE_EXPANDED) {
220             return suggestionSize;
221         }
222         return 0;
223     }
224
225     public boolean hasMoreSuggestions() {
226         if (mCombineSuggestionAndCondition) {
227             return mSuggestionConditionMode == HEADER_MODE_COLLAPSED && mSuggestions.size() > 0
228                 || mSuggestionConditionMode == HEADER_MODE_DEFAULT
229                     && mSuggestions.size() > DEFAULT_SUGGESTION_COUNT;
230         }
231         return mSuggestionMode == SUGGESTION_MODE_COLLAPSED
232                 || (mSuggestionMode == SUGGESTION_MODE_DEFAULT
233                 && mSuggestions.size() > DEFAULT_SUGGESTION_COUNT);
234     }
235
236     private void resetCount() {
237         mId = 0;
238     }
239
240     /**
241      * Count the item and add it into list when {@paramref add} is true.
242      *
243      * Note that {@link #mId} will increment automatically and the real
244      * id stored in {@link Item} is shifted by {@paramref nameSpace}. This is a
245      * simple way to keep the id stable.
246      *
247      * @param object    maybe {@link Condition}, {@link Tile}, {@link DashboardCategory} or null
248      * @param type      type of the item, and value is the layout id
249      * @param add       flag about whether to add item into list
250      * @param nameSpace namespace based on the type
251      */
252     private void countItem(Object object, int type, boolean add, int nameSpace) {
253         if (add) {
254             if (mCombineSuggestionAndCondition) {
255                 mItems.add(new Item(object, type, mId + nameSpace));
256             } else {
257                 mItems.add(new Item(object, type, mId + nameSpace, object == mExpandedCondition));
258             }
259         }
260         mId++;
261     }
262
263     /**
264      * A special count item method for just suggestions. Id is calculated using suggestion hash
265      * instead of the position of suggestion in list. This is a more stable id than countItem.
266      */
267     private void countSuggestion(Tile tile, boolean add) {
268         if (add) {
269             mItems.add(new Item(
270                     tile,
271                     tile.remoteViews != null
272                             ? R.layout.suggestion_tile_card
273                             : R.layout.suggestion_tile,
274                     Objects.hash(tile.title),
275                     false));
276         }
277         mId++;
278     }
279
280     /**
281      * Build the mItems list using mConditions, mSuggestions, mCategories data
282      * and mIsShowingAll, mSuggestionMode flag.
283      */
284     private void buildItemsData() {
285         final boolean hasSuggestions = sizeOf(mSuggestions) > 0;
286         if (!mCombineSuggestionAndCondition) {
287             boolean hasConditions = false;
288             for (int i = 0; mConditions != null && i < mConditions.size(); i++) {
289                 boolean shouldShow = mConditions.get(i).shouldShow();
290                 hasConditions |= shouldShow;
291                 countItem(mConditions.get(i), R.layout.condition_card, shouldShow, NS_CONDITION);
292             }
293
294             resetCount();
295             countItem(null, R.layout.dashboard_spacer, hasConditions && hasSuggestions, NS_SPACER);
296             countItem(buildSuggestionHeaderData(), R.layout.suggestion_header, hasSuggestions,
297                 NS_SPACER);
298
299             resetCount();
300             if (mSuggestions != null) {
301                 int maxSuggestions = getDisplayableSuggestionCount();
302                 for (int i = 0; i < mSuggestions.size(); i++) {
303                     countSuggestion(mSuggestions.get(i), i < maxSuggestions);
304                 }
305             }
306         } else {
307             final List<Condition> conditions = getConditionsToShow(mConditions);
308             final boolean hasConditions = sizeOf(conditions) > 0;
309
310             final List<Tile> suggestions = getSuggestionsToShow(mSuggestions);
311             final int hiddenSuggestion =
312                 hasSuggestions ? sizeOf(mSuggestions) - sizeOf(suggestions) : 0;
313
314             resetCount();
315             /* Top suggestion/condition header. This will be present when there is any suggestion or
316              * condition to show, except in the case that there is only conditions to show and the
317              * mode is fully expanded. */
318             countItem(new SuggestionConditionHeaderData(conditions, hiddenSuggestion),
319                 R.layout.suggestion_condition_header, hasSuggestions
320                     || hasConditions && mSuggestionConditionMode != HEADER_MODE_FULLY_EXPANDED,
321                 NS_SUGGESTION_CONDITION);
322
323             /* Suggestion container. This is the card view that contains the list of suggestions.
324              * This will be added whenever the suggestion list is not empty */
325             countItem(suggestions, R.layout.suggestion_condition_container, sizeOf(suggestions) > 0,
326                 NS_SUGGESTION_CONDITION);
327
328             /* Second suggestion/condition header. This will be added when there is at least one
329              * suggestion or condition that is not currently displayed, and the user can expand the
330               * section to view more items. */
331             countItem(new SuggestionConditionHeaderData(conditions, hiddenSuggestion),
332                 R.layout.suggestion_condition_header,
333                 mSuggestionConditionMode != HEADER_MODE_COLLAPSED
334                     && mSuggestionConditionMode != HEADER_MODE_FULLY_EXPANDED
335                     && (hiddenSuggestion > 0
336                         || hasConditions && hasSuggestions),
337                 NS_SUGGESTION_CONDITION);
338
339             /* Condition container. This is the card view that contains the list of conditions.
340              * This will be added whenever the condition list is not empty */
341             countItem(conditions, R.layout.suggestion_condition_container,
342                 hasConditions && mSuggestionConditionMode == HEADER_MODE_FULLY_EXPANDED,
343                 NS_SUGGESTION_CONDITION);
344
345             /* Suggestion/condition footer. This will be present when the section is fully expanded
346              * or when there is no conditions and no hidden suggestions */
347             countItem(null, R.layout.suggestion_condition_footer,
348                 (hasConditions || hasSuggestions) &&
349                     mSuggestionConditionMode == HEADER_MODE_FULLY_EXPANDED
350                 || hasSuggestions && !hasConditions && hiddenSuggestion == 0,
351                 NS_SUGGESTION_CONDITION);
352         }
353
354         resetCount();
355         for (int i = 0; mCategories != null && i < mCategories.size(); i++) {
356             DashboardCategory category = mCategories.get(i);
357             countItem(category, R.layout.dashboard_category,
358                     !TextUtils.isEmpty(category.title), NS_ITEMS);
359             for (int j = 0; j < category.tiles.size(); j++) {
360                 Tile tile = category.tiles.get(j);
361                 countItem(tile, R.layout.dashboard_tile, true, NS_ITEMS);
362             }
363         }
364     }
365
366     private static int sizeOf(List<?> list) {
367         return list == null ? 0 : list.size();
368     }
369
370     private SuggestionHeaderData buildSuggestionHeaderData() {
371         SuggestionHeaderData data;
372         if (mSuggestions == null) {
373             data = new SuggestionHeaderData();
374         } else {
375             final boolean hasMoreSuggestions = hasMoreSuggestions();
376             final int suggestionSize = mSuggestions.size();
377             final int undisplayedSuggestionCount = suggestionSize - getDisplayableSuggestionCount();
378             data = new SuggestionHeaderData(hasMoreSuggestions, suggestionSize,
379                     undisplayedSuggestionCount);
380         }
381
382         return data;
383     }
384
385     private List<Condition> getConditionsToShow(List<Condition> conditions) {
386         if (conditions == null) {
387             return null;
388         }
389         List<Condition> result = new ArrayList<Condition>();
390         final int size = conditions == null ? 0 : conditions.size();
391         for (int i = 0; i < size; i++) {
392             final Condition condition = conditions.get(i);
393             if (condition.shouldShow()) {
394                 result.add(condition);
395             }
396         }
397         return result;
398     }
399
400     private List<Tile> getSuggestionsToShow(List<Tile> suggestions) {
401         if (suggestions == null || mSuggestionConditionMode == HEADER_MODE_COLLAPSED) {
402             return null;
403         }
404         if (mSuggestionConditionMode != HEADER_MODE_DEFAULT
405                 || suggestions.size() <= DEFAULT_SUGGESTION_COUNT) {
406             return suggestions;
407         }
408         return suggestions.subList(0, DEFAULT_SUGGESTION_COUNT);
409     }
410
411     /**
412      * Builder used to build the ItemsData
413      * <p>
414      * {@link #mExpandedCondition}, {@link #mSuggestionConditionMode} and {@link #mSuggestionMode}
415      * have default value while others are not.
416      */
417     public static class Builder {
418         @Deprecated
419         private int mSuggestionMode = SUGGESTION_MODE_DEFAULT;
420         @Deprecated
421         private Condition mExpandedCondition = null;
422         private @HeaderMode int mSuggestionConditionMode = HEADER_MODE_DEFAULT;
423
424         private List<DashboardCategory> mCategories;
425         private List<Condition> mConditions;
426         private List<Tile> mSuggestions;
427         private boolean mCombineSuggestionAndCondition;
428
429         public Builder() {
430         }
431
432         public Builder(DashboardData dashboardData) {
433             mCategories = dashboardData.mCategories;
434             mConditions = dashboardData.mConditions;
435             mSuggestions = dashboardData.mSuggestions;
436             mSuggestionMode = dashboardData.mSuggestionMode;
437             mExpandedCondition = dashboardData.mExpandedCondition;
438             mSuggestionConditionMode = dashboardData.mSuggestionConditionMode;
439             mCombineSuggestionAndCondition = dashboardData.mCombineSuggestionAndCondition;
440         }
441
442         public Builder setCategories(List<DashboardCategory> categories) {
443             this.mCategories = categories;
444             return this;
445         }
446
447         public Builder setConditions(List<Condition> conditions) {
448             this.mConditions = conditions;
449             return this;
450         }
451
452         public Builder setSuggestions(List<Tile> suggestions) {
453             this.mSuggestions = suggestions;
454             return this;
455         }
456
457         public Builder setSuggestionMode(int suggestionMode) {
458             this.mSuggestionMode = suggestionMode;
459             return this;
460         }
461
462         @Deprecated
463         public Builder setExpandedCondition(Condition expandedCondition) {
464             this.mExpandedCondition = expandedCondition;
465             return this;
466         }
467
468         public Builder setSuggestionConditionMode(@HeaderMode int mode) {
469             this.mSuggestionConditionMode = mode;
470             return this;
471         }
472
473         public Builder setCombineSuggestionAndCondition(boolean combine) {
474             this.mCombineSuggestionAndCondition = combine;
475             return this;
476         }
477
478         public DashboardData build() {
479             return new DashboardData(this);
480         }
481     }
482
483     /**
484      * A DiffCallback to calculate the difference between old and new Item
485      * List in DashboardData
486      */
487     public static class ItemsDataDiffCallback extends DiffUtil.Callback {
488         final private List<Item> mOldItems;
489         final private List<Item> mNewItems;
490
491         public ItemsDataDiffCallback(List<Item> oldItems, List<Item> newItems) {
492             mOldItems = oldItems;
493             mNewItems = newItems;
494         }
495
496         @Override
497         public int getOldListSize() {
498             return mOldItems.size();
499         }
500
501         @Override
502         public int getNewListSize() {
503             return mNewItems.size();
504         }
505
506         @Override
507         public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
508             return mOldItems.get(oldItemPosition).id == mNewItems.get(newItemPosition).id;
509         }
510
511         @Override
512         public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
513             return mOldItems.get(oldItemPosition).equals(mNewItems.get(newItemPosition));
514         }
515
516         // not needed in combined UI
517         @Deprecated
518         @Nullable
519         @Override
520         public Object getChangePayload(int oldItemPosition, int newItemPosition) {
521             if (mOldItems.get(oldItemPosition).type == Item.TYPE_CONDITION_CARD) {
522                 return "condition"; // return anything but null to mark the payload
523             }
524             return null;
525         }
526     }
527
528     /**
529      * An item contains the data needed in the DashboardData.
530      */
531     private static class Item {
532         // valid types in field type
533         private static final int TYPE_DASHBOARD_CATEGORY = R.layout.dashboard_category;
534         private static final int TYPE_DASHBOARD_TILE = R.layout.dashboard_tile;
535         @Deprecated
536         private static final int TYPE_SUGGESTION_HEADER = R.layout.suggestion_header;
537         @Deprecated
538         private static final int TYPE_SUGGESTION_TILE = R.layout.suggestion_tile;
539         private static final int TYPE_SUGGESTION_CONDITION_CONTAINER =
540             R.layout.suggestion_condition_container;
541         private static final int TYPE_SUGGESTION_CONDITION_HEADER =
542             R.layout.suggestion_condition_header;
543         @Deprecated
544         private static final int TYPE_CONDITION_CARD = R.layout.condition_card;
545         private static final int TYPE_SUGGESTION_CONDITION_FOOTER =
546                 R.layout.suggestion_condition_footer;
547         private static final int TYPE_DASHBOARD_SPACER = R.layout.dashboard_spacer;
548
549         @IntDef({TYPE_DASHBOARD_CATEGORY, TYPE_DASHBOARD_TILE, TYPE_SUGGESTION_HEADER,
550                 TYPE_SUGGESTION_TILE, TYPE_SUGGESTION_CONDITION_CONTAINER,
551                 TYPE_SUGGESTION_CONDITION_HEADER, TYPE_CONDITION_CARD,
552                 TYPE_SUGGESTION_CONDITION_FOOTER, TYPE_DASHBOARD_SPACER})
553         @Retention(RetentionPolicy.SOURCE)
554         public @interface ItemTypes{}
555
556         /**
557          * The main data object in item, usually is a {@link Tile}, {@link Condition} or
558          * {@link DashboardCategory} object. This object can also be null when the
559          * item is an divider line. Please refer to {@link #buildItemsData()} for
560          * detail usage of the Item.
561          */
562         public final Object entity;
563
564         /**
565          * The type of item, value inside is the layout id(e.g. R.layout.dashboard_tile)
566          */
567         public final @ItemTypes int type;
568
569         /**
570          * Id of this item, used in the {@link ItemsDataDiffCallback} to identify the same item.
571          */
572         public final int id;
573
574         /**
575          * To store whether the condition is expanded, useless when {@link #type} is not
576          * {@link #TYPE_CONDITION_CARD}
577          */
578         @Deprecated
579         public final boolean conditionExpanded;
580
581         @Deprecated
582         public Item(Object entity, @ItemTypes int type, int id, boolean conditionExpanded) {
583             this.entity = entity;
584             this.type = type;
585             this.id = id;
586             this.conditionExpanded = conditionExpanded;
587         }
588
589         public Item(Object entity, @ItemTypes int type, int id) {
590             this(entity, type, id, false);
591         }
592
593         /**
594          * Override it to make comparision in the {@link ItemsDataDiffCallback}
595          * @param obj object to compared with
596          * @return true if the same object or has equal value.
597          */
598         @Override
599         public boolean equals(Object obj) {
600             if (this == obj) {
601                 return true;
602             }
603
604             if (!(obj instanceof Item)) {
605                 return false;
606             }
607
608             final Item targetItem = (Item) obj;
609             if (type != targetItem.type || id != targetItem.id) {
610                 return false;
611             }
612
613             switch (type) {
614                 case TYPE_DASHBOARD_CATEGORY:
615                     // Only check title for dashboard category
616                     return TextUtils.equals(((DashboardCategory) entity).title,
617                             ((DashboardCategory) targetItem.entity).title);
618                 case TYPE_DASHBOARD_TILE:
619                     final Tile localTile = (Tile) entity;
620                     final Tile targetTile = (Tile) targetItem.entity;
621
622                     // Only check title and summary for dashboard tile
623                     return TextUtils.equals(localTile.title, targetTile.title)
624                             && TextUtils.equals(localTile.summary, targetTile.summary);
625                 case TYPE_CONDITION_CARD:
626                     // First check conditionExpanded for quick return
627                     if (conditionExpanded != targetItem.conditionExpanded) {
628                         return false;
629                     }
630                     // After that, go to default to do final check
631                 default:
632                     return entity == null ? targetItem.entity == null
633                             : entity.equals(targetItem.entity);
634             }
635         }
636     }
637
638     /**
639      * This class contains the data needed to build the header. The data can also be
640      * used to check the diff in DiffUtil.Callback
641      */
642     public static class SuggestionHeaderData {
643         public final boolean hasMoreSuggestions;
644         public final int suggestionSize;
645         public final int undisplayedSuggestionCount;
646
647         public SuggestionHeaderData(boolean moreSuggestions, int suggestionSize, int
648                 undisplayedSuggestionCount) {
649             this.hasMoreSuggestions = moreSuggestions;
650             this.suggestionSize = suggestionSize;
651             this.undisplayedSuggestionCount = undisplayedSuggestionCount;
652         }
653
654         public SuggestionHeaderData() {
655             hasMoreSuggestions = false;
656             suggestionSize = 0;
657             undisplayedSuggestionCount = 0;
658         }
659
660         @Override
661         public boolean equals(Object obj) {
662             if (this == obj) {
663                 return true;
664             }
665
666             if (!(obj instanceof SuggestionHeaderData)) {
667                 return false;
668             }
669
670             SuggestionHeaderData targetData = (SuggestionHeaderData) obj;
671
672             return hasMoreSuggestions == targetData.hasMoreSuggestions
673                     && suggestionSize == targetData.suggestionSize
674                     && undisplayedSuggestionCount == targetData.undisplayedSuggestionCount;
675         }
676     }
677
678     /**
679      * This class contains the data needed to build the suggestion/condition header. The data can
680      * also be used to check the diff in DiffUtil.Callback
681      */
682     public static class SuggestionConditionHeaderData {
683         public final List<Icon> conditionIcons;
684         public final CharSequence title;
685         public final int conditionCount;
686         public final int hiddenSuggestionCount;
687
688         public SuggestionConditionHeaderData(List<Condition> conditions,
689                 int hiddenSuggestionCount) {
690             conditionCount = sizeOf(conditions);
691             this.hiddenSuggestionCount = hiddenSuggestionCount;
692             title = conditionCount > 0 ? conditions.get(0).getTitle() : null;
693             conditionIcons = new ArrayList<Icon>();
694             for (int i = 0; conditions != null && i < conditions.size(); i++) {
695                 final Condition condition = conditions.get(i);
696                 conditionIcons.add(condition.getIcon());
697             }
698         }
699     }
700
701 }