• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.launcher3.widget;
18 
19 import android.support.v7.widget.RecyclerView;
20 import android.util.Log;
21 
22 import com.android.launcher3.IconCache;
23 import com.android.launcher3.model.PackageItemInfo;
24 import com.android.launcher3.widget.WidgetsListAdapter.WidgetListRowEntryComparator;
25 
26 import java.util.ArrayList;
27 import java.util.Iterator;
28 
29 /**
30  * Do diff on widget's tray list items and call the {@link RecyclerView.Adapter}
31  * methods accordingly.
32  */
33 public class WidgetsDiffReporter {
34     private static final boolean DEBUG = false;
35     private static final String TAG = "WidgetsDiffReporter";
36 
37     private final IconCache mIconCache;
38     private final RecyclerView.Adapter mListener;
39 
WidgetsDiffReporter(IconCache iconCache, RecyclerView.Adapter listener)40     public WidgetsDiffReporter(IconCache iconCache, RecyclerView.Adapter listener) {
41         mIconCache = iconCache;
42         mListener = listener;
43     }
44 
process(ArrayList<WidgetListRowEntry> currentEntries, ArrayList<WidgetListRowEntry> newEntries, WidgetListRowEntryComparator comparator)45     public void process(ArrayList<WidgetListRowEntry> currentEntries,
46             ArrayList<WidgetListRowEntry> newEntries, WidgetListRowEntryComparator comparator) {
47         if (DEBUG) {
48             Log.d(TAG, "process oldEntries#=" + currentEntries.size()
49                     + " newEntries#=" + newEntries.size());
50         }
51         // Early exit if either of the list is empty
52         if (currentEntries.isEmpty() || newEntries.isEmpty()) {
53             // Skip if both list are empty.
54             // On rotation, we open the widget tray with empty. Then try to fetch the list again
55             // when the animation completes (which still gives empty). And we get the final result
56             // when the bind actually completes.
57             if (currentEntries.size() != newEntries.size()) {
58                 currentEntries.clear();
59                 currentEntries.addAll(newEntries);
60                 mListener.notifyDataSetChanged();
61             }
62             return;
63         }
64         ArrayList<WidgetListRowEntry> orgEntries =
65                 (ArrayList<WidgetListRowEntry>) currentEntries.clone();
66         Iterator<WidgetListRowEntry> orgIter = orgEntries.iterator();
67         Iterator<WidgetListRowEntry> newIter = newEntries.iterator();
68 
69         WidgetListRowEntry orgRowEntry = orgIter.next();
70         WidgetListRowEntry newRowEntry = newIter.next();
71 
72         do {
73             int diff = comparePackageName(orgRowEntry, newRowEntry, comparator);
74             if (DEBUG) {
75                 Log.d(TAG, String.format("diff=%d orgRowEntry (%s) newRowEntry (%s)",
76                         diff, orgRowEntry != null? orgRowEntry.toString() : null,
77                         newRowEntry != null? newRowEntry.toString() : null));
78             }
79             int index = -1;
80             if (diff < 0) {
81                 index = currentEntries.indexOf(orgRowEntry);
82                 mListener.notifyItemRemoved(index);
83                 if (DEBUG) {
84                     Log.d(TAG, String.format("notifyItemRemoved called (%d)%s", index,
85                             orgRowEntry.titleSectionName));
86                 }
87                 currentEntries.remove(index);
88                 orgRowEntry = orgIter.hasNext() ? orgIter.next() : null;
89             } else if (diff > 0) {
90                 index = orgRowEntry != null? currentEntries.indexOf(orgRowEntry):
91                         currentEntries.size();
92                 currentEntries.add(index, newRowEntry);
93                 if (DEBUG) {
94                     Log.d(TAG, String.format("notifyItemInserted called (%d)%s", index,
95                             newRowEntry.titleSectionName));
96                 }
97                 newRowEntry = newIter.hasNext() ? newIter.next() : null;
98                 mListener.notifyItemInserted(index);
99 
100             } else {
101                 // same package name but,
102                 // did the icon, title, etc, change?
103                 // or did the widget size and desc, span, etc change?
104                 if (!isSamePackageItemInfo(orgRowEntry.pkgItem, newRowEntry.pkgItem) ||
105                         !orgRowEntry.widgets.equals(newRowEntry.widgets)) {
106                     index = currentEntries.indexOf(orgRowEntry);
107                     currentEntries.set(index, newRowEntry);
108                     mListener.notifyItemChanged(index);
109                     if (DEBUG) {
110                         Log.d(TAG, String.format("notifyItemChanged called (%d)%s", index,
111                                 newRowEntry.titleSectionName));
112                     }
113                 }
114                 orgRowEntry = orgIter.hasNext() ? orgIter.next() : null;
115                 newRowEntry = newIter.hasNext() ? newIter.next() : null;
116             }
117         } while(orgRowEntry != null || newRowEntry != null);
118     }
119 
120     /**
121      * Compare package name using the same comparator as in {@link WidgetsListAdapter}.
122      * Also handle null row pointers.
123      */
comparePackageName(WidgetListRowEntry curRow, WidgetListRowEntry newRow, WidgetListRowEntryComparator comparator)124     private int comparePackageName(WidgetListRowEntry curRow, WidgetListRowEntry newRow,
125             WidgetListRowEntryComparator comparator) {
126         if (curRow == null && newRow == null) {
127             throw new IllegalStateException("Cannot compare PackageItemInfo if both rows are null.");
128         }
129 
130         if (curRow == null && newRow != null) {
131             return 1; // new row needs to be inserted
132         } else if (curRow != null && newRow == null) {
133             return -1; // old row needs to be deleted
134         }
135         return comparator.compare(curRow, newRow);
136     }
137 
isSamePackageItemInfo(PackageItemInfo curInfo, PackageItemInfo newInfo)138     private boolean isSamePackageItemInfo(PackageItemInfo curInfo, PackageItemInfo newInfo) {
139         return curInfo.iconBitmap.equals(newInfo.iconBitmap) &&
140                 !mIconCache.isDefaultIcon(curInfo.iconBitmap, curInfo.user);
141     }
142 }
143