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.common.Assert; 30 import com.android.dialer.searchfragment.common.RowClickListener; 31 import com.android.dialer.searchfragment.common.SearchCursor; 32 import com.android.dialer.searchfragment.cp2.SearchContactViewHolder; 33 import com.android.dialer.searchfragment.directories.DirectoryContactViewHolder; 34 import com.android.dialer.searchfragment.list.SearchCursorManager.RowType; 35 import com.android.dialer.searchfragment.nearbyplaces.NearbyPlaceViewHolder; 36 import java.util.List; 37 38 /** RecyclerView adapter for {@link NewSearchFragment}. */ 39 @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) 40 public final class SearchAdapter extends RecyclerView.Adapter<ViewHolder> { 41 42 private final SearchCursorManager searchCursorManager; 43 private final Context context; 44 45 private String query; 46 // Raw query number from dialpad, which may contain special character such as "+". This is used 47 // for actions to add contact or send sms. 48 private String rawNumber; 49 private OnClickListener allowClickListener; 50 private OnClickListener dismissClickListener; 51 private RowClickListener rowClickListener; 52 53 @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) SearchAdapter( Context context, SearchCursorManager searchCursorManager, RowClickListener rowClickListener)54 public SearchAdapter( 55 Context context, SearchCursorManager searchCursorManager, RowClickListener rowClickListener) { 56 this.context = context; 57 this.searchCursorManager = searchCursorManager; 58 this.rowClickListener = rowClickListener; 59 } 60 61 @Override onCreateViewHolder(ViewGroup root, @RowType int rowType)62 public ViewHolder onCreateViewHolder(ViewGroup root, @RowType int rowType) { 63 switch (rowType) { 64 case RowType.CONTACT_ROW: 65 return new SearchContactViewHolder( 66 LayoutInflater.from(context).inflate(R.layout.search_contact_row, root, false), 67 rowClickListener); 68 case RowType.NEARBY_PLACES_ROW: 69 return new NearbyPlaceViewHolder( 70 LayoutInflater.from(context).inflate(R.layout.search_contact_row, root, false), 71 rowClickListener); 72 case RowType.CONTACT_HEADER: 73 case RowType.DIRECTORY_HEADER: 74 case RowType.NEARBY_PLACES_HEADER: 75 return new HeaderViewHolder( 76 LayoutInflater.from(context).inflate(R.layout.header_layout, root, false)); 77 case RowType.DIRECTORY_ROW: 78 return new DirectoryContactViewHolder( 79 LayoutInflater.from(context).inflate(R.layout.search_contact_row, root, false), 80 rowClickListener); 81 case RowType.SEARCH_ACTION: 82 return new SearchActionViewHolder( 83 LayoutInflater.from(context).inflate(R.layout.search_action_layout, root, false), 84 rowClickListener); 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 getItemViewType(int position)97 public @RowType int getItemViewType(int position) { 98 return searchCursorManager.getRowType(position); 99 } 100 101 @Override onBindViewHolder(ViewHolder holder, int position)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 } else if (holder instanceof LocationPermissionViewHolder) { 120 // No-op 121 } else { 122 throw Assert.createIllegalStateFailException("Invalid ViewHolder: " + holder); 123 } 124 } 125 setContactsCursor(SearchCursor cursor)126 public void setContactsCursor(SearchCursor cursor) { 127 if (searchCursorManager.setContactsCursor(cursor)) { 128 // Since this is a new contacts cursor, we need to reapply the filter. 129 searchCursorManager.setQuery(query); 130 notifyDataSetChanged(); 131 } 132 } 133 clear()134 void clear() { 135 searchCursorManager.clear(); 136 } 137 138 @Override getItemCount()139 public int getItemCount() { 140 return searchCursorManager.getCount(); 141 } 142 setQuery(String query, @Nullable String rawNumber)143 public void setQuery(String query, @Nullable String rawNumber) { 144 this.query = query; 145 this.rawNumber = rawNumber; 146 if (searchCursorManager.setQuery(query)) { 147 notifyDataSetChanged(); 148 } 149 } 150 151 /** Sets the actions to be shown at the bottom of the search results. */ setSearchActions(List<Integer> actions)152 void setSearchActions(List<Integer> actions) { 153 if (searchCursorManager.setSearchActions(actions)) { 154 notifyDataSetChanged(); 155 } 156 } 157 setNearbyPlacesCursor(SearchCursor nearbyPlacesCursor)158 public void setNearbyPlacesCursor(SearchCursor nearbyPlacesCursor) { 159 if (searchCursorManager.setNearbyPlacesCursor(nearbyPlacesCursor)) { 160 notifyDataSetChanged(); 161 } 162 } 163 164 /** 165 * Updates the adapter to show the location request row element. If the element was previously 166 * hidden, the adapter will call {@link #notifyDataSetChanged()}. 167 */ showLocationPermissionRequest( OnClickListener allowClickListener, OnClickListener dismissClickListener)168 public void showLocationPermissionRequest( 169 OnClickListener allowClickListener, OnClickListener dismissClickListener) { 170 this.allowClickListener = Assert.isNotNull(allowClickListener); 171 this.dismissClickListener = Assert.isNotNull(dismissClickListener); 172 if (searchCursorManager.showLocationPermissionRequest(true)) { 173 notifyItemInserted(0); 174 } 175 } 176 177 /** 178 * Updates the adapter to hide the location request row element. If the element was previously 179 * visible, the adapter will call {@link #notifyDataSetChanged()}. 180 */ hideLocationPermissionRequest()181 void hideLocationPermissionRequest() { 182 allowClickListener = null; 183 dismissClickListener = null; 184 if (searchCursorManager.showLocationPermissionRequest(false)) { 185 notifyItemRemoved(0); 186 } 187 } 188 setDirectoryContactsCursor(SearchCursor directoryContactsCursor)189 void setDirectoryContactsCursor(SearchCursor directoryContactsCursor) { 190 if (searchCursorManager.setCorpDirectoryCursor(directoryContactsCursor)) { 191 notifyDataSetChanged(); 192 } 193 } 194 195 /** Viewholder for R.layout.location_permission_row that requests the location permission. */ 196 private static class LocationPermissionViewHolder extends RecyclerView.ViewHolder { 197 LocationPermissionViewHolder( View itemView, OnClickListener allowClickListener, OnClickListener dismissClickListener)198 LocationPermissionViewHolder( 199 View itemView, OnClickListener allowClickListener, OnClickListener dismissClickListener) { 200 super(itemView); 201 Assert.isNotNull(allowClickListener); 202 Assert.isNotNull(dismissClickListener); 203 itemView 204 .findViewById( 205 com.android.dialer.searchfragment.nearbyplaces.R.id.location_permission_allow) 206 .setOnClickListener(allowClickListener); 207 itemView 208 .findViewById( 209 com.android.dialer.searchfragment.nearbyplaces.R.id.location_permission_dismiss) 210 .setOnClickListener(dismissClickListener); 211 } 212 } 213 } 214