diff options
| author | Matt Garnes <matt@cyngn.com> | 2015-04-30 11:16:26 -0700 |
|---|---|---|
| committer | Matt Garnes <matt@cyngn.com> | 2015-04-30 11:16:26 -0700 |
| commit | 1d6073e5d62800910e9d0f5c701b4e19b49588e9 (patch) | |
| tree | 60f952095770b38e34394288409f21a0b0e21948 | |
| parent | fae1598668b4266cad74dec51f0a413075282039 (diff) | |
| parent | 402701dafda192618d02483ea6e513be4af064e7 (diff) | |
| download | packages_apps_ContactsCommon-1d6073e5d62800910e9d0f5c701b4e19b49588e9.tar.gz packages_apps_ContactsCommon-1d6073e5d62800910e9d0f5c701b4e19b49588e9.tar.bz2 packages_apps_ContactsCommon-1d6073e5d62800910e9d0f5c701b4e19b49588e9.zip | |
Merge remote-tracking branch 'caf/LA.BR.1.2.3' into HEAD
28 files changed, 826 insertions, 123 deletions
diff --git a/res/layout/default_account_checkbox.xml b/res/layout/default_account_checkbox.xml new file mode 100644 index 00000000..9a1a4504 --- /dev/null +++ b/res/layout/default_account_checkbox.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/default_account_checkbox_layout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="4dp" + android:orientation="vertical"> + <CheckBox + android:id="@+id/default_account_checkbox_view" + android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingStart="15dip" + android:layout_marginLeft="13dip" + android:layout_marginBottom="20dip" + android:gravity="center" + android:textAlignment="viewStart" + android:text="@string/set_default_account" + android:textColor="@color/dialtacts_secondary_text_color" + /> +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/select_account_list_item.xml b/res/layout/select_account_list_item.xml new file mode 100644 index 00000000..39a5af05 --- /dev/null +++ b/res/layout/select_account_list_item.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 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. +--> + +<!-- Layout of a single item in the InCallUI Account Chooser Dialog. --> +<view class="com.android.contacts.common.widget.ActivityTouchLinearLayout" + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="8dp" > + + <ImageView android:id="@+id/icon" + android:layout_width="48dp" + android:layout_height="48dp" + android:tint="@color/dialtacts_secondary_text_color" + android:scaleType="center" /> + + <LinearLayout + android:id="@+id/text" + android:gravity="start|center_vertical" + android:layout_marginLeft="8dp" + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="match_parent" + android:orientation="vertical" > + <TextView android:id="@+id/label" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="@color/dialtacts_primary_text_color" + android:includeFontPadding="false" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + <TextView android:id="@+id/number" + android:textAppearance="?android:attr/textAppearanceSmall" + android:includeFontPadding="false" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="gone" /> + </LinearLayout> + +</view> diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 66d3455b..c300e05a 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -283,4 +283,5 @@ <string name="no_ip_number_on_sim_card">SIM卡上没有IP号</string> <string name="ipcall_dialog_title">IP电话设置</string> <string name="ipcall_dialog_edit_hint">请输入IP电话前缀</string> + <string name="label_groups">群组</string> </resources> diff --git a/res/values/colors.xml b/res/values/colors.xml index 1da07fb8..e12634a6 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -46,6 +46,9 @@ <!-- Color of image view placeholder. --> <color name="image_placeholder">#DDDDDD</color> + <!-- Primary text color in the Phone app --> + <color name="dialtacts_primary_text_color">#333333</color> + <!-- Secondary text color in the Phone app --> <color name="dialtacts_secondary_text_color">#737373</color> diff --git a/res/values/strings.xml b/res/values/strings.xml index 9695179e..296d5878 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -840,4 +840,12 @@ a ren't members of any other group. [CHAR LIMIT=25] --> <string name="import_contacts_sim">Import contacts from SIM?</string> <string name="import_contacts_sim_confirm">Import</string> <string name="import_contacts_sim_cancel">Cancel</string> + <!-- Title for Select Account Dialog [CHAR LIMIT=30] --> + <string name="select_account_dialog_title">Select Account</string> + + <!-- Label for the check box to toggle default sim card setting [CHAR LIMIT=35]--> + <string name="set_default_account">Always use this for calls</string> + + <!-- Title for dialog to select Phone Account for outgoing call. [CHAR LIMIT=40] --> + <string name="select_phone_account_for_calls">Call with</string> </resources> diff --git a/src/com/android/contacts/common/CallUtil.java b/src/com/android/contacts/common/CallUtil.java index 7a280f26..ed22a731 100644 --- a/src/com/android/contacts/common/CallUtil.java +++ b/src/com/android/contacts/common/CallUtil.java @@ -141,6 +141,13 @@ public class CallUtil { } /** + * A variant of {@link #getCallIntent(android.net.Uri)} for calling Voicemail. + */ + public static Intent getVoicemailIntent() { + return getCallIntent(Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "", null)); + } + + /** * A variant of {@link #getCallIntent(android.net.Uri)} but also accept a call * origin and {@code Account} and {@code VideoCallProfile} state. * For more information about call origin, see comments in Phone package (PhoneApp). diff --git a/src/com/android/contacts/common/ContactsUtils.java b/src/com/android/contacts/common/ContactsUtils.java index 857450d9..a6e0e0e8 100644 --- a/src/com/android/contacts/common/ContactsUtils.java +++ b/src/com/android/contacts/common/ContactsUtils.java @@ -41,6 +41,8 @@ public class ContactsUtils { public static final String SCHEME_MAILTO = "mailto"; public static final String SCHEME_SMSTO = "smsto"; + private static final int DEFAULT_THUMBNAIL_SIZE = 96; + private static int sThumbnailSize = -1; // TODO find a proper place for the canonical version of these @@ -139,14 +141,17 @@ public class ContactsUtils { final Cursor c = context.getContentResolver().query( DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI, new String[] { DisplayPhoto.THUMBNAIL_MAX_DIM }, null, null, null); - try { - c.moveToFirst(); - sThumbnailSize = c.getInt(0); - } finally { - c.close(); + if (c != null) { + try { + if (c.moveToFirst()) { + sThumbnailSize = c.getInt(0); + } + } finally { + c.close(); + } } } - return sThumbnailSize; + return sThumbnailSize != -1 ? sThumbnailSize : DEFAULT_THUMBNAIL_SIZE; } private static Intent getCustomImIntent(ImDataItem im, int protocol) { diff --git a/src/com/android/contacts/common/interactions/ImportExportDialogFragment.java b/src/com/android/contacts/common/interactions/ImportExportDialogFragment.java index ef99300a..63fe01d6 100755 --- a/src/com/android/contacts/common/interactions/ImportExportDialogFragment.java +++ b/src/com/android/contacts/common/interactions/ImportExportDialogFragment.java @@ -580,6 +580,26 @@ public class ImportExportDialogFragment extends AnalyticsDialogFragment TOAST_EXPORT_FAILED, insertCount, 0)); break; } else { + // Failed to insert to SIM card + int anrNumber = 0; + if (!TextUtils.isEmpty(anrNum)) { + anrNumber += anrNum.toString().split( + SimContactsConstants.ANR_SEP).length; + } + // reset emptyNumber and emptyAnr to the value before + // the insert operation + emptyAnr += anrNumber; + emptyNumber += anrNumber; + if (!TextUtils.isEmpty(num)) { + emptyNumber++; + } + + if (!TextUtils.isEmpty(email)) { + // reset emptyEmail to the value before the insert + // operation + emptyEmail += email.toString().split( + SimContactsConstants.EMAIL_SEP).length; + } continue; } } diff --git a/src/com/android/contacts/common/list/ContactListAdapter.java b/src/com/android/contacts/common/list/ContactListAdapter.java index b987e4d8..ea2d8539 100755 --- a/src/com/android/contacts/common/list/ContactListAdapter.java +++ b/src/com/android/contacts/common/list/ContactListAdapter.java @@ -407,8 +407,7 @@ public abstract class ContactListAdapter extends ContactEntryListAdapter { super.changeCursor(partitionIndex, cursor); // Check if a profile exists - if (cursor != null && cursor.getCount() > 0) { - cursor.moveToFirst(); + if (cursor != null && cursor.moveToFirst()) { setProfileExists(cursor.getInt(ContactQuery.CONTACT_IS_USER_PROFILE) == 1); } } diff --git a/src/com/android/contacts/common/list/ContactListItemView.java b/src/com/android/contacts/common/list/ContactListItemView.java index 281f7ea3..913eb20a 100644 --- a/src/com/android/contacts/common/list/ContactListItemView.java +++ b/src/com/android/contacts/common/list/ContactListItemView.java @@ -50,6 +50,7 @@ import com.android.contacts.common.ContactPresenceIconUtil; import com.android.contacts.common.ContactStatusUtil; import com.android.contacts.common.R; import com.android.contacts.common.format.TextHighlighter; +import com.android.contacts.common.util.ContactDisplayUtils; import com.android.contacts.common.util.SearchUtil; import com.android.contacts.common.util.ViewUtil; import com.android.contacts.common.util.ContactsCommonRcsUtil; @@ -1190,6 +1191,13 @@ public class ContactListItemView extends ViewGroup } else { mTextHighlighter.setPrefixText(getSnippetView(), text, mHighlightedPrefix); mSnippetView.setVisibility(VISIBLE); + if (ContactDisplayUtils.isPossiblePhoneNumber(text)) { + // Give the text-to-speech engine a hint that it's a phone number + mSnippetView.setContentDescription( + ContactDisplayUtils.getTelephoneTtsSpannable(text)); + } else { + mSnippetView.setContentDescription(null); + } } } @@ -1334,6 +1342,14 @@ public class ContactListItemView extends ViewGroup name = mUnknownNameText; } setMarqueeText(getNameTextView(), name); + + if (ContactDisplayUtils.isPossiblePhoneNumber(name)) { + // Give the text-to-speech engine a hint that it's a phone number + mNameTextView.setContentDescription( + ContactDisplayUtils.getTelephoneTtsSpannable(name.toString())); + } else { + mNameTextView.setContentDescription(null); + } } public void hideDisplayName() { diff --git a/src/com/android/contacts/common/list/CustomContactListFilterActivity.java b/src/com/android/contacts/common/list/CustomContactListFilterActivity.java index b0bf9c3d..5066b294 100755 --- a/src/com/android/contacts/common/list/CustomContactListFilterActivity.java +++ b/src/com/android/contacts/common/list/CustomContactListFilterActivity.java @@ -148,9 +148,12 @@ public class CustomContactListFilterActivity extends Activity if (account.dataSet != null) { groupsUri.appendQueryParameter(Groups.DATA_SET, account.dataSet).build(); } + final Cursor cursor = resolver.query(groupsUri.build(), null, null, null, null); + if (cursor == null) { + continue; + } android.content.EntityIterator iterator = - ContactsContract.Groups.newEntityIterator(resolver.query( - groupsUri.build(), null, null, null, null)); + ContactsContract.Groups.newEntityIterator(cursor); try { boolean hasGroups = false; diff --git a/src/com/android/contacts/common/list/DefaultContactListAdapter.java b/src/com/android/contacts/common/list/DefaultContactListAdapter.java index 452e0bb7..cce1909b 100644 --- a/src/com/android/contacts/common/list/DefaultContactListAdapter.java +++ b/src/com/android/contacts/common/list/DefaultContactListAdapter.java @@ -273,16 +273,17 @@ public class DefaultContactListAdapter extends ContactListAdapter { if (ContactsCommonRcsUtil.getIsRcs()) { Long contactId = cursor.getLong(ContactQuery.CONTACT_ID); - boolean isUserProfile = cursor - .getInt(ContactQuery.CONTACT_IS_USER_PROFILE) == 1; - if (ContactsCommonRcsUtil.RcsCapabilityMapCache.containsKey(contactId)) { + boolean isUserProfile = cursor.getInt(ContactQuery.CONTACT_IS_USER_PROFILE) == 1; + if (ContactsCommonRcsUtil.RcsCapabilityMapCache.containsKey(contactId) + && ContactsCommonRcsUtil.RcsCapabilityMapCache.get(contactId) != null + && ContactsCommonRcsUtil.RcsCapabilityMapCache.get(contactId)) { ContactsCommonRcsUtil.RcsCapabilityMap.put(contactId, ContactsCommonRcsUtil.RcsCapabilityMapCache .get(contactId)); } - if (!isUserProfile - && ContactsCommonRcsUtil.RcsCapabilityMap - .containsKey(contactId)) { + if (!isUserProfile && ContactsCommonRcsUtil.RcsCapabilityMap.containsKey(contactId) + && ContactsCommonRcsUtil.RcsCapabilityMapCache.get(contactId) != null + && ContactsCommonRcsUtil.RcsCapabilityMapCache.get(contactId)) { view.setRCSCapability(mContext.getDrawable(R.drawable.rcs_capacity_icon), ContactsCommonRcsUtil.RcsCapabilityMap.get(contactId)); } else { diff --git a/src/com/android/contacts/common/list/ProfileAndContactsLoader.java b/src/com/android/contacts/common/list/ProfileAndContactsLoader.java index c19737d9..698ef96f 100644 --- a/src/com/android/contacts/common/list/ProfileAndContactsLoader.java +++ b/src/com/android/contacts/common/list/ProfileAndContactsLoader.java @@ -61,8 +61,8 @@ public class ProfileAndContactsLoader extends CursorLoader { Cursor cursor = null; try { cursor = super.loadInBackground(); - } catch (NullPointerException e) { - // Ignore NPEs thrown by providers + } catch (NullPointerException | SecurityException e) { + // Ignore NPEs and SecurityExceptions thrown by providers } final Cursor contactsCursor = cursor; cursors.add(contactsCursor); diff --git a/src/com/android/contacts/common/list/ViewPagerTabs.java b/src/com/android/contacts/common/list/ViewPagerTabs.java index ec95de6f..006d6321 100644 --- a/src/com/android/contacts/common/list/ViewPagerTabs.java +++ b/src/com/android/contacts/common/list/ViewPagerTabs.java @@ -198,7 +198,12 @@ public class ViewPagerTabs extends HorizontalScrollView implements ViewPager.OnP @Override public void onPageSelected(int position) { position = getRtlPosition(position); - if (mPrevSelected >= 0) { + int tabStripChildCount = mTabStrip.getChildCount(); + if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) { + return; + } + + if (mPrevSelected >= 0 && mPrevSelected < tabStripChildCount) { mTabStrip.getChildAt(mPrevSelected).setSelected(false); } final View selectedChild = mTabStrip.getChildAt(position); diff --git a/src/com/android/contacts/common/location/UpdateCountryService.java b/src/com/android/contacts/common/location/UpdateCountryService.java index e339306f..9403187e 100644 --- a/src/com/android/contacts/common/location/UpdateCountryService.java +++ b/src/com/android/contacts/common/location/UpdateCountryService.java @@ -38,6 +38,10 @@ public class UpdateCountryService extends IntentService { @Override protected void onHandleIntent(Intent intent) { + if (intent == null) { + Log.d(TAG, "onHandleIntent: could not handle null intent"); + return; + } if (ACTION_UPDATE_COUNTRY.equals(intent.getAction())) { final Location location = (Location) intent.getParcelableExtra(KEY_INTENT_LOCATION); final String country = getCountryFromLocation(getApplicationContext(), location); diff --git a/src/com/android/contacts/common/model/ContactLoader.java b/src/com/android/contacts/common/model/ContactLoader.java index eba825b1..59ab292e 100644 --- a/src/com/android/contacts/common/model/ContactLoader.java +++ b/src/com/android/contacts/common/model/ContactLoader.java @@ -63,9 +63,11 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; /** @@ -316,7 +318,7 @@ public class ContactLoader extends AsyncTaskLoader<Contact> { resultIsCached = true; } else { if (uriCurrentFormat.getLastPathSegment().equals(Constants.LOOKUP_URI_ENCODED)) { - result = loadEncodedContactEntity(uriCurrentFormat); + result = loadEncodedContactEntity(uriCurrentFormat, mLookupUri); } else { result = loadContactEntity(resolver, uriCurrentFormat); } @@ -349,7 +351,22 @@ public class ContactLoader extends AsyncTaskLoader<Contact> { } } - private Contact loadEncodedContactEntity(Uri uri) throws JSONException { + /** + * Parses a {@link Contact} stored as a JSON string in a lookup URI. + * + * @param lookupUri The contact information to parse . + * @return The parsed {@code Contact} information. + * @throws JSONException + */ + public static Contact parseEncodedContactEntity(Uri lookupUri) { + try { + return loadEncodedContactEntity(lookupUri, lookupUri); + } catch (JSONException je) { + return null; + } + } + + private static Contact loadEncodedContactEntity(Uri uri, Uri lookupUri) throws JSONException { final String jsonString = uri.getEncodedFragment(); final JSONObject json = new JSONObject(jsonString); @@ -363,7 +380,7 @@ public class ContactLoader extends AsyncTaskLoader<Contact> { final String photoUri = json.optString(Contacts.PHOTO_URI, null); final Contact contact = new Contact( uri, uri, - mLookupUri, + lookupUri, directoryId, null /* lookupKey */, -1 /* id */, @@ -423,7 +440,7 @@ public class ContactLoader extends AsyncTaskLoader<Contact> { return contact; } - private void processOneRecord(RawContact rawContact, JSONObject item, String mimetype) + private static void processOneRecord(RawContact rawContact, JSONObject item, String mimetype) throws JSONException { final ContentValues itemValues = new ContentValues(); itemValues.put(Data.MIMETYPE, mimetype); @@ -758,6 +775,34 @@ public class ContactLoader extends AsyncTaskLoader<Contact> { } } + static private class AccountKey { + private final String mAccountName; + private final String mAccountType; + private final String mDataSet; + + public AccountKey(String accountName, String accountType, String dataSet) { + mAccountName = accountName; + mAccountType = accountType; + mDataSet = dataSet; + } + + @Override + public int hashCode() { + return Objects.hash(mAccountName, mAccountType, mDataSet); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof AccountKey)) { + return false; + } + final AccountKey other = (AccountKey) obj; + return Objects.equals(mAccountName, other.mAccountName) + && Objects.equals(mAccountType, other.mAccountType) + && Objects.equals(mDataSet, other.mDataSet); + } + } + /** * Loads groups meta-data for all groups associated with all constituent raw contacts' * accounts. @@ -765,11 +810,15 @@ public class ContactLoader extends AsyncTaskLoader<Contact> { private void loadGroupMetaData(Contact result) { StringBuilder selection = new StringBuilder(); ArrayList<String> selectionArgs = new ArrayList<String>(); + final HashSet<AccountKey> accountsSeen = new HashSet<>(); for (RawContact rawContact : result.getRawContacts()) { final String accountName = rawContact.getAccountName(); final String accountType = rawContact.getAccountTypeString(); final String dataSet = rawContact.getDataSet(); - if (accountName != null && accountType != null) { + final AccountKey accountKey = new AccountKey(accountName, accountType, dataSet); + if (accountName != null && accountType != null && + !accountsSeen.contains(accountKey)) { + accountsSeen.add(accountKey); if (selection.length() != 0) { selection.append(" OR "); } diff --git a/src/com/android/contacts/common/model/account/ExternalAccountType.java b/src/com/android/contacts/common/model/account/ExternalAccountType.java index e4cef522..53089b84 100644 --- a/src/com/android/contacts/common/model/account/ExternalAccountType.java +++ b/src/com/android/contacts/common/model/account/ExternalAccountType.java @@ -17,9 +17,10 @@ package com.android.contacts.common.model.account; import android.content.Context; -import android.content.pm.PackageInfo; +import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.content.res.TypedArray; @@ -48,12 +49,15 @@ import java.util.List; public class ExternalAccountType extends BaseAccountType { private static final String TAG = "ExternalAccountType"; + private static final String SYNC_META_DATA = "android.content.SyncAdapter"; + /** * The metadata name for so-called "contacts.xml". * * On LMP and later, we also accept the "alternate" name. * This is to allow sync adapters to have a contacts.xml without making it visible on older - * platforms. + * platforms. If you modify this also update the corresponding list in + * ContactsProvider/PhotoPriorityResolver */ private static final String[] METADATA_CONTACTS_NAMES = new String[] { "android.provider.ALTERNATE_CONTACTS_STRUCTURE", @@ -114,15 +118,9 @@ public class ExternalAccountType extends BaseAccountType { this.resourcePackageName = packageName; this.syncAdapterPackageName = packageName; - final PackageManager pm = context.getPackageManager(); final XmlResourceParser parser; if (injectedMetadata == null) { - try { - parser = loadContactsXml(context, packageName); - } catch (NameNotFoundException e1) { - // If the package name is not found, we can't initialize this account type. - return; - } + parser = loadContactsXml(context, packageName); } else { parser = injectedMetadata; } @@ -181,35 +179,41 @@ public class ExternalAccountType extends BaseAccountType { /** * Returns the CONTACTS_STRUCTURE metadata (aka "contacts.xml") in the given apk package. * - * Unfortunately, there's no public way to determine which service defines a sync service for - * which account type, so this method looks through all services in the package, and just - * returns the first CONTACTS_STRUCTURE metadata defined in any of them. + * This method looks through all services in the package that handle sync adapter + * intents for the first one that contains CONTACTS_STRUCTURE metadata. We have to look + * through all sync adapters in the package in case there are contacts and other sync + * adapters (eg, calendar) in the same package. * * Returns {@code null} if the package has no CONTACTS_STRUCTURE metadata. In this case * the account type *will* be initialized with minimal configuration. - * - * On the other hand, if the package is not found, it throws a {@link NameNotFoundException}, - * in which case the account type will *not* be initialized. */ - private XmlResourceParser loadContactsXml(Context context, String resPackageName) - throws NameNotFoundException { + public static XmlResourceParser loadContactsXml(Context context, String resPackageName) { final PackageManager pm = context.getPackageManager(); - PackageInfo packageInfo = pm.getPackageInfo(resPackageName, - PackageManager.GET_SERVICES|PackageManager.GET_META_DATA); - for (ServiceInfo serviceInfo : packageInfo.services) { - for (String metadataName : METADATA_CONTACTS_NAMES) { - final XmlResourceParser parser = serviceInfo.loadXmlMetaData(pm, - metadataName); - if (parser != null) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, String.format("Metadata loaded from: %s, %s, %s", - serviceInfo.packageName, serviceInfo.name, - metadataName)); + final Intent intent = new Intent(SYNC_META_DATA).setPackage(resPackageName); + final List<ResolveInfo> intentServices = pm.queryIntentServices(intent, + PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); + + if (intentServices != null) { + for (final ResolveInfo resolveInfo : intentServices) { + final ServiceInfo serviceInfo = resolveInfo.serviceInfo; + if (serviceInfo == null) { + continue; + } + for (String metadataName : METADATA_CONTACTS_NAMES) { + final XmlResourceParser parser = serviceInfo.loadXmlMetaData( + pm, metadataName); + if (parser != null) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, String.format("Metadata loaded from: %s, %s, %s", + serviceInfo.packageName, serviceInfo.name, + metadataName)); + } + return parser; } - return parser; } } } + // Package was found, but that doesn't contain the CONTACTS_STRUCTURE metadata. return null; } diff --git a/src/com/android/contacts/common/model/dataitem/EventDataItem.java b/src/com/android/contacts/common/model/dataitem/EventDataItem.java index aae00e97..5096fea2 100644 --- a/src/com/android/contacts/common/model/dataitem/EventDataItem.java +++ b/src/com/android/contacts/common/model/dataitem/EventDataItem.java @@ -20,6 +20,7 @@ import android.content.ContentValues; import android.content.Context; import android.provider.ContactsContract; import android.provider.ContactsContract.CommonDataKinds.Event; +import android.text.TextUtils; /** * Represents an event data item, wrapping the columns in @@ -46,12 +47,14 @@ public class EventDataItem extends DataItem { } final EventDataItem that = (EventDataItem) t; // Events can be different (anniversary, birthday) but have the same start date - if (!getStartDate().equals(that.getStartDate())) { + if (!TextUtils.equals(getStartDate(), that.getStartDate())) { return false; + } else if (!hasKindTypeColumn(mKind) || !that.hasKindTypeColumn(that.getDataKind())) { + return hasKindTypeColumn(mKind) == that.hasKindTypeColumn(that.getDataKind()); } else if (getKindTypeColumn(mKind) != that.getKindTypeColumn(that.getDataKind())) { return false; } else if (getKindTypeColumn(mKind) == Event.TYPE_CUSTOM && - !getLabel().equals(that.getLabel())) { + !TextUtils.equals(getLabel(), that.getLabel())) { // Check if custom types are not the same return false; } diff --git a/src/com/android/contacts/common/model/dataitem/ImDataItem.java b/src/com/android/contacts/common/model/dataitem/ImDataItem.java index d1af2462..f89e5c6a 100644 --- a/src/com/android/contacts/common/model/dataitem/ImDataItem.java +++ b/src/com/android/contacts/common/model/dataitem/ImDataItem.java @@ -21,6 +21,7 @@ import android.content.Context; import android.provider.ContactsContract; import android.provider.ContactsContract.CommonDataKinds.Email; import android.provider.ContactsContract.CommonDataKinds.Im; +import android.text.TextUtils; /** * Represents an IM data item, wrapping the columns in @@ -91,21 +92,21 @@ public class ImDataItem extends DataItem { // IM can have the same data put different protocol. These should not collapse. if (!getData().equals(that.getData())) { return false; - } else if (isProtocolValid() && that.isProtocolValid() && - getProtocol() != that.getProtocol()) { + } else if (!isProtocolValid() || !that.isProtocolValid()) { + // Deal with invalid protocol as if it was custom. If either has a non valid + // protocol, check to see if the other has a valid that is not custom + if (isProtocolValid()) { + return getProtocol() == Im.PROTOCOL_CUSTOM; + } else if (that.isProtocolValid()) { + return that.getProtocol() == Im.PROTOCOL_CUSTOM; + } + return true; + } else if (getProtocol() != that.getProtocol()) { return false; - } else if (isProtocolValid() && that.isProtocolValid() && - getProtocol() == Im.PROTOCOL_CUSTOM && - !getCustomProtocol().equals(that.getCustomProtocol())) { + } else if (getProtocol() == Im.PROTOCOL_CUSTOM && + !TextUtils.equals(getCustomProtocol(), that.getCustomProtocol())) { // Check if custom protocols are not the same return false; - } else if ((isProtocolValid() && !that.isProtocolValid() && - getProtocol() != Im.PROTOCOL_CUSTOM) || - (that.isProtocolValid() && !isProtocolValid() && - that.getProtocol() != Im.PROTOCOL_CUSTOM)) { - // Deal with invalid protocol as if it was custom. If either has a non valid protocol, - // check to see if the other has a valid that is not custom - return false; } return true; } diff --git a/src/com/android/contacts/common/model/dataitem/RelationDataItem.java b/src/com/android/contacts/common/model/dataitem/RelationDataItem.java index 1ba3fcfb..9e883fef 100644 --- a/src/com/android/contacts/common/model/dataitem/RelationDataItem.java +++ b/src/com/android/contacts/common/model/dataitem/RelationDataItem.java @@ -20,6 +20,7 @@ import android.content.ContentValues; import android.content.Context; import android.provider.ContactsContract; import android.provider.ContactsContract.CommonDataKinds.Relation; +import android.text.TextUtils; /** * Represents a relation data item, wrapping the columns in @@ -46,12 +47,14 @@ public class RelationDataItem extends DataItem { } final RelationDataItem that = (RelationDataItem) t; // Relations can have different types (assistant, father) but have the same name - if (!getName().equals(that.getName())) { + if (!TextUtils.equals(getName(), that.getName())) { return false; + } else if (!hasKindTypeColumn(mKind) || !that.hasKindTypeColumn(that.getDataKind())) { + return hasKindTypeColumn(mKind) == that.hasKindTypeColumn(that.getDataKind()); } else if (getKindTypeColumn(mKind) != that.getKindTypeColumn(that.getDataKind())) { return false; } else if (getKindTypeColumn(mKind) == Relation.TYPE_CUSTOM && - !getLabel().equals(that.getLabel())) { + !TextUtils.equals(getLabel(), that.getLabel())) { // Check if custom types are not the same return false; } diff --git a/src/com/android/contacts/common/preference/ContactsPreferences.java b/src/com/android/contacts/common/preference/ContactsPreferences.java index 5c07f83f..4bd6be82 100644 --- a/src/com/android/contacts/common/preference/ContactsPreferences.java +++ b/src/com/android/contacts/common/preference/ContactsPreferences.java @@ -47,6 +47,8 @@ public final class ContactsPreferences implements OnSharedPreferenceChangeListen public static final String DISPLAY_ORDER_KEY = "android.contacts.DISPLAY_ORDER"; + private static final String LAST_UPDATED_MILLIS = "last_updated_millis"; + /** * The value for the SORT_ORDER key corresponding to sort by given name first. */ @@ -194,6 +196,8 @@ public final class ContactsPreferences implements OnSharedPreferenceChangeListen mDisplayOrder = getDisplayOrder(); } else if (SORT_ORDER_KEY.equals(key)) { mSortOrder = getSortOrder(); + } else if (LAST_UPDATED_MILLIS.equals(key)) { + return; } if (mListener != null) mListener.onChange(); } diff --git a/src/com/android/contacts/common/util/BitmapUtil.java b/src/com/android/contacts/common/util/BitmapUtil.java index a70831ec..66ab00f5 100644 --- a/src/com/android/contacts/common/util/BitmapUtil.java +++ b/src/com/android/contacts/common/util/BitmapUtil.java @@ -19,6 +19,11 @@ package com.android.contacts.common.util; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff.Mode; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.graphics.drawable.BitmapDrawable; @@ -107,4 +112,51 @@ public class BitmapUtil { return new BitmapDrawable(resources,rotated); } + + /** + * Given an input bitmap, scales it to the given width/height and makes it round. + * + * @param input {@link Bitmap} to scale and crop + * @param targetWidth desired output width + * @param targetHeight desired output height + * @return output bitmap scaled to the target width/height and cropped to an oval. The + * cropping algorithm will try to fit as much of the input into the output as possible, + * while preserving the target width/height ratio. + */ + public static Bitmap getRoundedBitmap(Bitmap input, int targetWidth, int targetHeight) { + if (input == null) { + return null; + } + final Bitmap result = Bitmap.createBitmap(targetWidth, targetHeight, input.getConfig()); + final Canvas canvas = new Canvas(result); + final Paint paint = new Paint(); + canvas.drawARGB(0, 0, 0, 0); + paint.setAntiAlias(true); + canvas.drawOval(0, 0, targetWidth, targetHeight, paint); + + // Specifies that only pixels present in the destination (i.e. the drawn oval) should + // be overwritten with pixels from the input bitmap. + paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); + + final int inputWidth = input.getWidth(); + final int inputHeight = input.getHeight(); + + // Choose the largest scale factor that will fit inside the dimensions of the + // input bitmap. + final float scaleBy = Math.min((float) inputWidth / targetWidth, + (float) inputHeight / targetHeight); + + final int xCropAmountHalved = (int) (scaleBy * targetWidth / 2); + final int yCropAmountHalved = (int) (scaleBy * targetHeight / 2); + + final Rect src = new Rect( + inputWidth / 2 - xCropAmountHalved, + inputHeight / 2 - yCropAmountHalved, + inputWidth / 2 + xCropAmountHalved, + inputHeight / 2 + yCropAmountHalved); + + final RectF dst = new RectF(0, 0, targetWidth, targetHeight); + canvas.drawBitmap(input, src, dst, paint); + return result; + } } diff --git a/src/com/android/contacts/common/util/ContactDisplayUtils.java b/src/com/android/contacts/common/util/ContactDisplayUtils.java index 7ec751a2..bb91b531 100644 --- a/src/com/android/contacts/common/util/ContactDisplayUtils.java +++ b/src/com/android/contacts/common/util/ContactDisplayUtils.java @@ -19,10 +19,18 @@ package com.android.contacts.common.util; import static android.provider.ContactsContract.CommonDataKinds.Phone; import android.content.Context; +import android.telephony.PhoneNumberUtils; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.style.TtsSpan; import android.util.Log; +import android.util.Patterns; import com.android.contacts.common.R; +import com.android.i18n.phonenumbers.NumberParseException; +import com.android.i18n.phonenumbers.PhoneNumberUtil; +import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber; import com.google.common.base.Preconditions; /** @@ -187,4 +195,80 @@ public class ContactDisplayUtils { } } + /** + * Whether the given text could be a phone number. + * + * Note this will miss many things that are legitimate phone numbers, for example, + * phone numbers with letters. + */ + public static boolean isPossiblePhoneNumber(CharSequence text) { + return text == null ? false : Patterns.PHONE.matcher(text.toString()).matches(); + } + + /** + * Returns a Spannable for the given phone number with a telephone {@link TtsSpan} set over + * the entire length of the given phone number. + */ + public static Spannable getTelephoneTtsSpannable(String phoneNumber) { + if (phoneNumber == null) { + return null; + } + final Spannable spannable = new SpannableString(phoneNumber); + final TtsSpan ttsSpan = getTelephoneTtsSpan(phoneNumber); + spannable.setSpan(ttsSpan, 0, phoneNumber.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + return spannable; + } + + /** + * Returns a Spannable for the given message with a telephone {@link TtsSpan} set for + * the given phone number text wherever it is found within the message. + */ + public static Spannable getTelephoneTtsSpannable(String message, String phoneNumber) { + if (message == null) { + return null; + } + final Spannable spannable = new SpannableString(message); + int start = phoneNumber == null ? -1 : message.indexOf(phoneNumber); + while (start >= 0) { + final int end = start + phoneNumber.length(); + final TtsSpan ttsSpan = getTelephoneTtsSpan(phoneNumber); + spannable.setSpan(ttsSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + start = message.indexOf(phoneNumber, end); + } + return spannable; + } + + /** + * Returns a telephone {@link TtsSpan} for the given phone number. + */ + public static TtsSpan getTelephoneTtsSpan(String phoneNumberString) { + if (phoneNumberString == null) { + throw new NullPointerException(); + } + + // Parse the phone number + final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance(); + PhoneNumber phoneNumber = null; + try { + // Don't supply a defaultRegion so this fails for non-international numbers because + // we don't want to TalkBalk to read a country code (e.g. +1) if it is not already + // present + phoneNumber = phoneNumberUtil.parse(phoneNumberString, /* defaultRegion */ null); + } catch (NumberParseException ignored) { + } + + // Build a telephone tts span + final TtsSpan.TelephoneBuilder builder = new TtsSpan.TelephoneBuilder(); + if (phoneNumber == null) { + // Strip separators otherwise TalkBack will be silent + // (this behavior was observed with TalkBalk 4.0.2 from their alpha channel) + builder.setNumberParts(PhoneNumberUtils.stripSeparators(phoneNumberString)); + } else { + if (phoneNumber.hasCountryCode()) { + builder.setCountryCode(Integer.toString(phoneNumber.getCountryCode())); + } + builder.setNumberParts(Long.toString(phoneNumber.getNationalNumber())); + } + return builder.build(); + } } diff --git a/src/com/android/contacts/common/util/ContactsCommonRcsUtil.java b/src/com/android/contacts/common/util/ContactsCommonRcsUtil.java index 4075262d..bc808fbb 100644 --- a/src/com/android/contacts/common/util/ContactsCommonRcsUtil.java +++ b/src/com/android/contacts/common/util/ContactsCommonRcsUtil.java @@ -27,19 +27,29 @@ import java.util.ArrayList; import java.util.HashMap; import com.android.contacts.common.list.DefaultContactListAdapter; +import com.suntek.mway.rcs.client.api.voip.impl.RichScreenApi; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.wifi.WifiManager; import android.os.AsyncTask; import android.provider.ContactsContract; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Contacts.Data; +import android.text.TextUtils; +import android.util.Log; public class ContactsCommonRcsUtil { + public static final String TAG = "ContactsCommonRcsUtil"; + + public static final boolean DEBUG = false; + public static final String RCS_CAPABILITY_CHANGED = "rcs_capability_changed"; public static final String RCS_CAPABILITY_CHANGED_CONTACT_ID = "rcs_capability_changed_contact_id"; @@ -48,12 +58,19 @@ public class ContactsCommonRcsUtil { public static final String RCS_CAPABILITY_MIMETYPE = "vnd.android.cursor.item/capability"; + // User requst to update enhance screen + public static final String UPDATE_ENHANCE_SCREEN_PHONE_EVENT = "933 10 12000"; + + private static int DEFAULT_NUMBER_LENGTH = 11; + public static final HashMap<Long, Boolean> RcsCapabilityMap = new HashMap<Long, Boolean>(); public static final HashMap<Long, Boolean> RcsCapabilityMapCache = new HashMap<Long, Boolean>(); private static boolean isRcs = false; + private static RichScreenApi mRichScreenApi = null; + // private static long rcsCapabilityUpdatedContactId = -1; /* @@ -82,15 +99,18 @@ public class ContactsCommonRcsUtil { @Override protected Void doInBackground(Void... params) { ContentResolver resolver = context.getContentResolver(); - Cursor c = resolver.query(Contacts.CONTENT_URI, new String[] { - Contacts._ID - }, null, null, null); - ArrayList<Long> contactIdList = new ArrayList<Long>(); + Cursor c = resolver.query(ContactsContract.Data.CONTENT_URI, + new String[] { + ContactsContract.Data.DATA1 + }, Data.MIMETYPE + " = ?" + " and " + + ContactsContract.Data.DATA2 + " = ?", new String[] { + RCS_CAPABILITY_MIMETYPE, String.valueOf(1) + }, null); try { if (c != null && c.moveToFirst()) { do { Long contactId = c.getLong(0); - contactIdList.add(contactId); + RcsCapabilityMap.put(contactId, true); } while (c.moveToNext()); } } finally { @@ -98,13 +118,6 @@ public class ContactsCommonRcsUtil { c.close(); } } - for (Long contactId : contactIdList) { - if (ContactsCommonRcsUtil.isRCSUser(context, contactId)) { - RcsCapabilityMap.put(contactId, true); - } else { - RcsCapabilityMap.put(contactId, false); - } - } return null; } @@ -116,27 +129,86 @@ public class ContactsCommonRcsUtil { }.execute(); } - public static boolean isRCSUser(final Context context, long contactId) { - Cursor c = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, - new String[] { - ContactsContract.Data.DATA2 - }, Data.MIMETYPE + " = ? and " + ContactsContract.Data.DATA1 + " = ?", - new String[] { - RCS_CAPABILITY_MIMETYPE, String.valueOf(contactId) - }, null); - try { - if (c != null && c.moveToFirst()) { - do { - if (c.getInt(0) == 1) { - return true; + public static void setRichScreenApi(RichScreenApi richScreenApi) { + mRichScreenApi = richScreenApi; + } + + public static RichScreenApi getRichScreenApi(Context context) { + if (mRichScreenApi == null) { + mRichScreenApi = new RichScreenApi(null); + mRichScreenApi.init(context, null); + Log.d(TAG, "_______mRichScreenApi init______"); + } + return mRichScreenApi; + } + + private static boolean isWifiEnabled(Context context) { + WifiManager wifiManager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE); + if (wifiManager.getWifiState() == WifiManager.WIFI_STATE_ENABLED) { + ConnectivityManager connManager = (ConnectivityManager)context + .getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo wifiInfo = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + return wifiInfo.isConnected(); + } else { + return false; + } + } + + public static String getFormatNumber(String number){ + if(null == number){ + return ""; + } + number = number.replaceAll("-", ""); + number = number.replaceAll(" ", ""); + number = number.replaceAll(",", ""); + int numberLen = number.length(); + if(numberLen > DEFAULT_NUMBER_LENGTH){ + number = number.substring(numberLen - DEFAULT_NUMBER_LENGTH, numberLen); + } + return number; + } + + public static void updateAllEnhanceScreeen(final Context context) { + if (!isWifiEnabled(context)) + return; + Thread t = new Thread(new Runnable() { + @Override + public void run() { + ArrayList<String> phoneNumberList = new ArrayList<String>(); + Cursor phonecursor = context.getContentResolver().query( + ContactsContract.CommonDataKinds.Phone.CONTENT_URI, new String[] { + Phone.NUMBER + }, null, null, null); + try { + if (phonecursor != null && phonecursor.moveToFirst()) { + // boolean hasTryToGet = false; + do { + String phoneNumber = phonecursor.getString(0); + phoneNumberList.add(getFormatNumber(phoneNumber)); + } while (phonecursor.moveToNext()); } - } while (c.moveToNext()); - } - } finally { - if(null != c){ - c.close(); + } finally { + if (null != phonecursor) { + phonecursor.close(); + } + } + try { + for (String aPhoneNumber : phoneNumberList) { + if (!TextUtils.isEmpty(aPhoneNumber)) { + if (DEBUG) { + Log.d(TAG, "Phone Number is: " + aPhoneNumber); + Log.d(TAG, "Calling downloadRichScrnObj for " + aPhoneNumber); + } + mRichScreenApi.downloadRichScrnObj(aPhoneNumber, + UPDATE_ENHANCE_SCREEN_PHONE_EVENT); + } + } + } catch (Exception e) { + e.printStackTrace(); + return; + } } - } - return false; + }); + t.start(); } } diff --git a/src/com/android/contacts/common/util/LocalizedNameResolver.java b/src/com/android/contacts/common/util/LocalizedNameResolver.java index 3c21946a..92104c44 100644 --- a/src/com/android/contacts/common/util/LocalizedNameResolver.java +++ b/src/com/android/contacts/common/util/LocalizedNameResolver.java @@ -19,10 +19,8 @@ package com.android.contacts.common.util; import android.accounts.AccountManager; import android.accounts.AuthenticatorDescription; import android.content.Context; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; import android.content.res.TypedArray; @@ -32,6 +30,7 @@ import android.util.Log; import android.util.Xml; import com.android.contacts.common.R; +import com.android.contacts.common.model.account.ExternalAccountType; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -45,11 +44,6 @@ import java.io.IOException; public class LocalizedNameResolver { private static final String TAG = "LocalizedNameResolver"; - /** - * Meta-data key for the contacts configuration associated with a sync service. - */ - private static final String METADATA_CONTACTS = "android.provider.CONTACTS_STRUCTURE"; - private static final String CONTACTS_DATA_KIND = "ContactsDataKind"; /** @@ -82,20 +76,9 @@ public class LocalizedNameResolver { * reads the picture priority from that file. */ private static String resolveAllContactsNameFromMetaData(Context context, String packageName) { - final PackageManager pm = context.getPackageManager(); - try { - PackageInfo pi = pm.getPackageInfo(packageName, PackageManager.GET_SERVICES - | PackageManager.GET_META_DATA); - if (pi != null && pi.services != null) { - for (ServiceInfo si : pi.services) { - final XmlResourceParser parser = si.loadXmlMetaData(pm, METADATA_CONTACTS); - if (parser != null) { - return loadAllContactsNameFromXml(context, parser, packageName); - } - } - } - } catch (NameNotFoundException e) { - Log.w(TAG, "Problem loading \"All Contacts\"-name: " + e.toString()); + final XmlResourceParser parser = ExternalAccountType.loadContactsXml(context, packageName); + if (parser != null) { + return loadAllContactsNameFromXml(context, parser, packageName); } return null; } diff --git a/src/com/android/contacts/common/util/MaterialColorMapUtils.java b/src/com/android/contacts/common/util/MaterialColorMapUtils.java index 9c8862c3..e12422f4 100644 --- a/src/com/android/contacts/common/util/MaterialColorMapUtils.java +++ b/src/com/android/contacts/common/util/MaterialColorMapUtils.java @@ -20,6 +20,8 @@ import com.android.contacts.common.R; import android.content.res.Resources; import android.content.res.TypedArray; +import android.os.Parcel; +import android.os.Parcelable; import android.os.Trace; public class MaterialColorMapUtils { @@ -34,13 +36,71 @@ public class MaterialColorMapUtils { com.android.contacts.common.R.array.letter_tile_colors_dark); } - public static class MaterialPalette { + public static class MaterialPalette implements Parcelable { public MaterialPalette(int primaryColor, int secondaryColor) { mPrimaryColor = primaryColor; mSecondaryColor = secondaryColor; } public final int mPrimaryColor; public final int mSecondaryColor; + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + MaterialPalette other = (MaterialPalette) obj; + if (mPrimaryColor != other.mPrimaryColor) { + return false; + } + if (mSecondaryColor != other.mSecondaryColor) { + return false; + } + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + mPrimaryColor; + result = prime * result + mSecondaryColor; + return result; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mPrimaryColor); + dest.writeInt(mSecondaryColor); + } + + private MaterialPalette(Parcel in) { + mPrimaryColor = in.readInt(); + mSecondaryColor = in.readInt(); + } + + public static final Creator<MaterialPalette> CREATOR = new Creator<MaterialPalette>() { + @Override + public MaterialPalette createFromParcel(Parcel in) { + return new MaterialPalette(in); + } + + @Override + public MaterialPalette[] newArray(int size) { + return new MaterialPalette[size]; + } + }; } /** @@ -79,6 +139,12 @@ public class MaterialColorMapUtils { return new MaterialPalette(primaryColor, secondaryColor); } + public static MaterialPalette getDefaultInCallPrimaryAndSecondaryColors(Resources resources) { + final int primaryColor = resources.getColor(R.color.dialer_theme_color); + final int secondaryColor = resources.getColor(R.color.dialer_theme_color_dark); + return new MaterialPalette(primaryColor, secondaryColor); + } + /** * Returns the hue component of a color int. * diff --git a/src/com/android/contacts/common/vcard/NotificationImportExportListener.java b/src/com/android/contacts/common/vcard/NotificationImportExportListener.java index 7117f9f5..7e04ae3f 100644 --- a/src/com/android/contacts/common/vcard/NotificationImportExportListener.java +++ b/src/com/android/contacts/common/vcard/NotificationImportExportListener.java @@ -30,6 +30,7 @@ import android.provider.ContactsContract.RawContacts; import android.widget.Toast; import com.android.contacts.common.R; +import com.android.contacts.common.util.ContactsCommonRcsUtil; import com.android.vcard.VCardEntry; public class NotificationImportExportListener implements VCardImportExportListener, @@ -130,6 +131,9 @@ public class NotificationImportExportListener implements VCardImportExportListen VCardService.TYPE_IMPORT, description, null, intent); mNotificationManager.notify(NotificationImportExportListener.DEFAULT_NOTIFICATION_TAG, jobId, notification); + if (ContactsCommonRcsUtil.getIsRcs()) { + ContactsCommonRcsUtil.updateAllEnhanceScreeen(mContext); + } } @Override diff --git a/src/com/android/contacts/common/widget/SelectPhoneAccountDialogFragment.java b/src/com/android/contacts/common/widget/SelectPhoneAccountDialogFragment.java new file mode 100644 index 00000000..86db1747 --- /dev/null +++ b/src/com/android/contacts/common/widget/SelectPhoneAccountDialogFragment.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2014 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.widget; + +import android.telecom.PhoneAccount; +import android.telecom.PhoneAccountHandle; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.app.FragmentManager; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.telecom.TelecomManager; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListAdapter; +import android.widget.TextView; + +import com.android.contacts.common.R; + +import java.util.List; + +/** + * Dialog that allows the user to select a phone accounts for a given action. Optionally provides + * the choice to set the phone account as default. + */ +public class SelectPhoneAccountDialogFragment extends DialogFragment { + private int mTitleResId; + private boolean mCanSetDefault; + private List<PhoneAccountHandle> mAccountHandles; + private boolean mIsSelected; + private boolean mIsDefaultChecked; + private TelecomManager mTelecomManager; + private SelectPhoneAccountListener mListener; + + /** + * Shows the account selection dialog. + * This is the preferred way to show this dialog. + * + * @param fragmentManager The fragment manager. + * @param accountHandles The {@code PhoneAccountHandle}s available to select from. + * @param listener The listener for the results of the account selection. + */ + public static void showAccountDialog(FragmentManager fragmentManager, + List<PhoneAccountHandle> accountHandles, SelectPhoneAccountListener listener) { + showAccountDialog(fragmentManager, R.string.select_account_dialog_title, false, + accountHandles, listener); + } + + /** + * Shows the account selection dialog. + * This is the preferred way to show this dialog. + * This method also allows specifying a custom title and "set default" checkbox. + * + * @param fragmentManager The fragment manager. + * @param titleResId The resource ID for the string to use in the title of the dialog. + * @param canSetDefault {@code true} if the dialog should include an option to set the selection + * as the default. False otherwise. + * @param accountHandles The {@code PhoneAccountHandle}s available to select from. + * @param listener The listener for the results of the account selection. + */ + public static void showAccountDialog(FragmentManager fragmentManager, int titleResId, + boolean canSetDefault, List<PhoneAccountHandle> accountHandles, + SelectPhoneAccountListener listener) { + SelectPhoneAccountDialogFragment fragment = + new SelectPhoneAccountDialogFragment( + titleResId, canSetDefault, accountHandles, listener); + fragment.show(fragmentManager, "selectAccount"); + } + + public SelectPhoneAccountDialogFragment(int titleResId, boolean canSetDefault, + List<PhoneAccountHandle> accountHandles, SelectPhoneAccountListener listener) { + super(); + mTitleResId = titleResId; + mCanSetDefault = canSetDefault; + mAccountHandles = accountHandles; + mListener = listener; + } + + public interface SelectPhoneAccountListener { + void onPhoneAccountSelected(PhoneAccountHandle selectedAccountHandle, boolean setDefault); + void onDialogDismissed(); + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + mIsSelected = false; + mIsDefaultChecked = false; + mTelecomManager = + (TelecomManager) getActivity().getSystemService(Context.TELECOM_SERVICE); + + final DialogInterface.OnClickListener selectionListener = + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + mIsSelected = true; + PhoneAccountHandle selectedAccountHandle = mAccountHandles.get(which); + mListener.onPhoneAccountSelected(selectedAccountHandle, mIsDefaultChecked); + } + }; + + final CompoundButton.OnCheckedChangeListener checkListener = + new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton check, boolean isChecked) { + mIsDefaultChecked = isChecked; + } + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + ListAdapter selectAccountListAdapter = new SelectAccountListAdapter( + builder.getContext(), + R.layout.select_account_list_item, + mAccountHandles); + + AlertDialog dialog = builder.setTitle(mTitleResId) + .setAdapter(selectAccountListAdapter, selectionListener) + .create(); + + if (mCanSetDefault) { + // Generate custom checkbox view + LinearLayout checkboxLayout = (LinearLayout) getActivity() + .getLayoutInflater() + .inflate(R.layout.default_account_checkbox, null); + + CheckBox cb = + (CheckBox) checkboxLayout.findViewById(R.id.default_account_checkbox_view); + cb.setOnCheckedChangeListener(checkListener); + + dialog.getListView().addFooterView(checkboxLayout); + } + + return dialog; + } + + private class SelectAccountListAdapter extends ArrayAdapter<PhoneAccountHandle> { + private int mResId; + + public SelectAccountListAdapter( + Context context, int resource, List<PhoneAccountHandle> accountHandles) { + super(context, resource, accountHandles); + mResId = resource; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + LayoutInflater inflater = (LayoutInflater) + getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + View rowView; + final ViewHolder holder; + + if (convertView == null) { + // Cache views for faster scrolling + rowView = inflater.inflate(mResId, null); + holder = new ViewHolder(); + holder.labelTextView = (TextView) rowView.findViewById(R.id.label); + holder.numberTextView = (TextView) rowView.findViewById(R.id.number); + holder.imageView = (ImageView) rowView.findViewById(R.id.icon); + rowView.setTag(holder); + } + else { + rowView = convertView; + holder = (ViewHolder) rowView.getTag(); + } + + PhoneAccountHandle accountHandle = getItem(position); + PhoneAccount account = mTelecomManager.getPhoneAccount(accountHandle); + holder.labelTextView.setText(account.getLabel()); + if (account.getAddress() == null || + TextUtils.isEmpty(account.getAddress().getSchemeSpecificPart())) { + holder.numberTextView.setVisibility(View.GONE); + } else { + holder.numberTextView.setVisibility(View.VISIBLE); + holder.numberTextView.setText(account.getAddress().getSchemeSpecificPart()); + } + holder.imageView.setImageDrawable(account.createIconDrawable(getContext())); + return rowView; + } + + private class ViewHolder { + TextView labelTextView; + TextView numberTextView; + ImageView imageView; + } + } + + @Override + public void onPause() { + if (!mIsSelected) { + mListener.onDialogDismissed(); + } + super.onPause(); + } +} |
