Răsfoiți Sursa

Clean up creation, injection and migrations for room database

Signed-off-by: Álvaro Brey <alvaro.brey@nextcloud.com>
Álvaro Brey 2 ani în urmă
părinte
comite
cc9b38b972

+ 48 - 0
app/src/main/java/com/nextcloud/client/database/DatabaseModule.kt

@@ -0,0 +1,48 @@
+/*
+ * Nextcloud Android client application
+ *
+ *  @author Álvaro Brey
+ *  Copyright (C) 2022 Álvaro Brey
+ *  Copyright (C) 2022 Nextcloud GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.nextcloud.client.database
+
+import android.content.Context
+import androidx.room.Room
+import com.nextcloud.client.core.Clock
+import com.nextcloud.client.database.migrations.RoomMigration
+import com.nextcloud.client.database.migrations.addLegacyMigrations
+import com.owncloud.android.db.ProviderMeta
+import dagger.Module
+import dagger.Provides
+import javax.inject.Singleton
+
+@Module
+class DatabaseModule {
+
+    @Provides
+    @Singleton
+    @Suppress("Detekt.SpreadOperator") // forced by Room API
+    fun database(context: Context, clock: Clock): NextcloudDatabase {
+        return Room
+            .databaseBuilder(context, NextcloudDatabase::class.java, ProviderMeta.DB_NAME)
+            .addLegacyMigrations(context, clock)
+            .addMigrations(RoomMigration())
+            .build()
+    }
+}

+ 2 - 103
app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt

@@ -22,12 +22,8 @@
 
 package com.nextcloud.client.database
 
-import android.content.Context
 import androidx.room.Database
 import androidx.room.RoomDatabase
-import androidx.room.migration.Migration
-import androidx.sqlite.db.SupportSQLiteDatabase
-import com.nextcloud.client.core.Clock
 import com.nextcloud.client.database.entity.ArbitraryDataEntity
 import com.nextcloud.client.database.entity.CapabilityEntity
 import com.nextcloud.client.database.entity.ExternalLinkEntity
@@ -54,106 +50,9 @@ import com.owncloud.android.db.ProviderMeta
     version = ProviderMeta.DB_VERSION,
     exportSchema = true
 )
+@Suppress("Detekt.UnnecessaryAbstractClass") // needed by Room
 abstract class NextcloudDatabase : RoomDatabase() {
-
-    // migrations from before Room was introduced
-    private class LegacyMigration(
-        private val from: Int,
-        private val to: Int,
-        private val context: Context,
-        private val clock: Clock
-    ) :
-        Migration(from, to) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            LegacyMigrationHelper(context, clock)
-                .onUpgrade(database, from, to)
-        }
-    }
-
     companion object {
-        private const val FIRST_ROOM_DB_VERSION = 65
-
-        @JvmField
-        val roomMigration = object : Migration(FIRST_ROOM_DB_VERSION - 1, FIRST_ROOM_DB_VERSION) {
-            override fun migrate(database: SupportSQLiteDatabase) {
-                // migrate LONG, STRING to INTEGER, TEXT
-                migrateFilesystemTable(database)
-                migrateUploadsTable(database)
-            }
-
-            private fun migrateFilesystemTable(database: SupportSQLiteDatabase) {
-                val table = "filesystem"
-                val newTable = "${table}_new"
-                // create table with fixed types
-                database.execSQL(
-                    "CREATE TABLE $newTable (" +
-                        "_id INTEGER PRIMARY KEY," +
-                        "local_path TEXT," +
-                        "is_folder INTEGER," +
-                        "found_at INTEGER," +
-                        "upload_triggered INTEGER," +
-                        "syncedfolder_id TEXT," +
-                        "crc32 TEXT," +
-                        "modified_at INTEGER " +
-                        ")"
-                )
-                // copy data
-                val columns =
-                    "_id, local_path, is_folder, found_at, upload_triggered, syncedfolder_id, crc32, modified_at"
-                database.execSQL(
-                    "INSERT INTO $newTable ($columns) " +
-                        "SELECT $columns FROM $table"
-                )
-                // replace table
-                database.execSQL("DROP TABLE $table")
-                database.execSQL("ALTER TABLE $newTable RENAME TO $table")
-            }
-
-            private fun migrateUploadsTable(database: SupportSQLiteDatabase) {
-                val table = "list_of_uploads"
-                val newTable = "${table}_new"
-                // create table with fixed types
-                database.execSQL(
-                    "CREATE TABLE $newTable (" +
-                        "_id INTEGER PRIMARY KEY," +
-                        "local_path TEXT, " +
-                        "remote_path TEXT, " +
-                        "account_name TEXT, " +
-                        "file_size INTEGER, " +
-                        "status INTEGER, " +
-                        "local_behaviour INTEGER, " +
-                        "upload_time INTEGER, " +
-                        "name_collision_policy INTEGER, " +
-                        "is_create_remote_folder INTEGER, " +
-                        "upload_end_timestamp INTEGER, " +
-                        "last_result INTEGER, " +
-                        "is_while_charging_only INTEGER, " +
-                        "is_wifi_only INTEGER, " +
-                        "created_by INTEGER, " +
-                        "folder_unlock_token TEXT " +
-                        ")"
-                )
-
-                // copy data
-                val columns =
-                    "_id, local_path, remote_path, account_name, file_size, status, local_behaviour, upload_time," +
-                        " name_collision_policy, is_create_remote_folder, upload_end_timestamp, last_result," +
-                        " is_while_charging_only, is_wifi_only, created_by, folder_unlock_token"
-                database.execSQL(
-                    "INSERT INTO $newTable ($columns) " +
-                        "SELECT $columns FROM $table"
-                )
-                // replace table
-                database.execSQL("DROP TABLE $table")
-                database.execSQL("ALTER TABLE $newTable RENAME TO $table")
-            }
-        }
-
-        @JvmStatic
-        fun getLegacyMigrations(context: Context, clock: Clock): Array<Migration> {
-            // no way to get old and new version inside the migration so we need to replicate it for every version bump
-            return (1 until FIRST_ROOM_DB_VERSION - 1)
-                .map { from -> LegacyMigration(from, from + 1, context, clock) }.toTypedArray()
-        }
+        const val FIRST_ROOM_DB_VERSION = 65
     }
 }

+ 61 - 0
app/src/main/java/com/nextcloud/client/database/migrations/LegacyMigration.kt

@@ -0,0 +1,61 @@
+/*
+ * Nextcloud Android client application
+ *
+ *  @author Álvaro Brey
+ *  Copyright (C) 2022 Álvaro Brey
+ *  Copyright (C) 2022 Nextcloud GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.nextcloud.client.database.migrations
+
+import android.content.Context
+import androidx.room.RoomDatabase
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+import com.nextcloud.client.core.Clock
+import com.nextcloud.client.database.NextcloudDatabase
+
+/**
+ * Migrations for DB versions before Room was introduced
+ */
+class LegacyMigration(
+    private val from: Int,
+    private val to: Int,
+    private val context: Context,
+    private val clock: Clock
+) : Migration(from, to) {
+
+    override fun migrate(database: SupportSQLiteDatabase) {
+        LegacyMigrationHelper(context, clock)
+            .onUpgrade(database, from, to)
+    }
+}
+
+/**
+ * Adds a legacy migration for all versions before Room was introduced
+ *
+ * This is needed because the [Migration] does not know which versions it's dealing with
+ */
+fun RoomDatabase.Builder<NextcloudDatabase>.addLegacyMigrations(
+    context: Context,
+    clock: Clock
+): RoomDatabase.Builder<NextcloudDatabase> {
+    (1 until NextcloudDatabase.FIRST_ROOM_DB_VERSION - 1)
+        .map { from -> LegacyMigration(from, from + 1, context, clock) }
+        .forEach { migration -> this.addMigrations(migration) }
+    return this
+}

+ 4 - 9
app/src/main/java/com/nextcloud/client/database/LegacyMigrationHelper.java → app/src/main/java/com/nextcloud/client/database/migrations/LegacyMigrationHelper.java

@@ -1,14 +1,9 @@
 /*
  * Nextcloud Android client application
  *
- * @author Bartek Przybylski
- * @author David A. Velasco
- * @author masensio
- * @author Álvaro Brey Vilas
- * Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2016 ownCloud Inc.
- * Copyright (C) 2022 Álvaro Brey Vilas
- * Copyright (C) 2022 Nextcloud GmbH
+ *  @author Álvaro Brey
+ *  Copyright (C) 2022 Álvaro Brey
+ *  Copyright (C) 2022 Nextcloud GmbH
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
@@ -25,7 +20,7 @@
  *
  */
 
-package com.nextcloud.client.database;
+package com.nextcloud.client.database.migrations;
 
 import android.accounts.Account;
 import android.accounts.AccountManager;

+ 102 - 0
app/src/main/java/com/nextcloud/client/database/migrations/RoomMigration.kt

@@ -0,0 +1,102 @@
+/*
+ * Nextcloud Android client application
+ *
+ *  @author Álvaro Brey
+ *  Copyright (C) 2022 Álvaro Brey
+ *  Copyright (C) 2022 Nextcloud GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.nextcloud.client.database.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+import com.nextcloud.client.database.NextcloudDatabase
+
+class RoomMigration : Migration(NextcloudDatabase.FIRST_ROOM_DB_VERSION - 1, NextcloudDatabase.FIRST_ROOM_DB_VERSION) {
+    override fun migrate(database: SupportSQLiteDatabase) {
+        // migrate LONG, STRING to INTEGER, TEXT
+        migrateFilesystemTable(database)
+        migrateUploadsTable(database)
+    }
+
+    private fun migrateFilesystemTable(database: SupportSQLiteDatabase) {
+        val table = "filesystem"
+        val newTable = "${table}_new"
+        // create table with fixed types
+        database.execSQL(
+            "CREATE TABLE $newTable (" +
+                "_id INTEGER PRIMARY KEY," +
+                "local_path TEXT," +
+                "is_folder INTEGER," +
+                "found_at INTEGER," +
+                "upload_triggered INTEGER," +
+                "syncedfolder_id TEXT," +
+                "crc32 TEXT," +
+                "modified_at INTEGER " +
+                ")"
+        )
+        // copy data
+        val columns =
+            "_id, local_path, is_folder, found_at, upload_triggered, syncedfolder_id, crc32, modified_at"
+        database.execSQL(
+            "INSERT INTO $newTable ($columns) " +
+                "SELECT $columns FROM $table"
+        )
+        // replace table
+        database.execSQL("DROP TABLE $table")
+        database.execSQL("ALTER TABLE $newTable RENAME TO $table")
+    }
+
+    private fun migrateUploadsTable(database: SupportSQLiteDatabase) {
+        val table = "list_of_uploads"
+        val newTable = "${table}_new"
+        // create table with fixed types
+        database.execSQL(
+            "CREATE TABLE $newTable (" +
+                "_id INTEGER PRIMARY KEY," +
+                "local_path TEXT, " +
+                "remote_path TEXT, " +
+                "account_name TEXT, " +
+                "file_size INTEGER, " +
+                "status INTEGER, " +
+                "local_behaviour INTEGER, " +
+                "upload_time INTEGER, " +
+                "name_collision_policy INTEGER, " +
+                "is_create_remote_folder INTEGER, " +
+                "upload_end_timestamp INTEGER, " +
+                "last_result INTEGER, " +
+                "is_while_charging_only INTEGER, " +
+                "is_wifi_only INTEGER, " +
+                "created_by INTEGER, " +
+                "folder_unlock_token TEXT " +
+                ")"
+        )
+
+        // copy data
+        val columns =
+            "_id, local_path, remote_path, account_name, file_size, status, local_behaviour, upload_time," +
+                " name_collision_policy, is_create_remote_folder, upload_end_timestamp, last_result," +
+                " is_while_charging_only, is_wifi_only, created_by, folder_unlock_token"
+        database.execSQL(
+            "INSERT INTO $newTable ($columns) " +
+                "SELECT $columns FROM $table"
+        )
+        // replace table
+        database.execSQL("DROP TABLE $table")
+        database.execSQL("ALTER TABLE $newTable RENAME TO $table")
+    }
+}

+ 3 - 1
app/src/main/java/com/nextcloud/client/di/AppComponent.java

@@ -23,6 +23,7 @@ package com.nextcloud.client.di;
 import android.app.Application;
 
 import com.nextcloud.client.appinfo.AppInfoModule;
+import com.nextcloud.client.database.DatabaseModule;
 import com.nextcloud.client.device.DeviceModule;
 import com.nextcloud.client.integrations.IntegrationsModule;
 import com.nextcloud.client.jobs.JobsModule;
@@ -51,7 +52,8 @@ import dagger.android.support.AndroidSupportInjectionModule;
     ViewModelModule.class,
     JobsModule.class,
     IntegrationsModule.class,
-    ThemeModule.class
+    ThemeModule.class,
+    DatabaseModule.class
 })
 @Singleton
 public interface AppComponent {

+ 0 - 11
app/src/main/java/com/nextcloud/client/di/AppModule.java

@@ -94,17 +94,6 @@ class AppModule {
         return application;
     }
 
-    @Provides
-    @Singleton
-    NextcloudDatabase database(Context context, Clock clock) {
-        // TODO move somewhere else to not pollute this file
-        return Room
-            .databaseBuilder(context, NextcloudDatabase.class, ProviderMeta.DB_NAME)
-            .addMigrations(NextcloudDatabase.getLegacyMigrations(context, clock))
-            .addMigrations(NextcloudDatabase.roomMigration)
-            .build();
-    }
-
     @Provides
     PackageManager packageManager(Application application) {
         return application.getPackageManager();