mirror of
https://github.com/nextcloud/android.git
synced 2024-11-28 10:18:59 +03:00
New upload manager
Signed-off-by: Chris Narkiewicz <hello@ezaquarii.com>
This commit is contained in:
parent
be29f9aa9a
commit
ebf63f064b
51 changed files with 913 additions and 372 deletions
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<Transfer>()
|
||||
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<Transfer>()
|
||||
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<Transfer>()
|
||||
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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -360,7 +361,7 @@ public abstract class AbstractIT {
|
|||
user,
|
||||
null,
|
||||
ocUpload,
|
||||
FileUploader.NameCollisionPolicy.DEFAULT,
|
||||
NameCollisionPolicy.DEFAULT,
|
||||
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
false,
|
||||
|
|
|
@ -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;
|
||||
|
@ -208,7 +209,7 @@ public abstract class AbstractOnServerIT extends AbstractIT {
|
|||
user,
|
||||
null,
|
||||
ocUpload,
|
||||
FileUploader.NameCollisionPolicy.DEFAULT,
|
||||
NameCollisionPolicy.DEFAULT,
|
||||
localBehaviour,
|
||||
targetContext,
|
||||
false,
|
||||
|
|
|
@ -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;
|
||||
|
@ -229,7 +230,7 @@ public class UploadIT extends AbstractOnServerIT {
|
|||
user,
|
||||
null,
|
||||
ocUpload,
|
||||
FileUploader.NameCollisionPolicy.DEFAULT,
|
||||
NameCollisionPolicy.DEFAULT,
|
||||
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
false,
|
||||
|
@ -276,7 +277,7 @@ public class UploadIT extends AbstractOnServerIT {
|
|||
user,
|
||||
null,
|
||||
ocUpload,
|
||||
FileUploader.NameCollisionPolicy.DEFAULT,
|
||||
NameCollisionPolicy.DEFAULT,
|
||||
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
false,
|
||||
|
@ -315,7 +316,7 @@ public class UploadIT extends AbstractOnServerIT {
|
|||
user,
|
||||
null,
|
||||
ocUpload,
|
||||
FileUploader.NameCollisionPolicy.DEFAULT,
|
||||
NameCollisionPolicy.DEFAULT,
|
||||
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
true,
|
||||
|
@ -344,7 +345,7 @@ public class UploadIT extends AbstractOnServerIT {
|
|||
user,
|
||||
null,
|
||||
ocUpload,
|
||||
FileUploader.NameCollisionPolicy.DEFAULT,
|
||||
NameCollisionPolicy.DEFAULT,
|
||||
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
true,
|
||||
|
@ -392,7 +393,7 @@ public class UploadIT extends AbstractOnServerIT {
|
|||
user,
|
||||
null,
|
||||
ocUpload,
|
||||
FileUploader.NameCollisionPolicy.DEFAULT,
|
||||
NameCollisionPolicy.DEFAULT,
|
||||
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
true,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -322,7 +322,7 @@
|
|||
|
||||
<service android:name=".services.OperationsService" />
|
||||
<service android:name=".files.services.FileDownloader" />
|
||||
<service android:name="com.nextcloud.client.files.downloader.DownloaderService" />
|
||||
<service android:name="com.nextcloud.client.files.downloader.FileTransferService" />
|
||||
<service android:name=".files.services.FileUploader" />
|
||||
<service android:name="com.nextcloud.client.media.PlayerService"/>
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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<String, String> get() {
|
||||
return defaultPreferences.all
|
||||
|
|
|
@ -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<Adapter.ViewHolder>() {
|
||||
|
||||
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
val uuid = view.findViewById<TextView>(R.id.etm_download_uuid)
|
||||
val path = view.findViewById<TextView>(R.id.etm_download_path)
|
||||
val user = view.findViewById<TextView>(R.id.etm_download_user)
|
||||
val state = view.findViewById<TextView>(R.id.etm_download_state)
|
||||
val progress = view.findViewById<TextView>(R.id.etm_download_progress)
|
||||
private val progressRow = view.findViewById<View>(R.id.etm_download_progress_row)
|
||||
val type = view.findViewById<TextView>(R.id.etm_transfer_type)
|
||||
val typeIcon = view.findViewById<ImageView>(R.id.etm_transfer_type_icon)
|
||||
val uuid = view.findViewById<TextView>(R.id.etm_transfer_uuid)
|
||||
val path = view.findViewById<TextView>(R.id.etm_transfer_remote_path)
|
||||
val user = view.findViewById<TextView>(R.id.etm_transfer_user)
|
||||
val state = view.findViewById<TextView>(R.id.etm_transfer_state)
|
||||
val progress = view.findViewById<TextView>(R.id.etm_transfer_progress)
|
||||
private val progressRow = view.findViewById<View>(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<Transfer>()
|
||||
private var transfers = listOf<Transfer>()
|
||||
|
||||
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) {
|
|
@ -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)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Chris Narkiewicz
|
||||
* Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
|
||||
* Copyright (C) 2021 Chris Narkiewicz <hello@ezaquarii.com>
|
||||
*
|
||||
* 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<DownloaderService>(service),
|
||||
service: FileTransferService
|
||||
) : LocalBinder<FileTransferService>(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<String, TransferManagerImpl> = 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<User>(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
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Chris Narkiewicz
|
||||
* Copyright (C) 2021 Chris Narkiewicz <hello@ezaquarii.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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);
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Chris Narkiewicz
|
||||
* Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
|
||||
* Copyright (C) 2021 Chris Narkiewicz <hello@ezaquarii.com>
|
||||
*
|
||||
* 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 {
|
||||
|
||||
constructor(
|
||||
user: User,
|
||||
file: OCFile,
|
||||
type: Direction = Direction.DOWNLOAD
|
||||
) : this(user, file, UUID.randomUUID(), type)
|
||||
|
||||
constructor(
|
||||
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,
|
||||
) : this(user, file, UUID.randomUUID(), Direction.DOWNLOAD)
|
||||
|
||||
constructor(
|
||||
user: User,
|
||||
file: OCFile,
|
||||
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>(User::class.java.classLoader) as User,
|
||||
|
@ -79,13 +88,144 @@ class Request internal constructor(
|
|||
return 0
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<Request> {
|
||||
override fun createFromParcel(parcel: Parcel): Request {
|
||||
return Request(parcel)
|
||||
companion object CREATOR : Parcelable.Creator<DownloadRequest> {
|
||||
override fun createFromParcel(parcel: Parcel): DownloadRequest {
|
||||
return DownloadRequest(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<Request?> {
|
||||
override fun newArray(size: Int): Array<DownloadRequest?> {
|
||||
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>(User::class.java.classLoader) as User,
|
||||
file = parcel.readParcelable<OCFile>(OCFile::class.java.classLoader) as OCFile,
|
||||
upload = parcel.readParcelable<OCUpload>(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<UploadRequest> {
|
||||
override fun createFromParcel(parcel: Parcel): UploadRequest {
|
||||
return UploadRequest(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<UploadRequest?> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Chris Narkiewicz
|
||||
* Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
|
||||
* Copyright (C) 2021 Chris Narkiewicz <hello@ezaquarii.com>
|
||||
*
|
||||
* 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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Chris Narkiewicz
|
||||
* Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
|
||||
* Copyright (C) 2021 Chris Narkiewicz <hello@ezaquarii.com>
|
||||
*
|
||||
* 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)
|
||||
|
||||
|
|
|
@ -30,11 +30,11 @@ import java.util.UUID
|
|||
class TransferManagerConnection(
|
||||
context: Context,
|
||||
val user: User
|
||||
) : LocalConnection<DownloaderService>(context), TransferManager {
|
||||
) : LocalConnection<FileTransferService>(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<UUID> = 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)
|
||||
}
|
||||
|
|
|
@ -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 = transferTask,
|
||||
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() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDownloadTask(request: Request): TaskFunction<DownloadTask.Result, Int> {
|
||||
private fun createDownloadTask(request: DownloadRequest): TaskFunction<DownloadTask.Result, Int> {
|
||||
return if (request.test) {
|
||||
{ progress: OnProgressCallback<Int>, isCancelled: IsCancelled ->
|
||||
testDownloadTask(request.file, progress, isCancelled)
|
||||
}
|
||||
} else {
|
||||
val downloadTask = taskFactory.create()
|
||||
val downloadTask = downloadTaskFactory.create()
|
||||
val wrapper: TaskFunction<DownloadTask.Result, Int> = { progress: ((Int) -> Unit), isCancelled ->
|
||||
downloadTask.download(request, progress, isCancelled)
|
||||
}
|
||||
|
@ -118,6 +126,25 @@ class TransferManagerImpl(
|
|||
}
|
||||
}
|
||||
|
||||
private fun createUploadTask(request: UploadRequest): TaskFunction<UploadTask.Result, Int> {
|
||||
return if (request.test) {
|
||||
{ progress: OnProgressCallback<Int>, 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<UploadTask.Result, Int> = { 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<Int>,
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Chris Narkiewicz
|
||||
* Copyright (C) 2021 Chris Narkiewicz <hello@ezaquarii.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Chris Narkiewicz
|
||||
* Copyright (C) 2021 Chris Narkiewicz <hello@ezaquarii.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
@ -296,7 +297,7 @@ public class SynchronizeFileOperation extends SyncOperation {
|
|||
mUser.toPlatformAccount(),
|
||||
file,
|
||||
FileUploader.LOCAL_BEHAVIOUR_MOVE,
|
||||
FileUploader.NameCollisionPolicy.OVERWRITE
|
||||
NameCollisionPolicy.OVERWRITE
|
||||
);
|
||||
|
||||
mTransferWasRequested = true;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
@ -124,7 +125,7 @@ public class ConflictsResolveActivity extends FileActivity implements OnConflict
|
|||
getAccount(),
|
||||
file,
|
||||
localBehaviour,
|
||||
FileUploader.NameCollisionPolicy.OVERWRITE
|
||||
NameCollisionPolicy.OVERWRITE
|
||||
);
|
||||
|
||||
uploadsStorageManager.removeUpload(upload);
|
||||
|
@ -135,7 +136,7 @@ public class ConflictsResolveActivity extends FileActivity implements OnConflict
|
|||
getAccount(),
|
||||
file,
|
||||
localBehaviour,
|
||||
FileUploader.NameCollisionPolicy.RENAME
|
||||
NameCollisionPolicy.RENAME
|
||||
);
|
||||
|
||||
uploadsStorageManager.removeUpload(upload);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
@ -906,7 +907,7 @@ public class ReceiveExternalFilesActivity extends FileActivity
|
|||
UploadFileOperation.CREATED_BY_USER,
|
||||
false,
|
||||
false,
|
||||
FileUploader.NameCollisionPolicy.ASK_USER
|
||||
NameCollisionPolicy.ASK_USER
|
||||
);
|
||||
finish();
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
@ -533,7 +534,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(),
|
||||
null,
|
||||
|
|
|
@ -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;
|
||||
|
@ -260,7 +261,7 @@ public class CopyAndUploadContentUrisTask extends AsyncTask<Object, Void, Result
|
|||
UploadFileOperation.CREATED_BY_USER,
|
||||
false,
|
||||
false,
|
||||
FileUploader.NameCollisionPolicy.ASK_USER
|
||||
NameCollisionPolicy.ASK_USER
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ import com.google.android.material.button.MaterialButton;
|
|||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.datamodel.MediaFolderType;
|
||||
import com.owncloud.android.datamodel.SyncedFolderDisplayItem;
|
||||
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.ui.activity.FolderPickerActivity;
|
||||
import com.owncloud.android.ui.activity.UploadFilesActivity;
|
||||
|
@ -589,7 +589,7 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
|
|||
* Get index for name collision selection dialog.
|
||||
* @return 0 if ASK_USER, 1 if OVERWRITE, 2 if RENAME, 3 if SKIP, Otherwise: 0
|
||||
*/
|
||||
static private Integer getSelectionIndexForNameCollisionPolicy(FileUploader.NameCollisionPolicy nameCollisionPolicy) {
|
||||
static private Integer getSelectionIndexForNameCollisionPolicy(NameCollisionPolicy nameCollisionPolicy) {
|
||||
switch (nameCollisionPolicy) {
|
||||
case OVERWRITE:
|
||||
return 1;
|
||||
|
@ -608,17 +608,17 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
|
|||
*
|
||||
* @return ASK_USER if 0, OVERWRITE if 1, RENAME if 2, SKIP if 3. Otherwise: ASK_USER
|
||||
*/
|
||||
static private FileUploader.NameCollisionPolicy getNameCollisionPolicyForSelectionIndex(int index) {
|
||||
static private NameCollisionPolicy getNameCollisionPolicyForSelectionIndex(int index) {
|
||||
switch (index) {
|
||||
case 1:
|
||||
return FileUploader.NameCollisionPolicy.OVERWRITE;
|
||||
return NameCollisionPolicy.OVERWRITE;
|
||||
case 2:
|
||||
return FileUploader.NameCollisionPolicy.RENAME;
|
||||
return NameCollisionPolicy.RENAME;
|
||||
case 3:
|
||||
return FileUploader.NameCollisionPolicy.CANCEL;
|
||||
return NameCollisionPolicy.CANCEL;
|
||||
case 0:
|
||||
default:
|
||||
return FileUploader.NameCollisionPolicy.ASK_USER;
|
||||
return NameCollisionPolicy.ASK_USER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import android.os.Parcelable;
|
|||
import com.owncloud.android.datamodel.MediaFolderType;
|
||||
import com.owncloud.android.datamodel.SyncedFolderDisplayItem;
|
||||
import com.owncloud.android.files.services.FileUploader;
|
||||
import com.owncloud.android.files.services.NameCollisionPolicy;
|
||||
|
||||
/**
|
||||
* Parcelable for {@link SyncedFolderDisplayItem} objects to transport them from/to dialog fragments.
|
||||
|
@ -40,7 +41,7 @@ public class SyncedFolderParcelable implements Parcelable {
|
|||
private boolean enabled = false;
|
||||
private boolean subfolderByDate = false;
|
||||
private Integer uploadAction;
|
||||
private FileUploader.NameCollisionPolicy nameCollisionPolicy = FileUploader.NameCollisionPolicy.ASK_USER;
|
||||
private NameCollisionPolicy nameCollisionPolicy = NameCollisionPolicy.ASK_USER;
|
||||
private MediaFolderType type;
|
||||
private boolean hidden = false;
|
||||
private long id;
|
||||
|
@ -60,7 +61,7 @@ public class SyncedFolderParcelable implements Parcelable {
|
|||
type = syncedFolderDisplayItem.getType();
|
||||
account = syncedFolderDisplayItem.getAccount();
|
||||
uploadAction = syncedFolderDisplayItem.getUploadAction();
|
||||
nameCollisionPolicy = FileUploader.NameCollisionPolicy.deserialize(
|
||||
nameCollisionPolicy = NameCollisionPolicy.deserialize(
|
||||
syncedFolderDisplayItem.getNameCollisionPolicyInt());
|
||||
this.section = section;
|
||||
hidden = syncedFolderDisplayItem.isHidden();
|
||||
|
@ -79,7 +80,7 @@ public class SyncedFolderParcelable implements Parcelable {
|
|||
type = MediaFolderType.getById(read.readInt());
|
||||
account = read.readString();
|
||||
uploadAction = read.readInt();
|
||||
nameCollisionPolicy = FileUploader.NameCollisionPolicy.deserialize(read.readInt());
|
||||
nameCollisionPolicy = NameCollisionPolicy.deserialize(read.readInt());
|
||||
section = read.readInt();
|
||||
hidden = read.readInt() != 0;
|
||||
}
|
||||
|
@ -191,7 +192,7 @@ public class SyncedFolderParcelable implements Parcelable {
|
|||
return this.uploadAction;
|
||||
}
|
||||
|
||||
public FileUploader.NameCollisionPolicy getNameCollisionPolicy() {
|
||||
public NameCollisionPolicy getNameCollisionPolicy() {
|
||||
return this.nameCollisionPolicy;
|
||||
}
|
||||
|
||||
|
@ -247,7 +248,7 @@ public class SyncedFolderParcelable implements Parcelable {
|
|||
this.subfolderByDate = subfolderByDate;
|
||||
}
|
||||
|
||||
public void setNameCollisionPolicy(FileUploader.NameCollisionPolicy nameCollisionPolicy) {
|
||||
public void setNameCollisionPolicy(NameCollisionPolicy nameCollisionPolicy) {
|
||||
this.nameCollisionPolicy = nameCollisionPolicy;
|
||||
}
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ import com.nextcloud.client.account.User;
|
|||
import com.nextcloud.client.account.UserAccountManager;
|
||||
import com.nextcloud.client.di.Injectable;
|
||||
import com.nextcloud.client.files.downloader.Direction;
|
||||
import com.nextcloud.client.files.downloader.DownloadRequest;
|
||||
import com.nextcloud.client.files.downloader.Request;
|
||||
import com.nextcloud.client.files.downloader.Transfer;
|
||||
import com.nextcloud.client.files.downloader.TransferManagerConnection;
|
||||
|
@ -186,7 +187,7 @@ public class ContactListFragment extends FileFragment implements Injectable {
|
|||
fileDownloader.registerTransferListener(this::onDownloadUpdate);
|
||||
fileDownloader.bind();
|
||||
if (!ocFile.isDown()) {
|
||||
Request request = new Request(user, ocFile, Direction.DOWNLOAD);
|
||||
Request request = new DownloadRequest(user, ocFile);
|
||||
fileDownloader.enqueue(request);
|
||||
} else {
|
||||
loadContactsTask.execute();
|
||||
|
|
|
@ -25,6 +25,7 @@ import android.os.Parcelable;
|
|||
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.utils.Log_OC;
|
||||
import com.owncloud.android.operations.UploadFileOperation;
|
||||
import com.owncloud.android.ui.activity.FileActivity;
|
||||
|
@ -165,7 +166,7 @@ public class UriUploader {
|
|||
UploadFileOperation.CREATED_BY_USER,
|
||||
false,
|
||||
false,
|
||||
FileUploader.NameCollisionPolicy.ASK_USER
|
||||
NameCollisionPolicy.ASK_USER
|
||||
);
|
||||
}
|
||||
|
||||
|
|
5
src/main/res/drawable/ic_cloud_download.xml
Normal file
5
src/main/res/drawable/ic_cloud_download.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#757575"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM17,13l-5,5 -5,-5h3V9h4v4h3z"/>
|
||||
</vector>
|
5
src/main/res/drawable/ic_cloud_upload.xml
Normal file
5
src/main/res/drawable/ic_cloud_upload.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#757575"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM14,13v4h-4v-4H7l5,-5 5,5h-3z"/>
|
||||
</vector>
|
|
@ -34,10 +34,48 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:text="@string/etm_download_uuid" />
|
||||
android:text="@string/etm_transfer_type" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/etm_transfer_type_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dip"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:src="@drawable/ic_cloud_download"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/etm_download_uuid"
|
||||
android:id="@+id/etm_transfer_type"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="0dip"
|
||||
android:layout_marginStart="4dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/etm_transfer_type_icon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:text="@string/etm_transfer_type_download" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:text="@string/etm_transfer_uuid" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/etm_transfer_uuid"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="d7edb387-0b61-4e4e-a728-ffab3055d700" />
|
||||
|
@ -51,10 +89,10 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:text="@string/etm_download_path" />
|
||||
android:text="@string/etm_transfer_remote_path" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/etm_download_path"
|
||||
android:id="@+id/etm_transfer_remote_path"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="file path" />
|
||||
|
@ -69,10 +107,10 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:text="@string/etm_download_user" />
|
||||
android:text="@string/etm_transfer_user" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/etm_download_user"
|
||||
android:id="@+id/etm_transfer_user"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="user@nextcloud.com" />
|
||||
|
@ -87,10 +125,10 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:text="@string/etm_download_state" />
|
||||
android:text="@string/etm_transfer_state" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/etm_download_state"
|
||||
android:id="@+id/etm_transfer_state"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="PENDING" />
|
||||
|
@ -98,7 +136,7 @@
|
|||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:id="@+id/etm_download_progress_row"
|
||||
android:id="@+id/etm_transfer_progress_row"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
|
@ -106,10 +144,10 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:text="@string/etm_download_progress" />
|
||||
android:text="@string/etm_transfer_progress" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/etm_download_progress"
|
||||
android:id="@+id/etm_transfer_progress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="50%" />
|
|
@ -21,7 +21,7 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.nextcloud.client.etm.pages.EtmDownloaderFragment">
|
||||
tools:context="com.nextcloud.client.etm.pages.EtmFileTransferFragment">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/etm_download_list"
|
||||
|
|
|
@ -25,9 +25,16 @@
|
|||
|
||||
<item
|
||||
android:id="@+id/etm_test_download"
|
||||
android:title="@string/etm_download_enqueue_test_download"
|
||||
android:title="@string/etm_transfer_enqueue_test_download"
|
||||
app:showAsAction="ifRoom"
|
||||
android:showAsAction="ifRoom"
|
||||
android:icon="@drawable/ic_plus" />
|
||||
android:icon="@drawable/ic_cloud_download" />
|
||||
|
||||
<item
|
||||
android:id="@+id/etm_test_upload"
|
||||
android:title="@string/etm_transfer_enqueue_test_upload"
|
||||
app:showAsAction="ifRoom"
|
||||
android:showAsAction="ifRoom"
|
||||
android:icon="@drawable/ic_cloud_upload" />
|
||||
|
||||
</menu>
|
|
@ -856,13 +856,17 @@
|
|||
<string name="etm_background_job_started">Started</string>
|
||||
<string name="etm_background_job_progress">Progress</string>
|
||||
<string name="etm_migrations">Migrations (app upgrade)</string>
|
||||
<string name="etm_downloader">Downloader</string>
|
||||
<string name="etm_download_path">Remote path</string>
|
||||
<string name="etm_download_enqueue_test_download">Enqueue test download</string>
|
||||
<string name="etm_download_uuid" translatable="false">@string/etm_background_job_uuid</string>
|
||||
<string name="etm_download_user" translatable="false">@string/etm_background_job_user</string>
|
||||
<string name="etm_download_state" translatable="false">@string/etm_background_job_state</string>
|
||||
<string name="etm_download_progress" translatable="false">@string/etm_background_job_progress</string>
|
||||
<string name="etm_transfer">File transfer</string>
|
||||
<string name="etm_transfer_remote_path">Remote path</string>
|
||||
<string name="etm_transfer_enqueue_test_download">Enqueue test download</string>
|
||||
<string name="etm_transfer_enqueue_test_upload">Enqueue test upload</string>
|
||||
<string name="etm_transfer_type">Transfer</string>
|
||||
<string name="etm_transfer_type_upload">upload</string>
|
||||
<string name="etm_transfer_type_download">download</string>
|
||||
<string name="etm_transfer_uuid" translatable="false">@string/etm_background_job_uuid</string>
|
||||
<string name="etm_transfer_user" translatable="false">@string/etm_background_job_user</string>
|
||||
<string name="etm_transfer_state" translatable="false">@string/etm_background_job_state</string>
|
||||
<string name="etm_transfer_progress" translatable="false">@string/etm_background_job_progress</string>
|
||||
|
||||
<string name="logs_status_loading">Loading…</string>
|
||||
<string name="logs_status_filtered">Logs: %1$d kB, query matched %2$d / %3$d in %4$d ms</string>
|
||||
|
|
|
@ -25,6 +25,7 @@ package com.owncloud.android.ui.activity;
|
|||
import com.owncloud.android.datamodel.MediaFolderType;
|
||||
import com.owncloud.android.datamodel.SyncedFolderDisplayItem;
|
||||
import com.owncloud.android.files.services.FileUploader;
|
||||
import com.owncloud.android.files.services.NameCollisionPolicy;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -168,7 +169,7 @@ public class SyncedFoldersActivityTest {
|
|||
true,
|
||||
"test@nextcloud.com",
|
||||
FileUploader.LOCAL_BEHAVIOUR_MOVE,
|
||||
FileUploader.NameCollisionPolicy.ASK_USER.serialize(),
|
||||
NameCollisionPolicy.ASK_USER.serialize(),
|
||||
enabled,
|
||||
System.currentTimeMillis(),
|
||||
new ArrayList<String>(),
|
||||
|
|
Loading…
Reference in a new issue