blob: b5541666b87fc974fc3240819dd0bdfd643952e5 [file] [log] [blame]
/*
* Copyright (C) 2010 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;
import com.android.contacts.list.MultiplePhoneExtraAdapter;
import com.android.contacts.list.MultiplePhonePickerAdapter;
import com.android.contacts.list.MultiplePhonePickerItemView;
import com.android.contacts.list.MultiplePhoneSelection;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Intent;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
import android.provider.ContactsContract.Intents;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.text.TextUtils;
import android.util.SparseIntArray;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewStub;
import android.view.View.OnClickListener;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Displays of phone numbers and allows selection of multiple numbers.
*/
public class MultiplePhonePickerActivity extends ContactsListActivity implements OnClickListener {
/**
* User selected phone number and id in MODE_PICK_MULTIPLE_PHONES mode.
*/
public final MultiplePhoneSelection mUserSelection = new MultiplePhoneSelection(this);
/**
* The adapter for the phone numbers, used in MODE_PICK_MULTIPLE_PHONES mode.
*/
public final MultiplePhoneExtraAdapter mPhoneNumberAdapter =
new MultiplePhoneExtraAdapter(this, this, mUserSelection);
private static int[] CHIP_COLOR_ARRAY = {
R.drawable.appointment_indicator_leftside_1,
R.drawable.appointment_indicator_leftside_2,
R.drawable.appointment_indicator_leftside_3,
R.drawable.appointment_indicator_leftside_4,
R.drawable.appointment_indicator_leftside_5,
R.drawable.appointment_indicator_leftside_6,
R.drawable.appointment_indicator_leftside_7,
R.drawable.appointment_indicator_leftside_8,
R.drawable.appointment_indicator_leftside_9,
R.drawable.appointment_indicator_leftside_10,
R.drawable.appointment_indicator_leftside_11,
R.drawable.appointment_indicator_leftside_12,
R.drawable.appointment_indicator_leftside_13,
R.drawable.appointment_indicator_leftside_14,
R.drawable.appointment_indicator_leftside_15,
R.drawable.appointment_indicator_leftside_16,
R.drawable.appointment_indicator_leftside_17,
R.drawable.appointment_indicator_leftside_18,
R.drawable.appointment_indicator_leftside_19,
R.drawable.appointment_indicator_leftside_20,
R.drawable.appointment_indicator_leftside_21,
};
/**
* This is the map from contact to color index.
* A colored chip in MODE_PICK_MULTIPLE_PHONES mode is used to indicate the number of phone
* numbers belong to one contact
*/
SparseIntArray mContactColor = new SparseIntArray();
/**
* UI control of action panel in MODE_PICK_MULTIPLE_PHONES mode.
*/
private View mFooterView;
/**
* Display only selected recipients or not in MODE_PICK_MULTIPLE_PHONES mode
*/
public boolean mShowSelectedOnly = false;
public OnClickListener mCheckBoxClickerListener = new OnClickListener () {
public void onClick(View v) {
final MultiplePhonePickerItemView itemView =
(MultiplePhonePickerItemView) v.getParent();
if (itemView.phoneId != MultiplePhoneExtraAdapter.INVALID_PHONE_ID) {
mUserSelection.setPhoneSelected(itemView.phoneId, ((CheckBox) v).isChecked());
} else {
mUserSelection.setPhoneSelected(itemView.phoneNumber,
((CheckBox) v).isChecked());
}
updateWidgets(true);
}
};
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
initMultiPicker(getIntent());
}
@Override
protected void onResume() {
super.onResume();
// TODO move this to onAttach of the corresponding fragment
ListView listView = (ListView)findViewById(android.R.id.list);
((MultiplePhonePickerAdapter)listView.getAdapter()).setExtraAdapter(mPhoneNumberAdapter);
ViewStub stub = (ViewStub)findViewById(R.id.footer_stub);
if (stub != null) {
View stubView = stub.inflate();
mFooterView = stubView.findViewById(R.id.footer);
mFooterView.setVisibility(View.GONE);
Button doneButton = (Button) stubView.findViewById(R.id.done);
doneButton.setOnClickListener(this);
Button revertButton = (Button) stubView.findViewById(R.id.revert);
revertButton.setOnClickListener(this);
}
}
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.done:
setMultiPickerResult();
finish();
break;
case R.id.revert:
finish();
break;
}
}
@Override
protected void onSaveInstanceState(Bundle icicle) {
super.onSaveInstanceState(icicle);
ListView listView = (ListView)findViewById(android.R.id.list);
if (listView != null) {
if (mUserSelection != null) {
mUserSelection.saveInstanceState(icicle);
}
}
}
@Override
protected void onRestoreInstanceState(Bundle icicle) {
super.onRestoreInstanceState(icicle);
// Retrieve list state. This will be applied after the QueryHandler has run
mUserSelection.restoreInstanceState(icicle);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
final MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.pick, menu);
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
if (mShowSelectedOnly) {
menu.findItem(R.id.menu_display_selected).setVisible(false);
menu.findItem(R.id.menu_display_all).setVisible(true);
menu.findItem(R.id.menu_select_all).setVisible(false);
menu.findItem(R.id.menu_select_none).setVisible(false);
return true;
}
menu.findItem(R.id.menu_display_all).setVisible(false);
menu.findItem(R.id.menu_display_selected).setVisible(true);
if (mUserSelection.isAllSelected()) {
menu.findItem(R.id.menu_select_all).setVisible(false);
menu.findItem(R.id.menu_select_none).setVisible(true);
} else {
menu.findItem(R.id.menu_select_all).setVisible(true);
menu.findItem(R.id.menu_select_none).setVisible(false);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_select_all: {
mUserSelection.setAllPhonesSelected(true);
checkAll(true);
updateWidgets(true);
return true;
}
case R.id.menu_select_none: {
mUserSelection.setAllPhonesSelected(false);
checkAll(false);
updateWidgets(true);
return true;
}
case R.id.menu_display_selected: {
mShowSelectedOnly = true;
startQuery();
return true;
}
case R.id.menu_display_all: {
mShowSelectedOnly = false;
startQuery();
return true;
}
}
return super.onOptionsItemSelected(item);
}
@Override
public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
boolean globalSearch) {
// TODO
// if (mProviderStatus != ProviderStatus.STATUS_NORMAL) {
// return;
// }
if (globalSearch) {
super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
} else {
if (!mSearchMode && (mMode & MODE_MASK_NO_FILTER) == 0) {
if ((mMode & MODE_MASK_PICKER) != 0) {
Bundle extras = getIntent().getExtras();
if (extras == null) {
extras = new Bundle();
}
mUserSelection.fillSelectionForSearchMode(extras);
ContactsSearchManager.startSearchForResult(this, initialQuery,
SUBACTIVITY_FILTER, extras);
} else {
ContactsSearchManager.startSearch(this, initialQuery);
}
}
}
}
@Override
public void onBackPressed() {
setMultiPickerResult();
super.onBackPressed();
}
@Override
protected void startQuery(Uri uri, String[] projection) {
// Filter unknown phone numbers first.
mPhoneNumberAdapter.doFilter(null, mShowSelectedOnly);
if (mShowSelectedOnly) {
StringBuilder idSetBuilder = new StringBuilder();
Iterator<Long> itr = mUserSelection.getSelectedPhonIds();
if (itr.hasNext()) {
idSetBuilder.append(Long.toString(itr.next()));
}
while (itr.hasNext()) {
idSetBuilder.append(',');
idSetBuilder.append(Long.toString(itr.next()));
}
String whereClause = Phone._ID + " IN (" + idSetBuilder.toString() + ")";
mQueryHandler.startQuery(QUERY_TOKEN, null, uri, projection, whereClause, null,
getSortOrder(projection));
} else {
mQueryHandler.startQuery(QUERY_TOKEN, null, uri,
projection, CLAUSE_ONLY_VISIBLE, null, getSortOrder(projection));
}
}
@Override
public Cursor doFilter(String filter) {
String[] projection = getProjectionForQuery();
if (mSearchMode && TextUtils.isEmpty(mListFragment.getQueryString())) {
return new MatrixCursor(projection);
}
final ContentResolver resolver = getContentResolver();
// Filter phone numbers as well.
mPhoneNumberAdapter.doFilter(filter, mShowSelectedOnly);
Uri uri = getUriToQuery();
if (!TextUtils.isEmpty(filter)) {
uri = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, Uri.encode(filter));
}
return resolver.query(uri, projection, CLAUSE_ONLY_VISIBLE, null, getSortOrder(projection));
}
private void initMultiPicker(final Intent intent) {
final Handler handler = new Handler();
// TODO : Shall we still show the progressDialog in search mode.
final ProgressDialog progressDialog = new ProgressDialog(this);
progressDialog.setMessage(getText(R.string.adding_recipients));
progressDialog.setIndeterminate(true);
progressDialog.setCancelable(false);
final Runnable showProgress = new Runnable() {
public void run() {
progressDialog.show();
}
};
handler.postDelayed(showProgress, 1);
new Thread(new Runnable() {
public void run() {
try {
loadSelectionFromIntent(intent);
} finally {
handler.removeCallbacks(showProgress);
progressDialog.dismiss();
}
final Runnable populateWorker = new Runnable() {
public void run() {
if (mAdapter != null) {
mAdapter.notifyDataSetChanged();
}
updateWidgets(false);
}
};
handler.post(populateWorker);
}
}).start();
}
private void getPhoneNumbersOrIdsFromURIs(final Parcelable[] uris,
final List<String> phoneNumbers, final List<Long> phoneIds) {
if (uris != null) {
for (Parcelable paracelable : uris) {
Uri uri = (Uri) paracelable;
if (uri == null) continue;
String scheme = uri.getScheme();
if (phoneNumbers != null && "tel".equals(scheme)) {
phoneNumbers.add(uri.getSchemeSpecificPart());
} else if (phoneIds != null && "content".equals(scheme)) {
phoneIds.add(ContentUris.parseId(uri));
}
}
}
}
private void loadSelectionFromIntent(Intent intent) {
Parcelable[] uris = intent.getParcelableArrayExtra(Intents.EXTRA_PHONE_URIS);
ArrayList<String> phoneNumbers = new ArrayList<String>();
ArrayList<Long> phoneIds = new ArrayList<Long>();
ArrayList<String> selectedPhoneNumbers = null;
if (mSearchMode) {
// All selection will be read from EXTRA_SELECTION
getPhoneNumbersOrIdsFromURIs(uris, phoneNumbers, null);
uris = intent.getParcelableArrayExtra(MultiplePhoneSelection.EXTRA_SELECTION);
if (uris != null) {
selectedPhoneNumbers = new ArrayList<String>();
getPhoneNumbersOrIdsFromURIs(uris, selectedPhoneNumbers, phoneIds);
}
} else {
getPhoneNumbersOrIdsFromURIs(uris, phoneNumbers, phoneIds);
selectedPhoneNumbers = phoneNumbers;
}
mPhoneNumberAdapter.setPhoneNumbers(phoneNumbers);
mUserSelection.setSelection(selectedPhoneNumbers, phoneIds);
}
private void setMultiPickerResult() {
setResult(RESULT_OK, mUserSelection.createSelectionIntent());
}
/**
* Go through the cursor and assign the chip color to contact who has more than one phone
* numbers.
* Assume the cursor is sorted by CONTACT_ID.
*/
public void updateChipColor(Cursor cursor) {
if (cursor == null || cursor.getCount() == 0) {
return;
}
mContactColor.clear();
int backupPos = cursor.getPosition();
cursor.moveToFirst();
int color = 0;
long prevContactId = cursor.getLong(PHONE_CONTACT_ID_COLUMN_INDEX);
while (cursor.moveToNext()) {
long contactId = cursor.getLong(PHONE_CONTACT_ID_COLUMN_INDEX);
if (prevContactId == contactId) {
if (mContactColor.indexOfKey(Long.valueOf(contactId).hashCode()) < 0) {
mContactColor.put(Long.valueOf(contactId).hashCode(), CHIP_COLOR_ARRAY[color]);
color++;
if (color >= CHIP_COLOR_ARRAY.length) {
color = 0;
}
}
}
prevContactId = contactId;
}
cursor.moveToPosition(backupPos);
}
/**
* Get assigned chip color resource id for a given contact, 0 is returned if there is no mapped
* resource.
*/
public int getChipColor(long contactId) {
return mContactColor.get(Long.valueOf(contactId).hashCode());
}
private void updateWidgets(boolean changed) {
int selected = mUserSelection.selectedCount();
if (selected >= 1) {
final String format =
getResources().getQuantityString(R.plurals.multiple_picker_title, selected);
setTitle(String.format(format, selected));
} else {
setTitle(getString(R.string.contactsList));
}
if (changed && mFooterView.getVisibility() == View.GONE) {
mFooterView.setVisibility(View.VISIBLE);
mFooterView.startAnimation(AnimationUtils.loadAnimation(this, R.anim.footer_appear));
}
}
private void checkAll(boolean checked) {
// TODO fix this. It should iterate over the cursor rather than the views in the list.
/*
final ListView listView = getListView();
int childCount = listView.getChildCount();
for (int i = 0; i < childCount; i++) {
final ContactListItemView child = (ContactListItemView)listView.getChildAt(i);
child.getCheckBoxView().setChecked(checked);
}
*/
}
}