summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt68
-rw-r--r--core/java/android/provider/VoicemailContract.java95
-rw-r--r--telecomm/java/android/telecom/AuthenticatorService.java99
-rw-r--r--telecomm/java/android/telecom/Voicemail.java266
-rw-r--r--telecomm/java/android/telecom/VvmSyncService.java114
5 files changed, 641 insertions, 1 deletions
diff --git a/api/current.txt b/api/current.txt
index 1e74695..4553ce1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -26521,8 +26521,13 @@ package android.provider {
public static final class VoicemailContract.Voicemails implements android.provider.BaseColumns android.provider.OpenableColumns {
method public static android.net.Uri buildSourceUri(java.lang.String);
+ method public static int deleteAll(android.content.Context);
+ method public static android.net.Uri insert(android.content.Context, android.telecom.Voicemail);
+ method public static int insert(android.content.Context, java.util.List<android.telecom.Voicemail>);
field public static final android.net.Uri CONTENT_URI;
field public static final java.lang.String DATE = "date";
+ field public static final java.lang.String DELETED = "deleted";
+ field public static final java.lang.String DIRTY = "dirty";
field public static final java.lang.String DIR_TYPE = "vnd.android.cursor.dir/voicemails";
field public static final java.lang.String DURATION = "duration";
field public static final java.lang.String HAS_CONTENT = "has_content";
@@ -26530,6 +26535,8 @@ package android.provider {
field public static final java.lang.String ITEM_TYPE = "vnd.android.cursor.item/voicemail";
field public static final java.lang.String MIME_TYPE = "mime_type";
field public static final java.lang.String NUMBER = "number";
+ field public static final java.lang.String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
+ field public static final java.lang.String PHONE_ACCOUNT_ID = "subscription_id";
field public static final java.lang.String SOURCE_DATA = "source_data";
field public static final java.lang.String SOURCE_PACKAGE = "source_package";
field public static final java.lang.String TRANSCRIPTION = "transcription";
@@ -28886,6 +28893,22 @@ package android.system {
package android.telecom {
+ public class AuthenticatorService extends android.app.Service {
+ ctor public AuthenticatorService();
+ method public android.os.IBinder onBind(android.content.Intent);
+ }
+
+ public class AuthenticatorService.Authenticator extends android.accounts.AbstractAccountAuthenticator {
+ ctor public AuthenticatorService.Authenticator(android.content.Context);
+ method public android.os.Bundle addAccount(android.accounts.AccountAuthenticatorResponse, java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle) throws android.accounts.NetworkErrorException;
+ method public android.os.Bundle confirmCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, android.os.Bundle) throws android.accounts.NetworkErrorException;
+ method public android.os.Bundle editProperties(android.accounts.AccountAuthenticatorResponse, java.lang.String);
+ method public android.os.Bundle getAuthToken(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
+ method public java.lang.String getAuthTokenLabel(java.lang.String);
+ method public android.os.Bundle hasFeatures(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String[]) throws android.accounts.NetworkErrorException;
+ method public android.os.Bundle updateCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
+ }
+
public class PhoneAccount implements android.os.Parcelable {
method public static android.telecom.PhoneAccount.Builder builder(android.telecom.PhoneAccountHandle, java.lang.CharSequence);
method public android.graphics.drawable.Drawable createIconDrawable(android.content.Context);
@@ -28995,6 +29018,51 @@ package android.telecom {
field public static final int TX_ENABLED = 1; // 0x1
}
+ public class Voicemail implements android.os.Parcelable {
+ method public static android.telecom.Voicemail.Builder createForInsertion(long, java.lang.String);
+ method public int describeContents();
+ method public long getDuration();
+ method public long getId();
+ method public java.lang.String getNumber();
+ method public java.lang.String getSourceData();
+ method public java.lang.String getSourcePackage();
+ method public long getTimestampMillis();
+ method public android.net.Uri getUri();
+ method public boolean hasContent();
+ method public boolean isRead();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telecom.Voicemail> CREATOR;
+ }
+
+ public static class Voicemail.Builder {
+ method public android.telecom.Voicemail build();
+ method public android.telecom.Voicemail.Builder setDuration(long);
+ method public android.telecom.Voicemail.Builder setHasContent(boolean);
+ method public android.telecom.Voicemail.Builder setId(long);
+ method public android.telecom.Voicemail.Builder setIsRead(boolean);
+ method public android.telecom.Voicemail.Builder setNumber(java.lang.String);
+ method public android.telecom.Voicemail.Builder setSourceData(java.lang.String);
+ method public android.telecom.Voicemail.Builder setSourcePackage(java.lang.String);
+ method public android.telecom.Voicemail.Builder setTimestamp(long);
+ method public android.telecom.Voicemail.Builder setUri(android.net.Uri);
+ }
+
+ public class VvmSyncService extends android.app.Service {
+ ctor public VvmSyncService();
+ method public android.os.IBinder onBind(android.content.Intent);
+ }
+
+ public class VvmSyncService.VvmSyncAdapter extends android.content.AbstractThreadedSyncAdapter {
+ ctor public VvmSyncService.VvmSyncAdapter(android.content.Context, boolean);
+ method protected java.util.List<android.telecom.Voicemail> downloadVoicemails();
+ method public void onPerformSync(android.accounts.Account, android.os.Bundle, java.lang.String, android.content.ContentProviderClient, android.content.SyncResult);
+ method protected void syncToServer();
+ field public static final java.lang.String NEW_VOICEMAIL_DATA = "extra_new_voicemail_data";
+ field public static final java.lang.String SYNC_EXTRA_CODE = "sync_extra_code";
+ field public static final int SYNC_EXTRA_MAILBOX_UPDATE = 2; // 0x2
+ field public static final int SYNC_EXTRA_NEW_VOICEMAIL = 1; // 0x1
+ }
+
}
package android.telephony {
diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java
index d707f35..03cf6e3 100644
--- a/core/java/android/provider/VoicemailContract.java
+++ b/core/java/android/provider/VoicemailContract.java
@@ -19,10 +19,16 @@ package android.provider;
import android.Manifest;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
import android.net.Uri;
import android.provider.CallLog.Calls;
+import android.telecom.Voicemail;
+
+import java.util.List;
/**
* The contract between the voicemail provider and applications. Contains
@@ -199,13 +205,100 @@ public class VoicemailContract {
*/
public static final String _DATA = "_data";
+ // Note: PHONE_ACCOUNT_* constant values are "subscription_*" due to a historic naming
+ // that was encoded into call log databases.
+
+ /**
+ * The component name of the account in string form.
+ * <P>Type: TEXT</P>
+ */
+ public static final String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
+
+ /**
+ * The identifier of a account that is unique to a specified component.
+ * <P>Type: TEXT</P>
+ */
+ public static final String PHONE_ACCOUNT_ID = "subscription_id";
+
+ /**
+ * Flag used to indicate that local, unsynced changes are present.
+ * Currently, this is used to indicate that the voicemail was read or deleted.
+ * The value will be 1 if dirty is true, 0 if false.
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String DIRTY = "dirty";
+
+ /**
+ * Flag used to indicate that the voicemail was deleted but not synced to the server.
+ * A deleted row should be ignored.
+ * The value will be 1 if deleted is true, 0 if false.
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String DELETED = "deleted";
+
/**
* A convenience method to build voicemail URI specific to a source package by appending
* {@link VoicemailContract#PARAM_KEY_SOURCE_PACKAGE} param to the base URI.
*/
public static Uri buildSourceUri(String packageName) {
return Voicemails.CONTENT_URI.buildUpon()
- .appendQueryParameter(PARAM_KEY_SOURCE_PACKAGE, packageName).build();
+ .appendQueryParameter(PARAM_KEY_SOURCE_PACKAGE, packageName)
+ .build();
+ }
+
+ /**
+ * Inserts a new voicemail into the voicemail content provider.
+ *
+ * @param context The context of the app doing the inserting
+ * @param voicemail Data to be inserted
+ * @return {@link Uri} of the newly inserted {@link Voicemail}
+ */
+ public static Uri insert(Context context, Voicemail voicemail) {
+ ContentResolver contentResolver = context.getContentResolver();
+ ContentValues contentValues = getContentValues(voicemail);
+ return contentResolver.insert(Voicemails.CONTENT_URI, contentValues);
+ }
+
+ /**
+ * Inserts a list of voicemails into the voicemail content provider.
+ *
+ * @param context The context of the app doing the inserting
+ * @param voicemails Data to be inserted
+ * @return the number of voicemails inserted
+ */
+ public static int insert(Context context, List<Voicemail> voicemails) {
+ ContentResolver contentResolver = context.getContentResolver();
+ int count = voicemails.size();
+ for (int i = 0; i < count; i++) {
+ ContentValues contentValues = getContentValues(voicemails.get(i));
+ contentResolver.insert(Voicemails.CONTENT_URI, contentValues);
+ }
+ return count;
+ }
+
+ /**
+ * Clears all voicemails accessible to this voicemail content provider for the calling
+ * package. By default, a package only has permission to delete voicemails it inserted.
+ *
+ * @return the number of voicemails deleted
+ */
+ public static int deleteAll(Context context) {
+ return context.getContentResolver().delete(
+ buildSourceUri(context.getPackageName()), "", new String[0]);
+ }
+
+ /**
+ * Maps structured {@link Voicemail} to {@link ContentValues} in content provider.
+ */
+ private static ContentValues getContentValues(Voicemail voicemail) {
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(Voicemails.DATE, String.valueOf(voicemail.getTimestampMillis()));
+ contentValues.put(Voicemails.NUMBER, voicemail.getNumber());
+ contentValues.put(Voicemails.DURATION, String.valueOf(voicemail.getDuration()));
+ contentValues.put(Voicemails.SOURCE_PACKAGE, voicemail.getSourcePackage());
+ contentValues.put(Voicemails.SOURCE_DATA, voicemail.getSourceData());
+ contentValues.put(Voicemails.IS_READ, voicemail.isRead() ? 1 : 0);
+ return contentValues;
}
}
diff --git a/telecomm/java/android/telecom/AuthenticatorService.java b/telecomm/java/android/telecom/AuthenticatorService.java
new file mode 100644
index 0000000..39717c3
--- /dev/null
+++ b/telecomm/java/android/telecom/AuthenticatorService.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 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 android.telecom;
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.NetworkErrorException;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+
+/**
+ * A generic stub account authenticator service often used for sync adapters that do not directly
+ * involve accounts.
+ */
+public class AuthenticatorService extends Service {
+ private static Authenticator mAuthenticator;
+
+ @Override
+ public void onCreate() {
+ mAuthenticator = new Authenticator(this);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mAuthenticator.getIBinder();
+ }
+
+ /**
+ * Stub account authenticator. All methods either return null or throw an exception.
+ */
+ public class Authenticator extends AbstractAccountAuthenticator {
+ public Authenticator(Context context) {
+ super(context);
+ }
+
+ @Override
+ public Bundle editProperties(AccountAuthenticatorResponse accountAuthenticatorResponse,
+ String s) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Bundle addAccount(AccountAuthenticatorResponse accountAuthenticatorResponse,
+ String s, String s2, String[] strings, Bundle bundle)
+ throws NetworkErrorException {
+ return null;
+ }
+
+ @Override
+ public Bundle confirmCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse,
+ Account account, Bundle bundle)
+ throws NetworkErrorException {
+ return null;
+ }
+
+ @Override
+ public Bundle getAuthToken(AccountAuthenticatorResponse accountAuthenticatorResponse,
+ Account account, String s, Bundle bundle)
+ throws NetworkErrorException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getAuthTokenLabel(String s) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Bundle updateCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse,
+ Account account, String s, Bundle bundle)
+ throws NetworkErrorException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Bundle hasFeatures(AccountAuthenticatorResponse accountAuthenticatorResponse,
+ Account account, String[] strings)
+ throws NetworkErrorException {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/telecomm/java/android/telecom/Voicemail.java b/telecomm/java/android/telecom/Voicemail.java
new file mode 100644
index 0000000..864c6b1
--- /dev/null
+++ b/telecomm/java/android/telecom/Voicemail.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2015 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 android.telecom;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents a single voicemail stored in the voicemail content provider.
+ */
+public class Voicemail implements Parcelable {
+ private final Long mTimestamp;
+ private final String mNumber;
+ private final Long mId;
+ private final Long mDuration;
+ private final String mSource;
+ private final String mProviderData;
+ private final Uri mUri;
+ private final Boolean mIsRead;
+ private final Boolean mHasContent;
+
+ private Voicemail(Long timestamp, String number, Long id, Long duration, String source,
+ String providerData, Uri uri, Boolean isRead, Boolean hasContent) {
+ mTimestamp = timestamp;
+ mNumber = number;
+ mId = id;
+ mDuration = duration;
+ mSource = source;
+ mProviderData = providerData;
+ mUri = uri;
+ mIsRead = isRead;
+ mHasContent = hasContent;
+ }
+
+ /**
+ * Create a {@link Builder} for a new {@link Voicemail} to be inserted.
+ * <p>
+ * The number and the timestamp are mandatory for insertion.
+ */
+ public static Builder createForInsertion(long timestamp, String number) {
+ return new Builder().setNumber(number).setTimestamp(timestamp);
+ }
+
+ /**
+ * Builder pattern for creating a {@link Voicemail}. The builder must be created with the
+ * {@link #createForInsertion(long, String)} method.
+ * <p>
+ * This class is <b>not thread safe</b>
+ */
+ public static class Builder {
+ private Long mBuilderTimestamp;
+ private String mBuilderNumber;
+ private Long mBuilderId;
+ private Long mBuilderDuration;
+ private String mBuilderSourcePackage;
+ private String mBuilderSourceData;
+ private Uri mBuilderUri;
+ private Boolean mBuilderIsRead;
+ private boolean mBuilderHasContent;
+
+ /** You should use the correct factory method to construct a builder. */
+ private Builder() {
+ }
+
+ public Builder setNumber(String number) {
+ mBuilderNumber = number;
+ return this;
+ }
+
+ public Builder setTimestamp(long timestamp) {
+ mBuilderTimestamp = timestamp;
+ return this;
+ }
+
+ public Builder setId(long id) {
+ mBuilderId = id;
+ return this;
+ }
+
+ public Builder setDuration(long duration) {
+ mBuilderDuration = duration;
+ return this;
+ }
+
+ public Builder setSourcePackage(String sourcePackage) {
+ mBuilderSourcePackage = sourcePackage;
+ return this;
+ }
+
+ public Builder setSourceData(String sourceData) {
+ mBuilderSourceData = sourceData;
+ return this;
+ }
+
+ public Builder setUri(Uri uri) {
+ mBuilderUri = uri;
+ return this;
+ }
+
+ public Builder setIsRead(boolean isRead) {
+ mBuilderIsRead = isRead;
+ return this;
+ }
+
+ public Builder setHasContent(boolean hasContent) {
+ mBuilderHasContent = hasContent;
+ return this;
+ }
+
+ public Voicemail build() {
+ mBuilderId = mBuilderId == null ? -1 : mBuilderId;
+ mBuilderTimestamp = mBuilderTimestamp == null ? 0 : mBuilderTimestamp;
+ mBuilderDuration = mBuilderDuration == null ? 0: mBuilderDuration;
+ mBuilderIsRead = mBuilderIsRead == null ? false : mBuilderIsRead;
+ return new Voicemail(mBuilderTimestamp, mBuilderNumber, mBuilderId, mBuilderDuration,
+ mBuilderSourcePackage, mBuilderSourceData, mBuilderUri, mBuilderIsRead,
+ mBuilderHasContent);
+ }
+ }
+
+ /**
+ * The identifier of the voicemail in the content provider.
+ * <p>
+ * This may be missing in the case of a new {@link Voicemail} that we plan to insert into the
+ * content provider, since until it has been inserted we don't know what id it should have. If
+ * none is specified, we return -1.
+ */
+ public long getId() {
+ return mId;
+ }
+
+ /** The number of the person leaving the voicemail, empty string if unknown, null if not set. */
+ public String getNumber() {
+ return mNumber;
+ }
+
+ /** The timestamp the voicemail was received, in millis since the epoch, zero if not set. */
+ public long getTimestampMillis() {
+ return mTimestamp;
+ }
+
+ /** Gets the duration of the voicemail in millis, or zero if the field is not set. */
+ public long getDuration() {
+ return mDuration;
+ }
+
+ /**
+ * Returns the package name of the source that added this voicemail, or null if this field is
+ * not set.
+ */
+ public String getSourcePackage() {
+ return mSource;
+ }
+
+ /**
+ * Returns the application-specific data type stored with the voicemail, or null if this field
+ * is not set.
+ * <p>
+ * Source data is typically used as an identifier to uniquely identify the voicemail against
+ * the voicemail server. This is likely to be something like the IMAP UID, or some other
+ * server-generated identifying string.
+ */
+ public String getSourceData() {
+ return mProviderData;
+ }
+
+ /**
+ * Gets the Uri that can be used to refer to this voicemail, and to make it play.
+ * <p>
+ * Returns null if we don't know the Uri.
+ */
+ public Uri getUri() {
+ return mUri;
+ }
+
+ /**
+ * Tells us if the voicemail message has been marked as read.
+ * <p>
+ * Always returns false if this field has not been set, i.e. if hasRead() returns false.
+ */
+ public boolean isRead() {
+ return mIsRead;
+ }
+
+ /**
+ * Tells us if there is content stored at the Uri.
+ */
+ public boolean hasContent() {
+ return mHasContent;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mTimestamp);
+ dest.writeCharSequence(mNumber);
+ dest.writeLong(mId);
+ dest.writeLong(mDuration);
+ dest.writeCharSequence(mSource);
+ dest.writeCharSequence(mProviderData);
+ if (mUri == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ mUri.writeToParcel(dest, flags);
+ }
+ if (mIsRead) {
+ dest.writeInt(1);
+ } else {
+ dest.writeInt(0);
+ }
+ if (mHasContent) {
+ dest.writeInt(1);
+ } else {
+ dest.writeInt(0);
+ }
+ }
+
+ public static final Creator<Voicemail> CREATOR
+ = new Creator<Voicemail>() {
+ @Override
+ public Voicemail createFromParcel(Parcel in) {
+ return new Voicemail(in);
+ }
+
+ @Override
+ public Voicemail[] newArray(int size) {
+ return new Voicemail[size];
+ }
+ };
+
+ private Voicemail(Parcel in) {
+ mTimestamp = in.readLong();
+ mNumber = (String) in.readCharSequence();
+ mId = in.readLong();
+ mDuration = in.readLong();
+ mSource = (String) in.readCharSequence();
+ mProviderData = (String) in.readCharSequence();
+ if (in.readInt() > 0) {
+ mUri = Uri.CREATOR.createFromParcel(in);
+ } else {
+ mUri = null;
+ }
+ mIsRead = in.readInt() > 0 ? true : false;
+ mHasContent = in.readInt() > 0 ? true : false;
+ }
+} \ No newline at end of file
diff --git a/telecomm/java/android/telecom/VvmSyncService.java b/telecomm/java/android/telecom/VvmSyncService.java
new file mode 100644
index 0000000..2aaf348
--- /dev/null
+++ b/telecomm/java/android/telecom/VvmSyncService.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2015 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
+ */
+
+/**
+ * A {@link Service} which runs the internal implementation of {@link AbstractThreadedSyncAdapter},
+ * syncing voicemails to and from a visual voicemail server.
+ */
+
+package android.telecom;
+
+import android.accounts.Account;
+import android.app.Service;
+import android.content.AbstractThreadedSyncAdapter;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SyncResult;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.provider.VoicemailContract;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A service to run the VvmSyncAdapter.
+ */
+public class VvmSyncService extends Service {
+ // Storage for an instance of the sync adapter
+ private static VvmSyncAdapter sSyncAdapter = null;
+ // Object to use as a thread-safe lock
+ private static final Object sSyncAdapterLock = new Object();
+
+ @Override
+ public void onCreate() {
+ synchronized (sSyncAdapterLock) {
+ if (sSyncAdapter == null) {
+ sSyncAdapter = new VvmSyncAdapter(getApplicationContext(), true);
+ }
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return sSyncAdapter.getSyncAdapterBinder();
+ }
+
+ public class VvmSyncAdapter extends AbstractThreadedSyncAdapter {
+ /** The key to get the extra designating the type of sync action to perform. */
+ public final static String SYNC_EXTRA_CODE = "sync_extra_code";
+ /** The key to get the {@code Voicemail} object for a new voicemail. */
+ public final static String NEW_VOICEMAIL_DATA = "extra_new_voicemail_data";
+ /** Sync a new voicemail from the carrier to the device. */
+ public final static int SYNC_EXTRA_NEW_VOICEMAIL = 1;
+ /** Sync all voicemails because the mailbox was changed remotely. */
+ public final static int SYNC_EXTRA_MAILBOX_UPDATE = 2;
+
+ private final Context mContext;
+
+ public VvmSyncAdapter(Context context, boolean autoInitialize) {
+ super(context, autoInitialize);
+ mContext = context;
+ }
+
+ @Override
+ public void onPerformSync(Account account, Bundle extras, String authority,
+ ContentProviderClient provider, SyncResult syncResult) {
+ if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false)) {
+ // notify server that voicemail has been changed
+ syncToServer();
+ }
+
+ final int syncAction = extras.getInt(SYNC_EXTRA_CODE);
+ switch (syncAction) {
+ /** sync from carrier */
+ case SYNC_EXTRA_NEW_VOICEMAIL:
+ // Log new voicemail in voicemail provider.
+ Voicemail newVoicemail = extras.getParcelable(NEW_VOICEMAIL_DATA);
+ VoicemailContract.Voicemails.insert(mContext, newVoicemail);
+ break;
+ case SYNC_EXTRA_MAILBOX_UPDATE:
+ // Clear and reload all voicemails because the mailbox was updated remotely.
+ VoicemailContract.Voicemails.deleteAll(mContext);
+ List<Voicemail> voicemails = downloadVoicemails();
+ VoicemailContract.Voicemails.insert(mContext, voicemails);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /** Subclasses should implement this method to sync changes to server */
+ protected void syncToServer() { }
+
+ /** Subclasses should implement this method to download voicemails */
+ protected List<Voicemail> downloadVoicemails() {
+ return new ArrayList<Voicemail>();
+ }
+ }
+}