Skip to content

Commit 660b2ff

Browse files
committed
save files that not exists in db before sync all folder
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
1 parent 75d127a commit 660b2ff

File tree

2 files changed

+110
-47
lines changed

2 files changed

+110
-47
lines changed

app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorker.kt

Lines changed: 105 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,21 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager
1212
import androidx.work.CoroutineWorker
1313
import androidx.work.ForegroundInfo
1414
import androidx.work.WorkerParameters
15+
import com.nextcloud.client.account.User
1516
import com.nextcloud.client.account.UserAccountManager
1617
import com.nextcloud.client.jobs.download.FileDownloadHelper
1718
import com.owncloud.android.datamodel.FileDataStorageManager
1819
import com.owncloud.android.datamodel.OCFile
20+
import com.owncloud.android.lib.common.OwnCloudClient
1921
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory
2022
import com.owncloud.android.lib.common.utils.Log_OC
2123
import com.owncloud.android.operations.DownloadFileOperation
2224
import com.owncloud.android.operations.DownloadType
25+
import com.owncloud.android.operations.RefreshFolderOperation
2326
import com.owncloud.android.utils.FileStorageUtils
2427
import com.owncloud.android.utils.theme.ViewThemeUtils
2528
import kotlinx.coroutines.Dispatchers
29+
import kotlinx.coroutines.delay
2630
import kotlinx.coroutines.withContext
2731
import java.util.concurrent.ConcurrentHashMap
2832

@@ -40,9 +44,7 @@ class FolderDownloadWorker(
4044
const val FOLDER_ID = "FOLDER_ID"
4145
const val ACCOUNT_NAME = "ACCOUNT_NAME"
4246
const val SYNC_ALL = "SYNC_ALL"
43-
4447
private val pendingDownloads: MutableSet<Long> = ConcurrentHashMap.newKeySet()
45-
4648
fun isDownloading(id: Long): Boolean = pendingDownloads.contains(id)
4749
}
4850

@@ -59,51 +61,65 @@ class FolderDownloadWorker(
5961

6062
val accountName = inputData.getString(ACCOUNT_NAME)
6163
if (accountName == null) {
62-
Log_OC.e(TAG, "failed accountName cannot be null")
64+
Log_OC.e(TAG, "failed: accountName cannot be null")
6365
return Result.failure()
6466
}
6567

6668
val optionalUser = accountManager.getUser(accountName)
6769
if (optionalUser.isEmpty) {
68-
Log_OC.e(TAG, "failed user is not present")
70+
Log_OC.e(TAG, "failed: user is not present")
6971
return Result.failure()
7072
}
7173

7274
val syncAll = inputData.getBoolean(SYNC_ALL, false)
73-
7475
val user = optionalUser.get()
7576
storageManager = FileDataStorageManager(user, context.contentResolver)
77+
7678
val folder = storageManager.getFileById(folderID)
7779
if (folder == null) {
78-
Log_OC.e(TAG, "failed folder cannot be nul")
80+
Log_OC.e(TAG, "failed: folder cannot be null")
7981
return Result.failure()
8082
}
8183

82-
if (syncAll) {
83-
Log_OC.d(TAG, "checking folder size including all nested subfolders")
84-
if (!FileStorageUtils.checkIfEnoughSpace(folder)) {
85-
notificationManager.showNotAvailableDiskSpace()
86-
return Result.failure()
87-
}
88-
}
89-
90-
Log_OC.d(TAG, "🕒 started for ${user.accountName} downloading ${folder.fileName}")
84+
Log_OC.d(TAG, "🕒 started for ${user.accountName} | folder=${folder.fileName} | syncAll=$syncAll")
9185

9286
trySetForeground(folder)
93-
9487
folderDownloadEventBroadcaster.sendDownloadEnqueued(folder.fileId)
9588
pendingDownloads.add(folder.fileId)
9689

9790
val downloadHelper = FileDownloadHelper.instance()
9891

9992
return withContext(Dispatchers.IO) {
10093
try {
101-
val files = getFiles(folder, storageManager, syncAll)
10294
val account = user.toOwnCloudAccount()
103-
val client = OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(account, context)
95+
val client = OwnCloudClientManagerFactory.getDefaultSingleton()
96+
.getClientFor(account, context)
97+
98+
if (syncAll) {
99+
Log_OC.d(TAG, "checking available disk space for full recursive download")
100+
if (!FileStorageUtils.checkIfEnoughSpace(folder)) {
101+
notificationManager.showNotAvailableDiskSpace()
102+
return@withContext Result.failure()
103+
}
104+
Log_OC.d(TAG, "🔄 syncing full folder tree from server before collecting files")
105+
syncFolderRecursivelyFromServer(folder, user, client)
106+
}
107+
108+
val files = getFiles(folder, storageManager, syncAll)
109+
if (files.isEmpty()) {
110+
Log_OC.d(TAG, "✅ no files need downloading")
111+
notificationManager.showCompletionNotification(folder.fileName, true)
112+
return@withContext Result.success()
113+
}
114+
115+
var overallSuccess = true
104116

105-
var result = true
106117
files.forEachIndexed { index, file ->
118+
if (isStopped) {
119+
Log_OC.d(TAG, "⚠️ worker stopped mid-download, aborting remaining files")
120+
return@withContext Result.failure()
121+
}
122+
107123
if (!FileStorageUtils.checkIfEnoughSpace(folder)) {
108124
notificationManager.showNotAvailableDiskSpace()
109125
return@withContext Result.failure()
@@ -117,46 +133,92 @@ class FolderDownloadWorker(
117133
files.size
118134
)
119135
notificationManager.showNotification(notification)
120-
121-
val foregroundInfo = notificationManager.getForegroundInfo(notification)
122-
setForeground(foregroundInfo)
136+
setForeground(notificationManager.getForegroundInfo(notification))
123137
}
124138

125139
val operation = DownloadFileOperation(user, file, context)
126140
val operationResult = operation.execute(client)
141+
127142
if (operationResult?.isSuccess == true && operation.downloadType === DownloadType.DOWNLOAD) {
128143
getOCFile(operation)?.let { ocFile ->
129144
downloadHelper.saveFile(ocFile, operation, storageManager)
130145
}
131146
}
132147

133-
if (!operationResult.isSuccess) {
134-
result = false
148+
if (operationResult?.isSuccess != true) {
149+
Log_OC.w(TAG, "⚠️ download failed for ${file.remotePath}: ${operationResult?.logMessage}")
150+
overallSuccess = false
135151
}
136152
}
137153

138-
withContext(Dispatchers.Main) {
139-
notificationManager.showCompletionNotification(folder.fileName, result)
140-
}
154+
notificationManager.showCompletionNotification(folder.fileName, overallSuccess)
141155

142-
if (result) {
143-
Log_OC.d(TAG, "✅ completed")
156+
if (overallSuccess) {
157+
Log_OC.d(TAG, "✅ completed successfully")
144158
Result.success()
145159
} else {
146-
Log_OC.d(TAG, "failed")
160+
Log_OC.d(TAG, "completed with failures")
147161
Result.failure()
148162
}
149163
} catch (e: Exception) {
150-
Log_OC.d(TAG, "❌ failed reason: $e")
164+
Log_OC.e(TAG, "❌ unexpected failure: $e")
165+
notificationManager.showCompletionNotification(folder.fileName, false)
151166
Result.failure()
152167
} finally {
153168
folderDownloadEventBroadcaster.sendDownloadCompleted(folder.fileId)
154169
pendingDownloads.remove(folder.fileId)
170+
171+
// delay so that user can see the error or success notification
172+
delay(2000)
155173
notificationManager.dismiss()
156174
}
157175
}
158176
}
159177

178+
private fun syncFolderRecursivelyFromServer(folder: OCFile, user: User, client: OwnCloudClient) {
179+
if (isStopped) return
180+
181+
try {
182+
Log_OC.d(TAG, "🔄 refreshing from server: ${folder.remotePath}")
183+
184+
val operation = RefreshFolderOperation(
185+
folder,
186+
System.currentTimeMillis(),
187+
false,
188+
true,
189+
false,
190+
storageManager,
191+
user,
192+
context
193+
)
194+
195+
val result = operation.execute(client)
196+
197+
if (!result.isSuccess) {
198+
Log_OC.w(TAG, "⚠️ failed to refresh ${folder.remotePath}: ${result.logMessage}")
199+
return
200+
}
201+
202+
val refreshedFolder = storageManager.getFileById(folder.fileId) ?: run {
203+
Log_OC.w(TAG, "⚠️ folder ${folder.fileId} missing from DB after refresh")
204+
return
205+
}
206+
207+
val subFolders = storageManager
208+
.getFolderContent(refreshedFolder, false)
209+
.filter { it.isFolder }
210+
211+
for (subFolder in subFolders) {
212+
if (isStopped) return
213+
syncFolderRecursivelyFromServer(subFolder, user, client)
214+
}
215+
} catch (e: Exception) {
216+
Log_OC.w(TAG, "⚠️ exception syncing ${folder.remotePath}: $e")
217+
} finally {
218+
Log_OC.d(TAG, "sub folders are fetched before downloading all")
219+
}
220+
}
221+
160222
@Suppress("ReturnCount")
161223
override suspend fun getForegroundInfo(): ForegroundInfo {
162224
return try {
@@ -167,11 +229,12 @@ class FolderDownloadWorker(
167229
return notificationManager.getForegroundInfo(null)
168230
}
169231

170-
val folder = storageManager.getFileById(folderID) ?: return notificationManager.getForegroundInfo(null)
232+
val folder = storageManager.getFileById(folderID)
233+
?: return notificationManager.getForegroundInfo(null)
171234

172-
return notificationManager.getForegroundInfo(folder)
235+
notificationManager.getForegroundInfo(folder)
173236
} catch (e: Exception) {
174-
Log_OC.w(TAG, "⚠️ Error getting foreground info: ${e.message}")
237+
Log_OC.w(TAG, "⚠️ error getting foreground info: ${e.message}")
175238
notificationManager.getForegroundInfo(null)
176239
}
177240
}
@@ -181,20 +244,18 @@ class FolderDownloadWorker(
181244
val foregroundInfo = notificationManager.getForegroundInfo(folder)
182245
setForeground(foregroundInfo)
183246
} catch (e: Exception) {
184-
Log_OC.w(TAG, "⚠️ Could not set foreground service: ${e.message}")
247+
Log_OC.w(TAG, "⚠️ could not set foreground service: ${e.message}")
185248
}
186249
}
187250

188-
private fun getOCFile(operation: DownloadFileOperation): OCFile? {
189-
val file = operation.file?.fileId?.let { storageManager.getFileById(it) }
190-
?: storageManager.getFileByDecryptedRemotePath(operation.file?.remotePath)
191-
?: run {
192-
Log_OC.e(TAG, "could not save ${operation.file?.remotePath}")
193-
return null
194-
}
195-
196-
return file
251+
private fun getOCFile(operation: DownloadFileOperation): OCFile? = operation.file?.fileId?.let {
252+
storageManager.getFileById(it)
197253
}
254+
?: storageManager.getFileByDecryptedRemotePath(operation.file?.remotePath)
255+
?: run {
256+
Log_OC.e(TAG, "could not resolve OCFile for save: ${operation.file?.remotePath}")
257+
null
258+
}
198259

199260
private fun getFiles(folder: OCFile, storageManager: FileDataStorageManager, syncAll: Boolean): List<OCFile> =
200261
if (syncAll) {

app/src/main/java/com/nextcloud/client/jobs/folderDownload/FolderDownloadWorkerNotificationManager.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import com.owncloud.android.R
1717
import com.owncloud.android.datamodel.OCFile
1818
import com.owncloud.android.ui.notifications.NotificationUtils
1919
import com.owncloud.android.utils.theme.ViewThemeUtils
20+
import kotlinx.coroutines.Dispatchers
21+
import kotlinx.coroutines.withContext
2022
import kotlin.random.Random
2123

2224
class FolderDownloadWorkerNotificationManager(private val context: Context, viewThemeUtils: ViewThemeUtils) :
@@ -78,7 +80,7 @@ class FolderDownloadWorkerNotificationManager(private val context: Context, view
7880
return getNotification(folderName, description, progress)
7981
}
8082

81-
fun showCompletionNotification(folderName: String, success: Boolean) {
83+
suspend fun showCompletionNotification(folderName: String, success: Boolean) = withContext(Dispatchers.Main) {
8284
val titleId = if (success) {
8385
R.string.folder_download_success_notification_title
8486
} else {
@@ -91,10 +93,10 @@ class FolderDownloadWorkerNotificationManager(private val context: Context, view
9193
notificationManager.notify(NOTIFICATION_ID, notification)
9294
}
9395

94-
fun showNotAvailableDiskSpace() {
96+
suspend fun showNotAvailableDiskSpace() = withContext(Dispatchers.Main) {
9597
val title = context.getString(R.string.folder_download_insufficient_disk_space_notification_title)
9698
val notification = getNotification(title)
97-
notificationManager.notify(NOTIFICATION_ID, notification)
99+
notificationManager.notify(Random.nextInt(), notification)
98100
}
99101

100102
fun getForegroundInfo(folder: OCFile?): ForegroundInfo {

0 commit comments

Comments
 (0)