): Boolean {
+ val objectUids = CollectionsHelper.commaAndSpaceSeparatedCollectionValues(uids.map { "'$it'" })
+ val clause = "$key NOT IN ($objectUids);"
+ return databaseAdapter.delete(tableName, clause, null) > 0
+ }
+}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/cleaners/internal/CollectionCleanerImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/cleaners/internal/CollectionCleanerImpl.kt
index e4f069bcf7..73e354cb38 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/arch/cleaners/internal/CollectionCleanerImpl.kt
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/cleaners/internal/CollectionCleanerImpl.kt
@@ -28,25 +28,27 @@
package org.hisp.dhis.android.core.arch.cleaners.internal
import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter
-import org.hisp.dhis.android.core.arch.helpers.CollectionsHelper
import org.hisp.dhis.android.core.common.IdentifiableColumns
import org.hisp.dhis.android.core.common.ObjectWithUidInterface
internal open class CollectionCleanerImpl(
- private val tableName: String,
- private val databaseAdapter: DatabaseAdapter,
-) : CollectionCleaner
{
+ tableName: String,
+ databaseAdapter: DatabaseAdapter,
+) : CollectionCleaner
,
+ BaseCollectionCleaner(
+ tableName = tableName,
+ databaseAdapter = databaseAdapter,
+ key = IdentifiableColumns.UID,
+ ) {
override fun deleteNotPresent(objects: Collection
?): Boolean {
if (objects == null) {
return false
}
- return deleteNotPresentByUid(objects.map { it.uid() })
+ return deleteNotPresentByKey(objects.map { it.uid() })
}
override fun deleteNotPresentByUid(uids: Collection): Boolean {
- val objectUids = CollectionsHelper.commaAndSpaceSeparatedCollectionValues(uids.map { "'$it'" })
- val clause = IdentifiableColumns.UID + " NOT IN (" + objectUids + ");"
- return databaseAdapter.delete(tableName, clause, null) > 0
+ return deleteNotPresentByKey(uids)
}
}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/cleaners/internal/LinkCleanerImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/cleaners/internal/LinkCleanerImpl.kt
index 81fc9f81dd..9fc22f79f9 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/arch/cleaners/internal/LinkCleanerImpl.kt
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/cleaners/internal/LinkCleanerImpl.kt
@@ -29,23 +29,25 @@ package org.hisp.dhis.android.core.arch.cleaners.internal
import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter
import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectStore
-import org.hisp.dhis.android.core.arch.helpers.UidsHelper.commaSeparatedUidsWithSingleQuotationMarks
import org.hisp.dhis.android.core.common.ObjectWithUidInterface
internal open class LinkCleanerImpl(
- private val tableName: String,
- private val applicableColumn: String,
+ tableName: String,
+ applicableColumn: String,
+ databaseAdapter: DatabaseAdapter,
private val parentStore: ObjectStore
,
- private val databaseAdapter: DatabaseAdapter,
-) : LinkCleaner
{
+) : LinkCleaner
,
+ BaseCollectionCleaner(
+ tableName = tableName,
+ databaseAdapter = databaseAdapter,
+ key = applicableColumn,
+ ) {
override fun deleteNotPresent(objects: Collection
?): Boolean {
if (objects == null) {
return false
}
- val objectUids = commaSeparatedUidsWithSingleQuotationMarks(objects)
- val clause = "$applicableColumn NOT IN ($objectUids);"
- return databaseAdapter.delete(tableName, clause, null) > 0
+ return deleteNotPresentByKey(objects.map { it.uid() })
}
override fun deleteNotPresentInDb(): Boolean {
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponent.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponent.kt
index 9b38382770..c281119023 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponent.kt
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponent.kt
@@ -33,6 +33,7 @@ import org.hisp.dhis.android.core.arch.api.executors.internal.CoroutineAPICallEx
import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter
import org.hisp.dhis.android.core.arch.storage.internal.*
import org.hisp.dhis.android.core.category.internal.CategoryOptionStore
+import org.hisp.dhis.android.core.configuration.internal.MultiUserDatabaseManager
import org.hisp.dhis.android.core.configuration.internal.MultiUserDatabaseManagerForD2Manager
import org.hisp.dhis.android.core.dataelement.internal.DataElementEndpointCallFactory
import org.hisp.dhis.android.core.dataset.internal.DataSetEndpointCallFactory
@@ -108,4 +109,7 @@ internal class D2DIComponent(
@get:VisibleForTesting
val interpreterSelector: InterpreterSelector,
+
+ @get:VisibleForTesting
+ val multiUserDatabaseManager: MultiUserDatabaseManager,
)
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponentFactory.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponentFactory.kt
index e717822123..7d810bde6e 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponentFactory.kt
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponentFactory.kt
@@ -50,6 +50,7 @@ import org.hisp.dhis.android.core.enrollment.EnrollmentDIModule
import org.hisp.dhis.android.core.event.EventDIModule
import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItemDIModule
import org.hisp.dhis.android.core.fileresource.FileResourceDIModule
+import org.hisp.dhis.android.core.icon.IconDIModule
import org.hisp.dhis.android.core.imports.ImportsDIModule
import org.hisp.dhis.android.core.indicator.IndicatorDIModule
import org.hisp.dhis.android.core.legendset.LegendSetDIModule
@@ -109,6 +110,7 @@ internal object D2DIComponentFactory {
EventDIModule().module,
ExpressionDimensionItemDIModule().module,
FileResourceDIModule().module,
+ IconDIModule().module,
ImportsDIModule().module,
IndicatorDIModule().module,
LegendSetDIModule().module,
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2Modules.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2Modules.kt
index d91afe6f78..5f553d1a76 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2Modules.kt
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2Modules.kt
@@ -39,6 +39,7 @@ import org.hisp.dhis.android.core.enrollment.EnrollmentModule
import org.hisp.dhis.android.core.event.EventModule
import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItemModule
import org.hisp.dhis.android.core.fileresource.FileResourceModule
+import org.hisp.dhis.android.core.icon.IconModule
import org.hisp.dhis.android.core.imports.internal.ImportModule
import org.hisp.dhis.android.core.indicator.IndicatorModule
import org.hisp.dhis.android.core.legendset.LegendSetModule
@@ -76,6 +77,7 @@ internal class D2Modules(
val expressionDimensionItem: ExpressionDimensionItemModule,
val fileResource: FileResourceModule,
val importModule: ImportModule,
+ val icon: IconModule,
val indicator: IndicatorModule,
val legendSet: LegendSetModule,
val dataStore: DataStoreModule,
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/JavaDIClasses.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/JavaDIClasses.kt
index 67f6c6d7cf..8a3da2cc76 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/JavaDIClasses.kt
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/JavaDIClasses.kt
@@ -30,7 +30,6 @@ package org.hisp.dhis.android.core.arch.d2.internal
import org.hisp.dhis.android.core.arch.call.executors.internal.D2CallExecutor
import org.hisp.dhis.android.core.arch.db.access.internal.DatabaseAdapterFactory
-import org.hisp.dhis.android.core.arch.db.access.internal.DatabaseExport
import org.hisp.dhis.android.core.configuration.internal.DatabaseEncryptionPasswordGenerator
import org.hisp.dhis.android.core.configuration.internal.DatabaseEncryptionPasswordManager
import org.hisp.dhis.android.core.maintenance.internal.ForeignKeyCleaner
@@ -38,7 +37,6 @@ import org.hisp.dhis.android.core.maintenance.internal.ForeignKeyCleanerImpl
import org.hisp.dhis.android.core.note.internal.NoteUniquenessManager
import org.hisp.dhis.android.core.period.internal.PeriodHelper
import org.hisp.dhis.android.core.period.internal.PeriodParser
-import org.hisp.dhis.android.core.relationship.internal.RelationshipDHISVersionManager
import org.hisp.dhis.android.core.sms.data.localdbrepository.internal.DataSetsStore
import org.hisp.dhis.android.core.sms.data.localdbrepository.internal.FileResourceCleaner
import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceService
@@ -54,8 +52,6 @@ internal val javaDIClasses = module {
single { DatabaseEncryptionPasswordGenerator() }
single { TrackedEntityInstanceService(get(), get(), get(), get()) }
single { DatabaseAdapterFactory(get(), get()) }
- single { DatabaseExport(get(), get(), get()) }
single { D2CallExecutor(get(), get()) }
- single { RelationshipDHISVersionManager(get()) }
single { DataSetsStore(get(), get(), get(), get()) }
}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/DatabaseExportMetadata.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/DatabaseExportMetadata.kt
new file mode 100644
index 0000000000..1fe5ea5cd9
--- /dev/null
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/DatabaseExportMetadata.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2004-2024, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.hisp.dhis.android.core.arch.db.access
+
+data class DatabaseExportMetadata(
+ val version: Int,
+ val date: String,
+ val serverUrl: String,
+ val username: String,
+ val encrypted: Boolean,
+)
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/DatabaseImportExport.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/DatabaseImportExport.kt
index 81b37bf5aa..34ee5233cf 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/DatabaseImportExport.kt
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/DatabaseImportExport.kt
@@ -30,6 +30,6 @@ package org.hisp.dhis.android.core.arch.db.access
import java.io.File
interface DatabaseImportExport {
- fun importDatabase(file: File)
+ fun importDatabase(file: File): DatabaseExportMetadata
fun exportLoggedUserDatabase(): File
}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/BaseDatabaseOpenHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/BaseDatabaseOpenHelper.kt
index 7d8aadd20a..2624a312ae 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/BaseDatabaseOpenHelper.kt
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/BaseDatabaseOpenHelper.kt
@@ -59,6 +59,6 @@ internal class BaseDatabaseOpenHelper(context: Context, targetVersion: Int) {
}
companion object {
- const val VERSION = 155
+ const val VERSION = 163
}
}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseExport.java b/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseExport.java
deleted file mode 100644
index 127b3d8498..0000000000
--- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseExport.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (c) 2004-2023, University of Oslo
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * Neither the name of the HISP project nor the names of its contributors may
- * be used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.hisp.dhis.android.core.arch.db.access.internal;
-
-import android.content.Context;
-import android.util.Log;
-
-import net.zetetic.database.sqlcipher.SQLiteDatabase;
-import net.zetetic.database.sqlcipher.SQLiteDatabaseHook;
-
-import org.hisp.dhis.android.core.common.internal.NativeLibraryLoader;
-import org.hisp.dhis.android.core.configuration.internal.DatabaseAccount;
-import org.hisp.dhis.android.core.configuration.internal.DatabaseConfigurationHelper;
-import org.hisp.dhis.android.core.configuration.internal.DatabaseEncryptionPasswordManager;
-
-import java.io.File;
-
-import io.reactivex.functions.Action;
-
-@SuppressWarnings("PMD.ExcessiveImports")
-public class DatabaseExport {
-
- private final Context context;
- private final DatabaseEncryptionPasswordManager passwordManager;
- private final DatabaseConfigurationHelper configurationHelper;
-
- public DatabaseExport(Context context, DatabaseEncryptionPasswordManager passwordManager,
- DatabaseConfigurationHelper configurationHelper) {
- this.context = context;
- this.passwordManager = passwordManager;
- this.configurationHelper = configurationHelper;
- }
-
- public void encrypt(String serverUrl, DatabaseAccount oldConfiguration) {
- DatabaseAccount newConfiguration = configurationHelper.changeEncryption(serverUrl, oldConfiguration);
- export(oldConfiguration, newConfiguration, null,
- passwordManager.getPassword(newConfiguration.databaseName()), "Encrypt", null,
- EncryptedDatabaseOpenHelper.hook);
- }
-
- public void decrypt(String serverUrl, DatabaseAccount oldConfiguration) {
- DatabaseAccount newConfiguration = configurationHelper.changeEncryption(serverUrl, oldConfiguration);
- export(oldConfiguration, newConfiguration, passwordManager.getPassword(oldConfiguration.databaseName()),
- "", "Decrypt", EncryptedDatabaseOpenHelper.hook, null);
- }
-
- private void export(DatabaseAccount oldConfiguration,
- DatabaseAccount newConfiguration, String oldPassword, String newPassword, String tag,
- SQLiteDatabaseHook oldHook, SQLiteDatabaseHook newHook) {
- wrapAction(() -> {
- File oldDatabaseFile = context.getDatabasePath(oldConfiguration.databaseName());
- File newDatabaseFile = context.getDatabasePath(newConfiguration.databaseName());
-
- NativeLibraryLoader.INSTANCE.loadSQLCipher();
- SQLiteDatabase oldDatabase = SQLiteDatabase.openOrCreateDatabase(oldDatabaseFile, oldPassword, null,
- null, oldHook);
- oldDatabase.rawExecSQL(String.format(
- "ATTACH DATABASE '%s' as alias KEY '%s';", newDatabaseFile.getAbsolutePath(), newPassword));
- if (newHook != null) {
- oldDatabase.rawExecSQL("PRAGMA alias.cipher_page_size = 16384;");
- oldDatabase.rawExecSQL("PRAGMA alias.cipher_memory_security = OFF;");
- }
- oldDatabase.rawExecSQL("SELECT sqlcipher_export('alias');");
- oldDatabase.rawExecSQL("DETACH DATABASE alias;");
-
- int version = oldDatabase.getVersion();
- SQLiteDatabase newDatabase = SQLiteDatabase.openOrCreateDatabase(newDatabaseFile, newPassword, null,
- null, newHook);
- newDatabase.setVersion(version);
-
- newDatabase.close();
- oldDatabase.close();
- }, tag);
- }
-
- @SuppressWarnings({"PMD.PrematureDeclaration", "PMD.PreserveStackTrace"})
- private void wrapAction(Action action, String tag) {
- long startMillis = System.currentTimeMillis();
- try {
- action.run();
- } catch (Exception e) {
- throw new RuntimeException("Exception thrown during database export action: " + tag);
- }
- long endMillis = System.currentTimeMillis();
-
- Log.e("DatabaseExport", tag + ": " + (endMillis - startMillis) + "ms");
- }
-}
\ No newline at end of file
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseExport.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseExport.kt
new file mode 100644
index 0000000000..839473e7cc
--- /dev/null
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseExport.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2004-2023, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.hisp.dhis.android.core.arch.db.access.internal
+
+import android.content.Context
+import android.util.Log
+import io.reactivex.functions.Action
+import net.zetetic.database.sqlcipher.SQLiteDatabase
+import net.zetetic.database.sqlcipher.SQLiteDatabaseHook
+import org.hisp.dhis.android.core.common.internal.NativeLibraryLoader.loadSQLCipher
+import org.hisp.dhis.android.core.configuration.internal.DatabaseAccount
+import org.hisp.dhis.android.core.configuration.internal.DatabaseConfigurationHelper
+import org.hisp.dhis.android.core.configuration.internal.DatabaseEncryptionPasswordManager
+import org.koin.core.annotation.Singleton
+import java.io.File
+
+@Singleton
+internal class DatabaseExport(
+ private val context: Context,
+ private val passwordManager: DatabaseEncryptionPasswordManager,
+ private val configurationHelper: DatabaseConfigurationHelper,
+) {
+ fun encrypt(serverUrl: String, oldConfiguration: DatabaseAccount) {
+ val newConfiguration = configurationHelper.changeEncryption(serverUrl, oldConfiguration)
+ export(
+ oldDatabaseFile = context.getDatabasePath(oldConfiguration.databaseName()),
+ newDatabaseFile = context.getDatabasePath(newConfiguration.databaseName()),
+ oldPassword = null,
+ newPassword = passwordManager.getPassword(newConfiguration.databaseName()),
+ tag = "Encrypt",
+ oldHook = null,
+ newHook = EncryptedDatabaseOpenHelper.hook,
+ )
+ }
+
+ fun encryptAndCopyTo(newConfiguration: DatabaseAccount, sourceFile: File, targetFile: File) {
+ export(
+ oldDatabaseFile = sourceFile,
+ newDatabaseFile = targetFile,
+ oldPassword = null,
+ newPassword = passwordManager.getPassword(newConfiguration.databaseName()),
+ tag = "Encrypt",
+ oldHook = null,
+ newHook = EncryptedDatabaseOpenHelper.hook,
+ )
+ }
+
+ fun decrypt(serverUrl: String, oldConfiguration: DatabaseAccount) {
+ val newConfiguration = configurationHelper.changeEncryption(serverUrl, oldConfiguration)
+ export(
+ oldDatabaseFile = context.getDatabasePath(oldConfiguration.databaseName()),
+ newDatabaseFile = context.getDatabasePath(newConfiguration.databaseName()),
+ oldPassword = passwordManager.getPassword(oldConfiguration.databaseName()),
+ newPassword = "",
+ tag = "Decrypt",
+ oldHook = EncryptedDatabaseOpenHelper.hook,
+ newHook = null,
+ )
+ }
+
+ fun decryptAndCopyTo(account: DatabaseAccount, destinationFile: File) {
+ export(
+ oldDatabaseFile = context.getDatabasePath(account.databaseName()),
+ newDatabaseFile = destinationFile,
+ oldPassword = passwordManager.getPassword(account.databaseName()),
+ newPassword = "",
+ tag = "Decrypt",
+ oldHook = EncryptedDatabaseOpenHelper.hook,
+ newHook = null,
+ )
+ }
+
+ @Suppress("LongParameterList")
+ private fun export(
+ oldDatabaseFile: File,
+ newDatabaseFile: File,
+ oldPassword: String?,
+ newPassword: String,
+ tag: String,
+ oldHook: SQLiteDatabaseHook?,
+ newHook: SQLiteDatabaseHook?,
+ ) {
+ wrapAction({
+ loadSQLCipher()
+
+ val oldDatabase = SQLiteDatabase.openOrCreateDatabase(oldDatabaseFile, oldPassword, null, null, oldHook)
+ oldDatabase.rawExecSQL(
+ String.format(
+ "ATTACH DATABASE '%s' as alias KEY '%s';",
+ newDatabaseFile.absolutePath,
+ newPassword,
+ ),
+ )
+
+ if (newHook != null) {
+ oldDatabase.rawExecSQL("PRAGMA alias.cipher_page_size = 16384;")
+ oldDatabase.rawExecSQL("PRAGMA alias.cipher_memory_security = OFF;")
+ }
+ oldDatabase.rawExecSQL("SELECT sqlcipher_export('alias');")
+ oldDatabase.rawExecSQL("DETACH DATABASE alias;")
+
+ val version = oldDatabase.version
+ val newDatabase = SQLiteDatabase.openOrCreateDatabase(
+ newDatabaseFile,
+ newPassword,
+ null,
+ null,
+ newHook,
+ )
+ newDatabase.version = version
+
+ newDatabase.close()
+ oldDatabase.close()
+ }, tag)
+ }
+
+ @Suppress("TooGenericExceptionCaught", "TooGenericExceptionThrown")
+ private fun wrapAction(action: Action, tag: String) {
+ val startMillis = System.currentTimeMillis()
+ try {
+ action.run()
+ } catch (e: Exception) {
+ throw RuntimeException("Exception thrown during database export action: $tag")
+ }
+ val endMillis = System.currentTimeMillis()
+ Log.e("DatabaseExport", tag + ": " + (endMillis - startMillis) + "ms")
+ }
+}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseImportExportImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseImportExportImpl.kt
index 668551d8e6..7154bf068a 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseImportExportImpl.kt
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseImportExportImpl.kt
@@ -28,45 +28,46 @@
package org.hisp.dhis.android.core.arch.db.access.internal
import android.content.Context
-import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter
+import org.hisp.dhis.android.core.arch.db.access.DatabaseExportMetadata
import org.hisp.dhis.android.core.arch.db.access.DatabaseImportExport
+import org.hisp.dhis.android.core.arch.helpers.DateUtils.getCurrentTimeAndDate
+import org.hisp.dhis.android.core.arch.json.internal.ObjectMapperFactory.objectMapper
import org.hisp.dhis.android.core.arch.storage.internal.CredentialsSecureStore
-import org.hisp.dhis.android.core.configuration.internal.DatabaseConfigurationHelper
-import org.hisp.dhis.android.core.configuration.internal.DatabaseConfigurationInsecureStore
-import org.hisp.dhis.android.core.configuration.internal.DatabaseNameGenerator
-import org.hisp.dhis.android.core.configuration.internal.DatabaseRenamer
+import org.hisp.dhis.android.core.configuration.internal.DatabaseAccount
import org.hisp.dhis.android.core.configuration.internal.MultiUserDatabaseManager
-import org.hisp.dhis.android.core.configuration.internal.ServerUrlParser
import org.hisp.dhis.android.core.maintenance.D2Error
import org.hisp.dhis.android.core.maintenance.D2ErrorCode
import org.hisp.dhis.android.core.maintenance.D2ErrorComponent
-import org.hisp.dhis.android.core.systeminfo.internal.SystemInfoStoreImpl
import org.hisp.dhis.android.core.user.UserModule
-import org.hisp.dhis.android.core.user.internal.UserStoreImpl
+import org.hisp.dhis.android.core.util.CipherUtil
+import org.hisp.dhis.android.core.util.FileUtils
+import org.hisp.dhis.android.core.util.deleteIfExists
+import org.hisp.dhis.android.core.util.simpleDateFormat
import org.koin.core.annotation.Singleton
import java.io.File
+import java.util.Date
@Singleton
internal class DatabaseImportExportImpl(
private val context: Context,
- private val nameGenerator: DatabaseNameGenerator,
private val multiUserDatabaseManager: MultiUserDatabaseManager,
private val userModule: UserModule,
private val credentialsStore: CredentialsSecureStore,
- private val databaseConfigurationSecureStore: DatabaseConfigurationInsecureStore,
- private val databaseRenamer: DatabaseRenamer,
- private val databaseAdapter: DatabaseAdapter,
+ private val databaseExport: DatabaseExport,
) : DatabaseImportExport {
companion object {
- const val TmpDatabase = "tmp-database.db"
const val ExportDatabase = "export-database.db"
+ const val ExportDatabaseProtected = "export-database-protected.db.zip"
+ const val ExportMetadata = "export-metadata.json"
+ const val ExportZip = "-database.zip"
}
private val d2ErrorBuilder = D2Error.builder()
.errorComponent(D2ErrorComponent.SDK)
- override fun importDatabase(file: File) {
+ @Suppress("TooGenericExceptionCaught")
+ override fun importDatabase(file: File): DatabaseExportMetadata {
if (userModule.blockingIsLogged()) {
throw d2ErrorBuilder
.errorDescription("Please log out to import database")
@@ -74,52 +75,63 @@ internal class DatabaseImportExportImpl(
.build()
}
- var databaseAdapter: DatabaseAdapter? = null
- try {
- context.deleteDatabase(TmpDatabase)
- val tmpDatabase = context.getDatabasePath(TmpDatabase)
- file.copyTo(tmpDatabase)
+ val importMetadataFile = getWorkingDir().resolve(ExportMetadata).also { it.deleteIfExists() }
+ val importDatabaseFile = getWorkingDir().resolve(ExportDatabaseProtected).also { it.deleteIfExists() }
- val openHelper = UnencryptedDatabaseOpenHelper(context, TmpDatabase, BaseDatabaseOpenHelper.VERSION)
- val database = openHelper.readableDatabase
- databaseAdapter = UnencryptedDatabaseAdapter(database, openHelper.databaseName)
+ return try {
+ FileUtils.unzipFiles(file, getWorkingDir())
- if (database.version > BaseDatabaseOpenHelper.VERSION) {
+ if (!importMetadataFile.exists() || !importDatabaseFile.exists()) {
throw d2ErrorBuilder
- .errorDescription("Import database version higher than supported")
- .errorCode(D2ErrorCode.DATABASE_IMPORT_VERSION_HIGHER_THAN_SUPPORTED)
+ .errorDescription("Import file is not valid")
+ .errorCode(D2ErrorCode.DATABASE_IMPORT_INVALID_FILE)
.build()
}
- val userStore = UserStoreImpl(databaseAdapter)
- val username = userStore.selectFirst()!!.username()
-
- val systemInfoStore = SystemInfoStoreImpl(databaseAdapter)
- val contextPath = systemInfoStore.selectFirst()!!.contextPath()!!
- val serverUrl = ServerUrlParser.parse(contextPath).toString()
-
- // TODO What to do if username is null?
- val databaseName = nameGenerator.getDatabaseName(serverUrl, username!!, false)
-
- if (!context.databaseList().contains(databaseName)) {
- val destDatabase = context.getDatabasePath(databaseName)
- file.copyTo(destDatabase)
-
- multiUserDatabaseManager.createNew(serverUrl, username, false)
- } else {
- throw d2ErrorBuilder
- .errorDescription("Import database already exists")
- .errorCode(D2ErrorCode.DATABASE_IMPORT_ALREADY_EXISTS)
- .build()
+ val metadataContent = importMetadataFile.readText(Charsets.UTF_8)
+ val metadata = objectMapper().readValue(metadataContent, DatabaseExportMetadata::class.java)
+
+ when {
+ metadata.version > BaseDatabaseOpenHelper.VERSION ->
+ throw d2ErrorBuilder
+ .errorDescription("Import database version higher than supported")
+ .errorCode(D2ErrorCode.DATABASE_IMPORT_VERSION_HIGHER_THAN_SUPPORTED)
+ .build()
+
+ getExistingAccountForMetadata(metadata) != null ->
+ throw d2ErrorBuilder
+ .errorDescription("Import database already exists")
+ .errorCode(D2ErrorCode.DATABASE_IMPORT_ALREADY_EXISTS)
+ .build()
+
+ else -> {
+ val databaseAccount = multiUserDatabaseManager.createNewPendingToImport(metadata)
+ val destDatabase = context.getDatabasePath(databaseAccount.importDB()!!.protectedDbName())
+ importDatabaseFile.copyTo(destDatabase)
+
+ metadata
+ }
+ }
+ } catch (e: Exception) {
+ when (e) {
+ is D2Error -> throw e
+ else ->
+ throw d2ErrorBuilder
+ .errorDescription("Import database failed")
+ .errorCode(D2ErrorCode.DATABASE_IMPORT_FAILED)
+ .originalException(e)
+ .build()
}
} finally {
- databaseAdapter?.close()
- context.deleteDatabase(TmpDatabase)
+ importMetadataFile.deleteIfExists()
+ importDatabaseFile.deleteIfExists()
}
}
override fun exportLoggedUserDatabase(): File {
- context.deleteDatabase(ExportDatabase)
+ val exportMetadataFile = getWorkingDir().resolve(ExportMetadata).also { it.deleteIfExists() }
+ val copiedDatabase = getWorkingDir().resolve(ExportDatabase).also { it.deleteIfExists() }
+ val protectedDatabase = getWorkingDir().resolve(ExportDatabaseProtected).also { it.deleteIfExists() }
if (!userModule.blockingIsLogged()) {
throw d2ErrorBuilder
@@ -129,23 +141,65 @@ internal class DatabaseImportExportImpl(
}
val credentials = credentialsStore.get()
- val databasesConfiguration = databaseConfigurationSecureStore.get()
- val userConfiguration = DatabaseConfigurationHelper.getLoggedAccount(
- databasesConfiguration,
- credentials.serverUrl,
- credentials.username,
- )
+ val userConfiguration = multiUserDatabaseManager.getAccount(
+ username = credentials.username,
+ serverUrl = credentials.serverUrl,
+ )!!
+
+ val databaseName = userConfiguration.databaseName()
+ val databaseFile = getDatabaseFile(databaseName)
if (userConfiguration.encrypted()) {
- throw d2ErrorBuilder
- .errorDescription("Database export of encrypted database not supported")
- .errorCode(D2ErrorCode.DATABASE_EXPORT_ENCRYPTED_NOT_SUPPORTED)
- .build()
+ databaseExport.decryptAndCopyTo(userConfiguration, copiedDatabase)
+ } else {
+ databaseFile.copyTo(copiedDatabase)
}
- databaseAdapter.close()
+ CipherUtil.createEncryptedZipFile(
+ input = copiedDatabase,
+ output = protectedDatabase,
+ password = credentials.password!!,
+ )
- val databaseName = userConfiguration.databaseName()
- return databaseRenamer.copyDatabase(databaseName, ExportDatabase)
+ val metadata = DatabaseExportMetadata(
+ version = BaseDatabaseOpenHelper.VERSION,
+ date = Date().simpleDateFormat()!!,
+ serverUrl = userConfiguration.serverUrl(),
+ username = userConfiguration.username(),
+ encrypted = userConfiguration.encrypted(),
+ )
+
+ exportMetadataFile.bufferedWriter(Charsets.UTF_8).use {
+ it.write(objectMapper().writeValueAsString(metadata))
+ }
+
+ val zipName = credentials.username + '-' + getCurrentTimeAndDate() + '-' + ExportZip
+ val zipFile = getWorkingDir().resolve(zipName).also { it.deleteIfExists() }
+
+ FileUtils.zipFiles(
+ files = listOf(exportMetadataFile, protectedDatabase),
+ zipFile = zipFile,
+ )
+
+ exportMetadataFile.deleteIfExists()
+ copiedDatabase.deleteIfExists()
+ protectedDatabase.deleteIfExists()
+
+ return zipFile
+ }
+
+ private fun getWorkingDir(): File {
+ return context.filesDir
+ }
+
+ private fun getDatabaseFile(dbName: String): File {
+ return context.getDatabasePath(dbName)
+ }
+
+ private fun getExistingAccountForMetadata(metadata: DatabaseExportMetadata): DatabaseAccount? {
+ return multiUserDatabaseManager.getAccount(
+ metadata.serverUrl,
+ metadata.username,
+ )
}
}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/TrackerDataViewColumnAdapter.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/TrackerDataViewColumnAdapter.kt
new file mode 100644
index 0000000000..c60559545a
--- /dev/null
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/TrackerDataViewColumnAdapter.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2004-2023, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.hisp.dhis.android.core.arch.db.adapters.custom.internal
+
+import android.content.ContentValues
+import android.database.Cursor
+import com.gabrielittner.auto.value.cursor.ColumnTypeAdapter
+import org.hisp.dhis.android.core.relationship.TrackerDataView
+class TrackerDataViewColumnAdapter : ColumnTypeAdapter {
+ override fun fromCursor(cursor: Cursor, columnName: String): TrackerDataView? {
+ return TrackerDataView.create(cursor)
+ }
+
+ override fun toContentValues(values: ContentValues, columnName: String, value: TrackerDataView?) {
+ value?.toContentValues()
+ }
+}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/TrackerVisualizationDimensionRepetitionColumnAdapter.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/TrackerVisualizationDimensionRepetitionColumnAdapter.kt
new file mode 100644
index 0000000000..7c1056dd75
--- /dev/null
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/TrackerVisualizationDimensionRepetitionColumnAdapter.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2004-2023, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.hisp.dhis.android.core.arch.db.adapters.custom.internal
+
+import org.hisp.dhis.android.core.arch.json.internal.ObjectMapperFactory
+import org.hisp.dhis.android.core.visualization.TrackerVisualizationDimensionRepetition
+
+internal class TrackerVisualizationDimensionRepetitionColumnAdapter :
+ JSONObjectColumnAdapter() {
+
+ override fun getEnumClass(): Class {
+ return TrackerVisualizationDimensionRepetition::class.java
+ }
+
+ override fun serialize(o: TrackerVisualizationDimensionRepetition?): String? {
+ return TrackerVisualizationDimensionRepetitionColumnAdapter.serialize(o)
+ }
+
+ companion object {
+ fun serialize(o: TrackerVisualizationDimensionRepetition?): String? {
+ return o?.let {
+ ObjectMapperFactory.objectMapper().writeValueAsString(it)
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/AnalyticsDhisVisualizationScopeColumnAdapter.java b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/AnalyticsDhisVisualizationScopeColumnAdapter.kt
similarity index 86%
rename from core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/AnalyticsDhisVisualizationScopeColumnAdapter.java
rename to core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/AnalyticsDhisVisualizationScopeColumnAdapter.kt
index 4c5afef0fa..0573c292b8 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/AnalyticsDhisVisualizationScopeColumnAdapter.java
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/AnalyticsDhisVisualizationScopeColumnAdapter.kt
@@ -25,14 +25,12 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+package org.hisp.dhis.android.core.arch.db.adapters.enums.internal
-package org.hisp.dhis.android.core.arch.db.adapters.enums.internal;
+import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualizationScope
-import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualizationScope;
-
-public class AnalyticsDhisVisualizationScopeColumnAdapter extends EnumColumnAdapter {
- @Override
- protected Class getEnumClass() {
- return AnalyticsDhisVisualizationScope.class;
+internal class AnalyticsDhisVisualizationScopeColumnAdapter : EnumColumnAdapter() {
+ override fun getEnumClass(): Class {
+ return AnalyticsDhisVisualizationScope::class.java
}
}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/AnalyticsDhisVisualizationTypeColumnAdapter.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/AnalyticsDhisVisualizationTypeColumnAdapter.kt
new file mode 100644
index 0000000000..56ae10991f
--- /dev/null
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/AnalyticsDhisVisualizationTypeColumnAdapter.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2004-2023, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.hisp.dhis.android.core.arch.db.adapters.enums.internal
+
+import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualizationType
+
+internal class AnalyticsDhisVisualizationTypeColumnAdapter : EnumColumnAdapter() {
+ override fun getEnumClass(): Class {
+ return AnalyticsDhisVisualizationType::class.java
+ }
+}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/ImageFormatColumnAdapter.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/ImageFormatColumnAdapter.kt
new file mode 100644
index 0000000000..7ea017a3fe
--- /dev/null
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/ImageFormatColumnAdapter.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2004-2023, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.hisp.dhis.android.core.arch.db.adapters.enums.internal
+
+import org.hisp.dhis.android.core.map.layer.ImageFormat
+
+internal class ImageFormatColumnAdapter : EnumColumnAdapter() {
+ override fun getEnumClass(): Class {
+ return ImageFormat::class.java
+ }
+}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/MapServiceColumnAdapter.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/MapServiceColumnAdapter.kt
new file mode 100644
index 0000000000..196ab4754d
--- /dev/null
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/MapServiceColumnAdapter.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2004-2023, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.hisp.dhis.android.core.arch.db.adapters.enums.internal
+
+import org.hisp.dhis.android.core.map.layer.MapService
+
+internal class MapServiceColumnAdapter : EnumColumnAdapter() {
+ override fun getEnumClass(): Class {
+ return MapService::class.java
+ }
+}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/TrackerVisualizationOutputTypeColumnAdapter.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/TrackerVisualizationOutputTypeColumnAdapter.kt
new file mode 100644
index 0000000000..2427c364ec
--- /dev/null
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/TrackerVisualizationOutputTypeColumnAdapter.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2004-2023, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.hisp.dhis.android.core.arch.db.adapters.enums.internal
+
+import org.hisp.dhis.android.core.visualization.TrackerVisualizationOutputType
+
+class TrackerVisualizationOutputTypeColumnAdapter : EnumColumnAdapter() {
+ override fun getEnumClass(): Class {
+ return TrackerVisualizationOutputType::class.java
+ }
+}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/TrackerVisualizationTypeColumnAdapter.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/TrackerVisualizationTypeColumnAdapter.kt
new file mode 100644
index 0000000000..9cde39b30d
--- /dev/null
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/TrackerVisualizationTypeColumnAdapter.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2004-2023, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.hisp.dhis.android.core.arch.db.adapters.enums.internal
+
+import org.hisp.dhis.android.core.visualization.TrackerVisualizationType
+
+class TrackerVisualizationTypeColumnAdapter : EnumColumnAdapter() {
+ override fun getEnumClass(): Class {
+ return TrackerVisualizationType::class.java
+ }
+}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/identifiable/internal/ObjectWithUidListColumnAdapter.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/identifiable/internal/ObjectWithUidListColumnAdapter.kt
new file mode 100644
index 0000000000..b2737fcb56
--- /dev/null
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/identifiable/internal/ObjectWithUidListColumnAdapter.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2004-2023, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.hisp.dhis.android.core.arch.db.adapters.identifiable.internal
+
+import android.content.ContentValues
+import android.database.Cursor
+import com.fasterxml.jackson.core.JsonProcessingException
+import com.fasterxml.jackson.databind.JsonMappingException
+import com.gabrielittner.auto.value.cursor.ColumnTypeAdapter
+import org.hisp.dhis.android.core.arch.json.internal.ObjectMapperFactory
+import org.hisp.dhis.android.core.common.ObjectWithUid
+
+internal class ObjectWithUidListColumnAdapter : ColumnTypeAdapter> {
+
+ override fun fromCursor(cursor: Cursor, columnName: String): List {
+ val columnIndex = cursor.getColumnIndex(columnName)
+ val str = cursor.getString(columnIndex)
+ return try {
+ val idList = ObjectMapperFactory.objectMapper().readValue(str, ArrayList().javaClass)
+ idList.map { ObjectWithUid.create(it) }
+ } catch (e: JsonProcessingException) {
+ listOf()
+ } catch (e: JsonMappingException) {
+ listOf()
+ } catch (e: IllegalArgumentException) {
+ listOf()
+ } catch (e: IllegalStateException) {
+ listOf()
+ }
+ }
+
+ override fun toContentValues(values: ContentValues, columnName: String, value: List?) {
+ try {
+ values.put(columnName, serialize(value))
+ } catch (e: JsonProcessingException) {
+ e.printStackTrace()
+ }
+ }
+
+ companion object {
+ fun serialize(o: List?): String? {
+ return o?.map { it.uid() }.let {
+ ObjectMapperFactory.objectMapper().writeValueAsString(it)
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/maintenance/MaintenanceModule.java b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreTrackerVisualizationDimensionListColumnAdapter.java
similarity index 77%
rename from core/src/main/java/org/hisp/dhis/android/core/maintenance/MaintenanceModule.java
rename to core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreTrackerVisualizationDimensionListColumnAdapter.java
index 7fc4e5bdea..b8abcc88e5 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/maintenance/MaintenanceModule.java
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreTrackerVisualizationDimensionListColumnAdapter.java
@@ -25,14 +25,13 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.hisp.dhis.android.core.maintenance;
-public interface MaintenanceModule {
- ForeignKeyViolationCollectionRepository foreignKeyViolations();
- D2ErrorCollectionRepository d2Errors();
- PerformanceHintsService getPerformanceHintsService(int organisationUnitThreshold,
- int programRulesPerProgramThreshold);
+package org.hisp.dhis.android.core.arch.db.adapters.ignore.internal;
+import org.hisp.dhis.android.core.visualization.TrackerVisualizationDimension;
- // TODO restore when finished DatabaseImportExport databaseImportExport();
+import java.util.List;
+
+public final class IgnoreTrackerVisualizationDimensionListColumnAdapter
+ extends IgnoreColumnAdapter> {
}
\ No newline at end of file
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreUserGroupListColumnAdapter.java b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreUserGroupListColumnAdapter.java
new file mode 100644
index 0000000000..ee9e851544
--- /dev/null
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreUserGroupListColumnAdapter.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2004-2023, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.hisp.dhis.android.core.arch.db.adapters.ignore.internal;
+
+import org.hisp.dhis.android.core.user.UserGroup;
+
+import java.util.List;
+
+public final class IgnoreUserGroupListColumnAdapter extends IgnoreColumnAdapter> {
+}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/uidseeker/internal/BaseUidsSeeker.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/uidseeker/internal/BaseUidsSeeker.kt
index da243f6167..ce5803d542 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/uidseeker/internal/BaseUidsSeeker.kt
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/uidseeker/internal/BaseUidsSeeker.kt
@@ -30,7 +30,7 @@ package org.hisp.dhis.android.core.arch.db.uidseeker.internal
import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter
-internal open class BaseUidsSeeker constructor(private val databaseAdapter: DatabaseAdapter) {
+internal open class BaseUidsSeeker(private val databaseAdapter: DatabaseAdapter) {
fun readSingleColumnResults(query: String): Set {
val cursor = databaseAdapter.rawQuery(query)
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/fields/internal/FieldsHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/fields/internal/FieldsHelper.kt
index b434ac5ebe..7e0500996b 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/arch/fields/internal/FieldsHelper.kt
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/fields/internal/FieldsHelper.kt
@@ -36,7 +36,7 @@ import org.hisp.dhis.android.core.common.ObjectWithUid
@Suppress("TooManyFunctions")
internal class FieldsHelper {
- fun field(fieldName: String): Property {
+ fun field(fieldName: String): Field {
return Field.create(fieldName)
}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/handlers/internal/IdentifiableDataHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/handlers/internal/IdentifiableDataHandler.kt
index 3c546a7838..b82fbf8601 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/arch/handlers/internal/IdentifiableDataHandler.kt
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/handlers/internal/IdentifiableDataHandler.kt
@@ -31,7 +31,7 @@ import org.hisp.dhis.android.core.common.DeletableDataObject
import org.hisp.dhis.android.core.common.ObjectWithUidInterface
import org.hisp.dhis.android.core.relationship.internal.RelationshipItemRelatives
-interface IdentifiableDataHandler where O : DeletableDataObject, O : ObjectWithUidInterface {
+internal interface IdentifiableDataHandler where O : DeletableDataObject, O : ObjectWithUidInterface {
@JvmSuppressWildcards
fun handleMany(
oCollection: Collection?,
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/handlers/internal/IdentifiableDataHandlerImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/handlers/internal/IdentifiableDataHandlerImpl.kt
index 1ae357f55a..ee3073f305 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/arch/handlers/internal/IdentifiableDataHandlerImpl.kt
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/handlers/internal/IdentifiableDataHandlerImpl.kt
@@ -141,7 +141,6 @@ internal abstract class IdentifiableDataHandlerImpl(
ownedRelationships,
parent.uid(),
relatives,
- relationshipHandler,
)
}
relationshipHandler.handleMany(
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/handlers/internal/ObjectWithoutUidHandlerImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/handlers/internal/ObjectWithoutUidHandlerImpl.kt
index cbdef7bca5..afd2048a1e 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/arch/handlers/internal/ObjectWithoutUidHandlerImpl.kt
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/handlers/internal/ObjectWithoutUidHandlerImpl.kt
@@ -30,7 +30,7 @@ package org.hisp.dhis.android.core.arch.handlers.internal
import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore
import org.hisp.dhis.android.core.common.CoreObject
-internal open class ObjectWithoutUidHandlerImpl(protected val store: ObjectWithoutUidStore) :
+internal open class ObjectWithoutUidHandlerImpl(protected val store: ObjectWithoutUidStore) :
HandlerBaseImpl() {
override fun deleteOrPersist(o: O): HandleAction {
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/DateUtils.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/DateUtils.kt
index eef30bd5eb..0c4ae84d92 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/DateUtils.kt
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/DateUtils.kt
@@ -27,6 +27,9 @@
*/
package org.hisp.dhis.android.core.arch.helpers
+import kotlinx.datetime.Clock
+import kotlinx.datetime.TimeZone
+import kotlinx.datetime.toLocalDateTime
import org.hisp.dhis.android.core.arch.dateformat.internal.SafeDateFormat
import org.hisp.dhis.android.core.period.Period
import org.hisp.dhis.android.core.period.PeriodType
@@ -95,4 +98,18 @@ object DateUtils {
c.add(Calendar.MONTH, amount)
return c.time
}
+
+ private fun Int.zeroPrefixed(length: Int = 2): String = this.toString().padStart(length, '0')
+ internal fun getCurrentTimeAndDate(): String {
+ val dateTime = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault())
+
+ val year = dateTime.year
+ val month = dateTime.monthNumber.zeroPrefixed()
+ val day = dateTime.dayOfMonth.zeroPrefixed()
+ val hour = dateTime.hour.zeroPrefixed()
+ val minute = dateTime.minute.zeroPrefixed()
+ val seconds = dateTime.second.zeroPrefixed()
+
+ return "$year$month$day-$hour$minute$seconds"
+ }
}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/collection/ReadOnlyCollectionRepository.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/collection/ReadOnlyCollectionRepository.kt
index d242f752e6..6fc97e38a9 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/collection/ReadOnlyCollectionRepository.kt
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/collection/ReadOnlyCollectionRepository.kt
@@ -29,7 +29,9 @@ package org.hisp.dhis.android.core.arch.repositories.collection
import androidx.lifecycle.LiveData
import androidx.paging.PagedList
+import androidx.paging.PagingData
import io.reactivex.Single
+import kotlinx.coroutines.flow.Flow
import org.hisp.dhis.android.core.arch.repositories.`object`.ReadOnlyObjectRepository
interface ReadOnlyCollectionRepository : BaseRepository {
@@ -57,6 +59,13 @@ interface ReadOnlyCollectionRepository : BaseRepository {
@Deprecated(message = "Use {@link #getPagingData()} instead}", replaceWith = ReplaceWith("getPagingData()"))
fun getPaged(pageSize: Int): LiveData>
+ /**
+ * Uses Paging3 library and return a Flow
+ * @param pageSize Length of the page
+ * @return a Flow of PagingData elements
+ */
+ fun getPagingData(pageSize: Int): Flow>
+
/**
* Get the count of elements in an asynchronous way, returning a `Single`.
* @return A `Single` object with the element count
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/collection/internal/ReadOnlyCollectionRepositoryImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/collection/internal/ReadOnlyCollectionRepositoryImpl.kt
index 665b2743cd..2992f48403 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/collection/internal/ReadOnlyCollectionRepositoryImpl.kt
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/collection/internal/ReadOnlyCollectionRepositoryImpl.kt
@@ -31,7 +31,12 @@ import androidx.lifecycle.LiveData
import androidx.paging.DataSource
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
+import androidx.paging.Pager
+import androidx.paging.PagingConfig
+import androidx.paging.PagingData
+import androidx.paging.PagingSource
import io.reactivex.Single
+import kotlinx.coroutines.flow.Flow
import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter
import org.hisp.dhis.android.core.arch.db.querybuilders.internal.OrderByClauseBuilder
import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder
@@ -42,10 +47,12 @@ import org.hisp.dhis.android.core.arch.repositories.collection.ReadOnlyCollectio
import org.hisp.dhis.android.core.arch.repositories.filters.internal.FilterConnectorFactory
import org.hisp.dhis.android.core.arch.repositories.`object`.ReadOnlyOneObjectRepositoryFinalImpl
import org.hisp.dhis.android.core.arch.repositories.paging.internal.RepositoryDataSource
+import org.hisp.dhis.android.core.arch.repositories.paging.internal.RepositoryPagingSource
import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope
import org.hisp.dhis.android.core.arch.repositories.scope.internal.WhereClauseFromScopeBuilder
import org.hisp.dhis.android.core.common.CoreObject
+@Suppress("TooManyFunctions")
open class ReadOnlyCollectionRepositoryImpl> internal constructor(
private val store: ReadableStore,
internal val databaseAdapter: DatabaseAdapter,
@@ -113,10 +120,25 @@ open class ReadOnlyCollectionRepositoryImpl> {
+ return getPager(pageSize).flow
+ }
+
+ fun getPager(pageSize: Int): Pager {
+ return Pager(
+ config = PagingConfig(pageSize = pageSize),
+ ) {
+ pagingSource
+ }
+ }
+
@Deprecated("Use {@link #getPagingData()} instead}", replaceWith = ReplaceWith("getPagingData()"))
val dataSource: DataSource
get() = RepositoryDataSource(store, databaseAdapter, scope, childrenAppenders)
+ private val pagingSource: PagingSource
+ get() = RepositoryPagingSource(store, databaseAdapter, scope, childrenAppenders)
+
/**
* Get the count of elements in an asynchronous way, returning a `Single`.
* @return A `Single` object with the element count
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/collection/internal/ReadOnlyWithTransformerCollectionRepositoryImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/collection/internal/ReadOnlyWithTransformerCollectionRepositoryImpl.kt
index 2e3c34d678..29c3469661 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/collection/internal/ReadOnlyWithTransformerCollectionRepositoryImpl.kt
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/collection/internal/ReadOnlyWithTransformerCollectionRepositoryImpl.kt
@@ -31,7 +31,12 @@ import androidx.lifecycle.LiveData
import androidx.paging.DataSource
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
+import androidx.paging.Pager
+import androidx.paging.PagingConfig
+import androidx.paging.PagingData
+import androidx.paging.PagingSource
import io.reactivex.Single
+import kotlinx.coroutines.flow.Flow
import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter
import org.hisp.dhis.android.core.arch.db.querybuilders.internal.OrderByClauseBuilder
import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder
@@ -44,10 +49,12 @@ import org.hisp.dhis.android.core.arch.repositories.filters.internal.FilterConne
import org.hisp.dhis.android.core.arch.repositories.`object`.ReadOnlyObjectRepository
import org.hisp.dhis.android.core.arch.repositories.`object`.internal.ReadOnlyWithTransformerObjectRepositoryImpl
import org.hisp.dhis.android.core.arch.repositories.paging.internal.RepositoryDataSourceWithTransformer
+import org.hisp.dhis.android.core.arch.repositories.paging.internal.RepositoryPagingSourceWithTransformer
import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope
import org.hisp.dhis.android.core.arch.repositories.scope.internal.WhereClauseFromScopeBuilder
import org.hisp.dhis.android.core.common.CoreObject
+@Suppress("TooManyFunctions")
internal open class ReadOnlyWithTransformerCollectionRepositoryImpl<
M : CoreObject,
T : Any,
@@ -126,9 +133,28 @@ internal open class ReadOnlyWithTransformerCollectionRepositoryImpl<
return LivePagedListBuilder(factory, pageSize).build()
}
+ override fun getPagingData(pageSize: Int): Flow> {
+ return Pager(
+ config = PagingConfig(pageSize = pageSize),
+ ) {
+ pagingSource
+ }.flow
+ }
+
+ fun getPager(pageSize: Int): Pager {
+ return Pager(
+ config = PagingConfig(pageSize = pageSize),
+ ) {
+ pagingSource
+ }
+ }
+
val dataSource: DataSource
get() = RepositoryDataSourceWithTransformer(store, databaseAdapter, scope, childrenAppenders, transformer)
+ private val pagingSource: PagingSource
+ get() = RepositoryPagingSourceWithTransformer(store, databaseAdapter, scope, childrenAppenders, transformer)
+
/**
* Get the count of elements in an asynchronous way, returning a `Single`.
* @return A `Single` object with the element count
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/paging/PageConfig.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/paging/PageConfig.kt
new file mode 100644
index 0000000000..d3c819a24b
--- /dev/null
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/paging/PageConfig.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2004-2024, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.hisp.dhis.android.core.arch.repositories.paging
+
+sealed class PageConfig {
+ data object NoPaging : PageConfig()
+ data class Paging(val page: Int, val pageSize: Int) : PageConfig()
+}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/paging/internal/RepositoryPagingSource.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/paging/internal/RepositoryPagingSource.kt
new file mode 100644
index 0000000000..bd908852fd
--- /dev/null
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/paging/internal/RepositoryPagingSource.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2004-2023, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.hisp.dhis.android.core.arch.repositories.paging.internal
+
+import androidx.paging.PagingSource
+import androidx.paging.PagingState
+import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter
+import org.hisp.dhis.android.core.arch.db.querybuilders.internal.OrderByClauseBuilder
+import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder
+import org.hisp.dhis.android.core.arch.db.stores.internal.ReadableStore
+import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppenderExecutor.appendInObjectCollection
+import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppenderGetter
+import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope
+import org.hisp.dhis.android.core.arch.repositories.scope.internal.WhereClauseFromScopeBuilder
+import org.hisp.dhis.android.core.common.CoreObject
+import java.io.IOException
+
+class RepositoryPagingSource internal constructor(
+ private val store: ReadableStore,
+ private val databaseAdapter: DatabaseAdapter,
+ private val scope: RepositoryScope,
+ private val childrenAppenders: ChildrenAppenderGetter,
+) : PagingSource() {
+
+ override fun getRefreshKey(state: PagingState): M? {
+ return state.anchorPosition?.let { state.closestPageToPosition(it)?.prevKey }
+ }
+
+ override suspend fun load(params: LoadParams): LoadResult {
+ try {
+ val whereClauseBuilder = WhereClauseBuilder()
+
+ params.key?.let { key ->
+ val reverse = when (params) {
+ is LoadParams.Prepend -> true
+ else -> false
+ }
+
+ OrderByClauseBuilder.addSortingClauses(
+ whereClauseBuilder,
+ scope.orderBy(),
+ key.toContentValues(),
+ reverse,
+ scope.pagingKey(),
+ )
+ }
+
+ val whereClause = WhereClauseFromScopeBuilder(whereClauseBuilder).getWhereClause(
+ scope,
+ )
+ val withoutChildren = store.selectWhere(
+ whereClause,
+ OrderByClauseBuilder.orderByFromItems(scope.orderBy(), scope.pagingKey()),
+ params.loadSize,
+ )
+
+ val items = appendInObjectCollection(withoutChildren, databaseAdapter, childrenAppenders, scope.children())
+ return LoadResult.Page(
+ data = items,
+ prevKey = items.firstOrNull(),
+ nextKey = items.getOrNull(params.loadSize - 1),
+ )
+ } catch (e: IOException) {
+ return LoadResult.Error(e)
+ }
+ }
+}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/paging/internal/RepositoryPagingSourceWithTransformer.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/paging/internal/RepositoryPagingSourceWithTransformer.kt
new file mode 100644
index 0000000000..f91b42b159
--- /dev/null
+++ b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/paging/internal/RepositoryPagingSourceWithTransformer.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2004-2023, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.hisp.dhis.android.core.arch.repositories.paging.internal
+
+import androidx.paging.PagingSource
+import androidx.paging.PagingState
+import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter
+import org.hisp.dhis.android.core.arch.db.querybuilders.internal.OrderByClauseBuilder
+import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder
+import org.hisp.dhis.android.core.arch.db.stores.internal.ReadableStore
+import org.hisp.dhis.android.core.arch.handlers.internal.TwoWayTransformer
+import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppenderExecutor
+import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppenderGetter
+import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope
+import org.hisp.dhis.android.core.arch.repositories.scope.internal.WhereClauseFromScopeBuilder
+import org.hisp.dhis.android.core.common.CoreObject
+import java.io.IOException
+
+internal class RepositoryPagingSourceWithTransformer internal constructor(
+ private val store: ReadableStore,
+ private val databaseAdapter: DatabaseAdapter,
+ private val scope: RepositoryScope,
+ private val childrenAppenders: ChildrenAppenderGetter,
+ private val transformer: TwoWayTransformer,
+) : PagingSource() {
+ override fun getRefreshKey(state: PagingState): M? {
+ return state.anchorPosition?.let { state.closestPageToPosition(it)?.prevKey }
+ }
+
+ override suspend fun load(params: LoadParams): LoadResult {
+ try {
+ val whereClauseBuilder = WhereClauseBuilder()
+
+ params.key?.let { key ->
+ val reverse = when (params) {
+ is LoadParams.Prepend -> true
+ else -> false
+ }
+
+ OrderByClauseBuilder.addSortingClauses(
+ whereClauseBuilder,
+ scope.orderBy(),
+ key.toContentValues(),
+ reverse,
+ scope.pagingKey(),
+ )
+ }
+
+ val whereClause = WhereClauseFromScopeBuilder(whereClauseBuilder).getWhereClause(scope)
+ val withoutChildren = store.selectWhere(
+ whereClause,
+ OrderByClauseBuilder.orderByFromItems(scope.orderBy(), scope.pagingKey()),
+ params.loadSize,
+ )
+ val items = ChildrenAppenderExecutor.appendInObjectCollection(
+ withoutChildren,
+ databaseAdapter,
+ childrenAppenders,
+ scope.children(),
+ )
+
+ return LoadResult.Page(
+ data = items.map { transformer.transform(it) },
+ prevKey = items.firstOrNull(),
+ nextKey = items.getOrNull(params.loadSize - 1),
+ )
+ } catch (e: IOException) {
+ return LoadResult.Error(e)
+ }
+ }
+}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/attribute/DataElementAttributeValueLink.java b/core/src/main/java/org/hisp/dhis/android/core/attribute/DataElementAttributeValueLink.java
index 219d1770a4..c36792513f 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/attribute/DataElementAttributeValueLink.java
+++ b/core/src/main/java/org/hisp/dhis/android/core/attribute/DataElementAttributeValueLink.java
@@ -60,7 +60,7 @@ public static Builder builder() {
public abstract Builder toBuilder();
@AutoValue.Builder
- public static abstract class Builder extends BaseObject.Builder {
+ public abstract static class Builder extends BaseObject.Builder {
public abstract Builder id(Long id);
public abstract Builder dataElement(String dataElement);
diff --git a/core/src/main/java/org/hisp/dhis/android/core/attribute/ProgramAttributeValueLink.java b/core/src/main/java/org/hisp/dhis/android/core/attribute/ProgramAttributeValueLink.java
index fb7a14f7bf..aa6cccc8c4 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/attribute/ProgramAttributeValueLink.java
+++ b/core/src/main/java/org/hisp/dhis/android/core/attribute/ProgramAttributeValueLink.java
@@ -60,7 +60,7 @@ public static Builder builder() {
public abstract Builder toBuilder();
@AutoValue.Builder
- public static abstract class Builder extends BaseObject.Builder {
+ public abstract static class Builder extends BaseObject.Builder {
public abstract Builder id(Long id);
public abstract Builder program(String program);
diff --git a/core/src/main/java/org/hisp/dhis/android/core/attribute/ProgramStageAttributeValueLink.java b/core/src/main/java/org/hisp/dhis/android/core/attribute/ProgramStageAttributeValueLink.java
index 679d6eb034..6b73887969 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/attribute/ProgramStageAttributeValueLink.java
+++ b/core/src/main/java/org/hisp/dhis/android/core/attribute/ProgramStageAttributeValueLink.java
@@ -60,7 +60,7 @@ public static Builder builder() {
public abstract Builder toBuilder();
@AutoValue.Builder
- public static abstract class Builder extends BaseObject.Builder {
+ public abstract static class Builder extends BaseObject.Builder {
public abstract Builder id(Long id);
public abstract Builder programStage(String programStage);
diff --git a/core/src/main/java/org/hisp/dhis/android/core/attribute/internal/AttributeValuesFields.kt b/core/src/main/java/org/hisp/dhis/android/core/attribute/internal/AttributeValuesFields.kt
new file mode 100644
index 0000000000..588c26631c
--- /dev/null
+++ b/core/src/main/java/org/hisp/dhis/android/core/attribute/internal/AttributeValuesFields.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2004-2023, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.hisp.dhis.android.core.attribute.internal
+
+import org.hisp.dhis.android.core.arch.api.fields.internal.Fields
+import org.hisp.dhis.android.core.arch.fields.internal.FieldsHelper
+import org.hisp.dhis.android.core.attribute.AttributeValue
+import org.hisp.dhis.android.core.common.ObjectWithUid
+
+internal object AttributeValuesFields {
+ const val VALUE = "value"
+ const val ATTRIBUTE = "attribute"
+
+ private val fh = FieldsHelper()
+
+ val allFields: Fields = Fields.builder()
+ .fields(
+ fh.field(VALUE),
+ fh.nestedField(ATTRIBUTE).with(ObjectWithUid.uid),
+ ).build()
+}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/category/Category.java b/core/src/main/java/org/hisp/dhis/android/core/category/Category.java
index d4b21479a0..bac8207357 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/category/Category.java
+++ b/core/src/main/java/org/hisp/dhis/android/core/category/Category.java
@@ -69,7 +69,7 @@ public static Builder builder() {
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "")
- public static abstract class Builder extends BaseIdentifiableObject.Builder {
+ public abstract static class Builder extends BaseIdentifiableObject.Builder {
public abstract Builder id(Long id);
public abstract Builder categoryOptions(@Nullable List categoryOptions);
diff --git a/core/src/main/java/org/hisp/dhis/android/core/category/CategoryCategoryComboLink.java b/core/src/main/java/org/hisp/dhis/android/core/category/CategoryCategoryComboLink.java
index 29b275a5ac..a728de20c9 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/category/CategoryCategoryComboLink.java
+++ b/core/src/main/java/org/hisp/dhis/android/core/category/CategoryCategoryComboLink.java
@@ -60,7 +60,7 @@ public static CategoryCategoryComboLink create(Cursor cursor) {
public abstract Builder toBuilder();
@AutoValue.Builder
- public static abstract class Builder extends BaseObject.Builder {
+ public abstract static class Builder extends BaseObject.Builder {
public abstract Builder id(Long id);
diff --git a/core/src/main/java/org/hisp/dhis/android/core/category/CategoryCategoryOptionLink.java b/core/src/main/java/org/hisp/dhis/android/core/category/CategoryCategoryOptionLink.java
index d54053f64a..be4ac31993 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/category/CategoryCategoryOptionLink.java
+++ b/core/src/main/java/org/hisp/dhis/android/core/category/CategoryCategoryOptionLink.java
@@ -60,7 +60,7 @@ public static CategoryCategoryOptionLink create(Cursor cursor) {
public abstract Builder toBuilder();
@AutoValue.Builder
- public static abstract class Builder extends BaseObject.Builder {
+ public abstract static class Builder extends BaseObject.Builder {
public abstract Builder id(Long id);
diff --git a/core/src/main/java/org/hisp/dhis/android/core/category/CategoryCombo.java b/core/src/main/java/org/hisp/dhis/android/core/category/CategoryCombo.java
index 0cea339861..e20af41e85 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/category/CategoryCombo.java
+++ b/core/src/main/java/org/hisp/dhis/android/core/category/CategoryCombo.java
@@ -77,7 +77,7 @@ public static Builder builder() {
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "")
- public static abstract class Builder extends BaseIdentifiableObject.Builder {
+ public abstract static class Builder extends BaseIdentifiableObject.Builder {
public abstract Builder id(Long id);
diff --git a/core/src/main/java/org/hisp/dhis/android/core/category/CategoryOption.java b/core/src/main/java/org/hisp/dhis/android/core/category/CategoryOption.java
index 5d9d7439e4..6b4298d9b7 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/category/CategoryOption.java
+++ b/core/src/main/java/org/hisp/dhis/android/core/category/CategoryOption.java
@@ -88,7 +88,7 @@ public static Builder builder() {
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "")
- public static abstract class Builder extends BaseNameableObject.Builder {
+ public abstract static class Builder extends BaseNameableObject.Builder {
public abstract Builder id(Long id);
public abstract Builder startDate(@Nullable Date startDate);
diff --git a/core/src/main/java/org/hisp/dhis/android/core/category/CategoryOptionCombo.java b/core/src/main/java/org/hisp/dhis/android/core/category/CategoryOptionCombo.java
index 7526563aed..dd5e289241 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/category/CategoryOptionCombo.java
+++ b/core/src/main/java/org/hisp/dhis/android/core/category/CategoryOptionCombo.java
@@ -72,7 +72,7 @@ public static Builder builder() {
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "")
- public static abstract class Builder extends BaseIdentifiableObject.Builder {
+ public abstract static class Builder extends BaseIdentifiableObject.Builder {
public abstract Builder id(Long id);
diff --git a/core/src/main/java/org/hisp/dhis/android/core/category/CategoryOptionComboCategoryOptionLink.java b/core/src/main/java/org/hisp/dhis/android/core/category/CategoryOptionComboCategoryOptionLink.java
index 436228c2d5..8e4eb9e2e5 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/category/CategoryOptionComboCategoryOptionLink.java
+++ b/core/src/main/java/org/hisp/dhis/android/core/category/CategoryOptionComboCategoryOptionLink.java
@@ -57,7 +57,7 @@ public static CategoryOptionComboCategoryOptionLink create(Cursor cursor) {
public abstract Builder toBuilder();
@AutoValue.Builder
- public static abstract class Builder extends BaseObject.Builder {
+ public abstract static class Builder extends BaseObject.Builder {
public abstract Builder id(Long id);
diff --git a/core/src/main/java/org/hisp/dhis/android/core/category/CategoryOptionOrganisationUnitLink.java b/core/src/main/java/org/hisp/dhis/android/core/category/CategoryOptionOrganisationUnitLink.java
index 551ad86b02..fe8fc321b1 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/category/CategoryOptionOrganisationUnitLink.java
+++ b/core/src/main/java/org/hisp/dhis/android/core/category/CategoryOptionOrganisationUnitLink.java
@@ -60,7 +60,7 @@ public static CategoryOptionOrganisationUnitLink create(Cursor cursor) {
}
@AutoValue.Builder
- public static abstract class Builder extends BaseObject.Builder {
+ public abstract static class Builder extends BaseObject.Builder {
public abstract Builder id(Long id);
public abstract Builder categoryOption(String categoryOption);
diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/BaseDataObject.java b/core/src/main/java/org/hisp/dhis/android/core/common/BaseDataObject.java
index b713ed08f9..a761893045 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/common/BaseDataObject.java
+++ b/core/src/main/java/org/hisp/dhis/android/core/common/BaseDataObject.java
@@ -57,7 +57,7 @@ public State state() {
public abstract State syncState();
@JsonPOJOBuilder(withPrefix = "")
- protected static abstract class Builder extends BaseObject.Builder {
+ protected abstract static class Builder extends BaseObject.Builder {
public abstract T syncState(@Nullable State syncState);
/**
diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/BaseDeletableDataObject.java b/core/src/main/java/org/hisp/dhis/android/core/common/BaseDeletableDataObject.java
index eac0a783b2..1c2ae17488 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/common/BaseDeletableDataObject.java
+++ b/core/src/main/java/org/hisp/dhis/android/core/common/BaseDeletableDataObject.java
@@ -43,7 +43,7 @@ public abstract class BaseDeletableDataObject extends BaseDataObject implements
public abstract Boolean deleted();
@JsonPOJOBuilder(withPrefix = "")
- protected static abstract class Builder extends BaseDataObject.Builder {
+ protected abstract static class Builder extends BaseDataObject.Builder {
public abstract T deleted(@Nullable Boolean deleted);
}
}
diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/BaseIdentifiableObject.java b/core/src/main/java/org/hisp/dhis/android/core/common/BaseIdentifiableObject.java
index f26ba9e568..215be21c5d 100644
--- a/core/src/main/java/org/hisp/dhis/android/core/common/BaseIdentifiableObject.java
+++ b/core/src/main/java/org/hisp/dhis/android/core/common/BaseIdentifiableObject.java
@@ -107,7 +107,7 @@ public static String dateToDateStr(Date date) {
}
@JsonPOJOBuilder(withPrefix = "")
- public static abstract class Builder {
+ public abstract static class Builder