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.dialer.searchfragment.list; 18 19 import android.content.Context; 20 import android.support.annotation.Nullable; 21 import android.support.annotation.VisibleForTesting; 22 import android.support.v7.widget.RecyclerView; 23 import android.support.v7.widget.RecyclerView.ViewHolder; 24 import android.text.TextUtils; 25 import android.view.LayoutInflater; 26 import android.view.View; 27 import android.view.View.OnClickListener; 28 import android.view.ViewGroup; 29 import com.android.dialer.callintent.CallInitiationType; 30 import com.android.dialer.common.Assert; 31 import com.android.dialer.searchfragment.common.RowClickListener; 32 import com.android.dialer.searchfragment.common.SearchCursor; 33 import com.android.dialer.searchfragment.cp2.SearchContactViewHolder; 34 import com.android.dialer.searchfragment.directories.DirectoryContactViewHolder; 35 import com.android.dialer.searchfragment.list.SearchCursorManager.RowType; 36 import com.android.dialer.searchfragment.nearbyplaces.NearbyPlaceViewHolder; 37 import java.util.List; 38 39 /** RecyclerView adapter for {@link NewSearchFragment}. */ 40 @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) 41 public final class SearchAdapter extends RecyclerView.Adapter<ViewHolder> { 42 43 private final SearchCursorManager searchCursorManager; 44 private final Context context; 45 46 private boolean showZeroSuggest; 47 private String query; 48 // Raw query number from dialpad, which may contain special character such as "+". This is used 49 // for actions to add contact or send sms. 50 private String rawNumber; 51 private CallInitiationType.Type callInitiationType; 52 private OnClickListener allowClickListener; 53 private OnClickListener dismissClickListener; 54 private RowClickListener rowClickListener; 55 56 @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) 57 public SearchAdapter( 58 Context context, SearchCursorManager searchCursorManager, RowClickListener rowClickListener) { 59 this.context = context; 60 this.searchCursorManager = searchCursorManager; 61 this.rowClickListener = rowClickListener; 62 } 63 64 @Override 65 public ViewHolder onCreateViewHolder(ViewGroup root, @RowType int rowType) { 66 switch (rowType) { 67 case RowType.CONTACT_ROW: 68 return new SearchContactViewHolder( 69 LayoutInflater.from(context).inflate(R.layout.search_contact_row, root, false), 70 rowClickListener); 71 case RowType.NEARBY_PLACES_ROW: 72 return new NearbyPlaceViewHolder( 73 LayoutInflater.from(context).inflate(R.layout.search_contact_row, root, false)); 74 case RowType.CONTACT_HEADER: 75 case RowType.DIRECTORY_HEADER: 76 case RowType.NEARBY_PLACES_HEADER: 77 return new HeaderViewHolder( 78 LayoutInflater.from(context).inflate(R.layout.header_layout, root, false)); 79 case RowType.DIRECTORY_ROW: 80 return new DirectoryContactViewHolder( 81 LayoutInflater.from(context).inflate(R.layout.search_contact_row, root, false)); 82 case RowType.SEARCH_ACTION: 83 return new SearchActionViewHolder( 84 LayoutInflater.from(context).inflate(R.layout.search_action_layout, root, false)); 85 case RowType.LOCATION_REQUEST: 86 return new LocationPermissionViewHolder( 87 LayoutInflater.from(context).inflate(R.layout.location_permission_row, root, false), 88 allowClickListener, 89 dismissClickListener); 90 case RowType.INVALID: 91 default: 92 throw Assert.createIllegalStateFailException("Invalid RowType: " + rowType); 93 } 94 } 95 96 @Override 97 public @RowType int getItemViewType(int position) { 98 return searchCursorManager.getRowType(position); 99 } 100 101 @Override 102 public void onBindViewHolder(ViewHolder holder, int position) { 103 if (holder instanceof SearchContactViewHolder) { 104 ((SearchContactViewHolder) holder).bind(searchCursorManager.getCursor(position), query); 105 } else if (holder instanceof NearbyPlaceViewHolder) { 106 ((NearbyPlaceViewHolder) holder).bind(searchCursorManager.getCursor(position), query); 107 } else if (holder instanceof DirectoryContactViewHolder) { 108 ((DirectoryContactViewHolder) holder).bind(searchCursorManager.getCursor(position), query); 109 } else if (holder instanceof HeaderViewHolder) { 110 String header = 111 searchCursorManager.getCursor(position).getString(SearchCursor.HEADER_TEXT_POSITION); 112 ((HeaderViewHolder) holder).setHeader(header); 113 } else if (holder instanceof SearchActionViewHolder) { 114 ((SearchActionViewHolder) holder) 115 .setAction( 116 searchCursorManager.getSearchAction(position), 117 position, 118 TextUtils.isEmpty(rawNumber) ? query : rawNumber, 119 callInitiationType); 120 } else if (holder instanceof LocationPermissionViewHolder) { 121 // No-op 122 } else { 123 throw Assert.createIllegalStateFailException("Invalid ViewHolder: " + holder); 124 } 125 } 126 127 public void setContactsCursor(SearchCursor cursor) { 128 if (searchCursorManager.setContactsCursor(cursor)) { 129 // Since this is a new contacts cursor, we need to reapply the filter. 130 searchCursorManager.setQuery(query); 131 notifyDataSetChanged(); 132 } 133 } 134 135 void clear() { 136 searchCursorManager.clear(); 137 } 138 139 @Override 140 public int getItemCount() { 141 if (TextUtils.isEmpty(query) && !showZeroSuggest) { 142 return 0; 143 } 144 return searchCursorManager.getCount(); 145 } 146 147 /** 148 * @param visible If true and query is empty, the adapter won't show any list elements. 149 * @see #setQuery(String, String, CallInitiationType.Type) 150 * @see #getItemCount() 151 */ 152 public void setZeroSuggestVisible(boolean visible) { 153 showZeroSuggest = visible; 154 } 155 156 public void setQuery(String query, @Nullable String rawNumber, CallInitiationType.Type type) { 157 this.query = query; 158 this.rawNumber = rawNumber; 159 this.callInitiationType = type; 160 if (searchCursorManager.setQuery(query)) { 161 notifyDataSetChanged(); 162 } 163 } 164 165 /** Sets the actions to be shown at the bottom of the search results. */ 166 void setSearchActions(List<Integer> actions) { 167 if (searchCursorManager.setSearchActions(actions)) { 168 notifyDataSetChanged(); 169 } 170 } 171 172 public void setNearbyPlacesCursor(SearchCursor nearbyPlacesCursor) { 173 if (searchCursorManager.setNearbyPlacesCursor(nearbyPlacesCursor)) { 174 notifyDataSetChanged(); 175 } 176 } 177 178 /** 179 * Updates the adapter to show the location request row element. If the element was previously 180 * hidden, the adapter will call {@link #notifyDataSetChanged()}. 181 */ 182 public void showLocationPermissionRequest( 183 OnClickListener allowClickListener, OnClickListener dismissClickListener) { 184 this.allowClickListener = Assert.isNotNull(allowClickListener); 185 this.dismissClickListener = Assert.isNotNull(dismissClickListener); 186 if (searchCursorManager.showLocationPermissionRequest(true)) { 187 notifyItemInserted(0); 188 } 189 } 190 191 /** 192 * Updates the adapter to hide the location request row element. If the element was previously 193 * visible, the adapter will call {@link #notifyDataSetChanged()}. 194 */ 195 void hideLocationPermissionRequest() { 196 allowClickListener = null; 197 dismissClickListener = null; 198 if (searchCursorManager.showLocationPermissionRequest(false)) { 199 notifyItemRemoved(0); 200 } 201 } 202 203 void setDirectoryContactsCursor(SearchCursor directoryContactsCursor) { 204 if (searchCursorManager.setCorpDirectoryCursor(directoryContactsCursor)) { 205 notifyDataSetChanged(); 206 } 207 } 208 209 /** Viewholder for R.layout.location_permission_row that requests the location permission. */ 210 private static class LocationPermissionViewHolder extends RecyclerView.ViewHolder { 211 212 LocationPermissionViewHolder( 213 View itemView, OnClickListener allowClickListener, OnClickListener dismissClickListener) { 214 super(itemView); 215 Assert.isNotNull(allowClickListener); 216 Assert.isNotNull(dismissClickListener); 217 itemView 218 .findViewById( 219 com.android.dialer.searchfragment.nearbyplaces.R.id.location_permission_allow) 220 .setOnClickListener(allowClickListener); 221 itemView 222 .findViewById( 223 com.android.dialer.searchfragment.nearbyplaces.R.id.location_permission_dismiss) 224 .setOnClickListener(dismissClickListener); 225 } 226 } 227 } 228