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
|
### Next version
|
||||||
|
|
||||||
* Small improvements and minor bug fixes
|
* Small improvements and minor bug fixes
|
||||||
|
* Updated AndroidX
|
||||||
|
|
||||||
### 1.1789 - 2021-12-14
|
### 1.1789 - 2021-12-14
|
||||||
|
|
||||||
|
|
|
@ -297,16 +297,16 @@ dependencies {
|
||||||
//implementation fileTree(dir: 'libs', include: ['*.jar'])
|
//implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
|
||||||
def startup_version = "1.1.0"
|
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 core_version = "1.6.0" // 1.7.0
|
||||||
def shortcuts_version = "1.0.0"
|
def shortcuts_version = "1.0.0"
|
||||||
def appcompat_version = "1.3.1"
|
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 activity_version = "1.4.0"
|
||||||
def fragment_version = "1.4.0"
|
def fragment_version = "1.4.0"
|
||||||
def webkit_version = "1.4.0"
|
def webkit_version = "1.4.0"
|
||||||
def recyclerview_version = "1.2.1"
|
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 constraintlayout_version = "2.1.2"
|
||||||
def material_version = "1.4.0"
|
def material_version = "1.4.0"
|
||||||
def browser_version = "1.4.0"
|
def browser_version = "1.4.0"
|
||||||
|
@ -315,8 +315,8 @@ dependencies {
|
||||||
def documentfile_version = "1.1.0-alpha01"
|
def documentfile_version = "1.1.0-alpha01"
|
||||||
def lifecycle_version = "2.4.0"
|
def lifecycle_version = "2.4.0"
|
||||||
def lifecycle_extensions_version = "2.2.0"
|
def lifecycle_extensions_version = "2.2.0"
|
||||||
def room_version = "2.3.0" // 2.4.0-rc01
|
def room_version = "2.4.0"
|
||||||
def sqlite_version = "2.1.0" // 2.2.0-rc01
|
def sqlite_version = "2.2.0"
|
||||||
def requery_version = "3.36.0"
|
def requery_version = "3.36.0"
|
||||||
def paging_version = "2.1.2" // 3.1.0
|
def paging_version = "2.1.2" // 3.1.0
|
||||||
def preference_version = "1.1.1" // 1.2.0-alpha02
|
def preference_version = "1.1.1" // 1.2.0-alpha02
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
### Next version
|
### Next version
|
||||||
|
|
||||||
* Small improvements and minor bug fixes
|
* Small improvements and minor bug fixes
|
||||||
|
* Updated AndroidX
|
||||||
|
|
||||||
### 1.1789 - 2021-12-14
|
### 1.1789 - 2021-12-14
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package androidx.room;
|
package androidx.room;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.database.CharArrayBuffer;
|
import android.database.CharArrayBuffer;
|
||||||
|
@ -36,6 +35,7 @@ import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
import androidx.arch.core.util.Function;
|
import androidx.arch.core.util.Function;
|
||||||
import androidx.room.util.SneakyThrow;
|
import androidx.room.util.SneakyThrow;
|
||||||
|
import androidx.sqlite.db.SupportSQLiteCompat;
|
||||||
import androidx.sqlite.db.SupportSQLiteDatabase;
|
import androidx.sqlite.db.SupportSQLiteDatabase;
|
||||||
import androidx.sqlite.db.SupportSQLiteOpenHelper;
|
import androidx.sqlite.db.SupportSQLiteOpenHelper;
|
||||||
import androidx.sqlite.db.SupportSQLiteQuery;
|
import androidx.sqlite.db.SupportSQLiteQuery;
|
||||||
|
@ -440,7 +440,6 @@ final class AutoClosingRoomOpenHelper implements SupportSQLiteOpenHelper, Delega
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("UnsafeNewApiCall")
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
|
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
|
||||||
@Override
|
@Override
|
||||||
public void setForeignKeyConstraintsEnabled(boolean enable) {
|
public void setForeignKeyConstraintsEnabled(boolean enable) {
|
||||||
|
@ -464,7 +463,6 @@ final class AutoClosingRoomOpenHelper implements SupportSQLiteOpenHelper, Delega
|
||||||
+ "OpenHelper instead of on the database directly.");
|
+ "OpenHelper instead of on the database directly.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("UnsafeNewApiCall")
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
|
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
|
||||||
@Override
|
@Override
|
||||||
public boolean isWriteAheadLoggingEnabled() {
|
public boolean isWriteAheadLoggingEnabled() {
|
||||||
|
@ -698,27 +696,24 @@ final class AutoClosingRoomOpenHelper implements SupportSQLiteOpenHelper, Delega
|
||||||
mDelegate.setNotificationUri(cr, uri);
|
mDelegate.setNotificationUri(cr, uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("UnsafeNewApiCall")
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||||
@Override
|
@Override
|
||||||
public void setNotificationUris(@NonNull ContentResolver cr,
|
public void setNotificationUris(@NonNull ContentResolver cr,
|
||||||
@NonNull List<Uri> uris) {
|
@NonNull List<Uri> uris) {
|
||||||
mDelegate.setNotificationUris(cr, uris);
|
SupportSQLiteCompat.Api29Impl.setNotificationUris(mDelegate, cr, uris);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("UnsafeNewApiCall")
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
|
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
|
||||||
@Override
|
@Override
|
||||||
public Uri getNotificationUri() {
|
public Uri getNotificationUri() {
|
||||||
return mDelegate.getNotificationUri();
|
return SupportSQLiteCompat.Api19Impl.getNotificationUri(mDelegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("UnsafeNewApiCall")
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public List<Uri> getNotificationUris() {
|
public List<Uri> getNotificationUris() {
|
||||||
return mDelegate.getNotificationUris();
|
return SupportSQLiteCompat.Api29Impl.getNotificationUris(mDelegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -726,11 +721,10 @@ final class AutoClosingRoomOpenHelper implements SupportSQLiteOpenHelper, Delega
|
||||||
return mDelegate.getWantsAllOnMoveCalls();
|
return mDelegate.getWantsAllOnMoveCalls();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("UnsafeNewApiCall")
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||||
@Override
|
@Override
|
||||||
public void setExtras(Bundle extras) {
|
public void setExtras(Bundle extras) {
|
||||||
mDelegate.setExtras(extras);
|
SupportSQLiteCompat.Api23Impl.setExtras(mDelegate, extras);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,10 +18,12 @@ package androidx.room;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RestrictTo;
|
import androidx.annotation.RestrictTo;
|
||||||
|
import androidx.room.migration.AutoMigrationSpec;
|
||||||
import androidx.sqlite.db.SupportSQLiteOpenHelper;
|
import androidx.sqlite.db.SupportSQLiteOpenHelper;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -69,6 +71,9 @@ public class DatabaseConfiguration {
|
||||||
@NonNull
|
@NonNull
|
||||||
public final List<Object> typeConverters;
|
public final List<Object> typeConverters;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public final List<AutoMigrationSpec> autoMigrationSpecs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether Room should throw an exception for queries run on the main thread.
|
* Whether Room should throw an exception for queries run on the main thread.
|
||||||
*/
|
*/
|
||||||
|
@ -98,6 +103,15 @@ public class DatabaseConfiguration {
|
||||||
*/
|
*/
|
||||||
public final boolean multiInstanceInvalidation;
|
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.
|
* If true, Room should crash if a migration is missing.
|
||||||
*/
|
*/
|
||||||
|
@ -133,14 +147,13 @@ public class DatabaseConfiguration {
|
||||||
@Nullable
|
@Nullable
|
||||||
public final Callable<InputStream> copyFromInputStream;
|
public final Callable<InputStream> copyFromInputStream;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a database configuration with the given values.
|
* Creates a database configuration with the given values.
|
||||||
*
|
*
|
||||||
* @deprecated Use {@link #DatabaseConfiguration(Context, String,
|
* @deprecated Use {@link #DatabaseConfiguration(Context, String,
|
||||||
* SupportSQLiteOpenHelper.Factory, RoomDatabase.MigrationContainer, List, boolean,
|
* SupportSQLiteOpenHelper.Factory, RoomDatabase.MigrationContainer, List, boolean,
|
||||||
* RoomDatabase.JournalMode, Executor, Executor, boolean, boolean, boolean, Set, String, File,
|
* RoomDatabase.JournalMode, Executor, Executor, Intent, boolean, boolean, Set, String, File,
|
||||||
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List<Object>)}
|
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List, List)}
|
||||||
*
|
*
|
||||||
* @param context The application context.
|
* @param context The application context.
|
||||||
* @param name Name of the database, can be null if it is in memory.
|
* @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) {
|
@Nullable Set<Integer> migrationNotRequiredFrom) {
|
||||||
this(context, name, sqliteOpenHelperFactory, migrationContainer, callbacks,
|
this(context, name, sqliteOpenHelperFactory, migrationContainer, callbacks,
|
||||||
allowMainThreadQueries, journalMode, queryExecutor, queryExecutor, false,
|
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,
|
* @deprecated Use {@link #DatabaseConfiguration(Context, String,
|
||||||
* SupportSQLiteOpenHelper.Factory, RoomDatabase.MigrationContainer, List, boolean,
|
* SupportSQLiteOpenHelper.Factory, RoomDatabase.MigrationContainer, List, boolean,
|
||||||
* RoomDatabase.JournalMode, Executor, Executor, boolean, boolean, boolean, Set, String, File,
|
* RoomDatabase.JournalMode, Executor, Executor, Intent, boolean, boolean, Set, String, File,
|
||||||
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List<Object>)}
|
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List, List)}
|
||||||
*
|
*
|
||||||
* @param context The application context.
|
* @param context The application context.
|
||||||
* @param name Name of the database, can be null if it is in memory.
|
* @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,
|
this(context, name, sqliteOpenHelperFactory, migrationContainer, callbacks,
|
||||||
allowMainThreadQueries, journalMode, queryExecutor, transactionExecutor,
|
allowMainThreadQueries, journalMode, queryExecutor, transactionExecutor,
|
||||||
multiInstanceInvalidation, requireMigration, allowDestructiveMigrationOnDowngrade,
|
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,
|
* @deprecated Use {@link #DatabaseConfiguration(Context, String,
|
||||||
* SupportSQLiteOpenHelper.Factory, RoomDatabase.MigrationContainer, List, boolean,
|
* SupportSQLiteOpenHelper.Factory, RoomDatabase.MigrationContainer, List, boolean,
|
||||||
* RoomDatabase.JournalMode, Executor, Executor, boolean, boolean, boolean, Set, String, File,
|
* RoomDatabase.JournalMode, Executor, Executor, Intent, boolean, boolean, Set, String, File,
|
||||||
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List<Object>)}
|
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List, List)}
|
||||||
*
|
*
|
||||||
* @param context The application context.
|
* @param context The application context.
|
||||||
* @param name Name of the database, can be null if it is in memory.
|
* @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,
|
this(context, name, sqliteOpenHelperFactory, migrationContainer, callbacks,
|
||||||
allowMainThreadQueries, journalMode, queryExecutor, transactionExecutor,
|
allowMainThreadQueries, journalMode, queryExecutor, transactionExecutor,
|
||||||
multiInstanceInvalidation, requireMigration, allowDestructiveMigrationOnDowngrade,
|
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,
|
* @deprecated Use {@link #DatabaseConfiguration(Context, String,
|
||||||
* SupportSQLiteOpenHelper.Factory, RoomDatabase.MigrationContainer, List, boolean,
|
* SupportSQLiteOpenHelper.Factory, RoomDatabase.MigrationContainer, List, boolean,
|
||||||
* RoomDatabase.JournalMode, Executor, Executor, boolean, boolean, boolean, Set, String, File,
|
* RoomDatabase.JournalMode, Executor, Executor, Intent, boolean, boolean, Set, String, File,
|
||||||
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List<Object>)}
|
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List, List)}
|
||||||
*
|
*
|
||||||
* @param context The application context.
|
* @param context The application context.
|
||||||
* @param name Name of the database, can be null if it is in memory.
|
* @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,
|
allowMainThreadQueries, journalMode, queryExecutor, transactionExecutor,
|
||||||
multiInstanceInvalidation, requireMigration, allowDestructiveMigrationOnDowngrade,
|
multiInstanceInvalidation, requireMigration, allowDestructiveMigrationOnDowngrade,
|
||||||
migrationNotRequiredFrom, copyFromAssetPath, copyFromFile, copyFromInputStream,
|
migrationNotRequiredFrom, copyFromAssetPath, copyFromFile, copyFromInputStream,
|
||||||
null, null);
|
null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a database configuration with the given values.
|
* Creates a database configuration with the given values.
|
||||||
*
|
*
|
||||||
* @deprecated Use {@link #DatabaseConfiguration(Context, String,
|
* @deprecated Use {@link #DatabaseConfiguration(Context, String,
|
||||||
* SupportSQLiteOpenHelper.Factory, RoomDatabase.MigrationContainer, List, boolean,
|
* SupportSQLiteOpenHelper.Factory, RoomDatabase.MigrationContainer, List, boolean,
|
||||||
* RoomDatabase.JournalMode, Executor, Executor, boolean, boolean, boolean, Set, String, File,
|
* RoomDatabase.JournalMode, Executor, Executor, Intent, boolean, boolean, Set, String, File,
|
||||||
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List<Object>)}
|
* Callable, RoomDatabase.PrepackagedDatabaseCallback, List, List)}
|
||||||
*
|
*
|
||||||
* @param context The application context.
|
* @param context The application context.
|
||||||
* @param name Name of the database, can be null if it is in memory.
|
* @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,
|
allowMainThreadQueries, journalMode, queryExecutor, transactionExecutor,
|
||||||
multiInstanceInvalidation, requireMigration, allowDestructiveMigrationOnDowngrade,
|
multiInstanceInvalidation, requireMigration, allowDestructiveMigrationOnDowngrade,
|
||||||
migrationNotRequiredFrom, copyFromAssetPath, copyFromFile, copyFromInputStream,
|
migrationNotRequiredFrom, copyFromAssetPath, copyFromFile, copyFromInputStream,
|
||||||
prepackagedDatabaseCallback, null);
|
prepackagedDatabaseCallback, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a database configuration with the given values.
|
* 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 context The application context.
|
||||||
* @param name Name of the database, can be null if it is in memory.
|
* @param name Name of the database, can be null if it is in memory.
|
||||||
* @param sqliteOpenHelperFactory The open helper factory to use.
|
* @param sqliteOpenHelperFactory The open helper factory to use.
|
||||||
|
@ -407,6 +426,7 @@ public class DatabaseConfiguration {
|
||||||
*
|
*
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
@SuppressLint("LambdaLast")
|
@SuppressLint("LambdaLast")
|
||||||
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
|
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
|
||||||
public DatabaseConfiguration(@NonNull Context context, @Nullable String name,
|
public DatabaseConfiguration(@NonNull Context context, @Nullable String name,
|
||||||
|
@ -426,6 +446,126 @@ public class DatabaseConfiguration {
|
||||||
@Nullable Callable<InputStream> copyFromInputStream,
|
@Nullable Callable<InputStream> copyFromInputStream,
|
||||||
@Nullable RoomDatabase.PrepackagedDatabaseCallback prepackagedDatabaseCallback,
|
@Nullable RoomDatabase.PrepackagedDatabaseCallback prepackagedDatabaseCallback,
|
||||||
@Nullable List<Object> typeConverters) {
|
@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.sqliteOpenHelperFactory = sqliteOpenHelperFactory;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
@ -435,7 +575,9 @@ public class DatabaseConfiguration {
|
||||||
this.journalMode = journalMode;
|
this.journalMode = journalMode;
|
||||||
this.queryExecutor = queryExecutor;
|
this.queryExecutor = queryExecutor;
|
||||||
this.transactionExecutor = transactionExecutor;
|
this.transactionExecutor = transactionExecutor;
|
||||||
this.multiInstanceInvalidation = multiInstanceInvalidation;
|
this.multiInstanceInvalidationServiceIntent =
|
||||||
|
multiInstanceInvalidationServiceIntent;
|
||||||
|
this.multiInstanceInvalidation = multiInstanceInvalidationServiceIntent != null;
|
||||||
this.requireMigration = requireMigration;
|
this.requireMigration = requireMigration;
|
||||||
this.allowDestructiveMigrationOnDowngrade = allowDestructiveMigrationOnDowngrade;
|
this.allowDestructiveMigrationOnDowngrade = allowDestructiveMigrationOnDowngrade;
|
||||||
this.mMigrationNotRequiredFrom = migrationNotRequiredFrom;
|
this.mMigrationNotRequiredFrom = migrationNotRequiredFrom;
|
||||||
|
@ -444,6 +586,8 @@ public class DatabaseConfiguration {
|
||||||
this.copyFromInputStream = copyFromInputStream;
|
this.copyFromInputStream = copyFromInputStream;
|
||||||
this.prepackagedDatabaseCallback = prepackagedDatabaseCallback;
|
this.prepackagedDatabaseCallback = prepackagedDatabaseCallback;
|
||||||
this.typeConverters = typeConverters == null ? Collections.emptyList() : typeConverters;
|
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.
|
* APIs marked with ExperimentalRoomApi are experimental and may change.
|
||||||
*/
|
*/
|
||||||
@Target({ElementType.METHOD})
|
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||||
@RequiresOptIn()
|
@RequiresOptIn()
|
||||||
public @interface ExperimentalRoomApi {}
|
public @interface ExperimentalRoomApi {}
|
||||||
|
|
|
@ -18,6 +18,7 @@ package androidx.room;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.sqlite.SQLiteException;
|
import android.database.sqlite.SQLiteException;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
@ -210,9 +211,9 @@ public class InvalidationTracker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void startMultiInstanceInvalidation(Context context, String name) {
|
void startMultiInstanceInvalidation(Context context, String name, Intent serviceIntent) {
|
||||||
mMultiInstanceInvalidationClient = new MultiInstanceInvalidationClient(context, name, this,
|
mMultiInstanceInvalidationClient = new MultiInstanceInvalidationClient(context, name,
|
||||||
mDatabase.getQueryExecutor());
|
serviceIntent, this, mDatabase.getQueryExecutor());
|
||||||
}
|
}
|
||||||
|
|
||||||
void stopMultiInstanceInvalidation() {
|
void stopMultiInstanceInvalidation() {
|
||||||
|
@ -422,19 +423,15 @@ public class InvalidationTracker {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mDatabase.mWriteAheadLoggingEnabled) {
|
// This transaction has to be on the underlying DB rather than the RoomDatabase
|
||||||
// This transaction has to be on the underlying DB rather than the RoomDatabase
|
// in order to avoid a recursive loop after endTransaction.
|
||||||
// in order to avoid a recursive loop after endTransaction.
|
SupportSQLiteDatabase db = mDatabase.getOpenHelper().getWritableDatabase();
|
||||||
SupportSQLiteDatabase db = mDatabase.getOpenHelper().getWritableDatabase();
|
db.beginTransactionNonExclusive();
|
||||||
db.beginTransactionNonExclusive();
|
try {
|
||||||
try {
|
|
||||||
invalidatedTableIds = checkUpdatedTable();
|
|
||||||
db.setTransactionSuccessful();
|
|
||||||
} finally {
|
|
||||||
db.endTransaction();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
invalidatedTableIds = checkUpdatedTable();
|
invalidatedTableIds = checkUpdatedTable();
|
||||||
|
db.setTransactionSuccessful();
|
||||||
|
} finally {
|
||||||
|
db.endTransaction();
|
||||||
}
|
}
|
||||||
} catch (IllegalStateException | SQLiteException exception) {
|
} catch (IllegalStateException | SQLiteException exception) {
|
||||||
// may happen if db is closed. just log.
|
// may happen if db is closed. just log.
|
||||||
|
|
|
@ -142,10 +142,12 @@ class MultiInstanceInvalidationClient {
|
||||||
* @param context The Context to be used for binding
|
* @param context The Context to be used for binding
|
||||||
* {@link IMultiInstanceInvalidationService}.
|
* {@link IMultiInstanceInvalidationService}.
|
||||||
* @param name The name of the database file.
|
* @param name The name of the database file.
|
||||||
|
* @param serviceIntent The {@link Intent} used for binding
|
||||||
|
* {@link IMultiInstanceInvalidationService}.
|
||||||
* @param invalidationTracker The {@link InvalidationTracker}
|
* @param invalidationTracker The {@link InvalidationTracker}
|
||||||
* @param executor The background executor.
|
* @param executor The background executor.
|
||||||
*/
|
*/
|
||||||
MultiInstanceInvalidationClient(Context context, String name,
|
MultiInstanceInvalidationClient(Context context, String name, Intent serviceIntent,
|
||||||
InvalidationTracker invalidationTracker, Executor executor) {
|
InvalidationTracker invalidationTracker, Executor executor) {
|
||||||
mAppContext = context.getApplicationContext();
|
mAppContext = context.getApplicationContext();
|
||||||
mName = name;
|
mName = name;
|
||||||
|
@ -174,8 +176,7 @@ class MultiInstanceInvalidationClient {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Intent intent = new Intent(mAppContext, MultiInstanceInvalidationService.class);
|
mAppContext.bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
|
||||||
mAppContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop() {
|
void stop() {
|
||||||
|
|
|
@ -23,8 +23,8 @@ import android.os.RemoteCallbackList;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RestrictTo;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
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}
|
* This service runs in the main app process. All the instances of {@link InvalidationTracker}
|
||||||
* (potentially in other processes) has to connect to this service.
|
* (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 {
|
public class MultiInstanceInvalidationService extends Service {
|
||||||
|
|
||||||
// synthetic access
|
// synthetic access
|
||||||
|
@ -128,7 +131,7 @@ public class MultiInstanceInvalidationService extends Service {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public IBinder onBind(Intent intent) {
|
public IBinder onBind(@NonNull Intent intent) {
|
||||||
return mBinder;
|
return mBinder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package androidx.room;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.RestrictTo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class for Room.
|
* Utility class for Room.
|
||||||
|
@ -75,7 +76,9 @@ public class Room {
|
||||||
|
|
||||||
@SuppressWarnings({"TypeParameterUnusedInFormals", "ClassNewInstance"})
|
@SuppressWarnings({"TypeParameterUnusedInFormals", "ClassNewInstance"})
|
||||||
@NonNull
|
@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();
|
final String fullPackage = klass.getPackage().getName();
|
||||||
String name = klass.getCanonicalName();
|
String name = klass.getCanonicalName();
|
||||||
final String postPackageName = fullPackage.isEmpty()
|
final String postPackageName = fullPackage.isEmpty()
|
||||||
|
|
|
@ -19,6 +19,7 @@ package androidx.room;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.CancellationSignal;
|
import android.os.CancellationSignal;
|
||||||
|
@ -33,9 +34,11 @@ import androidx.annotation.RequiresApi;
|
||||||
import androidx.annotation.RestrictTo;
|
import androidx.annotation.RestrictTo;
|
||||||
import androidx.annotation.WorkerThread;
|
import androidx.annotation.WorkerThread;
|
||||||
import androidx.arch.core.executor.ArchTaskExecutor;
|
import androidx.arch.core.executor.ArchTaskExecutor;
|
||||||
|
import androidx.room.migration.AutoMigrationSpec;
|
||||||
import androidx.room.migration.Migration;
|
import androidx.room.migration.Migration;
|
||||||
import androidx.room.util.SneakyThrow;
|
import androidx.room.util.SneakyThrow;
|
||||||
import androidx.sqlite.db.SimpleSQLiteQuery;
|
import androidx.sqlite.db.SimpleSQLiteQuery;
|
||||||
|
import androidx.sqlite.db.SupportSQLiteCompat;
|
||||||
import androidx.sqlite.db.SupportSQLiteDatabase;
|
import androidx.sqlite.db.SupportSQLiteDatabase;
|
||||||
import androidx.sqlite.db.SupportSQLiteOpenHelper;
|
import androidx.sqlite.db.SupportSQLiteOpenHelper;
|
||||||
import androidx.sqlite.db.SupportSQLiteQuery;
|
import androidx.sqlite.db.SupportSQLiteQuery;
|
||||||
|
@ -99,6 +102,15 @@ public abstract class RoomDatabase {
|
||||||
@Deprecated
|
@Deprecated
|
||||||
protected List<Callback> mCallbacks;
|
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();
|
private final ReentrantReadWriteLock mCloseLock = new ReentrantReadWriteLock();
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -150,7 +162,6 @@ public abstract class RoomDatabase {
|
||||||
// Updated later to an unmodifiable map when init is called.
|
// Updated later to an unmodifiable map when init is called.
|
||||||
private final Map<Class<?>, Object> mTypeConverters;
|
private final Map<Class<?>, Object> mTypeConverters;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the instance of the given Type Converter.
|
* Gets the instance of the given Type Converter.
|
||||||
*
|
*
|
||||||
|
@ -174,6 +185,7 @@ public abstract class RoomDatabase {
|
||||||
public RoomDatabase() {
|
public RoomDatabase() {
|
||||||
mInvalidationTracker = createInvalidationTracker();
|
mInvalidationTracker = createInvalidationTracker();
|
||||||
mTypeConverters = new HashMap<>();
|
mTypeConverters = new HashMap<>();
|
||||||
|
mAutoMigrationSpecs = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -184,6 +196,47 @@ public abstract class RoomDatabase {
|
||||||
@CallSuper
|
@CallSuper
|
||||||
public void init(@NonNull DatabaseConfiguration configuration) {
|
public void init(@NonNull DatabaseConfiguration configuration) {
|
||||||
mOpenHelper = createOpenHelper(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:
|
// Configure SqliteCopyOpenHelper if it is available:
|
||||||
SQLiteCopyOpenHelper copyOpenHelper = unwrapOpenHelper(SQLiteCopyOpenHelper.class,
|
SQLiteCopyOpenHelper copyOpenHelper = unwrapOpenHelper(SQLiteCopyOpenHelper.class,
|
||||||
|
@ -211,9 +264,9 @@ public abstract class RoomDatabase {
|
||||||
mTransactionExecutor = new TransactionExecutor(configuration.transactionExecutor);
|
mTransactionExecutor = new TransactionExecutor(configuration.transactionExecutor);
|
||||||
mAllowMainThreadQueries = configuration.allowMainThreadQueries;
|
mAllowMainThreadQueries = configuration.allowMainThreadQueries;
|
||||||
mWriteAheadLoggingEnabled = wal;
|
mWriteAheadLoggingEnabled = wal;
|
||||||
if (configuration.multiInstanceInvalidation) {
|
if (configuration.multiInstanceInvalidationServiceIntent != null) {
|
||||||
mInvalidationTracker.startMultiInstanceInvalidation(configuration.context,
|
mInvalidationTracker.startMultiInstanceInvalidation(configuration.context,
|
||||||
configuration.name);
|
configuration.name, configuration.multiInstanceInvalidationServiceIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<Class<?>, List<Class<?>>> requiredFactories = getRequiredTypeConverters();
|
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.
|
* Unwraps (delegating) open helpers until it finds clazz, otherwise returns null.
|
||||||
*
|
*
|
||||||
|
@ -322,6 +391,21 @@ public abstract class RoomDatabase {
|
||||||
return Collections.emptyMap();
|
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
|
* Deletes all rows from all the tables that are registered to this database as
|
||||||
* {@link Database#entities()}.
|
* {@link Database#entities()}.
|
||||||
|
@ -625,7 +709,7 @@ public abstract class RoomDatabase {
|
||||||
/**
|
/**
|
||||||
* Journal modes for SQLite database.
|
* Journal modes for SQLite database.
|
||||||
*
|
*
|
||||||
* @see RoomDatabase.Builder#setJournalMode(JournalMode)
|
* @see Builder#setJournalMode(JournalMode)
|
||||||
*/
|
*/
|
||||||
public enum JournalMode {
|
public enum JournalMode {
|
||||||
|
|
||||||
|
@ -653,7 +737,6 @@ public abstract class RoomDatabase {
|
||||||
* Resolves {@link #AUTOMATIC} to either {@link #TRUNCATE} or
|
* Resolves {@link #AUTOMATIC} to either {@link #TRUNCATE} or
|
||||||
* {@link #WRITE_AHEAD_LOGGING}.
|
* {@link #WRITE_AHEAD_LOGGING}.
|
||||||
*/
|
*/
|
||||||
@SuppressLint("NewApi")
|
|
||||||
JournalMode resolve(Context context) {
|
JournalMode resolve(Context context) {
|
||||||
if (this != AUTOMATIC) {
|
if (this != AUTOMATIC) {
|
||||||
return this;
|
return this;
|
||||||
|
@ -670,7 +753,7 @@ public abstract class RoomDatabase {
|
||||||
|
|
||||||
private static boolean isLowRamDevice(@NonNull ActivityManager activityManager) {
|
private static boolean isLowRamDevice(@NonNull ActivityManager activityManager) {
|
||||||
if (Build.VERSION.SDK_INT >= 19) {
|
if (Build.VERSION.SDK_INT >= 19) {
|
||||||
return activityManager.isLowRamDevice();
|
return SupportSQLiteCompat.Api19Impl.isLowRamDevice(activityManager);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -690,6 +773,7 @@ public abstract class RoomDatabase {
|
||||||
private QueryCallback mQueryCallback;
|
private QueryCallback mQueryCallback;
|
||||||
private Executor mQueryCallbackExecutor;
|
private Executor mQueryCallbackExecutor;
|
||||||
private List<Object> mTypeConverters;
|
private List<Object> mTypeConverters;
|
||||||
|
private List<AutoMigrationSpec> mAutoMigrationSpecs;
|
||||||
|
|
||||||
/** The Executor used to run database queries. This should be background-threaded. */
|
/** The Executor used to run database queries. This should be background-threaded. */
|
||||||
private Executor mQueryExecutor;
|
private Executor mQueryExecutor;
|
||||||
|
@ -698,7 +782,7 @@ public abstract class RoomDatabase {
|
||||||
private SupportSQLiteOpenHelper.Factory mFactory;
|
private SupportSQLiteOpenHelper.Factory mFactory;
|
||||||
private boolean mAllowMainThreadQueries;
|
private boolean mAllowMainThreadQueries;
|
||||||
private JournalMode mJournalMode;
|
private JournalMode mJournalMode;
|
||||||
private boolean mMultiInstanceInvalidation;
|
private Intent mMultiInstanceInvalidationIntent;
|
||||||
private boolean mRequireMigration;
|
private boolean mRequireMigration;
|
||||||
private boolean mAllowDestructiveMigrationOnDowngrade;
|
private boolean mAllowDestructiveMigrationOnDowngrade;
|
||||||
|
|
||||||
|
@ -961,6 +1045,23 @@ public abstract class RoomDatabase {
|
||||||
return this;
|
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.
|
* Disables the main thread query check for Room.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -1067,7 +1168,33 @@ public abstract class RoomDatabase {
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public Builder<T> enableMultiInstanceInvalidation() {
|
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;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1346,7 +1473,7 @@ public abstract class RoomDatabase {
|
||||||
mJournalMode.resolve(mContext),
|
mJournalMode.resolve(mContext),
|
||||||
mQueryExecutor,
|
mQueryExecutor,
|
||||||
mTransactionExecutor,
|
mTransactionExecutor,
|
||||||
mMultiInstanceInvalidation,
|
mMultiInstanceInvalidationIntent,
|
||||||
mRequireMigration,
|
mRequireMigration,
|
||||||
mAllowDestructiveMigrationOnDowngrade,
|
mAllowDestructiveMigrationOnDowngrade,
|
||||||
mMigrationsNotRequiredFrom,
|
mMigrationsNotRequiredFrom,
|
||||||
|
@ -1354,7 +1481,8 @@ public abstract class RoomDatabase {
|
||||||
mCopyFromFile,
|
mCopyFromFile,
|
||||||
mCopyFromInputStream,
|
mCopyFromInputStream,
|
||||||
mPrepackagedDatabaseCallback,
|
mPrepackagedDatabaseCallback,
|
||||||
mTypeConverters);
|
mTypeConverters,
|
||||||
|
mAutoMigrationSpecs);
|
||||||
T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);
|
T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);
|
||||||
db.init(configuration);
|
db.init(configuration);
|
||||||
return db;
|
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) {
|
private void addMigration(Migration migration) {
|
||||||
final int start = migration.startVersion;
|
final int start = migration.startVersion;
|
||||||
final int end = migration.endVersion;
|
final int end = migration.endVersion;
|
||||||
|
@ -1395,6 +1535,17 @@ public abstract class RoomDatabase {
|
||||||
targetMap.put(end, migration);
|
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
|
* Finds the list of migrations that should be run to move from {@code start} version to
|
||||||
* {@code end} version.
|
* {@code end} version.
|
||||||
|
|
|
@ -248,18 +248,16 @@ class SQLiteCopyOpenHelper implements SupportSQLiteOpenHelper, DelegatingOpenHel
|
||||||
}
|
}
|
||||||
|
|
||||||
private SupportSQLiteOpenHelper createFrameworkOpenHelper(File databaseFile) {
|
private SupportSQLiteOpenHelper createFrameworkOpenHelper(File databaseFile) {
|
||||||
String databaseName = databaseFile.getName();
|
final int version;
|
||||||
int version;
|
|
||||||
try {
|
try {
|
||||||
version = DBUtil.readVersion(databaseFile);
|
version = DBUtil.readVersion(databaseFile);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("Malformed database file, unable to read version.", e);
|
throw new RuntimeException("Malformed database file, unable to read version.", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
FrameworkSQLiteOpenHelperFactory factory = new FrameworkSQLiteOpenHelperFactory();
|
FrameworkSQLiteOpenHelperFactory factory = new FrameworkSQLiteOpenHelperFactory();
|
||||||
Configuration configuration = Configuration.builder(mContext)
|
Configuration configuration = Configuration.builder(mContext)
|
||||||
.name(databaseName)
|
.name(databaseFile.getAbsolutePath())
|
||||||
.callback(new Callback(version) {
|
.callback(new Callback(Math.max(version, 1)) {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@NonNull SupportSQLiteDatabase db) {
|
public void onCreate(@NonNull SupportSQLiteDatabase db) {
|
||||||
}
|
}
|
||||||
|
@ -268,6 +266,18 @@ class SQLiteCopyOpenHelper implements SupportSQLiteOpenHelper, DelegatingOpenHel
|
||||||
public void onUpgrade(@NonNull SupportSQLiteDatabase db, int oldVersion,
|
public void onUpgrade(@NonNull SupportSQLiteDatabase db, int oldVersion,
|
||||||
int newVersion) {
|
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();
|
.build();
|
||||||
return factory.create(configuration);
|
return factory.create(configuration);
|
||||||
|
|
|
@ -16,21 +16,23 @@
|
||||||
|
|
||||||
package androidx.room.migration;
|
package androidx.room.migration;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.RestrictTo;
|
import androidx.room.AutoMigration;
|
||||||
import androidx.sqlite.db.SupportSQLiteDatabase;
|
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
|
public interface AutoMigrationSpec {
|
||||||
// annotation.
|
|
||||||
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
|
|
||||||
public interface AutoMigrationCallback {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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) {}
|
default void onPostMigrate(@NonNull SupportSQLiteDatabase db) {}
|
||||||
}
|
}
|
|
@ -80,7 +80,7 @@
|
||||||
* void delete(Song song);
|
* void delete(Song song);
|
||||||
* }
|
* }
|
||||||
* // File: MusicDatabase.java
|
* // File: MusicDatabase.java
|
||||||
* {@literal @}Database(entities = {Song.java})
|
* {@literal @}Database(entities = {Song.class})
|
||||||
* public abstract class MusicDatabase extends RoomDatabase {
|
* public abstract class MusicDatabase extends RoomDatabase {
|
||||||
* public abstract SongDao songDao();
|
* public abstract SongDao songDao();
|
||||||
* }
|
* }
|
||||||
|
|
|
@ -25,6 +25,7 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RestrictTo;
|
import androidx.annotation.RestrictTo;
|
||||||
import androidx.room.RoomDatabase;
|
import androidx.room.RoomDatabase;
|
||||||
|
import androidx.sqlite.db.SupportSQLiteCompat;
|
||||||
import androidx.sqlite.db.SupportSQLiteDatabase;
|
import androidx.sqlite.db.SupportSQLiteDatabase;
|
||||||
import androidx.sqlite.db.SupportSQLiteQuery;
|
import androidx.sqlite.db.SupportSQLiteQuery;
|
||||||
|
|
||||||
|
@ -34,7 +35,9 @@ import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.FileChannel;
|
import java.nio.channels.FileChannel;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Database utilities for Room
|
* 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.
|
* Reads the user version number out of the database header from the given file.
|
||||||
*
|
*
|
||||||
|
@ -165,11 +184,60 @@ public class DBUtil {
|
||||||
@Nullable
|
@Nullable
|
||||||
public static CancellationSignal createCancellationSignal() {
|
public static CancellationSignal createCancellationSignal() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||||
return new CancellationSignal();
|
return SupportSQLiteCompat.Api16Impl.createCancellationSignal();
|
||||||
}
|
}
|
||||||
return null;
|
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() {
|
private DBUtil() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -237,6 +237,7 @@ public final class TableInfo {
|
||||||
|
|
||||||
private static Map<String, Column> readColumns(SupportSQLiteDatabase database,
|
private static Map<String, Column> readColumns(SupportSQLiteDatabase database,
|
||||||
String tableName) {
|
String tableName) {
|
||||||
|
|
||||||
Cursor cursor = database
|
Cursor cursor = database
|
||||||
.query("PRAGMA table_info(`" + tableName + "`)");
|
.query("PRAGMA table_info(`" + tableName + "`)");
|
||||||
//noinspection TryFinallyCanBeTryWithResources
|
//noinspection TryFinallyCanBeTryWithResources
|
||||||
|
@ -312,11 +313,14 @@ public final class TableInfo {
|
||||||
final int seqnoColumnIndex = cursor.getColumnIndex("seqno");
|
final int seqnoColumnIndex = cursor.getColumnIndex("seqno");
|
||||||
final int cidColumnIndex = cursor.getColumnIndex("cid");
|
final int cidColumnIndex = cursor.getColumnIndex("cid");
|
||||||
final int nameColumnIndex = cursor.getColumnIndex("name");
|
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.
|
// we cannot read them so better not validate any index.
|
||||||
return null;
|
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()) {
|
while (cursor.moveToNext()) {
|
||||||
int cid = cursor.getInt(cidColumnIndex);
|
int cid = cursor.getInt(cidColumnIndex);
|
||||||
|
@ -326,11 +330,16 @@ public final class TableInfo {
|
||||||
}
|
}
|
||||||
int seq = cursor.getInt(seqnoColumnIndex);
|
int seq = cursor.getInt(seqnoColumnIndex);
|
||||||
String columnName = cursor.getString(nameColumnIndex);
|
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());
|
final List<String> columns = new ArrayList<>(columnsMap.size());
|
||||||
columns.addAll(results.values());
|
columns.addAll(columnsMap.values());
|
||||||
return new Index(name, unique, columns);
|
final List<String> orders = new ArrayList<>(ordersMap.size());
|
||||||
|
orders.addAll(ordersMap.values());
|
||||||
|
return new Index(name, unique, columns, orders);
|
||||||
} finally {
|
} finally {
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
|
@ -456,15 +465,18 @@ public final class TableInfo {
|
||||||
// from the compiler itself has it. b/136019383
|
// from the compiler itself has it. b/136019383
|
||||||
if (mCreatedFrom == CREATED_FROM_ENTITY
|
if (mCreatedFrom == CREATED_FROM_ENTITY
|
||||||
&& column.mCreatedFrom == CREATED_FROM_DATABASE
|
&& column.mCreatedFrom == CREATED_FROM_DATABASE
|
||||||
&& (defaultValue != null && !defaultValue.equals(column.defaultValue))) {
|
&& (defaultValue != null && !defaultValueEquals(defaultValue,
|
||||||
|
column.defaultValue))) {
|
||||||
return false;
|
return false;
|
||||||
} else if (mCreatedFrom == CREATED_FROM_DATABASE
|
} else if (mCreatedFrom == CREATED_FROM_DATABASE
|
||||||
&& column.mCreatedFrom == CREATED_FROM_ENTITY
|
&& column.mCreatedFrom == CREATED_FROM_ENTITY
|
||||||
&& (column.defaultValue != null && !column.defaultValue.equals(defaultValue))) {
|
&& (column.defaultValue != null && !defaultValueEquals(
|
||||||
|
column.defaultValue, defaultValue))) {
|
||||||
return false;
|
return false;
|
||||||
} else if (mCreatedFrom != CREATED_FROM_UNKNOWN
|
} else if (mCreatedFrom != CREATED_FROM_UNKNOWN
|
||||||
&& mCreatedFrom == column.mCreatedFrom
|
&& mCreatedFrom == column.mCreatedFrom
|
||||||
&& (defaultValue != null ? !defaultValue.equals(column.defaultValue)
|
&& (defaultValue != null ? !defaultValueEquals(defaultValue,
|
||||||
|
column.defaultValue)
|
||||||
: column.defaultValue != null)) {
|
: column.defaultValue != null)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -472,6 +484,56 @@ public final class TableInfo {
|
||||||
return affinity == column.affinity;
|
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.
|
* 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 String name;
|
||||||
public final boolean unique;
|
public final boolean unique;
|
||||||
public final List<String> columns;
|
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) {
|
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.name = name;
|
||||||
this.unique = unique;
|
this.unique = unique;
|
||||||
this.columns = columns;
|
this.columns = columns;
|
||||||
|
this.orders = orders == null || orders.size() == 0
|
||||||
|
? Collections.nCopies(columns.size(), androidx.room.Index.Order.ASC.name())
|
||||||
|
: orders;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -633,6 +706,9 @@ public final class TableInfo {
|
||||||
if (!columns.equals(index.columns)) {
|
if (!columns.equals(index.columns)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!orders.equals(index.orders)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (name.startsWith(Index.DEFAULT_PREFIX)) {
|
if (name.startsWith(Index.DEFAULT_PREFIX)) {
|
||||||
return index.name.startsWith(Index.DEFAULT_PREFIX);
|
return index.name.startsWith(Index.DEFAULT_PREFIX);
|
||||||
} else {
|
} else {
|
||||||
|
@ -650,6 +726,7 @@ public final class TableInfo {
|
||||||
}
|
}
|
||||||
result = 31 * result + (unique ? 1 : 0);
|
result = 31 * result + (unique ? 1 : 0);
|
||||||
result = 31 * result + columns.hashCode();
|
result = 31 * result + columns.hashCode();
|
||||||
|
result = 31 * result + orders.hashCode();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -659,6 +736,7 @@ public final class TableInfo {
|
||||||
+ "name='" + name + '\''
|
+ "name='" + name + '\''
|
||||||
+ ", unique=" + unique
|
+ ", unique=" + unique
|
||||||
+ ", columns=" + columns
|
+ ", 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
|
### Next version
|
||||||
|
|
||||||
* Small improvements and minor bug fixes
|
* Small improvements and minor bug fixes
|
||||||
|
* Updated AndroidX
|
||||||
|
|
||||||
### 1.1789 - 2021-12-14
|
### 1.1789 - 2021-12-14
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue