Merge remote-tracking branch 'origin/master' into dev

This commit is contained in:
Tobias Kaminsky 2024-11-21 03:46:22 +01:00
commit e0af72d32a
109 changed files with 1015 additions and 476 deletions

View file

@ -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

View file

@ -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
...

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View file

@ -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(

View file

@ -1,86 +0,0 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2024 Alper Ozturk <alper.ozturk@nextcloud.com>
* 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)
}
}
}

View file

@ -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" }
}
}

View file

@ -313,6 +313,11 @@
android:name=".ui.activity.ContactsPreferenceActivity"
android:exported="false"
android:launchMode="singleInstance" />
<activity
android:name=".ui.activity.ChooseStorageLocationActivity"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
android:exported="false"
android:theme="@style/Theme.NoBackground" />
<activity
android:name=".ui.activity.ReceiveExternalFilesActivity"
android:excludeFromRecents="true"

View file

@ -46,6 +46,11 @@ class OfflineOperationTypeAdapter : JsonSerializer<OfflineOperationType>, 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<OfflineOperationType>, JsonDe
jsonObject.get("newName").asString
)
OfflineOperationRawType.RemoveFile.name -> OfflineOperationType.RemoveFile(
jsonObject.get("type").asString,
jsonObject.get("path").asString
)
else -> null
}
}

View file

@ -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();

View file

@ -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 {

View file

@ -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

View file

@ -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)

View file

@ -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;

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}
}

View file

@ -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)
}

View file

@ -0,0 +1,134 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2024 Alper Ozturk <alper.ozturk@nextcloud.com>
* 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 <T : Any> 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
}
}
}

View file

@ -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() {

View file

@ -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) {

View file

@ -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 {

View file

@ -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
}

View file

@ -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) {

View file

@ -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<Integer> 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() {

View file

@ -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

View file

@ -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<String> 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<String> fileNames,
boolean encrypted) {

View file

@ -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
}
}

View file

@ -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<ExternalLink> 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;
}

View file

@ -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) {

View file

@ -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"
}

View file

@ -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());
}

View file

@ -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<String> 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

View file

@ -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) {

View file

@ -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<RecyclerView.ViewHol
}
updateLivePhotoIndicators(gridViewHolder, file);
if (!MDMConfig.INSTANCE.sharingSupport(activity)) {
gridViewHolder.getShared().setVisibility(View.GONE);
}
}
}
@ -607,28 +612,11 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
localSize = localFile.length();
}
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(localSize));
}
holder.getFileSizeSeparator().setVisibility(View.VISIBLE);
prepareFileSize(holder, file, localSize);
} else {
final long fileLength = file.getFileLength();
if (fileLength >= 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<RecyclerView.ViewHol
applyVisualsForOfflineOperations(holder, file);
}
private void prepareFileSize(ListItemViewHolder holder, OCFile file, long size) {
holder.getFileSize().setVisibility(View.VISIBLE);
ViewExtensionsKt.setVisibleIf(holder.getFileSizeSeparator(), !file.isOfflineOperation());
String fileSizeText = getFileSizeText(file, size);
holder.getFileSize().setText(fileSizeText);
}
private String getFileSizeText(OCFile file, long size) {
OfflineOperationEntity entity = mStorageManager.getOfflineEntityFromOCFile(file);
boolean isRemoveOperation = entity != null && entity.getType() instanceof OfflineOperationType.RemoveFile;
if (isRemoveOperation) {
return activity.getString(R.string.oc_file_list_adapter_offline_operation_remove_description_text);
}
if (file.isOfflineOperation()) {
return activity.getString(R.string.oc_file_list_adapter_offline_operation_description_text);
}
return DisplayUtils.bytesToHumanReadable(size);
}
private void applyVisualsForOfflineOperations(ListItemViewHolder holder, OCFile file) {
ViewExtensionsKt.setVisibleIf(holder.getShared(), !file.isOfflineOperation());

View file

@ -22,6 +22,7 @@ import com.nextcloud.client.jobs.download.FileDownloadHelper
import com.nextcloud.client.jobs.upload.FileUploadHelper
import com.nextcloud.client.preferences.AppPreferences
import com.nextcloud.utils.extensions.createRoundedOutline
import com.nextcloud.utils.mdm.MDMConfig
import com.owncloud.android.R
import com.owncloud.android.datamodel.FileDataStorageManager
import com.owncloud.android.datamodel.OCFile
@ -368,6 +369,12 @@ class OCFileListDelegate(
private fun showShareIcon(gridViewHolder: ListViewHolder, file: OCFile) {
val sharedIconView = gridViewHolder.shared
if (!MDMConfig.sharingSupport(context)) {
sharedIconView.visibility = View.GONE
return
}
if (gridViewHolder is OCFileListItemViewHolder || file.unreadCommentsCount == 0) {
sharedIconView.visibility = View.VISIBLE
if (file.isSharedWithSharee || file.isSharedWithMe) {

View file

@ -14,10 +14,12 @@ package com.owncloud.android.ui.adapter;
import android.annotation.SuppressLint;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.nextcloud.client.account.User;
import com.nextcloud.utils.mdm.MDMConfig;
import com.owncloud.android.R;
import com.owncloud.android.databinding.FileDetailsShareInternalShareLinkBinding;
import com.owncloud.android.databinding.FileDetailsShareLinkShareItemBinding;
@ -79,43 +81,51 @@ public class ShareeListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
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),
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<RecyclerView.ViewHol
final OCShare share = shares.get(position);
boolean shareViaLink = MDMConfig.INSTANCE.shareViaLink(fileActivity);
if (!shareViaLink) {
if (holder instanceof InternalShareViewHolder internalShareViewHolder) {
internalShareViewHolder.bind(share, listener);
}
return;
}
if (holder instanceof LinkShareViewHolder publicShareViewHolder) {
publicShareViewHolder.bind(share, listener);
} else if (holder instanceof InternalShareViewHolder internalShareViewHolder) {
@ -148,7 +168,12 @@ public class ShareeListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
@Override
public int getItemCount() {
return shares.size();
boolean shareViaLink = MDMConfig.INSTANCE.shareViaLink(fileActivity);
if (shareViaLink) {
return shares.size();
} else {
return 1;
}
}
@SuppressLint("NotifyDataSetChanged")

View file

@ -22,6 +22,7 @@ import android.widget.ImageView;
import com.nextcloud.client.account.User;
import com.nextcloud.client.account.UserAccountManager;
import com.nextcloud.utils.mdm.MDMConfig;
import com.owncloud.android.R;
import com.owncloud.android.databinding.AccountActionBinding;
import com.owncloud.android.databinding.AccountItemBinding;
@ -94,7 +95,9 @@ public class UserListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
viewThemeUtils);
} else {
return new AddAccountViewHolderItem(
AccountActionBinding.inflate(LayoutInflater.from(context), parent, false));
AccountActionBinding.inflate(LayoutInflater.from(context), parent, false),
context
);
}
}
@ -303,8 +306,11 @@ public class UserListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
*/
static class AddAccountViewHolderItem extends RecyclerView.ViewHolder {
AddAccountViewHolderItem(@NonNull AccountActionBinding binding) {
private final Context context;
AddAccountViewHolderItem(@NonNull AccountActionBinding binding, Context context) {
super(binding.getRoot());
this.context = context;
}
/**
@ -313,11 +319,12 @@ public class UserListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
* @param accountListAdapterListener {@link Listener}
*/
private void bind(Listener accountListAdapterListener) {
// bind action listener
boolean isProviderOrOwnInstallationVisible = itemView.getContext().getResources()
.getBoolean(R.bool.show_provider_or_own_installation);
if (context == null) {
Log_OC.d(TAG,"Context cannot be null, AddAccountViewHolderItem onClick is disabled");
return;
}
if (isProviderOrOwnInstallationVisible) {
if (MDMConfig.INSTANCE.showIntro(context)) {
itemView.setOnClickListener(v -> accountListAdapterListener.showFirstRunActivity());
} else {
itemView.setOnClickListener(v -> accountListAdapterListener.startAccountCreation());

View file

@ -65,7 +65,7 @@ class MultipleAccountsDialog : DialogFragment(), Injectable, UserListAdapter.Cli
private val accountListItems: List<UserListItem>
/**
* 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
*/

View file

@ -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?) {

View file

@ -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)

View file

@ -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());

View file

@ -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) {

View file

@ -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);

View file

@ -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;
}

View file

@ -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(

View file

@ -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")
}

View file

@ -1,68 +0,0 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2024 Alper Ozturk <alper.ozturk@nextcloud.com>
* 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
}
}
}

View file

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Nextcloud - Android Client
~
~ SPDX-FileCopyrightText: 2024 ZetaTom <70907959+ZetaTom@users.noreply.github.com>
~ SPDX-License-Identifier: AGPL-3.0-or-later
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingHorizontal="@dimen/dialog_padding">
<RadioGroup
android:id="@+id/storage_radioGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<RadioButton
android:id="@+id/storage_internal_radio"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:enabled="false"
android:text="@string/storage_internal_storage" />
<RadioButton
android:id="@+id/storage_external_radio"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:enabled="false"
android:text="@string/storage_external_storage" />
</RadioGroup>
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/allow_media_index_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="true"
android:enabled="false"
android:text="@string/file_migration_allow_media_indexing"
app:layout_constraintTop_toBottomOf="@+id/storage_radioGroup"
tools:layout_editor_absoluteX="24dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -693,7 +693,7 @@
<string name="prefs_show_ecosystem_apps_summary">مقترحات بتطبيقات من نكست كلاود في شريط التصفح navigation heading</string>
<string name="prefs_show_hidden_files">عرض الملفات المخفية</string>
<string name="prefs_sourcecode">الحصول على الشفرة المصدرية</string>
<string name="prefs_storage_path">مجلد تخزين البيانات</string>
<string name="prefs_data_storage_location">مجلد تخزين البيانات</string>
<string name="prefs_sycned_folders_summary">إدارة الملفات للتحميل التلقائي</string>
<string name="prefs_synced_folders_local_path_title">مجلد محلي</string>
<string name="prefs_synced_folders_remote_path_title">مجلد عن بعد</string>

View file

@ -687,7 +687,7 @@
<string name="prefs_show_ecosystem_apps_summary">Nextcloud app suggestions in navigation heading</string>
<string name="prefs_show_hidden_files">Show hidden files</string>
<string name="prefs_sourcecode">Get source code</string>
<string name="prefs_storage_path">Data storage folder</string>
<string name="prefs_data_storage_location">Data storage folder</string>
<string name="prefs_sycned_folders_summary">Manage folders for auto upload</string>
<string name="prefs_synced_folders_local_path_title">Local folder</string>
<string name="prefs_synced_folders_remote_path_title">Remote folder</string>

View file

@ -581,7 +581,7 @@
<string name="prefs_setup_e2e">Настройка на цялостно криптиране</string>
<string name="prefs_show_hidden_files">Показване и на скрити файлове</string>
<string name="prefs_sourcecode">Получаване на изходния код</string>
<string name="prefs_storage_path">Папка за съхранение на данни</string>
<string name="prefs_data_storage_location">Папка за съхранение на данни</string>
<string name="prefs_sycned_folders_summary">Управление на папки за автоматично качване</string>
<string name="prefs_synced_folders_local_path_title">Локална папка</string>
<string name="prefs_synced_folders_remote_path_title">Отдалечена папка</string>

View file

@ -600,7 +600,7 @@
<string name="prefs_setup_e2e">Configureu el xifratge d\'extrem a extrem</string>
<string name="prefs_show_hidden_files">Mostra els fitxers ocults</string>
<string name="prefs_sourcecode">Aconseguiu el codi font</string>
<string name="prefs_storage_path">Carpeta de dades</string>
<string name="prefs_data_storage_location">Carpeta de dades</string>
<string name="prefs_sycned_folders_summary">Gestiona les carpetes per a la pujada automàtica</string>
<string name="prefs_synced_folders_local_path_title">Carpeta local</string>
<string name="prefs_synced_folders_remote_path_title">Carpeta remota</string>

View file

@ -689,7 +689,7 @@
<string name="prefs_show_ecosystem_apps_summary">Doporučování Nextcloud aplikací v záhlaví navigace</string>
<string name="prefs_show_hidden_files">Zobrazit skryté soubory</string>
<string name="prefs_sourcecode">Získat zdrojové kódy</string>
<string name="prefs_storage_path">Složka pro ukládání dat</string>
<string name="prefs_data_storage_location">Složka pro ukládání dat</string>
<string name="prefs_sycned_folders_summary">Spravovat složky pro automatické nahrávání</string>
<string name="prefs_synced_folders_local_path_title">Místní složka</string>
<string name="prefs_synced_folders_remote_path_title">Vzdálená složka</string>

View file

@ -632,7 +632,7 @@ Enheds legitimationsoplysninger er sat op
<string name="prefs_show_ecosystem_apps_summary">Nextcloud app anbefalinger i navigationspanelet</string>
<string name="prefs_show_hidden_files">Vis skjulte filer</string>
<string name="prefs_sourcecode">Hent kildetekst</string>
<string name="prefs_storage_path">Lagringsmappe for data</string>
<string name="prefs_data_storage_location">Lagringsmappe for data</string>
<string name="prefs_sycned_folders_summary">Administrér mapper til auto upload</string>
<string name="prefs_synced_folders_local_path_title">Lokal mappe</string>
<string name="prefs_synced_folders_remote_path_title">Ekstern mappe</string>

View file

@ -689,7 +689,7 @@
<string name="prefs_show_ecosystem_apps_summary">Nextcloud-App-Vorschläge in der Navigationsüberschrift</string>
<string name="prefs_show_hidden_files">Versteckte Dateien anzeigen</string>
<string name="prefs_sourcecode">Zum Programmcode</string>
<string name="prefs_storage_path">Speicherordner</string>
<string name="prefs_data_storage_location">Speicherordner</string>
<string name="prefs_sycned_folders_summary">Ordner für \"Automatisches Hochladen\" verwalten</string>
<string name="prefs_synced_folders_local_path_title">Lokaler Ordner</string>
<string name="prefs_synced_folders_remote_path_title">Remote-Ordner</string>

View file

@ -587,7 +587,7 @@
<string name="prefs_setup_e2e">Ρύθμιση κρυπτογράφησης από άκρο σε άκρο</string>
<string name="prefs_show_hidden_files">Εμφάνιση κρυφών αρχείων</string>
<string name="prefs_sourcecode">Λήψη πηγαίου κώδικα</string>
<string name="prefs_storage_path">Φάκελος αποθήκευσης δεδομένων</string>
<string name="prefs_data_storage_location">Φάκελος αποθήκευσης δεδομένων</string>
<string name="prefs_sycned_folders_summary">Διαχείριση φακέλων για αυτόματη μεταφόρτωση</string>
<string name="prefs_synced_folders_local_path_title">Τοπικός φάκελος</string>
<string name="prefs_synced_folders_remote_path_title">Απομακρυσμένος φάκελος</string>

View file

@ -655,7 +655,7 @@
<string name="prefs_show_ecosystem_apps_summary">Sugerencias de aplicaciones de Nextcloud en el encabezado de navegación</string>
<string name="prefs_show_hidden_files">Mostrar archivos ocultos</string>
<string name="prefs_sourcecode">Obtener el código fuente</string>
<string name="prefs_storage_path">Carpeta de almacenamiento de datos</string>
<string name="prefs_data_storage_location">Carpeta de almacenamiento de datos</string>
<string name="prefs_sycned_folders_summary">Administrar carpetas para carga automática</string>
<string name="prefs_synced_folders_local_path_title">Carpeta local</string>
<string name="prefs_synced_folders_remote_path_title">Carpeta remota</string>

View file

@ -589,7 +589,7 @@
<string name="prefs_setup_e2e">Configurar encriptación de extremo a extremo</string>
<string name="prefs_show_hidden_files">Mostrar archivos ocultos</string>
<string name="prefs_sourcecode">Obtener el código fuente</string>
<string name="prefs_storage_path">Carpeta de almacenamiento de datos</string>
<string name="prefs_data_storage_location">Carpeta de almacenamiento de datos</string>
<string name="prefs_sycned_folders_summary">Administrar carpetas para carga automática</string>
<string name="prefs_synced_folders_local_path_title">Carpeta local</string>
<string name="prefs_synced_folders_remote_path_title">Carpeta remota</string>

View file

@ -655,7 +655,7 @@
<string name="prefs_show_ecosystem_apps_summary">Sugerencias de aplicaciones de Nextcloud en el encabezado de navegación</string>
<string name="prefs_show_hidden_files">Mostrar archivos ocultos</string>
<string name="prefs_sourcecode">Obtener el código fuente</string>
<string name="prefs_storage_path">Carpeta de almacenamiento de datos</string>
<string name="prefs_data_storage_location">Carpeta de almacenamiento de datos</string>
<string name="prefs_sycned_folders_summary">Administrar carpetas para carga automática</string>
<string name="prefs_synced_folders_local_path_title">Carpeta local</string>
<string name="prefs_synced_folders_remote_path_title">Carpeta remota</string>

View file

@ -687,7 +687,7 @@
<string name="prefs_show_ecosystem_apps_summary">Sugerencias de Nextcloud app en encabezado de navegación</string>
<string name="prefs_show_hidden_files">Mostrar archivos ocultos</string>
<string name="prefs_sourcecode">Obtener el código fuente</string>
<string name="prefs_storage_path">Carpeta de almacenamiento de datos</string>
<string name="prefs_data_storage_location">Carpeta de almacenamiento de datos</string>
<string name="prefs_sycned_folders_summary">Administrar carpetas para auto-subida</string>
<string name="prefs_synced_folders_local_path_title">Carpeta local</string>
<string name="prefs_synced_folders_remote_path_title">Carpeta remota</string>

View file

@ -687,7 +687,7 @@
<string name="prefs_show_ecosystem_apps_summary">Nextcloud aplikazioaren iradokizunak nabigazio goiburuan</string>
<string name="prefs_show_hidden_files">Erakutsi ezkutuko fitxategiak</string>
<string name="prefs_sourcecode">Eskuratu iturburu-kodea</string>
<string name="prefs_storage_path">Datu-biltegiratze karpeta</string>
<string name="prefs_data_storage_location">Datu-biltegiratze karpeta</string>
<string name="prefs_sycned_folders_summary">Kudeatu karpeten igotze automatikoa</string>
<string name="prefs_synced_folders_local_path_title">Karpeta lokala</string>
<string name="prefs_synced_folders_remote_path_title">Urruneko karpeta</string>

View file

@ -628,7 +628,7 @@
<string name="prefs_show_ecosystem_apps_summary">Nextcloud app suggestions in navigation heading</string>
<string name="prefs_show_hidden_files">فایل های مخفی را نشان بده </string>
<string name="prefs_sourcecode">دریافت کد منبع</string>
<string name="prefs_storage_path">مسیر ذخیره‌سازی</string>
<string name="prefs_data_storage_location">مسیر ذخیره‌سازی</string>
<string name="prefs_sycned_folders_summary">مدیریت پوشه‌ها جهت آپلود خودکار</string>
<string name="prefs_synced_folders_local_path_title">پوشه محلی</string>
<string name="prefs_synced_folders_remote_path_title">پوشه از راه دور</string>

View file

@ -643,7 +643,7 @@ GNU yleinen lisenssi, versio 2</string>
<string name="prefs_show_ecosystem_apps_summary">Nextcloud-sovellusehdotukset navigointipalkissa</string>
<string name="prefs_show_hidden_files">Näytä piilotetut tiedostot</string>
<string name="prefs_sourcecode">Hanki lähdekoodi</string>
<string name="prefs_storage_path">Tiedostojen tallennuskansio</string>
<string name="prefs_data_storage_location">Tiedostojen tallennuskansio</string>
<string name="prefs_sycned_folders_summary">Hallinnoi kansioita automaattista latausta varten</string>
<string name="prefs_synced_folders_local_path_title">Paikallinen kansio</string>
<string name="prefs_synced_folders_remote_path_title">Etäkansio</string>

View file

@ -687,7 +687,7 @@
<string name="prefs_show_ecosystem_apps_summary">Suggestions d\'applications Nextcloud dans l\'en-tête de navigation</string>
<string name="prefs_show_hidden_files">Afficher les fichiers masqués</string>
<string name="prefs_sourcecode">Obtenir le code source</string>
<string name="prefs_storage_path">Dossier de stockage des données</string>
<string name="prefs_data_storage_location">Dossier de stockage des données</string>
<string name="prefs_sycned_folders_summary">Gérer les dossiers pour le téléversement automatique</string>
<string name="prefs_synced_folders_local_path_title">Dossier local</string>
<string name="prefs_synced_folders_remote_path_title">Dossier distant</string>

View file

@ -687,7 +687,7 @@
<string name="prefs_show_ecosystem_apps_summary">Moltaí aip Nextcloud sa cheannteideal nascleanúna</string>
<string name="prefs_show_hidden_files">Taispeáin comhaid i bhfolach</string>
<string name="prefs_sourcecode">Faigh cód foinse</string>
<string name="prefs_storage_path">Fillteán stórála sonraí</string>
<string name="prefs_data_storage_location">Fillteán stórála sonraí</string>
<string name="prefs_sycned_folders_summary">Bainistigh fillteáin le haghaidh uaslódáil uathoibríoch</string>
<string name="prefs_synced_folders_local_path_title">Fillteán áitiúil</string>
<string name="prefs_synced_folders_remote_path_title">Fillteán cianda</string>

View file

@ -495,7 +495,7 @@
<string name="prefs_manage_accounts">Stiùirich cunntasan</string>
<string name="prefs_show_hidden_files">Seall faidhlichean falaichte</string>
<string name="prefs_sourcecode">Faigh am bun-tùs</string>
<string name="prefs_storage_path">Pasgan stòradh dàta</string>
<string name="prefs_data_storage_location">Pasgan stòradh dàta</string>
<string name="prefs_sycned_folders_summary">Stiùirich pasganan an luchdaidh suas fhèin-obrachail</string>
<string name="prefs_synced_folders_local_path_title">Pasgan ionadail</string>
<string name="prefs_synced_folders_remote_path_title">Pasgan cèin</string>

View file

@ -689,7 +689,7 @@
<string name="prefs_show_ecosystem_apps_summary">Suxestións da aplicación Nextcloud no título de navegación</string>
<string name="prefs_show_hidden_files">Amosar ficheiros agochados</string>
<string name="prefs_sourcecode">Obter o código fonte</string>
<string name="prefs_storage_path">Cartafol de almacenamento de datos</string>
<string name="prefs_data_storage_location">Cartafol de almacenamento de datos</string>
<string name="prefs_sycned_folders_summary">Xestionar os cartafoles para a envío automático</string>
<string name="prefs_synced_folders_local_path_title">Cartafol local</string>
<string name="prefs_synced_folders_remote_path_title">Cartafol remoto</string>

View file

@ -550,7 +550,7 @@
<string name="prefs_recommend">Preporuči prijatelju</string>
<string name="prefs_show_hidden_files">Prikaz skrivenih datoteka</string>
<string name="prefs_sourcecode">Preuzmi izvorni kod</string>
<string name="prefs_storage_path">Mapa za pohranu podataka</string>
<string name="prefs_data_storage_location">Mapa za pohranu podataka</string>
<string name="prefs_sycned_folders_summary">Upravljanje mapama za automatsko otpremanje</string>
<string name="prefs_synced_folders_local_path_title">Lokalna mapa</string>
<string name="prefs_synced_folders_remote_path_title">Udaljena mapa</string>

View file

@ -612,7 +612,7 @@
<string name="prefs_show_ecosystem_apps_summary">Nextcloud alkalmazásjavaslatok a navigációs fejlécben</string>
<string name="prefs_show_hidden_files">Rejtett fájlok megjelenítése</string>
<string name="prefs_sourcecode">Forráskód beszerzése</string>
<string name="prefs_storage_path">Adattároló mappa</string>
<string name="prefs_data_storage_location">Adattároló mappa</string>
<string name="prefs_sycned_folders_summary">Mappák kezelése az automatikus feltöltéshez</string>
<string name="prefs_synced_folders_local_path_title">Helyi mappa</string>
<string name="prefs_synced_folders_remote_path_title">Távoli mappa</string>

View file

@ -608,7 +608,7 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini
<string name="prefs_show_ecosystem_apps_summary">Saran aplikasi Nextcloud dalam judul navigasi</string>
<string name="prefs_show_hidden_files">Lihat berkas tersembunyi</string>
<string name="prefs_sourcecode">Dapatkan kode sumber</string>
<string name="prefs_storage_path"> Folder penyimpanan data</string>
<string name="prefs_data_storage_location"> Folder penyimpanan data</string>
<string name="prefs_sycned_folders_summary">Kelola folder untuk pengunggahan otomatis</string>
<string name="prefs_synced_folders_local_path_title">Berkas lokal</string>
<string name="prefs_synced_folders_remote_path_title">Folder remot</string>

View file

@ -591,7 +591,7 @@
<string name="prefs_show_ecosystem_apps_summary">Tillögur Nextcloud-forrita í flakkfyrirsögn</string>
<string name="prefs_show_hidden_files">Sýna faldar skrár</string>
<string name="prefs_sourcecode">Náðu í grunnkóðann</string>
<string name="prefs_storage_path">Mappa fyrir geymslu gagna</string>
<string name="prefs_data_storage_location">Mappa fyrir geymslu gagna</string>
<string name="prefs_sycned_folders_summary">Sýsla með möppur vegna sjálfvirkra innsendinga</string>
<string name="prefs_synced_folders_local_path_title">Staðvær mappa</string>
<string name="prefs_synced_folders_remote_path_title">Fjartengd mappa</string>

View file

@ -632,7 +632,7 @@
<string name="prefs_show_ecosystem_apps_summary">Consigli delle applicazioni di Nextcloud nell\'intestazione di navigazione</string>
<string name="prefs_show_hidden_files">Mostra i file nascosti</string>
<string name="prefs_sourcecode">Ottieni codice sorgente</string>
<string name="prefs_storage_path">Cartella di archiviazione dei dati</string>
<string name="prefs_data_storage_location">Cartella di archiviazione dei dati</string>
<string name="prefs_sycned_folders_summary">Gestisci le cartelle per il caricamento automatico</string>
<string name="prefs_synced_folders_local_path_title">Cartella locale</string>
<string name="prefs_synced_folders_remote_path_title">Cartella remota</string>

View file

@ -651,7 +651,7 @@
<string name="prefs_show_ecosystem_apps">アップスイッチャーを表示</string>
<string name="prefs_show_hidden_files">隠しファイルを表示</string>
<string name="prefs_sourcecode">ソースコードを入手</string>
<string name="prefs_storage_path">データ保存フォルダー</string>
<string name="prefs_data_storage_location">データ保存フォルダー</string>
<string name="prefs_sycned_folders_summary">自動アップロードするフォルダーを管理する</string>
<string name="prefs_synced_folders_local_path_title">ローカルフォルダー</string>
<string name="prefs_synced_folders_remote_path_title">リモートフォルダー</string>

View file

@ -607,7 +607,7 @@
<string name="prefs_show_ecosystem_apps_summary">Nextcloud app suggestions in navigation heading</string>
<string name="prefs_show_hidden_files">Show hidden files</string>
<string name="prefs_sourcecode">Get source code</string>
<string name="prefs_storage_path">Data storage folder</string>
<string name="prefs_data_storage_location">Data storage folder</string>
<string name="prefs_sycned_folders_summary">Manage folders for auto upload</string>
<string name="prefs_synced_folders_local_path_title">Local folder</string>
<string name="prefs_synced_folders_remote_path_title">Remote folder</string>

View file

@ -660,7 +660,7 @@
<string name="prefs_show_ecosystem_apps_summary">탐색창 상단에 표시되는 Nextcloud 앱 제안</string>
<string name="prefs_show_hidden_files">숨겨진 파일 보기</string>
<string name="prefs_sourcecode">소스 코드 보기</string>
<string name="prefs_storage_path">데이터 저장 폴더</string>
<string name="prefs_data_storage_location">데이터 저장 폴더</string>
<string name="prefs_sycned_folders_summary">자동 업로드를 위한 폴더 관리</string>
<string name="prefs_synced_folders_local_path_title">로컬 폴더</string>
<string name="prefs_synced_folders_remote_path_title">원격 폴더</string>

View file

@ -495,7 +495,7 @@
<string name="prefs_manage_accounts">ຈັດການບັນຊີ</string>
<string name="prefs_show_hidden_files">ສະແດງຟາຍທີ່ເຊື່ອງໄວ້</string>
<string name="prefs_sourcecode">ຮັບລະຫັດແຫຼ່ງຂໍ້ມູນ</string>
<string name="prefs_storage_path">ໂຟນເດີການເກັບຂໍ້ມູນ</string>
<string name="prefs_data_storage_location">ໂຟນເດີການເກັບຂໍ້ມູນ</string>
<string name="prefs_sycned_folders_summary">ຈັດການໂຟນເດີສໍາລັບການອັບໂຫລດອັດຕະໂນມັດ</string>
<string name="prefs_synced_folders_local_path_title">ໂຟນເດີ</string>
<string name="prefs_synced_folders_remote_path_title">ໂຟນໄລຍະໄກ</string>

View file

@ -588,7 +588,7 @@
<string name="prefs_recommend">Rekomenduoti draugui</string>
<string name="prefs_show_hidden_files">Rodyti paslėptus failus</string>
<string name="prefs_sourcecode">Gauti pirminį kodą</string>
<string name="prefs_storage_path">Duomenų aplankas</string>
<string name="prefs_data_storage_location">Duomenų aplankas</string>
<string name="prefs_sycned_folders_summary">Tvarkykite automatinio įkėlimo aplankus</string>
<string name="prefs_synced_folders_local_path_title">Aplankas vietinis</string>
<string name="prefs_synced_folders_remote_path_title">Nuotolinis aplankas</string>

View file

@ -510,7 +510,7 @@
<string name="prefs_manage_accounts">Управување со сметки</string>
<string name="prefs_show_hidden_files">Прикажи сокриени датотеки</string>
<string name="prefs_sourcecode">Превземи го изворниот код</string>
<string name="prefs_storage_path">Папка за складиште на податоци</string>
<string name="prefs_data_storage_location">Папка за складиште на податоци</string>
<string name="prefs_sycned_folders_summary">Уреди папки за автоматско прикачување</string>
<string name="prefs_synced_folders_local_path_title">Папка на уредот</string>
<string name="prefs_synced_folders_remote_path_title">Папка на серверот</string>

View file

@ -665,7 +665,7 @@
<string name="prefs_show_ecosystem_apps_summary">Vis Nextcloud app-forslag i navigasjonen</string>
<string name="prefs_show_hidden_files">Vis skjulte filer</string>
<string name="prefs_sourcecode">Hent kildekode</string>
<string name="prefs_storage_path">Datalagringsmappe</string>
<string name="prefs_data_storage_location">Datalagringsmappe</string>
<string name="prefs_sycned_folders_summary">Styr mapper for automatisk opplastning</string>
<string name="prefs_synced_folders_local_path_title">Lokal mappe</string>
<string name="prefs_synced_folders_remote_path_title">Mappe på server</string>

View file

@ -656,7 +656,7 @@
<string name="prefs_show_ecosystem_apps_summary">Nextcloud app suggesties in navigatiekopregel</string>
<string name="prefs_show_hidden_files">Verborgen bestanden weergeven</string>
<string name="prefs_sourcecode">Krijg broncode</string>
<string name="prefs_storage_path">Gegevensopslagmap</string>
<string name="prefs_data_storage_location">Gegevensopslagmap</string>
<string name="prefs_sycned_folders_summary">Mappen beheren voor automatisch uploaden</string>
<string name="prefs_synced_folders_local_path_title">Lokale map</string>
<string name="prefs_synced_folders_remote_path_title">Externe map</string>

View file

@ -687,7 +687,7 @@
<string name="prefs_show_ecosystem_apps_summary">Sugestie aplikacji Nextcloud w nagłówku nawigacji</string>
<string name="prefs_show_hidden_files">Pokaż ukryte pliki</string>
<string name="prefs_sourcecode">Pobierz kod źródłowy</string>
<string name="prefs_storage_path">Katalog przechowywania danych</string>
<string name="prefs_data_storage_location">Katalog przechowywania danych</string>
<string name="prefs_sycned_folders_summary">Zarządzaj katalogami do automatycznego wysyłania</string>
<string name="prefs_synced_folders_local_path_title">Katalog lokalny</string>
<string name="prefs_synced_folders_remote_path_title">Katalog zdalny</string>

View file

@ -35,6 +35,7 @@
<string name="advanced_settings">Configurações avançadas </string>
<string name="allow_resharing">Permitir recompartilhamento</string>
<string name="app_config_base_url_title">URL base</string>
<string name="app_config_proxy_host_title">Nome do Host do Proxy</string>
<string name="app_config_proxy_port_title">Porta do Proxy</string>
<string name="app_widget_description">Mostra um widget do painel</string>
<string name="appbar_search_in">Pesquisar em %s</string>
@ -55,6 +56,7 @@
<string name="assistant_screen_task_delete_success_message">Tarefa excluída com sucesso</string>
<string name="assistant_screen_task_list_error_state_message">Não foi possível buscar a lista de tarefas. Verifique sua conexão com a Internet.</string>
<string name="assistant_screen_task_more_actions_bottom_sheet_delete_action">Excluir tarefa</string>
<string name="assistant_screen_task_output_empty_text">O resultado da tarefa ainda não está pronto.</string>
<string name="assistant_screen_task_types_error_state_message">Não foi possível buscar os tipos de tarefas. Verifique sua conexão com a Internet.</string>
<string name="assistant_screen_top_bar_title">Assistente</string>
<string name="assistant_screen_unknown_task_status_text">Desconhecido</string>
@ -687,7 +689,7 @@
<string name="prefs_show_ecosystem_apps_summary">Sugestões de aplicativos Nextcloud no título de navegação</string>
<string name="prefs_show_hidden_files">Mostrar arquivos ocultos</string>
<string name="prefs_sourcecode">Obter código-fonte</string>
<string name="prefs_storage_path">Pasta de armazenamento de dados</string>
<string name="prefs_data_storage_location">Pasta de armazenamento de dados</string>
<string name="prefs_sycned_folders_summary">Gerenciar pastas para envio automático</string>
<string name="prefs_synced_folders_local_path_title">Pasta local</string>
<string name="prefs_synced_folders_remote_path_title">Pasta remota</string>
@ -760,6 +762,7 @@
<string name="screenshot_06_davdroid_subline">Sincronizar com DAVx5</string>
<string name="search_error">Erro ao obter os resultados da pesquisa </string>
<string name="secure_share_not_set_up">O compartilhamento seguro não está configurado para este usuário</string>
<string name="secure_share_search">Compartilhamento seguro…</string>
<string name="select_all">Selecionar tudo</string>
<string name="select_media_folder">Definir pasta de mídia</string>
<string name="select_one_template">Selecione um modelo</string>
@ -803,6 +806,7 @@
<string name="share_permissions">Permissões de compartilhamento </string>
<string name="share_remote_clarification">%1$s (remoto)</string>
<string name="share_room_clarification">%1$s (conversa)</string>
<string name="share_search">Nome, Federated Cloud ID ou endereço de e-mail…</string>
<string name="share_send_new_email">Enviar novo e-mail </string>
<string name="share_send_note">Anotação ao destinatário</string>
<string name="share_settings">Configurações</string>

View file

@ -588,7 +588,7 @@
<string name="prefs_show_ecosystem_apps_summary">Sugestões de aplicações Nextcloud no cabeçalho da navegação</string>
<string name="prefs_show_hidden_files">Mostrar ficheiros ocultados</string>
<string name="prefs_sourcecode">Obter código fonte</string>
<string name="prefs_storage_path">Pasta de armazenamento de dados</string>
<string name="prefs_data_storage_location">Pasta de armazenamento de dados</string>
<string name="prefs_sycned_folders_summary">Gerir as pastas para o envio automático</string>
<string name="prefs_synced_folders_local_path_title">Pasta Local</string>
<string name="prefs_synced_folders_remote_path_title">Pasta Remota</string>

View file

@ -610,7 +610,7 @@
<string name="prefs_setup_e2e">Configurează encripție end-to-end</string>
<string name="prefs_show_hidden_files">Arată fișierele ascunse</string>
<string name="prefs_sourcecode">Obține codul sursă</string>
<string name="prefs_storage_path">Dosarul de stocare a datelor</string>
<string name="prefs_data_storage_location">Dosarul de stocare a datelor</string>
<string name="prefs_sycned_folders_summary">Administrează dosare pentru încărcare automată</string>
<string name="prefs_synced_folders_local_path_title">Dosar local</string>
<string name="prefs_synced_folders_remote_path_title">Dosar la distanță</string>

View file

@ -688,7 +688,7 @@
<string name="prefs_show_ecosystem_apps_summary">Рекомендации по приложению Nextcloud в заголовке навигации</string>
<string name="prefs_show_hidden_files">Показывать скрытые файлы</string>
<string name="prefs_sourcecode">Исходный код</string>
<string name="prefs_storage_path">Папка хранения данных</string>
<string name="prefs_data_storage_location">Папка хранения данных</string>
<string name="prefs_sycned_folders_summary">Управление папками для автозагрузки</string>
<string name="prefs_synced_folders_local_path_title">Папка на устройстве</string>
<string name="prefs_synced_folders_remote_path_title">Папка на сервере</string>

View file

@ -522,7 +522,7 @@
<string name="prefs_manage_accounts">Gesti is contos</string>
<string name="prefs_show_hidden_files">Mustra documentos cuados</string>
<string name="prefs_sourcecode">Otene su còdighe sorgente</string>
<string name="prefs_storage_path">Cartella de archiviatzione de is datos</string>
<string name="prefs_data_storage_location">Cartella de archiviatzione de is datos</string>
<string name="prefs_sycned_folders_summary">Gesti cartellas pro carrigamentu automàticu</string>
<string name="prefs_synced_folders_local_path_title">Cartella locale</string>
<string name="prefs_synced_folders_remote_path_title">Cartella remota</string>

View file

@ -651,7 +651,7 @@
<string name="prefs_show_ecosystem_apps_summary">Návrhy Nextcloud aplikácií v navigačnom záhlaví</string>
<string name="prefs_show_hidden_files">Zobraziť skryté súbory</string>
<string name="prefs_sourcecode">Získajte zdrojový kód</string>
<string name="prefs_storage_path">Priečinok dátového úložiska</string>
<string name="prefs_data_storage_location">Priečinok dátového úložiska</string>
<string name="prefs_sycned_folders_summary">Spravovať priečinky pre automatické nahrávanie</string>
<string name="prefs_synced_folders_local_path_title">Lokálny priečinok</string>
<string name="prefs_synced_folders_remote_path_title">Vzdialený priečinok</string>

View file

@ -610,7 +610,7 @@
<string name="prefs_show_ecosystem_apps_summary">Predlogi programov Nextcloud v naslovu</string>
<string name="prefs_show_hidden_files">Pokaži skrite datoteke</string>
<string name="prefs_sourcecode">Pridobi izvorno kodo</string>
<string name="prefs_storage_path">Mapa podatkovne shrambe</string>
<string name="prefs_data_storage_location">Mapa podatkovne shrambe</string>
<string name="prefs_sycned_folders_summary">Upravljanje map za samodejno pošiljanje</string>
<string name="prefs_synced_folders_local_path_title">Krajevna mapa</string>
<string name="prefs_synced_folders_remote_path_title">Mapa na strežniku</string>

View file

@ -687,7 +687,7 @@
<string name="prefs_show_ecosystem_apps_summary">Предлози Nextcloud апликација у заглављу навигације</string>
<string name="prefs_show_hidden_files">Прикажи скривене фајлове</string>
<string name="prefs_sourcecode">Узми изворни кôд</string>
<string name="prefs_storage_path">Фасцикла за податке</string>
<string name="prefs_data_storage_location">Фасцикла за податке</string>
<string name="prefs_sycned_folders_summary">Управљање фолдерима за аутоматско отпремање</string>
<string name="prefs_synced_folders_local_path_title">Локална фасцикла</string>
<string name="prefs_synced_folders_remote_path_title">Удаљена фасцикла</string>

View file

@ -689,7 +689,7 @@
<string name="prefs_show_ecosystem_apps_summary">Nextcloud-appförslag i navigeringsrubriken</string>
<string name="prefs_show_hidden_files">Visa dolda filer</string>
<string name="prefs_sourcecode">Hämta källkod</string>
<string name="prefs_storage_path">Datalagringsmapp</string>
<string name="prefs_data_storage_location">Datalagringsmapp</string>
<string name="prefs_sycned_folders_summary">Hantera mappar för automatiskt uppladdning</string>
<string name="prefs_synced_folders_local_path_title">Lokal mapp</string>
<string name="prefs_synced_folders_remote_path_title">Extern mapp</string>

View file

@ -485,7 +485,7 @@
<string name="prefs_manage_accounts">Hasabyňyzy dolandyrmak</string>
<string name="prefs_show_hidden_files">Gizlin faýllary görkez</string>
<string name="prefs_sourcecode">Çeşme koduny alyň</string>
<string name="prefs_storage_path">Maglumat saklaýyş bukjasy</string>
<string name="prefs_data_storage_location">Maglumat saklaýyş bukjasy</string>
<string name="prefs_sycned_folders_summary">Awtomatik ýüklemek üçin bukjalary dolandyrmak</string>
<string name="prefs_synced_folders_local_path_title">ýerli bukjasy</string>
<string name="prefs_synced_folders_remote_path_title">Uzakdaky bukja</string>

View file

@ -687,7 +687,7 @@
<string name="prefs_show_ecosystem_apps_summary">Gezinme başlığında Nextcloud uygulama önerileri</string>
<string name="prefs_show_hidden_files">Gizli dosyaları görüntüle</string>
<string name="prefs_sourcecode">Kaynak kodunu alın</string>
<string name="prefs_storage_path">Veri depolama klasörü</string>
<string name="prefs_data_storage_location">Veri depolama klasörü</string>
<string name="prefs_sycned_folders_summary">Otomatik yükleme klasörleri yönetimi</string>
<string name="prefs_synced_folders_local_path_title">Yerel klasör</string>
<string name="prefs_synced_folders_remote_path_title">Uzak klasör</string>

View file

@ -687,7 +687,7 @@
<string name="prefs_show_ecosystem_apps_summary">يول باشلاش ماۋزۇسىدىكى Nextcloud ئەپ تەكلىپلىرى</string>
<string name="prefs_show_hidden_files">يوشۇرۇن ھۆججەتلەرنى كۆرسەت</string>
<string name="prefs_sourcecode">ئەسلى كودقا ئېرىشىش</string>
<string name="prefs_storage_path">سانلىق مەلۇمات ساقلاش قىسقۇچى</string>
<string name="prefs_data_storage_location">سانلىق مەلۇمات ساقلاش قىسقۇچى</string>
<string name="prefs_sycned_folders_summary">ئاپتوماتىك يوللاش ئۈچۈن ھۆججەت قىسقۇچلارنى باشقۇرۇڭ</string>
<string name="prefs_synced_folders_local_path_title">يەرلىك ھۆججەت قىسقۇچ</string>
<string name="prefs_synced_folders_remote_path_title">يىراقتىن قىسقۇچ</string>

View file

@ -680,7 +680,7 @@
<string name="prefs_show_ecosystem_apps_summary">Пропозиції застосунків Nextcloud у заголовку навігації</string>
<string name="prefs_show_hidden_files">Показувати приховані файли</string>
<string name="prefs_sourcecode">Отримати вихідний код</string>
<string name="prefs_storage_path">Місце збереження файлів</string>
<string name="prefs_data_storage_location">Місце збереження файлів</string>
<string name="prefs_sycned_folders_summary">Налаштуйте каталоги, які будуть автоматично завантажуватися</string>
<string name="prefs_synced_folders_local_path_title">Каталог на пристрої</string>
<string name="prefs_synced_folders_remote_path_title">Віддалений каталог</string>

View file

@ -556,7 +556,7 @@
<string name="prefs_setup_e2e">Thiết lập mã hoá đầu cuối</string>
<string name="prefs_show_hidden_files">Hiển thị các tệp ẩn</string>
<string name="prefs_sourcecode">Lấy mã nguồn</string>
<string name="prefs_storage_path">Thư mục lưu trữ dữ liệu</string>
<string name="prefs_data_storage_location">Thư mục lưu trữ dữ liệu</string>
<string name="prefs_sycned_folders_summary">Quản lý các thư mục để tự động tải lên</string>
<string name="prefs_synced_folders_local_path_title">Thư mục cục bộ</string>
<string name="prefs_synced_folders_remote_path_title">Thư mục từ xa</string>

Some files were not shown because too many files have changed in this diff Show more