• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.car.carlauncher;
17 
18 import android.app.Activity;
19 import android.support.annotation.NonNull;
20 import android.support.v7.widget.RecyclerView;
21 import android.view.View;
22 import android.view.ViewGroup;
23 import android.widget.Filter;
24 import android.widget.Filterable;
25 import android.widget.ImageView;
26 import android.widget.TextView;
27 
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.List;
31 import java.util.regex.Pattern;
32 
33 /**
34  * The search result adapter that binds the filtered result to a list view.
35  */
36 
37 final class SearchResultAdapter
38         extends RecyclerView.Adapter<SearchResultAdapter.ViewHolder> implements Filterable {
39     private final Activity mContext;
40     private List<AppMetaData> mAllApps;
41     private List<AppMetaData> mSearchResults;
42     private AppSearchFilter filter;
43     private boolean mIsDistractionOptimizationRequired;
44 
SearchResultAdapter(Activity context)45     public SearchResultAdapter(Activity context) {
46         mContext = context;
47         mSearchResults = null;
48     }
49 
setIsDistractionOptimizationRequired(boolean isDistractionOptimizationRequired)50     public void setIsDistractionOptimizationRequired(boolean isDistractionOptimizationRequired) {
51         mIsDistractionOptimizationRequired = isDistractionOptimizationRequired;
52         notifyDataSetChanged();
53     }
54 
setAllApps(List<AppMetaData> apps)55     public void setAllApps(List<AppMetaData> apps) {
56         mAllApps = apps;
57         notifyDataSetChanged();
58     }
59 
60     public static class ViewHolder extends RecyclerView.ViewHolder {
61         private View mAppItem;
62         private ImageView mAppIconView;
63         private TextView mAppNameView;
64 
ViewHolder(View view)65         public ViewHolder(View view) {
66             super(view);
67             mAppItem = view.findViewById(R.id.app_item);
68             mAppIconView = mAppItem.findViewById(R.id.app_icon);
69             mAppNameView = mAppItem.findViewById(R.id.app_name);
70         }
71 
bind(AppMetaData app, View.OnClickListener listener)72         public void bind(AppMetaData app, View.OnClickListener listener) {
73             if (app == null) {
74                 // Empty out the view
75                 mAppIconView.setImageDrawable(null);
76                 mAppNameView.setText(null);
77                 mAppItem.setClickable(false);
78                 mAppItem.setOnClickListener(null);
79             } else {
80                 mAppIconView.setImageDrawable(app.getIcon());
81                 mAppNameView.setText(app.getDisplayName());
82                 mAppItem.setClickable(true);
83                 mAppItem.setOnClickListener(listener);
84             }
85         }
86     }
87 
88     /**
89      * This filter does a simple case insensitive text match based on the application
90      * display name. e.g. "gm" -> "gmail".
91      * It only matches apps starting with the search query. It does not do sub-string matching.
92      */
93     private class AppSearchFilter extends Filter {
94 
95         @Override
performFiltering(CharSequence constraint)96         protected FilterResults performFiltering(CharSequence constraint) {
97             FilterResults results = new FilterResults();
98             if (constraint != null && constraint.length() > 0) {
99                 List filterList = new ArrayList();
100                 for (int i = 0; i < mAllApps.size(); i++) {
101                     AppMetaData app = mAllApps.get(i);
102                     if (shouldShowApp(
103                             app.getDisplayName(),
104                             constraint.toString(),
105                             app.getIsDistractionOptimized())) {
106                         filterList.add(app);
107                     }
108                 }
109                 Collections.sort(filterList, AppLauncherUtils.ALPHABETICAL_COMPARATOR);
110                 results.count = filterList.size();
111                 results.values = filterList;
112             } else {
113                 results.count = 0;
114                 results.values = null;
115             }
116 
117             return results;
118         }
119 
shouldShowApp( String displayName, String constraint, boolean isDistractionOptimized)120         private boolean shouldShowApp(
121                 String displayName, String constraint, boolean isDistractionOptimized) {
122             if (!mIsDistractionOptimizationRequired
123                     || (mIsDistractionOptimizationRequired && isDistractionOptimized)) {
124                 Pattern pattern = Pattern.compile(
125                         "^" + constraint + ".*$", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
126                 return pattern.matcher(displayName).matches();
127             }
128             return false;
129         }
130 
131         @Override
publishResults(CharSequence constraint, FilterResults results)132         protected void publishResults(CharSequence constraint, FilterResults results) {
133             mSearchResults = (ArrayList) results.values;
134             notifyDataSetChanged();
135         }
136     }
137 
138     @Override
getFilter()139     public Filter getFilter() {
140         if (filter == null) {
141             filter = new AppSearchFilter();
142         }
143         return filter;
144     }
145 
146     @NonNull
147     @Override
onCreateViewHolder( @onNull ViewGroup parent, int viewType)148     public SearchResultAdapter.ViewHolder onCreateViewHolder(
149             @NonNull ViewGroup parent, int viewType) {
150         View view = mContext.getLayoutInflater().inflate(R.layout.app_search_result_item, null);
151         return new ViewHolder(view);
152     }
153 
154     @Override
onBindViewHolder(@onNull SearchResultAdapter.ViewHolder holder, int position)155     public void onBindViewHolder(@NonNull SearchResultAdapter.ViewHolder holder, int position) {
156         // Get the data item for this position
157         AppMetaData app = mSearchResults.get(position);
158         holder.bind(app, v -> AppLauncherUtils.launchApp(mContext, app));
159     }
160 
161     @Override
getItemId(int position)162     public long getItemId(int position) {
163         return position;
164     }
165 
166     @Override
getItemCount()167     public int getItemCount() {
168         if (mSearchResults != null) {
169             return mSearchResults.size();
170         } else {
171             return 0;
172         }
173     }
174 
clearResults()175     void clearResults() {
176         mSearchResults.clear();
177         notifyDataSetChanged();
178     }
179 }
180