mirror of https://github.com/M66B/FairEmail.git
Updated AndroidX
This commit is contained in:
parent
b99088d1ad
commit
5a5d3ee1a0
|
@ -7,6 +7,7 @@
|
|||
### Next version
|
||||
|
||||
* Small improvements and minor bug fixes
|
||||
* Updated AndroidX
|
||||
|
||||
### 1.1789 - 2021-12-14
|
||||
|
||||
|
|
|
@ -297,16 +297,16 @@ dependencies {
|
|||
//implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
def startup_version = "1.1.0"
|
||||
def annotation_version_experimental = "1.1.0" // 1.2.0-rc01
|
||||
def annotation_version_experimental = "1.2.0"
|
||||
def core_version = "1.6.0" // 1.7.0
|
||||
def shortcuts_version = "1.0.0"
|
||||
def appcompat_version = "1.3.1"
|
||||
def emoji_version = "1.0.0"
|
||||
def emoji_version = "1.0.1"
|
||||
def activity_version = "1.4.0"
|
||||
def fragment_version = "1.4.0"
|
||||
def webkit_version = "1.4.0"
|
||||
def recyclerview_version = "1.2.1"
|
||||
def coordinatorlayout_version = "1.1.0" // 1.2.0-beta01
|
||||
def coordinatorlayout_version = "1.1.0" // 1.2.0-rc01
|
||||
def constraintlayout_version = "2.1.2"
|
||||
def material_version = "1.4.0"
|
||||
def browser_version = "1.4.0"
|
||||
|
@ -315,8 +315,8 @@ dependencies {
|
|||
def documentfile_version = "1.1.0-alpha01"
|
||||
def lifecycle_version = "2.4.0"
|
||||
def lifecycle_extensions_version = "2.2.0"
|
||||
def room_version = "2.3.0" // 2.4.0-rc01
|
||||
def sqlite_version = "2.1.0" // 2.2.0-rc01
|
||||
def room_version = "2.4.0"
|
||||
def sqlite_version = "2.2.0"
|
||||
def requery_version = "3.36.0"
|
||||
def paging_version = "2.1.2" // 3.1.0
|
||||
def preference_version = "1.1.1" // 1.2.0-alpha02
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
### Next version
|
||||
|
||||
* Small improvements and minor bug fixes
|
||||
* Updated AndroidX
|
||||
|
||||
### 1.1789 - 2021-12-14
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package androidx.room;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.database.CharArrayBuffer;
|
||||
|
@ -36,6 +35,7 @@ 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;
|
||||
|
@ -440,7 +440,6 @@ final class AutoClosingRoomOpenHelper implements SupportSQLiteOpenHelper, Delega
|
|||
});
|
||||
}
|
||||
|
||||
@SuppressLint("UnsafeNewApiCall")
|
||||
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
|
||||
@Override
|
||||
public void setForeignKeyConstraintsEnabled(boolean enable) {
|
||||
|
@ -464,7 +463,6 @@ final class AutoClosingRoomOpenHelper implements SupportSQLiteOpenHelper, Delega
|
|||
+ "OpenHelper instead of on the database directly.");
|
||||
}
|
||||
|
||||
@SuppressLint("UnsafeNewApiCall")
|
||||
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
|
||||
@Override
|
||||
public boolean isWriteAheadLoggingEnabled() {
|
||||
|
@ -698,27 +696,24 @@ final class AutoClosingRoomOpenHelper implements SupportSQLiteOpenHelper, Delega
|
|||
mDelegate.setNotificationUri(cr, uri);
|
||||
}
|
||||
|
||||
@SuppressLint("UnsafeNewApiCall")
|
||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
@Override
|
||||
public void setNotificationUris(@NonNull ContentResolver cr,
|
||||
@NonNull List<Uri> uris) {
|
||||
mDelegate.setNotificationUris(cr, uris);
|
||||
SupportSQLiteCompat.Api29Impl.setNotificationUris(mDelegate, cr, uris);
|
||||
}
|
||||
|
||||
@SuppressLint("UnsafeNewApiCall")
|
||||
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
|
||||
@Override
|
||||
public Uri getNotificationUri() {
|
||||
return mDelegate.getNotificationUri();
|
||||
return SupportSQLiteCompat.Api19Impl.getNotificationUri(mDelegate);
|
||||
}
|
||||
|
||||
@SuppressLint("UnsafeNewApiCall")
|
||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
@Nullable
|
||||
@Override
|
||||
public List<Uri> getNotificationUris() {
|
||||
return mDelegate.getNotificationUris();
|
||||
return SupportSQLiteCompat.Api29Impl.getNotificationUris(mDelegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -726,11 +721,10 @@ final class AutoClosingRoomOpenHelper implements SupportSQLiteOpenHelper, Delega
|
|||
return mDelegate.getWantsAllOnMoveCalls();
|
||||
}
|
||||
|
||||
@SuppressLint("UnsafeNewApiCall")
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
@Override
|
||||
public void setExtras(Bundle extras) {
|
||||
mDelegate.setExtras(extras);
|
||||
SupportSQLiteCompat.Api23Impl.setExtras(mDelegate, extras);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,10 +18,12 @@ package androidx.room;
|
|||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RestrictTo;
|
||||
import androidx.room.migration.AutoMigrationSpec;
|
||||
import androidx.sqlite.db.SupportSQLiteOpenHelper;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -69,6 +71,9 @@ public class DatabaseConfiguration {
|
|||
@NonNull
|
||||
public final List<Object> typeConverters;
|
||||
|
||||
@NonNull
|
||||
public final List<AutoMigrationSpec> autoMigrationSpecs;
|
||||
|
||||
/**
|
||||
* Whether Room should throw an exception for queries run on the main thread.
|
||||
*/
|
||||
|
@ -98,6 +103,15 @@ public class DatabaseConfiguration {
|
|||
*/
|
||||
public final boolean multiInstanceInvalidation;
|
||||
|
||||
/**
|
||||
* Intent that should be bound to acquire the invalidation service or {@code null} if not used.
|
||||
*
|
||||
* @see {@link #multiInstanceInvalidation}
|
||||
* @hide
|
||||
*/
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
|
||||
public final Intent multiInstanceInvalidationServiceIntent;
|
||||
|
||||
/**
|
||||
* If true, Room should crash if a migration is missing.
|
||||
*/
|
||||
|
@ -133,14 +147,13 @@ public class DatabaseConfiguration {
|
|||
@Nullable
|
||||
public final Callable<InputStream> copyFromInputStream;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a database configuration with the given values.
|
||||
*
|
||||
* @deprecated Use {@link #DatabaseConfiguration(Context, String,
|
||||
* SupportSQLiteOpenHelper.Factory, RoomDatabase.MigrationContainer, List, boolean,
|
||||
* RoomDatabase.JournalMode, Executor, Executor, boolean, boolean, boolean, Set, String, File,
|
||||
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List<Object>)}
|
||||
* RoomDatabase.JournalMode, Executor, Executor, Intent, boolean, boolean, Set, String, File,
|
||||
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List, List)}
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param name Name of the database, can be null if it is in memory.
|
||||
|
@ -170,7 +183,8 @@ public class DatabaseConfiguration {
|
|||
@Nullable Set<Integer> migrationNotRequiredFrom) {
|
||||
this(context, name, sqliteOpenHelperFactory, migrationContainer, callbacks,
|
||||
allowMainThreadQueries, journalMode, queryExecutor, queryExecutor, false,
|
||||
requireMigration, false, migrationNotRequiredFrom, null, null, null, null, null);
|
||||
requireMigration, false, migrationNotRequiredFrom, null, null, null, null, null,
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -178,8 +192,8 @@ public class DatabaseConfiguration {
|
|||
*
|
||||
* @deprecated Use {@link #DatabaseConfiguration(Context, String,
|
||||
* SupportSQLiteOpenHelper.Factory, RoomDatabase.MigrationContainer, List, boolean,
|
||||
* RoomDatabase.JournalMode, Executor, Executor, boolean, boolean, boolean, Set, String, File,
|
||||
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List<Object>)}
|
||||
* RoomDatabase.JournalMode, Executor, Executor, Intent, boolean, boolean, Set, String, File,
|
||||
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List, List)}
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param name Name of the database, can be null if it is in memory.
|
||||
|
@ -216,7 +230,7 @@ public class DatabaseConfiguration {
|
|||
this(context, name, sqliteOpenHelperFactory, migrationContainer, callbacks,
|
||||
allowMainThreadQueries, journalMode, queryExecutor, transactionExecutor,
|
||||
multiInstanceInvalidation, requireMigration, allowDestructiveMigrationOnDowngrade,
|
||||
migrationNotRequiredFrom, null, null, null, null, null);
|
||||
migrationNotRequiredFrom, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -224,8 +238,8 @@ public class DatabaseConfiguration {
|
|||
*
|
||||
* @deprecated Use {@link #DatabaseConfiguration(Context, String,
|
||||
* SupportSQLiteOpenHelper.Factory, RoomDatabase.MigrationContainer, List, boolean,
|
||||
* RoomDatabase.JournalMode, Executor, Executor, boolean, boolean, boolean, Set, String, File,
|
||||
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List<Object>)}
|
||||
* RoomDatabase.JournalMode, Executor, Executor, Intent, boolean, boolean, Set, String, File,
|
||||
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List, List)}
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param name Name of the database, can be null if it is in memory.
|
||||
|
@ -266,7 +280,7 @@ public class DatabaseConfiguration {
|
|||
this(context, name, sqliteOpenHelperFactory, migrationContainer, callbacks,
|
||||
allowMainThreadQueries, journalMode, queryExecutor, transactionExecutor,
|
||||
multiInstanceInvalidation, requireMigration, allowDestructiveMigrationOnDowngrade,
|
||||
migrationNotRequiredFrom, copyFromAssetPath, copyFromFile, null, null, null);
|
||||
migrationNotRequiredFrom, copyFromAssetPath, copyFromFile, null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -274,8 +288,8 @@ public class DatabaseConfiguration {
|
|||
*
|
||||
* @deprecated Use {@link #DatabaseConfiguration(Context, String,
|
||||
* SupportSQLiteOpenHelper.Factory, RoomDatabase.MigrationContainer, List, boolean,
|
||||
* RoomDatabase.JournalMode, Executor, Executor, boolean, boolean, boolean, Set, String, File,
|
||||
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List<Object>)}
|
||||
* RoomDatabase.JournalMode, Executor, Executor, Intent, boolean, boolean, Set, String, File,
|
||||
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List, List)}
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param name Name of the database, can be null if it is in memory.
|
||||
|
@ -320,16 +334,16 @@ public class DatabaseConfiguration {
|
|||
allowMainThreadQueries, journalMode, queryExecutor, transactionExecutor,
|
||||
multiInstanceInvalidation, requireMigration, allowDestructiveMigrationOnDowngrade,
|
||||
migrationNotRequiredFrom, copyFromAssetPath, copyFromFile, copyFromInputStream,
|
||||
null, null);
|
||||
null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a database configuration with the given values.
|
||||
*
|
||||
* @deprecated Use {@link #DatabaseConfiguration(Context, String,
|
||||
/**
|
||||
* Creates a database configuration with the given values.
|
||||
*
|
||||
* @deprecated Use {@link #DatabaseConfiguration(Context, String,
|
||||
* SupportSQLiteOpenHelper.Factory, RoomDatabase.MigrationContainer, List, boolean,
|
||||
* RoomDatabase.JournalMode, Executor, Executor, boolean, boolean, boolean, Set, String, File,
|
||||
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List<Object>)}
|
||||
* RoomDatabase.JournalMode, Executor, Executor, Intent, boolean, boolean, Set, String, File,
|
||||
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List, List)}
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param name Name of the database, can be null if it is in memory.
|
||||
|
@ -377,12 +391,17 @@ public class DatabaseConfiguration {
|
|||
allowMainThreadQueries, journalMode, queryExecutor, transactionExecutor,
|
||||
multiInstanceInvalidation, requireMigration, allowDestructiveMigrationOnDowngrade,
|
||||
migrationNotRequiredFrom, copyFromAssetPath, copyFromFile, copyFromInputStream,
|
||||
prepackagedDatabaseCallback, null);
|
||||
prepackagedDatabaseCallback, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a database configuration with the given values.
|
||||
*
|
||||
* @deprecated Use {@link #DatabaseConfiguration(Context, String,
|
||||
* SupportSQLiteOpenHelper.Factory, RoomDatabase.MigrationContainer, List, boolean,
|
||||
* RoomDatabase.JournalMode, Executor, Executor, Intent, boolean, boolean, Set, String, File,
|
||||
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List, List)}
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param name Name of the database, can be null if it is in memory.
|
||||
* @param sqliteOpenHelperFactory The open helper factory to use.
|
||||
|
@ -407,6 +426,7 @@ public class DatabaseConfiguration {
|
|||
*
|
||||
* @hide
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressLint("LambdaLast")
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
|
||||
public DatabaseConfiguration(@NonNull Context context, @Nullable String name,
|
||||
|
@ -426,6 +446,126 @@ public class DatabaseConfiguration {
|
|||
@Nullable Callable<InputStream> copyFromInputStream,
|
||||
@Nullable RoomDatabase.PrepackagedDatabaseCallback prepackagedDatabaseCallback,
|
||||
@Nullable List<Object> typeConverters) {
|
||||
this(context, name, sqliteOpenHelperFactory, migrationContainer, callbacks,
|
||||
allowMainThreadQueries, journalMode, queryExecutor, transactionExecutor,
|
||||
multiInstanceInvalidation, requireMigration, allowDestructiveMigrationOnDowngrade,
|
||||
migrationNotRequiredFrom, copyFromAssetPath, copyFromFile, copyFromInputStream,
|
||||
prepackagedDatabaseCallback, typeConverters, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a database configuration with the given values.
|
||||
*
|
||||
* @deprecated Use {@link #DatabaseConfiguration(Context, String,
|
||||
* SupportSQLiteOpenHelper.Factory, RoomDatabase.MigrationContainer, List, boolean,
|
||||
* RoomDatabase.JournalMode, Executor, Executor, Intent, boolean, boolean, Set, String, File,
|
||||
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List, List)}
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param name Name of the database, can be null if it is in memory.
|
||||
* @param sqliteOpenHelperFactory The open helper factory to use.
|
||||
* @param migrationContainer The migration container for migrations.
|
||||
* @param callbacks The list of callbacks for database events.
|
||||
* @param allowMainThreadQueries Whether to allow main thread reads/writes or not.
|
||||
* @param journalMode The journal mode. This has to be either TRUNCATE or WRITE_AHEAD_LOGGING.
|
||||
* @param queryExecutor The Executor used to execute asynchronous queries.
|
||||
* @param transactionExecutor The Executor used to execute asynchronous transactions.
|
||||
* @param multiInstanceInvalidation True if Room should perform multi-instance invalidation.
|
||||
* @param requireMigration True if Room should require a valid migration if version changes,
|
||||
* @param allowDestructiveMigrationOnDowngrade True if Room should recreate tables if no
|
||||
* migration is supplied during a downgrade.
|
||||
* @param migrationNotRequiredFrom The collection of schema versions from which migrations
|
||||
* aren't required.
|
||||
* @param copyFromAssetPath The assets path to the pre-packaged database.
|
||||
* @param copyFromFile The pre-packaged database file.
|
||||
* @param copyFromInputStream The callable to get the input stream from which a
|
||||
* pre-package database file will be copied from.
|
||||
* @param prepackagedDatabaseCallback The pre-packaged callback.
|
||||
* @param typeConverters The type converters.
|
||||
* @param autoMigrationSpecs The auto migration specs.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressLint("LambdaLast")
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
|
||||
public DatabaseConfiguration(@NonNull Context context, @Nullable String name,
|
||||
@NonNull SupportSQLiteOpenHelper.Factory sqliteOpenHelperFactory,
|
||||
@NonNull RoomDatabase.MigrationContainer migrationContainer,
|
||||
@Nullable List<RoomDatabase.Callback> callbacks,
|
||||
boolean allowMainThreadQueries,
|
||||
@NonNull RoomDatabase.JournalMode journalMode,
|
||||
@NonNull Executor queryExecutor,
|
||||
@NonNull Executor transactionExecutor,
|
||||
boolean multiInstanceInvalidation,
|
||||
boolean requireMigration,
|
||||
boolean allowDestructiveMigrationOnDowngrade,
|
||||
@Nullable Set<Integer> migrationNotRequiredFrom,
|
||||
@Nullable String copyFromAssetPath,
|
||||
@Nullable File copyFromFile,
|
||||
@Nullable Callable<InputStream> copyFromInputStream,
|
||||
@Nullable RoomDatabase.PrepackagedDatabaseCallback prepackagedDatabaseCallback,
|
||||
@Nullable List<Object> typeConverters,
|
||||
@Nullable List<AutoMigrationSpec> autoMigrationSpecs) {
|
||||
this(context, name, sqliteOpenHelperFactory, migrationContainer, callbacks,
|
||||
allowMainThreadQueries, journalMode, queryExecutor, transactionExecutor,
|
||||
multiInstanceInvalidation ? new Intent(context,
|
||||
MultiInstanceInvalidationService.class) : null,
|
||||
requireMigration, allowDestructiveMigrationOnDowngrade, migrationNotRequiredFrom,
|
||||
copyFromAssetPath, copyFromFile, copyFromInputStream, prepackagedDatabaseCallback,
|
||||
typeConverters, autoMigrationSpecs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a database configuration with the given values.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param name Name of the database, can be null if it is in memory.
|
||||
* @param sqliteOpenHelperFactory The open helper factory to use.
|
||||
* @param migrationContainer The migration container for migrations.
|
||||
* @param callbacks The list of callbacks for database events.
|
||||
* @param allowMainThreadQueries Whether to allow main thread reads/writes or not.
|
||||
* @param journalMode The journal mode. This has to be either TRUNCATE or WRITE_AHEAD_LOGGING.
|
||||
* @param queryExecutor The Executor used to execute asynchronous queries.
|
||||
* @param transactionExecutor The Executor used to execute asynchronous transactions.
|
||||
* @param multiInstanceInvalidationServiceIntent The intent to use to bind to the
|
||||
* invalidation service or {@code null} if not
|
||||
* used.
|
||||
* @param requireMigration True if Room should require a valid migration if version changes,
|
||||
* @param allowDestructiveMigrationOnDowngrade True if Room should recreate tables if no
|
||||
* migration is supplied during a downgrade.
|
||||
* @param migrationNotRequiredFrom The collection of schema versions from which migrations
|
||||
* aren't required.
|
||||
* @param copyFromAssetPath The assets path to the pre-packaged database.
|
||||
* @param copyFromFile The pre-packaged database file.
|
||||
* @param copyFromInputStream The callable to get the input stream from which a
|
||||
* pre-package database file will be copied from.
|
||||
* @param prepackagedDatabaseCallback The pre-packaged callback.
|
||||
* @param typeConverters The type converters.
|
||||
* @param autoMigrationSpecs The auto migration specs.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SuppressLint("LambdaLast")
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
|
||||
public DatabaseConfiguration(@NonNull Context context, @Nullable String name,
|
||||
@NonNull SupportSQLiteOpenHelper.Factory sqliteOpenHelperFactory,
|
||||
@NonNull RoomDatabase.MigrationContainer migrationContainer,
|
||||
@Nullable List<RoomDatabase.Callback> callbacks,
|
||||
boolean allowMainThreadQueries,
|
||||
@NonNull RoomDatabase.JournalMode journalMode,
|
||||
@NonNull Executor queryExecutor,
|
||||
@NonNull Executor transactionExecutor,
|
||||
@Nullable Intent multiInstanceInvalidationServiceIntent,
|
||||
boolean requireMigration,
|
||||
boolean allowDestructiveMigrationOnDowngrade,
|
||||
@Nullable Set<Integer> migrationNotRequiredFrom,
|
||||
@Nullable String copyFromAssetPath,
|
||||
@Nullable File copyFromFile,
|
||||
@Nullable Callable<InputStream> copyFromInputStream,
|
||||
@Nullable RoomDatabase.PrepackagedDatabaseCallback prepackagedDatabaseCallback,
|
||||
@Nullable List<Object> typeConverters,
|
||||
@Nullable List<AutoMigrationSpec> autoMigrationSpecs) {
|
||||
this.sqliteOpenHelperFactory = sqliteOpenHelperFactory;
|
||||
this.context = context;
|
||||
this.name = name;
|
||||
|
@ -435,7 +575,9 @@ public class DatabaseConfiguration {
|
|||
this.journalMode = journalMode;
|
||||
this.queryExecutor = queryExecutor;
|
||||
this.transactionExecutor = transactionExecutor;
|
||||
this.multiInstanceInvalidation = multiInstanceInvalidation;
|
||||
this.multiInstanceInvalidationServiceIntent =
|
||||
multiInstanceInvalidationServiceIntent;
|
||||
this.multiInstanceInvalidation = multiInstanceInvalidationServiceIntent != null;
|
||||
this.requireMigration = requireMigration;
|
||||
this.allowDestructiveMigrationOnDowngrade = allowDestructiveMigrationOnDowngrade;
|
||||
this.mMigrationNotRequiredFrom = migrationNotRequiredFrom;
|
||||
|
@ -444,6 +586,8 @@ public class DatabaseConfiguration {
|
|||
this.copyFromInputStream = copyFromInputStream;
|
||||
this.prepackagedDatabaseCallback = prepackagedDatabaseCallback;
|
||||
this.typeConverters = typeConverters == null ? Collections.emptyList() : typeConverters;
|
||||
this.autoMigrationSpecs = autoMigrationSpecs == null
|
||||
? Collections.emptyList() : autoMigrationSpecs;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,6 +24,6 @@ import java.lang.annotation.Target;
|
|||
/**
|
||||
* APIs marked with ExperimentalRoomApi are experimental and may change.
|
||||
*/
|
||||
@Target({ElementType.METHOD})
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@RequiresOptIn()
|
||||
public @interface ExperimentalRoomApi {}
|
||||
|
|
|
@ -18,6 +18,7 @@ package androidx.room;
|
|||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.os.Build;
|
||||
|
@ -210,9 +211,9 @@ public class InvalidationTracker {
|
|||
}
|
||||
}
|
||||
|
||||
void startMultiInstanceInvalidation(Context context, String name) {
|
||||
mMultiInstanceInvalidationClient = new MultiInstanceInvalidationClient(context, name, this,
|
||||
mDatabase.getQueryExecutor());
|
||||
void startMultiInstanceInvalidation(Context context, String name, Intent serviceIntent) {
|
||||
mMultiInstanceInvalidationClient = new MultiInstanceInvalidationClient(context, name,
|
||||
serviceIntent, this, mDatabase.getQueryExecutor());
|
||||
}
|
||||
|
||||
void stopMultiInstanceInvalidation() {
|
||||
|
@ -422,19 +423,15 @@ public class InvalidationTracker {
|
|||
return;
|
||||
}
|
||||
|
||||
if (mDatabase.mWriteAheadLoggingEnabled) {
|
||||
// This transaction has to be on the underlying DB rather than the RoomDatabase
|
||||
// in order to avoid a recursive loop after endTransaction.
|
||||
SupportSQLiteDatabase db = mDatabase.getOpenHelper().getWritableDatabase();
|
||||
db.beginTransactionNonExclusive();
|
||||
try {
|
||||
invalidatedTableIds = checkUpdatedTable();
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
} else {
|
||||
// This transaction has to be on the underlying DB rather than the RoomDatabase
|
||||
// in order to avoid a recursive loop after endTransaction.
|
||||
SupportSQLiteDatabase db = mDatabase.getOpenHelper().getWritableDatabase();
|
||||
db.beginTransactionNonExclusive();
|
||||
try {
|
||||
invalidatedTableIds = checkUpdatedTable();
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
} catch (IllegalStateException | SQLiteException exception) {
|
||||
// may happen if db is closed. just log.
|
||||
|
|
|
@ -142,10 +142,12 @@ class MultiInstanceInvalidationClient {
|
|||
* @param context The Context to be used for binding
|
||||
* {@link IMultiInstanceInvalidationService}.
|
||||
* @param name The name of the database file.
|
||||
* @param serviceIntent The {@link Intent} used for binding
|
||||
* {@link IMultiInstanceInvalidationService}.
|
||||
* @param invalidationTracker The {@link InvalidationTracker}
|
||||
* @param executor The background executor.
|
||||
*/
|
||||
MultiInstanceInvalidationClient(Context context, String name,
|
||||
MultiInstanceInvalidationClient(Context context, String name, Intent serviceIntent,
|
||||
InvalidationTracker invalidationTracker, Executor executor) {
|
||||
mAppContext = context.getApplicationContext();
|
||||
mName = name;
|
||||
|
@ -174,8 +176,7 @@ class MultiInstanceInvalidationClient {
|
|||
return true;
|
||||
}
|
||||
};
|
||||
Intent intent = new Intent(mAppContext, MultiInstanceInvalidationService.class);
|
||||
mAppContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
|
||||
mAppContext.bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
void stop() {
|
||||
|
|
|
@ -23,8 +23,8 @@ import android.os.RemoteCallbackList;
|
|||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RestrictTo;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
|
@ -33,9 +33,12 @@ import java.util.HashMap;
|
|||
* This service runs in the main app process. All the instances of {@link InvalidationTracker}
|
||||
* (potentially in other processes) has to connect to this service.
|
||||
*
|
||||
* @hide
|
||||
* <p>The intent to launch it can be specified by
|
||||
* {@link RoomDatabase.Builder#setMultiInstanceInvalidationServiceIntent}, although the service is
|
||||
* defined in the manifest by default so there should be no need to override it in a normal
|
||||
* situation.
|
||||
*/
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
|
||||
@ExperimentalRoomApi
|
||||
public class MultiInstanceInvalidationService extends Service {
|
||||
|
||||
// synthetic access
|
||||
|
@ -128,7 +131,7 @@ public class MultiInstanceInvalidationService extends Service {
|
|||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
public IBinder onBind(@NonNull Intent intent) {
|
||||
return mBinder;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ package androidx.room;
|
|||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RestrictTo;
|
||||
|
||||
/**
|
||||
* Utility class for Room.
|
||||
|
@ -75,7 +76,9 @@ public class Room {
|
|||
|
||||
@SuppressWarnings({"TypeParameterUnusedInFormals", "ClassNewInstance"})
|
||||
@NonNull
|
||||
static <T, C> T getGeneratedImplementation(Class<C> klass, String suffix) {
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
|
||||
public static <T, C> T getGeneratedImplementation(@NonNull Class<C> klass,
|
||||
@NonNull String suffix) {
|
||||
final String fullPackage = klass.getPackage().getName();
|
||||
String name = klass.getCanonicalName();
|
||||
final String postPackageName = fullPackage.isEmpty()
|
||||
|
|
|
@ -19,6 +19,7 @@ package androidx.room;
|
|||
import android.annotation.SuppressLint;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.os.Build;
|
||||
import android.os.CancellationSignal;
|
||||
|
@ -33,9 +34,11 @@ import androidx.annotation.RequiresApi;
|
|||
import androidx.annotation.RestrictTo;
|
||||
import androidx.annotation.WorkerThread;
|
||||
import androidx.arch.core.executor.ArchTaskExecutor;
|
||||
import androidx.room.migration.AutoMigrationSpec;
|
||||
import androidx.room.migration.Migration;
|
||||
import androidx.room.util.SneakyThrow;
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery;
|
||||
import androidx.sqlite.db.SupportSQLiteCompat;
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase;
|
||||
import androidx.sqlite.db.SupportSQLiteOpenHelper;
|
||||
import androidx.sqlite.db.SupportSQLiteQuery;
|
||||
|
@ -99,6 +102,15 @@ public abstract class RoomDatabase {
|
|||
@Deprecated
|
||||
protected List<Callback> mCallbacks;
|
||||
|
||||
/**
|
||||
* A map of auto migration spec classes to their provided instance.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
|
||||
@NonNull
|
||||
protected Map<Class<? extends AutoMigrationSpec>, AutoMigrationSpec> mAutoMigrationSpecs;
|
||||
|
||||
private final ReentrantReadWriteLock mCloseLock = new ReentrantReadWriteLock();
|
||||
|
||||
@Nullable
|
||||
|
@ -150,7 +162,6 @@ public abstract class RoomDatabase {
|
|||
// Updated later to an unmodifiable map when init is called.
|
||||
private final Map<Class<?>, Object> mTypeConverters;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the instance of the given Type Converter.
|
||||
*
|
||||
|
@ -174,6 +185,7 @@ public abstract class RoomDatabase {
|
|||
public RoomDatabase() {
|
||||
mInvalidationTracker = createInvalidationTracker();
|
||||
mTypeConverters = new HashMap<>();
|
||||
mAutoMigrationSpecs = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -184,6 +196,47 @@ public abstract class RoomDatabase {
|
|||
@CallSuper
|
||||
public void init(@NonNull DatabaseConfiguration configuration) {
|
||||
mOpenHelper = createOpenHelper(configuration);
|
||||
Set<Class<? extends AutoMigrationSpec>> requiredAutoMigrationSpecs =
|
||||
getRequiredAutoMigrationSpecs();
|
||||
BitSet usedSpecs = new BitSet();
|
||||
for (Class<? extends AutoMigrationSpec> spec : requiredAutoMigrationSpecs) {
|
||||
int foundIndex = -1;
|
||||
for (int providedIndex = configuration.autoMigrationSpecs.size() - 1;
|
||||
providedIndex >= 0; providedIndex--
|
||||
) {
|
||||
Object provided = configuration.autoMigrationSpecs.get(providedIndex);
|
||||
if (spec.isAssignableFrom(provided.getClass())) {
|
||||
foundIndex = providedIndex;
|
||||
usedSpecs.set(foundIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (foundIndex < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"A required auto migration spec (" + spec.getCanonicalName()
|
||||
+ ") is missing in the database configuration.");
|
||||
}
|
||||
mAutoMigrationSpecs.put(spec, configuration.autoMigrationSpecs.get(foundIndex));
|
||||
}
|
||||
|
||||
for (int providedIndex = configuration.autoMigrationSpecs.size() - 1;
|
||||
providedIndex >= 0; providedIndex--) {
|
||||
if (!usedSpecs.get(providedIndex)) {
|
||||
throw new IllegalArgumentException("Unexpected auto migration specs found. "
|
||||
+ "Annotate AutoMigrationSpec implementation with "
|
||||
+ "@ProvidedAutoMigrationSpec annotation or remove this spec from the "
|
||||
+ "builder.");
|
||||
}
|
||||
}
|
||||
|
||||
List<Migration> autoMigrations = getAutoMigrations(mAutoMigrationSpecs);
|
||||
for (Migration autoMigration : autoMigrations) {
|
||||
boolean migrationExists = configuration.migrationContainer.getMigrations()
|
||||
.containsKey(autoMigration.startVersion);
|
||||
if (!migrationExists) {
|
||||
configuration.migrationContainer.addMigrations(autoMigration);
|
||||
}
|
||||
}
|
||||
|
||||
// Configure SqliteCopyOpenHelper if it is available:
|
||||
SQLiteCopyOpenHelper copyOpenHelper = unwrapOpenHelper(SQLiteCopyOpenHelper.class,
|
||||
|
@ -211,9 +264,9 @@ public abstract class RoomDatabase {
|
|||
mTransactionExecutor = new TransactionExecutor(configuration.transactionExecutor);
|
||||
mAllowMainThreadQueries = configuration.allowMainThreadQueries;
|
||||
mWriteAheadLoggingEnabled = wal;
|
||||
if (configuration.multiInstanceInvalidation) {
|
||||
if (configuration.multiInstanceInvalidationServiceIntent != null) {
|
||||
mInvalidationTracker.startMultiInstanceInvalidation(configuration.context,
|
||||
configuration.name);
|
||||
configuration.name, configuration.multiInstanceInvalidationServiceIntent);
|
||||
}
|
||||
|
||||
Map<Class<?>, List<Class<?>>> requiredFactories = getRequiredTypeConverters();
|
||||
|
@ -256,6 +309,22 @@ public abstract class RoomDatabase {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of {@link Migration} of a database that have been automatically generated.
|
||||
*
|
||||
* @return A list of migration instances each of which is a generated autoMigration
|
||||
* @param autoMigrationSpecs
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
|
||||
public List<Migration> getAutoMigrations(
|
||||
@NonNull Map<Class<? extends AutoMigrationSpec>, AutoMigrationSpec> autoMigrationSpecs
|
||||
) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwraps (delegating) open helpers until it finds clazz, otherwise returns null.
|
||||
*
|
||||
|
@ -322,6 +391,21 @@ public abstract class RoomDatabase {
|
|||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Set of required AutoMigrationSpec classes.
|
||||
* <p>
|
||||
* This is implemented by the generated code.
|
||||
*
|
||||
* @return Creates a set that will include all required auto migration specs for this database.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
|
||||
public Set<Class<? extends AutoMigrationSpec>> getRequiredAutoMigrationSpecs() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all rows from all the tables that are registered to this database as
|
||||
* {@link Database#entities()}.
|
||||
|
@ -625,7 +709,7 @@ public abstract class RoomDatabase {
|
|||
/**
|
||||
* Journal modes for SQLite database.
|
||||
*
|
||||
* @see RoomDatabase.Builder#setJournalMode(JournalMode)
|
||||
* @see Builder#setJournalMode(JournalMode)
|
||||
*/
|
||||
public enum JournalMode {
|
||||
|
||||
|
@ -653,7 +737,6 @@ public abstract class RoomDatabase {
|
|||
* Resolves {@link #AUTOMATIC} to either {@link #TRUNCATE} or
|
||||
* {@link #WRITE_AHEAD_LOGGING}.
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
JournalMode resolve(Context context) {
|
||||
if (this != AUTOMATIC) {
|
||||
return this;
|
||||
|
@ -670,7 +753,7 @@ public abstract class RoomDatabase {
|
|||
|
||||
private static boolean isLowRamDevice(@NonNull ActivityManager activityManager) {
|
||||
if (Build.VERSION.SDK_INT >= 19) {
|
||||
return activityManager.isLowRamDevice();
|
||||
return SupportSQLiteCompat.Api19Impl.isLowRamDevice(activityManager);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -690,6 +773,7 @@ public abstract class RoomDatabase {
|
|||
private QueryCallback mQueryCallback;
|
||||
private Executor mQueryCallbackExecutor;
|
||||
private List<Object> mTypeConverters;
|
||||
private List<AutoMigrationSpec> mAutoMigrationSpecs;
|
||||
|
||||
/** The Executor used to run database queries. This should be background-threaded. */
|
||||
private Executor mQueryExecutor;
|
||||
|
@ -698,7 +782,7 @@ public abstract class RoomDatabase {
|
|||
private SupportSQLiteOpenHelper.Factory mFactory;
|
||||
private boolean mAllowMainThreadQueries;
|
||||
private JournalMode mJournalMode;
|
||||
private boolean mMultiInstanceInvalidation;
|
||||
private Intent mMultiInstanceInvalidationIntent;
|
||||
private boolean mRequireMigration;
|
||||
private boolean mAllowDestructiveMigrationOnDowngrade;
|
||||
|
||||
|
@ -961,6 +1045,23 @@ public abstract class RoomDatabase {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an auto migration spec to the builder.
|
||||
*
|
||||
* @param autoMigrationSpec The auto migration object that is annotated with
|
||||
* {@link AutoMigrationSpec} and is declared in an {@link AutoMigration} annotation.
|
||||
* @return This {@link Builder} instance.
|
||||
*/
|
||||
@NonNull
|
||||
@SuppressWarnings("MissingGetterMatchingBuilder")
|
||||
public Builder<T> addAutoMigrationSpec(@NonNull AutoMigrationSpec autoMigrationSpec) {
|
||||
if (mAutoMigrationSpecs == null) {
|
||||
mAutoMigrationSpecs = new ArrayList<>();
|
||||
}
|
||||
mAutoMigrationSpecs.add(autoMigrationSpec);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the main thread query check for Room.
|
||||
* <p>
|
||||
|
@ -1067,7 +1168,33 @@ public abstract class RoomDatabase {
|
|||
*/
|
||||
@NonNull
|
||||
public Builder<T> enableMultiInstanceInvalidation() {
|
||||
mMultiInstanceInvalidation = mName != null;
|
||||
mMultiInstanceInvalidationIntent = mName != null ? new Intent(mContext,
|
||||
MultiInstanceInvalidationService.class) : null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether table invalidation in this instance of {@link RoomDatabase} should be
|
||||
* broadcast and synchronized with other instances of the same {@link RoomDatabase},
|
||||
* including those in a separate process. In order to enable multi-instance invalidation,
|
||||
* this has to be turned on both ends and need to point to the same
|
||||
* {@link MultiInstanceInvalidationService}.
|
||||
* <p>
|
||||
* This is not enabled by default.
|
||||
* <p>
|
||||
* This does not work for in-memory databases. This does not work between database instances
|
||||
* targeting different database files.
|
||||
*
|
||||
* @return This {@link Builder} instance.
|
||||
* @param invalidationServiceIntent Intent to bind to the
|
||||
* {@link MultiInstanceInvalidationService}.
|
||||
*/
|
||||
@SuppressWarnings("MissingGetterMatchingBuilder")
|
||||
@NonNull
|
||||
@ExperimentalRoomApi
|
||||
public Builder<T> setMultiInstanceInvalidationServiceIntent(
|
||||
@NonNull Intent invalidationServiceIntent) {
|
||||
mMultiInstanceInvalidationIntent = mName != null ? invalidationServiceIntent : null;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -1346,7 +1473,7 @@ public abstract class RoomDatabase {
|
|||
mJournalMode.resolve(mContext),
|
||||
mQueryExecutor,
|
||||
mTransactionExecutor,
|
||||
mMultiInstanceInvalidation,
|
||||
mMultiInstanceInvalidationIntent,
|
||||
mRequireMigration,
|
||||
mAllowDestructiveMigrationOnDowngrade,
|
||||
mMigrationsNotRequiredFrom,
|
||||
|
@ -1354,7 +1481,8 @@ public abstract class RoomDatabase {
|
|||
mCopyFromFile,
|
||||
mCopyFromInputStream,
|
||||
mPrepackagedDatabaseCallback,
|
||||
mTypeConverters);
|
||||
mTypeConverters,
|
||||
mAutoMigrationSpecs);
|
||||
T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);
|
||||
db.init(configuration);
|
||||
return db;
|
||||
|
@ -1380,6 +1508,18 @@ public abstract class RoomDatabase {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given migrations to the list of available migrations. If 2 migrations have the
|
||||
* same start-end versions, the latter migration overrides the previous one.
|
||||
*
|
||||
* @param migrations List of available migrations.
|
||||
*/
|
||||
public void addMigrations(@NonNull List<Migration> migrations) {
|
||||
for (Migration migration : migrations) {
|
||||
addMigration(migration);
|
||||
}
|
||||
}
|
||||
|
||||
private void addMigration(Migration migration) {
|
||||
final int start = migration.startVersion;
|
||||
final int end = migration.endVersion;
|
||||
|
@ -1395,6 +1535,17 @@ public abstract class RoomDatabase {
|
|||
targetMap.put(end, migration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map of available migrations where the key is the start version of the
|
||||
* migration, and the value is a map of (end version -> Migration).
|
||||
*
|
||||
* @return Map of migrations keyed by the start version
|
||||
*/
|
||||
@NonNull
|
||||
public Map<Integer, Map<Integer, Migration>> getMigrations() {
|
||||
return Collections.unmodifiableMap(mMigrations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the list of migrations that should be run to move from {@code start} version to
|
||||
* {@code end} version.
|
||||
|
|
|
@ -248,18 +248,16 @@ class SQLiteCopyOpenHelper implements SupportSQLiteOpenHelper, DelegatingOpenHel
|
|||
}
|
||||
|
||||
private SupportSQLiteOpenHelper createFrameworkOpenHelper(File databaseFile) {
|
||||
String databaseName = databaseFile.getName();
|
||||
int version;
|
||||
final int version;
|
||||
try {
|
||||
version = DBUtil.readVersion(databaseFile);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Malformed database file, unable to read version.", e);
|
||||
}
|
||||
|
||||
FrameworkSQLiteOpenHelperFactory factory = new FrameworkSQLiteOpenHelperFactory();
|
||||
Configuration configuration = Configuration.builder(mContext)
|
||||
.name(databaseName)
|
||||
.callback(new Callback(version) {
|
||||
.name(databaseFile.getAbsolutePath())
|
||||
.callback(new Callback(Math.max(version, 1)) {
|
||||
@Override
|
||||
public void onCreate(@NonNull SupportSQLiteDatabase db) {
|
||||
}
|
||||
|
@ -268,6 +266,18 @@ class SQLiteCopyOpenHelper implements SupportSQLiteOpenHelper, DelegatingOpenHel
|
|||
public void onUpgrade(@NonNull SupportSQLiteDatabase db, int oldVersion,
|
||||
int newVersion) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(@NonNull SupportSQLiteDatabase db) {
|
||||
// If pre-packaged database has a version < 1 we will open it as if it was
|
||||
// version 1 because the framework open helper does not allow version < 1.
|
||||
// The database will be considered as newly created and onCreate() will be
|
||||
// invoked, but we do nothing and reset the version back so Room later runs
|
||||
// migrations as usual.
|
||||
if (version < 1) {
|
||||
db.setVersion(version);
|
||||
}
|
||||
}
|
||||
})
|
||||
.build();
|
||||
return factory.create(configuration);
|
||||
|
|
|
@ -16,21 +16,23 @@
|
|||
|
||||
package androidx.room.migration;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RestrictTo;
|
||||
import androidx.room.AutoMigration;
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase;
|
||||
|
||||
/**
|
||||
* Interface for defining automatic migration strategy for Room databases.
|
||||
* Interface for defining an automatic migration specification for Room databases.
|
||||
* <p>
|
||||
* The methods defined in this interface will be called on a background thread from the executor
|
||||
* set in Room's builder. It is important to note that the methods are all in a transaction when
|
||||
* it is called.
|
||||
*
|
||||
* @hide
|
||||
* @see AutoMigration
|
||||
*/
|
||||
// TODO: (b/181655460) Complete code usage documentation for this class and the AutoMigration
|
||||
// annotation.
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
|
||||
public interface AutoMigrationCallback {
|
||||
public interface AutoMigrationSpec {
|
||||
|
||||
/**
|
||||
* Handles any changes the user may want to implement after migration is completed.
|
||||
* Invoked after the migration is completed.
|
||||
* @param db The SQLite database.
|
||||
*/
|
||||
default void onPostMigrate(@NonNull SupportSQLiteDatabase db) {}
|
||||
}
|
|
@ -80,7 +80,7 @@
|
|||
* void delete(Song song);
|
||||
* }
|
||||
* // File: MusicDatabase.java
|
||||
* {@literal @}Database(entities = {Song.java})
|
||||
* {@literal @}Database(entities = {Song.class})
|
||||
* public abstract class MusicDatabase extends RoomDatabase {
|
||||
* public abstract SongDao songDao();
|
||||
* }
|
||||
|
|
|
@ -25,6 +25,7 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RestrictTo;
|
||||
import androidx.room.RoomDatabase;
|
||||
import androidx.sqlite.db.SupportSQLiteCompat;
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase;
|
||||
import androidx.sqlite.db.SupportSQLiteQuery;
|
||||
|
||||
|
@ -34,7 +35,9 @@ import java.io.IOException;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Database utilities for Room
|
||||
|
@ -125,6 +128,22 @@ public class DBUtil {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for foreign key violations by executing a PRAGMA foreign_key_check.
|
||||
*/
|
||||
public static void foreignKeyCheck(@NonNull SupportSQLiteDatabase db,
|
||||
@NonNull String tableName) {
|
||||
Cursor cursor = db.query("PRAGMA foreign_key_check(`" + tableName + "`)");
|
||||
try {
|
||||
if (cursor.getCount() > 0) {
|
||||
String errorMsg = processForeignKeyCheckFailure(cursor);
|
||||
throw new IllegalStateException(errorMsg);
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the user version number out of the database header from the given file.
|
||||
*
|
||||
|
@ -165,11 +184,60 @@ public class DBUtil {
|
|||
@Nullable
|
||||
public static CancellationSignal createCancellationSignal() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
return new CancellationSignal();
|
||||
return SupportSQLiteCompat.Api16Impl.createCancellationSignal();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts the {@link Cursor} returned in case of a foreign key violation into a detailed
|
||||
* error message for debugging.
|
||||
* <p>
|
||||
* The foreign_key_check pragma returns one row output for each foreign key violation.
|
||||
* <p>
|
||||
* The cursor received has four columns for each row output. The first column is the name of
|
||||
* the child table. The second column is the rowId of the row that contains the foreign key
|
||||
* violation (or NULL if the child table is a WITHOUT ROWID table). The third column is the
|
||||
* name of the parent table. The fourth column is the index of the specific foreign key
|
||||
* constraint that failed.
|
||||
*
|
||||
* @param cursor Cursor containing information regarding the FK violation
|
||||
* @return Error message generated containing debugging information
|
||||
*/
|
||||
private static String processForeignKeyCheckFailure(Cursor cursor) {
|
||||
int rowCount = cursor.getCount();
|
||||
String childTableName = null;
|
||||
Map<String, String> fkParentTables = new HashMap<>();
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
if (childTableName == null) {
|
||||
childTableName = cursor.getString(0);
|
||||
}
|
||||
String constraintIndex = cursor.getString(3);
|
||||
if (!fkParentTables.containsKey(constraintIndex)) {
|
||||
fkParentTables.put(constraintIndex, cursor.getString(2));
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Foreign key violation(s) detected in '")
|
||||
.append(childTableName).append("'.\n");
|
||||
sb.append("Number of different violations discovered: ")
|
||||
.append(fkParentTables.keySet().size()).append("\n");
|
||||
sb.append("Number of rows in violation: ")
|
||||
.append(rowCount).append("\n");
|
||||
sb.append("Violation(s) detected in the following constraint(s):\n");
|
||||
|
||||
for (Map.Entry<String, String> entry : fkParentTables.entrySet()) {
|
||||
sb.append("\tParent Table = ")
|
||||
.append(entry.getValue());
|
||||
sb.append(", Foreign Key Constraint Index = ")
|
||||
.append(entry.getKey()).append("\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private DBUtil() {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -237,6 +237,7 @@ public final class TableInfo {
|
|||
|
||||
private static Map<String, Column> readColumns(SupportSQLiteDatabase database,
|
||||
String tableName) {
|
||||
|
||||
Cursor cursor = database
|
||||
.query("PRAGMA table_info(`" + tableName + "`)");
|
||||
//noinspection TryFinallyCanBeTryWithResources
|
||||
|
@ -312,11 +313,14 @@ public final class TableInfo {
|
|||
final int seqnoColumnIndex = cursor.getColumnIndex("seqno");
|
||||
final int cidColumnIndex = cursor.getColumnIndex("cid");
|
||||
final int nameColumnIndex = cursor.getColumnIndex("name");
|
||||
if (seqnoColumnIndex == -1 || cidColumnIndex == -1 || nameColumnIndex == -1) {
|
||||
final int descColumnIndex = cursor.getColumnIndex("desc");
|
||||
if (seqnoColumnIndex == -1 || cidColumnIndex == -1
|
||||
|| nameColumnIndex == -1 || descColumnIndex == -1) {
|
||||
// we cannot read them so better not validate any index.
|
||||
return null;
|
||||
}
|
||||
final TreeMap<Integer, String> results = new TreeMap<>();
|
||||
final TreeMap<Integer, String> columnsMap = new TreeMap<>();
|
||||
final TreeMap<Integer, String> ordersMap = new TreeMap<>();
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
int cid = cursor.getInt(cidColumnIndex);
|
||||
|
@ -326,11 +330,16 @@ public final class TableInfo {
|
|||
}
|
||||
int seq = cursor.getInt(seqnoColumnIndex);
|
||||
String columnName = cursor.getString(nameColumnIndex);
|
||||
results.put(seq, columnName);
|
||||
String order = cursor.getInt(descColumnIndex) > 0 ? "DESC" : "ASC";
|
||||
|
||||
columnsMap.put(seq, columnName);
|
||||
ordersMap.put(seq, order);
|
||||
}
|
||||
final List<String> columns = new ArrayList<>(results.size());
|
||||
columns.addAll(results.values());
|
||||
return new Index(name, unique, columns);
|
||||
final List<String> columns = new ArrayList<>(columnsMap.size());
|
||||
columns.addAll(columnsMap.values());
|
||||
final List<String> orders = new ArrayList<>(ordersMap.size());
|
||||
orders.addAll(ordersMap.values());
|
||||
return new Index(name, unique, columns, orders);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
|
@ -456,15 +465,18 @@ public final class TableInfo {
|
|||
// from the compiler itself has it. b/136019383
|
||||
if (mCreatedFrom == CREATED_FROM_ENTITY
|
||||
&& column.mCreatedFrom == CREATED_FROM_DATABASE
|
||||
&& (defaultValue != null && !defaultValue.equals(column.defaultValue))) {
|
||||
&& (defaultValue != null && !defaultValueEquals(defaultValue,
|
||||
column.defaultValue))) {
|
||||
return false;
|
||||
} else if (mCreatedFrom == CREATED_FROM_DATABASE
|
||||
&& column.mCreatedFrom == CREATED_FROM_ENTITY
|
||||
&& (column.defaultValue != null && !column.defaultValue.equals(defaultValue))) {
|
||||
&& (column.defaultValue != null && !defaultValueEquals(
|
||||
column.defaultValue, defaultValue))) {
|
||||
return false;
|
||||
} else if (mCreatedFrom != CREATED_FROM_UNKNOWN
|
||||
&& mCreatedFrom == column.mCreatedFrom
|
||||
&& (defaultValue != null ? !defaultValue.equals(column.defaultValue)
|
||||
&& (defaultValue != null ? !defaultValueEquals(defaultValue,
|
||||
column.defaultValue)
|
||||
: column.defaultValue != null)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -472,6 +484,56 @@ public final class TableInfo {
|
|||
return affinity == column.affinity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the default values provided match. Handles the special case in which the
|
||||
* default value is surrounded by parenthesis (e.g. encountered in b/182284899).
|
||||
*
|
||||
* Surrounding parenthesis are removed by SQLite when reading from the database, hence
|
||||
* this function will check if they are present in the actual value, if so, it will
|
||||
* compare the two values by ignoring the surrounding parenthesis.
|
||||
*
|
||||
*/
|
||||
public static boolean defaultValueEquals(@NonNull String actual, @Nullable String other) {
|
||||
if (other == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actual.equals(other)) {
|
||||
return true;
|
||||
} else if (containsSurroundingParenthesis(actual)) {
|
||||
return actual.substring(1, actual.length() - 1).trim().equals(other);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for potential surrounding parenthesis, if found, removes them and checks if
|
||||
* remaining paranthesis are balanced. If so, the surrounding parenthesis are redundant,
|
||||
* and returns true.
|
||||
*/
|
||||
private static boolean containsSurroundingParenthesis(@NonNull String actual) {
|
||||
if (actual.length() == 0) {
|
||||
return false;
|
||||
}
|
||||
int surroundingParenthesis = 0;
|
||||
for (int i = 0; i < actual.length(); i++) {
|
||||
char c = actual.charAt(i);
|
||||
if (i == 0 && c != '(') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c == '(') {
|
||||
surroundingParenthesis++;
|
||||
} else if (c == ')') {
|
||||
surroundingParenthesis--;
|
||||
if (surroundingParenthesis == 0 && i != actual.length() - 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return surroundingParenthesis == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this column is part of the primary key or not.
|
||||
*
|
||||
|
@ -614,11 +676,22 @@ public final class TableInfo {
|
|||
public final String name;
|
||||
public final boolean unique;
|
||||
public final List<String> columns;
|
||||
public final List<String> orders;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #Index(String, boolean, List, List)}
|
||||
*/
|
||||
public Index(String name, boolean unique, List<String> columns) {
|
||||
this(name, unique, columns, null);
|
||||
}
|
||||
|
||||
public Index(String name, boolean unique, List<String> columns, List<String> orders) {
|
||||
this.name = name;
|
||||
this.unique = unique;
|
||||
this.columns = columns;
|
||||
this.orders = orders == null || orders.size() == 0
|
||||
? Collections.nCopies(columns.size(), androidx.room.Index.Order.ASC.name())
|
||||
: orders;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -633,6 +706,9 @@ public final class TableInfo {
|
|||
if (!columns.equals(index.columns)) {
|
||||
return false;
|
||||
}
|
||||
if (!orders.equals(index.orders)) {
|
||||
return false;
|
||||
}
|
||||
if (name.startsWith(Index.DEFAULT_PREFIX)) {
|
||||
return index.name.startsWith(Index.DEFAULT_PREFIX);
|
||||
} else {
|
||||
|
@ -650,6 +726,7 @@ public final class TableInfo {
|
|||
}
|
||||
result = 31 * result + (unique ? 1 : 0);
|
||||
result = 31 * result + columns.hashCode();
|
||||
result = 31 * result + orders.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -659,6 +736,7 @@ public final class TableInfo {
|
|||
+ "name='" + name + '\''
|
||||
+ ", unique=" + unique
|
||||
+ ", columns=" + columns
|
||||
+ ", orders=" + orders
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright 2021 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.util;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RestrictTo;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* UUID / byte[] two-way conversion utility for Room
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
|
||||
public final class UUIDUtil {
|
||||
|
||||
// private constructor to prevent instantiation
|
||||
private UUIDUtil() {}
|
||||
|
||||
/**
|
||||
* Converts a 16-bytes array BLOB into a UUID pojo
|
||||
*
|
||||
* @param bytes byte array stored in database as BLOB
|
||||
* @return a UUID object created based on the provided byte array
|
||||
*/
|
||||
@NonNull
|
||||
public static UUID convertByteToUUID(@NonNull byte[] bytes) {
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes);
|
||||
long firstLong = buffer.getLong();
|
||||
long secondLong = buffer.getLong();
|
||||
return new UUID(firstLong, secondLong);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a UUID pojo into a 16-bytes array to store into database as BLOB
|
||||
*
|
||||
* @param uuid the UUID pojo
|
||||
* @return a byte array to store into database
|
||||
*/
|
||||
@NonNull
|
||||
public static byte[] convertUUIDToByte(@NonNull UUID uuid) {
|
||||
byte[] bytes = new byte[16];
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes);
|
||||
buffer.putLong(uuid.getMostSignificantBits());
|
||||
buffer.putLong(uuid.getLeastSignificantBits());
|
||||
return buffer.array();
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
### Next version
|
||||
|
||||
* Small improvements and minor bug fixes
|
||||
* Updated AndroidX
|
||||
|
||||
### 1.1789 - 2021-12-14
|
||||
|
||||
|
|
Loading…
Reference in New Issue