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
This commit is contained in:
Daniel Lehmann
2011-09-29 17:08:36 -07:00
parent 10cb19aefe
commit 1cf776aee1
27 changed files with 436 additions and 802 deletions

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2011 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.samplesync.activities;
import com.example.android.samplesync.R;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
/**
* Activity to handle the invite-intent. In a real app, this would look up the user on the network
* and either connect ("add as friend", "follow") or invite them to the network
*/
public class InviteContactActivity extends Activity {
private static final String TAG = "InviteContactActivity";
private TextView mUriTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.invite_contact_activity);
mUriTextView = (TextView) findViewById(R.id.invite_contact_uri);
mUriTextView.setText(getIntent().getDataString());
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2011 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.samplesync.activities;
import com.example.android.samplesync.R;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
/**
* Activity to handle the view-group action. In a real app, this would show a rich view of the
* group, like members, updates etc.
*/
public class ViewGroupActivity extends Activity {
private static final String TAG = "ViewGroupActivity";
private TextView mUriTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view_group_activity);
mUriTextView = (TextView) findViewById(R.id.view_group_uri);
mUriTextView.setText(getIntent().getDataString());
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2011 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.samplesync.activities;
import com.example.android.samplesync.R;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
/**
* Activity to handle view a stream-item. In a real app, this would show a rich view of the
* item.
*/
public class ViewStreamItemActivity extends Activity {
private static final String TAG = "ViewStreamItemActivity";
private TextView mUriTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view_stream_item_activity);
mUriTextView = (TextView) findViewById(R.id.view_stream_item_uri);
mUriTextView.setText(getIntent().getDataString());
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2011 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.samplesync.activities;
import com.example.android.samplesync.R;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
/**
* Activity to view a stream-item-photo. In a real app, this would show a fullscreen view of the
* photo, potentially with ways to interact with it
*/
public class ViewStreamItemPhotoActivity extends Activity {
private static final String TAG = "ViewStreamItemPhotoActivity";
private TextView mUriTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view_stream_item_photo_activity);
mUriTextView = (TextView) findViewById(R.id.view_stream_item_photo_uri);
mUriTextView.setText(getIntent().getDataString());
}
}

View File

@@ -110,7 +110,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity {
mMessage = (TextView) findViewById(R.id.message);
mUsernameEdit = (EditText) findViewById(R.id.username_edit);
mPasswordEdit = (EditText) findViewById(R.id.password_edit);
mUsernameEdit.setText(mUsername);
if (!TextUtils.isEmpty(mUsername)) mUsernameEdit.setText(mUsername);
mMessage.setText(getMessage());
}

View File

@@ -16,16 +16,6 @@
package com.example.android.samplesync.client;
import android.accounts.Account;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import com.example.android.samplesync.authenticator.AuthenticatorActivity;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
@@ -41,9 +31,15 @@ import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.accounts.Account;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.text.TextUtils;
import android.util.Log;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
@@ -54,11 +50,8 @@ import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
/**
* Provides utility methods for communicating with the server.

View File

@@ -1,430 +0,0 @@
/*
* 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.example.android.samplesync.editor;
import com.example.android.samplesync.Constants;
import com.example.android.samplesync.R;
import com.example.android.samplesync.client.RawContact;
import com.example.android.samplesync.platform.BatchOperation;
import com.example.android.samplesync.platform.ContactManager;
import com.example.android.samplesync.platform.ContactManager.ContactQuery;
import com.example.android.samplesync.platform.ContactManager.EditorQuery;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.RawContacts;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.EditText;
import android.widget.TextView;
/**
* Implements a sample editor for a contact that belongs to a remote contact service.
* The editor can be invoked for an existing SampleSyncAdapter contact, or it can
* be used to create a brand new SampleSyncAdapter contact. We look at the Intent
* object to figure out whether this is a "new" or "edit" operation.
*/
public class ContactEditorActivity extends Activity {
private static final String TAG = "SampleSyncAdapter";
// Keep track of whether we're inserting a new contact or editing an
// existing contact.
private boolean mIsInsert;
// The name of the external account we're syncing this contact to.
private String mAccountName;
// For existing contacts, this is the URI to the contact data.
private Uri mRawContactUri;
// The raw clientId for this contact
private long mRawContactId;
// Make sure we only attempt to save the contact once if the
// user presses the "done" button multiple times...
private boolean mSaveInProgress = false;
// Keep track of the controls used to edit contact values, so we can get/set
// those values easily.
private EditText mNameEditText;
private EditText mHomePhoneEditText;
private EditText mMobilePhoneEditText;
private EditText mWorkPhoneEditText;
private EditText mEmailEditText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.editor);
mNameEditText = (EditText)findViewById(R.id.editor_name);
mHomePhoneEditText = (EditText)findViewById(R.id.editor_phone_home);
mMobilePhoneEditText = (EditText)findViewById(R.id.editor_phone_mobile);
mWorkPhoneEditText = (EditText)findViewById(R.id.editor_phone_work);
mEmailEditText = (EditText)findViewById(R.id.editor_email);
// Figure out whether we're creating a new contact (ACTION_INSERT), editing
// an existing contact, or adding a new one to existing contact (INVITE_CONTACT).
Intent intent = getIntent();
String action = intent.getAction();
if (Intent.ACTION_INSERT.equals(action)) {
// We're inserting a new contact, so save off the external account name
// which should have been added to the intent we were passed.
mIsInsert = true;
String accountName = intent.getStringExtra(RawContacts.ACCOUNT_NAME);
if (accountName == null) {
Log.e(TAG, "Account name is required");
finish();
}
setAccountName(accountName);
} else if (ContactsContract.Intents.INVITE_CONTACT.equals(action)) {
// Adding to an existing contact.
mIsInsert = true;
// Use the first account found.
Account[] myAccounts = AccountManager.get(this).getAccountsByType(
Constants.ACCOUNT_TYPE);
if (myAccounts.length == 0) {
Log.e(TAG, "Account not configured");
finish();
}
setAccountName(myAccounts[0].name);
Uri lookupUri = intent.getData();
if (lookupUri == null) {
Log.e(TAG, "Contact lookup URI is required");
finish();
}
startLoadContactEntity(lookupUri);
} else {
// We're editing an existing contact. Load in the data from the contact
// so that the user can edit it.
mIsInsert = false;
mRawContactUri = intent.getData();
if (mRawContactUri == null) {
Log.e(TAG, "Raw contact URI is required");
finish();
}
startLoadRawContactEntity();
}
}
@Override
public void onBackPressed() {
// This method will have been called if the user presses the "Back" button
// in the ActionBar. We treat that the same way as the "Done" button in
// the ActionBar.
save();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// This method gets called so that we can place items in the main Options menu -
// for example, the ActionBar items. We add our menus from the res/menu/edit.xml
// file.
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.edit, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
case R.id.menu_done:
// The user pressed the "Home" button or our "Done" button - both
// in the ActionBar. In both cases, we want to save the contact
// and exit.
save();
return true;
case R.id.menu_cancel:
// The user pressed the Cancel menu item in the ActionBar.
// Close the editor without saving any changes.
finish();
return true;
}
return false;
}
/**
* Create an AsyncTask to load the contact from the Contacts data provider
*/
private void startLoadRawContactEntity() {
Uri uri = Uri.withAppendedPath(mRawContactUri, RawContacts.Entity.CONTENT_DIRECTORY);
new LoadRawContactTask().execute(uri);
}
/**
* Called by the LoadRawContactTask when the contact information has been
* successfully loaded from the Contacts data provider.
*/
public void onRawContactEntityLoaded(Cursor cursor) {
if (cursor.moveToFirst()) {
String mimetype = cursor.getString(EditorQuery.COLUMN_MIMETYPE);
if (StructuredName.CONTENT_ITEM_TYPE.equals(mimetype)) {
setAccountName(cursor.getString(EditorQuery.COLUMN_ACCOUNT_NAME));
mRawContactId = cursor.getLong(EditorQuery.COLUMN_RAW_CONTACT_ID);
mNameEditText.setText(cursor.getString(EditorQuery.COLUMN_FULL_NAME));
} else if (Phone.CONTENT_ITEM_TYPE.equals(mimetype)) {
final int type = cursor.getInt(EditorQuery.COLUMN_PHONE_TYPE);
if (type == Phone.TYPE_HOME) {
mHomePhoneEditText.setText(cursor.getString(EditorQuery.COLUMN_PHONE_NUMBER));
} else if (type == Phone.TYPE_MOBILE) {
mMobilePhoneEditText.setText(cursor.getString(EditorQuery.COLUMN_PHONE_NUMBER));
} else if (type == Phone.TYPE_WORK) {
mWorkPhoneEditText.setText(cursor.getString(EditorQuery.COLUMN_PHONE_NUMBER));
}
} else if (Email.CONTENT_ITEM_TYPE.equals(mimetype)) {
mEmailEditText.setText(cursor.getString(EditorQuery.COLUMN_DATA1));
}
}
}
/**
* Create an AsyncTask to load the contact from the Contacts data provider
*/
private void startLoadContactEntity(Uri lookupUri) {
new LoadContactTask().execute(lookupUri);
}
/**
* Called by the LoadContactTask when the contact information has been
* successfully loaded from the Contacts data provider.
*/
public void onContactEntityLoaded(Cursor cursor) {
if (cursor.moveToFirst()) {
mNameEditText.setText(cursor.getString(ContactQuery.COLUMN_DISPLAY_NAME));
}
}
/**
* Save the updated contact data. We actually take two different actions
* depending on whether we are creating a new contact or editing an
* existing contact.
*/
public void save() {
// If we're already saving this contact, don't kick-off yet
// another save - the user probably just pressed the "Done"
// button multiple times...
if (mSaveInProgress) {
return;
}
mSaveInProgress = true;
if (mIsInsert) {
saveNewContact();
} else {
saveChanges();
}
}
/**
* Save off the external contacts provider account name. We show the account name
* in the header section of the edit panel, and we also need it later when we
* save off a brand new contact.
*/
private void setAccountName(String accountName) {
mAccountName = accountName;
Log.i(TAG, "account=" + mAccountName);
if (accountName != null) {
TextView accountNameLabel = (TextView)findViewById(R.id.header_account_name);
if (accountNameLabel != null) {
accountNameLabel.setText(accountName);
}
}
}
/**
* Save a new contact using the Contacts content provider. The actual insertion
* is performed in an AsyncTask.
*/
@SuppressWarnings("unchecked")
private void saveNewContact() {
new InsertContactTask().execute(buildRawContact());
}
/**
* Save changes to an existing contact. The actual update is performed in
* an AsyncTask.
*/
@SuppressWarnings("unchecked")
private void saveChanges() {
new UpdateContactTask().execute(buildRawContact());
}
/**
* Build a RawContact object from the data in the user-editable form
* @return a new RawContact object representing the edited user
*/
private RawContact buildRawContact() {
return RawContact.create(mNameEditText.getText().toString(),
null,
null,
mMobilePhoneEditText.getText().toString(),
mWorkPhoneEditText.getText().toString(),
mHomePhoneEditText.getText().toString(),
mEmailEditText.getText().toString(),
null,
false,
mRawContactId,
-1);
}
/**
* Called after a contact is saved - both for edited contacts and new contacts.
* We set the final result of the activity to be "ok", and then close the activity
* by calling finish().
*/
public void onContactSaved(Uri result) {
if (result != null) {
Intent intent = new Intent();
intent.setData(result);
setResult(RESULT_OK, intent);
finish();
}
mSaveInProgress = false;
}
/**
* Represents an asynchronous task used to load a contact from
* the Contacts content provider.
*
*/
public class LoadRawContactTask extends AsyncTask<Uri, Void, Cursor> {
@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<RawContact, Void, Uri> {
@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<RawContact, Void, Uri> {
@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<Uri, Void, Cursor> {
@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();
}
}
}
}

View File

@@ -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<RawContact> rawContacts, long lastSyncMarker) {
List<RawContact> 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.

View File

@@ -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);

View File

@@ -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