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