Fix tracking current download status

Signed-off-by: alperozturk <alper_ozturk@proton.me>
This commit is contained in:
alperozturk 2024-01-05 11:06:04 +01:00 committed by Alper Öztürk
parent bb0fae654f
commit de4f673740
11 changed files with 92 additions and 128 deletions

View file

@ -63,7 +63,7 @@ class FileDownloadHelper {
return backgroundJobManager.isStartFileDownloadJobScheduled( return backgroundJobManager.isStartFileDownloadJobScheduled(
user, user,
file file
) ) || FileDownloadWorker.isDownloading(user, file)
} }
fun cancelPendingOrCurrentDownloads(user: User?, file: OCFile?) { fun cancelPendingOrCurrentDownloads(user: User?, file: OCFile?) {

View file

@ -24,6 +24,7 @@ package com.nextcloud.client.files.downloader
import android.accounts.Account import android.accounts.Account
import android.accounts.AccountManager import android.accounts.AccountManager
import android.accounts.OnAccountsUpdateListener import android.accounts.OnAccountsUpdateListener
import android.annotation.SuppressLint
import android.app.PendingIntent import android.app.PendingIntent
import android.content.Context import android.content.Context
import androidx.core.util.component1 import androidx.core.util.component1
@ -34,8 +35,8 @@ import androidx.work.WorkerParameters
import com.nextcloud.client.account.User import com.nextcloud.client.account.User
import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.account.UserAccountManager
import com.nextcloud.java.util.Optional import com.nextcloud.java.util.Optional
import com.nextcloud.model.DownloadWorkerState import com.nextcloud.model.WorkerState
import com.nextcloud.model.DownloadWorkerStateLiveData import com.nextcloud.model.WorkerStateLiveData
import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.FileDataStorageManager
import com.owncloud.android.datamodel.OCFile import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.datamodel.UploadsStorageManager import com.owncloud.android.datamodel.UploadsStorageManager
@ -66,8 +67,10 @@ class FileDownloadWorker(
companion object { companion object {
private val TAG = FileDownloadWorker::class.java.simpleName private val TAG = FileDownloadWorker::class.java.simpleName
@SuppressLint("StaticFieldLeak")
private var currentDownload: DownloadFileOperation? = null
const val FILES_SEPARATOR = "," const val FILES_SEPARATOR = ","
const val WORKER_TAG = "WORKER_TAG"
const val FOLDER_PATH = "FOLDER_PATH" const val FOLDER_PATH = "FOLDER_PATH"
const val USER_NAME = "USER" const val USER_NAME = "USER"
const val FILE_PATH = "FILE_PATH" const val FILE_PATH = "FILE_PATH"
@ -84,6 +87,11 @@ class FileDownloadWorker(
const val EXTRA_LINKED_TO_PATH = "LINKED_TO" const val EXTRA_LINKED_TO_PATH = "LINKED_TO"
const val ACCOUNT_NAME = "ACCOUNT_NAME" const val ACCOUNT_NAME = "ACCOUNT_NAME"
fun isDownloading(user: User, file: OCFile): Boolean {
return currentDownload?.file?.fileId == file.fileId &&
currentDownload?.user?.accountName == user.accountName
}
fun getDownloadAddedMessage(): String { fun getDownloadAddedMessage(): String {
return FileDownloadWorker::class.java.name + "DOWNLOAD_ADDED" return FileDownloadWorker::class.java.name + "DOWNLOAD_ADDED"
} }
@ -93,7 +101,7 @@ class FileDownloadWorker(
} }
} }
private var currentDownload: DownloadFileOperation? = null private val pendingDownloads = IndexedForest<DownloadFileOperation>()
private var conflictUploadId: Long? = null private var conflictUploadId: Long? = null
private var lastPercent = 0 private var lastPercent = 0
private val intents = FileDownloadIntents(context) private val intents = FileDownloadIntents(context)
@ -106,8 +114,6 @@ class FileDownloadWorker(
private var user: User? = null private var user: User? = null
private var folder: OCFile? = null private var folder: OCFile? = null
private var isAnyOperationFailed = true private var isAnyOperationFailed = true
private var workerTag: String? = null
private val pendingDownloads = IndexedForest<DownloadFileOperation>()
@Suppress("TooGenericExceptionCaught") @Suppress("TooGenericExceptionCaught")
override fun doWork(): Result { override fun doWork(): Result {
@ -147,12 +153,11 @@ class FileDownloadWorker(
} }
private fun setWorkerState(user: User?) { private fun setWorkerState(user: User?) {
val worker = DownloadWorkerState(workerTag ?: "", user, pendingDownloads) WorkerStateLiveData.instance().setWorkState(WorkerState.Download(user, currentDownload))
DownloadWorkerStateLiveData.instance().addWorker(worker)
} }
private fun setIdleWorkerState() { private fun setIdleWorkerState() {
DownloadWorkerStateLiveData.instance().removeWorker(workerTag ?: "") WorkerStateLiveData.instance().setWorkState(WorkerState.Idle)
} }
private fun notifyForFolderResult(folder: OCFile) { private fun notifyForFolderResult(folder: OCFile) {
@ -165,7 +170,6 @@ class FileDownloadWorker(
val files = getFiles() val files = getFiles()
val downloadType = getDownloadType() val downloadType = getDownloadType()
workerTag = inputData.keyValueMap[WORKER_TAG] as String? ?: ""
conflictUploadId = inputData.keyValueMap[CONFLICT_UPLOAD_ID] as Long? conflictUploadId = inputData.keyValueMap[CONFLICT_UPLOAD_ID] as Long?
val behaviour = inputData.keyValueMap[BEHAVIOUR] as String? ?: "" val behaviour = inputData.keyValueMap[BEHAVIOUR] as String? ?: ""
@ -216,6 +220,7 @@ class FileDownloadWorker(
it.value.payload?.cancel() it.value.payload?.cancel()
} }
pendingDownloads.all.clear() pendingDownloads.all.clear()
currentDownload = null
} }
private fun setUser() { private fun setUser() {

View file

@ -34,7 +34,6 @@ import androidx.work.PeriodicWorkRequest
import androidx.work.WorkInfo import androidx.work.WorkInfo
import androidx.work.WorkManager import androidx.work.WorkManager
import androidx.work.workDataOf import androidx.work.workDataOf
import com.google.gson.Gson
import com.nextcloud.client.account.User import com.nextcloud.client.account.User
import com.nextcloud.client.core.Clock import com.nextcloud.client.core.Clock
import com.nextcloud.client.di.Injectable import com.nextcloud.client.di.Injectable
@ -42,7 +41,6 @@ import com.nextcloud.client.documentscan.GeneratePdfFromImagesWork
import com.nextcloud.client.files.downloader.FileDownloadWorker import com.nextcloud.client.files.downloader.FileDownloadWorker
import com.nextcloud.client.preferences.AppPreferences import com.nextcloud.client.preferences.AppPreferences
import com.nextcloud.utils.extensions.isWorkScheduled import com.nextcloud.utils.extensions.isWorkScheduled
import com.owncloud.android.datamodel.FileDataStorageManager
import com.owncloud.android.datamodel.OCFile import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.operations.DownloadType import com.owncloud.android.operations.DownloadType
import java.util.Date import java.util.Date
@ -549,7 +547,6 @@ internal class BackgroundJobManagerImpl(
val tag = startFileDownloadJobTag(user, file) val tag = startFileDownloadJobTag(user, file)
val data = workDataOf( val data = workDataOf(
FileDownloadWorker.WORKER_TAG to tag,
FileDownloadWorker.USER_NAME to user.accountName, FileDownloadWorker.USER_NAME to user.accountName,
FileDownloadWorker.FILE_PATH to file.remotePath, FileDownloadWorker.FILE_PATH to file.remotePath,
FileDownloadWorker.BEHAVIOUR to behaviour, FileDownloadWorker.BEHAVIOUR to behaviour,

View file

@ -1,71 +0,0 @@
/*
* Nextcloud Android client application
*
* @author Alper Ozturk
* Copyright (C) 2023 Alper Ozturk
* Copyright (C) 2023 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.nextcloud.model
import androidx.lifecycle.LiveData
import com.nextcloud.client.account.User
import com.owncloud.android.datamodel.OCFile
class DownloadWorkerStateLiveData private constructor(): LiveData<ArrayList<DownloadWorkerState>>() {
private var workers: ArrayList<DownloadWorkerState> = arrayListOf()
fun isDownloading(user: User?, file: OCFile?): Boolean {
if (user == null || file == null) {
return false
}
var result = false
workers.forEach { downloadState ->
downloadState.pendingDownloads?.all?.forEach { download ->
result = download.value?.payload?.file?.fileId == file.fileId
}
}
return result
}
fun removeWorker(tag: String) {
workers.forEach {
if (it.tag == tag) {
workers.remove(it)
}
}
postValue(workers)
}
fun addWorker(state: DownloadWorkerState) {
workers.add(state)
postValue(workers)
}
companion object {
private var instance: DownloadWorkerStateLiveData? = null
fun instance(): DownloadWorkerStateLiveData {
return instance ?: synchronized(this) {
instance ?: DownloadWorkerStateLiveData().also { instance = it }
}
}
}
}

View file

@ -22,7 +22,9 @@
package com.nextcloud.model package com.nextcloud.model
import com.nextcloud.client.account.User import com.nextcloud.client.account.User
import com.owncloud.android.files.services.IndexedForest
import com.owncloud.android.operations.DownloadFileOperation import com.owncloud.android.operations.DownloadFileOperation
data class DownloadWorkerState(var tag: String, var user: User?, var pendingDownloads: IndexedForest<DownloadFileOperation>?) sealed class WorkerState {
object Idle : WorkerState()
class Download(var user: User?, var currentDownload: DownloadFileOperation?) : WorkerState()
}

View file

@ -0,0 +1,41 @@
/*
* Nextcloud Android client application
*
* @author Alper Ozturk
* Copyright (C) 2023 Alper Ozturk
* Copyright (C) 2023 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.nextcloud.model
import androidx.lifecycle.LiveData
class WorkerStateLiveData private constructor() : LiveData<WorkerState>() {
fun setWorkState(state: WorkerState) {
postValue(state)
}
companion object {
private var instance: WorkerStateLiveData? = null
fun instance(): WorkerStateLiveData {
return instance ?: synchronized(this) {
instance ?: WorkerStateLiveData().also { instance = it }
}
}
}
}

View file

@ -73,7 +73,8 @@ import com.nextcloud.client.network.ConnectivityService;
import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.client.preferences.AppPreferences;
import com.nextcloud.client.utils.IntentUtil; import com.nextcloud.client.utils.IntentUtil;
import com.nextcloud.java.util.Optional; import com.nextcloud.java.util.Optional;
import com.nextcloud.model.DownloadWorkerStateLiveData; import com.nextcloud.model.WorkerState;
import com.nextcloud.model.WorkerStateLiveData;
import com.nextcloud.utils.extensions.BundleExtensionsKt; import com.nextcloud.utils.extensions.BundleExtensionsKt;
import com.nextcloud.utils.extensions.IntentExtensionsKt; import com.nextcloud.utils.extensions.IntentExtensionsKt;
import com.nextcloud.utils.view.FastScrollUtils; import com.nextcloud.utils.view.FastScrollUtils;
@ -1562,8 +1563,8 @@ public class FileDisplayActivity extends FileActivity
} }
private void observeDownloadWorkerState() { private void observeDownloadWorkerState() {
DownloadWorkerStateLiveData.Companion.instance().observe(this, state -> { WorkerStateLiveData.Companion.instance().observe(this, state -> {
if (!state.isEmpty()) { if (state instanceof WorkerState.Download) {
Log_OC.d(TAG, "Download worker started"); Log_OC.d(TAG, "Download worker started");
handleDownloadWorkerState(); handleDownloadWorkerState();
} }

View file

@ -37,6 +37,7 @@ import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.nextcloud.client.account.User; import com.nextcloud.client.account.User;
import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.account.UserAccountManager;
@ -44,8 +45,8 @@ import com.nextcloud.client.files.downloader.FileDownloadHelper;
import com.nextcloud.client.jobs.BackgroundJobManager; import com.nextcloud.client.jobs.BackgroundJobManager;
import com.nextcloud.client.onboarding.FirstRunActivity; import com.nextcloud.client.onboarding.FirstRunActivity;
import com.nextcloud.java.util.Optional; import com.nextcloud.java.util.Optional;
import com.nextcloud.model.DownloadWorkerState; import com.nextcloud.model.WorkerState;
import com.nextcloud.model.DownloadWorkerStateLiveData; import com.nextcloud.model.WorkerStateLiveData;
import com.nextcloud.utils.extensions.BundleExtensionsKt; import com.nextcloud.utils.extensions.BundleExtensionsKt;
import com.owncloud.android.MainApp; import com.owncloud.android.MainApp;
import com.owncloud.android.R; import com.owncloud.android.R;
@ -54,7 +55,6 @@ import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl; import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.files.services.FileUploader;
import com.owncloud.android.files.services.IndexedForest;
import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudAccount;
import com.owncloud.android.lib.common.UserInfo; import com.owncloud.android.lib.common.UserInfo;
import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.common.utils.Log_OC;
@ -115,7 +115,8 @@ public class ManageAccountsActivity extends FileActivity implements UserListAdap
private ArbitraryDataProvider arbitraryDataProvider; private ArbitraryDataProvider arbitraryDataProvider;
private boolean multipleAccountsSupported; private boolean multipleAccountsSupported;
private final ArrayList<DownloadWorkerState> downloadWorkerStates = new ArrayList<>(); private String workerAccountName;
private DownloadFileOperation workerCurrentDownload;
@Inject BackgroundJobManager backgroundJobManager; @Inject BackgroundJobManager backgroundJobManager;
@Inject UserAccountManager accountManager; @Inject UserAccountManager accountManager;
@ -164,7 +165,7 @@ public class ManageAccountsActivity extends FileActivity implements UserListAdap
recyclerView.setAdapter(userListAdapter); recyclerView.setAdapter(userListAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setLayoutManager(new LinearLayoutManager(this));
initializeComponentGetters(); initializeComponentGetters();
observeDownloadWorkerState(); observeWorkerState();
} }
@ -341,7 +342,7 @@ public class ManageAccountsActivity extends FileActivity implements UserListAdap
mUploaderBinder.cancel(accountName); mUploaderBinder.cancel(accountName);
} }
cancelAllDownloadsForAccount(); FileDownloadHelper.Companion.instance().cancelAllDownloadsForAccount(workerAccountName, workerCurrentDownload);
} }
User currentUser = getUserAccountManager().getUser(); User currentUser = getUserAccountManager().getUser();
@ -409,20 +410,6 @@ public class ManageAccountsActivity extends FileActivity implements UserListAdap
return new ManageAccountsServiceConnection(); return new ManageAccountsServiceConnection();
} }
private void cancelAllDownloadsForAccount() {
for (DownloadWorkerState workerState : downloadWorkerStates) {
User currentUser = workerState.getUser();
IndexedForest<DownloadFileOperation> pendingDownloads = workerState.getPendingDownloads();
if (currentUser != null && pendingDownloads != null) {
pendingDownloads.getAll().values().forEach((value) -> {
DownloadFileOperation operation = value.getPayload();
FileDownloadHelper.Companion.instance().cancelAllDownloadsForAccount(currentUser.getAccountName(), operation);
});
}
}
}
private void performAccountRemoval(User user) { private void performAccountRemoval(User user) {
// disable account in recycler view // disable account in recycler view
for (int i = 0; i < userListAdapter.getItemCount(); i++) { for (int i = 0; i < userListAdapter.getItemCount(); i++) {
@ -445,7 +432,8 @@ public class ManageAccountsActivity extends FileActivity implements UserListAdap
mUploaderBinder.cancel(user); mUploaderBinder.cancel(user);
} }
cancelAllDownloadsForAccount(); FileDownloadHelper.Companion.instance().cancelAllDownloadsForAccount(workerAccountName, workerCurrentDownload);
backgroundJobManager.startAccountRemovalJob(user.getAccountName(), false); backgroundJobManager.startAccountRemovalJob(user.getAccountName(), false);
// immediately select a new account // immediately select a new account
@ -529,10 +517,13 @@ public class ManageAccountsActivity extends FileActivity implements UserListAdap
} }
} }
private void observeDownloadWorkerState() { private void observeWorkerState() {
DownloadWorkerStateLiveData.Companion.instance().observe(this, state -> { WorkerStateLiveData.Companion.instance().observe(this, state -> {
Log_OC.d(TAG, "Download worker started"); if (state instanceof WorkerState.Download) {
downloadWorkerStates.addAll(state); Log_OC.d(TAG, "Download worker started");
workerAccountName = ((WorkerState.Download) state).getUser().getAccountName();
workerCurrentDownload = ((WorkerState.Download) state).getCurrentDownload();
}
}); });
} }

View file

@ -31,7 +31,6 @@ import android.os.Looper
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import androidx.lifecycle.LifecycleOwner
import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter
import com.afollestad.sectionedrecyclerview.SectionedViewHolder import com.afollestad.sectionedrecyclerview.SectionedViewHolder
import com.nextcloud.client.account.User import com.nextcloud.client.account.User

View file

@ -34,7 +34,6 @@ import com.nextcloud.android.common.ui.theme.utils.ColorRole
import com.nextcloud.client.account.User import com.nextcloud.client.account.User
import com.nextcloud.client.files.downloader.FileDownloadHelper import com.nextcloud.client.files.downloader.FileDownloadHelper
import com.nextcloud.client.preferences.AppPreferences import com.nextcloud.client.preferences.AppPreferences
import com.nextcloud.model.DownloadWorkerStateLiveData
import com.nextcloud.utils.extensions.createRoundedOutline import com.nextcloud.utils.extensions.createRoundedOutline
import com.owncloud.android.R import com.owncloud.android.R
import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.FileDataStorageManager
@ -344,26 +343,25 @@ class OCFileListDelegate(
val operationsServiceBinder = transferServiceGetter.operationsServiceBinder val operationsServiceBinder = transferServiceGetter.operationsServiceBinder
val fileUploaderBinder = transferServiceGetter.fileUploaderBinder val fileUploaderBinder = transferServiceGetter.fileUploaderBinder
val isDownloading = if (file.isFolder) {
FileDownloadHelper.instance().isDownloading(user, file)
} else {
DownloadWorkerStateLiveData.instance().isDownloading(user, file)
}
val icon: Int? = when { val icon: Int? = when {
operationsServiceBinder?.isSynchronizing(user, file) == true || operationsServiceBinder?.isSynchronizing(user, file) == true ||
isDownloading || FileDownloadHelper.instance().isDownloading(user, file) ||
fileUploaderBinder?.isUploading(user, file) == true -> { fileUploaderBinder?.isUploading(user, file) == true -> {
// synchronizing, downloading or uploading // synchronizing, downloading or uploading
R.drawable.ic_synchronizing R.drawable.ic_synchronizing
} }
file.etagInConflict != null -> { file.etagInConflict != null -> {
R.drawable.ic_synchronizing_error R.drawable.ic_synchronizing_error
} }
file.isDown -> { file.isDown -> {
R.drawable.ic_synced R.drawable.ic_synced
} }
else -> { null }
else -> {
null
}
} }
gridViewHolder.localFileIndicator.run { gridViewHolder.localFileIndicator.run {

View file

@ -41,7 +41,8 @@ import com.nextcloud.client.files.downloader.FileDownloadHelper;
import com.nextcloud.client.files.downloader.FileDownloadWorker; import com.nextcloud.client.files.downloader.FileDownloadWorker;
import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.client.preferences.AppPreferences;
import com.nextcloud.java.util.Optional; import com.nextcloud.java.util.Optional;
import com.nextcloud.model.DownloadWorkerStateLiveData; import com.nextcloud.model.WorkerState;
import com.nextcloud.model.WorkerStateLiveData;
import com.nextcloud.utils.extensions.IntentExtensionsKt; import com.nextcloud.utils.extensions.IntentExtensionsKt;
import com.owncloud.android.MainApp; import com.owncloud.android.MainApp;
import com.owncloud.android.R; import com.owncloud.android.R;
@ -306,8 +307,8 @@ public class PreviewImageActivity extends FileActivity implements
} }
private void observeDownloadWorkerState() { private void observeDownloadWorkerState() {
DownloadWorkerStateLiveData.Companion.instance().observe(this, state -> { WorkerStateLiveData.Companion.instance().observe(this, state -> {
if (!state.isEmpty()) { if (state instanceof WorkerState.Download) {
Log_OC.d(TAG, "Download worker started"); Log_OC.d(TAG, "Download worker started");
isDownloadWorkStarted = true; isDownloadWorkStarted = true;