mirror of https://github.com/M66B/FairEmail.git
870 lines
28 KiB
Java
870 lines
28 KiB
Java
/*
|
|
* Copyright (C) 2020 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 androidx.room;
|
|
|
|
import android.content.ContentResolver;
|
|
import android.content.ContentValues;
|
|
import android.database.CharArrayBuffer;
|
|
import android.database.ContentObserver;
|
|
import android.database.Cursor;
|
|
import android.database.DataSetObserver;
|
|
import android.database.SQLException;
|
|
import android.database.sqlite.SQLiteTransactionListener;
|
|
import android.net.Uri;
|
|
import android.os.Build;
|
|
import android.os.Bundle;
|
|
import android.os.CancellationSignal;
|
|
import android.util.Pair;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.annotation.RequiresApi;
|
|
import androidx.arch.core.util.Function;
|
|
import androidx.room.util.SneakyThrow;
|
|
import androidx.sqlite.db.SupportSQLiteCompat;
|
|
import androidx.sqlite.db.SupportSQLiteDatabase;
|
|
import androidx.sqlite.db.SupportSQLiteOpenHelper;
|
|
import androidx.sqlite.db.SupportSQLiteQuery;
|
|
import androidx.sqlite.db.SupportSQLiteStatement;
|
|
|
|
import java.io.IOException;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
|
|
/**
|
|
* A SupportSQLiteOpenHelper that has autoclose enabled for database connections.
|
|
*/
|
|
final class AutoClosingRoomOpenHelper implements SupportSQLiteOpenHelper, DelegatingOpenHelper {
|
|
@NonNull
|
|
private final SupportSQLiteOpenHelper mDelegateOpenHelper;
|
|
|
|
@NonNull
|
|
private final AutoClosingSupportSQLiteDatabase mAutoClosingDb;
|
|
|
|
@NonNull
|
|
private final AutoCloser mAutoCloser;
|
|
|
|
AutoClosingRoomOpenHelper(@NonNull SupportSQLiteOpenHelper supportSQLiteOpenHelper,
|
|
@NonNull AutoCloser autoCloser) {
|
|
mDelegateOpenHelper = supportSQLiteOpenHelper;
|
|
mAutoCloser = autoCloser;
|
|
autoCloser.init(mDelegateOpenHelper);
|
|
mAutoClosingDb = new AutoClosingSupportSQLiteDatabase(mAutoCloser);
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public String getDatabaseName() {
|
|
return mDelegateOpenHelper.getDatabaseName();
|
|
}
|
|
|
|
@Override
|
|
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
|
|
public void setWriteAheadLoggingEnabled(boolean enabled) {
|
|
mDelegateOpenHelper.setWriteAheadLoggingEnabled(enabled);
|
|
}
|
|
|
|
@NonNull
|
|
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
@Override
|
|
public SupportSQLiteDatabase getWritableDatabase() {
|
|
// Note we don't differentiate between writable db and readable db
|
|
// We try to open the db so the open callbacks run
|
|
mAutoClosingDb.pokeOpen();
|
|
return mAutoClosingDb;
|
|
}
|
|
|
|
@NonNull
|
|
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
@Override
|
|
public SupportSQLiteDatabase getReadableDatabase() {
|
|
// Note we don't differentiate between writable db and readable db
|
|
// We try to open the db so the open callbacks run
|
|
mAutoClosingDb.pokeOpen();
|
|
return mAutoClosingDb;
|
|
}
|
|
|
|
@Override
|
|
public void close() {
|
|
try {
|
|
mAutoClosingDb.close();
|
|
} catch (IOException e) {
|
|
SneakyThrow.reThrow(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* package protected to pass it to invalidation tracker...
|
|
*/
|
|
@NonNull
|
|
AutoCloser getAutoCloser() {
|
|
return this.mAutoCloser;
|
|
}
|
|
|
|
@NonNull
|
|
SupportSQLiteDatabase getAutoClosingDb() {
|
|
return this.mAutoClosingDb;
|
|
}
|
|
|
|
@Override
|
|
@NonNull
|
|
public SupportSQLiteOpenHelper getDelegate() {
|
|
return mDelegateOpenHelper;
|
|
}
|
|
|
|
/**
|
|
* SupportSQLiteDatabase that also keeps refcounts and autocloses the database
|
|
*/
|
|
static final class AutoClosingSupportSQLiteDatabase implements SupportSQLiteDatabase {
|
|
@NonNull
|
|
private final AutoCloser mAutoCloser;
|
|
|
|
AutoClosingSupportSQLiteDatabase(@NonNull AutoCloser autoCloser) {
|
|
mAutoCloser = autoCloser;
|
|
}
|
|
|
|
void pokeOpen() {
|
|
mAutoCloser.executeRefCountingFunction(db -> null);
|
|
}
|
|
|
|
@Override
|
|
public SupportSQLiteStatement compileStatement(String sql) {
|
|
return new AutoClosingSupportSqliteStatement(sql, mAutoCloser);
|
|
}
|
|
|
|
@Override
|
|
public void beginTransaction() {
|
|
// We assume that after every successful beginTransaction() call there *must* be a
|
|
// endTransaction() call.
|
|
SupportSQLiteDatabase db = mAutoCloser.incrementCountAndEnsureDbIsOpen();
|
|
try {
|
|
db.beginTransaction();
|
|
} catch (Throwable t) {
|
|
// Note: we only want to decrement the ref count if the beginTransaction call
|
|
// fails since there won't be a corresponding endTransaction call.
|
|
mAutoCloser.decrementCountAndScheduleClose();
|
|
throw t;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void beginTransactionNonExclusive() {
|
|
// We assume that after every successful beginTransaction() call there *must* be a
|
|
// endTransaction() call.
|
|
SupportSQLiteDatabase db = mAutoCloser.incrementCountAndEnsureDbIsOpen();
|
|
try {
|
|
db.beginTransactionNonExclusive();
|
|
} catch (Throwable t) {
|
|
// Note: we only want to decrement the ref count if the beginTransaction call
|
|
// fails since there won't be a corresponding endTransaction call.
|
|
mAutoCloser.decrementCountAndScheduleClose();
|
|
throw t;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void beginTransactionWithListener(SQLiteTransactionListener transactionListener) {
|
|
// We assume that after every successful beginTransaction() call there *must* be a
|
|
// endTransaction() call.
|
|
SupportSQLiteDatabase db = mAutoCloser.incrementCountAndEnsureDbIsOpen();
|
|
try {
|
|
db.beginTransactionWithListener(transactionListener);
|
|
} catch (Throwable t) {
|
|
// Note: we only want to decrement the ref count if the beginTransaction call
|
|
// fails since there won't be a corresponding endTransaction call.
|
|
mAutoCloser.decrementCountAndScheduleClose();
|
|
throw t;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void beginTransactionWithListenerNonExclusive(
|
|
SQLiteTransactionListener transactionListener) {
|
|
// We assume that after every successful beginTransaction() call there *will* always
|
|
// be a corresponding endTransaction() call. Without a corresponding
|
|
// endTransactionCall we will never close the db.
|
|
SupportSQLiteDatabase db = mAutoCloser.incrementCountAndEnsureDbIsOpen();
|
|
try {
|
|
db.beginTransactionWithListenerNonExclusive(transactionListener);
|
|
} catch (Throwable t) {
|
|
// Note: we only want to decrement the ref count if the beginTransaction call
|
|
// fails since there won't be a corresponding endTransaction call.
|
|
mAutoCloser.decrementCountAndScheduleClose();
|
|
throw t;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void endTransaction() {
|
|
if (mAutoCloser.getDelegateDatabase() == null) {
|
|
// This should never happen.
|
|
throw new IllegalStateException("End transaction called but delegateDb is null");
|
|
}
|
|
|
|
try {
|
|
mAutoCloser.getDelegateDatabase().endTransaction();
|
|
} finally {
|
|
mAutoCloser.decrementCountAndScheduleClose();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setTransactionSuccessful() {
|
|
SupportSQLiteDatabase delegate = mAutoCloser.getDelegateDatabase();
|
|
|
|
if (delegate == null) {
|
|
// This should never happen.
|
|
throw new IllegalStateException("setTransactionSuccessful called but delegateDb "
|
|
+ "is null");
|
|
}
|
|
|
|
delegate.setTransactionSuccessful();
|
|
}
|
|
|
|
@Override
|
|
public boolean inTransaction() {
|
|
if (mAutoCloser.getDelegateDatabase() == null) {
|
|
return false;
|
|
}
|
|
return mAutoCloser.executeRefCountingFunction(SupportSQLiteDatabase::inTransaction);
|
|
}
|
|
|
|
@Override
|
|
public boolean isDbLockedByCurrentThread() {
|
|
if (mAutoCloser.getDelegateDatabase() == null) {
|
|
return false;
|
|
}
|
|
|
|
return mAutoCloser.executeRefCountingFunction(
|
|
SupportSQLiteDatabase::isDbLockedByCurrentThread);
|
|
}
|
|
|
|
@Override
|
|
public boolean yieldIfContendedSafely() {
|
|
return mAutoCloser.executeRefCountingFunction(
|
|
SupportSQLiteDatabase::yieldIfContendedSafely);
|
|
}
|
|
|
|
@Override
|
|
public boolean yieldIfContendedSafely(long sleepAfterYieldDelay) {
|
|
return mAutoCloser.executeRefCountingFunction(
|
|
SupportSQLiteDatabase::yieldIfContendedSafely);
|
|
|
|
}
|
|
|
|
@Override
|
|
public int getVersion() {
|
|
return mAutoCloser.executeRefCountingFunction(SupportSQLiteDatabase::getVersion);
|
|
}
|
|
|
|
@Override
|
|
public void setVersion(int version) {
|
|
mAutoCloser.executeRefCountingFunction(db -> {
|
|
db.setVersion(version);
|
|
return null;
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public long getMaximumSize() {
|
|
return mAutoCloser.executeRefCountingFunction(SupportSQLiteDatabase::getMaximumSize);
|
|
}
|
|
|
|
@Override
|
|
public long setMaximumSize(long numBytes) {
|
|
return mAutoCloser.executeRefCountingFunction(db -> db.setMaximumSize(numBytes));
|
|
}
|
|
|
|
@Override
|
|
public long getPageSize() {
|
|
return mAutoCloser.executeRefCountingFunction(SupportSQLiteDatabase::getPageSize);
|
|
}
|
|
|
|
@Override
|
|
public void setPageSize(long numBytes) {
|
|
mAutoCloser.executeRefCountingFunction(db -> {
|
|
db.setPageSize(numBytes);
|
|
return null;
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public Cursor query(String query) {
|
|
Cursor result;
|
|
try {
|
|
SupportSQLiteDatabase db = mAutoCloser.incrementCountAndEnsureDbIsOpen();
|
|
result = db.query(query);
|
|
} catch (Throwable throwable) {
|
|
mAutoCloser.decrementCountAndScheduleClose();
|
|
throw throwable;
|
|
}
|
|
|
|
return new KeepAliveCursor(result, mAutoCloser);
|
|
}
|
|
|
|
@Override
|
|
public Cursor query(String query, Object[] bindArgs) {
|
|
Cursor result;
|
|
try {
|
|
SupportSQLiteDatabase db = mAutoCloser.incrementCountAndEnsureDbIsOpen();
|
|
result = db.query(query, bindArgs);
|
|
} catch (Throwable throwable) {
|
|
mAutoCloser.decrementCountAndScheduleClose();
|
|
throw throwable;
|
|
}
|
|
|
|
return new KeepAliveCursor(result, mAutoCloser);
|
|
}
|
|
|
|
@Override
|
|
public Cursor query(SupportSQLiteQuery query) {
|
|
|
|
Cursor result;
|
|
try {
|
|
SupportSQLiteDatabase db = mAutoCloser.incrementCountAndEnsureDbIsOpen();
|
|
result = db.query(query);
|
|
} catch (Throwable throwable) {
|
|
mAutoCloser.decrementCountAndScheduleClose();
|
|
throw throwable;
|
|
}
|
|
|
|
return new KeepAliveCursor(result, mAutoCloser);
|
|
}
|
|
|
|
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
@Override
|
|
public Cursor query(SupportSQLiteQuery query, CancellationSignal cancellationSignal) {
|
|
Cursor result;
|
|
try {
|
|
SupportSQLiteDatabase db = mAutoCloser.incrementCountAndEnsureDbIsOpen();
|
|
result = db.query(query, cancellationSignal);
|
|
} catch (Throwable throwable) {
|
|
mAutoCloser.decrementCountAndScheduleClose();
|
|
throw throwable;
|
|
}
|
|
|
|
return new KeepAliveCursor(result, mAutoCloser);
|
|
}
|
|
|
|
@Override
|
|
public long insert(String table, int conflictAlgorithm, ContentValues values)
|
|
throws SQLException {
|
|
return mAutoCloser.executeRefCountingFunction(db -> db.insert(table, conflictAlgorithm,
|
|
values));
|
|
}
|
|
|
|
@Override
|
|
public int delete(String table, String whereClause, Object[] whereArgs) {
|
|
return mAutoCloser.executeRefCountingFunction(
|
|
db -> db.delete(table, whereClause, whereArgs));
|
|
}
|
|
|
|
@Override
|
|
public int update(String table, int conflictAlgorithm, ContentValues values,
|
|
String whereClause, Object[] whereArgs) {
|
|
return mAutoCloser.executeRefCountingFunction(db -> db.update(table, conflictAlgorithm,
|
|
values, whereClause, whereArgs));
|
|
}
|
|
|
|
@Override
|
|
public void execSQL(String sql) throws SQLException {
|
|
mAutoCloser.executeRefCountingFunction(db -> {
|
|
db.execSQL(sql);
|
|
return null;
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void execSQL(String sql, Object[] bindArgs) throws SQLException {
|
|
mAutoCloser.executeRefCountingFunction(db -> {
|
|
db.execSQL(sql, bindArgs);
|
|
return null;
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public boolean isReadOnly() {
|
|
return mAutoCloser.executeRefCountingFunction(SupportSQLiteDatabase::isReadOnly);
|
|
}
|
|
|
|
@Override
|
|
public boolean isOpen() {
|
|
// Get the db without incrementing the reference cause we don't want to open
|
|
// the db for an isOpen call.
|
|
SupportSQLiteDatabase localDelegate = mAutoCloser.getDelegateDatabase();
|
|
|
|
if (localDelegate == null) {
|
|
return false;
|
|
}
|
|
return localDelegate.isOpen();
|
|
}
|
|
|
|
@Override
|
|
public boolean needUpgrade(int newVersion) {
|
|
return mAutoCloser.executeRefCountingFunction(db -> db.needUpgrade(newVersion));
|
|
}
|
|
|
|
@Override
|
|
public String getPath() {
|
|
return mAutoCloser.executeRefCountingFunction(SupportSQLiteDatabase::getPath);
|
|
}
|
|
|
|
@Override
|
|
public void setLocale(Locale locale) {
|
|
mAutoCloser.executeRefCountingFunction(db -> {
|
|
db.setLocale(locale);
|
|
return null;
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void setMaxSqlCacheSize(int cacheSize) {
|
|
mAutoCloser.executeRefCountingFunction(db -> {
|
|
db.setMaxSqlCacheSize(cacheSize);
|
|
return null;
|
|
});
|
|
}
|
|
|
|
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
|
|
@Override
|
|
public void setForeignKeyConstraintsEnabled(boolean enable) {
|
|
mAutoCloser.executeRefCountingFunction(db -> {
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
|
db.setForeignKeyConstraintsEnabled(enable);
|
|
}
|
|
return null;
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public boolean enableWriteAheadLogging() {
|
|
throw new UnsupportedOperationException("Enable/disable write ahead logging on the "
|
|
+ "OpenHelper instead of on the database directly.");
|
|
}
|
|
|
|
@Override
|
|
public void disableWriteAheadLogging() {
|
|
throw new UnsupportedOperationException("Enable/disable write ahead logging on the "
|
|
+ "OpenHelper instead of on the database directly.");
|
|
}
|
|
|
|
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
|
|
@Override
|
|
public boolean isWriteAheadLoggingEnabled() {
|
|
return mAutoCloser.executeRefCountingFunction(db -> {
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
|
return db.isWriteAheadLoggingEnabled();
|
|
}
|
|
return false;
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public List<Pair<String, String>> getAttachedDbs() {
|
|
return mAutoCloser.executeRefCountingFunction(SupportSQLiteDatabase::getAttachedDbs);
|
|
}
|
|
|
|
@Override
|
|
public boolean isDatabaseIntegrityOk() {
|
|
return mAutoCloser.executeRefCountingFunction(
|
|
SupportSQLiteDatabase::isDatabaseIntegrityOk);
|
|
}
|
|
|
|
@Override
|
|
public void close() throws IOException {
|
|
mAutoCloser.closeDatabaseIfOpen();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* We need to keep the db alive until the cursor is closed, so we can't decrement our
|
|
* reference count until the cursor is closed. The underlying database will not close until
|
|
* this cursor is closed.
|
|
*/
|
|
private static final class KeepAliveCursor implements Cursor {
|
|
private final Cursor mDelegate;
|
|
private final AutoCloser mAutoCloser;
|
|
|
|
KeepAliveCursor(Cursor delegate, AutoCloser autoCloser) {
|
|
mDelegate = delegate;
|
|
mAutoCloser = autoCloser;
|
|
}
|
|
|
|
// close is the only important/changed method here:
|
|
@Override
|
|
public void close() {
|
|
mDelegate.close();
|
|
mAutoCloser.decrementCountAndScheduleClose();
|
|
}
|
|
|
|
@Override
|
|
public boolean isClosed() {
|
|
return mDelegate.isClosed();
|
|
}
|
|
|
|
|
|
@Override
|
|
public int getCount() {
|
|
return mDelegate.getCount();
|
|
}
|
|
|
|
@Override
|
|
public int getPosition() {
|
|
return mDelegate.getPosition();
|
|
}
|
|
|
|
@Override
|
|
public boolean move(int offset) {
|
|
return mDelegate.move(offset);
|
|
}
|
|
|
|
@Override
|
|
public boolean moveToPosition(int position) {
|
|
return mDelegate.moveToPosition(position);
|
|
}
|
|
|
|
@Override
|
|
public boolean moveToFirst() {
|
|
return mDelegate.moveToFirst();
|
|
}
|
|
|
|
@Override
|
|
public boolean moveToLast() {
|
|
return mDelegate.moveToLast();
|
|
}
|
|
|
|
@Override
|
|
public boolean moveToNext() {
|
|
return mDelegate.moveToNext();
|
|
}
|
|
|
|
@Override
|
|
public boolean moveToPrevious() {
|
|
return mDelegate.moveToPrevious();
|
|
}
|
|
|
|
@Override
|
|
public boolean isFirst() {
|
|
return mDelegate.isFirst();
|
|
}
|
|
|
|
@Override
|
|
public boolean isLast() {
|
|
return mDelegate.isLast();
|
|
}
|
|
|
|
@Override
|
|
public boolean isBeforeFirst() {
|
|
return mDelegate.isBeforeFirst();
|
|
}
|
|
|
|
@Override
|
|
public boolean isAfterLast() {
|
|
return mDelegate.isAfterLast();
|
|
}
|
|
|
|
@Override
|
|
public int getColumnIndex(String columnName) {
|
|
return mDelegate.getColumnIndex(columnName);
|
|
}
|
|
|
|
@Override
|
|
public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException {
|
|
return mDelegate.getColumnIndexOrThrow(columnName);
|
|
}
|
|
|
|
@Override
|
|
public String getColumnName(int columnIndex) {
|
|
return mDelegate.getColumnName(columnIndex);
|
|
}
|
|
|
|
@Override
|
|
public String[] getColumnNames() {
|
|
return mDelegate.getColumnNames();
|
|
}
|
|
|
|
@Override
|
|
public int getColumnCount() {
|
|
return mDelegate.getColumnCount();
|
|
}
|
|
|
|
@Override
|
|
public byte[] getBlob(int columnIndex) {
|
|
return mDelegate.getBlob(columnIndex);
|
|
}
|
|
|
|
@Override
|
|
public String getString(int columnIndex) {
|
|
return mDelegate.getString(columnIndex);
|
|
}
|
|
|
|
@Override
|
|
public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
|
|
mDelegate.copyStringToBuffer(columnIndex, buffer);
|
|
}
|
|
|
|
@Override
|
|
public short getShort(int columnIndex) {
|
|
return mDelegate.getShort(columnIndex);
|
|
}
|
|
|
|
@Override
|
|
public int getInt(int columnIndex) {
|
|
return mDelegate.getInt(columnIndex);
|
|
}
|
|
|
|
@Override
|
|
public long getLong(int columnIndex) {
|
|
return mDelegate.getLong(columnIndex);
|
|
}
|
|
|
|
@Override
|
|
public float getFloat(int columnIndex) {
|
|
return mDelegate.getFloat(columnIndex);
|
|
}
|
|
|
|
@Override
|
|
public double getDouble(int columnIndex) {
|
|
return mDelegate.getDouble(columnIndex);
|
|
}
|
|
|
|
@Override
|
|
public int getType(int columnIndex) {
|
|
return mDelegate.getType(columnIndex);
|
|
}
|
|
|
|
@Override
|
|
public boolean isNull(int columnIndex) {
|
|
return mDelegate.isNull(columnIndex);
|
|
}
|
|
|
|
/**
|
|
* @deprecated see Cursor.deactivate
|
|
*/
|
|
@Override
|
|
@Deprecated
|
|
public void deactivate() {
|
|
mDelegate.deactivate();
|
|
}
|
|
|
|
/**
|
|
* @deprecated see Cursor.requery
|
|
*/
|
|
@Override
|
|
@Deprecated
|
|
public boolean requery() {
|
|
return mDelegate.requery();
|
|
}
|
|
|
|
@Override
|
|
public void registerContentObserver(ContentObserver observer) {
|
|
mDelegate.registerContentObserver(observer);
|
|
}
|
|
|
|
@Override
|
|
public void unregisterContentObserver(ContentObserver observer) {
|
|
mDelegate.unregisterContentObserver(observer);
|
|
}
|
|
|
|
@Override
|
|
public void registerDataSetObserver(DataSetObserver observer) {
|
|
mDelegate.registerDataSetObserver(observer);
|
|
}
|
|
|
|
@Override
|
|
public void unregisterDataSetObserver(DataSetObserver observer) {
|
|
mDelegate.unregisterDataSetObserver(observer);
|
|
}
|
|
|
|
@Override
|
|
public void setNotificationUri(ContentResolver cr, Uri uri) {
|
|
mDelegate.setNotificationUri(cr, uri);
|
|
}
|
|
|
|
@RequiresApi(api = Build.VERSION_CODES.Q)
|
|
@Override
|
|
public void setNotificationUris(@NonNull ContentResolver cr,
|
|
@NonNull List<Uri> uris) {
|
|
SupportSQLiteCompat.Api29Impl.setNotificationUris(mDelegate, cr, uris);
|
|
}
|
|
|
|
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
|
|
@Override
|
|
public Uri getNotificationUri() {
|
|
return SupportSQLiteCompat.Api19Impl.getNotificationUri(mDelegate);
|
|
}
|
|
|
|
@RequiresApi(api = Build.VERSION_CODES.Q)
|
|
@Nullable
|
|
@Override
|
|
public List<Uri> getNotificationUris() {
|
|
return SupportSQLiteCompat.Api29Impl.getNotificationUris(mDelegate);
|
|
}
|
|
|
|
@Override
|
|
public boolean getWantsAllOnMoveCalls() {
|
|
return mDelegate.getWantsAllOnMoveCalls();
|
|
}
|
|
|
|
@RequiresApi(api = Build.VERSION_CODES.M)
|
|
@Override
|
|
public void setExtras(Bundle extras) {
|
|
SupportSQLiteCompat.Api23Impl.setExtras(mDelegate, extras);
|
|
}
|
|
|
|
@Override
|
|
public Bundle getExtras() {
|
|
return mDelegate.getExtras();
|
|
}
|
|
|
|
@Override
|
|
public Bundle respond(Bundle extras) {
|
|
return mDelegate.respond(extras);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* We can't close our db if the SupportSqliteStatement is open.
|
|
*
|
|
* Each of these that are created need to be registered with RefCounter.
|
|
*
|
|
* On auto-close, RefCounter needs to close each of these before closing the db that these
|
|
* were constructed from.
|
|
*
|
|
* Each of the methods here need to get
|
|
*/
|
|
//TODO(rohitsat) cache the prepared statement... I'm not sure what the performance implications
|
|
// are for the way it's done here, but caching the prepared statement would definitely be more
|
|
// complicated since we need to invalidate any of the PreparedStatements that were created
|
|
// with this db
|
|
private static class AutoClosingSupportSqliteStatement implements SupportSQLiteStatement {
|
|
private final String mSql;
|
|
private final ArrayList<Object> mBinds = new ArrayList<>();
|
|
private final AutoCloser mAutoCloser;
|
|
|
|
AutoClosingSupportSqliteStatement(
|
|
String sql, AutoCloser autoCloser) {
|
|
mSql = sql;
|
|
mAutoCloser = autoCloser;
|
|
}
|
|
|
|
private <T> T executeSqliteStatementWithRefCount(Function<SupportSQLiteStatement, T> func) {
|
|
return mAutoCloser.executeRefCountingFunction(
|
|
db -> {
|
|
SupportSQLiteStatement statement = db.compileStatement(mSql);
|
|
doBinds(statement);
|
|
return func.apply(statement);
|
|
}
|
|
);
|
|
}
|
|
|
|
private void doBinds(SupportSQLiteStatement supportSQLiteStatement) {
|
|
// Replay the binds
|
|
for (int i = 0; i < mBinds.size(); i++) {
|
|
int bindIndex = i + 1; // Bind indices are 1 based so we start at 1 not 0
|
|
Object bind = mBinds.get(i);
|
|
if (bind == null) {
|
|
supportSQLiteStatement.bindNull(bindIndex);
|
|
} else if (bind instanceof Long) {
|
|
supportSQLiteStatement.bindLong(bindIndex, (Long) bind);
|
|
} else if (bind instanceof Double) {
|
|
supportSQLiteStatement.bindDouble(bindIndex, (Double) bind);
|
|
} else if (bind instanceof String) {
|
|
supportSQLiteStatement.bindString(bindIndex, (String) bind);
|
|
} else if (bind instanceof byte[]) {
|
|
supportSQLiteStatement.bindBlob(bindIndex, (byte[]) bind);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void saveBinds(int bindIndex, Object value) {
|
|
int index = bindIndex - 1;
|
|
if (index >= mBinds.size()) {
|
|
// Add null entries to the list until we have the desired # of indices
|
|
for (int i = mBinds.size(); i <= index; i++) {
|
|
mBinds.add(null);
|
|
}
|
|
}
|
|
mBinds.set(index, value);
|
|
}
|
|
|
|
@Override
|
|
public void close() throws IOException {
|
|
// Nothing to do here since we re-compile the statement each time.
|
|
}
|
|
|
|
@Override
|
|
public void execute() {
|
|
executeSqliteStatementWithRefCount(statement -> {
|
|
statement.execute();
|
|
return null;
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public int executeUpdateDelete() {
|
|
return executeSqliteStatementWithRefCount(SupportSQLiteStatement::executeUpdateDelete);
|
|
}
|
|
|
|
@Override
|
|
public long executeInsert() {
|
|
return executeSqliteStatementWithRefCount(SupportSQLiteStatement::executeInsert);
|
|
}
|
|
|
|
@Override
|
|
public long simpleQueryForLong() {
|
|
return executeSqliteStatementWithRefCount(SupportSQLiteStatement::simpleQueryForLong);
|
|
}
|
|
|
|
@Override
|
|
public String simpleQueryForString() {
|
|
return executeSqliteStatementWithRefCount(SupportSQLiteStatement::simpleQueryForString);
|
|
}
|
|
|
|
@Override
|
|
public void bindNull(int index) {
|
|
saveBinds(index, null);
|
|
}
|
|
|
|
@Override
|
|
public void bindLong(int index, long value) {
|
|
saveBinds(index, value);
|
|
}
|
|
|
|
@Override
|
|
public void bindDouble(int index, double value) {
|
|
saveBinds(index, value);
|
|
}
|
|
|
|
@Override
|
|
public void bindString(int index, String value) {
|
|
saveBinds(index, value);
|
|
}
|
|
|
|
@Override
|
|
public void bindBlob(int index, byte[] value) {
|
|
saveBinds(index, value);
|
|
}
|
|
|
|
@Override
|
|
public void clearBindings() {
|
|
mBinds.clear();
|
|
}
|
|
}
|
|
}
|