summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/android/contacts/ContactSaveService.java246
-rw-r--r--src/com/android/contacts/GroupListLoader.java4
-rw-r--r--src/com/android/contacts/GroupMetaDataLoader.java4
-rw-r--r--src/com/android/contacts/activities/ActionBarAdapter.java3
-rw-r--r--src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java33
-rw-r--r--src/com/android/contacts/activities/ContactEditorBaseActivity.java5
-rw-r--r--src/com/android/contacts/activities/MemoryStatusActivity.java285
-rw-r--r--src/com/android/contacts/activities/MultiPickContactActivity.java1965
-rw-r--r--src/com/android/contacts/activities/PeopleActivity.java298
-rw-r--r--src/com/android/contacts/editor/CompactPhotoEditorView.java5
-rwxr-xr-x[-rw-r--r--]src/com/android/contacts/editor/CompactRawContactsEditorView.java61
-rw-r--r--src/com/android/contacts/editor/ContactEditorBaseFragment.java96
-rw-r--r--src/com/android/contacts/editor/ContactEditorFragment.java2
-rw-r--r--src/com/android/contacts/editor/ContactEditorUtils.java8
-rwxr-xr-x[-rw-r--r--]src/com/android/contacts/editor/EditorUiUtils.java0
-rw-r--r--src/com/android/contacts/editor/PhotoEditorView.java5
-rwxr-xr-x[-rw-r--r--]src/com/android/contacts/editor/RawContactEditorView.java47
-rw-r--r--src/com/android/contacts/editor/TextFieldsEditorView.java19
-rw-r--r--src/com/android/contacts/group/GroupBrowseListAdapter.java8
-rw-r--r--src/com/android/contacts/group/GroupBrowseListFragment.java11
-rw-r--r--src/com/android/contacts/group/GroupDetailFragment.java21
-rw-r--r--src/com/android/contacts/group/GroupEditorFragment.java88
-rw-r--r--src/com/android/contacts/group/SuggestedMemberListAdapter.java10
-rw-r--r--src/com/android/contacts/list/ContactPickerFragment.java2
-rw-r--r--src/com/android/contacts/list/EmailAddressListAdapter.java21
-rw-r--r--src/com/android/contacts/list/JoinContactListAdapter.java15
-rw-r--r--src/com/android/contacts/list/PostalAddressListAdapter.java20
-rw-r--r--src/com/android/contacts/quickcontact/ExpandingEntryCardView.java15
-rw-r--r--src/com/android/contacts/quickcontact/QuickContactActivity.java820
-rw-r--r--src/com/android/contacts/util/ImageViewDrawableSetter.java27
-rw-r--r--src/com/android/contacts/widget/MultiShrinkScroller.java73
31 files changed, 4049 insertions, 168 deletions
diff --git a/src/com/android/contacts/ContactSaveService.java b/src/com/android/contacts/ContactSaveService.java
index 6178e9de4..f46e72f57 100644
--- a/src/com/android/contacts/ContactSaveService.java
+++ b/src/com/android/contacts/ContactSaveService.java
@@ -29,12 +29,14 @@ import android.content.Context;
import android.content.Intent;
import android.content.OperationApplicationException;
import android.database.Cursor;
+import android.database.sqlite.SQLiteFullException;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.provider.ContactsContract;
import android.provider.ContactsContract.AggregationExceptions;
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
@@ -46,6 +48,10 @@ import android.provider.ContactsContract.PinnedPositions;
import android.provider.ContactsContract.Profile;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.RawContactsEntity;
+import android.text.TextUtils;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
+import android.telephony.SubscriptionManager;
import android.util.Log;
import android.widget.Toast;
@@ -56,15 +62,22 @@ import com.android.contacts.common.model.RawContactDeltaList;
import com.android.contacts.common.model.RawContactModifier;
import com.android.contacts.common.model.account.AccountWithDataSet;
import com.android.contacts.common.util.PermissionsUtil;
+import com.android.contacts.common.SimContactsConstants;
+import com.android.contacts.common.SimContactsOperation;
+import com.android.contacts.common.MoreContactUtils;
import com.android.contacts.editor.ContactEditorFragment;
import com.android.contacts.util.ContactPhotoUtils;
+import com.android.internal.telephony.uicc.AdnRecord;
+import com.android.internal.telephony.uicc.IccConstants;
+import com.android.internal.telephony.IIccPhoneBook;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
+import java.util.HashMap;
import java.util.concurrent.CopyOnWriteArrayList;
/**
@@ -90,6 +103,7 @@ public class ContactSaveService extends IntentService {
public static final String EXTRA_SAVE_IS_PROFILE = "saveIsProfile";
public static final String EXTRA_SAVE_SUCCEEDED = "saveSucceeded";
public static final String EXTRA_UPDATED_PHOTOS = "updatedPhotos";
+ public static final String SAVE_CONTACT_RESULT = "saveResult";
public static final String ACTION_CREATE_GROUP = "createGroup";
public static final String ACTION_RENAME_GROUP = "renameGroup";
@@ -143,6 +157,33 @@ public class ContactSaveService extends IntentService {
);
private static final int PERSIST_TRIES = 3;
+ private static int count = TelephonyManager.getDefault().getPhoneCount();
+ private static int[] mSimMaxCount = new int[count];
+
+ public static final int RESULT_UNCHANGED = 0;
+ public static final int RESULT_SUCCESS = 1;
+ public static final int RESULT_FAILURE = 2;
+ public static final int RESULT_NO_NUMBER_AND_EMAIL = 3;
+ public static final int RESULT_SIM_FAILURE = 4; //only for sim operation failure
+ public static final int RESULT_EMAIL_FAILURE = 5; // only for sim email operation failure
+ // only for sim failure of number or anr is too long
+ public static final int RESULT_NUMBER_ANR_FAILURE = 6;
+ public static final int RESULT_SIM_FULL_FAILURE = 7; // only for sim card is full
+ public static final int RESULT_TAG_FAILURE = 8; // only for sim failure of name is too long
+ public static final int RESULT_NUMBER_INVALID = 9; // only for sim failure of number is valid
+
+ public static final int RESULT_MEMORY_FULL_FAILURE = 11; //for memory full exception
+ public static final int RESULT_NUMBER_TYPE_FAILURE =12; //only for sim failure of number TYPE
+
+ private final int MAX_NUM_LENGTH = 20;
+ private final int MAX_EMAIL_LENGTH = 40;
+ private final int MAX_EN_LENGTH = 14;
+ private final int MAX_CH_LENGTH = 6;
+
+ // Only for request accessing SIM card
+ // when device is in the "AirPlane" mode.
+ public static final int RESULT_AIR_PLANE_MODE = 10;
+ public static SimContactsOperation mSimContactsOperation;
private static final int MAX_CONTACTS_PROVIDER_BATCH_SIZE = 499;
@@ -183,6 +224,40 @@ public class ContactSaveService extends IntentService {
return getApplicationContext().getSystemService(name);
}
+ /**
+ * when isMultiSimEnabled is true,get the maximum how many contacts can save to sim card
+ */
+ private int getMSimCardMaxCount(int subscription) {
+ if (0 != mSimMaxCount[subscription]) {
+ return mSimMaxCount[subscription];
+ }
+ int[] subId = SubscriptionManager.getSubId(subscription);
+ try {
+ IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
+ ServiceManager.getService("simphonebook"));
+
+ if (iccIpb != null) {
+ if (subId != null
+ && TelephonyManager.getDefault().isMultiSimEnabled()) {
+ List<AdnRecord> list = iccIpb.getAdnRecordsInEfForSubscriber(
+ subId[0], IccConstants.EF_ADN);
+ if (null != list) {
+ mSimMaxCount[subscription] = list.size();
+ }
+ } else {
+ List<AdnRecord> list = iccIpb
+ .getAdnRecordsInEf(IccConstants.EF_ADN);
+ if (null != list) {
+ mSimMaxCount[subscription] = list.size();
+ }
+ }
+ }
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to IIccPhoneBookMSim", ex);
+ }
+ return mSimMaxCount[subscription];
+ }
+
@Override
protected void onHandleIntent(Intent intent) {
if (intent == null) {
@@ -378,8 +453,28 @@ public class ContactSaveService extends IntentService {
long insertedRawContactId = -1;
// Attempt to persist changes
+ Integer result = RESULT_FAILURE;
+
+ ArrayList<Long> rawContactsList = new ArrayList<Long>();
+ boolean isCardOperation = false;
+ for (int i=0; i < state.size(); i++) {
+ final RawContactDelta entity = state.get(i);
+ final String accountType = entity.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
+ final String accountName = entity.getValues().getAsString(RawContacts.ACCOUNT_NAME);
+ rawContactsList.add(entity.getRawContactId());
+
+ final int subscription = MoreContactUtils.getSubscription(
+ accountType, accountName);
+ isCardOperation = (subscription != SubscriptionManager.INVALID_SUBSCRIPTION_ID) ?
+ true : false;
+ if (isCardOperation) {
+ result = doSaveToSimCard(entity, resolver, subscription);
+ Log.d(TAG, "doSaveToSimCard result is " + result);
+ }
+ }
int tries = 0;
while (tries++ < PERSIST_TRIES) {
+ if (result == RESULT_SUCCESS || result == RESULT_FAILURE) {
try {
// Build operations and try applying
final ArrayList<ContentProviderOperation> diff = state.buildDiff();
@@ -449,7 +544,17 @@ public class ContactSaveService extends IntentService {
Log.e(TAG, "Problem persisting user edits", e);
showToast(R.string.contactSavedErrorToast);
break;
-
+ } catch (SQLiteFullException e) {
+ // Memory is full. don't do any thing
+ Log.e(TAG, "Memory is full", e);
+ Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT);
+ if (callbackIntent != null) {
+ callbackIntent.putExtra(EXTRA_SAVE_SUCCEEDED, false);
+ callbackIntent.setData(null);
+ callbackIntent.putExtra(SAVE_CONTACT_RESULT, RESULT_MEMORY_FULL_FAILURE);
+ deliverCallback(callbackIntent);
+ }
+ return;
} catch (OperationApplicationException e) {
// Version consistency failed, re-parent change and try again
Log.w(TAG, "Version consistency failed, re-parenting: " + e.toString());
@@ -484,6 +589,7 @@ public class ContactSaveService extends IntentService {
if (isProfile) {
for (RawContactDelta delta : state) {
delta.setProfileQueryUri();
+ }
}
}
}
@@ -518,6 +624,8 @@ public class ContactSaveService extends IntentService {
callbackIntent.putExtra(EXTRA_SAVE_SUCCEEDED, true);
}
callbackIntent.setData(lookupUri);
+ callbackIntent.putExtra(SAVE_CONTACT_RESULT, result);
+
deliverCallback(callbackIntent);
}
}
@@ -534,7 +642,124 @@ public class ContactSaveService extends IntentService {
return ContactPhotoUtils.savePhotoFromUriToUri(this, photoUri, outputUri, true);
}
- /**
+ private Integer doSaveToSimCard(RawContactDelta entity, ContentResolver resolver,
+ int subscription) {
+ // Return Error code to indicate caller that device is in
+ // the "AirPlane" mode and application can't access SIM card.
+ if (MoreContactUtils.isAPMOnAndSIMPowerDown(this)) {
+ return RESULT_AIR_PLANE_MODE;
+ }
+
+ boolean isInsert = entity.isContactInsert();
+ Integer result = RESULT_SIM_FAILURE;
+ mSimContactsOperation = new SimContactsOperation(this);
+
+ ContentValues values = entity.buildSimDiff();
+ String tag = null;
+ String number = null;
+ String anr = null;
+ String email = null;
+
+ if(entity.isContactInsert()){
+ tag = values.getAsString(SimContactsConstants.STR_TAG);
+ number = values.getAsString(SimContactsConstants.STR_NUMBER);
+ anr = values.getAsString(SimContactsConstants.STR_ANRS);
+ email = values.getAsString(SimContactsConstants.STR_EMAILS);
+ } else {
+ tag = values.getAsString(SimContactsConstants.STR_NEW_TAG);
+ number = values.getAsString(SimContactsConstants.STR_NEW_NUMBER);
+ anr = values.getAsString(SimContactsConstants.STR_NEW_ANRS);
+ email = values.getAsString(SimContactsConstants.STR_NEW_EMAILS);
+ }
+
+ if (TextUtils.isEmpty(number) && TextUtils.isEmpty(anr) && TextUtils.isEmpty(email)) {
+ return RESULT_NO_NUMBER_AND_EMAIL;
+ }
+
+ if (!TextUtils.isEmpty(number)) {
+ if (number.length() > MAX_NUM_LENGTH) {
+ return RESULT_NUMBER_ANR_FAILURE;
+ } else if (number.contains(SimContactsConstants.STR_ANRS)) {
+ return RESULT_NUMBER_TYPE_FAILURE;
+ }
+ }
+
+ if (!TextUtils.isEmpty(anr)) {
+ String[] anrs = anr.split(SimContactsConstants.ANR_SEP);
+ if (anrs != null) {
+ if (anrs.length > MoreContactUtils
+ .getOneSimAnrCount(subscription)) {
+ return RESULT_NUMBER_TYPE_FAILURE;
+ }
+ for (String mAnr : anrs) {
+ if (mAnr.length() > MAX_NUM_LENGTH) {
+ return RESULT_NUMBER_ANR_FAILURE;
+ }
+ }
+ }
+ }
+
+ if (!TextUtils.isEmpty(number) && TextUtils.isEmpty(PhoneNumberUtils
+ .stripSeparators(number))) {
+ return RESULT_NUMBER_INVALID;
+ }
+
+ if (!TextUtils.isEmpty(email)) {
+ String[] emails = email.split(SimContactsConstants.EMAIL_SEP);
+ for (String mEmail : emails) {
+ if (mEmail != null && mEmail.length() > MAX_EMAIL_LENGTH) {
+ return RESULT_EMAIL_FAILURE;
+ }
+ }
+ }
+
+ if (!TextUtils.isEmpty(tag)) {
+ if (tag.getBytes().length > MAX_EN_LENGTH) {
+ return RESULT_TAG_FAILURE;
+ }
+ }
+
+ if (entity.isContactInsert()) {
+ int count = 0;
+ Cursor c = null;
+ Uri iccUri;
+ int[] subId = SubscriptionManager.getSubId(subscription);
+ if (!TelephonyManager.getDefault().isMultiSimEnabled()) {
+ iccUri = Uri.parse(SimContactsConstants.SIM_URI);
+ } else {
+ iccUri = Uri.parse(SimContactsConstants.SIM_SUB_URI + subId[0]);
+ }
+ try {
+ c = resolver.query(iccUri, null, null, null, null);
+ if (c != null) {
+ count = c.getCount();
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+
+ if (count == getMSimCardMaxCount(subscription)) {
+ return RESULT_SIM_FULL_FAILURE;
+ }
+ }
+
+ if (isInsert) {
+ Uri resultUri = mSimContactsOperation.insert(values,
+ subscription);
+ if (resultUri != null)
+ result = RESULT_SUCCESS;
+ } else {
+ int resultInt = mSimContactsOperation.update(values,
+ subscription);
+ if (resultInt == 1)
+ result = RESULT_SUCCESS;
+ }
+ return result;
+ }
+
+ /**
* Find the ID of an existing or newly-inserted raw-contact. If none exists, return -1.
*/
private long getRawContactId(RawContactDeltaList state,
@@ -998,12 +1223,27 @@ public class ContactSaveService extends IntentService {
private void deleteContact(Intent intent) {
Uri contactUri = intent.getParcelableExtra(EXTRA_CONTACT_URI);
+ mSimContactsOperation = new SimContactsOperation(this);
if (contactUri == null) {
Log.e(TAG, "Invalid arguments for deleteContact request");
return;
}
- getContentResolver().delete(contactUri, null, null);
+ final List<String> segments = contactUri.getPathSegments();
+ // Contains an Id.
+ final long uriContactId = Long.parseLong(segments.get(3));
+ int subscription = mSimContactsOperation
+ .getSimSubscription(uriContactId);
+ if (subscription != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ ContentValues values = mSimContactsOperation
+ .getSimAccountValues(uriContactId);
+ int result = mSimContactsOperation.delete(values, subscription);
+ if (result == RESULT_SUCCESS) {
+ getContentResolver().delete(contactUri, null, null);
+ }
+ } else {
+ getContentResolver().delete(contactUri, null, null);
+ }
}
private void deleteMultipleContacts(Intent intent) {
diff --git a/src/com/android/contacts/GroupListLoader.java b/src/com/android/contacts/GroupListLoader.java
index 2589c9bb4..e7fd6428f 100644
--- a/src/com/android/contacts/GroupListLoader.java
+++ b/src/com/android/contacts/GroupListLoader.java
@@ -49,8 +49,6 @@ public final class GroupListLoader extends CursorLoader {
public GroupListLoader(Context context) {
super(context, GROUP_LIST_URI, COLUMNS, Groups.ACCOUNT_TYPE + " NOT NULL AND "
+ Groups.ACCOUNT_NAME + " NOT NULL AND " + Groups.AUTO_ADD + "=0 AND " +
- Groups.FAVORITES + "=0 AND " + Groups.DELETED + "=0", null,
- Groups.ACCOUNT_TYPE + ", " + Groups.ACCOUNT_NAME + ", " + Groups.DATA_SET + ", " +
- Groups.TITLE + " COLLATE LOCALIZED ASC");
+ Groups.FAVORITES + "=0 AND " + Groups.DELETED + "=0", null, "account_id");
}
}
diff --git a/src/com/android/contacts/GroupMetaDataLoader.java b/src/com/android/contacts/GroupMetaDataLoader.java
index 834404138..ea503d017 100644
--- a/src/com/android/contacts/GroupMetaDataLoader.java
+++ b/src/com/android/contacts/GroupMetaDataLoader.java
@@ -50,7 +50,9 @@ public final class GroupMetaDataLoader extends CursorLoader {
public GroupMetaDataLoader(Context context, Uri groupUri) {
super(context, ensureIsGroupUri(groupUri), COLUMNS, Groups.ACCOUNT_TYPE + " NOT NULL AND "
- + Groups.ACCOUNT_NAME + " NOT NULL", null, null);
+ + Groups.ACCOUNT_NAME + " NOT NULL AND " + Groups.DELETED + " != ?"
+ , new String[] {"1"}
+ , null);
}
/**
diff --git a/src/com/android/contacts/activities/ActionBarAdapter.java b/src/com/android/contacts/activities/ActionBarAdapter.java
index 5a95c90bc..6a81d066b 100644
--- a/src/com/android/contacts/activities/ActionBarAdapter.java
+++ b/src/com/android/contacts/activities/ActionBarAdapter.java
@@ -110,8 +110,9 @@ public class ActionBarAdapter implements OnCloseListener {
public interface TabState {
public static int FAVORITES = 0;
public static int ALL = 1;
+ public static int GROUPS = 2;
- public static int COUNT = 2;
+ public static int COUNT = 3;
public static int DEFAULT = ALL;
}
diff --git a/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java b/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
index a922ca128..500d7c177 100644
--- a/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
@@ -84,9 +84,9 @@ public class ContactEditorAccountsChangedActivity extends Activity {
throw new IllegalStateException("Cannot have a negative number of accounts");
}
- if (numAccounts >= 2) {
- // When the user has 2+ writable accounts, show a list of accounts so the user can pick
- // which account to create a contact in.
+ if (numAccounts > 0) {
+ // When the user has writable accounts, show a list of accounts so the user can pick
+ // which account to create a contact in (add also the phone-local storage account).
setContentView(R.layout.contact_editor_accounts_changed_activity_with_picker);
final TextView textView = (TextView) findViewById(R.id.text);
@@ -101,33 +101,6 @@ public class ContactEditorAccountsChangedActivity extends Activity {
AccountListFilter.ACCOUNTS_CONTACT_WRITABLE);
accountListView.setAdapter(mAccountListAdapter);
accountListView.setOnItemClickListener(mAccountListItemClickListener);
- } else if (numAccounts == 1) {
- // If the user has 1 writable account we will just show the user a message with 2
- // possible action buttons.
- setContentView(R.layout.contact_editor_accounts_changed_activity_with_text);
-
- final TextView textView = (TextView) findViewById(R.id.text);
- final Button leftButton = (Button) findViewById(R.id.left_button);
- final Button rightButton = (Button) findViewById(R.id.right_button);
-
- final AccountWithDataSet account = accounts.get(0);
- textView.setText(getString(R.string.contact_editor_prompt_one_account,
- account.name));
-
- // This button allows the user to add a new account to the device and return to
- // this app afterwards.
- leftButton.setText(getString(R.string.add_new_account));
- leftButton.setOnClickListener(mAddAccountClickListener);
-
- // This button allows the user to continue creating the contact in the specified
- // account.
- rightButton.setText(getString(android.R.string.ok));
- rightButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- saveAccountAndReturnResult(account);
- }
- });
} else {
// If the user has 0 writable accounts, we will just show the user a message with 2
// possible action buttons.
diff --git a/src/com/android/contacts/activities/ContactEditorBaseActivity.java b/src/com/android/contacts/activities/ContactEditorBaseActivity.java
index 12f1e961b..ac5fe67da 100644
--- a/src/com/android/contacts/activities/ContactEditorBaseActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorBaseActivity.java
@@ -172,7 +172,7 @@ abstract public class ContactEditorBaseActivity extends ContactsActivity
*/
void onSaveCompleted(boolean hadChanges, int saveMode, boolean saveSucceeded,
Uri contactLookupUri, Bundle updatedPhotos, boolean backPressed, long photoId,
- long nameId);
+ long nameId, int result);
/**
* Invoked after the contact is joined.
@@ -264,7 +264,8 @@ abstract public class ContactEditorBaseActivity extends ContactsActivity
intent.getBooleanExtra(ContactEditorFragment.INTENT_EXTRA_SAVE_BACK_PRESSED,
false),
intent.getLongExtra(ContactEditorFragment.INTENT_EXTRA_PHOTO_ID, -1),
- intent.getLongExtra(ContactEditorFragment.INTENT_EXTRA_NAME_ID, -1));
+ intent.getLongExtra(ContactEditorFragment.INTENT_EXTRA_NAME_ID, -1),
+ intent.getIntExtra(ContactSaveService.SAVE_CONTACT_RESULT, 0));
} else if (ACTION_JOIN_COMPLETED.equals(action)) {
mFragment.onJoinCompleted(intent.getData());
}
diff --git a/src/com/android/contacts/activities/MemoryStatusActivity.java b/src/com/android/contacts/activities/MemoryStatusActivity.java
new file mode 100644
index 000000000..2622c4018
--- /dev/null
+++ b/src/com/android/contacts/activities/MemoryStatusActivity.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ *
+ * Not a Contribution.
+ *
+ * Copyright (C) 2011 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.activities;
+
+import android.app.ActionBar;
+import android.content.Context;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Bundle;
+import android.os.Message;
+import android.provider.ContactsContract.RawContacts;
+import android.telecom.PhoneAccount;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.android.contacts.ContactsActivity;
+import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.MoreContactUtils;
+import com.android.contacts.common.SimContactsConstants;
+import com.android.contacts.R;
+import com.android.contacts.common.model.account.PhoneAccountType;
+import com.google.android.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Shows a list of all available accounts, letting the user select under which
+ * account to view contacts.
+ */
+public class MemoryStatusActivity extends ContactsActivity {
+ private static final String TAG = "MemoryStatusActivity";
+ private static final int INVALID_COUNT = 0;
+ private ListView mListView;
+ private View empty;
+ private List<AccountListItem> mFilters;
+ private AccountListAdapter mAdapter;
+ private Handler mHandler;
+ private LoaderThread mThread = null;
+
+ private final class AccountListItem {
+ public final String accountType;
+ public final String accountName;
+ public final String dataSet;
+ public final Drawable icon;
+ public final int total;
+ public final int count;
+
+ public AccountListItem(String accountType, String accountName, String dataSet,
+ Drawable icon, int total, int count) {
+ this.accountType = accountType;
+ this.accountName = accountName;
+ this.dataSet = dataSet;
+ this.icon = icon;
+ this.total = total;
+ this.count = count;
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.contact_memory_list);
+
+ mListView = (ListView) findViewById(android.R.id.list);
+ empty = (View) findViewById(R.id.empty);
+ mListView.setEmptyView(empty);
+
+ ActionBar actionBar = getActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+ mFilters = Lists.newArrayList();
+ mAdapter = new AccountListAdapter(this);
+ mListView.setAdapter(mAdapter);
+
+ mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+ mFilters = (List<AccountListItem>) msg.obj;
+ mAdapter.notifyDataSetChanged();
+ }
+ };
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (mFilters.isEmpty()) {
+ if (mThread == null) {
+ mThread = new LoaderThread();
+ }
+ try {
+ mThread.start();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ public class LoaderThread extends Thread {
+ @Override
+ public void run() {
+ List<AccountListItem> list = loadAccountFilters(MemoryStatusActivity.this);
+ Message msg = Message.obtain();
+ msg.obj = list;
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ private List<AccountListItem> loadAccountFilters(Context context) {
+ final ArrayList<AccountListItem> accountFilters = Lists.newArrayList();
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(context);
+ List<AccountWithDataSet> accounts = accountTypes.getAccounts(true,
+ AccountTypeManager.FLAG_ALL_ACCOUNTS_WITHOUT_LOCAL);
+ ContentResolver cr = context.getContentResolver();
+
+ // Add the local account first, this is a special case.
+ accounts.add(0, new AccountWithDataSet(SimContactsConstants.PHONE_NAME,
+ PhoneAccountType.ACCOUNT_TYPE,
+ null));
+ for (AccountWithDataSet account : accounts) {
+ AccountType accountType = accountTypes.getAccountType(account.type, account.dataSet);
+ if (accountType.isExtension() && !account.hasData(context)) {
+ // Hide extensions with no raw_contacts.
+ continue;
+ }
+ Drawable icon = accountType != null ? accountType.getDisplayIcon(context) : null;
+ int total = INVALID_COUNT;
+ int count = INVALID_COUNT;
+ if (!TextUtils.isEmpty(account.type)) {
+ if (account.type.equals(SimContactsConstants.ACCOUNT_TYPE_SIM)) {
+ total = MoreContactUtils.getAdnCount(MoreContactUtils
+ .getSubscription(account.type, account.name));
+ if (total > 0) {
+ Cursor cursor = cr.query(RawContacts.CONTENT_URI, new String[] {
+ RawContacts._ID
+ }, RawContacts.ACCOUNT_NAME + " = '" + account.name + "' AND "
+ + RawContacts.DELETED + " = 0", null, null);
+ if (cursor != null) {
+ try {
+ count = cursor.getCount();
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+ } else {
+ Cursor cursor = cr.query(RawContacts.CONTENT_URI, new String[] {
+ RawContacts._ID
+ }, RawContacts.ACCOUNT_NAME + " = '" + account.name + "' AND "
+ + RawContacts.DELETED + " = 0", null, null);
+ if (cursor != null) {
+ try {
+ count = cursor.getCount();
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+ }
+ accountFilters.add(new AccountListItem(
+ account.type, account.name, account.dataSet, icon, total, count));
+ }
+
+ return accountFilters;
+ }
+
+ private class AccountListAdapter extends BaseAdapter {
+ private final LayoutInflater mLayoutInflater;
+ private Context accountContext;
+
+ public AccountListAdapter(Context context) {
+ mLayoutInflater = (LayoutInflater) context.getSystemService
+ (Context.LAYOUT_INFLATER_SERVICE);
+ accountContext = context;
+ }
+
+ @Override
+ public int getCount() {
+ return mFilters.size();
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public AccountListItem getItem(int position) {
+ return mFilters.get(position);
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final View view;
+ AccountListItemViewCache viewCache;
+ if (convertView != null) {
+ view = convertView;
+ viewCache = (AccountListItemViewCache) view.getTag();
+ } else {
+ view = mLayoutInflater.inflate(R.layout.memory_account_list_item, parent, false);
+ viewCache = new AccountListItemViewCache(view);
+ view.setTag(viewCache);
+ }
+ bindView(position, convertView, parent, viewCache);
+ return view;
+ }
+
+ private void bindView(int position, View convertView, ViewGroup parent,
+ AccountListItemViewCache viewCache) {
+ final AccountListItem filter = mFilters.get(position);
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(accountContext);
+ final AccountType accountType =
+ accountTypes.getAccountType(filter.accountType, filter.dataSet);
+ viewCache.accountName.setText(accountType.getDisplayLabel(accountContext)
+ + "<" + filter.accountName + ">");
+ viewCache.totally.setVisibility((filter.total != INVALID_COUNT) ? View.VISIBLE
+ : View.GONE);
+ viewCache.count_total.setText(Integer.toString(filter.total));
+ viewCache.count_cur.setText(Integer.toString(filter.count));
+ }
+
+ /**
+ * Cache of the children views of a contact detail entry represented by
+ * a {@link GroupListItem}
+ */
+ public class AccountListItemViewCache {
+ public final TextView accountName;
+ public final TextView count_total;
+ public final TextView count_cur;
+ public final LinearLayout totally;
+
+ public AccountListItemViewCache(View view) {
+ accountName = (TextView) view.findViewById(R.id.account_name);
+ count_total = (TextView) view.findViewById(R.id.count_max);
+ count_cur = (TextView) view.findViewById(R.id.count_cur);
+ totally = (LinearLayout) view.findViewById(R.id.totally);
+ }
+ }
+ }
+}
diff --git a/src/com/android/contacts/activities/MultiPickContactActivity.java b/src/com/android/contacts/activities/MultiPickContactActivity.java
new file mode 100644
index 000000000..ee7614b88
--- /dev/null
+++ b/src/com/android/contacts/activities/MultiPickContactActivity.java
@@ -0,0 +1,1965 @@
+/**
+ * Copyright (C) 2013-2015, 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.activities;
+
+import android.app.ActionBar;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ListActivity;
+import android.app.ProgressDialog;
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.content.AsyncQueryHandler;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.ContentProviderOperation;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.OperationApplicationException;
+import android.database.Cursor;
+import android.net.Uri;
+import android.net.Uri.Builder;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.provider.CallLog;
+import android.provider.CallLog.Calls;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.ContactCounts;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Groups;
+import android.provider.ContactsContract.RawContacts;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
+import android.telephony.SubscriptionManager;
+import android.text.Editable;
+import android.text.format.DateUtils;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CursorAdapter;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.SearchView;
+import android.widget.SectionIndexer;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.contacts.R;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.common.SimContactsConstants;
+import com.android.contacts.common.SimContactsOperation;
+import com.android.contacts.common.list.AccountFilterActivity;
+import com.android.contacts.common.list.ContactListFilter;
+import com.android.contacts.common.list.ContactListItemView;
+import com.android.contacts.common.list.ContactsSectionIndexer;
+import com.android.contacts.common.list.DefaultContactListAdapter;
+import com.android.contacts.common.MoreContactUtils;
+import com.android.contacts.common.model.account.SimAccountType;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+public class MultiPickContactActivity extends ListActivity implements
+ TextView.OnEditorActionListener, View.OnTouchListener,
+ SearchView.OnQueryTextListener, SearchView.OnCloseListener,
+ View.OnFocusChangeListener, DialogInterface.OnClickListener,
+ DialogInterface.OnKeyListener {
+ private final static String TAG = "MultiPickContactActivity";
+ private final static boolean DEBUG = true;
+
+ private static final String SORT_ORDER = " desc";
+
+ static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
+ Contacts._ID, // 0
+ Contacts.DISPLAY_NAME_PRIMARY, // 1
+ Contacts.PHOTO_ID, // 2
+ Contacts.LOOKUP_KEY, // 3
+ RawContacts.ACCOUNT_TYPE, // 4
+ RawContacts.ACCOUNT_NAME, // 5
+ Contacts.NAME_RAW_CONTACT_ID, // 6
+ Contacts.PHOTO_THUMBNAIL_URI // 7
+ };
+
+ static final String[] PHONES_PROJECTION = new String[] {
+ Data.CONTACT_ID, // 0
+ Data.DISPLAY_NAME, // 1
+ Data.PHOTO_ID, // 2
+ Data.LOOKUP_KEY, // 3
+ Phone._ID, // 4
+ Phone.TYPE, // 5
+ Phone.LABEL, // 6
+ Phone.NUMBER, // 7
+ };
+
+ static final String[] EMAILS_PROJECTION = new String[] {
+ Data.CONTACT_ID, // 0
+ Data.DISPLAY_NAME, // 1
+ Data.PHOTO_ID, // 2
+ Data.LOOKUP_KEY, // 3
+ Email._ID, // 4
+ Email.ADDRESS // 5
+ };
+
+ static final String[] CALL_LOG_PROJECTION = new String[] {
+ Calls._ID,
+ Calls.NUMBER,
+ Calls.DATE,
+ Calls.DURATION,
+ Calls.TYPE,
+ Calls.CACHED_NAME,
+ Calls.CACHED_NUMBER_TYPE,
+ Calls.CACHED_NUMBER_LABEL,
+ Calls.PHONE_ACCOUNT_ID,
+ Calls.GEOCODED_LOCATION,
+ };
+
+ static final String CONTACTS_SELECTION = Contacts.IN_VISIBLE_GROUP + "=1";
+
+ static final String PHONES_SELECTION = RawContacts.ACCOUNT_TYPE + "<>?";
+
+ static final String[] PHONES_SELECTION_ARGS = {
+ SimContactsConstants.ACCOUNT_TYPE_SIM
+ };
+
+ public static final int CONTACT_COLUMN_ID = 0;
+ public static final int CONTACT_COLUMN_DISPLAY_NAME = 1;
+ public static final int CONTACT_COLUMN_PHOTO_ID = 2;
+ public static final int CONTACT_COLUMN_LOOKUP_KEY = 3;
+ // contacts query specific columns
+ public static final int CONTACT_COLUMN_ACCOUNT_TYPE = 4;
+ public static final int CONTACT_COLUMN_ACCOUNT_NAME = 5;
+ public static final int CONTACT_COLUMN_RAW_CONTACT_ID = 6;
+ public static final int CONTACT_COLUMN_PHOTO_URI = 7;
+ // phone query specific columns
+ public static final int PHONE_COLUMN_ID = 4;
+ public static final int PHONE_COLUMN_TYPE = 5;
+ public static final int PHONE_COLUMN_LABEL = 6;
+ public static final int PHONE_COLUMN_NUMBER = 7;
+ // email query specific columns
+ public static final int EMAIL_COLUMN_ID = 4;
+ public static final int EMAIL_COLUMN_ADDRESS = 5;
+
+ public static final int CALLLOG_COLUMN_ID = 0;
+ public static final int CALLLOG_COLUMN_NUMBER = 1;
+ public static final int CALLLOG_COLUMN_DATE = 2;
+ public static final int CALLLOG_COLUMN_DURATION = 3;
+ public static final int CALLLOG_COLUMN_CALL_TYPE = 4;
+ public static final int CALLLOG_COLUMN_CALLER_NAME = 5;
+ public static final int CALLLOG_COLUMN_CALLER_NUMBERTYPE = 6;
+ public static final int CALLLOG_COLUMN_CALLER_NUMBERLABEL = 7;
+ public static final int CALLLOG_COLUMN_PHONE_ACCOUNT = 8;
+ public static final int CALLLOG_COLUMN_CALLER_LOCATION = 9;
+
+ private static final int QUERY_TOKEN = 42;
+ private static final int MODE_MASK_SEARCH = 0x80000000;
+
+ private static final int MODE_DEFAULT_CONTACT = 0;
+ private static final int MODE_DEFAULT_PHONE = 1;
+ private static final int MODE_DEFAULT_EMAIL = 1 << 1;
+ private static final int MODE_DEFAULT_CALL = 1 << 1 << 1;
+ private static final int MODE_DEFAULT_SIM = 1 << 1 << 1 << 1;
+ private static final int MODE_SEARCH_CONTACT = MODE_DEFAULT_CONTACT | MODE_MASK_SEARCH;
+ private static final int MODE_SEARCH_PHONE = MODE_DEFAULT_PHONE | MODE_MASK_SEARCH;
+ private static final int MODE_SEARCH_EMAIL = MODE_DEFAULT_EMAIL | MODE_MASK_SEARCH;
+ private static final int MODE_SEARCH_CALL = MODE_DEFAULT_CALL | MODE_MASK_SEARCH;
+ private static final int MODE_SEARCH_SIM = MODE_DEFAULT_SIM | MODE_MASK_SEARCH;
+ private static final int DIALOG_DEL_CALL = 1;
+
+ public static final String ADD_GROUP_MEMBERS = "add_group_members";
+
+ public static final String ACTION_MULTI_PICK = "com.android.contacts.action.MULTI_PICK";
+ static final String ACTION_MULTI_PICK_EMAIL = "com.android.contacts.action.MULTI_PICK_EMAIL";
+ static final String ACTION_MULTI_PICK_CALL = "com.android.contacts.action.MULTI_PICK_CALL";
+ static final String ACTION_MULTI_PICK_SIM = "com.android.contacts.action.MULTI_PICK_SIM";
+
+ public static final String EXTRA_IS_SELECT_ALL_DISALLOWED = "is_select_all_disallowed";
+ public static final String EXTRA_SELECT_CALLLOG = "selectcalllog";
+ public static final String EXTRA_NOT_SHOW_SIM_FLAG = "not_sim_show";
+ public static final String EXTRA_GROUP_ID = "group_id";
+ public static final String EXTRA_GROUP_ACTION = "group_action";
+
+ public static final int GROUP_ACTION_ADD_MEMBER = 0;
+ public static final int GROUP_ACTION_MOVE_MEMBER = 1;
+ public static final int GROUP_ACTION_NONE = -1;
+
+ private ContactItemListAdapter mAdapter;
+ private QueryHandler mQueryHandler;
+ private Bundle mChoiceSet;
+
+ private ActionBar mActionBar;
+ private SearchView mSearchView;
+ private ViewGroup mSearchViewContainer;
+
+ private int mMode;
+ private boolean mSelectCallLog;
+ private boolean mSearchUiVisible = false;
+
+ private ArrayList<Long> mGroupIds = new ArrayList<Long>();
+
+ private ProgressDialog mProgressDialog;
+ private SimContactsOperation mSimContactsOperation;
+ private AccountManager accountManager;
+ private CharSequence[] mLabelArray;
+ private AccountManager mAccountManager;
+
+ private static final String[] SIM_COLUMN_NAMES = new String[] {
+ "name",
+ "number",
+ "emails",
+ "anrs",
+ "_id"
+ };
+
+ private static final int SIM_COLUMN_DISPLAY_NAME = 0;
+ private static final int SIM_COLUMN_NUMBER = 1;
+ private static final int SIM_COLUMN_EMAILS = 2;
+ private static final int SIM_COLUMN_ANRS = 3;
+ private static final int SIM_COLUMN_ID = 4;
+
+ private int MAX_CONTACTS_NUM_TO_SELECT_ONCE = 500;
+
+ //registerReceiver to update content when airplane mode change.
+ private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
+ updateContent();
+
+ // If now is airplane mode, should cancel import sim contacts
+ if (isPickSim() && MoreContactUtils.isAPMOnAndSIMPowerDown(context)) {
+ cancelSimContactsImporting();
+ }
+ }
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Intent intent = getIntent();
+ String action = intent.getAction();
+ boolean isContact = intent.getBooleanExtra(SimContactsConstants.IS_CONTACT, false);
+
+ if (Intent.ACTION_DELETE.equals(action)) {
+ mMode = MODE_DEFAULT_CONTACT;
+ setTitle(R.string.menu_deleteContact);
+ } else if (SimContactsConstants.ACTION_MULTI_PICK.equals(action)) {
+ mMode = isContact ? MODE_DEFAULT_CONTACT : MODE_DEFAULT_PHONE;
+ } else if (SimContactsConstants.ACTION_MULTI_PICK_EMAIL.equals(action)) {
+ mMode = MODE_DEFAULT_EMAIL;
+ } else if (SimContactsConstants.ACTION_MULTI_PICK_CALL.equals(action)) {
+ mMode = MODE_DEFAULT_CALL;
+ setTitle(R.string.delete_call_title);
+ if (intent.getBooleanExtra(EXTRA_SELECT_CALLLOG, false)) {
+ mSelectCallLog = true;
+ setTitle(R.string.select_call_title);
+ }
+ } else if (SimContactsConstants.ACTION_MULTI_PICK_SIM.equals(action)) {
+ mMode = MODE_DEFAULT_SIM;
+ }
+
+ mChoiceSet = new Bundle();
+ mAdapter = new ContactItemListAdapter(this);
+ getListView().setAdapter(mAdapter);
+ mQueryHandler = new QueryHandler(this);
+ mSimContactsOperation = new SimContactsOperation(this);
+ mAccountManager = AccountManager.get(this);
+
+ mActionBar = getActionBar();
+ mActionBar.setHomeButtonEnabled(true);
+ mActionBar.setDisplayHomeAsUpEnabled(true);
+ mActionBar.setDisplayShowTitleEnabled(true);
+ inflateSearchView();
+
+ startQuery();
+ //register receiver.
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ registerReceiver(mBroadcastReceiver, filter);
+ }
+
+ private void inflateSearchView() {
+ LayoutInflater inflater = LayoutInflater.from(mActionBar.getThemedContext());
+ mSearchViewContainer = (ViewGroup) inflater.inflate(R.layout.custom_action_bar, null);
+ mSearchView = (SearchView) mSearchViewContainer.findViewById(R.id.search_view);
+
+ if (isPickCall() || isPickSim()) {
+ mSearchView.setVisibility(View.GONE);
+ return;
+ }
+
+ // In order to make the SearchView look like "shown via search menu", we need to
+ // manually setup its state. See also DialtactsActivity.java and ActionBarAdapter.java.
+ mSearchView.setIconifiedByDefault(true);
+ mSearchView.setQueryHint(getString(R.string.hint_findContacts));
+ mSearchView.setIconified(false);
+ mSearchView.setFocusable(true);
+
+ mSearchView.setOnQueryTextListener(this);
+ mSearchView.setOnCloseListener(this);
+ mSearchView.setOnQueryTextFocusChangeListener(this);
+
+ mActionBar.setCustomView(mSearchViewContainer, new ActionBar.LayoutParams(
+ ActionBar.LayoutParams.MATCH_PARENT, ActionBar.LayoutParams.WRAP_CONTENT));
+ mActionBar.setDisplayShowCustomEnabled(true);
+
+ configureSearchMode();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+
+ inflater.inflate(R.menu.search_menu, menu);
+ inflater.inflate(R.menu.multi_contact_picker_options, menu);
+
+ final MenuItem selectAllItem = menu.findItem(R.id.select_all_check);
+ selectAllItem.setVisible(!mSearchUiVisible);
+ selectAllItem.setChecked(mChoiceSet.size() == mAdapter.getCount());
+
+ final MenuItem doneItem = menu.findItem(R.id.done);
+ doneItem.setVisible(!mChoiceSet.isEmpty());
+
+ final MenuItem searchItem = menu.findItem(R.id.menu_search);
+ searchItem.setVisible(!mSearchUiVisible && !isPickCall() && !isPickSim());
+
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_search:
+ mSearchUiVisible = !mSearchUiVisible;
+ if (!mSearchUiVisible) {
+ exitSearchMode();
+ } else {
+ configureSearchMode();
+ }
+ return true;
+ case R.id.select_all_check:
+ selectAll(!item.isChecked());
+ return true;
+ case R.id.done:
+ if (isSearchMode()) {
+ exitSearchMode();
+ }
+ if (mMode == MODE_DEFAULT_CONTACT) {
+ if (SimContactsConstants.ACTION_MULTI_PICK.equals(getIntent().getAction())) {
+ if (mChoiceSet.size() > MAX_CONTACTS_NUM_TO_SELECT_ONCE) {
+ String text = getString(R.string.too_many_contacts_add_to_group,
+ MAX_CONTACTS_NUM_TO_SELECT_ONCE);
+ Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
+ } else {
+ int memberAction = getIntent().getIntExtra(EXTRA_GROUP_ACTION,
+ GROUP_ACTION_NONE);
+ switch (memberAction) {
+ case GROUP_ACTION_ADD_MEMBER:
+ setResult(RESULT_OK, new Intent().putExtras(mChoiceSet));
+ finish();
+ break;
+ case GROUP_ACTION_MOVE_MEMBER:
+ String account = getIntent().getStringExtra(
+ SimContactsConstants.ACCOUNT_TYPE);
+ long groupId = getIntent().getLongExtra(EXTRA_GROUP_ID, -1);
+ showGroupSelectionList(account, groupId);
+ break;
+ default:
+ setResultAndFinish();
+ break;
+ }
+ }
+ } else if (mChoiceSet.size() > 0) {
+ showDialog(R.id.dialog_delete_contact_confirmation);
+ }
+ } else if (mMode == MODE_DEFAULT_PHONE) {
+ setResultAndFinish();
+ } else if (mMode == MODE_DEFAULT_SIM) {
+ if (mChoiceSet.size() > 0) {
+ showDialog(R.id.dialog_import_sim_contact_confirmation);
+ }
+ } else if (mMode == MODE_DEFAULT_EMAIL) {
+ setResultAndFinish();
+ } else if (mMode == MODE_DEFAULT_CALL) {
+ if (mChoiceSet.size() > 0) {
+ if (mSelectCallLog) {
+ setResultAndFinish();
+ } else {
+ showDialog(DIALOG_DEL_CALL);
+ }
+ }
+ }
+ return true;
+ case android.R.id.home:
+ finish();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (mSearchUiVisible) {
+ exitSearchMode();
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ updateState(query);
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ updateState(newText);
+ return true;
+ }
+
+ @Override
+ public boolean onClose() {
+ if (!TextUtils.isEmpty(mSearchView.getQuery())) {
+ mSearchView.setQuery(null, true);
+ }
+ return true;
+ }
+
+ @Override
+ public void onFocusChange(View view, boolean hasFocus) {
+ switch (view.getId()) {
+ case R.id.search_view: {
+ if (hasFocus) {
+ final InputMethodManager imm =
+ (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(mSearchView.findFocus(), 0);
+ }
+ }
+ }
+ }
+
+ private void updateState(String query) {
+ if (!TextUtils.isEmpty(query)) {
+ if (!isSearchMode()) {
+ enterSearchMode();
+ }
+ } else if (isSearchMode()) {
+ exitSearchMode();
+ }
+ doFilter(query);
+ }
+
+ private void setResultAndFinish() {
+ Intent intent = new Intent();
+ Bundle bundle = new Bundle();
+ bundle.putBundle(SimContactsConstants.RESULT_KEY, mChoiceSet);
+ intent.putExtras(bundle);
+ setResult(RESULT_OK, intent);
+ finish();
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ hideSoftKeyboard();
+
+ ContactItemCache cache = (ContactItemCache) v.getTag();
+ String key = String.valueOf(cache.id);
+
+ if (!mChoiceSet.containsKey(key)) {
+ String[] value = null;
+ if (isPickContact()) {
+ value = new String[] {
+ cache.lookupKey, String.valueOf(cache.id),
+ String.valueOf(cache.nameRawContactId),
+ cache.photoUri, cache.name
+ };
+ } else if (isPickPhone()) {
+ value = new String[] {
+ cache.name, cache.number, cache.type,
+ cache.label, cache.contact_id
+ };
+ } else if (isPickEmail()) {
+ value = new String[] {
+ cache.name, cache.email
+ };
+ } else if (isPickSim()) {
+ value = new String[] {
+ cache.name, cache.number, cache.email, cache.anrs
+ };
+ } else if (isPickCall() && mSelectCallLog) {
+ value = new String[] {
+ cache.name, cache.number
+ };
+ }
+ mChoiceSet.putStringArray(key, value);
+ } else {
+ mChoiceSet.remove(key);
+ }
+
+ updateActionBar();
+ mAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_BACK: {
+ if (isSearchMode()) {
+ exitSearchMode();
+ return true;
+ }
+ }
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ private void updateActionBar() {
+ String countTitle = null;
+ if (!mChoiceSet.isEmpty()) {
+ countTitle = getResources().getQuantityString(R.plurals.contacts_selected,
+ mChoiceSet.size(), mChoiceSet.size());
+ }
+ mActionBar.setSubtitle(countTitle);
+ invalidateOptionsMenu();
+ }
+
+ private boolean isSearchMode() {
+ return (mMode & MODE_MASK_SEARCH) == MODE_MASK_SEARCH;
+ }
+
+ private void enterSearchMode() {
+ mMode |= MODE_MASK_SEARCH;
+ configureSearchMode();
+ }
+
+ private void exitSearchMode() {
+ mSearchUiVisible = false;
+ mMode &= ~MODE_MASK_SEARCH;
+ configureSearchMode();
+ }
+
+ private void configureSearchMode() {
+ if (mSearchUiVisible) {
+ mSearchViewContainer.setVisibility(View.VISIBLE);
+ mSearchView.requestFocus();
+ } else {
+ mSearchViewContainer.setVisibility(View.GONE);
+ mSearchView.setQuery(null, true);
+ }
+ mActionBar.setDisplayShowTitleEnabled(!mSearchUiVisible);
+ updateActionBar();
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id, Bundle bundle) {
+ switch (id) {
+ case R.id.dialog_delete_contact_confirmation:
+ return new AlertDialog.Builder(this)
+ .setTitle(R.string.deleteConfirmation_title)
+ .setMessage(getResources().getQuantityString(
+ R.plurals.ContactMultiDeleteConfirmation,
+ mChoiceSet.size(), mChoiceSet.size()))
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, this)
+ .create();
+ case DIALOG_DEL_CALL:
+ return new AlertDialog.Builder(this)
+ .setTitle(R.string.title_del_call)
+ .setMessage(R.string.delete_call_alert)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, this)
+ .create();
+ case R.id.dialog_import_sim_contact_confirmation:
+ return new AlertDialog.Builder(this)
+ .setTitle(R.string.importConfirmation_title)
+ .setMessage(getResources().getQuantityString(
+ R.plurals.ContactMultiImportConfirmation,
+ mChoiceSet.size(), mChoiceSet.size()))
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, this)
+ .create();
+ }
+
+ return super.onCreateDialog(id, bundle);
+ }
+
+ @Override
+ protected void onPrepareDialog(int id, Dialog dialog, Bundle bundle) {
+ switch (id) {
+ case R.id.dialog_delete_contact_confirmation:
+ ((AlertDialog) dialog).setMessage(getResources().getQuantityString(
+ R.plurals.ContactMultiDeleteConfirmation,
+ mChoiceSet.size(), mChoiceSet.size()));
+ break;
+ case R.id.dialog_import_sim_contact_confirmation:
+ ((AlertDialog) dialog).setMessage(getResources().getQuantityString(
+ R.plurals.ContactMultiImportConfirmation,
+ mChoiceSet.size(), mChoiceSet.size()));
+ break;
+ }
+ }
+
+ private class DeleteContactsThread extends Thread implements
+ DialogInterface.OnCancelListener, DialogInterface.OnClickListener {
+ boolean mCanceled = false;
+ private String name = null;
+ private String number = null;
+ private final String[] PROJECTION = new String[] {
+ Phone.CONTACT_ID,
+ Phone.NUMBER,
+ Phone.DISPLAY_NAME
+ };
+ private final int COLUMN_NUMBER = 1;
+ private final int COLUMN_NAME = 2;
+
+ private ArrayList<ContentProviderOperation> mOpsCalls = null;
+ private ArrayList<ContentProviderOperation> mOpsContacts = null;
+
+ public DeleteContactsThread() {
+ }
+
+ @Override
+ public void run() {
+ final Context context = MultiPickContactActivity.this;
+ final ContentResolver resolver = getContentResolver();
+
+ // The mChoiceSet object will change when activity restart, but
+ // DeleteContactsThread running in background, so we need clone the
+ // choiceSet to avoid ConcurrentModificationException.
+ Bundle choiceSet = (Bundle) mChoiceSet.clone();
+ Set<String> keySet = choiceSet.keySet();
+ Iterator<String> it = keySet.iterator();
+
+ ContentProviderOperation.Builder builder = null;
+ ContentProviderOperation cpo = null;
+
+ // Current contact count we can delete.
+ int count = 0;
+
+ // The contacts we batch delete once.
+ final int BATCH_DELETE_CONTACT_NUMBER = 100;
+
+ mOpsCalls = new ArrayList<ContentProviderOperation>();
+ mOpsContacts = new ArrayList<ContentProviderOperation>();
+
+ while (!mCanceled && it.hasNext()) {
+ String id = it.next();
+ Uri uri = null;
+ if (isPickCall()) {
+ uri = Uri.withAppendedPath(Calls.CONTENT_URI, id);
+ builder = ContentProviderOperation.newDelete(uri);
+ cpo = builder.build();
+ mOpsCalls.add(cpo);
+ } else {
+ uri = Uri.withAppendedPath(Contacts.CONTENT_URI, id);
+ long longId = Long.parseLong(id);
+ int subscription = mSimContactsOperation.getSimSubscription(longId);
+
+ if (subscription == SimContactsConstants.SUB_1
+ || subscription == SimContactsConstants.SUB_2) {
+ if (MoreContactUtils.isAPMOnAndSIMPowerDown(context)) {
+ break;
+ }
+ ContentValues values =
+ mSimContactsOperation.getSimAccountValues(longId);
+ log("values is : " + values + "; sub is " + subscription);
+ if (mSimContactsOperation.delete(values, subscription) == 0) {
+ mProgressDialog.incrementProgressBy(1);
+ continue;
+ }
+ }
+ builder = ContentProviderOperation.newDelete(uri);
+ cpo = builder.build();
+ mOpsContacts.add(cpo);
+ }
+ // If contacts more than 2000, delete all contacts
+ // one by one will cause UI nonresponse.
+ mProgressDialog.incrementProgressBy(1);
+ // We batch delete contacts every 100.
+ if (count % BATCH_DELETE_CONTACT_NUMBER == 0) {
+ batchDelete();
+ }
+ count++;
+ }
+
+ batchDelete();
+ mOpsCalls = null;
+ mOpsContacts = null;
+ Log.d(TAG, "DeleteContactsThread run, progress:" + mProgressDialog.getProgress());
+ mProgressDialog.dismiss();
+ finish();
+ }
+
+ /**
+ * Batch delete contacts more efficient than one by one.
+ */
+ private void batchDelete() {
+ try {
+ getContentResolver().applyBatch(CallLog.AUTHORITY, mOpsCalls);
+ getContentResolver().applyBatch(ContactsContract.AUTHORITY, mOpsContacts);
+ mOpsCalls.clear();
+ mOpsContacts.clear();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ } catch (OperationApplicationException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ mCanceled = true;
+ Log.d(TAG, "DeleteContactsThread onCancel, progress:" + mProgressDialog.getProgress());
+ // Give a toast show to tell user delete termination
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_NEGATIVE) {
+ mCanceled = true;
+ mProgressDialog.dismiss();
+ }
+ }
+ }
+
+ @Override
+ public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_SEARCH:
+ case KeyEvent.KEYCODE_CALL:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ CharSequence title = null;
+ CharSequence message = null;
+
+ if (isPickCall()) {
+ title = getString(R.string.delete_call_title);
+ message = getString(R.string.delete_call_message);
+ } else if (isPickSim()) {
+ title = getString(R.string.import_sim_contacts_title);
+ message = getString(R.string.import_sim_contacts_message);
+ } else {
+ title = getString(R.string.delete_contacts_title);
+ message = getString(R.string.delete_contacts_message);
+ }
+
+ Thread thread = isPickSim()
+ ? new ImportAllSimContactsThread() : new DeleteContactsThread();
+
+ mProgressDialog = new ProgressDialog(MultiPickContactActivity.this);
+ mProgressDialog.setTitle(title);
+ mProgressDialog.setMessage(message);
+ mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+ mProgressDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
+ getString(android.R.string.cancel), (DialogInterface.OnClickListener) thread);
+ mProgressDialog.setOnCancelListener((DialogInterface.OnCancelListener) thread);
+ mProgressDialog.setOnKeyListener(this);
+ mProgressDialog.setProgress(0);
+ mProgressDialog.setMax(mChoiceSet.size());
+
+ // set dialog can not be canceled by touching outside area of dialog
+ mProgressDialog.setCanceledOnTouchOutside(false);
+ mProgressDialog.show();
+
+ thread.start();
+ }
+
+ @Override
+ public void onDestroy() {
+ mQueryHandler.removeCallbacksAndMessages(QUERY_TOKEN);
+ if (mAdapter.getCursor() != null) {
+ mAdapter.getCursor().close();
+ }
+
+ if (mProgressDialog != null) {
+ mProgressDialog.cancel();
+ }
+
+ // unregister receiver.
+ if (mBroadcastReceiver != null) {
+ unregisterReceiver(mBroadcastReceiver);
+ }
+
+ super.onDestroy();
+ }
+
+ /**
+ * Just get the uri we need to query contacts.
+ *
+ * @return uri with account info parameter if explicit request contacts fit
+ * current account, else just search contacts fit specified keyword.
+ */
+ private Uri getContactsFilterUri() {
+ Uri filterUri = Contacts.CONTENT_FILTER_URI;
+
+ // To confirm if the search rule must contain account limitation.
+ Intent intent = getIntent();
+ ContactListFilter filter = (ContactListFilter) intent.getParcelableExtra(
+ AccountFilterActivity.KEY_EXTRA_CONTACT_LIST_FILTER);
+ int operation = getIntent().getIntExtra(EXTRA_GROUP_ACTION, GROUP_ACTION_NONE);
+ long groupId = getIntent().getLongExtra(EXTRA_GROUP_ID, -1);
+ String accountName = getIntent().getStringExtra(SimContactsConstants.ACCOUNT_NAME);
+ String accountType = getIntent().getStringExtra(SimContactsConstants.ACCOUNT_TYPE);
+ switch (operation) {
+ case GROUP_ACTION_ADD_MEMBER:
+ case GROUP_ACTION_MOVE_MEMBER:
+ Uri.Builder builder = Contacts.CONTENT_FILTER_URI.buildUpon();
+ builder.appendQueryParameter(ADD_GROUP_MEMBERS,
+ operation == GROUP_ACTION_ADD_MEMBER ? "true" : "false");
+ builder.appendQueryParameter(Groups._ID, String.valueOf(groupId));
+ builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, accountName);
+ builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, accountType);
+ return builder.build();
+ }
+ if (filter != null &&
+ filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
+ // Need consider account info limitation, construct the uri with
+ // account info query parameter.
+ Uri.Builder builder = filterUri.buildUpon();
+ filter.addAccountQueryParameterToUrl(builder);
+ return builder.build();
+ }
+
+ if (!isShowSIM()) {
+ filterUri = filterUri.buildUpon()
+ .appendQueryParameter(RawContacts.ACCOUNT_TYPE, SimAccountType.ACCOUNT_TYPE)
+ .appendQueryParameter(SimContactsConstants.WITHOUT_SIM_FLAG, "true")
+ .build();
+ }
+ // No need to consider account info limitation, just return a uri
+ // with "filter" path.
+ return filterUri;
+ }
+
+ private Uri getUriToQuery() {
+ Uri uri;
+ switch (mMode) {
+ case MODE_DEFAULT_CONTACT: {
+ Intent intent = getIntent();
+ int operation = intent.getIntExtra(EXTRA_GROUP_ACTION, GROUP_ACTION_NONE);
+ long groupId = intent.getLongExtra(EXTRA_GROUP_ID, -1);
+ String accountName = intent.getStringExtra(SimContactsConstants.ACCOUNT_NAME);
+ String accountType = intent.getStringExtra(SimContactsConstants.ACCOUNT_TYPE);
+ switch (operation) {
+ case GROUP_ACTION_ADD_MEMBER:
+ case GROUP_ACTION_MOVE_MEMBER:
+ Uri.Builder builder = Contacts.CONTENT_GROUP_URI.buildUpon();
+ builder.appendQueryParameter(ADD_GROUP_MEMBERS,
+ operation == GROUP_ACTION_ADD_MEMBER ? "true" : "false");
+ builder.appendQueryParameter(Groups._ID, String.valueOf(groupId));
+ builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, accountName);
+ builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, accountType);
+ uri = builder.build();
+ break;
+ default:
+ uri = Contacts.CONTENT_URI;
+ break;
+ }
+ break;
+ }
+ case MODE_SEARCH_CONTACT:
+ uri = Contacts.CONTENT_URI;
+ break;
+ case MODE_DEFAULT_EMAIL:
+ case MODE_SEARCH_EMAIL:
+ uri = Email.CONTENT_URI;
+ break;
+ case MODE_DEFAULT_PHONE:
+ case MODE_SEARCH_PHONE:
+ uri = Phone.CONTENT_URI;
+ break;
+ case MODE_DEFAULT_CALL:
+ case MODE_SEARCH_CALL:
+ uri = Calls.CONTENT_URI_WITH_VOICEMAIL;
+ break;
+ case MODE_DEFAULT_SIM:
+ case MODE_SEARCH_SIM: {
+ int subscription = getIntent().getIntExtra(SimContactsConstants.SUB, 0);
+ uri = querySimContacts(subscription);
+ break;
+ }
+ default:
+ throw new IllegalArgumentException("getUriToQuery: Incorrect mode: " + mMode);
+ }
+ return uri.buildUpon()
+ .appendQueryParameter(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX, "true")
+ .build();
+ }
+
+ private Uri getFilterUri() {
+ switch (mMode) {
+ case MODE_SEARCH_CONTACT:
+ return getContactsFilterUri();
+ case MODE_SEARCH_PHONE:
+ return Phone.CONTENT_FILTER_URI;
+ case MODE_SEARCH_EMAIL:
+ return Email.CONTENT_FILTER_URI;
+ default:
+ log("getFilterUri: Incorrect mode: " + mMode);
+ }
+ return Contacts.CONTENT_FILTER_URI;
+ }
+
+ public String[] getProjectionForQuery() {
+ switch (mMode) {
+ case MODE_DEFAULT_CONTACT:
+ case MODE_SEARCH_CONTACT:
+ return CONTACTS_SUMMARY_PROJECTION;
+ case MODE_DEFAULT_PHONE:
+ case MODE_SEARCH_PHONE:
+ return PHONES_PROJECTION;
+ case MODE_DEFAULT_EMAIL:
+ case MODE_SEARCH_EMAIL:
+ return EMAILS_PROJECTION;
+ case MODE_DEFAULT_CALL:
+ case MODE_SEARCH_CALL:
+ return CALL_LOG_PROJECTION;
+ case MODE_DEFAULT_SIM:
+ case MODE_SEARCH_SIM:
+ return SIM_COLUMN_NAMES;
+ default:
+ log("getProjectionForQuery: Incorrect mode: " + mMode);
+ }
+ return CONTACTS_SUMMARY_PROJECTION;
+ }
+
+ private String getSortOrder(String[] projection) {
+ switch (mMode) {
+ case MODE_DEFAULT_CALL:
+ case MODE_SEARCH_CALL:
+ return CALL_LOG_PROJECTION[2] + SORT_ORDER;
+ }
+ return RawContacts.SORT_KEY_PRIMARY;
+ }
+
+ private String getSelectionForQuery() {
+ switch (mMode) {
+ case MODE_DEFAULT_EMAIL:
+ case MODE_SEARCH_EMAIL:
+ case MODE_DEFAULT_PHONE:
+ case MODE_SEARCH_PHONE:
+ if (isShowSIM()) {
+ return null;
+ }
+ return PHONES_SELECTION;
+ case MODE_DEFAULT_CONTACT:
+ return getSelectionForAccount();
+ case MODE_DEFAULT_SIM:
+ case MODE_SEARCH_SIM:
+ return null;
+ case MODE_DEFAULT_CALL:
+ // Add a subscription judgement, if selection = -1 that means
+ // need query both cards.
+ String selection = null;
+ int slot = getIntent().getIntExtra(SimContactsConstants.SUB,
+ SimContactsConstants.SUB_INVALID);
+ int[] subIds = SubscriptionManager.getSubId(slot);
+ if (SimContactsConstants.SUB_INVALID != slot
+ && subIds != null && subIds.length > 0) {
+ selection = Calls.PHONE_ACCOUNT_ID + "=" + Long.toString(subIds[0]);
+ }
+ return selection;
+ default:
+ return null;
+ }
+ }
+
+ private String getSelectionForAccount() {
+ @SuppressWarnings("deprecation")
+ ContactListFilter filter = (ContactListFilter) getIntent().getExtra(
+ AccountFilterActivity.KEY_EXTRA_CONTACT_LIST_FILTER);
+ if (filter == null) {
+ return null;
+ }
+ switch (filter.filterType) {
+ case ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS:
+ return null;
+ case ContactListFilter.FILTER_TYPE_CUSTOM:
+ return CONTACTS_SELECTION;
+ case ContactListFilter.FILTER_TYPE_ACCOUNT:
+ return null;
+ }
+ return null;
+ }
+
+ private String[] getSelectionArgsForQuery() {
+ switch (mMode) {
+ case MODE_DEFAULT_EMAIL:
+ case MODE_SEARCH_EMAIL:
+ case MODE_DEFAULT_PHONE:
+ case MODE_SEARCH_PHONE:
+ if (isShowSIM()) {
+ return null;
+ }
+ return PHONES_SELECTION_ARGS;
+ case MODE_DEFAULT_SIM:
+ case MODE_SEARCH_SIM:
+ return null;
+ default:
+ return null;
+ }
+ }
+
+ private boolean isShowSIM() {
+ // if airplane mode on, do not show SIM.
+ return !getIntent().hasExtra(EXTRA_NOT_SHOW_SIM_FLAG)
+ && !MoreContactUtils.isAPMOnAndSIMPowerDown(this);
+ }
+
+ public void startQuery() {
+ Uri uri = getUriToQuery();
+ ContactListFilter filter = (ContactListFilter) getIntent().getExtra(
+ AccountFilterActivity.KEY_EXTRA_CONTACT_LIST_FILTER);
+ if (filter != null) {
+ if (filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
+ // We should exclude the invisiable contacts.
+ uri = uri.buildUpon()
+ .appendQueryParameter(RawContacts.ACCOUNT_NAME, filter.accountName)
+ .appendQueryParameter(RawContacts.ACCOUNT_TYPE, filter.accountType)
+ .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(ContactsContract.Directory.DEFAULT))
+ .build();
+ } else if (filter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
+ // Do not query sim contacts in airplane mode.
+ if (!isShowSIM()) {
+ uri = uri.buildUpon()
+ .appendQueryParameter(RawContacts.ACCOUNT_TYPE,
+ SimAccountType.ACCOUNT_TYPE)
+ .appendQueryParameter(SimContactsConstants.WITHOUT_SIM_FLAG, "true")
+ .build();
+ }
+ }
+ }
+ String[] projection = getProjectionForQuery();
+ mQueryHandler.startQuery(QUERY_TOKEN, null, uri, projection,
+ getSelectionForQuery(), getSelectionArgsForQuery(), getSortOrder(projection));
+ }
+
+ public void doFilter(CharSequence s) {
+ if (TextUtils.isEmpty(s)) {
+ startQuery();
+ return;
+ }
+
+ Uri uri = Uri.withAppendedPath(getFilterUri(), Uri.encode(s.toString()));
+ String[] projection = getProjectionForQuery();
+ mQueryHandler.startQuery(QUERY_TOKEN, null, uri, projection,
+ getSelectionForQuery(), getSelectionArgsForQuery(), getSortOrder(projection));
+ }
+
+ public void updateContent() {
+ if (isSearchMode()) {
+ doFilter(mSearchView.getQuery().toString());
+ } else {
+ startQuery();
+ }
+ }
+
+ private CharSequence getDisplayNumber(CharSequence number) {
+ if (TextUtils.isEmpty(number)) {
+ return "";
+ }
+ if (PhoneNumberUtils.isVoiceMailNumber(number.toString())) {
+ return getString(R.string.voicemail);
+ }
+ return number;
+ }
+
+ private boolean isPickContact() {
+ return mMode == MODE_DEFAULT_CONTACT || mMode == MODE_SEARCH_CONTACT;
+ }
+
+ private boolean isPickPhone() {
+ return mMode == MODE_DEFAULT_PHONE || mMode == MODE_SEARCH_PHONE;
+ }
+
+ private boolean isPickSim() {
+ return mMode == MODE_DEFAULT_SIM || mMode == MODE_SEARCH_SIM;
+ }
+
+ private boolean isPickEmail() {
+ return mMode == MODE_DEFAULT_EMAIL || mMode == MODE_SEARCH_EMAIL;
+ }
+
+ private boolean isPickCall() {
+ return mMode == MODE_DEFAULT_CALL || mMode == MODE_SEARCH_CALL;
+ }
+
+ private void selectAll(boolean isSelected) {
+ // update mChoiceSet.
+ // TODO: make it more efficient
+ Cursor cursor = mAdapter.getCursor();
+ if (cursor == null) {
+ log("cursor is null.");
+ return;
+ }
+
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ String id = null;
+ String[] value = null;
+ if (isPickContact()) {
+ id = String.valueOf(cursor.getLong(CONTACT_COLUMN_ID));
+ value = new String[] {
+ cursor.getString(CONTACT_COLUMN_LOOKUP_KEY), id,
+ cursor.getString(CONTACT_COLUMN_RAW_CONTACT_ID),
+ cursor.getString(CONTACT_COLUMN_PHOTO_URI),
+ cursor.getString(CONTACT_COLUMN_DISPLAY_NAME)
+ };
+ } else if (isPickPhone()) {
+ id = String.valueOf(cursor.getLong(PHONE_COLUMN_ID));
+ value = new String[] {
+ cursor.getString(CONTACT_COLUMN_DISPLAY_NAME),
+ cursor.getString(PHONE_COLUMN_NUMBER),
+ String.valueOf(cursor.getInt(PHONE_COLUMN_TYPE)),
+ cursor.getString(PHONE_COLUMN_LABEL),
+ String.valueOf(cursor.getLong(CONTACT_COLUMN_ID))
+ };
+ } else if (isPickEmail()) {
+ id = String.valueOf(cursor.getLong(EMAIL_COLUMN_ID));
+ value = new String[] {
+ cursor.getString(CONTACT_COLUMN_DISPLAY_NAME),
+ cursor.getString(EMAIL_COLUMN_ADDRESS),
+ id
+ };
+ } else if (isPickCall()) {
+ id = String.valueOf(cursor.getLong(CALLLOG_COLUMN_ID));
+ if (mSelectCallLog) {
+ value = new String[] {
+ cursor.getString(CALLLOG_COLUMN_NUMBER),
+ cursor.getString(CALLLOG_COLUMN_CALLER_NAME)
+ };
+ } else {
+ value = new String[] {
+ id
+ };
+ }
+ } else if (isPickSim()) {
+ id = String.valueOf(cursor.getLong(SIM_COLUMN_ID));
+ value = new String[] {
+ cursor.getString(SIM_COLUMN_DISPLAY_NAME),
+ cursor.getString(SIM_COLUMN_NUMBER),
+ cursor.getString(SIM_COLUMN_EMAILS),
+ cursor.getString(SIM_COLUMN_ANRS)
+ };
+ }
+ if (isSelected) {
+ mChoiceSet.putStringArray(id, value);
+ } else {
+ mChoiceSet.remove(id);
+ }
+ }
+
+ updateActionBar();
+ mAdapter.notifyDataSetChanged();
+ }
+
+ private class QueryHandler extends AsyncQueryHandler {
+ public QueryHandler(Context context) {
+ super(context.getContentResolver());
+ }
+
+ @Override
+ protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ mAdapter.changeCursor(cursor);
+ if (cursor == null || cursor.getCount() == 0) {
+ Toast.makeText(MultiPickContactActivity.this,
+ R.string.listFoundAllContactsZero, Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ private final class ContactItemCache {
+ long id;
+ String name;
+ String number;
+ String lookupKey;
+ String type;
+ String label;
+ String contact_id;
+ String email;
+ String anrs;
+ long nameRawContactId;
+ String photoUri;
+ }
+
+ private final class ContactItemListAdapter extends CursorAdapter implements SectionIndexer {
+ Context mContext;
+ protected LayoutInflater mInflater;
+ private ContactsSectionIndexer mIndexer;
+ private ContactPhotoManager mContactPhotoManager;
+
+ public ContactItemListAdapter(Context context) {
+ super(context, null, false);
+
+ mContext = context;
+ mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mContactPhotoManager = ContactPhotoManager.getInstance(mContext);
+ }
+
+ private void assignContactAndFillCache(ContactListItemView cliv, Cursor cursor,
+ Account account, ContactItemCache cache) {
+ String newLookupKey = cursor.getString(CONTACT_COLUMN_LOOKUP_KEY);
+ boolean rebound = !TextUtils.equals(cache.lookupKey, newLookupKey);
+
+ cache.lookupKey = newLookupKey;
+ cache.name = cursor.getString(CONTACT_COLUMN_DISPLAY_NAME);
+
+ long photoId = cursor.getLong(CONTACT_COLUMN_PHOTO_ID);
+ mContactPhotoManager.loadThumbnail(cliv.getPhotoView(), photoId, account, false, true,
+ new DefaultImageRequest(cache.name, cache.lookupKey, true));
+
+ CharSequence query = mSearchView != null ? mSearchView.getQuery() : null;
+ cliv.setHighlightedPrefix(query != null ? query.toString().toUpperCase() : null);
+ cliv.setDisplayName(cache.name);
+ cliv.setChecked(mChoiceSet.containsKey(String.valueOf(cache.id)), !rebound);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ ContactItemCache cache = (ContactItemCache) view.getTag();
+ ContactListItemView cliv = (ContactListItemView) view.findViewById(R.id.contact);
+
+ if (isPickContact()) {
+ cache.id = cursor.getLong(CONTACT_COLUMN_ID);
+ cache.nameRawContactId = cursor.getLong(CONTACT_COLUMN_RAW_CONTACT_ID);
+ cache.photoUri = cursor.getString(CONTACT_COLUMN_PHOTO_URI);
+
+ Account account = null;
+ if (!cursor.isNull(CONTACT_COLUMN_ACCOUNT_TYPE)
+ && !cursor.isNull(CONTACT_COLUMN_ACCOUNT_NAME)) {
+ final String accountType = cursor.getString(CONTACT_COLUMN_ACCOUNT_TYPE);
+ final String accountName = cursor.getString(CONTACT_COLUMN_ACCOUNT_NAME);
+ account = new Account(accountName, accountType);
+ }
+
+ assignContactAndFillCache(cliv, cursor, account, cache);
+ cliv.setPhoneNumber(null, null);
+ } else if (isPickPhone()) {
+ cache.id = cursor.getLong(PHONE_COLUMN_ID);
+ cache.number = cursor.getString(PHONE_COLUMN_NUMBER);
+ cache.label = cursor.getString(PHONE_COLUMN_LABEL);
+ cache.type = String.valueOf(cursor.getInt(PHONE_COLUMN_TYPE));
+
+ assignContactAndFillCache(cliv, cursor, null, cache);
+ cliv.setPhoneNumber(cache.number, null);
+ } else if (isPickSim()) {
+ cache.id = cursor.getLong(SIM_COLUMN_ID);
+ cache.name = cursor.getString(SIM_COLUMN_DISPLAY_NAME);
+ cache.number = cursor.getString(SIM_COLUMN_NUMBER);
+ cache.email = cursor.getString(SIM_COLUMN_EMAILS);
+ cache.anrs = cursor.getString(SIM_COLUMN_ANRS);
+
+ cliv.setDisplayName(cache.name);
+ mContactPhotoManager.loadThumbnail(cliv.getPhotoView(), -1, null, false, true,
+ new DefaultImageRequest(cache.name, cache.lookupKey, true));
+ if (!TextUtils.isEmpty(cache.number)) {
+ cliv.setPhoneNumber(cache.number, null);
+ } else if (!TextUtils.isEmpty(cache.email)) {
+ String[] emailArray = (cache.email).split(",");
+ cliv.setPhoneNumber(emailArray[0], null);
+ } else {
+ cliv.setPhoneNumber(null, null);
+ }
+ cliv.setChecked(mChoiceSet.containsKey(String.valueOf(cache.id)), true);
+ } else if (isPickEmail()) {
+ cache.id = cursor.getLong(EMAIL_COLUMN_ID);
+ cache.email = cursor.getString(EMAIL_COLUMN_ADDRESS);
+
+ assignContactAndFillCache(cliv, cursor, null, cache);
+ cliv.setPhoneNumber(cache.email, null);
+ } else if (isPickCall()) {
+ cache.id = cursor.getLong(CALLLOG_COLUMN_ID);
+ cache.name = cursor.getString(CALLLOG_COLUMN_CALLER_NAME);
+ cache.number = cursor.getString(CALLLOG_COLUMN_NUMBER);
+
+ String callerName = cursor.getString(CALLLOG_COLUMN_CALLER_NAME);
+ int callerNumberType = cursor.getInt(CALLLOG_COLUMN_CALLER_NUMBERTYPE);
+ String callerNumberLabel = cursor.getString(CALLLOG_COLUMN_CALLER_NUMBERLABEL);
+ String geocodedLocation = cursor.getString(CALLLOG_COLUMN_CALLER_LOCATION);
+ String accountId = cursor.getString(CALLLOG_COLUMN_PHONE_ACCOUNT);
+ long date = cursor.getLong(CALLLOG_COLUMN_DATE);
+ long duration = cursor.getLong(CALLLOG_COLUMN_DURATION);
+ int type = cursor.getInt(CALLLOG_COLUMN_CALL_TYPE);
+
+ ImageView callType = (ImageView) view.findViewById(R.id.call_type_icon);
+ TextView dateText = (TextView) view.findViewById(R.id.call_date);
+ TextView durationText = (TextView) view.findViewById(R.id.duration);
+ TextView subSlotText = (TextView) view.findViewById(R.id.subscription);
+ TextView numberLabelText = (TextView) view.findViewById(R.id.label);
+ TextView nameText = (TextView) view.findViewById(R.id.name);
+
+ // only for monkey test, callType can not be null in normal behaviour
+ if (callType == null) {
+ return;
+ }
+
+ callType.setVisibility(View.VISIBLE);
+ // Set the icon
+ switch (type) {
+ case Calls.INCOMING_TYPE:
+ callType.setImageResource(R.drawable.ic_call_incoming_holo_dark);
+ break;
+ case Calls.OUTGOING_TYPE:
+ callType.setImageResource(R.drawable.ic_call_outgoing_holo_dark);
+ break;
+ case Calls.MISSED_TYPE:
+ callType.setImageResource(R.drawable.ic_call_missed_holo_dark);
+ break;
+ default:
+ callType.setVisibility(View.INVISIBLE);
+ break;
+ }
+
+ // set the number
+ if (!TextUtils.isEmpty(callerName)) {
+ nameText.setText(callerName);
+ } else {
+ nameText.setText(getDisplayNumber(cache.number));
+ }
+
+ CharSequence numberLabel = null;
+ if (callerNumberType != 0 && !PhoneNumberUtils.isUriNumber(cache.number)) {
+ numberLabel = Phone.getDisplayLabel(context, callerNumberType,
+ callerNumberLabel);
+ } else {
+ numberLabel = geocodedLocation;
+ }
+ numberLabelText.setText(numberLabel);
+ numberLabelText.setVisibility(TextUtils.isEmpty(numberLabel)
+ ? View.GONE : View.VISIBLE);
+
+ // set date
+ dateText.setText(DateUtils.getRelativeTimeSpanString(date,
+ System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS,
+ DateUtils.FORMAT_ABBREV_RELATIVE));
+
+ // set duration
+ durationText.setText(DateUtils.formatElapsedTime(duration));
+
+ // set slot
+ if (isMultiSimEnabled()) {
+ int slotId = SimContactsConstants.SUB_INVALID;
+ if (accountId != null) {
+ try {
+ slotId = SubscriptionManager.getSlotId(Integer.valueOf(accountId));
+ } catch (NumberFormatException e) {
+ // ignore and keep the default 'invalid'
+ }
+ }
+ subSlotText.setText(MoreContactUtils.getMultiSimAliasesName(
+ MultiPickContactActivity.this, slotId));
+ } else {
+ subSlotText.setVisibility(View.GONE);
+ }
+
+ CheckBox checkBox = (CheckBox) view.findViewById(R.id.pick_contact_check);
+ checkBox.setChecked(mChoiceSet.containsKey(String.valueOf(cache.id)));
+ }
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ int layoutResId = isPickCall() ? R.layout.pick_calls_item : R.layout.pick_contact_item;
+ View v = mInflater.inflate(layoutResId, parent, false);
+ ContactListItemView cliv = (ContactListItemView) v.findViewById(R.id.contact);
+ if (cliv != null) {
+ cliv.setUnknownNameText(getString(R.string.missing_name));
+ }
+ ContactItemCache cache = new ContactItemCache();
+ v.setTag(cache);
+ return v;
+ }
+
+ @Override
+ protected void onContentChanged() {
+ updateContent();
+ }
+
+ @Override
+ public void changeCursor(Cursor cursor) {
+ super.changeCursor(cursor);
+ String[] sections = null;
+ int[] counts = null;
+ Bundle extras = cursor != null ? cursor.getExtras() : null;
+ if (extras != null &&
+ extras.containsKey(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES)) {
+ sections = extras.getStringArray(
+ ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES);
+ counts = extras.getIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS);
+ } else {
+ sections = new String[0];
+ counts = new int[0];
+ }
+ mIndexer = new ContactsSectionIndexer(sections, counts);
+ updateActionBar();
+ }
+
+ @Override
+ public Object[] getSections() {
+ if (mIndexer != null) {
+ return mIndexer.getSections();
+ }
+ return null;
+ }
+
+ @Override
+ public int getPositionForSection(int section) {
+ Cursor cursor = getCursor();
+ if (cursor == null) {
+ return 0;
+ }
+ if (mIndexer != null) {
+ return mIndexer.getPositionForSection(section);
+ }
+ return 0;
+ }
+
+ @Override
+ public int getSectionForPosition(int position) {
+ if (mIndexer != null) {
+ return mIndexer.getSectionForPosition(position);
+ }
+ return -1;
+ }
+
+ public int getSortIndex() {
+ switch (mMode) {
+ case MODE_DEFAULT_CONTACT:
+ case MODE_SEARCH_CONTACT:
+ case MODE_DEFAULT_PHONE:
+ case MODE_SEARCH_PHONE:
+ case MODE_DEFAULT_EMAIL:
+ case MODE_SEARCH_EMAIL:
+ return CONTACT_COLUMN_DISPLAY_NAME;
+ case MODE_DEFAULT_CALL:
+ case MODE_SEARCH_CALL:
+ return CALLLOG_COLUMN_CALLER_NAME;
+ case MODE_DEFAULT_SIM:
+ case MODE_SEARCH_SIM:
+ return SIM_COLUMN_DISPLAY_NAME;
+ default:
+ throw new IllegalArgumentException("Incorrect mode for multi pick");
+ }
+ }
+ }
+
+ /**
+ * Dismisses the soft keyboard when the list takes focus.
+ */
+ public boolean onTouch(View view, MotionEvent event) {
+ if (view == getListView()) {
+ hideSoftKeyboard();
+ }
+ return false;
+ }
+
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (actionId == EditorInfo.IME_ACTION_DONE) {
+ hideSoftKeyboard();
+ return true;
+ }
+ return false;
+ }
+
+ private void hideSoftKeyboard() {
+ // Hide soft keyboard, if visible
+ InputMethodManager inputMethodManager =
+ (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ inputMethodManager.hideSoftInputFromWindow(mSearchView.getWindowToken(), 0);
+ }
+
+ protected static void log(String msg) {
+ if (DEBUG) Log.d(TAG, msg);
+ }
+
+ private Uri querySimContacts(int subscription) {
+ Uri uri = null;
+ if (subscription != SimContactsConstants.SUB_1
+ && subscription != SimContactsConstants.SUB_2) {
+ return uri;
+ }
+ int[] subId = SubscriptionManager.getSubId(subscription);
+ if (subId != null && isMultiSimEnabled()) {
+ uri = Uri.parse(SimContactsConstants.SIM_SUB_URI + subId[0]);
+ }
+ else {
+ uri = Uri.parse(SimContactsConstants.SIM_URI);
+ }
+
+ return uri;
+ }
+
+ private boolean isMultiSimEnabled() {
+ return TelephonyManager.getDefault().isMultiSimEnabled();
+ }
+
+ protected Account[] getSimAccounts() {
+ return mAccountManager.getAccountsByType(SimContactsConstants.ACCOUNT_TYPE_SIM);
+ }
+
+ private class ImportAllSimContactsThread extends Thread
+ implements DialogInterface.OnCancelListener, DialogInterface.OnClickListener {
+ boolean mCanceled = false;
+ // The total count how many to import.
+ private int mTotalCount = 0;
+ // The real count have imported.
+ private int mActualCount = 0;
+
+ private Account mAccount;
+
+ public ImportAllSimContactsThread() {
+ }
+
+ @Override
+ public void run() {
+ final ContentValues emptyContentValues = new ContentValues();
+ final ContentResolver resolver = getContentResolver();
+
+ String type = getIntent().getStringExtra(SimContactsConstants.ACCOUNT_TYPE);
+ String name = getIntent().getStringExtra(SimContactsConstants.ACCOUNT_NAME);
+ mAccount = new Account(name != null ? name : SimContactsConstants.PHONE_NAME,
+ type != null ? type : SimContactsConstants.ACCOUNT_TYPE_PHONE);
+ log("import sim contact to account: " + mAccount);
+ mTotalCount = mChoiceSet.size();
+
+ for (String key : mChoiceSet.keySet()) {
+ if (mCanceled) {
+ break;
+ }
+ String[] values = mChoiceSet.getStringArray(key);
+ actuallyImportOneSimContact(values, resolver, mAccount);
+ mActualCount++;
+ mProgressDialog.incrementProgressBy(1);
+ }
+ finish();
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ final Context context = MultiPickContactActivity.this;
+ mCanceled = true;
+ // Give a toast show to tell user import termination.
+ if (mActualCount < mTotalCount) {
+ String text = getResources().getQuantityString(R.plurals.import_progress,
+ mActualCount, mActualCount);
+ Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(context, R.string.import_finish, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_NEGATIVE) {
+ mCanceled = true;
+ mProgressDialog.dismiss();
+ }
+ }
+ }
+
+ private static void actuallyImportOneSimContact(
+ String[] values, final ContentResolver resolver, Account account) {
+
+ final String name = values[SIM_COLUMN_DISPLAY_NAME];
+ final String phoneNumber = values[SIM_COLUMN_NUMBER];
+ final String emailAddresses = values[SIM_COLUMN_EMAILS];
+ final String anrs = values[SIM_COLUMN_ANRS];
+ final String[] emailAddressArray;
+ final String[] anrArray;
+ if (!TextUtils.isEmpty(emailAddresses)) {
+ emailAddressArray = emailAddresses.split(",");
+ } else {
+ emailAddressArray = null;
+ }
+ if (!TextUtils.isEmpty(anrs)) {
+ anrArray = anrs.split(",");
+ } else {
+ anrArray = null;
+ }
+ log(" actuallyImportOneSimContact: name= " + name +
+ ", phoneNumber= " + phoneNumber + ", emails= " + emailAddresses
+ + ", anrs= " + anrs + ", account is " + account);
+ final ArrayList<ContentProviderOperation> operationList =
+ new ArrayList<ContentProviderOperation>();
+ ContentProviderOperation.Builder builder =
+ ContentProviderOperation.newInsert(RawContacts.CONTENT_URI);
+ builder.withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED);
+ if (account != null) {
+ builder.withValue(RawContacts.ACCOUNT_NAME, account.name);
+ builder.withValue(RawContacts.ACCOUNT_TYPE, account.type);
+ }
+ operationList.add(builder.build());
+
+ builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+ builder.withValueBackReference(StructuredName.RAW_CONTACT_ID, 0);
+ builder.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
+ builder.withValue(StructuredName.DISPLAY_NAME, name);
+ operationList.add(builder.build());
+
+ builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+ builder.withValueBackReference(Phone.RAW_CONTACT_ID, 0);
+ builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+ builder.withValue(Phone.TYPE, Phone.TYPE_MOBILE);
+ builder.withValue(Phone.NUMBER, phoneNumber);
+ builder.withValue(Data.IS_PRIMARY, 1);
+ operationList.add(builder.build());
+
+ if (anrArray != null) {
+ for (String anr : anrArray) {
+ builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+ builder.withValueBackReference(Phone.RAW_CONTACT_ID, 0);
+ builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+ builder.withValue(Phone.TYPE, Phone.TYPE_HOME);
+ builder.withValue(Phone.NUMBER, anr);
+ operationList.add(builder.build());
+ }
+ }
+
+ if (emailAddresses != null) {
+ for (String emailAddress : emailAddressArray) {
+ builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+ builder.withValueBackReference(Email.RAW_CONTACT_ID, 0);
+ builder.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+ builder.withValue(Email.TYPE, Email.TYPE_MOBILE);
+ builder.withValue(Email.ADDRESS, emailAddress);
+ operationList.add(builder.build());
+ }
+ }
+
+ try {
+ resolver.applyBatch(ContactsContract.AUTHORITY, operationList);
+ } catch (RemoteException e) {
+ log(String.format("%s: %s", e.toString(), e.getMessage()));
+ } catch (OperationApplicationException e) {
+ log(String.format("%s: %s", e.toString(), e.getMessage()));
+ }
+ }
+
+ /**
+ * After turn on airplane mode, cancel import sim contacts operation.
+ */
+ private void cancelSimContactsImporting() {
+ if (mProgressDialog != null && mProgressDialog.isShowing()) {
+ mProgressDialog.cancel();
+ }
+ }
+
+ private void showGroupSelectionList(String accountType, long srcGroupId) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(getString(R.string.label_groups));
+ ContentResolver resolver = getContentResolver();
+ String selection = Groups.ACCOUNT_TYPE + " =? AND " + Groups.DELETED + " != ?";
+ ArrayList<String> items = new ArrayList<String>();
+
+ mGroupIds.clear();
+ items.clear();
+ Cursor cursor = resolver.query(Groups.CONTENT_URI, new String[] {
+ Groups._ID, Groups.TITLE
+ },
+ selection,
+ new String[] {
+ accountType, "1"
+ },
+ null);
+ if (cursor == null || cursor.getCount() == 0) {
+ Toast.makeText(this, R.string.message_can_not_move_members,
+ Toast.LENGTH_LONG).show();
+ return;
+ } else {
+ try {
+ while (cursor.moveToNext()) {
+ if (!cursor.getString(0).equals(String.valueOf(srcGroupId))) {
+ mGroupIds.add(cursor.getLong(0));
+ items.add(cursor.getString(1));
+ }
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+ if (mGroupIds.size() == 0) {
+ Toast.makeText(this, R.string.message_can_not_move_members,
+ Toast.LENGTH_LONG).show();
+ return;
+ }
+ String[] groupItem = new String[items.size()];
+ for (int i = 0; i < items.size(); i++) {
+ groupItem[i] = items.get(i);
+ }
+ builder.setItems(groupItem, new ChooseActionListener());
+ builder.create().show();
+ }
+
+ private class ChooseActionListener implements DialogInterface.OnClickListener {
+ public void onClick(DialogInterface dialog, int which) {
+ new MoveGroupMemberTask(mChoiceSet,
+ getIntent().getLongExtra(EXTRA_GROUP_ID, -1),
+ mGroupIds.get(which)).execute();
+ }
+ }
+
+ class MoveGroupMemberTask extends AsyncTask<Object, Object, Object> {
+
+ private static final String GROUP_QUERY_GROUP_MEMBER_SELECTION =
+ Data.MIMETYPE + "=? AND "
+ + GroupMembership.GROUP_ROW_ID + "=?";
+
+ private static final String GROUP_QUERY_RAW_CONTACTS_SELECTION =
+ RawContacts.CONTACT_ID + "=?";
+
+ private static final String GROUP_DELETE_MEMBER_SELECTION = Data.CONTACT_ID
+ + "=? AND "
+ + Data.MIMETYPE
+ + "=? AND "
+ + GroupMembership.GROUP_ROW_ID
+ + "=?";
+
+ private static final int BUFFER_LENGTH = 499;
+
+ private Bundle mChoiceSet;
+ private long mDestGroupId;
+ private long mSrcGroupId;
+ private boolean mCanceled = false;
+
+ private ArrayList<ContentProviderOperation> mAddOrMoveOperation;
+ private ArrayList<ContentProviderOperation> mDeleteOperation;
+ private ArrayList<String> mGroupMemberList = new ArrayList<String>();
+
+ public MoveGroupMemberTask(Bundle choiceSet,
+ long srcGroupId, long destGroupId) {
+ mChoiceSet = choiceSet;
+ mSrcGroupId = srcGroupId;
+ mDestGroupId = destGroupId;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ mProgressDialog = new ProgressDialog(MultiPickContactActivity.this,
+ com.android.internal.R.style.Theme_Holo_Light_Dialog_Alert);
+ mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+ mProgressDialog.setTitle(getProgressDialogTitle());
+ mProgressDialog.setMessage(getProgressDialogMessage());
+ mProgressDialog.setMax(mChoiceSet != null ? mChoiceSet.keySet().size() : 100);
+ mProgressDialog.setProgress(0);
+ mProgressDialog.setCanceledOnTouchOutside(false);
+ mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ mCanceled = true;
+ }
+ });
+ mProgressDialog.show();
+ }
+
+ @Override
+ protected Bundle doInBackground(Object... params) {
+ if (mChoiceSet == null || mSrcGroupId <= 0) {
+ return null;
+ }
+ ContentResolver resolver = getContentResolver();
+ Cursor memberCursor = null;
+
+ memberCursor = resolver.query(Data.CONTENT_URI,
+ new String[] {
+ Data.CONTACT_ID
+ },
+ GROUP_QUERY_GROUP_MEMBER_SELECTION,
+ new String[] {
+ GroupMembership.CONTENT_ITEM_TYPE,
+ String.valueOf(mDestGroupId)
+ },
+ null);
+
+ if (memberCursor != null && memberCursor.getCount() > 0) {
+ try {
+ while (memberCursor.moveToNext()) {
+ // Mark those contacts that already exist in the dest
+ // group
+ mGroupMemberList.add(String.valueOf(memberCursor.getLong(0)));
+ }
+ } finally {
+ if (memberCursor != null) {
+ memberCursor.close();
+ }
+ }
+ }
+
+ Set<String> keySet = mChoiceSet.keySet();
+ Iterator<String> it = keySet.iterator();
+
+ ContentProviderOperation.Builder builder;
+
+ mAddOrMoveOperation = new ArrayList<ContentProviderOperation>();
+ mDeleteOperation = new ArrayList<ContentProviderOperation>();
+ String id;
+ int count = 0;
+ int maxSize = mChoiceSet.keySet().size();
+ while (!mCanceled && it.hasNext()) {
+ id = it.next();
+ ++count;
+
+ if (mDestGroupId <= 0) {
+ // Invalid group id, cancel the task
+ return null;
+ }
+ if (mProgressDialog != null && mProgressDialog.isShowing()
+ && count < maxSize - (maxSize) / 100) {
+ mProgressDialog.incrementProgressBy(1);
+ }
+ if (mGroupMemberList.contains(id)) {
+ // If the contact already exists in the group, need to
+ // delete those
+ // contacts that in the previous group
+ builder = ContentProviderOperation.newDelete(Data.CONTENT_URI);
+ builder.withSelection(GROUP_DELETE_MEMBER_SELECTION,
+ new String[] {
+ id,
+ GroupMembership.CONTENT_ITEM_TYPE,
+ String.valueOf(mSrcGroupId)
+ });
+ mDeleteOperation.add(builder.build());
+ continue;
+ }
+ ContentValues values = new ContentValues();
+ values.put(GroupMembership.GROUP_ROW_ID, mDestGroupId);
+ builder = ContentProviderOperation.newUpdate(Data.CONTENT_URI);
+ builder.withSelection(GROUP_DELETE_MEMBER_SELECTION,
+ new String[] {
+ id,
+ GroupMembership.CONTENT_ITEM_TYPE,
+ String.valueOf(mSrcGroupId)
+ });
+ builder.withValues(values);
+ mAddOrMoveOperation.add(builder.build());
+ }
+
+ if (mDeleteOperation.size() > 0) {
+ if (mDeleteOperation.size() > BUFFER_LENGTH) {
+ addOrMoveApplyBatchByBuffer(mDeleteOperation, resolver);
+ } else {
+ try {
+ resolver.applyBatch(ContactsContract.AUTHORITY, mDeleteOperation);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ } catch (OperationApplicationException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ if (mAddOrMoveOperation.size() > BUFFER_LENGTH) {
+ addOrMoveApplyBatchByBuffer(mAddOrMoveOperation, resolver);
+ } else {
+ try {
+ resolver.applyBatch(ContactsContract.AUTHORITY, mAddOrMoveOperation);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ } catch (OperationApplicationException e) {
+ e.printStackTrace();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Object result) {
+ if (mProgressDialog != null && mProgressDialog.isShowing()) {
+ mProgressDialog.dismiss();
+ finish();
+ }
+ }
+
+ private void addOrMoveApplyBatchByBuffer(ArrayList<ContentProviderOperation> list,
+ ContentResolver cr) {
+ final ArrayList<ContentProviderOperation> temp
+ = new ArrayList<ContentProviderOperation>(BUFFER_LENGTH);
+ int bufferSize = list.size() / BUFFER_LENGTH;
+ for (int index = 0; index <= bufferSize; index++) {
+ temp.clear();
+ if (index == bufferSize) {
+ for (int i = index * BUFFER_LENGTH; i < list.size(); i++) {
+ temp.add(list.get(i));
+ }
+ } else {
+ for (int i = index * BUFFER_LENGTH;
+ i < index * BUFFER_LENGTH + BUFFER_LENGTH; i++) {
+ temp.add(list.get(i));
+ }
+ }
+ if (!temp.isEmpty()) {
+ try {
+ cr.applyBatch(ContactsContract.AUTHORITY, temp);
+ } catch (Exception e) {
+ Log.e(TAG, "apply batch by buffer error:" + e);
+ }
+ }
+ }
+ }
+
+ private String getProgressDialogTitle() {
+ return getString(R.string.title_move_members);
+ }
+
+ private String getProgressDialogMessage() {
+ return getString(R.string.message_move_members);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 97c6ea028..bad3f6996 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -16,12 +16,15 @@
package com.android.contacts.activities;
+import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.ActivityNotFoundException;
import android.content.ContentUris;
import android.content.Context;
+import android.content.BroadcastReceiver;
+import android.content.IntentFilter;
import android.content.Intent;
import android.graphics.Rect;
import android.net.Uri;
@@ -33,6 +36,7 @@ import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.ProviderStatus;
import android.provider.Settings;
+import android.preference.PreferenceManager;
import android.support.v13.app.FragmentPagerAdapter;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
@@ -59,8 +63,14 @@ import com.android.contacts.common.dialog.ClearFrequentsDialog;
import com.android.contacts.common.util.ImplicitIntentsUtil;
import com.android.contacts.common.widget.FloatingActionButtonController;
import com.android.contacts.editor.EditorIntents;
+import com.android.contacts.common.editor.SelectAccountDialogFragment;
+import com.android.contacts.group.GroupBrowseListFragment;
+import com.android.contacts.group.GroupBrowseListFragment.OnGroupBrowserActionListener;
+import com.android.contacts.group.GroupDetailFragment;
import com.android.contacts.interactions.ContactDeletionInteraction;
import com.android.contacts.common.interactions.ImportExportDialogFragment;
+import com.android.contacts.common.interactions.ImportExportDialogFragment.ExportToSimThread;
+import com.android.contacts.common.list.AccountFilterActivity;
import com.android.contacts.common.list.ContactEntryListFragment;
import com.android.contacts.common.list.ContactListFilter;
import com.android.contacts.common.list.ContactListFilterController;
@@ -82,17 +92,24 @@ import com.android.contacts.list.OnContactsUnavailableActionListener;
import com.android.contacts.list.ProviderStatusWatcher;
import com.android.contacts.list.ProviderStatusWatcher.ProviderStatusListener;
import com.android.contacts.common.list.ViewPagerTabs;
+import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.preference.ContactsPreferenceActivity;
+import com.android.contacts.common.SimContactsConstants;
import com.android.contacts.common.util.AccountFilterUtil;
import com.android.contacts.common.util.ViewUtil;
import com.android.contacts.quickcontact.QuickContactActivity;
import com.android.contacts.util.AccountPromptUtils;
import com.android.contacts.common.util.Constants;
+import com.android.contacts.common.vcard.ExportVCardActivity;
+import com.android.contacts.common.vcard.VCardCommonArguments;
import com.android.contacts.util.DialogManager;
import com.android.contactsbind.HelpUtils;
import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
import java.util.Locale;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -110,11 +127,13 @@ public class PeopleActivity extends ContactsActivity implements
private static final String TAG = "PeopleActivity";
+ public static String EDITABLE_KEY = "search_contacts";
private static final String ENABLE_DEBUG_OPTIONS_HIDDEN_CODE = "debug debug!";
// These values needs to start at 2. See {@link ContactEntryListFragment}.
private static final int SUBACTIVITY_ACCOUNT_FILTER = 2;
-
+ private static final int SUBACTIVITY_NEW_GROUP = 4;
+ private static final int SUBACTIVITY_EDIT_GROUP = 5;
private final DialogManager mDialogManager = new DialogManager(this);
private ContactsIntentResolver mIntentResolver;
@@ -122,6 +141,9 @@ public class PeopleActivity extends ContactsActivity implements
private ActionBarAdapter mActionBarAdapter;
private FloatingActionButtonController mFloatingActionButtonController;
+ private GroupDetailFragment mGroupDetailFragment;
+ private final GroupDetailFragmentListener mGroupDetailFragmentListener =
+ new GroupDetailFragmentListener();
private View mFloatingActionButtonContainer;
private boolean wasLastFabAnimationScaleIn = false;
@@ -141,6 +163,7 @@ public class PeopleActivity extends ContactsActivity implements
*/
private MultiSelectContactsListFragment mAllFragment;
private ContactTileListFragment mFavoritesFragment;
+ private GroupBrowseListFragment mGroupsFragment;
/** ViewPager for swipe */
private ViewPager mTabPager;
@@ -151,6 +174,7 @@ public class PeopleActivity extends ContactsActivity implements
private boolean mEnableDebugMenuOptions;
+ private ExportToSimThread mExportThread = null;
/**
* True if this activity instance is a re-created one. i.e. set true after orientation change.
* This is set in {@link #onCreate} for later use in {@link #onStart}.
@@ -173,6 +197,11 @@ public class PeopleActivity extends ContactsActivity implements
/** Sequential ID assigned to each instance; used for logging */
private final int mInstanceId;
private static final AtomicInteger sNextInstanceId = new AtomicInteger();
+ // TODO: we need to refactor the export code in future release.
+ // QRD enhancement: contacts list for multi contact pick
+ private ArrayList<String[]> mContactList;
+
+ private BroadcastReceiver mExportToSimCompleteListener = null;
public PeopleActivity() {
mInstanceId = sNextInstanceId.getAndIncrement();
@@ -247,6 +276,7 @@ public class PeopleActivity extends ContactsActivity implements
Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity.onCreate finish");
}
getWindow().setBackgroundDrawable(null);
+ registerReceiver();
}
@Override
@@ -266,6 +296,21 @@ public class PeopleActivity extends ContactsActivity implements
invalidateOptionsMenuIfNeeded();
}
+ private void registerReceiver() {
+ mExportToSimCompleteListener = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(SimContactsConstants.INTENT_EXPORT_COMPLETE)) {
+ ImportExportDialogFragment.destroyExportToSimThread();
+ mExportThread = null;
+ }
+ }
+ };
+ IntentFilter exportCompleteFilter = new IntentFilter(
+ SimContactsConstants.INTENT_EXPORT_COMPLETE);
+ registerReceiver(mExportToSimCompleteListener, exportCompleteFilter);
+ }
+
/**
* Resolve the intent and initialize {@link #mRequest}, and launch another activity if redirect
* is needed.
@@ -311,6 +356,7 @@ public class PeopleActivity extends ContactsActivity implements
mTabTitles = new String[TabState.COUNT];
mTabTitles[TabState.FAVORITES] = getString(R.string.favorites_tab_label);
mTabTitles[TabState.ALL] = getString(R.string.all_contacts_tab_label);
+ mTabTitles[TabState.GROUPS] = getString(R.string.contacts_groups_label);
mTabPager = getView(R.id.tab_pager);
mTabPagerAdapter = new TabPagerAdapter();
mTabPager.setAdapter(mTabPagerAdapter);
@@ -333,6 +379,7 @@ public class PeopleActivity extends ContactsActivity implements
final String FAVORITE_TAG = "tab-pager-favorite";
final String ALL_TAG = "tab-pager-all";
+ final String GROUPS_TAG = "tab-pager-groups";
// Create the fragments and add as children of the view pager.
// The pager adapter will only change the visibility; it'll never create/destroy
@@ -344,13 +391,17 @@ public class PeopleActivity extends ContactsActivity implements
fragmentManager.findFragmentByTag(FAVORITE_TAG);
mAllFragment = (MultiSelectContactsListFragment)
fragmentManager.findFragmentByTag(ALL_TAG);
+ mGroupsFragment = (GroupBrowseListFragment)
+ fragmentManager.findFragmentByTag(GROUPS_TAG);
if (mFavoritesFragment == null) {
mFavoritesFragment = new ContactTileListFragment();
mAllFragment = new MultiSelectContactsListFragment();
+ mGroupsFragment = new GroupBrowseListFragment();
transaction.add(R.id.tab_pager, mFavoritesFragment, FAVORITE_TAG);
transaction.add(R.id.tab_pager, mAllFragment, ALL_TAG);
+ transaction.add(R.id.tab_pager, mGroupsFragment, GROUPS_TAG);
}
mFavoritesFragment.setListener(mFavoritesFragmentListener);
@@ -358,10 +409,13 @@ public class PeopleActivity extends ContactsActivity implements
mAllFragment.setOnContactListActionListener(new ContactBrowserActionListener());
mAllFragment.setCheckBoxListListener(new CheckBoxListListener());
+ mGroupsFragment.setListener(new GroupBrowserActionListener());
+
// Hide all fragments for now. We adjust visibility when we get onSelectedTabChanged()
// from ActionBarAdapter.
transaction.hide(mFavoritesFragment);
transaction.hide(mAllFragment);
+ transaction.hide(mGroupsFragment);
transaction.commitAllowingStateLoss();
fragmentManager.executePendingTransactions();
@@ -417,6 +471,8 @@ public class PeopleActivity extends ContactsActivity implements
mOptionsMenuContactsAvailable = false;
mProviderStatusWatcher.stop();
super.onPause();
+ dismissDialog(ImportExportDialogFragment.TAG);
+ dismissDialog(SelectAccountDialogFragment.TAG);
}
@Override
@@ -451,9 +507,22 @@ public class PeopleActivity extends ContactsActivity implements
mContactListFilterController.removeListener(this);
}
+ if (mExportToSimCompleteListener != null) {
+ unregisterReceiver(mExportToSimCompleteListener);
+ }
super.onDestroy();
}
+ private void dismissDialog(String tag) {
+ // when this activity lose focus,dismiss the dialog
+ Fragment dialogFragment = getFragmentManager().findFragmentByTag(tag);
+ if (dialogFragment != null) {
+ if (dialogFragment instanceof DialogFragment) {
+ ((DialogFragment) dialogFragment).dismiss();
+ }
+ }
+ }
+
private void configureFragments(boolean fromRequest) {
if (fromRequest) {
ContactListFilter filter = null;
@@ -480,6 +549,9 @@ public class PeopleActivity extends ContactsActivity implements
case ContactsRequest.ACTION_VIEW_CONTACT:
tabToOpen = TabState.ALL;
break;
+ case ContactsRequest.ACTION_GROUP:
+ tabToOpen = TabState.GROUPS;
+ break;
default:
tabToOpen = -1;
break;
@@ -502,6 +574,7 @@ public class PeopleActivity extends ContactsActivity implements
}
configureContactListFragment();
+ configureGroupListFragment();
invalidateOptionsMenuIfNeeded();
}
@@ -621,6 +694,9 @@ public class PeopleActivity extends ContactsActivity implements
}
invalidateOptionsMenu();
showEmptyStateForTab(tab);
+ if (tab == TabState.GROUPS) {
+ mGroupsFragment.setAddAccountsVisibility(!areGroupWritableAccountsAvailable());
+ }
}
private void showEmptyStateForTab(int tab) {
@@ -630,6 +706,10 @@ public class PeopleActivity extends ContactsActivity implements
mContactsUnavailableFragment.setMessageText(
R.string.listTotalAllContactsZeroStarred, -1);
break;
+ case TabState.GROUPS:
+ mContactsUnavailableFragment.setMessageText(R.string.noGroups,
+ areGroupWritableAccountsAvailable() ? -1 : R.string.noAccounts);
+ break;
case TabState.ALL:
mContactsUnavailableFragment.setMessageText(R.string.noContacts, -1);
break;
@@ -678,6 +758,9 @@ public class PeopleActivity extends ContactsActivity implements
mActionBarAdapter.setCurrentTab(position, false);
mViewPagerTabs.onPageSelected(position);
showEmptyStateForTab(position);
+ if (position == TabState.GROUPS) {
+ mGroupsFragment.setAddAccountsVisibility(!areGroupWritableAccountsAvailable());
+ }
invalidateOptionsMenu();
}
}
@@ -736,6 +819,9 @@ public class PeopleActivity extends ContactsActivity implements
if (object == mAllFragment) {
return getTabPositionForTextDirection(TabState.ALL);
}
+ if (object == mGroupsFragment) {
+ return TabState.GROUPS;
+ }
}
return POSITION_NONE;
}
@@ -759,6 +845,8 @@ public class PeopleActivity extends ContactsActivity implements
return mFavoritesFragment;
} else if (position == TabState.ALL) {
return mAllFragment;
+ } else if (position == TabState.GROUPS) {
+ return mGroupsFragment;
}
}
throw new IllegalArgumentException("position: " + position);
@@ -866,6 +954,11 @@ public class PeopleActivity extends ContactsActivity implements
return TextUtils.getLayoutDirectionFromLocale(locale) == View.LAYOUT_DIRECTION_RTL;
}
+ private void configureGroupListFragment() {
+ mGroupsFragment.setVerticalScrollbarPosition(getScrollBarPosition());
+ mGroupsFragment.setSelectionVisible(false);
+ }
+
@Override
public void onProviderStatusChange() {
updateViewConfiguration(false);
@@ -1040,6 +1133,67 @@ public class PeopleActivity extends ContactsActivity implements
}
}
+ private final class GroupBrowserActionListener implements OnGroupBrowserActionListener {
+
+ GroupBrowserActionListener() {}
+
+ @Override
+ public void onViewGroupAction(Uri groupUri) {
+ Intent intent = new Intent(PeopleActivity.this, GroupDetailActivity.class);
+ intent.setData(groupUri);
+ startActivity(intent);
+ }
+ }
+
+ private class GroupDetailFragmentListener implements GroupDetailFragment.Listener {
+
+ GroupDetailFragmentListener() {}
+
+ @Override
+ public void onGroupSizeUpdated(String size) {
+ // Nothing needs to be done here because the size will be displayed in the detail
+ // fragment
+ }
+
+ @Override
+ public void onGroupTitleUpdated(String title) {
+ // Nothing needs to be done here because the title will be displayed in the detail
+ // fragment
+ }
+
+ @Override
+ public void onAccountTypeUpdated(String accountTypeString, String dataSet) {
+ // Nothing needs to be done here because the group source will be displayed in the
+ // detail fragment
+ }
+
+ @Override
+ public void onEditRequested(Uri groupUri) {
+ final Intent intent = new Intent(PeopleActivity.this, GroupEditorActivity.class);
+ intent.setData(groupUri);
+ intent.setAction(Intent.ACTION_EDIT);
+ startActivityForResult(intent, SUBACTIVITY_EDIT_GROUP);
+ }
+
+ @Override
+ public void onContactSelected(Uri contactUri) {
+ // Nothing needs to be done here because either quickcontact will be displayed
+ // or activity will take care of selection
+ }
+ }
+
+ public void startActivityAndForwardResult(final Intent intent) {
+ intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+
+ // Forward extras to the new activity
+ Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ intent.putExtras(extras);
+ }
+ startActivity(intent);
+ finish();
+ }
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
if (!areContactsAvailable()) {
@@ -1069,6 +1223,10 @@ public class PeopleActivity extends ContactsActivity implements
return true;
}
+ if (mGroupDetailFragment != null && mGroupDetailFragment.isOptionsMenuChanged()) {
+ return true;
+ }
+
return false;
}
@@ -1081,25 +1239,39 @@ public class PeopleActivity extends ContactsActivity implements
// Get references to individual menu items in the menu
final MenuItem contactsFilterMenu = menu.findItem(R.id.menu_contacts_filter);
+ MenuItem addGroupMenu = menu.findItem(R.id.menu_add_group);
final MenuItem clearFrequentsMenu = menu.findItem(R.id.menu_clear_frequents);
final MenuItem helpMenu = menu.findItem(R.id.menu_help);
final boolean isSearchOrSelectionMode = mActionBarAdapter.isSearchMode()
|| mActionBarAdapter.isSelectionMode();
if (isSearchOrSelectionMode) {
+ addGroupMenu.setVisible(false);
contactsFilterMenu.setVisible(false);
clearFrequentsMenu.setVisible(false);
helpMenu.setVisible(false);
+ makeMenuItemVisible(menu, R.id.menu_delete, false);
} else {
switch (getTabPositionForTextDirection(mActionBarAdapter.getCurrentTab())) {
case TabState.FAVORITES:
+ addGroupMenu.setVisible(false);
contactsFilterMenu.setVisible(false);
clearFrequentsMenu.setVisible(hasFrequents());
break;
case TabState.ALL:
+ addGroupMenu.setVisible(false);
contactsFilterMenu.setVisible(true);
clearFrequentsMenu.setVisible(false);
break;
+ case TabState.GROUPS:
+ // Do not display the "new group" button if no accounts are available
+ if (areGroupWritableAccountsAvailable()) {
+ addGroupMenu.setVisible(true);
+ } else {
+ addGroupMenu.setVisible(false);
+ }
+ contactsFilterMenu.setVisible(false);
+ clearFrequentsMenu.setVisible(false);
}
helpMenu.setVisible(HelpUtils.isHelpAndFeedbackAvailable());
}
@@ -1107,13 +1279,13 @@ public class PeopleActivity extends ContactsActivity implements
makeMenuItemVisible(menu, R.id.menu_search, showMiscOptions);
makeMenuItemVisible(menu, R.id.menu_import_export, showMiscOptions);
makeMenuItemVisible(menu, R.id.menu_accounts, showMiscOptions);
+ makeMenuItemVisible(menu, R.id.menu_memory_status, showMiscOptions);
makeMenuItemVisible(menu, R.id.menu_settings,
showMiscOptions && !ContactsPreferenceActivity.isEmpty(this));
final boolean showSelectedContactOptions = mActionBarAdapter.isSelectionMode()
&& mAllFragment.getSelectedContactIds().size() != 0;
makeMenuItemVisible(menu, R.id.menu_share, showSelectedContactOptions);
- makeMenuItemVisible(menu, R.id.menu_delete, showSelectedContactOptions);
makeMenuItemVisible(menu, R.id.menu_join, showSelectedContactOptions);
makeMenuItemEnabled(menu, R.id.menu_join, mAllFragment.getSelectedContactIds().size() > 1);
@@ -1191,9 +1363,21 @@ public class PeopleActivity extends ContactsActivity implements
case R.id.menu_join:
joinSelectedContacts();
return true;
- case R.id.menu_delete:
- deleteSelectedContacts();
+ case R.id.menu_add_group: {
+ createNewGroup();
+ return true;
+ }
+ case R.id.menu_delete: {
+ final Intent intent = new Intent(Intent.ACTION_DELETE, Contacts.CONTENT_URI);
+ intent.putExtra(EDITABLE_KEY, mActionBarAdapter.getQueryString());
+
+ ContactListFilter filter = ContactListFilter.restoreDefaultPreferences(
+ PreferenceManager.getDefaultSharedPreferences(this));
+ intent.putExtra(AccountFilterActivity.KEY_EXTRA_CONTACT_LIST_FILTER, filter);
+
+ startActivity(intent);
return true;
+ }
case R.id.menu_import_export: {
ImportExportDialogFragment.show(getFragmentManager(), areContactsAvailable(),
PeopleActivity.class);
@@ -1221,6 +1405,12 @@ public class PeopleActivity extends ContactsActivity implements
ImplicitIntentsUtil.startActivityOutsideApp(this, intent);
return true;
}
+
+ case R.id.menu_memory_status: {
+ final Intent intent = new Intent(this, MemoryStatusActivity.class);
+ startActivity(intent);
+ return true;
+ }
}
return false;
}
@@ -1271,6 +1461,12 @@ public class PeopleActivity extends ContactsActivity implements
mAllFragment.getSelectedContactIds());
}
+ private void createNewGroup() {
+ final Intent intent = new Intent(this, GroupEditorActivity.class);
+ intent.setAction(Intent.ACTION_INSERT);
+ startActivityForResult(intent, SUBACTIVITY_NEW_GROUP);
+ }
+
@Override
public void onDeletionFinished() {
mActionBarAdapter.setSelectionMode(false);
@@ -1284,6 +1480,14 @@ public class PeopleActivity extends ContactsActivity implements
mContactListFilterController, resultCode, data);
break;
}
+ case SUBACTIVITY_NEW_GROUP:
+ case SUBACTIVITY_EDIT_GROUP: {
+ if (resultCode == RESULT_OK) {
+ mRequest.setActionCode(ContactsRequest.ACTION_GROUP);
+ mGroupsFragment.setSelectedUri(data.getData());
+ }
+ break;
+ }
// TODO: Using the new startActivityWithResultFromFragment API this should not be needed
// anymore
@@ -1291,6 +1495,7 @@ public class PeopleActivity extends ContactsActivity implements
if (resultCode == RESULT_OK) {
mAllFragment.onPickerResult(data);
}
+ break;
// TODO fix or remove multipicker code
// else if (resultCode == RESULT_CANCELED && mMode == MODE_PICK_MULTIPLE_PHONES) {
@@ -1299,6 +1504,91 @@ public class PeopleActivity extends ContactsActivity implements
// finish();
// }
// break;
+ case ImportExportDialogFragment.SUBACTIVITY_MULTI_PICK_CONTACT:
+ if (resultCode == RESULT_OK) {
+ mContactList = new ArrayList<String[]>();
+ Bundle b = data.getExtras();
+ Bundle choiceSet = b.getBundle(SimContactsConstants.RESULT_KEY);
+ Set<String> set = choiceSet.keySet();
+ Iterator<String> i = set.iterator();
+ while (i.hasNext()) {
+ String contactInfo[] = choiceSet.getStringArray(i.next());
+ mContactList.add(contactInfo);
+ }
+ Log.d(TAG, "return " + mContactList.size() + " contacts");
+ if (!mContactList.isEmpty()) {
+ if (!ImportExportDialogFragment.isExportingToSIM()) {
+ ImportExportDialogFragment.destroyExportToSimThread();
+ mExportThread =
+ new ImportExportDialogFragment().createExportToSimThread(
+ ImportExportDialogFragment.mExportSub, mContactList,
+ PeopleActivity.this);
+ mExportThread.start();
+ }
+ }
+ }
+ break;
+ case ImportExportDialogFragment.SUBACTIVITY_EXPORT_CONTACTS:
+ if (resultCode == RESULT_OK) {
+ Bundle result = data.getExtras().getBundle(
+ SimContactsConstants.RESULT_KEY);
+ Set<String> keySet = result.keySet();
+ Iterator<String> it = keySet.iterator();
+ StringBuilder selExportBuilder = new StringBuilder();
+ while (it.hasNext()) {
+ String id = it.next();
+ if (0 != selExportBuilder.length()) {
+ selExportBuilder.append(",");
+ }
+ selExportBuilder.append(id);
+ }
+ selExportBuilder.insert(0, "_id IN (");
+ selExportBuilder.append(")");
+ Intent exportIntent = new Intent(this,
+ ExportVCardActivity.class);
+ exportIntent.putExtra("SelExport", selExportBuilder.toString());
+ exportIntent.putExtra(
+ VCardCommonArguments.ARG_CALLING_ACTIVITY,
+ PeopleActivity.class.getName());
+ this.startActivity(exportIntent);
+ }
+ break;
+ case ImportExportDialogFragment.SUBACTIVITY_SHARE_VISILBLE_CONTACTS:
+ if (resultCode == RESULT_OK) {
+ Bundle result = data.getExtras().getBundle(
+ SimContactsConstants.RESULT_KEY);
+ StringBuilder uriListBuilder = new StringBuilder();
+ int index = 0;
+ int size = result.keySet().size();
+ // The premise of allowing to share contacts is that the
+ // amount of those contacts which have been selected to
+ // append and will be put into intent as extra data to
+ // deliver is not more that 2000, because too long arguments
+ // will cause TransactionTooLargeException in binder.
+ if (size > ImportExportDialogFragment.MAX_COUNT_ALLOW_SHARE_CONTACT) {
+ Toast.makeText(this, R.string.share_failed,
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ Iterator<String> it = result.keySet().iterator();
+ String[] values = null;
+ while (it.hasNext()) {
+ if (index != 0) {
+ uriListBuilder.append(':');
+ }
+ values = result.getStringArray(it.next());
+ uriListBuilder.append(values[0]);
+ index++;
+ }
+ Uri uri = Uri.withAppendedPath(
+ Contacts.CONTENT_MULTI_VCARD_URI,
+ Uri.encode(uriListBuilder.toString()));
+ final Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.setType(Contacts.CONTENT_VCARD_TYPE);
+ intent.putExtra(Intent.EXTRA_STREAM, uri);
+ startActivity(intent);
+ }
+ break;
}
}
diff --git a/src/com/android/contacts/editor/CompactPhotoEditorView.java b/src/com/android/contacts/editor/CompactPhotoEditorView.java
index 5f3e9afad..64df22850 100644
--- a/src/com/android/contacts/editor/CompactPhotoEditorView.java
+++ b/src/com/android/contacts/editor/CompactPhotoEditorView.java
@@ -31,6 +31,7 @@ import com.android.contacts.util.ContactPhotoUtils;
import com.android.contacts.util.SchedulingUtils;
import com.android.contacts.widget.QuickContactImageView;
+import android.accounts.Account;
import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
@@ -291,8 +292,8 @@ public class CompactPhotoEditorView extends RelativeLayout implements View.OnCli
if (photoUri != null) {
final DefaultImageProvider fallbackToPreviousImage = new DefaultImageProvider() {
@Override
- public void applyDefaultImage(ImageView view, int extent, boolean darkTheme,
- DefaultImageRequest defaultImageRequest) {
+ public void applyDefaultImage(ImageView view, Account account, int extent,
+ boolean darkTheme, DefaultImageRequest defaultImageRequest) {
// Before we finish setting the full sized image, don't change the current
// image that is set in any way.
}
diff --git a/src/com/android/contacts/editor/CompactRawContactsEditorView.java b/src/com/android/contacts/editor/CompactRawContactsEditorView.java
index 6b7df6768..e04343c63 100644..100755
--- a/src/com/android/contacts/editor/CompactRawContactsEditorView.java
+++ b/src/com/android/contacts/editor/CompactRawContactsEditorView.java
@@ -17,6 +17,8 @@
package com.android.contacts.editor;
import com.android.contacts.R;
+import com.android.contacts.common.model.account.PhoneAccountType;
+import com.android.contacts.common.model.account.SimAccountType;
import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.RawContactDelta;
import com.android.contacts.common.model.RawContactDeltaList;
@@ -24,14 +26,21 @@ import com.android.contacts.common.model.RawContactModifier;
import com.android.contacts.common.model.ValuesDelta;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.common.model.account.AccountType.EditField;
+import com.android.contacts.common.model.account.AccountType.EditType;
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 com.android.contacts.common.model.dataitem.DataKind;
+import com.android.contacts.common.SimContactsConstants;
+import com.android.contacts.common.MoreContactUtils;
import com.android.contacts.common.util.MaterialColorMapUtils;
import com.android.contacts.editor.CompactContactEditorFragment.PhotoHandler;
+import com.android.internal.telephony.PhoneConstants;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
+import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import android.provider.ContactsContract.CommonDataKinds.Nickname;
@@ -365,7 +374,8 @@ public class CompactRawContactsEditorView extends LinearLayout implements View.O
return;
}
vlog("Account info loaded");
- if (accountInfo.first == null) {
+ if (accountInfo.first == null || SimAccountType.ACCOUNT_TYPE.equals(accountType.
+ accountType)|| PhoneAccountType.ACCOUNT_TYPE.equals(accountType.accountType)) {
mAccountNameView.setVisibility(View.GONE);
} else {
mAccountNameView.setVisibility(View.VISIBLE);
@@ -640,7 +650,7 @@ public class CompactRawContactsEditorView extends LinearLayout implements View.O
for (RawContactDelta rawContactDelta : rawContactDeltas) {
if (!rawContactDelta.isVisible()) continue;
final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
-
+ final String accountName = rawContactDelta.getAccountName();
for (DataKind dataKind : accountType.getSortedDataKinds()) {
if (!dataKind.editable) continue;
@@ -658,7 +668,7 @@ public class CompactRawContactsEditorView extends LinearLayout implements View.O
final ValuesDelta valuesDelta = rawContactDelta.getSuperPrimaryEntry(
StructuredName.CONTENT_ITEM_TYPE, /* forceSelection =*/ true);
if (hasNonEmptyValue(dataKind, valuesDelta)) {
- mPhoneticNames.addView(inflatePhoneticNameEditorView(
+ mPhoneticNames.addView(inflatePhoneticNameEditorView(
mPhoneticNames, accountType, valuesDelta, rawContactDelta));
}
} else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) {
@@ -667,7 +677,7 @@ public class CompactRawContactsEditorView extends LinearLayout implements View.O
rawContactDelta, Nickname.CONTENT_ITEM_TYPE, dataKind);
if (valuesDeltas != null && !valuesDeltas.isEmpty()) {
for (ValuesDelta valuesDelta : valuesDeltas) {
- mNicknames.addView(inflateNicknameEditorView(
+ mNicknames.addView(inflateNicknameEditorView(
mNicknames, dataKind, valuesDelta, rawContactDelta));
}
}
@@ -687,6 +697,31 @@ public class CompactRawContactsEditorView extends LinearLayout implements View.O
updateKindEditorIcons(mPhoneNumbers);
}
});
+ if (SimContactsConstants.ACCOUNT_TYPE_SIM
+ .equals(accountType.accountType)) {
+ int sub = PhoneConstants.SUB1;
+ if (SimContactsConstants.SIM_NAME_2.equals(accountName)) {
+ sub = PhoneConstants.SUB2;
+ }
+ EditType typeHome = new EditType(Phone.TYPE_HOME,
+ Phone.getTypeLabelResource(Phone.TYPE_HOME));
+ if (!MoreContactUtils.canSaveAnr(sub)) {
+ dataKind.typeOverallMax = 1;
+ if (null != dataKind.typeList) {
+ // When the sim card is not 3g the interface should
+ // remove the TYPE_HOME number view.
+ dataKind.typeList.remove(typeHome);
+ }
+ } else {
+ dataKind.typeOverallMax = MoreContactUtils.getOneSimAnrCount(sub) + 1;
+ if (null != dataKind.typeList && !dataKind.typeList.contains(
+ typeHome)) {
+ // When the sim card is 3g the interface should
+ // add the TYPE_HOME number view.
+ dataKind.typeList.add(typeHome);
+ }
+ }
+ }
mPhoneNumbers.addView(kindSectionView);
} else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
final KindSectionView kindSectionView =
@@ -704,7 +739,19 @@ public class CompactRawContactsEditorView extends LinearLayout implements View.O
updateKindEditorIcons(mEmails);
}
});
- mEmails.addView(kindSectionView);
+ if (SimContactsConstants.ACCOUNT_TYPE_SIM.equals(
+ accountType.accountType)) {
+ int sub = PhoneConstants.SUB1;
+ if (SimContactsConstants.SIM_NAME_2.equals(accountName)) {
+ sub = PhoneConstants.SUB2;
+ }
+ if (MoreContactUtils.canSaveEmail(sub)) {
+ dataKind.typeOverallMax = MoreContactUtils.getOneSimEmailCount(sub);
+ mEmails.addView(kindSectionView);
+ }
+ } else {
+ mEmails.addView(kindSectionView);
+ }
} else if (hasNonEmptyValuesDelta(rawContactDelta, mimeType, dataKind)) {
final LinearLayout otherTypeViewGroup;
if (mOtherTypesMap.containsKey(mimeType)) {
@@ -842,6 +889,10 @@ public class CompactRawContactsEditorView extends LinearLayout implements View.O
rawContactDelta,
readOnly,
mViewIdGenerator);
+ if (rawContactDelta.getAccountType() != null && rawContactDelta.getAccountType().equals(
+ SimContactsConstants.ACCOUNT_TYPE_SIM)) {
+ result.setExpansionViewContainerDisabled();
+ }
return result;
}
diff --git a/src/com/android/contacts/editor/ContactEditorBaseFragment.java b/src/com/android/contacts/editor/ContactEditorBaseFragment.java
index 18b138190..646bf4fe2 100644
--- a/src/com/android/contacts/editor/ContactEditorBaseFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorBaseFragment.java
@@ -25,6 +25,7 @@ import com.android.contacts.R;
import com.android.contacts.activities.ContactEditorAccountsChangedActivity;
import com.android.contacts.activities.ContactEditorBaseActivity;
import com.android.contacts.activities.ContactEditorBaseActivity.ContactEditor;
+import com.android.contacts.common.SimContactsConstants;
import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.Contact;
import com.android.contacts.common.model.ContactLoader;
@@ -35,6 +36,7 @@ import com.android.contacts.common.model.RawContactModifier;
import com.android.contacts.common.model.ValuesDelta;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.model.account.SimAccountType;
import com.android.contacts.common.util.ImplicitIntentsUtil;
import com.android.contacts.common.util.MaterialColorMapUtils;
import com.android.contacts.editor.AggregationSuggestionEngine.Suggestion;
@@ -652,6 +654,14 @@ abstract public class ContactEditorBaseFragment extends Fragment implements
}
@Override
+ public void onResume() {
+ super.onResume();
+ if (Intent.ACTION_EDIT.equals(mAction)) {
+ mHasNewContact = false;
+ }
+ }
+
+ @Override
public void onStop() {
super.onStop();
@@ -786,10 +796,18 @@ abstract public class ContactEditorBaseFragment extends Fragment implements
// even if they have never added their own information and splitting will create a
// name only contact.
final boolean isSingleReadOnlyContact = mHasNewContact && mState.size() == 2;
+ String accountType = null;
+ if (mState.size() > 0) {
+ accountType = mState.get(0).getAccountType();
+ }
splitMenu.setVisible(mState.size() > 1 && !isEditingUserProfile()
&& !isSingleReadOnlyContact);
// Cannot join a user profile
- joinMenu.setVisible(!isEditingUserProfile());
+ if (accountType != null && SimAccountType.ACCOUNT_TYPE.equals(accountType)) {
+ joinMenu.setVisible(false);
+ } else {
+ joinMenu.setVisible(!isEditingUserProfile());
+ }
deleteMenu.setVisible(!mDisableDeleteMenuOption);
} else {
// something else, so don't show the help menu
@@ -950,7 +968,9 @@ abstract public class ContactEditorBaseFragment extends Fragment implements
}
onSaveCompleted(/* hadChanges =*/ false, saveMode,
/* saveSucceeded =*/ mLookupUri != null, mLookupUri,
- /* updatedPhotos =*/ null, backPressed, mPhotoId, mNameId);
+ /* updatedPhotos =*/ null, backPressed, mPhotoId, mNameId,
+ getActivity().getIntent().getIntExtra(
+ ContactSaveService.SAVE_CONTACT_RESULT, 0));
return true;
}
@@ -1275,7 +1295,8 @@ abstract public class ContactEditorBaseFragment extends Fragment implements
// For profile contacts, we need a different query URI
rawContactDelta.setProfileQueryUri();
// Try to find a local profile contact
- if (rawContactDelta.getValues().getAsString(RawContacts.ACCOUNT_TYPE) == null) {
+ if (SimContactsConstants.ACCOUNT_TYPE_PHONE
+ .equals(rawContactDelta.getAccountType())) {
localProfileExists = true;
}
}
@@ -1401,20 +1422,81 @@ abstract public class ContactEditorBaseFragment extends Fragment implements
@Override
public void onJoinCompleted(Uri uri) {
onSaveCompleted(false, SaveMode.RELOAD, uri != null, uri, /* updatedPhotos =*/ null,
- /* backPressed =*/ false, mPhotoId, mNameId);
+ /* backPressed =*/ false, mPhotoId, mNameId,
+ getActivity().getIntent().getIntExtra(ContactSaveService.SAVE_CONTACT_RESULT,
+ 0));
}
@Override
public void onSaveCompleted(boolean hadChanges, int saveMode, boolean saveSucceeded,
Uri contactLookupUri, Bundle updatedPhotos, boolean backPressed, long photoId,
- long nameId) {
+ long nameId, int result) {
+ Log.d(TAG, "onSaveCompleted(" + saveMode + ", " + contactLookupUri + ", saveResult:"
+ + result);
if (hadChanges) {
if (saveSucceeded) {
if (saveMode != SaveMode.JOIN) {
- Toast.makeText(mContext, R.string.contactSavedToast, Toast.LENGTH_SHORT).show();
+ if (null != contactLookupUri) {
+ Toast.makeText(mContext, R.string.contactSavedToast,
+ Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(mContext, R.string.contacts_deleted_toast,
+ Toast.LENGTH_SHORT).show();
+ }
}
} else {
- Toast.makeText(mContext, R.string.contactSavedErrorToast, Toast.LENGTH_LONG).show();
+ if (result == ContactSaveService.RESULT_AIR_PLANE_MODE) {
+ // Access SIM card in the "AirPlane"
+ // mode prompt a toast to alert user.
+ Toast.makeText(mContext, R.string.airplane_mode_on, Toast.LENGTH_LONG).show();
+ } else if (result == ContactSaveService.RESULT_SIM_FAILURE) {
+ Toast.makeText(mContext, R.string.contactSavedToSimCardError,
+ Toast.LENGTH_LONG).show();
+ } else if (result == ContactSaveService.RESULT_NUMBER_ANR_FAILURE) {
+ Toast.makeText(mContext, R.string.number_anr_too_long, Toast.LENGTH_LONG)
+ .show();
+ mStatus = Status.EDITING;
+ setEnabled(true);
+ bindEditors();
+ return;
+ } else if (result == ContactSaveService.RESULT_EMAIL_FAILURE) {
+ Toast.makeText(mContext, R.string.email_address_too_long, Toast.LENGTH_LONG)
+ .show();
+ mStatus = Status.EDITING;
+ setEnabled(true);
+ bindEditors();
+ return;
+ } else if (result == ContactSaveService.RESULT_SIM_FULL_FAILURE) {
+ Toast.makeText(mContext, R.string.sim_card_full, Toast.LENGTH_LONG).show();
+ } else if (result == ContactSaveService.RESULT_TAG_FAILURE) {
+ Toast.makeText(mContext, R.string.tag_too_long, Toast.LENGTH_SHORT).show();
+ mStatus = Status.EDITING;
+ setEnabled(true);
+ bindEditors();
+ return;
+ } else if (result == ContactSaveService.RESULT_NO_NUMBER_AND_EMAIL) {
+ Toast.makeText(mContext, R.string.no_phone_number_or_email, Toast.LENGTH_SHORT)
+ .show();
+ mStatus = Status.EDITING;
+ setEnabled(true);
+ bindEditors();
+ return;
+ } else if (result == ContactSaveService.RESULT_NUMBER_INVALID) {
+ Toast.makeText(mContext, R.string.invalid_phone_number, Toast.LENGTH_SHORT)
+ .show();
+ mStatus = Status.EDITING;
+ setEnabled(true);
+ return;
+ } else if (result == ContactSaveService.RESULT_MEMORY_FULL_FAILURE) {
+ Toast.makeText(mContext, R.string.memory_card_full, Toast.LENGTH_SHORT)
+ .show();
+ } else if(result == ContactSaveService.RESULT_NUMBER_TYPE_FAILURE) {
+ Toast.makeText(mContext, R.string.invalid_number_type, Toast.LENGTH_SHORT)
+ .show();
+ } else {
+ Toast.makeText(mContext, R.string.contactSavedErrorToast, Toast.LENGTH_LONG)
+ .show();
+ }
}
}
switch (saveMode) {
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index 4da17faf2..31e8d5155 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -380,7 +380,7 @@ public class ContactEditorFragment extends ContactEditorBaseFragment implements
final RawContactEditorView rawContactEditorView = (RawContactEditorView) view;
final PhoneticNameEditorView phoneticNameEditorView =
(PhoneticNameEditorView) rawContactEditorView.getPhoneticNameEditor();
- if (phoneticNameEditorView != null) {
+ if (phoneticNameEditorView != null && phoneticNameEditorView.getEntry() != null) {
final String phoneticName = phoneticNameEditorView.getPhoneticName();
if (!TextUtils.isEmpty(phoneticName)) {
return phoneticName;
diff --git a/src/com/android/contacts/editor/ContactEditorUtils.java b/src/com/android/contacts/editor/ContactEditorUtils.java
index 105b88552..2f00a2894 100644
--- a/src/com/android/contacts/editor/ContactEditorUtils.java
+++ b/src/com/android/contacts/editor/ContactEditorUtils.java
@@ -30,6 +30,7 @@ import com.android.contacts.common.testing.NeededForTesting;
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.SimContactsConstants;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
@@ -242,7 +243,12 @@ public class ContactEditorUtils {
String[] getWritableAccountTypeStrings() {
final Set<String> types = Sets.newHashSet();
for (AccountType type : mAccountTypes.getAccountTypes(true)) {
- types.add(type.accountType);
+ if (type.accountType.equals(SimContactsConstants.ACCOUNT_TYPE_SIM)
+ || type.accountType.equals(SimContactsConstants.ACCOUNT_TYPE_PHONE)) {
+ continue;
+ } else {
+ types.add(type.accountType);
+ }
}
return types.toArray(new String[types.size()]);
}
diff --git a/src/com/android/contacts/editor/EditorUiUtils.java b/src/com/android/contacts/editor/EditorUiUtils.java
index 78f7a42d2..78f7a42d2 100644..100755
--- a/src/com/android/contacts/editor/EditorUiUtils.java
+++ b/src/com/android/contacts/editor/EditorUiUtils.java
diff --git a/src/com/android/contacts/editor/PhotoEditorView.java b/src/com/android/contacts/editor/PhotoEditorView.java
index f69c93514..99647312b 100644
--- a/src/com/android/contacts/editor/PhotoEditorView.java
+++ b/src/com/android/contacts/editor/PhotoEditorView.java
@@ -16,6 +16,7 @@
package com.android.contacts.editor;
+import android.accounts.Account;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -208,8 +209,8 @@ public class PhotoEditorView extends LinearLayout implements Editor {
if (photoUri != null) {
final DefaultImageProvider fallbackToPreviousImage = new DefaultImageProvider() {
@Override
- public void applyDefaultImage(ImageView view, int extent, boolean darkTheme,
- DefaultImageRequest defaultImageRequest) {
+ public void applyDefaultImage(ImageView view, Account account, int extent,
+ boolean darkTheme, DefaultImageRequest defaultImageRequest) {
// Before we finish setting the full sized image, don't change the current
// image that is set in any way.
}
diff --git a/src/com/android/contacts/editor/RawContactEditorView.java b/src/com/android/contacts/editor/RawContactEditorView.java
index eeba401de..2741f2a68 100644..100755
--- a/src/com/android/contacts/editor/RawContactEditorView.java
+++ b/src/com/android/contacts/editor/RawContactEditorView.java
@@ -37,11 +37,14 @@ import android.widget.TextView;
import com.android.contacts.GroupMetaDataLoader;
import com.android.contacts.R;
import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.model.account.PhoneAccountType;
+import com.android.contacts.common.model.account.SimAccountType;
import com.android.contacts.common.model.account.AccountType.EditType;
import com.android.contacts.common.model.dataitem.DataKind;
import com.android.contacts.common.model.RawContactDelta;
import com.android.contacts.common.model.ValuesDelta;
import com.android.contacts.common.model.RawContactModifier;
+import com.android.contacts.common.SimContactsConstants;
import com.google.common.base.Objects;
@@ -196,7 +199,8 @@ public class RawContactEditorView extends BaseRawContactEditorView {
// Hide this view so the other text view will be centered vertically
mAccountHeaderNameTextView.setVisibility(View.GONE);
} else {
- if (accountInfo.first == null) {
+ if (accountInfo.first == null || SimAccountType.ACCOUNT_TYPE.equals(type.accountType)
+ || PhoneAccountType.ACCOUNT_TYPE.equals(type.accountType)) {
mAccountHeaderNameTextView.setVisibility(View.GONE);
} else {
mAccountHeaderNameTextView.setVisibility(View.VISIBLE);
@@ -249,23 +253,34 @@ public class RawContactEditorView extends BaseRawContactEditorView {
mName.setValues(
type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME),
primary, state, false, vig);
- mPhoneticName.setValues(
- type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME),
- primary, state, false, vig);
- // It is useful to use Nickname outside of a KindSectionView so that we can treat it
- // as a part of StructuredName's fake KindSectionView, even though it uses a
- // different CP2 mime-type. We do a bit of extra work below to make this possible.
- final DataKind nickNameKind = type.getKindForMimetype(Nickname.CONTENT_ITEM_TYPE);
- if (nickNameKind != null) {
- ValuesDelta primaryNickNameEntry = state.getPrimaryEntry(nickNameKind.mimeType);
- if (primaryNickNameEntry == null) {
- primaryNickNameEntry = RawContactModifier.insertChild(state, nickNameKind);
+ if (!(SimContactsConstants.ACCOUNT_TYPE_SIM).equals(type.accountType)) {
+ mPhoneticName.setValues(
+ type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME),
+ primary, state, false, vig);
+ // It is useful to use Nickname outside of a KindSectionView so that we can
+ // treat it as a part of StructuredName's fake KindSectionView, even though
+ // it uses adifferent CP2 mime-type. We do a bit of extra work below to make
+ // this possible.
+ final DataKind nickNameKind = type
+ .getKindForMimetype(Nickname.CONTENT_ITEM_TYPE);
+ if (nickNameKind != null) {
+ ValuesDelta primaryNickNameEntry = state
+ .getPrimaryEntry(nickNameKind.mimeType);
+ if (primaryNickNameEntry == null) {
+ primaryNickNameEntry = RawContactModifier
+ .insertChild(state, nickNameKind);
+ }
+ mNickName.setValues(nickNameKind, primaryNickNameEntry, state, false, vig);
+ mNickName.setDeletable(false);
+ } else {
+ mPhoneticName.setPadding(0, 0, 0, (int) getResources().getDimension(
+ R.dimen.editor_padding_between_editor_views));
+ mNickName.setVisibility(View.GONE);
}
- mNickName.setValues(nickNameKind, primaryNickNameEntry, state, false, vig);
- mNickName.setDeletable(false);
} else {
- mPhoneticName.setPadding(0, 0, 0, (int) getResources().getDimension(
- R.dimen.editor_padding_between_editor_views));
+ //sim card can't store expand fields,so set it disabled.
+ mName.setExpansionViewContainerDisabled();
+ mPhoneticName.setVisibility(View.GONE);
mNickName.setVisibility(View.GONE);
}
} else if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
diff --git a/src/com/android/contacts/editor/TextFieldsEditorView.java b/src/com/android/contacts/editor/TextFieldsEditorView.java
index fe476ed01..88a8e30ee 100644
--- a/src/com/android/contacts/editor/TextFieldsEditorView.java
+++ b/src/com/android/contacts/editor/TextFieldsEditorView.java
@@ -377,10 +377,12 @@ public class TextFieldsEditorView extends LabeledEditorView {
mHideOptional = ss.mHideOptional;
- int numChildren = Math.min(mFieldEditTexts == null ? 0 : mFieldEditTexts.length,
- ss.mVisibilities == null ? 0 : ss.mVisibilities.length);
- for (int i = 0; i < numChildren; i++) {
- mFieldEditTexts[i].setVisibility(ss.mVisibilities[i]);
+ if (mFieldEditTexts != null) {
+ int numChildren = Math.min(mFieldEditTexts == null ? 0 : mFieldEditTexts.length,
+ ss.mVisibilities == null ? 0 : ss.mVisibilities.length);
+ for (int i = 0; i < numChildren; i++) {
+ mFieldEditTexts[i].setVisibility(ss.mVisibilities[i]);
+ }
}
}
@@ -429,4 +431,13 @@ public class TextFieldsEditorView extends LabeledEditorView {
}
}
}
+ /**
+ * use for account type is ACCOUNT_TYPE_SIM only because that sim card
+ * can not store expand fields.
+ */
+
+ public void setExpansionViewContainerDisabled() {
+ mExpansionViewContainer.setEnabled(false);
+ mExpansionView.setVisibility(View.INVISIBLE);
+ }
}
diff --git a/src/com/android/contacts/group/GroupBrowseListAdapter.java b/src/com/android/contacts/group/GroupBrowseListAdapter.java
index 48751e72d..40a3b6fba 100644
--- a/src/com/android/contacts/group/GroupBrowseListAdapter.java
+++ b/src/com/android/contacts/group/GroupBrowseListAdapter.java
@@ -30,6 +30,7 @@ import android.widget.TextView;
import com.android.contacts.GroupListLoader;
import com.android.contacts.R;
import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.model.account.PhoneAccountType;
import com.android.contacts.common.model.AccountTypeManager;
import com.google.common.base.Objects;
@@ -198,7 +199,12 @@ public class GroupBrowseListAdapter extends BaseAdapter {
AccountType accountType = mAccountTypeManager.getAccountType(
entry.getAccountType(), entry.getDataSet());
viewCache.accountType.setText(accountType.getDisplayLabel(mContext));
- viewCache.accountName.setText(entry.getAccountName());
+ // According to the UI SPEC, we will not show the account name for Phone account
+ if (!PhoneAccountType.ACCOUNT_TYPE.equals(entry.getAccountType())) {
+ viewCache.accountName.setText(entry.getAccountName());
+ } else {
+ viewCache.accountName.setText("");
+ }
}
private static Uri getGroupUriFromId(long groupId) {
diff --git a/src/com/android/contacts/group/GroupBrowseListFragment.java b/src/com/android/contacts/group/GroupBrowseListFragment.java
index d39501a16..46dc92d79 100644
--- a/src/com/android/contacts/group/GroupBrowseListFragment.java
+++ b/src/com/android/contacts/group/GroupBrowseListFragment.java
@@ -160,17 +160,6 @@ public class GroupBrowseListFragment extends Fragment
private void configureVerticalScrollbar() {
mListView.setVerticalScrollbarPosition(mVerticalScrollbarPosition);
mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
- int leftPadding = 0;
- int rightPadding = 0;
- if (mVerticalScrollbarPosition == View.SCROLLBAR_POSITION_LEFT) {
- leftPadding = mContext.getResources().getDimensionPixelOffset(
- R.dimen.list_visible_scrollbar_padding);
- } else {
- rightPadding = mContext.getResources().getDimensionPixelOffset(
- R.dimen.list_visible_scrollbar_padding);
- }
- mListView.setPadding(leftPadding, mListView.getPaddingTop(),
- rightPadding, mListView.getPaddingBottom());
}
@Override
diff --git a/src/com/android/contacts/group/GroupDetailFragment.java b/src/com/android/contacts/group/GroupDetailFragment.java
index c9cf6bd58..724f37bf8 100644
--- a/src/com/android/contacts/group/GroupDetailFragment.java
+++ b/src/com/android/contacts/group/GroupDetailFragment.java
@@ -31,6 +31,7 @@ import android.database.Cursor;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
+import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Groups;
import android.text.TextUtils;
import android.util.Log;
@@ -58,6 +59,8 @@ import com.android.contacts.common.list.ContactTileView;
import com.android.contacts.list.GroupMemberTileAdapter;
import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.activities.MultiPickContactActivity;
+import com.android.contacts.common.SimContactsConstants;
/**
* Displays the details of a group and shows a list of actions possible for the group.
@@ -115,6 +118,7 @@ public class GroupDetailFragment extends Fragment implements OnScrollListener {
private Uri mGroupUri;
private long mGroupId;
private String mGroupName;
+ private String mAccountNameString;
private String mAccountTypeString;
private String mDataSet;
private boolean mIsReadOnly;
@@ -286,6 +290,7 @@ public class GroupDetailFragment extends Fragment implements OnScrollListener {
Log.e(TAG, "Failed to load group members");
return;
}
+ getActivity().invalidateOptionsMenu();
updateSize(data.getCount());
mAdapter.setContactCursor(data);
mMemberListView.setEmptyView(mEmptyView);
@@ -298,6 +303,7 @@ public class GroupDetailFragment extends Fragment implements OnScrollListener {
private void bindGroupMetaData(Cursor cursor) {
cursor.moveToPosition(-1);
if (cursor.moveToNext()) {
+ mAccountNameString = cursor.getString(GroupMetaDataLoader.ACCOUNT_NAME);
mAccountTypeString = cursor.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
mDataSet = cursor.getString(GroupMetaDataLoader.DATA_SET);
mGroupId = cursor.getLong(GroupMetaDataLoader.GROUP_ID);
@@ -454,6 +460,9 @@ public class GroupDetailFragment extends Fragment implements OnScrollListener {
final MenuItem deleteMenu = menu.findItem(R.id.menu_delete_group);
deleteMenu.setVisible(mOptionsMenuGroupDeletable);
+
+ final MenuItem moveMenu = menu.findItem(R.id.menu_move_group_members);
+ moveMenu.setVisible(isVisible() && mAdapter != null && mAdapter.getCount() > 0);
}
@Override
@@ -468,6 +477,18 @@ public class GroupDetailFragment extends Fragment implements OnScrollListener {
mCloseActivityAfterDelete);
return true;
}
+ case R.id.menu_move_group_members: {
+ Intent intent = new Intent(SimContactsConstants.ACTION_MULTI_PICK);
+ intent.setType(Contacts.CONTENT_TYPE);
+ intent.putExtra(SimContactsConstants.IS_CONTACT, true);
+ intent.putExtra(MultiPickContactActivity.EXTRA_GROUP_ID, getGroupId());
+ intent.putExtra(SimContactsConstants.ACCOUNT_TYPE, mAccountTypeString);
+ intent.putExtra(SimContactsConstants.ACCOUNT_NAME, mAccountNameString);
+ intent.putExtra(MultiPickContactActivity.EXTRA_GROUP_ACTION,
+ MultiPickContactActivity.GROUP_ACTION_MOVE_MEMBER);
+ startActivity(intent);
+ return true;
+ }
}
return false;
}
diff --git a/src/com/android/contacts/group/GroupEditorFragment.java b/src/com/android/contacts/group/GroupEditorFragment.java
index eda5d4f39..ff2ee7905 100644
--- a/src/com/android/contacts/group/GroupEditorFragment.java
+++ b/src/com/android/contacts/group/GroupEditorFragment.java
@@ -63,20 +63,25 @@ import com.android.contacts.GroupMemberLoader.GroupEditorQuery;
import com.android.contacts.GroupMetaDataLoader;
import com.android.contacts.R;
import com.android.contacts.activities.GroupEditorActivity;
+import com.android.contacts.activities.MultiPickContactActivity;
import com.android.contacts.common.ContactPhotoManager;
import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
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.editor.SelectAccountDialogFragment;
import com.android.contacts.group.SuggestedMemberListAdapter.SuggestedMember;
import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.util.AccountsListAdapter.AccountListFilter;
import com.android.contacts.common.util.ViewUtil;
+import com.android.contacts.common.SimContactsConstants;
import com.google.common.base.Objects;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
+import java.util.Set;
public class GroupEditorFragment extends Fragment implements SelectAccountDialogFragment.Listener {
private static final String TAG = "GroupEditorFragment";
@@ -98,6 +103,8 @@ public class GroupEditorFragment extends Fragment implements SelectAccountDialog
private static final String CURRENT_EDITOR_TAG = "currentEditorForAccount";
+ public static final int REQUEST_CODE_PICK_GROUP_MEM = 1001;
+
public static interface Listener {
/**
* Group metadata was not found, close the fragment now.
@@ -183,6 +190,7 @@ public class GroupEditorFragment extends Fragment implements SelectAccountDialog
private TextView mGroupNameView;
private AutoCompleteTextView mAutoCompleteTextView;
+ private ImageView mAddGroupMemberView;
private String mAccountName;
private String mAccountType;
@@ -311,8 +319,9 @@ public class GroupEditorFragment extends Fragment implements SelectAccountDialog
}
private void selectAccountAndCreateGroup() {
- final List<AccountWithDataSet> accounts =
- AccountTypeManager.getInstance(mContext).getAccounts(true /* writeable */);
+ final List<AccountWithDataSet> accounts = AccountTypeManager
+ .getInstance(mContext).getAccounts(true /* writeable */,
+ AccountTypeManager.FLAG_ALL_ACCOUNTS_WITHOUT_SIM);
// No Accounts available
if (accounts.isEmpty()) {
Log.e(TAG, "No accounts were found.");
@@ -404,6 +413,7 @@ public class GroupEditorFragment extends Fragment implements SelectAccountDialog
mGroupNameView = (TextView) editorView.findViewById(R.id.group_name);
mAutoCompleteTextView = (AutoCompleteTextView) editorView.findViewById(
R.id.add_member_field);
+ mAddGroupMemberView = (ImageView) editorView.findViewById(R.id.addGroupMember);
mListView = (ListView) editorView.findViewById(android.R.id.list);
mListView.setAdapter(mMemberListAdapter);
@@ -414,9 +424,13 @@ public class GroupEditorFragment extends Fragment implements SelectAccountDialog
ImageView accountIcon = (ImageView) editorView.findViewById(R.id.account_icon);
TextView accountTypeTextView = (TextView) editorView.findViewById(R.id.account_type);
TextView accountNameTextView = (TextView) editorView.findViewById(R.id.account_name);
- if (!TextUtils.isEmpty(mAccountName)) {
+ if (!TextUtils.isEmpty(mAccountName)
+ && !SimContactsConstants.PHONE_NAME.equals(mAccountName)) {
accountNameTextView.setText(
mContext.getString(R.string.from_account_format, mAccountName));
+ accountNameTextView.setVisibility(View.VISIBLE);
+ } else {
+ accountNameTextView.setVisibility(View.GONE);
}
accountTypeTextView.setText(accountTypeDisplayLabel);
accountIcon.setImageDrawable(accountType.getDisplayIcon(mContext));
@@ -428,6 +442,7 @@ public class GroupEditorFragment extends Fragment implements SelectAccountDialog
if (mAutoCompleteTextView != null) {
mAutoCompleteAdapter = new SuggestedMemberListAdapter(mContext,
android.R.layout.simple_dropdown_item_1line);
+ mAutoCompleteTextView.setThreshold(1);
mAutoCompleteAdapter.setContentResolver(mContentResolver);
mAutoCompleteAdapter.setAccountType(mAccountType);
mAutoCompleteAdapter.setAccountName(mAccountName);
@@ -455,6 +470,23 @@ public class GroupEditorFragment extends Fragment implements SelectAccountDialog
mAutoCompleteAdapter.updateExistingMembersList(mListToDisplay);
}
+ if (mAddGroupMemberView != null) {
+ mAddGroupMemberView.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(SimContactsConstants.ACTION_MULTI_PICK);
+ intent.setType(Contacts.CONTENT_TYPE);
+ intent.putExtra(SimContactsConstants.IS_CONTACT, true);
+ intent.putExtra(SimContactsConstants.ACCOUNT_NAME, mAccountName);
+ intent.putExtra(SimContactsConstants.ACCOUNT_TYPE, mAccountType);
+ intent.putExtra(MultiPickContactActivity.EXTRA_GROUP_ACTION,
+ MultiPickContactActivity.GROUP_ACTION_ADD_MEMBER);
+ intent.putExtra(MultiPickContactActivity.EXTRA_GROUP_ID, mGroupId);
+ startActivityForResult(intent, REQUEST_CODE_PICK_GROUP_MEM);
+ }
+ });
+ }
+
// If the group name is ready only, don't let the user focus on the field.
mGroupNameView.setFocusable(!mGroupNameIsReadOnly);
if(isNewEditor) {
@@ -463,6 +495,47 @@ public class GroupEditorFragment extends Fragment implements SelectAccountDialog
mStatus = Status.EDITING;
}
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCode != Activity.RESULT_OK) {
+ return;
+ }
+ if (requestCode == REQUEST_CODE_PICK_GROUP_MEM) {
+ Bundle mChoiceSet = data.getExtras();
+ Set<String> keys = mChoiceSet.keySet();
+ Iterator<String> iterator = keys.iterator();
+ String key;
+ String[] info;
+ String contactId;
+ String nameRawContactId;
+ String displayName;
+ String lookupKey;
+ String photoUri;
+
+ while (iterator.hasNext()) {
+ key = iterator.next();
+ info = mChoiceSet.getStringArray(key);
+
+ contactId = info[1];
+
+ if (!mAutoCompleteAdapter.containsMember(Long.valueOf(contactId))) {
+ // Retrieve the contact data fields that will be sufficient
+ // to update
+ // the adapter with a new entry for this contact
+ lookupKey = info[0];
+ nameRawContactId = info[2];
+ photoUri= info[3];
+ displayName = info[4];
+
+ Member member = new Member(Long.valueOf(nameRawContactId), lookupKey,
+ Long.valueOf(contactId), displayName, photoUri);
+ addMember(member);
+ }
+ }
+ }
+ }
+
public void load(String action, Uri groupUri, Bundle intentExtras) {
mAction = action;
mGroupUri = groupUri;
@@ -713,8 +786,13 @@ public class GroupEditorFragment extends Fragment implements SelectAccountDialog
}
private void addMember(Member member) {
- // Update the display list
- mListMembersToAdd.add(member);
+ // If the contact was just removed during this session, remove it from
+ // the list of members to remove
+ if (mListMembersToRemove.contains(member)) {
+ mListMembersToRemove.remove(member);
+ } else {
+ mListMembersToAdd.add(member);
+ }
mListToDisplay.add(member);
mMemberListAdapter.notifyDataSetChanged();
diff --git a/src/com/android/contacts/group/SuggestedMemberListAdapter.java b/src/com/android/contacts/group/SuggestedMemberListAdapter.java
index 19ff61177..18ad34c7c 100644
--- a/src/com/android/contacts/group/SuggestedMemberListAdapter.java
+++ b/src/com/android/contacts/group/SuggestedMemberListAdapter.java
@@ -116,7 +116,13 @@ public class SuggestedMemberListAdapter extends ArrayAdapter<SuggestedMember> {
}
public void addNewMember(long contactId) {
- mExistingMemberContactIds.add(contactId);
+ if (!containsMember(contactId)) {
+ mExistingMemberContactIds.add(contactId);
+ }
+ }
+
+ public boolean containsMember(long contactId) {
+ return mExistingMemberContactIds.contains(contactId);
}
public void removeMember(long contactId) {
@@ -186,7 +192,7 @@ public class SuggestedMemberListAdapter extends ArrayAdapter<SuggestedMember> {
// and have the same account name and type as specified in this adapter
String searchQuery = prefix.toString() + "%";
String accountClause = RawContacts.ACCOUNT_NAME + "=? AND " +
- RawContacts.ACCOUNT_TYPE + "=?";
+ RawContacts.ACCOUNT_TYPE + "=? AND " + RawContacts.DELETED + "!= 1";
String[] args;
if (mDataSet == null) {
accountClause += " AND " + RawContacts.DATA_SET + " IS NULL";
diff --git a/src/com/android/contacts/list/ContactPickerFragment.java b/src/com/android/contacts/list/ContactPickerFragment.java
index 4e8138916..fa1d2679c 100644
--- a/src/com/android/contacts/list/ContactPickerFragment.java
+++ b/src/com/android/contacts/list/ContactPickerFragment.java
@@ -165,7 +165,7 @@ public class ContactPickerFragment extends ContactEntryListFragment<ContactEntry
HeaderEntryContactListAdapter adapter
= new HeaderEntryContactListAdapter(getActivity());
adapter.setFilter(ContactListFilter.createFilterWithType(
- ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS));
+ ContactListFilter.FILTER_TYPE_ALL_WITHOUT_SIM));
adapter.setSectionHeaderDisplayEnabled(true);
adapter.setDisplayPhotos(true);
adapter.setQuickContactEnabled(false);
diff --git a/src/com/android/contacts/list/EmailAddressListAdapter.java b/src/com/android/contacts/list/EmailAddressListAdapter.java
index d19f960b8..b7aeaac4e 100644
--- a/src/com/android/contacts/list/EmailAddressListAdapter.java
+++ b/src/com/android/contacts/list/EmailAddressListAdapter.java
@@ -15,6 +15,7 @@
*/
package com.android.contacts.list;
+import android.accounts.Account;
import android.content.ContentUris;
import android.content.Context;
import android.content.CursorLoader;
@@ -24,6 +25,7 @@ import android.net.Uri.Builder;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
@@ -47,6 +49,8 @@ public class EmailAddressListAdapter extends ContactEntryListAdapter {
Email.PHOTO_ID, // 4
Email.LOOKUP_KEY, // 5
Email.DISPLAY_NAME_PRIMARY, // 6
+ RawContacts.ACCOUNT_TYPE, // 7
+ RawContacts.ACCOUNT_NAME // 8
};
private static final String[] PROJECTION_ALTERNATIVE = new String[] {
@@ -57,6 +61,8 @@ public class EmailAddressListAdapter extends ContactEntryListAdapter {
Email.PHOTO_ID, // 4
Email.LOOKUP_KEY, // 5
Email.DISPLAY_NAME_ALTERNATIVE, // 6
+ RawContacts.ACCOUNT_TYPE, // 7
+ RawContacts.ACCOUNT_NAME // 8
};
public static final int EMAIL_ID = 0;
@@ -66,6 +72,8 @@ public class EmailAddressListAdapter extends ContactEntryListAdapter {
public static final int EMAIL_PHOTO_ID = 4;
public static final int EMAIL_LOOKUP_KEY = 5;
public static final int EMAIL_DISPLAY_NAME = 6;
+ public static final int EMAIL_ACCOUNT_TYPE = 7;
+ public static final int EMAIL_ACCOUNT_NAME = 8;
}
private final CharSequence mUnknownNameText;
@@ -173,13 +181,22 @@ public class EmailAddressListAdapter extends ContactEntryListAdapter {
if (!cursor.isNull(EmailQuery.EMAIL_PHOTO_ID)) {
photoId = cursor.getLong(EmailQuery.EMAIL_PHOTO_ID);
}
+
+ Account account = null;
+ if (!cursor.isNull(EmailQuery.EMAIL_ACCOUNT_TYPE)
+ && !cursor.isNull(EmailQuery.EMAIL_ACCOUNT_NAME)) {
+ final String accountType = cursor.getString(EmailQuery.EMAIL_ACCOUNT_TYPE);
+ final String accountName = cursor.getString(EmailQuery.EMAIL_ACCOUNT_NAME);
+ account = new Account(accountName, accountType);
+ }
+
DefaultImageRequest request = null;
if (photoId == 0) {
request = getDefaultImageRequestFromCursor(cursor, EmailQuery.EMAIL_DISPLAY_NAME,
EmailQuery.EMAIL_LOOKUP_KEY);
}
- getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false, getCircularPhotos(),
- request);
+ getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, account,
+ false, getCircularPhotos(), request);
}
//
// protected void bindSearchSnippet(final ContactListItemView view, Cursor cursor) {
diff --git a/src/com/android/contacts/list/JoinContactListAdapter.java b/src/com/android/contacts/list/JoinContactListAdapter.java
index f08fcbbe3..553993702 100644
--- a/src/com/android/contacts/list/JoinContactListAdapter.java
+++ b/src/com/android/contacts/list/JoinContactListAdapter.java
@@ -24,6 +24,7 @@ import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Contacts.AggregationSuggestions;
import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.RawContacts;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
@@ -34,6 +35,7 @@ import com.android.contacts.R;
import com.android.contacts.common.list.ContactListAdapter;
import com.android.contacts.common.list.ContactListItemView;
import com.android.contacts.common.list.DirectoryListLoader;
+import com.android.contacts.common.SimContactsConstants;
import com.android.contacts.common.preference.ContactsPreferences;
public class JoinContactListAdapter extends ContactListAdapter {
@@ -81,7 +83,10 @@ public class JoinContactListAdapter extends ContactListAdapter {
}
builder.appendQueryParameter("limit", String.valueOf(MAX_SUGGESTIONS));
-
+ builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE,
+ SimContactsConstants.ACCOUNT_TYPE_SIM);
+ builder.appendQueryParameter(SimContactsConstants.WITHOUT_SIM_FLAG,
+ "true");
loader.setSuggestionUri(builder.build());
// TODO simplify projection
@@ -92,11 +97,19 @@ public class JoinContactListAdapter extends ContactListAdapter {
.appendEncodedPath(Uri.encode(filter))
.appendQueryParameter(
ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT))
+ .appendQueryParameter(RawContacts.ACCOUNT_TYPE,
+ SimContactsConstants.ACCOUNT_TYPE_SIM)
+ .appendQueryParameter(
+ SimContactsConstants.WITHOUT_SIM_FLAG, "true")
.build();
} else {
allContactsUri = buildSectionIndexerUri(Contacts.CONTENT_URI).buildUpon()
.appendQueryParameter(
ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT))
+ .appendQueryParameter(RawContacts.ACCOUNT_TYPE,
+ SimContactsConstants.ACCOUNT_TYPE_SIM)
+ .appendQueryParameter(
+ SimContactsConstants.WITHOUT_SIM_FLAG, "true")
.build();
}
loader.setUri(allContactsUri);
diff --git a/src/com/android/contacts/list/PostalAddressListAdapter.java b/src/com/android/contacts/list/PostalAddressListAdapter.java
index 951a933fa..2bff7282a 100644
--- a/src/com/android/contacts/list/PostalAddressListAdapter.java
+++ b/src/com/android/contacts/list/PostalAddressListAdapter.java
@@ -15,6 +15,7 @@
*/
package com.android.contacts.list;
+import android.accounts.Account;
import android.content.ContentUris;
import android.content.Context;
import android.content.CursorLoader;
@@ -24,6 +25,7 @@ import android.net.Uri.Builder;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
import android.view.View;
import android.view.ViewGroup;
@@ -46,6 +48,8 @@ public class PostalAddressListAdapter extends ContactEntryListAdapter {
StructuredPostal.PHOTO_ID, // 4
StructuredPostal.LOOKUP_KEY, // 5
StructuredPostal.DISPLAY_NAME_PRIMARY, // 6
+ RawContacts.ACCOUNT_TYPE, // 7
+ RawContacts.ACCOUNT_NAME // 8
};
private static final String[] PROJECTION_ALTERNATIVE = new String[] {
@@ -56,6 +60,8 @@ public class PostalAddressListAdapter extends ContactEntryListAdapter {
StructuredPostal.PHOTO_ID, // 4
StructuredPostal.LOOKUP_KEY, // 5
StructuredPostal.DISPLAY_NAME_ALTERNATIVE, // 6
+ RawContacts.ACCOUNT_TYPE, // 7
+ RawContacts.ACCOUNT_NAME, // 8
};
public static final int POSTAL_ID = 0;
@@ -65,6 +71,8 @@ public class PostalAddressListAdapter extends ContactEntryListAdapter {
public static final int POSTAL_PHOTO_ID = 4;
public static final int POSTAL_LOOKUP_KEY = 5;
public static final int POSTAL_DISPLAY_NAME = 6;
+ public static final int POSTAL_ACCOUNT_TYPE = 7;
+ public static final int POSTAL_ACCOUNT_NAME = 8;
}
private final CharSequence mUnknownNameText;
@@ -165,14 +173,22 @@ public class PostalAddressListAdapter extends ContactEntryListAdapter {
photoId = cursor.getLong(PostalQuery.POSTAL_PHOTO_ID);
}
+ Account account = null;
+ if (!cursor.isNull(PostalQuery.POSTAL_ACCOUNT_TYPE)
+ && !cursor.isNull(PostalQuery.POSTAL_ACCOUNT_NAME)) {
+ final String accountType = cursor.getString(PostalQuery.POSTAL_ACCOUNT_TYPE);
+ final String accountName = cursor.getString(PostalQuery.POSTAL_ACCOUNT_NAME);
+ account = new Account(accountName, accountType);
+ }
+
DefaultImageRequest request = null;
if (photoId == 0) {
request = getDefaultImageRequestFromCursor(cursor, PostalQuery.POSTAL_DISPLAY_NAME,
PostalQuery.POSTAL_LOOKUP_KEY);
}
- getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false, getCircularPhotos(),
- request);
+ getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, account,
+ false, getCircularPhotos(), request);
}
//
// protected void bindSearchSnippet(final ContactListItemView view, Cursor cursor) {
diff --git a/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java b/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
index 10887cb57..c45ed41b8 100644
--- a/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
+++ b/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
@@ -1056,6 +1056,7 @@ public class ExpandingEntryCardView extends CardView {
private final String mMimeType;
private final long mId;
private final boolean mIsSuperPrimary;
+ private String mData;
public EntryContextMenuInfo(String copyText, String copyLabel, String mimeType, long id,
boolean isSuperPrimary) {
@@ -1066,6 +1067,16 @@ public class ExpandingEntryCardView extends CardView {
mIsSuperPrimary = isSuperPrimary;
}
+ public EntryContextMenuInfo(String copyText, String copyLabel, String mimeType, long id,
+ boolean isSuperPrimary, String data) {
+ mCopyText = copyText;
+ mCopyLabel = copyLabel;
+ mMimeType = mimeType;
+ mId = id;
+ mIsSuperPrimary = isSuperPrimary;
+ mData = data;
+ }
+
public String getCopyText() {
return mCopyText;
}
@@ -1085,6 +1096,10 @@ public class ExpandingEntryCardView extends CardView {
public boolean isSuperPrimary() {
return mIsSuperPrimary;
}
+
+ public String getData() {
+ return mData;
+ }
}
static final class EntryTag {
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 26f84a850..99af33c36 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -20,10 +20,12 @@ import android.accounts.Account;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.app.Activity;
+import android.app.DialogFragment;
import android.app.Fragment;
import android.app.LoaderManager.LoaderCallbacks;
import android.app.SearchManager;
import android.content.ActivityNotFoundException;
+import android.content.ContentValues;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
@@ -32,6 +34,7 @@ import android.content.Loader;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
+import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
@@ -45,6 +48,8 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Trace;
import android.provider.CalendarContract;
+import android.os.Handler;
+import android.os.Message;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Event;
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
@@ -56,6 +61,7 @@ import android.provider.ContactsContract.CommonDataKinds.Organization;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.Relation;
import android.provider.ContactsContract.CommonDataKinds.SipAddress;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.CommonDataKinds.Website;
import android.provider.ContactsContract.Contacts;
@@ -66,6 +72,7 @@ import android.provider.ContactsContract.DataUsageFeedback;
import android.provider.ContactsContract.Intents;
import android.provider.ContactsContract.QuickContact;
import android.provider.ContactsContract.RawContacts;
+import android.provider.Telephony;
import android.support.v7.graphics.Palette;
import android.telecom.PhoneAccount;
import android.telecom.TelecomManager;
@@ -73,6 +80,8 @@ import android.text.BidiFormatter;
import android.text.SpannableString;
import android.text.TextDirectionHeuristics;
import android.text.TextUtils;
+import android.telephony.TelephonyManager;
+import android.telephony.SubscriptionManager;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
@@ -90,14 +99,18 @@ import android.widget.Toolbar;
import com.android.contacts.ContactSaveService;
import com.android.contacts.ContactsActivity;
import com.android.contacts.NfcHandler;
+import com.android.contacts.common.MoreContactUtils;
+import com.android.contacts.common.SimContactsConstants;
import com.android.contacts.R;
import com.android.contacts.common.CallUtil;
import com.android.contacts.common.ClipboardUtils;
import com.android.contacts.common.Collapser;
import com.android.contacts.common.ContactsUtils;
+import com.android.contacts.common.GroupMetaData;
import com.android.contacts.common.activity.RequestPermissionsActivity;
import com.android.contacts.common.dialog.CallSubjectDialog;
import com.android.contacts.common.editor.SelectAccountDialogFragment;
+import com.android.contacts.common.activity.fragment.BlockContactDialogFragment;
import com.android.contacts.common.interactions.TouchPointManager;
import com.android.contacts.common.lettertiles.LetterTileDrawable;
import com.android.contacts.common.list.ShortcutIntentBuilder;
@@ -106,12 +119,14 @@ import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.Contact;
import com.android.contacts.common.model.ContactLoader;
import com.android.contacts.common.model.RawContact;
+import com.android.contacts.common.model.RawContactDelta;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.common.model.account.AccountWithDataSet;
import com.android.contacts.common.model.dataitem.DataItem;
import com.android.contacts.common.model.dataitem.DataKind;
import com.android.contacts.common.model.dataitem.EmailDataItem;
import com.android.contacts.common.model.dataitem.EventDataItem;
+import com.android.contacts.common.model.dataitem.GroupMembershipDataItem;
import com.android.contacts.common.model.dataitem.ImDataItem;
import com.android.contacts.common.model.dataitem.NicknameDataItem;
import com.android.contacts.common.model.dataitem.NoteDataItem;
@@ -122,7 +137,11 @@ import com.android.contacts.common.model.dataitem.SipAddressDataItem;
import com.android.contacts.common.model.dataitem.StructuredNameDataItem;
import com.android.contacts.common.model.dataitem.StructuredPostalDataItem;
import com.android.contacts.common.model.dataitem.WebsiteDataItem;
+import com.android.contacts.common.util.BlockContactHelper;
import com.android.contacts.common.util.ImplicitIntentsUtil;
+import com.android.contacts.common.MoreContactUtils;
+import com.android.contacts.common.SimContactsConstants;
+import com.android.contacts.common.util.BitmapUtil;
import com.android.contacts.common.util.DateUtils;
import com.android.contacts.common.util.MaterialColorMapUtils;
import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
@@ -136,6 +155,7 @@ import com.android.contacts.interactions.CallLogInteractionsLoader;
import com.android.contacts.interactions.ContactDeletionInteraction;
import com.android.contacts.interactions.ContactInteraction;
import com.android.contacts.interactions.SmsInteractionsLoader;
+import com.android.internal.telephony.PhoneConstants;
import com.android.contacts.quickcontact.ExpandingEntryCardView.Entry;
import com.android.contacts.quickcontact.ExpandingEntryCardView.EntryContextMenuInfo;
import com.android.contacts.quickcontact.ExpandingEntryCardView.EntryTag;
@@ -150,8 +170,11 @@ import com.android.contacts.widget.MultiShrinkScroller.MultiShrinkScrollerListen
import com.android.contacts.widget.QuickContactImageView;
import com.android.contactsbind.HelpUtils;
+import com.cyanogen.lookup.phonenumber.provider.LookupProviderImpl;
import com.google.common.collect.Lists;
-
+import com.google.common.collect.ImmutableList;
+import com.squareup.picasso.Picasso;
+import com.squareup.picasso.Target;
import java.lang.SecurityException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -169,7 +192,8 @@ import java.util.concurrent.ConcurrentHashMap;
* data asynchronously, and then shows a popup with details centered around
* {@link Intent#getSourceBounds()}.
*/
-public class QuickContactActivity extends ContactsActivity {
+public class QuickContactActivity extends ContactsActivity implements
+ BlockContactDialogFragment.BlockContactCallbacks {
/**
* QuickContacts immediately takes up the full screen. All possible information is shown.
@@ -257,6 +281,9 @@ public class QuickContactActivity extends ContactsActivity {
private final ImageViewDrawableSetter mPhotoSetter = new ImageViewDrawableSetter();
+ private Target mContactBitmapTarget;
+ private BlockContactHelper mBlockContactHelper;
+
/**
* {@link #LEADING_MIMETYPES} is used to sort MIME-types.
*
@@ -311,6 +338,7 @@ public class QuickContactActivity extends ContactsActivity {
private static final int CARD_ENTRY_ID_EDIT_CONTACT = -2;
+ private static final int MAX_NUM_LENGTH = 3; // add limit length to show IP call item
private static final int[] mRecentLoaderIds = new int[]{
LOADER_SMS_ID,
LOADER_CALENDAR_ID,
@@ -342,31 +370,6 @@ public class QuickContactActivity extends ContactsActivity {
return;
}
- // Pass the touch point through the intent for use in the InCallUI
- if (Intent.ACTION_CALL.equals(intent.getAction())) {
- if (TouchPointManager.getInstance().hasValidPoint()) {
- Bundle extras = new Bundle();
- extras.putParcelable(TouchPointManager.TOUCH_POINT,
- TouchPointManager.getInstance().getPoint());
- intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
- }
- }
-
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
- mHasIntentLaunched = true;
- try {
- startActivity(intent);
- } catch (SecurityException ex) {
- Toast.makeText(QuickContactActivity.this, R.string.missing_app,
- Toast.LENGTH_SHORT).show();
- Log.e(TAG, "QuickContacts does not have permission to launch "
- + intent);
- } catch (ActivityNotFoundException ex) {
- Toast.makeText(QuickContactActivity.this, R.string.missing_app,
- Toast.LENGTH_SHORT).show();
- }
-
// Default to USAGE_TYPE_CALL. Usage is summed among all types for sorting each data id
// so the exact usage type is not necessary in all cases
String usageType = DataUsageFeedback.USAGE_TYPE_CALL;
@@ -442,10 +445,23 @@ public class QuickContactActivity extends ContactsActivity {
}
};
+ @Override
+ public void onBlockContact(boolean notifyLookupProvider) {
+ mBlockContactHelper.blockContactAsync(notifyLookupProvider);
+ }
+
+ @Override
+ public void onUnblockContact(boolean notifyLookupProvider) {
+ mBlockContactHelper.unblockContactAsync(notifyLookupProvider);
+ }
+
private interface ContextMenuIds {
static final int COPY_TEXT = 0;
static final int CLEAR_DEFAULT = 1;
static final int SET_DEFAULT = 2;
+ static final int EDIT_BEFORE_CALL = 3;
+ static final int IPCALL1 = 4;
+ static final int IPCALL2 = 5; // add for new feature: ip call prefix
}
private final OnCreateContextMenuListener mEntryContextMenuListener =
@@ -485,6 +501,29 @@ public class QuickContactActivity extends ContactsActivity {
menu.add(ContextMenu.NONE, ContextMenuIds.SET_DEFAULT,
ContextMenu.NONE, getString(R.string.set_default));
}
+ if (Phone.CONTENT_ITEM_TYPE.equals(info.getMimeType())) {
+ menu.add(ContextMenu.NONE, ContextMenuIds.EDIT_BEFORE_CALL,
+ ContextMenu.NONE, getString(R.string.edit_before_call));
+ // add limit length to show IP call item
+ if (info.getData().length() > MAX_NUM_LENGTH) {
+ if (MoreContactUtils.isMultiSimEnable(QuickContactActivity.this,
+ PhoneConstants.SUB1)) {
+ String sub1Name = MoreContactUtils.getMultiSimAliasesName(
+ getApplicationContext(), PhoneConstants.SUB1);
+ menu.add(ContextMenu.NONE, ContextMenuIds.IPCALL1, ContextMenu.NONE,
+ getApplicationContext().getString(
+ com.android.contacts.common.R.string.ip_call_by_slot, sub1Name));
+ }
+ if (MoreContactUtils.isMultiSimEnable(QuickContactActivity.this,
+ PhoneConstants.SUB2)) {
+ String sub2Name = MoreContactUtils.getMultiSimAliasesName(
+ getApplicationContext(), PhoneConstants.SUB2);
+ menu.add(ContextMenu.NONE, ContextMenuIds.IPCALL2, ContextMenu.NONE,
+ getApplicationContext().getString(
+ com.android.contacts.common.R.string.ip_call_by_slot, sub2Name));
+ }
+ }
+ }
}
};
@@ -513,11 +552,32 @@ public class QuickContactActivity extends ContactsActivity {
menuInfo.getId());
this.startService(clearIntent);
return true;
+ case ContextMenuIds.EDIT_BEFORE_CALL:
+ callByEdit(menuInfo.getData());
+ return true;
+ case ContextMenuIds.IPCALL1:
+ ipCallBySlot(menuInfo.getData(), PhoneConstants.SUB1);
+ return true;
+ case ContextMenuIds.IPCALL2:
+ ipCallBySlot(menuInfo.getData(), PhoneConstants.SUB2);
+ return true;
default:
throw new IllegalArgumentException("Unknown menu option " + item.getItemId());
}
}
+ private void ipCallBySlot(String data, int subscription) {
+ String ipCallPrefix = MoreContactUtils.getIPCallPrefix(this,
+ subscription);
+ if (!TextUtils.isEmpty(ipCallPrefix)) {
+ Intent callIntent = CallUtil.getCallIntent(ipCallPrefix + data,
+ MoreContactUtils.getAccount(subscription));
+ startActivity(callIntent);
+ } else {
+ MoreContactUtils.showNoIPNumberDialog(this, subscription);
+ }
+ }
+
/**
* Headless fragment used to handle account selection callbacks invoked from
* {@link DirectoryContactUtil}.
@@ -698,7 +758,6 @@ public class QuickContactActivity extends ContactsActivity {
getWindow().setStatusBarColor(Color.TRANSPARENT);
- processIntent(getIntent());
// Show QuickContact in front of soft input
getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
@@ -813,7 +872,12 @@ public class QuickContactActivity extends ContactsActivity {
}
});
}
-
+ processIntent(getIntent());
+ mBlockContactHelper = new BlockContactHelper(this, new LookupProviderImpl(this));
+ if (mContactData != null) {
+ mBlockContactHelper.setContactInfo(mContactData);
+ mBlockContactHelper.gatherDataInBackground();
+ }
Trace.endSection();
}
@@ -863,13 +927,24 @@ public class QuickContactActivity extends ContactsActivity {
mExtraPrioritizedMimeType = getIntent().getStringExtra(QuickContact.EXTRA_PRIORITIZED_MIMETYPE);
final Uri oldLookupUri = mLookupUri;
- if (lookupUri == null) {
+ mLookupUri = lookupUri;
+ mExcludeMimes = intent.getStringArrayExtra(QuickContact.EXTRA_EXCLUDE_MIMES);
+
+ if (mLookupUri == null) {
finish();
return;
}
- mLookupUri = lookupUri;
- mExcludeMimes = intent.getStringArrayExtra(QuickContact.EXTRA_EXCLUDE_MIMES);
- if (oldLookupUri == null) {
+
+ Contact contact = null;
+ if (UriUtils.isEncodedContactUri(mLookupUri)) {
+ // try to parse it as an ENHANCED_CALLER_META_DATA uri
+ contact = ContactLoader.parseEncodedContactEntity(mLookupUri,
+ ContactLoader.EncodedContactEntitySchemaVersion.ENHANCED_CALLER_META_DATA);
+ }
+
+ if (contact != null) {
+ bindContactData(contact);
+ } else if (oldLookupUri == null) {
mContactLoader = (ContactLoader) getLoaderManager().initLoader(
LOADER_CONTACT_ID, null, mLoaderContactCallbacks);
} else if (oldLookupUri != mLookupUri) {
@@ -914,6 +989,21 @@ public class QuickContactActivity extends ContactsActivity {
}
}
+ private void setAttributionText(String value) {
+ if (!TextUtils.isEmpty(value)) {
+ if (mScroller != null) {
+ mScroller.setAttributionText(getString(R.string.powered_by_provider, value));
+ }
+ }
+ }
+
+ private void setSpamCountText(int value) {
+ if (mScroller != null && value > 0) {
+ mScroller.setSpamCountText(
+ getResources().getQuantityString(R.plurals.spam_count_text, value, value));
+ }
+ }
+
/**
* Check if the given MIME-type appears in the list of excluded MIME-types
* that the most-recent caller requested.
@@ -940,7 +1030,28 @@ public class QuickContactActivity extends ContactsActivity {
Trace.beginSection("Set display photo & name");
mPhotoView.setIsBusiness(mContactData.isDisplayNameFromOrganization());
- mPhotoSetter.setupContactPhoto(data, mPhotoView);
+ if (mContactData.getPhotoBinaryData() == null && mContactData.getPhotoUri() != null) {
+ mContactBitmapTarget = new Target() {
+ @Override
+ public void onPrepareLoad(Drawable d){}
+ @Override
+ public void onBitmapLoaded(Bitmap result, Picasso.LoadedFrom from) {
+ if (result != null) {
+ mContactData.setPhotoBinaryData(BitmapUtil.bitmapToByteArray(result));
+ mPhotoSetter.setupContactPhoto(data, mPhotoView);
+ }
+ mContactBitmapTarget = null;
+ }
+ @Override
+ public void onBitmapFailed(Drawable drawable) {
+ mPhotoSetter.setupContactPhoto(data, mPhotoView);
+ mContactBitmapTarget = null;
+ }
+ };
+ Picasso.with(this).load(mContactData.getPhotoUri()).into(mContactBitmapTarget);
+ } else {
+ mPhotoSetter.setupContactPhoto(data, mPhotoView);
+ }
extractAndApplyTintFromPhotoViewAsynchronously();
String phoneticName = ContactDisplayUtils.getPhoneticName(this, data);
String displayName = ContactDisplayUtils.getDisplayName(this, data).toString();
@@ -953,6 +1064,16 @@ public class QuickContactActivity extends ContactsActivity {
setHeaderNameText(displayName);
}
+ setAttributionText(data.getProviderName());
+ final int spamCount = data.getSpamCount();
+ if (spamCount > 0) {
+ mHasComputedThemeColor = true;
+ setThemeColor(mMaterialColorMapUtils
+ .calculatePrimaryAndSecondaryColor(getResources()
+ .getColor(R.color.letter_tile_red_color)));
+ setSpamCountText(spamCount);
+ }
+
Trace.endSection();
mEntriesAndActionsTask = new AsyncTask<Void, Void, Cp2DataCardModel>() {
@@ -1059,7 +1180,7 @@ public class QuickContactActivity extends ContactsActivity {
// the name mimetype.
final List<Entry> aboutEntries = dataItemsToEntries(mimeTypeItems,
/* aboutCardTitleOut = */ null);
- if (aboutEntries.size() > 0) {
+ if (aboutEntries != null && aboutEntries.size() > 0) {
aboutCardEntries.add(aboutEntries);
}
}
@@ -1177,6 +1298,30 @@ public class QuickContactActivity extends ContactsActivity {
}
/**
+ * Maps group ID to the corresponding group name, collapses all synonymous groups. Ignores
+ * default groups (e.g. My Contacts) and favorites groups.
+ */
+ private static String getGroupName(List<GroupMetaData> groupMetaData, long groupId) {
+ if (groupMetaData == null) {
+ return "";
+ }
+
+ for (GroupMetaData group : groupMetaData) {
+ if (group.getGroupId() == groupId) {
+ if (!group.isDefaultGroup() && !group.isFavorites()) {
+ String title = group.getTitle();
+ if (!TextUtils.isEmpty(title)) {
+ return title;
+ }
+ }
+ break;
+ }
+ }
+
+ return "";
+ }
+
+ /**
* Create a card that shows "Add email" and "Add phone number" entries in grey.
*/
private void initializeNoContactDetailCard() {
@@ -1496,7 +1641,7 @@ public class QuickContactActivity extends ContactsActivity {
TextDirectionHeuristics.LTR);
entryContextMenuInfo = new EntryContextMenuInfo(header,
res.getString(R.string.phoneLabelsGroup), dataItem.getMimeType(),
- dataItem.getId(), dataItem.isSuperPrimary());
+ dataItem.getId(), dataItem.isSuperPrimary(), header);
if (phone.hasKindTypeColumn(kind)) {
final int kindTypeColumn = phone.getKindTypeColumn(kind);
final String label = phone.getLabel();
@@ -1634,6 +1779,35 @@ public class QuickContactActivity extends ContactsActivity {
aboutCardName.value = res.getString(R.string.about_card_title);
}
}
+ } else if (dataItem instanceof GroupMembershipDataItem) {
+ GroupMembershipDataItem groupMembership =
+ (GroupMembershipDataItem) dataItem;
+ Long groupId = groupMembership.getGroupRowId();
+ if (groupId != null) {
+ return new Entry(/* viewId = */-1,
+ /* icon = */null,
+ res.getString(R.string.groupsLabel),
+ getGroupName(contactData.getGroupMetaData(),
+ groupId),
+ /* mSubHeaderIcon= */null,
+ /* text = */null,
+ /* mTextIcon= */null,
+ /* primaryContentDescription = */null,
+ /* intent = */null,
+ /* alternateIcon = */null,
+ /* alternateIntent = */null,
+ /* alternateContentDescription = */null,
+ /* shouldApplyColor = */false,
+ /* isEditable = */false,
+ /* EntryContextMenuInfo = */null,
+ /* thirdIcon = */null,
+ /* thirdIntent = */null,
+ /* thirdContentDescription = */null,
+ /* thirdAction = */ Entry.ACTION_NONE,
+ /* thirdExtras = */ null,
+ /* iconResourceId = */0);
+ }
+ return null;
} else {
// Custom DataItem
header = dataItem.buildDataStringForDisplay(context, kind);
@@ -1754,6 +1928,12 @@ public class QuickContactActivity extends ContactsActivity {
if (dataItems.get(0).getMimeType().equals(MIMETYPE_GPLUS_PROFILE) ||
dataItems.get(0).getMimeType().equals(MIMETYPE_HANGOUTS)) {
return gPlusOrHangoutsDataItemsToEntries(dataItems);
+ } else if (dataItems.get(0).getMimeType().equals(GroupMembership.CONTENT_ITEM_TYPE)) {
+ final Entry entry = groupDataItemsToEntry(dataItems);
+ if (entry != null) {
+ return Lists.newArrayList(entry);
+ }
+ return null;
} else {
final List<Entry> entries = new ArrayList<>();
for (DataItem dataItem : dataItems) {
@@ -1767,6 +1947,48 @@ public class QuickContactActivity extends ContactsActivity {
}
}
+ private Entry groupDataItemsToEntry(List<DataItem> dataItems) {
+ final List<String> titles = new ArrayList<>();
+ for (DataItem dataItem : dataItems) {
+ if (!(dataItem instanceof GroupMembershipDataItem)) {
+ continue;
+ }
+
+ final GroupMembershipDataItem groupItem = (GroupMembershipDataItem) dataItem;
+ if (groupItem.isDefaultGroup() || groupItem.isFavoritesGroup()) {
+ continue;
+ }
+ final String title = groupItem.getGroupTitle();
+ if (title != null) {
+ titles.add(title);
+ }
+ }
+ if (titles.isEmpty()) {
+ return null;
+ }
+
+ return new Entry(/* viewId = */ -1, /* icon = */ null,
+ /* header */ getResources().getString(R.string.contacts_groups_label),
+ /* subHeader */ null,
+ /* subHeaderIcon = */ null,
+ /* text = */ TextUtils.join(", ", titles),
+ /* textIcon = */ null,
+ /* primaryContentDescription = */ null,
+ /* intent = */ null,
+ /* alternateIcon = */ null,
+ /* alternateIntent = */ null,
+ /* alternateContentDescription = */ null,
+ /* shouldApplyColor = */ true,
+ /* isEditable = */ false,
+ /* EntryContextMenuInfo = */ null,
+ /* thirdIcon = */ null,
+ /* thirdIntent = */ null,
+ /* thirdContentDescription = */ null,
+ /* thirdAction = */ Entry.ACTION_NONE,
+ /* thirdExtras = */ null,
+ /* iconResourceId = */ 0);
+ }
+
/**
* G+ and Hangout entries are unique in that a single ExpandingEntryCardView.Entry consists
* of two data items. This method attempts to build each entry using the two data items if
@@ -2054,7 +2276,8 @@ public class QuickContactActivity extends ContactsActivity {
finish();
return;
}
-
+ mBlockContactHelper.setContactInfo(data);
+ mBlockContactHelper.gatherDataInBackground();
bindContactData(data);
} finally {
@@ -2092,6 +2315,7 @@ public class QuickContactActivity extends ContactsActivity {
// override transitions to skip the standard window animations
overridePendingTransition(0, 0);
+ mBlockContactHelper.destroy();
}
private final LoaderCallbacks<List<ContactInteraction>> mLoaderInteractionsCallbacks =
@@ -2328,6 +2552,12 @@ public class QuickContactActivity extends ContactsActivity {
}
}
+ private void callByEdit(String data) {
+ Intent intent = new Intent(Intent.ACTION_DIAL, Uri.fromParts(PhoneAccount.SCHEME_TEL,
+ data, null));
+ startActivity(intent);
+ }
+
/**
* Creates a launcher shortcut with the current contact.
*/
@@ -2355,7 +2585,8 @@ public class QuickContactActivity extends ContactsActivity {
private boolean isShortcutCreatable() {
if (mContactData == null || mContactData.isUserProfile() ||
- mContactData.isDirectoryEntry()) {
+ mContactData.isDirectoryEntry() ||
+ !TextUtils.isEmpty(mContactData.getProviderName())) {
return false;
}
final Intent createShortcutIntent = new Intent();
@@ -2365,6 +2596,88 @@ public class QuickContactActivity extends ContactsActivity {
return receivers != null && receivers.size() > 0;
}
+ private void sendContactViaSMS() {
+ // Get name string
+ String name = mContactData.getDisplayName();
+ String phone = null;
+ String email = null;
+ String postal = null;
+ String organization = null;
+ String sipAddress = null;
+
+ Log.d(TAG, "Contact name: " + name);
+
+ for (RawContact raw: mContactData.getRawContacts()) {
+ for (DataItem dataItem : raw.getDataItems()) {
+ final ContentValues entryValues = dataItem.getContentValues();
+ final String mimeType = dataItem.getMimeType();
+
+ Log.d(TAG, " entryValues:" + entryValues);
+
+ if (mimeType == null) continue;
+
+ if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) { // Get phone string
+ if (phone == null) {
+ phone = entryValues.getAsString(Phone.NUMBER);
+ } else {
+ phone = phone + ", " + entryValues.getAsString(Phone.NUMBER);
+ }
+ } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) { // Get email string
+ if (email == null) {
+ email = entryValues.getAsString(Email.ADDRESS);
+ } else {
+ email = email + ", " + entryValues.getAsString(Email.ADDRESS);
+ }
+ } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ if (postal == null) {
+ postal = entryValues.getAsString(StructuredPostal.FORMATTED_ADDRESS);
+ } else {
+ postal = postal + ", " + entryValues.getAsString(
+ StructuredPostal.FORMATTED_ADDRESS);
+ }
+ } else if (Organization.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ if (organization == null) {
+ organization = entryValues.getAsString(Organization.COMPANY);
+ } else {
+ organization = organization + ", " + entryValues
+ .getAsString(Organization.COMPANY);
+ }
+ } else if (SipAddress.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ if (sipAddress == null) {
+ sipAddress = entryValues.getAsString(SipAddress.SIP_ADDRESS);
+ } else {
+ sipAddress = sipAddress + ", " + entryValues
+ .getAsString(SipAddress.SIP_ADDRESS);
+ }
+ }
+ }
+ }
+
+ if (TextUtils.isEmpty(name)) {
+ name = getResources().getString(R.string.missing_name);
+ }
+
+ name = getString(R.string.nameLabelsGroup) + ":" + name + "\r\n";
+ phone = (phone == null) ? "" : getString(R.string.phoneLabelsGroup)
+ + ":" + phone + "\r\n";
+ email = (email == null )? "" : getString(R.string.emailLabelsGroup)
+ + ":" + email + "\r\n";
+ postal = (postal == null) ? "" : getString(R.string.postalLabelsGroup)
+ + ":" + postal + "\r\n";
+ organization = (organization == null) ? "" : getString(R.string.organizationLabelsGroup)
+ + ":" + organization + "\r\n";
+ sipAddress = (sipAddress == null) ? "" : getString(R.string.label_sip_address) + ":"
+ + sipAddress + "\r\n";
+ String defaultSmsPackageName = Telephony.Sms.getDefaultSmsPackage(this);
+ Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.putExtra("sms_body", name + phone + email + postal + organization + sipAddress);
+ intent.setType("text/plain");
+ if (defaultSmsPackageName != null) {
+ intent.setPackage(defaultSmsPackageName);
+ }
+ startActivity(intent);
+ }
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
final MenuInflater inflater = getMenuInflater();
@@ -2405,6 +2718,82 @@ public class QuickContactActivity extends ContactsActivity {
final MenuItem helpMenu = menu.findItem(R.id.menu_help);
helpMenu.setVisible(HelpUtils.isHelpAndFeedbackAvailable());
+ String accoutName = null;
+ String accoutType = null;
+
+ final RawContact rawContact = mContactData.getRawContacts().get(0);
+ accoutName = rawContact.getAccountName();
+ accoutType = rawContact.getAccountTypeString();
+
+ final MenuItem copyToPhoneMenu = menu.findItem(R.id.menu_copy_to_phone);
+ if (copyToPhoneMenu != null) {
+ copyToPhoneMenu.setVisible(false);
+ }
+
+ final MenuItem copyToSim1Menu = menu.findItem(R.id.menu_copy_to_sim1);
+ if (copyToSim1Menu != null) {
+ copyToSim1Menu.setVisible(false);
+ }
+
+ final MenuItem copyToSim2Menu = menu.findItem(R.id.menu_copy_to_sim2);
+ if (copyToSim2Menu != null) {
+ copyToSim2Menu.setVisible(false);
+ }
+
+ if (!TextUtils.isEmpty(accoutType)) {
+ if (SimContactsConstants.ACCOUNT_TYPE_SIM.equals(accoutType)) {
+ copyToPhoneMenu.setVisible(true);
+ copyToPhoneMenu.setTitle(getString(R.string.menu_copyTo,
+ getString(R.string.phoneLabelsGroup)));
+ if (TelephonyManager.getDefault().isMultiSimEnabled()) {
+ if (SimContactsConstants.SIM_NAME_1.equals(accoutName)
+ && simIsReady(PhoneConstants.SUB2)) {
+ copyToSim2Menu.setTitle(getString(R.string.menu_copyTo,
+ MoreContactUtils.getMultiSimAliasesName(
+ this, PhoneConstants.SUB2)));
+ copyToSim2Menu.setVisible(true);
+ }
+ if (SimContactsConstants.SIM_NAME_2.equals(accoutName)
+ && simIsReady(PhoneConstants.SUB1)) {
+ copyToSim1Menu.setTitle(getString(R.string.menu_copyTo,
+ MoreContactUtils.getMultiSimAliasesName(
+ this, PhoneConstants.SUB1)));
+ copyToSim1Menu.setVisible(true);
+ }
+ }
+ } else if (SimContactsConstants.ACCOUNT_TYPE_PHONE.equals(accoutType)) {
+ copyToPhoneMenu.setVisible(false);
+ boolean hasPhoneOrEmail = hasPhoneOrEmailDate(mContactData);
+ if (TelephonyManager.getDefault().isMultiSimEnabled()) {
+ if (hasPhoneOrEmail && simIsReady(PhoneConstants.SUB1)) {
+ copyToSim1Menu.setTitle(getString(R.string.menu_copyTo,
+ MoreContactUtils.getMultiSimAliasesName(
+ this, PhoneConstants.SUB1)));
+ copyToSim1Menu.setVisible(true);
+ }
+ if (hasPhoneOrEmail && simIsReady(PhoneConstants.SUB2)) {
+ copyToSim2Menu.setTitle(getString(R.string.menu_copyTo,
+ MoreContactUtils.getMultiSimAliasesName(
+ this, PhoneConstants.SUB2)));
+ copyToSim2Menu.setVisible(true);
+ }
+ } else {
+ if (hasPhoneOrEmail && simIsReady(PhoneConstants.SUB1)) {
+ copyToSim1Menu.setTitle(getString(R.string.menu_copyTo,
+ SimContactsConstants.SIM_NAME));
+ copyToSim1Menu.setVisible(true);
+ }
+ }
+ }
+ }
+
+ // set block or un-block menu titles accordingly
+ final MenuItem blockMenuItem = menu.findItem(R.id.menu_block_contact);
+ if (mBlockContactHelper.isContactBlacklisted()) {
+ blockMenuItem.setTitle(R.string.menu_unblock_contact);
+ } else {
+ blockMenuItem.setTitle(R.string.menu_block_contact);
+ }
return true;
}
@@ -2495,8 +2884,363 @@ public class QuickContactActivity extends ContactsActivity {
case R.id.menu_help:
HelpUtils.launchHelpAndFeedbackForContactScreen(this);
return true;
+
+ case R.id.menu_send_via_sms: {
+ if (mContactData == null) {
+ return false;
+ }
+ sendContactViaSMS();
+ return true;
+ }
+ case R.id.menu_copy_to_phone: {
+ if (mContactData == null) return false;
+ copyToPhone();
+ return true;
+ }
+ case R.id.menu_copy_to_sim1: {
+ if (mContactData == null) return false;
+ copyToCard(PhoneConstants.SUB1);
+ return true;
+ }
+ case R.id.menu_copy_to_sim2: {
+ if (mContactData == null) return false;
+ copyToCard(PhoneConstants.SUB2);
+ return true;
+ }
+ case R.id.menu_block_contact: {
+ // block contact dialog fragment
+ DialogFragment f = mBlockContactHelper.getBlockContactDialog(
+ mBlockContactHelper.isContactBlacklisted() ?
+ BlockContactHelper.BlockOperation.UNBLOCK :
+ BlockContactHelper.BlockOperation.BLOCK
+ );
+ f.show(getFragmentManager(), "block_contact");
+ return true;
+ }
default:
return super.onOptionsItemSelected(item);
}
}
+ private boolean hasPhoneOrEmailDate(Contact contact){
+ int phoneCount = 0;
+ int emailCount = 0;
+ ImmutableList<RawContact> rawContacts = contact.getRawContacts();
+ for (RawContact rawContact : rawContacts) {
+ RawContactDelta rawContactDelta = RawContactDelta.fromBefore(rawContact);
+ phoneCount += rawContactDelta.getMimeEntriesCount(
+ Phone.CONTENT_ITEM_TYPE, true);
+ emailCount += rawContactDelta.getMimeEntriesCount(
+ Email.CONTENT_ITEM_TYPE, true);
+ }
+ if (phoneCount > 0 || emailCount > 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ //supply phone number and email which could stored in one ADN
+ class UsimEntity {
+ private ArrayList<String> mNumberList = new ArrayList<String>();
+ private ArrayList<String> mEmailList = new ArrayList<String>();
+
+ public ArrayList<String> getEmailList() {
+ return mEmailList;
+ }
+
+ public ArrayList<String> getNumberList() {
+ return mNumberList;
+ }
+
+ public void putEmailList(ArrayList<String> list) {
+ mEmailList = list;
+ }
+
+ public void putNumberList(ArrayList<String> list) {
+ mNumberList = list;
+ }
+
+ public boolean containsEmail() {
+ return !mEmailList.isEmpty();
+ }
+
+ public boolean containsNumber() {
+ return !mNumberList.isEmpty();
+ }
+ }
+
+ private void copyToPhone() {
+ String name = mContactData.getDisplayName();
+ if (TextUtils.isEmpty(name)) {
+ name = "";
+ }
+ String phoneNumber = "";
+ StringBuilder anrNumber = new StringBuilder();
+ StringBuilder email = new StringBuilder();
+
+ //get phonenumber,email,anr from SIM contacts,then insert them to phone
+ for (RawContact rawContact : mContactData.getRawContacts()) {
+ for (DataItem dataItem : rawContact.getDataItems()) {
+ if (dataItem.getMimeType() == null) {
+ continue;
+ }
+ if (dataItem instanceof PhoneDataItem) {
+ PhoneDataItem phoneNum = (PhoneDataItem) dataItem;
+ final String number = phoneNum.getNumber();
+ if (!TextUtils.isEmpty(number)) {
+ if (Phone.TYPE_MOBILE == phoneNum.getContentValues().getAsInteger(
+ Phone.TYPE)) {
+ phoneNumber = number;
+ } else {
+ if(!TextUtils.isEmpty(anrNumber.toString())) {
+ anrNumber.append(",");
+ }
+ anrNumber.append(number);
+ }
+ }
+ } else if (dataItem instanceof EmailDataItem) {
+ EmailDataItem emailData = (EmailDataItem) dataItem;
+ final String address = emailData.getData();
+ if (!TextUtils.isEmpty(address)) {
+ if(!TextUtils.isEmpty(email.toString())) {
+ email.append(",");
+ }
+ email.append(address);
+ }
+ }
+ }
+ }
+
+ String[] value = new String[] {
+ name, phoneNumber, email.toString(), anrNumber.toString()
+ };
+ boolean success = MoreContactUtils
+ .insertToPhone(value, getContentResolver(),
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ Toast.makeText(this, success ? R.string.copy_done : R.string.copy_failure,
+ Toast.LENGTH_SHORT).show();
+ }
+
+ private Handler mHandler = null;
+
+ private void copyToCard(final int sub) {
+ final Contact contactData = mContactData;
+ final int MSG_COPY_DONE = 0;
+ final int MSG_COPY_FAILURE = 1;
+ final int MSG_CARD_NO_SPACE = 2;
+ final int MSG_NO_EMPTY_EMAIL = 3;
+ if (mHandler == null) {
+ mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_COPY_DONE:
+ Toast.makeText(QuickContactActivity.this, R.string.copy_done,
+ Toast.LENGTH_SHORT).show();
+ break;
+ case MSG_COPY_FAILURE:
+ Toast.makeText(QuickContactActivity.this, R.string.copy_failure,
+ Toast.LENGTH_SHORT).show();
+ break;
+ case MSG_CARD_NO_SPACE:
+ Toast.makeText(QuickContactActivity.this, R.string.card_no_space,
+ Toast.LENGTH_SHORT).show();
+ break;
+ case MSG_NO_EMPTY_EMAIL:
+ Toast.makeText(QuickContactActivity.this,
+ R.string.no_empty_email_in_usim,
+ Toast.LENGTH_SHORT).show();
+ break;
+ }
+ }
+ };
+ }
+
+ new Thread(new Runnable() {
+ public void run() {
+ synchronized (this) {
+ int adnCountInSimContact = 1;
+ int anrCountInSimContact = 0;
+ int emailCountInSimContact = 0;
+
+ Cursor cr = null;
+ // call query first, otherwise the count queries will fail
+ try{
+ int[] subId = SubscriptionManager.getSubId(sub);
+ if (subId != null
+ && TelephonyManager.getDefault().isMultiSimEnabled()) {
+ cr = getContentResolver().query(
+ Uri.parse(SimContactsConstants.SIM_SUB_URI
+ + subId[0]), null, null, null, null);
+ } else {
+ cr = getContentResolver().query(
+ Uri.parse(SimContactsConstants.SIM_URI), null,
+ null, null, null);
+ }
+ } catch (NullPointerException e) {
+ Log.e(TAG, "Exception:" + e);
+ } finally {
+ if (cr != null) {
+ cr.close();
+ }
+ }
+
+ if (MoreContactUtils.canSaveAnr(sub)) {
+ anrCountInSimContact = MoreContactUtils.getOneSimAnrCount(sub);
+ }
+ if (MoreContactUtils.canSaveEmail(sub)) {
+ emailCountInSimContact = MoreContactUtils.getOneSimEmailCount(sub);
+ }
+ int totalEmptyAdn = MoreContactUtils.getSimFreeCount(
+ QuickContactActivity.this, sub);
+ int totalEmptyAnr = MoreContactUtils.getSpareAnrCount(sub);
+ int totalEmptyEmail = MoreContactUtils.getSpareEmailCount(sub);
+
+ Message msg = Message.obtain();
+ if (totalEmptyAdn <= 0) {
+ msg.what = MSG_CARD_NO_SPACE;
+ mHandler.sendMessage(msg);
+ return;
+ }
+
+ //to indiacate how many number in one ADN can saved to SIM card,
+ //1 means can only save one number,2,3 ... means can save anr
+ int numEntitySize = adnCountInSimContact + anrCountInSimContact;
+
+ //empty number is equals to the sum of adn and anr
+ int emptyNumTotal = totalEmptyAdn + totalEmptyAnr;
+
+ // Get name string
+ String strName = contactData.getDisplayName();
+
+ ArrayList<String> arrayNumber = new ArrayList<String>();
+ ArrayList<String> arrayEmail = new ArrayList<String>();
+
+ for (RawContact rawContact : contactData.getRawContacts()) {
+ for (DataItem dataItem : rawContact.getDataItems()) {
+ if (dataItem.getMimeType() == null) {
+ continue;
+ }
+ if (dataItem instanceof PhoneDataItem) {
+ // Get phone string
+ PhoneDataItem phoneNum = (PhoneDataItem) dataItem;
+ final String number = phoneNum.getNumber();
+ if (!TextUtils.isEmpty(number) && emptyNumTotal-- > 0) {
+ arrayNumber.add(number);
+ }
+ } else if (dataItem instanceof EmailDataItem) {
+ // Get email string
+ EmailDataItem emailData = (EmailDataItem) dataItem;
+ final String address = emailData.getData();
+ if (!TextUtils.isEmpty(address) && totalEmptyEmail-- > 0) {
+ arrayEmail.add(address);
+ }
+ }
+ }
+ }
+
+ //calculate how many ADN needed according to the number,name,phone,email,
+ //then uses the max of them
+ int nameCount = (strName != null && !strName.equals("")) ? 1 : 0;
+ int groupNumCount = (arrayNumber.size() % numEntitySize) != 0 ? (arrayNumber
+ .size() / numEntitySize + 1) : (arrayNumber.size() / numEntitySize);
+ int groupEmailCount = emailCountInSimContact == 0 ? 0
+ : ((arrayEmail.size() % emailCountInSimContact) != 0 ? (arrayEmail
+ .size() / emailCountInSimContact + 1)
+ : (arrayEmail.size() / emailCountInSimContact));
+
+ int groupCount = Math.max(groupEmailCount, Math.max(nameCount, groupNumCount));
+
+ ArrayList<UsimEntity> results = new ArrayList<UsimEntity>();
+ for (int i = 0; i < groupCount; i++) {
+ results.add(new UsimEntity());
+ }
+
+ UsimEntity value;
+ //get the phone number for each ADN from arrayNumber,put them in UsimEntity
+ for (int i = 0; i < groupNumCount; i++) {
+ value = results.get(i);
+ ArrayList<String> numberItem = new ArrayList<String>();
+ for (int j = 0; j < numEntitySize; j++) {
+ if ((i * numEntitySize + j) < arrayNumber.size()) {
+ numberItem.add(arrayNumber.get(i * numEntitySize + j));
+ }
+ }
+ value.putNumberList(numberItem);
+ }
+
+ for (int i = 0; i < groupEmailCount; i++) {
+ value = results.get(i);
+ ArrayList<String> emailItem = new ArrayList<String>();
+ for (int j = 0; j < emailCountInSimContact; j++) {
+ if ((i * emailCountInSimContact + j) < arrayEmail.size()) {
+ emailItem.add(arrayEmail.get(i * emailCountInSimContact + j));
+ }
+ }
+ value.putEmailList(emailItem);
+ }
+
+ ArrayList<String> emptyList = new ArrayList<String>();
+ Uri itemUri = null;
+ if (totalEmptyEmail < 0 && MoreContactUtils.canSaveEmail(sub)) {
+ Message e_msg = Message.obtain();
+ e_msg.what = MSG_NO_EMPTY_EMAIL;
+ mHandler.sendMessage(e_msg);
+ }
+
+ //get phone number from UsimEntity,then insert to SIM card
+ for (int i = 0; i < groupCount; i++) {
+ value = results.get(i);
+ if (value.containsNumber()) {
+ arrayNumber = (ArrayList<String>) value.getNumberList();
+ } else {
+ arrayNumber = emptyList;
+ }
+
+ if (value.containsEmail()) {
+ arrayEmail = (ArrayList<String>) value.getEmailList();
+ } else {
+ arrayEmail = emptyList;
+ }
+ String strNum = arrayNumber.size() > 0 ? arrayNumber.get(0) : null;
+ StringBuilder strAnrNum = new StringBuilder();
+ for (int j = 1; j < arrayNumber.size(); j++) {
+ String s = arrayNumber.get(j);
+ if (s.length() > MoreContactUtils.MAX_LENGTH_NUMBER_IN_SIM) {
+ s = s.substring(
+ 0, MoreContactUtils.MAX_LENGTH_NUMBER_IN_SIM);
+ }
+ strAnrNum.append(s);
+ strAnrNum.append(SimContactsConstants.ANR_SEP);
+ }
+ StringBuilder strEmail = new StringBuilder();
+ for (int j = 0; j < arrayEmail.size(); j++) {
+ String s = arrayEmail.get(j);
+ if (s.length() > MoreContactUtils.MAX_LENGTH_EMAIL_IN_SIM) {
+ s = s.substring(
+ 0, MoreContactUtils.MAX_LENGTH_EMAIL_IN_SIM);
+ }
+ strEmail.append(s);
+ strEmail.append(SimContactsConstants.EMAIL_SEP);
+ }
+ itemUri = MoreContactUtils.insertToCard(QuickContactActivity.this, strName,
+ strNum, strEmail.toString(), strAnrNum.toString(), sub);
+ }
+ if (itemUri != null) {
+ msg.what = MSG_COPY_DONE;
+ mHandler.sendMessage(msg);
+ } else {
+ msg.what = MSG_COPY_FAILURE;
+ mHandler.sendMessage(msg);
+ }
+ }
+ }
+ }).start();
+ }
+
+ private boolean simIsReady(int sub) {
+ if (TelephonyManager.getDefault().getSimState(sub)
+ == TelephonyManager.SIM_STATE_READY)
+ return true;
+ return false;
+ }
}
diff --git a/src/com/android/contacts/util/ImageViewDrawableSetter.java b/src/com/android/contacts/util/ImageViewDrawableSetter.java
index 6147c3975..1ca82124e 100644
--- a/src/com/android/contacts/util/ImageViewDrawableSetter.java
+++ b/src/com/android/contacts/util/ImageViewDrawableSetter.java
@@ -16,6 +16,8 @@
package com.android.contacts.util;
+import android.accounts.Account;
+import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -30,6 +32,7 @@ import com.android.contacts.common.ContactPhotoManager;
import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
import com.android.contacts.common.lettertiles.LetterTileDrawable;
import com.android.contacts.common.model.Contact;
+import com.android.contacts.common.model.RawContact;
import java.util.Arrays;
@@ -53,9 +56,17 @@ public class ImageViewDrawableSetter {
}
public Bitmap setupContactPhoto(Contact contactData, ImageView photoView) {
+ Account account = null;
mContact = contactData;
setTarget(photoView);
- return setCompressedImage(contactData.getPhotoBinaryData());
+ RawContact rawContact = contactData.getRawContacts().get(0);
+ final String accountType = rawContact.getAccountTypeString();
+ final String accountName = rawContact.getAccountName();
+ if (!TextUtils.isEmpty(accountType) && !TextUtils.isEmpty(accountName)) {
+ account = new Account(accountName, accountType);
+ }
+ return setCompressedImage(contactData.getPhotoBinaryData(),
+ photoView.getContext(), account);
}
public void setTransitionDuration(int durationInMillis) {
@@ -83,7 +94,7 @@ public class ImageViewDrawableSetter {
return mCompressed;
}
- protected Bitmap setCompressedImage(byte[] compressed) {
+ protected Bitmap setCompressedImage(byte[] compressed, Context c, Account account) {
if (mPreviousDrawable == null) {
// If we don't already have a drawable, skip the exit-early test
// below; otherwise we might not end up setting the default image.
@@ -97,10 +108,9 @@ public class ImageViewDrawableSetter {
return previousBitmap();
}
- Drawable newDrawable = decodedBitmapDrawable(compressed);
- if (newDrawable == null) {
- newDrawable = defaultDrawable();
- }
+ final Drawable newDrawable = (compressed == null)
+ ? defaultDrawable(c,account)
+ : decodedBitmapDrawable(compressed);
// Remember this for next time, so that we can check if it changed.
mCompressed = compressed;
@@ -140,7 +150,7 @@ public class ImageViewDrawableSetter {
* retrieve a default drawable for this contact. If not, then use the name as the contact
* identifier instead.
*/
- private Drawable defaultDrawable() {
+ private Drawable defaultDrawable(Context c, Account account) {
Resources resources = mTarget.getResources();
DefaultImageRequest request;
int contactType = ContactPhotoManager.TYPE_DEFAULT;
@@ -156,7 +166,8 @@ public class ImageViewDrawableSetter {
request = new DefaultImageRequest(mContact.getDisplayName(), mContact.getLookupKey(),
contactType, false /* isCircular */);
}
- return ContactPhotoManager.getDefaultAvatarDrawableForContact(resources, true, request);
+ return ContactPhotoManager.getDefaultAvatarDrawableForContact(
+ c, resources, true, request, account);
}
private BitmapDrawable decodedBitmapDrawable(byte[] compressed) {
diff --git a/src/com/android/contacts/widget/MultiShrinkScroller.java b/src/com/android/contacts/widget/MultiShrinkScroller.java
index 7c46a86c9..80d69d363 100644
--- a/src/com/android/contacts/widget/MultiShrinkScroller.java
+++ b/src/com/android/contacts/widget/MultiShrinkScroller.java
@@ -20,7 +20,9 @@ import android.graphics.ColorMatrixColorFilter;
import android.graphics.drawable.GradientDrawable;
import android.hardware.display.DisplayManager;
import android.os.Trace;
+import android.text.TextUtils;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.TypedValue;
import android.view.Display;
import android.view.Gravity;
@@ -108,6 +110,9 @@ public class MultiShrinkScroller extends FrameLayout {
private View mTransparentView;
private MultiShrinkScrollerListener mListener;
private TextView mLargeTextView;
+ private TextView mAttributionTextView;
+ private TextView mSpamCountTextView;
+ private View mContactInfo;
private View mPhotoTouchInterceptOverlay;
/** Contains desired size & vertical offset of the title, once the header is fully compressed */
private TextView mInvisiblePlaceholderTextView;
@@ -130,6 +135,7 @@ public class MultiShrinkScroller extends FrameLayout {
*/
private boolean mIsOpenContactSquare;
private int mMaximumHeaderTextSize;
+ private int mMaximumHeaderInfoSize;
private int mCollapsedTitleBottomMargin;
private int mCollapsedTitleStartMargin;
private int mMinimumPortraitHeaderHeight;
@@ -176,7 +182,7 @@ public class MultiShrinkScroller extends FrameLayout {
};
private final PathInterpolator mTextSizePathInterpolator
- = new PathInterpolator(0.16f, 0.4f, 0.2f, 1);
+ = new PathInterpolator(0.19f, 0.0f, 0.2f, 1);
private final int[] mGradientColors = new int[] {0,0x88000000};
private GradientDrawable mTitleGradientDrawable = new GradientDrawable(
@@ -288,7 +294,10 @@ public class MultiShrinkScroller extends FrameLayout {
mToolbar = findViewById(R.id.toolbar_parent);
mPhotoViewContainer = findViewById(R.id.toolbar_parent);
mTransparentView = findViewById(R.id.transparent_view);
+ mContactInfo = findViewById(R.id.contact_info);
mLargeTextView = (TextView) findViewById(R.id.large_title);
+ mAttributionTextView = (TextView) findViewById(R.id.contact_info_attribution);
+ mSpamCountTextView = (TextView) findViewById(R.id.contact_spam_count);
mInvisiblePlaceholderTextView = (TextView) findViewById(R.id.placeholder_textview);
mStartColumn = findViewById(R.id.empty_start_column);
// Touching the empty space should close the card
@@ -340,6 +349,7 @@ public class MultiShrinkScroller extends FrameLayout {
: mPhotoViewContainer.getWidth();
setHeaderHeight(getMaximumScrollableHeaderHeight());
mMaximumHeaderTextSize = mLargeTextView.getHeight();
+ mMaximumHeaderInfoSize = mContactInfo.getHeight();
if (mIsTwoPanel) {
mMaximumHeaderHeight = getHeight();
mMinimumHeaderHeight = mMaximumHeaderHeight;
@@ -353,8 +363,8 @@ public class MultiShrinkScroller extends FrameLayout {
mPhotoViewContainer.setLayoutParams(photoLayoutParams);
// Permanently set title width and margin.
- final FrameLayout.LayoutParams largeTextLayoutParams
- = (FrameLayout.LayoutParams) mLargeTextView.getLayoutParams();
+ final LinearLayout.LayoutParams largeTextLayoutParams
+ = (LinearLayout.LayoutParams) mLargeTextView.getLayoutParams();
largeTextLayoutParams.width = photoLayoutParams.width -
largeTextLayoutParams.leftMargin - largeTextLayoutParams.rightMargin;
largeTextLayoutParams.gravity = Gravity.BOTTOM | Gravity.START;
@@ -382,8 +392,8 @@ public class MultiShrinkScroller extends FrameLayout {
= (FrameLayout.LayoutParams) mTitleGradientView.getLayoutParams();
final float TITLE_GRADIENT_SIZE_COEFFICIENT = 1.25f;
final FrameLayout.LayoutParams largeTextLayoutParms
- = (FrameLayout.LayoutParams) mLargeTextView.getLayoutParams();
- titleGradientLayoutParams.height = (int) ((mLargeTextView.getHeight()
+ = (FrameLayout.LayoutParams) mContactInfo.getLayoutParams();
+ titleGradientLayoutParams.height = (int) ((mContactInfo.getHeight()
+ largeTextLayoutParms.bottomMargin) * TITLE_GRADIENT_SIZE_COEFFICIENT);
mTitleGradientView.setLayoutParams(titleGradientLayoutParams);
}
@@ -393,6 +403,24 @@ public class MultiShrinkScroller extends FrameLayout {
mPhotoTouchInterceptOverlay.setContentDescription(title);
}
+ public void setAttributionText(String attribution) {
+ if (!TextUtils.isEmpty(attribution)) {
+ mAttributionTextView.setText(attribution);
+ mAttributionTextView.setVisibility(View.VISIBLE);
+ } else {
+ mAttributionTextView.setVisibility(View.GONE);
+ }
+ }
+
+ public void setSpamCountText(String spamCount) {
+ if (!TextUtils.isEmpty(spamCount)) {
+ mSpamCountTextView.setText(spamCount);
+ mSpamCountTextView.setVisibility(View.VISIBLE);
+ } else {
+ mSpamCountTextView.setVisibility(View.GONE);
+ }
+ }
+
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mVelocityTracker == null) {
@@ -997,7 +1025,7 @@ public class MultiShrinkScroller extends FrameLayout {
} else {
mLargeTextView.setPivotX(0);
}
- mLargeTextView.setPivotY(mLargeTextView.getHeight() / 2);
+ mLargeTextView.setPivotY(mContactInfo.getHeight() / 2);
final int toolbarHeight = mToolbar.getLayoutParams().height;
mPhotoTouchInterceptOverlay.setClickable(toolbarHeight != mMaximumHeaderHeight);
@@ -1009,7 +1037,7 @@ public class MultiShrinkScroller extends FrameLayout {
setInterpolatedTitleMargins(1);
return;
}
-
+ mMaximumHeaderInfoSize = mContactInfo.getHeight();
final float ratio = (toolbarHeight - mMinimumHeaderHeight)
/ (float)(mMaximumHeaderHeight - mMinimumHeaderHeight);
final float minimumSize = mInvisiblePlaceholderTextView.getHeight();
@@ -1019,8 +1047,8 @@ public class MultiShrinkScroller extends FrameLayout {
// Clamp to reasonable/finite values before passing into framework. The values
// can be wacky before the first pre-render.
- bezierOutput = (float) Math.min(bezierOutput, 1.0f);
- scale = (float) Math.min(scale, 1.0f);
+ bezierOutput = Math.min(bezierOutput, 1.0f);
+ scale = Math.min(scale, 1.0f);
mLargeTextView.setScaleX(scale);
mLargeTextView.setScaleY(scale);
@@ -1051,7 +1079,7 @@ public class MultiShrinkScroller extends FrameLayout {
*/
private void setInterpolatedTitleMargins(float x) {
final FrameLayout.LayoutParams titleLayoutParams
- = (FrameLayout.LayoutParams) mLargeTextView.getLayoutParams();
+ = (FrameLayout.LayoutParams) mContactInfo.getLayoutParams();
final LinearLayout.LayoutParams toolbarLayoutParams
= (LinearLayout.LayoutParams) mToolbar.getLayoutParams();
@@ -1067,11 +1095,12 @@ public class MultiShrinkScroller extends FrameLayout {
// calling mLargeTextView.getHeight() use the mMaximumHeaderTextSize for this calculation.
// The getHeight() value acts unexpectedly when mLargeTextView is partially clipped by
// its parent.
- titleLayoutParams.topMargin = getTransparentViewHeight()
- + toolbarLayoutParams.height - pretendBottomMargin
- - mMaximumHeaderTextSize;
+ final int minHeaderInfoTopMargin = getTransparentViewHeight()
+ + toolbarLayoutParams.height - pretendBottomMargin - mMaximumHeaderInfoSize;
+ final int topMargin = Math.max(minHeaderInfoTopMargin, 0);
+ titleLayoutParams.topMargin = topMargin;
titleLayoutParams.bottomMargin = 0;
- mLargeTextView.setLayoutParams(titleLayoutParams);
+ mContactInfo.setLayoutParams(titleLayoutParams);
}
private void updatePhotoTintAndDropShadow() {
@@ -1148,7 +1177,11 @@ public class MultiShrinkScroller extends FrameLayout {
mPhotoView.setTint(mHeaderTintColor);
mTitleGradientDrawable.setAlpha(gradientAlpha);
mActionBarGradientDrawable.setAlpha(gradientAlpha);
-
+ final int attributionAlpha = calculateAttributionTextAlpha(toolbarHeight);
+ mAttributionTextView
+ .setTextColor(mAttributionTextView.getTextColors().withAlpha(attributionAlpha));
+ mSpamCountTextView
+ .setTextColor(mSpamCountTextView.getTextColors().withAlpha(attributionAlpha));
Trace.endSection();
}
@@ -1167,6 +1200,16 @@ public class MultiShrinkScroller extends FrameLayout {
return (intermediateHeight - height) / interpolatingHeightRange;
}
+ private int calculateAttributionTextAlpha(int height) {
+ final float ratio = calculateHeightRatioToBlendingStartHeight(height);
+ final float alpha = 1.0f - (float) Math.min(Math.pow(ratio, 1.5f) * 2f, 1f);
+ final float tint = (float) Math.min(Math.pow(ratio, 1.5f) * 3f, 1f);
+ mColorMatrix.setSaturation(alpha);
+ mColorMatrix.postConcat(alphaMatrix(alpha, Color.WHITE));
+ mColorMatrix.postConcat(multiplyBlendMatrix(mHeaderTintColor, tint));
+ return (int) (255 * alpha);
+ }
+
/**
* Simulates alpha blending an image with {@param color}.
*/