| /* |
| * 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; |
| } |
| } |
| } |