From 1122a58d3e8b4f85ffbc97aecbf76f57b2711065 Mon Sep 17 00:00:00 2001 From: Yujing Gu Date: Thu, 24 Jul 2014 14:32:13 +0800 Subject: Add the support for SIM and Phone account - Add SIM and Phone account type - Add insert/update/delete operation for SIM contacts - Add the support for filter SIM contacts - Add support to save anr and email for SIM contacts Change-Id: I1aaa9046462ed114488e4cd0cfa0d061a7330c69 --- Android.mk | 2 + .../common/test/mocks/MockAccountTypeManager.java | 5 + .../contacts/common/ContactTileLoaderFactory.java | 16 +- .../android/contacts/common/MoreContactUtils.java | 167 ++++++++++ .../contacts/common/SimContactsConstants.java | 77 +++++ .../contacts/common/SimContactsOperation.java | 340 +++++++++++++++++++++ .../common/list/ContactEntryListFragment.java | 26 ++ .../common/list/DefaultContactListAdapter.java | 51 ++++ .../common/list/PhoneNumberListAdapter.java | 29 ++ .../contacts/common/model/AccountTypeManager.java | 90 +++++- .../contacts/common/model/RawContactDelta.java | 138 +++++++++ .../android/contacts/common/model/ValuesDelta.java | 16 + .../contacts/common/model/account/AccountType.java | 63 +++- .../common/model/account/PhoneAccountType.java | 99 ++++++ .../common/model/account/SimAccountType.java | 123 ++++++++ .../contacts/common/util/AccountSelectionUtil.java | 35 ++- .../contacts/common/util/AccountsListAdapter.java | 23 +- 17 files changed, 1275 insertions(+), 25 deletions(-) create mode 100644 src/com/android/contacts/common/SimContactsConstants.java create mode 100644 src/com/android/contacts/common/SimContactsOperation.java create mode 100644 src/com/android/contacts/common/model/account/PhoneAccountType.java create mode 100644 src/com/android/contacts/common/model/account/SimAccountType.java diff --git a/Android.mk b/Android.mk index 626a737b..3687e476 100644 --- a/Android.mk +++ b/Android.mk @@ -28,6 +28,8 @@ LOCAL_AAPT_FLAGS := \ --auto-add-overlay \ --extra-packages com.android.phone.common +LOCAL_JAVA_LIBRARIES := telephony-common + LOCAL_STATIC_JAVA_LIBRARIES := \ com.android.vcard \ guava \ diff --git a/TestCommon/src/com/android/contacts/common/test/mocks/MockAccountTypeManager.java b/TestCommon/src/com/android/contacts/common/test/mocks/MockAccountTypeManager.java index ab2d3954..d3dd722c 100644 --- a/TestCommon/src/com/android/contacts/common/test/mocks/MockAccountTypeManager.java +++ b/TestCommon/src/com/android/contacts/common/test/mocks/MockAccountTypeManager.java @@ -65,6 +65,11 @@ public class MockAccountTypeManager extends AccountTypeManager { return Arrays.asList(mAccounts); } + @Override + public List getAccounts(boolean writableOnly, int flag) { + return Arrays.asList(mAccounts); + } + @Override public List getGroupWritableAccounts() { return Arrays.asList(mAccounts); diff --git a/src/com/android/contacts/common/ContactTileLoaderFactory.java b/src/com/android/contacts/common/ContactTileLoaderFactory.java index f8b0c359..4377d057 100644 --- a/src/com/android/contacts/common/ContactTileLoaderFactory.java +++ b/src/com/android/contacts/common/ContactTileLoaderFactory.java @@ -23,6 +23,8 @@ import android.net.Uri; import android.provider.ContactsContract; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.Contacts; +import android.provider.ContactsContract.RawContacts; +import android.text.TextUtils; /** * Used to create {@link CursorLoader}s to load different groups of @@ -88,10 +90,16 @@ public final class ContactTileLoaderFactory { } public static CursorLoader createStrequentPhoneOnlyLoader(Context context) { - Uri uri = Contacts.CONTENT_STREQUENT_URI.buildUpon() - .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true").build(); - - return new CursorLoader(context, uri, COLUMNS_PHONE_ONLY, null, null, null); + Uri.Builder builder = Contacts.CONTENT_STREQUENT_URI.buildUpon(); + builder.appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true"); + // Do not show contacts in disabled SIM card + String disabledSimFilter = MoreContactUtils.getDisabledSimFilter(); + if (!TextUtils.isEmpty(disabledSimFilter)) { + builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, disabledSimFilter); + builder.appendQueryParameter(SimContactsConstants + .WITHOUT_SIM_FLAG, "true"); + } + return new CursorLoader(context, builder.build(), COLUMNS_PHONE_ONLY, null, null, null); } public static CursorLoader createStarredLoader(Context context) { diff --git a/src/com/android/contacts/common/MoreContactUtils.java b/src/com/android/contacts/common/MoreContactUtils.java index 16b4e8d9..52fd564b 100644 --- a/src/com/android/contacts/common/MoreContactUtils.java +++ b/src/com/android/contacts/common/MoreContactUtils.java @@ -16,6 +16,8 @@ package com.android.contacts.common; +import android.accounts.Account; + import com.google.i18n.phonenumbers.NumberParseException; import com.google.i18n.phonenumbers.PhoneNumberUtil; @@ -23,13 +25,20 @@ import android.content.Context; import android.content.Intent; import android.graphics.Rect; import android.net.Uri; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemProperties; import android.provider.ContactsContract; +import android.provider.Settings; +import android.telephony.SubscriptionManager; import android.telephony.PhoneNumberUtils; +import android.telephony.TelephonyManager; import android.text.TextUtils; import android.view.View; import android.widget.TextView; import com.android.contacts.common.model.account.AccountType; +import com.android.internal.telephony.IIccPhoneBook; /** * Shared static contact utility methods. @@ -232,4 +241,162 @@ public class MoreContactUtils { intent.setData(lookupUri); return intent; } + + /** get disabled SIM card's name */ + public static String getDisabledSimFilter() { + int count = TelephonyManager.getDefault().getPhoneCount(); + StringBuilder simFilter = new StringBuilder(""); + + for (int i = 0; i < count; i++) { + if (!TelephonyManager.getDefault().hasIccCard(i)) { + continue; + } + if (TelephonyManager.SIM_STATE_UNKNOWN == TelephonyManager + .getDefault().getSimState(i)) { + simFilter.append(getSimAccountName(i) + ','); + } + } + + return simFilter.toString(); + } + + public static boolean isAPMOnAndSIMPowerDown(Context context) { + if (context == null) { + return false; + } + boolean isAirPlaneMode = Settings.System.getInt(context.getContentResolver(), + Settings.System.AIRPLANE_MODE_ON, 0) == 1; + boolean isSIMPowerDown = SystemProperties.getInt( + "persist.radio.apm_sim_not_pwdn", 0) == 0; + return isAirPlaneMode && isSIMPowerDown; + } + + /** + * Get SIM card account name + */ + public static String getSimAccountName(int subscription) { + if (TelephonyManager.getDefault().isMultiSimEnabled()) { + return SimContactsConstants.SIM_NAME + (subscription + 1); + } else { + return SimContactsConstants.SIM_NAME; + } + } + + public static int getSubscription(String accountType, String accountName) { + int subscription = SimContactsConstants.SUB_INVALID; + if (accountType == null || accountName == null) + return subscription; + if (accountType.equals(SimContactsConstants.ACCOUNT_TYPE_SIM)) { + if (accountName.equals(SimContactsConstants.SIM_NAME) + || accountName.equals(SimContactsConstants.SIM_NAME_1)) { + subscription = SimContactsConstants.SUB_1; + } else if (accountName.equals(SimContactsConstants.SIM_NAME_2)) { + subscription = SimContactsConstants.SUB_2; + } + } + return subscription; + } + + public static int getAnrCount(int slot) { + int anrCount = 0; + long[] subId = SubscriptionManager.getSubId(slot); + try { + IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( + ServiceManager.getService("simphonebook")); + + if (iccIpb != null) { + if (subId != null + && TelephonyManager.getDefault().isMultiSimEnabled()) { + anrCount = iccIpb.getAnrCountUsingSubId(subId[0]); + } else { + anrCount = iccIpb.getAnrCount(); + } + } + } catch (RemoteException ex) { + // ignore it + } + + return anrCount; + } + + public static int getAdnCount(int slot) { + int adnCount = 0; + long[] subId = SubscriptionManager.getSubId(slot); + try { + IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( + ServiceManager.getService("simphonebook")); + + if (iccIpb != null) { + if (subId != null + && TelephonyManager.getDefault().isMultiSimEnabled()) { + adnCount = iccIpb.getAdnCountUsingSubId(subId[0]); + } else { + adnCount = iccIpb.getAdnCount(); + } + } + } catch (RemoteException ex) { + // ignore it + } + + return adnCount; + } + + public static int getEmailCount(int slot) { + int emailCount = 0; + long[] subId = SubscriptionManager.getSubId(slot); + try { + IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( + ServiceManager.getService("simphonebook")); + + if (iccIpb != null) { + if (subId != null + && TelephonyManager.getDefault().isMultiSimEnabled()) { + emailCount = iccIpb.getEmailCountUsingSubId(subId[0]); + } else { + emailCount = iccIpb.getEmailCount(); + } + } + } catch (RemoteException ex) { + // ignore it + } + + return emailCount; + } + + /** + * Returns the subscription's card can save anr or not. + */ + public static boolean canSaveAnr(int slot) { + return getAnrCount(slot) > 0 ? true : false; + } + + /** + * Returns the subscription's card can save email or not. + */ + public static boolean canSaveEmail(int slot) { + return getEmailCount(slot) > 0 ? true : false; + } + + public static int getOneSimAnrCount(int slot) { + int count = 0; + int anrCount = getAnrCount(slot); + int adnCount = getAdnCount(slot); + if (adnCount > 0) { + count = anrCount % adnCount != 0 ? (anrCount / adnCount + 1) + : (anrCount / adnCount); + } + return count; + } + + public static int getOneSimEmailCount(int slot) { + int count = 0; + int emailCount = getEmailCount(slot); + int adnCount = getAdnCount(slot); + if (adnCount > 0) { + count = emailCount % adnCount != 0 ? (emailCount + / adnCount + 1) + : (emailCount / adnCount); + } + return count; + } } diff --git a/src/com/android/contacts/common/SimContactsConstants.java b/src/com/android/contacts/common/SimContactsConstants.java new file mode 100644 index 00000000..759dcd9e --- /dev/null +++ b/src/com/android/contacts/common/SimContactsConstants.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014, The Linux Foundation. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ +package com.android.contacts.common; + +import android.telephony.SubscriptionManager; +import com.android.internal.telephony.PhoneConstants; + +public interface SimContactsConstants { + + public static final String SIM_NAME_1 = "SIM1"; + public static final String SIM_NAME_2 = "SIM2"; + public static final String SIM_NAME = "SIM"; + public static final String PHONE_NAME = "PHONE"; + public static final String PASSWORD = ""; + public static final String ACCOUNT_TYPE_SIM = "com.android.sim"; + public static final String ACCOUNT_TYPE_PHONE = "com.android.localphone"; + public static final String SUB = PhoneConstants.SLOT_KEY; + public static final String ACCOUNT_TYPE = "account_type"; + public static final String ACCOUNT_NAME = "account_name"; + public static final String ACCOUNT_DATA = "data_set"; + public static final String STR_TAG = "tag"; + public static final String STR_NUMBER = "number"; + public static final String STR_EMAILS = "emails"; + public static final String STR_ANRS = "anrs"; + public static final String STR_NEW_TAG = "newTag"; + public static final String STR_NEW_NUMBER = "newNumber"; + public static final String STR_NEW_EMAILS = "newEmails"; + public static final String STR_NEW_ANRS = "newAnrs"; + public static final String INTENT_EXPORT_COMPLETE = + "com.android.sim.INTENT_EXPORT_COMPLETE"; + public static final String SIM_URI = "content://icc/adn"; + public static final String SIM_SUB_URI = "content://icc/adn/subId/"; + public static final String WITHOUT_SIM_FLAG ="no_sim"; + public static final String IS_CONTACT = "is_contact"; + public static final String RESULT_KEY = "result"; + public static final String ACTION_MULTI_PICK = + "com.android.contacts.action.MULTI_PICK"; + public static final String ACTION_MULTI_PICK_EMAIL = + "com.android.contacts.action.MULTI_PICK_EMAIL"; + public static final String ACTION_MULTI_PICK_CALL = + "com.android.contacts.action.MULTI_PICK_CALL"; + public static final String ACTION_MULTI_PICK_SIM = + "com.android.contacts.action.MULTI_PICK_SIM"; + public static final int SUB_1 = PhoneConstants.SUB1; + public static final int SUB_2 = PhoneConstants.SUB2; + public static final int SUB_INVALID = SubscriptionManager.INVALID_SLOT_ID; + +} + + diff --git a/src/com/android/contacts/common/SimContactsOperation.java b/src/com/android/contacts/common/SimContactsOperation.java new file mode 100644 index 00000000..55c7b79f --- /dev/null +++ b/src/com/android/contacts/common/SimContactsOperation.java @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2014, The Linux Foundation. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ + +package com.android.contacts.common; + +import android.content.AsyncQueryHandler; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.ContentUris; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.provider.ContactsContract; +import android.provider.ContactsContract.Data; +import android.provider.ContactsContract.Contacts; +import android.provider.ContactsContract.CommonDataKinds; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.CommonDataKinds.Email; +import android.provider.ContactsContract.CommonDataKinds.StructuredName; +import android.provider.ContactsContract.RawContacts; +import android.telephony.PhoneNumberUtils; +import android.telephony.TelephonyManager; +import android.telephony.SubscriptionManager; +import android.text.TextUtils; +import android.util.Log; + +import com.android.contacts.common.SimContactsConstants; + +public class SimContactsOperation { + + private static final String TAG = "SimContactsOperation"; + private static final boolean DBG = true; + private static final int QUERY_TOKEN = 0; + private static final int INSERT_TOKEN = 1; + private static final int UPDATE_TOKEN = 2; + private static final int DELETE_TOKEN = 3; + + public static final String[] ACCOUNT_PROJECTION = new String[] { + RawContacts._ID, + RawContacts.CONTACT_ID, + RawContacts.ACCOUNT_NAME, + RawContacts.ACCOUNT_TYPE, + }; + + + private static final int ACCOUNT_COLUMN_RAW_ID = 0; + private static final int ACCOUNT_COLUMN_CONTACT_ID = 1; + private static final int ACCOUNT_COLUMN_NAME = 2; + private static final int ACCOUNT_COLUMN_TYPE = 3; + private static final int ACCOUNT_COLUMN_PHONE_NAME = 4; + + + + private static Context mContext; + private ContentResolver mResolver; + private ContentValues mValues = new ContentValues(); + + + public SimContactsOperation(Context context) { + this.mContext = context; + this.mResolver = context.getContentResolver(); + } + + + public Uri insert(ContentValues values, int subscription) { + + Uri uri = getContentUri(subscription); + String number = values.getAsString(SimContactsConstants.STR_NUMBER); + String anrs = values.getAsString(SimContactsConstants.STR_ANRS); + String emails = values.getAsString(SimContactsConstants.STR_EMAILS); + values.put(SimContactsConstants.STR_NUMBER,PhoneNumberUtils.stripSeparators(number)); + values.put(SimContactsConstants.STR_ANRS,PhoneNumberUtils.stripSeparators(anrs)); + values.put(SimContactsConstants.STR_EMAILS,emails); + + Uri resultUri; + resultUri = mResolver.insert(uri,values); + return resultUri; + } + + public int update(ContentValues values,int subscription) { + Uri uri = getContentUri(subscription); + + int result; + String oldNumber = values.getAsString(SimContactsConstants.STR_NUMBER); + String newNumber = values.getAsString(SimContactsConstants.STR_NEW_NUMBER); + String oldAnrs = values.getAsString(SimContactsConstants.STR_ANRS); + String newAnrs = values.getAsString(SimContactsConstants.STR_NEW_ANRS); + values.put(SimContactsConstants.STR_NUMBER,PhoneNumberUtils.stripSeparators(oldNumber)); + values.put(SimContactsConstants.STR_NEW_NUMBER,PhoneNumberUtils.stripSeparators(newNumber)); + values.put(SimContactsConstants.STR_ANRS,PhoneNumberUtils.stripSeparators(oldAnrs)); + values.put(SimContactsConstants.STR_NEW_ANRS,PhoneNumberUtils.stripSeparators(newAnrs)); + + result = mResolver.update(uri,values,null,null); + return result; + + } + + public int delete(ContentValues values, int subscription) { + int result; + StringBuilder buf = new StringBuilder(); + String num = null; + String name = values.getAsString(SimContactsConstants.STR_TAG); + String number = values.getAsString(SimContactsConstants.STR_NUMBER); + String emails = values.getAsString(SimContactsConstants.STR_EMAILS); + String anrs = values.getAsString(SimContactsConstants.STR_ANRS); + if (number != null) + num = PhoneNumberUtils.stripSeparators(number); + if (anrs != null) + anrs = PhoneNumberUtils.stripSeparators(anrs); + Uri uri = getContentUri(subscription); + + + if (!TextUtils.isEmpty(name)) { + buf.append("tag='"); + buf.append(name); + buf.append("'"); + } + if (!TextUtils.isEmpty(num)) { + buf.append(" AND number='"); + buf.append(num); + buf.append("'"); + } + if (!TextUtils.isEmpty(emails)) { + buf.append(" AND emails='"); + buf.append(emails); + buf.append("'"); + } + if (!TextUtils.isEmpty(anrs)) { + buf.append(" AND anrs='"); + buf.append(anrs); + buf.append("'"); + } + + result = mResolver.delete(uri,buf.toString(),null); + return result; + + } + + private Uri getContentUri(int subscription) { + Uri uri = null; + long[] subId = SubscriptionManager.getSubId(subscription); + + if (subId != null && TelephonyManager.getDefault().isMultiSimEnabled()) { + uri = Uri.parse(SimContactsConstants.SIM_SUB_URI + subId[0]); + } else { + uri = Uri.parse(SimContactsConstants.SIM_URI); + } + return uri; + } + + private static Cursor setupAccountCursor(long contactId) { + ContentResolver resolver = mContext.getContentResolver(); + + Cursor cursor = null; + try { + cursor = resolver.query(RawContacts.CONTENT_URI, + ACCOUNT_PROJECTION, + RawContacts.CONTACT_ID + "=" + + Long.toString(contactId), null, null); + } catch (Exception e) { + Log.e(TAG, e.getMessage()); + } finally { + if (cursor != null && cursor.moveToFirst()) { + return cursor; + } + if (cursor != null) { + cursor.close(); + } + cursor = null; + return null; + } + } + + public static ContentValues getSimAccountValues(long contactId) { + ContentValues mValues = new ContentValues(); + Cursor cursor = setupAccountCursor(contactId); + if (cursor == null || cursor.getCount() == 0) { + mValues.clear(); + return mValues; + } + long rawContactId = cursor.getLong(cursor.getColumnIndex(RawContacts._ID)); + String accountName = cursor.getString(cursor.getColumnIndex(RawContacts.ACCOUNT_NAME)); + String accountType = cursor.getString(cursor.getColumnIndex(RawContacts.ACCOUNT_TYPE)); + cursor.close(); + if (SimContactsConstants.ACCOUNT_TYPE_SIM.equals(accountType)) { + mValues.clear(); + String name = getContactItems(rawContactId,StructuredName.CONTENT_ITEM_TYPE, + ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME); + mValues.put(SimContactsConstants.STR_TAG,name); + + String number = getContactPhoneNumber(rawContactId, + Phone.CONTENT_ITEM_TYPE, String.valueOf(Phone.TYPE_MOBILE), + ContactsContract.CommonDataKinds.Phone.DATA); + mValues.put(SimContactsConstants.STR_NUMBER,number); + + int sub = getSimSubscription(contactId); + + if (MoreContactUtils.canSaveAnr(sub)) { + String anrs = getContactPhoneNumber(rawContactId, + Phone.CONTENT_ITEM_TYPE, String.valueOf(Phone.TYPE_HOME), + ContactsContract.CommonDataKinds.Phone.DATA); + mValues.put(SimContactsConstants.STR_ANRS, anrs); + } + + if (MoreContactUtils.canSaveEmail(sub)) { + String emails = getContactItems(rawContactId, + Email.CONTENT_ITEM_TYPE, ContactsContract.CommonDataKinds.Email.DATA); + mValues.put(SimContactsConstants.STR_EMAILS, emails); + } + } + return mValues; + } + + public static int getSimSubscription(long contactId) { + int subscription = SimContactsConstants.SUB_INVALID; + Cursor cursor = setupAccountCursor(contactId); + if (cursor == null || cursor.getCount() == 0) { + return subscription; + } + + String accountName = cursor.getString(cursor.getColumnIndex(RawContacts.ACCOUNT_NAME)); + String accountType = cursor.getString(cursor.getColumnIndex(RawContacts.ACCOUNT_TYPE)); + if (accountType == null || accountName == null) { + return subscription; + } + if (SimContactsConstants.ACCOUNT_TYPE_SIM.equals(accountType)) { + subscription = MoreContactUtils.getSubscription(accountType, accountName); + } + cursor.close(); + return subscription; + } + + + private static String getContactItems(long rawContactId, String selectionArg, + String columnName) { + StringBuilder retval = new StringBuilder(); + Uri baseUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); + Uri dataUri = Uri.withAppendedPath(baseUri, RawContacts.Data.CONTENT_DIRECTORY); + + Cursor c = null; + try { + c = mContext.getContentResolver().query(dataUri, null, + Data.MIMETYPE + "=?", new String[] {selectionArg}, null); + if (c == null || c.getCount() == 0) { + if(c != null) { + c.close(); + } + return null; + } + c.moveToPosition(-1); + + while (c.moveToNext()) { + if (!TextUtils.isEmpty(retval.toString())) { + retval.append(","); + } + retval.append(c.getString(c.getColumnIndex(columnName))); + } + + } catch (Exception e) { + Log.e(TAG, e.getMessage()); + } finally { + if (c != null) { + c.close(); + } + } + + return retval.toString(); + } + + private static + String getContactPhoneNumber(long rawContactId, String selectionArg1, + String selectionArg2, String columnName) { + StringBuilder retval = new StringBuilder(); + Uri baseUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); + Uri dataUri = Uri.withAppendedPath(baseUri, RawContacts.Data.CONTENT_DIRECTORY); + + Cursor c = null; + try { + c = mContext.getContentResolver().query(dataUri, null, + Data.MIMETYPE + "=? AND " + Phone.TYPE + "=?", + new String[] {selectionArg1,selectionArg2}, null); + if (c == null || c.getCount() == 0) { + if(c != null) { + c.close(); + } + return null; + } + c.moveToPosition(-1); + + while (c.moveToNext()) { + if (!TextUtils.isEmpty(retval.toString())) { + retval.append(","); + } + retval.append(c.getString(c.getColumnIndex(columnName))); + } + + } catch (Exception e) { + Log.e(TAG, e.getMessage()); + } finally { + if (c != null) { + c.close(); + } + } + + return retval.toString(); + } + + + private void log(String msg) { + if (DBG) Log.d(TAG, msg); + } + +} diff --git a/src/com/android/contacts/common/list/ContactEntryListFragment.java b/src/com/android/contacts/common/list/ContactEntryListFragment.java index 62515e23..715fade1 100644 --- a/src/com/android/contacts/common/list/ContactEntryListFragment.java +++ b/src/com/android/contacts/common/list/ContactEntryListFragment.java @@ -19,9 +19,11 @@ package com.android.contacts.common.list; import android.app.Activity; import android.app.LoaderManager; import android.app.LoaderManager.LoaderCallbacks; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.CursorLoader; import android.content.Intent; +import android.content.IntentFilter; import android.content.Loader; import android.content.res.Resources; import android.database.Cursor; @@ -51,6 +53,7 @@ import com.android.contacts.common.preference.ContactsPreferences; import com.android.contacts.common.util.ContactListViewUtils; import com.android.contacts.common.util.SchedulingUtils; import com.android.dialerbind.analytics.AnalyticsFragment; +import com.android.internal.telephony.TelephonyIntents; import java.util.Locale; @@ -148,6 +151,13 @@ public abstract class ContactEntryListFragment selectionArgs = new ArrayList(); + boolean isAirMode = MoreContactUtils.isAPMOnAndSIMPowerDown(getContext()); + String disabledSimFilter = MoreContactUtils.getDisabledSimFilter(); + switch (filter.filterType) { case ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS: { // We have already added directory=0 to the URI, which takes care of this // filter + // Do not show contacts in SIM card when airplane mode is on + if (isAirMode) { + appendUriQueryParameterWithoutSim(loader, RawContacts.ACCOUNT_TYPE, + SimAccountType.ACCOUNT_TYPE); + } else if (!TextUtils.isEmpty(disabledSimFilter)) { + appendUriQueryParameterWithoutSim(loader, RawContacts.ACCOUNT_NAME, + disabledSimFilter); + } break; } case ContactListFilter.FILTER_TYPE_SINGLE_CONTACT: { @@ -163,6 +206,14 @@ public class DefaultContactListAdapter extends ContactListAdapter { if (isCustomFilterForPhoneNumbersOnly()) { selection.append(" AND " + Contacts.HAS_PHONE_NUMBER + "=1"); } + // Do not show contacts in SIM card when airplane mode is on + if (isAirMode) { + appendUriQueryParameterWithoutSim(loader, RawContacts.ACCOUNT_TYPE, + SimAccountType.ACCOUNT_TYPE); + } else if (!TextUtils.isEmpty(disabledSimFilter)) { + appendUriQueryParameterWithoutSim(loader, RawContacts.ACCOUNT_NAME, + disabledSimFilter); + } break; } case ContactListFilter.FILTER_TYPE_ACCOUNT: { diff --git a/src/com/android/contacts/common/list/PhoneNumberListAdapter.java b/src/com/android/contacts/common/list/PhoneNumberListAdapter.java index 8c8700c7..5f5e37f1 100644 --- a/src/com/android/contacts/common/list/PhoneNumberListAdapter.java +++ b/src/com/android/contacts/common/list/PhoneNumberListAdapter.java @@ -28,6 +28,8 @@ import android.provider.ContactsContract.CommonDataKinds.SipAddress; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.Directory; +import android.provider.ContactsContract.RawContacts; +import android.text.TextUtils; import android.telephony.PhoneNumberUtils; import android.text.TextUtils; import android.util.Log; @@ -41,6 +43,9 @@ import com.android.contacts.common.extensions.ExtendedPhoneDirectoriesManager; import com.android.contacts.common.extensions.ExtensionsFactory; import com.android.contacts.common.preference.ContactsPreferences; import com.android.contacts.common.util.Constants; +import com.android.contacts.common.SimContactsConstants; +import com.android.contacts.common.MoreContactUtils; +import com.android.contacts.common.model.account.SimAccountType; import java.util.ArrayList; import java.util.List; @@ -193,6 +198,13 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { // Remove duplicates when it is possible. builder.appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true"); + + // Do not show contacts in disabled SIM card + String disabledSimFilter = MoreContactUtils.getDisabledSimFilter(); + if (!TextUtils.isEmpty(disabledSimFilter)) { + String disabledSimName = getDisabledSimName(disabledSimFilter); + loader.setSelection(RawContacts.ACCOUNT_NAME+ "<>" + disabledSimName); + } loader.setUri(builder.build()); // TODO a projection that includes the search snippet @@ -541,4 +553,21 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { .encodedFragment(cursor.getString(lookUpKeyColumn)) .build(); } + + private String getDisabledSimName(String disabledSimFilter){ + String[] disabledSimArray = disabledSimFilter.split(",");//it will never be null + String disabledSimName = ""; + for (int i = 0; i < disabledSimArray.length; i++) { + if (i < disabledSimArray.length -1) { + //If disabledSimArray[i] is not the last one of the array, + //add "or" after every member of the array. + disabledSimName = disabledSimName + "'" + disabledSimArray[i] + "'" + "or"; + } else { + //If disabledSimArray[i] is the last one of the array, + //should not add anything after it. + disabledSimName = disabledSimName + "'" + disabledSimArray[i] + "'"; + } + } + return disabledSimName; + } } diff --git a/src/com/android/contacts/common/model/AccountTypeManager.java b/src/com/android/contacts/common/model/AccountTypeManager.java index 6efe8199..ba303c4d 100644 --- a/src/com/android/contacts/common/model/AccountTypeManager.java +++ b/src/com/android/contacts/common/model/AccountTypeManager.java @@ -38,6 +38,7 @@ import android.os.Message; import android.os.SystemClock; import android.provider.ContactsContract; import android.text.TextUtils; +import android.telephony.TelephonyManager; import android.util.Log; import android.util.TimingLogger; @@ -50,7 +51,10 @@ import com.android.contacts.common.model.account.ExchangeAccountType; import com.android.contacts.common.model.account.ExternalAccountType; import com.android.contacts.common.model.account.FallbackAccountType; import com.android.contacts.common.model.account.GoogleAccountType; +import com.android.contacts.common.model.account.PhoneAccountType; +import com.android.contacts.common.model.account.SimAccountType; import com.android.contacts.common.model.dataitem.DataKind; +import com.android.contacts.common.SimContactsConstants; import com.android.contacts.common.testing.NeededForTesting; import com.android.contacts.common.util.Constants; import com.google.common.annotations.VisibleForTesting; @@ -79,6 +83,13 @@ public abstract class AccountTypeManager { private static final Object mInitializationLock = new Object(); private static AccountTypeManager mAccountTypeManager; + public static final int FLAG_ALL_ACCOUNTS = 0; + public static final int FLAG_ALL_ACCOUNTS_WITHOUT_SIM = 1; + /** + * without sim and phone accounts + */ + public static final int FLAG_ALL_ACCOUNTS_WITHOUT_LOCAL = 2; + /** * Requests the singleton instance of {@link AccountTypeManager} with data bound from * the available authenticators. This method can safely be called from the UI thread. @@ -112,6 +123,8 @@ public abstract class AccountTypeManager { * contact writable accounts (if contactWritableOnly is true). */ // TODO: Consider splitting this into getContactWritableAccounts() and getAllAccounts() + public abstract List getAccounts(boolean contactWritableOnly, int flag); + public abstract List getAccounts(boolean contactWritableOnly); /** @@ -418,6 +431,10 @@ class AccountTypeManagerImpl extends AccountTypeManager accountType = new GoogleAccountType(mContext, auth.packageName); } else if (ExchangeAccountType.isExchangeType(type)) { accountType = new ExchangeAccountType(mContext, auth.packageName, type); + } else if (SimAccountType.ACCOUNT_TYPE.equals(type)) { + accountType = new SimAccountType(mContext, auth.packageName); + } else if (PhoneAccountType.ACCOUNT_TYPE.equals(type)) { + accountType = new PhoneAccountType(mContext, auth.packageName); } else { Log.d(TAG, "Registering external account type=" + type + ", packageName=" + auth.packageName); @@ -563,15 +580,86 @@ class AccountTypeManagerImpl extends AccountTypeManager return null; } + @Override + public List getAccounts(boolean contactWritableOnly) { + return getAccounts(contactWritableOnly, FLAG_ALL_ACCOUNTS); + } + /** * Return list of all known, contact writable {@link AccountWithDataSet}'s. */ @Override - public List getAccounts(boolean contactWritableOnly) { + public List getAccounts(boolean contactWritableOnly, + int flag) { ensureAccountsLoaded(); + boolean isAirMode = MoreContactUtils.isAPMOnAndSIMPowerDown(mContext); + switch (flag) { + case FLAG_ALL_ACCOUNTS: + return trimAccountByType( + contactWritableOnly ? mContactWritableAccounts : mAccounts, + isAirMode ? SimAccountType.ACCOUNT_TYPE : null); + case FLAG_ALL_ACCOUNTS_WITHOUT_LOCAL: + return trimAccountByType( + contactWritableOnly ? mContactWritableAccounts : mAccounts, + SimAccountType.ACCOUNT_TYPE, PhoneAccountType.ACCOUNT_TYPE); + case FLAG_ALL_ACCOUNTS_WITHOUT_SIM: + return trimAccountByType( + contactWritableOnly ? mContactWritableAccounts : mAccounts, + SimAccountType.ACCOUNT_TYPE); + } return contactWritableOnly ? mContactWritableAccounts : mAccounts; } + private boolean isSimStateUnknown(Account account) { + int subscription = SimContactsConstants.SUB_INVALID; + subscription = MoreContactUtils.getSubscription(account.type, account.name); + + if (subscription > SimContactsConstants.SUB_INVALID + && TelephonyManager.getDefault().getSimState(subscription) + == TelephonyManager.SIM_STATE_UNKNOWN) { + return true; + } else { + return false; + } + } + + private boolean isSimAccountInvalid(Account account) { + if ((SimContactsConstants.ACCOUNT_TYPE_SIM).equals(account.type)) { + if (TelephonyManager.getDefault().isMultiSimEnabled()) { + if (account.name.equals(SimContactsConstants.SIM_NAME)) + return true; + } else { + if (!account.name.equals(SimContactsConstants.SIM_NAME)) { + return true; + } + } + } + return false; + } + + private List trimAccountByType(final List list, + String... trimAccountTypes) { + List tempList = Lists.newArrayList(); + outer: for (AccountWithDataSet accountWithDataSet : list) { + if (trimAccountTypes != null && trimAccountTypes.length > 0) { + for (String type : trimAccountTypes) { + if (type != null && type.equals(accountWithDataSet.type)) { + continue outer; + } + } + } + + if (isSimStateUnknown(accountWithDataSet)) { + continue outer; + } + if (isSimAccountInvalid(accountWithDataSet)) { + continue outer; + } + tempList.add(accountWithDataSet); + } + return tempList; + } + /** * Return the list of all known, group writable {@link AccountWithDataSet}'s. */ diff --git a/src/com/android/contacts/common/model/RawContactDelta.java b/src/com/android/contacts/common/model/RawContactDelta.java index 7304f023..cf5bf304 100644 --- a/src/com/android/contacts/common/model/RawContactDelta.java +++ b/src/com/android/contacts/common/model/RawContactDelta.java @@ -24,15 +24,20 @@ import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; import android.provider.BaseColumns; +import android.provider.ContactsContract.CommonDataKinds.Email; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.CommonDataKinds.StructuredName; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.Profile; import android.provider.ContactsContract.RawContacts; +import android.text.TextUtils; import android.util.Log; import com.android.contacts.common.model.AccountTypeManager; import com.android.contacts.common.model.ValuesDelta; import com.android.contacts.common.model.account.AccountType; import com.android.contacts.common.testing.NeededForTesting; +import com.android.contacts.common.SimContactsConstants; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -416,6 +421,139 @@ public class RawContactDelta implements Parcelable { } } + public ContentValues buildSimDiff() { + ContentValues values = new ContentValues(); + ArrayList names = getMimeEntries(StructuredName.CONTENT_ITEM_TYPE); + ArrayList phones = getMimeEntries(Phone.CONTENT_ITEM_TYPE); + ArrayList emails = getMimeEntries(Email.CONTENT_ITEM_TYPE); + + ValuesDelta nameValuesDelta = null; + ValuesDelta emailValuesDelta = null; + + if (getMimeEntriesCount(StructuredName.CONTENT_ITEM_TYPE, true) > 0) { + nameValuesDelta = names.get(0); + names.get(0).putNull(StructuredName.GIVEN_NAME); + names.get(0).putNull(StructuredName.FAMILY_NAME); + names.get(0).putNull(StructuredName.PREFIX); + names.get(0).putNull(StructuredName.MIDDLE_NAME); + names.get(0).putNull(StructuredName.SUFFIX); + names.get(0).put(StructuredName.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); + } + if (emails != null && emails.size() > 0) { + emailValuesDelta = emails.get(0); + } + + String name = null; + String number = null; + String newName = null; + String newNumber = null; + StringBuilder email = new StringBuilder(); + StringBuilder anr = new StringBuilder(); + StringBuilder newEmail = new StringBuilder(); + StringBuilder newAnr = new StringBuilder(); + + if (nameValuesDelta != null) { + if (isContactInsert()) { + name = nameValuesDelta.getAsString(StructuredName.DISPLAY_NAME); + } else { + if (nameValuesDelta.getBefore() != null) { + name = nameValuesDelta.getBefore() + .getAsString(StructuredName.DISPLAY_NAME); + } + if (nameValuesDelta.getAfter() != null) { + newName = nameValuesDelta.getAfter() + .getAsString(StructuredName.DISPLAY_NAME); + } + } + } + + if (isContactInsert() && phones != null) { + for (ValuesDelta valuesDelta : phones) { + if (valuesDelta.getAfter() != null + && valuesDelta.getAfter().size() != 0) { + if (Phone.TYPE_MOBILE == valuesDelta.getAfter().getAsLong(Phone.TYPE)) { + if (TextUtils.isEmpty(number)) { + number = valuesDelta.getAfter().getAsString(Phone.NUMBER); + } else { + // don't supports to save two mobile number,so gives + // invalid str here + number = SimContactsConstants.STR_ANRS; + } + } else { + anr.append(valuesDelta.getAfter().getAsString(Phone.NUMBER)); + anr.append(","); + } + } + } + } else if(phones != null) { + for (ValuesDelta valuesDelta : phones) { + if (valuesDelta.getBefore() != null + && valuesDelta.getBefore().size() != 0) { + if (Phone.TYPE_MOBILE == valuesDelta.getBefore().getAsLong(Phone.TYPE) ) { + number = valuesDelta.getBefore().getAsString(Phone.NUMBER); + } else { + anr.append(valuesDelta.getBefore().getAsString(Phone.NUMBER)); + anr.append(","); + } + } + if (valuesDelta.getAfter() != null + && valuesDelta.getAfter().size() != 0) { + if (Phone.TYPE_MOBILE == valuesDelta.getAsLong(Phone.TYPE)) { + if (TextUtils.isEmpty(newNumber)) { + newNumber = valuesDelta.getAfter().getAsString(Phone.NUMBER); + } else { + newNumber = SimContactsConstants.STR_ANRS; + } + } else { + newAnr.append(valuesDelta.getAfter().getAsString(Phone.NUMBER)); + newAnr.append(","); + } + } + } + } + + if (isContactInsert() && emails != null) { + for (ValuesDelta valuesDelta : emails) { + if (valuesDelta.getAfter() != null + && valuesDelta.getAfter().size() != 0) { + email.append(valuesDelta.getAfter().getAsString(Email.DATA)); + email.append(","); + } + } + } else if (emails != null) { + for (ValuesDelta valuesDelta : emails) { + if (valuesDelta.getBefore() != null + && valuesDelta.getBefore().size() != 0) { + email.append(valuesDelta.getBefore().getAsString(Email.DATA)); + email.append(","); + } + if (valuesDelta.getAfter() != null + && valuesDelta.getAfter().size() != 0) { + newEmail.append(valuesDelta.getAfter().getAsString(Email.DATA)); + newEmail.append(","); + } + } + } + + if (isContactInsert()) { + if (name != null || number != null || anr != null || email != null) { + values.put(SimContactsConstants.STR_TAG, name); + values.put(SimContactsConstants.STR_NUMBER, number); + values.put(SimContactsConstants.STR_EMAILS, email.toString()); + values.put(SimContactsConstants.STR_ANRS, anr.toString()); + } + } else { + values.put(SimContactsConstants.STR_TAG, name); + values.put(SimContactsConstants.STR_NUMBER, number); + values.put(SimContactsConstants.STR_EMAILS, email.toString()); + values.put(SimContactsConstants.STR_ANRS, anr.toString()); + values.put(SimContactsConstants.STR_NEW_TAG, newName); + values.put(SimContactsConstants.STR_NEW_NUMBER, newNumber); + values.put(SimContactsConstants.STR_NEW_EMAILS, newEmail.toString()); + values.put(SimContactsConstants.STR_NEW_ANRS, newAnr.toString()); + } + return values; + } /** * Build a list of {@link ContentProviderOperation} that will transform the * current "before" {@link Entity} state into the modified state which this diff --git a/src/com/android/contacts/common/model/ValuesDelta.java b/src/com/android/contacts/common/model/ValuesDelta.java index e30cdd62..2db7e1c8 100644 --- a/src/com/android/contacts/common/model/ValuesDelta.java +++ b/src/com/android/contacts/common/model/ValuesDelta.java @@ -23,6 +23,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.provider.BaseColumns; import android.provider.ContactsContract; +import android.provider.ContactsContract.Data; import com.android.contacts.common.testing.NeededForTesting; import com.google.common.collect.Sets; @@ -61,6 +62,17 @@ public class ValuesDelta implements Parcelable { final ValuesDelta entry = new ValuesDelta(); entry.mBefore = before; entry.mAfter = new ContentValues(); + + // init data1 to mAfter map. when no operation edittext of + // sim phone in the UI, the mAfter init have no data1 value, + // it will cause the builddiff data not right. + if (before.containsKey(Data.DATA1)) { + String contactInfo = before.getAsString(Data.DATA1); + if (null != contactInfo && !"".equals(contactInfo)) { + entry.mAfter.put(Data.DATA1, contactInfo); + } + } + return entry; } @@ -83,6 +95,10 @@ public class ValuesDelta implements Parcelable { return mAfter; } + public ContentValues getBefore() { + return mBefore; + } + public boolean containsKey(String key) { return ((mAfter != null && mAfter.containsKey(key)) || (mBefore != null && mBefore.containsKey(key))); diff --git a/src/com/android/contacts/common/model/account/AccountType.java b/src/com/android/contacts/common/model/account/AccountType.java index 53ab47d9..560d33d0 100644 --- a/src/com/android/contacts/common/model/account/AccountType.java +++ b/src/com/android/contacts/common/model/account/AccountType.java @@ -16,17 +16,22 @@ package com.android.contacts.common.model.account; +import android.accounts.AccountManager; +import android.accounts.AuthenticatorDescription; import android.content.ContentValues; import android.content.Context; import android.content.pm.PackageManager; +import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.RawContacts; +import android.util.Log; import android.view.inputmethod.EditorInfo; import android.widget.EditText; +import com.android.contacts.common.MoreContactUtils; import com.android.contacts.common.R; import com.android.contacts.common.model.dataitem.DataKind; import com.google.common.annotations.VisibleForTesting; @@ -39,6 +44,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Internal structure that represents constraints and styles for a specific data @@ -96,6 +102,11 @@ public abstract class AccountType { protected boolean mIsInitialized; + private Map mTypeToAuthDescription + = new HashMap(); + + private AuthenticatorDescription[] mAuthDescs; + protected static class DefinitionException extends Exception { public DefinitionException(String message) { super(message); @@ -196,12 +207,6 @@ public abstract class AccountType { public String getViewGroupActivity() { return null; } - - public CharSequence getDisplayLabel(Context context) { - // Note this resource is defined in the sync adapter package, not resourcePackageName. - return getResourceText(context, syncAdapterPackageName, titleRes, accountType); - } - /** * @return resource ID for the "invite contact" action label, or -1 if not defined. */ @@ -276,15 +281,47 @@ public abstract class AccountType { } } + public void updateAuthDescriptions(Context context) { + mAuthDescs = AccountManager.get(context).getAuthenticatorTypes(); + for (int i = 0; i < mAuthDescs.length; i++) { + mTypeToAuthDescription.put(mAuthDescs[i].type, mAuthDescs[i]); + } + } + public CharSequence getDisplayLabel(Context context) { + CharSequence label = null; + updateAuthDescriptions(context); + if (mTypeToAuthDescription.containsKey(accountType)) { + try { + AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType); + Context authContext = context.createPackageContext(desc.packageName, 0); + label = authContext.getResources().getText(desc.labelId); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "No label name for account type " + accountType); + } catch (Resources.NotFoundException e) { + Log.w(TAG, "No label icon for account type " + accountType); + } + } + return label; + } + public Drawable getDisplayIcon(Context context) { - if (this.titleRes != -1 && this.syncAdapterPackageName != null) { - final PackageManager pm = context.getPackageManager(); - return pm.getDrawable(this.syncAdapterPackageName, this.iconRes, null); - } else if (this.titleRes != -1) { - return context.getResources().getDrawable(this.iconRes); - } else { - return null; + Drawable icon = null; + updateAuthDescriptions(context); + if (mTypeToAuthDescription.containsKey(accountType)) { + try { + AuthenticatorDescription desc = mTypeToAuthDescription + .get(accountType); + Context authContext = context.createPackageContext( + desc.packageName, 0); + icon = authContext.getResources().getDrawable(desc.iconId); + } catch (PackageManager.NameNotFoundException e) { + } catch (Resources.NotFoundException e) { + } + } + if (icon == null) { + icon = context.getPackageManager().getDefaultActivityIcon(); } + return icon; } /** diff --git a/src/com/android/contacts/common/model/account/PhoneAccountType.java b/src/com/android/contacts/common/model/account/PhoneAccountType.java new file mode 100644 index 00000000..f6e1f1f5 --- /dev/null +++ b/src/com/android/contacts/common/model/account/PhoneAccountType.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2014, The Linux Foundation. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.android.contacts.common.model.account; + +import android.content.Context; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; +import android.os.SystemProperties; +import android.provider.ContactsContract.CommonDataKinds.Email; +import android.provider.ContactsContract.CommonDataKinds.StructuredName; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.telephony.TelephonyManager; +import android.util.Log; +import android.view.inputmethod.EditorInfo; + +import com.android.contacts.common.R; +import com.android.contacts.common.SimContactsConstants; +import com.android.contacts.common.model.account.AccountType.DefinitionException; +import com.google.android.collect.Lists; + + +public class PhoneAccountType extends BaseAccountType{ + private static final String TAG = "PhoneAccountType"; + + public static final String ACCOUNT_TYPE = SimContactsConstants.ACCOUNT_TYPE_PHONE; + public static final int FLAGS_PERSON_NAME = EditorInfo.TYPE_CLASS_TEXT + | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS | EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME; + protected static final int FLAGS_PHONE = EditorInfo.TYPE_CLASS_PHONE; + + public PhoneAccountType(Context context, String resPackageName) { + this.accountType = ACCOUNT_TYPE; + this.resourcePackageName = null; + this.syncAdapterPackageName = resPackageName; + + try { + + addDataKindStructuredName(context); + addDataKindDisplayName(context); + addDataKindPhoneticName(context); + addDataKindNickname(context); + addDataKindPhone(context); + addDataKindEmail(context); + addDataKindStructuredPostal(context); + addDataKindIm(context); + addDataKindOrganization(context); + addDataKindPhoto(context); + addDataKindNote(context); + addDataKindWebsite(context); + //addDataKindGroupMembership(context); + if (context.getResources().getBoolean( + com.android.internal.R.bool.config_built_in_sip_phone)) { + addDataKindSipAddress(context); + } + + mIsInitialized = true; + } catch (DefinitionException e) { + Log.e(TAG, "Problem building account type", e); + } + + } + + @Override + public boolean isGroupMembershipEditable() { + return true; + } + + @Override + public boolean areContactsWritable() { + return true; + } + +} diff --git a/src/com/android/contacts/common/model/account/SimAccountType.java b/src/com/android/contacts/common/model/account/SimAccountType.java new file mode 100644 index 00000000..6473f2b0 --- /dev/null +++ b/src/com/android/contacts/common/model/account/SimAccountType.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2014, The Linux Foundation. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.android.contacts.common.model.account; + +import android.content.Context; +import android.provider.ContactsContract.CommonDataKinds.Email; +import android.provider.ContactsContract.CommonDataKinds.StructuredName; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.util.Log; +import android.view.inputmethod.EditorInfo; +import android.graphics.drawable.Drawable; + +import com.android.contacts.common.R; +import com.android.contacts.common.SimContactsConstants; +import com.android.contacts.common.model.account.AccountType.DefinitionException; +import com.android.contacts.common.model.dataitem.DataKind; +import com.google.android.collect.Lists; + +public class SimAccountType extends BaseAccountType{ + private static final String TAG = "SimContactsType"; + + public static final String ACCOUNT_TYPE = SimContactsConstants.ACCOUNT_TYPE_SIM; + public static final int FLAGS_PERSON_NAME = EditorInfo.TYPE_CLASS_TEXT + | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS | EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME; + public static final int FLAGS_PHONETIC = EditorInfo.TYPE_CLASS_TEXT + | EditorInfo.TYPE_TEXT_VARIATION_PHONETIC; + protected static final int FLAGS_PHONE = EditorInfo.TYPE_CLASS_PHONE; + private static Context mContext; + + public SimAccountType(Context context, String resPackageName) { + this.accountType = ACCOUNT_TYPE; + this.resourcePackageName = resPackageName; + this.syncAdapterPackageName = resPackageName; + + this.mContext = context; + + try { + addDataKindStructuredName(context); + addDataKindDisplayName(context); + addDataKindPhone(context); + addDataKindEmail(context); + mIsInitialized = true; + } catch (DefinitionException e) { + Log.e(TAG, "Problem building account type", e); + } + } + + @Override + protected DataKind addDataKindStructuredName(Context context) throws DefinitionException { + final DataKind kind = super.addDataKindStructuredName(context); + kind.fieldList = Lists.newArrayList(); + kind.fieldList.add(new EditField(StructuredName.DISPLAY_NAME, R.string.nameLabelsGroup, + FLAGS_PERSON_NAME)); + + return kind; + } + + @Override + protected DataKind addDataKindPhone(Context context) throws DefinitionException { + final DataKind kind = super.addDataKindPhone(context); + + kind.iconAltRes = R.drawable.ic_text_holo_light; + kind.typeOverallMax = 2; + kind.typeColumn = Phone.TYPE; + kind.typeList = Lists.newArrayList(); + kind.typeList.add(buildPhoneType(Phone.TYPE_MOBILE)); + kind.typeList.add(buildPhoneType(Phone.TYPE_HOME));// This is used to save ANR records + kind.fieldList = Lists.newArrayList(); + kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup, FLAGS_PHONE)); + + return kind; + } + + @Override + protected DataKind addDataKindEmail(Context context) throws DefinitionException { + final DataKind kind = super.addDataKindEmail(context); + + kind.typeOverallMax = 1; + kind.typeColumn = Email.TYPE; + kind.fieldList = Lists.newArrayList(); + kind.fieldList.add(new EditField(Email.ADDRESS, R.string.emailLabelsGroup, FLAGS_EMAIL)); + return kind; + } + + + @Override + public boolean isGroupMembershipEditable() { + return true; + } + + @Override + public boolean areContactsWritable() { + return true; + } + +} diff --git a/src/com/android/contacts/common/util/AccountSelectionUtil.java b/src/com/android/contacts/common/util/AccountSelectionUtil.java index 8f314882..6a573574 100644 --- a/src/com/android/contacts/common/util/AccountSelectionUtil.java +++ b/src/com/android/contacts/common/util/AccountSelectionUtil.java @@ -63,7 +63,7 @@ public class AccountSelectionUtil { final private Context mContext; final private int mResId; - final protected List mAccountList; + protected List mAccountList; public AccountSelectedListener(Context context, List accountList, int resId) { @@ -79,6 +79,15 @@ public class AccountSelectionUtil { dialog.dismiss(); doImport(mContext, mResId, mAccountList.get(which)); } + /** + * Reset the account list for this listener, to make sure the selected + * items reflect the displayed items. + * + * @param accountList The reset account list. + */ + void setAccountList(List accountList) { + mAccountList = accountList; + } } public static Dialog getSelectAccountDialog(Context context, int resId) { @@ -90,15 +99,28 @@ public class AccountSelectionUtil { return getSelectAccountDialog(context, resId, onClickListener, null); } + public static Dialog getSelectAccountDialog(Context context, int resId, + DialogInterface.OnClickListener onClickListener, + DialogInterface.OnCancelListener onCancelListener) { + return getSelectAccountDialog(context, resId, onClickListener, + onCancelListener, true); + } + /** * When OnClickListener or OnCancelListener is null, uses a default listener. * The default OnCancelListener just closes itself with {@link Dialog#dismiss()}. */ public static Dialog getSelectAccountDialog(Context context, int resId, DialogInterface.OnClickListener onClickListener, - DialogInterface.OnCancelListener onCancelListener) { + DialogInterface.OnCancelListener onCancelListener, boolean includeSIM) { final AccountTypeManager accountTypes = AccountTypeManager.getInstance(context); - final List writableAccountList = accountTypes.getAccounts(true); + List writableAccountList = accountTypes.getAccounts(true); + if (includeSIM) { + writableAccountList = accountTypes.getAccounts(true); + } else { + writableAccountList = accountTypes.getAccounts(true, + AccountTypeManager.FLAG_ALL_ACCOUNTS_WITHOUT_SIM); + } Log.i(LOG_TAG, "The number of available accounts: " + writableAccountList.size()); @@ -143,6 +165,13 @@ public class AccountSelectionUtil { AccountSelectedListener accountSelectedListener = new AccountSelectedListener(context, writableAccountList, resId); onClickListener = accountSelectedListener; + } else if (onClickListener instanceof AccountSelectedListener) { + // Because the writableAccountList is different if includeSIM or not, so + // should reset the account list for the AccountSelectedListener which + // is initialized with FLAG_ALL_ACCOUNTS. + // Reset the account list to make sure the selected account is contained + // in these display accounts. + ((AccountSelectedListener) onClickListener).setAccountList(writableAccountList); } if (onCancelListener == null) { onCancelListener = new DialogInterface.OnCancelListener() { diff --git a/src/com/android/contacts/common/util/AccountsListAdapter.java b/src/com/android/contacts/common/util/AccountsListAdapter.java index 84435df8..575dddbd 100644 --- a/src/com/android/contacts/common/util/AccountsListAdapter.java +++ b/src/com/android/contacts/common/util/AccountsListAdapter.java @@ -29,6 +29,8 @@ import com.android.contacts.common.R; import com.android.contacts.common.model.AccountTypeManager; import com.android.contacts.common.model.account.AccountType; import com.android.contacts.common.model.account.AccountWithDataSet; +import com.android.contacts.common.model.account.PhoneAccountType; +import com.android.contacts.common.model.account.SimAccountType; import java.util.ArrayList; import java.util.List; @@ -48,7 +50,8 @@ public final class AccountsListAdapter extends BaseAdapter { public enum AccountListFilter { ALL_ACCOUNTS, // All read-only and writable accounts ACCOUNTS_CONTACT_WRITABLE, // Only where the account type is contact writable - ACCOUNTS_GROUP_WRITABLE // Only accounts where the account type is group writable + ACCOUNTS_GROUP_WRITABLE, // Only accounts where the account type is group writable + ACCOUNTS_CONTACT_WRITABLE_WITHOUT_SIM } public AccountsListAdapter(Context context, AccountListFilter accountListFilter) { @@ -75,10 +78,22 @@ public final class AccountsListAdapter extends BaseAdapter { private List getAccounts(AccountListFilter accountListFilter) { if (accountListFilter == AccountListFilter.ACCOUNTS_GROUP_WRITABLE) { - return new ArrayList(mAccountTypes.getGroupWritableAccounts()); + return new ArrayList(mAccountTypes.getAccounts(true, + AccountTypeManager.FLAG_ALL_ACCOUNTS_WITHOUT_LOCAL)); } - return new ArrayList(mAccountTypes.getAccounts( - accountListFilter == AccountListFilter.ACCOUNTS_CONTACT_WRITABLE)); + final List writableAccountList = mAccountTypes + .getAccounts(accountListFilter == AccountListFilter.ACCOUNTS_CONTACT_WRITABLE + || accountListFilter == AccountListFilter.ACCOUNTS_CONTACT_WRITABLE_WITHOUT_SIM); + List deletedList = new ArrayList(); + + if (accountListFilter == AccountListFilter.ACCOUNTS_CONTACT_WRITABLE_WITHOUT_SIM) { + for (AccountWithDataSet account : writableAccountList) { + if (SimAccountType.ACCOUNT_TYPE.equals(account.type)) + deletedList.add(account); + } + writableAccountList.removeAll(deletedList); + } + return writableAccountList; } @Override -- cgit v1.2.3