Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[New arch] Migrate pending uploads to new database #3729

Merged
merged 11 commits into from
Aug 26, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
import com.owncloud.android.db.UploadResult;
import com.owncloud.android.domain.camerauploads.model.UploadBehavior;
import com.owncloud.android.domain.files.model.OCFile;
import com.owncloud.android.usecases.transfers.uploads.UploadEnqueuedBy;
import com.owncloud.android.domain.transfers.model.UploadEnqueuedBy;
import com.owncloud.android.utils.MimetypeIconUtil;
import timber.log.Timber;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ import com.owncloud.android.domain.transfers.usecases.ClearFailedTransfersUseCas
import com.owncloud.android.domain.transfers.usecases.ClearSuccessfulTransfersUseCase
import com.owncloud.android.domain.transfers.usecases.DeleteTransferWithIdUseCase
import com.owncloud.android.domain.transfers.usecases.GetAllTransfersAsLiveDataUseCase
import com.owncloud.android.domain.transfers.usecases.UpdatePendingUploadsPathUseCase
import com.owncloud.android.domain.user.usecases.GetStoredQuotaUseCase
import com.owncloud.android.domain.user.usecases.GetUserAvatarAsyncUseCase
import com.owncloud.android.domain.user.usecases.GetUserInfoAsyncUseCase
Expand Down Expand Up @@ -172,6 +173,7 @@ val useCaseModule = module {
factory { RetryFailedUploadsUseCase(get(), get(), get(), get()) }
factory { ClearSuccessfulTransfersUseCase(get()) }
factory { CancelUploadsFromAccountUseCase(get(), get()) }
factory { UpdatePendingUploadsPathUseCase(get()) }

// User
factory { GetStoredQuotaUseCase(get()) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ val viewModelModule = module {
viewModel { SettingsAdvancedViewModel(get()) }
viewModel { RemoveAccountDialogViewModel(get(), get(), get(), get()) }
viewModel { LogListViewModel(get()) }
viewModel { MigrationViewModel(MainApp.dataFolder, get(), get(), get(), get(), get(), get()) }
viewModel { MigrationViewModel(MainApp.dataFolder, get(), get(), get(), get(), get(), get(), get()) }
viewModel { PatternViewModel(get()) }
viewModel { BiometricViewModel(get(), get()) }
viewModel { ReleaseNotesViewModel(get(), get()) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@

package com.owncloud.android.extensions

import android.content.Context
import android.net.Uri
import androidx.annotation.StringRes
import androidx.documentfile.provider.DocumentFile
import com.owncloud.android.R
import com.owncloud.android.domain.transfers.model.OCTransfer
import com.owncloud.android.domain.transfers.model.TransferResult
Expand Down Expand Up @@ -61,3 +64,7 @@ fun OCTransfer.statusToStringRes(): Int {
}
}
}

fun OCTransfer.isContentUri(context: Context): Boolean {
return DocumentFile.isDocumentUri(context, Uri.parse(localPath))
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import androidx.work.WorkManager
import androidx.work.WorkQuery
import com.owncloud.android.domain.files.model.OCFile
import com.owncloud.android.usecases.transfers.TRANSFER_TAG_DOWNLOAD
import com.owncloud.android.workers.UploadFileFromContentUriWorker.Companion.TRANSFER_TAG_MANUAL_UPLOAD

val PENDING_WORK_STATUS = listOf(WorkInfo.State.ENQUEUED, WorkInfo.State.RUNNING, WorkInfo.State.BLOCKED)
val FINISHED_WORK_STATUS = listOf(WorkInfo.State.SUCCEEDED, WorkInfo.State.FAILED, WorkInfo.State.CANCELLED)
Expand Down Expand Up @@ -70,9 +69,6 @@ fun WorkManager.isUploadPending(account: Account, file: OCFile): Boolean = false
fun getTagsForDownload(file: OCFile, accountName: String) =
listOf(TRANSFER_TAG_DOWNLOAD, file.id.toString(), accountName)

fun getTagsForUpload(file: OCFile, account: Account) =
listOf(TRANSFER_TAG_MANUAL_UPLOAD, file.id.toString(), account.name)

/**
* Take care with WorkQueries. It will return workers that match at least ONE of the tags.
* If we perform a query with tags {"account@server", "2"}, [WorkManager.getWorkInfos] will return workers that
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCUpload;
import com.owncloud.android.datamodel.UploadsStorageManager;
import com.owncloud.android.domain.UseCaseResult;
import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus;
import com.owncloud.android.domain.files.model.OCFile;
Expand Down Expand Up @@ -61,7 +59,6 @@
import java.util.concurrent.atomic.AtomicBoolean;

import static com.owncloud.android.usecases.synchronization.SynchronizeFolderUseCase.SyncFolderMode.REFRESH_FOLDER;
import static com.owncloud.android.usecases.synchronization.SynchronizeFolderUseCase.SyncFolderMode.SYNC_FOLDER_RECURSIVELY;
import static org.koin.java.KoinJavaComponent.get;
import static org.koin.java.KoinJavaComponent.inject;

Expand Down Expand Up @@ -447,7 +444,9 @@ private boolean addToSyncContents(OCFile localFile, OCFile remoteFile) {
// files do not use treeEtag
serverUnchanged = (remoteFile == null) || localFile.getEtag().equals(remoteFile.getEtag());

if (shouldSyncContents && !isBlockedForAutomatedSync(localFile)) {
// TODO: check if necessary
// if (shouldSyncContents && !isBlockedForAutomatedSync(localFile)) {
if (shouldSyncContents) {
/// synchronization for files
mFilesToSyncContents.add(localFile);
}
Expand Down Expand Up @@ -545,6 +544,7 @@ public String getFolderPath() {
* @return 'True' if the received file should not be automatically synced due to a previous
* upload error that requires an user action.
*/
/*
private boolean isBlockedForAutomatedSync(OCFile file) {
UploadsStorageManager uploadsStorageManager = new UploadsStorageManager(mContext.getContentResolver());
OCUpload failedUpload = uploadsStorageManager.getLastUploadFor(file, mAccount.name);
Expand All @@ -561,6 +561,7 @@ private boolean isBlockedForAutomatedSync(OCFile file) {
}
return false;
}
*/

public String getRemotePath() {
return mRemotePath;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
*
* @author David González Verdugo
* @author Abel García de Prada
* Copyright (C) 2020 ownCloud GmbH.
* @author Juan Carlos Garrote Gascón
*
* Copyright (C) 2022 ownCloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
Expand All @@ -30,15 +32,14 @@ import com.owncloud.android.data.preferences.datasources.SharedPreferencesProvid
import com.owncloud.android.data.storage.LegacyStorageProvider
import com.owncloud.android.data.storage.LocalStorageProvider
import com.owncloud.android.datamodel.FileDataStorageManager
import com.owncloud.android.datamodel.OCUpload
import com.owncloud.android.datamodel.UploadsStorageManager
import com.owncloud.android.domain.transfers.usecases.GetAllTransfersUseCase
import com.owncloud.android.domain.transfers.usecases.UpdatePendingUploadsPathUseCase
import com.owncloud.android.domain.utils.Event
import com.owncloud.android.presentation.ui.migration.StorageMigrationActivity.Companion.PREFERENCE_ALREADY_MIGRATED_TO_SCOPED_STORAGE
import com.owncloud.android.providers.AccountProvider
import com.owncloud.android.providers.ContextProvider
import com.owncloud.android.providers.CoroutinesDispatcherProvider
import kotlinx.coroutines.launch
import timber.log.Timber
import java.io.File

/**
Expand All @@ -48,9 +49,10 @@ class MigrationViewModel(
rootFolder: String,
private val localStorageProvider: LocalStorageProvider,
private val preferencesProvider: SharedPreferencesProvider,
private val uploadsStorageManager: UploadsStorageManager,
private val contextProvider: ContextProvider,
private val accountProvider: AccountProvider,
private val updatePendingUploadsPathUseCase: UpdatePendingUploadsPathUseCase,
private val getAllTransfersUseCase: GetAllTransfersUseCase,
private val coroutineDispatcherProvider: CoroutinesDispatcherProvider,
) : ViewModel() {

Expand Down Expand Up @@ -82,43 +84,18 @@ class MigrationViewModel(
}

private fun updatePendingUploadsPath() {
uploadsStorageManager.clearSuccessfulUploads()
val storedUploads: Array<OCUpload> = uploadsStorageManager.allStoredUploads
val uploadsWithUpdatedPath =
storedUploads.map {
it.apply { localPath = localPath.replace(legacyStorageDirectoryPath, localStorageProvider.getRootFolderPath()) }
}
uploadsWithUpdatedPath.forEach { uploadsStorageManager.updateUpload(it) }
clearUnrelatedTemporalFiles(uploadsWithUpdatedPath)
}

private fun clearUnrelatedTemporalFiles(pendingUploads: List<OCUpload>) {
val listOfAccounts = accountProvider.getLoggedAccounts()

listOfAccounts.forEach { account ->
val temporalFolderForAccount = File(localStorageProvider.getTemporalPath(account.name))

cleanTemporalRecursively(temporalFolderForAccount) { temporalFile ->
if (!pendingUploads.map { it.localPath }.contains(temporalFile.absolutePath)) {
Timber.d("Found a temporary file that is not needed: $temporalFile, so let's delete it")
temporalFile.delete()
}
}
}
}

private fun cleanTemporalRecursively(
temporalFolder: File,
deleteFileInCaseItIsNotNeeded: (file: File) -> Unit
) {
temporalFolder.listFiles()?.forEach { temporalFile ->
if (temporalFile.isDirectory) {
cleanTemporalRecursively(temporalFile, deleteFileInCaseItIsNotNeeded)
} else {
deleteFileInCaseItIsNotNeeded(temporalFile)
}

updatePendingUploadsPathUseCase.execute(
UpdatePendingUploadsPathUseCase.Params(
oldDirectory = legacyStorageDirectoryPath,
newDirectory = localStorageProvider.getRootFolderPath()
)
)
val uploads = getAllTransfersUseCase.execute(Unit)
val accountsNames = mutableListOf<String>()
accountProvider.getLoggedAccounts().forEach { account ->
accountsNames.add(localStorageProvider.getTemporalPath(account.name))
}
localStorageProvider.clearUnrelatedTemporalFiles(uploads, accountsNames)
}

private fun updateAlreadyDownloadedFilesPath() {
Expand Down
Loading