From 1cf776aee1b2c0be0d9d7d2fdcb8696beefa5e1d Mon Sep 17 00:00:00 2001 From: Daniel Lehmann Date: Thu, 29 Sep 2011 17:08:36 -0700 Subject: [PATCH] Updated the sample - Removed editing, because it is not ready for the sdk yet - Added sync of a default group - Demo for "View group" - Demo for "View Stream Item" and "View Stream Item Photo" - Demo for invite flow (at least show a dialog) - Hardcoded demo username and password into the dialog (it is in the source anyway) Bug:5383087 Change-Id: I4cf18eb8e90df872f832ecd74f22d68de6099e60 --- samples/SampleSyncAdapter/AndroidManifest.xml | 70 ++- .../SampleSyncAdapter/res/drawable/border.xml | 6 - .../res/drawable/done_menu_icon.png | Bin 658 -> 0 bytes .../res/layout-xlarge/editor.xml | 43 -- .../res/layout-xlarge/editor_header.xml | 58 --- .../res/layout/editor_fields.xml | 127 ------ ...editor.xml => invite_contact_activity.xml} | 28 +- .../res/layout/login_activity.xml | 6 +- .../res/layout/view_group_activity.xml | 32 ++ .../res/layout/view_stream_item_activity.xml | 32 ++ .../view_stream_item_photo_activity.xml | 32 ++ samples/SampleSyncAdapter/res/menu/edit.xml | 30 -- .../SampleSyncAdapter/res/values/strings.xml | 30 ++ .../SampleSyncAdapter/res/values/styles.xml | 27 -- .../res/xml-v11/contacts.xml | 2 - .../res/xml-v14/contacts.xml | 13 +- .../SampleSyncAdapter/res/xml/contacts.xml | 2 - .../activities/InviteContactActivity.java | 41 ++ .../activities/ViewGroupActivity.java | 41 ++ .../activities/ViewStreamItemActivity.java | 41 ++ .../ViewStreamItemPhotoActivity.java | 41 ++ .../authenticator/AuthenticatorActivity.java | 2 +- .../samplesync/client/NetworkUtilities.java | 21 +- .../editor/ContactEditorActivity.java | 430 ------------------ .../samplesync/platform/ContactManager.java | 44 +- .../platform/ContactOperations.java | 15 + .../samplesync/syncadapter/SyncAdapter.java | 24 +- 27 files changed, 436 insertions(+), 802 deletions(-) delete mode 100644 samples/SampleSyncAdapter/res/drawable/border.xml delete mode 100644 samples/SampleSyncAdapter/res/drawable/done_menu_icon.png delete mode 100644 samples/SampleSyncAdapter/res/layout-xlarge/editor.xml delete mode 100644 samples/SampleSyncAdapter/res/layout-xlarge/editor_header.xml delete mode 100644 samples/SampleSyncAdapter/res/layout/editor_fields.xml rename samples/SampleSyncAdapter/res/layout/{editor.xml => invite_contact_activity.xml} (60%) create mode 100644 samples/SampleSyncAdapter/res/layout/view_group_activity.xml create mode 100644 samples/SampleSyncAdapter/res/layout/view_stream_item_activity.xml create mode 100644 samples/SampleSyncAdapter/res/layout/view_stream_item_photo_activity.xml delete mode 100644 samples/SampleSyncAdapter/res/menu/edit.xml delete mode 100644 samples/SampleSyncAdapter/res/values/styles.xml create mode 100644 samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/InviteContactActivity.java create mode 100644 samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/ViewGroupActivity.java create mode 100644 samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/ViewStreamItemActivity.java create mode 100644 samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/ViewStreamItemPhotoActivity.java delete mode 100644 samples/SampleSyncAdapter/src/com/example/android/samplesync/editor/ContactEditorActivity.java diff --git a/samples/SampleSyncAdapter/AndroidManifest.xml b/samples/SampleSyncAdapter/AndroidManifest.xml index 25e9e9926..285abfba8 100644 --- a/samples/SampleSyncAdapter/AndroidManifest.xml +++ b/samples/SampleSyncAdapter/AndroidManifest.xml @@ -46,7 +46,7 @@ - + - - - - - - - - - - + android:name=".activites.InviteContactActivity" + android:theme="@android:style/Theme.Dialog"> + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/SampleSyncAdapter/res/drawable/border.xml b/samples/SampleSyncAdapter/res/drawable/border.xml deleted file mode 100644 index ab71f2c28..000000000 --- a/samples/SampleSyncAdapter/res/drawable/border.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/samples/SampleSyncAdapter/res/drawable/done_menu_icon.png b/samples/SampleSyncAdapter/res/drawable/done_menu_icon.png deleted file mode 100644 index 3468bbd5f8438efb3a8d17c4c1db1077b39e07b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 658 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(epwmK8Xr18D^?ZvQoBE&~JOEKe855Rc<;ubj=gk|1*Ihbd-DAF)me65Ob8YiHj_?m(A|-UW|C1lNX$urA#tyoX`+qx6mIW*u=&Gs^T` zZv1Lfx~Sj1|HbD%Kbx~b^uNs3gMH>jY90$ZU+90BWujkmqBt?ZX7k;^$mPxZSi4@C zil4RYU2$>0Lt}d0>@6u1_jLbixR*ck%e@umPR3_1H$O1FAZE z>#Uw`zH$0%#quN-!__bMJ+9-t^eA`2j-+Ga$#H)=%bzJ6p0mAgNAm0~bI*P}omL|n z{`hEm4$I2DQQxXF4s|IAtLRxio_%9;)}EToiAUsg*=>v-F5eX@KId!fo0o1CyPGE+ z$5URbRuPL - - - - - - - - - \ No newline at end of file diff --git a/samples/SampleSyncAdapter/res/layout-xlarge/editor_header.xml b/samples/SampleSyncAdapter/res/layout-xlarge/editor_header.xml deleted file mode 100644 index 648e6f98d..000000000 --- a/samples/SampleSyncAdapter/res/layout-xlarge/editor_header.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - diff --git a/samples/SampleSyncAdapter/res/layout/editor_fields.xml b/samples/SampleSyncAdapter/res/layout/editor_fields.xml deleted file mode 100644 index 31d0128c1..000000000 --- a/samples/SampleSyncAdapter/res/layout/editor_fields.xml +++ /dev/null @@ -1,127 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/samples/SampleSyncAdapter/res/layout/editor.xml b/samples/SampleSyncAdapter/res/layout/invite_contact_activity.xml similarity index 60% rename from samples/SampleSyncAdapter/res/layout/editor.xml rename to samples/SampleSyncAdapter/res/layout/invite_contact_activity.xml index a0c36d2fa..1e09d5be1 100644 --- a/samples/SampleSyncAdapter/res/layout/editor.xml +++ b/samples/SampleSyncAdapter/res/layout/invite_contact_activity.xml @@ -18,19 +18,15 @@ --> - - - - - - \ No newline at end of file + android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + + diff --git a/samples/SampleSyncAdapter/res/layout/login_activity.xml b/samples/SampleSyncAdapter/res/layout/login_activity.xml index 7408ffe97..6c93f9073 100644 --- a/samples/SampleSyncAdapter/res/layout/login_activity.xml +++ b/samples/SampleSyncAdapter/res/layout/login_activity.xml @@ -55,7 +55,8 @@ android:scrollHorizontally="true" android:capitalize="none" android:autoText="false" - android:inputType="textEmailAddress" /> + android:inputType="textEmailAddress" + android:text="user" /> + android:inputType="textPassword" + android:text="test" /> + + + + + diff --git a/samples/SampleSyncAdapter/res/layout/view_stream_item_activity.xml b/samples/SampleSyncAdapter/res/layout/view_stream_item_activity.xml new file mode 100644 index 000000000..a04d07fe2 --- /dev/null +++ b/samples/SampleSyncAdapter/res/layout/view_stream_item_activity.xml @@ -0,0 +1,32 @@ + + + + + + diff --git a/samples/SampleSyncAdapter/res/layout/view_stream_item_photo_activity.xml b/samples/SampleSyncAdapter/res/layout/view_stream_item_photo_activity.xml new file mode 100644 index 000000000..ddc09d098 --- /dev/null +++ b/samples/SampleSyncAdapter/res/layout/view_stream_item_photo_activity.xml @@ -0,0 +1,32 @@ + + + + + + diff --git a/samples/SampleSyncAdapter/res/menu/edit.xml b/samples/SampleSyncAdapter/res/menu/edit.xml deleted file mode 100644 index 1227584ea..000000000 --- a/samples/SampleSyncAdapter/res/menu/edit.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - diff --git a/samples/SampleSyncAdapter/res/values/strings.xml b/samples/SampleSyncAdapter/res/values/strings.xml index 7ac95f586..22fe14ec7 100644 --- a/samples/SampleSyncAdapter/res/values/strings.xml +++ b/samples/SampleSyncAdapter/res/values/strings.xml @@ -109,4 +109,34 @@ Add to Sample SyncAdaper + + Congratulations! The user wants to add the contact + to the amazing Sample SyncAdapter social network. If this was a real app, it should now + make best efforts to add the contact to this network. This would probably involve + looking up the person on the network, inviting if he is not there already and syncing + the new contact down. + + Ideally, when the user gets back to the People app, the new contact should already + be there, enriching the original contact. + + This is the information we got to lookup the contact: + + + Show sample group details + + + This would now show the details of the group. + + This is the group uri: + + + This would now show the details of the stream item. + + This is the uri of the stream item: + + + This would now show the details of the stream item photo. + + This is the uri of the photo: + \ No newline at end of file diff --git a/samples/SampleSyncAdapter/res/values/styles.xml b/samples/SampleSyncAdapter/res/values/styles.xml deleted file mode 100644 index 074613e40..000000000 --- a/samples/SampleSyncAdapter/res/values/styles.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - #ffffff - #cccccc - - diff --git a/samples/SampleSyncAdapter/res/xml-v11/contacts.xml b/samples/SampleSyncAdapter/res/xml-v11/contacts.xml index 62dfa8009..48cf503c6 100644 --- a/samples/SampleSyncAdapter/res/xml-v11/contacts.xml +++ b/samples/SampleSyncAdapter/res/xml-v11/contacts.xml @@ -19,8 +19,6 @@ - { - - @Override - protected Cursor doInBackground(Uri... params) { - // Our background task is to load the contact from the Contacts provider - return getContentResolver().query(params[0], EditorQuery.PROJECTION, null, null, null); - } - - @Override - protected void onPostExecute(Cursor cursor) { - if (cursor == null) return; - // After we've successfully loaded the contact, call back into - // the ContactEditorActivity so we can update the UI - try { - onRawContactEntityLoaded(cursor); - } finally { - cursor.close(); - } - } - } - - /** - * Represents an asynchronous task used to save a new contact - * into the contacts database. - */ - public class InsertContactTask extends AsyncTask { - - @Override - protected Uri doInBackground(RawContact... params) { - try { - final RawContact rawContact = params[0]; - final Context context = getApplicationContext(); - final ContentResolver resolver = getContentResolver(); - final BatchOperation batchOperation = new BatchOperation(context, resolver); - ContactManager.addContact(context, mAccountName, rawContact, false, batchOperation); - Uri rawContactUri = batchOperation.execute(); - - // Convert the raw contact URI to a contact URI - if (rawContactUri != null) { - return RawContacts.getContactLookupUri(resolver, rawContactUri); - } else { - Log.e(TAG, "Could not save new contact"); - return null; - } - } catch (Exception e) { - Log.e(TAG, "An error occurred while saving new contact", e); - } - return null; - } - - @Override - protected void onPostExecute(Uri result) { - // Tell the UI that the contact has been successfully saved - onContactSaved(result); - } - } - - - /** - * Represents an asynchronous task used to save an updated contact - * into the contacts database. - */ - public class UpdateContactTask extends AsyncTask { - - @Override - protected Uri doInBackground(RawContact... params) { - try { - final RawContact rawContact = params[0]; - final Context context = getApplicationContext(); - final ContentResolver resolver = getContentResolver(); - final BatchOperation batchOperation = new BatchOperation(context, resolver); - ContactManager.updateContact(context, resolver, rawContact, false, false, false, - false, rawContact.getRawContactId(), batchOperation); - batchOperation.execute(); - - // Convert the raw contact URI to a contact URI - return RawContacts.getContactLookupUri(resolver, mRawContactUri); - } catch (Exception e) { - Log.e(TAG, "Could not save changes", e); - } - return null; - } - - @Override - protected void onPostExecute(Uri result) { - // Tell the UI that the contact has been successfully saved - onContactSaved(result); - } - } - - /** - * Loads contact information by a lookup URI. - */ - public class LoadContactTask extends AsyncTask { - - @Override - protected Cursor doInBackground(Uri... params) { - return getContentResolver().query(params[0], ContactQuery.PROJECTION, null, null, null); - } - - @Override - protected void onPostExecute(Cursor cursor) { - if (cursor == null) return; - try { - onContactEntityLoaded(cursor); - } finally { - cursor.close(); - } - } - } -} diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactManager.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactManager.java index 035c9766f..6b2dfb14c 100644 --- a/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactManager.java +++ b/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactManager.java @@ -34,6 +34,7 @@ import android.provider.ContactsContract.CommonDataKinds.Photo; 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.provider.ContactsContract.Settings; import android.provider.ContactsContract.StatusUpdates; @@ -54,6 +55,41 @@ public class ContactManager { private static final String TAG = "ContactManager"; + public static final String SAMPLE_GROUP_NAME = "Sample Group"; + + public static long ensureSampleGroupExists(Context context, Account account) { + final ContentResolver resolver = context.getContentResolver(); + + // Lookup the sample group + long groupId = 0; + final Cursor cursor = resolver.query(Groups.CONTENT_URI, new String[] { Groups._ID }, + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=? AND " + + Groups.TITLE + "=?", + new String[] { account.name, account.type, SAMPLE_GROUP_NAME }, null); + if (cursor != null) { + try { + if (cursor.moveToFirst()) { + groupId = cursor.getLong(0); + } + } finally { + cursor.close(); + } + } + + if (groupId == 0) { + // Sample group doesn't exist yet, so create it + final ContentValues contentValues = new ContentValues(); + contentValues.put(Groups.ACCOUNT_NAME, account.name); + contentValues.put(Groups.ACCOUNT_TYPE, account.type); + contentValues.put(Groups.TITLE, SAMPLE_GROUP_NAME); + contentValues.put(Groups.GROUP_IS_READ_ONLY, true); + + final Uri newGroupUri = resolver.insert(Groups.CONTENT_URI, contentValues); + groupId = ContentUris.parseId(newGroupUri); + } + return groupId; + } + /** * Take a list of updated contacts and apply those changes to the * contacts database. Typically this list of contacts would have been @@ -67,7 +103,7 @@ public class ContactManager { * sync request. */ public static synchronized long updateContacts(Context context, String account, - List rawContacts, long lastSyncMarker) { + List rawContacts, long groupId, long lastSyncMarker) { long currentSyncMarker = lastSyncMarker; final ContentResolver resolver = context.getContentResolver(); @@ -112,7 +148,7 @@ public class ContactManager { Log.d(TAG, "In addContact"); if (!rawContact.isDeleted()) { newUsers.add(rawContact); - addContact(context, account, rawContact, true, batchOperation); + addContact(context, account, rawContact, groupId, true, batchOperation); } } // A sync adapter should batch operations on multiple contacts, @@ -235,12 +271,13 @@ public class ContactManager { * @param context the Authenticator Activity context * @param accountName the account the contact belongs to * @param rawContact the sample SyncAdapter User object + * @param groupId the id of the sample group * @param inSync is the add part of a client-server sync? * @param batchOperation allow us to batch together multiple operations * into a single provider call */ public static void addContact(Context context, String accountName, RawContact rawContact, - boolean inSync, BatchOperation batchOperation) { + long groupId, boolean inSync, BatchOperation batchOperation) { // Put the data in the contacts provider final ContactOperations contactOp = ContactOperations.createNewContact( @@ -252,6 +289,7 @@ public class ContactManager { .addPhone(rawContact.getCellPhone(), Phone.TYPE_MOBILE) .addPhone(rawContact.getHomePhone(), Phone.TYPE_HOME) .addPhone(rawContact.getOfficePhone(), Phone.TYPE_WORK) + .addGroupMembership(groupId) .addAvatar(rawContact.getAvatarUrl()); // If we have a serverId, then go ahead and create our status profile. diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactOperations.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactOperations.java index cb8e97b8e..1445e55dd 100644 --- a/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactOperations.java +++ b/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactOperations.java @@ -25,6 +25,7 @@ import android.content.Context; import android.net.Uri; import android.provider.ContactsContract; import android.provider.ContactsContract.CommonDataKinds.Email; +import android.provider.ContactsContract.CommonDataKinds.GroupMembership; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.CommonDataKinds.Photo; import android.provider.ContactsContract.CommonDataKinds.StructuredName; @@ -185,6 +186,20 @@ public class ContactOperations { return this; } + /** + * Adds a group membership + * + * @param id The id of the group to assign + * @return instance of ContactOperations + */ + public ContactOperations addGroupMembership(long groupId) { + mValues.clear(); + mValues.put(GroupMembership.GROUP_ROW_ID, groupId); + mValues.put(GroupMembership.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE); + addInsertOp(); + return this; + } + public ContactOperations addAvatar(String avatarUrl) { if (avatarUrl != null) { byte[] avatarBuffer = NetworkUtilities.downloadAvatar(avatarUrl); diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/syncadapter/SyncAdapter.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/syncadapter/SyncAdapter.java index 0ca8dee5b..0f570cd32 100644 --- a/samples/SampleSyncAdapter/src/com/example/android/samplesync/syncadapter/SyncAdapter.java +++ b/samples/SampleSyncAdapter/src/com/example/android/samplesync/syncadapter/SyncAdapter.java @@ -15,6 +15,15 @@ */ package com.example.android.samplesync.syncadapter; +import com.example.android.samplesync.Constants; +import com.example.android.samplesync.client.NetworkUtilities; +import com.example.android.samplesync.client.RawContact; +import com.example.android.samplesync.platform.ContactManager; + +import org.apache.http.ParseException; +import org.apache.http.auth.AuthenticationException; +import org.json.JSONException; + import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AuthenticatorException; @@ -27,18 +36,7 @@ import android.os.Bundle; import android.text.TextUtils; import android.util.Log; -import com.example.android.samplesync.Constants; -import com.example.android.samplesync.client.NetworkUtilities; -import com.example.android.samplesync.client.RawContact; -import com.example.android.samplesync.platform.ContactManager; - -import org.apache.http.ParseException; -import org.apache.http.auth.AuthenticationException; -import org.json.JSONException; - import java.io.IOException; -import java.util.ArrayList; -import java.util.Date; import java.util.List; /** @@ -91,6 +89,9 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { final String authtoken = mAccountManager.blockingGetAuthToken(account, Constants.AUTHTOKEN_TYPE, NOTIFY_AUTH_FAILURE); + // Make sure that the sample group exists + final long groupId = ContactManager.ensureSampleGroupExists(mContext, account); + // Find the local 'dirty' contacts that we need to tell the server about... // Find the local users that need to be sync'd to the server... dirtyContacts = ContactManager.getDirtyContacts(mContext, account); @@ -106,6 +107,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { long newSyncState = ContactManager.updateContacts(mContext, account.name, updatedContacts, + groupId, lastSyncMarker); // This is a demo of how you can update IM-style status messages