?,
private val onError: OnErrorCallback?,
private val onProgress: OnProgressCallback?
-) : Runnable, Cancellable {
+) : Runnable,
+ Cancellable {
val isCancelled: Boolean
get() = cancelled.get()
diff --git a/app/src/main/java/com/nextcloud/client/database/DatabaseModule.kt b/app/src/main/java/com/nextcloud/client/database/DatabaseModule.kt
index 31973c7c97d6..a0989a2c3bf5 100644
--- a/app/src/main/java/com/nextcloud/client/database/DatabaseModule.kt
+++ b/app/src/main/java/com/nextcloud/client/database/DatabaseModule.kt
@@ -11,6 +11,7 @@ import android.content.Context
import com.nextcloud.client.core.Clock
import com.nextcloud.client.database.dao.ArbitraryDataDao
import com.nextcloud.client.database.dao.FileDao
+import com.nextcloud.client.database.dao.OfflineOperationDao
import dagger.Module
import dagger.Provides
import javax.inject.Singleton
@@ -20,17 +21,15 @@ class DatabaseModule {
@Provides
@Singleton
- fun database(context: Context, clock: Clock): NextcloudDatabase {
- return NextcloudDatabase.getInstance(context, clock)
- }
+ fun database(context: Context, clock: Clock): NextcloudDatabase = NextcloudDatabase.getInstance(context, clock)
@Provides
- fun arbitraryDataDao(nextcloudDatabase: NextcloudDatabase): ArbitraryDataDao {
- return nextcloudDatabase.arbitraryDataDao()
- }
+ fun arbitraryDataDao(nextcloudDatabase: NextcloudDatabase): ArbitraryDataDao = nextcloudDatabase.arbitraryDataDao()
@Provides
- fun fileDao(nextcloudDatabase: NextcloudDatabase): FileDao {
- return nextcloudDatabase.fileDao()
- }
+ fun fileDao(nextcloudDatabase: NextcloudDatabase): FileDao = nextcloudDatabase.fileDao()
+
+ @Provides
+ fun offlineOperationsDao(nextcloudDatabase: NextcloudDatabase): OfflineOperationDao =
+ nextcloudDatabase.offlineOperationDao()
}
diff --git a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt
index 8b80435e6b1c..39fa5c7b33ae 100644
--- a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt
+++ b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt
@@ -12,23 +12,40 @@ import androidx.room.AutoMigration
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
+import androidx.room.TypeConverters
import com.nextcloud.client.core.Clock
import com.nextcloud.client.core.ClockImpl
import com.nextcloud.client.database.dao.ArbitraryDataDao
+import com.nextcloud.client.database.dao.AssistantDao
+import com.nextcloud.client.database.dao.CapabilityDao
import com.nextcloud.client.database.dao.FileDao
+import com.nextcloud.client.database.dao.FileSystemDao
+import com.nextcloud.client.database.dao.OfflineOperationDao
+import com.nextcloud.client.database.dao.RecommendedFileDao
+import com.nextcloud.client.database.dao.ShareDao
+import com.nextcloud.client.database.dao.SyncedFolderDao
+import com.nextcloud.client.database.dao.UploadDao
import com.nextcloud.client.database.entity.ArbitraryDataEntity
+import com.nextcloud.client.database.entity.AssistantEntity
import com.nextcloud.client.database.entity.CapabilityEntity
import com.nextcloud.client.database.entity.ExternalLinkEntity
import com.nextcloud.client.database.entity.FileEntity
import com.nextcloud.client.database.entity.FilesystemEntity
+import com.nextcloud.client.database.entity.OfflineOperationEntity
+import com.nextcloud.client.database.entity.RecommendedFileEntity
import com.nextcloud.client.database.entity.ShareEntity
import com.nextcloud.client.database.entity.SyncedFolderEntity
import com.nextcloud.client.database.entity.UploadEntity
import com.nextcloud.client.database.entity.VirtualEntity
import com.nextcloud.client.database.migrations.DatabaseMigrationUtil
+import com.nextcloud.client.database.migrations.MIGRATION_88_89
+import com.nextcloud.client.database.migrations.MIGRATION_97_98
+import com.nextcloud.client.database.migrations.MIGRATION_99_100
import com.nextcloud.client.database.migrations.Migration67to68
import com.nextcloud.client.database.migrations.RoomMigration
import com.nextcloud.client.database.migrations.addLegacyMigrations
+import com.nextcloud.client.database.typeConverter.OfflineOperationTypeConverter
+import com.owncloud.android.MainApp
import com.owncloud.android.db.ProviderMeta
@Database(
@@ -41,7 +58,10 @@ import com.owncloud.android.db.ProviderMeta
ShareEntity::class,
SyncedFolderEntity::class,
UploadEntity::class,
- VirtualEntity::class
+ VirtualEntity::class,
+ OfflineOperationEntity::class,
+ RecommendedFileEntity::class,
+ AssistantEntity::class
],
version = ProviderMeta.DB_VERSION,
autoMigrations = [
@@ -59,40 +79,73 @@ import com.owncloud.android.db.ProviderMeta
AutoMigration(from = 77, to = 78),
AutoMigration(from = 78, to = 79, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class),
AutoMigration(from = 79, to = 80),
- AutoMigration(from = 80, to = 81)
+ AutoMigration(from = 80, to = 81),
+ AutoMigration(from = 81, to = 82),
+ AutoMigration(from = 82, to = 83),
+ AutoMigration(from = 83, to = 84),
+ AutoMigration(from = 84, to = 85, spec = DatabaseMigrationUtil.DeleteColumnSpec::class),
+ AutoMigration(from = 85, to = 86, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class),
+ AutoMigration(from = 86, to = 87, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class),
+ AutoMigration(from = 87, to = 88, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class),
+ // manual migration used for 88 to 89
+ AutoMigration(from = 89, to = 90),
+ AutoMigration(from = 90, to = 91),
+ AutoMigration(from = 91, to = 92),
+ AutoMigration(from = 92, to = 93, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class),
+ AutoMigration(from = 93, to = 94, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class),
+ AutoMigration(from = 94, to = 95, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class),
+ AutoMigration(from = 95, to = 96),
+ AutoMigration(from = 96, to = 97, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class),
+ // manual migration used for 97 to 98
+ AutoMigration(from = 98, to = 99)
+ // manual migration used for 99 to 100
],
exportSchema = true
)
@Suppress("Detekt.UnnecessaryAbstractClass") // needed by Room
+@TypeConverters(OfflineOperationTypeConverter::class)
abstract class NextcloudDatabase : RoomDatabase() {
abstract fun arbitraryDataDao(): ArbitraryDataDao
abstract fun fileDao(): FileDao
+ abstract fun offlineOperationDao(): OfflineOperationDao
+ abstract fun uploadDao(): UploadDao
+ abstract fun recommendedFileDao(): RecommendedFileDao
+ abstract fun fileSystemDao(): FileSystemDao
+ abstract fun syncedFolderDao(): SyncedFolderDao
+ abstract fun assistantDao(): AssistantDao
+ abstract fun shareDao(): ShareDao
+ abstract fun capabilityDao(): CapabilityDao
companion object {
const val FIRST_ROOM_DB_VERSION = 65
- private var INSTANCE: NextcloudDatabase? = null
+ private var instance: NextcloudDatabase? = null
@JvmStatic
@Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated("Here for legacy purposes, inject this class or use getInstance(context, clock) instead")
- fun getInstance(context: Context): NextcloudDatabase {
- return getInstance(context, ClockImpl())
- }
+ fun getInstance(context: Context): NextcloudDatabase = getInstance(context, ClockImpl())
@JvmStatic
fun getInstance(context: Context, clock: Clock): NextcloudDatabase {
- if (INSTANCE == null) {
- INSTANCE = Room
+ if (instance == null) {
+ instance = Room
.databaseBuilder(context, NextcloudDatabase::class.java, ProviderMeta.DB_NAME)
.allowMainThreadQueries()
+ .addTypeConverter(OfflineOperationTypeConverter())
.addLegacyMigrations(clock, context)
.addMigrations(RoomMigration())
.addMigrations(Migration67to68())
- .fallbackToDestructiveMigration()
+ .addMigrations(MIGRATION_88_89)
+ .addMigrations(MIGRATION_97_98)
+ .addMigrations(MIGRATION_99_100)
.build()
}
- return INSTANCE!!
+ return instance!!
}
+
+ @Suppress("DEPRECATION")
+ @JvmStatic
+ fun instance(): NextcloudDatabase = getInstance(MainApp.getAppContext())
}
}
diff --git a/app/src/main/java/com/nextcloud/client/database/dao/AssistantDao.kt b/app/src/main/java/com/nextcloud/client/database/dao/AssistantDao.kt
new file mode 100644
index 000000000000..f674ae4f3c6c
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/client/database/dao/AssistantDao.kt
@@ -0,0 +1,41 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2025 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+package com.nextcloud.client.database.dao
+
+import androidx.room.Dao
+import androidx.room.Delete
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import androidx.room.Update
+import com.nextcloud.client.database.entity.AssistantEntity
+import com.owncloud.android.db.ProviderMeta
+
+@Dao
+interface AssistantDao {
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun insertAssistantTask(task: AssistantEntity)
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun insertAssistantTasks(tasks: List)
+
+ @Update
+ suspend fun updateAssistantTask(task: AssistantEntity)
+
+ @Delete
+ suspend fun deleteAssistantTask(task: AssistantEntity)
+
+ @Query(
+ """
+ SELECT * FROM ${ProviderMeta.ProviderTableMeta.ASSISTANT_TABLE_NAME}
+ WHERE accountName = :accountName AND type = :taskType
+ ORDER BY lastUpdated DESC
+"""
+ )
+ suspend fun getAssistantTasksByAccount(accountName: String, taskType: String): List
+}
diff --git a/app/src/main/java/com/nextcloud/client/database/dao/CapabilityDao.kt b/app/src/main/java/com/nextcloud/client/database/dao/CapabilityDao.kt
new file mode 100644
index 000000000000..51ea739b2fe3
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/client/database/dao/CapabilityDao.kt
@@ -0,0 +1,19 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2026 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+package com.nextcloud.client.database.dao
+
+import androidx.room.Dao
+import androidx.room.Query
+import com.nextcloud.client.database.entity.CapabilityEntity
+
+@Dao
+interface CapabilityDao {
+
+ @Query("SELECT * FROM capabilities WHERE account = :accountName LIMIT 1")
+ suspend fun getByAccountName(accountName: String): CapabilityEntity?
+}
diff --git a/app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt b/app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt
index 4dc78224018f..7f15f5ec3252 100644
--- a/app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt
+++ b/app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt
@@ -9,14 +9,23 @@ package com.nextcloud.client.database.dao
import androidx.room.Dao
import androidx.room.Query
+import androidx.room.Update
import com.nextcloud.client.database.entity.FileEntity
import com.owncloud.android.db.ProviderMeta.ProviderTableMeta
+import com.owncloud.android.utils.MimeType
+@Suppress("TooManyFunctions")
@Dao
interface FileDao {
+ @Update
+ fun update(entity: FileEntity)
+
@Query("SELECT * FROM filelist WHERE _id = :id LIMIT 1")
fun getFileById(id: Long): FileEntity?
+ @Query("SELECT * FROM filelist WHERE local_id = :localId LIMIT 1")
+ fun getFileByLocalId(localId: Long): FileEntity?
+
@Query("SELECT * FROM filelist WHERE path = :path AND file_owner = :fileOwner LIMIT 1")
fun getFileByEncryptedRemotePath(path: String, fileOwner: String): FileEntity?
@@ -29,9 +38,15 @@ interface FileDao {
@Query("SELECT * FROM filelist WHERE remote_id = :remoteId AND file_owner = :fileOwner LIMIT 1")
fun getFileByRemoteId(remoteId: String, fileOwner: String): FileEntity?
+ @Query("SELECT * FROM filelist WHERE remote_id = :remoteId LIMIT 1")
+ suspend fun getFileByRemoteId(remoteId: String): FileEntity?
+
@Query("SELECT * FROM filelist WHERE parent = :parentId ORDER BY ${ProviderTableMeta.FILE_DEFAULT_SORT_ORDER}")
fun getFolderContent(parentId: Long): List
+ @Query("SELECT * FROM filelist WHERE parent = :parentId ORDER BY ${ProviderTableMeta.FILE_DEFAULT_SORT_ORDER}")
+ suspend fun getFolderContentSuspended(parentId: Long): List
+
@Query(
"SELECT * FROM filelist WHERE modified >= :startDate" +
" AND modified < :endDate" +
@@ -49,4 +64,102 @@ interface FileDao {
@Query("SELECT * FROM filelist where file_owner = :fileOwner AND etag_in_conflict IS NOT NULL")
fun getFilesWithSyncConflict(fileOwner: String): List
+
+ @Query(
+ "SELECT * FROM filelist where file_owner = :fileOwner AND internal_two_way_sync_timestamp >= 0 " +
+ "ORDER BY internal_two_way_sync_timestamp DESC"
+ )
+ fun getInternalTwoWaySyncFolders(fileOwner: String): List
+
+ @Query(
+ """
+ SELECT *
+ FROM filelist
+ WHERE parent = :parentId
+ AND file_owner = :accountName
+ AND is_encrypted = 0
+ AND (content_type = :dirType OR content_type = :webdavType)
+ ORDER BY ${ProviderTableMeta._ID} ASC
+ """
+ )
+ fun getNonEncryptedSubfolders(
+ parentId: Long,
+ accountName: String,
+ dirType: String = MimeType.DIRECTORY,
+ webdavType: String = MimeType.WEBDAV_FOLDER
+ ): List
+
+ @Query(
+ """
+ SELECT NOT EXISTS (
+ SELECT 1
+ FROM filelist
+ WHERE parent = :parentId
+ AND file_owner = :accountName
+ AND content_type IS NOT NULL
+ AND content_type != '${MimeType.DIRECTORY}'
+ AND content_type != '${MimeType.WEBDAV_FOLDER}'
+ AND (media_path IS NULL OR TRIM(media_path) = '')
+ )
+"""
+ )
+ fun areAllFilesHaveMediaPath(parentId: Long, accountName: String): Boolean
+
+ @Query(
+ """
+ SELECT *
+ FROM filelist
+ WHERE file_owner = :fileOwner
+ AND parent = :parentId
+ AND ${ProviderTableMeta.FILE_NAME} LIKE '%' || :query || '%'
+ ORDER BY ${ProviderTableMeta.FILE_DEFAULT_SORT_ORDER}
+ """
+ )
+ fun searchFilesInFolder(parentId: Long, fileOwner: String, query: String): List
+
+ @Query(
+ """
+ SELECT *
+ FROM filelist
+ WHERE file_owner = :accountName
+ AND (
+ share_by_link = 1
+ OR shared_via_users = 1
+ OR permissions LIKE '%S%'
+ )
+ ORDER BY ${ProviderTableMeta.FILE_DEFAULT_SORT_ORDER}
+ """
+ )
+ suspend fun getSharedFiles(accountName: String): List
+
+ @Query(
+ """
+ SELECT *
+ FROM filelist
+ WHERE file_owner = :fileOwner
+ AND favorite = 1
+ ORDER BY ${ProviderTableMeta.FILE_DEFAULT_SORT_ORDER}
+ """
+ )
+ suspend fun getFavoriteFiles(fileOwner: String): List
+
+ @Query("SELECT remote_id FROM filelist WHERE file_owner = :accountName AND remote_id IS NOT NULL")
+ fun getAllRemoteIds(accountName: String): List
+
+ @Query(
+ """
+ WITH RECURSIVE descendants AS (
+ SELECT _id FROM filelist WHERE _id = :folderId AND file_owner = :fileOwner
+ UNION ALL
+ SELECT f._id FROM filelist f
+ INNER JOIN descendants d ON f.parent = d._id
+ WHERE f.file_owner = :fileOwner
+ )
+ DELETE FROM filelist WHERE _id IN (SELECT _id FROM descendants)
+"""
+ )
+ fun deleteFolderWithDescendants(fileOwner: String, folderId: Long): Int
+
+ @Query("DELETE FROM filelist WHERE file_owner = :fileOwner AND path = :remotePath")
+ fun deleteFileByRemotePath(fileOwner: String, remotePath: String): Int
}
diff --git a/app/src/main/java/com/nextcloud/client/database/dao/FileSystemDao.kt b/app/src/main/java/com/nextcloud/client/database/dao/FileSystemDao.kt
new file mode 100644
index 000000000000..068b819e8c4b
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/client/database/dao/FileSystemDao.kt
@@ -0,0 +1,110 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2025 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+package com.nextcloud.client.database.dao
+
+import androidx.room.Dao
+import androidx.room.Delete
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import com.nextcloud.client.database.entity.FilesystemEntity
+import com.owncloud.android.db.ProviderMeta
+
+@Dao
+interface FileSystemDao {
+ @Query(
+ """
+ UPDATE ${ProviderMeta.ProviderTableMeta.FILESYSTEM_TABLE_NAME}
+ SET ${ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_REMOTE_PATH} = :remotePath
+ WHERE ${ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH} = :localPath
+ AND ${ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID} = :syncedFolderId
+ """
+ )
+ suspend fun updateRemotePath(remotePath: String, localPath: String, syncedFolderId: String)
+
+ @Query(
+ """
+ SELECT *
+ FROM ${ProviderMeta.ProviderTableMeta.FILESYSTEM_TABLE_NAME}
+ WHERE ${ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID} = :syncedFolderId
+ """
+ )
+ suspend fun getBySyncedFolderId(syncedFolderId: String): List
+
+ @Query(
+ """
+ SELECT COUNT(*) > 0 FROM ${ProviderMeta.ProviderTableMeta.FILESYSTEM_TABLE_NAME}
+ WHERE ${ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH} = :localPath
+ AND ${ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID} IS NOT NULL
+ LIMIT 1
+"""
+ )
+ suspend fun isBelongToAnyAutoFolder(localPath: String): Boolean
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ fun insertOrReplace(filesystemEntity: FilesystemEntity)
+
+ @Delete
+ fun delete(entity: FilesystemEntity)
+
+ @Query(
+ """
+ DELETE FROM ${ProviderMeta.ProviderTableMeta.FILESYSTEM_TABLE_NAME}
+ WHERE ${ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH} = :localPath
+ AND ${ProviderMeta.ProviderTableMeta._ID} = :id
+ """
+ )
+ suspend fun deleteByLocalPathAndId(localPath: String, id: Int)
+
+ @Query(
+ """
+ SELECT *
+ FROM ${ProviderMeta.ProviderTableMeta.FILESYSTEM_TABLE_NAME}
+ WHERE ${ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID} = :syncedFolderId
+ AND ${ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD} = 0
+ AND ${ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER} = 0
+ AND ${ProviderMeta.ProviderTableMeta._ID} > :lastId
+ ORDER BY ${ProviderMeta.ProviderTableMeta._ID}
+ LIMIT :limit
+ """
+ )
+ suspend fun getAutoUploadFilesEntities(syncedFolderId: String, limit: Int, lastId: Int): List
+
+ @Query(
+ """
+ UPDATE ${ProviderMeta.ProviderTableMeta.FILESYSTEM_TABLE_NAME}
+ SET ${ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD} = 1
+ WHERE ${ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH} = :localPath
+ AND ${ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID} = :syncedFolderId
+ """
+ )
+ suspend fun markFileAsUploaded(localPath: String, syncedFolderId: String)
+
+ @Query(
+ """
+ SELECT *
+ FROM ${ProviderMeta.ProviderTableMeta.FILESYSTEM_TABLE_NAME}
+ WHERE ${ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH} = :localPath
+ AND ${ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID} = :syncedFolderId
+ LIMIT 1
+ """
+ )
+ fun getFileByPathAndFolder(localPath: String, syncedFolderId: String): FilesystemEntity?
+
+ @Query(
+ """
+ SELECT COUNT(*) > 0
+ FROM ${ProviderMeta.ProviderTableMeta.FILESYSTEM_TABLE_NAME}
+ WHERE ${ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID} = :syncedFolderId
+ AND ${ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD} = 0
+ AND ${ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER} = 0
+ LIMIT 1
+ """
+ )
+ suspend fun hasPendingFiles(syncedFolderId: String): Boolean
+}
diff --git a/app/src/main/java/com/nextcloud/client/database/dao/OfflineOperationDao.kt b/app/src/main/java/com/nextcloud/client/database/dao/OfflineOperationDao.kt
new file mode 100644
index 000000000000..50817daf28ee
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/client/database/dao/OfflineOperationDao.kt
@@ -0,0 +1,46 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2024 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+package com.nextcloud.client.database.dao
+
+import androidx.room.Dao
+import androidx.room.Delete
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import androidx.room.Update
+import com.nextcloud.client.database.entity.OfflineOperationEntity
+
+@Dao
+interface OfflineOperationDao {
+ @Query("SELECT * FROM offline_operations")
+ fun getAll(): List
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ fun insert(vararg entity: OfflineOperationEntity)
+
+ @Update
+ fun update(entity: OfflineOperationEntity)
+
+ @Delete
+ fun delete(entity: OfflineOperationEntity)
+
+ @Query("DELETE FROM offline_operations WHERE offline_operations_path = :path")
+ fun deleteByPath(path: String)
+
+ @Query("SELECT * FROM offline_operations WHERE offline_operations_path = :path LIMIT 1")
+ fun getByPath(path: String): OfflineOperationEntity?
+
+ @Query("SELECT * FROM offline_operations WHERE offline_operations_parent_oc_file_id = :parentOCFileId")
+ fun getSubEntitiesByParentOCFileId(parentOCFileId: Long): List
+
+ @Query("DELETE FROM offline_operations")
+ fun clearTable()
+
+ @Query("DELETE FROM offline_operations WHERE _id = :id")
+ fun deleteById(id: Int)
+}
diff --git a/app/src/main/java/com/nextcloud/client/database/dao/RecommendedFileDao.kt b/app/src/main/java/com/nextcloud/client/database/dao/RecommendedFileDao.kt
new file mode 100644
index 000000000000..a93f857dac8d
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/client/database/dao/RecommendedFileDao.kt
@@ -0,0 +1,26 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2025 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+package com.nextcloud.client.database.dao
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import com.nextcloud.client.database.entity.RecommendedFileEntity
+import com.owncloud.android.db.ProviderMeta
+
+@Dao
+interface RecommendedFileDao {
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun insertAll(recommendedFiles: List)
+
+ @Query(
+ "SELECT * FROM ${ProviderMeta.ProviderTableMeta.RECOMMENDED_FILE_TABLE_NAME} WHERE account_name = :accountName"
+ )
+ suspend fun getAll(accountName: String): List
+}
diff --git a/app/src/main/java/com/nextcloud/client/database/dao/ShareDao.kt b/app/src/main/java/com/nextcloud/client/database/dao/ShareDao.kt
new file mode 100644
index 000000000000..61ea4a09c54a
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/client/database/dao/ShareDao.kt
@@ -0,0 +1,24 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2025 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+package com.nextcloud.client.database.dao
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import com.nextcloud.client.database.entity.ShareEntity
+
+@Dao
+interface ShareDao {
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun insertAll(shares: List)
+
+ @Query("DELETE FROM ocshares WHERE owner_share = :accountName")
+ suspend fun clearSharesForAccount(accountName: String)
+}
diff --git a/app/src/main/java/com/nextcloud/client/database/dao/SyncedFolderDao.kt b/app/src/main/java/com/nextcloud/client/database/dao/SyncedFolderDao.kt
new file mode 100644
index 000000000000..fb07cbeca955
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/client/database/dao/SyncedFolderDao.kt
@@ -0,0 +1,40 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2025 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+package com.nextcloud.client.database.dao
+
+import androidx.room.Dao
+import androidx.room.Query
+import com.nextcloud.client.database.entity.SyncedFolderEntity
+import com.owncloud.android.db.ProviderMeta
+import kotlinx.coroutines.flow.Flow
+
+@Dao
+interface SyncedFolderDao {
+ @Query(
+ """
+ SELECT * FROM ${ProviderMeta.ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME}
+ WHERE ${ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LOCAL_PATH} = :localPath
+ AND ${ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT} = :account
+ LIMIT 1
+ """
+ )
+ fun findByLocalPathAndAccount(localPath: String, account: String): SyncedFolderEntity?
+
+ @Query("SELECT * FROM ${ProviderMeta.ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME}")
+ fun getAllAsFlow(): Flow>
+
+ @Query(
+ """
+ SELECT * FROM ${ProviderMeta.ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME}
+ WHERE ${ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_REMOTE_PATH} = :remotePath
+ AND ${ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT} = :account
+ LIMIT 1
+"""
+ )
+ suspend fun findByRemotePathAndAccount(remotePath: String, account: String): SyncedFolderEntity?
+}
diff --git a/app/src/main/java/com/nextcloud/client/database/dao/UploadDao.kt b/app/src/main/java/com/nextcloud/client/database/dao/UploadDao.kt
new file mode 100644
index 000000000000..07db239a7dd6
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/client/database/dao/UploadDao.kt
@@ -0,0 +1,115 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2025 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+package com.nextcloud.client.database.dao
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import com.nextcloud.client.database.entity.UploadEntity
+import com.owncloud.android.db.ProviderMeta.ProviderTableMeta
+
+@Dao
+interface UploadDao {
+ @Query(
+ "SELECT _id FROM " + ProviderTableMeta.UPLOADS_TABLE_NAME +
+ " WHERE " + ProviderTableMeta.UPLOADS_STATUS + " = :status AND " +
+ ProviderTableMeta.UPLOADS_ACCOUNT_NAME + " = :accountName AND _id IS NOT NULL"
+ )
+ fun getAllIds(status: Int, accountName: String): List
+
+ @Query(
+ "SELECT * FROM " + ProviderTableMeta.UPLOADS_TABLE_NAME +
+ " WHERE " + ProviderTableMeta._ID + " IN (:ids) AND " +
+ ProviderTableMeta.UPLOADS_ACCOUNT_NAME + " = :accountName"
+ )
+ fun getUploadsByIds(ids: LongArray, accountName: String): List
+
+ @Query(
+ "SELECT * FROM ${ProviderTableMeta.UPLOADS_TABLE_NAME} " +
+ "WHERE ${ProviderTableMeta.UPLOADS_REMOTE_PATH} = :remotePath LIMIT 1"
+ )
+ fun getByRemotePath(remotePath: String): UploadEntity?
+
+ @Query(
+ "DELETE FROM ${ProviderTableMeta.UPLOADS_TABLE_NAME} " +
+ "WHERE ${ProviderTableMeta.UPLOADS_ACCOUNT_NAME} = :accountName " +
+ "AND ${ProviderTableMeta.UPLOADS_REMOTE_PATH} = :remotePath"
+ )
+ fun deleteByRemotePathAndAccountName(remotePath: String, accountName: String)
+
+ @Query(
+ """
+ DELETE FROM ${ProviderTableMeta.UPLOADS_TABLE_NAME}
+ WHERE ${ProviderTableMeta.UPLOADS_LOCAL_PATH} = :localPath
+ AND ${ProviderTableMeta.UPLOADS_REMOTE_PATH} = :remotePath
+"""
+ )
+ suspend fun deleteByLocalRemotePath(localPath: String, remotePath: String)
+
+ @Query(
+ "SELECT * FROM " + ProviderTableMeta.UPLOADS_TABLE_NAME +
+ " WHERE " + ProviderTableMeta._ID + " = :id AND " +
+ ProviderTableMeta.UPLOADS_ACCOUNT_NAME + " = :accountName " +
+ "LIMIT 1"
+ )
+ fun getUploadById(id: Long, accountName: String): UploadEntity?
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ fun insertOrReplace(entity: UploadEntity): Long
+
+ @Query(
+ "SELECT * FROM " + ProviderTableMeta.UPLOADS_TABLE_NAME +
+ " WHERE " + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + " = :accountName AND " +
+ ProviderTableMeta.UPLOADS_LOCAL_PATH + " = :localPath AND " +
+ ProviderTableMeta.UPLOADS_REMOTE_PATH + " = :remotePath " +
+ "LIMIT 1"
+ )
+ fun getUploadByAccountAndPaths(accountName: String, localPath: String, remotePath: String): UploadEntity?
+
+ @Query(
+ "UPDATE ${ProviderTableMeta.UPLOADS_TABLE_NAME} " +
+ "SET ${ProviderTableMeta.UPLOADS_STATUS} = :status " +
+ "WHERE ${ProviderTableMeta.UPLOADS_REMOTE_PATH} = :remotePath " +
+ "AND ${ProviderTableMeta.UPLOADS_ACCOUNT_NAME} = :accountName"
+ )
+ suspend fun updateStatus(remotePath: String, accountName: String, status: Int): Int
+
+ @Query(
+ """
+ UPDATE ${ProviderTableMeta.UPLOADS_TABLE_NAME}
+ SET ${ProviderTableMeta.UPLOADS_STATUS} = :status
+ WHERE ${ProviderTableMeta.UPLOADS_ACCOUNT_NAME} = :accountName
+ AND ${ProviderTableMeta.UPLOADS_REMOTE_PATH} IN (:remotePaths)
+"""
+ )
+ suspend fun updateStatuses(remotePaths: List, accountName: String, status: Int): Int
+
+ @Query(
+ """
+ SELECT * FROM ${ProviderTableMeta.UPLOADS_TABLE_NAME}
+ WHERE ${ProviderTableMeta.UPLOADS_STATUS} = :status
+ AND (:nameCollisionPolicy IS NULL OR ${ProviderTableMeta.UPLOADS_NAME_COLLISION_POLICY} = :nameCollisionPolicy)
+"""
+ )
+ suspend fun getUploadsByStatus(status: Int, nameCollisionPolicy: Int? = null): List
+
+ @Query(
+ """
+ SELECT * FROM ${ProviderTableMeta.UPLOADS_TABLE_NAME}
+ WHERE ${ProviderTableMeta.UPLOADS_ACCOUNT_NAME} = :accountName
+ AND ${ProviderTableMeta.UPLOADS_STATUS} = :status
+ AND (:nameCollisionPolicy IS NULL OR ${ProviderTableMeta.UPLOADS_NAME_COLLISION_POLICY} = :nameCollisionPolicy)
+"""
+ )
+ suspend fun getUploadsByAccountNameAndStatus(
+ accountName: String,
+ status: Int,
+ nameCollisionPolicy: Int? = null
+ ): List
+}
diff --git a/app/src/main/java/com/nextcloud/client/database/entity/AssistantEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/AssistantEntity.kt
new file mode 100644
index 000000000000..4c13c25b340c
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/client/database/entity/AssistantEntity.kt
@@ -0,0 +1,30 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2025 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+package com.nextcloud.client.database.entity
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import com.owncloud.android.db.ProviderMeta
+
+@Entity(tableName = ProviderMeta.ProviderTableMeta.ASSISTANT_TABLE_NAME)
+data class AssistantEntity(
+ @PrimaryKey(autoGenerate = true)
+ val id: Long = 0L,
+ val accountName: String?,
+ val type: String?,
+ val status: String?,
+ val userId: String?,
+ val appId: String?,
+ val input: String? = null,
+ val output: String? = null,
+ val completionExpectedAt: Int? = null,
+ var progress: Int? = null,
+ val lastUpdated: Int? = null,
+ val scheduledAt: Int? = null,
+ val endedAt: Int? = null
+)
diff --git a/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt
index 7fd31416e6f2..eb225bab7972 100644
--- a/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt
+++ b/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt
@@ -11,6 +11,9 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.owncloud.android.db.ProviderMeta.ProviderTableMeta
+import com.owncloud.android.lib.resources.status.CapabilityBooleanType
+import com.owncloud.android.lib.resources.status.E2EVersion
+import com.owncloud.android.lib.resources.status.OCCapability
@Entity(tableName = ProviderTableMeta.CAPABILITIES_TABLE_NAME)
data class CapabilityEntity(
@@ -122,5 +125,113 @@ data class CapabilityEntity(
@ColumnInfo(name = ProviderTableMeta.CAPABILITIES_DROP_ACCOUNT)
val dropAccount: Int?,
@ColumnInfo(name = ProviderTableMeta.CAPABILITIES_SECURITY_GUARD)
- val securityGuard: Int?
+ val securityGuard: Int?,
+ @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FORBIDDEN_FILENAME_CHARACTERS)
+ val forbiddenFileNameCharacters: String?,
+ @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FORBIDDEN_FILENAMES)
+ val forbiddenFileNames: String?,
+ @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_EXTENSIONS)
+ val forbiddenFileNameExtensions: String?,
+ @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_BASE_NAMES)
+ val forbiddenFilenameBaseNames: String?,
+ @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT)
+ val filesDownloadLimit: Int?,
+ @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT_DEFAULT)
+ val filesDownloadLimitDefault: Int?,
+ @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_RECOMMENDATION)
+ val recommendation: Int?,
+ @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_NOTES_FOLDER_PATH)
+ val notesFolderPath: String?,
+ @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_DEFAULT_PERMISSIONS)
+ val defaultPermissions: Int?,
+ @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_USER_STATUS_SUPPORTS_BUSY)
+ val userStatusSupportsBusy: Int?,
+ @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_WINDOWS_COMPATIBLE_FILENAMES)
+ val isWCFEnabled: Int?,
+ @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_HAS_VALID_SUBSCRIPTION)
+ val hasValidSubscription: Int?,
+ @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_CLIENT_INTEGRATION_JSON)
+ val clientIntegrationJson: String?
)
+
+@Suppress("LongMethod", "ReturnCount")
+fun CapabilityEntity?.toOCCapability(): OCCapability {
+ val capability = OCCapability()
+ if (this == null) return capability
+ val id = this.id ?: return capability
+
+ fun intToBoolean(value: Int?): CapabilityBooleanType =
+ value?.let { CapabilityBooleanType.fromValue(it) } ?: CapabilityBooleanType.UNKNOWN
+
+ capability.id = id.toLong()
+ capability.accountName = this.accountName
+ capability.versionMayor = this.versionMajor ?: 0
+ capability.versionMinor = this.versionMinor ?: 0
+ capability.versionMicro = this.versionMicro ?: 0
+ capability.versionString = this.versionString
+ capability.versionEdition = this.versionEditor
+ capability.extendedSupport = intToBoolean(this.extendedSupport)
+ capability.corePollInterval = this.corePollinterval ?: 0
+ capability.filesSharingApiEnabled = intToBoolean(this.sharingApiEnabled)
+ capability.filesSharingPublicEnabled = intToBoolean(this.sharingPublicEnabled)
+ capability.filesSharingPublicPasswordEnforced = intToBoolean(this.sharingPublicPasswordEnforced)
+ capability.filesSharingPublicAskForOptionalPassword = intToBoolean(this.sharingPublicAskForOptionalPassword)
+ capability.filesSharingPublicExpireDateEnabled = intToBoolean(this.sharingPublicExpireDateEnabled)
+ capability.filesSharingPublicExpireDateDays = this.sharingPublicExpireDateDays ?: 0
+ capability.filesSharingPublicExpireDateEnforced = intToBoolean(this.sharingPublicExpireDateEnforced)
+ capability.filesSharingPublicSendMail = intToBoolean(this.sharingPublicSendMail)
+ capability.filesSharingPublicUpload = intToBoolean(this.sharingPublicUpload)
+ capability.filesSharingUserSendMail = intToBoolean(this.sharingUserSendMail)
+ capability.filesSharingResharing = intToBoolean(this.sharingResharing)
+ capability.filesSharingFederationOutgoing = intToBoolean(this.sharingFederationOutgoing)
+ capability.filesSharingFederationIncoming = intToBoolean(this.sharingFederationIncoming)
+ capability.filesBigFileChunking = intToBoolean(this.filesBigfilechunking)
+ capability.filesUndelete = intToBoolean(this.filesUndelete)
+ capability.filesVersioning = intToBoolean(this.filesVersioning)
+ capability.externalLinks = intToBoolean(this.externalLinks)
+ capability.serverName = this.serverName
+ capability.serverColor = this.serverColor
+ capability.serverTextColor = this.serverTextColor
+ capability.serverElementColor = this.serverElementColor
+ capability.serverSlogan = this.serverSlogan
+ capability.serverLogo = this.serverLogo
+ capability.serverBackground = this.serverBackgroundUrl
+ capability.endToEndEncryption = intToBoolean(this.endToEndEncryption)
+ capability.endToEndEncryptionKeysExist = intToBoolean(this.endToEndEncryptionKeysExist)
+ capability.endToEndEncryptionApiVersion = this.endToEndEncryptionApiVersion?.let {
+ E2EVersion.fromValue(it)
+ } ?: E2EVersion.UNKNOWN
+ capability.serverBackgroundDefault = intToBoolean(this.serverBackgroundDefault)
+ capability.serverBackgroundPlain = intToBoolean(this.serverBackgroundPlain)
+ capability.activity = intToBoolean(this.activity)
+ capability.richDocuments = intToBoolean(this.richdocument)
+ capability.richDocumentsDirectEditing = intToBoolean(this.richdocumentDirectEditing)
+ capability.richDocumentsTemplatesAvailable = intToBoolean(this.richdocumentTemplates)
+ capability.richDocumentsMimeTypeList = this.richdocumentMimetypeList?.split(",") ?: emptyList()
+ capability.richDocumentsOptionalMimeTypeList = this.richdocumentOptionalMimetypeList?.split(",") ?: emptyList()
+ capability.richDocumentsProductName = this.richdocumentProductName
+ capability.directEditingEtag = this.directEditingEtag
+ capability.etag = this.etag
+ capability.userStatus = intToBoolean(this.userStatus)
+ capability.userStatusSupportsEmoji = intToBoolean(this.userStatusSupportsEmoji)
+ capability.userStatusSupportsBusy = intToBoolean(this.userStatusSupportsBusy)
+ capability.filesLockingVersion = this.filesLockingVersion
+ capability.assistant = intToBoolean(this.assistant)
+ capability.groupfolders = intToBoolean(this.groupfolders)
+ capability.dropAccount = intToBoolean(this.dropAccount)
+ capability.securityGuard = intToBoolean(this.securityGuard)
+ capability.forbiddenFilenameCharactersJson = this.forbiddenFileNameCharacters
+ capability.forbiddenFilenamesJson = this.forbiddenFileNames
+ capability.forbiddenFilenameExtensionJson = this.forbiddenFileNameExtensions
+ capability.forbiddenFilenameBaseNamesJson = this.forbiddenFilenameBaseNames
+ capability.isWCFEnabled = intToBoolean(this.isWCFEnabled)
+ capability.filesDownloadLimit = intToBoolean(this.filesDownloadLimit)
+ capability.filesDownloadLimitDefault = this.filesDownloadLimitDefault ?: 0
+ capability.recommendations = intToBoolean(this.recommendation)
+ capability.notesFolderPath = this.notesFolderPath
+ capability.defaultPermissions = this.defaultPermissions ?: 0
+ capability.hasValidSubscription = intToBoolean(this.hasValidSubscription)
+ capability.clientIntegrationJson = this.clientIntegrationJson
+
+ return capability
+}
diff --git a/app/src/main/java/com/nextcloud/client/database/entity/FileEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/FileEntity.kt
index 2281a06ea9da..175287ba738c 100644
--- a/app/src/main/java/com/nextcloud/client/database/entity/FileEntity.kt
+++ b/app/src/main/java/com/nextcloud/client/database/entity/FileEntity.kt
@@ -50,7 +50,7 @@ data class FileEntity(
@ColumnInfo(name = ProviderTableMeta.FILE_ETAG_ON_SERVER)
val etagOnServer: String?,
@ColumnInfo(name = ProviderTableMeta.FILE_SHARED_VIA_LINK)
- val sharedViaLink: Int?,
+ var sharedViaLink: Int?,
@ColumnInfo(name = ProviderTableMeta.FILE_PERMISSIONS)
val permissions: String?,
@ColumnInfo(name = ProviderTableMeta.FILE_REMOTE_ID)
@@ -73,7 +73,7 @@ data class FileEntity(
@ColumnInfo(name = ProviderTableMeta.FILE_ETAG_IN_CONFLICT)
val etagInConflict: String?,
@ColumnInfo(name = ProviderTableMeta.FILE_SHARED_WITH_SHAREE)
- val sharedWithSharee: Int?,
+ var sharedWithSharee: Int?,
@ColumnInfo(name = ProviderTableMeta.FILE_MOUNT_TYPE)
val mountType: Int?,
@ColumnInfo(name = ProviderTableMeta.FILE_HAS_PREVIEW)
@@ -115,5 +115,11 @@ data class FileEntity(
@ColumnInfo(name = ProviderTableMeta.FILE_METADATA_GPS)
val metadataGPS: String?,
@ColumnInfo(name = ProviderTableMeta.FILE_E2E_COUNTER)
- val e2eCounter: Long?
+ val e2eCounter: Long?,
+ @ColumnInfo(name = ProviderTableMeta.FILE_INTERNAL_TWO_WAY_SYNC_TIMESTAMP)
+ val internalTwoWaySync: Long?,
+ @ColumnInfo(name = ProviderTableMeta.FILE_INTERNAL_TWO_WAY_SYNC_RESULT)
+ val internalTwoWaySyncResult: String?,
+ @ColumnInfo(name = ProviderTableMeta.FILE_UPLOADED)
+ val uploaded: Long?
)
diff --git a/app/src/main/java/com/nextcloud/client/database/entity/FilesystemEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/FilesystemEntity.kt
index 7247ee15cadd..031fc52baeb3 100644
--- a/app/src/main/java/com/nextcloud/client/database/entity/FilesystemEntity.kt
+++ b/app/src/main/java/com/nextcloud/client/database/entity/FilesystemEntity.kt
@@ -19,6 +19,8 @@ data class FilesystemEntity(
val id: Int?,
@ColumnInfo(name = ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH)
val localPath: String?,
+ @ColumnInfo(name = ProviderTableMeta.FILESYSTEM_FILE_REMOTE_PATH)
+ val remotePath: String?,
@ColumnInfo(name = ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER)
val fileIsFolder: Int?,
@ColumnInfo(name = ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY)
diff --git a/app/src/main/java/com/nextcloud/client/database/entity/OfflineOperationEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/OfflineOperationEntity.kt
new file mode 100644
index 000000000000..1d5b151922b8
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/client/database/entity/OfflineOperationEntity.kt
@@ -0,0 +1,68 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2024 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+package com.nextcloud.client.database.entity
+
+import android.content.Context
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import com.nextcloud.model.OfflineOperationType
+import com.owncloud.android.R
+import com.owncloud.android.db.ProviderMeta.ProviderTableMeta
+
+@Entity(tableName = ProviderTableMeta.OFFLINE_OPERATION_TABLE_NAME)
+data class OfflineOperationEntity(
+ @PrimaryKey(autoGenerate = true)
+ @ColumnInfo(name = ProviderTableMeta._ID)
+ val id: Int? = null,
+
+ @ColumnInfo(name = ProviderTableMeta.OFFLINE_OPERATION_PARENT_OC_FILE_ID)
+ var parentOCFileId: Long? = null,
+
+ @ColumnInfo(name = ProviderTableMeta.OFFLINE_OPERATION_PATH)
+ var path: String? = null,
+
+ @ColumnInfo(name = ProviderTableMeta.OFFLINE_OPERATION_TYPE)
+ var type: OfflineOperationType? = null,
+
+ @ColumnInfo(name = ProviderTableMeta.OFFLINE_OPERATION_FILE_NAME)
+ var filename: String? = null,
+
+ @ColumnInfo(name = ProviderTableMeta.OFFLINE_OPERATION_CREATED_AT)
+ var createdAt: Long? = null,
+
+ @ColumnInfo(name = ProviderTableMeta.OFFLINE_OPERATION_MODIFIED_AT)
+ var modifiedAt: Long? = null
+) {
+ fun isRenameOrRemove(): Boolean =
+ (type is OfflineOperationType.RenameFile || type is OfflineOperationType.RemoveFile)
+
+ fun isCreate(): Boolean = (type is OfflineOperationType.CreateFile || type is OfflineOperationType.CreateFolder)
+
+ fun getConflictText(context: Context): String {
+ val resId = when (type) {
+ is OfflineOperationType.RemoveFile -> {
+ R.string.offline_operations_worker_notification_remove_conflict_text
+ }
+
+ is OfflineOperationType.RenameFile -> {
+ R.string.offline_operations_worker_notification_rename_conflict_text
+ }
+
+ is OfflineOperationType.CreateFile -> {
+ R.string.offline_operations_worker_notification_create_file_conflict_text
+ }
+
+ else -> {
+ R.string.offline_operations_worker_notification_create_folder_conflict_text
+ }
+ }
+
+ return context.getString(resId, filename)
+ }
+}
diff --git a/app/src/main/java/com/nextcloud/client/database/entity/RecommendedFileEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/RecommendedFileEntity.kt
new file mode 100644
index 000000000000..650179700271
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/client/database/entity/RecommendedFileEntity.kt
@@ -0,0 +1,72 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2025 Alper Ozturk
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+package com.nextcloud.client.database.entity
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import com.nextcloud.android.lib.resources.recommendations.Recommendation
+import com.owncloud.android.datamodel.FileDataStorageManager
+import com.owncloud.android.datamodel.OCFile
+import com.owncloud.android.db.ProviderMeta.ProviderTableMeta
+
+@Entity(tableName = ProviderTableMeta.RECOMMENDED_FILE_TABLE_NAME)
+data class RecommendedFileEntity(
+ @PrimaryKey(autoGenerate = true)
+ @ColumnInfo(name = ProviderTableMeta._ID)
+ val id: Long,
+
+ @ColumnInfo(name = ProviderTableMeta.RECOMMENDED_FILE_NAME)
+ val name: String,
+
+ @ColumnInfo(name = ProviderTableMeta.RECOMMENDED_FILE_DIRECTORY)
+ val directory: String,
+
+ @ColumnInfo(name = ProviderTableMeta.RECOMMENDED_FILE_EXTENSIONS)
+ val extension: String,
+
+ @ColumnInfo(name = ProviderTableMeta.RECOMMENDED_FILE_MIME_TYPE)
+ val mimeType: String,
+
+ @ColumnInfo(name = ProviderTableMeta.RECOMMENDED_FILE_HAS_PREVIEW)
+ val hasPreview: Boolean,
+
+ @ColumnInfo(name = ProviderTableMeta.RECOMMENDED_FILE_REASON)
+ val reason: String,
+
+ @ColumnInfo(name = ProviderTableMeta.RECOMMENDED_TIMESTAMP)
+ val timestamp: Long,
+
+ @ColumnInfo(name = ProviderTableMeta.RECOMMENDED_FILE_ACCOUNT_NAME)
+ val accountName: String?
+)
+
+fun ArrayList.toEntity(accountName: String): List = this.map { recommendation ->
+ RecommendedFileEntity(
+ id = recommendation.id,
+ name = recommendation.name,
+ directory = recommendation.directory,
+ extension = recommendation.extension,
+ mimeType = recommendation.mimeType,
+ hasPreview = recommendation.hasPreview,
+ reason = recommendation.reason,
+ timestamp = recommendation.timestamp,
+ accountName = accountName
+ )
+}
+
+fun List.toOCFile(storageManager: FileDataStorageManager): ArrayList =
+ mapNotNull { entity ->
+ entity.id.let {
+ storageManager.getFileByLocalId(it).apply {
+ this?.reason = entity.reason
+ this?.setIsRecommendedFile(true)
+ }
+ }
+ }
+ .toCollection(ArrayList())
diff --git a/app/src/main/java/com/nextcloud/client/database/entity/ShareEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/ShareEntity.kt
index b24c21c873d3..ad5005efef20 100644
--- a/app/src/main/java/com/nextcloud/client/database/entity/ShareEntity.kt
+++ b/app/src/main/java/com/nextcloud/client/database/entity/ShareEntity.kt
@@ -54,5 +54,11 @@ data class ShareEntity(
@ColumnInfo(name = ProviderTableMeta.OCSHARES_SHARE_LINK)
val shareLink: String?,
@ColumnInfo(name = ProviderTableMeta.OCSHARES_SHARE_LABEL)
- val shareLabel: String?
+ val shareLabel: String?,
+ @ColumnInfo(name = ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_LIMIT)
+ val downloadLimitLimit: Int?,
+ @ColumnInfo(name = ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_COUNT)
+ val downloadLimitCount: Int?,
+ @ColumnInfo(name = ProviderTableMeta.OCSHARES_ATTRIBUTES)
+ val attributes: String?
)
diff --git a/app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt
index bcc9d4c864d2..18b0911209ed 100644
--- a/app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt
+++ b/app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt
@@ -10,6 +10,9 @@ package com.nextcloud.client.database.entity
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
+import com.nextcloud.client.preferences.SubFolderRule
+import com.owncloud.android.datamodel.MediaFolderType
+import com.owncloud.android.datamodel.SyncedFolder
import com.owncloud.android.db.ProviderMeta.ProviderTableMeta
@Entity(tableName = ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME)
@@ -50,3 +53,40 @@ data class SyncedFolderEntity(
@ColumnInfo(name = ProviderTableMeta.SYNCED_FOLDER_LAST_SCAN_TIMESTAMP_MS)
val lastScanTimestampMs: Long?
)
+
+fun SyncedFolderEntity.toSyncedFolder(): SyncedFolder = SyncedFolder(
+ // id
+ (this.id ?: SyncedFolder.UNPERSISTED_ID).toLong(),
+ // localPath
+ this.localPath ?: "",
+ // remotePath
+ this.remotePath ?: "",
+ // wifiOnly
+ this.wifiOnly == 1,
+ // chargingOnly
+ this.chargingOnly == 1,
+ // existing
+ this.existing == 1,
+ // subfolderByDate
+ this.subfolderByDate == 1,
+ // account
+ this.account ?: "",
+ // uploadAction
+ this.uploadAction ?: 0,
+ // nameCollisionPolicy
+ this.nameCollisionPolicy ?: 0,
+ // enabled
+ this.enabled == 1,
+ // timestampMs
+ (this.enabledTimestampMs ?: SyncedFolder.EMPTY_ENABLED_TIMESTAMP_MS).toLong(),
+ // type
+ MediaFolderType.getById(this.type ?: MediaFolderType.CUSTOM.id),
+ // hidden
+ this.hidden == 1,
+ // subFolderRule
+ this.subFolderRule?.let { SubFolderRule.entries[it] },
+ // excludeHidden
+ this.excludeHidden == 1,
+ // lastScanTimestampMs
+ this.lastScanTimestampMs ?: SyncedFolder.NOT_SCANNED_YET
+)
diff --git a/app/src/main/java/com/nextcloud/client/database/entity/UploadEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/UploadEntity.kt
index 8ec1dc9cb82f..ff4ff9e91c6f 100644
--- a/app/src/main/java/com/nextcloud/client/database/entity/UploadEntity.kt
+++ b/app/src/main/java/com/nextcloud/client/database/entity/UploadEntity.kt
@@ -1,6 +1,7 @@
/*
* Nextcloud - Android Client
*
+ * SPDX-FileCopyrightText: 2025 Alper Ozturk