| /* | |
| * Copyright (C) 2009 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| package com.example.android.contactmanager; | |
| import android.accounts.Account; | |
| import android.accounts.AccountManager; | |
| import android.accounts.AuthenticatorDescription; | |
| import android.accounts.OnAccountsUpdateListener; | |
| import android.app.Activity; | |
| import android.content.ContentProviderOperation; | |
| import android.content.Context; | |
| import android.content.pm.PackageManager; | |
| import android.graphics.drawable.Drawable; | |
| import android.os.Bundle; | |
| import android.provider.ContactsContract; | |
| import android.util.Log; | |
| import android.view.LayoutInflater; | |
| import android.view.View; | |
| import android.view.ViewGroup; | |
| import android.widget.AdapterView; | |
| import android.widget.ArrayAdapter; | |
| import android.widget.Button; | |
| import android.widget.EditText; | |
| import android.widget.ImageView; | |
| import android.widget.Spinner; | |
| import android.widget.TextView; | |
| import android.widget.Toast; | |
| import android.widget.AdapterView.OnItemSelectedListener; | |
| import java.util.ArrayList; | |
| import java.util.Iterator; | |
| public final class ContactAdder extends Activity implements OnAccountsUpdateListener | |
| { | |
| public static final String TAG = "ContactsAdder"; | |
| public static final String ACCOUNT_NAME = | |
| "com.example.android.contactmanager.ContactsAdder.ACCOUNT_NAME"; | |
| public static final String ACCOUNT_TYPE = | |
| "com.example.android.contactmanager.ContactsAdder.ACCOUNT_TYPE"; | |
| private ArrayList<AccountData> mAccounts; | |
| private AccountAdapter mAccountAdapter; | |
| private Spinner mAccountSpinner; | |
| private EditText mContactEmailEditText; | |
| private ArrayList<Integer> mContactEmailTypes; | |
| private Spinner mContactEmailTypeSpinner; | |
| private EditText mContactNameEditText; | |
| private EditText mContactPhoneEditText; | |
| private ArrayList<Integer> mContactPhoneTypes; | |
| private Spinner mContactPhoneTypeSpinner; | |
| private Button mContactSaveButton; | |
| private AccountData mSelectedAccount; | |
| /** | |
| * Called when the activity is first created. Responsible for initializing the UI. | |
| */ | |
| @Override | |
| public void onCreate(Bundle savedInstanceState) | |
| { | |
| Log.v(TAG, "Activity State: onCreate()"); | |
| super.onCreate(savedInstanceState); | |
| setContentView(R.layout.contact_adder); | |
| // Obtain handles to UI objects | |
| mAccountSpinner = (Spinner) findViewById(R.id.accountSpinner); | |
| mContactNameEditText = (EditText) findViewById(R.id.contactNameEditText); | |
| mContactPhoneEditText = (EditText) findViewById(R.id.contactPhoneEditText); | |
| mContactEmailEditText = (EditText) findViewById(R.id.contactEmailEditText); | |
| mContactPhoneTypeSpinner = (Spinner) findViewById(R.id.contactPhoneTypeSpinner); | |
| mContactEmailTypeSpinner = (Spinner) findViewById(R.id.contactEmailTypeSpinner); | |
| mContactSaveButton = (Button) findViewById(R.id.contactSaveButton); | |
| // Prepare list of supported account types | |
| // Note: Other types are available in ContactsContract.CommonDataKinds | |
| // Also, be aware that type IDs differ between Phone and Email, and MUST be computed | |
| // separately. | |
| mContactPhoneTypes = new ArrayList<Integer>(); | |
| mContactPhoneTypes.add(ContactsContract.CommonDataKinds.Phone.TYPE_HOME); | |
| mContactPhoneTypes.add(ContactsContract.CommonDataKinds.Phone.TYPE_WORK); | |
| mContactPhoneTypes.add(ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE); | |
| mContactPhoneTypes.add(ContactsContract.CommonDataKinds.Phone.TYPE_OTHER); | |
| mContactEmailTypes = new ArrayList<Integer>(); | |
| mContactEmailTypes.add(ContactsContract.CommonDataKinds.Email.TYPE_HOME); | |
| mContactEmailTypes.add(ContactsContract.CommonDataKinds.Email.TYPE_WORK); | |
| mContactEmailTypes.add(ContactsContract.CommonDataKinds.Email.TYPE_MOBILE); | |
| mContactEmailTypes.add(ContactsContract.CommonDataKinds.Email.TYPE_OTHER); | |
| // Prepare model for account spinner | |
| mAccounts = new ArrayList<AccountData>(); | |
| mAccountAdapter = new AccountAdapter(this, mAccounts); | |
| mAccountSpinner.setAdapter(mAccountAdapter); | |
| // Populate list of account types for phone | |
| ArrayAdapter<String> adapter; | |
| adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item); | |
| adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); | |
| Iterator<Integer> iter; | |
| iter = mContactPhoneTypes.iterator(); | |
| while (iter.hasNext()) { | |
| adapter.add(ContactsContract.CommonDataKinds.Phone.getTypeLabel( | |
| this.getResources(), | |
| iter.next(), | |
| getString(R.string.undefinedTypeLabel)).toString()); | |
| } | |
| mContactPhoneTypeSpinner.setAdapter(adapter); | |
| mContactPhoneTypeSpinner.setPrompt(getString(R.string.selectLabel)); | |
| // Populate list of account types for email | |
| adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item); | |
| adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); | |
| iter = mContactEmailTypes.iterator(); | |
| while (iter.hasNext()) { | |
| adapter.add(ContactsContract.CommonDataKinds.Email.getTypeLabel( | |
| this.getResources(), | |
| iter.next(), | |
| getString(R.string.undefinedTypeLabel)).toString()); | |
| } | |
| mContactEmailTypeSpinner.setAdapter(adapter); | |
| mContactEmailTypeSpinner.setPrompt(getString(R.string.selectLabel)); | |
| // Prepare the system account manager. On registering the listener below, we also ask for | |
| // an initial callback to pre-populate the account list. | |
| AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true); | |
| // Register handlers for UI elements | |
| mAccountSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { | |
| public void onItemSelected(AdapterView<?> parent, View view, int position, long i) { | |
| updateAccountSelection(); | |
| } | |
| public void onNothingSelected(AdapterView<?> parent) { | |
| // We don't need to worry about nothing being selected, since Spinners don't allow | |
| // this. | |
| } | |
| }); | |
| mContactSaveButton.setOnClickListener(new View.OnClickListener() { | |
| public void onClick(View v) { | |
| onSaveButtonClicked(); | |
| } | |
| }); | |
| } | |
| /** | |
| * Actions for when the Save button is clicked. Creates a contact entry and terminates the | |
| * activity. | |
| */ | |
| private void onSaveButtonClicked() { | |
| Log.v(TAG, "Save button clicked"); | |
| createContactEntry(); | |
| finish(); | |
| } | |
| /** | |
| * Creates a contact entry from the current UI values in the account named by mSelectedAccount. | |
| */ | |
| protected void createContactEntry() { | |
| // Get values from UI | |
| String name = mContactNameEditText.getText().toString(); | |
| String phone = mContactPhoneEditText.getText().toString(); | |
| String email = mContactEmailEditText.getText().toString(); | |
| int phoneType = mContactPhoneTypes.get( | |
| mContactPhoneTypeSpinner.getSelectedItemPosition()); | |
| int emailType = mContactEmailTypes.get( | |
| mContactEmailTypeSpinner.getSelectedItemPosition());; | |
| // Prepare contact creation request | |
| // | |
| // Note: We use RawContacts because this data must be associated with a particular account. | |
| // The system will aggregate this with any other data for this contact and create a | |
| // coresponding entry in the ContactsContract.Contacts provider for us. | |
| ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); | |
| ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) | |
| .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType()) | |
| .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName()) | |
| .build()); | |
| ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) | |
| .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) | |
| .withValue(ContactsContract.Data.MIMETYPE, | |
| ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) | |
| .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name) | |
| .build()); | |
| ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) | |
| .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) | |
| .withValue(ContactsContract.Data.MIMETYPE, | |
| ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) | |
| .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone) | |
| .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType) | |
| .build()); | |
| ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) | |
| .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) | |
| .withValue(ContactsContract.Data.MIMETYPE, | |
| ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) | |
| .withValue(ContactsContract.CommonDataKinds.Email.DATA, email) | |
| .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType) | |
| .build()); | |
| // Ask the Contact provider to create a new contact | |
| Log.i(TAG,"Selected account: " + mSelectedAccount.getName() + " (" + | |
| mSelectedAccount.getType() + ")"); | |
| Log.i(TAG,"Creating contact: " + name); | |
| try { | |
| getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); | |
| } catch (Exception e) { | |
| // Display warning | |
| Context ctx = getApplicationContext(); | |
| CharSequence txt = getString(R.string.contactCreationFailure); | |
| int duration = Toast.LENGTH_SHORT; | |
| Toast toast = Toast.makeText(ctx, txt, duration); | |
| toast.show(); | |
| // Log exception | |
| Log.e(TAG, "Exceptoin encoutered while inserting contact: " + e); | |
| } | |
| } | |
| /** | |
| * Called when this activity is about to be destroyed by the system. | |
| */ | |
| @Override | |
| public void onDestroy() { | |
| // Remove AccountManager callback | |
| AccountManager.get(this).removeOnAccountsUpdatedListener(this); | |
| super.onDestroy(); | |
| } | |
| /** | |
| * Updates account list spinner when the list of Accounts on the system changes. Satisfies | |
| * OnAccountsUpdateListener implementation. | |
| */ | |
| public void onAccountsUpdated(Account[] a) { | |
| Log.i(TAG, "Account list update detected"); | |
| // Clear out any old data to prevent duplicates | |
| mAccounts.clear(); | |
| // Get account data from system | |
| AuthenticatorDescription[] accountTypes = AccountManager.get(this).getAuthenticatorTypes(); | |
| // Populate tables | |
| for (int i = 0; i < a.length; i++) { | |
| // The user may have multiple accounts with the same name, so we need to construct a | |
| // meaningful display name for each. | |
| String systemAccountType = a[i].type; | |
| AuthenticatorDescription ad = getAuthenticatorDescription(systemAccountType, | |
| accountTypes); | |
| AccountData data = new AccountData(a[i].name, ad); | |
| mAccounts.add(data); | |
| } | |
| // Update the account spinner | |
| mAccountAdapter.notifyDataSetChanged(); | |
| } | |
| /** | |
| * Obtain the AuthenticatorDescription for a given account type. | |
| * @param type The account type to locate. | |
| * @param dictionary An array of AuthenticatorDescriptions, as returned by AccountManager. | |
| * @return The description for the specified account type. | |
| */ | |
| private static AuthenticatorDescription getAuthenticatorDescription(String type, | |
| AuthenticatorDescription[] dictionary) { | |
| for (int i = 0; i < dictionary.length; i++) { | |
| if (dictionary[i].type.equals(type)) { | |
| return dictionary[i]; | |
| } | |
| } | |
| // No match found | |
| throw new RuntimeException("Unable to find matching authenticator"); | |
| } | |
| /** | |
| * Update account selection. If NO_ACCOUNT is selected, then we prohibit inserting new contacts. | |
| */ | |
| private void updateAccountSelection() { | |
| // Read current account selection | |
| mSelectedAccount = (AccountData) mAccountSpinner.getSelectedItem(); | |
| } | |
| /** | |
| * A container class used to repreresent all known information about an account. | |
| */ | |
| private class AccountData { | |
| private String mName; | |
| private String mType; | |
| private CharSequence mTypeLabel; | |
| private Drawable mIcon; | |
| /** | |
| * @param name The name of the account. This is usually the user's email address or | |
| * username. | |
| * @param description The description for this account. This will be dictated by the | |
| * type of account returned, and can be obtained from the system AccountManager. | |
| */ | |
| public AccountData(String name, AuthenticatorDescription description) { | |
| mName = name; | |
| if (description != null) { | |
| mType = description.type; | |
| // The type string is stored in a resource, so we need to convert it into something | |
| // human readable. | |
| String packageName = description.packageName; | |
| PackageManager pm = getPackageManager(); | |
| if (description.labelId != 0) { | |
| mTypeLabel = pm.getText(packageName, description.labelId, null); | |
| if (mTypeLabel == null) { | |
| throw new IllegalArgumentException("LabelID provided, but label not found"); | |
| } | |
| } else { | |
| mTypeLabel = ""; | |
| } | |
| if (description.iconId != 0) { | |
| mIcon = pm.getDrawable(packageName, description.iconId, null); | |
| if (mIcon == null) { | |
| throw new IllegalArgumentException("IconID provided, but drawable not " + | |
| "found"); | |
| } | |
| } else { | |
| mIcon = getResources().getDrawable(android.R.drawable.sym_def_app_icon); | |
| } | |
| } | |
| } | |
| public String getName() { | |
| return mName; | |
| } | |
| public String getType() { | |
| return mType; | |
| } | |
| public CharSequence getTypeLabel() { | |
| return mTypeLabel; | |
| } | |
| public Drawable getIcon() { | |
| return mIcon; | |
| } | |
| public String toString() { | |
| return mName; | |
| } | |
| } | |
| /** | |
| * Custom adapter used to display account icons and descriptions in the account spinner. | |
| */ | |
| private class AccountAdapter extends ArrayAdapter<AccountData> { | |
| public AccountAdapter(Context context, ArrayList<AccountData> accountData) { | |
| super(context, android.R.layout.simple_spinner_item, accountData); | |
| setDropDownViewResource(R.layout.account_entry); | |
| } | |
| public View getDropDownView(int position, View convertView, ViewGroup parent) { | |
| // Inflate a view template | |
| if (convertView == null) { | |
| LayoutInflater layoutInflater = getLayoutInflater(); | |
| convertView = layoutInflater.inflate(R.layout.account_entry, parent, false); | |
| } | |
| TextView firstAccountLine = (TextView) convertView.findViewById(R.id.firstAccountLine); | |
| TextView secondAccountLine = (TextView) convertView.findViewById(R.id.secondAccountLine); | |
| ImageView accountIcon = (ImageView) convertView.findViewById(R.id.accountIcon); | |
| // Populate template | |
| AccountData data = getItem(position); | |
| firstAccountLine.setText(data.getName()); | |
| secondAccountLine.setText(data.getTypeLabel()); | |
| Drawable icon = data.getIcon(); | |
| if (icon == null) { | |
| icon = getResources().getDrawable(android.R.drawable.ic_menu_search); | |
| } | |
| accountIcon.setImageDrawable(icon); | |
| return convertView; | |
| } | |
| } | |
| } |