diff --git a/src/androidTest/java/com/nextcloud/client/files/downloader/DownloaderServiceTest.kt b/src/androidTest/java/com/nextcloud/client/files/downloader/DownloaderServiceTest.kt index 08d6045442..86029131b3 100644 --- a/src/androidTest/java/com/nextcloud/client/files/downloader/DownloaderServiceTest.kt +++ b/src/androidTest/java/com/nextcloud/client/files/downloader/DownloaderServiceTest.kt @@ -44,15 +44,15 @@ class DownloaderServiceTest { @Test(expected = TimeoutException::class) fun cannot_bind_to_service_without_user() { - val intent = DownloaderService.createBindIntent(getApplicationContext(), user) - intent.removeExtra(DownloaderService.EXTRA_USER) + val intent = FileTransferService.createBindIntent(getApplicationContext(), user) + intent.removeExtra(FileTransferService.EXTRA_USER) service.bindService(intent) } @Test fun bind_with_user() { - val intent = DownloaderService.createBindIntent(getApplicationContext(), user) + val intent = FileTransferService.createBindIntent(getApplicationContext(), user) val binder = service.bindService(intent) - assertTrue(binder is DownloaderService.Binder) + assertTrue(binder is FileTransferService.Binder) } } diff --git a/src/androidTest/java/com/nextcloud/client/files/downloader/RegistryTest.kt b/src/androidTest/java/com/nextcloud/client/files/downloader/RegistryTest.kt index 60b1878e5b..21f39a484f 100644 --- a/src/androidTest/java/com/nextcloud/client/files/downloader/RegistryTest.kt +++ b/src/androidTest/java/com/nextcloud/client/files/downloader/RegistryTest.kt @@ -97,7 +97,7 @@ class RegistryTest { // new transfer requests added val addedTransfersCount = 10 for (i in 0 until addedTransfersCount) { - val request = Request(user, file) + val request = DownloadRequest(user, file) registry.add(request) } @@ -116,7 +116,7 @@ class RegistryTest { @Before fun setUp() { for (i in 0 until ENQUEUED_REQUESTS_COUNT) { - registry.add(Request(user, file)) + registry.add(DownloadRequest(user, file)) } assertEquals(ENQUEUED_REQUESTS_COUNT, registry.pending.size) } @@ -180,7 +180,7 @@ class RegistryTest { @Before fun setUp() { - val request = Request(user, file) + val request = DownloadRequest(user, file) uuid = registry.add(request) registry.startNext() assertEquals(uuid, request.uuid) @@ -246,7 +246,7 @@ class RegistryTest { @Before fun setUp() { - uuid = registry.add(Request(user, file)) + uuid = registry.add(DownloadRequest(user, file)) registry.startNext() registry.progress(uuid, PROGRESS_FULL) resetMocks() @@ -320,14 +320,14 @@ class RegistryTest { @Before fun setUp() { - completedTransferId = registry.add(Request(user, completedTransferFile)) + completedTransferId = registry.add(DownloadRequest(user, completedTransferFile)) registry.startNext() registry.complete(completedTransferId, true) - runningTransferId = registry.add(Request(user, runningTransferFile)) + runningTransferId = registry.add(DownloadRequest(user, runningTransferFile)) registry.startNext() - pendingTransferId = registry.add(Request(user, pendingTransferFile)) + pendingTransferId = registry.add(DownloadRequest(user, pendingTransferFile)) resetMocks() assertEquals(1, registry.pending.size) @@ -475,7 +475,8 @@ class RegistryTest { fun request_pending() { // WHEN // request is enqueued - registry.add(Request(user, OCFile("/path/alpha/1"))) + val request = DownloadRequest(user, OCFile("/path/alpha/1")) + registry.add(request) assertEquals(1, registry.pending.size) assertEquals(0, registry.running.size) assertEquals(0, registry.completed.size) @@ -489,7 +490,8 @@ class RegistryTest { fun request_running() { // WHEN // request is running - registry.add(Request(user, OCFile("/path/alpha/1"))) + val request = DownloadRequest(user, OCFile("/path/alpha/1")) + registry.add(request) registry.startNext() assertEquals(0, registry.pending.size) assertEquals(1, registry.running.size) @@ -504,7 +506,8 @@ class RegistryTest { fun request_completed() { // WHEN // request is running - val id = registry.add(Request(user, OCFile("/path/alpha/1"))) + val request = DownloadRequest(user, OCFile("/path/alpha/1")) + val id = registry.add(request) registry.startNext() registry.complete(id, true) assertEquals(0, registry.pending.size) diff --git a/src/androidTest/java/com/nextcloud/client/files/downloader/TransferManagerConnectionTest.kt b/src/androidTest/java/com/nextcloud/client/files/downloader/TransferManagerConnectionTest.kt index a1fe430ae8..d5bbe70705 100644 --- a/src/androidTest/java/com/nextcloud/client/files/downloader/TransferManagerConnectionTest.kt +++ b/src/androidTest/java/com/nextcloud/client/files/downloader/TransferManagerConnectionTest.kt @@ -54,10 +54,10 @@ class TransferManagerConnectionTest { lateinit var secondStatusListener: (TransferManager.Status) -> Unit @MockK - lateinit var binder: DownloaderService.Binder + lateinit var binder: FileTransferService.Binder val file get() = OCFile("/path") - val componentName = ComponentName("", DownloaderService::class.java.simpleName) + val componentName = ComponentName("", FileTransferService::class.java.simpleName) val user = MockUser() @Before @@ -128,11 +128,11 @@ class TransferManagerConnectionTest { connection.registerTransferListener(firstDownloadListener) connection.registerTransferListener(secondDownloadListener) - val request1 = Request(user, file) + val request1 = DownloadRequest(user, file) connection.enqueue(request1) val download1 = Transfer(request1.uuid, TransferState.RUNNING, 50, request1.file, request1) - val request2 = Request(user, file) + val request2 = DownloadRequest(user, file) connection.enqueue(request2) val download2 = Transfer(request2.uuid, TransferState.RUNNING, 50, request2.file, request1) @@ -223,7 +223,7 @@ class TransferManagerConnectionTest { // GIVEN // not bound // some downloads requested without listener - val request = Request(user, file) + val request = DownloadRequest(user, file) connection.enqueue(request) val download = Transfer(request.uuid, TransferState.RUNNING, 50, request.file, request) connection.registerTransferListener(firstDownloadListener) diff --git a/src/androidTest/java/com/nextcloud/client/files/downloader/TransferManagerTest.kt b/src/androidTest/java/com/nextcloud/client/files/downloader/TransferManagerTest.kt index 4ccefc9f52..aa69648e8b 100644 --- a/src/androidTest/java/com/nextcloud/client/files/downloader/TransferManagerTest.kt +++ b/src/androidTest/java/com/nextcloud/client/files/downloader/TransferManagerTest.kt @@ -59,7 +59,10 @@ class TransferManagerTest { lateinit var client: OwnCloudClient @MockK - lateinit var mockTaskFactory: DownloadTask.Factory + lateinit var mockDownloadTaskFactory: DownloadTask.Factory + + @MockK + lateinit var mockUploadTaskFactory: UploadTask.Factory /** * All task mock functions created during test run are @@ -88,11 +91,12 @@ class TransferManagerTest { runner = ManualAsyncRunner() transferManager = TransferManagerImpl( runner = runner, - taskFactory = mockTaskFactory, + downloadTaskFactory = mockDownloadTaskFactory, + uploadTaskFactory = mockUploadTaskFactory, threads = MAX_TRANSFER_THREADS ) downloadTaskResult = true - every { mockTaskFactory.create() } answers { createMockTask() } + every { mockDownloadTaskFactory.create() } answers { createMockTask() } } private fun createMockTask(): DownloadTask { @@ -119,7 +123,7 @@ class TransferManagerTest { // WHEN // download is enqueued val file = OCFile("/path") - val request = Request(user, file) + val request = DownloadRequest(user, file) transferManager.enqueue(request) // THEN @@ -134,7 +138,7 @@ class TransferManagerTest { // downloader is downloading max simultaneous files for (i in 0 until MAX_TRANSFER_THREADS) { val file = OCFile("/running/download/path/$i") - val request = Request(user, file) + val request = DownloadRequest(user, file) transferManager.enqueue(request) val runningDownload = transferManager.getTransfer(request.uuid) assertEquals(runningDownload?.state, TransferState.RUNNING) @@ -143,7 +147,7 @@ class TransferManagerTest { // WHEN // another download is enqueued val file = OCFile("/path") - val request = Request(user, file) + val request = DownloadRequest(user, file) transferManager.enqueue(request) // THEN @@ -167,7 +171,7 @@ class TransferManagerTest { // download is being observed val downloadUpdates = mutableListOf() transferManager.registerTransferListener { downloadUpdates.add(it) } - transferManager.enqueue(Request(user, file)) + transferManager.enqueue(DownloadRequest(user, file)) // WHEN // download task finishes successfully @@ -186,7 +190,7 @@ class TransferManagerTest { // download is being observed val downloadUpdates = mutableListOf() transferManager.registerTransferListener { downloadUpdates.add(it) } - transferManager.enqueue(Request(user, file)) + transferManager.enqueue(DownloadRequest(user, file)) // WHEN // download task fails @@ -205,7 +209,7 @@ class TransferManagerTest { // download is running val downloadUpdates = mutableListOf() transferManager.registerTransferListener { downloadUpdates.add(it) } - transferManager.enqueue(Request(user, file)) + transferManager.enqueue(DownloadRequest(user, file)) // WHEN // download progress updated 4 times before completion @@ -233,7 +237,7 @@ class TransferManagerTest { // WHEN // multiple downloads are enqueued for (i in 0 until MAX_TRANSFER_THREADS * 2) { - transferManager.enqueue(Request(user, file)) + transferManager.enqueue(DownloadRequest(user, file)) } // THEN @@ -252,7 +256,7 @@ class TransferManagerTest { // WHEN // download is enqueued val file = OCFile("/path/to/file") - val request = Request(user, file) + val request = DownloadRequest(user, file) transferManager.enqueue(request) // THEN @@ -265,7 +269,7 @@ class TransferManagerTest { // GIVEN // a download is in progress val file = OCFile("/path/to/file") - val request = Request(user, file) + val request = DownloadRequest(user, file) transferManager.enqueue(request) assertTrue(transferManager.isRunning) diff --git a/src/androidTest/java/com/owncloud/android/AbstractIT.java b/src/androidTest/java/com/owncloud/android/AbstractIT.java index ba0f5f806c..cb99c1407d 100644 --- a/src/androidTest/java/com/owncloud/android/AbstractIT.java +++ b/src/androidTest/java/com/owncloud/android/AbstractIT.java @@ -29,6 +29,7 @@ import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.db.OCUpload; import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.files.services.NameCollisionPolicy; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientFactory; import com.owncloud.android.lib.common.accounts.AccountUtils; @@ -354,17 +355,17 @@ public abstract class AbstractIT { targetContext.getContentResolver()); UploadFileOperation newUpload = new UploadFileOperation( - uploadsStorageManager, - connectivityServiceMock, - powerManagementServiceMock, - user, - null, - ocUpload, - FileUploader.NameCollisionPolicy.DEFAULT, - FileUploader.LOCAL_BEHAVIOUR_COPY, - targetContext, - false, - false + uploadsStorageManager, + connectivityServiceMock, + powerManagementServiceMock, + user, + null, + ocUpload, + NameCollisionPolicy.DEFAULT, + FileUploader.LOCAL_BEHAVIOUR_COPY, + targetContext, + false, + false ); newUpload.addRenameUploadListener(() -> { // dummy diff --git a/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java b/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java index 5d3cb7151f..c32fe022c3 100644 --- a/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java +++ b/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java @@ -20,6 +20,7 @@ import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.db.OCUpload; import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.files.services.NameCollisionPolicy; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientFactory; import com.owncloud.android.lib.common.accounts.AccountUtils; @@ -202,17 +203,17 @@ public abstract class AbstractOnServerIT extends AbstractIT { targetContext.getContentResolver()); UploadFileOperation newUpload = new UploadFileOperation( - uploadsStorageManager, - connectivityServiceMock, - powerManagementServiceMock, - user, - null, - ocUpload, - FileUploader.NameCollisionPolicy.DEFAULT, - localBehaviour, - targetContext, - false, - false + uploadsStorageManager, + connectivityServiceMock, + powerManagementServiceMock, + user, + null, + ocUpload, + NameCollisionPolicy.DEFAULT, + localBehaviour, + targetContext, + false, + false ); newUpload.addRenameUploadListener(() -> { // dummy diff --git a/src/androidTest/java/com/owncloud/android/UploadIT.java b/src/androidTest/java/com/owncloud/android/UploadIT.java index a2582e1542..6ead810bbd 100644 --- a/src/androidTest/java/com/owncloud/android/UploadIT.java +++ b/src/androidTest/java/com/owncloud/android/UploadIT.java @@ -30,6 +30,7 @@ import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.db.OCUpload; import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.files.services.NameCollisionPolicy; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.operations.RefreshFolderOperation; import com.owncloud.android.operations.RemoveFileOperation; @@ -223,17 +224,17 @@ public class UploadIT extends AbstractOnServerIT { ocUpload.setWhileChargingOnly(true); UploadFileOperation newUpload = new UploadFileOperation( - uploadsStorageManager, - connectivityServiceMock, - powerManagementServiceMock, - user, - null, - ocUpload, - FileUploader.NameCollisionPolicy.DEFAULT, - FileUploader.LOCAL_BEHAVIOUR_COPY, - targetContext, - false, - true + uploadsStorageManager, + connectivityServiceMock, + powerManagementServiceMock, + user, + null, + ocUpload, + NameCollisionPolicy.DEFAULT, + FileUploader.LOCAL_BEHAVIOUR_COPY, + targetContext, + false, + true ); newUpload.setRemoteFolderToBeCreated(); newUpload.addRenameUploadListener(() -> { @@ -270,17 +271,17 @@ public class UploadIT extends AbstractOnServerIT { ocUpload.setWhileChargingOnly(true); UploadFileOperation newUpload = new UploadFileOperation( - uploadsStorageManager, - connectivityServiceMock, - powerManagementServiceMock, - user, - null, - ocUpload, - FileUploader.NameCollisionPolicy.DEFAULT, - FileUploader.LOCAL_BEHAVIOUR_COPY, - targetContext, - false, - true + uploadsStorageManager, + connectivityServiceMock, + powerManagementServiceMock, + user, + null, + ocUpload, + NameCollisionPolicy.DEFAULT, + FileUploader.LOCAL_BEHAVIOUR_COPY, + targetContext, + false, + true ); newUpload.setRemoteFolderToBeCreated(); newUpload.addRenameUploadListener(() -> { @@ -309,17 +310,17 @@ public class UploadIT extends AbstractOnServerIT { ocUpload.setUseWifiOnly(true); UploadFileOperation newUpload = new UploadFileOperation( - uploadsStorageManager, - connectivityServiceMock, - powerManagementServiceMock, - user, - null, - ocUpload, - FileUploader.NameCollisionPolicy.DEFAULT, - FileUploader.LOCAL_BEHAVIOUR_COPY, - targetContext, - true, - false + uploadsStorageManager, + connectivityServiceMock, + powerManagementServiceMock, + user, + null, + ocUpload, + NameCollisionPolicy.DEFAULT, + FileUploader.LOCAL_BEHAVIOUR_COPY, + targetContext, + true, + false ); newUpload.setRemoteFolderToBeCreated(); newUpload.addRenameUploadListener(() -> { @@ -338,17 +339,17 @@ public class UploadIT extends AbstractOnServerIT { ocUpload.setWhileChargingOnly(true); UploadFileOperation newUpload = new UploadFileOperation( - uploadsStorageManager, - connectivityServiceMock, - powerManagementServiceMock, - user, - null, - ocUpload, - FileUploader.NameCollisionPolicy.DEFAULT, - FileUploader.LOCAL_BEHAVIOUR_COPY, - targetContext, - true, - false + uploadsStorageManager, + connectivityServiceMock, + powerManagementServiceMock, + user, + null, + ocUpload, + NameCollisionPolicy.DEFAULT, + FileUploader.LOCAL_BEHAVIOUR_COPY, + targetContext, + true, + false ); newUpload.setRemoteFolderToBeCreated(); newUpload.addRenameUploadListener(() -> { @@ -386,17 +387,17 @@ public class UploadIT extends AbstractOnServerIT { ocUpload.setUseWifiOnly(true); UploadFileOperation newUpload = new UploadFileOperation( - uploadsStorageManager, - connectivityServiceMock, - powerManagementServiceMock, - user, - null, - ocUpload, - FileUploader.NameCollisionPolicy.DEFAULT, - FileUploader.LOCAL_BEHAVIOUR_COPY, - targetContext, - true, - false + uploadsStorageManager, + connectivityServiceMock, + powerManagementServiceMock, + user, + null, + ocUpload, + NameCollisionPolicy.DEFAULT, + FileUploader.LOCAL_BEHAVIOUR_COPY, + targetContext, + true, + false ); newUpload.setRemoteFolderToBeCreated(); newUpload.addRenameUploadListener(() -> { diff --git a/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.java b/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.java index b04eeab3b6..17e9530e69 100644 --- a/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.java +++ b/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.java @@ -13,7 +13,7 @@ import com.owncloud.android.AbstractIT; import com.owncloud.android.MainApp; import com.owncloud.android.db.OCUpload; import com.owncloud.android.db.UploadResult; -import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.files.services.NameCollisionPolicy; import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.operations.UploadFileOperation; @@ -191,7 +191,7 @@ public class UploadStorageManagerTest extends AbstractIT { upload.setFileSize(new Random().nextInt(20000) * 10000); upload.setUploadStatus(UploadsStorageManager.UploadStatus.UPLOAD_IN_PROGRESS); upload.setLocalAction(2); - upload.setNameCollisionPolicy(FileUploader.NameCollisionPolicy.ASK_USER); + upload.setNameCollisionPolicy(NameCollisionPolicy.ASK_USER); upload.setCreateRemoteFolder(false); upload.setUploadEndTimestamp(System.currentTimeMillis()); upload.setLastResult(UploadResult.DELAYED_FOR_WIFI); diff --git a/src/androidTest/java/com/owncloud/android/files/services/FileUploaderIT.kt b/src/androidTest/java/com/owncloud/android/files/services/FileUploaderIT.kt index 497ce518f3..5339bfb760 100644 --- a/src/androidTest/java/com/owncloud/android/files/services/FileUploaderIT.kt +++ b/src/androidTest/java/com/owncloud/android/files/services/FileUploaderIT.kt @@ -145,7 +145,7 @@ class FileUploaderIT : AbstractOnServerIT() { UploadFileOperation.CREATED_BY_USER, false, false, - FileUploader.NameCollisionPolicy.DEFAULT + NameCollisionPolicy.DEFAULT ) longSleep() @@ -163,7 +163,7 @@ class FileUploaderIT : AbstractOnServerIT() { account, ocFile2, FileUploader.LOCAL_BEHAVIOUR_COPY, - FileUploader.NameCollisionPolicy.OVERWRITE + NameCollisionPolicy.OVERWRITE ) shortSleep() @@ -192,7 +192,7 @@ class FileUploaderIT : AbstractOnServerIT() { user, null, ocUpload, - FileUploader.NameCollisionPolicy.DEFAULT, + NameCollisionPolicy.DEFAULT, FileUploader.LOCAL_BEHAVIOUR_COPY, targetContext, false, @@ -219,7 +219,7 @@ class FileUploaderIT : AbstractOnServerIT() { user, null, ocUpload2, - FileUploader.NameCollisionPolicy.RENAME, + NameCollisionPolicy.RENAME, FileUploader.LOCAL_BEHAVIOUR_COPY, targetContext, false, @@ -262,7 +262,7 @@ class FileUploaderIT : AbstractOnServerIT() { UploadFileOperation.CREATED_BY_USER, false, false, - FileUploader.NameCollisionPolicy.DEFAULT + NameCollisionPolicy.DEFAULT ) longSleep() @@ -280,7 +280,7 @@ class FileUploaderIT : AbstractOnServerIT() { account, ocFile2, FileUploader.LOCAL_BEHAVIOUR_COPY, - FileUploader.NameCollisionPolicy.RENAME + NameCollisionPolicy.RENAME ) shortSleep() @@ -312,7 +312,7 @@ class FileUploaderIT : AbstractOnServerIT() { user, null, ocUpload, - FileUploader.NameCollisionPolicy.DEFAULT, + NameCollisionPolicy.DEFAULT, FileUploader.LOCAL_BEHAVIOUR_COPY, targetContext, false, @@ -338,7 +338,7 @@ class FileUploaderIT : AbstractOnServerIT() { user, null, ocUpload2, - FileUploader.NameCollisionPolicy.CANCEL, + NameCollisionPolicy.CANCEL, FileUploader.LOCAL_BEHAVIOUR_COPY, targetContext, false, @@ -371,7 +371,7 @@ class FileUploaderIT : AbstractOnServerIT() { UploadFileOperation.CREATED_BY_USER, false, false, - FileUploader.NameCollisionPolicy.DEFAULT + NameCollisionPolicy.DEFAULT ) longSleep() @@ -389,7 +389,7 @@ class FileUploaderIT : AbstractOnServerIT() { account, ocFile2, FileUploader.LOCAL_BEHAVIOUR_COPY, - FileUploader.NameCollisionPolicy.CANCEL + NameCollisionPolicy.CANCEL ) shortSleep() @@ -416,7 +416,7 @@ class FileUploaderIT : AbstractOnServerIT() { user, null, ocUpload, - FileUploader.NameCollisionPolicy.CANCEL, + NameCollisionPolicy.CANCEL, FileUploader.LOCAL_BEHAVIOUR_COPY, targetContext, false, @@ -441,7 +441,7 @@ class FileUploaderIT : AbstractOnServerIT() { user, null, ocUpload2, - FileUploader.NameCollisionPolicy.CANCEL, + NameCollisionPolicy.CANCEL, FileUploader.LOCAL_BEHAVIOUR_COPY, targetContext, false, @@ -476,7 +476,7 @@ class FileUploaderIT : AbstractOnServerIT() { UploadFileOperation.CREATED_BY_USER, false, false, - FileUploader.NameCollisionPolicy.DEFAULT + NameCollisionPolicy.DEFAULT ) longSleep() @@ -494,7 +494,7 @@ class FileUploaderIT : AbstractOnServerIT() { account, ocFile2, FileUploader.LOCAL_BEHAVIOUR_COPY, - FileUploader.NameCollisionPolicy.CANCEL + NameCollisionPolicy.CANCEL ) shortSleep() diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 10a7e9956a..63cc5400d0 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -322,7 +322,7 @@ - + diff --git a/src/main/java/com/nextcloud/client/di/ComponentsModule.java b/src/main/java/com/nextcloud/client/di/ComponentsModule.java index 9e17d19704..aedc673284 100644 --- a/src/main/java/com/nextcloud/client/di/ComponentsModule.java +++ b/src/main/java/com/nextcloud/client/di/ComponentsModule.java @@ -21,7 +21,7 @@ package com.nextcloud.client.di; import com.nextcloud.client.etm.EtmActivity; -import com.nextcloud.client.files.downloader.DownloaderService; +import com.nextcloud.client.files.downloader.FileTransferService; import com.nextcloud.client.jobs.NotificationWork; import com.nextcloud.client.logger.ui.LogsActivity; import com.nextcloud.client.media.PlayerService; @@ -207,5 +207,5 @@ abstract class ComponentsModule { @ContributesAndroidInjector abstract PlayerService playerService(); @ContributesAndroidInjector - abstract DownloaderService fileDownloaderService(); + abstract FileTransferService fileDownloaderService(); } diff --git a/src/main/java/com/nextcloud/client/etm/EtmViewModel.kt b/src/main/java/com/nextcloud/client/etm/EtmViewModel.kt index 23d58b097c..8c367cbe41 100644 --- a/src/main/java/com/nextcloud/client/etm/EtmViewModel.kt +++ b/src/main/java/com/nextcloud/client/etm/EtmViewModel.kt @@ -32,7 +32,7 @@ import com.nextcloud.client.account.User import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.etm.pages.EtmAccountsFragment import com.nextcloud.client.etm.pages.EtmBackgroundJobsFragment -import com.nextcloud.client.etm.pages.EtmDownloaderFragment +import com.nextcloud.client.etm.pages.EtmFileTransferFragment import com.nextcloud.client.etm.pages.EtmMigrations import com.nextcloud.client.etm.pages.EtmPreferencesFragment import com.nextcloud.client.files.downloader.TransferManagerConnection @@ -104,12 +104,12 @@ class EtmViewModel @Inject constructor( pageClass = EtmMigrations::class ), EtmMenuEntry( - iconRes = R.drawable.ic_download_grey600, - titleRes = R.string.etm_downloader, - pageClass = EtmDownloaderFragment::class + iconRes = R.drawable.ic_cloud_download, + titleRes = R.string.etm_transfer, + pageClass = EtmFileTransferFragment::class ) ) - val downloaderConnection = TransferManagerConnection(context, accountManager.user) + val transferManagerConnection = TransferManagerConnection(context, accountManager.user) val preferences: Map get() { return defaultPreferences.all diff --git a/src/main/java/com/nextcloud/client/etm/pages/EtmDownloaderFragment.kt b/src/main/java/com/nextcloud/client/etm/pages/EtmFileTransferFragment.kt similarity index 58% rename from src/main/java/com/nextcloud/client/etm/pages/EtmDownloaderFragment.kt rename to src/main/java/com/nextcloud/client/etm/pages/EtmFileTransferFragment.kt index 18d2627f43..6f3c3b8c40 100644 --- a/src/main/java/com/nextcloud/client/etm/pages/EtmDownloaderFragment.kt +++ b/src/main/java/com/nextcloud/client/etm/pages/EtmFileTransferFragment.kt @@ -7,19 +7,21 @@ import android.view.MenuInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup +import android.widget.ImageView import android.widget.TextView import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.nextcloud.client.etm.EtmBaseFragment -import com.nextcloud.client.files.downloader.Direction +import com.nextcloud.client.files.downloader.DownloadRequest import com.nextcloud.client.files.downloader.Transfer import com.nextcloud.client.files.downloader.TransferManager -import com.nextcloud.client.files.downloader.Request +import com.nextcloud.client.files.downloader.UploadRequest import com.owncloud.android.R import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.db.OCUpload -class EtmDownloaderFragment : EtmBaseFragment() { +class EtmFileTransferFragment : EtmBaseFragment() { companion object { private const val TEST_DOWNLOAD_DUMMY_PATH = "/test/dummy_file.txt" @@ -28,12 +30,14 @@ class EtmDownloaderFragment : EtmBaseFragment() { class Adapter(private val inflater: LayoutInflater) : RecyclerView.Adapter() { class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { - val uuid = view.findViewById(R.id.etm_download_uuid) - val path = view.findViewById(R.id.etm_download_path) - val user = view.findViewById(R.id.etm_download_user) - val state = view.findViewById(R.id.etm_download_state) - val progress = view.findViewById(R.id.etm_download_progress) - private val progressRow = view.findViewById(R.id.etm_download_progress_row) + val type = view.findViewById(R.id.etm_transfer_type) + val typeIcon = view.findViewById(R.id.etm_transfer_type_icon) + val uuid = view.findViewById(R.id.etm_transfer_uuid) + val path = view.findViewById(R.id.etm_transfer_remote_path) + val user = view.findViewById(R.id.etm_transfer_user) + val state = view.findViewById(R.id.etm_transfer_state) + val progress = view.findViewById(R.id.etm_transfer_progress) + private val progressRow = view.findViewById(R.id.etm_transfer_progress_row) var progressEnabled: Boolean = progressRow.visibility == View.VISIBLE get() { @@ -49,31 +53,44 @@ class EtmDownloaderFragment : EtmBaseFragment() { } } - private var downloads = listOf() + private var transfers = listOf() fun setStatus(status: TransferManager.Status) { - downloads = listOf(status.pending, status.running, status.completed).flatten().reversed() + transfers = listOf(status.pending, status.running, status.completed).flatten().reversed() notifyDataSetChanged() } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val view = inflater.inflate(R.layout.etm_download_list_item, parent, false) + val view = inflater.inflate(R.layout.etm_transfer_list_item, parent, false) return ViewHolder(view) } override fun getItemCount(): Int { - return downloads.size + return transfers.size } override fun onBindViewHolder(vh: ViewHolder, position: Int) { - val download = downloads[position] - vh.uuid.text = download.uuid.toString() - vh.path.text = download.request.file.remotePath - vh.user.text = download.request.user.accountName - vh.state.text = download.state.toString() - if (download.progress >= 0) { + val transfer = transfers[position] + + val transferTypeStrId = when (transfer.request) { + is DownloadRequest -> R.string.etm_transfer_type_download + is UploadRequest -> R.string.etm_transfer_type_upload + } + + val transferTypeIconId = when (transfer.request) { + is DownloadRequest -> R.drawable.ic_cloud_download + is UploadRequest -> R.drawable.ic_cloud_upload + } + + vh.type.setText(transferTypeStrId) + vh.typeIcon.setImageResource(transferTypeIconId) + vh.uuid.text = transfer.uuid.toString() + vh.path.text = transfer.request.file.remotePath + vh.user.text = transfer.request.user.accountName + vh.state.text = transfer.state.toString() + if (transfer.progress >= 0) { vh.progressEnabled = true - vh.progress.text = download.progress.toString() + vh.progress.text = transfer.progress.toString() } else { vh.progressEnabled = false } @@ -100,18 +117,18 @@ class EtmDownloaderFragment : EtmBaseFragment() { override fun onResume() { super.onResume() - vm.downloaderConnection.bind() - vm.downloaderConnection.registerStatusListener(this::onDownloaderStatusChanged) + vm.transferManagerConnection.bind() + vm.transferManagerConnection.registerStatusListener(this::onDownloaderStatusChanged) } override fun onPause() { super.onPause() - vm.downloaderConnection.unbind() + vm.transferManagerConnection.unbind() } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { super.onCreateOptionsMenu(menu, inflater) - inflater.inflate(R.menu.fragment_etm_downloader, menu) + inflater.inflate(R.menu.fragment_etm_file_transfer, menu) } override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -119,18 +136,29 @@ class EtmDownloaderFragment : EtmBaseFragment() { R.id.etm_test_download -> { scheduleTestDownload(); true } + R.id.etm_test_upload -> { + scheduleTestUpload(); true + } else -> super.onOptionsItemSelected(item) } } private fun scheduleTestDownload() { - val request = Request( + val request = DownloadRequest( vm.currentUser, OCFile(TEST_DOWNLOAD_DUMMY_PATH), - Direction.DOWNLOAD, true ) - vm.downloaderConnection.enqueue(request) + vm.transferManagerConnection.enqueue(request) + } + + private fun scheduleTestUpload() { + val request = UploadRequest( + vm.currentUser, + OCUpload(TEST_DOWNLOAD_DUMMY_PATH, TEST_DOWNLOAD_DUMMY_PATH, vm.currentUser.accountName), + true + ) + vm.transferManagerConnection.enqueue(request) } private fun onDownloaderStatusChanged(status: TransferManager.Status) { diff --git a/src/main/java/com/nextcloud/client/files/downloader/DownloadTask.kt b/src/main/java/com/nextcloud/client/files/downloader/DownloadTask.kt index 1698785dfe..4a8588eb66 100644 --- a/src/main/java/com/nextcloud/client/files/downloader/DownloadTask.kt +++ b/src/main/java/com/nextcloud/client/files/downloader/DownloadTask.kt @@ -62,7 +62,7 @@ class DownloadTask( } } - fun download(request: Request, progress: (Int) -> Unit, isCancelled: IsCancelled): Result { + fun download(request: DownloadRequest, progress: (Int) -> Unit, isCancelled: IsCancelled): Result { val op = DownloadFileOperation(request.user.toPlatformAccount(), request.file, context) val client = clientProvider.invoke() val result = op.execute(client) diff --git a/src/main/java/com/nextcloud/client/files/downloader/DownloaderService.kt b/src/main/java/com/nextcloud/client/files/downloader/FileTransferService.kt similarity index 64% rename from src/main/java/com/nextcloud/client/files/downloader/DownloaderService.kt rename to src/main/java/com/nextcloud/client/files/downloader/FileTransferService.kt index 09a64f8d81..5a34b284a7 100644 --- a/src/main/java/com/nextcloud/client/files/downloader/DownloaderService.kt +++ b/src/main/java/com/nextcloud/client/files/downloader/FileTransferService.kt @@ -2,7 +2,7 @@ * Nextcloud Android client application * * @author Chris Narkiewicz - * Copyright (C) 2020 Chris Narkiewicz + * Copyright (C) 2021 Chris Narkiewicz * * 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 @@ -26,30 +26,33 @@ import android.os.IBinder import com.nextcloud.client.account.User import com.nextcloud.client.core.AsyncRunner import com.nextcloud.client.core.LocalBinder +import com.nextcloud.client.device.PowerManagementService import com.nextcloud.client.logger.Logger import com.nextcloud.client.network.ClientFactory +import com.nextcloud.client.network.ConnectivityService import com.nextcloud.client.notifications.AppNotificationManager +import com.owncloud.android.datamodel.UploadsStorageManager import dagger.android.AndroidInjection import javax.inject.Inject import javax.inject.Named -class DownloaderService : Service() { +class FileTransferService : Service() { companion object { const val TAG = "DownloaderService" - const val ACTION_DOWNLOAD = "download" + const val ACTION_TRANSFER = "transfer" const val EXTRA_REQUEST = "request" const val EXTRA_USER = "user" fun createBindIntent(context: Context, user: User): Intent { - return Intent(context, DownloaderService::class.java).apply { + return Intent(context, FileTransferService::class.java).apply { putExtra(EXTRA_USER, user) } } - fun createDownloadIntent(context: Context, request: Request): Intent { - return Intent(context, DownloaderService::class.java).apply { - action = ACTION_DOWNLOAD + fun createTransferRequestIntent(context: Context, request: Request): Intent { + return Intent(context, FileTransferService::class.java).apply { + action = ACTION_TRANSFER putExtra(EXTRA_REQUEST, request) } } @@ -60,8 +63,8 @@ class DownloaderService : Service() { */ class Binder( downloader: TransferManagerImpl, - service: DownloaderService - ) : LocalBinder(service), + service: FileTransferService + ) : LocalBinder(service), TransferManager by downloader @Inject @@ -77,6 +80,15 @@ class DownloaderService : Service() { @Inject lateinit var logger: Logger + @Inject + lateinit var uploadsStorageManager: UploadsStorageManager + + @Inject + lateinit var connectivityService: ConnectivityService + + @Inject + lateinit var powerManagementService: PowerManagementService + val isRunning: Boolean get() = downloaders.any { it.value.isRunning } private val downloaders: MutableMap = mutableMapOf() @@ -86,22 +98,22 @@ class DownloaderService : Service() { } override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { - if (intent.action != ACTION_DOWNLOAD) { + if (intent.action != ACTION_TRANSFER) { return START_NOT_STICKY } if (!isRunning) { startForeground( - AppNotificationManager.DOWNLOAD_NOTIFICATION_ID, + AppNotificationManager.TRANSFER_NOTIFICATION_ID, notificationsManager.buildDownloadServiceForegroundNotification() ) } val request = intent.getParcelableExtra(EXTRA_REQUEST) as Request - val downloader = getDownloader(request.user) - downloader.enqueue(request) + val transferManager = getTransferManager(request.user) + transferManager.enqueue(request) - logger.d(TAG, "Enqueued new download: ${request.uuid} ${request.file.remotePath}") + logger.d(TAG, "Enqueued new transfer: ${request.uuid} ${request.file.remotePath}") return START_NOT_STICKY } @@ -109,25 +121,31 @@ class DownloaderService : Service() { override fun onBind(intent: Intent?): IBinder? { val user = intent?.getParcelableExtra(EXTRA_USER) if (user != null) { - return Binder(getDownloader(user), this) + return Binder(getTransferManager(user), this) } else { return null } } - private fun onDownloadUpdate(transfer: Transfer) { + private fun onTransferUpdate(transfer: Transfer) { if (!isRunning) { logger.d(TAG, "All downloads completed") - notificationsManager.cancelDownloadProgress() + notificationsManager.cancelTransferNotification() stopForeground(true) stopSelf() - } else { - notificationsManager.postDownloadProgress( + } else if (transfer.direction == Direction.DOWNLOAD) { + notificationsManager.postDownloadTransferProgress( fileOwner = transfer.request.user, file = transfer.request.file, progress = transfer.progress, allowPreview = !transfer.request.test ) + } else if (transfer.direction == Direction.UPLOAD) { + notificationsManager.postUploadTransferProgress( + fileOwner = transfer.request.user, + file = transfer.request.file, + progress = transfer.progress + ) } } @@ -136,7 +154,7 @@ class DownloaderService : Service() { logger.d(TAG, "Stopping downloader service") } - private fun getDownloader(user: User): TransferManagerImpl { + private fun getTransferManager(user: User): TransferManagerImpl { val existingDownloader = downloaders[user.accountName] return if (existingDownloader != null) { existingDownloader @@ -146,8 +164,15 @@ class DownloaderService : Service() { { clientFactory.create(user) }, contentResolver ) - val newDownloader = TransferManagerImpl(runner, downloadTaskFactory) - newDownloader.registerTransferListener(this::onDownloadUpdate) + val uploadTaskFactory = UploadTask.Factory( + applicationContext, + uploadsStorageManager, + connectivityService, + powerManagementService, + { clientFactory.create(user) } + ) + val newDownloader = TransferManagerImpl(runner, downloadTaskFactory, uploadTaskFactory) + newDownloader.registerTransferListener(this::onTransferUpdate) downloaders[user.accountName] = newDownloader newDownloader } diff --git a/src/main/java/com/nextcloud/client/files/downloader/PostUploadAction.kt b/src/main/java/com/nextcloud/client/files/downloader/PostUploadAction.kt new file mode 100644 index 0000000000..ab1d6edeba --- /dev/null +++ b/src/main/java/com/nextcloud/client/files/downloader/PostUploadAction.kt @@ -0,0 +1,29 @@ +/* + * Nextcloud Android client application + * + * @author Chris Narkiewicz + * Copyright (C) 2021 Chris Narkiewicz + * + * 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 . + */ +package com.nextcloud.client.files.downloader + +import com.owncloud.android.files.services.FileUploader + +enum class PostUploadAction(val value: Int) { + NONE(FileUploader.LOCAL_BEHAVIOUR_FORGET), + COPY_TO_APP(FileUploader.LOCAL_BEHAVIOUR_COPY), + MOVE_TO_APP(FileUploader.LOCAL_BEHAVIOUR_MOVE), + DELETE_SOURCE(FileUploader.LOCAL_BEHAVIOUR_DELETE); +} diff --git a/src/main/java/com/nextcloud/client/files/downloader/Request.kt b/src/main/java/com/nextcloud/client/files/downloader/Request.kt index f27d8528d3..c88e4afad5 100644 --- a/src/main/java/com/nextcloud/client/files/downloader/Request.kt +++ b/src/main/java/com/nextcloud/client/files/downloader/Request.kt @@ -2,7 +2,7 @@ * Nextcloud Android client application * * @author Chris Narkiewicz - * Copyright (C) 2020 Chris Narkiewicz + * Copyright (C) 2021 Chris Narkiewicz * * 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 @@ -23,8 +23,19 @@ import android.os.Parcel import android.os.Parcelable import com.nextcloud.client.account.User import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.datamodel.UploadsStorageManager +import com.owncloud.android.db.OCUpload +import com.owncloud.android.files.services.NameCollisionPolicy import java.util.UUID +sealed class Request( + val user: User, + val file: OCFile, + val uuid: UUID, + val type: Direction, + val test: Boolean +) : Parcelable + /** * Transfer request. This class should collect all information * required to trigger transfer operation. @@ -38,26 +49,24 @@ import java.util.UUID * @property uuid Unique request identifier; this identifier can be set in [Transfer] * @property dummy if true, this requests a dummy test transfer; no real file transfer will occur */ -class Request internal constructor( - val user: User, - val file: OCFile, - val uuid: UUID, - val type: Direction = Direction.DOWNLOAD, - val test: Boolean = false -) : Parcelable { +class DownloadRequest internal constructor( + user: User, + file: OCFile, + uuid: UUID, + type: Direction, + test: Boolean = false +) : Request(user, file, uuid, type, test) { constructor( user: User, file: OCFile, - type: Direction = Direction.DOWNLOAD - ) : this(user, file, UUID.randomUUID(), type) + ) : this(user, file, UUID.randomUUID(), Direction.DOWNLOAD) constructor( user: User, file: OCFile, - type: Direction, test: Boolean - ) : this(user, file, UUID.randomUUID(), type, test) + ) : this(user, file, UUID.randomUUID(), Direction.DOWNLOAD, test) constructor(parcel: Parcel) : this( user = parcel.readParcelable(User::class.java.classLoader) as User, @@ -79,13 +88,144 @@ class Request internal constructor( return 0 } - companion object CREATOR : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): Request { - return Request(parcel) + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): DownloadRequest { + return DownloadRequest(parcel) } - override fun newArray(size: Int): Array { + override fun newArray(size: Int): Array { return arrayOfNulls(size) } } } + +@Suppress("LongParameterList") +class UploadRequest internal constructor( + user: User, + file: OCFile, + val upload: OCUpload, + uuid: UUID, + type: Direction, + test: Boolean, +) : Request(user, file, uuid, type, test) { + + constructor( + user: User, + upload: OCUpload, + test: Boolean + ) : this( + user, + OCFile(upload.remotePath).apply { + storagePath = upload.localPath + fileLength = upload.fileSize + }, + upload, + UUID.randomUUID(), + Direction.UPLOAD, + test + ) + + constructor( + user: User, + upload: OCUpload + ) : this(user, upload, false) + + constructor(parcel: Parcel) : this( + user = parcel.readParcelable(User::class.java.classLoader) as User, + file = parcel.readParcelable(OCFile::class.java.classLoader) as OCFile, + upload = parcel.readParcelable(OCUpload::class.java.classLoader) as OCUpload, + uuid = parcel.readSerializable() as UUID, + type = parcel.readSerializable() as Direction, + test = parcel.readInt() != 0 + ) + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeParcelable(user, flags) + parcel.writeParcelable(file, flags) + parcel.writeParcelable(upload, flags) + parcel.writeSerializable(uuid) + parcel.writeSerializable(type) + parcel.writeInt(if (test) 1 else 0) + } + + override fun describeContents(): Int { + return 0 + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): UploadRequest { + return UploadRequest(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + + /** + * This class provides a builder pattern with API convenient to be used in Java. + */ + class Builder(private val user: User, private var source: String, private var destination: String) { + private var fileSize: Long = 0 + private var nameConflictPolicy = NameCollisionPolicy.ASK_USER + private var createRemoteFolder = true + private var trigger = UploadTrigger.USER + private var requireWifi = false + private var requireCharging = false + private var postUploadAction = PostUploadAction.NONE + + fun setPaths(source: String, destination: String): Builder { + this.source = source + this.destination = destination + return this + } + + fun setFileSize(fileSize: Long): Builder { + this.fileSize = fileSize + return this + } + + fun setNameConflicPolicy(policy: NameCollisionPolicy): Builder { + this.nameConflictPolicy = policy + return this + } + + fun setCreateRemoteFolder(create: Boolean): Builder { + this.createRemoteFolder = create + return this + } + + fun setTrigger(trigger: UploadTrigger): Builder { + this.trigger = trigger + return this + } + + fun setRequireWifi(require: Boolean): Builder { + this.requireWifi = require + return this + } + + fun setRequireCharging(require: Boolean): Builder { + this.requireCharging = require + return this + } + + fun setPostAction(action: PostUploadAction): Builder { + this.postUploadAction = action + return this + } + + fun build(): Request { + val upload = OCUpload(source, destination, user.accountName) + upload.fileSize = fileSize + upload.nameCollisionPolicy = this.nameConflictPolicy + upload.isCreateRemoteFolder = this.createRemoteFolder + upload.createdBy = this.trigger.value + upload.localAction = this.postUploadAction.value + upload.isUseWifiOnly = this.requireWifi + upload.isWhileChargingOnly = this.requireCharging + upload.uploadStatus = UploadsStorageManager.UploadStatus.UPLOAD_IN_PROGRESS + return UploadRequest(user, upload) + } + } +} diff --git a/src/main/java/com/nextcloud/client/files/downloader/Transfer.kt b/src/main/java/com/nextcloud/client/files/downloader/Transfer.kt index d6abbe35b3..fff6c420f7 100644 --- a/src/main/java/com/nextcloud/client/files/downloader/Transfer.kt +++ b/src/main/java/com/nextcloud/client/files/downloader/Transfer.kt @@ -2,7 +2,7 @@ * Nextcloud Android client application * * @author Chris Narkiewicz - * Copyright (C) 2020 Chris Narkiewicz + * Copyright (C) 2021 Chris Narkiewicz * * 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 @@ -34,6 +34,7 @@ import java.util.UUID * @property progress transfer progress, 0-100 percent * @property file transferred file * @property request initial transfer request + * @property direction transfer direction, download or upload */ data class Transfer( val uuid: UUID, @@ -46,4 +47,9 @@ data class Transfer( * True if download is no longer running, false if it is still being processed. */ val isFinished: Boolean get() = state == TransferState.COMPLETED || state == TransferState.FAILED + + val direction: Direction get() = when (request) { + is DownloadRequest -> Direction.DOWNLOAD + is UploadRequest -> Direction.UPLOAD + } } diff --git a/src/main/java/com/nextcloud/client/files/downloader/TransferManager.kt b/src/main/java/com/nextcloud/client/files/downloader/TransferManager.kt index 6198c6fca9..957e492877 100644 --- a/src/main/java/com/nextcloud/client/files/downloader/TransferManager.kt +++ b/src/main/java/com/nextcloud/client/files/downloader/TransferManager.kt @@ -2,7 +2,7 @@ * Nextcloud Android client application * * @author Chris Narkiewicz - * Copyright (C) 2020 Chris Narkiewicz + * Copyright (C) 2021 Chris Narkiewicz * * 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 @@ -22,6 +22,9 @@ package com.nextcloud.client.files.downloader import com.owncloud.android.datamodel.OCFile import java.util.UUID +/** + * Transfer manager provides API to upload and download files. + */ interface TransferManager { /** @@ -48,7 +51,7 @@ interface TransferManager { val status: Status /** - * Register transfer progress listener. Registration is idempotent - listener can be registered only once. + * Register transfer progress listener. Registration is idempotent - a listener will be registered only once. */ fun registerTransferListener(listener: (Transfer) -> Unit) @@ -58,7 +61,7 @@ interface TransferManager { fun removeTransferListener(listener: (Transfer) -> Unit) /** - * Register transfer manager status listener. Registration is idempotent - listener can be registered only once. + * Register transfer manager status listener. Registration is idempotent - a listener will be registered only once. */ fun registerStatusListener(listener: (Status) -> Unit) diff --git a/src/main/java/com/nextcloud/client/files/downloader/TransferManagerConnection.kt b/src/main/java/com/nextcloud/client/files/downloader/TransferManagerConnection.kt index 46a80b9b24..be2ea7fe45 100644 --- a/src/main/java/com/nextcloud/client/files/downloader/TransferManagerConnection.kt +++ b/src/main/java/com/nextcloud/client/files/downloader/TransferManagerConnection.kt @@ -30,11 +30,11 @@ import java.util.UUID class TransferManagerConnection( context: Context, val user: User -) : LocalConnection(context), TransferManager { +) : LocalConnection(context), TransferManager { private var transferListeners: MutableSet<(Transfer) -> Unit> = mutableSetOf() private var statusListeners: MutableSet<(TransferManager.Status) -> Unit> = mutableSetOf() - private var binder: DownloaderService.Binder? = null + private var binder: FileTransferService.Binder? = null private val transfersRequiringStatusRedelivery: MutableSet = mutableSetOf() override val isRunning: Boolean @@ -48,7 +48,7 @@ class TransferManagerConnection( override fun getTransfer(file: OCFile): Transfer? = binder?.getTransfer(file) override fun enqueue(request: Request) { - val intent = DownloaderService.createDownloadIntent(context, request) + val intent = FileTransferService.createTransferRequestIntent(context, request) context.startService(intent) if (!isConnected && transferListeners.size > 0) { transfersRequiringStatusRedelivery.add(request.uuid) @@ -76,12 +76,12 @@ class TransferManagerConnection( } override fun createBindIntent(): Intent { - return DownloaderService.createBindIntent(context, user) + return FileTransferService.createBindIntent(context, user) } override fun onBound(binder: IBinder) { super.onBound(binder) - this.binder = binder as DownloaderService.Binder + this.binder = binder as FileTransferService.Binder transferListeners.forEach { listener -> binder.registerTransferListener(listener) } diff --git a/src/main/java/com/nextcloud/client/files/downloader/TransferManagerImpl.kt b/src/main/java/com/nextcloud/client/files/downloader/TransferManagerImpl.kt index 3cd9254827..d8857f7db5 100644 --- a/src/main/java/com/nextcloud/client/files/downloader/TransferManagerImpl.kt +++ b/src/main/java/com/nextcloud/client/files/downloader/TransferManagerImpl.kt @@ -24,6 +24,7 @@ import com.nextcloud.client.core.IsCancelled import com.nextcloud.client.core.OnProgressCallback import com.nextcloud.client.core.TaskFunction import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.operations.UploadFileOperation import java.util.UUID /** @@ -33,13 +34,14 @@ import java.util.UUID * in the background. * * @param runner Background task runner. It is important to provide runner that is not shared with UI code. - * @param taskFactory Download task factory + * @param downloadTaskFactory Download task factory * @param threads maximum number of concurrent transfer processes */ @Suppress("LongParameterList") // transfer operations requires those resources class TransferManagerImpl( private val runner: AsyncRunner, - private val taskFactory: DownloadTask.Factory, + private val downloadTaskFactory: DownloadTask.Factory, + private val uploadTaskFactory: UploadTask.Factory, threads: Int = 1 ) : TransferManager { @@ -47,6 +49,7 @@ class TransferManagerImpl( const val PROGRESS_PERCENTAGE_MAX = 100 const val PROGRESS_PERCENTAGE_MIN = 0 const val TEST_DOWNLOAD_PROGRESS_UPDATE_PERIOD_MS = 200L + const val TEST_UPLOAD_PROGRESS_UPDATE_PERIOD_MS = 200L } private val registry = Registry( @@ -92,25 +95,30 @@ class TransferManagerImpl( override fun getTransfer(file: OCFile): Transfer? = registry.getTransfer(file) private fun onStartTransfer(uuid: UUID, request: Request) { - val transferTask = when (request.type) { - Direction.DOWNLOAD -> createDownloadTask(request) - Direction.UPLOAD -> createDownloadTask(request) // plug a hole for now - uploads are not supported + if (request is DownloadRequest) { + runner.postTask( + task = createDownloadTask(request), + onProgress = { progress: Int -> registry.progress(uuid, progress) }, + onResult = { result -> registry.complete(uuid, result.success, result.file); registry.startNext() }, + onError = { registry.complete(uuid, false); registry.startNext() } + ) + } else if (request is UploadRequest) { + runner.postTask( + task = createUploadTask(request), + onProgress = { progress: Int -> registry.progress(uuid, progress) }, + onResult = { result -> registry.complete(uuid, result.success, result.file); registry.startNext() }, + onError = { registry.complete(uuid, false); registry.startNext() } + ) } - runner.postTask( - task = transferTask, - onProgress = { progress: Int -> registry.progress(uuid, progress) }, - onResult = { result -> registry.complete(uuid, result.success, result.file); registry.startNext() }, - onError = { registry.complete(uuid, false); registry.startNext() } - ) } - private fun createDownloadTask(request: Request): TaskFunction { + private fun createDownloadTask(request: DownloadRequest): TaskFunction { return if (request.test) { { progress: OnProgressCallback, isCancelled: IsCancelled -> testDownloadTask(request.file, progress, isCancelled) } } else { - val downloadTask = taskFactory.create() + val downloadTask = downloadTaskFactory.create() val wrapper: TaskFunction = { progress: ((Int) -> Unit), isCancelled -> downloadTask.download(request, progress, isCancelled) } @@ -118,6 +126,25 @@ class TransferManagerImpl( } } + private fun createUploadTask(request: UploadRequest): TaskFunction { + return if (request.test) { + { progress: OnProgressCallback, isCancelled: IsCancelled -> + val file = UploadFileOperation.obtainNewOCFileToUpload( + request.upload.remotePath, + request.upload.localPath, + request.upload.mimeType + ) + testUploadTask(file, progress, isCancelled) + } + } else { + val uploadTask = uploadTaskFactory.create() + val wrapper: TaskFunction = { progress: ((Int) -> Unit), isCancelled -> + uploadTask.upload(request.user, request.upload) + } + wrapper + } + } + private fun onTransferUpdate(transfer: Transfer) { transferListeners.forEach { it.invoke(transfer) } if (statusListeners.isNotEmpty()) { @@ -144,4 +171,23 @@ class TransferManagerImpl( } return DownloadTask.Result(file, true) } + + /** + * Test upload task is used only to simulate upload process without + * any network traffic. It is used for development. + */ + private fun testUploadTask( + file: OCFile, + onProgress: OnProgressCallback, + isCancelled: IsCancelled + ): UploadTask.Result { + for (i in PROGRESS_PERCENTAGE_MIN..PROGRESS_PERCENTAGE_MAX) { + onProgress(i) + if (isCancelled()) { + return UploadTask.Result(file, false) + } + Thread.sleep(TEST_UPLOAD_PROGRESS_UPDATE_PERIOD_MS) + } + return UploadTask.Result(file, true) + } } diff --git a/src/main/java/com/nextcloud/client/files/downloader/UploadTask.kt b/src/main/java/com/nextcloud/client/files/downloader/UploadTask.kt new file mode 100644 index 0000000000..2dfa4f4c04 --- /dev/null +++ b/src/main/java/com/nextcloud/client/files/downloader/UploadTask.kt @@ -0,0 +1,93 @@ +/* + * Nextcloud Android client application + * + * @author Chris Narkiewicz + * Copyright (C) 2021 Chris Narkiewicz + * + * 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 . + */ +package com.nextcloud.client.files.downloader + +import android.content.Context +import com.nextcloud.client.account.User +import com.nextcloud.client.device.PowerManagementService +import com.nextcloud.client.network.ConnectivityService +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.datamodel.UploadsStorageManager +import com.owncloud.android.db.OCUpload +import com.owncloud.android.files.services.NameCollisionPolicy +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.operations.UploadFileOperation + +@Suppress("LongParameterList") +class UploadTask( + private val applicationContext: Context, + private val uploadsStorageManager: UploadsStorageManager, + private val connectivityService: ConnectivityService, + private val powerManagementService: PowerManagementService, + private val clientProvider: () -> OwnCloudClient +) { + + data class Result(val file: OCFile, val success: Boolean) + + /** + * This class is a helper factory to to keep static dependencies + * injection out of the upload task instance. + */ + @Suppress("LongParameterList") + class Factory( + private val applicationContext: Context, + private val uploadsStorageManager: UploadsStorageManager, + private val connectivityService: ConnectivityService, + private val powerManagementService: PowerManagementService, + private val clientProvider: () -> OwnCloudClient + ) { + fun create(): UploadTask { + return UploadTask( + applicationContext, + uploadsStorageManager, + connectivityService, + powerManagementService, + clientProvider + ) + } + } + + fun upload(user: User, upload: OCUpload): Result { + val file = UploadFileOperation.obtainNewOCFileToUpload( + upload.remotePath, + upload.localPath, + upload.mimeType + ) + val op = UploadFileOperation( + uploadsStorageManager, + connectivityService, + powerManagementService, + user, + file, + upload, + NameCollisionPolicy.ASK_USER, + upload.localAction, + applicationContext, + upload.isUseWifiOnly, + upload.isWhileChargingOnly, + false + ) + val client = clientProvider() + uploadsStorageManager.updateDatabaseUploadStart(op) + val result = op.execute(client) + uploadsStorageManager.updateDatabaseUploadResult(result, op) + return Result(file, result.isSuccess) + } +} diff --git a/src/main/java/com/nextcloud/client/files/downloader/UploadTrigger.kt b/src/main/java/com/nextcloud/client/files/downloader/UploadTrigger.kt new file mode 100644 index 0000000000..c71802229b --- /dev/null +++ b/src/main/java/com/nextcloud/client/files/downloader/UploadTrigger.kt @@ -0,0 +1,53 @@ +/* + * Nextcloud Android client application + * + * @author Chris Narkiewicz + * Copyright (C) 2021 Chris Narkiewicz + * + * 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 . + */ +package com.nextcloud.client.files.downloader + +import com.owncloud.android.operations.UploadFileOperation + +/** + * Upload transfer trigger. + */ +enum class UploadTrigger(val value: Int) { + + /** + * Transfer triggered manually by the user. + */ + USER(UploadFileOperation.CREATED_BY_USER), + + /** + * Transfer triggered automatically by taking a photo. + */ + PHOTO(UploadFileOperation.CREATED_AS_INSTANT_PICTURE), + + /** + * Transfer triggered automatically by making a video. + */ + VIDEO(UploadFileOperation.CREATED_AS_INSTANT_VIDEO); + + companion object { + @JvmStatic + fun fromValue(value: Int) = when (value) { + UploadFileOperation.CREATED_BY_USER -> USER + UploadFileOperation.CREATED_AS_INSTANT_PICTURE -> PHOTO + UploadFileOperation.CREATED_AS_INSTANT_VIDEO -> VIDEO + else -> USER + } + } +} diff --git a/src/main/java/com/nextcloud/client/jobs/ContactsBackupWork.kt b/src/main/java/com/nextcloud/client/jobs/ContactsBackupWork.kt index 3d3ca43167..89e70c8d3b 100644 --- a/src/main/java/com/nextcloud/client/jobs/ContactsBackupWork.kt +++ b/src/main/java/com/nextcloud/client/jobs/ContactsBackupWork.kt @@ -35,13 +35,16 @@ import androidx.work.Worker import androidx.work.WorkerParameters import com.nextcloud.client.account.User import com.nextcloud.client.account.UserAccountManager +import com.nextcloud.client.files.downloader.PostUploadAction +import com.nextcloud.client.files.downloader.TransferManagerConnection +import com.nextcloud.client.files.downloader.UploadRequest +import com.nextcloud.client.files.downloader.UploadTrigger import com.owncloud.android.R import com.owncloud.android.datamodel.ArbitraryDataProvider import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.OCFile -import com.owncloud.android.files.services.FileUploader +import com.owncloud.android.files.services.NameCollisionPolicy import com.owncloud.android.lib.common.utils.Log_OC -import com.owncloud.android.operations.UploadFileOperation import com.owncloud.android.services.OperationsService import com.owncloud.android.services.OperationsService.OperationsServiceBinder import com.owncloud.android.ui.activity.ContactsPreferenceActivity @@ -158,19 +161,19 @@ class ContactsBackupWork( } } } - FileUploader.uploadNewFile( - applicationContext, - user.toPlatformAccount(), - file.absolutePath, - backupFolder + filename, - FileUploader.LOCAL_BEHAVIOUR_MOVE, - null, - true, - UploadFileOperation.CREATED_BY_USER, - false, - false, - FileUploader.NameCollisionPolicy.RENAME - ) + + val request = UploadRequest.Builder(user = user, source = file.absolutePath, destination = backupFolder + file) + .setFileSize(file.length()) + .setNameConflicPolicy(NameCollisionPolicy.RENAME) + .setCreateRemoteFolder(true) + .setTrigger(UploadTrigger.USER) + .setPostAction(PostUploadAction.MOVE_TO_APP) + .setRequireWifi(false) + .setRequireCharging(false) + .build() + + val connection = TransferManagerConnection(applicationContext, user) + connection.enqueue(request) } private fun expireFiles(daysToExpire: Int, backupFolderString: String, user: User) { // -1 disables expiration diff --git a/src/main/java/com/nextcloud/client/notifications/AppNotificationManager.kt b/src/main/java/com/nextcloud/client/notifications/AppNotificationManager.kt index badac38aa1..189801f168 100644 --- a/src/main/java/com/nextcloud/client/notifications/AppNotificationManager.kt +++ b/src/main/java/com/nextcloud/client/notifications/AppNotificationManager.kt @@ -12,7 +12,7 @@ import com.owncloud.android.datamodel.OCFile interface AppNotificationManager { companion object { - const val DOWNLOAD_NOTIFICATION_ID = 1_000_000 + const val TRANSFER_NOTIFICATION_ID = 1_000_000 } /** @@ -23,16 +23,28 @@ interface AppNotificationManager { fun buildDownloadServiceForegroundNotification(): Notification /** - * Post download progress notification. + * Post download transfer progress notification. Subsequent calls will update + * currently displayed transfer notification. + * * @param fileOwner User owning the downloaded file * @param file File being downloaded * @param progress Progress as percentage (0-100) * @param allowPreview if true, pending intent with preview action is added to the notification */ - fun postDownloadProgress(fileOwner: User, file: OCFile, progress: Int, allowPreview: Boolean = true) + fun postDownloadTransferProgress(fileOwner: User, file: OCFile, progress: Int, allowPreview: Boolean = true) /** - * Removes download progress notification. + * Post upload transfer progress notification. Subsequent calls will update + * currently displayed transfer notification. + * + * @param fileOwner User owning the downloaded file + * @param file File being downloaded + * @param progress Progress as percentage (0-100) */ - fun cancelDownloadProgress() + fun postUploadTransferProgress(fileOwner: User, file: OCFile, progress: Int) + + /** + * Removes download or upload progress notification. + */ + fun cancelTransferNotification() } diff --git a/src/main/java/com/nextcloud/client/notifications/AppNotificationManagerImpl.kt b/src/main/java/com/nextcloud/client/notifications/AppNotificationManagerImpl.kt index 4b24ddb2db..ee56f6f080 100644 --- a/src/main/java/com/nextcloud/client/notifications/AppNotificationManagerImpl.kt +++ b/src/main/java/com/nextcloud/client/notifications/AppNotificationManagerImpl.kt @@ -49,7 +49,7 @@ class AppNotificationManagerImpl @Inject constructor( .build() } - override fun postDownloadProgress(fileOwner: User, file: OCFile, progress: Int, allowPreview: Boolean) { + override fun postDownloadTransferProgress(fileOwner: User, file: OCFile, progress: Int, allowPreview: Boolean) { val builder = builder(NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD) val content = resources.getString( R.string.downloader_download_in_progress_content, @@ -57,7 +57,7 @@ class AppNotificationManagerImpl @Inject constructor( file.fileName ) builder - .setSmallIcon(R.drawable.notification_icon) + .setSmallIcon(R.drawable.ic_cloud_download) .setTicker(resources.getString(R.string.downloader_download_in_progress_ticker)) .setContentTitle(resources.getString(R.string.downloader_download_in_progress_ticker)) .setOngoing(true) @@ -79,10 +79,28 @@ class AppNotificationManagerImpl @Inject constructor( ) builder.setContentIntent(pendingOpenFileIntent) } - platformNotificationsManager.notify(AppNotificationManager.DOWNLOAD_NOTIFICATION_ID, builder.build()) + platformNotificationsManager.notify(AppNotificationManager.TRANSFER_NOTIFICATION_ID, builder.build()) } - override fun cancelDownloadProgress() { - platformNotificationsManager.cancel(AppNotificationManager.DOWNLOAD_NOTIFICATION_ID) + override fun postUploadTransferProgress(fileOwner: User, file: OCFile, progress: Int) { + val builder = builder(NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD) + val content = resources.getString( + R.string.uploader_upload_in_progress_content, + progress, + file.fileName + ) + builder + .setSmallIcon(R.drawable.ic_cloud_upload) + .setTicker(resources.getString(R.string.uploader_upload_in_progress_ticker)) + .setContentTitle(resources.getString(R.string.uploader_upload_in_progress_ticker)) + .setOngoing(true) + .setProgress(PROGRESS_PERCENTAGE_MAX, progress, progress <= PROGRESS_PERCENTAGE_MIN) + .setContentText(content) + + platformNotificationsManager.notify(AppNotificationManager.TRANSFER_NOTIFICATION_ID, builder.build()) + } + + override fun cancelTransferNotification() { + platformNotificationsManager.cancel(AppNotificationManager.TRANSFER_NOTIFICATION_ID) } } diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java index 668018dc3c..282d0a0ef4 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java @@ -21,7 +21,7 @@ package com.owncloud.android.datamodel; -import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.files.services.NameCollisionPolicy; import java.io.Serializable; @@ -184,8 +184,8 @@ public class SyncedFolder implements Serializable, Cloneable { return this.nameCollisionPolicy; } - public FileUploader.NameCollisionPolicy getNameCollisionPolicy() { - return FileUploader.NameCollisionPolicy.deserialize(nameCollisionPolicy); + public NameCollisionPolicy getNameCollisionPolicy() { + return NameCollisionPolicy.deserialize(nameCollisionPolicy); } public boolean isEnabled() { diff --git a/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java b/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java index 2f1ad7b276..75ba020fa3 100644 --- a/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java +++ b/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java @@ -35,6 +35,7 @@ import com.owncloud.android.db.OCUpload; import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; import com.owncloud.android.db.UploadResult; import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.files.services.NameCollisionPolicy; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.operations.UploadFileOperation; @@ -409,7 +410,7 @@ public class UploadsStorageManager extends Observable { UploadStatus.fromValue(c.getInt(c.getColumnIndex(ProviderTableMeta.UPLOADS_STATUS))) ); upload.setLocalAction(c.getInt(c.getColumnIndex(ProviderTableMeta.UPLOADS_LOCAL_BEHAVIOUR))); - upload.setNameCollisionPolicy(FileUploader.NameCollisionPolicy.deserialize(c.getInt( + upload.setNameCollisionPolicy(NameCollisionPolicy.deserialize(c.getInt( c.getColumnIndex(ProviderTableMeta.UPLOADS_NAME_COLLISION_POLICY)))); upload.setCreateRemoteFolder(c.getInt( c.getColumnIndex(ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER)) == 1); diff --git a/src/main/java/com/owncloud/android/db/OCUpload.java b/src/main/java/com/owncloud/android/db/OCUpload.java index 8b230e5d57..045115a043 100644 --- a/src/main/java/com/owncloud/android/db/OCUpload.java +++ b/src/main/java/com/owncloud/android/db/OCUpload.java @@ -32,6 +32,7 @@ import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.datamodel.UploadsStorageManager.UploadStatus; import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.files.services.NameCollisionPolicy; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.operations.UploadFileOperation; import com.owncloud.android.utils.MimeTypeUtil; @@ -79,7 +80,7 @@ public class OCUpload implements Parcelable { /** * What to do in case of name collision. */ - private FileUploader.NameCollisionPolicy nameCollisionPolicy; + private NameCollisionPolicy nameCollisionPolicy; /** * Create destination folder? @@ -172,7 +173,7 @@ public class OCUpload implements Parcelable { fileSize = -1; uploadId = -1; localAction = FileUploader.LOCAL_BEHAVIOUR_COPY; - nameCollisionPolicy = FileUploader.NameCollisionPolicy.DEFAULT; + nameCollisionPolicy = NameCollisionPolicy.DEFAULT; createRemoteFolder = false; uploadStatus = UploadStatus.UPLOAD_IN_PROGRESS; lastResult = UploadResult.UNKNOWN; @@ -281,7 +282,7 @@ public class OCUpload implements Parcelable { remotePath = source.readString(); accountName = source.readString(); localAction = source.readInt(); - nameCollisionPolicy = FileUploader.NameCollisionPolicy.deserialize(source.readInt()); + nameCollisionPolicy = NameCollisionPolicy.deserialize(source.readInt()); createRemoteFolder = source.readInt() == 1; try { uploadStatus = UploadStatus.valueOf(source.readString()); @@ -368,7 +369,7 @@ public class OCUpload implements Parcelable { return this.localAction; } - public FileUploader.NameCollisionPolicy getNameCollisionPolicy() { + public NameCollisionPolicy getNameCollisionPolicy() { return this.nameCollisionPolicy; } @@ -424,7 +425,7 @@ public class OCUpload implements Parcelable { this.localAction = localAction; } - public void setNameCollisionPolicy(FileUploader.NameCollisionPolicy nameCollisionPolicy) { + public void setNameCollisionPolicy(NameCollisionPolicy nameCollisionPolicy) { this.nameCollisionPolicy = nameCollisionPolicy; } diff --git a/src/main/java/com/owncloud/android/files/services/FileUploader.java b/src/main/java/com/owncloud/android/files/services/FileUploader.java index 385c3ccdac..c62c0a8ce1 100644 --- a/src/main/java/com/owncloud/android/files/services/FileUploader.java +++ b/src/main/java/com/owncloud/android/files/services/FileUploader.java @@ -1090,17 +1090,19 @@ public class FileUploader extends Service accountMatch = account == null || account.name.equals(failedUpload.getAccountName()); resultMatch = uploadResult == null || uploadResult == failedUpload.getLastResult(); if (accountMatch && resultMatch) { + // 1. extract failed upload owner account in efficient name (expensive query) if (currentAccount == null || !currentAccount.name.equals(failedUpload.getAccountName())) { currentAccount = failedUpload.getAccount(accountManager); } if (!new File(failedUpload.getLocalPath()).exists()) { + // 2A. for deleted files, mark as permanently failed if (failedUpload.getLastResult() != UploadResult.FILE_NOT_FOUND) { failedUpload.setLastResult(UploadResult.FILE_NOT_FOUND); uploadsStorageManager.updateUpload(failedUpload); } } else { - + // 2B. for existing local files, try restarting it if possible if (!isPowerSaving && gotNetwork && canUploadBeRetried(failedUpload, gotWifi, charging)) { retryUpload(context, currentAccount, failedUpload); } @@ -1130,27 +1132,6 @@ public class FileUploader extends Service } - /** - * Ordinal of enumerated constants is important for old data compatibility. - */ - public enum NameCollisionPolicy { - RENAME, // Ordinal corresponds to old forceOverwrite = false (0 in database) - OVERWRITE, // Ordinal corresponds to old forceOverwrite = true (1 in database) - CANCEL, - ASK_USER; - - public static final NameCollisionPolicy DEFAULT = RENAME; - - public static NameCollisionPolicy deserialize(int ordinal) { - NameCollisionPolicy[] values = NameCollisionPolicy.values(); - return ordinal >= 0 && ordinal < values.length ? values[ordinal] : DEFAULT; - } - - public int serialize() { - return this.ordinal(); - } - } - /** * Binder to let client components to perform operations on the queue of uploads. * diff --git a/src/main/java/com/owncloud/android/files/services/NameCollisionPolicy.java b/src/main/java/com/owncloud/android/files/services/NameCollisionPolicy.java new file mode 100644 index 0000000000..a87fe439f4 --- /dev/null +++ b/src/main/java/com/owncloud/android/files/services/NameCollisionPolicy.java @@ -0,0 +1,22 @@ +package com.owncloud.android.files.services; + +/** + * Ordinal of enumerated constants is important for old data compatibility. + */ +public enum NameCollisionPolicy { + RENAME, // Ordinal corresponds to old forceOverwrite = false (0 in database) + OVERWRITE, // Ordinal corresponds to old forceOverwrite = true (1 in database) + CANCEL, + ASK_USER; + + public static final NameCollisionPolicy DEFAULT = RENAME; + + public static NameCollisionPolicy deserialize(int ordinal) { + NameCollisionPolicy[] values = NameCollisionPolicy.values(); + return ordinal >= 0 && ordinal < values.length ? values[ordinal] : DEFAULT; + } + + public int serialize() { + return this.ordinal(); + } +} diff --git a/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java b/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java index 918a72569e..7681278300 100644 --- a/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java +++ b/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java @@ -29,6 +29,7 @@ import com.nextcloud.client.account.User; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.services.FileDownloader; import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.files.services.NameCollisionPolicy; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; @@ -292,11 +293,11 @@ public class SynchronizeFileOperation extends SyncOperation { */ private void requestForUpload(OCFile file) { FileUploader.uploadUpdateFile( - mContext, - mUser.toPlatformAccount(), - file, - FileUploader.LOCAL_BEHAVIOUR_MOVE, - FileUploader.NameCollisionPolicy.OVERWRITE + mContext, + mUser.toPlatformAccount(), + file, + FileUploader.LOCAL_BEHAVIOUR_MOVE, + NameCollisionPolicy.OVERWRITE ); mTransferWasRequested = true; diff --git a/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 1a4289d66d..7061cf4906 100644 --- a/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -43,6 +43,7 @@ import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.db.OCUpload; import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.files.services.NameCollisionPolicy; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.network.OnDatatransferProgressListener; import com.owncloud.android.lib.common.network.ProgressiveDataTransfer; @@ -113,7 +114,7 @@ public class UploadFileOperation extends SyncOperation { private String mRemotePath; private String mFolderUnlockToken; private boolean mRemoteFolderToBeCreated; - private FileUploader.NameCollisionPolicy mNameCollisionPolicy; + private NameCollisionPolicy mNameCollisionPolicy; private int mLocalBehaviour; private int mCreatedBy; private boolean mOnWifiOnly; @@ -177,7 +178,7 @@ public class UploadFileOperation extends SyncOperation { User user, OCFile file, OCUpload upload, - FileUploader.NameCollisionPolicy nameCollisionPolicy, + NameCollisionPolicy nameCollisionPolicy, int localBehaviour, Context context, boolean onWifiOnly, @@ -192,7 +193,7 @@ public class UploadFileOperation extends SyncOperation { User user, OCFile file, OCUpload upload, - FileUploader.NameCollisionPolicy nameCollisionPolicy, + NameCollisionPolicy nameCollisionPolicy, int localBehaviour, Context context, boolean onWifiOnly, diff --git a/src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java b/src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java index 4921250f99..313f250429 100644 --- a/src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java +++ b/src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java @@ -54,7 +54,7 @@ import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.files.services.FileDownloader; import com.owncloud.android.files.services.FileUploader; -import com.owncloud.android.files.services.FileUploader.NameCollisionPolicy; +import com.owncloud.android.files.services.NameCollisionPolicy; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index 19489bd3ba..7664ffb8c9 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -49,7 +49,7 @@ import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.SyncedFolder; import com.owncloud.android.db.ProviderMeta; import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; -import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.files.services.NameCollisionPolicy; import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.shares.ShareType; @@ -2185,7 +2185,7 @@ public class FileContentProvider extends ContentProvider { // make sure all existing folders set to FileUploader.NameCollisionPolicy.ASK_USER. db.execSQL("UPDATE " + ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME + " SET " + ProviderTableMeta.SYNCED_FOLDER_NAME_COLLISION_POLICY + " = " + - FileUploader.NameCollisionPolicy.ASK_USER.serialize()); + NameCollisionPolicy.ASK_USER.serialize()); upgraded = true; db.setTransactionSuccessful(); } finally { diff --git a/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.java b/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.java index 61bf3b7cff..59684eebcd 100644 --- a/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.java @@ -31,6 +31,7 @@ import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.db.OCUpload; import com.owncloud.android.files.services.FileDownloader; import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.files.services.NameCollisionPolicy; 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; @@ -120,22 +121,22 @@ public class ConflictsResolveActivity extends FileActivity implements OnConflict break; case KEEP_LOCAL: // Upload FileUploader.uploadUpdateFile( - getBaseContext(), - getAccount(), - file, - localBehaviour, - FileUploader.NameCollisionPolicy.OVERWRITE + getBaseContext(), + getAccount(), + file, + localBehaviour, + NameCollisionPolicy.OVERWRITE ); uploadsStorageManager.removeUpload(upload); break; case KEEP_BOTH: // Upload FileUploader.uploadUpdateFile( - getBaseContext(), - getAccount(), - file, - localBehaviour, - FileUploader.NameCollisionPolicy.RENAME + getBaseContext(), + getAccount(), + file, + localBehaviour, + NameCollisionPolicy.RENAME ); uploadsStorageManager.removeUpload(upload); diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 23a7ec5def..8778cfdb81 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -71,6 +71,7 @@ import com.owncloud.android.files.services.FileDownloader; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; +import com.owncloud.android.files.services.NameCollisionPolicy; import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; @@ -1001,7 +1002,7 @@ public class FileDisplayActivity extends FileActivity UploadFileOperation.CREATED_BY_USER, false, false, - FileUploader.NameCollisionPolicy.ASK_USER + NameCollisionPolicy.ASK_USER ); } else { diff --git a/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java b/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java index 03ff72cae9..3c399e2438 100755 --- a/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java @@ -69,6 +69,7 @@ import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.files.services.NameCollisionPolicy; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; @@ -896,17 +897,17 @@ public class ReceiveExternalFilesActivity extends FileActivity public void uploadFile(String tmpName, String filename) { FileUploader.uploadNewFile( - getBaseContext(), - getAccount(), - tmpName, - mFile.getRemotePath() + filename, - FileUploader.LOCAL_BEHAVIOUR_COPY, - null, - true, - UploadFileOperation.CREATED_BY_USER, - false, - false, - FileUploader.NameCollisionPolicy.ASK_USER + getBaseContext(), + getAccount(), + tmpName, + mFile.getRemotePath() + filename, + FileUploader.LOCAL_BEHAVIOUR_COPY, + null, + true, + UploadFileOperation.CREATED_BY_USER, + false, + false, + NameCollisionPolicy.ASK_USER ); finish(); } diff --git a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java index 264c66e7cd..b9ecf2dede 100644 --- a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java @@ -61,6 +61,7 @@ import com.owncloud.android.datamodel.SyncedFolder; import com.owncloud.android.datamodel.SyncedFolderDisplayItem; import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.files.services.NameCollisionPolicy; import com.owncloud.android.ui.adapter.SyncedFolderAdapter; import com.owncloud.android.ui.decoration.MediaGridItemDecoration; import com.owncloud.android.ui.dialog.SyncedFolderPreferencesDialogFragment; @@ -445,7 +446,7 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA false, getAccount().name, FileUploader.LOCAL_BEHAVIOUR_FORGET, - FileUploader.NameCollisionPolicy.ASK_USER.serialize(), + NameCollisionPolicy.ASK_USER.serialize(), false, clock.getCurrentTime(), mediaFolder.filePaths, @@ -524,21 +525,21 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA } else if (itemId == R.id.action_create_custom_folder) { Log.d(TAG, "Show custom folder dialog"); SyncedFolderDisplayItem emptyCustomFolder = new SyncedFolderDisplayItem( - UNPERSISTED_ID, - null, - null, - true, - false, - true, - false, - getAccount().name, - FileUploader.LOCAL_BEHAVIOUR_FORGET, - FileUploader.NameCollisionPolicy.ASK_USER.serialize(), - false, - clock.getCurrentTime(), - null, - MediaFolderType.CUSTOM, - false); + UNPERSISTED_ID, + null, + null, + true, + false, + true, + false, + getAccount().name, + FileUploader.LOCAL_BEHAVIOUR_FORGET, + NameCollisionPolicy.ASK_USER.serialize(), + false, + clock.getCurrentTime(), + null, + MediaFolderType.CUSTOM, + false); onSyncFolderSettingsClick(0, emptyCustomFolder); result = super.onOptionsItemSelected(item); diff --git a/src/main/java/com/owncloud/android/ui/asynctasks/CopyAndUploadContentUrisTask.java b/src/main/java/com/owncloud/android/ui/asynctasks/CopyAndUploadContentUrisTask.java index 0091cdd498..874d2c0979 100644 --- a/src/main/java/com/owncloud/android/ui/asynctasks/CopyAndUploadContentUrisTask.java +++ b/src/main/java/com/owncloud/android/ui/asynctasks/CopyAndUploadContentUrisTask.java @@ -33,6 +33,7 @@ import android.widget.Toast; import com.nextcloud.client.account.User; import com.owncloud.android.R; import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.files.services.NameCollisionPolicy; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.operations.UploadFileOperation; @@ -250,17 +251,17 @@ public class CopyAndUploadContentUrisTask extends AsyncTask