diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index ebd29943e2..fc1e12d253 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:noble@sha256:99c35190e22d294cdace2783ac55effc69d32896daaa265f0bbedbcde4fbe3e5 +FROM ubuntu:noble@sha256:278628f08d4979fb9af9ead44277dbc9c92c2465922310916ad0c46ec9999295 ARG DEBIAN_FRONTEND=noninteractive ENV ANDROID_HOME=/usr/lib/android-sdk diff --git a/.drone.yml b/.drone.yml index ec4ca94603..2b770068b2 100644 --- a/.drone.yml +++ b/.drone.yml @@ -9,7 +9,7 @@ name: tests-stable steps: - name: gplay - image: ghcr.io/nextcloud/continuous-integration-android8:3 + image: ghcr.io/nextcloud/continuous-integration-android8:4 privileged: true environment: LOG_USERNAME: @@ -132,7 +132,7 @@ name: allScreenshots steps: - name: runAllScreenshots - image: ghcr.io/nextcloud/continuous-integration-android8:3 + image: ghcr.io/nextcloud/continuous-integration-android8:4 privileged: true environment: GIT_USERNAME: @@ -183,6 +183,6 @@ name: GIT_TOKEN data: XIoa9IYq+xQ+N5iln8dlpWv0jV6ROr7HuE24ioUr4uQ8m8SjyH0yognWYLYLqnbTKrFWlFZiEMQTH/sZiWjRFvV1iL0= --- kind: signature -hmac: 8aeb253a66781661eee55fd4111a40a2fe222f5bfec35f2f3a7775e3d6db6b09 +hmac: f0d3fa25f7f901e8b3deacec52db0d5a81f05f7d4f13af27c633ad712ecdbc4d ... diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index e0609e2603..ff223f223b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -39,7 +39,7 @@ jobs: with: swap-size-gb: 10 - name: Initialize CodeQL - uses: github/codeql-action/init@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # v3.27.4 + uses: github/codeql-action/init@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5 with: languages: ${{ matrix.language }} - name: Set up JDK 17 @@ -53,4 +53,4 @@ jobs: echo "org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError" > "$HOME/.gradle/gradle.properties" ./gradlew assembleDebug - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # v3.27.4 + uses: github/codeql-action/analyze@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index a109dd3c10..7f3c962e1b 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -42,6 +42,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # v3.27.4 + uses: github/codeql-action/upload-sarif@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5 with: sarif_file: results.sarif diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 335c462ef9..6d7c9fa92e 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -42,7 +42,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: scripts/uploadReport.sh "${{ secrets.LOG_USERNAME }}" "${{ secrets.LOG_PASSWORD }}" ${{github.event.number}} "test" "Unit" ${{github.event.number}} - name: Upload coverage to codecov - uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0 + uses: codecov/codecov-action@985343d70564a82044c1b7fcb84c2fa05405c1a2 # v5.0.4 with: token: ${{ secrets.CODECOV_TOKEN }} flags: unit diff --git a/app/build.gradle b/app/build.gradle index d1cddd2e1f..08236d87e6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -412,7 +412,7 @@ dependencies { implementation "com.github.stateless4j:stateless4j:2.6.0" // upon each update first test: new registration, receive push - gplayImplementation "com.google.firebase:firebase-messaging:24.0.2" + gplayImplementation "com.google.firebase:firebase-messaging:24.1.0" gplayImplementation 'com.google.android.gms:play-services-base:18.5.0' gplayImplementation 'com.google.android.play:review-ktx:2.0.2' diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SettingsActivityIT_open.png b/app/screenshots/gplay/debug/com.nextcloud.client.SettingsActivityIT_open.png index ccfd4cb3c4..deb02e7f02 100644 Binary files a/app/screenshots/gplay/debug/com.nextcloud.client.SettingsActivityIT_open.png and b/app/screenshots/gplay/debug/com.nextcloud.client.SettingsActivityIT_open.png differ diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SettingsActivityIT_showMnemonic_Error.png b/app/screenshots/gplay/debug/com.nextcloud.client.SettingsActivityIT_showMnemonic_Error.png index 62ff29f981..5306a63410 100644 Binary files a/app/screenshots/gplay/debug/com.nextcloud.client.SettingsActivityIT_showMnemonic_Error.png and b/app/screenshots/gplay/debug/com.nextcloud.client.SettingsActivityIT_showMnemonic_Error.png differ diff --git a/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.kt b/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.kt index 3a0638c06b..4b88be021d 100644 --- a/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.kt +++ b/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.kt @@ -155,43 +155,43 @@ class FileDisplayActivityIT : AbstractOnServerIT() { Assert.assertEquals(storageManager.getFileByPath("/"), sut.currentDir) } - @Test - fun checkToolbarTitleOnNavigation() { - // Create folder structure - val topFolder = "folder1" - val childFolder = "folder2" - - CreateFolderOperation("/$topFolder/", user, targetContext, storageManager) - .execute(client) - - CreateFolderOperation("/$topFolder/$childFolder/", user, targetContext, storageManager) - .execute(client) - - activityRule.launchActivity(null) - - shortSleep() - - // go into "foo" - onView(withText(topFolder)).perform(click()) - shortSleep() - - // check title is right - checkToolbarTitle(topFolder) - - // go into "bar" - onView(withText(childFolder)).perform(click()) - shortSleep() - - // check title is right - checkToolbarTitle(childFolder) - - // browse back up, we should be back in "foo" - Espresso.pressBack() - shortSleep() - - // check title is right - checkToolbarTitle(topFolder) - } +// @Test +// fun checkToolbarTitleOnNavigation() { +// // Create folder structure +// val topFolder = "folder1" +// val childFolder = "folder2" +// +// CreateFolderOperation("/$topFolder/", user, targetContext, storageManager) +// .execute(client) +// +// CreateFolderOperation("/$topFolder/$childFolder/", user, targetContext, storageManager) +// .execute(client) +// +// activityRule.launchActivity(null) +// +// shortSleep() +// +// // go into "foo" +// onView(withText(topFolder)).perform(click()) +// shortSleep() +// +// // check title is right +// checkToolbarTitle(topFolder) +// +// // go into "bar" +// onView(withText(childFolder)).perform(click()) +// shortSleep() +// +// // check title is right +// checkToolbarTitle(childFolder) +// +// // browse back up, we should be back in "foo" +// Espresso.pressBack() +// shortSleep() +// +// // check title is right +// checkToolbarTitle(topFolder) +// } private fun checkToolbarTitle(childFolder: String) { onView(withId(R.id.appbar)).check( diff --git a/app/src/androidTest/java/com/nextcloud/utils/AppConfigManagerTests.kt b/app/src/androidTest/java/com/nextcloud/utils/AppConfigManagerTests.kt deleted file mode 100644 index 8392ae549f..0000000000 --- a/app/src/androidTest/java/com/nextcloud/utils/AppConfigManagerTests.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2024 Alper Ozturk - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -package com.nextcloud.utils - -import android.os.Bundle -import com.owncloud.android.AbstractIT -import com.owncloud.android.lib.common.OwnCloudClientManagerFactory -import com.owncloud.android.utils.appConfig.AppConfigKeys -import com.owncloud.android.utils.appConfig.AppConfigManager -import org.junit.AfterClass -import org.junit.Test - -class AppConfigManagerTests : AbstractIT() { - - private val testBaseUrl = "nextcloud.cloud.cloud" - private val testProxyHost = "nextcloud.cloud.cloud.com" - - @Suppress("MagicNumber") - private val testProxyPort = 8800 - - @Test - fun testSetProxyConfigWhenGivenClientBrandedPlusAndCorrectBundleDataProxyConfigurationShouldSet() { - val proxySetting = Bundle().apply { - putString(AppConfigKeys.ProxyHost.key, testProxyHost) - putInt(AppConfigKeys.ProxyPort.key, testProxyPort) - } - - AppConfigManager(targetContext, proxySetting).run { - setProxyConfig(true) - } - - val proxyHost = OwnCloudClientManagerFactory.getProxyHost() - val proxyPort = OwnCloudClientManagerFactory.getProxyPort() - - assert(proxyHost.equals(testProxyHost)) - assert(proxyPort == testProxyPort) - } - - @Test - fun testSetProxyConfigWhenGivenClientNotBrandedPlusAndCorrectBundleDataProxyConfigurationShouldNotSet() { - val proxySetting = Bundle().apply { - putString(AppConfigKeys.ProxyHost.key, testProxyHost) - putInt(AppConfigKeys.ProxyPort.key, testProxyPort) - } - - AppConfigManager(targetContext, proxySetting).run { - setProxyConfig(false) - } - - val proxyHost = OwnCloudClientManagerFactory.getProxyHost() - val proxyPort = OwnCloudClientManagerFactory.getProxyPort() - - assert(proxyHost.equals("")) - assert(proxyPort == -1) - } - - @Test - fun testGetBaseUrlConfigWhenGivenClientBrandedPlusAndCorrectBundleDataBaseUrlConfigurationShouldSet() { - val baseUrlConfig = Bundle().apply { - putString(AppConfigKeys.BaseUrl.key, testBaseUrl) - } - val sut = AppConfigManager(targetContext, baseUrlConfig) - assert(!sut.getBaseUrl(true).isNullOrEmpty()) - } - - @Test - fun testGetBaseUrlConfigWhenGivenClientBrandedPlusAndBrokenBundleDataBaseUrlConfigurationShouldNotSet() { - val baseUrlConfig = Bundle() - val sut = AppConfigManager(targetContext, baseUrlConfig) - assert(sut.getBaseUrl(true).isNullOrEmpty()) - } - - companion object { - @JvmStatic - @AfterClass - fun tearDown() { - OwnCloudClientManagerFactory.setProxyHost("") - OwnCloudClientManagerFactory.setProxyPort(-1) - } - } -} diff --git a/app/src/androidTest/java/com/nextcloud/utils/AutoRenameTests.kt b/app/src/androidTest/java/com/nextcloud/utils/AutoRenameTests.kt index 57abe6362b..d3b21d4bb2 100644 --- a/app/src/androidTest/java/com/nextcloud/utils/AutoRenameTests.kt +++ b/app/src/androidTest/java/com/nextcloud/utils/AutoRenameTests.kt @@ -167,4 +167,12 @@ class AutoRenameTests : AbstractOnServerIT() { val expectedFolderName = "/COm02/2569.webp" assert(result == expectedFolderName) { "Expected $expectedFolderName but got $result" } } + + @Test + fun testValidFilename() { + val filename = ".file.TXT" + val result = AutoRename.rename(filename, capability) + val expectedFilename = "_file.txt" + assert(result == expectedFilename) { "Expected $expectedFilename but got $result" } + } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7114b8d9b7..2d3bfe327d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -313,6 +313,11 @@ android:name=".ui.activity.ContactsPreferenceActivity" android:exported="false" android:launchMode="singleInstance" /> + , JsonDe jsonObject.addProperty("newName", src.newName) } + is OfflineOperationType.RemoveFile -> { + jsonObject.addProperty("type", src.type) + jsonObject.addProperty("path", src.path) + } + null -> Unit } @@ -78,6 +83,11 @@ class OfflineOperationTypeAdapter : JsonSerializer, JsonDe jsonObject.get("newName").asString ) + OfflineOperationRawType.RemoveFile.name -> OfflineOperationType.RemoveFile( + jsonObject.get("type").asString, + jsonObject.get("path").asString + ) + else -> null } } diff --git a/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java b/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java index d54af89fed..1715075ba9 100644 --- a/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java +++ b/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java @@ -27,6 +27,7 @@ import com.nextcloud.client.widget.DashboardWidgetProvider; import com.nextcloud.client.widget.DashboardWidgetService; import com.nextcloud.receiver.NetworkChangeReceiver; import com.nextcloud.ui.ChooseAccountDialogFragment; +import com.nextcloud.ui.ChooseStorageLocationDialogFragment; import com.nextcloud.ui.ImageDetailFragment; import com.nextcloud.ui.SetStatusDialogFragment; import com.nextcloud.ui.composeActivity.ComposeActivity; @@ -89,13 +90,13 @@ import com.owncloud.android.ui.dialog.RenameFileDialogFragment; import com.owncloud.android.ui.dialog.RenamePublicShareDialogFragment; import com.owncloud.android.ui.dialog.SendFilesDialog; import com.owncloud.android.ui.dialog.SendShareDialog; -import com.owncloud.android.ui.dialog.setupEncryption.SetupEncryptionDialogFragment; import com.owncloud.android.ui.dialog.SharePasswordDialogFragment; import com.owncloud.android.ui.dialog.SortingOrderDialogFragment; import com.owncloud.android.ui.dialog.SslUntrustedCertDialog; import com.owncloud.android.ui.dialog.StoragePermissionDialogFragment; import com.owncloud.android.ui.dialog.SyncFileNotEnoughSpaceDialogFragment; import com.owncloud.android.ui.dialog.SyncedFolderPreferencesDialogFragment; +import com.owncloud.android.ui.dialog.setupEncryption.SetupEncryptionDialogFragment; import com.owncloud.android.ui.fragment.ExtendedListFragment; import com.owncloud.android.ui.fragment.FeatureFragment; import com.owncloud.android.ui.fragment.FileDetailActivitiesFragment; @@ -416,6 +417,9 @@ abstract class ComponentsModule { @ContributesAndroidInjector abstract SetupEncryptionDialogFragment setupEncryptionDialogFragment(); + @ContributesAndroidInjector + abstract ChooseStorageLocationDialogFragment chooseStorageLocationDialogFragment(); + @ContributesAndroidInjector abstract SharePasswordDialogFragment sharePasswordDialogFragment(); diff --git a/app/src/main/java/com/nextcloud/client/jobs/offlineOperations/OfflineOperationsWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/offlineOperations/OfflineOperationsWorker.kt index db471b79e2..f7aa61f3f2 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/offlineOperations/OfflineOperationsWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/offlineOperations/OfflineOperationsWorker.kt @@ -25,6 +25,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.lib.resources.files.UploadFileRemoteOperation import com.owncloud.android.operations.CreateFolderOperation +import com.owncloud.android.operations.RemoveFileOperation import com.owncloud.android.operations.RenameFileOperation import com.owncloud.android.utils.theme.ViewThemeUtils import kotlinx.coroutines.Dispatchers @@ -163,6 +164,16 @@ class OfflineOperationsWorker( renameFileOperation?.execute(client) to renameFileOperation } + is OfflineOperationType.RemoveFile -> { + val removeFileOperation = withContext(NonCancellable) { + val operationType = (operation.type as OfflineOperationType.RemoveFile) + val ocFile = fileDataStorageManager.getFileByDecryptedRemotePath(operationType.path) + RemoveFileOperation(ocFile, false, user, true, context, fileDataStorageManager) + } + + removeFileOperation.execute(client) to removeFileOperation + } + else -> { Log_OC.d(TAG, "Unsupported operation type: ${operation.type}") null @@ -186,7 +197,15 @@ class OfflineOperationsWorker( Log_OC.d(TAG, "$logMessage filename: ${operation.filename}, type: ${operation.type}") if (result.isSuccess) { - repository.updateNextOperations(operation) + if (operation.type is OfflineOperationType.RemoveFile) { + val operationType = operation.type as OfflineOperationType.RemoveFile + fileDataStorageManager.getFileByDecryptedRemotePath(operationType.path)?.let { ocFile -> + repository.deleteOperation(ocFile) + } + } else { + repository.updateNextOperations(operation) + } + fileDataStorageManager.offlineOperationDao.delete(operation) notificationManager.update(totalOperations, currentSuccessfulOperationIndex, operation.filename ?: "") } else { diff --git a/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.kt b/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.kt index 132189c36a..5f9e872464 100644 --- a/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.kt +++ b/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.kt @@ -24,6 +24,7 @@ import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.appinfo.AppInfo import com.nextcloud.client.di.Injectable import com.nextcloud.client.preferences.AppPreferences +import com.nextcloud.utils.mdm.MDMConfig import com.owncloud.android.BuildConfig import com.owncloud.android.R import com.owncloud.android.authentication.AuthenticatorActivity @@ -76,13 +77,12 @@ class FirstRunActivity : BaseActivity(), Injectable { binding = FirstRunActivityBinding.inflate(layoutInflater) setContentView(binding.root) - val isProviderOrOwnInstallationVisible = resources.getBoolean(R.bool.show_provider_or_own_installation) setSlideshowSize(resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) registerActivityResult() setupLoginButton() - setupSignupButton(isProviderOrOwnInstallationVisible) - setupHostOwnServerTextView(isProviderOrOwnInstallationVisible) + setupSignupButton(MDMConfig.showIntro(this)) + setupHostOwnServerTextView(MDMConfig.showIntro(this)) deleteAccountAtFirstLaunch() setupFeaturesViewAdapter() handleOnBackPressed() @@ -207,10 +207,9 @@ class FirstRunActivity : BaseActivity(), Injectable { } private fun setSlideshowSize(isLandscape: Boolean) { - val isProviderOrOwnInstallationVisible = resources.getBoolean(R.bool.show_provider_or_own_installation) binding.buttonLayout.orientation = if (isLandscape) LinearLayout.HORIZONTAL else LinearLayout.VERTICAL - val layoutParams: LinearLayout.LayoutParams = if (isProviderOrOwnInstallationVisible) { + val layoutParams: LinearLayout.LayoutParams = if (MDMConfig.showIntro(this)) { LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT diff --git a/app/src/main/java/com/nextcloud/client/onboarding/OnboardingServiceImpl.kt b/app/src/main/java/com/nextcloud/client/onboarding/OnboardingServiceImpl.kt index 95b9b677f7..e121d32d02 100644 --- a/app/src/main/java/com/nextcloud/client/onboarding/OnboardingServiceImpl.kt +++ b/app/src/main/java/com/nextcloud/client/onboarding/OnboardingServiceImpl.kt @@ -13,13 +13,14 @@ import android.content.Intent import android.content.res.Resources import com.nextcloud.client.account.CurrentAccountProvider import com.nextcloud.client.preferences.AppPreferences +import com.nextcloud.utils.mdm.MDMConfig import com.owncloud.android.BuildConfig import com.owncloud.android.R import com.owncloud.android.authentication.AuthenticatorActivity import com.owncloud.android.features.FeatureItem import com.owncloud.android.ui.activity.PassCodeActivity -internal class OnboardingServiceImpl constructor( +internal class OnboardingServiceImpl( private val resources: Resources, private val preferences: AppPreferences, private val accountProvider: CurrentAccountProvider @@ -61,8 +62,7 @@ internal class OnboardingServiceImpl constructor( } override fun launchFirstRunIfNeeded(activity: Activity): Boolean { - val isProviderOrOwnInstallationVisible = resources.getBoolean(R.bool.show_provider_or_own_installation) - val canLaunch = isProviderOrOwnInstallationVisible && isFirstRun && activity is AuthenticatorActivity + val canLaunch = MDMConfig.showIntro(activity) && isFirstRun && activity is AuthenticatorActivity if (canLaunch) { val intent = Intent(activity, FirstRunActivity::class.java) activity.startActivityForResult(intent, AuthenticatorActivity.REQUEST_CODE_FIRST_RUN) diff --git a/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java b/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java index a20fd40be3..b0eb0dd110 100644 --- a/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java +++ b/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java @@ -55,6 +55,7 @@ public final class AppPreferencesImpl implements AppPreferences { */ public static final String AUTO_PREF__LAST_SEEN_VERSION_CODE = "lastSeenVersionCode"; public static final String STORAGE_PATH = "storage_path"; + public static final String DATA_STORAGE_LOCATION = "data_storage_location"; public static final String STORAGE_PATH_VALID = "storage_path_valid"; public static final String PREF__DARK_THEME = "dark_theme_mode"; public static final float DEFAULT_GRID_COLUMN = 3f; diff --git a/app/src/main/java/com/nextcloud/model/OfflineOperationType.kt b/app/src/main/java/com/nextcloud/model/OfflineOperationType.kt index 028c8ac3dc..17b7e35298 100644 --- a/app/src/main/java/com/nextcloud/model/OfflineOperationType.kt +++ b/app/src/main/java/com/nextcloud/model/OfflineOperationType.kt @@ -24,10 +24,13 @@ sealed class OfflineOperationType { var ocFileId: Long, val newName: String ) : OfflineOperationType() + + data class RemoveFile(override val type: String, var path: String) : OfflineOperationType() } enum class OfflineOperationRawType { CreateFolder, CreateFile, - RenameFile + RenameFile, + RemoveFile } diff --git a/app/src/main/java/com/nextcloud/ui/ChooseAccountDialogFragment.kt b/app/src/main/java/com/nextcloud/ui/ChooseAccountDialogFragment.kt index 920c595398..edf674d988 100644 --- a/app/src/main/java/com/nextcloud/ui/ChooseAccountDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/ChooseAccountDialogFragment.kt @@ -25,6 +25,7 @@ import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.di.Injectable import com.nextcloud.client.network.ClientFactory import com.nextcloud.utils.extensions.getParcelableArgument +import com.nextcloud.utils.mdm.MDMConfig import com.owncloud.android.R import com.owncloud.android.databinding.DialogChooseAccountBinding import com.owncloud.android.datamodel.FileDataStorageManager @@ -120,8 +121,7 @@ class ChooseAccountDialogFragment : viewThemeUtils ) - // hide "add account" when no multi account - if (!resources.getBoolean(R.bool.multiaccount_support)) { + if (!MDMConfig.multiAccountSupport(requireContext())) { binding.addAccount.visibility = View.GONE } diff --git a/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt b/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt new file mode 100644 index 0000000000..747b78820d --- /dev/null +++ b/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt @@ -0,0 +1,167 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 ZetaTom <70907959+ZetaTom@users.noreply.github.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.ui + +import android.app.Dialog +import android.content.DialogInterface +import android.os.Bundle +import android.preference.PreferenceManager +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.nextcloud.client.di.Injectable +import com.nextcloud.client.preferences.AppPreferencesImpl +import com.owncloud.android.MainApp +import com.owncloud.android.R +import com.owncloud.android.databinding.DialogDataStorageLocationBinding +import com.owncloud.android.datastorage.DataStorageProvider +import com.owncloud.android.datastorage.StoragePoint +import com.owncloud.android.datastorage.StoragePoint.PrivacyType +import com.owncloud.android.datastorage.StoragePoint.StorageType +import com.owncloud.android.utils.DisplayUtils +import com.owncloud.android.utils.theme.ViewThemeUtils +import java.io.File +import javax.inject.Inject + +class ChooseStorageLocationDialogFragment : DialogFragment(), Injectable { + + private lateinit var binding: DialogDataStorageLocationBinding + + @Inject + lateinit var viewThemeUtils: ViewThemeUtils + + private val storagePoints = DataStorageProvider.getInstance().availableStoragePoints + + private val selectedStorageType + get() = if (!binding.storageExternalRadio.isChecked) StorageType.INTERNAL else StorageType.EXTERNAL + private val selectedPrivacyType + get() = if (binding.allowMediaIndexSwitch.isChecked) PrivacyType.PUBLIC else PrivacyType.PRIVATE + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + binding = DialogDataStorageLocationBinding.inflate(layoutInflater) + + viewThemeUtils.material.colorMaterialSwitch(binding.allowMediaIndexSwitch) + viewThemeUtils.platform.themeRadioButton(binding.storageInternalRadio) + viewThemeUtils.platform.themeRadioButton(binding.storageExternalRadio) + + val builder = MaterialAlertDialogBuilder(requireContext()).setTitle(R.string.storage_choose_location) + .setPositiveButton(R.string.common_ok) { dialog: DialogInterface, _ -> + notifyResult() + dialog.dismiss() + }.setView(binding.root) + + viewThemeUtils.dialog.colorMaterialAlertDialogBackground(requireContext(), builder) + + binding.storageRadioGroup.setOnCheckedChangeListener { _, _ -> + updateMediaIndexSwitch() + } + + binding.allowMediaIndexSwitch.setOnCheckedChangeListener { _, _ -> + updateStorageTypeSelection() + } + + return builder.create() + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + setupLocationSelection() + super.onViewCreated(view, savedInstanceState) + } + + override fun onDismiss(dialog: DialogInterface) { + super.onDismiss(dialog) + activity?.finish() + } + + private fun setupLocationSelection() { + updateStorageTypeSelection() + + val radioButton = when (getCurrentStorageLocation().storageType) { + StorageType.EXTERNAL -> binding.storageExternalRadio + else -> binding.storageInternalRadio + } + + radioButton.isChecked = true + updateMediaIndexSwitch() + } + + private fun getStoragePointLabel(storageType: StorageType, privacyType: PrivacyType): String { + val typeString = when (storageType) { + StorageType.INTERNAL -> getString(R.string.storage_internal_storage) + StorageType.EXTERNAL -> getString(R.string.storage_external_storage) + } + + val storagePath = + storagePoints.firstOrNull { it.storageType == storageType && it.privacyType == privacyType }?.path + + return storagePath?.let { + val file = File(it) + val totalSpace = file.totalSpace + val usedSpace = totalSpace - file.freeSpace + return String.format( + getString(R.string.file_migration_free_space), + typeString, + DisplayUtils.bytesToHumanReadable(usedSpace), + DisplayUtils.bytesToHumanReadable(totalSpace) + ) + } ?: typeString + } + + private fun updateMediaIndexSwitch() { + val privacyTypes = + storagePoints.filter { it.storageType == selectedStorageType }.map { it.privacyType }.distinct() + binding.allowMediaIndexSwitch.isEnabled = privacyTypes.size > 1 + binding.allowMediaIndexSwitch.isChecked = privacyTypes.contains(PrivacyType.PUBLIC) + } + + private fun updateStorageTypeSelection() { + val hasInternalStorage = storagePoints.any { it.storageType == StorageType.INTERNAL } + val hasExternalStorage = storagePoints.any { it.storageType == StorageType.EXTERNAL } + + binding.storageInternalRadio.isEnabled = hasInternalStorage + binding.storageInternalRadio.text = getStoragePointLabel(StorageType.INTERNAL, selectedPrivacyType) + + binding.storageExternalRadio.isEnabled = hasExternalStorage + binding.storageExternalRadio.text = getStoragePointLabel(StorageType.EXTERNAL, selectedPrivacyType) + } + + private fun getCurrentStorageLocation(): StoragePoint { + val appContext = MainApp.getAppContext() + val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(appContext) + val storagePath = sharedPreferences.getString(AppPreferencesImpl.STORAGE_PATH, appContext.filesDir.absolutePath) + return storagePoints.first { it.path == storagePath } + } + + private fun notifyResult() { + val newPath = + storagePoints.first { it.storageType == selectedStorageType && it.privacyType == selectedPrivacyType } + + val resultBundle = Bundle().apply { + putString(KEY_RESULT_STORAGE_LOCATION, newPath.path) + } + + parentFragmentManager.setFragmentResult(KEY_RESULT_STORAGE_LOCATION, resultBundle) + } + + companion object { + const val KEY_RESULT_STORAGE_LOCATION = "KEY_RESULT_STORAGE_LOCATION" + const val STORAGE_LOCATION_RESULT_CODE = 100 + + @JvmStatic + fun newInstance() = ChooseStorageLocationDialogFragment() + + @JvmStatic + val TAG: String = Companion::class.java.simpleName + } +} diff --git a/app/src/main/java/com/nextcloud/utils/autoRename/AutoRename.kt b/app/src/main/java/com/nextcloud/utils/autoRename/AutoRename.kt index 33a63bbe92..5f3260a3c2 100644 --- a/app/src/main/java/com/nextcloud/utils/autoRename/AutoRename.kt +++ b/app/src/main/java/com/nextcloud/utils/autoRename/AutoRename.kt @@ -59,7 +59,7 @@ object AutoRename { forbiddenFilenameExtensions.find { it == StringConstants.DOT }?.let { forbiddenExtension -> pathSegments.replaceAll { segment -> - replacePathSegment(forbiddenExtension, segment) + replaceDots(forbiddenExtension, segment) } } @@ -67,9 +67,13 @@ object AutoRename { .filter { it != StringConstants.SPACE && it != StringConstants.DOT } .forEach { forbiddenExtension -> pathSegments.replaceAll { segment -> - replacePathSegment(forbiddenExtension, segment) + replaceFileExtensions(forbiddenExtension, segment) } } + + pathSegments.replaceAll { segment -> + lowercaseFileExtension(segment) + } } } @@ -82,16 +86,41 @@ object AutoRename { } } - private fun replacePathSegment(forbiddenExtension: String, segment: String): String { - return if (segment.endsWith(forbiddenExtension, ignoreCase = true) || - segment.startsWith(forbiddenExtension, ignoreCase = true) - ) { - segment.replace(forbiddenExtension, REPLACEMENT) + private fun lowercaseFileExtension(input: String): String { + val lastDotIndex = input.lastIndexOf('.') + + return if (lastDotIndex > 0) { + val base = input.substring(0, lastDotIndex) + val extension = input.substring(lastDotIndex + 1).lowercase() // Convert extension to lowercase + + "$base.$extension" + } else { + input + } + } + + private fun replaceDots(forbiddenExtension: String, segment: String): String { + return if (isSegmentContainsForbiddenExtension(forbiddenExtension, segment)) { + segment.replaceFirst(forbiddenExtension, REPLACEMENT) } else { segment } } + private fun replaceFileExtensions(forbiddenExtension: String, segment: String): String { + return if (isSegmentContainsForbiddenExtension(forbiddenExtension, segment)) { + val newExtension = forbiddenExtension.replace(StringConstants.DOT, REPLACEMENT, ignoreCase = true) + segment.replace(forbiddenExtension, newExtension.lowercase(), ignoreCase = true) + } else { + segment + } + } + + private fun isSegmentContainsForbiddenExtension(forbiddenExtension: String, segment: String): Boolean { + return segment.endsWith(forbiddenExtension, ignoreCase = true) || + segment.startsWith(forbiddenExtension, ignoreCase = true) + } + private fun convertToUTF8(filename: String): String { return String(filename.toByteArray(), Charsets.UTF_8) } diff --git a/app/src/main/java/com/nextcloud/utils/mdm/MDMConfig.kt b/app/src/main/java/com/nextcloud/utils/mdm/MDMConfig.kt new file mode 100644 index 0000000000..126dcf3404 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/mdm/MDMConfig.kt @@ -0,0 +1,134 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.mdm + +import android.content.Context +import android.content.RestrictionsManager +import com.owncloud.android.BuildConfig +import com.owncloud.android.R +import com.owncloud.android.utils.appConfig.AppConfigKeys + +object MDMConfig { + fun multiAccountSupport(context: Context): Boolean { + val multiAccountSupport = context.resources.getBoolean(R.bool.multiaccount_support) + + val disableMultiAccountViaMDM = context.getRestriction( + AppConfigKeys.DisableMultiAccount, + context.resources.getBoolean(R.bool.disable_multiaccount) + ) + + return multiAccountSupport && !disableMultiAccountViaMDM + } + + fun shareViaLink(context: Context): Boolean { + val disableShareViaMDM = context.getRestriction( + AppConfigKeys.DisableSharing, + context.resources.getBoolean(R.bool.disable_sharing) + ) + + val shareViaLink = context.resources.getBoolean(R.bool.share_via_link_feature) + + return shareViaLink && !disableShareViaMDM + } + + fun shareViaUser(context: Context): Boolean { + val disableShareViaMDM = context.getRestriction( + AppConfigKeys.DisableSharing, + context.resources.getBoolean(R.bool.disable_sharing) + ) + + val shareViaUsers = context.resources.getBoolean(R.bool.share_with_users_feature) + + return shareViaUsers && !disableShareViaMDM + } + + fun sendFilesSupport(context: Context): Boolean { + val disableShareViaMDM = context.getRestriction( + AppConfigKeys.DisableSharing, + context.resources.getBoolean(R.bool.disable_sharing) + ) + + val sendFilesToOtherApp = "on".equals(context.getString(R.string.send_files_to_other_apps), ignoreCase = true) + + return sendFilesToOtherApp && !disableShareViaMDM + } + + fun sharingSupport(context: Context): Boolean { + val disableShareViaMDM = context.getRestriction( + AppConfigKeys.DisableSharing, + context.resources.getBoolean(R.bool.disable_sharing) + ) + + val sendFilesToOtherApp = "on".equals(context.getString(R.string.send_files_to_other_apps), ignoreCase = true) + + val shareViaUsers = context.resources.getBoolean(R.bool.share_with_users_feature) + + val shareViaLink = context.resources.getBoolean(R.bool.share_via_link_feature) + + return sendFilesToOtherApp && shareViaLink && shareViaUsers && !disableShareViaMDM + } + + fun clipBoardSupport(context: Context): Boolean { + val disableClipboardSupport = context.getRestriction( + AppConfigKeys.DisableClipboard, + context.resources.getBoolean(R.bool.disable_clipboard) + ) + + return !disableClipboardSupport + } + + fun externalSiteSupport(context: Context): Boolean { + val disableMoreExternalSiteViaMDM = context.getRestriction( + AppConfigKeys.DisableMoreExternalSite, + context.resources.getBoolean(R.bool.disable_more_external_site) + ) + + val showExternalLinks = context.resources.getBoolean(R.bool.show_external_links) + + return showExternalLinks && !disableMoreExternalSiteViaMDM + } + + fun showIntro(context: Context): Boolean { + val disableIntroViaMDM = + context.getRestriction(AppConfigKeys.DisableIntro, context.resources.getBoolean(R.bool.disable_intro)) + + val isProviderOrOwnInstallationVisible = context.resources.getBoolean(R.bool.show_provider_or_own_installation) + + return isProviderOrOwnInstallationVisible && !disableIntroViaMDM + } + + fun isLogEnabled(context: Context): Boolean { + val disableLogViaMDM = + context.getRestriction(AppConfigKeys.DisableLog, context.resources.getBoolean(R.bool.disable_log)) + + val loggerEnabled = context.resources.getBoolean(R.bool.logger_enabled) + + return loggerEnabled && !disableLogViaMDM && BuildConfig.DEBUG + } + + fun getBaseUrl(context: Context): String = context.getRestriction(AppConfigKeys.BaseUrl, "") + + fun getHost(context: Context): String = + context.getRestriction(AppConfigKeys.ProxyHost, context.getString(R.string.proxy_host)) + + fun getPort(context: Context): Int = + context.getRestriction(AppConfigKeys.ProxyPort, context.resources.getInteger(R.integer.proxy_port)) + + @Suppress("UNCHECKED_CAST") + private fun Context.getRestriction(appConfigKey: AppConfigKeys, defaultValue: T): T { + val restrictionsManager = getSystemService(Context.RESTRICTIONS_SERVICE) as? RestrictionsManager + val appRestrictions = restrictionsManager?.getApplicationRestrictions() ?: return defaultValue + + return when (defaultValue) { + is String -> appRestrictions.getString(appConfigKey.key, defaultValue) as T? ?: defaultValue + is Int -> appRestrictions.getInt(appConfigKey.key, defaultValue) as T? ?: defaultValue + is Boolean -> appRestrictions.getBoolean(appConfigKey.key, defaultValue) as T? ?: defaultValue + else -> defaultValue + } + } +} diff --git a/app/src/main/java/com/owncloud/android/MainApp.java b/app/src/main/java/com/owncloud/android/MainApp.java index 08b52ea776..d8c5d37b1c 100644 --- a/app/src/main/java/com/owncloud/android/MainApp.java +++ b/app/src/main/java/com/owncloud/android/MainApp.java @@ -22,11 +22,11 @@ import android.app.Application; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.RestrictionsManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.res.Resources; @@ -62,6 +62,7 @@ import com.nextcloud.client.preferences.DarkMode; import com.nextcloud.receiver.NetworkChangeListener; import com.nextcloud.receiver.NetworkChangeReceiver; import com.nextcloud.utils.extensions.ContextExtensionsKt; +import com.nextcloud.utils.mdm.MDMConfig; import com.nmc.android.ui.LauncherActivity; import com.owncloud.android.authentication.PassCodeManager; import com.owncloud.android.datamodel.ArbitraryDataProvider; @@ -87,7 +88,6 @@ import com.owncloud.android.utils.FilesSyncHelper; import com.owncloud.android.utils.PermissionUtil; import com.owncloud.android.utils.ReceiversHelper; import com.owncloud.android.utils.SecurityUtils; -import com.owncloud.android.utils.appConfig.AppConfigManager; import com.owncloud.android.utils.theme.ViewThemeUtils; import org.conscrypt.Conscrypt; @@ -199,8 +199,6 @@ public class MainApp extends Application implements HasAndroidInjector, NetworkC @SuppressWarnings("unused") private boolean mBound; - private AppConfigManager appConfigManager; - private static AppComponent appComponent; private NetworkChangeReceiver networkChangeReceiver; @@ -333,11 +331,7 @@ public class MainApp extends Application implements HasAndroidInjector, NetworkC OwnCloudClientManagerFactory.setUserAgent(getUserAgent()); if (isClientBrandedPlus()) { - RestrictionsManager restrictionsManager = (RestrictionsManager) getSystemService(Context.RESTRICTIONS_SERVICE); - appConfigManager = new AppConfigManager(this, restrictionsManager.getApplicationRestrictions()); - appConfigManager.setProxyConfig(isClientBrandedPlus()); - - // Listen app config changes + setProxyConfig(); ContextExtensionsKt.registerBroadcastReceiver(this, restrictionsReceiver, restrictionsFilter, ReceiverFlag.NotExported); } else { setProxyForNonBrandedPlusClients(); @@ -346,8 +340,7 @@ public class MainApp extends Application implements HasAndroidInjector, NetworkC // initialise thumbnails cache on background thread new ThumbnailsCacheManager.InitDiskCacheTask().execute(); - - if (BuildConfig.DEBUG || getApplicationContext().getResources().getBoolean(R.bool.logger_enabled)) { + if (MDMConfig.INSTANCE.isLogEnabled(this)) { // use app writable dir, no permissions needed Log_OC.setLoggerImplementation(new LegacyLoggerAdapter(logger)); Log_OC.d("Debug", "start logging"); @@ -388,6 +381,18 @@ public class MainApp extends Application implements HasAndroidInjector, NetworkC registerGlobalPassCodeProtection(); networkChangeReceiver = new NetworkChangeReceiver(this, connectivityService); registerNetworkChangeReceiver(); + + if (!MDMConfig.INSTANCE.sendFilesSupport(this)) { + disableDocumentsStorageProvider(); + } + } + + public void disableDocumentsStorageProvider() { + String packageName = getPackageName(); + String providerClassName = "com.owncloud.android.providers.DocumentsStorageProvider"; + ComponentName componentName = new ComponentName(packageName, providerClassName); + PackageManager packageManager = getPackageManager(); + packageManager.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); } private final LifecycleEventObserver lifecycleEventObserver = ((lifecycleOwner, event) -> { @@ -397,8 +402,7 @@ public class MainApp extends Application implements HasAndroidInjector, NetworkC passCodeManager.setCanAskPin(true); Log_OC.d(TAG, "APP IN BACKGROUND"); } else if (event == Lifecycle.Event.ON_RESUME) { - if (appConfigManager == null) return; - appConfigManager.setProxyConfig(isClientBrandedPlus()); + setProxyConfig(); Log_OC.d(TAG, "APP ON RESUME"); } }); @@ -420,11 +424,34 @@ public class MainApp extends Application implements HasAndroidInjector, NetworkC private final BroadcastReceiver restrictionsReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (appConfigManager == null) return; - appConfigManager.setProxyConfig(isClientBrandedPlus()); + setProxyConfig(); } }; + private void setProxyConfig() { + if (!isClientBrandedPlus()) { + Log_OC.d(TAG, "Proxy configuration cannot be set. Client is not branded plus."); + return; + } + + String host = MDMConfig.INSTANCE.getHost(this); + int port = MDMConfig.INSTANCE.getPort(this); + + if (TextUtils.isEmpty(host) || port == -1) { + Log_OC.d(TAG, "Proxy configuration cannot be found"); + return; + } + + try { + OwnCloudClientManagerFactory.setProxyHost(host); + OwnCloudClientManagerFactory.setProxyPort(port); + + Log_OC.d(TAG, "Proxy configuration successfully set"); + } catch (Resources.NotFoundException e) { + Log_OC.e(TAG, "Proxy config cannot able to set due to: $e"); + } + } + private void registerGlobalPassCodeProtection() { registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { diff --git a/app/src/main/java/com/owncloud/android/authentication/AccountAuthenticator.java b/app/src/main/java/com/owncloud/android/authentication/AccountAuthenticator.java index 591d3ecc74..6f3adf6a3a 100644 --- a/app/src/main/java/com/owncloud/android/authentication/AccountAuthenticator.java +++ b/app/src/main/java/com/owncloud/android/authentication/AccountAuthenticator.java @@ -21,6 +21,7 @@ import android.os.Bundle; import android.os.Handler; import android.widget.Toast; +import com.nextcloud.utils.mdm.MDMConfig; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.lib.common.accounts.AccountTypeUtils; @@ -70,7 +71,7 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator { final Bundle bundle = new Bundle(); - if (mContext.getResources().getBoolean(R.bool.multiaccount_support) || accounts.length < 1) { + if (accounts.length < 1 || MDMConfig.INSTANCE.multiAccountSupport(mContext)) { try { validateAccountType(accountType); } catch (AuthenticatorException e) { diff --git a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java index 158cfcd9e0..10f63668bc 100644 --- a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java +++ b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java @@ -21,7 +21,6 @@ import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.RestrictionsManager; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.content.pm.PackageManager; @@ -71,6 +70,7 @@ import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.common.PlainClient; import com.nextcloud.operations.PostMethod; import com.nextcloud.utils.extensions.BundleExtensionsKt; +import com.nextcloud.utils.mdm.MDMConfig; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.databinding.AccountSetupBinding; @@ -112,7 +112,6 @@ import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.ErrorMessageAdapter; import com.owncloud.android.utils.PermissionUtil; import com.owncloud.android.utils.WebViewUtil; -import com.owncloud.android.utils.appConfig.AppConfigManager; import com.owncloud.android.utils.theme.CapabilityUtils; import com.owncloud.android.utils.theme.ViewThemeUtils; @@ -320,11 +319,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity String webloginUrl = null; if (MainApp.isClientBrandedPlus()) { - RestrictionsManager restrictionsManager = (RestrictionsManager) getSystemService(Context.RESTRICTIONS_SERVICE); - AppConfigManager appConfigManager = new AppConfigManager(this, restrictionsManager.getApplicationRestrictions()); - - if (!TextUtils.isEmpty(appConfigManager.getBaseUrl(MainApp.isClientBrandedPlus()))) { - webloginUrl = appConfigManager.getBaseUrl(MainApp.isClientBrandedPlus()) + WEB_LOGIN; + String baseUrl = MDMConfig.INSTANCE.getBaseUrl(this); + if (!TextUtils.isEmpty(baseUrl)) { + webloginUrl = baseUrl + WEB_LOGIN; } } @@ -812,9 +809,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity passCodeManager.onActivityResumed(this); Uri data = intent.getData(); - if (data != null && data.toString().startsWith(getString(R.string.login_data_own_scheme))) { - if (!getResources().getBoolean(R.bool.multiaccount_support) && + if (!MDMConfig.INSTANCE.multiAccountSupport(this) && accountManager.getAccounts().length == 1) { Toast.makeText(this, R.string.no_mutliple_accounts_allowed, Toast.LENGTH_LONG).show(); finish(); @@ -1535,7 +1531,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity return; } - if (!getResources().getBoolean(R.bool.multiaccount_support) && + if (!MDMConfig.INSTANCE.multiAccountSupport(this) && accountManager.getAccounts().length == 1) { Toast.makeText(this, R.string.no_mutliple_accounts_allowed, Toast.LENGTH_LONG).show(); } else { diff --git a/app/src/main/java/com/owncloud/android/authentication/DeepLinkLoginActivity.kt b/app/src/main/java/com/owncloud/android/authentication/DeepLinkLoginActivity.kt index 25283adcbc..971fde8fc0 100644 --- a/app/src/main/java/com/owncloud/android/authentication/DeepLinkLoginActivity.kt +++ b/app/src/main/java/com/owncloud/android/authentication/DeepLinkLoginActivity.kt @@ -11,16 +11,14 @@ import android.os.Bundle import android.widget.TextView import android.widget.Toast import com.nextcloud.client.di.Injectable +import com.nextcloud.utils.mdm.MDMConfig import com.owncloud.android.R class DeepLinkLoginActivity : AuthenticatorActivity(), Injectable { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - - if (!resources.getBoolean(R.bool.multiaccount_support) && - accountManager.accounts.size == 1 - ) { + if (!MDMConfig.multiAccountSupport(this) && accountManager.accounts.size == 1) { Toast.makeText(this, R.string.no_mutliple_accounts_allowed, Toast.LENGTH_LONG).show() return } diff --git a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index f2f0666a73..8df5366f51 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -175,6 +175,10 @@ public class FileDataStorageManager { } } + public OfflineOperationEntity getOfflineEntityFromOCFile(OCFile file) { + return offlineOperationDao.getByPath(file.getDecryptedRemotePath()); + } + public OfflineOperationEntity addCreateFolderOfflineOperation(String path, String filename, Long parentOCFileId) { OfflineOperationEntity entity = new OfflineOperationEntity(); @@ -246,6 +250,25 @@ public class FileDataStorageManager { return filename; } + public void addRemoveFileOfflineOperation(String path, String filename, Long parentOCFileId) { + OfflineOperationEntity entity = new OfflineOperationEntity(); + + entity.setFilename(filename); + entity.setParentOCFileId(parentOCFileId); + + OfflineOperationType.RemoveFile operationType = new OfflineOperationType.RemoveFile(OfflineOperationRawType.RemoveFile.name(), path); + entity.setType(operationType); + entity.setPath(path); + + long createdAt = System.currentTimeMillis(); + long modificationTimestamp = System.currentTimeMillis(); + + entity.setCreatedAt(createdAt); + entity.setModifiedAt(modificationTimestamp / 1000); + + offlineOperationDao.insert(entity); + } + public void renameOfflineOperation(OCFile file, String newFolderName) { var entity = offlineOperationDao.getByPath(file.getDecryptedRemotePath()); if (entity == null) { diff --git a/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java b/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java index 3170f934cd..2bb9e99e4c 100644 --- a/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java +++ b/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java @@ -23,6 +23,7 @@ import com.nextcloud.client.editimage.EditImageActivity; import com.nextcloud.client.jobs.download.FileDownloadHelper; import com.nextcloud.client.jobs.upload.FileUploadHelper; import com.nextcloud.utils.EditorUtils; +import com.nextcloud.utils.mdm.MDMConfig; import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; @@ -184,9 +185,24 @@ public class FileMenuFilter { } private void filterSendFiles(List toHide, boolean inSingleFileFragment) { - if ((overflowMenu || SEND_OFF.equalsIgnoreCase(context.getString(R.string.send_files_to_other_apps)) || containsEncryptedFile()) || - (!inSingleFileFragment && (isSingleSelection() || !allFileDown())) || - !toHide.contains(R.id.action_send_share_file)) { + boolean sendFilesNotSupported = context != null && !MDMConfig.INSTANCE.sendFilesSupport(context); + boolean hasEncryptedFile = containsEncryptedFile(); + boolean isSingleSelection = isSingleSelection(); + boolean allFilesNotDown = !allFileDown(); + + if (sendFilesNotSupported) { + toHide.add(R.id.action_send_file); + return; + } + + if (overflowMenu || hasEncryptedFile) { + toHide.add(R.id.action_send_file); + return; + } + + if (!inSingleFileFragment && (isSingleSelection || allFilesNotDown)) { + toHide.add(R.id.action_send_file); + } else if (!toHide.contains(R.id.action_send_share_file)) { toHide.add(R.id.action_send_file); } } @@ -425,13 +441,11 @@ public class FileMenuFilter { } private boolean isShareWithUsersAllowed() { - return context != null && - context.getResources().getBoolean(R.bool.share_with_users_feature); + return context != null && MDMConfig.INSTANCE.shareViaUser(context); } private boolean isShareViaLinkAllowed() { - return context != null && - context.getResources().getBoolean(R.bool.share_via_link_feature); + return context != null && MDMConfig.INSTANCE.shareViaLink(context); } private boolean isSingleSelection() { diff --git a/app/src/main/java/com/owncloud/android/operations/CopyFileOperation.java b/app/src/main/java/com/owncloud/android/operations/CopyFileOperation.java index b052b03465..bd7d21a6db 100644 --- a/app/src/main/java/com/owncloud/android/operations/CopyFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/CopyFileOperation.java @@ -64,6 +64,19 @@ public class CopyFileOperation extends SyncOperation { if (file.isFolder()) { targetPath += OCFile.PATH_SEPARATOR; } + + // auto rename, to allow copy + if (targetPath.equals(srcPath)) { + if (file.isFolder()) { + targetPath = targetParentPath + file.getFileName(); + } + targetPath = UploadFileOperation.getNewAvailableRemotePath(client, targetPath, null, false); + + if (file.isFolder()) { + targetPath += OCFile.PATH_SEPARATOR; + } + } + RemoteOperationResult result = new CopyFileRemoteOperation(srcPath, targetPath, false).execute(client); /// 3. local copy diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 0771bae6b7..2ff9e9e1a3 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -1342,7 +1342,7 @@ public class UploadFileOperation extends SyncOperation { * @param fileNames list of decrypted file names * @return new remote path */ - private String getNewAvailableRemotePath(OwnCloudClient client, + public static String getNewAvailableRemotePath(OwnCloudClient client, String remotePath, List fileNames, boolean encrypted) { @@ -1368,7 +1368,7 @@ public class UploadFileOperation extends SyncOperation { return newPath; } - private boolean existsFile(OwnCloudClient client, + private static boolean existsFile(OwnCloudClient client, String remotePath, List fileNames, boolean encrypted) { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ChooseStorageLocationActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/ChooseStorageLocationActivity.kt new file mode 100644 index 0000000000..8d13d53e7d --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/ChooseStorageLocationActivity.kt @@ -0,0 +1,38 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 ZetaTom <70907959+ZetaTom@users.noreply.github.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.ui.activity + +import android.content.Intent +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.nextcloud.ui.ChooseStorageLocationDialogFragment + +class ChooseStorageLocationActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val chooseStorageLocationDialogFragment = ChooseStorageLocationDialogFragment.newInstance() + supportFragmentManager.setFragmentResultListener( + KEY_RESULT_STORAGE_LOCATION, + this + ) { _, result -> + setResult( + ChooseStorageLocationDialogFragment.STORAGE_LOCATION_RESULT_CODE, + Intent().putExtra( + KEY_RESULT_STORAGE_LOCATION, + result.getString(KEY_RESULT_STORAGE_LOCATION) + ) + ) + } + chooseStorageLocationDialogFragment.show(supportFragmentManager, "choose_storage_location") + } + + companion object { + const val KEY_RESULT_STORAGE_LOCATION = ChooseStorageLocationDialogFragment.KEY_RESULT_STORAGE_LOCATION + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 9d6e8704c3..98658dbf06 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -59,6 +59,7 @@ import com.nextcloud.common.NextcloudClient; import com.nextcloud.ui.ChooseAccountDialogFragment; import com.nextcloud.ui.composeActivity.ComposeActivity; import com.nextcloud.ui.composeActivity.ComposeDestination; +import com.nextcloud.utils.mdm.MDMConfig; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.PassCodeManager; @@ -602,10 +603,7 @@ public abstract class DrawerActivity extends ToolbarActivity } public void openAddAccount() { - boolean isProviderOrOwnInstallationVisible = getResources() - .getBoolean(R.bool.show_provider_or_own_installation); - - if (isProviderOrOwnInstallationVisible) { + if (MDMConfig.INSTANCE.showIntro(this)) { Intent firstRunIntent = new Intent(getApplicationContext(), FirstRunActivity.class); firstRunIntent.putExtra(FirstRunActivity.EXTRA_ALLOW_CLOSE, true); startActivity(firstRunIntent); @@ -824,7 +822,7 @@ public abstract class DrawerActivity extends ToolbarActivity private void updateQuotaLink() { if (mQuotaTextLink != null) { - if (getBaseContext().getResources().getBoolean(R.bool.show_external_links)) { + if (MDMConfig.INSTANCE.externalSiteSupport(this)) { List quotas = externalLinksProvider.getExternalLink(ExternalLinkType.QUOTA); float density = getResources().getDisplayMetrics().density; @@ -973,7 +971,7 @@ public abstract class DrawerActivity extends ToolbarActivity } private void updateExternalLinksInDrawer() { - if (mNavigationView != null && getBaseContext().getResources().getBoolean(R.bool.show_external_links)) { + if (mNavigationView != null && MDMConfig.INSTANCE.externalSiteSupport(this)) { mNavigationView.getMenu().removeGroup(R.id.drawer_menu_external_links); int greyColor = ContextCompat.getColor(this, R.color.drawer_menu_icon); @@ -1222,7 +1220,7 @@ public abstract class DrawerActivity extends ToolbarActivity * Retrieves external links via api from 'external' app */ public void fetchExternalLinks(final boolean force) { - if (!getBaseContext().getResources().getBoolean(R.bool.show_external_links)) { + if (!MDMConfig.INSTANCE.externalSiteSupport(this)) { return; } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java index 95b726f2f6..8f3faef621 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java @@ -45,6 +45,7 @@ import com.nextcloud.utils.extensions.ActivityExtensionsKt; import com.nextcloud.utils.extensions.BundleExtensionsKt; import com.nextcloud.utils.extensions.FileExtensionsKt; import com.nextcloud.utils.extensions.IntentExtensionsKt; +import com.nextcloud.utils.mdm.MDMConfig; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.AuthenticatorActivity; @@ -718,12 +719,14 @@ public abstract class FileActivity extends DrawerActivity OCFile file, String link, final ViewThemeUtils viewThemeUtils) { - ClipboardUtil.copyToClipboard(activity, link, false); - Snackbar snackbar = Snackbar.make(activity.findViewById(android.R.id.content), R.string.clipboard_text_copied, - Snackbar.LENGTH_LONG) - .setAction(R.string.share, v -> showShareLinkDialog(activity, file, link)); - viewThemeUtils.material.themeSnackbar(snackbar); - snackbar.show(); + if (MDMConfig.INSTANCE.shareViaLink(activity) && MDMConfig.INSTANCE.clipBoardSupport(activity)) { + ClipboardUtil.copyToClipboard(activity, link, false); + Snackbar snackbar = Snackbar.make(activity.findViewById(android.R.id.content), R.string.clipboard_text_copied, + Snackbar.LENGTH_LONG) + .setAction(R.string.share, v -> showShareLinkDialog(activity, file, link)); + viewThemeUtils.material.themeSnackbar(snackbar); + snackbar.show(); + } } public static void showShareLinkDialog(FileActivity activity, ServerFileInterface file, String link) { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt index e3cab8ab92..30dc48141c 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt @@ -403,15 +403,16 @@ open class FolderPickerActivity : private fun checkButtonStates(isConditionMet: Boolean) { folderPickerBinding.run { folderPickerBtnChoose.isEnabled = isConditionMet - folderPickerBtnCopy.isEnabled = isFolderSelectable() && isConditionMet - folderPickerBtnMove.isEnabled = isFolderSelectable() && isConditionMet + folderPickerBtnCopy.isEnabled = isFolderSelectable(COPY) && isConditionMet + folderPickerBtnMove.isEnabled = isFolderSelectable(MOVE) && isConditionMet } } // for copy and move, disable selecting parent folder of target files - private fun isFolderSelectable(): Boolean { + private fun isFolderSelectable(type: String): Boolean { return when { action != MOVE_OR_COPY -> true + action == MOVE_OR_COPY && type == COPY -> true targetFilePaths.isNullOrEmpty() -> true file?.isFolder != true -> true @@ -688,6 +689,8 @@ open class FolderPickerActivity : const val MOVE_OR_COPY = "MOVE_OR_COPY" const val CHOOSE_LOCATION = "CHOOSE_LOCATION" private val TAG = FolderPickerActivity::class.java.simpleName + private const val MOVE = "MOVE" + private const val COPY = "COPY" const val TAG_LIST_OF_FOLDERS = "LIST_OF_FOLDERS" } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java index 4ff007d2bc..579173fffd 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java @@ -30,6 +30,7 @@ import com.nextcloud.client.onboarding.FirstRunActivity; import com.nextcloud.model.WorkerState; import com.nextcloud.model.WorkerStateLiveData; import com.nextcloud.utils.extensions.BundleExtensionsKt; +import com.nextcloud.utils.mdm.MDMConfig; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.AuthenticatorActivity; @@ -131,8 +132,7 @@ public class ManageAccountsActivity extends FileActivity implements UserListAdap } arbitraryDataProvider = new ArbitraryDataProviderImpl(this); - - multipleAccountsSupported = getResources().getBoolean(R.bool.multiaccount_support); + multipleAccountsSupported = MDMConfig.INSTANCE.multiAccountSupport(this); userListAdapter = new UserListAdapter(this, accountManager, @@ -230,7 +230,7 @@ public class ManageAccountsActivity extends FileActivity implements UserListAdap userListItems.add(new UserListItem(user, !pendingForRemoval)); } - if (getResources().getBoolean(R.bool.multiaccount_support)) { + if (MDMConfig.INSTANCE.multiAccountSupport(this)) { userListItems.add(new UserListItem()); } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java index b17f814b57..77d2675752 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java @@ -50,15 +50,13 @@ import com.nextcloud.client.network.ConnectivityService; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.client.preferences.AppPreferencesImpl; import com.nextcloud.client.preferences.DarkMode; -import com.owncloud.android.BuildConfig; +import com.nextcloud.utils.mdm.MDMConfig; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.AuthenticatorActivity; import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.ArbitraryDataProviderImpl; import com.owncloud.android.datamodel.ExternalLinksProvider; -import com.owncloud.android.datastorage.DataStorageProvider; -import com.owncloud.android.datastorage.StoragePoint; import com.owncloud.android.lib.common.ExternalLink; import com.owncloud.android.lib.common.ExternalLinkType; import com.owncloud.android.lib.common.utils.Log_OC; @@ -115,6 +113,7 @@ public class SettingsActivity extends PreferenceActivity private static final int ACTION_REQUEST_CODE_DAVDROID_SETUP = 10; private static final int ACTION_SHOW_MNEMONIC = 11; private static final int ACTION_E2E = 12; + private static final int ACTION_SET_STORAGE_LOCATION = 13; private static final int TRUE_VALUE = 1; private static final String DAV_PATH = "/remote.php/dav"; @@ -128,7 +127,7 @@ public class SettingsActivity extends PreferenceActivity private ThemeableSwitchPreference showEcosystemApps; private AppCompatDelegate delegate; - private ListPreference prefStoragePath; + private Preference prefDataLoc; private String storagePath; private String pendingLock; @@ -379,11 +378,9 @@ public class SettingsActivity extends PreferenceActivity } private void setupLoggingPreference(PreferenceCategory preferenceCategoryMore) { - - boolean loggerEnabled = getResources().getBoolean(R.bool.logger_enabled) || BuildConfig.DEBUG; Preference pLogger = findPreference("logger"); if (pLogger != null) { - if (loggerEnabled) { + if (MDMConfig.INSTANCE.isLogEnabled(this)) { pLogger.setOnPreferenceClickListener(preference -> { Intent loggerIntent = new Intent(getApplicationContext(), LogsActivity.class); startActivity(loggerIntent); @@ -802,34 +799,16 @@ public class SettingsActivity extends PreferenceActivity final PreferenceCategory preferenceCategoryGeneral = (PreferenceCategory) findPreference("general"); viewThemeUtils.files.themePreferenceCategory(preferenceCategoryGeneral); - prefStoragePath = (ListPreference) findPreference(AppPreferencesImpl.STORAGE_PATH); - if (prefStoragePath != null) { - StoragePoint[] storageOptions = DataStorageProvider.getInstance().getAvailableStoragePoints(); - String[] entries = new String[storageOptions.length]; - String[] values = new String[storageOptions.length]; - for (int i = 0; i < storageOptions.length; ++i) { - entries[i] = storageOptions[i].getDescription(); - values[i] = storageOptions[i].getPath(); - } - prefStoragePath.setEntries(entries); - prefStoragePath.setEntryValues(values); - - prefStoragePath.setOnPreferenceChangeListener((preference, newValue) -> { - String newPath = (String) newValue; - - if (storagePath.equals(newPath)) { - return true; - } - StorageMigration storageMigration = new StorageMigration(this, user, storagePath, newPath, viewThemeUtils); - storageMigration.setStorageMigrationProgressListener(this); - storageMigration.migrate(); - - return false; + prefDataLoc = findPreference(AppPreferencesImpl.DATA_STORAGE_LOCATION); + if (prefDataLoc != null) { + prefDataLoc.setOnPreferenceClickListener(p -> { + Intent intent = new Intent(MainApp.getAppContext(), ChooseStorageLocationActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + startActivityForResult(intent, ACTION_SET_STORAGE_LOCATION); + return true; }); } - loadStoragePath(); - ListPreference themePref = (ListPreference) findPreference("darkMode"); List themeEntries = new ArrayList<>(3); @@ -996,6 +975,14 @@ public class SettingsActivity extends PreferenceActivity i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); i.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); startActivity(i); + } else if (requestCode == ACTION_SET_STORAGE_LOCATION && data != null) { + String newPath = data.getStringExtra(ChooseStorageLocationActivity.KEY_RESULT_STORAGE_LOCATION); + + if (!storagePath.equals(newPath)) { + StorageMigration storageMigration = new StorageMigration(this, user, storagePath, newPath, viewThemeUtils); + storageMigration.setStorageMigrationProgressListener(this); + storageMigration.migrate(); + } } } @@ -1101,7 +1088,7 @@ public class SettingsActivity extends PreferenceActivity } private void loadExternalSettingLinks(PreferenceCategory preferenceCategory) { - if (getBaseContext().getResources().getBoolean(R.bool.show_external_links)) { + if (MDMConfig.INSTANCE.externalSiteSupport(this)) { ExternalLinksProvider externalLinksProvider = new ExternalLinksProvider(getContentResolver()); for (final ExternalLink link : externalLinksProvider.getExternalLink(ExternalLinkType.SETTINGS)) { @@ -1139,21 +1126,6 @@ public class SettingsActivity extends PreferenceActivity SharedPreferences.Editor editor = appPrefs.edit(); editor.putString(AppPreferencesImpl.STORAGE_PATH, storagePath); editor.apply(); - String storageDescription = DataStorageProvider.getInstance().getStorageDescriptionByPath(storagePath); - prefStoragePath.setSummary(storageDescription); - prefStoragePath.setValue(newStoragePath); - } - - /** - * Load storage path set on preferences - */ - private void loadStoragePath() { - SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); - // Load storage path from shared preferences. Use private internal storage by default. - storagePath = appPrefs.getString(AppPreferencesImpl.STORAGE_PATH, - getApplicationContext().getFilesDir().getAbsolutePath()); - String storageDescription = DataStorageProvider.getInstance().getStorageDescriptionByPath(storagePath); - prefStoragePath.setSummary(storageDescription); } @Override diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java index f6666905d8..f35d5ce770 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java @@ -19,6 +19,7 @@ import android.graphics.PorterDuff; import android.text.TextUtils; import android.view.View; +import com.nextcloud.utils.mdm.MDMConfig; import com.owncloud.android.R; import com.owncloud.android.databinding.FileDetailsShareLinkShareItemBinding; import com.owncloud.android.lib.resources.shares.OCShare; @@ -78,11 +79,16 @@ class LinkShareViewHolder extends RecyclerView.ViewHolder { String permissionName = SharingMenuHelper.getPermissionName(context, publicShare); setPermissionName(publicShare, permissionName); - binding.copyLink.setOnClickListener(v -> listener.copyLink(publicShare)); binding.overflowMenu.setOnClickListener(v -> listener.showSharingMenuActionSheet(publicShare)); if (!SharingMenuHelper.isSecureFileDrop(publicShare)) { binding.shareByLinkContainer.setOnClickListener(v -> listener.showPermissionsDialog(publicShare)); } + + if (MDMConfig.INSTANCE.clipBoardSupport(context)) { + binding.copyLink.setOnClickListener(v -> listener.copyLink(publicShare)); + } else { + binding.copyLink.setVisibility(View.GONE); + } } private void setPermissionName(OCShare publicShare, String permissionName) { diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java index 6c290c5548..8725dd6ac5 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java @@ -36,9 +36,10 @@ import com.nextcloud.client.account.User; import com.nextcloud.client.database.entity.OfflineOperationEntity; import com.nextcloud.client.jobs.upload.FileUploadHelper; import com.nextcloud.client.preferences.AppPreferences; -import com.nextcloud.model.OfflineOperationType; import com.nextcloud.model.OCFileFilterType; +import com.nextcloud.model.OfflineOperationType; import com.nextcloud.utils.extensions.ViewExtensionsKt; +import com.nextcloud.utils.mdm.MDMConfig; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.databinding.GridImageBinding; @@ -458,6 +459,10 @@ public class OCFileListAdapter extends RecyclerView.Adapter= 0) { - holder.getFileSize().setVisibility(View.VISIBLE); - - if (file.isOfflineOperation()) { - holder.getFileSize().setText(MainApp.string(R.string.oc_file_list_adapter_offline_operation_description_text)); - } else { - holder.getFileSize().setText(DisplayUtils.bytesToHumanReadable(fileLength)); - } - - holder.getFileSizeSeparator().setVisibility(View.VISIBLE); + prepareFileSize(holder, file, fileLength); } else { holder.getFileSize().setVisibility(View.GONE); holder.getFileSizeSeparator().setVisibility(View.GONE); @@ -666,6 +654,28 @@ public class OCFileListAdapter extends RecyclerView.Adapter { - return new LinkShareViewHolder( - FileDetailsShareLinkShareItemBinding.inflate(LayoutInflater.from(fileActivity), - parent, - false), - fileActivity, - viewThemeUtils); - } - case NEW_PUBLIC_LINK -> { - if (encrypted) { - return new NewSecureFileDropViewHolder( - FileDetailsShareSecureFileDropAddNewItemBinding.inflate(LayoutInflater.from(fileActivity), + boolean shareViaLink = MDMConfig.INSTANCE.shareViaLink(fileActivity); + + if (shareViaLink) { + switch (ShareType.fromValue(viewType)) { + case PUBLIC_LINK, EMAIL -> { + return new LinkShareViewHolder( + FileDetailsShareLinkShareItemBinding.inflate(LayoutInflater.from(fileActivity), + parent, + false), + fileActivity, + viewThemeUtils); + } + case NEW_PUBLIC_LINK -> { + if (encrypted) { + return new NewSecureFileDropViewHolder( + FileDetailsShareSecureFileDropAddNewItemBinding.inflate(LayoutInflater.from(fileActivity), + parent, + false) + ); + } else { + return new NewLinkShareViewHolder( + FileDetailsSharePublicLinkAddNewItemBinding.inflate(LayoutInflater.from(fileActivity), parent, false) - ); - } else { - return new NewLinkShareViewHolder( - FileDetailsSharePublicLinkAddNewItemBinding.inflate(LayoutInflater.from(fileActivity), - parent, - false) - ); + ); + } + } + case INTERNAL -> { + return new InternalShareViewHolder( + FileDetailsShareInternalShareLinkBinding.inflate(LayoutInflater.from(fileActivity), parent, false), + fileActivity); + } + default -> { + return new ShareViewHolder(FileDetailsShareShareItemBinding.inflate(LayoutInflater.from(fileActivity), + parent, + false), + user, + fileActivity, + viewThemeUtils); } } - case INTERNAL -> { - return new InternalShareViewHolder( - FileDetailsShareInternalShareLinkBinding.inflate(LayoutInflater.from(fileActivity), parent, false), - fileActivity); - } - default -> { - return new ShareViewHolder(FileDetailsShareShareItemBinding.inflate(LayoutInflater.from(fileActivity), - parent, - false), - user, - fileActivity, - viewThemeUtils); - } + } else { + return new InternalShareViewHolder( + FileDetailsShareInternalShareLinkBinding.inflate(LayoutInflater.from(fileActivity), parent, false), + fileActivity); } } @@ -127,6 +137,16 @@ public class ShareeListAdapter extends RecyclerView.Adapter accountListAdapterListener.showFirstRunActivity()); } else { itemView.setOnClickListener(v -> accountListAdapterListener.startAccountCreation()); diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/MultipleAccountsDialog.kt b/app/src/main/java/com/owncloud/android/ui/dialog/MultipleAccountsDialog.kt index 0bb2043c4b..5b95a263a3 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/MultipleAccountsDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/MultipleAccountsDialog.kt @@ -65,7 +65,7 @@ class MultipleAccountsDialog : DialogFragment(), Injectable, UserListAdapter.Cli private val accountListItems: List /** * creates the account list items list including the add-account action in case - * multiaccount_support is enabled. + * multi account support is enabled. * * @return list of account list items */ diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/RemoveFilesDialogFragment.kt b/app/src/main/java/com/owncloud/android/ui/dialog/RemoveFilesDialogFragment.kt index 801f6bc173..c9cb0d25c1 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/RemoveFilesDialogFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/RemoveFilesDialogFragment.kt @@ -90,17 +90,33 @@ class RemoveFilesDialogFragment : ConfirmationDialogFragment(), ConfirmationDial fileDataStorageManager.deleteOfflineOperation(it) } - if (files.isNotEmpty()) { - val cg = activity as ComponentsGetter? - cg?.fileOperationsHelper?.removeFiles(files, onlyLocalCopy, false) - } + if (requireActivity() is FileDisplayActivity) { + val activity = requireActivity() as FileDisplayActivity + activity.connectivityService.isNetworkAndServerAvailable { result -> + if (result) { + if (files.isNotEmpty()) { + val cg = activity as ComponentsGetter? + cg?.fileOperationsHelper?.removeFiles(files, onlyLocalCopy, false) + } - if (offlineFiles.isNotEmpty()) { - val activity = requireActivity() as? FileDisplayActivity - activity?.refreshCurrentDirectory() - } + if (offlineFiles.isNotEmpty()) { + activity.refreshCurrentDirectory() + } + } else { + files.forEach { file -> + fileDataStorageManager.addRemoveFileOfflineOperation( + file.decryptedRemotePath, + file.fileName, + file.parentId + ) + } - finishActionMode() + activity.refreshCurrentDirectory() + } + + finishActionMode() + } + } } override fun onNeutral(callerTag: String?) { diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.kt b/app/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.kt index 9be56aff7e..c45d2f854f 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.kt @@ -27,6 +27,7 @@ import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.client.di.Injectable import com.nextcloud.client.utils.IntentUtil.createSendIntent import com.nextcloud.utils.extensions.getParcelableArgument +import com.nextcloud.utils.mdm.MDMConfig import com.owncloud.android.BuildConfig import com.owncloud.android.R import com.owncloud.android.databinding.SendShareFragmentBinding @@ -73,7 +74,12 @@ class SendShareDialog : BottomSheetDialogFragment(R.layout.send_share_fragment), binding = SendShareFragmentBinding.inflate(inflater, container, false) binding.btnShare.setOnClickListener { shareFile(file) } - binding.btnLink.setOnClickListener { shareByLink() } + + if (MDMConfig.shareViaLink(requireContext()) && MDMConfig.clipBoardSupport(requireContext())) { + binding.btnLink.setOnClickListener { shareByLink() } + } else { + binding.btnLink.visibility = View.GONE + } applyTintColor() setupBottomSheetBehaviour() @@ -85,6 +91,10 @@ class SendShareDialog : BottomSheetDialogFragment(R.layout.send_share_fragment), @Suppress("MagicNumber") private fun setupSendButtonRecyclerView() { + if (!MDMConfig.sendFilesSupport(requireContext())) { + return + } + val sendIntent = createSendIntent(requireContext(), file!!) val sendButtonDataList = setupSendButtonData(sendIntent) val clickListener = setupSendButtonClickListener(sendIntent) diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index 2dc7fe4b10..b36cf8f2cd 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -36,6 +36,7 @@ import com.nextcloud.ui.fileactions.FileActionsBottomSheet; import com.nextcloud.utils.MenuUtils; import com.nextcloud.utils.extensions.BundleExtensionsKt; import com.nextcloud.utils.extensions.FileExtensionsKt; +import com.nextcloud.utils.mdm.MDMConfig; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.databinding.FileDetailsFragmentBinding; @@ -841,6 +842,10 @@ public class FileDetailFragment extends FileFragment implements OnClickListener, } private boolean showSharingTab() { + if (!MDMConfig.INSTANCE.shareViaLink(requireContext()) && !MDMConfig.INSTANCE.shareViaUser(requireContext())) { + return false; + } + if (getFile().isEncrypted()) { if (parentFolder == null) { parentFolder = storageManager.getFileById(getFile().getParentId()); diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 0cdeb930c7..5854953c8d 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -37,6 +37,7 @@ import com.nextcloud.client.di.Injectable; import com.nextcloud.client.network.ClientFactory; import com.nextcloud.utils.extensions.BundleExtensionsKt; import com.nextcloud.utils.extensions.FileExtensionsKt; +import com.nextcloud.utils.mdm.MDMConfig; import com.owncloud.android.R; import com.owncloud.android.databinding.FileDetailsSharingFragmentBinding; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -244,6 +245,14 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda binding.pickContactEmailBtn.setVisibility(View.GONE); disableSearchView(binding.searchView); } + + checkShareViaUser(); + } + + private void checkShareViaUser() { + if (!MDMConfig.INSTANCE.shareViaUser(requireContext())) { + binding.searchContainer.setVisibility(View.GONE); + } } private void disableSearchView(View view) { @@ -494,9 +503,13 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda } private void pickContactEmail() { - Intent intent = new Intent(Intent.ACTION_PICK); - intent.setDataAndType(ContactsContract.Contacts.CONTENT_URI, ContactsContract.CommonDataKinds.Email.CONTENT_TYPE); - onContactSelectionResultLauncher.launch(intent); + Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.CommonDataKinds.Email.CONTENT_URI); + + if (intent.resolveActivity(requireContext().getPackageManager()) != null) { + onContactSelectionResultLauncher.launch(intent); + } else { + DisplayUtils.showSnackMessage(requireActivity(), getString(R.string.file_detail_sharing_fragment_no_contact_app_message)); + } } private void handleContactResult(@NonNull Uri contactUri) { diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingMenuBottomSheetDialog.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingMenuBottomSheetDialog.java index bf2c1ae72d..8a9d373d8a 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingMenuBottomSheetDialog.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingMenuBottomSheetDialog.java @@ -16,6 +16,7 @@ import android.view.ViewGroup; import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.bottomsheet.BottomSheetDialog; +import com.nextcloud.utils.mdm.MDMConfig; import com.owncloud.android.databinding.FileDetailsSharingMenuBottomSheetFragmentBinding; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.shares.ShareType; @@ -72,7 +73,10 @@ public class FileDetailSharingMenuBottomSheetDialog extends BottomSheetDialog { private void updateUI() { if (ocShare.getShareType() == ShareType.PUBLIC_LINK) { binding.menuShareAddAnotherLink.setVisibility(View.VISIBLE); - binding.menuShareSendLink.setVisibility(View.VISIBLE); + + if (MDMConfig.INSTANCE.sendFilesSupport(getContext())) { + binding.menuShareSendLink.setVisibility(View.VISIBLE); + } } else { binding.menuShareAddAnotherLink.setVisibility(View.GONE); binding.menuShareSendLink.setVisibility(View.GONE); diff --git a/app/src/main/java/com/owncloud/android/utils/BitmapUtils.java b/app/src/main/java/com/owncloud/android/utils/BitmapUtils.java index 70c141f6ea..e899c52e9d 100644 --- a/app/src/main/java/com/owncloud/android/utils/BitmapUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/BitmapUtils.java @@ -60,13 +60,9 @@ public final class BitmapUtils { } public static Bitmap addColorFilter(Bitmap originalBitmap, int filterColor, int opacity) { - int width = originalBitmap.getWidth(); - int height = originalBitmap.getHeight(); - - Bitmap resultBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Bitmap resultBitmap = originalBitmap.copy(Bitmap.Config.ARGB_8888, true); Canvas canvas = new Canvas(resultBitmap); - - canvas.drawBitmap(originalBitmap, 0, 0, null); + canvas.drawBitmap(resultBitmap, 0, 0, null); Paint paint = new Paint(); paint.setColor(filterColor); @@ -74,7 +70,7 @@ public final class BitmapUtils { paint.setAlpha(opacity); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP)); - canvas.drawRect(0, 0, width, height, paint); + canvas.drawRect(0, 0, resultBitmap.getWidth(), resultBitmap.getHeight(), paint); return resultBitmap; } diff --git a/app/src/main/java/com/owncloud/android/utils/ClipboardUtil.kt b/app/src/main/java/com/owncloud/android/utils/ClipboardUtil.kt index da2f4b65d7..d10e31329a 100644 --- a/app/src/main/java/com/owncloud/android/utils/ClipboardUtil.kt +++ b/app/src/main/java/com/owncloud/android/utils/ClipboardUtil.kt @@ -12,6 +12,7 @@ import android.content.ClipboardManager import android.content.Context import android.text.TextUtils import android.widget.Toast +import com.nextcloud.utils.mdm.MDMConfig import com.owncloud.android.R import com.owncloud.android.lib.common.utils.Log_OC @@ -25,6 +26,10 @@ object ClipboardUtil { @JvmOverloads @Suppress("TooGenericExceptionCaught") fun copyToClipboard(activity: Activity, text: String?, showToast: Boolean = true) { + if (!MDMConfig.clipBoardSupport(activity)) { + return + } + if (!TextUtils.isEmpty(text)) { try { val clip = ClipData.newPlainText( diff --git a/app/src/main/java/com/owncloud/android/utils/appConfig/AppConfigKeys.kt b/app/src/main/java/com/owncloud/android/utils/appConfig/AppConfigKeys.kt index 5df2a65a05..30a86e413a 100644 --- a/app/src/main/java/com/owncloud/android/utils/appConfig/AppConfigKeys.kt +++ b/app/src/main/java/com/owncloud/android/utils/appConfig/AppConfigKeys.kt @@ -13,5 +13,11 @@ package com.owncloud.android.utils.appConfig enum class AppConfigKeys(val key: String) { BaseUrl("base_url"), ProxyHost("proxy_host"), - ProxyPort("proxy_port") + ProxyPort("proxy_port"), + DisableMultiAccount("disable_multiaccount"), + DisableSharing("disable_sharing"), + DisableClipboard("disable_clipboard"), + DisableMoreExternalSite("disable_more_external_site"), + DisableIntro("disable_intro"), + DisableLog("disable_log") } diff --git a/app/src/main/java/com/owncloud/android/utils/appConfig/AppConfigManager.kt b/app/src/main/java/com/owncloud/android/utils/appConfig/AppConfigManager.kt deleted file mode 100644 index a390a6aee1..0000000000 --- a/app/src/main/java/com/owncloud/android/utils/appConfig/AppConfigManager.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2024 Alper Ozturk - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -package com.owncloud.android.utils.appConfig - -import android.content.Context -import android.content.res.Resources -import android.os.Bundle -import android.text.TextUtils -import com.owncloud.android.R -import com.owncloud.android.lib.common.OwnCloudClientManagerFactory -import com.owncloud.android.lib.common.utils.Log_OC - -class AppConfigManager(private val context: Context, private val appRestrictions: Bundle) { - - private val tag = "AppConfigManager" - - fun setProxyConfig(isBrandedPlus: Boolean) { - if (!isBrandedPlus) { - Log_OC.d(tag, "Proxy configuration cannot be set. Client is not branded plus.") - return - } - - val host = if (appRestrictions.containsKey(AppConfigKeys.ProxyHost.key)) { - appRestrictions.getString(AppConfigKeys.ProxyHost.key) - } else { - context.getString(R.string.proxy_host) - } - - val port = if (appRestrictions.containsKey(AppConfigKeys.ProxyPort.key)) { - appRestrictions.getInt(AppConfigKeys.ProxyPort.key) - } else { - context.resources.getInteger(R.integer.proxy_port) - } - - if (TextUtils.isEmpty(host) || port == -1) { - Log_OC.d(tag, "Proxy configuration cannot be found") - return - } - - try { - OwnCloudClientManagerFactory.setProxyHost(host) - OwnCloudClientManagerFactory.setProxyPort(port) - - Log_OC.d(tag, "Proxy configuration successfully set") - } catch (e: Resources.NotFoundException) { - Log_OC.e(tag, "Proxy config cannot able to set due to: $e") - } - } - - fun getBaseUrl(isBrandedPlus: Boolean): String? { - if (!isBrandedPlus) { - Log_OC.d(tag, "Proxy configuration cannot be set. Client is not branded plus. Default url applied") - return null - } - - return if (appRestrictions.containsKey(AppConfigKeys.BaseUrl.key)) { - appRestrictions.getString(AppConfigKeys.BaseUrl.key) - } else { - Log_OC.d(tag, "BaseUrl configuration cannot be found, default url applied") - null - } - } -} diff --git a/app/src/main/res/layout/dialog_data_storage_location.xml b/app/src/main/res/layout/dialog_data_storage_location.xml new file mode 100644 index 0000000000..9c1e447932 --- /dev/null +++ b/app/src/main/res/layout/dialog_data_storage_location.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 9142859ec4..89187a3c37 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -693,7 +693,7 @@ مقترحات بتطبيقات من نكست كلاود في شريط التصفح navigation heading عرض الملفات المخفية الحصول على الشفرة المصدرية - مجلد تخزين البيانات + مجلد تخزين البيانات إدارة الملفات للتحميل التلقائي مجلد محلي مجلد عن بعد diff --git a/app/src/main/res/values-b+en+001/strings.xml b/app/src/main/res/values-b+en+001/strings.xml index dfdd5f8f6e..08c1cabbba 100644 --- a/app/src/main/res/values-b+en+001/strings.xml +++ b/app/src/main/res/values-b+en+001/strings.xml @@ -687,7 +687,7 @@ Nextcloud app suggestions in navigation heading Show hidden files Get source code - Data storage folder + Data storage folder Manage folders for auto upload Local folder Remote folder diff --git a/app/src/main/res/values-bg-rBG/strings.xml b/app/src/main/res/values-bg-rBG/strings.xml index 696d0c7263..b2637aa37d 100644 --- a/app/src/main/res/values-bg-rBG/strings.xml +++ b/app/src/main/res/values-bg-rBG/strings.xml @@ -581,7 +581,7 @@ Настройка на цялостно криптиране Показване и на скрити файлове Получаване на изходния код - Папка за съхранение на данни + Папка за съхранение на данни Управление на папки за автоматично качване Локална папка Отдалечена папка diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index c18eb6240e..ad044bf89c 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -600,7 +600,7 @@ Configureu el xifratge d\'extrem a extrem Mostra els fitxers ocults Aconseguiu el codi font - Carpeta de dades + Carpeta de dades Gestiona les carpetes per a la pujada automàtica Carpeta local Carpeta remota diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index 204f768cb8..bae689e873 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -689,7 +689,7 @@ Doporučování Nextcloud aplikací v záhlaví navigace Zobrazit skryté soubory Získat zdrojové kódy - Složka pro ukládání dat + Složka pro ukládání dat Spravovat složky pro automatické nahrávání Místní složka Vzdálená složka diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 88c3100832..201c8f688b 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -632,7 +632,7 @@ Enheds legitimationsoplysninger er sat op Nextcloud app anbefalinger i navigationspanelet Vis skjulte filer Hent kildetekst - Lagringsmappe for data + Lagringsmappe for data Administrér mapper til auto upload Lokal mappe Ekstern mappe diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 0080ceeda4..7fb926eeda 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -689,7 +689,7 @@ Nextcloud-App-Vorschläge in der Navigationsüberschrift Versteckte Dateien anzeigen Zum Programmcode - Speicherordner + Speicherordner Ordner für \"Automatisches Hochladen\" verwalten Lokaler Ordner Remote-Ordner diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 4677ea312b..e512169da9 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -587,7 +587,7 @@ Ρύθμιση κρυπτογράφησης από άκρο σε άκρο Εμφάνιση κρυφών αρχείων Λήψη πηγαίου κώδικα - Φάκελος αποθήκευσης δεδομένων + Φάκελος αποθήκευσης δεδομένων Διαχείριση φακέλων για αυτόματη μεταφόρτωση Τοπικός φάκελος Απομακρυσμένος φάκελος diff --git a/app/src/main/res/values-es-rAR/strings.xml b/app/src/main/res/values-es-rAR/strings.xml index 303df3c328..bbea774837 100644 --- a/app/src/main/res/values-es-rAR/strings.xml +++ b/app/src/main/res/values-es-rAR/strings.xml @@ -655,7 +655,7 @@ Sugerencias de aplicaciones de Nextcloud en el encabezado de navegación Mostrar archivos ocultos Obtener el código fuente - Carpeta de almacenamiento de datos + Carpeta de almacenamiento de datos Administrar carpetas para carga automática Carpeta local Carpeta remota diff --git a/app/src/main/res/values-es-rEC/strings.xml b/app/src/main/res/values-es-rEC/strings.xml index 03317462ae..8889b12d9c 100644 --- a/app/src/main/res/values-es-rEC/strings.xml +++ b/app/src/main/res/values-es-rEC/strings.xml @@ -589,7 +589,7 @@ Configurar encriptación de extremo a extremo Mostrar archivos ocultos Obtener el código fuente - Carpeta de almacenamiento de datos + Carpeta de almacenamiento de datos Administrar carpetas para carga automática Carpeta local Carpeta remota diff --git a/app/src/main/res/values-es-rMX/strings.xml b/app/src/main/res/values-es-rMX/strings.xml index f2b9c35f00..a693af5e38 100644 --- a/app/src/main/res/values-es-rMX/strings.xml +++ b/app/src/main/res/values-es-rMX/strings.xml @@ -655,7 +655,7 @@ Sugerencias de aplicaciones de Nextcloud en el encabezado de navegación Mostrar archivos ocultos Obtener el código fuente - Carpeta de almacenamiento de datos + Carpeta de almacenamiento de datos Administrar carpetas para carga automática Carpeta local Carpeta remota diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index dd20ceecb0..7dcc7d3392 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -687,7 +687,7 @@ Sugerencias de Nextcloud app en encabezado de navegación Mostrar archivos ocultos Obtener el código fuente - Carpeta de almacenamiento de datos + Carpeta de almacenamiento de datos Administrar carpetas para auto-subida Carpeta local Carpeta remota diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 559dd02616..69f5a8c7a1 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -687,7 +687,7 @@ Nextcloud aplikazioaren iradokizunak nabigazio goiburuan Erakutsi ezkutuko fitxategiak Eskuratu iturburu-kodea - Datu-biltegiratze karpeta + Datu-biltegiratze karpeta Kudeatu karpeten igotze automatikoa Karpeta lokala Urruneko karpeta diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index e01de3da13..8fb24f80f5 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -628,7 +628,7 @@ Nextcloud app suggestions in navigation heading فایل های مخفی را نشان بده دریافت کد منبع - مسیر ذخیره‌سازی + مسیر ذخیره‌سازی مدیریت پوشه‌ها جهت آپلود خودکار پوشه محلی پوشه از راه دور diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml index 0c01fab402..ee4f3bc958 100644 --- a/app/src/main/res/values-fi-rFI/strings.xml +++ b/app/src/main/res/values-fi-rFI/strings.xml @@ -643,7 +643,7 @@ GNU yleinen lisenssi, versio 2 Nextcloud-sovellusehdotukset navigointipalkissa Näytä piilotetut tiedostot Hanki lähdekoodi - Tiedostojen tallennuskansio + Tiedostojen tallennuskansio Hallinnoi kansioita automaattista latausta varten Paikallinen kansio Etäkansio diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index b17c260304..b272b18916 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -687,7 +687,7 @@ Suggestions d\'applications Nextcloud dans l\'en-tête de navigation Afficher les fichiers masqués Obtenir le code source - Dossier de stockage des données + Dossier de stockage des données Gérer les dossiers pour le téléversement automatique Dossier local Dossier distant diff --git a/app/src/main/res/values-ga/strings.xml b/app/src/main/res/values-ga/strings.xml index 38180e8174..f97d990486 100644 --- a/app/src/main/res/values-ga/strings.xml +++ b/app/src/main/res/values-ga/strings.xml @@ -687,7 +687,7 @@ Moltaí aip Nextcloud sa cheannteideal nascleanúna Taispeáin comhaid i bhfolach Faigh cód foinse - Fillteán stórála sonraí + Fillteán stórála sonraí Bainistigh fillteáin le haghaidh uaslódáil uathoibríoch Fillteán áitiúil Fillteán cianda diff --git a/app/src/main/res/values-gd/strings.xml b/app/src/main/res/values-gd/strings.xml index b50dee945c..06fa03c612 100644 --- a/app/src/main/res/values-gd/strings.xml +++ b/app/src/main/res/values-gd/strings.xml @@ -495,7 +495,7 @@ Stiùirich cunntasan Seall faidhlichean falaichte Faigh am bun-tùs - Pasgan stòradh dàta + Pasgan stòradh dàta Stiùirich pasganan an luchdaidh suas fhèin-obrachail Pasgan ionadail Pasgan cèin diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index eafd8070b6..299fb659a7 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -689,7 +689,7 @@ Suxestións da aplicación Nextcloud no título de navegación Amosar ficheiros agochados Obter o código fonte - Cartafol de almacenamento de datos + Cartafol de almacenamento de datos Xestionar os cartafoles para a envío automático Cartafol local Cartafol remoto diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index d8b2ca106e..b2cdcdc225 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -550,7 +550,7 @@ Preporuči prijatelju Prikaz skrivenih datoteka Preuzmi izvorni kod - Mapa za pohranu podataka + Mapa za pohranu podataka Upravljanje mapama za automatsko otpremanje Lokalna mapa Udaljena mapa diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml index f61309baa5..f410fbeb53 100644 --- a/app/src/main/res/values-hu-rHU/strings.xml +++ b/app/src/main/res/values-hu-rHU/strings.xml @@ -612,7 +612,7 @@ Nextcloud alkalmazásjavaslatok a navigációs fejlécben Rejtett fájlok megjelenítése Forráskód beszerzése - Adattároló mappa + Adattároló mappa Mappák kezelése az automatikus feltöltéshez Helyi mappa Távoli mappa diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 987458d8c0..5ece1de013 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -608,7 +608,7 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Saran aplikasi Nextcloud dalam judul navigasi Lihat berkas tersembunyi Dapatkan kode sumber - Folder penyimpanan data + Folder penyimpanan data Kelola folder untuk pengunggahan otomatis Berkas lokal Folder remot diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index 22619d88f7..fd0aad2c60 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -591,7 +591,7 @@ Tillögur Nextcloud-forrita í flakkfyrirsögn Sýna faldar skrár Náðu í grunnkóðann - Mappa fyrir geymslu gagna + Mappa fyrir geymslu gagna Sýsla með möppur vegna sjálfvirkra innsendinga Staðvær mappa Fjartengd mappa diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 08a969a545..e93535c197 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -632,7 +632,7 @@ Consigli delle applicazioni di Nextcloud nell\'intestazione di navigazione Mostra i file nascosti Ottieni codice sorgente - Cartella di archiviazione dei dati + Cartella di archiviazione dei dati Gestisci le cartelle per il caricamento automatico Cartella locale Cartella remota diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index c55deb3277..29710fa936 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -651,7 +651,7 @@ アップスイッチャーを表示 隠しファイルを表示 ソースコードを入手 - データ保存フォルダー + データ保存フォルダー 自動アップロードするフォルダーを管理する ローカルフォルダー リモートフォルダー diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index b41599382c..8bd6ed52bc 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -607,7 +607,7 @@ Nextcloud app suggestions in navigation heading Show hidden files Get source code - Data storage folder + Data storage folder Manage folders for auto upload Local folder Remote folder diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index f993c5afa9..99ce4fd44b 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -660,7 +660,7 @@ 탐색창 상단에 표시되는 Nextcloud 앱 제안 숨겨진 파일 보기 소스 코드 보기 - 데이터 저장 폴더 + 데이터 저장 폴더 자동 업로드를 위한 폴더 관리 로컬 폴더 원격 폴더 diff --git a/app/src/main/res/values-lo/strings.xml b/app/src/main/res/values-lo/strings.xml index 6c63a9645c..b5f7090087 100644 --- a/app/src/main/res/values-lo/strings.xml +++ b/app/src/main/res/values-lo/strings.xml @@ -495,7 +495,7 @@ ຈັດການບັນຊີ ສະແດງຟາຍທີ່ເຊື່ອງໄວ້ ຮັບລະຫັດແຫຼ່ງຂໍ້ມູນ - ໂຟນເດີການເກັບຂໍ້ມູນ + ໂຟນເດີການເກັບຂໍ້ມູນ ຈັດການໂຟນເດີສໍາລັບການອັບໂຫລດອັດຕະໂນມັດ ໂຟນເດີ ໂຟນໄລຍະໄກ diff --git a/app/src/main/res/values-lt-rLT/strings.xml b/app/src/main/res/values-lt-rLT/strings.xml index 8647b90089..e74aab6d3b 100644 --- a/app/src/main/res/values-lt-rLT/strings.xml +++ b/app/src/main/res/values-lt-rLT/strings.xml @@ -588,7 +588,7 @@ Rekomenduoti draugui Rodyti paslėptus failus Gauti pirminį kodą - Duomenų aplankas + Duomenų aplankas Tvarkykite automatinio įkėlimo aplankus Aplankas vietinis Nuotolinis aplankas diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index fd33d58eab..8503383c0f 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -510,7 +510,7 @@ Управување со сметки Прикажи сокриени датотеки Превземи го изворниот код - Папка за складиште на податоци + Папка за складиште на податоци Уреди папки за автоматско прикачување Папка на уредот Папка на серверот diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 8f016b7903..9b08cfcdfc 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -665,7 +665,7 @@ Vis Nextcloud app-forslag i navigasjonen Vis skjulte filer Hent kildekode - Datalagringsmappe + Datalagringsmappe Styr mapper for automatisk opplastning Lokal mappe Mappe på server diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index a63a979403..80b1be83a3 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -656,7 +656,7 @@ Nextcloud app suggesties in navigatiekopregel Verborgen bestanden weergeven Krijg broncode - Gegevensopslagmap + Gegevensopslagmap Mappen beheren voor automatisch uploaden Lokale map Externe map diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 12bebb2c80..fb99411b76 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -687,7 +687,7 @@ Sugestie aplikacji Nextcloud w nagłówku nawigacji Pokaż ukryte pliki Pobierz kod źródłowy - Katalog przechowywania danych + Katalog przechowywania danych Zarządzaj katalogami do automatycznego wysyłania Katalog lokalny Katalog zdalny diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 5e6037cdc6..3ae35f814c 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -35,6 +35,7 @@ Configurações avançadas Permitir recompartilhamento URL base + Nome do Host do Proxy Porta do Proxy Mostra um widget do painel Pesquisar em %s @@ -55,6 +56,7 @@ Tarefa excluída com sucesso Não foi possível buscar a lista de tarefas. Verifique sua conexão com a Internet. Excluir tarefa + O resultado da tarefa ainda não está pronto. Não foi possível buscar os tipos de tarefas. Verifique sua conexão com a Internet. Assistente Desconhecido @@ -687,7 +689,7 @@ Sugestões de aplicativos Nextcloud no título de navegação Mostrar arquivos ocultos Obter código-fonte - Pasta de armazenamento de dados + Pasta de armazenamento de dados Gerenciar pastas para envio automático Pasta local Pasta remota @@ -760,6 +762,7 @@ Sincronizar com DAVx5 Erro ao obter os resultados da pesquisa O compartilhamento seguro não está configurado para este usuário + Compartilhamento seguro… Selecionar tudo Definir pasta de mídia Selecione um modelo @@ -803,6 +806,7 @@ Permissões de compartilhamento %1$s (remoto) %1$s (conversa) + Nome, Federated Cloud ID ou endereço de e-mail… Enviar novo e-mail Anotação ao destinatário Configurações diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 7165479282..032dc0364b 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -588,7 +588,7 @@ Sugestões de aplicações Nextcloud no cabeçalho da navegação Mostrar ficheiros ocultados Obter código fonte - Pasta de armazenamento de dados + Pasta de armazenamento de dados Gerir as pastas para o envio automático Pasta Local Pasta Remota diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 43bd8ef235..b3904c4e96 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -610,7 +610,7 @@ Configurează encripție end-to-end Arată fișierele ascunse Obține codul sursă - Dosarul de stocare a datelor + Dosarul de stocare a datelor Administrează dosare pentru încărcare automată Dosar local Dosar la distanță diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 754ade0e14..42c8d7ff7f 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -688,7 +688,7 @@ Рекомендации по приложению Nextcloud в заголовке навигации Показывать скрытые файлы Исходный код - Папка хранения данных + Папка хранения данных Управление папками для автозагрузки Папка на устройстве Папка на сервере diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index d3a7805858..c0212311cf 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -522,7 +522,7 @@ Gesti is contos Mustra documentos cuados Otene su còdighe sorgente - Cartella de archiviatzione de is datos + Cartella de archiviatzione de is datos Gesti cartellas pro carrigamentu automàticu Cartella locale Cartella remota diff --git a/app/src/main/res/values-sk-rSK/strings.xml b/app/src/main/res/values-sk-rSK/strings.xml index ed07836dc4..6eca3c22ae 100644 --- a/app/src/main/res/values-sk-rSK/strings.xml +++ b/app/src/main/res/values-sk-rSK/strings.xml @@ -651,7 +651,7 @@ Návrhy Nextcloud aplikácií v navigačnom záhlaví Zobraziť skryté súbory Získajte zdrojový kód - Priečinok dátového úložiska + Priečinok dátového úložiska Spravovať priečinky pre automatické nahrávanie Lokálny priečinok Vzdialený priečinok diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index b44ff524a5..5dbf0e6c4f 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -610,7 +610,7 @@ Predlogi programov Nextcloud v naslovu Pokaži skrite datoteke Pridobi izvorno kodo - Mapa podatkovne shrambe + Mapa podatkovne shrambe Upravljanje map za samodejno pošiljanje Krajevna mapa Mapa na strežniku diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index aba240092f..9d17e8c71b 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -687,7 +687,7 @@ Предлози Nextcloud апликација у заглављу навигације Прикажи скривене фајлове Узми изворни кôд - Фасцикла за податке + Фасцикла за податке Управљање фолдерима за аутоматско отпремање Локална фасцикла Удаљена фасцикла diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 09dc2c4be8..f5952f5ca5 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -689,7 +689,7 @@ Nextcloud-appförslag i navigeringsrubriken Visa dolda filer Hämta källkod - Datalagringsmapp + Datalagringsmapp Hantera mappar för automatiskt uppladdning Lokal mapp Extern mapp diff --git a/app/src/main/res/values-tk/strings.xml b/app/src/main/res/values-tk/strings.xml index 5cff2f37bd..8ff88f3041 100644 --- a/app/src/main/res/values-tk/strings.xml +++ b/app/src/main/res/values-tk/strings.xml @@ -485,7 +485,7 @@ Hasabyňyzy dolandyrmak Gizlin faýllary görkez Çeşme koduny alyň - Maglumat saklaýyş bukjasy + Maglumat saklaýyş bukjasy Awtomatik ýüklemek üçin bukjalary dolandyrmak ýerli bukjasy Uzakdaky bukja diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 65d5bba5bc..1d03398554 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -687,7 +687,7 @@ Gezinme başlığında Nextcloud uygulama önerileri Gizli dosyaları görüntüle Kaynak kodunu alın - Veri depolama klasörü + Veri depolama klasörü Otomatik yükleme klasörleri yönetimi Yerel klasör Uzak klasör diff --git a/app/src/main/res/values-ug/strings.xml b/app/src/main/res/values-ug/strings.xml index 71e27debe9..54ccf9fb4c 100644 --- a/app/src/main/res/values-ug/strings.xml +++ b/app/src/main/res/values-ug/strings.xml @@ -687,7 +687,7 @@ يول باشلاش ماۋزۇسىدىكى Nextcloud ئەپ تەكلىپلىرى يوشۇرۇن ھۆججەتلەرنى كۆرسەت ئەسلى كودقا ئېرىشىش - سانلىق مەلۇمات ساقلاش قىسقۇچى + سانلىق مەلۇمات ساقلاش قىسقۇچى ئاپتوماتىك يوللاش ئۈچۈن ھۆججەت قىسقۇچلارنى باشقۇرۇڭ يەرلىك ھۆججەت قىسقۇچ يىراقتىن قىسقۇچ diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index eed9023e68..e9e44faaef 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -680,7 +680,7 @@ Пропозиції застосунків Nextcloud у заголовку навігації Показувати приховані файли Отримати вихідний код - Місце збереження файлів + Місце збереження файлів Налаштуйте каталоги, які будуть автоматично завантажуватися Каталог на пристрої Віддалений каталог diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index f0dbee4a17..d961efbc95 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -556,7 +556,7 @@ Thiết lập mã hoá đầu cuối Hiển thị các tệp ẩn Lấy mã nguồn - Thư mục lưu trữ dữ liệu + Thư mục lưu trữ dữ liệu Quản lý các thư mục để tự động tải lên Thư mục cục bộ Thư mục từ xa diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index e585545f04..ca1abf9513 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -687,7 +687,7 @@ 导航栏中的 Nextcloud 应用推荐 显示隐藏文件 获取源代码 - 数据存储文件夹 + 数据存储文件夹 管理自动上传文件夹 本地文件夹 远端文件夹 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index f0d3d34c67..9e4c679bd2 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -35,6 +35,7 @@ 進階設定 允許轉貼分享 基礎 URL + 代理伺服器主機名稱 proxy代理伺服器連接埠 顯示儀表板中的一個小部件 %s內搜尋 @@ -55,6 +56,7 @@ 任務已刪除 無法擷取任務清單,請檢查您的網際網路連線。 刪除任務 + 任務輸出尚未就緒。 無法擷取任務類型,請檢查您的網際網路連線。 助手 不詳 @@ -687,7 +689,7 @@ 導覽列中的 Nextcloud 應用程式建議 顯示隱藏檔案 取得原始碼 - 資料儲存資料夾 + 資料儲存資料夾 管理資料夾以便自動上傳 近端資料夾 遠端資料夾 @@ -760,6 +762,7 @@ 跟 DAVx5 同步 獲取搜索結果時出錯 尚未為此用戶設定安全分享功能。 + 安全分享 … 全選 設置媒體資料夾 請選擇一個模板 @@ -803,6 +806,7 @@ 分享權限 %1$s(遠端) %1$s﹙對話﹚ + 名稱、聯合雲端 ID 或電郵地址 … 傳送新電子郵件 給接收者的訊息 設定 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 7570e3efc1..a3c8b19086 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -689,7 +689,7 @@ 導覽標頭中的 Nextcloud 應用程式建議 顯示隱藏檔案 取得原始碼 - 資料儲存資料夾 + 資料儲存資料夾 管理自動上傳使用的資料夾 本機資料夾 遠端資料夾 diff --git a/app/src/main/res/values/setup.xml b/app/src/main/res/values/setup.xml index 3b6c753293..aa6be51253 100644 --- a/app/src/main/res/values/setup.xml +++ b/app/src/main/res/values/setup.xml @@ -36,6 +36,13 @@ -1 + false + false + false + false + false + false + on true diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index df0aa8fc00..bbf49635e8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -26,6 +26,12 @@ Pending Operation Assistant Unexpected error occurred + Disable Multi Account + Disable Sharing + Disable Clipboard + Disable External Sites + Disable Intro + Disable Log Base URL Proxy Hostname Proxy Port @@ -510,6 +516,8 @@ Nextcloud folder already exists Failed during migration Failed to update index + %1$s\n(%2$s / %3$s) + Allow access from other apps Data folder already exists. Choose one of the following: Replace @@ -578,7 +586,8 @@ kept in original folder moved to app folder deleted - Data storage folder + Data storage location + Manage data storage location What to do if the file already exists? What to do if the file already exists? @@ -808,7 +817,7 @@ Add name, picture and contact details on your profile page. Background image of drawer header Account icon - + No app available to select contacts This folder is not empty. Error while decrypting. Wrong password? Decrypting… @@ -957,6 +966,7 @@ New Notification Choose storage location Internal storage + External storage Camera Pictures Movies @@ -1263,4 +1273,6 @@ To set up a two way sync folder, please enable it in the details tab of the folder in question. Internal two way sync Disable for all folders + + Pending Remove Operation diff --git a/app/src/main/res/xml/app_config.xml b/app/src/main/res/xml/app_config.xml index e4043cbc1e..a8475a9d28 100644 --- a/app/src/main/res/xml/app_config.xml +++ b/app/src/main/res/xml/app_config.xml @@ -25,4 +25,40 @@ android:restrictionType="string" android:title="@string/app_config_base_url_title" /> + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index aa1e05dc55..90599ffb49 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -13,9 +13,10 @@ - + + + + + + + + + diff --git a/renovate.json5 b/renovate.json5 index c3508dec97..71213c4511 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -1,13 +1,12 @@ { - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended", - ":switchToGradleLite", - ":gitSignOff" - ], - "timezone": "Europe/Berlin", - "labels": [ - "dependencies", - "3. to review" - ], + $schema: 'https://docs.renovatebot.com/renovate-schema.json', + extends: [ + 'config:recommended', + ':gitSignOff', + ], + timezone: 'Europe/Berlin', + labels: [ + 'dependencies', + '3. to review', + ], }