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.dialer.app.list;
18 
19 import android.content.Context;
20 import android.content.res.Resources;
21 import android.database.Cursor;
22 import android.telephony.PhoneNumberUtils;
23 import android.text.BidiFormatter;
24 import android.text.TextDirectionHeuristics;
25 import android.view.View;
26 import android.view.ViewGroup;
27 import com.android.contacts.common.list.ContactListItemView;
28 import com.android.contacts.common.list.PhoneNumberListAdapter;
29 import com.android.contacts.common.util.ContactDisplayUtils;
30 import com.android.dialer.app.R;
31 import com.android.dialer.location.GeoUtil;
32 import com.android.dialer.util.CallUtil;
33 
34 /**
35  * {@link PhoneNumberListAdapter} with the following added shortcuts, that are displayed as list
36  * items: 1) Directly calling the phone number query 2) Adding the phone number query to a contact
37  *
38  * <p>These shortcuts can be enabled or disabled to toggle whether or not they show up in the list.
39  */
40 public class DialerPhoneNumberListAdapter extends PhoneNumberListAdapter {
41 
42   public static final int SHORTCUT_INVALID = -1;
43   public static final int SHORTCUT_DIRECT_CALL = 0;
44   public static final int SHORTCUT_CREATE_NEW_CONTACT = 1;
45   public static final int SHORTCUT_ADD_TO_EXISTING_CONTACT = 2;
46   public static final int SHORTCUT_SEND_SMS_MESSAGE = 3;
47   public static final int SHORTCUT_MAKE_VIDEO_CALL = 4;
48   public static final int SHORTCUT_BLOCK_NUMBER = 5;
49   public static final int SHORTCUT_COUNT = 6;
50 
51   private final boolean[] mShortcutEnabled = new boolean[SHORTCUT_COUNT];
52   private final BidiFormatter mBidiFormatter = BidiFormatter.getInstance();
53   private final boolean mVideoCallingEnabled;
54   private final String mCountryIso;
55 
56   private String mFormattedQueryString;
57 
DialerPhoneNumberListAdapter(Context context)58   public DialerPhoneNumberListAdapter(Context context) {
59     super(context);
60 
61     mCountryIso = GeoUtil.getCurrentCountryIso(context);
62     mVideoCallingEnabled = CallUtil.isVideoEnabled(context);
63   }
64 
65   @Override
getCount()66   public int getCount() {
67     return super.getCount() + getShortcutCount();
68   }
69 
70   /** @return The number of enabled shortcuts. Ranges from 0 to a maximum of SHORTCUT_COUNT */
getShortcutCount()71   public int getShortcutCount() {
72     int count = 0;
73     for (int i = 0; i < mShortcutEnabled.length; i++) {
74       if (mShortcutEnabled[i]) {
75         count++;
76       }
77     }
78     return count;
79   }
80 
disableAllShortcuts()81   public void disableAllShortcuts() {
82     for (int i = 0; i < mShortcutEnabled.length; i++) {
83       mShortcutEnabled[i] = false;
84     }
85   }
86 
87   @Override
getItemViewType(int position)88   public int getItemViewType(int position) {
89     final int shortcut = getShortcutTypeFromPosition(position);
90     if (shortcut >= 0) {
91       // shortcutPos should always range from 1 to SHORTCUT_COUNT
92       return super.getViewTypeCount() + shortcut;
93     } else {
94       return super.getItemViewType(position);
95     }
96   }
97 
98   @Override
getViewTypeCount()99   public int getViewTypeCount() {
100     // Number of item view types in the super implementation + 2 for the 2 new shortcuts
101     return super.getViewTypeCount() + SHORTCUT_COUNT;
102   }
103 
104   @Override
getView(int position, View convertView, ViewGroup parent)105   public View getView(int position, View convertView, ViewGroup parent) {
106     final int shortcutType = getShortcutTypeFromPosition(position);
107     if (shortcutType >= 0) {
108       if (convertView != null) {
109         assignShortcutToView((ContactListItemView) convertView, shortcutType);
110         return convertView;
111       } else {
112         final ContactListItemView v =
113             new ContactListItemView(
114                 getContext(), null, mVideoCallingEnabled, isCallAndShareEnabled());
115         assignShortcutToView(v, shortcutType);
116         return v;
117       }
118     } else {
119       return super.getView(position, convertView, parent);
120     }
121   }
122 
123   @Override
newView( Context context, int partition, Cursor cursor, int position, ViewGroup parent)124   protected ContactListItemView newView(
125       Context context, int partition, Cursor cursor, int position, ViewGroup parent) {
126     final ContactListItemView view = super.newView(context, partition, cursor, position, parent);
127 
128     view.setSupportVideoCallIcon(mVideoCallingEnabled);
129     view.setSupportCallAndShareIcon(isCallAndShareEnabled());
130     return view;
131   }
132 
133   /**
134    * @param position The position of the item
135    * @return The enabled shortcut type matching the given position if the item is a shortcut, -1
136    *     otherwise
137    */
getShortcutTypeFromPosition(int position)138   public int getShortcutTypeFromPosition(int position) {
139     int shortcutCount = position - super.getCount();
140     if (shortcutCount >= 0) {
141       // Iterate through the array of shortcuts, looking only for shortcuts where
142       // mShortcutEnabled[i] is true
143       for (int i = 0; shortcutCount >= 0 && i < mShortcutEnabled.length; i++) {
144         if (mShortcutEnabled[i]) {
145           shortcutCount--;
146           if (shortcutCount < 0) {
147             return i;
148           }
149         }
150       }
151       throw new IllegalArgumentException(
152           "Invalid position - greater than cursor count " + " but not a shortcut.");
153     }
154     return SHORTCUT_INVALID;
155   }
156 
157   @Override
isEmpty()158   public boolean isEmpty() {
159     return getShortcutCount() == 0 && super.isEmpty();
160   }
161 
162   @Override
isEnabled(int position)163   public boolean isEnabled(int position) {
164     final int shortcutType = getShortcutTypeFromPosition(position);
165     if (shortcutType >= 0) {
166       return true;
167     } else {
168       return super.isEnabled(position);
169     }
170   }
171 
assignShortcutToView(ContactListItemView v, int shortcutType)172   private void assignShortcutToView(ContactListItemView v, int shortcutType) {
173     final CharSequence text;
174     final int drawableId;
175     final Resources resources = getContext().getResources();
176     final String number = getFormattedQueryString();
177     switch (shortcutType) {
178       case SHORTCUT_DIRECT_CALL:
179         text =
180             ContactDisplayUtils.getTtsSpannedPhoneNumber(
181                 resources,
182                 R.string.search_shortcut_call_number,
183                 mBidiFormatter.unicodeWrap(number, TextDirectionHeuristics.LTR));
184         drawableId = R.drawable.ic_search_phone;
185         break;
186       case SHORTCUT_CREATE_NEW_CONTACT:
187         text = resources.getString(R.string.search_shortcut_create_new_contact);
188         drawableId = R.drawable.ic_search_add_contact;
189         break;
190       case SHORTCUT_ADD_TO_EXISTING_CONTACT:
191         text = resources.getString(R.string.search_shortcut_add_to_contact);
192         drawableId = R.drawable.quantum_ic_person_white_24;
193         break;
194       case SHORTCUT_SEND_SMS_MESSAGE:
195         text = resources.getString(R.string.search_shortcut_send_sms_message);
196         drawableId = R.drawable.quantum_ic_message_white_24;
197         break;
198       case SHORTCUT_MAKE_VIDEO_CALL:
199         text = resources.getString(R.string.search_shortcut_make_video_call);
200         drawableId = R.drawable.quantum_ic_videocam_white_24;
201         break;
202       case SHORTCUT_BLOCK_NUMBER:
203         text = resources.getString(R.string.search_shortcut_block_number);
204         drawableId = R.drawable.ic_not_interested_googblue_24dp;
205         break;
206       default:
207         throw new IllegalArgumentException("Invalid shortcut type");
208     }
209     v.setDrawableResource(drawableId);
210     v.setDisplayName(text);
211     v.setPhotoPosition(super.getPhotoPosition());
212     v.setAdjustSelectionBoundsEnabled(false);
213   }
214 
215   /** @return True if the shortcut state (disabled vs enabled) was changed by this operation */
setShortcutEnabled(int shortcutType, boolean visible)216   public boolean setShortcutEnabled(int shortcutType, boolean visible) {
217     final boolean changed = mShortcutEnabled[shortcutType] != visible;
218     mShortcutEnabled[shortcutType] = visible;
219     return changed;
220   }
221 
getFormattedQueryString()222   public String getFormattedQueryString() {
223     return mFormattedQueryString;
224   }
225 
226   @Override
setQueryString(String queryString)227   public void setQueryString(String queryString) {
228     mFormattedQueryString =
229         PhoneNumberUtils.formatNumber(PhoneNumberUtils.normalizeNumber(queryString), mCountryIso);
230     super.setQueryString(queryString);
231   }
232 }
233