Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,308 changes: 1,308 additions & 0 deletions app/schemas/com.nextcloud.client.database.NextcloudDatabase/99.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,9 @@ import com.owncloud.android.db.ProviderMeta
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)
AutoMigration(from = 96, to = 97, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class),
// manual migration used for 97 to 98
AutoMigration(from = 98, to = 99)
],
exportSchema = true
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ 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)

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertOrReplace(filesystemEntity: FilesystemEntity)

Expand Down Expand Up @@ -78,4 +88,13 @@ interface FileSystemDao {
"""
)
suspend fun hasPendingFiles(syncedFolderId: String): Boolean

@Query(
"""
SELECT *
FROM ${ProviderMeta.ProviderTableMeta.FILESYSTEM_TABLE_NAME}
WHERE ${ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID} = :syncedFolderId
"""
)
suspend fun getBySyncedFolderId(syncedFolderId: String): List<FilesystemEntity>
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,14 @@ interface SyncedFolderDao {

@Query("SELECT * FROM ${ProviderMeta.ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME}")
fun getAllAsFlow(): Flow<List<SyncedFolderEntity>>

@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?
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ interface UploadDao {
)
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 " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ class AutoUploadWorker(
try {
// Insert/update to IN_PROGRESS state before starting upload
val generatedId = uploadsStorageManager.uploadDao.insertOrReplace(uploadEntity)
repository.updateRemotePath(upload, syncedFolder)
uploadEntity = uploadEntity.copy(id = generatedId.toInt())
upload.uploadId = generatedId

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import com.nextcloud.utils.extensions.shouldSkipFile
import com.nextcloud.utils.extensions.toFile
import com.owncloud.android.datamodel.SyncedFolder
import com.owncloud.android.datamodel.UploadsStorageManager
import com.owncloud.android.db.OCUpload
import com.owncloud.android.lib.common.utils.Log_OC
import com.owncloud.android.utils.SyncedFolderUtils
import java.io.File
Expand Down Expand Up @@ -87,6 +88,21 @@ class FileSystemRepository(
return filtered
}

suspend fun updateRemotePath(upload: OCUpload, syncedFolder: SyncedFolder) {
val syncedFolderIdStr = syncedFolder.id.toString()

try {
dao.updateRemotePath(remotePath = upload.remotePath, localPath = upload.localPath, syncedFolderIdStr)
Log_OC.d(
TAG,
"file system entity remote path updated. remotePath: ${upload.remotePath}, localPath: " +
"${upload.localPath} for syncedFolderId=$syncedFolderIdStr"
)
} catch (e: Exception) {
Log_OC.e(TAG, "updateRemotePath(): ${e.message}", e)
}
}

suspend fun markFileAsHandled(localPath: String, syncedFolder: SyncedFolder) {
val syncedFolderIdStr = syncedFolder.id.toString()

Expand Down Expand Up @@ -206,6 +222,7 @@ class FileSystemRepository(
val newEntity = FilesystemEntity(
id = entity?.id,
localPath = localPath,
remotePath = null, // will be updated later
fileIsFolder = if (file.isDirectory) 1 else 0,
fileFoundRecently = System.currentTimeMillis(),
fileSentForUpload = 0, // Reset to 0 to queue for upload
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import android.content.Context
import android.content.Intent
import com.nextcloud.client.account.User
import com.nextcloud.client.account.UserAccountManager
import com.nextcloud.client.database.entity.SyncedFolderEntity
import com.nextcloud.client.database.entity.UploadEntity
import com.nextcloud.client.database.entity.toOCUpload
import com.nextcloud.client.database.entity.toUploadEntity
Expand Down Expand Up @@ -40,9 +41,11 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.utils.Log_OC
import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation
import com.owncloud.android.lib.resources.files.model.RemoteFile
import com.owncloud.android.lib.resources.files.model.ServerFileInterface
import com.owncloud.android.lib.resources.status.OCCapability
import com.owncloud.android.operations.RemoveFileOperation
import com.owncloud.android.operations.UploadFileOperation
import com.owncloud.android.ui.activity.SyncedFoldersActivity
import com.owncloud.android.utils.DisplayUtils
import com.owncloud.android.utils.FileUtil
import kotlinx.coroutines.CoroutineScope
Expand Down Expand Up @@ -616,4 +619,33 @@ class FileUploadHelper {
}
}
}

/**
* When a synced folder is disabled or deleted, its associated OCUpload entries in the uploads
* table must be cleaned up. Without this, stale upload entries outlive the folder config that
* created them, causing FileUploadWorker to keep retrying uploads for a folder that no longer
* exists or is intentionally turned off, and AutoUploadWorker to re-queue already handled files
* on its next scan via FileSystemRepository.getFilePathsWithIds.
*/
suspend fun removeEntityFromUploadEntities(id: Long) {
uploadsStorageManager.fileSystemDao.getBySyncedFolderId(id.toString())
.filter { it.localPath != null && it.remotePath != null }
.forEach {
Log_OC.d(
TAG,
"deleting upload entity localPath: ${it.localPath}, " + "remotePath: ${it.remotePath}"
)
uploadsStorageManager.uploadDao.deleteByLocalRemotePath(
localPath = it.localPath!!,
remotePath = it.remotePath!!
)
}
}

suspend fun getAutoUploadFolderEntity(file: ServerFileInterface, user: User): SyncedFolderEntity? {
val dao = uploadsStorageManager.syncedFolderDao
val normalizedRemotePath = file.remotePath.trimEnd()
if (normalizedRemotePath.isEmpty()) return null
return dao.findByRemotePathAndAccount(normalizedRemotePath, user.accountName)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import com.nextcloud.client.account.CurrentAccountProvider;
import com.nextcloud.client.account.User;
import com.nextcloud.client.database.NextcloudDatabase;
import com.nextcloud.client.database.dao.FileSystemDao;
import com.nextcloud.client.database.dao.SyncedFolderDao;
import com.nextcloud.client.database.dao.UploadDao;
import com.nextcloud.client.database.entity.UploadEntity;
import com.nextcloud.client.database.entity.UploadEntityKt;
Expand Down Expand Up @@ -69,7 +71,9 @@ public class UploadsStorageManager extends Observable {
private final ContentResolver contentResolver;
private final CurrentAccountProvider currentAccountProvider;
private OCCapability capability;
public final UploadDao uploadDao = NextcloudDatabase.getInstance(MainApp.getAppContext()).uploadDao();
public final UploadDao uploadDao = NextcloudDatabase.instance().uploadDao();
public final FileSystemDao fileSystemDao = NextcloudDatabase.instance().fileSystemDao();
public final SyncedFolderDao syncedFolderDao = NextcloudDatabase.instance().syncedFolderDao();

public UploadsStorageManager(
CurrentAccountProvider currentAccountProvider,
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/com/owncloud/android/db/ProviderMeta.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
*/
public class ProviderMeta {
public static final String DB_NAME = "filelist";
public static final int DB_VERSION = 98;
public static final int DB_VERSION = 99;

private ProviderMeta() {
// No instance
Expand Down Expand Up @@ -361,6 +361,7 @@ static public class ProviderTableMeta implements BaseColumns {

// Columns of filesystem data table
public static final String FILESYSTEM_FILE_LOCAL_PATH = "local_path";
public static final String FILESYSTEM_FILE_REMOTE_PATH = "remote_path";
public static final String FILESYSTEM_FILE_MODIFIED = "modified_at";
public static final String FILESYSTEM_FILE_IS_FOLDER = "is_folder";
public static final String FILESYSTEM_FILE_FOUND_RECENTLY = "found_at";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2172,6 +2172,21 @@ class FileDisplayActivity :
}
supportInvalidateOptionsMenu()
fetchRecommendedFilesIfNeeded(ignoreETag = true, currentDir)

// clean stale upload entities for auto upload folder
if (removedFile.isFolder) {
lifecycleScope.launch(Dispatchers.IO) {
val optionalUser = user
if (user.isEmpty) {
return@launch
}

val autoUploadFolder = fileUploadHelper
.getAutoUploadFolderEntity(removedFile, optionalUser.get()) ?: return@launch

autoUploadFolder.id?.toLong()?.let { fileUploadHelper.removeEntityFromUploadEntities(it) }
}
}
} else {
if (result.isSslRecoverableException) {
mLastSslUntrustedServerResult = result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -569,26 +569,6 @@ class SyncedFoldersActivity :
return result
}

override fun onSyncStatusToggleClick(section: Int, syncedFolderDisplayItem: SyncedFolderDisplayItem?) {
if (syncedFolderDisplayItem == null) return

if (syncedFolderDisplayItem.id > SyncedFolder.UNPERSISTED_ID) {
syncedFolderProvider.updateSyncedFolderEnabled(
syncedFolderDisplayItem.id,
syncedFolderDisplayItem.isEnabled
)
} else {
val storedId = syncedFolderProvider.storeSyncedFolder(syncedFolderDisplayItem)
if (storedId != -1L) {
syncedFolderDisplayItem.id = storedId
}
}
if (syncedFolderDisplayItem.isEnabled) {
backgroundJobManager.startAutoUpload(syncedFolderDisplayItem, overridePowerSaving = false)
showBatteryOptimizationDialogIfNeeded()
}
}

override fun onSyncFolderSettingsClick(section: Int, syncedFolderDisplayItem: SyncedFolderDisplayItem?) {
check(Looper.getMainLooper().isCurrentThread) { "This must be called on the main thread!" }

Expand Down Expand Up @@ -776,13 +756,46 @@ class SyncedFoldersActivity :
dialogFragment = null
}

override fun onSyncStatusToggleClick(section: Int, item: SyncedFolderDisplayItem?) {
item ?: return

// Ensure the item is persisted
if (item.id <= SyncedFolder.UNPERSISTED_ID) {
syncedFolderProvider.storeSyncedFolder(item)
.takeIf { it != -1L }
?.let { item.id = it }
} else {
syncedFolderProvider.updateSyncedFolderEnabled(item.id, item.isEnabled)
}

if (item.isEnabled) {
Log_OC.d(TAG, "auto-upload configuration sync status is enabled: " + item.remotePath)
backgroundJobManager.startAutoUpload(item, overridePowerSaving = false)
showBatteryOptimizationDialogIfNeeded()
return
}

Log_OC.d(TAG, "auto-upload configuration sync status is disabled: " + item.remotePath)

lifecycleScope.launch(Dispatchers.IO) {
fileUploadHelper.removeEntityFromUploadEntities(item.id)
}
}

override fun onDeleteSyncedFolderPreference(syncedFolder: SyncedFolderParcelable?) {
if (syncedFolder == null) {
return
}

syncedFolderProvider.deleteSyncedFolder(syncedFolder.id)
adapter.removeItem(syncedFolder.section)
Log_OC.d(TAG, "deleting auto upload configuration: " + syncedFolder.remotePath)

lifecycleScope.launch(Dispatchers.IO) {
fileUploadHelper.removeEntityFromUploadEntities(syncedFolder.id)
syncedFolderProvider.deleteSyncedFolder(syncedFolder.id)
withContext(Dispatchers.Main) {
adapter.removeItem(syncedFolder.section)
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ class SyncedFolderAdapter(
get() = syncFolderItems.size - filteredSyncFolderItems.size

interface ClickListener {
fun onSyncStatusToggleClick(section: Int, syncedFolderDisplayItem: SyncedFolderDisplayItem?)
fun onSyncStatusToggleClick(section: Int, item: SyncedFolderDisplayItem?)
fun onSyncFolderSettingsClick(section: Int, syncedFolderDisplayItem: SyncedFolderDisplayItem?)
fun onVisibilityToggleClick(section: Int, item: SyncedFolderDisplayItem?)
fun showSubFolderWarningDialog()
Expand Down
Loading