15ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee/* 25ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Copyright (C) 2009 The Android Open Source Project 35ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * 45ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Licensed under the Apache License, Version 2.0 (the "License"); 55ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * you may not use this file except in compliance with the License. 65ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * You may obtain a copy of the License at 75ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * 85ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * http://www.apache.org/licenses/LICENSE-2.0 95ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * 105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Unless required by applicable law or agreed to in writing, software 115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * distributed under the License is distributed on an "AS IS" BASIS, 125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * See the License for the specific language governing permissions and 145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * limitations under the License. 155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */ 165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leepackage com.android.contacts.common; 185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.content.Context; 205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.content.Intent; 215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.database.Cursor; 2235408bef2b7a180dfa5860e87d4d62209d9d1e09Paul Soulosimport android.net.Uri; 235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.CommonDataKinds.Im; 245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.DisplayPhoto; 255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.telephony.PhoneNumberUtils; 265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.text.TextUtils; 27c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulosimport android.util.Pair; 285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.android.contacts.common.model.account.AccountWithDataSet; 3035408bef2b7a180dfa5860e87d4d62209d9d1e09Paul Soulosimport com.android.contacts.common.model.dataitem.ImDataItem; 3188bede3df22a27296afcec83c2f2f430d1bc1372Yorke Leeimport com.android.contacts.common.testing.NeededForTesting; 325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.android.contacts.common.model.AccountTypeManager; 335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport java.util.List; 355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leepublic class ContactsUtils { 375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee private static final String TAG = "ContactsUtils"; 385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 39382b6dacb5d1582fa198c84825489d846ba9e5bcJay Shrauner // Telecomm related schemes are in CallUtil 40382b6dacb5d1582fa198c84825489d846ba9e5bcJay Shrauner public static final String SCHEME_IMTO = "imto"; 41382b6dacb5d1582fa198c84825489d846ba9e5bcJay Shrauner public static final String SCHEME_MAILTO = "mailto"; 42382b6dacb5d1582fa198c84825489d846ba9e5bcJay Shrauner public static final String SCHEME_SMSTO = "smsto"; 43382b6dacb5d1582fa198c84825489d846ba9e5bcJay Shrauner 44114c36573f4860ccbc2d4fd041228b95576d89beJay Shrauner private static final int DEFAULT_THUMBNAIL_SIZE = 96; 45114c36573f4860ccbc2d4fd041228b95576d89beJay Shrauner 465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee private static int sThumbnailSize = -1; 475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee // TODO find a proper place for the canonical version of these 495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public interface ProviderNames { 505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee String YAHOO = "Yahoo"; 515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee String GTALK = "GTalk"; 525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee String MSN = "MSN"; 535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee String ICQ = "ICQ"; 545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee String AIM = "AIM"; 555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee String XMPP = "XMPP"; 565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee String JABBER = "JABBER"; 575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee String SKYPE = "SKYPE"; 585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee String QQ = "QQ"; 595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee /** 625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * This looks up the provider name defined in 635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * ProviderNames from the predefined IM protocol id. 645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * This is used for interacting with the IM application. 655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * 665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * @param protocol the protocol ID 675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * @return the provider name the IM app uses for the given protocol, or null if no 685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * provider is defined for the given protocol 695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * @hide 705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */ 715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public static String lookupProviderNameFromId(int protocol) { 725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee switch (protocol) { 735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee case Im.PROTOCOL_GOOGLE_TALK: 745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return ProviderNames.GTALK; 755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee case Im.PROTOCOL_AIM: 765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return ProviderNames.AIM; 775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee case Im.PROTOCOL_MSN: 785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return ProviderNames.MSN; 795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee case Im.PROTOCOL_YAHOO: 805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return ProviderNames.YAHOO; 815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee case Im.PROTOCOL_ICQ: 825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return ProviderNames.ICQ; 835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee case Im.PROTOCOL_JABBER: 845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return ProviderNames.JABBER; 855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee case Im.PROTOCOL_SKYPE: 865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return ProviderNames.SKYPE; 875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee case Im.PROTOCOL_QQ: 885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return ProviderNames.QQ; 895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return null; 915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee /** 945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Test if the given {@link CharSequence} contains any graphic characters, 955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * first checking {@link TextUtils#isEmpty(CharSequence)} to handle null. 965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */ 975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public static boolean isGraphic(CharSequence str) { 985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return !TextUtils.isEmpty(str) && TextUtils.isGraphic(str); 995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 1005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee /** 1025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Returns true if two objects are considered equal. Two null references are equal here. 1035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */ 1045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee @NeededForTesting 1055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public static boolean areObjectsEqual(Object a, Object b) { 1065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return a == b || (a != null && a.equals(b)); 1075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 1085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee /** 1105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Returns true if two {@link Intent}s are both null, or have the same action. 1115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */ 1125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public static final boolean areIntentActionEqual(Intent a, Intent b) { 1135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (a == b) { 1145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return true; 1155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 1165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (a == null || b == null) { 1175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return false; 1185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 1195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return TextUtils.equals(a.getAction(), b.getAction()); 1205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 1215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public static boolean areContactWritableAccountsAvailable(Context context) { 1235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final List<AccountWithDataSet> accounts = 1245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee AccountTypeManager.getInstance(context).getAccounts(true /* writeable */); 1255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return !accounts.isEmpty(); 1265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 1275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public static boolean areGroupWritableAccountsAvailable(Context context) { 1295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final List<AccountWithDataSet> accounts = 1305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee AccountTypeManager.getInstance(context).getGroupWritableAccounts(); 1315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return !accounts.isEmpty(); 1325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 1335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee /** 1355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Returns the size (width and height) of thumbnail pictures as configured in the provider. This 1365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * can safely be called from the UI thread, as the provider can serve this without performing 1375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * a database access 1385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */ 1395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public static int getThumbnailSize(Context context) { 1405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (sThumbnailSize == -1) { 1415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final Cursor c = context.getContentResolver().query( 1425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI, 1435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee new String[] { DisplayPhoto.THUMBNAIL_MAX_DIM }, null, null, null); 144114c36573f4860ccbc2d4fd041228b95576d89beJay Shrauner if (c != null) { 145114c36573f4860ccbc2d4fd041228b95576d89beJay Shrauner try { 146114c36573f4860ccbc2d4fd041228b95576d89beJay Shrauner if (c.moveToFirst()) { 147114c36573f4860ccbc2d4fd041228b95576d89beJay Shrauner sThumbnailSize = c.getInt(0); 148114c36573f4860ccbc2d4fd041228b95576d89beJay Shrauner } 149114c36573f4860ccbc2d4fd041228b95576d89beJay Shrauner } finally { 150114c36573f4860ccbc2d4fd041228b95576d89beJay Shrauner c.close(); 151114c36573f4860ccbc2d4fd041228b95576d89beJay Shrauner } 1525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 1535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 154114c36573f4860ccbc2d4fd041228b95576d89beJay Shrauner return sThumbnailSize != -1 ? sThumbnailSize : DEFAULT_THUMBNAIL_SIZE; 1555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 1565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 157c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos private static Intent getCustomImIntent(ImDataItem im, int protocol) { 15835408bef2b7a180dfa5860e87d4d62209d9d1e09Paul Soulos String host = im.getCustomProtocol(); 15935408bef2b7a180dfa5860e87d4d62209d9d1e09Paul Soulos final String data = im.getData(); 16035408bef2b7a180dfa5860e87d4d62209d9d1e09Paul Soulos if (TextUtils.isEmpty(data)) { 16135408bef2b7a180dfa5860e87d4d62209d9d1e09Paul Soulos return null; 16235408bef2b7a180dfa5860e87d4d62209d9d1e09Paul Soulos } 16335408bef2b7a180dfa5860e87d4d62209d9d1e09Paul Soulos if (protocol != Im.PROTOCOL_CUSTOM) { 16435408bef2b7a180dfa5860e87d4d62209d9d1e09Paul Soulos // Try bringing in a well-known host for specific protocols 16535408bef2b7a180dfa5860e87d4d62209d9d1e09Paul Soulos host = ContactsUtils.lookupProviderNameFromId(protocol); 16635408bef2b7a180dfa5860e87d4d62209d9d1e09Paul Soulos } 16735408bef2b7a180dfa5860e87d4d62209d9d1e09Paul Soulos if (TextUtils.isEmpty(host)) { 16835408bef2b7a180dfa5860e87d4d62209d9d1e09Paul Soulos return null; 16935408bef2b7a180dfa5860e87d4d62209d9d1e09Paul Soulos } 17035408bef2b7a180dfa5860e87d4d62209d9d1e09Paul Soulos final String authority = host.toLowerCase(); 171382b6dacb5d1582fa198c84825489d846ba9e5bcJay Shrauner final Uri imUri = new Uri.Builder().scheme(SCHEME_IMTO).authority( 17235408bef2b7a180dfa5860e87d4d62209d9d1e09Paul Soulos authority).appendPath(data).build(); 17335408bef2b7a180dfa5860e87d4d62209d9d1e09Paul Soulos final Intent intent = new Intent(Intent.ACTION_SENDTO, imUri); 17435408bef2b7a180dfa5860e87d4d62209d9d1e09Paul Soulos return intent; 17535408bef2b7a180dfa5860e87d4d62209d9d1e09Paul Soulos } 176c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos 177c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos /** 178c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos * Returns the proper Intent for an ImDatItem. If available, a secondary intent is stored 179c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos * in the second Pair slot 180c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos */ 181c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos public static Pair<Intent, Intent> buildImIntent(Context context, ImDataItem im) { 182c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos Intent intent = null; 183c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos Intent secondaryIntent = null; 184c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos final boolean isEmail = im.isCreatedFromEmail(); 185c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos 186c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos if (!isEmail && !im.isProtocolValid()) { 187ca2ae1848c20aced422c44249b57e9dd3a1353ebJay Shrauner return new Pair<>(null, null); 188c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos } 189c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos 190c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos final String data = im.getData(); 191c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos if (TextUtils.isEmpty(data)) { 192ca2ae1848c20aced422c44249b57e9dd3a1353ebJay Shrauner return new Pair<>(null, null); 193c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos } 194c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos 195c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos final int protocol = isEmail ? Im.PROTOCOL_GOOGLE_TALK : im.getProtocol(); 196c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos 197c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos if (protocol == Im.PROTOCOL_GOOGLE_TALK) { 198c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos final int chatCapability = im.getChatCapability(); 199c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos if ((chatCapability & Im.CAPABILITY_HAS_CAMERA) != 0) { 200c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos intent = new Intent(Intent.ACTION_SENDTO, 201c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos Uri.parse("xmpp:" + data + "?message")); 202c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos secondaryIntent = new Intent(Intent.ACTION_SENDTO, 203c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos Uri.parse("xmpp:" + data + "?call")); 204c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos } else if ((chatCapability & Im.CAPABILITY_HAS_VOICE) != 0) { 205c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos // Allow Talking and Texting 206c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos intent = 207c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?message")); 208c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos secondaryIntent = 209c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?call")); 210c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos } else { 211c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos intent = 212c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?message")); 213c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos } 214c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos } else { 215c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos // Build an IM Intent 216c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos intent = getCustomImIntent(im, protocol); 217c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos } 218c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos return new Pair<>(intent, secondaryIntent); 219c219d047ada3c8f7a43e850baa7c7acb71252870Paul Soulos } 2205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee} 221