diff options
| author | Jean-Baptiste Queru <jbq@google.com> | 2009-11-12 18:45:17 -0800 |
|---|---|---|
| committer | Jean-Baptiste Queru <jbq@google.com> | 2009-11-13 15:09:24 -0800 |
| commit | 2c8ead32c7ebf08db568e2e9d5dbb3738ac69797 (patch) | |
| tree | a4b9a0b6b9a125c492e6584d48a15b3b62b327d7 /samples/BusinessCard/src | |
| parent | 50992e805e758e2231f28ec2127b57a1a9fd0ddc (diff) | |
| download | android_development-2c8ead32c7ebf08db568e2e9d5dbb3738ac69797.tar.gz android_development-2c8ead32c7ebf08db568e2e9d5dbb3738ac69797.tar.bz2 android_development-2c8ead32c7ebf08db568e2e9d5dbb3738ac69797.zip | |
eclair snapshot
Diffstat (limited to 'samples/BusinessCard/src')
5 files changed, 425 insertions, 0 deletions
diff --git a/samples/BusinessCard/src/com/example/android/businesscard/BusinessCardActivity.java b/samples/BusinessCard/src/com/example/android/businesscard/BusinessCardActivity.java new file mode 100644 index 000000000..d8d277e24 --- /dev/null +++ b/samples/BusinessCard/src/com/example/android/businesscard/BusinessCardActivity.java @@ -0,0 +1,125 @@ +/* + * 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.businesscard; + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.TextView; + +/** + * A simple activity that shows a "Pick Contact" button and two fields: contact's name + * and phone number. The user taps on the Pick Contact button to bring up + * the contact chooser. Once this activity receives the result from contact picker, + * it launches an asynchronous query (queries should always be asynchronous) to load + * contact's name and phone number. When the query completes, the activity displays + * the loaded data. + */ +public class BusinessCardActivity extends Activity { + + // Request code for the contact picker activity + private static final int PICK_CONTACT_REQUEST = 1; + + /** + * An SDK-specific instance of {@link ContactAccessor}. The activity does not need + * to know what SDK it is running in: all idiosyncrasies of different SDKs are + * encapsulated in the implementations of the ContactAccessor class. + */ + private final ContactAccessor mContactAccessor = ContactAccessor.getInstance(); + + /** + * Called with the activity is first created. + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.business_card); + + // Install a click handler on the Pick Contact button + Button pickContact = (Button)findViewById(R.id.pick_contact_button); + pickContact.setOnClickListener(new OnClickListener() { + + public void onClick(View v) { + pickContact(); + } + }); + } + + /** + * Click handler for the Pick Contact button. Invokes a contact picker activity. + * The specific intent used to bring up that activity differs between versions + * of the SDK, which is why we delegate the creation of the intent to ContactAccessor. + */ + protected void pickContact() { + startActivityForResult(mContactAccessor.getPickContactIntent(), PICK_CONTACT_REQUEST); + } + + /** + * Invoked when the contact picker activity is finished. The {@code contactUri} parameter + * will contain a reference to the contact selected by the user. We will treat it as + * an opaque URI and allow the SDK-specific ContactAccessor to handle the URI accordingly. + */ + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == PICK_CONTACT_REQUEST && resultCode == RESULT_OK) { + loadContactInfo(data.getData()); + } + } + + /** + * Load contact information on a background thread. + */ + private void loadContactInfo(Uri contactUri) { + + /* + * We should always run database queries on a background thread. The database may be + * locked by some process for a long time. If we locked up the UI thread while waiting + * for the query to come back, we might get an "Application Not Responding" dialog. + */ + AsyncTask<Uri, Void, ContactInfo> task = new AsyncTask<Uri, Void, ContactInfo>() { + + @Override + protected ContactInfo doInBackground(Uri... uris) { + return mContactAccessor.loadContact(getContentResolver(), uris[0]); + } + + @Override + protected void onPostExecute(ContactInfo result) { + bindView(result); + } + }; + + task.execute(contactUri); + } + + /** + * Displays contact information: name and phone number. + */ + protected void bindView(ContactInfo contactInfo) { + TextView displayNameView = (TextView) findViewById(R.id.display_name_text_view); + displayNameView.setText(contactInfo.getDisplayName()); + + TextView phoneNumberView = (TextView) findViewById(R.id.phone_number_text_view); + phoneNumberView.setText(contactInfo.getPhoneNumber()); + } +} diff --git a/samples/BusinessCard/src/com/example/android/businesscard/ContactAccessor.java b/samples/BusinessCard/src/com/example/android/businesscard/ContactAccessor.java new file mode 100644 index 000000000..6a402e9c6 --- /dev/null +++ b/samples/BusinessCard/src/com/example/android/businesscard/ContactAccessor.java @@ -0,0 +1,89 @@ +/* + * 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.businesscard; + +import android.content.ContentResolver; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; + +/** + * This abstract class defines SDK-independent API for communication with + * Contacts Provider. The actual implementation used by the application depends + * on the level of API available on the device. If the API level is Cupcake or + * Donut, we want to use the {@link ContactAccessorSdk3_4} class. If it is + * Eclair or higher, we want to use {@link ContactAccessorSdk5}. + */ +public abstract class ContactAccessor { + + /** + * Static singleton instance of {@link ContactAccessor} holding the + * SDK-specific implementation of the class. + */ + private static ContactAccessor sInstance; + + public static ContactAccessor getInstance() { + if (sInstance == null) { + String className; + + /* + * Check the version of the SDK we are running on. Choose an + * implementation class designed for that version of the SDK. + * + * Unfortunately we have to use strings to represent the class + * names. If we used the conventional ContactAccessorSdk5.class.getName() + * syntax, we would get a ClassNotFoundException at runtime on pre-Eclair SDKs. + * Using the above syntax would force Dalvik to load the class and try to + * resolve references to all other classes it uses. Since the pre-Eclair + * does not have those classes, the loading of ContactAccessorSdk5 would fail. + */ + @SuppressWarnings("deprecation") + int sdkVersion = Integer.parseInt(Build.VERSION.SDK); // Cupcake style + if (sdkVersion < Build.VERSION_CODES.ECLAIR) { + className = "com.example.android.businesscard.ContactAccessorSdk3_4"; + } else { + className = "com.example.android.businesscard.ContactAccessorSdk5"; + } + + /* + * Find the required class by name and instantiate it. + */ + try { + Class<? extends ContactAccessor> clazz = + Class.forName(className).asSubclass(ContactAccessor.class); + sInstance = clazz.newInstance(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + return sInstance; + } + + /** + * Returns the {@link Intent#ACTION_PICK} intent configured for the right authority: legacy + * or current. + */ + public abstract Intent getPickContactIntent(); + + /** + * Loads contact data for the supplied URI. The actual queries will differ for different APIs + * used, but the result is the same: the {@link #mDisplayName} and {@link #mPhoneNumber} + * fields are populated with correct data. + */ + public abstract ContactInfo loadContact(ContentResolver contentResolver, Uri contactUri); +} diff --git a/samples/BusinessCard/src/com/example/android/businesscard/ContactAccessorSdk3_4.java b/samples/BusinessCard/src/com/example/android/businesscard/ContactAccessorSdk3_4.java new file mode 100644 index 000000000..7fcd3886e --- /dev/null +++ b/samples/BusinessCard/src/com/example/android/businesscard/ContactAccessorSdk3_4.java @@ -0,0 +1,81 @@ +/* + * 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.businesscard; + +import android.content.AsyncQueryHandler; +import android.content.ContentResolver; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.provider.Contacts.People; +import android.provider.Contacts.People.Phones; + +/** + * An implementation of {@link ContactAccessor} that uses legacy Contacts API. + * These APIs are deprecated and should not be used unless we are running on a + * pre-Eclair SDK. + * <p> + * There are several reasons why we wouldn't want to use this class on an Eclair device: + * <ul> + * <li>It would see at most one account, namely the first Google account created on the device. + * <li>It would work through a compatibility layer, which would make it inherently less efficient. + * <li>Not relevant to this particular example, but it would not have access to new kinds + * of data available through current APIs. + * </ul> + */ +@SuppressWarnings("deprecation") +public class ContactAccessorSdk3_4 extends ContactAccessor { + + /** + * Returns a Pick Contact intent using the pre-Eclair "people" URI. + */ + @Override + public Intent getPickContactIntent() { + return new Intent(Intent.ACTION_PICK, People.CONTENT_URI); + } + + /** + * Retrieves the contact information. + */ + @Override + public ContactInfo loadContact(ContentResolver contentResolver, Uri contactUri) { + ContactInfo contactInfo = new ContactInfo(); + Cursor cursor = contentResolver.query(contactUri, + new String[]{People.DISPLAY_NAME}, null, null, null); + try { + if (cursor.moveToFirst()) { + contactInfo.setDisplayName(cursor.getString(0)); + } + } finally { + cursor.close(); + } + + Uri phoneUri = Uri.withAppendedPath(contactUri, Phones.CONTENT_DIRECTORY); + cursor = contentResolver.query(phoneUri, + new String[]{Phones.NUMBER}, null, null, Phones.ISPRIMARY + " DESC"); + + try { + if (cursor.moveToFirst()) { + contactInfo.setPhoneNumber(cursor.getString(0)); + } + } finally { + cursor.close(); + } + + return contactInfo; + } +} diff --git a/samples/BusinessCard/src/com/example/android/businesscard/ContactAccessorSdk5.java b/samples/BusinessCard/src/com/example/android/businesscard/ContactAccessorSdk5.java new file mode 100644 index 000000000..6855597f0 --- /dev/null +++ b/samples/BusinessCard/src/com/example/android/businesscard/ContactAccessorSdk5.java @@ -0,0 +1,88 @@ +/* + * 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.businesscard; + +import android.content.ContentResolver; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.provider.ContactsContract.Contacts; +import android.provider.ContactsContract.CommonDataKinds.Phone; + +/** + * An implementation of {@link ContactAccessor} that uses current Contacts API. + * This class should be used on Eclair or beyond, but would not work on any earlier + * release of Android. As a matter of fact, it could not even be loaded. + * <p> + * This implementation has several advantages: + * <ul> + * <li>It sees contacts from multiple accounts. + * <li>It works with aggregated contacts. So for example, if the contact is the result + * of aggregation of two raw contacts from different accounts, it may return the name from + * one and the phone number from the other. + * <li>It is efficient because it uses the more efficient current API. + * <li>Not obvious in this particular example, but it has access to new kinds + * of data available exclusively through the new APIs. Exercise for the reader: add support + * for nickname (see {@link android.provider.ContactsContract.CommonDataKinds.Nickname}) or + * social status updates (see {@link android.provider.ContactsContract.StatusUpdates}). + * </ul> + */ +public class ContactAccessorSdk5 extends ContactAccessor { + + /** + * Returns a Pick Contact intent using the Eclair "contacts" URI. + */ + @Override + public Intent getPickContactIntent() { + return new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI); + } + + /** + * Retrieves the contact information. + */ + @Override + public ContactInfo loadContact(ContentResolver contentResolver, Uri contactUri) { + ContactInfo contactInfo = new ContactInfo(); + long contactId = -1; + + // Load the display name for the specified person + Cursor cursor = contentResolver.query(contactUri, + new String[]{Contacts._ID, Contacts.DISPLAY_NAME}, null, null, null); + try { + if (cursor.moveToFirst()) { + contactId = cursor.getLong(0); + contactInfo.setDisplayName(cursor.getString(1)); + } + } finally { + cursor.close(); + } + + // Load the phone number (if any). + cursor = contentResolver.query(Phone.CONTENT_URI, + new String[]{Phone.NUMBER}, + Phone.CONTACT_ID + "=" + contactId, null, Phone.IS_SUPER_PRIMARY + " DESC"); + try { + if (cursor.moveToFirst()) { + contactInfo.setPhoneNumber(cursor.getString(0)); + } + } finally { + cursor.close(); + } + + return contactInfo; + } +} diff --git a/samples/BusinessCard/src/com/example/android/businesscard/ContactInfo.java b/samples/BusinessCard/src/com/example/android/businesscard/ContactInfo.java new file mode 100644 index 000000000..61a6f3b3f --- /dev/null +++ b/samples/BusinessCard/src/com/example/android/businesscard/ContactInfo.java @@ -0,0 +1,42 @@ +/* + * 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.businesscard; + +/** + * A model object containing contact data. + */ +public class ContactInfo { + + private String mDisplayName; + private String mPhoneNumber; + + public void setDisplayName(String displayName) { + this.mDisplayName = displayName; + } + + public String getDisplayName() { + return mDisplayName; + } + + public void setPhoneNumber(String phoneNumber) { + this.mPhoneNumber = phoneNumber; + } + + public String getPhoneNumber() { + return mPhoneNumber; + } +} |
