Methods Summary |
---|
protected void | bootstrapDatabase(android.database.sqlite.SQLiteDatabase db)Override to create your schema and do anything else you need to do with a new database.
This is run inside a transaction (so you don't need to use one).
This method may not use getDatabase(), or call content provider methods, it must only
use the database handle passed to it.
|
public final int | bulkInsert(android.net.Uri uri, ContentValues[] values)
int size = values.length;
int completed = 0;
final boolean isSyncStateUri = mSyncState.matches(uri);
mDb = mOpenHelper.getWritableDatabase();
mDb.beginTransaction();
try {
for (int i = 0; i < size; i++) {
Uri result;
if (isTemporary() && isSyncStateUri) {
result = mSyncState.asContentProvider().insert(uri, values[i]);
} else {
result = insertInternal(uri, values[i]);
mDb.yieldIfContended();
}
if (result != null) {
completed++;
}
}
mDb.setTransactionSuccessful();
} finally {
mDb.endTransaction();
}
if (!isTemporary() && completed == size) {
getContext().getContentResolver().notifyChange(uri, null /* observer */,
changeRequiresLocalSync(uri));
}
return completed;
|
public boolean | changeRequiresLocalSync(android.net.Uri uri)Check if changes to this URI can be syncable changes.
return true;
|
public void | close()Close resources that must be closed. You must call this to properly release
the resources used by the AbstractSyncableContentProvider.
if (mOpenHelper != null) {
mOpenHelper.close(); // OK to call .close() repeatedly.
}
|
public final int | delete(android.net.Uri url, java.lang.String selection, java.lang.String[] selectionArgs)
mDb = mOpenHelper.getWritableDatabase();
mDb.beginTransaction();
try {
if (isTemporary() && mSyncState.matches(url)) {
int numRows = mSyncState.asContentProvider().delete(url, selection, selectionArgs);
mDb.setTransactionSuccessful();
return numRows;
}
int result = deleteInternal(url, selection, selectionArgs);
mDb.setTransactionSuccessful();
if (!isTemporary() && result > 0) {
getContext().getContentResolver().notifyChange(url, null /* observer */,
changeRequiresLocalSync(url));
}
return result;
} finally {
mDb.endTransaction();
}
|
protected abstract int | deleteInternal(android.net.Uri url, java.lang.String selection, java.lang.String[] selectionArgs)Subclasses should override this instead of delete(). See delete()
for details.
This method is called within a acquireDbLock()/releaseDbLock() block,
which means a database transaction will be active during the call;
|
protected void | deleteRowsForRemovedAccounts(java.util.Map accounts, java.lang.String table, java.lang.String accountColumnName)A helper method to delete all rows whose account is not in the accounts
map. The accountColumnName is the name of the column that is expected
to hold the account. If a row has an empty account it is never deleted.
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Cursor c = db.query(table, sAccountProjection, null, null,
accountColumnName, null, null);
try {
while (c.moveToNext()) {
String account = c.getString(0);
if (TextUtils.isEmpty(account)) {
continue;
}
if (!accounts.containsKey(account)) {
int numDeleted;
numDeleted = db.delete(table, accountColumnName + "=?", new String[]{account});
if (Config.LOGV) {
Log.v(TAG, "deleted " + numDeleted
+ " records from table " + table
+ " for account " + account);
}
}
}
} finally {
c.close();
}
|
public boolean | getContainsDiffs()
return mContainsDiffs;
|
public android.database.sqlite.SQLiteDatabase | getDatabase()
if (mDb == null) mDb = mOpenHelper.getWritableDatabase();
return mDb;
|
protected java.lang.Iterable | getMergers()Each subclass of this class should define a subclass of {@link
android.content.AbstractTableMerger} for each table they wish to merge. It
should then override this method and return one instance of
each merger, in sequence. Their {@link
android.content.AbstractTableMerger#merge merge} methods will be called, one at a
time, in the order supplied.
The default implementation returns an empty list, so that no
merging will occur.
return Collections.emptyList();
|
public java.lang.String | getSyncingAccount()The account of the most recent call to onSyncStart()
return mSyncingAccount;
|
public android.content.AbstractSyncableContentProvider | getTemporaryInstance()Get a non-persistent instance of this content provider.
You must call {@link #close} on the returned
SyncableContentProvider when you are done with it.
AbstractSyncableContentProvider temp;
try {
temp = getClass().newInstance();
} catch (InstantiationException e) {
throw new RuntimeException("unable to instantiate class, "
+ "this should never happen", e);
} catch (IllegalAccessException e) {
throw new RuntimeException(
"IllegalAccess while instantiating class, "
+ "this should never happen", e);
}
// Note: onCreate() isn't run for the temp provider, and it has no Context.
temp.mIsTemporary = true;
temp.setContainsDiffs(true);
temp.mOpenHelper = temp.new DatabaseHelper(null, null);
temp.mSyncState = new SyncStateContentProviderHelper(temp.mOpenHelper);
if (!isTemporary()) {
mSyncState.copySyncState(
mOpenHelper.getReadableDatabase(),
temp.mOpenHelper.getWritableDatabase(),
getSyncingAccount());
}
return temp;
|
public final android.net.Uri | insert(android.net.Uri url, ContentValues values)
mDb = mOpenHelper.getWritableDatabase();
mDb.beginTransaction();
try {
if (isTemporary() && mSyncState.matches(url)) {
Uri result = mSyncState.asContentProvider().insert(url, values);
mDb.setTransactionSuccessful();
return result;
}
Uri result = insertInternal(url, values);
mDb.setTransactionSuccessful();
if (!isTemporary() && result != null) {
getContext().getContentResolver().notifyChange(url, null /* observer */,
changeRequiresLocalSync(url));
}
return result;
} finally {
mDb.endTransaction();
}
|
protected abstract android.net.Uri | insertInternal(android.net.Uri url, ContentValues values)Subclasses should override this instead of insert(). See insert()
for details.
This method is called within a acquireDbLock()/releaseDbLock() block,
which means a database transaction will be active during the call;
|
public boolean | isMergeCancelled()
return mIsMergeCancelled;
|
protected boolean | isTemporary()
return mIsTemporary;
|
public void | merge(SyncContext context, SyncableContentProvider diffs, TempProviderSyncResult result, SyncResult syncResult)Merge diffs from a sync source with this content provider.
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
db.beginTransaction();
try {
synchronized(this) {
mIsMergeCancelled = false;
}
Iterable<? extends AbstractTableMerger> mergers = getMergers();
try {
for (AbstractTableMerger merger : mergers) {
synchronized(this) {
if (mIsMergeCancelled) break;
mCurrentMerger = merger;
}
merger.merge(context, getSyncingAccount(), diffs, result, syncResult, this);
}
if (mIsMergeCancelled) return;
if (diffs != null) {
mSyncState.copySyncState(
((AbstractSyncableContentProvider)diffs).mOpenHelper.getReadableDatabase(),
mOpenHelper.getWritableDatabase(),
getSyncingAccount());
}
} finally {
synchronized (this) {
mCurrentMerger = null;
}
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
|
protected void | onAccountsChanged(java.lang.String[] accountsArray)Make sure that there are no entries for accounts that no longer exist
Map<String, Boolean> accounts = new HashMap<String, Boolean>();
for (String account : accountsArray) {
accounts.put(account, false);
}
accounts.put(SyncConstValue.NON_SYNCABLE_ACCOUNT, false);
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Map<String, String> tableMap = db.getSyncedTables();
Vector<String> tables = new Vector<String>();
tables.addAll(tableMap.keySet());
tables.addAll(tableMap.values());
db.beginTransaction();
try {
mSyncState.onAccountsChanged(accountsArray);
for (String table : tables) {
deleteRowsForRemovedAccounts(accounts, table,
SyncConstValue._SYNC_ACCOUNT);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
|
public boolean | onCreate()
if (isTemporary()) throw new IllegalStateException("onCreate() called for temp provider");
mOpenHelper = new AbstractSyncableContentProvider.DatabaseHelper(getContext(), mDatabaseName);
mSyncState = new SyncStateContentProviderHelper(mOpenHelper);
AccountMonitorListener listener = new AccountMonitorListener() {
public void onAccountsUpdated(String[] accounts) {
// Some providers override onAccountsChanged(); give them a database to work with.
mDb = mOpenHelper.getWritableDatabase();
onAccountsChanged(accounts);
TempProviderSyncAdapter syncAdapter = (TempProviderSyncAdapter)getSyncAdapter();
if (syncAdapter != null) {
syncAdapter.onAccountsChanged(accounts);
}
}
};
mAccountMonitor = new AccountMonitor(getContext(), listener);
return true;
|
protected void | onDatabaseOpened(android.database.sqlite.SQLiteDatabase db)Override to do anything (like cleanups or checks) you need to do after opening a database.
Does nothing by default. This is run inside a transaction (so you don't need to use one).
This method may not use getDatabase(), or call content provider methods, it must only
use the database handle passed to it.
|
public void | onSyncCanceled()Invoked when the active sync has been canceled. Sets the sync state of this provider and
its merger to canceled.
synchronized (this) {
mIsMergeCancelled = true;
if (mCurrentMerger != null) {
mCurrentMerger.onMergeCancelled();
}
}
|
public void | onSyncStart(SyncContext context, java.lang.String account)Called right before a sync is started.
if (TextUtils.isEmpty(account)) {
throw new IllegalArgumentException("you passed in an empty account");
}
mSyncingAccount = account;
|
public void | onSyncStop(SyncContext context, boolean success)Called right after a sync is completed
|
public final android.database.Cursor | query(android.net.Uri url, java.lang.String[] projection, java.lang.String selection, java.lang.String[] selectionArgs, java.lang.String sortOrder)
mDb = mOpenHelper.getReadableDatabase();
if (isTemporary() && mSyncState.matches(url)) {
return mSyncState.asContentProvider().query(
url, projection, selection, selectionArgs, sortOrder);
}
return queryInternal(url, projection, selection, selectionArgs, sortOrder);
|
protected abstract android.database.Cursor | queryInternal(android.net.Uri url, java.lang.String[] projection, java.lang.String selection, java.lang.String[] selectionArgs, java.lang.String sortOrder)Subclasses should override this instead of query(). See query()
for details.
This method is *not* called within a acquireDbLock()/releaseDbLock()
block for performance reasons. If an implementation needs atomic access
to the database the lock can be acquired then.
|
public byte[] | readSyncDataBytes(java.lang.String account)Retrieves the SyncData bytes for the given account. The byte array returned may be null.
return mSyncState.readSyncDataBytes(mOpenHelper.getReadableDatabase(), account);
|
public void | setContainsDiffs(boolean containsDiffs)
if (containsDiffs && !isTemporary()) {
throw new IllegalStateException(
"only a temporary provider can contain diffs");
}
mContainsDiffs = containsDiffs;
|
public final int | update(android.net.Uri url, ContentValues values, java.lang.String selection, java.lang.String[] selectionArgs)
mDb = mOpenHelper.getWritableDatabase();
mDb.beginTransaction();
try {
if (isTemporary() && mSyncState.matches(url)) {
int numRows = mSyncState.asContentProvider().update(
url, values, selection, selectionArgs);
mDb.setTransactionSuccessful();
return numRows;
}
int result = updateInternal(url, values, selection, selectionArgs);
mDb.setTransactionSuccessful();
if (!isTemporary() && result > 0) {
getContext().getContentResolver().notifyChange(url, null /* observer */,
changeRequiresLocalSync(url));
}
return result;
} finally {
mDb.endTransaction();
}
|
protected abstract int | updateInternal(android.net.Uri url, ContentValues values, java.lang.String selection, java.lang.String[] selectionArgs)Subclasses should override this instead of update(). See update()
for details.
This method is called within a acquireDbLock()/releaseDbLock() block,
which means a database transaction will be active during the call;
|
protected abstract boolean | upgradeDatabase(android.database.sqlite.SQLiteDatabase db, int oldVersion, int newVersion)Override to upgrade your database from an old version to the version you specified.
Don't set the DB version; this will automatically be done after the method returns.
This method may not use getDatabase(), or call content provider methods, it must only
use the database handle passed to it.
|
public void | wipeAccount(java.lang.String account)Called when the sync system determines that this provider should no longer
contain records for the specified account.
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Map<String, String> tableMap = db.getSyncedTables();
ArrayList<String> tables = new ArrayList<String>();
tables.addAll(tableMap.keySet());
tables.addAll(tableMap.values());
db.beginTransaction();
try {
// remove the SyncState data
mSyncState.discardSyncData(db, account);
// remove the data in the synced tables
for (String table : tables) {
db.delete(table, SYNC_ACCOUNT_WHERE_CLAUSE, new String[]{account});
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
|
public void | writeSyncDataBytes(java.lang.String account, byte[] data)Sets the SyncData bytes for the given account. The byte array may be null.
mSyncState.writeSyncDataBytes(mOpenHelper.getWritableDatabase(), account, data);
|