diff --git a/changelog/unreleased/3293 b/changelog/unreleased/3293
new file mode 100644
index 00000000000..69c4f2c9fa0
--- /dev/null
+++ b/changelog/unreleased/3293
@@ -0,0 +1,7 @@
+Enhancement: Replace picker to select camera folder with native one
+
+The custom picker to select the camera folder was replaced with the native one. Now, it is ready for
+scoped storage and some problems to select a folder in the SD Card were fixed. Also, a new field to show
+the last synchronization timestamp was added.
+
+https://github.com/owncloud/android/issues/2899 https://github.com/owncloud/android/pull/3293
\ No newline at end of file
diff --git a/owncloud-android-library b/owncloud-android-library
index b491641eff8..22719c8f40f 160000
--- a/owncloud-android-library
+++ b/owncloud-android-library
@@ -1 +1 @@
-Subproject commit b491641eff8da4c1cf4971c133bf1e002d57d8dd
+Subproject commit 22719c8f40fd804a104d92819b2c304d58e9c085
diff --git a/owncloudApp/build.gradle b/owncloudApp/build.gradle
index dbf26a705bf..3c09d09bbcd 100644
--- a/owncloudApp/build.gradle
+++ b/owncloudApp/build.gradle
@@ -36,9 +36,11 @@ dependencies {
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$archLifecycleVersion"
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
kapt "androidx.lifecycle:lifecycle-common-java8:$archLifecycleVersion"
kapt "org.xerial:sqlite-jdbc:3.34.0" // fix kapt for Apple Silicon https://stackoverflow.com/a/68285501/1079990
@@ -48,13 +50,16 @@ dependencies {
implementation "org.koin:koin-core:$koinVersion"
implementation "org.koin:koin-androidx-viewmodel:$koinVersion"
+ // WorkManager
+ implementation "androidx.work:work-runtime-ktx:2.5.0"
+
// KTX extensions, see https://developer.android.com/kotlin/ktx.html
implementation "androidx.core:core-ktx:$ktxCoreVersion"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$ktxViewModelVersion"
implementation "androidx.fragment:fragment-ktx:$ktxFragmentVersion"
// Preferences
- implementation 'androidx.preference:preference:1.1.1'
+ implementation 'androidx.preference:preference-ktx:1.1.1'
// Tests
testImplementation project(':owncloudTestUtil')
diff --git a/owncloudApp/src/androidTest/java/com/owncloud/android/settings/camerauploads/SettingsPictureUploadsFragmentTest.kt b/owncloudApp/src/androidTest/java/com/owncloud/android/settings/camerauploads/SettingsPictureUploadsFragmentTest.kt
deleted file mode 100644
index b0b4045b2c0..00000000000
--- a/owncloudApp/src/androidTest/java/com/owncloud/android/settings/camerauploads/SettingsPictureUploadsFragmentTest.kt
+++ /dev/null
@@ -1,251 +0,0 @@
-/**
- * ownCloud Android client application
- *
- * @author Juan Carlos Garrote Gascón
- *
- * Copyright (C) 2021 ownCloud GmbH.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.owncloud.android.settings.camerauploads
-
-import android.content.Context
-import android.os.Build
-import android.os.Environment
-import androidx.fragment.app.testing.FragmentScenario
-import androidx.fragment.app.testing.launchFragmentInContainer
-import androidx.preference.CheckBoxPreference
-import androidx.preference.ListPreference
-import androidx.preference.Preference
-import androidx.preference.SwitchPreferenceCompat
-import androidx.test.espresso.Espresso.onView
-import androidx.test.espresso.action.ViewActions.click
-import androidx.test.espresso.intent.Intents
-import androidx.test.espresso.intent.Intents.intended
-import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
-import androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra
-import androidx.test.espresso.matcher.ViewMatchers.withText
-import androidx.test.platform.app.InstrumentationRegistry
-import com.owncloud.android.R
-import com.owncloud.android.db.PreferenceManager
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_BEHAVIOUR
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_ENABLED
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_PATH
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_SOURCE
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_WIFI_ONLY
-import com.owncloud.android.presentation.ui.settings.fragments.SettingsPictureUploadsFragment
-import com.owncloud.android.presentation.viewmodels.settings.SettingsPictureUploadsViewModel
-import com.owncloud.android.ui.activity.LocalFolderPickerActivity
-import com.owncloud.android.utils.matchers.verifyPreference
-import io.mockk.every
-import io.mockk.mockk
-import org.junit.After
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertFalse
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Test
-import org.koin.androidx.viewmodel.dsl.viewModel
-import org.koin.core.context.startKoin
-import org.koin.core.context.stopKoin
-import org.koin.dsl.module
-
-class SettingsPictureUploadsFragmentTest {
-
- private lateinit var fragmentScenario: FragmentScenario
-
- private lateinit var prefEnablePictureUploads: SwitchPreferenceCompat
- private lateinit var prefPictureUploadsPath: Preference
- private lateinit var prefPictureUploadsOnWifi: CheckBoxPreference
- private lateinit var prefPictureUploadsSourcePath: Preference
- private lateinit var prefPictureUploadsBehaviour: ListPreference
- private lateinit var prefPictureUploadsAccount: ListPreference
-
- private lateinit var picturesViewModel: SettingsPictureUploadsViewModel
- private lateinit var context: Context
-
- private val exampleUploadPath = "/Upload/Path"
- private val exampleUploadSourcePath = "/Upload/Source/Path"
- private val listOfLoggedAccounts = arrayOf("first", "second", "third")
-
- @Before
- fun setUp() {
- context = InstrumentationRegistry.getInstrumentation().targetContext
- picturesViewModel = mockk(relaxUnitFun = true)
-
- every { picturesViewModel.getPictureUploadsPath() } returns exampleUploadPath
- every { picturesViewModel.getPictureUploadsSourcePath() } returns exampleUploadSourcePath
- every { picturesViewModel.isPictureUploadEnabled() } returns false
- every { picturesViewModel.getLoggedAccountNames() } returns listOfLoggedAccounts
- every { picturesViewModel.getPictureUploadsAccount() } returns listOfLoggedAccounts.first()
-
- stopKoin()
-
- startKoin {
- context
- modules(
- module(override = true) {
- viewModel {
- picturesViewModel
- }
- }
- )
- }
-
- fragmentScenario = launchFragmentInContainer(themeResId = R.style.Theme_ownCloud)
- fragmentScenario.onFragment { fragment ->
- prefEnablePictureUploads = fragment.findPreference(PREF__CAMERA_PICTURE_UPLOADS_ENABLED)!!
- prefPictureUploadsPath = fragment.findPreference(PREF__CAMERA_PICTURE_UPLOADS_PATH)!!
- prefPictureUploadsOnWifi = fragment.findPreference(PREF__CAMERA_PICTURE_UPLOADS_WIFI_ONLY)!!
- prefPictureUploadsSourcePath = fragment.findPreference(PREF__CAMERA_PICTURE_UPLOADS_SOURCE)!!
- prefPictureUploadsBehaviour = fragment.findPreference(PREF__CAMERA_PICTURE_UPLOADS_BEHAVIOUR)!!
- prefPictureUploadsAccount = fragment.findPreference(PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME)!!
- }
- }
-
- @After
- fun tearDown() {
- androidx.preference.PreferenceManager.getDefaultSharedPreferences(context).edit().clear().commit()
- }
-
- @Test
- fun pictureUploadsView() {
- prefEnablePictureUploads.verifyPreference(
- keyPref = PREF__CAMERA_PICTURE_UPLOADS_ENABLED,
- titlePref = context.getString(R.string.prefs_camera_picture_upload),
- summaryPref = context.getString(R.string.prefs_camera_picture_upload_summary),
- visible = true,
- enabled = true
- )
- assertFalse(prefEnablePictureUploads.isChecked)
-
- prefPictureUploadsPath.verifyPreference(
- keyPref = PREF__CAMERA_PICTURE_UPLOADS_PATH,
- titlePref = context.getString(R.string.prefs_camera_picture_upload_path_title),
- summaryPref = exampleUploadPath,
- visible = true,
- enabled = false
- )
-
- prefPictureUploadsOnWifi.verifyPreference(
- keyPref = PREF__CAMERA_PICTURE_UPLOADS_WIFI_ONLY,
- titlePref = context.getString(R.string.prefs_camera_picture_upload_on_wifi),
- visible = true,
- enabled = false
- )
- assertFalse(prefPictureUploadsOnWifi.isChecked)
-
- val comment =
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) context.getString(
- R.string.prefs_camera_upload_source_path_title_optional
- )
- else context.getString(
- R.string.prefs_camera_upload_source_path_title_required
- )
- prefPictureUploadsSourcePath.verifyPreference(
- keyPref = PREF__CAMERA_PICTURE_UPLOADS_SOURCE,
- titlePref = String.format(prefPictureUploadsSourcePath.title.toString(), comment),
- summaryPref = exampleUploadSourcePath,
- visible = true,
- enabled = false
- )
-
- prefPictureUploadsBehaviour.verifyPreference(
- keyPref = PREF__CAMERA_PICTURE_UPLOADS_BEHAVIOUR,
- titlePref = context.getString(R.string.prefs_camera_upload_behaviour_title),
- summaryPref = context.getString(R.string.pref_behaviour_entries_keep_file),
- visible = true,
- enabled = false
- )
-
- prefPictureUploadsAccount.verifyPreference(
- keyPref = PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME,
- titlePref = context.getString(R.string.prefs_picture_upload_account),
- summaryPref = prefPictureUploadsAccount.context.getString(androidx.preference.R.string.not_set),
- visible = true,
- enabled = false
- )
- }
-
- @Test
- fun enablePictureUploads() {
- firstEnablePictureUploads()
- checkPreferencesEnabled(true)
- checkPreferencesInitialized()
- }
-
- @Test
- fun disablePictureUploadsAccept() {
- firstEnablePictureUploads()
- onView(withText(R.string.prefs_camera_picture_upload)).perform(click())
- onView(withText(R.string.common_yes)).perform(click())
- checkPreferencesEnabled(false)
- checkPreferencesReset()
- }
-
- @Test
- fun disablePictureUploadsRefuse() {
- firstEnablePictureUploads()
- onView(withText(R.string.prefs_camera_picture_upload)).perform(click())
- onView(withText(R.string.common_no)).perform(click())
- checkPreferencesEnabled(true)
- }
-
- @Ignore("Makes the subsequent tests crash. Will have to be updated when changed to Android's file picker")
- @Test
- fun openPictureUploadSourcePathPicker() {
- firstEnablePictureUploads()
- val cameraFolder = Environment.getExternalStoragePublicDirectory(
- Environment.DIRECTORY_DCIM
- ).absolutePath + "/Camera"
- Intents.init()
- onView(
- withText(
- String.format(
- context.getString(R.string.prefs_camera_upload_source_path_title),
- context.getString(R.string.prefs_camera_upload_source_path_title_required)
- )
- )
- ).perform(click())
- intended(hasComponent(LocalFolderPickerActivity::class.java.name))
- hasExtra(LocalFolderPickerActivity.EXTRA_PATH, cameraFolder)
- Intents.release()
- onView(withText(android.R.string.cancel)).perform(click())
- }
-
- private fun firstEnablePictureUploads() {
- onView(withText(R.string.prefs_camera_picture_upload)).perform(click())
- onView(withText(android.R.string.ok)).perform(click())
- }
-
- private fun checkPreferencesEnabled(enabled: Boolean) {
- assertEquals(enabled, prefEnablePictureUploads.isChecked)
- assertEquals(enabled, prefPictureUploadsPath.isEnabled)
- assertEquals(enabled, prefPictureUploadsOnWifi.isEnabled)
- assertEquals(enabled, prefPictureUploadsSourcePath.isEnabled)
- assertEquals(enabled, prefPictureUploadsBehaviour.isEnabled)
- assertEquals(enabled, prefPictureUploadsAccount.isEnabled)
- }
-
- private fun checkPreferencesInitialized() {
- assertEquals(listOfLoggedAccounts.first(), prefPictureUploadsAccount.summary)
- assertEquals(exampleUploadPath, prefPictureUploadsPath.summary)
- }
-
- private fun checkPreferencesReset() {
- assertEquals(context.getString(androidx.preference.R.string.not_set), prefPictureUploadsAccount.summary)
- assertEquals(picturesViewModel.getPictureUploadsPath(), prefPictureUploadsPath.summary)
- }
-}
diff --git a/owncloudApp/src/androidTest/java/com/owncloud/android/settings/camerauploads/SettingsVideoUploadsFragmentTest.kt b/owncloudApp/src/androidTest/java/com/owncloud/android/settings/camerauploads/SettingsVideoUploadsFragmentTest.kt
deleted file mode 100644
index 74e787e46b0..00000000000
--- a/owncloudApp/src/androidTest/java/com/owncloud/android/settings/camerauploads/SettingsVideoUploadsFragmentTest.kt
+++ /dev/null
@@ -1,250 +0,0 @@
-/**
- * ownCloud Android client application
- *
- * @author Juan Carlos Garrote Gascón
- *
- * Copyright (C) 2021 ownCloud GmbH.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.owncloud.android.settings.camerauploads
-
-import android.content.Context
-import android.os.Build
-import android.os.Environment
-import androidx.fragment.app.testing.FragmentScenario
-import androidx.fragment.app.testing.launchFragmentInContainer
-import androidx.preference.CheckBoxPreference
-import androidx.preference.ListPreference
-import androidx.preference.Preference
-import androidx.preference.SwitchPreferenceCompat
-import androidx.test.espresso.Espresso.onView
-import androidx.test.espresso.action.ViewActions.click
-import androidx.test.espresso.intent.Intents
-import androidx.test.espresso.intent.Intents.intended
-import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
-import androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra
-import androidx.test.espresso.matcher.ViewMatchers.withText
-import androidx.test.platform.app.InstrumentationRegistry
-import com.owncloud.android.R
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_BEHAVIOUR
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_ENABLED
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_PATH
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_SOURCE
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_WIFI_ONLY
-import com.owncloud.android.presentation.ui.settings.fragments.SettingsVideoUploadsFragment
-import com.owncloud.android.presentation.viewmodels.settings.SettingsVideoUploadsViewModel
-import com.owncloud.android.ui.activity.LocalFolderPickerActivity
-import com.owncloud.android.utils.matchers.verifyPreference
-import io.mockk.every
-import io.mockk.mockk
-import org.junit.After
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertFalse
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Test
-import org.koin.androidx.viewmodel.dsl.viewModel
-import org.koin.core.context.startKoin
-import org.koin.core.context.stopKoin
-import org.koin.dsl.module
-
-class SettingsVideoUploadsFragmentTest {
-
- private lateinit var fragmentScenario: FragmentScenario
-
- private lateinit var prefEnableVideoUploads: SwitchPreferenceCompat
- private lateinit var prefVideoUploadsPath: Preference
- private lateinit var prefVideoUploadsOnWifi: CheckBoxPreference
- private lateinit var prefVideoUploadsSourcePath: Preference
- private lateinit var prefVideoUploadsBehaviour: ListPreference
- private lateinit var prefVideoUploadsAccount: ListPreference
-
- private lateinit var videosViewModel: SettingsVideoUploadsViewModel
- private lateinit var context: Context
-
- private val exampleUploadPath = "/Upload/Path"
- private val exampleUploadSourcePath = "/Upload/Source/Path"
- private val listOfLoggedAccounts = arrayOf("first", "second", "third")
-
- @Before
- fun setUp() {
- context = InstrumentationRegistry.getInstrumentation().targetContext
- videosViewModel = mockk(relaxUnitFun = true)
-
- every { videosViewModel.getVideoUploadsPath() } returns exampleUploadPath
- every { videosViewModel.getVideoUploadsSourcePath() } returns exampleUploadSourcePath
- every { videosViewModel.isVideoUploadEnabled() } returns false
- every { videosViewModel.getLoggedAccountNames() } returns listOfLoggedAccounts
- every { videosViewModel.getVideoUploadsAccount() } returns listOfLoggedAccounts.first()
-
- stopKoin()
-
- startKoin {
- context
- modules(
- module(override = true) {
- viewModel {
- videosViewModel
- }
- }
- )
- }
-
- fragmentScenario = launchFragmentInContainer(themeResId = R.style.Theme_ownCloud)
- fragmentScenario.onFragment { fragment ->
- prefEnableVideoUploads = fragment.findPreference(PREF__CAMERA_VIDEO_UPLOADS_ENABLED)!!
- prefVideoUploadsPath = fragment.findPreference(PREF__CAMERA_VIDEO_UPLOADS_PATH)!!
- prefVideoUploadsOnWifi = fragment.findPreference(PREF__CAMERA_VIDEO_UPLOADS_WIFI_ONLY)!!
- prefVideoUploadsSourcePath = fragment.findPreference(PREF__CAMERA_VIDEO_UPLOADS_SOURCE)!!
- prefVideoUploadsBehaviour = fragment.findPreference(PREF__CAMERA_VIDEO_UPLOADS_BEHAVIOUR)!!
- prefVideoUploadsAccount = fragment.findPreference(PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME)!!
- }
- }
-
- @After
- fun tearDown() {
- androidx.preference.PreferenceManager.getDefaultSharedPreferences(context).edit().clear().commit()
- }
-
- @Test
- fun videoUploadsView() {
- prefEnableVideoUploads.verifyPreference(
- keyPref = PREF__CAMERA_VIDEO_UPLOADS_ENABLED,
- titlePref = context.getString(R.string.prefs_camera_video_upload),
- summaryPref = context.getString(R.string.prefs_camera_video_upload_summary),
- visible = true,
- enabled = true
- )
- assertFalse(prefEnableVideoUploads.isChecked)
-
- prefVideoUploadsPath.verifyPreference(
- keyPref = PREF__CAMERA_VIDEO_UPLOADS_PATH,
- titlePref = context.getString(R.string.prefs_camera_video_upload_path_title),
- summaryPref = exampleUploadPath,
- visible = true,
- enabled = false
- )
-
- prefVideoUploadsOnWifi.verifyPreference(
- keyPref = PREF__CAMERA_VIDEO_UPLOADS_WIFI_ONLY,
- titlePref = context.getString(R.string.prefs_camera_video_upload_on_wifi),
- visible = true,
- enabled = false
- )
- assertFalse(prefVideoUploadsOnWifi.isChecked)
-
- val comment =
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) context.getString(
- R.string.prefs_camera_upload_source_path_title_optional
- )
- else context.getString(
- R.string.prefs_camera_upload_source_path_title_required
- )
- prefVideoUploadsSourcePath.verifyPreference(
- keyPref = PREF__CAMERA_VIDEO_UPLOADS_SOURCE,
- titlePref = String.format(prefVideoUploadsSourcePath.title.toString(), comment),
- summaryPref = exampleUploadSourcePath,
- visible = true,
- enabled = false
- )
-
- prefVideoUploadsBehaviour.verifyPreference(
- keyPref = PREF__CAMERA_VIDEO_UPLOADS_BEHAVIOUR,
- titlePref = context.getString(R.string.prefs_camera_upload_behaviour_title),
- summaryPref = context.getString(R.string.pref_behaviour_entries_keep_file),
- visible = true,
- enabled = false
- )
-
- prefVideoUploadsAccount.verifyPreference(
- keyPref = PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME,
- titlePref = context.getString(R.string.prefs_video_upload_account),
- summaryPref = prefVideoUploadsAccount.context.getString(androidx.preference.R.string.not_set),
- visible = true,
- enabled = false
- )
- }
-
- @Test
- fun enableVideoUploads() {
- firstEnableVideoUploads()
- checkPreferencesEnabled(true)
- checkPreferencesInitialized()
- }
-
- @Test
- fun disableVideoUploadsAccept() {
- firstEnableVideoUploads()
- onView(withText(R.string.prefs_camera_video_upload)).perform(click())
- onView(withText(R.string.common_yes)).perform(click())
- checkPreferencesEnabled(false)
- checkPreferencesReset()
- }
-
- @Test
- fun disableVideoUploadsRefuse() {
- firstEnableVideoUploads()
- onView(withText(R.string.prefs_camera_video_upload)).perform(click())
- onView(withText(R.string.common_no)).perform(click())
- checkPreferencesEnabled(true)
- }
-
- @Ignore("Makes the subsequent tests crash. Will have to be updated when changed to Android's file picker")
- @Test
- fun openVideoUploadSourcePathPicker() {
- firstEnableVideoUploads()
- val cameraFolder = Environment.getExternalStoragePublicDirectory(
- Environment.DIRECTORY_DCIM
- ).absolutePath + "/Camera"
- Intents.init()
- onView(
- withText(
- String.format(
- context.getString(R.string.prefs_camera_upload_source_path_title),
- context.getString(R.string.prefs_camera_upload_source_path_title_required)
- )
- )
- ).perform(click())
- intended(hasComponent(LocalFolderPickerActivity::class.java.name))
- hasExtra(LocalFolderPickerActivity.EXTRA_PATH, cameraFolder)
- Intents.release()
- onView(withText(android.R.string.cancel)).perform(click())
- }
-
- private fun firstEnableVideoUploads() {
- onView(withText(R.string.prefs_camera_video_upload)).perform(click())
- onView(withText(android.R.string.ok)).perform(click())
- }
-
- private fun checkPreferencesEnabled(enabled: Boolean) {
- assertEquals(enabled, prefEnableVideoUploads.isChecked)
- assertEquals(enabled, prefVideoUploadsPath.isEnabled)
- assertEquals(enabled, prefVideoUploadsOnWifi.isEnabled)
- assertEquals(enabled, prefVideoUploadsSourcePath.isEnabled)
- assertEquals(enabled, prefVideoUploadsBehaviour.isEnabled)
- assertEquals(enabled, prefVideoUploadsAccount.isEnabled)
- }
-
- private fun checkPreferencesInitialized() {
- assertEquals(listOfLoggedAccounts.first(), prefVideoUploadsAccount.summary)
- assertEquals(exampleUploadPath, prefVideoUploadsPath.summary)
- }
-
- private fun checkPreferencesReset() {
- assertEquals(context.getString(androidx.preference.R.string.not_set), prefVideoUploadsAccount.summary)
- assertEquals(videosViewModel.getVideoUploadsPath(), prefVideoUploadsPath.summary)
- }
-}
diff --git a/owncloudApp/src/main/AndroidManifest.xml b/owncloudApp/src/main/AndroidManifest.xml
index 5ef13af44b5..798a25c43dd 100644
--- a/owncloudApp/src/main/AndroidManifest.xml
+++ b/owncloudApp/src/main/AndroidManifest.xml
@@ -143,10 +143,6 @@
android:name=".files.services.RetryDownloadJobService"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
-
@@ -213,24 +209,11 @@
android:name=".ui.activity.WhatsNewActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
-
-
-
-
-
-
-
-
-
-
diff --git a/owncloudApp/src/main/java/com/owncloud/android/broadcastreceivers/ConnectivityActionReceiver.java b/owncloudApp/src/main/java/com/owncloud/android/broadcastreceivers/ConnectivityActionReceiver.java
deleted file mode 100755
index 3255a866442..00000000000
--- a/owncloudApp/src/main/java/com/owncloud/android/broadcastreceivers/ConnectivityActionReceiver.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/**
- * ownCloud Android client application
- *
- * @author LukeOwncloud
- * @author Christian Schabesberger
- * @author David González Verdugo
- * Copyright (C) 2020 ownCloud GmbH.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.owncloud.android.broadcastreceivers;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.net.NetworkInfo;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-
-import com.owncloud.android.MainApp;
-import com.owncloud.android.db.PreferenceManager;
-import com.owncloud.android.db.UploadResult;
-import com.owncloud.android.files.services.FileUploader;
-import com.owncloud.android.files.services.TransferRequester;
-import timber.log.Timber;
-
-/**
- * Receives all connectivity action from Android OS at all times and performs
- * required OC actions. For now that are: - Signal connectivity to
- * {@link FileUploader}.
- *
- * Later can be added: - Signal connectivity to download service, deletion
- * service, ... - Handle offline mode (cf.
- * https://github.com/owncloud/android/issues/162)
- *
- * Have fun with the comments :S
- */
-public class ConnectivityActionReceiver extends BroadcastReceiver {
-
- /**
- * Magic keyword, by Google.
- *
- * {@See http://developer.android.com/intl/es/reference/android/net/wifi/WifiInfo.html#getSSID()}
- */
- private static final String UNKNOWN_SSID = "";
-
- @Override
- public void onReceive(final Context context, Intent intent) {
- // LOG ALL EVENTS:
- Timber.v("action: %s", intent.getAction());
- Timber.v("component: %s", intent.getComponent());
- Bundle extras = intent.getExtras();
- if (extras != null) {
- for (String key : extras.keySet()) {
- Timber.v("key [" + key + "]: " + extras.get(key));
- }
- } else {
- Timber.v("no extras");
- }
-
- /*
- * There is an interesting mess to process WifiManager.NETWORK_STATE_CHANGED_ACTION and
- * ConnectivityManager.CONNECTIVITY_ACTION in a simple and reliable way.
- *
- * The former triggers much more events than what we really need to know about Wifi connection.
- *
- * But there are annoying uncertainties about ConnectivityManager.CONNECTIVITY_ACTION due
- * to the deprecation of ConnectivityManager.EXTRA_NETWORK_INFO in API level 14, and the absence
- * of ConnectivityManager.EXTRA_NETWORK_TYPE until API level 17. Dear Google, how should we
- * handle API levels 14 to 16?
- *
- * In the end maybe we need to keep in memory the current knowledge about connectivity
- * and update it taking into account several Intents received in a row
- *
- * But first let's try something "simple" to keep a basic retry of camera uploads in
- * version 1.9.2, similar to the existent until 1.9.1. To be improved.
- */
- if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
- NetworkInfo networkInfo =
- intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
- WifiInfo wifiInfo =
- intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
- String bssid =
- intent.getStringExtra(WifiManager.EXTRA_BSSID);
- if (networkInfo.isConnected() && // not enough; see (*) right below
- wifiInfo != null &&
- !UNKNOWN_SSID.equals(wifiInfo.getSSID().toLowerCase()) &&
- bssid != null
- ) {
- Timber.d("WiFi connected");
-
- wifiConnected(context);
- } else {
- // TODO tons of things to check to conclude disconnection;
- // TODO maybe alternative commented below, based on CONNECTIVITY_ACTION is better
- Timber.d("WiFi disconnected ... but don't know if right now");
- }
- }
- // (*) When WiFi is lost, an Intent with network state CONNECTED and SSID "" is
- // received right before another Intent with network state DISCONNECTED; needs to
- // be differentiated of a new Wifi connection.
- //
- // Besides, with a new connection two Intents are received, having only the second the extra
- // WifiManager.EXTRA_BSSID, with the BSSID of the access point accessed.
- //
- // Not sure if this protocol is exact, since it's not documented. Only found mild references in
- // - http://developer.android.com/intl/es/reference/android/net/wifi/WifiInfo.html#getSSID()
- // - http://developer.android.com/intl/es/reference/android/net/wifi/WifiManager.html#EXTRA_BSSID
- // and reproduced in Nexus 5 with Android 6.
-
- // TODO check if this helps us
- /*
- * Possible alternative attending ConnectivityManager.CONNECTIVITY_ACTION.
- *
- * Let's see what QA has to say
- *
- if(intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
- NetworkInfo networkInfo = intent.getParcelableExtra(
- ConnectivityManager.EXTRA_NETWORK_INFO // deprecated in API 14
- );
- int networkType = intent.getIntExtra(
- ConnectivityManager.EXTRA_NETWORK_TYPE, // only from API level 17
- -1
- );
- boolean couldBeWifiAction =
- (networkInfo == null && networkType < 0) || // cases of lack of info
- networkInfo.getType() == ConnectivityManager.TYPE_WIFI ||
- networkType == ConnectivityManager.TYPE_WIFI;
-
- if (couldBeWifiAction) {
- if (ConnectivityUtils.isAppConnectedViaWiFi(context)) {
- Timber.d("WiFi connected");
- wifiConnected(context);
- } else {
- Timber.d("WiFi disconnected");
- wifiDisconnected(context);
- }
- } /* else, CONNECTIVIY_ACTION is (probably) about other network interface (mobile, bluetooth, ...)
- }
- */
- }
-
- private void wifiConnected(Context context) {
- // for the moment, only recovery of camera uploads, similar to behaviour in release 1.9.1
- if (
- (PreferenceManager.cameraPictureUploadEnabled(context) &&
- PreferenceManager.cameraPictureUploadViaWiFiOnly(context)) ||
- (PreferenceManager.cameraVideoUploadEnabled(context) &&
- PreferenceManager.cameraVideoUploadViaWiFiOnly(context))
- ) {
-
- Handler h = new Handler(Looper.getMainLooper());
- h.postDelayed(() -> {
- Timber.d("Requesting retry of camera uploads (& friends)");
- TransferRequester requester = new TransferRequester();
-
- requester.retryFailedUploads(
- MainApp.Companion.getAppContext(),
- null,
- UploadResult.DELAYED_FOR_WIFI, // for the rest of enqueued when Wifi fell
- true
- );
- },
- 500
- );
- }
- }
-}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/datamodel/CameraUploadsSyncStorageManager.java b/owncloudApp/src/main/java/com/owncloud/android/datamodel/CameraUploadsSyncStorageManager.java
deleted file mode 100644
index f0adcc7c021..00000000000
--- a/owncloudApp/src/main/java/com/owncloud/android/datamodel/CameraUploadsSyncStorageManager.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/**
- * ownCloud Android client application
- *
- * @author David González Verdugo
- * Copyright (C) 2017 ownCloud GmbH.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.owncloud.android.datamodel;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-
-import com.owncloud.android.db.ProviderMeta;
-import timber.log.Timber;
-
-import java.util.Observable;
-
-public class CameraUploadsSyncStorageManager extends Observable {
-
- private ContentResolver mContentResolver;
-
- public CameraUploadsSyncStorageManager(ContentResolver contentResolver) {
- if (contentResolver == null) {
- throw new IllegalArgumentException("Cannot create an instance with a NULL contentResolver");
- }
- mContentResolver = contentResolver;
- }
-
- /**
- * Stores a camera upload sync object in DB
- *
- * @param ocCameraUploadSync Camera upload sync object to store
- * @return camera upload sync id, -1 if the insert process fails
- */
- public long storeCameraUploadSync(OCCameraUploadSync ocCameraUploadSync) {
- Timber.v("Inserting camera upload sync with timestamp of last pictures synchronization "
- + ocCameraUploadSync.getPicturesLastSync() + " and timestamp of last videos " +
- "synchronzization" + ocCameraUploadSync.getVideosLastSync());
-
- ContentValues cv = new ContentValues();
- cv.put(ProviderMeta.ProviderTableMeta.PICTURES_LAST_SYNC_TIMESTAMP, ocCameraUploadSync.
- getPicturesLastSync());
- cv.put(ProviderMeta.ProviderTableMeta.VIDEOS_LAST_SYNC_TIMESTAMP, ocCameraUploadSync.
- getVideosLastSync());
-
- Uri result = getDB().insert(ProviderMeta.ProviderTableMeta.CONTENT_URI_CAMERA_UPLOADS_SYNC,
- cv);
-
- Timber.d("storeUpload returns with: " + result + " for camera upload sync " + ocCameraUploadSync.getId());
- if (result == null) {
- Timber.e("Failed to insert camera upload sync " + ocCameraUploadSync.getId() + " into camera uploads sync db.");
- return -1;
- } else {
- long new_id = Long.parseLong(result.getPathSegments().get(1));
- ocCameraUploadSync.setId(new_id);
- notifyObserversNow();
- return new_id;
- }
- }
-
- /**
- * Update a camera upload sync object in DB.
- *
- * @param ocCameraUploadSync Camera upload sync object with state to update
- * @return num of updated camera upload sync
- */
- public int updateCameraUploadSync(OCCameraUploadSync ocCameraUploadSync) {
- Timber.v("Updating %s", ocCameraUploadSync.getId());
-
- ContentValues cv = new ContentValues();
- cv.put(ProviderMeta.ProviderTableMeta.PICTURES_LAST_SYNC_TIMESTAMP, ocCameraUploadSync.
- getPicturesLastSync());
- cv.put(ProviderMeta.ProviderTableMeta.VIDEOS_LAST_SYNC_TIMESTAMP, ocCameraUploadSync.
- getVideosLastSync());
-
- int result = getDB().update(ProviderMeta.ProviderTableMeta.CONTENT_URI_CAMERA_UPLOADS_SYNC,
- cv,
- ProviderMeta.ProviderTableMeta._ID + "=?",
- new String[]{String.valueOf(ocCameraUploadSync.getId())}
- );
-
- Timber.d("updateCameraUploadSync returns with: " + result + " for camera upload sync: " +
- ocCameraUploadSync.getId());
- if (result != 1) {
- Timber.e("Failed to update item " + ocCameraUploadSync.getId() + " into " +
- "camera upload sync db.");
- } else {
- notifyObserversNow();
- }
-
- return result;
- }
-
- /**
- * Retrieves a camera upload sync object from DB
- * @param selection filter declaring which rows to return, formatted as an SQL WHERE clause
- * @param selectionArgs include ?s in selection, which will be replaced by the values from here
- * @param sortOrder How to order the rows, formatted as an SQL ORDER BY clause
- * @return camera upload sync object
- */
- public OCCameraUploadSync getCameraUploadSync(String selection, String[] selectionArgs,
- String sortOrder) {
- Cursor c = getDB().query(
- ProviderMeta.ProviderTableMeta.CONTENT_URI_CAMERA_UPLOADS_SYNC,
- null,
- selection,
- selectionArgs,
- sortOrder
- );
-
- OCCameraUploadSync ocCameraUploadSync = null;
-
- if (c.moveToFirst()) {
- ocCameraUploadSync = createOCCameraUploadSyncFromCursor(c);
- if (ocCameraUploadSync == null) {
- Timber.e("Camera upload sync could not be created from cursor");
- }
- }
-
- c.close();
-
- return ocCameraUploadSync;
- }
-
- private OCCameraUploadSync createOCCameraUploadSyncFromCursor(Cursor c) {
- OCCameraUploadSync cameraUploadSync = null;
- if (c != null) {
- long picturesLastSync = c.getLong(c.getColumnIndex(ProviderMeta.ProviderTableMeta.
- PICTURES_LAST_SYNC_TIMESTAMP));
- long videosLastSync = c.getLong(c.getColumnIndex(ProviderMeta.ProviderTableMeta.
- VIDEOS_LAST_SYNC_TIMESTAMP));
-
- cameraUploadSync = new OCCameraUploadSync(picturesLastSync, videosLastSync);
-
- cameraUploadSync.setId(c.getLong(c.getColumnIndex(ProviderMeta.ProviderTableMeta._ID)));
- }
- return cameraUploadSync;
- }
-
- private ContentResolver getDB() {
- return mContentResolver;
- }
-
- /**
- * Should be called when some value of this DB was changed. All observers
- * are informed.
- */
- private void notifyObserversNow() {
- Timber.d("notifyObserversNow");
- setChanged();
- notifyObservers();
- }
-}
\ No newline at end of file
diff --git a/owncloudApp/src/main/java/com/owncloud/android/datamodel/OCUpload.java b/owncloudApp/src/main/java/com/owncloud/android/datamodel/OCUpload.java
index 7b2bc4b1c17..de3da3690af 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/datamodel/OCUpload.java
+++ b/owncloudApp/src/main/java/com/owncloud/android/datamodel/OCUpload.java
@@ -110,7 +110,7 @@ public class OCUpload implements Parcelable {
* @param accountName Name of an ownCloud account to update the file to.
*/
public OCUpload(String localPath, String remotePath, String accountName) {
- if (localPath == null || !localPath.startsWith(File.separator)) {
+ if (localPath == null) {
throw new IllegalArgumentException("Local path must be an absolute path in the local file system");
}
if (remotePath == null || !remotePath.startsWith(File.separator)) {
diff --git a/owncloudApp/src/main/java/com/owncloud/android/db/PreferenceManager.java b/owncloudApp/src/main/java/com/owncloud/android/db/PreferenceManager.java
index 3818f938813..92ae58ee411 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/db/PreferenceManager.java
+++ b/owncloudApp/src/main/java/com/owncloud/android/db/PreferenceManager.java
@@ -23,32 +23,16 @@
package com.owncloud.android.db;
-import android.accounts.Account;
import android.content.Context;
import android.content.SharedPreferences;
-import com.owncloud.android.R;
-import com.owncloud.android.authentication.AccountUtils;
-import com.owncloud.android.files.services.FileUploader;
import com.owncloud.android.ui.activity.BiometricActivity;
import com.owncloud.android.utils.FileStorageUtils;
-import java.io.File;
-
/**
* Helper to simplify reading of Preferences all around the app
*/
public abstract class PreferenceManager {
- /**
- * Constant to access value of last path selected by the user to upload a file shared from other app.
- * Value handled by the app without direct access in the UI.
- */
- private static final String AUTO_PREF__LAST_UPLOAD_PATH = "last_upload_path";
- private static final String AUTO_PREF__SORT_ORDER_FILE_DISP = "sortOrderFileDisp";
- private static final String AUTO_PREF__SORT_ASCENDING_FILE_DISP = "sortAscendingFileDisp";
- private static final String AUTO_PREF__SORT_ORDER_UPLOAD = "sortOrderUpload";
- private static final String AUTO_PREF__SORT_ASCENDING_UPLOAD = "sortAscendingUpload";
-
// Legacy preferences - done in version 2.18
public static final String PREF__LEGACY_CLICK_DEV_MENU = "clickDeveloperMenu";
public static final String PREF__LEGACY_CAMERA_PICTURE_UPLOADS_ENABLED = "camera_picture_uploads";
@@ -60,7 +44,6 @@ public abstract class PreferenceManager {
public static final String PREF__LEGACY_CAMERA_UPLOADS_BEHAVIOUR = "camera_uploads_behaviour";
public static final String PREF__LEGACY_CAMERA_UPLOADS_SOURCE = "camera_uploads_source_path";
public static final String PREF__LEGACY_CAMERA_UPLOADS_ACCOUNT_NAME = "camera_uploads_account_name";
-
public static final String PREF__CAMERA_PICTURE_UPLOADS_ENABLED = "enable_picture_uploads";
public static final String PREF__CAMERA_VIDEO_UPLOADS_ENABLED = "enable_video_uploads";
public static final String PREF__CAMERA_PICTURE_UPLOADS_WIFI_ONLY = "picture_uploads_on_wifi";
@@ -73,10 +56,19 @@ public abstract class PreferenceManager {
public static final String PREF__CAMERA_VIDEO_UPLOADS_SOURCE = "video_uploads_source_path";
public static final String PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME = "picture_uploads_account_name";
public static final String PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME = "video_uploads_account_name";
-
+ public static final String PREF__CAMERA_PICTURE_UPLOADS_LAST_SYNC = "picture_uploads_last_sync";
+ public static final String PREF__CAMERA_VIDEO_UPLOADS_LAST_SYNC = "video_uploads_last_sync";
public static final String PREF__CAMERA_UPLOADS_DEFAULT_PATH = "/CameraUpload";
-
public static final String PREF__LEGACY_FINGERPRINT = "set_fingerprint";
+ /**
+ * Constant to access value of last path selected by the user to upload a file shared from other app.
+ * Value handled by the app without direct access in the UI.
+ */
+ private static final String AUTO_PREF__LAST_UPLOAD_PATH = "last_upload_path";
+ private static final String AUTO_PREF__SORT_ORDER_FILE_DISP = "sortOrderFileDisp";
+ private static final String AUTO_PREF__SORT_ASCENDING_FILE_DISP = "sortAscendingFileDisp";
+ private static final String AUTO_PREF__SORT_ORDER_UPLOAD = "sortOrderUpload";
+ private static final String AUTO_PREF__SORT_ASCENDING_UPLOAD = "sortAscendingUpload";
public static void migrateFingerprintToBiometricKey(Context context) {
SharedPreferences sharedPref = getDefaultSharedPreferences(context);
@@ -127,84 +119,6 @@ public static void deleteOldSettingsPreferences(Context context) {
editor.apply();
}
- public static boolean cameraPictureUploadEnabled(Context context) {
- return getDefaultSharedPreferences(context).getBoolean(PREF__CAMERA_PICTURE_UPLOADS_ENABLED, false);
- }
-
- public static boolean cameraVideoUploadEnabled(Context context) {
- return getDefaultSharedPreferences(context).getBoolean(PREF__CAMERA_VIDEO_UPLOADS_ENABLED, false);
- }
-
- public static boolean cameraPictureUploadViaWiFiOnly(Context context) {
- return getDefaultSharedPreferences(context).getBoolean(PREF__CAMERA_PICTURE_UPLOADS_WIFI_ONLY, false);
- }
-
- public static boolean cameraVideoUploadViaWiFiOnly(Context context) {
- return getDefaultSharedPreferences(context).getBoolean(PREF__CAMERA_VIDEO_UPLOADS_WIFI_ONLY, false);
- }
-
- public static CameraUploadsConfiguration getCameraUploadsConfiguration(Context context) {
- CameraUploadsConfiguration result = new CameraUploadsConfiguration();
- SharedPreferences prefs = getDefaultSharedPreferences(context);
- result.setEnabledForPictures(
- prefs.getBoolean(PREF__CAMERA_PICTURE_UPLOADS_ENABLED, false)
- );
- result.setEnabledForVideos(
- prefs.getBoolean(PREF__CAMERA_VIDEO_UPLOADS_ENABLED, false)
- );
- result.setWifiOnlyForPictures(
- prefs.getBoolean(PREF__CAMERA_PICTURE_UPLOADS_WIFI_ONLY, false)
- );
- result.setWifiOnlyForVideos(
- prefs.getBoolean(PREF__CAMERA_VIDEO_UPLOADS_WIFI_ONLY, false)
- );
- result.setUploadAccountNameForPictures(
- prefs.getString(PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME, "" )
- );
- result.setUploadAccountNameForVideos(
- prefs.getString(PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME, "" )
- );
- String uploadPath = prefs.getString(
- PREF__CAMERA_PICTURE_UPLOADS_PATH,
- PREF__CAMERA_UPLOADS_DEFAULT_PATH + File.separator
- );
- result.setUploadPathForPictures(
- uploadPath.endsWith(File.separator) ? uploadPath : uploadPath + File.separator
- );
- uploadPath = prefs.getString(
- PREF__CAMERA_VIDEO_UPLOADS_PATH,
- PREF__CAMERA_UPLOADS_DEFAULT_PATH + File.separator
- );
- result.setUploadPathForVideos(
- uploadPath.endsWith(File.separator) ? uploadPath : uploadPath + File.separator
- );
- result.setBehaviourAfterUploadPictures(
- prefs.getString(
- PREF__CAMERA_PICTURE_UPLOADS_BEHAVIOUR,
- context.getResources().getStringArray(R.array.pref_behaviour_entryValues)[0]
- )
- );
- result.setBehaviourAfterUploadVideos(
- prefs.getString(
- PREF__CAMERA_VIDEO_UPLOADS_BEHAVIOUR,
- context.getResources().getStringArray(R.array.pref_behaviour_entryValues)[0]
- )
- );
- result.setSourcePathPictures(
- prefs.getString(
- PREF__CAMERA_PICTURE_UPLOADS_SOURCE,
- CameraUploadsConfiguration.getDefaultSourcePath()
- )
- );
- result.setSourcePathVideos(
- prefs.getString(
- PREF__CAMERA_VIDEO_UPLOADS_SOURCE,
- CameraUploadsConfiguration.getDefaultSourcePath()
- )
- );
- return result;
- }
-
/**
* Gets the path where the user selected to do the last upload of a file shared from other app.
*
@@ -307,129 +221,4 @@ private static void saveIntPreference(String key, int value, Context context) {
public static SharedPreferences getDefaultSharedPreferences(Context context) {
return android.preference.PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());
}
-
- /**
- * Aggregates preferences related to camera uploads in a single object.
- */
- public static class CameraUploadsConfiguration {
-
- private boolean mEnabledForPictures;
- private boolean mEnabledForVideos;
- private boolean mWifiOnlyForPictures;
- private boolean mWifiOnlyForVideos;
- private String mUploadAccountNameForPictures;
- private String mUploadAccountNameForVideos;
- private String mUploadPathForPictures;
- private String mUploadPathForVideos;
- private String mBehaviourAfterUploadPictures;
- private String mBehaviourAfterUploadVideos;
- private String mSourcePathPictures;
- private String mSourcePathVideos;
-
- public static String getDefaultSourcePath() {
- return FileStorageUtils.getDefaultCameraSourcePath();
- }
-
- public boolean isEnabledForPictures() {
- return mEnabledForPictures;
- }
-
- public void setEnabledForPictures(boolean uploadPictures) {
- mEnabledForPictures = uploadPictures;
- }
-
- public boolean isEnabledForVideos() {
- return mEnabledForVideos;
- }
-
- public void setEnabledForVideos(boolean uploadVideos) {
- mEnabledForVideos = uploadVideos;
- }
-
- public boolean isWifiOnlyForPictures() {
- return mWifiOnlyForPictures;
- }
-
- public void setWifiOnlyForPictures(boolean wifiOnlyForPictures) {
- mWifiOnlyForPictures = wifiOnlyForPictures;
- }
-
- public boolean isWifiOnlyForVideos() {
- return mWifiOnlyForVideos;
- }
-
- public void setWifiOnlyForVideos(boolean wifiOnlyForVideos) {
- mWifiOnlyForVideos = wifiOnlyForVideos;
- }
-
- public String getUploadAccountNameForPictures() {
- return mUploadAccountNameForPictures;
- }
-
- public String getUploadAccountNameForVideos() {
- return mUploadAccountNameForVideos;
- }
-
- public void setUploadAccountNameForPictures(String uploadAccountName) {
- mUploadAccountNameForPictures = uploadAccountName;
- }
-
- public void setUploadAccountNameForVideos(String uploadAccountName) {
- mUploadAccountNameForVideos = uploadAccountName;
- }
-
- public String getUploadPathForPictures() {
- return mUploadPathForPictures;
- }
-
- public void setUploadPathForPictures(String uploadPathForPictures) {
- mUploadPathForPictures = uploadPathForPictures;
- }
-
- public String getUploadPathForVideos() {
- return mUploadPathForVideos;
- }
-
- public void setUploadPathForVideos(String uploadPathForVideos) {
- mUploadPathForVideos = uploadPathForVideos;
- }
-
- public int getBehaviourAfterUploadPictures() {
- if (mBehaviourAfterUploadPictures.equalsIgnoreCase("MOVE")) {
- return FileUploader.LOCAL_BEHAVIOUR_MOVE;
- }
- return FileUploader.LOCAL_BEHAVIOUR_FORGET; // "NOTHING"
- }
-
- public void setBehaviourAfterUploadPictures(String behaviourAfterUploadPictures) {
- mBehaviourAfterUploadPictures = behaviourAfterUploadPictures;
- }
-
- public int getBehaviourAfterUploadVideos() {
- if (mBehaviourAfterUploadVideos.equalsIgnoreCase("MOVE")) {
- return FileUploader.LOCAL_BEHAVIOUR_MOVE;
- }
- return FileUploader.LOCAL_BEHAVIOUR_FORGET; // "NOTHING"
- }
-
- public void setBehaviourAfterUploadVideos(String behaviourAfterUploadVideos) {
- mBehaviourAfterUploadVideos = behaviourAfterUploadVideos;
- }
-
- public String getSourcePathPictures() {
- return mSourcePathPictures;
- }
-
- public void setSourcePathPictures(String sourcePathPictures) {
- mSourcePathPictures = sourcePathPictures;
- }
-
- public String getSourcePathVideos() {
- return mSourcePathVideos;
- }
-
- public void setSourcePathVideos(String sourcePathVideos) {
- mSourcePathVideos = sourcePathVideos;
- }
- }
}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/CommonModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/CommonModule.kt
index 630fe6a9a3f..53d7b664993 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/CommonModule.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/CommonModule.kt
@@ -22,11 +22,11 @@ package com.owncloud.android.dependecyinjection
import com.owncloud.android.presentation.manager.AvatarManager
import com.owncloud.android.providers.AccountProvider
-import com.owncloud.android.providers.CameraUploadsHandlerProvider
import com.owncloud.android.providers.ContextProvider
import com.owncloud.android.providers.CoroutinesDispatcherProvider
import com.owncloud.android.providers.LogsProvider
import com.owncloud.android.providers.OCContextProvider
+import com.owncloud.android.providers.WorkManagerProvider
import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module
@@ -36,6 +36,6 @@ val commonModule = module {
single { CoroutinesDispatcherProvider() }
factory { OCContextProvider(androidContext()) }
single { LogsProvider(get()) }
- single { CameraUploadsHandlerProvider(androidContext()) }
+ single { WorkManagerProvider(androidContext()) }
single { AccountProvider(androidContext()) }
}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/LocalDataSourceModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/LocalDataSourceModule.kt
index 8eee467813e..e5a261e09b7 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/LocalDataSourceModule.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/LocalDataSourceModule.kt
@@ -22,9 +22,13 @@ package com.owncloud.android.dependecyinjection
import android.accounts.AccountManager
import com.owncloud.android.MainApp.Companion.accountType
+import com.owncloud.android.MainApp.Companion.dataFolder
+import com.owncloud.android.data.LocalStorageProvider
import com.owncloud.android.data.OwncloudDatabase
import com.owncloud.android.data.authentication.datasources.LocalAuthenticationDataSource
import com.owncloud.android.data.authentication.datasources.implementation.OCLocalAuthenticationDataSource
+import com.owncloud.android.data.folderbackup.datasources.FolderBackupLocalDataSource
+import com.owncloud.android.data.folderbackup.datasources.implementation.FolderBackupLocalDataSourceImpl
import com.owncloud.android.data.capabilities.datasources.LocalCapabilitiesDataSource
import com.owncloud.android.data.capabilities.datasources.implementation.OCLocalCapabilitiesDataSource
import com.owncloud.android.data.preferences.datasources.SharedPreferencesProvider
@@ -42,11 +46,14 @@ val localDataSourceModule = module {
single { OwncloudDatabase.getDatabase(androidContext()).capabilityDao() }
single { OwncloudDatabase.getDatabase(androidContext()).shareDao() }
single { OwncloudDatabase.getDatabase(androidContext()).userDao() }
+ single { OwncloudDatabase.getDatabase(androidContext()).folderBackUpDao() }
single { SharedPreferencesProviderImpl(get()) }
+ single { LocalStorageProvider(dataFolder) }
factory { OCLocalAuthenticationDataSource(androidContext(), get(), get(), accountType) }
factory { OCLocalCapabilitiesDataSource(get()) }
factory { OCLocalShareDataSource(get()) }
factory { OCLocalUserDataSource(get()) }
+ factory { FolderBackupLocalDataSourceImpl(get()) }
}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RepositoryModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RepositoryModule.kt
index 7ce3d8fa084..e61ff9dddb3 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RepositoryModule.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RepositoryModule.kt
@@ -22,6 +22,7 @@ package com.owncloud.android.dependecyinjection
import com.owncloud.android.data.authentication.repository.OCAuthenticationRepository
import com.owncloud.android.data.capabilities.repository.OCCapabilityRepository
import com.owncloud.android.data.files.repository.OCFileRepository
+import com.owncloud.android.data.folderbackup.FolderBackupRepositoryImpl
import com.owncloud.android.data.oauth.OAuthRepositoryImpl
import com.owncloud.android.data.server.repository.OCServerInfoRepository
import com.owncloud.android.data.sharing.sharees.repository.OCShareeRepository
@@ -29,6 +30,7 @@ import com.owncloud.android.data.sharing.shares.repository.OCShareRepository
import com.owncloud.android.data.user.repository.OCUserRepository
import com.owncloud.android.domain.authentication.AuthenticationRepository
import com.owncloud.android.domain.authentication.oauth.OAuthRepository
+import com.owncloud.android.domain.camerauploads.FolderBackupRepository
import com.owncloud.android.domain.capabilities.CapabilityRepository
import com.owncloud.android.domain.files.FileRepository
import com.owncloud.android.domain.server.ServerInfoRepository
@@ -46,4 +48,5 @@ val repositoryModule = module {
factory { OCShareRepository(get(), get()) }
factory { OCUserRepository(get(), get()) }
factory { OAuthRepositoryImpl(get()) }
+ factory { FolderBackupRepositoryImpl(get()) }
}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt
index 98bcd9df194..a6be0bf81af 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt
@@ -26,6 +26,13 @@ import com.owncloud.android.domain.authentication.usecases.GetBaseUrlUseCase
import com.owncloud.android.domain.authentication.usecases.LoginBasicAsyncUseCase
import com.owncloud.android.domain.authentication.usecases.LoginOAuthAsyncUseCase
import com.owncloud.android.domain.authentication.usecases.SupportsOAuth2UseCase
+import com.owncloud.android.domain.camerauploads.usecases.GetCameraUploadsConfigurationUseCase
+import com.owncloud.android.domain.camerauploads.usecases.GetPictureUploadsConfigurationStreamUseCase
+import com.owncloud.android.domain.camerauploads.usecases.GetVideoUploadsConfigurationStreamUseCase
+import com.owncloud.android.domain.camerauploads.usecases.ResetPictureUploadsUseCase
+import com.owncloud.android.domain.camerauploads.usecases.ResetVideoUploadsUseCase
+import com.owncloud.android.domain.camerauploads.usecases.SavePictureUploadsConfigurationUseCase
+import com.owncloud.android.domain.camerauploads.usecases.SaveVideoUploadsConfigurationUseCase
import com.owncloud.android.domain.capabilities.usecases.GetCapabilitiesAsLiveDataUseCase
import com.owncloud.android.domain.capabilities.usecases.GetStoredCapabilitiesUseCase
import com.owncloud.android.domain.capabilities.usecases.RefreshCapabilitiesFromServerAsyncUseCase
@@ -81,4 +88,13 @@ val useCaseModule = module {
// Server
factory { GetServerInfoAsyncUseCase(get()) }
+
+ // Camera Uploads
+ factory { GetCameraUploadsConfigurationUseCase(get()) }
+ factory { SavePictureUploadsConfigurationUseCase(get()) }
+ factory { SaveVideoUploadsConfigurationUseCase(get()) }
+ factory { ResetPictureUploadsUseCase(get()) }
+ factory { ResetVideoUploadsUseCase(get()) }
+ factory { GetPictureUploadsConfigurationStreamUseCase(get()) }
+ factory { GetVideoUploadsConfigurationStreamUseCase(get()) }
}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt
index a39f6ca5666..dbd852791e1 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt
@@ -32,6 +32,7 @@ import com.owncloud.android.presentation.viewmodels.settings.SettingsSecurityVie
import com.owncloud.android.presentation.viewmodels.settings.SettingsVideoUploadsViewModel
import com.owncloud.android.presentation.viewmodels.settings.SettingsViewModel
import com.owncloud.android.presentation.viewmodels.sharing.OCShareViewModel
+import com.owncloud.android.ui.dialog.RemoveAccountDialogViewModel
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
@@ -53,6 +54,7 @@ val viewModelModule = module {
viewModel { SettingsSecurityViewModel(get()) }
viewModel { SettingsLogsViewModel(get(), get()) }
viewModel { SettingsMoreViewModel(get()) }
- viewModel { SettingsPictureUploadsViewModel(get(), get(), get()) }
- viewModel { SettingsVideoUploadsViewModel(get(), get(), get()) }
+ viewModel { SettingsPictureUploadsViewModel(get(), get(), get(), get(), get(), get()) }
+ viewModel { SettingsVideoUploadsViewModel(get(), get(), get(), get(), get(), get()) }
+ viewModel { RemoveAccountDialogViewModel(get(), get(), get(), get()) }
}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/extensions/ThrowableExt.kt b/owncloudApp/src/main/java/com/owncloud/android/extensions/ThrowableExt.kt
index 053bea11e5a..4a30bc4ae34 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/extensions/ThrowableExt.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/extensions/ThrowableExt.kt
@@ -27,6 +27,7 @@ import com.owncloud.android.domain.exceptions.BadOcVersionException
import com.owncloud.android.domain.exceptions.FileNotFoundException
import com.owncloud.android.domain.exceptions.IncorrectAddressException
import com.owncloud.android.domain.exceptions.InstanceNotConfiguredException
+import com.owncloud.android.domain.exceptions.LocalFileNotFoundException
import com.owncloud.android.domain.exceptions.NoConnectionWithServerException
import com.owncloud.android.domain.exceptions.NoNetworkConnectionException
import com.owncloud.android.domain.exceptions.OAuth2ErrorAccessDeniedException
@@ -68,6 +69,7 @@ fun Throwable.parseError(
is AccountNotNewException -> resources.getString(R.string.auth_account_not_new)
is AccountNotTheSameException -> resources.getString(R.string.auth_account_not_the_same)
is RedirectToNonSecureException -> resources.getString(R.string.auth_redirect_non_secure_connection_title)
+ is LocalFileNotFoundException -> resources.getString(R.string.local_file_not_found_toast)
else -> resources.getString(R.string.common_error_unknown)
}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/files/services/CameraUploadsHandler.java b/owncloudApp/src/main/java/com/owncloud/android/files/services/CameraUploadsHandler.java
deleted file mode 100644
index 6d4c5fccaaf..00000000000
--- a/owncloudApp/src/main/java/com/owncloud/android/files/services/CameraUploadsHandler.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * ownCloud Android client application
- *
- * @author David González Verdugo
- * @author Juan Carlos Garrote Gascón
- *
- * Copyright (C) 2017 ownCloud GmbH.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.owncloud.android.files.services;
-
-import android.app.job.JobInfo;
-import android.app.job.JobScheduler;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.PersistableBundle;
-
-import com.owncloud.android.datamodel.CameraUploadsSyncStorageManager;
-import com.owncloud.android.datamodel.OCCameraUploadSync;
-import com.owncloud.android.db.PreferenceManager.CameraUploadsConfiguration;
-import com.owncloud.android.utils.Extras;
-import timber.log.Timber;
-
-/**
- * Schedule the periodic job responsible for camera uploads and initialize the required
- * information, as long as it matches the configuration for camera uploads
- */
-public class CameraUploadsHandler {
-
- private static final long MILLISECONDS_INTERVAL_CAMERA_UPLOAD = 900000;
-
- // It needs to be always the same so that the previous job is removed and replaced with a new one with the recent
- // configuration
- private static final int JOB_ID_CAMERA_UPLOAD = 1;
-
- private CameraUploadsConfiguration mCameraUploadsConfig; // Camera uploads configuration, set by the user
-
- public CameraUploadsHandler(CameraUploadsConfiguration cameraUploadsConfiguration) {
- mCameraUploadsConfig = cameraUploadsConfiguration;
- }
-
- /**
- * Schedule a periodic job to check pictures and videos to be uploaded
- */
- public void scheduleCameraUploadsSyncJob(Context context) {
- // DB Connection
- CameraUploadsSyncStorageManager cameraUploadsSyncStorageManager = new
- CameraUploadsSyncStorageManager(context.getContentResolver());
-
- OCCameraUploadSync ocCameraUploadSync = cameraUploadsSyncStorageManager.
- getCameraUploadSync(null, null, null);
-
- // Initialize synchronization timestamps for pictures/videos, if needed
- if (ocCameraUploadSync == null ||
- ocCameraUploadSync.getPicturesLastSync() == 0 ||
- ocCameraUploadSync.getVideosLastSync() == 0) {
-
- initializeCameraUploadSync(cameraUploadsSyncStorageManager, ocCameraUploadSync);
- }
-
- ComponentName serviceComponent = new ComponentName(context, CameraUploadsSyncJobService.class);
- JobInfo.Builder builder;
-
- builder = new JobInfo.Builder(JOB_ID_CAMERA_UPLOAD, serviceComponent);
-
- builder.setPersisted(true);
-
- // Execute job every 15 minutes
- builder.setPeriodic(MILLISECONDS_INTERVAL_CAMERA_UPLOAD);
-
- // Extra data
- PersistableBundle extras = new PersistableBundle();
-
- extras.putInt(Extras.EXTRA_CAMERA_UPLOADS_SYNC_JOB_ID, JOB_ID_CAMERA_UPLOAD);
-
- if (mCameraUploadsConfig.isEnabledForPictures()) {
- extras.putString(Extras.EXTRA_CAMERA_UPLOADS_PICTURES_PATH, mCameraUploadsConfig.
- getUploadPathForPictures());
- extras.putString(Extras.EXTRA_CAMERA_UPLOADS_PICTURES_SOURCE_PATH, mCameraUploadsConfig.
- getSourcePathPictures());
- extras.putInt(Extras.EXTRA_CAMERA_UPLOADS_PICTURES_BEHAVIOR_AFTER_UPLOAD, mCameraUploadsConfig.
- getBehaviourAfterUploadPictures());
- extras.putString(Extras.EXTRA_CAMERA_UPLOADS_PICTURES_ACCOUNT, mCameraUploadsConfig.
- getUploadAccountNameForPictures());
- }
-
- if (mCameraUploadsConfig.isEnabledForVideos()) {
- extras.putString(Extras.EXTRA_CAMERA_UPLOADS_VIDEOS_PATH, mCameraUploadsConfig.
- getUploadPathForVideos());
- extras.putString(Extras.EXTRA_CAMERA_UPLOADS_VIDEOS_SOURCE_PATH, mCameraUploadsConfig.
- getSourcePathVideos());
- extras.putInt(Extras.EXTRA_CAMERA_UPLOADS_VIDEOS_BEHAVIOR_AFTER_UPLOAD, mCameraUploadsConfig.
- getBehaviourAfterUploadVideos());
- extras.putString(Extras.EXTRA_CAMERA_UPLOADS_VIDEOS_ACCOUNT, mCameraUploadsConfig.
- getUploadAccountNameForVideos());
- }
-
- builder.setExtras(extras);
-
- Timber.d("Scheduling a CameraUploadsSyncJobService");
-
- JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
-
- jobScheduler.schedule(builder.build());
- }
-
- /**
- * Initialize the timestamps for upload pictures/videos. These timestamps define the start of the
- * period in which to check the pictures/videos saved, discarding those created before enabling
- * Camera Uploads feature
- */
- private void initializeCameraUploadSync(CameraUploadsSyncStorageManager cameraUploadsSyncStorageManager,
- OCCameraUploadSync ocCameraUploadSync) {
-
- // Set synchronization timestamps not needed
- if (!mCameraUploadsConfig.isEnabledForPictures() && !mCameraUploadsConfig.isEnabledForVideos()) {
- return;
- }
-
- long timeStamp = System.currentTimeMillis();
-
- if (ocCameraUploadSync == null) { // No synchronization timestamp for pictures/videos yet
-
- long firstPicturesTimeStamp = mCameraUploadsConfig.isEnabledForPictures() ? timeStamp : 0;
- long firstVideosTimeStamp = mCameraUploadsConfig.isEnabledForVideos() ? timeStamp : 0;
-
- // Initialize synchronization timestamp for pictures or videos in database
- OCCameraUploadSync firstOcCameraUploadSync = new OCCameraUploadSync(firstPicturesTimeStamp,
- firstVideosTimeStamp);
-
- Timber.d("Storing synchronization timestamp in database");
-
- cameraUploadsSyncStorageManager.storeCameraUploadSync(firstOcCameraUploadSync);
-
- } else {
-
- if (ocCameraUploadSync.getPicturesLastSync() == 0 && mCameraUploadsConfig.isEnabledForPictures()) {
- // Pictures synchronization timestamp not initialized yet, initialize it
- ocCameraUploadSync.setPicturesLastSync(timeStamp);
- }
-
- if (ocCameraUploadSync.getVideosLastSync() == 0 && mCameraUploadsConfig.isEnabledForVideos()) {
- // Videos synchronization timestamp not initialized yet, initialize it
- ocCameraUploadSync.setVideosLastSync(timeStamp);
- }
-
- cameraUploadsSyncStorageManager.updateCameraUploadSync(ocCameraUploadSync);
- }
- }
-
- /**
- * Update timestamp (in milliseconds) from which to start checking pictures to upload
- *
- * @param lastSyncTimestamp
- */
- public void updatePicturesLastSync(Context context, long lastSyncTimestamp) {
- // DB connection
- CameraUploadsSyncStorageManager cameraUploadsSyncStorageManager = new
- CameraUploadsSyncStorageManager(context.getContentResolver());
-
- OCCameraUploadSync ocCameraUploadSync = cameraUploadsSyncStorageManager.
- getCameraUploadSync(null, null, null);
-
- if (ocCameraUploadSync != null) {
- ocCameraUploadSync.setPicturesLastSync(lastSyncTimestamp);
- cameraUploadsSyncStorageManager.updateCameraUploadSync(ocCameraUploadSync);
- }
- }
-
- /**
- * Update timestamp (in milliseconds) from which to start checking videos to upload
- *
- * @param lastSyncTimestamp
- */
- public void updateVideosLastSync(Context context, long lastSyncTimestamp) {
- // DB connection
- CameraUploadsSyncStorageManager cameraUploadsSyncStorageManager = new
- CameraUploadsSyncStorageManager(context.getContentResolver());
-
- OCCameraUploadSync ocCameraUploadSync = cameraUploadsSyncStorageManager.
- getCameraUploadSync(null, null, null);
-
- if (ocCameraUploadSync == null) {
- return;
- } else {
- ocCameraUploadSync.setVideosLastSync(lastSyncTimestamp);
- cameraUploadsSyncStorageManager.updateCameraUploadSync(ocCameraUploadSync);
- }
- }
-
- public void setCameraUploadsConfig(CameraUploadsConfiguration mCameraUploadsConfig) {
- this.mCameraUploadsConfig = mCameraUploadsConfig;
- }
-}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/files/services/CameraUploadsSyncJobService.java b/owncloudApp/src/main/java/com/owncloud/android/files/services/CameraUploadsSyncJobService.java
deleted file mode 100644
index a1f2b39cc12..00000000000
--- a/owncloudApp/src/main/java/com/owncloud/android/files/services/CameraUploadsSyncJobService.java
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * ownCloud Android client application
- *
- * @author David González Verdugo
- * @author Christian Schabesberger
- * @author Juan Carlos Garrote Gascón
- *
- * Copyright (C) 2020 ownCloud GmbH.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.owncloud.android.files.services;
-
-import android.accounts.Account;
-import android.app.job.JobParameters;
-import android.app.job.JobScheduler;
-import android.app.job.JobService;
-import android.content.Context;
-import android.os.AsyncTask;
-
-import com.owncloud.android.authentication.AccountUtils;
-import com.owncloud.android.datamodel.CameraUploadsSyncStorageManager;
-import com.owncloud.android.datamodel.OCCameraUploadSync;
-import com.owncloud.android.db.PreferenceManager;
-import com.owncloud.android.db.PreferenceManager.CameraUploadsConfiguration;
-import com.owncloud.android.operations.UploadFileOperation;
-import com.owncloud.android.utils.Extras;
-import com.owncloud.android.utils.MimetypeIconUtil;
-import timber.log.Timber;
-
-import java.io.File;
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.Locale;
-
-public class CameraUploadsSyncJobService extends JobService {
-
- @Override
- public boolean onStartJob(JobParameters jobParameters) {
-
- Timber.d("Starting job to sync camera folder");
-
- new CameraUploadsSyncJobTask(this).execute(jobParameters);
-
- return true; // True because we have a thread still running in background
- }
-
- private static class CameraUploadsSyncJobTask extends AsyncTask {
-
- private final JobService mCameraUploadsSyncJobService;
-
- private Account mCameraUploadsVideosAccount;
- private Account mCameraUploadsPicturesAccount;
- private CameraUploadsSyncStorageManager mCameraUploadsSyncStorageManager;
- private OCCameraUploadSync mOCCameraUploadSync;
- private String mCameraUploadsPicturesPath;
- private String mCameraUploadsVideosPath;
- private String mCameraUploadsPicturesSourcePath;
- private String mCameraUploadsVideosSourcePath;
- private int mCameraUploadsPicturesBehaviorAfterUpload;
- private int mCameraUploadsVideosBehaviorAfterUpload;
-
- public CameraUploadsSyncJobTask(JobService mCameraUploadsSyncJobService) {
- this.mCameraUploadsSyncJobService = mCameraUploadsSyncJobService;
- }
-
- @Override
- protected JobParameters doInBackground(JobParameters... jobParams) {
- // Cancel periodic job if feature is disabled
- CameraUploadsConfiguration cameraUploadsConfiguration = PreferenceManager.
- getCameraUploadsConfiguration(mCameraUploadsSyncJobService);
-
- if (!cameraUploadsConfiguration.isEnabledForPictures() &&
- !cameraUploadsConfiguration.isEnabledForVideos()) {
- cancelPeriodicJob(jobParams[0].getJobId());
-
- return jobParams[0];
- }
-
- String accountNameForPictureUploads = jobParams[0].getExtras().getString(Extras.EXTRA_CAMERA_UPLOADS_PICTURES_ACCOUNT);
- mCameraUploadsPicturesAccount = AccountUtils.getOwnCloudAccountByName(mCameraUploadsSyncJobService, accountNameForPictureUploads);
- String accountNameForVideoUploads = jobParams[0].getExtras().getString(Extras.EXTRA_CAMERA_UPLOADS_VIDEOS_ACCOUNT);
- mCameraUploadsVideosAccount = AccountUtils.getOwnCloudAccountByName(mCameraUploadsSyncJobService, accountNameForVideoUploads);
- mCameraUploadsSyncStorageManager = new CameraUploadsSyncStorageManager(
- mCameraUploadsSyncJobService.getContentResolver());
-
- mCameraUploadsPicturesPath = jobParams[0].getExtras().getString(Extras.EXTRA_CAMERA_UPLOADS_PICTURES_PATH);
- mCameraUploadsVideosPath = jobParams[0].getExtras().getString(Extras.EXTRA_CAMERA_UPLOADS_VIDEOS_PATH);
- mCameraUploadsPicturesSourcePath = jobParams[0].getExtras().getString(Extras.EXTRA_CAMERA_UPLOADS_PICTURES_SOURCE_PATH);
- mCameraUploadsPicturesBehaviorAfterUpload = jobParams[0].getExtras().
- getInt(Extras.EXTRA_CAMERA_UPLOADS_PICTURES_BEHAVIOR_AFTER_UPLOAD);
- mCameraUploadsVideosSourcePath = jobParams[0].getExtras().getString(Extras.EXTRA_CAMERA_UPLOADS_VIDEOS_SOURCE_PATH);
- mCameraUploadsVideosBehaviorAfterUpload = jobParams[0].getExtras().
- getInt(Extras.EXTRA_CAMERA_UPLOADS_VIDEOS_BEHAVIOR_AFTER_UPLOAD);
-
- syncFiles();
-
- return jobParams[0];
- }
-
- @Override
- protected void onPostExecute(JobParameters jobParameters) {
- mCameraUploadsSyncJobService.jobFinished(jobParameters, false);
- }
-
- /**
- * Get local images and videos and start handling them
- */
- private void syncFiles() {
-
- // Get local images and videos
- String localCameraPicturesPath = mCameraUploadsPicturesSourcePath;
- String localCameraVideosPath = mCameraUploadsVideosSourcePath;
-
- File[] localPictureFiles = null;
- File[] localVideoFiles = null;
-
- if (localCameraPicturesPath != null) {
- File cameraPicturesFolder = new File(localCameraPicturesPath);
- localPictureFiles = cameraPicturesFolder.listFiles();
- }
-
- if (localCameraVideosPath != null) {
- File cameraVideosFolder = new File(localCameraVideosPath);
- localVideoFiles = cameraVideosFolder.listFiles();
- }
-
- if (localPictureFiles != null) {
- orderFilesByCreationTimestamp(localPictureFiles);
-
- for (File localFile : localPictureFiles) {
- String fileName = localFile.getName();
- String mimeType = MimetypeIconUtil.getBestMimeTypeByFilename(fileName);
- boolean isImage = mimeType.startsWith("image/");
- if (isImage) {
- handleFile(localFile);
- }
- }
- }
-
- if (localVideoFiles != null) {
- orderFilesByCreationTimestamp(localVideoFiles);
-
- for (File localFile : localVideoFiles) {
- String fileName = localFile.getName();
- String mimeType = MimetypeIconUtil.getBestMimeTypeByFilename(fileName);
- boolean isVideo = mimeType.startsWith("video/");
- if (isVideo) {
- handleFile(localFile);
- }
- }
- }
-
- Timber.d("All files synced, finishing job");
- }
-
- private void orderFilesByCreationTimestamp(File[] localFiles) {
- Arrays.sort(localFiles, (file1, file2) -> Long.compare(file1.lastModified(), file2.lastModified()));
- }
-
- /**
- * Request the upload of a file just created if matches the criteria of the current
- * configuration for camera uploads.
- *
- * @param localFile image or video to upload to the server
- */
- private synchronized void handleFile(File localFile) {
-
- String fileName = localFile.getName();
- String mimeType = MimetypeIconUtil.getBestMimeTypeByFilename(fileName);
- boolean isImage = mimeType.startsWith("image/");
- boolean isVideo = mimeType.startsWith("video/");
-
- String remotePath = (isImage ? mCameraUploadsPicturesPath : mCameraUploadsVideosPath) + fileName;
-
- int createdBy = isImage ? UploadFileOperation.CREATED_AS_CAMERA_UPLOAD_PICTURE :
- UploadFileOperation.CREATED_AS_CAMERA_UPLOAD_VIDEO;
-
- String localPath = (isImage ? mCameraUploadsPicturesSourcePath : mCameraUploadsVideosSourcePath) + File.separator + fileName;
-
- int behaviour = isImage ? mCameraUploadsPicturesBehaviorAfterUpload : mCameraUploadsVideosBehaviorAfterUpload;
- Account account = isImage ? mCameraUploadsPicturesAccount : mCameraUploadsVideosAccount;
-
- mOCCameraUploadSync = mCameraUploadsSyncStorageManager.getCameraUploadSync(null, null,
- null);
-
- if (mOCCameraUploadSync == null) {
- Timber.d("There's no timestamp to compare with in database yet, not continue");
- return;
- }
-
- SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault());
- if (isImage && localFile.lastModified() <= mOCCameraUploadSync.getPicturesLastSync()) {
- Timber.i("Image " + localPath + " created before period to check, ignoring " +
- simpleDateFormat.format(new Date(localFile.lastModified())) + " <= " +
- simpleDateFormat.format(new Date(mOCCameraUploadSync.getPicturesLastSync()))
- );
- return;
- }
-
- if (isVideo && localFile.lastModified() <= mOCCameraUploadSync.getVideosLastSync()) {
- Timber.i("Video " + localPath + " created before period to check, ignoring " +
- simpleDateFormat.format(new Date(localFile.lastModified())) + " <= " +
- simpleDateFormat.format(new Date(mOCCameraUploadSync.getVideosLastSync()))
- );
- return;
- }
-
- TransferRequester requester = new TransferRequester();
- requester.uploadNewFile(
- mCameraUploadsSyncJobService,
- account,
- localPath,
- remotePath,
- behaviour,
- mimeType,
- true, // create parent folder if not existent
- createdBy
- );
-
- // Update timestamps once the first picture/video has been enqueued
- updateTimestamps(isImage, isVideo, localFile.lastModified());
-
- if (account != null) {
- Timber.i("Requested upload of %1s to %2s in %3s", localPath, remotePath, account.name);
- } else {
- Timber.w("Requested upload of %1s to %2s with no account!!", localPath, remotePath);
- }
- }
-
- /**
- * Update pictures and videos timestamps to upload only the pictures and videos taken later
- * than those timestamps
- *
- * @param isImage true if file is an image, false otherwise
- * @param isVideo true if file is a video, false otherwise
- */
- private void updateTimestamps(boolean isImage, boolean isVideo, long fileTimestamp) {
-
- long picturesTimestamp = mOCCameraUploadSync.getPicturesLastSync();
- long videosTimestamp = mOCCameraUploadSync.getVideosLastSync();
-
- if (isImage) {
-
- Timber.d("Updating timestamp for pictures");
-
- picturesTimestamp = fileTimestamp;
- }
-
- if (isVideo) {
-
- Timber.d("Updating timestamp for videos");
-
- videosTimestamp = fileTimestamp;
- }
-
- OCCameraUploadSync newOCCameraUploadSync = new OCCameraUploadSync(picturesTimestamp,
- videosTimestamp);
-
- newOCCameraUploadSync.setId(mOCCameraUploadSync.getId());
-
- mCameraUploadsSyncStorageManager.updateCameraUploadSync(newOCCameraUploadSync);
- }
-
- /**
- * Cancel the periodic job
- *
- * @param jobId id of the job to cancel
- */
- private void cancelPeriodicJob(int jobId) {
-
- JobScheduler jobScheduler = (JobScheduler) mCameraUploadsSyncJobService.getSystemService(
- Context.JOB_SCHEDULER_SERVICE);
-
- jobScheduler.cancel(jobId);
-
- Timber.d("Camera uploads disabled, cancelling the periodic job");
- }
- }
-
- @Override
- /*
- * Called by the system if the job is cancelled before being finished
- */
- public boolean onStopJob(JobParameters jobParameters) {
-
- Timber.d("Job was cancelled before finishing.");
-
- return true;
- }
-}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/files/services/FileDownloader.java b/owncloudApp/src/main/java/com/owncloud/android/files/services/FileDownloader.java
index 3a8d52d9b84..2615fbcc028 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/files/services/FileDownloader.java
+++ b/owncloudApp/src/main/java/com/owncloud/android/files/services/FileDownloader.java
@@ -52,8 +52,6 @@
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
import com.owncloud.android.operations.DownloadFileOperation;
-import com.owncloud.android.presentation.ui.authentication.AuthenticatorConstants;
-import com.owncloud.android.presentation.ui.authentication.LoginActivity;
import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.ui.activity.FileDisplayActivity;
import com.owncloud.android.ui.errorhandling.ErrorMessageAdapter;
@@ -602,20 +600,10 @@ private void notifyDownloadResult(DownloadFileOperation download,
if (needsToUpdateCredentials) {
// let the user update credentials with one click
- Intent updateAccountCredentials = new Intent(this, LoginActivity.class);
- updateAccountCredentials.putExtra(AuthenticatorConstants.EXTRA_ACCOUNT,
- download.getAccount());
- updateAccountCredentials.putExtra(
- AuthenticatorConstants.EXTRA_ACTION,
- AuthenticatorConstants.ACTION_UPDATE_EXPIRED_TOKEN
- );
- updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);
- getNotificationBuilder()
- .setContentIntent(PendingIntent.getActivity(
- this, (int) System.currentTimeMillis(), updateAccountCredentials,
- PendingIntent.FLAG_ONE_SHOT));
+ PendingIntent pendingIntentToRefreshCredentials =
+ NotificationUtils.INSTANCE.composePendingIntentToRefreshCredentials(this, download.getAccount());
+
+ getNotificationBuilder().setContentIntent(pendingIntentToRefreshCredentials);
} else {
// TODO put something smart in showDetailsIntent
diff --git a/owncloudApp/src/main/java/com/owncloud/android/files/services/FileUploader.java b/owncloudApp/src/main/java/com/owncloud/android/files/services/FileUploader.java
index f6095c4f625..076f16b7516 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/files/services/FileUploader.java
+++ b/owncloudApp/src/main/java/com/owncloud/android/files/services/FileUploader.java
@@ -65,8 +65,6 @@
import com.owncloud.android.operations.ChunkedUploadFileOperation;
import com.owncloud.android.operations.RemoveChunksFolderOperation;
import com.owncloud.android.operations.UploadFileOperation;
-import com.owncloud.android.presentation.ui.authentication.AuthenticatorConstants;
-import com.owncloud.android.presentation.ui.authentication.LoginActivity;
import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.ui.activity.UploadListActivity;
import com.owncloud.android.ui.errorhandling.ErrorMessageAdapter;
@@ -1025,24 +1023,10 @@ uploadResult, upload, getResources()
if (needsToUpdateCredentials) {
// let the user update credentials with one click
- Intent updateAccountCredentials = new Intent(this, LoginActivity.class);
- updateAccountCredentials.putExtra(
- AuthenticatorConstants.EXTRA_ACCOUNT, upload.getAccount()
- );
- updateAccountCredentials.putExtra(
- AuthenticatorConstants.EXTRA_ACTION,
- AuthenticatorConstants.ACTION_UPDATE_EXPIRED_TOKEN
- );
+ PendingIntent pendingIntentToRefreshCredentials =
+ NotificationUtils.INSTANCE.composePendingIntentToRefreshCredentials(this, upload.getAccount());
- updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);
- mNotificationBuilder.setContentIntent(PendingIntent.getActivity(
- this,
- (int) System.currentTimeMillis(),
- updateAccountCredentials,
- PendingIntent.FLAG_ONE_SHOT
- ));
+ mNotificationBuilder.setContentIntent(pendingIntentToRefreshCredentials);
} else {
mNotificationBuilder.setContentText(content);
diff --git a/owncloudApp/src/main/java/com/owncloud/android/files/services/TransferRequester.java b/owncloudApp/src/main/java/com/owncloud/android/files/services/TransferRequester.java
index b201f482d0b..00f1b124aad 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/files/services/TransferRequester.java
+++ b/owncloudApp/src/main/java/com/owncloud/android/files/services/TransferRequester.java
@@ -27,15 +27,18 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.net.Uri;
import android.os.Build;
import android.os.PersistableBundle;
+import androidx.documentfile.provider.DocumentFile;
+import androidx.work.WorkManager;
import com.owncloud.android.authentication.AccountUtils;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.datamodel.OCUpload;
import com.owncloud.android.datamodel.UploadsStorageManager;
-import com.owncloud.android.db.PreferenceManager;
import com.owncloud.android.db.UploadResult;
+import com.owncloud.android.usecases.UploadFileFromContentUriUseCase;
import com.owncloud.android.utils.ConnectivityUtils;
import com.owncloud.android.utils.Extras;
import com.owncloud.android.utils.PowerUtils;
@@ -205,7 +208,13 @@ public void retryFailedUploads(Context context, Account account, UploadResult up
* false otherwise
*/
private void retry(Context context, Account account, OCUpload upload, boolean requestedFromWifiBackEvent) {
- if (upload != null) {
+ if (upload == null) {
+ return;
+ }
+
+ if (isContentUri(context, upload)) {
+ enqueueRetry(upload, context);
+ } else {
Intent intent = new Intent(context, FileUploader.class);
intent.putExtra(FileUploader.KEY_RETRY, true);
intent.putExtra(FileUploader.KEY_ACCOUNT, account);
@@ -230,6 +239,24 @@ private void retry(Context context, Account account, OCUpload upload, boolean re
}
}
+ private Boolean isContentUri(Context context, OCUpload upload) {
+ return DocumentFile.isDocumentUri(context, Uri.parse(upload.getLocalPath()));
+ }
+
+ private void enqueueRetry(OCUpload upload, Context context) {
+ new UploadFileFromContentUriUseCase(WorkManager.getInstance(context)).execute(
+ new UploadFileFromContentUriUseCase.Params(
+ upload.getAccountName(),
+ Uri.parse(upload.getLocalPath()),
+ upload.getUploadEndTimestamp() / 1000 + "",
+ "COPY",
+ upload.getRemotePath(),
+ upload.getUploadId(),
+ false
+ )
+ );
+ }
+
/**
* Return 'true' when conditions for a scheduled retry are met.
*
@@ -352,19 +379,9 @@ private int getRequiredNetworkType(Context context, String accountName, String r
// Get last upload to be retried
OCUpload ocUpload = uploadsStorageManager.getLastUploadFor(new OCFile(remotePath), accountName);
- PreferenceManager.CameraUploadsConfiguration mConfig = PreferenceManager.getCameraUploadsConfiguration(context);
-
// Wifi by default
int networkType = JobInfo.NETWORK_TYPE_UNMETERED;
- if (ocUpload != null && (ocUpload.getCreatedBy() == CREATED_AS_CAMERA_UPLOAD_PICTURE &&
- !mConfig.isWifiOnlyForPictures() || ocUpload.getCreatedBy() == CREATED_AS_CAMERA_UPLOAD_VIDEO &&
- !mConfig.isWifiOnlyForVideos())) {
-
- // Wifi or cellular
- networkType = JobInfo.NETWORK_TYPE_ANY;
- }
-
return networkType;
}
}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/owncloudApp/src/main/java/com/owncloud/android/operations/UploadFileOperation.java
index 384915fd6db..57dea4d1999 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/operations/UploadFileOperation.java
+++ b/owncloudApp/src/main/java/com/owncloud/android/operations/UploadFileOperation.java
@@ -281,12 +281,6 @@ protected RemoteOperationResult run(OwnCloudClient client) {
try {
- /// Check that connectivity conditions are met and delays the upload otherwise
- if (delayForWifi()) {
- Timber.d("Upload delayed until WiFi is available: %s", getRemotePath());
- return new RemoteOperationResult(ResultCode.DELAYED_FOR_WIFI);
- }
-
/// check if the file continues existing before schedule the operation
if (!originalFile.exists()) {
Timber.d("%s not exists anymore", mOriginalStoragePath);
@@ -469,25 +463,6 @@ protected void moveTemporalOriginalFiles(File temporalFile, File originalFile, S
}
}
- /**
- * Checks origin of current upload and network type to decide if should be delayed, according to
- * current user preferences.
- *
- * @return 'True' if the upload was delayed until WiFi connectivity is available, 'false' otherwise.
- */
- private boolean delayForWifi() {
- boolean delayCameraUploadsPicture = (
- isCameraUploadsPicture() && PreferenceManager.cameraPictureUploadViaWiFiOnly(mContext)
- );
- boolean delayCameraUploadsVideo = (
- isCameraUploadsVideo() && PreferenceManager.cameraVideoUploadViaWiFiOnly(mContext)
- );
- return (
- (delayCameraUploadsPicture || delayCameraUploadsVideo) &&
- !ConnectivityUtils.isAppConnectedViaWiFi(mContext)
- );
- }
-
/**
* Checks the existence of the folder where the current file will be uploaded both
* in the remote server and in the local database.
diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/settings/SettingsActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/settings/SettingsActivity.kt
index eb31a60f977..b2a58316091 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/settings/SettingsActivity.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/settings/SettingsActivity.kt
@@ -56,13 +56,7 @@ class SettingsActivity : AppCompatActivity() {
if (savedInstanceState != null) return
- supportFragmentManager
- .beginTransaction()
- .replace(
- R.id.settings_container,
- SettingsFragment()
- )
- .commit()
+ redirectToSubsection(intent)
}
private fun updateToolbarTitle() {
@@ -93,4 +87,23 @@ class SettingsActivity : AppCompatActivity() {
}
return super.onOptionsItemSelected(item)
}
+
+ private fun redirectToSubsection(intent: Intent?) {
+ val fragment = when (intent?.getStringExtra(KEY_NOTIFICATION_INTENT)) {
+ NOTIFICATION_INTENT_PICTURES -> SettingsPictureUploadsFragment()
+ NOTIFICATION_INTENT_VIDEOS -> SettingsVideoUploadsFragment()
+ else -> SettingsFragment()
+ }
+
+ supportFragmentManager
+ .beginTransaction()
+ .replace(R.id.settings_container, fragment)
+ .commit()
+ }
+
+ companion object {
+ const val KEY_NOTIFICATION_INTENT = "key_notification_intent"
+ const val NOTIFICATION_INTENT_PICTURES = "picture_uploads"
+ const val NOTIFICATION_INTENT_VIDEOS = "video_uploads"
+ }
}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/settings/fragments/SettingsPictureUploadsFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/settings/fragments/SettingsPictureUploadsFragment.kt
index 74539525162..7079f8bceda 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/settings/fragments/SettingsPictureUploadsFragment.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/settings/fragments/SettingsPictureUploadsFragment.kt
@@ -25,7 +25,10 @@ import android.content.DialogInterface
import android.content.Intent
import android.os.Build
import android.os.Bundle
+import android.provider.DocumentsContract
+import android.view.View
import androidx.activity.result.contract.ActivityResultContracts
+import androidx.core.net.toUri
import androidx.preference.CheckBoxPreference
import androidx.preference.ListPreference
import androidx.preference.Preference
@@ -35,12 +38,13 @@ import com.owncloud.android.R
import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME
import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_BEHAVIOUR
import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_ENABLED
+import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_LAST_SYNC
import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_PATH
import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_SOURCE
import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_WIFI_ONLY
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration
import com.owncloud.android.extensions.showAlertDialog
import com.owncloud.android.presentation.viewmodels.settings.SettingsPictureUploadsViewModel
-import com.owncloud.android.ui.activity.LocalFolderPickerActivity
import com.owncloud.android.ui.activity.UploadPathActivity
import com.owncloud.android.utils.DisplayUtils
import org.koin.androidx.viewmodel.ext.android.viewModel
@@ -57,21 +61,23 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() {
private var prefPictureUploadsSourcePath: Preference? = null
private var prefPictureUploadsBehaviour: ListPreference? = null
private var prefPictureUploadsAccount: ListPreference? = null
+ private var prefPictureUploadsLastSync: Preference? = null
private val selectPictureUploadsPathLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode != Activity.RESULT_OK) return@registerForActivityResult
picturesViewModel.handleSelectPictureUploadsPath(result.data)
- prefPictureUploadsPath?.summary =
- DisplayUtils.getPathWithoutLastSlash(picturesViewModel.getPictureUploadsPath())
}
private val selectPictureUploadsSourcePathLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode != Activity.RESULT_OK) return@registerForActivityResult
- picturesViewModel.handleSelectPictureUploadsSourcePath(result.data)
- prefPictureUploadsSourcePath?.summary =
- DisplayUtils.getPathWithoutLastSlash(picturesViewModel.getPictureUploadsSourcePath())
+ // here we ask the content resolver to persist the permission for us
+ val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ val contentUriForTree = result.data!!.data!!
+
+ requireContext().contentResolver.takePersistableUriPermission(contentUriForTree, takeFlags)
+ picturesViewModel.handleSelectPictureUploadsSourcePath(contentUriForTree)
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
@@ -81,38 +87,48 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() {
prefPictureUploadsPath = findPreference(PREF__CAMERA_PICTURE_UPLOADS_PATH)
prefPictureUploadsOnWifi = findPreference(PREF__CAMERA_PICTURE_UPLOADS_WIFI_ONLY)
prefPictureUploadsSourcePath = findPreference(PREF__CAMERA_PICTURE_UPLOADS_SOURCE)
- prefPictureUploadsBehaviour = findPreference(PREF__CAMERA_PICTURE_UPLOADS_BEHAVIOUR)
+ prefPictureUploadsLastSync = findPreference(PREF__CAMERA_PICTURE_UPLOADS_LAST_SYNC)
+ prefPictureUploadsBehaviour = findPreference(PREF__CAMERA_PICTURE_UPLOADS_BEHAVIOUR)?.apply {
+ entries = listOf(getString(R.string.pref_behaviour_entries_keep_file), getString(R.string.pref_behaviour_entries_move)).toTypedArray()
+ entryValues = listOf(FolderBackUpConfiguration.Behavior.COPY.name, FolderBackUpConfiguration.Behavior.MOVE.name).toTypedArray()
+ }
prefPictureUploadsAccount = findPreference(PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME)?.apply {
entries = picturesViewModel.getLoggedAccountNames()
entryValues = picturesViewModel.getLoggedAccountNames()
}
- enablePictureUploads(picturesViewModel.isPictureUploadEnabled())
-
- prefPictureUploadsPath?.summary =
- DisplayUtils.getPathWithoutLastSlash(picturesViewModel.getPictureUploadsPath())
- prefPictureUploadsSourcePath?.summary =
- DisplayUtils.getPathWithoutLastSlash(picturesViewModel.getPictureUploadsSourcePath())
- val comment =
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) getString(
- R.string.prefs_camera_upload_source_path_title_optional
- )
- else getString(
- R.string.prefs_camera_upload_source_path_title_required
- )
+ val comment = getString(R.string.prefs_camera_upload_source_path_title_required)
prefPictureUploadsSourcePath?.title = String.format(prefPictureUploadsSourcePath?.title.toString(), comment)
initPreferenceListeners()
}
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ initLiveDataObservers()
+ }
+
+ private fun initLiveDataObservers() {
+ picturesViewModel.pictureUploads.observe(viewLifecycleOwner, { pictureUploadsConfiguration ->
+ enablePictureUploads(pictureUploadsConfiguration != null)
+ pictureUploadsConfiguration?.let {
+ prefPictureUploadsAccount?.value = it.accountName
+ prefPictureUploadsPath?.summary = DisplayUtils.getPathWithoutLastSlash(it.uploadPath)
+ prefPictureUploadsSourcePath?.summary = DisplayUtils.getPathWithoutLastSlash(it.sourcePath.toUri().path)
+ prefPictureUploadsOnWifi?.isChecked = it.wifiOnly
+ prefPictureUploadsBehaviour?.value = it.behavior.name
+ prefPictureUploadsLastSync?.summary = DisplayUtils.unixTimeToHumanReadable(it.lastSyncTimestamp)
+ } ?: resetFields()
+ })
+ }
+
private fun initPreferenceListeners() {
prefEnablePictureUploads?.setOnPreferenceChangeListener { _: Preference?, newValue: Any ->
val value = newValue as Boolean
if (value) {
- picturesViewModel.setEnablePictureUpload(value)
- enablePictureUploads(value)
- prefPictureUploadsAccount?.value = picturesViewModel.getPictureUploadsAccount()
+ picturesViewModel.enablePictureUploads()
showAlertDialog(
title = getString(R.string.common_important),
message = getString(R.string.proper_pics_folder_warning_camera_upload)
@@ -124,10 +140,7 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() {
message = getString(R.string.confirmation_disable_pictures_upload_message),
positiveButtonText = getString(R.string.common_yes),
positiveButtonListener = { _: DialogInterface?, _: Int ->
- picturesViewModel.updatePicturesLastSync()
- picturesViewModel.setEnablePictureUpload(value)
- enablePictureUploads(false)
- resetPreferencesAfterDisablingPicturesUploads()
+ picturesViewModel.disablePictureUploads()
},
negativeButtonText = getString(R.string.common_no)
)
@@ -137,31 +150,60 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() {
prefPictureUploadsPath?.setOnPreferenceClickListener {
var uploadPath = picturesViewModel.getPictureUploadsPath()
- if (uploadPath?.endsWith(File.separator) == false) {
+ if (!uploadPath.endsWith(File.separator)) {
uploadPath += File.separator
}
- val intent = Intent(activity, UploadPathActivity::class.java)
- intent.putExtra(UploadPathActivity.KEY_CAMERA_UPLOAD_PATH, uploadPath)
- intent.putExtra(UploadPathActivity.KEY_CAMERA_UPLOAD_ACCOUNT, picturesViewModel.getPictureUploadsAccount())
+ val intent = Intent(activity, UploadPathActivity::class.java).apply {
+ putExtra(UploadPathActivity.KEY_CAMERA_UPLOAD_PATH, uploadPath)
+ putExtra(UploadPathActivity.KEY_CAMERA_UPLOAD_ACCOUNT, picturesViewModel.getPictureUploadsAccount())
+ }
selectPictureUploadsPathLauncher.launch(intent)
true
}
prefPictureUploadsSourcePath?.setOnPreferenceClickListener {
var sourcePath = picturesViewModel.getPictureUploadsSourcePath()
- if (sourcePath?.endsWith(File.separator) == false) {
+ .takeUnless { it.isNullOrBlank() } ?: picturesViewModel.getDefaultSourcePath()
+ if (!sourcePath.endsWith(File.separator)) {
sourcePath += File.separator
}
- val intent = Intent(activity, LocalFolderPickerActivity::class.java)
- intent.putExtra(LocalFolderPickerActivity.EXTRA_PATH, sourcePath)
+ val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ putExtra(DocumentsContract.EXTRA_INITIAL_URI, sourcePath)
+ }
+ addFlags(
+ Intent.FLAG_GRANT_READ_URI_PERMISSION
+ or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ or Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+ or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
+ )
+ }
selectPictureUploadsSourcePathLauncher.launch(intent)
true
}
+
+ prefPictureUploadsOnWifi?.setOnPreferenceChangeListener { _, newValue ->
+ newValue as Boolean
+ picturesViewModel.useWifiOnly(newValue)
+ newValue
+ }
+
+ prefPictureUploadsAccount?.setOnPreferenceChangeListener { _, newValue ->
+ newValue as String
+ picturesViewModel.handleSelectAccount(newValue)
+ true
+ }
+
+ prefPictureUploadsBehaviour?.setOnPreferenceChangeListener { _, newValue ->
+ newValue as String
+ picturesViewModel.handleSelectBehaviour(newValue)
+ true
+ }
}
- override fun onStop() {
- picturesViewModel.schedulePictureUploadsSyncJob()
- super.onStop()
+ override fun onDestroy() {
+ picturesViewModel.schedulePictureUploads()
+ super.onDestroy()
}
private fun enablePictureUploads(value: Boolean) {
@@ -171,10 +213,15 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() {
prefPictureUploadsSourcePath?.isEnabled = value
prefPictureUploadsBehaviour?.isEnabled = value
prefPictureUploadsAccount?.isEnabled = value
+ prefPictureUploadsLastSync?.isEnabled = value
}
- private fun resetPreferencesAfterDisablingPicturesUploads() {
+ private fun resetFields() {
prefPictureUploadsAccount?.value = null
- prefPictureUploadsPath?.summary = DisplayUtils.getPathWithoutLastSlash(picturesViewModel.getPictureUploadsPath())
+ prefPictureUploadsPath?.summary = null
+ prefPictureUploadsSourcePath?.summary = null
+ prefPictureUploadsOnWifi?.isChecked = false
+ prefPictureUploadsBehaviour?.value = FolderBackUpConfiguration.Behavior.COPY.name
+ prefPictureUploadsLastSync?.summary = null
}
}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/settings/fragments/SettingsVideoUploadsFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/settings/fragments/SettingsVideoUploadsFragment.kt
index ab35426166c..4ca47ebf970 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/settings/fragments/SettingsVideoUploadsFragment.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/settings/fragments/SettingsVideoUploadsFragment.kt
@@ -25,22 +25,26 @@ import android.content.DialogInterface
import android.content.Intent
import android.os.Build
import android.os.Bundle
+import android.provider.DocumentsContract
+import android.view.View
import androidx.activity.result.contract.ActivityResultContracts
+import androidx.core.net.toUri
import androidx.preference.CheckBoxPreference
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreferenceCompat
import com.owncloud.android.R
+import com.owncloud.android.db.PreferenceManager
import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME
import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_BEHAVIOUR
import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_ENABLED
import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_PATH
import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_SOURCE
import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_WIFI_ONLY
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration
import com.owncloud.android.extensions.showAlertDialog
import com.owncloud.android.presentation.viewmodels.settings.SettingsVideoUploadsViewModel
-import com.owncloud.android.ui.activity.LocalFolderPickerActivity
import com.owncloud.android.ui.activity.UploadPathActivity
import com.owncloud.android.utils.DisplayUtils
import org.koin.androidx.viewmodel.ext.android.viewModel
@@ -57,21 +61,23 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() {
private var prefVideoUploadsSourcePath: Preference? = null
private var prefVideoUploadsBehaviour: ListPreference? = null
private var prefVideoUploadsAccount: ListPreference? = null
+ private var prefVideoUploadsLastSync: Preference? = null
private val selectVideoUploadsPathLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode != Activity.RESULT_OK) return@registerForActivityResult
videosViewModel.handleSelectVideoUploadsPath(result.data)
- prefVideoUploadsPath?.summary =
- DisplayUtils.getPathWithoutLastSlash(videosViewModel.getVideoUploadsPath())
}
private val selectVideoUploadsSourcePathLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode != Activity.RESULT_OK) return@registerForActivityResult
- videosViewModel.handleSelectVideoUploadsSourcePath(result.data)
- prefVideoUploadsSourcePath?.summary =
- DisplayUtils.getPathWithoutLastSlash(videosViewModel.getVideoUploadsSourcePath())
+ // here we ask the content resolver to persist the permission for us
+ val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ val contentUriForTree = result.data!!.data!!
+
+ requireContext().contentResolver.takePersistableUriPermission(contentUriForTree, takeFlags)
+ videosViewModel.handleSelectVideoUploadsSourcePath(contentUriForTree)
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
@@ -81,38 +87,48 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() {
prefVideoUploadsPath = findPreference(PREF__CAMERA_VIDEO_UPLOADS_PATH)
prefVideoUploadsOnWifi = findPreference(PREF__CAMERA_VIDEO_UPLOADS_WIFI_ONLY)
prefVideoUploadsSourcePath = findPreference(PREF__CAMERA_VIDEO_UPLOADS_SOURCE)
- prefVideoUploadsBehaviour = findPreference(PREF__CAMERA_VIDEO_UPLOADS_BEHAVIOUR)
+ prefVideoUploadsLastSync = findPreference(PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_LAST_SYNC)
+ prefVideoUploadsBehaviour = findPreference(PREF__CAMERA_VIDEO_UPLOADS_BEHAVIOUR)?.apply {
+ entries = listOf(getString(R.string.pref_behaviour_entries_keep_file), getString(R.string.pref_behaviour_entries_move)).toTypedArray()
+ entryValues = listOf(FolderBackUpConfiguration.Behavior.COPY.name, FolderBackUpConfiguration.Behavior.MOVE.name).toTypedArray()
+ }
prefVideoUploadsAccount = findPreference(PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME)?.apply {
entries = videosViewModel.getLoggedAccountNames()
entryValues = videosViewModel.getLoggedAccountNames()
}
- enableVideoUploads(videosViewModel.isVideoUploadEnabled())
-
- prefVideoUploadsPath?.summary =
- DisplayUtils.getPathWithoutLastSlash(videosViewModel.getVideoUploadsPath())
- prefVideoUploadsSourcePath?.summary =
- DisplayUtils.getPathWithoutLastSlash(videosViewModel.getVideoUploadsSourcePath())
- val comment =
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) getString(
- R.string.prefs_camera_upload_source_path_title_optional
- )
- else getString(
- R.string.prefs_camera_upload_source_path_title_required
- )
+ val comment = getString(R.string.prefs_camera_upload_source_path_title_required)
prefVideoUploadsSourcePath?.title = String.format(prefVideoUploadsSourcePath?.title.toString(), comment)
initPreferenceListeners()
}
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ initLiveDataObservers()
+ }
+
+ private fun initLiveDataObservers() {
+ videosViewModel.videoUploads.observe(viewLifecycleOwner, { videoUploadsConfiguration ->
+ enableVideoUploads(videoUploadsConfiguration != null)
+ videoUploadsConfiguration?.let {
+ prefVideoUploadsAccount?.value = it.accountName
+ prefVideoUploadsPath?.summary = DisplayUtils.getPathWithoutLastSlash(it.uploadPath)
+ prefVideoUploadsSourcePath?.summary = DisplayUtils.getPathWithoutLastSlash(it.sourcePath.toUri().path)
+ prefVideoUploadsOnWifi?.isChecked = it.wifiOnly
+ prefVideoUploadsBehaviour?.value = it.behavior.name
+ prefVideoUploadsLastSync?.summary = DisplayUtils.unixTimeToHumanReadable(it.lastSyncTimestamp)
+ } ?: resetFields()
+ })
+ }
+
private fun initPreferenceListeners() {
prefEnableVideoUploads?.setOnPreferenceChangeListener { _: Preference?, newValue: Any ->
val value = newValue as Boolean
if (value) {
- videosViewModel.setEnableVideoUpload(value)
- enableVideoUploads(value)
- prefVideoUploadsAccount?.value = videosViewModel.getVideoUploadsAccount()
+ videosViewModel.enableVideoUploads()
showAlertDialog(
title = getString(R.string.common_important),
message = getString(R.string.proper_videos_folder_warning_camera_upload)
@@ -124,10 +140,7 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() {
message = getString(R.string.confirmation_disable_videos_upload_message),
positiveButtonText = getString(R.string.common_yes),
positiveButtonListener = { _: DialogInterface?, _: Int ->
- videosViewModel.updateVideosLastSync()
- videosViewModel.setEnableVideoUpload(value)
- enableVideoUploads(false)
- resetPreferencesAfterDisablingVideosUploads()
+ videosViewModel.disableVideoUploads()
},
negativeButtonText = getString(R.string.common_no)
)
@@ -137,31 +150,60 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() {
prefVideoUploadsPath?.setOnPreferenceClickListener {
var uploadPath = videosViewModel.getVideoUploadsPath()
- if (uploadPath?.endsWith(File.separator) == false) {
+ if (!uploadPath.endsWith(File.separator)) {
uploadPath += File.separator
}
- val intent = Intent(activity, UploadPathActivity::class.java)
- intent.putExtra(UploadPathActivity.KEY_CAMERA_UPLOAD_PATH, uploadPath)
- intent.putExtra(UploadPathActivity.KEY_CAMERA_UPLOAD_ACCOUNT, videosViewModel.getVideoUploadsAccount())
+ val intent = Intent(activity, UploadPathActivity::class.java).apply {
+ putExtra(UploadPathActivity.KEY_CAMERA_UPLOAD_PATH, uploadPath)
+ putExtra(UploadPathActivity.KEY_CAMERA_UPLOAD_ACCOUNT, videosViewModel.getVideoUploadsAccount())
+ }
selectVideoUploadsPathLauncher.launch(intent)
true
}
prefVideoUploadsSourcePath?.setOnPreferenceClickListener {
var sourcePath = videosViewModel.getVideoUploadsSourcePath()
- if (sourcePath?.endsWith(File.separator) == false) {
+ .takeUnless { it.isNullOrBlank() } ?: videosViewModel.getDefaultCameraSourcePath()
+ if (!sourcePath.endsWith(File.separator)) {
sourcePath += File.separator
}
- val intent = Intent(activity, LocalFolderPickerActivity::class.java)
- intent.putExtra(LocalFolderPickerActivity.EXTRA_PATH, sourcePath)
+ val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ putExtra(DocumentsContract.EXTRA_INITIAL_URI, sourcePath)
+ }
+ addFlags(
+ Intent.FLAG_GRANT_READ_URI_PERMISSION
+ or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ or Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+ or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
+ )
+ }
selectVideoUploadsSourcePathLauncher.launch(intent)
true
}
+
+ prefVideoUploadsOnWifi?.setOnPreferenceChangeListener { _, newValue ->
+ newValue as Boolean
+ videosViewModel.useWifiOnly(newValue)
+ newValue
+ }
+
+ prefVideoUploadsAccount?.setOnPreferenceChangeListener { _, newValue ->
+ newValue as String
+ videosViewModel.handleSelectAccount(newValue)
+ true
+ }
+
+ prefVideoUploadsBehaviour?.setOnPreferenceChangeListener { _, newValue ->
+ newValue as String
+ videosViewModel.handleSelectBehaviour(newValue)
+ true
+ }
}
- override fun onStop() {
- videosViewModel.scheduleVideoUploadsSyncJob()
- super.onStop()
+ override fun onDestroy() {
+ videosViewModel.scheduleVideoUploads()
+ super.onDestroy()
}
private fun enableVideoUploads(value: Boolean) {
@@ -171,10 +213,15 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() {
prefVideoUploadsSourcePath?.isEnabled = value
prefVideoUploadsBehaviour?.isEnabled = value
prefVideoUploadsAccount?.isEnabled = value
+ prefVideoUploadsLastSync?.isEnabled = value
}
- private fun resetPreferencesAfterDisablingVideosUploads() {
+ private fun resetFields() {
prefVideoUploadsAccount?.value = null
- prefVideoUploadsPath?.summary = DisplayUtils.getPathWithoutLastSlash(videosViewModel.getVideoUploadsPath())
+ prefVideoUploadsPath?.summary = null
+ prefVideoUploadsSourcePath?.summary = null
+ prefVideoUploadsOnWifi?.isChecked = false
+ prefVideoUploadsBehaviour?.value = FolderBackUpConfiguration.Behavior.COPY.name
+ prefVideoUploadsLastSync?.summary = null
}
}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/viewmodels/settings/SettingsPictureUploadsViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/viewmodels/settings/SettingsPictureUploadsViewModel.kt
index 6bfcf72740a..28801072829 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/presentation/viewmodels/settings/SettingsPictureUploadsViewModel.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/viewmodels/settings/SettingsPictureUploadsViewModel.kt
@@ -21,98 +21,154 @@
package com.owncloud.android.presentation.viewmodels.settings
import android.content.Intent
+import android.net.Uri
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
-import com.owncloud.android.data.preferences.datasources.SharedPreferencesProvider
+import androidx.lifecycle.viewModelScope
import com.owncloud.android.datamodel.OCFile
-import com.owncloud.android.db.PreferenceManager.CameraUploadsConfiguration
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_ENABLED
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_PATH
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_SOURCE
import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_UPLOADS_DEFAULT_PATH
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration.Companion.pictureUploadsName
+import com.owncloud.android.domain.camerauploads.usecases.GetPictureUploadsConfigurationStreamUseCase
+import com.owncloud.android.domain.camerauploads.usecases.ResetPictureUploadsUseCase
+import com.owncloud.android.domain.camerauploads.usecases.SavePictureUploadsConfigurationUseCase
import com.owncloud.android.providers.AccountProvider
-import com.owncloud.android.providers.CameraUploadsHandlerProvider
-import com.owncloud.android.ui.activity.LocalFolderPickerActivity
+import com.owncloud.android.providers.CoroutinesDispatcherProvider
+import com.owncloud.android.providers.WorkManagerProvider
import com.owncloud.android.ui.activity.UploadPathActivity
+import com.owncloud.android.utils.FileStorageUtils.getDefaultCameraSourcePath
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
import java.io.File
class SettingsPictureUploadsViewModel(
- private val preferencesProvider: SharedPreferencesProvider,
- private val cameraUploadsHandlerProvider: CameraUploadsHandlerProvider,
- private val accountProvider: AccountProvider
+ private val accountProvider: AccountProvider,
+ private val savePictureUploadsConfigurationUseCase: SavePictureUploadsConfigurationUseCase,
+ private val getPictureUploadsConfigurationStreamUseCase: GetPictureUploadsConfigurationStreamUseCase,
+ private val resetPictureUploadsUseCase: ResetPictureUploadsUseCase,
+ private val workManagerProvider: WorkManagerProvider,
+ private val coroutinesDispatcherProvider: CoroutinesDispatcherProvider,
) : ViewModel() {
- fun isPictureUploadEnabled() =
- preferencesProvider.getBoolean(PREF__CAMERA_PICTURE_UPLOADS_ENABLED, false)
+ private val _pictureUploads: MutableLiveData = MutableLiveData()
+ val pictureUploads: LiveData = _pictureUploads
- fun setEnablePictureUpload(value: Boolean) {
- preferencesProvider.putBoolean(PREF__CAMERA_PICTURE_UPLOADS_ENABLED, value)
+ init {
+ initPictureUploads()
+ }
+
+ private fun initPictureUploads() {
+ viewModelScope.launch(coroutinesDispatcherProvider.io) {
+ getPictureUploadsConfigurationStreamUseCase.execute(Unit).collect() { pictureUploadsConfiguration ->
+ _pictureUploads.postValue(pictureUploadsConfiguration)
+ }
+ }
+ }
- if (value) {
- // Use current account as default. It should never be null. If no accounts are attached, picture uploads are hidden
- accountProvider.getCurrentOwnCloudAccount()?.name?.let { name ->
- preferencesProvider.putString(
- key = PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME,
- value = name
+ fun enablePictureUploads() {
+ // Use current account as default. It should never be null. If no accounts are attached, picture uploads are hidden
+ accountProvider.getCurrentOwnCloudAccount()?.name?.let { name ->
+ viewModelScope.launch(coroutinesDispatcherProvider.io) {
+ savePictureUploadsConfigurationUseCase.execute(
+ SavePictureUploadsConfigurationUseCase.Params(composePictureUploadsConfiguration(accountName = name))
)
}
- } else {
- // Reset fields after disabling the feature
- preferencesProvider.removePreference(key = PREF__CAMERA_PICTURE_UPLOADS_PATH)
- preferencesProvider.removePreference(key = PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME)
}
}
- fun updatePicturesLastSync() = cameraUploadsHandlerProvider.updatePicturesLastSync(0)
+ fun disablePictureUploads() {
+ viewModelScope.launch(coroutinesDispatcherProvider.io) {
+ resetPictureUploadsUseCase.execute(Unit)
+ }
+ }
- fun getPictureUploadsAccount() = preferencesProvider.getString(
- key = PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME,
- defaultValue = null
- )
+ fun useWifiOnly(wifiOnly: Boolean) {
+ viewModelScope.launch(coroutinesDispatcherProvider.io) {
+ savePictureUploadsConfigurationUseCase.execute(
+ SavePictureUploadsConfigurationUseCase.Params(
+ composePictureUploadsConfiguration(wifiOnly = wifiOnly)
+ )
+ )
+ }
+ }
+
+ fun getPictureUploadsAccount() = _pictureUploads.value?.accountName
fun getLoggedAccountNames(): Array = accountProvider.getLoggedAccounts().map { it.name }.toTypedArray()
- fun getPictureUploadsPath() = preferencesProvider.getString(
- key = PREF__CAMERA_PICTURE_UPLOADS_PATH,
- defaultValue = PREF__CAMERA_UPLOADS_DEFAULT_PATH
- )
+ fun getPictureUploadsPath() = _pictureUploads.value?.uploadPath ?: PREF__CAMERA_UPLOADS_DEFAULT_PATH
- fun getPictureUploadsSourcePath() = preferencesProvider.getString(
- key = PREF__CAMERA_PICTURE_UPLOADS_SOURCE,
- defaultValue = CameraUploadsConfiguration.getDefaultSourcePath()
- )
+ fun getPictureUploadsSourcePath(): String? = _pictureUploads.value?.sourcePath
+
+ fun getDefaultSourcePath(): String = getDefaultCameraSourcePath()
fun handleSelectPictureUploadsPath(data: Intent?) {
val folderToUpload = data?.getParcelableExtra(UploadPathActivity.EXTRA_FOLDER)
folderToUpload?.remotePath?.let {
- preferencesProvider.putString(
- key = PREF__CAMERA_PICTURE_UPLOADS_PATH,
- value = it
- )
+ viewModelScope.launch(coroutinesDispatcherProvider.io) {
+ savePictureUploadsConfigurationUseCase.execute(
+ SavePictureUploadsConfigurationUseCase.Params(composePictureUploadsConfiguration(uploadPath = it))
+ )
+ }
}
}
- fun handleSelectPictureUploadsSourcePath(data: Intent?) {
- // If the source path has changed, update camera uploads last sync
- var previousSourcePath = preferencesProvider.getString(
- key = PREF__CAMERA_PICTURE_UPLOADS_SOURCE,
- defaultValue = CameraUploadsConfiguration.getDefaultSourcePath()
- )
+ fun handleSelectAccount(accountName: String) {
+ viewModelScope.launch(coroutinesDispatcherProvider.io) {
+ savePictureUploadsConfigurationUseCase.execute(
+ SavePictureUploadsConfigurationUseCase.Params(composePictureUploadsConfiguration(accountName = accountName))
+ )
+ }
+ }
- previousSourcePath = previousSourcePath?.trimEnd(File.separatorChar)
+ fun handleSelectBehaviour(behaviorString: String) {
+ val behavior = FolderBackUpConfiguration.Behavior.fromString(behaviorString)
- if (previousSourcePath != data?.getStringExtra(LocalFolderPickerActivity.EXTRA_PATH)) {
- val currentTimeStamp = System.currentTimeMillis()
- cameraUploadsHandlerProvider.updatePicturesLastSync(currentTimeStamp)
+ viewModelScope.launch(coroutinesDispatcherProvider.io) {
+ savePictureUploadsConfigurationUseCase.execute(
+ SavePictureUploadsConfigurationUseCase.Params(composePictureUploadsConfiguration(behavior = behavior))
+ )
}
+ }
- data?.getStringExtra(LocalFolderPickerActivity.EXTRA_PATH)?.let {
- preferencesProvider.putString(
- key = PREF__CAMERA_PICTURE_UPLOADS_SOURCE,
- value = it
+ fun handleSelectPictureUploadsSourcePath(contentUriForTree: Uri) {
+ // If the source path has changed, update camera uploads last sync
+ var previousSourcePath = _pictureUploads.value?.sourcePath ?: getDefaultCameraSourcePath()
+
+ previousSourcePath = previousSourcePath.trimEnd(File.separatorChar)
+
+ viewModelScope.launch(coroutinesDispatcherProvider.io) {
+ savePictureUploadsConfigurationUseCase.execute(
+ SavePictureUploadsConfigurationUseCase.Params(
+ composePictureUploadsConfiguration(
+ sourcePath = contentUriForTree.toString(),
+ timestamp = System.currentTimeMillis().takeIf { previousSourcePath != contentUriForTree.encodedPath }
+ )
+ )
)
}
}
- fun schedulePictureUploadsSyncJob() = cameraUploadsHandlerProvider.schedulePictureUploadsSyncJob()
+ fun schedulePictureUploads() {
+ workManagerProvider.enqueueCameraUploadsWorker()
+ }
+
+ private fun composePictureUploadsConfiguration(
+ accountName: String? = _pictureUploads.value?.accountName,
+ uploadPath: String? = _pictureUploads.value?.uploadPath,
+ wifiOnly: Boolean? = _pictureUploads.value?.wifiOnly,
+ sourcePath: String? = _pictureUploads.value?.sourcePath,
+ behavior: FolderBackUpConfiguration.Behavior? = _pictureUploads.value?.behavior,
+ timestamp: Long? = _pictureUploads.value?.lastSyncTimestamp
+ ): FolderBackUpConfiguration =
+ FolderBackUpConfiguration(
+ accountName = accountName ?: accountProvider.getCurrentOwnCloudAccount()!!.name,
+ behavior = behavior ?: FolderBackUpConfiguration.Behavior.COPY,
+ sourcePath = sourcePath.orEmpty(),
+ uploadPath = uploadPath ?: PREF__CAMERA_UPLOADS_DEFAULT_PATH,
+ wifiOnly = wifiOnly ?: false,
+ lastSyncTimestamp = timestamp ?: System.currentTimeMillis(),
+ name = _pictureUploads.value?.name ?: pictureUploadsName
+ )
}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/viewmodels/settings/SettingsVideoUploadsViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/viewmodels/settings/SettingsVideoUploadsViewModel.kt
index a760f590303..6dbda70b18f 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/presentation/viewmodels/settings/SettingsVideoUploadsViewModel.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/viewmodels/settings/SettingsVideoUploadsViewModel.kt
@@ -21,98 +21,154 @@
package com.owncloud.android.presentation.viewmodels.settings
import android.content.Intent
+import android.net.Uri
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
-import com.owncloud.android.data.preferences.datasources.SharedPreferencesProvider
+import androidx.lifecycle.viewModelScope
import com.owncloud.android.datamodel.OCFile
-import com.owncloud.android.db.PreferenceManager.CameraUploadsConfiguration
import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_UPLOADS_DEFAULT_PATH
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_ENABLED
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_PATH
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_SOURCE
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration.Companion.videoUploadsName
+import com.owncloud.android.domain.camerauploads.usecases.GetVideoUploadsConfigurationStreamUseCase
+import com.owncloud.android.domain.camerauploads.usecases.ResetVideoUploadsUseCase
+import com.owncloud.android.domain.camerauploads.usecases.SaveVideoUploadsConfigurationUseCase
import com.owncloud.android.providers.AccountProvider
-import com.owncloud.android.providers.CameraUploadsHandlerProvider
-import com.owncloud.android.ui.activity.LocalFolderPickerActivity
+import com.owncloud.android.providers.CoroutinesDispatcherProvider
+import com.owncloud.android.providers.WorkManagerProvider
import com.owncloud.android.ui.activity.UploadPathActivity
+import com.owncloud.android.utils.FileStorageUtils
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
import java.io.File
class SettingsVideoUploadsViewModel(
- private val preferencesProvider: SharedPreferencesProvider,
- private val cameraUploadsHandlerProvider: CameraUploadsHandlerProvider,
- private val accountProvider: AccountProvider
+ private val accountProvider: AccountProvider,
+ private val saveVideoUploadsConfigurationUseCase: SaveVideoUploadsConfigurationUseCase,
+ private val getVideoUploadsConfigurationStreamUseCase: GetVideoUploadsConfigurationStreamUseCase,
+ private val resetVideoUploadsUseCase: ResetVideoUploadsUseCase,
+ private val workManagerProvider: WorkManagerProvider,
+ private val coroutinesDispatcherProvider: CoroutinesDispatcherProvider,
) : ViewModel() {
- fun isVideoUploadEnabled() =
- preferencesProvider.getBoolean(PREF__CAMERA_VIDEO_UPLOADS_ENABLED, false)
+ private val _videoUploads: MutableLiveData = MutableLiveData()
+ val videoUploads: LiveData = _videoUploads
- fun setEnableVideoUpload(value: Boolean) {
- preferencesProvider.putBoolean(PREF__CAMERA_VIDEO_UPLOADS_ENABLED, value)
+ init {
+ initVideoUploads()
+ }
+
+ private fun initVideoUploads() {
+ viewModelScope.launch(coroutinesDispatcherProvider.io) {
+ getVideoUploadsConfigurationStreamUseCase.execute(Unit).collect { videoUploadsConfiguration ->
+ _videoUploads.postValue(videoUploadsConfiguration)
+ }
+ }
+ }
- if (value) {
- // Use current account as default. It should never be null. If no accounts are attached, video uploads are hidden
- accountProvider.getCurrentOwnCloudAccount()?.name?.let { name ->
- preferencesProvider.putString(
- key = PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME,
- value = name
+ fun enableVideoUploads() {
+ // Use current account as default. It should never be null. If no accounts are attached, video uploads are hidden
+ accountProvider.getCurrentOwnCloudAccount()?.name?.let { name ->
+ viewModelScope.launch(coroutinesDispatcherProvider.io) {
+ saveVideoUploadsConfigurationUseCase.execute(
+ SaveVideoUploadsConfigurationUseCase.Params(composeVideoUploadsConfiguration(accountName = name))
)
}
- } else {
- // Reset fields after disabling the feature
- preferencesProvider.removePreference(key = PREF__CAMERA_VIDEO_UPLOADS_PATH)
- preferencesProvider.removePreference(key = PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME)
}
}
- fun updateVideosLastSync() = cameraUploadsHandlerProvider.updateVideosLastSync(0)
+ fun disableVideoUploads() {
+ viewModelScope.launch(coroutinesDispatcherProvider.io) {
+ resetVideoUploadsUseCase.execute(Unit)
+ }
+ }
- fun getVideoUploadsAccount() = preferencesProvider.getString(
- key = PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME,
- defaultValue = null
- )
+ fun useWifiOnly(wifiOnly: Boolean) {
+ viewModelScope.launch(coroutinesDispatcherProvider.io) {
+ saveVideoUploadsConfigurationUseCase.execute(
+ SaveVideoUploadsConfigurationUseCase.Params(
+ composeVideoUploadsConfiguration(wifiOnly = wifiOnly)
+ )
+ )
+ }
+ }
+
+ fun getVideoUploadsAccount() = _videoUploads.value?.accountName
fun getLoggedAccountNames(): Array = accountProvider.getLoggedAccounts().map { it.name }.toTypedArray()
- fun getVideoUploadsPath() = preferencesProvider.getString(
- key = PREF__CAMERA_VIDEO_UPLOADS_PATH,
- defaultValue = PREF__CAMERA_UPLOADS_DEFAULT_PATH
- )
+ fun getVideoUploadsPath() = _videoUploads.value?.uploadPath ?: PREF__CAMERA_UPLOADS_DEFAULT_PATH
- fun getVideoUploadsSourcePath() = preferencesProvider.getString(
- key = PREF__CAMERA_VIDEO_UPLOADS_SOURCE,
- defaultValue = CameraUploadsConfiguration.getDefaultSourcePath()
- )
+ fun getVideoUploadsSourcePath(): String? = _videoUploads.value?.sourcePath
+
+ fun getDefaultCameraSourcePath(): String = FileStorageUtils.getDefaultCameraSourcePath()
fun handleSelectVideoUploadsPath(data: Intent?) {
val folderToUpload = data?.getParcelableExtra(UploadPathActivity.EXTRA_FOLDER)
folderToUpload?.remotePath?.let {
- preferencesProvider.putString(
- key = PREF__CAMERA_VIDEO_UPLOADS_PATH,
- value = it
- )
+ viewModelScope.launch(coroutinesDispatcherProvider.io) {
+ saveVideoUploadsConfigurationUseCase.execute(
+ SaveVideoUploadsConfigurationUseCase.Params(composeVideoUploadsConfiguration(uploadPath = it))
+ )
+ }
}
}
- fun handleSelectVideoUploadsSourcePath(data: Intent?) {
- // If the source path has changed, update camera uploads last sync
- var previousSourcePath = preferencesProvider.getString(
- key = PREF__CAMERA_VIDEO_UPLOADS_SOURCE,
- defaultValue = CameraUploadsConfiguration.getDefaultSourcePath()
- )
+ fun handleSelectAccount(accountName: String) {
+ viewModelScope.launch(coroutinesDispatcherProvider.io) {
+ saveVideoUploadsConfigurationUseCase.execute(
+ SaveVideoUploadsConfigurationUseCase.Params(composeVideoUploadsConfiguration(accountName = accountName))
+ )
+ }
+ }
- previousSourcePath = previousSourcePath?.trimEnd(File.separatorChar)
+ fun handleSelectBehaviour(behaviorString: String) {
+ val behavior = FolderBackUpConfiguration.Behavior.fromString(behaviorString)
- if (previousSourcePath != data?.getStringExtra(LocalFolderPickerActivity.EXTRA_PATH)) {
- val currentTimeStamp = System.currentTimeMillis()
- cameraUploadsHandlerProvider.updateVideosLastSync(currentTimeStamp)
+ viewModelScope.launch(coroutinesDispatcherProvider.io) {
+ saveVideoUploadsConfigurationUseCase.execute(
+ SaveVideoUploadsConfigurationUseCase.Params(composeVideoUploadsConfiguration(behavior = behavior))
+ )
}
+ }
- data?.getStringExtra(LocalFolderPickerActivity.EXTRA_PATH)?.let {
- preferencesProvider.putString(
- key = PREF__CAMERA_VIDEO_UPLOADS_SOURCE,
- value = it
+ fun handleSelectVideoUploadsSourcePath(contentUriForTree: Uri) {
+ // If the source path has changed, update camera uploads last sync
+ var previousSourcePath = _videoUploads.value?.sourcePath ?: getDefaultCameraSourcePath()
+
+ previousSourcePath = previousSourcePath.trimEnd(File.separatorChar)
+
+ viewModelScope.launch(coroutinesDispatcherProvider.io) {
+ saveVideoUploadsConfigurationUseCase.execute(
+ SaveVideoUploadsConfigurationUseCase.Params(
+ composeVideoUploadsConfiguration(
+ sourcePath = contentUriForTree.toString(),
+ timestamp = System.currentTimeMillis().takeIf { previousSourcePath != contentUriForTree.encodedPath }
+ )
+ )
)
}
}
- fun scheduleVideoUploadsSyncJob() = cameraUploadsHandlerProvider.scheduleVideoUploadsSyncJob()
+ fun scheduleVideoUploads() {
+ workManagerProvider.enqueueCameraUploadsWorker()
+ }
+
+ private fun composeVideoUploadsConfiguration(
+ accountName: String? = _videoUploads.value?.accountName,
+ uploadPath: String? = _videoUploads.value?.uploadPath,
+ wifiOnly: Boolean? = _videoUploads.value?.wifiOnly,
+ sourcePath: String? = _videoUploads.value?.sourcePath,
+ behavior: FolderBackUpConfiguration.Behavior? = _videoUploads.value?.behavior,
+ timestamp: Long? = _videoUploads.value?.lastSyncTimestamp
+ ): FolderBackUpConfiguration =
+ FolderBackUpConfiguration(
+ accountName = accountName ?: accountProvider.getCurrentOwnCloudAccount()!!.name,
+ behavior = behavior ?: FolderBackUpConfiguration.Behavior.COPY,
+ sourcePath = sourcePath.orEmpty(),
+ uploadPath = uploadPath ?: PREF__CAMERA_UPLOADS_DEFAULT_PATH,
+ wifiOnly = wifiOnly ?: false,
+ lastSyncTimestamp = timestamp ?: System.currentTimeMillis(),
+ name = _videoUploads.value?.name ?: videoUploadsName
+ )
}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/providers/CameraUploadsHandlerProvider.kt b/owncloudApp/src/main/java/com/owncloud/android/providers/CameraUploadsHandlerProvider.kt
deleted file mode 100644
index 488003733f4..00000000000
--- a/owncloudApp/src/main/java/com/owncloud/android/providers/CameraUploadsHandlerProvider.kt
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- * ownCloud Android client application
- *
- * @author Juan Carlos Garrote Gascón
- *
- * Copyright (C) 2021 ownCloud GmbH.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.owncloud.android.providers
-
-import android.content.Context
-import com.owncloud.android.data.preferences.datasources.implementation.SharedPreferencesProviderImpl
-import com.owncloud.android.db.PreferenceManager
-import com.owncloud.android.files.services.CameraUploadsHandler
-
-class CameraUploadsHandlerProvider(
- private val context: Context
-) {
- private val cameraUploadsHandler = CameraUploadsHandler(PreferenceManager.getCameraUploadsConfiguration(context))
-
- fun updatePicturesLastSync(timestamp: Long) = cameraUploadsHandler.updatePicturesLastSync(context, timestamp)
-
- fun updateVideosLastSync(timestamp: Long) = cameraUploadsHandler.updateVideosLastSync(context, timestamp)
-
- fun schedulePictureUploadsSyncJob() {
- val configuration = PreferenceManager.getCameraUploadsConfiguration(context)
- if (configuration.isEnabledForPictures) {
- cameraUploadsHandler.setCameraUploadsConfig(configuration)
- cameraUploadsHandler.scheduleCameraUploadsSyncJob(context)
- }
- }
-
- fun scheduleVideoUploadsSyncJob() {
- val configuration = PreferenceManager.getCameraUploadsConfiguration(context)
- if (configuration.isEnabledForVideos) {
- cameraUploadsHandler.setCameraUploadsConfig(configuration)
- cameraUploadsHandler.scheduleCameraUploadsSyncJob(context)
- }
- }
-
- fun hasCameraUploadsAttached(accountName: String): Boolean {
- val cameraUploadsConfiguration = PreferenceManager.getCameraUploadsConfiguration(context)
-
- return accountName == cameraUploadsConfiguration.uploadAccountNameForPictures ||
- accountName == cameraUploadsConfiguration.uploadAccountNameForVideos
- }
-
- fun resetCameraUploadsForAccount(accountName: String) {
- val preferencesProvider = SharedPreferencesProviderImpl(context)
- val cameraUploadsConfiguration = PreferenceManager.getCameraUploadsConfiguration(context)
-
- if (accountName == cameraUploadsConfiguration.uploadAccountNameForPictures) {
- preferencesProvider.putBoolean(PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_ENABLED, false)
- preferencesProvider.removePreference(key = PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_PATH)
- preferencesProvider.removePreference(key = PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME)
- }
- if (accountName == cameraUploadsConfiguration.uploadAccountNameForVideos) {
- preferencesProvider.putBoolean(PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_ENABLED, false)
- preferencesProvider.removePreference(key = PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_PATH)
- preferencesProvider.removePreference(key = PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME)
- }
- }
-}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/providers/FileContentProvider.kt b/owncloudApp/src/main/java/com/owncloud/android/providers/FileContentProvider.kt
index 0f431802560..aa0f46fdc87 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/providers/FileContentProvider.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/providers/FileContentProvider.kt
@@ -54,6 +54,9 @@ import com.owncloud.android.data.ProviderMeta
import com.owncloud.android.data.capabilities.datasources.implementation.OCLocalCapabilitiesDataSource
import com.owncloud.android.data.capabilities.datasources.implementation.OCLocalCapabilitiesDataSource.Companion.toModel
import com.owncloud.android.data.capabilities.db.OCCapabilityEntity
+import com.owncloud.android.data.folderbackup.datasources.FolderBackupLocalDataSource
+import com.owncloud.android.data.migrations.CameraUploadsMigrationToRoom
+import com.owncloud.android.data.preferences.datasources.SharedPreferencesProvider
import com.owncloud.android.data.sharing.shares.db.OCShareEntity
import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.datamodel.UploadsStorageManager
@@ -994,6 +997,47 @@ class FileContentProvider(val executors: Executors = Executors()) : ContentProvi
}
}
+ if (oldVersion < 34 && newVersion >= 34) {
+ Timber.i("SQL : Entering in the #34 Migrate old camera uploads configuration to room database")
+ db.beginTransaction()
+
+ var pictureUploadsTimestamp: Long = 0
+ var videoUploadsTimestamp: Long = 0
+
+ try {
+ val cursor = db.query(ProviderTableMeta.CAMERA_UPLOADS_SYNC_TABLE_NAME, null, null, null, null, null, null)
+
+ if (cursor.moveToFirst()) {
+ pictureUploadsTimestamp = cursor.getLong(cursor.getColumnIndex(ProviderTableMeta.PICTURES_LAST_SYNC_TIMESTAMP))
+ videoUploadsTimestamp = cursor.getLong(cursor.getColumnIndex(ProviderTableMeta.VIDEOS_LAST_SYNC_TIMESTAMP))
+ }
+
+ val sharedPreferencesProvider: SharedPreferencesProvider by inject()
+ val migrationToRoom = CameraUploadsMigrationToRoom(sharedPreferencesProvider)
+
+ val pictureUploadsConfiguration = migrationToRoom.getPictureUploadsConfigurationPreferences(pictureUploadsTimestamp)
+ val videoUploadsConfiguration = migrationToRoom.getVideoUploadsConfigurationPreferences(videoUploadsTimestamp)
+
+ val backupLocalDataSource: FolderBackupLocalDataSource by inject()
+ // Insert camera uploads configuration in new database
+ executors.diskIO().execute {
+ pictureUploadsConfiguration?.let { backupLocalDataSource.saveFolderBackupConfiguration(it) }
+ videoUploadsConfiguration?.let { backupLocalDataSource.saveFolderBackupConfiguration(it) }
+ if (pictureUploadsConfiguration != null || videoUploadsConfiguration != null) {
+ val workManagerProvider: WorkManagerProvider by inject()
+ workManagerProvider.enqueueCameraUploadsWorker()
+ }
+ }
+ cursor.close()
+ // Drop camera uploads timestamps from old database
+ db.execSQL("DROP TABLE IF EXISTS " + ProviderTableMeta.CAMERA_UPLOADS_SYNC_TABLE_NAME + ";")
+ db.setTransactionSuccessful()
+ upgraded = true
+ } finally {
+ db.endTransaction()
+ }
+ }
+
if (!upgraded) {
Timber.i("SQL : OUT of the ADD in onUpgrade; oldVersion == $oldVersion, newVersion == $newVersion")
}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/providers/WorkManagerProvider.kt b/owncloudApp/src/main/java/com/owncloud/android/providers/WorkManagerProvider.kt
new file mode 100644
index 00000000000..d3b6b4a3435
--- /dev/null
+++ b/owncloudApp/src/main/java/com/owncloud/android/providers/WorkManagerProvider.kt
@@ -0,0 +1,41 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.owncloud.android.providers
+
+import android.content.Context
+import androidx.work.ExistingPeriodicWorkPolicy
+import androidx.work.PeriodicWorkRequestBuilder
+import androidx.work.WorkManager
+import com.owncloud.android.workers.CameraUploadsWorker
+
+class WorkManagerProvider(
+ val context: Context
+) {
+ fun enqueueCameraUploadsWorker() {
+ val cameraUploadsWorker = PeriodicWorkRequestBuilder(
+ repeatInterval = CameraUploadsWorker.repeatInterval,
+ repeatIntervalTimeUnit = CameraUploadsWorker.repeatIntervalTimeUnit
+ ).addTag(CameraUploadsWorker.CAMERA_UPLOADS_WORKER)
+ .build()
+
+ WorkManager.getInstance(context)
+ .enqueueUniquePeriodicWork(CameraUploadsWorker.CAMERA_UPLOADS_WORKER, ExistingPeriodicWorkPolicy.KEEP, cameraUploadsWorker)
+ }
+}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/syncadapter/FileSyncAdapter.java b/owncloudApp/src/main/java/com/owncloud/android/syncadapter/FileSyncAdapter.java
index b9000e9d0c7..2754dec7ebc 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/syncadapter/FileSyncAdapter.java
+++ b/owncloudApp/src/main/java/com/owncloud/android/syncadapter/FileSyncAdapter.java
@@ -45,8 +45,6 @@
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
import com.owncloud.android.operations.SyncCapabilitiesOperation;
import com.owncloud.android.operations.SynchronizeFolderOperation;
-import com.owncloud.android.presentation.ui.authentication.AuthenticatorConstants;
-import com.owncloud.android.presentation.ui.authentication.LoginActivity;
import com.owncloud.android.ui.activity.ErrorsWhileCopyingHandlerActivity;
import com.owncloud.android.utils.NotificationUtils;
import timber.log.Timber;
@@ -423,20 +421,13 @@ private void notifyFailedSynchronization() {
);
if (needsToUpdateCredentials) {
// let the user update credentials with one click
- Intent updateAccountCredentials = new Intent(getContext(), LoginActivity.class);
- updateAccountCredentials.putExtra(AuthenticatorConstants.EXTRA_ACCOUNT, getAccount());
- updateAccountCredentials.putExtra(AuthenticatorConstants.EXTRA_ACTION,
- AuthenticatorConstants.ACTION_UPDATE_EXPIRED_TOKEN);
- updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);
+ PendingIntent pendingIntentToRefreshCredentials =
+ NotificationUtils.INSTANCE.composePendingIntentToRefreshCredentials(getContext(), getAccount());
+
notificationBuilder
.setTicker(i18n(R.string.sync_fail_ticker_unauthorized))
.setContentTitle(i18n(R.string.sync_fail_ticker_unauthorized))
- .setContentIntent(PendingIntent.getActivity(
- getContext(), (int) System.currentTimeMillis(), updateAccountCredentials,
- PendingIntent.FLAG_ONE_SHOT
- ))
+ .setContentIntent(pendingIntentToRefreshCredentials)
.setContentText(i18n(R.string.sync_fail_content_unauthorized, getAccount().name));
} else {
notificationBuilder
diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/LocalFolderPickerActivity.java b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/LocalFolderPickerActivity.java
deleted file mode 100644
index b1ca96ef462..00000000000
--- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/LocalFolderPickerActivity.java
+++ /dev/null
@@ -1,259 +0,0 @@
-/**
- * ownCloud Android client application
- *
- * @author David A. Velasco
- * @author Christian Schabesberger
- * @author David González Verdugo
- * @author Shashvat Kedia
- * Copyright (C) 2020 ownCloud GmbH.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.owncloud.android.ui.activity;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Environment;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.ActionBar;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentTransaction;
-import com.owncloud.android.R;
-import com.owncloud.android.ui.fragment.LocalFileListFragment;
-import com.owncloud.android.utils.PreferenceUtils;
-import timber.log.Timber;
-
-import java.io.File;
-
-/**
- * Displays local folders and let the user choose one of them, which path is set as result.
- */
-
-public class LocalFolderPickerActivity extends ToolbarActivity implements LocalFileListFragment.ContainerActivity {
-
- public static final String EXTRA_PATH = LocalFolderPickerActivity.class.getCanonicalName() + ".PATH";
-
- private static final String FTAG_LIST_OF_FOLDERS = "LIST_OF_FOLDERS";
-
- private File mCurrentFolder = null;
-
- protected Button mCancelBtn;
- protected Button mChooseBtn;
- protected ImageButton mHomeBtn;
-
- /**
- * Helper to launch a {@link LocalFolderPickerActivity} for which you would like a result when finished.
- * Your onActivityResult() method will be called with the given requestCode.
- *
- * @param activity Activity calling {@link LocalFolderPickerActivity} for a result.
- * @param startPath Absolute path to the local folder to show when the activity is shown.
- * @param requestCode If >= 0, this code will be returned in onActivityResult().
- */
- public static void startLocalFolderPickerActivityForResult(
- Activity activity,
- String startPath,
- int requestCode
- ) {
- Intent action = new Intent(activity, LocalFolderPickerActivity.class);
- action.putExtra(LocalFolderPickerActivity.EXTRA_PATH, startPath);
- activity.startActivityForResult(action, requestCode);
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- Timber.v("onCreate() start");
- super.onCreate(savedInstanceState);
-
- // set current folder
- String startPath = (savedInstanceState != null) ?
- savedInstanceState.getString(LocalFolderPickerActivity.EXTRA_PATH) :
- getIntent().getStringExtra(EXTRA_PATH);
- if (startPath != null) {
- mCurrentFolder = new File(startPath);
- }
- if (mCurrentFolder == null || !mCurrentFolder.exists()) {
- mCurrentFolder = Environment.getExternalStorageDirectory(); // default
- } else if (!mCurrentFolder.isDirectory()) {
- mCurrentFolder = mCurrentFolder.getParentFile();
- }
-
- // inflate and set the layout view
- setContentView(R.layout.files_folder_picker); // beware - inflated in other activities too
-
- // Allow or disallow touches with other visible windows
- LinearLayout filesFolderPickerLayout = findViewById(R.id.filesFolderPickerLayout);
- filesFolderPickerLayout.setFilterTouchesWhenObscured(
- PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(this)
- );
-
- if (savedInstanceState == null) {
- createFragments();
- }
-
- // set input controllers
- mCancelBtn = findViewById(R.id.folder_picker_btn_cancel);
- mCancelBtn.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- setResult(RESULT_CANCELED);
- finish();
- }
- });
- mChooseBtn = findViewById(R.id.folder_picker_btn_choose);
- mChooseBtn.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- // return the path of the current folder
- Intent data = new Intent();
- data.putExtra(EXTRA_PATH, mCurrentFolder.getAbsolutePath());
- setResult(RESULT_OK, data);
- finish();
- }
- });
- mHomeBtn = findViewById(R.id.folder_picker_btn_home);
- mHomeBtn.setVisibility(View.VISIBLE);
- mHomeBtn.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mCurrentFolder = Environment.getExternalStorageDirectory();
- getListFragment().listFolder(mCurrentFolder);
- updateActionBar();
- }
- });
-
- // init toolbar
- setupStandardToolbar(null, true, true, true);
- updateActionBar();
-
- Timber.v("onCreate() end");
- }
-
- private void createFragments() {
- LocalFileListFragment listOfFiles = LocalFileListFragment.newInstance(true);
- FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
- transaction.add(R.id.fragment_container, listOfFiles, FTAG_LIST_OF_FOLDERS);
- transaction.commit();
- }
-
- /**
- * Updates contents shown by action bar.
- */
- private void updateActionBar() {
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- boolean mayBrowseUp = mayBrowseUp();
- updateStandardToolbar(
- mayBrowseUp ? mCurrentFolder.getName() : File.separator,
- mayBrowseUp,
- mayBrowseUp
- );
- } else {
- Timber.w("Action bar missing in action");
- }
- }
-
- /**
- * Handles presses on 'Up' button, not exactly the same as 'BACK' button.
- *
- * @param item Action in option menu tapped by the user.
- * @return 'true' if consumed, 'false' otherwise.
- */
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- boolean retval = true;
- switch (item.getItemId()) {
- case android.R.id.home: {
- if (mayBrowseUp()) {
- onBackPressed();
- }
- break;
- }
- default:
- retval = super.onOptionsItemSelected(item);
- }
- return retval;
- }
-
- /**
- * Handles presses on 'BACK' button.
- */
- @Override
- public void onBackPressed() {
- if (!mayBrowseUp()) {
- finish();
- return;
- }
- LocalFileListFragment listFragment = getListFragment();
- if (listFragment != null) {
- listFragment.browseUp();
- mCurrentFolder = listFragment.getCurrentFolder();
- updateActionBar();
- } else {
- Timber.e("List of files not found - cannot browse up");
- }
- }
-
- /**
- * @return 'true' when browsing to the parent folder is possible, 'false' otherwise
- */
- private boolean mayBrowseUp() {
- return (mCurrentFolder != null && mCurrentFolder.getParentFile() != null);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- Timber.v("onSaveInstanceState() start");
- super.onSaveInstanceState(outState);
- outState.putString(LocalFolderPickerActivity.EXTRA_PATH, mCurrentFolder.getAbsolutePath());
- Timber.v("onSaveInstanceState() end");
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onFolderClicked(File folder) {
- if (folder.isDirectory()) {
- mCurrentFolder = folder;
- }
- updateActionBar();
- }
-
- @Override
- public File getCurrentFolder() {
- return mCurrentFolder;
- }
-
- @Nullable
- protected LocalFileListFragment getListFragment() {
- Fragment listOfFiles = getSupportFragmentManager().findFragmentByTag(
- FTAG_LIST_OF_FOLDERS
- );
- if (listOfFiles != null) {
- return (LocalFileListFragment) listOfFiles;
- }
- return null;
- }
-
-}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java
index 0748ccb1951..f4ae06225b4 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java
+++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java
@@ -42,28 +42,37 @@
import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat;
+import androidx.work.WorkManager;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.authentication.AccountUtils;
import com.owncloud.android.datamodel.FileDataStorageManager;
-import com.owncloud.android.files.services.CameraUploadsHandler;
+import com.owncloud.android.domain.UseCaseResult;
+import com.owncloud.android.domain.camerauploads.model.CameraUploadsConfiguration;
+import com.owncloud.android.domain.camerauploads.usecases.GetCameraUploadsConfigurationUseCase;
import com.owncloud.android.files.services.FileDownloader;
import com.owncloud.android.files.services.FileUploader;
import com.owncloud.android.presentation.ui.authentication.AuthenticatorConstants;
import com.owncloud.android.presentation.ui.authentication.LoginActivity;
-import com.owncloud.android.providers.CameraUploadsHandlerProvider;
import com.owncloud.android.services.OperationsService;
import com.owncloud.android.ui.adapter.AccountListAdapter;
import com.owncloud.android.ui.adapter.AccountListItem;
import com.owncloud.android.ui.dialog.RemoveAccountDialogFragment;
+import com.owncloud.android.ui.dialog.RemoveAccountDialogViewModel;
import com.owncloud.android.ui.helpers.FileOperationsHelper;
+import com.owncloud.android.usecases.CancelUploadFromAccountUseCase;
import com.owncloud.android.utils.PreferenceUtils;
+import kotlin.Lazy;
+import kotlin.Unit;
+import org.jetbrains.annotations.NotNull;
import timber.log.Timber;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
+import static org.koin.java.KoinJavaComponent.inject;
+
/**
* An Activity that allows the user to manage accounts.
*/
@@ -87,9 +96,13 @@ public class ManageAccountsActivity extends FileActivity
String mOriginalCurrentAccount;
private Drawable mTintedCheck;
+ private RemoveAccountDialogViewModel mRemoveAccountDialogViewModel = null;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ @NotNull Lazy removeAccountDialogViewModelLazy = inject(RemoveAccountDialogViewModel.class);
+ mRemoveAccountDialogViewModel = removeAccountDialogViewModelLazy.getValue();
mTintedCheck = ContextCompat.getDrawable(this, R.drawable.ic_current_white);
mTintedCheck = DrawableCompat.wrap(mTintedCheck);
@@ -228,10 +241,9 @@ public boolean onOptionsItemSelected(MenuItem item) {
@Override
public void removeAccount(Account account) {
mAccountBeingRemoved = account.name;
- CameraUploadsHandlerProvider cameraUploadsHandlerProvider = new CameraUploadsHandlerProvider(this);
RemoveAccountDialogFragment dialog = RemoveAccountDialogFragment.newInstance(
account,
- cameraUploadsHandlerProvider.hasCameraUploadsAttached(account.name)
+ mRemoveAccountDialogViewModel.hasCameraUploadsAttached(account.name)
);
dialog.show(getSupportFragmentManager(), RemoveAccountDialogFragment.FTAG_CONFIRMATION);
}
@@ -320,6 +332,8 @@ public void run(AccountManagerFuture future) {
if (mDownloaderBinder != null) {
mDownloaderBinder.cancel(account);
}
+ CancelUploadFromAccountUseCase cancelUploadFromAccountUseCase = new CancelUploadFromAccountUseCase(WorkManager.getInstance(getBaseContext()));
+ cancelUploadFromAccountUseCase.execute(new CancelUploadFromAccountUseCase.Params(account.name));
}
mAccountListAdapter = new AccountListAdapter(this, getAccountListItems(), mTintedCheck);
diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java b/owncloudApp/src/main/java/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java
index a567bc0da05..62d09af99b5 100755
--- a/owncloudApp/src/main/java/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java
+++ b/owncloudApp/src/main/java/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Bitmap;
+import android.net.Uri;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
@@ -38,6 +39,8 @@
import android.widget.TextView;
import androidx.appcompat.widget.AppCompatButton;
+import androidx.documentfile.provider.DocumentFile;
+import androidx.work.WorkManager;
import com.google.android.material.snackbar.Snackbar;
import com.owncloud.android.R;
import com.owncloud.android.authentication.AccountUtils;
@@ -54,6 +57,7 @@
import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.ui.fragment.OptionsInUploadListClickListener;
import com.owncloud.android.ui.fragment.UploadListFragment;
+import com.owncloud.android.usecases.CancelUploadWithIdUseCase;
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.MimetypeIconUtil;
import com.owncloud.android.utils.PreferenceUtils;
@@ -347,11 +351,19 @@ private View getView(OCUpload[] uploadsItems, int position, View convertView, Vi
rightButton.setImageResource(R.drawable.ic_action_cancel_grey);
rightButton.setVisibility(View.VISIBLE);
rightButton.setOnClickListener(v -> {
- FileUploader.FileUploaderBinder uploaderBinder = mParentActivity.getFileUploaderBinder();
- if (uploaderBinder != null) {
- uploaderBinder.cancel(upload);
- refreshView();
+ String localPath = upload.getLocalPath();
+ boolean isDocumentUri = DocumentFile.isDocumentUri(parent.getContext(), Uri.parse(localPath));
+ if (isDocumentUri) {
+ CancelUploadWithIdUseCase cancelUploadWithIdUseCase = new CancelUploadWithIdUseCase(WorkManager.getInstance(parent.getContext()));
+ cancelUploadWithIdUseCase.execute(new CancelUploadWithIdUseCase.Params(upload));
+ } else {
+ FileUploader.FileUploaderBinder uploaderBinder = mParentActivity.getFileUploaderBinder();
+
+ if (uploaderBinder != null) {
+ uploaderBinder.cancel(upload);
+ }
}
+ refreshView();
});
} else if (upload.getUploadStatus() == UploadStatus.UPLOAD_FAILED) {
diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/dialog/RemoveAccountDialogFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/dialog/RemoveAccountDialogFragment.kt
index 1800ebb8cac..5b4c6e1de95 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/ui/dialog/RemoveAccountDialogFragment.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/ui/dialog/RemoveAccountDialogFragment.kt
@@ -29,8 +29,8 @@ import android.os.Bundle
import android.os.Handler
import android.provider.DocumentsContract
import com.owncloud.android.R
-import com.owncloud.android.providers.CameraUploadsHandlerProvider
import com.owncloud.android.ui.dialog.ConfirmationDialogFragment.ConfirmationDialogFragmentListener
+import org.koin.java.KoinJavaComponent.inject
/**
* Dialog requiring confirmation before removing an OC Account.
@@ -40,6 +40,7 @@ import com.owncloud.android.ui.dialog.ConfirmationDialogFragment.ConfirmationDia
* Container Activity needs to implement AccountManagerCallback.
*/
class RemoveAccountDialogFragment : ConfirmationDialogFragment(), ConfirmationDialogFragmentListener {
+ val viewModel: RemoveAccountDialogViewModel by inject(RemoveAccountDialogViewModel::class.java)
private var targetAccount: Account? = null
@@ -72,8 +73,7 @@ class RemoveAccountDialogFragment : ConfirmationDialogFragment(), ConfirmationDi
am.removeAccount(targetAccount, callback, Handler())
// Reset camera uploads if they were enabled for this account
- val cameraUploadsHandlerProvider = CameraUploadsHandlerProvider(requireContext())
- cameraUploadsHandlerProvider.resetCameraUploadsForAccount(targetAccount!!.name)
+ viewModel.resetCameraUploadsForAccount(targetAccount!!.name)
// Notify removal to Document Provider
val authority = resources.getString(R.string.document_provider_authority)
diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/dialog/RemoveAccountDialogViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/dialog/RemoveAccountDialogViewModel.kt
new file mode 100644
index 00000000000..09ec3c0ba07
--- /dev/null
+++ b/owncloudApp/src/main/java/com/owncloud/android/ui/dialog/RemoveAccountDialogViewModel.kt
@@ -0,0 +1,68 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ *
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.owncloud.android.ui.dialog
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.owncloud.android.domain.camerauploads.model.CameraUploadsConfiguration
+import com.owncloud.android.domain.camerauploads.usecases.GetCameraUploadsConfigurationUseCase
+import com.owncloud.android.domain.camerauploads.usecases.ResetPictureUploadsUseCase
+import com.owncloud.android.domain.camerauploads.usecases.ResetVideoUploadsUseCase
+import com.owncloud.android.providers.CoroutinesDispatcherProvider
+import kotlinx.coroutines.launch
+
+class RemoveAccountDialogViewModel(
+ private val getCameraUploadsConfigurationUseCase: GetCameraUploadsConfigurationUseCase,
+ private val resetPictureUploadsUseCase: ResetPictureUploadsUseCase,
+ private val resetVideoUploadsUseCase: ResetVideoUploadsUseCase,
+ private val coroutinesDispatcherProvider: CoroutinesDispatcherProvider,
+) : ViewModel() {
+
+ init {
+ initCameraUploadsConfiguration()
+ }
+
+ private var cameraUploadsConfiguration: CameraUploadsConfiguration? = null
+
+ private fun initCameraUploadsConfiguration() {
+ viewModelScope.launch(coroutinesDispatcherProvider.io) {
+ cameraUploadsConfiguration = getCameraUploadsConfigurationUseCase.execute(Unit).getDataOrNull()
+ }
+ }
+
+ fun hasCameraUploadsAttached(accountName: String): Boolean {
+ return accountName == cameraUploadsConfiguration?.pictureUploadsConfiguration?.accountName ||
+ accountName == cameraUploadsConfiguration?.videoUploadsConfiguration?.accountName
+ }
+
+ fun resetCameraUploadsForAccount(accountName: String) {
+ viewModelScope.launch(coroutinesDispatcherProvider.io) {
+ val cameraUploadsConfiguration = getCameraUploadsConfigurationUseCase.execute(Unit)
+
+ if (accountName == cameraUploadsConfiguration.getDataOrNull()?.pictureUploadsConfiguration?.accountName) {
+ resetPictureUploadsUseCase.execute(Unit)
+ }
+ if (accountName == cameraUploadsConfiguration.getDataOrNull()?.videoUploadsConfiguration?.accountName) {
+ resetVideoUploadsUseCase.execute(Unit)
+ }
+ }
+ }
+}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/usecases/CancelUploadFromAccountUseCase.kt b/owncloudApp/src/main/java/com/owncloud/android/usecases/CancelUploadFromAccountUseCase.kt
new file mode 100644
index 00000000000..503e24ffc44
--- /dev/null
+++ b/owncloudApp/src/main/java/com/owncloud/android/usecases/CancelUploadFromAccountUseCase.kt
@@ -0,0 +1,43 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.usecases
+
+import androidx.work.WorkManager
+import com.owncloud.android.MainApp
+import com.owncloud.android.datamodel.UploadsStorageManager
+import com.owncloud.android.domain.BaseUseCase
+import timber.log.Timber
+
+class CancelUploadFromAccountUseCase(
+ private val workManager: WorkManager
+) : BaseUseCase() {
+
+ override fun run(params: Params) {
+ workManager.cancelAllWorkByTag(params.accountName)
+
+ val uploadsStorageManager = UploadsStorageManager(MainApp.appContext.contentResolver)
+ uploadsStorageManager.removeUploads(params.accountName)
+
+ Timber.i("Uploads of ${params.accountName} has been cancelled.")
+ }
+
+ data class Params(
+ val accountName: String,
+ )
+}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/usecases/CancelUploadWithIdUseCase.kt b/owncloudApp/src/main/java/com/owncloud/android/usecases/CancelUploadWithIdUseCase.kt
new file mode 100644
index 00000000000..1740befac41
--- /dev/null
+++ b/owncloudApp/src/main/java/com/owncloud/android/usecases/CancelUploadWithIdUseCase.kt
@@ -0,0 +1,44 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.usecases
+
+import androidx.work.WorkManager
+import com.owncloud.android.MainApp
+import com.owncloud.android.datamodel.OCUpload
+import com.owncloud.android.datamodel.UploadsStorageManager
+import com.owncloud.android.domain.BaseUseCase
+import timber.log.Timber
+
+class CancelUploadWithIdUseCase(
+ private val workManager: WorkManager
+) : BaseUseCase() {
+
+ override fun run(params: Params) {
+ workManager.cancelAllWorkByTag(params.upload.uploadId.toString())
+
+ val uploadsStorageManager = UploadsStorageManager(MainApp.appContext.contentResolver)
+ uploadsStorageManager.removeUpload(params.upload)
+
+ Timber.i("Upload with id ${params.upload.uploadId} has been cancelled.")
+ }
+
+ data class Params(
+ val upload: OCUpload,
+ )
+}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/usecases/UploadFileFromContentUriUseCase.kt b/owncloudApp/src/main/java/com/owncloud/android/usecases/UploadFileFromContentUriUseCase.kt
new file mode 100644
index 00000000000..dbeef88270a
--- /dev/null
+++ b/owncloudApp/src/main/java/com/owncloud/android/usecases/UploadFileFromContentUriUseCase.kt
@@ -0,0 +1,71 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.usecases
+
+import android.net.Uri
+import androidx.work.Constraints
+import androidx.work.NetworkType
+import androidx.work.OneTimeWorkRequestBuilder
+import androidx.work.WorkManager
+import androidx.work.workDataOf
+import com.owncloud.android.domain.BaseUseCase
+import com.owncloud.android.workers.UploadFileFromContentUriWorker
+import timber.log.Timber
+
+class UploadFileFromContentUriUseCase(
+ private val workManager: WorkManager
+) : BaseUseCase() {
+
+ override fun run(params: Params) {
+ val inputData = workDataOf(
+ UploadFileFromContentUriWorker.KEY_PARAM_ACCOUNT_NAME to params.accountName,
+ UploadFileFromContentUriWorker.KEY_PARAM_BEHAVIOR to params.behavior,
+ UploadFileFromContentUriWorker.KEY_PARAM_CONTENT_URI to params.contentUri.toString(),
+ UploadFileFromContentUriWorker.KEY_PARAM_LAST_MODIFIED to params.lastModifiedInSeconds,
+ UploadFileFromContentUriWorker.KEY_PARAM_UPLOAD_PATH to params.uploadPath,
+ UploadFileFromContentUriWorker.KEY_PARAM_UPLOAD_ID to params.uploadIdInStorageManager
+ )
+
+ val networkRequired = if (params.wifiOnly) NetworkType.UNMETERED else NetworkType.CONNECTED
+ val constraints = Constraints.Builder()
+ .setRequiredNetworkType(networkRequired)
+ .build()
+
+ val uploadFileFromContentUriWorker = OneTimeWorkRequestBuilder()
+ .setInputData(inputData)
+ .setConstraints(constraints)
+ .addTag(params.accountName)
+ .addTag(params.uploadIdInStorageManager.toString())
+ .addTag(UploadFileFromContentUriWorker.TRANSFER_TAG_CAMERA_UPLOAD)
+ .build()
+
+ workManager.enqueue(uploadFileFromContentUriWorker)
+ Timber.i("Upload of ${params.contentUri.path} has been enqueued.")
+ }
+
+ data class Params(
+ val accountName: String,
+ val contentUri: Uri,
+ val lastModifiedInSeconds: String,
+ val behavior: String,
+ val uploadPath: String,
+ val uploadIdInStorageManager: Long,
+ val wifiOnly: Boolean
+ )
+}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/utils/FileStorageUtils.java b/owncloudApp/src/main/java/com/owncloud/android/utils/FileStorageUtils.java
index 99112ee2272..1a2567d4fd4 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/utils/FileStorageUtils.java
+++ b/owncloudApp/src/main/java/com/owncloud/android/utils/FileStorageUtils.java
@@ -47,7 +47,6 @@ public class FileStorageUtils {
public static final int SORT_DATE = 1;
public static final int SORT_SIZE = 2;
public static final int FILE_DISPLAY_SORT = 3;
- public static final String CAMERA_FOLDER = "/Camera";
public static Integer mSortOrderFileDisp = SORT_NAME;
public static Boolean mSortAscendingFileDisp = true;
@@ -239,6 +238,6 @@ public static boolean deleteDir(File dir) {
}
public static String getDefaultCameraSourcePath() {
- return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath() + CAMERA_FOLDER;
+ return getLocalStorageProvider().getDefaultCameraSourcePath();
}
}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/utils/NotificationUtils.kt b/owncloudApp/src/main/java/com/owncloud/android/utils/NotificationUtils.kt
index 5948f6030c8..6a9ed83ee5e 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/utils/NotificationUtils.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/utils/NotificationUtils.kt
@@ -26,6 +26,8 @@ import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
+import android.os.Build
+import android.os.Build.VERSION.SDK_INT
import android.os.Handler
import android.os.HandlerThread
import android.os.Process
@@ -33,7 +35,14 @@ import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import com.owncloud.android.R
import com.owncloud.android.datamodel.OCFile
+import com.owncloud.android.presentation.ui.authentication.ACTION_UPDATE_EXPIRED_TOKEN
+import com.owncloud.android.presentation.ui.authentication.EXTRA_ACCOUNT
+import com.owncloud.android.presentation.ui.authentication.EXTRA_ACTION
+import com.owncloud.android.presentation.ui.authentication.LoginActivity
+import com.owncloud.android.presentation.ui.settings.SettingsActivity
+import com.owncloud.android.presentation.ui.settings.SettingsActivity.Companion.KEY_NOTIFICATION_INTENT
import com.owncloud.android.ui.activity.ConflictsResolveActivity
+import com.owncloud.android.ui.activity.UploadListActivity
import java.util.Random
object NotificationUtils {
@@ -46,6 +55,80 @@ object NotificationUtils {
}
}
+ fun createBasicNotification(
+ context: Context,
+ contentTitle: String,
+ contentText: String,
+ notificationChannelId: String,
+ notificationId: Int,
+ intent: PendingIntent?,
+ onGoing: Boolean = false,
+ timeOut: Long?,
+ ) {
+ val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+
+ val notificationBuilder = newNotificationBuilder(context, notificationChannelId).apply {
+ setContentTitle(contentTitle)
+ color = ContextCompat.getColor(context, R.color.primary)
+ setSmallIcon(R.drawable.notification_icon)
+ setWhen(System.currentTimeMillis())
+ setContentText(contentText)
+ setOngoing(onGoing)
+ setAutoCancel(true)
+ }
+
+ intent?.let {
+ notificationBuilder.setContentIntent(it)
+ }
+
+ timeOut?.let {
+ // [setTimeoutAfter] was introduced in API 26.
+ // https://developer.android.com/reference/android/app/Notification.Builder#setTimeoutAfter(long)
+ notificationBuilder.setTimeoutAfter(it)
+ }
+
+ notificationManager.notify(notificationId, notificationBuilder.build())
+
+ // Remove success notification for devices with API < 26 with a workaround
+ if (SDK_INT < Build.VERSION_CODES.O && timeOut != null) {
+ cancelWithDelay(
+ notificationManager = notificationManager,
+ notificationId = notificationId,
+ delayInMillis = timeOut
+ )
+ }
+ }
+
+ fun composePendingIntentToRefreshCredentials(context: Context, account: Account): PendingIntent {
+ val updateCredentialsIntent =
+ Intent(context, LoginActivity::class.java).apply {
+ putExtra(EXTRA_ACCOUNT, account)
+ putExtra(EXTRA_ACTION, ACTION_UPDATE_EXPIRED_TOKEN)
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+ addFlags(Intent.FLAG_FROM_BACKGROUND)
+ }
+
+ return PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), updateCredentialsIntent, PendingIntent.FLAG_ONE_SHOT)
+ }
+
+ fun composePendingIntentToUploadList(context: Context): PendingIntent {
+ val showUploadListIntent = Intent(context, UploadListActivity::class.java).apply {
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ }
+
+ return PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), showUploadListIntent, 0)
+ }
+
+ fun composePendingIntentToCameraUploads(context: Context, notificationKey: String): PendingIntent {
+ val openSettingsActivity = Intent(context, SettingsActivity::class.java).apply {
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ putExtra(KEY_NOTIFICATION_INTENT, notificationKey)
+ }
+
+ return PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), openSettingsActivity, 0)
+ }
+
@JvmStatic
fun cancelWithDelay(
notificationManager: NotificationManager,
diff --git a/owncloudApp/src/main/java/com/owncloud/android/workers/CameraUploadsWorker.kt b/owncloudApp/src/main/java/com/owncloud/android/workers/CameraUploadsWorker.kt
new file mode 100644
index 00000000000..cc73406de07
--- /dev/null
+++ b/owncloudApp/src/main/java/com/owncloud/android/workers/CameraUploadsWorker.kt
@@ -0,0 +1,294 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.workers
+
+import android.content.Context
+import android.net.Uri
+import androidx.core.net.toUri
+import androidx.documentfile.provider.DocumentFile
+import androidx.work.CoroutineWorker
+import androidx.work.WorkManager
+import androidx.work.WorkerParameters
+import com.owncloud.android.R
+import com.owncloud.android.datamodel.OCUpload
+import com.owncloud.android.datamodel.UploadsStorageManager
+import com.owncloud.android.datamodel.UploadsStorageManager.UploadStatus
+import com.owncloud.android.domain.UseCaseResult
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration
+import com.owncloud.android.domain.camerauploads.usecases.GetCameraUploadsConfigurationUseCase
+import com.owncloud.android.domain.camerauploads.usecases.SavePictureUploadsConfigurationUseCase
+import com.owncloud.android.domain.camerauploads.usecases.SaveVideoUploadsConfigurationUseCase
+import com.owncloud.android.operations.UploadFileOperation.CREATED_AS_CAMERA_UPLOAD_PICTURE
+import com.owncloud.android.operations.UploadFileOperation.CREATED_AS_CAMERA_UPLOAD_VIDEO
+import com.owncloud.android.presentation.ui.settings.SettingsActivity
+import com.owncloud.android.usecases.UploadFileFromContentUriUseCase
+import com.owncloud.android.utils.MimetypeIconUtil
+import com.owncloud.android.utils.NotificationUtils
+import com.owncloud.android.utils.UPLOAD_NOTIFICATION_CHANNEL_ID
+import org.koin.core.KoinComponent
+import org.koin.core.inject
+import timber.log.Timber
+import java.io.File
+import java.util.Date
+import java.util.concurrent.TimeUnit
+
+class CameraUploadsWorker(
+ val appContext: Context,
+ workerParameters: WorkerParameters
+) : CoroutineWorker(
+ appContext,
+ workerParameters
+), KoinComponent {
+
+ enum class SyncType(val prefixForType: String) {
+ PICTURE_UPLOADS("image/"), VIDEO_UPLOADS("video/");
+
+ fun getNotificationId(): Int =
+ when (this) {
+ PICTURE_UPLOADS -> pictureUploadsNotificationId
+ VIDEO_UPLOADS -> videoUploadsNotificationId
+ }
+ }
+
+ private val getCameraUploadsConfigurationUseCase: GetCameraUploadsConfigurationUseCase by inject()
+
+ override suspend fun doWork(): Result {
+
+ when (val useCaseResult = getCameraUploadsConfigurationUseCase.execute(Unit)) {
+ is UseCaseResult.Success -> {
+ val cameraUploadsConfiguration = useCaseResult.data
+ if (cameraUploadsConfiguration == null || cameraUploadsConfiguration.areCameraUploadsDisabled()) {
+ cancelWorker()
+ return Result.success()
+ }
+ cameraUploadsConfiguration.pictureUploadsConfiguration?.let { pictureUploadsConfiguration ->
+ try {
+ checkSourcePathIsAValidUriOrThrowException(pictureUploadsConfiguration.sourcePath)
+ syncFolder(pictureUploadsConfiguration)
+ } catch (illegalArgumentException: IllegalArgumentException) {
+ showNotificationToUpdateUri(SyncType.PICTURE_UPLOADS)
+ return Result.failure()
+ }
+ }
+ cameraUploadsConfiguration.videoUploadsConfiguration?.let { videoUploadsConfiguration ->
+ try {
+ checkSourcePathIsAValidUriOrThrowException(videoUploadsConfiguration.sourcePath)
+ syncFolder(videoUploadsConfiguration)
+ } catch (illegalArgumentException: IllegalArgumentException) {
+ showNotificationToUpdateUri(SyncType.VIDEO_UPLOADS)
+ return Result.failure()
+ }
+ }
+ }
+ is UseCaseResult.Error -> {
+ Timber.e(useCaseResult.throwable, "Worker ${useCaseResult.throwable}")
+ }
+ }
+ return Result.success()
+ }
+
+ @Throws(IllegalArgumentException::class)
+ private fun checkSourcePathIsAValidUriOrThrowException(sourcePath: String) {
+ val sourceUri: Uri = sourcePath.toUri()
+ DocumentFile.fromTreeUri(applicationContext, sourceUri)
+ }
+
+ private fun cancelWorker() {
+ WorkManager.getInstance(appContext).cancelUniqueWork(CAMERA_UPLOADS_WORKER)
+ }
+
+ private fun syncFolder(folderBackUpConfiguration: FolderBackUpConfiguration?) {
+ if (folderBackUpConfiguration == null) return
+
+ val syncType = when {
+ folderBackUpConfiguration.isPictureUploads -> SyncType.PICTURE_UPLOADS
+ folderBackUpConfiguration.isVideoUploads -> SyncType.VIDEO_UPLOADS
+ // Else should not happen for the moment. Maybe in upcoming features..
+ else -> SyncType.PICTURE_UPLOADS
+ }
+
+ val localPicturesDocumentFiles: List = getFilesReadyToUpload(
+ syncType = syncType,
+ sourcePath = folderBackUpConfiguration.sourcePath,
+ lastSyncTimestamp = folderBackUpConfiguration.lastSyncTimestamp
+ )
+
+ showNotification(syncType, localPicturesDocumentFiles.size)
+
+ for (documentFile in localPicturesDocumentFiles) {
+ val uploadId = storeInUploadsDatabase(
+ documentFile = documentFile,
+ uploadPath = folderBackUpConfiguration.uploadPath.plus(File.separator).plus(documentFile.name),
+ accountName = folderBackUpConfiguration.accountName,
+ behavior = folderBackUpConfiguration.behavior,
+ createdByWorker = when (syncType) {
+ SyncType.PICTURE_UPLOADS -> CREATED_AS_CAMERA_UPLOAD_PICTURE
+ SyncType.VIDEO_UPLOADS -> CREATED_AS_CAMERA_UPLOAD_VIDEO
+ }
+ )
+ enqueueSingleUpload(
+ contentUri = documentFile.uri,
+ uploadPath = folderBackUpConfiguration.uploadPath.plus(File.separator).plus(documentFile.name),
+ lastModified = documentFile.lastModified(),
+ behavior = folderBackUpConfiguration.behavior.toString(),
+ accountName = folderBackUpConfiguration.accountName,
+ uploadId = uploadId,
+ wifiOnly = folderBackUpConfiguration.wifiOnly
+ )
+ }
+ updateTimestamp(folderBackUpConfiguration, syncType)
+ }
+
+ private fun showNotification(
+ syncType: SyncType,
+ numberOfFilesToUpload: Int
+ ) {
+ if (numberOfFilesToUpload == 0) return
+
+ val contentText = when (syncType) {
+ SyncType.PICTURE_UPLOADS -> R.string.uploader_upload_picture_upload_files
+ SyncType.VIDEO_UPLOADS -> R.string.uploader_upload_video_upload_files
+ }
+
+ NotificationUtils.createBasicNotification(
+ context = appContext,
+ contentTitle = appContext.getString(R.string.uploader_upload_camera_upload_files),
+ contentText = appContext.getString(contentText, numberOfFilesToUpload),
+ notificationChannelId = UPLOAD_NOTIFICATION_CHANNEL_ID,
+ notificationId = syncType.getNotificationId(),
+ intent = NotificationUtils.composePendingIntentToUploadList(appContext),
+ onGoing = false,
+ timeOut = 5_000
+ )
+ }
+
+ private fun showNotificationToUpdateUri(
+ syncType: SyncType
+ ) {
+ val contentText: Int = when (syncType) {
+ SyncType.PICTURE_UPLOADS -> R.string.uploader_upload_picture_upload_error
+ SyncType.VIDEO_UPLOADS -> R.string.uploader_upload_video_upload_error
+ }
+ val notificationKey: String = when (syncType) {
+ SyncType.PICTURE_UPLOADS -> SettingsActivity.NOTIFICATION_INTENT_PICTURES
+ SyncType.VIDEO_UPLOADS -> SettingsActivity.NOTIFICATION_INTENT_VIDEOS
+ }
+ NotificationUtils.createBasicNotification(
+ context = appContext,
+ contentTitle = appContext.getString(R.string.uploader_upload_camera_upload_source_path_error),
+ contentText = appContext.getString(contentText),
+ notificationChannelId = UPLOAD_NOTIFICATION_CHANNEL_ID,
+ notificationId = syncType.getNotificationId(),
+ intent = NotificationUtils.composePendingIntentToCameraUploads(appContext, notificationKey),
+ onGoing = false,
+ timeOut = null
+ )
+ }
+
+ private fun updateTimestamp(folderBackUpConfiguration: FolderBackUpConfiguration, syncType: SyncType) {
+ val currentTimestamp = System.currentTimeMillis()
+
+ when (syncType) {
+ SyncType.PICTURE_UPLOADS -> {
+ val savePictureUploadsConfigurationUseCase: SavePictureUploadsConfigurationUseCase by inject()
+ savePictureUploadsConfigurationUseCase.execute(
+ SavePictureUploadsConfigurationUseCase.Params(folderBackUpConfiguration.copy(lastSyncTimestamp = currentTimestamp))
+ )
+ }
+ SyncType.VIDEO_UPLOADS -> {
+ val saveVideoUploadsConfigurationUseCase: SaveVideoUploadsConfigurationUseCase by inject()
+ saveVideoUploadsConfigurationUseCase.execute(
+ SaveVideoUploadsConfigurationUseCase.Params(folderBackUpConfiguration.copy(lastSyncTimestamp = currentTimestamp))
+ )
+ }
+ }
+ }
+
+ private fun getFilesReadyToUpload(
+ syncType: SyncType,
+ sourcePath: String,
+ lastSyncTimestamp: Long,
+ ): List {
+ val sourceUri: Uri = sourcePath.toUri()
+ val documentTree = DocumentFile.fromTreeUri(applicationContext, sourceUri)
+ val arrayOfLocalFiles = documentTree?.listFiles() ?: arrayOf()
+
+ val filteredList: List = arrayOfLocalFiles
+ .sortedBy { it.lastModified() }
+ .filter { it.lastModified() > lastSyncTimestamp }
+ .filter { MimetypeIconUtil.getBestMimeTypeByFilename(it.name).startsWith(syncType.prefixForType) }
+
+ Timber.i("Last sync ${syncType.name}: ${Date(lastSyncTimestamp)}")
+ Timber.i("${arrayOfLocalFiles.size} files found in folder: ${sourceUri.path}")
+ Timber.i("${filteredList.size} files are ${syncType.name} and were taken after last sync")
+
+ return filteredList
+ }
+
+ private fun enqueueSingleUpload(
+ contentUri: Uri,
+ uploadPath: String,
+ lastModified: Long,
+ behavior: String,
+ accountName: String,
+ uploadId: Long,
+ wifiOnly: Boolean
+ ) {
+ val lastModifiedInSeconds = (lastModified / 1000L).toString()
+
+ UploadFileFromContentUriUseCase(WorkManager.getInstance(appContext)).execute(
+ UploadFileFromContentUriUseCase.Params(
+ accountName = accountName,
+ contentUri = contentUri,
+ lastModifiedInSeconds = lastModifiedInSeconds,
+ behavior = behavior,
+ uploadPath = uploadPath,
+ uploadIdInStorageManager = uploadId,
+ wifiOnly = wifiOnly
+ )
+ )
+ }
+
+ private fun storeInUploadsDatabase(
+ documentFile: DocumentFile,
+ uploadPath: String,
+ accountName: String,
+ behavior: FolderBackUpConfiguration.Behavior,
+ createdByWorker: Int
+ ): Long {
+ val uploadStorageManager = UploadsStorageManager(appContext.contentResolver)
+
+ val ocUpload = OCUpload(documentFile.uri.toString(), uploadPath, accountName).apply {
+ fileSize = documentFile.length()
+ isForceOverwrite = false
+ createdBy = createdByWorker
+ localAction = behavior.ordinal
+ uploadStatus = UploadStatus.UPLOAD_IN_PROGRESS
+ }
+ return uploadStorageManager.storeUpload(ocUpload)
+ }
+
+ companion object {
+ const val CAMERA_UPLOADS_WORKER = "CAMERA_UPLOADS_WORKER"
+ const val repeatInterval: Long = 15L
+ val repeatIntervalTimeUnit: TimeUnit = TimeUnit.MINUTES
+ private const val pictureUploadsNotificationId = 101
+ private const val videoUploadsNotificationId = 102
+ }
+}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/workers/UploadFileFromContentUriWorker.kt b/owncloudApp/src/main/java/com/owncloud/android/workers/UploadFileFromContentUriWorker.kt
new file mode 100644
index 00000000000..3ab52111322
--- /dev/null
+++ b/owncloudApp/src/main/java/com/owncloud/android/workers/UploadFileFromContentUriWorker.kt
@@ -0,0 +1,246 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.workers
+
+import android.accounts.Account
+import android.content.Context
+import android.net.Uri
+import androidx.core.net.toUri
+import androidx.documentfile.provider.DocumentFile
+import androidx.work.CoroutineWorker
+import androidx.work.WorkerParameters
+import com.owncloud.android.R
+import com.owncloud.android.authentication.AccountUtils
+import com.owncloud.android.data.executeRemoteOperation
+import com.owncloud.android.datamodel.OCUpload
+import com.owncloud.android.datamodel.UploadsStorageManager
+import com.owncloud.android.db.UploadResult
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration
+import com.owncloud.android.domain.exceptions.ConflictException
+import com.owncloud.android.domain.exceptions.FileNotFoundException
+import com.owncloud.android.domain.exceptions.ForbiddenException
+import com.owncloud.android.domain.exceptions.LocalFileNotFoundException
+import com.owncloud.android.domain.exceptions.NoConnectionWithServerException
+import com.owncloud.android.domain.exceptions.QuotaExceededException
+import com.owncloud.android.domain.exceptions.SSLRecoverablePeerUnverifiedException
+import com.owncloud.android.domain.exceptions.ServiceUnavailableException
+import com.owncloud.android.domain.exceptions.SpecificUnsupportedMediaTypeException
+import com.owncloud.android.domain.exceptions.UnauthorizedException
+import com.owncloud.android.extensions.parseError
+import com.owncloud.android.lib.common.OwnCloudAccount
+import com.owncloud.android.lib.common.OwnCloudClient
+import com.owncloud.android.lib.common.SingleSessionManager
+import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
+import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation
+import com.owncloud.android.lib.resources.files.ContentUriRequestBody
+import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation
+import com.owncloud.android.lib.resources.files.UploadFileFromContentUriOperation
+import com.owncloud.android.utils.NotificationUtils
+import com.owncloud.android.utils.RemoteFileUtils.Companion.getAvailableRemotePath
+import com.owncloud.android.utils.UPLOAD_NOTIFICATION_CHANNEL_ID
+import org.koin.core.KoinComponent
+import timber.log.Timber
+import java.io.File
+
+class UploadFileFromContentUriWorker(
+ private val appContext: Context,
+ private val workerParameters: WorkerParameters
+) : CoroutineWorker(
+ appContext,
+ workerParameters
+), KoinComponent {
+
+ private lateinit var account: Account
+ private lateinit var contentUri: Uri
+ private lateinit var lastModified: String
+ private lateinit var behavior: FolderBackUpConfiguration.Behavior
+ private lateinit var uploadPath: String
+ private var uploadIdInStorageManager: Long = -1
+
+ override suspend fun doWork(): Result {
+
+ if (!areParametersValid()) return Result.failure()
+
+ return try {
+ checkDocumentFileExists()
+ checkPermissionsToReadDocumentAreGranted()
+ checkParentFolderExistence()
+ checkNameCollisionAndGetAnAvailableOneInCase()
+ uploadDocument()
+ updateUploadsDatabaseWithResult(null)
+ Result.success()
+ } catch (throwable: Throwable) {
+ Timber.e(throwable)
+ showNotification(throwable)
+ updateUploadsDatabaseWithResult(throwable)
+ Result.failure()
+ }
+ }
+
+ private fun areParametersValid(): Boolean {
+ val paramAccountName = workerParameters.inputData.getString(KEY_PARAM_ACCOUNT_NAME)
+ val paramUploadPath = workerParameters.inputData.getString(KEY_PARAM_UPLOAD_PATH)
+ val paramLastModified = workerParameters.inputData.getString(KEY_PARAM_LAST_MODIFIED)
+ val paramBehavior = workerParameters.inputData.getString(KEY_PARAM_BEHAVIOR)
+ val paramContentUri = workerParameters.inputData.getString(KEY_PARAM_CONTENT_URI)
+ val paramUploadId = workerParameters.inputData.getLong(KEY_PARAM_UPLOAD_ID, -1)
+
+ account = AccountUtils.getOwnCloudAccountByName(appContext, paramAccountName) ?: return false
+ contentUri = paramContentUri?.toUri() ?: return false
+ uploadPath = paramUploadPath ?: return false
+ behavior = paramBehavior?.let { FolderBackUpConfiguration.Behavior.fromString(it) } ?: return false
+ lastModified = paramLastModified ?: return false
+ uploadIdInStorageManager = paramUploadId
+
+ return true
+ }
+
+ private fun checkPermissionsToReadDocumentAreGranted() {
+ val documentFile = DocumentFile.fromSingleUri(appContext, contentUri)
+ if (documentFile?.canRead() != true) {
+ // Permissions not granted. Throw an exception to ask for them.
+ throw Throwable("Cannot read the file")
+ }
+ }
+
+ private fun checkDocumentFileExists() {
+ val documentFile = DocumentFile.fromSingleUri(appContext, contentUri)
+ if (documentFile?.exists() != true && documentFile?.isFile != true) {
+ // File does not exists anymore. Throw an exception to tell the user
+ throw LocalFileNotFoundException()
+ }
+ }
+
+ private fun checkParentFolderExistence() {
+ var pathToGrant: String = File(uploadPath).parent ?: ""
+ pathToGrant = if (pathToGrant.endsWith(File.separator)) pathToGrant else pathToGrant + File.separator
+
+ val checkPathExistenceOperation = CheckPathExistenceRemoteOperation(pathToGrant, false)
+ val checkPathExistenceResult = checkPathExistenceOperation.execute(getClientForThisUpload())
+ if (checkPathExistenceResult.code == ResultCode.FILE_NOT_FOUND) {
+ val createRemoteFolderOperation = CreateRemoteFolderOperation(pathToGrant, true)
+ createRemoteFolderOperation.execute(getClientForThisUpload())
+ }
+ }
+
+ private fun checkNameCollisionAndGetAnAvailableOneInCase() {
+ Timber.d("Checking name collision in server")
+ val remotePath = getAvailableRemotePath(getClientForThisUpload(), uploadPath)
+ if (remotePath != null && remotePath != uploadPath) {
+ uploadPath = remotePath
+ Timber.d("Name collision detected, let's rename it to %s", remotePath)
+ }
+ }
+
+ private fun uploadDocument() {
+ val client = getClientForThisUpload()
+ val requestBody = ContentUriRequestBody(appContext.contentResolver, contentUri)
+
+ val uploadFileFromContentUriOperation = UploadFileFromContentUriOperation(uploadPath, lastModified, requestBody)
+
+ val result = executeRemoteOperation { uploadFileFromContentUriOperation.execute(client) }
+
+ if (result == Unit && behavior == FolderBackUpConfiguration.Behavior.MOVE) {
+ removeLocalFile()
+ }
+ }
+
+ private fun removeLocalFile() {
+ val documentFile = DocumentFile.fromSingleUri(appContext, contentUri)
+ documentFile?.delete()
+ }
+
+ private fun updateUploadsDatabaseWithResult(throwable: Throwable?) {
+ val uploadStorageManager = UploadsStorageManager(appContext.contentResolver)
+
+ val ocUpload = OCUpload(contentUri.toString(), uploadPath, account.name).apply {
+ uploadStatus = getUploadStatusForThrowable(throwable)
+ uploadEndTimestamp = System.currentTimeMillis()
+ lastResult = getUploadResultFromThrowable(throwable)
+ uploadId = uploadIdInStorageManager
+ }
+
+ uploadStorageManager.updateUpload(ocUpload)
+ }
+
+ private fun getUploadStatusForThrowable(throwable: Throwable?): UploadsStorageManager.UploadStatus {
+ return if (throwable == null) {
+ UploadsStorageManager.UploadStatus.UPLOAD_SUCCEEDED
+ } else {
+ UploadsStorageManager.UploadStatus.UPLOAD_FAILED
+ }
+ }
+
+ private fun showNotification(throwable: Throwable) {
+ // check credentials error
+ val needsToUpdateCredentials = throwable is UnauthorizedException
+
+ val tickerId =
+ if (needsToUpdateCredentials) R.string.uploader_upload_failed_credentials_error else R.string.uploader_upload_failed_ticker
+
+ val pendingIntent = if (needsToUpdateCredentials) {
+ NotificationUtils.composePendingIntentToRefreshCredentials(appContext, account)
+ } else {
+ NotificationUtils.composePendingIntentToUploadList(appContext)
+ }
+
+ NotificationUtils.createBasicNotification(
+ context = appContext,
+ contentTitle = appContext.getString(tickerId),
+ contentText = throwable.parseError("", appContext.resources, true).toString(),
+ notificationChannelId = UPLOAD_NOTIFICATION_CHANNEL_ID,
+ notificationId = 12,
+ intent = pendingIntent,
+ onGoing = false,
+ timeOut = null
+ )
+ }
+
+ private fun getUploadResultFromThrowable(throwable: Throwable?): UploadResult {
+ if (throwable == null) return UploadResult.UPLOADED
+
+ return when (throwable) {
+ is LocalFileNotFoundException -> UploadResult.FOLDER_ERROR
+ is NoConnectionWithServerException -> UploadResult.NETWORK_CONNECTION
+ is UnauthorizedException -> UploadResult.CREDENTIAL_ERROR
+ is FileNotFoundException -> UploadResult.FILE_NOT_FOUND
+ is ConflictException -> UploadResult.CONFLICT_ERROR
+ is ForbiddenException -> UploadResult.PRIVILEDGES_ERROR
+ is ServiceUnavailableException -> UploadResult.SERVICE_UNAVAILABLE
+ is QuotaExceededException -> UploadResult.QUOTA_EXCEEDED
+ is SpecificUnsupportedMediaTypeException -> UploadResult.SPECIFIC_UNSUPPORTED_MEDIA_TYPE
+ is SSLRecoverablePeerUnverifiedException -> UploadResult.SSL_RECOVERABLE_PEER_UNVERIFIED
+ else -> UploadResult.UNKNOWN
+ }
+ }
+
+ private fun getClientForThisUpload(): OwnCloudClient = SingleSessionManager.getDefaultSingleton()
+ .getClientFor(OwnCloudAccount(AccountUtils.getOwnCloudAccountByName(appContext, account.name), appContext), appContext)
+
+ companion object {
+ const val TRANSFER_TAG_CAMERA_UPLOAD = "TRANSFER_TAG_CAMERA_UPLOAD"
+
+ const val KEY_PARAM_ACCOUNT_NAME = "KEY_PARAM_ACCOUNT_NAME"
+ const val KEY_PARAM_BEHAVIOR = "KEY_PARAM_BEHAVIOR"
+ const val KEY_PARAM_CONTENT_URI = "KEY_PARAM_CONTENT_URI"
+ const val KEY_PARAM_LAST_MODIFIED = "KEY_PARAM_LAST_MODIFIED"
+ const val KEY_PARAM_UPLOAD_PATH = "KEY_PARAM_UPLOAD_PATH"
+ const val KEY_PARAM_UPLOAD_ID = "KEY_PARAM_UPLOAD_ID"
+ }
+}
diff --git a/owncloudApp/src/main/res/values-ar/strings.xml b/owncloudApp/src/main/res/values-ar/strings.xml
index 36813de9d57..97eb99a1281 100644
--- a/owncloudApp/src/main/res/values-ar/strings.xml
+++ b/owncloudApp/src/main/res/values-ar/strings.xml
@@ -434,8 +434,7 @@
%1$d ملفات، %2$d مجلدات
مجلد الكاميرا (%1$s)
مطلوب
- اختياري
- الملف الأصلي
+ الملف الأصلي
الملف الأصلي
نسخ الملف
نقل الملف
diff --git a/owncloudApp/src/main/res/values-bg-rBG/strings.xml b/owncloudApp/src/main/res/values-bg-rBG/strings.xml
index 187b8b46a1f..ee2b3187c5e 100644
--- a/owncloudApp/src/main/res/values-bg-rBG/strings.xml
+++ b/owncloudApp/src/main/res/values-bg-rBG/strings.xml
@@ -431,8 +431,7 @@
%1$d файла, %2$d папки
Папка за камерата (%1$s)
задължителен
- по избор
- Оригиналният файл ще бъде
+ Оригиналният файл ще бъде
Оригиналният файл ще бъде
Копиране на файл
Преместване на файл
diff --git a/owncloudApp/src/main/res/values-ca/strings.xml b/owncloudApp/src/main/res/values-ca/strings.xml
index ce66aa1d20a..14ae77f3507 100644
--- a/owncloudApp/src/main/res/values-ca/strings.xml
+++ b/owncloudApp/src/main/res/values-ca/strings.xml
@@ -432,8 +432,7 @@
%1$d fitxers, %2$d carpetes
Carpeta de la Càmera(%1$s)
requerit
- opcional
- L\'arxiu original serà
+ L\'arxiu original serà
L\'arxiu original serà
Copiar fitxer
Moure fitxer
diff --git a/owncloudApp/src/main/res/values-cs-rCZ/strings.xml b/owncloudApp/src/main/res/values-cs-rCZ/strings.xml
index bfcae2ae1a2..d8edef11d41 100644
--- a/owncloudApp/src/main/res/values-cs-rCZ/strings.xml
+++ b/owncloudApp/src/main/res/values-cs-rCZ/strings.xml
@@ -433,8 +433,7 @@ správce systému.
%1$d soubory(ů), %2$d adresáře(ů)
Adresář fotoaparátu (%1$s)
vyžadován
- nepovinný
- Původní soubor bude
+ Původní soubor bude
Původní soubor bude
Zkopírovat soubor
Přesunout soubor
diff --git a/owncloudApp/src/main/res/values-da/strings.xml b/owncloudApp/src/main/res/values-da/strings.xml
index d8855149c5e..1753ce9c89a 100644
--- a/owncloudApp/src/main/res/values-da/strings.xml
+++ b/owncloudApp/src/main/res/values-da/strings.xml
@@ -397,8 +397,7 @@
%1$d filer, %2$d mapper
Kamera mappe (%1$s)
Krævet
- Valgfri
- Kopier fil
+ Kopier fil
Flyt fil
opbevares i den oprindelige mappe
flyttet til app-mappe
diff --git a/owncloudApp/src/main/res/values-de-rCH/strings.xml b/owncloudApp/src/main/res/values-de-rCH/strings.xml
index 2e9571bf71e..285cfb396eb 100644
--- a/owncloudApp/src/main/res/values-de-rCH/strings.xml
+++ b/owncloudApp/src/main/res/values-de-rCH/strings.xml
@@ -435,8 +435,7 @@
%1$d Dateien, %2$d Ordner
Kamera-Ordner (%1$s)
Benötigt
- Optional
- Originale Datei wird
+ Originale Datei wird
Originale Datei wird
kopiere Datei
verschiebe Datei
diff --git a/owncloudApp/src/main/res/values-de-rDE/strings.xml b/owncloudApp/src/main/res/values-de-rDE/strings.xml
index 5ded538e541..fefa0b3e98f 100644
--- a/owncloudApp/src/main/res/values-de-rDE/strings.xml
+++ b/owncloudApp/src/main/res/values-de-rDE/strings.xml
@@ -437,8 +437,7 @@
%1$d Dateien, %2$d Ordner
Kamera-Ordner (%1$s)
Benötigt
- Optional
- Originaldatei wird...
+ Originaldatei wird...
Originaldatei wird...
Datei kopieren
Datei verschieben
diff --git a/owncloudApp/src/main/res/values-de/strings.xml b/owncloudApp/src/main/res/values-de/strings.xml
index 75ee828250f..701b9abda9c 100644
--- a/owncloudApp/src/main/res/values-de/strings.xml
+++ b/owncloudApp/src/main/res/values-de/strings.xml
@@ -439,8 +439,7 @@
%1$d Dateien, %2$d Ordner
Kamera-Ordner (%1$s)
Benötigt
- Optional
- Originale Datei wird
+ Originale Datei wird
Originale Datei wird
kopiere Datei
verschiebe Datei
diff --git a/owncloudApp/src/main/res/values-el/strings.xml b/owncloudApp/src/main/res/values-el/strings.xml
index b788ffdb005..da3163c6e7f 100644
--- a/owncloudApp/src/main/res/values-el/strings.xml
+++ b/owncloudApp/src/main/res/values-el/strings.xml
@@ -428,8 +428,7 @@
%1$d αρχεία, %2$d φάκελοι
Φάκελος κάμερας (%1$s)
απαιτείται
- προαιρετικό
- Το αρχικό αρχείο θα είναι
+ Το αρχικό αρχείο θα είναι
Το αρχικό αρχείο θα είναι
Αντιγραφή αρχείου
Μετακίνηση αρχείου
diff --git a/owncloudApp/src/main/res/values-en-rGB/strings.xml b/owncloudApp/src/main/res/values-en-rGB/strings.xml
index a9de4e3bf83..2b1a59a0a19 100644
--- a/owncloudApp/src/main/res/values-en-rGB/strings.xml
+++ b/owncloudApp/src/main/res/values-en-rGB/strings.xml
@@ -436,8 +436,7 @@
%1$d files, %2$d folders
Camera folder (%1$s)
required
- optional
- Original file will be
+ Original file will be
Original file will be
Copy file
Move file
diff --git a/owncloudApp/src/main/res/values-es-rAR/strings.xml b/owncloudApp/src/main/res/values-es-rAR/strings.xml
index dcde66eaefb..1b04fed3d69 100644
--- a/owncloudApp/src/main/res/values-es-rAR/strings.xml
+++ b/owncloudApp/src/main/res/values-es-rAR/strings.xml
@@ -438,8 +438,7 @@ Abajo, encontrás la lista con los enlaces a los archivos locales y remotos en %
%1$d archivos, %2$d carpetas
Carpeta de la cámara (%1$s)
requerido
- opcional
- El archivo original debe ser
+ El archivo original debe ser
El archivo original debe ser
Copiar archivo
Mover archivo
diff --git a/owncloudApp/src/main/res/values-es-rMX/strings.xml b/owncloudApp/src/main/res/values-es-rMX/strings.xml
index 7f5b0bcd57b..dd65a037494 100644
--- a/owncloudApp/src/main/res/values-es-rMX/strings.xml
+++ b/owncloudApp/src/main/res/values-es-rMX/strings.xml
@@ -404,8 +404,7 @@
%1$d archivos, %2$d carpetas
Carpeta de la Camera (%1$s)
requerido
- opcional
- El archivo original será...
+ El archivo original será...
El archivo original será...
Copiar archivo
Mover archivo
diff --git a/owncloudApp/src/main/res/values-es/strings.xml b/owncloudApp/src/main/res/values-es/strings.xml
index 456b2652ebe..0c7b89b755e 100644
--- a/owncloudApp/src/main/res/values-es/strings.xml
+++ b/owncloudApp/src/main/res/values-es/strings.xml
@@ -453,8 +453,7 @@
Cuenta para subir videos
Carpeta de cámara (%1$s)
requerido
- opcional
- Archivo original será
+ Archivo original será
Archivo original será
Copiar archivo
Mover archivo
diff --git a/owncloudApp/src/main/res/values-et-rEE/strings.xml b/owncloudApp/src/main/res/values-et-rEE/strings.xml
index 4bd2ffcd624..818b1722c05 100644
--- a/owncloudApp/src/main/res/values-et-rEE/strings.xml
+++ b/owncloudApp/src/main/res/values-et-rEE/strings.xml
@@ -352,8 +352,7 @@ Allpool on loend kohalikest failidest ning serveris asuvatest failidest %5$s, mi
%1$d faili, 1 kaust
%1$d faili, %2$d kausta
nõutud
- valikuline
- Algne fail
+ Algne fail
Algne failAlgne fail
Kopeeri fail
Liiguta fail
diff --git a/owncloudApp/src/main/res/values-fa/strings.xml b/owncloudApp/src/main/res/values-fa/strings.xml
index 995c0bd4833..bf1690acd5c 100644
--- a/owncloudApp/src/main/res/values-fa/strings.xml
+++ b/owncloudApp/src/main/res/values-fa/strings.xml
@@ -366,8 +366,7 @@
%1$d فایل، 1 پوشه
%1$d فایل, %2$d پوشه
ضروری
- اختیاری
- رونوشت از پرونده
+ رونوشت از پرونده
جابهجایی پرونده
اشتراک گذاری
همرسانی %1$s
diff --git a/owncloudApp/src/main/res/values-fi-rFI/strings.xml b/owncloudApp/src/main/res/values-fi-rFI/strings.xml
index fdf6c47ce40..ec1de0af38d 100644
--- a/owncloudApp/src/main/res/values-fi-rFI/strings.xml
+++ b/owncloudApp/src/main/res/values-fi-rFI/strings.xml
@@ -378,8 +378,7 @@
%1$d tiedostoa, %2$d kansiota
Kamerakansio (%1$s)
vaadittu
- valinnainen
- Kopioi tiedosto
+ Kopioi tiedosto
Siirrä tiedosto
pidetään alkuperäisessä kansiossa
siirretään sovelluskansioon
diff --git a/owncloudApp/src/main/res/values-fr/strings.xml b/owncloudApp/src/main/res/values-fr/strings.xml
index 4c9e9e9d479..31430c70f49 100644
--- a/owncloudApp/src/main/res/values-fr/strings.xml
+++ b/owncloudApp/src/main/res/values-fr/strings.xml
@@ -431,8 +431,7 @@ Ci-dessous la liste des fichiers locaux, et les fichiers distants dans %5$s auxq
%1$d fichiers, %2$d dossiers
Dossier de l\'appareil photo (%1$s)
Requis
- Optionnel
- Le fichier original sera
+ Le fichier original sera
Le fichier original sera
Copier le fichier
Déplacer le fichier
diff --git a/owncloudApp/src/main/res/values-gl/strings.xml b/owncloudApp/src/main/res/values-gl/strings.xml
index c47b13dfd18..64125852766 100644
--- a/owncloudApp/src/main/res/values-gl/strings.xml
+++ b/owncloudApp/src/main/res/values-gl/strings.xml
@@ -438,8 +438,7 @@ Descárgueo de aquí: %2$s
%1$d ficheiros, %2$d cartafoles
Cartafol da cámara (%1$s)
requirido
- opcional
- O ficheiro orixinal vai ser
+ O ficheiro orixinal vai ser
O ficheiro orixinal vai ser
Copiar ficheiro
Mover ficheiro
diff --git a/owncloudApp/src/main/res/values-he/strings.xml b/owncloudApp/src/main/res/values-he/strings.xml
index 2623df234fe..225f7a3996f 100644
--- a/owncloudApp/src/main/res/values-he/strings.xml
+++ b/owncloudApp/src/main/res/values-he/strings.xml
@@ -437,8 +437,7 @@
%1$d קבצים, %2$d תיקיות
תיקיית מצלמה (%1$s)
נדרש
- אופציונלי
- קובץ מקורי יהיה
+ קובץ מקורי יהיה
קובץ מקורי יהיה
העתקת קובץ
העברת קובץ
diff --git a/owncloudApp/src/main/res/values-hu-rHU/strings.xml b/owncloudApp/src/main/res/values-hu-rHU/strings.xml
index 92ac7e3ea5e..7d1e9fdf899 100644
--- a/owncloudApp/src/main/res/values-hu-rHU/strings.xml
+++ b/owncloudApp/src/main/res/values-hu-rHU/strings.xml
@@ -419,8 +419,7 @@
%1$d fájl, %2$d könyvtár
Kamera mappa (%1$s)
kötelező
- választható
- Eredeti fájl lesz ...
+ Eredeti fájl lesz ...
Eredeti fájl lesz ...
Fájl másolása
Fájl mozgatása
diff --git a/owncloudApp/src/main/res/values-id/strings.xml b/owncloudApp/src/main/res/values-id/strings.xml
index cbca3e2d61c..7775f98ad3e 100644
--- a/owncloudApp/src/main/res/values-id/strings.xml
+++ b/owncloudApp/src/main/res/values-id/strings.xml
@@ -424,8 +424,7 @@
%1$d berkas, %2$d folder
Folder kamera (%1$s)
Diperlukan
- Pilihan
- File asli akan di
+ File asli akan di
File asli akan di
Salin file
Pindahkan file
diff --git a/owncloudApp/src/main/res/values-is/strings.xml b/owncloudApp/src/main/res/values-is/strings.xml
index e95f01f2c22..18dff207ab6 100644
--- a/owncloudApp/src/main/res/values-is/strings.xml
+++ b/owncloudApp/src/main/res/values-is/strings.xml
@@ -395,8 +395,7 @@
%1$d skrár, %2$d möppur
Myndavélarmappa (%1$s)
krafist
- valkvætt
- Upprunaleg skrá verður
+ Upprunaleg skrá verður
Upprunaleg skrá verður
Afrita skrá
Færa skrá
diff --git a/owncloudApp/src/main/res/values-it/strings.xml b/owncloudApp/src/main/res/values-it/strings.xml
index 5403de3a131..2b3cd27f83e 100644
--- a/owncloudApp/src/main/res/values-it/strings.xml
+++ b/owncloudApp/src/main/res/values-it/strings.xml
@@ -434,8 +434,7 @@
%1$d file, %2$d cartelle
Cartella della fotocamera (%1$s)
richiesto
- opzionale
- Il file originale verrà
+ Il file originale verrà
Il file originale verrà
Copia file
Sposta file
diff --git a/owncloudApp/src/main/res/values-ja-rJP/strings.xml b/owncloudApp/src/main/res/values-ja-rJP/strings.xml
index e861d0bc895..6d71ae24041 100644
--- a/owncloudApp/src/main/res/values-ja-rJP/strings.xml
+++ b/owncloudApp/src/main/res/values-ja-rJP/strings.xml
@@ -389,8 +389,7 @@
%1$d ファイル、%2$d フォルダー
カメラフォルダー (%1$s)
必須
- 任意
- ファイルをコピー
+ ファイルをコピー
ファイルを移動
元のフォルダーに保持
アプリフォルダーに移動
diff --git a/owncloudApp/src/main/res/values-ko/strings.xml b/owncloudApp/src/main/res/values-ko/strings.xml
index 12d03092912..e14f3f709ce 100644
--- a/owncloudApp/src/main/res/values-ko/strings.xml
+++ b/owncloudApp/src/main/res/values-ko/strings.xml
@@ -382,8 +382,7 @@
파일 %1$d개, 폴더 %2$d개
카메라 폴더(%1$s)
필수
- 선택
- 파일 복사
+ 파일 복사
파일 이동
원래 폴더에 유지됨
앱 폴더로 이동함
diff --git a/owncloudApp/src/main/res/values-lt-rLT/strings.xml b/owncloudApp/src/main/res/values-lt-rLT/strings.xml
index 5c3f6faf59b..cf24ff41705 100644
--- a/owncloudApp/src/main/res/values-lt-rLT/strings.xml
+++ b/owncloudApp/src/main/res/values-lt-rLT/strings.xml
@@ -391,8 +391,7 @@
%1$d failai, %2$d aplankai
Kameros katalogas (%1$s)
privalomas
- neprivalomas
- Kopijuoti failą
+ Kopijuoti failą
Perkelti failą
paliktas pradiniame aplanke
perkelti į app aplanką
diff --git a/owncloudApp/src/main/res/values-nb-rNO/strings.xml b/owncloudApp/src/main/res/values-nb-rNO/strings.xml
index d032a02e6c9..1739e4f87e6 100644
--- a/owncloudApp/src/main/res/values-nb-rNO/strings.xml
+++ b/owncloudApp/src/main/res/values-nb-rNO/strings.xml
@@ -423,8 +423,7 @@
%1$d filer, %2$d mapper
Kamera-mappe (%1$s)
obligatorisk
- valgfri
- Den originale filen vil bli
+ Den originale filen vil bli
Den originale filen vil bli
Kopier fil
Flytt fil
diff --git a/owncloudApp/src/main/res/values-nl/strings.xml b/owncloudApp/src/main/res/values-nl/strings.xml
index 857bc866137..76649b99837 100644
--- a/owncloudApp/src/main/res/values-nl/strings.xml
+++ b/owncloudApp/src/main/res/values-nl/strings.xml
@@ -438,8 +438,7 @@ Hieronder staan de lokale bestanden en de externe bestanden in %5$s waar ze naar
%1$d bestanden, %2$d mappen
Cameramap (%1$s)
vereist
- optioneel
- Origineel bestand zal zijn
+ Origineel bestand zal zijn
Origineel bestand zal zijn
Kopiëren bestand
Verplaatsen bestand
diff --git a/owncloudApp/src/main/res/values-oc/strings.xml b/owncloudApp/src/main/res/values-oc/strings.xml
index 25438bd6ce5..0f518ab29a2 100644
--- a/owncloudApp/src/main/res/values-oc/strings.xml
+++ b/owncloudApp/src/main/res/values-oc/strings.xml
@@ -320,8 +320,7 @@ En rason d\'aquesta modificacion, totes los fichièrs mandats amb de versions an
%1$d fichièrs, %2$d dorsièrs
Dorsièr de l\'aparelh fòto (%1$s)
Requesit
- Opcional
- Lo fichièr original serà…
+ Lo fichièr original serà…
Lo fichièr original serà…
Copiar lo fichièr
Desplaçar lo fichièr
diff --git a/owncloudApp/src/main/res/values-pl/strings.xml b/owncloudApp/src/main/res/values-pl/strings.xml
index 57dee2a8ca4..b5d9bd1a135 100644
--- a/owncloudApp/src/main/res/values-pl/strings.xml
+++ b/owncloudApp/src/main/res/values-pl/strings.xml
@@ -427,8 +427,7 @@
%1$d plików, %2$d folderów
Folder aparatu (%1$s)
wymagane
- opcjonalne
- Oryginalny plik zostanie
+ Oryginalny plik zostanie
Oryginalny plik zostanie
Kopiuj plik
Przenieś plik
diff --git a/owncloudApp/src/main/res/values-pt-rBR/strings.xml b/owncloudApp/src/main/res/values-pt-rBR/strings.xml
index 2a5f41c690d..87493537dde 100644
--- a/owncloudApp/src/main/res/values-pt-rBR/strings.xml
+++ b/owncloudApp/src/main/res/values-pt-rBR/strings.xml
@@ -453,8 +453,7 @@
Conta para fazer envio de vídeos
Pasta da câmera (%1$s)
exigido
- opcional
- Arquivo original será
+ Arquivo original será
Arquivo original será
Copiar o arquivo
Mover o arquivo
diff --git a/owncloudApp/src/main/res/values-pt-rPT/strings.xml b/owncloudApp/src/main/res/values-pt-rPT/strings.xml
index 4bdcb694592..1fbfb6d6fb2 100644
--- a/owncloudApp/src/main/res/values-pt-rPT/strings.xml
+++ b/owncloudApp/src/main/res/values-pt-rPT/strings.xml
@@ -368,8 +368,7 @@
%1$d ficheiros, %2$d pastas
Pasta da Camara (%1$s)
obrigatório
- opcional
- Copiar ficheiro
+ Copiar ficheiro
Mover ficheiro
mantido na pasta original
movido para pasta da aplicação
diff --git a/owncloudApp/src/main/res/values-ro/strings.xml b/owncloudApp/src/main/res/values-ro/strings.xml
index b81c2eb3c5a..9b53fbb7b48 100644
--- a/owncloudApp/src/main/res/values-ro/strings.xml
+++ b/owncloudApp/src/main/res/values-ro/strings.xml
@@ -399,8 +399,7 @@
%1$d fișiere, %2$d foldere
Dosarul camerei (%1$s)
necesar
- opțional
- Fișierul original va fi
+ Fișierul original va fi
Fișierul original va fi
Copiază fișier
Mută fișier
diff --git a/owncloudApp/src/main/res/values-ru/strings.xml b/owncloudApp/src/main/res/values-ru/strings.xml
index 5fd7b3e8823..9a12af0498c 100644
--- a/owncloudApp/src/main/res/values-ru/strings.xml
+++ b/owncloudApp/src/main/res/values-ru/strings.xml
@@ -454,8 +454,7 @@
Учётная запись для закачки видео
Каталог камеры (%1$s)
обязательно
- не обязательно
- Исходный файл будет
+ Исходный файл будет
Исходный файл будет
Копировать файл
Переместить файл
diff --git a/owncloudApp/src/main/res/values-sk-rSK/strings.xml b/owncloudApp/src/main/res/values-sk-rSK/strings.xml
index 0126f9d9b3e..3ed1c3bdddb 100644
--- a/owncloudApp/src/main/res/values-sk-rSK/strings.xml
+++ b/owncloudApp/src/main/res/values-sk-rSK/strings.xml
@@ -366,8 +366,7 @@
%1$d súb., %2$d prieč.
Adresár fotoaparátu (%1$s)
Požaduje sa
- Volitelné
- Originálny súbor bude
+ Originálny súbor bude
Originálny súbor bude
Kopírovanie súboru
Presunutie súboru
diff --git a/owncloudApp/src/main/res/values-sl/strings.xml b/owncloudApp/src/main/res/values-sl/strings.xml
index b3c57a10770..99b46df8b78 100644
--- a/owncloudApp/src/main/res/values-sl/strings.xml
+++ b/owncloudApp/src/main/res/values-sl/strings.xml
@@ -404,8 +404,7 @@
%1$d datotek, %2$d map
Mapa kamere (%1$s)
zahtevano
- izbirno
- Kopiraj datoteko
+ Kopiraj datoteko
Premakni datoteko
Ohrani v izvorni mapi
Premakni v mapo programa
diff --git a/owncloudApp/src/main/res/values-sq/strings.xml b/owncloudApp/src/main/res/values-sq/strings.xml
index c7ced70c7ec..58d4cc9a30c 100644
--- a/owncloudApp/src/main/res/values-sq/strings.xml
+++ b/owncloudApp/src/main/res/values-sq/strings.xml
@@ -451,8 +451,7 @@
Llogari ku të ngarkohen video
Dosje kamere (%1$s)
e domosdoshme
- opsionale
- Kartela origjinale do të jetë
+ Kartela origjinale do të jetë
Kartela origjinale do të jetë
Kopjoje kartelën
Lëvize kartelën
diff --git a/owncloudApp/src/main/res/values-sr/strings.xml b/owncloudApp/src/main/res/values-sr/strings.xml
index 2a9fe10293e..08178e127d7 100644
--- a/owncloudApp/src/main/res/values-sr/strings.xml
+++ b/owncloudApp/src/main/res/values-sr/strings.xml
@@ -314,8 +314,7 @@
%1$d фајлова, 1 фасцикла
%1$d фајлова, %2$d фасцикли
неопходно
- опционо
- Оригинални фајл ће бити…
+ Оригинални фајл ће бити…
Оригинални фајл ће бити…
Копирај фајл
Премести фајл
diff --git a/owncloudApp/src/main/res/values-sv/strings.xml b/owncloudApp/src/main/res/values-sv/strings.xml
index d6f51a023e0..3cc0d993c4a 100644
--- a/owncloudApp/src/main/res/values-sv/strings.xml
+++ b/owncloudApp/src/main/res/values-sv/strings.xml
@@ -365,8 +365,7 @@
%1$d filer, %2$d mappar
Kameramapp (%1$s)
krävs
- valfritt
- Kopiera fil
+ Kopiera fil
Flytta fil
behållas i orginalmapp
flyttas till ownCloudmapp
diff --git a/owncloudApp/src/main/res/values-th-rTH/strings.xml b/owncloudApp/src/main/res/values-th-rTH/strings.xml
index 8833a165b05..9c5ae7bf272 100644
--- a/owncloudApp/src/main/res/values-th-rTH/strings.xml
+++ b/owncloudApp/src/main/res/values-th-rTH/strings.xml
@@ -451,8 +451,7 @@
บัญชีสำหรับอัปโหลดวิดีโอ
โฟลเดอร์ของกล้อง (%1$s)
สิ่งจำเป็นต้องใช้
- ตัวเลือก
- ไฟล์ต้นฉบับจะเป็น
+ ไฟล์ต้นฉบับจะเป็น
ไฟล์ต้นฉบับจะเป็น
คัดลอกไฟล์
ย้ายไฟล์
diff --git a/owncloudApp/src/main/res/values-tr/strings.xml b/owncloudApp/src/main/res/values-tr/strings.xml
index 007fbad0dbc..aca41f083fb 100644
--- a/owncloudApp/src/main/res/values-tr/strings.xml
+++ b/owncloudApp/src/main/res/values-tr/strings.xml
@@ -435,8 +435,7 @@
%1$d dosya, %2$d klasör
Kamera klasörü (%1$s)
gerekli
- isteğe bağlı
- şu olacak
+ şu olacak
şu olacak
Dosyayı kopyala
Dosyayı taşı
diff --git a/owncloudApp/src/main/res/values-uk/strings.xml b/owncloudApp/src/main/res/values-uk/strings.xml
index b3e21976627..d563bfc0b33 100644
--- a/owncloudApp/src/main/res/values-uk/strings.xml
+++ b/owncloudApp/src/main/res/values-uk/strings.xml
@@ -397,8 +397,7 @@
%1$d файлів, %2$d тек
Тека камери (%1$s)
вимагається
- необов\'язково
- Копіювати файл
+ Копіювати файл
Перемістити файл
залишено в оригінальній теці
перенесено до теки програми
diff --git a/owncloudApp/src/main/res/values-zh-rCN/strings.xml b/owncloudApp/src/main/res/values-zh-rCN/strings.xml
index 23e88221fed..0c922107329 100644
--- a/owncloudApp/src/main/res/values-zh-rCN/strings.xml
+++ b/owncloudApp/src/main/res/values-zh-rCN/strings.xml
@@ -415,8 +415,7 @@
%1$d 个文件,%2$d 个文件夹
相机文件夹 (%1$s)
必需
- 可选
- 原始文件将会
+ 原始文件将会
原始文件将会
复制文件
移动文件
diff --git a/owncloudApp/src/main/res/values-zh-rTW/strings.xml b/owncloudApp/src/main/res/values-zh-rTW/strings.xml
index 38220669d01..ab37d3331f3 100644
--- a/owncloudApp/src/main/res/values-zh-rTW/strings.xml
+++ b/owncloudApp/src/main/res/values-zh-rTW/strings.xml
@@ -436,8 +436,7 @@
%1$d 個檔案, %2$d 個資料夾
相機資料夾 (%1$s)
必填
- 選填
- 原始檔案將
+ 原始檔案將
原始檔案將
複製檔案
移動檔案
diff --git a/owncloudApp/src/main/res/values/strings.xml b/owncloudApp/src/main/res/values/strings.xml
index 06a5a0ae938..40a232c11f7 100644
--- a/owncloudApp/src/main/res/values/strings.xml
+++ b/owncloudApp/src/main/res/values/strings.xml
@@ -187,6 +187,11 @@
Filename must not be more than %d characters
Filename
Uploading camera upload files
+ %d new pictures will be uploaded
+ %d new videos will be uploaded
+ Camera Uploads failed
+ Picture uploads source path is not valid anymore
+ Video uploads source path is not valid anymore
Uploading available offline files
Uploading requested from wifi files
Server certificate is not trusted
@@ -488,9 +493,9 @@
Account to upload videos
Camera folder (%1$s)
required
- optional
Original file will be
Original file will be
+ Last synchronization
Copy file
Move file
diff --git a/owncloudApp/src/main/res/values/strings__not_to_translate.xml b/owncloudApp/src/main/res/values/strings__not_to_translate.xml
index 15c00cefe10..228f0168aff 100644
--- a/owncloudApp/src/main/res/values/strings__not_to_translate.xml
+++ b/owncloudApp/src/main/res/values/strings__not_to_translate.xml
@@ -18,15 +18,4 @@
-->
-
-
- - @string/pref_behaviour_entries_keep_file
- - @string/pref_behaviour_entries_move
-
-
-
- - NOTHING
- - MOVE
-
-
diff --git a/owncloudApp/src/main/res/xml/settings_picture_uploads.xml b/owncloudApp/src/main/res/xml/settings_picture_uploads.xml
index 7a18d195382..c91198b86dc 100644
--- a/owncloudApp/src/main/res/xml/settings_picture_uploads.xml
+++ b/owncloudApp/src/main/res/xml/settings_picture_uploads.xml
@@ -44,12 +44,14 @@
-
+
diff --git a/owncloudApp/src/main/res/xml/settings_video_uploads.xml b/owncloudApp/src/main/res/xml/settings_video_uploads.xml
index b8b2b0c38ef..eb51e995a17 100644
--- a/owncloudApp/src/main/res/xml/settings_video_uploads.xml
+++ b/owncloudApp/src/main/res/xml/settings_video_uploads.xml
@@ -44,12 +44,14 @@
-
+
\ No newline at end of file
diff --git a/owncloudApp/src/test/java/com/owncloud/android/presentation/viewmodels/settings/SettingsPictureUploadsViewModelTest.kt b/owncloudApp/src/test/java/com/owncloud/android/presentation/viewmodels/settings/SettingsPictureUploadsViewModelTest.kt
deleted file mode 100644
index 95cd0c67712..00000000000
--- a/owncloudApp/src/test/java/com/owncloud/android/presentation/viewmodels/settings/SettingsPictureUploadsViewModelTest.kt
+++ /dev/null
@@ -1,257 +0,0 @@
-/**
- * ownCloud Android client application
- *
- * @author Juan Carlos Garrote Gascón
- *
- * Copyright (C) 2021 ownCloud GmbH.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.owncloud.android.presentation.viewmodels.settings
-
-import android.content.Intent
-import com.owncloud.android.data.preferences.datasources.SharedPreferencesProvider
-import com.owncloud.android.datamodel.OCFile
-import com.owncloud.android.db.PreferenceManager
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_ENABLED
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_PATH
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_SOURCE
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_UPLOADS_DEFAULT_PATH
-import com.owncloud.android.presentation.viewmodels.ViewModelTest
-import com.owncloud.android.providers.AccountProvider
-import com.owncloud.android.providers.CameraUploadsHandlerProvider
-import com.owncloud.android.testutil.OC_ACCOUNT
-import com.owncloud.android.ui.activity.LocalFolderPickerActivity
-import com.owncloud.android.ui.activity.UploadPathActivity
-import io.mockk.every
-import io.mockk.mockk
-import io.mockk.mockkStatic
-import io.mockk.verify
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import org.junit.After
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-
-@ExperimentalCoroutinesApi
-class SettingsPictureUploadsViewModelTest : ViewModelTest() {
- private lateinit var picturesViewModel: SettingsPictureUploadsViewModel
- private lateinit var preferencesProvider: SharedPreferencesProvider
- private lateinit var cameraUploadsHandlerProvider: CameraUploadsHandlerProvider
- private lateinit var accountProvider: AccountProvider
-
- private val examplePath = "/Example/Path"
- private val exampleSourcePath = "/Example/Source/Path"
- private val exampleRemotePath = "/Example/Remote/Path"
-
- @Before
- fun setUp() {
- preferencesProvider = mockk(relaxUnitFun = true)
- cameraUploadsHandlerProvider = mockk(relaxUnitFun = true)
- accountProvider = mockk()
-
- picturesViewModel = SettingsPictureUploadsViewModel(
- preferencesProvider,
- cameraUploadsHandlerProvider,
- accountProvider
- )
- }
-
- @After
- override fun tearDown() {
- super.tearDown()
- }
-
- @Test
- fun `is picture upload enabled - ok - true`() {
- every { preferencesProvider.getBoolean(any(), any()) } returns true
-
- val pictureUploadEnabled = picturesViewModel.isPictureUploadEnabled()
-
- assertTrue(pictureUploadEnabled)
-
- verify(exactly = 1) {
- preferencesProvider.getBoolean(PREF__CAMERA_PICTURE_UPLOADS_ENABLED, false)
- }
- }
-
- @Test
- fun `is picture upload enabled - ok - false`() {
- every { preferencesProvider.getBoolean(any(), any()) } returns false
-
- val pictureUploadEnabled = picturesViewModel.isPictureUploadEnabled()
-
- assertFalse(pictureUploadEnabled)
-
- verify(exactly = 1) {
- preferencesProvider.getBoolean(PREF__CAMERA_PICTURE_UPLOADS_ENABLED, false)
- }
- }
-
- @Test
- fun `set enable picture upload - ok - true`() {
- every { accountProvider.getCurrentOwnCloudAccount() } returns OC_ACCOUNT
-
- picturesViewModel.setEnablePictureUpload(true)
-
- verify(exactly = 1) {
- preferencesProvider.putString(PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME, OC_ACCOUNT.name)
- preferencesProvider.putBoolean(PREF__CAMERA_PICTURE_UPLOADS_ENABLED, true)
- }
- }
-
- @Test
- fun `set enable picture upload - ok - false`() {
- picturesViewModel.setEnablePictureUpload(false)
-
- verify(exactly = 1) {
- preferencesProvider.putBoolean(PREF__CAMERA_PICTURE_UPLOADS_ENABLED, false)
- preferencesProvider.removePreference(key = PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME)
- preferencesProvider.removePreference(key = PREF__CAMERA_PICTURE_UPLOADS_PATH)
- }
- }
-
- @Test
- fun `update pictures last sync - ok`() {
- picturesViewModel.updatePicturesLastSync()
-
- verify(exactly = 1) {
- cameraUploadsHandlerProvider.updatePicturesLastSync(0)
- }
- }
-
- @Test
- fun `get picture uploads path - ok`() {
- every { preferencesProvider.getString(any(), any()) } returns examplePath
-
- val uploadPath = picturesViewModel.getPictureUploadsPath()
-
- assertEquals(examplePath, uploadPath)
-
- verify(exactly = 1) {
- preferencesProvider.getString(
- PREF__CAMERA_PICTURE_UPLOADS_PATH,
- PREF__CAMERA_UPLOADS_DEFAULT_PATH
- )
- }
- }
-
- @Test
- fun `get picture uploads source path - ok`() {
- mockkStatic(PreferenceManager.CameraUploadsConfiguration::class)
-
- every { preferencesProvider.getString(any(), any()) } returns exampleSourcePath
- every { PreferenceManager.CameraUploadsConfiguration.getDefaultSourcePath() } returns ""
-
- val uploadSourcePath = picturesViewModel.getPictureUploadsSourcePath()
-
- assertEquals(exampleSourcePath, uploadSourcePath)
-
- verify(exactly = 1) {
- preferencesProvider.getString(
- PREF__CAMERA_PICTURE_UPLOADS_SOURCE,
- PreferenceManager.CameraUploadsConfiguration.getDefaultSourcePath()
- )
- }
- }
-
- @Test
- fun `handle select picture uploads path - ok`() {
- val data: Intent = mockk()
- val ocFile: OCFile = mockk()
-
- every { ocFile.remotePath } returns exampleRemotePath
- every { data.getParcelableExtra(any()) } returns ocFile
-
- picturesViewModel.handleSelectPictureUploadsPath(data)
-
- verify(exactly = 1) {
- data.getParcelableExtra(UploadPathActivity.EXTRA_FOLDER)
- ocFile.remotePath
- preferencesProvider.putString(PREF__CAMERA_PICTURE_UPLOADS_PATH, exampleRemotePath)
- }
- }
-
- @Test
- fun `handle select picture uploads path - ko - folder to upload is null`() {
- val data: Intent = mockk()
-
- every { data.getParcelableExtra(any()) } returns null
-
- picturesViewModel.handleSelectPictureUploadsPath(data)
-
- verify(exactly = 1) {
- data.getParcelableExtra(UploadPathActivity.EXTRA_FOLDER)
- }
- }
-
- @Test
- fun `handle select picture uploads source path - ok - source path hasn't changed`() {
- val data: Intent = mockk()
- mockkStatic(PreferenceManager.CameraUploadsConfiguration::class)
-
- every { preferencesProvider.getString(any(), any()) } returns exampleSourcePath
- // It has to be "" for the test to pass
- every { PreferenceManager.CameraUploadsConfiguration.getDefaultSourcePath() } returns ""
- every { data.getStringExtra(any()) } returns exampleSourcePath
-
- picturesViewModel.handleSelectPictureUploadsSourcePath(data)
-
- verify(exactly = 2) {
- data.getStringExtra(LocalFolderPickerActivity.EXTRA_PATH)
- }
- verify(exactly = 1) {
- preferencesProvider.putString(PREF__CAMERA_PICTURE_UPLOADS_SOURCE, exampleSourcePath)
- }
- }
-
- @Test
- fun `handle select picture uploads source path - ok - source path has changed`() {
- val data: Intent = mockk()
- val sourcePath = "/New/Source/Path"
- mockkStatic(PreferenceManager.CameraUploadsConfiguration::class)
-
- every { preferencesProvider.getString(any(), any()) } returns exampleSourcePath
- // It has to be "" for the test to pass
- every { PreferenceManager.CameraUploadsConfiguration.getDefaultSourcePath() } returns ""
- every { data.getStringExtra(any()) } returns sourcePath
-
- picturesViewModel.handleSelectPictureUploadsSourcePath(data)
-
- every { preferencesProvider.getString(any(), any()) } returns sourcePath
-
- val newSourcePath = picturesViewModel.getPictureUploadsSourcePath()
- assertEquals(sourcePath, newSourcePath)
-
- verify(exactly = 2) {
- data.getStringExtra(LocalFolderPickerActivity.EXTRA_PATH)
- }
- verify(exactly = 1) {
- cameraUploadsHandlerProvider.updatePicturesLastSync(any())
- preferencesProvider.putString(PREF__CAMERA_PICTURE_UPLOADS_SOURCE, sourcePath)
- }
- }
-
- @Test
- fun `schedule picture uploads sync job - ok`() {
- picturesViewModel.schedulePictureUploadsSyncJob()
-
- verify(exactly = 1) {
- cameraUploadsHandlerProvider.schedulePictureUploadsSyncJob()
- }
- }
-}
diff --git a/owncloudApp/src/test/java/com/owncloud/android/presentation/viewmodels/settings/SettingsVideoUploadsViewModelTest.kt b/owncloudApp/src/test/java/com/owncloud/android/presentation/viewmodels/settings/SettingsVideoUploadsViewModelTest.kt
deleted file mode 100644
index 3ebe85485aa..00000000000
--- a/owncloudApp/src/test/java/com/owncloud/android/presentation/viewmodels/settings/SettingsVideoUploadsViewModelTest.kt
+++ /dev/null
@@ -1,256 +0,0 @@
-/**
- * ownCloud Android client application
- *
- * @author Juan Carlos Garrote Gascón
- *
- * Copyright (C) 2021 ownCloud GmbH.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.owncloud.android.presentation.viewmodels.settings
-
-import android.content.Intent
-import com.owncloud.android.data.preferences.datasources.SharedPreferencesProvider
-import com.owncloud.android.datamodel.OCFile
-import com.owncloud.android.db.PreferenceManager
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_UPLOADS_DEFAULT_PATH
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_ENABLED
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_PATH
-import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_SOURCE
-import com.owncloud.android.presentation.viewmodels.ViewModelTest
-import com.owncloud.android.providers.AccountProvider
-import com.owncloud.android.providers.CameraUploadsHandlerProvider
-import com.owncloud.android.testutil.OC_ACCOUNT
-import com.owncloud.android.ui.activity.LocalFolderPickerActivity
-import com.owncloud.android.ui.activity.UploadPathActivity
-import io.mockk.every
-import io.mockk.mockk
-import io.mockk.mockkStatic
-import io.mockk.verify
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import org.junit.After
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-
-@ExperimentalCoroutinesApi
-class SettingsVideoUploadsViewModelTest : ViewModelTest() {
- private lateinit var videosViewModel: SettingsVideoUploadsViewModel
- private lateinit var preferencesProvider: SharedPreferencesProvider
- private lateinit var cameraUploadsHandlerProvider: CameraUploadsHandlerProvider
- private lateinit var accountProvider: AccountProvider
-
- private val examplePath = "/Example/Path"
- private val exampleSourcePath = "/Example/Source/Path"
- private val exampleRemotePath = "/Example/Remote/Path"
-
- @Before
- fun setUp() {
- preferencesProvider = mockk(relaxUnitFun = true)
- cameraUploadsHandlerProvider = mockk(relaxUnitFun = true)
- accountProvider = mockk()
-
- videosViewModel = SettingsVideoUploadsViewModel(
- preferencesProvider,
- cameraUploadsHandlerProvider,
- accountProvider
- )
- }
-
- @After
- override fun tearDown() {
- super.tearDown()
- }
-
- @Test
- fun `is video upload enabled - ok - true`() {
- every { preferencesProvider.getBoolean(any(), any()) } returns true
-
- val videoUploadEnabled = videosViewModel.isVideoUploadEnabled()
-
- assertTrue(videoUploadEnabled)
-
- verify(exactly = 1) {
- preferencesProvider.getBoolean(PREF__CAMERA_VIDEO_UPLOADS_ENABLED, false)
- }
- }
-
- @Test
- fun `is video upload enabled - ok - false`() {
- every { preferencesProvider.getBoolean(any(), any()) } returns false
-
- val videoUploadEnabled = videosViewModel.isVideoUploadEnabled()
-
- assertFalse(videoUploadEnabled)
-
- verify(exactly = 1) {
- preferencesProvider.getBoolean(PREF__CAMERA_VIDEO_UPLOADS_ENABLED, false)
- }
- }
-
- @Test
- fun `set enable video upload - ok - true`() {
- every { accountProvider.getCurrentOwnCloudAccount() } returns OC_ACCOUNT
-
- videosViewModel.setEnableVideoUpload(true)
-
- verify(exactly = 1) {
- preferencesProvider.putString(PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME, OC_ACCOUNT.name)
- preferencesProvider.putBoolean(PREF__CAMERA_VIDEO_UPLOADS_ENABLED, true)
- }
- }
-
- @Test
- fun `set enable video upload - ok - false`() {
- videosViewModel.setEnableVideoUpload(false)
-
- verify(exactly = 1) {
- preferencesProvider.putBoolean(PREF__CAMERA_VIDEO_UPLOADS_ENABLED, false)
- preferencesProvider.removePreference(key = PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME)
- preferencesProvider.removePreference(key = PREF__CAMERA_VIDEO_UPLOADS_PATH)
- }
- }
-
- @Test
- fun `update videos last sync - ok`() {
- videosViewModel.updateVideosLastSync()
-
- verify(exactly = 1) {
- cameraUploadsHandlerProvider.updateVideosLastSync(0)
- }
- }
-
- @Test
- fun `get video uploads path - ok`() {
- every { preferencesProvider.getString(any(), any()) } returns examplePath
- val uploadPath = videosViewModel.getVideoUploadsPath()
-
- assertEquals(examplePath, uploadPath)
-
- verify(exactly = 1) {
- preferencesProvider.getString(
- PREF__CAMERA_VIDEO_UPLOADS_PATH,
- PREF__CAMERA_UPLOADS_DEFAULT_PATH
- )
- }
- }
-
- @Test
- fun `get video uploads source path - ok`() {
- mockkStatic(PreferenceManager.CameraUploadsConfiguration::class)
-
- every { preferencesProvider.getString(any(), any()) } returns exampleSourcePath
- every { PreferenceManager.CameraUploadsConfiguration.getDefaultSourcePath() } returns ""
-
- val uploadSourcePath = videosViewModel.getVideoUploadsSourcePath()
-
- assertEquals(exampleSourcePath, uploadSourcePath)
-
- verify(exactly = 1) {
- preferencesProvider.getString(
- PREF__CAMERA_VIDEO_UPLOADS_SOURCE,
- PreferenceManager.CameraUploadsConfiguration.getDefaultSourcePath()
- )
- }
- }
-
- @Test
- fun `handle select video uploads path - ok`() {
- val data: Intent = mockk()
- val ocFile: OCFile = mockk()
-
- every { ocFile.remotePath } returns exampleRemotePath
- every { data.getParcelableExtra(any()) } returns ocFile
-
- videosViewModel.handleSelectVideoUploadsPath(data)
-
- verify(exactly = 1) {
- data.getParcelableExtra(UploadPathActivity.EXTRA_FOLDER)
- ocFile.remotePath
- preferencesProvider.putString(PREF__CAMERA_VIDEO_UPLOADS_PATH, exampleRemotePath)
- }
- }
-
- @Test
- fun `handle select video uploads path - ko - folder to upload is null`() {
- val data: Intent = mockk()
-
- every { data.getParcelableExtra(any()) } returns null
-
- videosViewModel.handleSelectVideoUploadsPath(data)
-
- verify(exactly = 1) {
- data.getParcelableExtra(UploadPathActivity.EXTRA_FOLDER)
- }
- }
-
- @Test
- fun `handle select video uploads source path - ok - source path hasn't changed`() {
- val data: Intent = mockk()
- mockkStatic(PreferenceManager.CameraUploadsConfiguration::class)
-
- every { preferencesProvider.getString(any(), any()) } returns exampleSourcePath
- // It has to be "" for the test to pass
- every { PreferenceManager.CameraUploadsConfiguration.getDefaultSourcePath() } returns ""
- every { data.getStringExtra(any()) } returns exampleSourcePath
-
- videosViewModel.handleSelectVideoUploadsSourcePath(data)
-
- verify(exactly = 2) {
- data.getStringExtra(LocalFolderPickerActivity.EXTRA_PATH)
- }
- verify(exactly = 1) {
- preferencesProvider.putString(PREF__CAMERA_VIDEO_UPLOADS_SOURCE, exampleSourcePath)
- }
- }
-
- @Test
- fun `handle select video uploads source path - ok - source path has changed`() {
- val data: Intent = mockk()
- val sourcePath = "/New/Source/Path"
- mockkStatic(PreferenceManager.CameraUploadsConfiguration::class)
-
- every { preferencesProvider.getString(any(), any()) } returns exampleSourcePath
- // It has to be "" for the test to pass
- every { PreferenceManager.CameraUploadsConfiguration.getDefaultSourcePath() } returns ""
- every { data.getStringExtra(any()) } returns sourcePath
-
- videosViewModel.handleSelectVideoUploadsSourcePath(data)
-
- every { preferencesProvider.getString(any(), any()) } returns sourcePath
-
- val newSourcePath = videosViewModel.getVideoUploadsSourcePath()
- assertEquals(sourcePath, newSourcePath)
-
- verify(exactly = 2) {
- data.getStringExtra(LocalFolderPickerActivity.EXTRA_PATH)
- }
- verify(exactly = 1) {
- cameraUploadsHandlerProvider.updateVideosLastSync(any())
- preferencesProvider.putString(PREF__CAMERA_VIDEO_UPLOADS_SOURCE, sourcePath)
- }
- }
-
- @Test
- fun `schedule video uploads sync job - ok`() {
- videosViewModel.scheduleVideoUploadsSyncJob()
-
- verify(exactly = 1) {
- cameraUploadsHandlerProvider.scheduleVideoUploadsSyncJob()
- }
- }
-}
diff --git a/owncloudData/build.gradle b/owncloudData/build.gradle
index d799f17d68a..999932632b1 100644
--- a/owncloudData/build.gradle
+++ b/owncloudData/build.gradle
@@ -52,9 +52,12 @@ dependencies {
// Kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"
+
+ implementation "androidx.legacy:legacy-support-v4:$androidX"
// Room
- implementation "androidx.room:room-runtime:$roomVersion"
+ implementation "androidx.room:room-ktx:$roomVersion"
kapt "androidx.room:room-compiler:$roomVersion"
// Dependencies for unit tests
diff --git a/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/34.json b/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/34.json
new file mode 100644
index 00000000000..52af4a5bff5
--- /dev/null
+++ b/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/34.json
@@ -0,0 +1,405 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 34,
+ "identityHash": "23b7b67de0e6641230799a550a78bde9",
+ "entities": [
+ {
+ "tableName": "ocshares",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`share_type` INTEGER NOT NULL, `share_with` TEXT, `path` TEXT NOT NULL, `permissions` INTEGER NOT NULL, `shared_date` INTEGER NOT NULL, `expiration_date` INTEGER NOT NULL, `token` TEXT, `shared_with_display_name` TEXT, `share_with_additional_info` TEXT, `is_directory` INTEGER NOT NULL, `id_remote_shared` TEXT NOT NULL, `owner_share` TEXT NOT NULL, `name` TEXT, `url` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "shareType",
+ "columnName": "share_type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "shareWith",
+ "columnName": "share_with",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "permissions",
+ "columnName": "permissions",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sharedDate",
+ "columnName": "shared_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "expirationDate",
+ "columnName": "expiration_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "token",
+ "columnName": "token",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "sharedWithDisplayName",
+ "columnName": "shared_with_display_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "sharedWithAdditionalInfo",
+ "columnName": "share_with_additional_info",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isFolder",
+ "columnName": "is_directory",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "remoteId",
+ "columnName": "id_remote_shared",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "accountOwner",
+ "columnName": "owner_share",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "shareLink",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "capabilities",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`account` TEXT, `version_mayor` INTEGER NOT NULL, `version_minor` INTEGER NOT NULL, `version_micro` INTEGER NOT NULL, `version_string` TEXT, `version_edition` TEXT, `core_pollinterval` INTEGER NOT NULL, `dav_chunking_version` TEXT NOT NULL, `sharing_api_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_write` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_public_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_days` INTEGER NOT NULL, `sharing_public_expire_date_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_upload` INTEGER NOT NULL DEFAULT -1, `sharing_public_multiple` INTEGER NOT NULL DEFAULT -1, `supports_upload_only` INTEGER NOT NULL DEFAULT -1, `sharing_resharing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_outgoing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_incoming` INTEGER NOT NULL DEFAULT -1, `files_bigfilechunking` INTEGER NOT NULL DEFAULT -1, `files_undelete` INTEGER NOT NULL DEFAULT -1, `files_versioning` INTEGER NOT NULL DEFAULT -1, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "accountName",
+ "columnName": "account",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "versionMayor",
+ "columnName": "version_mayor",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "versionMinor",
+ "columnName": "version_minor",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "versionMicro",
+ "columnName": "version_micro",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "versionString",
+ "columnName": "version_string",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "versionEdition",
+ "columnName": "version_edition",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "corePollInterval",
+ "columnName": "core_pollinterval",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "davChunkingVersion",
+ "columnName": "dav_chunking_version",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "filesSharingApiEnabled",
+ "columnName": "sharing_api_enabled",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "-1"
+ },
+ {
+ "fieldPath": "filesSharingPublicEnabled",
+ "columnName": "sharing_public_enabled",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "-1"
+ },
+ {
+ "fieldPath": "filesSharingPublicPasswordEnforced",
+ "columnName": "sharing_public_password_enforced",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "-1"
+ },
+ {
+ "fieldPath": "filesSharingPublicPasswordEnforcedReadOnly",
+ "columnName": "sharing_public_password_enforced_read_only",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "-1"
+ },
+ {
+ "fieldPath": "filesSharingPublicPasswordEnforcedReadWrite",
+ "columnName": "sharing_public_password_enforced_read_write",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "-1"
+ },
+ {
+ "fieldPath": "filesSharingPublicPasswordEnforcedUploadOnly",
+ "columnName": "sharing_public_password_enforced_public_only",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "-1"
+ },
+ {
+ "fieldPath": "filesSharingPublicExpireDateEnabled",
+ "columnName": "sharing_public_expire_date_enabled",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "-1"
+ },
+ {
+ "fieldPath": "filesSharingPublicExpireDateDays",
+ "columnName": "sharing_public_expire_date_days",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "filesSharingPublicExpireDateEnforced",
+ "columnName": "sharing_public_expire_date_enforced",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "-1"
+ },
+ {
+ "fieldPath": "filesSharingPublicUpload",
+ "columnName": "sharing_public_upload",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "-1"
+ },
+ {
+ "fieldPath": "filesSharingPublicMultiple",
+ "columnName": "sharing_public_multiple",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "-1"
+ },
+ {
+ "fieldPath": "filesSharingPublicSupportsUploadOnly",
+ "columnName": "supports_upload_only",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "-1"
+ },
+ {
+ "fieldPath": "filesSharingResharing",
+ "columnName": "sharing_resharing",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "-1"
+ },
+ {
+ "fieldPath": "filesSharingFederationOutgoing",
+ "columnName": "sharing_federation_outgoing",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "-1"
+ },
+ {
+ "fieldPath": "filesSharingFederationIncoming",
+ "columnName": "sharing_federation_incoming",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "-1"
+ },
+ {
+ "fieldPath": "filesBigFileChunking",
+ "columnName": "files_bigfilechunking",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "-1"
+ },
+ {
+ "fieldPath": "filesUndelete",
+ "columnName": "files_undelete",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "-1"
+ },
+ {
+ "fieldPath": "filesVersioning",
+ "columnName": "files_versioning",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "-1"
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "user_quotas",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountName` TEXT NOT NULL, `used` INTEGER NOT NULL, `available` INTEGER NOT NULL, PRIMARY KEY(`accountName`))",
+ "fields": [
+ {
+ "fieldPath": "accountName",
+ "columnName": "accountName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "used",
+ "columnName": "used",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "available",
+ "columnName": "available",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "accountName"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "folder_backup",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountName` TEXT NOT NULL, `behavior` TEXT NOT NULL, `sourcePath` TEXT NOT NULL, `uploadPath` TEXT NOT NULL, `wifiOnly` INTEGER NOT NULL, `name` TEXT NOT NULL, `lastSyncTimestamp` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "accountName",
+ "columnName": "accountName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "behavior",
+ "columnName": "behavior",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sourcePath",
+ "columnName": "sourcePath",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "uploadPath",
+ "columnName": "uploadPath",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "wifiOnly",
+ "columnName": "wifiOnly",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastSyncTimestamp",
+ "columnName": "lastSyncTimestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '23b7b67de0e6641230799a550a78bde9')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/owncloudData/src/main/java/com/owncloud/android/data/LocalStorageProvider.kt b/owncloudData/src/main/java/com/owncloud/android/data/LocalStorageProvider.kt
index eaa1915ac71..0c4960fb4b8 100644
--- a/owncloudData/src/main/java/com/owncloud/android/data/LocalStorageProvider.kt
+++ b/owncloudData/src/main/java/com/owncloud/android/data/LocalStorageProvider.kt
@@ -26,6 +26,7 @@ package com.owncloud.android.data
import android.annotation.SuppressLint
import android.net.Uri
import android.os.Environment
+import androidx.documentfile.provider.DocumentFile
import java.io.File
class LocalStorageProvider(
@@ -56,6 +57,12 @@ class LocalStorageProvider(
@SuppressLint("UsableSpace")
fun getUsableSpace(): Long = getPrimaryStorageDirectory().usableSpace
+ fun getDefaultCameraSourcePath(): String {
+ return DocumentFile.fromFile(
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
+ ).createDirectory(CAMERA_FOLDER)?.uri.toString()
+ }
+
/**
* Return the root path of primary shared/external storage directory for this application.
* For example: /storage/emulated/0/owncloud
@@ -73,4 +80,8 @@ class LocalStorageProvider(
* that can be in the accountName since 0.1.190B
*/
private fun getEncodedAccountName(accountName: String?): String = Uri.encode(accountName, "@")
+
+ companion object {
+ private const val CAMERA_FOLDER = "/Camera"
+ }
}
diff --git a/owncloudData/src/main/java/com/owncloud/android/data/OwncloudDatabase.kt b/owncloudData/src/main/java/com/owncloud/android/data/OwncloudDatabase.kt
index ecc7515bed7..e61f7ef74cc 100644
--- a/owncloudData/src/main/java/com/owncloud/android/data/OwncloudDatabase.kt
+++ b/owncloudData/src/main/java/com/owncloud/android/data/OwncloudDatabase.kt
@@ -27,6 +27,8 @@ import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.migration.Migration
+import com.owncloud.android.data.folderbackup.db.FolderBackupDao
+import com.owncloud.android.data.folderbackup.db.FolderBackUpEntity
import com.owncloud.android.data.capabilities.db.OCCapabilityDao
import com.owncloud.android.data.capabilities.db.OCCapabilityEntity
import com.owncloud.android.data.migrations.MIGRATION_27_28
@@ -35,6 +37,7 @@ import com.owncloud.android.data.migrations.MIGRATION_29_30
import com.owncloud.android.data.migrations.MIGRATION_30_31
import com.owncloud.android.data.migrations.MIGRATION_31_32
import com.owncloud.android.data.migrations.MIGRATION_32_33
+import com.owncloud.android.data.migrations.MIGRATION_33_34
import com.owncloud.android.data.sharing.shares.db.OCShareDao
import com.owncloud.android.data.sharing.shares.db.OCShareEntity
import com.owncloud.android.data.user.db.UserDao
@@ -44,7 +47,8 @@ import com.owncloud.android.data.user.db.UserQuotaEntity
entities = [
OCShareEntity::class,
OCCapabilityEntity::class,
- UserQuotaEntity::class
+ UserQuotaEntity::class,
+ FolderBackUpEntity::class,
],
version = ProviderMeta.DB_VERSION,
exportSchema = true
@@ -53,6 +57,7 @@ abstract class OwncloudDatabase : RoomDatabase() {
abstract fun shareDao(): OCShareDao
abstract fun capabilityDao(): OCCapabilityDao
abstract fun userDao(): UserDao
+ abstract fun folderBackUpDao(): FolderBackupDao
companion object {
@Volatile
@@ -64,7 +69,8 @@ abstract class OwncloudDatabase : RoomDatabase() {
MIGRATION_29_30,
MIGRATION_30_31,
MIGRATION_31_32,
- MIGRATION_32_33
+ MIGRATION_32_33,
+ MIGRATION_33_34
)
fun getDatabase(
diff --git a/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java b/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java
index fae486fb25f..93320eca228 100644
--- a/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java
+++ b/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java
@@ -31,7 +31,7 @@ public class ProviderMeta {
public static final String DB_NAME = "filelist";
public static final String NEW_DB_NAME = "owncloud_database";
- public static final int DB_VERSION = 33;
+ public static final int DB_VERSION = 34;
private ProviderMeta() {
}
@@ -40,6 +40,7 @@ static public class ProviderTableMeta implements BaseColumns {
public static final String OCSHARES_TABLE_NAME = "ocshares";
public static final String CAPABILITIES_TABLE_NAME = "capabilities";
public static final String USER_QUOTAS_TABLE_NAME = "user_quotas";
+ public static final String FOLDER_BACKUP_TABLE_NAME = "folder_backup";
// Columns of ocshares table
public static final String OCSHARES_SHARE_TYPE = "share_type";
diff --git a/owncloudData/src/main/java/com/owncloud/android/data/folderbackup/FolderBackupRepositoryImpl.kt b/owncloudData/src/main/java/com/owncloud/android/data/folderbackup/FolderBackupRepositoryImpl.kt
new file mode 100644
index 00000000000..d4c3335959e
--- /dev/null
+++ b/owncloudData/src/main/java/com/owncloud/android/data/folderbackup/FolderBackupRepositoryImpl.kt
@@ -0,0 +1,44 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.data.folderbackup
+
+import com.owncloud.android.data.folderbackup.datasources.FolderBackupLocalDataSource
+import com.owncloud.android.domain.camerauploads.FolderBackupRepository
+import com.owncloud.android.domain.camerauploads.model.CameraUploadsConfiguration
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration
+import kotlinx.coroutines.flow.Flow
+
+class FolderBackupRepositoryImpl(
+ private val folderBackupLocalDataSource: FolderBackupLocalDataSource
+) : FolderBackupRepository {
+
+ override fun getCameraUploadsConfiguration(): CameraUploadsConfiguration? =
+ folderBackupLocalDataSource.getCameraUploadsConfiguration()
+
+ override fun getFolderBackupConfigurationStreamByName(name: String): Flow =
+ folderBackupLocalDataSource.getFolderBackupConfigurationStreamByName(name)
+
+ override fun saveFolderBackupConfiguration(folderBackUpConfiguration: FolderBackUpConfiguration) {
+ folderBackupLocalDataSource.saveFolderBackupConfiguration(folderBackUpConfiguration)
+ }
+
+ override fun resetFolderBackupConfigurationByName(name: String) =
+ folderBackupLocalDataSource.resetFolderBackupConfigurationByName(name)
+
+}
diff --git a/owncloudData/src/main/java/com/owncloud/android/data/folderbackup/datasources/FolderBackupLocalDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/folderbackup/datasources/FolderBackupLocalDataSource.kt
new file mode 100644
index 00000000000..102119cb62c
--- /dev/null
+++ b/owncloudData/src/main/java/com/owncloud/android/data/folderbackup/datasources/FolderBackupLocalDataSource.kt
@@ -0,0 +1,33 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.data.folderbackup.datasources
+
+import com.owncloud.android.domain.camerauploads.model.CameraUploadsConfiguration
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration
+import kotlinx.coroutines.flow.Flow
+
+interface FolderBackupLocalDataSource {
+ fun getCameraUploadsConfiguration(): CameraUploadsConfiguration?
+
+ fun getFolderBackupConfigurationStreamByName(name: String): Flow
+
+ fun saveFolderBackupConfiguration(folderBackUpConfiguration: FolderBackUpConfiguration)
+
+ fun resetFolderBackupConfigurationByName(name: String)
+}
diff --git a/owncloudData/src/main/java/com/owncloud/android/data/folderbackup/datasources/implementation/FolderBackupLocalDataSourceImpl.kt b/owncloudData/src/main/java/com/owncloud/android/data/folderbackup/datasources/implementation/FolderBackupLocalDataSourceImpl.kt
new file mode 100644
index 00000000000..7f022f6a3a0
--- /dev/null
+++ b/owncloudData/src/main/java/com/owncloud/android/data/folderbackup/datasources/implementation/FolderBackupLocalDataSourceImpl.kt
@@ -0,0 +1,82 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.data.folderbackup.datasources.implementation
+
+import com.owncloud.android.data.folderbackup.datasources.FolderBackupLocalDataSource
+import com.owncloud.android.data.folderbackup.db.FolderBackUpEntity
+import com.owncloud.android.data.folderbackup.db.FolderBackupDao
+import com.owncloud.android.domain.camerauploads.model.CameraUploadsConfiguration
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration.Companion.pictureUploadsName
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration.Companion.videoUploadsName
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+class FolderBackupLocalDataSourceImpl(
+ private val folderBackupDao: FolderBackupDao,
+) : FolderBackupLocalDataSource {
+
+ override fun getCameraUploadsConfiguration(): CameraUploadsConfiguration? {
+ val pictureUploadsConfiguration = folderBackupDao.getFolderBackUpConfigurationByName(pictureUploadsName)
+ val videoUploadsConfiguration = folderBackupDao.getFolderBackUpConfigurationByName(videoUploadsName)
+
+ if (pictureUploadsConfiguration == null && videoUploadsConfiguration == null) return null
+
+ return CameraUploadsConfiguration(
+ pictureUploadsConfiguration = pictureUploadsConfiguration?.toModel(),
+ videoUploadsConfiguration = videoUploadsConfiguration?.toModel(),
+ )
+ }
+
+ override fun getFolderBackupConfigurationStreamByName(name: String): Flow =
+ folderBackupDao.getFolderBackUpConfigurationByNameStream(name = name).map { it?.toModel() }
+
+ override fun saveFolderBackupConfiguration(folderBackUpConfiguration: FolderBackUpConfiguration) {
+ folderBackupDao.update(folderBackUpConfiguration.toEntity())
+ }
+
+ override fun resetFolderBackupConfigurationByName(name: String) {
+ folderBackupDao.delete(name)
+ }
+
+ /**************************************************************************************************************
+ ************************************************* Mappers ****************************************************
+ **************************************************************************************************************/
+ private fun FolderBackUpEntity.toModel() =
+ FolderBackUpConfiguration(
+ accountName = accountName,
+ behavior = FolderBackUpConfiguration.Behavior.fromString(behavior),
+ sourcePath = sourcePath,
+ uploadPath = uploadPath,
+ wifiOnly = wifiOnly,
+ lastSyncTimestamp = lastSyncTimestamp,
+ name = name
+ )
+
+ private fun FolderBackUpConfiguration.toEntity(): FolderBackUpEntity =
+ FolderBackUpEntity(
+ accountName = accountName,
+ behavior = behavior.toString(),
+ sourcePath = sourcePath,
+ uploadPath = uploadPath,
+ wifiOnly = wifiOnly,
+ name = name,
+ lastSyncTimestamp = lastSyncTimestamp
+ )
+}
diff --git a/owncloudData/src/main/java/com/owncloud/android/data/folderbackup/db/FolderBackUpEntity.kt b/owncloudData/src/main/java/com/owncloud/android/data/folderbackup/db/FolderBackUpEntity.kt
new file mode 100644
index 00000000000..429b8e09463
--- /dev/null
+++ b/owncloudData/src/main/java/com/owncloud/android/data/folderbackup/db/FolderBackUpEntity.kt
@@ -0,0 +1,41 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.data.folderbackup.db
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import com.owncloud.android.data.ProviderMeta
+
+@Entity(tableName = ProviderMeta.ProviderTableMeta.FOLDER_BACKUP_TABLE_NAME)
+data class FolderBackUpEntity(
+ val accountName: String,
+ val behavior: String,
+ val sourcePath: String,
+ val uploadPath: String,
+ val wifiOnly: Boolean,
+ @ColumnInfo(name = folderBackUpEntityNameField) val name: String,
+ val lastSyncTimestamp: Long,
+) {
+ @PrimaryKey(autoGenerate = true) var id: Int = 0
+
+ companion object {
+ internal const val folderBackUpEntityNameField = "name"
+ }
+}
diff --git a/owncloudData/src/main/java/com/owncloud/android/data/folderbackup/db/FolderBackupDao.kt b/owncloudData/src/main/java/com/owncloud/android/data/folderbackup/db/FolderBackupDao.kt
new file mode 100644
index 00000000000..c4893d300fe
--- /dev/null
+++ b/owncloudData/src/main/java/com/owncloud/android/data/folderbackup/db/FolderBackupDao.kt
@@ -0,0 +1,61 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.data.folderbackup.db
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import androidx.room.Transaction
+import com.owncloud.android.data.ProviderMeta
+import kotlinx.coroutines.flow.Flow
+
+@Dao
+abstract class FolderBackupDao {
+ @Query(
+ "SELECT * from " + ProviderMeta.ProviderTableMeta.FOLDER_BACKUP_TABLE_NAME + " WHERE " +
+ FolderBackUpEntity.folderBackUpEntityNameField + " = :name"
+ )
+ abstract fun getFolderBackUpConfigurationByName(
+ name: String
+ ): FolderBackUpEntity?
+
+ @Query(
+ "SELECT * from " + ProviderMeta.ProviderTableMeta.FOLDER_BACKUP_TABLE_NAME + " WHERE " +
+ FolderBackUpEntity.folderBackUpEntityNameField + " = :name"
+ )
+ abstract fun getFolderBackUpConfigurationByNameStream(
+ name: String
+ ): Flow
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ abstract fun insert(folderBackUpEntity: FolderBackUpEntity): Long
+
+ @Transaction
+ open fun update(folderBackUpEntity: FolderBackUpEntity): Long {
+ delete(folderBackUpEntity.name)
+ return insert(folderBackUpEntity)
+ }
+
+ @Query(
+ "DELETE from " + ProviderMeta.ProviderTableMeta.FOLDER_BACKUP_TABLE_NAME + " WHERE " +
+ FolderBackUpEntity.folderBackUpEntityNameField + " = :name"
+ )
+ abstract fun delete(name: String): Int
+}
diff --git a/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_34.kt b/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_34.kt
new file mode 100644
index 00000000000..ec6d3ff75a7
--- /dev/null
+++ b/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_34.kt
@@ -0,0 +1,103 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2020 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+package com.owncloud.android.data.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+import com.owncloud.android.data.ProviderMeta.ProviderTableMeta.FOLDER_BACKUP_TABLE_NAME
+import com.owncloud.android.data.preferences.datasources.SharedPreferencesProvider
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration.Companion.pictureUploadsName
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration.Companion.videoUploadsName
+import java.io.File
+
+val MIGRATION_33_34 = object : Migration(33, 34) {
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("CREATE TABLE IF NOT EXISTS `$FOLDER_BACKUP_TABLE_NAME` (`accountName` TEXT NOT NULL, `behavior` TEXT NOT NULL, `sourcePath` TEXT NOT NULL, `uploadPath` TEXT NOT NULL, `wifiOnly` INTEGER NOT NULL, `name` TEXT NOT NULL, `lastSyncTimestamp` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)")
+ }
+}
+
+@Deprecated("Legacy code. Only used to migrate old camera uploads configuration from ")
+class CameraUploadsMigrationToRoom(val sharedPreferencesProvider: SharedPreferencesProvider) {
+
+ fun getPictureUploadsConfigurationPreferences(timestamp: Long): FolderBackUpConfiguration? {
+
+ if (!sharedPreferencesProvider.getBoolean(PREF__CAMERA_PICTURE_UPLOADS_ENABLED, false)) return null
+
+ return FolderBackUpConfiguration(
+ accountName = sharedPreferencesProvider.getString(PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME, null) ?: "",
+ wifiOnly = sharedPreferencesProvider.getBoolean(PREF__CAMERA_PICTURE_UPLOADS_WIFI_ONLY, false),
+ uploadPath = getUploadPathForPreference(PREF__CAMERA_PICTURE_UPLOADS_PATH),
+ sourcePath = getSourcePathForPreference(PREF__CAMERA_PICTURE_UPLOADS_SOURCE),
+ behavior = getBehaviorForPreference(PREF__CAMERA_PICTURE_UPLOADS_BEHAVIOUR),
+ lastSyncTimestamp = timestamp,
+ name = pictureUploadsName,
+ )
+ }
+
+ fun getVideoUploadsConfigurationPreferences(timestamp: Long): FolderBackUpConfiguration? {
+ if (!sharedPreferencesProvider.getBoolean(PREF__CAMERA_VIDEO_UPLOADS_ENABLED, false)) return null
+
+ return FolderBackUpConfiguration(
+ accountName = sharedPreferencesProvider.getString(PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME, null) ?: "",
+ wifiOnly = sharedPreferencesProvider.getBoolean(PREF__CAMERA_VIDEO_UPLOADS_WIFI_ONLY, false),
+ uploadPath = getUploadPathForPreference(PREF__CAMERA_VIDEO_UPLOADS_PATH),
+ sourcePath = getSourcePathForPreference(PREF__CAMERA_VIDEO_UPLOADS_SOURCE),
+ behavior = getBehaviorForPreference(PREF__CAMERA_VIDEO_UPLOADS_BEHAVIOUR),
+ lastSyncTimestamp = timestamp,
+ name = videoUploadsName,
+ )
+ }
+
+ private fun getUploadPathForPreference(keyPreference: String): String {
+ val uploadPath = sharedPreferencesProvider.getString(
+ key = keyPreference,
+ defaultValue = DEFAULT_PATH_FOR_CAMERA_UPLOADS + File.separator
+ )
+ return if (uploadPath!!.endsWith(File.separator)) uploadPath else uploadPath + File.separator
+ }
+
+ private fun getSourcePathForPreference(keyPreference: String): String {
+ return sharedPreferencesProvider.getString(keyPreference, null) ?: ""
+ }
+
+ private fun getBehaviorForPreference(keyPreference: String): FolderBackUpConfiguration.Behavior {
+ val storedBehaviour = sharedPreferencesProvider.getString(keyPreference, null) ?: return FolderBackUpConfiguration.Behavior.COPY
+
+ return FolderBackUpConfiguration.Behavior.fromString(storedBehaviour)
+ }
+
+ companion object {
+ private const val PREF__CAMERA_PICTURE_UPLOADS_ENABLED = "enable_picture_uploads"
+ private const val PREF__CAMERA_VIDEO_UPLOADS_ENABLED = "enable_video_uploads"
+ private const val PREF__CAMERA_PICTURE_UPLOADS_WIFI_ONLY = "picture_uploads_on_wifi"
+ private const val PREF__CAMERA_VIDEO_UPLOADS_WIFI_ONLY = "video_uploads_on_wifi"
+ private const val PREF__CAMERA_PICTURE_UPLOADS_PATH = "picture_uploads_path"
+ private const val PREF__CAMERA_VIDEO_UPLOADS_PATH = "video_uploads_path"
+ private const val PREF__CAMERA_PICTURE_UPLOADS_BEHAVIOUR = "picture_uploads_behaviour"
+ private const val PREF__CAMERA_PICTURE_UPLOADS_SOURCE = "picture_uploads_source_path"
+ private const val PREF__CAMERA_VIDEO_UPLOADS_BEHAVIOUR = "video_uploads_behaviour"
+ private const val PREF__CAMERA_VIDEO_UPLOADS_SOURCE = "video_uploads_source_path"
+ private const val PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME = "picture_uploads_account_name"
+ private const val PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME = "video_uploads_account_name"
+
+ private const val DEFAULT_PATH_FOR_CAMERA_UPLOADS = "/CameraUpload"
+ }
+}
diff --git a/owncloudDomain/build.gradle b/owncloudDomain/build.gradle
index 968a0aedf67..b7b681004ae 100644
--- a/owncloudDomain/build.gradle
+++ b/owncloudDomain/build.gradle
@@ -39,6 +39,7 @@ dependencies {
// Kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"
// Dependencies for unit tests
testImplementation project(':owncloudTestUtil')
diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/FolderBackupRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/FolderBackupRepository.kt
new file mode 100644
index 00000000000..7d7734121fd
--- /dev/null
+++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/FolderBackupRepository.kt
@@ -0,0 +1,33 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.domain.camerauploads
+
+import com.owncloud.android.domain.camerauploads.model.CameraUploadsConfiguration
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration
+import kotlinx.coroutines.flow.Flow
+
+interface FolderBackupRepository {
+ fun getCameraUploadsConfiguration(): CameraUploadsConfiguration?
+
+ fun getFolderBackupConfigurationStreamByName(name: String): Flow
+
+ fun saveFolderBackupConfiguration(folderBackUpConfiguration: FolderBackUpConfiguration)
+
+ fun resetFolderBackupConfigurationByName(name: String)
+}
diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/model/CameraUploadsConfiguration.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/model/CameraUploadsConfiguration.kt
new file mode 100644
index 00000000000..7fccecc74f7
--- /dev/null
+++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/model/CameraUploadsConfiguration.kt
@@ -0,0 +1,26 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.domain.camerauploads.model
+
+data class CameraUploadsConfiguration(
+ val pictureUploadsConfiguration: FolderBackUpConfiguration?,
+ val videoUploadsConfiguration: FolderBackUpConfiguration?
+) {
+ fun areCameraUploadsDisabled() = pictureUploadsConfiguration == null && videoUploadsConfiguration == null
+}
diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/model/FolderBackUpConfiguration.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/model/FolderBackUpConfiguration.kt
new file mode 100644
index 00000000000..c8b977a9762
--- /dev/null
+++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/model/FolderBackUpConfiguration.kt
@@ -0,0 +1,52 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.domain.camerauploads.model
+
+data class FolderBackUpConfiguration(
+ val accountName: String,
+ val behavior: Behavior,
+ val sourcePath: String,
+ val uploadPath: String,
+ val wifiOnly: Boolean,
+ val lastSyncTimestamp: Long,
+ val name: String,
+) {
+
+ val isPictureUploads get() = name == pictureUploadsName
+ val isVideoUploads get() = name == videoUploadsName
+
+ companion object {
+ const val pictureUploadsName = "Picture uploads"
+ const val videoUploadsName = "Video uploads"
+ }
+
+ enum class Behavior {
+ MOVE, COPY;
+
+ companion object {
+ fun fromString(string: String): Behavior {
+ return if (string.equals("MOVE", ignoreCase = true)) {
+ MOVE
+ } else {
+ COPY
+ }
+ }
+ }
+ }
+}
diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/usecases/GetCameraUploadsConfigurationUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/usecases/GetCameraUploadsConfigurationUseCase.kt
new file mode 100644
index 00000000000..73497f5cc43
--- /dev/null
+++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/usecases/GetCameraUploadsConfigurationUseCase.kt
@@ -0,0 +1,31 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.domain.camerauploads.usecases
+
+import com.owncloud.android.domain.BaseUseCaseWithResult
+import com.owncloud.android.domain.camerauploads.FolderBackupRepository
+import com.owncloud.android.domain.camerauploads.model.CameraUploadsConfiguration
+
+class GetCameraUploadsConfigurationUseCase(
+ private val folderBackupRepository: FolderBackupRepository
+) : BaseUseCaseWithResult() {
+
+ override fun run(params: Unit): CameraUploadsConfiguration? =
+ folderBackupRepository.getCameraUploadsConfiguration()
+}
diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/usecases/GetPictureUploadsConfigurationStreamUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/usecases/GetPictureUploadsConfigurationStreamUseCase.kt
new file mode 100644
index 00000000000..baa286f53c9
--- /dev/null
+++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/usecases/GetPictureUploadsConfigurationStreamUseCase.kt
@@ -0,0 +1,32 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.domain.camerauploads.usecases
+
+import com.owncloud.android.domain.BaseUseCase
+import com.owncloud.android.domain.camerauploads.FolderBackupRepository
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration
+import kotlinx.coroutines.flow.Flow
+
+class GetPictureUploadsConfigurationStreamUseCase(
+ private val folderBackupRepository: FolderBackupRepository
+) : BaseUseCase, Unit>() {
+
+ override fun run(params: Unit): Flow =
+ folderBackupRepository.getFolderBackupConfigurationStreamByName(FolderBackUpConfiguration.pictureUploadsName)
+}
diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/usecases/GetVideoUploadsConfigurationStreamUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/usecases/GetVideoUploadsConfigurationStreamUseCase.kt
new file mode 100644
index 00000000000..2af192b2c71
--- /dev/null
+++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/usecases/GetVideoUploadsConfigurationStreamUseCase.kt
@@ -0,0 +1,33 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.domain.camerauploads.usecases
+
+import com.owncloud.android.domain.BaseUseCase
+import com.owncloud.android.domain.camerauploads.FolderBackupRepository
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration.Companion.videoUploadsName
+import kotlinx.coroutines.flow.Flow
+
+class GetVideoUploadsConfigurationStreamUseCase(
+ private val folderBackupRepository: FolderBackupRepository
+) : BaseUseCase, Unit>() {
+
+ override fun run(params: Unit): Flow =
+ folderBackupRepository.getFolderBackupConfigurationStreamByName(videoUploadsName)
+}
diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/usecases/ResetPictureUploadsUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/usecases/ResetPictureUploadsUseCase.kt
new file mode 100644
index 00000000000..615b8828bbe
--- /dev/null
+++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/usecases/ResetPictureUploadsUseCase.kt
@@ -0,0 +1,31 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.domain.camerauploads.usecases
+
+import com.owncloud.android.domain.BaseUseCase
+import com.owncloud.android.domain.camerauploads.FolderBackupRepository
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration.Companion.pictureUploadsName
+
+class ResetPictureUploadsUseCase(
+ private val folderBackupRepository: FolderBackupRepository
+) : BaseUseCase() {
+
+ override fun run(params: Unit) =
+ folderBackupRepository.resetFolderBackupConfigurationByName(pictureUploadsName)
+}
diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/usecases/ResetVideoUploadsUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/usecases/ResetVideoUploadsUseCase.kt
new file mode 100644
index 00000000000..78b908bf546
--- /dev/null
+++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/usecases/ResetVideoUploadsUseCase.kt
@@ -0,0 +1,31 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.domain.camerauploads.usecases
+
+import com.owncloud.android.domain.BaseUseCase
+import com.owncloud.android.domain.camerauploads.FolderBackupRepository
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration.Companion.videoUploadsName
+
+class ResetVideoUploadsUseCase(
+ private val folderBackupRepository: FolderBackupRepository
+) : BaseUseCase() {
+
+ override fun run(params: Unit) =
+ folderBackupRepository.resetFolderBackupConfigurationByName(videoUploadsName)
+}
diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/usecases/SavePictureUploadsConfigurationUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/usecases/SavePictureUploadsConfigurationUseCase.kt
new file mode 100644
index 00000000000..198bdec59d4
--- /dev/null
+++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/usecases/SavePictureUploadsConfigurationUseCase.kt
@@ -0,0 +1,35 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.domain.camerauploads.usecases
+
+import com.owncloud.android.domain.BaseUseCaseWithResult
+import com.owncloud.android.domain.camerauploads.FolderBackupRepository
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration
+
+class SavePictureUploadsConfigurationUseCase(
+ private val folderBackupRepository: FolderBackupRepository
+) : BaseUseCaseWithResult() {
+
+ override fun run(params: Params) =
+ folderBackupRepository.saveFolderBackupConfiguration(params.pictureUploadsConfiguration)
+
+ data class Params(
+ val pictureUploadsConfiguration: FolderBackUpConfiguration
+ )
+}
diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/usecases/SaveVideoUploadsConfigurationUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/usecases/SaveVideoUploadsConfigurationUseCase.kt
new file mode 100644
index 00000000000..7759101de0d
--- /dev/null
+++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/camerauploads/usecases/SaveVideoUploadsConfigurationUseCase.kt
@@ -0,0 +1,35 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Abel García de Prada
+ * Copyright (C) 2021 ownCloud GmbH.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.owncloud.android.domain.camerauploads.usecases
+
+import com.owncloud.android.domain.BaseUseCaseWithResult
+import com.owncloud.android.domain.camerauploads.FolderBackupRepository
+import com.owncloud.android.domain.camerauploads.model.FolderBackUpConfiguration
+
+class SaveVideoUploadsConfigurationUseCase(
+ private val folderBackupRepository: FolderBackupRepository
+) : BaseUseCaseWithResult() {
+
+ override fun run(params: Params) =
+ folderBackupRepository.saveFolderBackupConfiguration(params.videoUploadsConfiguration)
+
+ data class Params(
+ val videoUploadsConfiguration: FolderBackUpConfiguration
+ )
+}