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 
17 package com.android.settings.search2;
18 
19 import android.content.Context;
20 import android.support.annotation.MainThread;
21 import android.support.annotation.VisibleForTesting;
22 import android.support.v7.util.DiffUtil;
23 import android.support.v7.widget.RecyclerView;
24 import android.util.ArrayMap;
25 import android.view.LayoutInflater;
26 import android.view.View;
27 import android.view.ViewGroup;
28 
29 import com.android.settings.R;
30 import com.android.settings.search2.ResultPayload.PayloadType;
31 
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.Map;
35 
36 import static com.android.settings.search2.SearchResult.BOTTOM_RANK;
37 import static com.android.settings.search2.SearchResult.TOP_RANK;
38 
39 public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder> {
40 
41     private final SearchFragment mFragment;
42 
43     private List<SearchResult> mSearchResults;
44     private Map<String, List<? extends SearchResult>> mResultsMap;
45 
SearchResultsAdapter(SearchFragment fragment)46     public SearchResultsAdapter(SearchFragment fragment) {
47         mFragment = fragment;
48         mSearchResults = new ArrayList<>();
49         mResultsMap = new ArrayMap<>();
50 
51         setHasStableIds(true);
52     }
53 
54     @Override
onCreateViewHolder(ViewGroup parent, int viewType)55     public SearchViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
56         final Context context = parent.getContext();
57         final LayoutInflater inflater = LayoutInflater.from(context);
58         final View view;
59         switch (viewType) {
60             case PayloadType.INTENT:
61                 view = inflater.inflate(R.layout.search_intent_item, parent, false);
62                 return new IntentSearchViewHolder(view);
63             case PayloadType.INLINE_SWITCH:
64                 view = inflater.inflate(R.layout.search_inline_switch_item, parent, false);
65                 return new InlineSwitchViewHolder(view, context);
66             case PayloadType.SAVED_QUERY:
67                 view = inflater.inflate(R.layout.search_saved_query_item, parent, false);
68                 return new SavedQueryViewHolder(view);
69             default:
70                 return null;
71         }
72     }
73 
74     @Override
onBindViewHolder(SearchViewHolder holder, int position)75     public void onBindViewHolder(SearchViewHolder holder, int position) {
76         holder.onBind(mFragment, mSearchResults.get(position));
77     }
78 
79     @Override
getItemId(int position)80     public long getItemId(int position) {
81         return mSearchResults.get(position).stableId;
82     }
83 
84     @Override
getItemViewType(int position)85     public int getItemViewType(int position) {
86         return mSearchResults.get(position).viewType;
87     }
88 
89     @Override
getItemCount()90     public int getItemCount() {
91         return mSearchResults.size();
92     }
93 
94     /**
95      * Store the results from each of the loaders to be merged when all loaders are finished.
96      *
97      * @param results         the results from the loader.
98      * @param loaderClassName class name of the loader.
99      */
100     @MainThread
addSearchResults(List<? extends SearchResult> results, String loaderClassName)101     public void addSearchResults(List<? extends SearchResult> results, String loaderClassName) {
102         if (results == null) {
103             return;
104         }
105         mResultsMap.put(loaderClassName, results);
106     }
107 
108     /**
109      * Displays recent searched queries.
110      *
111      * @return The number of saved queries to display
112      */
displaySavedQuery(List<? extends SearchResult> data)113     public int displaySavedQuery(List<? extends SearchResult> data) {
114         clearResults();
115         mSearchResults.addAll(data);
116         notifyDataSetChanged();
117         return mSearchResults.size();
118     }
119 
120     /**
121      * Merge the results from each of the loaders into one list for the adapter.
122      * Prioritizes results from the local database over installed apps.
123      *
124      * @return Number of matched results
125      */
displaySearchResults()126     public int displaySearchResults() {
127         final List<? extends SearchResult> databaseResults = mResultsMap
128                 .get(DatabaseResultLoader.class.getName());
129         final List<? extends SearchResult> installedAppResults = mResultsMap
130                 .get(InstalledAppResultLoader.class.getName());
131         final int dbSize = (databaseResults != null) ? databaseResults.size() : 0;
132         final int appSize = (installedAppResults != null) ? installedAppResults.size() : 0;
133         final List<SearchResult> newResults = new ArrayList<>(dbSize + appSize);
134 
135         int dbIndex = 0;
136         int appIndex = 0;
137         int rank = TOP_RANK;
138 
139         while (rank <= BOTTOM_RANK) {
140             while ((dbIndex < dbSize) && (databaseResults.get(dbIndex).rank == rank)) {
141                 newResults.add(databaseResults.get(dbIndex++));
142             }
143             while ((appIndex < appSize) && (installedAppResults.get(appIndex).rank == rank)) {
144                 newResults.add(installedAppResults.get(appIndex++));
145             }
146             rank++;
147         }
148 
149         while (dbIndex < dbSize) {
150             newResults.add(databaseResults.get(dbIndex++));
151         }
152         while (appIndex < appSize) {
153             newResults.add(installedAppResults.get(appIndex++));
154         }
155 
156         final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(
157                 new SearchResultDiffCallback(mSearchResults, newResults), false /* detectMoves */);
158         mSearchResults = newResults;
159         diffResult.dispatchUpdatesTo(this);
160 
161         return mSearchResults.size();
162     }
163 
clearResults()164     public void clearResults() {
165         mSearchResults.clear();
166         mResultsMap.clear();
167         notifyDataSetChanged();
168     }
169 
170     @VisibleForTesting
getSearchResults()171     public List<SearchResult> getSearchResults() {
172         return mSearchResults;
173     }
174 }
175