• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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.contacts;
18 
19 import com.android.contacts.model.AccountType;
20 import com.android.contacts.model.AccountTypeManager;
21 import com.android.contacts.model.AccountWithDataSet;
22 import com.android.contacts.test.NeededForTesting;
23 import com.android.i18n.phonenumbers.PhoneNumberUtil;
24 
25 import android.content.Context;
26 import android.content.Intent;
27 import android.location.CountryDetector;
28 import android.net.Uri;
29 import android.provider.ContactsContract;
30 import android.provider.ContactsContract.CommonDataKinds.Im;
31 import android.provider.ContactsContract.CommonDataKinds.Phone;
32 import android.telephony.PhoneNumberUtils;
33 import android.text.TextUtils;
34 
35 import java.util.List;
36 
37 public class ContactsUtils {
38     private static final String TAG = "ContactsUtils";
39     private static final String WAIT_SYMBOL_AS_STRING = String.valueOf(PhoneNumberUtils.WAIT);
40 
41 
42     // TODO find a proper place for the canonical version of these
43     public interface ProviderNames {
44         String YAHOO = "Yahoo";
45         String GTALK = "GTalk";
46         String MSN = "MSN";
47         String ICQ = "ICQ";
48         String AIM = "AIM";
49         String XMPP = "XMPP";
50         String JABBER = "JABBER";
51         String SKYPE = "SKYPE";
52         String QQ = "QQ";
53     }
54 
55     /**
56      * This looks up the provider name defined in
57      * ProviderNames from the predefined IM protocol id.
58      * This is used for interacting with the IM application.
59      *
60      * @param protocol the protocol ID
61      * @return the provider name the IM app uses for the given protocol, or null if no
62      * provider is defined for the given protocol
63      * @hide
64      */
lookupProviderNameFromId(int protocol)65     public static String lookupProviderNameFromId(int protocol) {
66         switch (protocol) {
67             case Im.PROTOCOL_GOOGLE_TALK:
68                 return ProviderNames.GTALK;
69             case Im.PROTOCOL_AIM:
70                 return ProviderNames.AIM;
71             case Im.PROTOCOL_MSN:
72                 return ProviderNames.MSN;
73             case Im.PROTOCOL_YAHOO:
74                 return ProviderNames.YAHOO;
75             case Im.PROTOCOL_ICQ:
76                 return ProviderNames.ICQ;
77             case Im.PROTOCOL_JABBER:
78                 return ProviderNames.JABBER;
79             case Im.PROTOCOL_SKYPE:
80                 return ProviderNames.SKYPE;
81             case Im.PROTOCOL_QQ:
82                 return ProviderNames.QQ;
83         }
84         return null;
85     }
86 
87     /**
88      * Test if the given {@link CharSequence} contains any graphic characters,
89      * first checking {@link TextUtils#isEmpty(CharSequence)} to handle null.
90      */
isGraphic(CharSequence str)91     public static boolean isGraphic(CharSequence str) {
92         return !TextUtils.isEmpty(str) && TextUtils.isGraphic(str);
93     }
94 
95     /**
96      * Returns true if two objects are considered equal.  Two null references are equal here.
97      */
98     @NeededForTesting
areObjectsEqual(Object a, Object b)99     public static boolean areObjectsEqual(Object a, Object b) {
100         return a == b || (a != null && a.equals(b));
101     }
102 
103     /**
104      * Returns true if two data with mimetypes which represent values in contact entries are
105      * considered equal for collapsing in the GUI. For caller-id, use
106      * {@link PhoneNumberUtils#compare(Context, String, String)} instead
107      */
shouldCollapse(CharSequence mimetype1, CharSequence data1, CharSequence mimetype2, CharSequence data2)108     public static final boolean shouldCollapse(CharSequence mimetype1, CharSequence data1,
109             CharSequence mimetype2, CharSequence data2) {
110         // different mimetypes? don't collapse
111         if (!TextUtils.equals(mimetype1, mimetype2)) return false;
112 
113         // exact same string? good, bail out early
114         if (TextUtils.equals(data1, data2)) return true;
115 
116         // so if either is null, these two must be different
117         if (data1 == null || data2 == null) return false;
118 
119         // if this is not about phone numbers, we know this is not a match (of course, some
120         // mimetypes could have more sophisticated matching is the future, e.g. addresses)
121         if (!TextUtils.equals(Phone.CONTENT_ITEM_TYPE, mimetype1)) return false;
122 
123         // Now do the full phone number thing. split into parts, seperated by waiting symbol
124         // and compare them individually
125         final String[] dataParts1 = data1.toString().split(WAIT_SYMBOL_AS_STRING);
126         final String[] dataParts2 = data2.toString().split(WAIT_SYMBOL_AS_STRING);
127         if (dataParts1.length != dataParts2.length) return false;
128         final PhoneNumberUtil util = PhoneNumberUtil.getInstance();
129         for (int i = 0; i < dataParts1.length; i++) {
130             final String dataPart1 = dataParts1[i];
131             final String dataPart2 = dataParts2[i];
132 
133             // substrings equal? shortcut, don't parse
134             if (TextUtils.equals(dataPart1, dataPart2)) continue;
135 
136             // do a full parse of the numbers
137             switch (util.isNumberMatch(dataPart1, dataPart2)) {
138                 case NOT_A_NUMBER:
139                     // don't understand the numbers? let's play it safe
140                     return false;
141                 case NO_MATCH:
142                     return false;
143                 case EXACT_MATCH:
144                 case SHORT_NSN_MATCH:
145                 case NSN_MATCH:
146                     break;
147                 default:
148                     throw new IllegalStateException("Unknown result value from phone number " +
149                             "library");
150             }
151         }
152         return true;
153     }
154 
155     /**
156      * Returns true if two {@link Intent}s are both null, or have the same action.
157      */
areIntentActionEqual(Intent a, Intent b)158     public static final boolean areIntentActionEqual(Intent a, Intent b) {
159         if (a == b) {
160             return true;
161         }
162         if (a == null || b == null) {
163             return false;
164         }
165         return TextUtils.equals(a.getAction(), b.getAction());
166     }
167 
168     /**
169      * @return The ISO 3166-1 two letters country code of the country the user
170      *         is in.
171      */
getCurrentCountryIso(Context context)172     public static final String getCurrentCountryIso(Context context) {
173         CountryDetector detector =
174                 (CountryDetector) context.getSystemService(Context.COUNTRY_DETECTOR);
175         return detector.detectCountry().getCountryIso();
176     }
177 
areContactWritableAccountsAvailable(Context context)178     public static boolean areContactWritableAccountsAvailable(Context context) {
179         final List<AccountWithDataSet> accounts =
180                 AccountTypeManager.getInstance(context).getAccounts(true /* writeable */);
181         return !accounts.isEmpty();
182     }
183 
areGroupWritableAccountsAvailable(Context context)184     public static boolean areGroupWritableAccountsAvailable(Context context) {
185         final List<AccountWithDataSet> accounts =
186                 AccountTypeManager.getInstance(context).getGroupWritableAccounts();
187         return !accounts.isEmpty();
188     }
189 
190     /**
191      * Returns the intent to launch for the given invitable account type and contact lookup URI.
192      * This will return null if the account type is not invitable (i.e. there is no
193      * {@link AccountType#getInviteContactActivityClassName()} or
194      * {@link AccountType#resPackageName}).
195      */
getInvitableIntent(AccountType accountType, Uri lookupUri)196     public static Intent getInvitableIntent(AccountType accountType, Uri lookupUri) {
197         String resPackageName = accountType.resPackageName;
198         String className = accountType.getInviteContactActivityClassName();
199         if (TextUtils.isEmpty(resPackageName) || TextUtils.isEmpty(className)) {
200             return null;
201         }
202         Intent intent = new Intent();
203         intent.setClassName(resPackageName, className);
204 
205         intent.setAction(ContactsContract.Intents.INVITE_CONTACT);
206 
207         // Data is the lookup URI.
208         intent.setData(lookupUri);
209         return intent;
210     }
211 }
212