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:
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user