diff options
Diffstat (limited to 'java/com/android/contacts/common/ContactsUtils.java')
-rw-r--r-- | java/com/android/contacts/common/ContactsUtils.java | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/java/com/android/contacts/common/ContactsUtils.java b/java/com/android/contacts/common/ContactsUtils.java new file mode 100644 index 000000000..60af44b9a --- /dev/null +++ b/java/com/android/contacts/common/ContactsUtils.java @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.contacts.common; + +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.provider.ContactsContract.CommonDataKinds.Im; +import android.provider.ContactsContract.DisplayPhoto; +import android.support.annotation.IntDef; +import android.text.TextUtils; +import android.util.Pair; +import com.android.contacts.common.compat.ContactsCompat; +import com.android.contacts.common.compat.DirectoryCompat; +import com.android.contacts.common.model.AccountTypeManager; +import com.android.contacts.common.model.account.AccountWithDataSet; +import com.android.contacts.common.model.dataitem.ImDataItem; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; + +public class ContactsUtils { + + // Telecomm related schemes are in CallUtil + public static final String SCHEME_IMTO = "imto"; + public static final String SCHEME_MAILTO = "mailto"; + public static final String SCHEME_SMSTO = "smsto"; + public static final long USER_TYPE_CURRENT = 0; + public static final long USER_TYPE_WORK = 1; + private static final String TAG = "ContactsUtils"; + private static final int DEFAULT_THUMBNAIL_SIZE = 96; + private static int sThumbnailSize = -1; + + /** + * This looks up the provider name defined in ProviderNames from the predefined IM protocol id. + * This is used for interacting with the IM application. + * + * @param protocol the protocol ID + * @return the provider name the IM app uses for the given protocol, or null if no provider is + * defined for the given protocol + * @hide + */ + public static String lookupProviderNameFromId(int protocol) { + switch (protocol) { + case Im.PROTOCOL_GOOGLE_TALK: + return ProviderNames.GTALK; + case Im.PROTOCOL_AIM: + return ProviderNames.AIM; + case Im.PROTOCOL_MSN: + return ProviderNames.MSN; + case Im.PROTOCOL_YAHOO: + return ProviderNames.YAHOO; + case Im.PROTOCOL_ICQ: + return ProviderNames.ICQ; + case Im.PROTOCOL_JABBER: + return ProviderNames.JABBER; + case Im.PROTOCOL_SKYPE: + return ProviderNames.SKYPE; + case Im.PROTOCOL_QQ: + return ProviderNames.QQ; + } + return null; + } + + /** + * Test if the given {@link CharSequence} contains any graphic characters, first checking {@link + * TextUtils#isEmpty(CharSequence)} to handle null. + */ + public static boolean isGraphic(CharSequence str) { + return !TextUtils.isEmpty(str) && TextUtils.isGraphic(str); + } + + /** Returns true if two objects are considered equal. Two null references are equal here. */ + public static boolean areObjectsEqual(Object a, Object b) { + return a == b || (a != null && a.equals(b)); + } + + /** Returns true if two {@link Intent}s are both null, or have the same action. */ + public static final boolean areIntentActionEqual(Intent a, Intent b) { + if (a == b) { + return true; + } + if (a == null || b == null) { + return false; + } + return TextUtils.equals(a.getAction(), b.getAction()); + } + + public static boolean areGroupWritableAccountsAvailable(Context context) { + final List<AccountWithDataSet> accounts = + AccountTypeManager.getInstance(context).getGroupWritableAccounts(); + return !accounts.isEmpty(); + } + + /** + * Returns the size (width and height) of thumbnail pictures as configured in the provider. This + * can safely be called from the UI thread, as the provider can serve this without performing a + * database access + */ + public static int getThumbnailSize(Context context) { + if (sThumbnailSize == -1) { + final Cursor c = + context + .getContentResolver() + .query( + DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI, + new String[] {DisplayPhoto.THUMBNAIL_MAX_DIM}, + null, + null, + null); + if (c != null) { + try { + if (c.moveToFirst()) { + sThumbnailSize = c.getInt(0); + } + } finally { + c.close(); + } + } + } + return sThumbnailSize != -1 ? sThumbnailSize : DEFAULT_THUMBNAIL_SIZE; + } + + private static Intent getCustomImIntent(ImDataItem im, int protocol) { + String host = im.getCustomProtocol(); + final String data = im.getData(); + if (TextUtils.isEmpty(data)) { + return null; + } + if (protocol != Im.PROTOCOL_CUSTOM) { + // Try bringing in a well-known host for specific protocols + host = ContactsUtils.lookupProviderNameFromId(protocol); + } + if (TextUtils.isEmpty(host)) { + return null; + } + final String authority = host.toLowerCase(); + final Uri imUri = + new Uri.Builder().scheme(SCHEME_IMTO).authority(authority).appendPath(data).build(); + final Intent intent = new Intent(Intent.ACTION_SENDTO, imUri); + return intent; + } + + /** + * Returns the proper Intent for an ImDatItem. If available, a secondary intent is stored in the + * second Pair slot + */ + public static Pair<Intent, Intent> buildImIntent(Context context, ImDataItem im) { + Intent intent = null; + Intent secondaryIntent = null; + final boolean isEmail = im.isCreatedFromEmail(); + + if (!isEmail && !im.isProtocolValid()) { + return new Pair<>(null, null); + } + + final String data = im.getData(); + if (TextUtils.isEmpty(data)) { + return new Pair<>(null, null); + } + + final int protocol = isEmail ? Im.PROTOCOL_GOOGLE_TALK : im.getProtocol(); + + if (protocol == Im.PROTOCOL_GOOGLE_TALK) { + final int chatCapability = im.getChatCapability(); + if ((chatCapability & Im.CAPABILITY_HAS_CAMERA) != 0) { + intent = new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?message")); + secondaryIntent = new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?call")); + } else if ((chatCapability & Im.CAPABILITY_HAS_VOICE) != 0) { + // Allow Talking and Texting + intent = new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?message")); + secondaryIntent = new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?call")); + } else { + intent = new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?message")); + } + } else { + // Build an IM Intent + intent = getCustomImIntent(im, protocol); + } + return new Pair<>(intent, secondaryIntent); + } + + /** + * Determine UserType from directory id and contact id. + * + * <p>3 types of query + * + * <p>1. 2 profile query: content://com.android.contacts/phone_lookup_enterprise/1234567890 + * personal and work contact are mixed into one cursor. no directory id. contact_id indicates if + * it's work contact + * + * <p>2. work local query: + * content://com.android.contacts/phone_lookup_enterprise/1234567890?directory=1000000000 either + * directory_id or contact_id is enough to identify work contact + * + * <p>3. work remote query: + * content://com.android.contacts/phone_lookup_enterprise/1234567890?directory=1000000003 + * contact_id is random. only directory_id is available + * + * <p>Summary: If directory_id is not null, always use directory_id to identify work contact. + * (which is the case here) Otherwise, use contact_id. + * + * @param directoryId directory id of ContactsProvider query + * @param contactId contact id + * @return UserType indicates the user type of the contact. A directory id or contact id larger + * than a thredshold indicates that the contact is stored in Work Profile, but not in current + * user. It's a contract by ContactsProvider and check by Contacts.isEnterpriseDirectoryId and + * Contacts.isEnterpriseContactId. Currently, only 2 kinds of users can be detected from the + * directoryId and contactId as ContactsProvider can only access current and work user's + * contacts + */ + public static @UserType long determineUserType(Long directoryId, Long contactId) { + // First check directory id + if (directoryId != null) { + return DirectoryCompat.isEnterpriseDirectoryId(directoryId) + ? USER_TYPE_WORK + : USER_TYPE_CURRENT; + } + // Only check contact id if directory id is null + if (contactId != null && contactId != 0L && ContactsCompat.isEnterpriseContactId(contactId)) { + return USER_TYPE_WORK; + } else { + return USER_TYPE_CURRENT; + } + } + + // TODO find a proper place for the canonical version of these + public interface ProviderNames { + + String YAHOO = "Yahoo"; + String GTALK = "GTalk"; + String MSN = "MSN"; + String ICQ = "ICQ"; + String AIM = "AIM"; + String XMPP = "XMPP"; + String JABBER = "JABBER"; + String SKYPE = "SKYPE"; + String QQ = "QQ"; + } + + /** + * UserType indicates the user type of the contact. If the contact is from Work User (Work Profile + * in Android Multi-User System), it's {@link #USER_TYPE_WORK}, otherwise, {@link + * #USER_TYPE_CURRENT}. Please note that current user can be in work profile, where the dialer is + * running inside Work Profile. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({USER_TYPE_CURRENT, USER_TYPE_WORK}) + public @interface UserType {} +} |