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.cp2; 18 19 import android.content.Context; 20 import android.content.res.Resources; 21 import android.database.Cursor; 22 import android.net.Uri; 23 import android.provider.ContactsContract.CommonDataKinds.Phone; 24 import android.provider.ContactsContract.Contacts; 25 import android.support.annotation.IntDef; 26 import android.support.v7.widget.RecyclerView.ViewHolder; 27 import android.text.TextUtils; 28 import android.view.View; 29 import android.view.View.OnClickListener; 30 import android.widget.ImageView; 31 import android.widget.QuickContactBadge; 32 import android.widget.TextView; 33 import com.android.contacts.common.ContactPhotoManager; 34 import com.android.contacts.common.lettertiles.LetterTileDrawable; 35 import com.android.dialer.callintent.CallInitiationType.Type; 36 import com.android.dialer.callintent.CallIntentBuilder; 37 import com.android.dialer.common.Assert; 38 import com.android.dialer.searchfragment.common.Projections; 39 import com.android.dialer.searchfragment.common.QueryBoldingUtil; 40 import com.android.dialer.searchfragment.common.R; 41 import com.android.dialer.telecom.TelecomUtil; 42 import java.lang.annotation.Retention; 43 import java.lang.annotation.RetentionPolicy; 44 45 /** ViewHolder for a contact row. */ 46 public final class SearchContactViewHolder extends ViewHolder implements OnClickListener { 47 48 /** IntDef for the different types of actions that can be shown. */ 49 @Retention(RetentionPolicy.SOURCE) 50 @IntDef({CallToAction.NONE, CallToAction.VIDEO_CALL, CallToAction.SHARE_AND_CALL}) 51 @interface CallToAction { 52 int NONE = 0; 53 int VIDEO_CALL = 1; 54 int SHARE_AND_CALL = 2; 55 } 56 57 private final QuickContactBadge photo; 58 private final TextView nameOrNumberView; 59 private final TextView numberView; 60 private final ImageView callToActionView; 61 private final Context context; 62 63 private String number; 64 private @CallToAction int currentAction; 65 SearchContactViewHolder(View view)66 public SearchContactViewHolder(View view) { 67 super(view); 68 view.setOnClickListener(this); 69 photo = view.findViewById(R.id.photo); 70 nameOrNumberView = view.findViewById(R.id.primary); 71 numberView = view.findViewById(R.id.secondary); 72 callToActionView = view.findViewById(R.id.call_to_action); 73 context = view.getContext(); 74 } 75 76 /** 77 * Binds the ViewHolder with a cursor from {@link SearchContactsCursorLoader} with the data found 78 * at the cursors set position. 79 */ bind(Cursor cursor, String query)80 public void bind(Cursor cursor, String query) { 81 number = cursor.getString(Projections.PHONE_NUMBER); 82 String name = cursor.getString(Projections.PHONE_DISPLAY_NAME); 83 String label = getLabel(context.getResources(), cursor); 84 String secondaryInfo = 85 TextUtils.isEmpty(label) 86 ? number 87 : context.getString( 88 com.android.contacts.common.R.string.call_subject_type_and_number, label, number); 89 90 nameOrNumberView.setText(QueryBoldingUtil.getNameWithQueryBolded(query, name)); 91 numberView.setText(QueryBoldingUtil.getNumberWithQueryBolded(query, secondaryInfo)); 92 setCallToAction(cursor); 93 94 if (shouldShowPhoto(cursor, name)) { 95 nameOrNumberView.setVisibility(View.VISIBLE); 96 photo.setVisibility(View.VISIBLE); 97 String photoUri = cursor.getString(Projections.PHONE_PHOTO_URI); 98 ContactPhotoManager.getInstance(context) 99 .loadDialerThumbnailOrPhoto( 100 photo, 101 getContactUri(cursor), 102 cursor.getLong(Projections.PHONE_PHOTO_ID), 103 photoUri == null ? null : Uri.parse(photoUri), 104 name, 105 LetterTileDrawable.TYPE_DEFAULT); 106 } else { 107 nameOrNumberView.setVisibility(View.GONE); 108 photo.setVisibility(View.INVISIBLE); 109 } 110 } 111 shouldShowPhoto(Cursor cursor, String currentName)112 private boolean shouldShowPhoto(Cursor cursor, String currentName) { 113 int currentPosition = cursor.getPosition(); 114 if (currentPosition == 0) { 115 return true; 116 } else { 117 cursor.moveToPosition(currentPosition - 1); 118 String previousName = cursor.getString(Projections.PHONE_DISPLAY_NAME); 119 cursor.moveToPosition(currentPosition); 120 return !currentName.equals(previousName); 121 } 122 } 123 getContactUri(Cursor cursor)124 private static Uri getContactUri(Cursor cursor) { 125 long contactId = cursor.getLong(Projections.PHONE_ID); 126 String lookupKey = cursor.getString(Projections.PHONE_LOOKUP_KEY); 127 return Contacts.getLookupUri(contactId, lookupKey); 128 } 129 130 // TODO: handle CNAP and cequint types. 131 // TODO: unify this into a utility method with CallLogAdapter#getNumberType getLabel(Resources resources, Cursor cursor)132 private static String getLabel(Resources resources, Cursor cursor) { 133 int numberType = cursor.getInt(Projections.PHONE_TYPE); 134 String numberLabel = cursor.getString(Projections.PHONE_LABEL); 135 136 // Returns empty label instead of "custom" if the custom label is empty. 137 if (numberType == Phone.TYPE_CUSTOM && TextUtils.isEmpty(numberLabel)) { 138 return ""; 139 } 140 return (String) Phone.getTypeLabel(resources, numberType, numberLabel); 141 } 142 setCallToAction(Cursor cursor)143 private void setCallToAction(Cursor cursor) { 144 currentAction = getCallToAction(cursor); 145 switch (currentAction) { 146 case CallToAction.NONE: 147 callToActionView.setVisibility(View.GONE); 148 callToActionView.setOnClickListener(null); 149 break; 150 case CallToAction.SHARE_AND_CALL: 151 callToActionView.setVisibility(View.VISIBLE); 152 callToActionView.setImageDrawable( 153 context.getDrawable(com.android.contacts.common.R.drawable.ic_phone_attach)); 154 callToActionView.setOnClickListener(this); 155 break; 156 case CallToAction.VIDEO_CALL: 157 callToActionView.setVisibility(View.VISIBLE); 158 callToActionView.setImageDrawable( 159 context.getDrawable(R.drawable.quantum_ic_videocam_white_24)); 160 callToActionView.setOnClickListener(this); 161 break; 162 default: 163 throw Assert.createIllegalStateFailException( 164 "Invalid Call to action type: " + currentAction); 165 } 166 } 167 getCallToAction(Cursor cursor)168 private static @CallToAction int getCallToAction(Cursor cursor) { 169 int carrierPresence = cursor.getInt(Projections.PHONE_CARRIER_PRESENCE); 170 if ((carrierPresence & Phone.CARRIER_PRESENCE_VT_CAPABLE) == 1) { 171 return CallToAction.VIDEO_CALL; 172 } 173 174 // TODO: enriched calling 175 return CallToAction.NONE; 176 } 177 178 @Override onClick(View view)179 public void onClick(View view) { 180 if (view == callToActionView) { 181 switch (currentAction) { 182 case CallToAction.SHARE_AND_CALL: 183 callToActionView.setVisibility(View.VISIBLE); 184 callToActionView.setImageDrawable( 185 context.getDrawable(com.android.contacts.common.R.drawable.ic_phone_attach)); 186 // TODO: open call composer. 187 break; 188 case CallToAction.VIDEO_CALL: 189 callToActionView.setVisibility(View.VISIBLE); 190 callToActionView.setImageDrawable( 191 context.getDrawable(R.drawable.quantum_ic_videocam_white_24)); 192 // TODO: place a video call 193 break; 194 case CallToAction.NONE: 195 default: 196 throw Assert.createIllegalStateFailException( 197 "Invalid Call to action type: " + currentAction); 198 } 199 } else { 200 // TODO: set the correct call initiation type. 201 TelecomUtil.placeCall(context, new CallIntentBuilder(number, Type.REGULAR_SEARCH).build()); 202 } 203 } 204 } 205