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

This commit is contained in:
Tobias Kaminsky 2022-11-21 02:31:32 +01:00
commit 704ee11f7e
54 changed files with 516 additions and 409 deletions

View file

@ -27,6 +27,7 @@ import android.accounts.AccountManager;
import com.nextcloud.test.RandomStringGenerator;
import com.owncloud.android.AbstractOnServerIT;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.db.OCUpload;
import com.owncloud.android.files.services.FileUploader;
@ -93,7 +94,7 @@ public class EndToEndRandomIT extends AbstractOnServerIT {
@BeforeClass
public static void initClass() throws Exception {
arbitraryDataProvider = new ArbitraryDataProvider(targetContext.getContentResolver());
arbitraryDataProvider = new ArbitraryDataProviderImpl(targetContext);
createKeys();
}

View file

@ -26,7 +26,7 @@ import android.content.Intent
import android.os.Looper
import androidx.test.espresso.intent.rule.IntentsTestRule
import com.owncloud.android.AbstractIT
import com.owncloud.android.datamodel.ArbitraryDataProvider
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl
import com.owncloud.android.ui.activity.RequestCredentialsActivity
import com.owncloud.android.ui.activity.SettingsActivity
import com.owncloud.android.utils.EncryptionUtils
@ -71,7 +71,7 @@ class SettingsActivityIT : AbstractIT() {
}
val intent = Intent()
intent.putExtra(RequestCredentialsActivity.KEY_CHECK_RESULT, RequestCredentialsActivity.KEY_CHECK_RESULT_TRUE)
val arbitraryDataProvider = ArbitraryDataProvider(targetContext.contentResolver)
val arbitraryDataProvider = ArbitraryDataProviderImpl(targetContext)
arbitraryDataProvider.storeOrUpdateKeyValue(user.accountName, EncryptionUtils.MNEMONIC, "Secret mnemonic")
val sut = activityRule.launchActivity(null)
sut.runOnUiThread {

View file

@ -26,7 +26,7 @@ import org.junit.Assert.assertEquals
import org.junit.Test
class ArbitraryDataProviderIT : AbstractIT() {
private val arbitraryDataProvider = ArbitraryDataProvider(targetContext.contentResolver)
private val arbitraryDataProvider = ArbitraryDataProviderImpl(targetContext)
@Test
fun testNull() {
@ -56,11 +56,11 @@ class ArbitraryDataProviderIT : AbstractIT() {
fun testBoolean() {
val key = "DUMMY_KEY"
var value = true
arbitraryDataProvider.storeOrUpdateKeyValue(user.accountName, key, value.toString())
arbitraryDataProvider.storeOrUpdateKeyValue(user.accountName, key, value)
assertEquals(value, arbitraryDataProvider.getBooleanValue(user.accountName, key))
value = false
arbitraryDataProvider.storeOrUpdateKeyValue(user.accountName, key, value.toString())
arbitraryDataProvider.storeOrUpdateKeyValue(user.accountName, key, value)
assertEquals(value, arbitraryDataProvider.getBooleanValue(user.accountName, key))
}

View file

@ -48,6 +48,7 @@ import com.nextcloud.ui.fileactions.FileActionsBottomSheet;
import com.owncloud.android.AbstractIT;
import com.owncloud.android.MainApp;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.common.Creator;
@ -390,9 +391,9 @@ public class DialogFragmentIT extends AbstractIT {
String json = new Gson().toJson(directEditing);
new ArbitraryDataProvider(targetContext.getContentResolver()).storeOrUpdateKeyValue(user.getAccountName(),
ArbitraryDataProvider.DIRECT_EDITING,
json);
new ArbitraryDataProviderImpl(targetContext).storeOrUpdateKeyValue(user.getAccountName(),
ArbitraryDataProvider.DIRECT_EDITING,
json);
// activate templates
OCCapability capability = fda.getCapabilities();

View file

@ -30,7 +30,7 @@ import com.nextcloud.client.network.ConnectivityService
import com.nextcloud.utils.EditorUtils
import com.owncloud.android.R
import com.owncloud.android.databinding.TestLayoutBinding
import com.owncloud.android.datamodel.ArbitraryDataProvider
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl
import com.owncloud.android.datamodel.FileDataStorageManager
import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.files.services.FileDownloader
@ -146,7 +146,14 @@ class TestActivity :
override fun getFileOperationsHelper(): FileOperationsHelper {
if (!this::fileOperation.isInitialized) {
fileOperation = FileOperationsHelper(this, userAccountManager, connectivityServiceMock, EditorUtils(ArbitraryDataProvider(contentResolver)))
fileOperation = FileOperationsHelper(
this,
userAccountManager,
connectivityServiceMock,
EditorUtils(
ArbitraryDataProviderImpl(baseContext)
)
)
}
return fileOperation

View file

@ -38,6 +38,7 @@ import com.nextcloud.client.preferences.AppPreferencesImpl;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.datamodel.PushConfigurationState;
import com.owncloud.android.datamodel.SignatureVerification;
import com.owncloud.android.lib.common.OwnCloudAccount;
@ -151,7 +152,7 @@ public final class PushUtils {
private static void deleteRegistrationForAccount(Account account) {
Context context = MainApp.getAppContext();
OwnCloudAccount ocAccount;
arbitraryDataProvider = new ArbitraryDataProvider(MainApp.getAppContext().getContentResolver());
arbitraryDataProvider = new ArbitraryDataProviderImpl(MainApp.getAppContext());
try {
ocAccount = new OwnCloudAccount(account, context);
@ -193,7 +194,7 @@ public final class PushUtils {
}
public static void pushRegistrationToServer(final UserAccountManager accountManager, final String token) {
arbitraryDataProvider = new ArbitraryDataProvider(MainApp.getAppContext().getContentResolver());
arbitraryDataProvider = new ArbitraryDataProviderImpl(MainApp.getAppContext());
if (!TextUtils.isEmpty(MainApp.getAppContext().getResources().getString(R.string.push_server_url)) &&
!TextUtils.isEmpty(token)) {
@ -418,7 +419,7 @@ public final class PushUtils {
Account[] accounts = accountManager.getAccounts();
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver());
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(context);
String arbitraryValue;
Gson gson = new Gson();
PushConfigurationState pushArbitraryData;

View file

@ -280,10 +280,6 @@
android:pathPrefix="/external_links"
android:readPermission="false"
android:writePermission="false" />
<path-permission
android:pathPrefix="/arbitrary_data"
android:readPermission="false"
android:writePermission="false" />
<path-permission
android:pathPrefix="/virtual"
android:readPermission="false"

View file

@ -38,6 +38,7 @@ 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.OCFile;
import com.owncloud.android.lib.common.OwnCloudAccount;
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
@ -148,7 +149,7 @@ public class UserAccountManagerImpl implements UserAccountManager {
Account[] ocAccounts = getAccounts();
Account defaultAccount = null;
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver());
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(context);
SharedPreferences appPreferences = PreferenceManager.getDefaultSharedPreferences(context);
String accountName = appPreferences.getString(PREF_SELECT_OC_ACCOUNT, null);

View file

@ -23,11 +23,8 @@
package com.nextcloud.client.database
import android.content.Context
import androidx.room.Room
import com.nextcloud.client.core.Clock
import com.nextcloud.client.database.migrations.RoomMigration
import com.nextcloud.client.database.migrations.addLegacyMigrations
import com.owncloud.android.db.ProviderMeta
import com.nextcloud.client.database.dao.ArbitraryDataDao
import dagger.Module
import dagger.Provides
import javax.inject.Singleton
@ -37,13 +34,12 @@ class DatabaseModule {
@Provides
@Singleton
@Suppress("Detekt.SpreadOperator") // forced by Room API
fun database(context: Context, clock: Clock): NextcloudDatabase {
return Room
.databaseBuilder(context, NextcloudDatabase::class.java, ProviderMeta.DB_NAME)
.addLegacyMigrations(clock)
.addMigrations(RoomMigration())
.fallbackToDestructiveMigration()
.build()
return NextcloudDatabase.getInstance(context, clock)
}
@Provides
fun arbitraryDataDao(nextcloudDatabase: NextcloudDatabase): ArbitraryDataDao {
return nextcloudDatabase.arbitraryDataDao()
}
}

View file

@ -22,8 +22,13 @@
package com.nextcloud.client.database
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import com.nextcloud.client.core.Clock
import com.nextcloud.client.core.ClockImpl
import com.nextcloud.client.database.dao.ArbitraryDataDao
import com.nextcloud.client.database.entity.ArbitraryDataEntity
import com.nextcloud.client.database.entity.CapabilityEntity
import com.nextcloud.client.database.entity.ExternalLinkEntity
@ -33,6 +38,8 @@ import com.nextcloud.client.database.entity.ShareEntity
import com.nextcloud.client.database.entity.SyncedFolderEntity
import com.nextcloud.client.database.entity.UploadEntity
import com.nextcloud.client.database.entity.VirtualEntity
import com.nextcloud.client.database.migrations.RoomMigration
import com.nextcloud.client.database.migrations.addLegacyMigrations
import com.owncloud.android.db.ProviderMeta
@Database(
@ -52,7 +59,32 @@ import com.owncloud.android.db.ProviderMeta
)
@Suppress("Detekt.UnnecessaryAbstractClass") // needed by Room
abstract class NextcloudDatabase : RoomDatabase() {
abstract fun arbitraryDataDao(): ArbitraryDataDao
companion object {
const val FIRST_ROOM_DB_VERSION = 65
private var INSTANCE: NextcloudDatabase? = null
@JvmStatic
@Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated("Here for legacy purposes, inject this class or use getInstance(context, clock) instead")
fun getInstance(context: Context): NextcloudDatabase {
return getInstance(context, ClockImpl())
}
@JvmStatic
fun getInstance(context: Context, clock: Clock): NextcloudDatabase {
if (INSTANCE == null) {
INSTANCE = Room
.databaseBuilder(context, NextcloudDatabase::class.java, ProviderMeta.DB_NAME)
.allowMainThreadQueries()
.addLegacyMigrations(clock)
.addMigrations(RoomMigration())
.fallbackToDestructiveMigration()
.build()
}
return INSTANCE!!
}
}
}

View file

@ -0,0 +1,42 @@
/*
* Nextcloud Android client application
*
* @author Álvaro Brey
* Copyright (C) 2022 Álvaro Brey
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.nextcloud.client.database.dao
import androidx.room.Dao
import androidx.room.Query
import com.nextcloud.client.database.entity.ArbitraryDataEntity
@Dao
interface ArbitraryDataDao {
@Query("INSERT INTO arbitrary_data(cloud_id, `key`, value) VALUES(:accountName, :key, :value)")
fun insertValue(accountName: String, key: String, value: String?)
@Query("SELECT * FROM arbitrary_data WHERE cloud_id = :accountName AND `key` = :key LIMIT 1")
fun getByAccountAndKey(accountName: String, key: String): ArbitraryDataEntity?
@Query("UPDATE arbitrary_data SET value = :value WHERE cloud_id = :accountName AND `key` = :key ")
fun updateValue(accountName: String, key: String, value: String?)
@Query("DELETE FROM arbitrary_data WHERE cloud_id = :accountName AND `key` = :key")
fun deleteValue(accountName: String, key: String)
}

View file

@ -41,6 +41,9 @@ public class LegacyMigrationHelper {
private static final String TAG = LegacyMigrationHelper.class.getSimpleName();
public static final int ARBITRARY_DATA_TABLE_INTRODUCTION_VERSION = 20;
private static final String ALTER_TABLE = "ALTER TABLE ";
private static final String ADD_COLUMN = " ADD COLUMN ";
private static final String INTEGER = " INTEGER, ";
@ -176,7 +179,7 @@ public class LegacyMigrationHelper {
db.execSQL(ALTER_TABLE + ProviderMeta.ProviderTableMeta.FILE_TABLE_NAME +
ADD_COLUMN + ProviderMeta.ProviderTableMeta.FILE_ENCRYPTED_NAME + " TEXT ");
}
if (oldVersion > FileContentProvider.ARBITRARY_DATA_TABLE_INTRODUCTION_VERSION) {
if (oldVersion > ARBITRARY_DATA_TABLE_INTRODUCTION_VERSION) {
if (!checkIfColumnExists(db, ProviderMeta.ProviderTableMeta.CAPABILITIES_TABLE_NAME,
ProviderMeta.ProviderTableMeta.CAPABILITIES_END_TO_END_ENCRYPTION)) {
db.execSQL(ALTER_TABLE + ProviderMeta.ProviderTableMeta.CAPABILITIES_TABLE_NAME +

View file

@ -39,7 +39,7 @@ import com.nextcloud.client.core.AsyncRunner;
import com.nextcloud.client.core.Clock;
import com.nextcloud.client.core.ClockImpl;
import com.nextcloud.client.core.ThreadPoolAsyncRunner;
import com.nextcloud.client.database.NextcloudDatabase;
import com.nextcloud.client.database.dao.ArbitraryDataDao;
import com.nextcloud.client.device.DeviceInfo;
import com.nextcloud.client.logger.FileLogHandler;
import com.nextcloud.client.logger.Logger;
@ -56,9 +56,9 @@ import com.nextcloud.client.preferences.AppPreferences;
import com.nextcloud.client.utils.Throttler;
import com.owncloud.android.authentication.PassCodeManager;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.UploadsStorageManager;
import com.owncloud.android.db.ProviderMeta;
import com.owncloud.android.ui.activities.data.activities.ActivitiesRepository;
import com.owncloud.android.ui.activities.data.activities.ActivitiesServiceApi;
import com.owncloud.android.ui.activities.data.activities.ActivitiesServiceApiImpl;
@ -77,7 +77,6 @@ import javax.inject.Provider;
import javax.inject.Singleton;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.room.Room;
import dagger.Module;
import dagger.Provides;
@ -118,9 +117,8 @@ class AppModule {
}
@Provides
ArbitraryDataProvider arbitraryDataProvider(Context context) {
final ContentResolver resolver = context.getContentResolver();
return new ArbitraryDataProvider(resolver);
ArbitraryDataProvider arbitraryDataProvider(ArbitraryDataDao dao) {
return new ArbitraryDataProviderImpl(dao);
}
@Provides

View file

@ -38,6 +38,7 @@ import com.nextcloud.java.util.Optional
import com.owncloud.android.MainApp
import com.owncloud.android.R
import com.owncloud.android.datamodel.ArbitraryDataProvider
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl
import com.owncloud.android.datamodel.FileDataStorageManager
import com.owncloud.android.datamodel.FilesystemDataProvider
import com.owncloud.android.datamodel.PushConfigurationState
@ -88,7 +89,7 @@ class AccountRemovalWork(
return Result.failure()
}
val remoteWipe = inputData.getBoolean(REMOTE_WIPE, false)
val arbitraryDataProvider = ArbitraryDataProvider(context.contentResolver)
val arbitraryDataProvider: ArbitraryDataProvider = ArbitraryDataProviderImpl(context)
val user = optionalUser.get()
backgroundJobManager.cancelPeriodicContactsBackup(user)
val userRemoved = userAccountManager.removeUser(user)

View file

@ -55,7 +55,6 @@ import java.io.FileWriter
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
import java.util.ArrayList
import java.util.Calendar
@Suppress("LongParameterList") // legacy code

View file

@ -36,6 +36,7 @@ import com.nextcloud.client.network.ConnectivityService
import com.nextcloud.client.preferences.AppPreferences
import com.owncloud.android.R
import com.owncloud.android.datamodel.ArbitraryDataProvider
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl
import com.owncloud.android.datamodel.FilesystemDataProvider
import com.owncloud.android.datamodel.MediaFolderType
import com.owncloud.android.datamodel.SyncedFolder
@ -133,8 +134,8 @@ class FilesSyncWork(
return
}
val user = optionalUser.get()
val arbitraryDataProvider = if (lightVersion) {
ArbitraryDataProvider(contentResolver)
val arbitraryDataProvider: ArbitraryDataProvider? = if (lightVersion) {
ArbitraryDataProviderImpl(context)
} else {
null
}

View file

@ -46,6 +46,7 @@ import com.nextcloud.client.preferences.AppPreferencesImpl
import com.owncloud.android.MainApp
import com.owncloud.android.R
import com.owncloud.android.datamodel.ArbitraryDataProvider
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl
import com.owncloud.android.datamodel.MediaFolderType
import com.owncloud.android.datamodel.MediaFoldersModel
import com.owncloud.android.datamodel.MediaProvider
@ -84,7 +85,7 @@ class MediaFoldersDetectionWork constructor(
@Suppress("LongMethod", "ComplexMethod", "NestedBlockDepth") // legacy code
override fun doWork(): Result {
val arbitraryDataProvider = ArbitraryDataProvider(contentResolver)
val arbitraryDataProvider: ArbitraryDataProvider = ArbitraryDataProviderImpl(context)
val syncedFolderProvider = SyncedFolderProvider(contentResolver, preferences, clock)
val gson = Gson()
val mediaFoldersModel: MediaFoldersModel

View file

@ -29,6 +29,7 @@ import com.nextcloud.client.account.User;
import com.nextcloud.client.account.UserAccountManager;
import com.nextcloud.client.account.UserAccountManagerImpl;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.ui.activity.PassCodeActivity;
@ -337,7 +338,7 @@ public final class AppPreferencesImpl implements AppPreferences {
return defaultOrder;
}
ArbitraryDataProvider dataProvider = new ArbitraryDataProvider(context.getContentResolver());
ArbitraryDataProvider dataProvider = new ArbitraryDataProviderImpl(context);
String value = dataProvider.getValue(user.getAccountName(), PREF__FOLDER_SORT_ORDER + "_" + type);
@ -347,7 +348,7 @@ public final class AppPreferencesImpl implements AppPreferences {
@Override
public void setSortOrder(FileSortOrder.Type type, FileSortOrder sortOrder) {
User user = userAccountManager.getUser();
ArbitraryDataProvider dataProvider = new ArbitraryDataProvider(context.getContentResolver());
ArbitraryDataProvider dataProvider = new ArbitraryDataProviderImpl(context);
dataProvider.storeOrUpdateKeyValue(user.getAccountName(), PREF__FOLDER_SORT_ORDER + "_" + type, sortOrder.name);
}
@ -604,7 +605,7 @@ public final class AppPreferencesImpl implements AppPreferences {
return defaultValue;
}
ArbitraryDataProvider dataProvider = new ArbitraryDataProvider(context.getContentResolver());
ArbitraryDataProvider dataProvider = new ArbitraryDataProviderImpl(context);
FileDataStorageManager storageManager = new FileDataStorageManager(user, context.getContentResolver());
String value = dataProvider.getValue(user.getAccountName(), getKeyFromFolder(preferenceName, folder));
@ -629,7 +630,7 @@ public final class AppPreferencesImpl implements AppPreferences {
final String preferenceName,
@Nullable final OCFile folder,
final String value) {
ArbitraryDataProvider dataProvider = new ArbitraryDataProvider(context.getContentResolver());
ArbitraryDataProvider dataProvider = new ArbitraryDataProviderImpl(context);
dataProvider.storeOrUpdateKeyValue(user.getAccountName(), getKeyFromFolder(preferenceName, folder), value);
}

View file

@ -62,6 +62,7 @@ import com.nextcloud.client.preferences.DarkMode;
import com.owncloud.android.authentication.AuthenticatorActivity;
import com.owncloud.android.authentication.PassCodeManager;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.datamodel.MediaFolder;
import com.owncloud.android.datamodel.MediaFolderType;
import com.owncloud.android.datamodel.MediaProvider;
@ -413,7 +414,7 @@ public class MainApp extends MultiDexApplication implements HasAndroidInjector {
}
public static void initContactsBackup(UserAccountManager accountManager, BackgroundJobManager backgroundJobManager) {
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(mContext.getContentResolver());
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(mContext);
List<User> users = accountManager.getAllUsers();
for (User user : users) {
if (arbitraryDataProvider.getBooleanValue(user, PREFERENCE_CONTACTS_AUTOMATIC_BACKUP)) {

View file

@ -1,238 +0,0 @@
/*
* Nextcloud Android client application
*
* Copyright (C) 2017 Tobias Kaminsky
* Copyright (C) 2017 Mario Danic
* Copyright (C) 2017 Nextcloud.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.owncloud.android.datamodel;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import com.nextcloud.client.account.User;
import com.owncloud.android.db.ProviderMeta;
import com.owncloud.android.lib.common.utils.Log_OC;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* Database provider for handling the persistence aspects of arbitrary data table.
*/
public class ArbitraryDataProvider {
public static final String DIRECT_EDITING = "DIRECT_EDITING";
public static final String DIRECT_EDITING_ETAG = "DIRECT_EDITING_ETAG";
public static final String PREDEFINED_STATUS = "PREDEFINED_STATUS";
private static final String TAG = ArbitraryDataProvider.class.getSimpleName();
private static final String TRUE = "true";
private ContentResolver contentResolver;
public ArbitraryDataProvider(ContentResolver contentResolver) {
if (contentResolver == null) {
throw new IllegalArgumentException("Cannot create an instance with a NULL contentResolver");
}
this.contentResolver = contentResolver;
}
public int deleteKeyForAccount(String account, String key) {
return contentResolver.delete(
ProviderMeta.ProviderTableMeta.CONTENT_URI_ARBITRARY_DATA,
ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID + " = ? AND " +
ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_KEY + "= ?",
new String[]{account, key}
);
}
public void storeOrUpdateKeyValue(String accountName, String key, long newValue) {
storeOrUpdateKeyValue(accountName, key, String.valueOf(newValue));
}
public void storeOrUpdateKeyValue(final String accountName, final String key, final boolean newValue) {
storeOrUpdateKeyValue(accountName, key, String.valueOf(newValue));
}
public void storeOrUpdateKeyValue(@NonNull String accountName,
@NonNull String key,
@Nullable String newValue) {
ArbitraryDataSet data = getArbitraryDataSet(accountName, key);
String value;
if (newValue == null) {
value = "";
} else {
value = newValue;
}
if (data == null) {
Log_OC.v(TAG, "Adding arbitrary data with cloud id: " + accountName + " key: " + key
+ " value: " + value);
ContentValues cv = new ContentValues();
cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID, accountName);
cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_KEY, key);
cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_VALUE, value);
Uri result = contentResolver.insert(ProviderMeta.ProviderTableMeta.CONTENT_URI_ARBITRARY_DATA, cv);
if (result == null) {
Log_OC.v(TAG, "Failed to store arbitrary data with cloud id: " + accountName + " key: " + key
+ " value: " + value);
}
} else {
Log_OC.v(TAG, "Updating arbitrary data with cloud id: " + accountName + " key: " + key
+ " value: " + value);
ContentValues cv = new ContentValues();
cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID, data.getCloudId());
cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_KEY, data.getKey());
cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_VALUE, value);
int result = contentResolver.update(
ProviderMeta.ProviderTableMeta.CONTENT_URI_ARBITRARY_DATA,
cv,
ProviderMeta.ProviderTableMeta._ID + "=?",
new String[]{String.valueOf(data.getId())}
);
if (result == 0) {
Log_OC.v(TAG, "Failed to update arbitrary data with cloud id: " + accountName + " key: " + key
+ " value: " + value);
}
}
}
Long getLongValue(String accountName, String key) {
String value = getValue(accountName, key);
if (value.isEmpty()) {
return -1L;
} else {
return Long.valueOf(value);
}
}
public Long getLongValue(User user, String key) {
return getLongValue(user.getAccountName(), key);
}
public boolean getBooleanValue(String accountName, String key) {
return TRUE.equalsIgnoreCase(getValue(accountName, key));
}
public boolean getBooleanValue(User user, String key) {
return getBooleanValue(user.getAccountName(), key);
}
/**
* returns integer if found else -1
*
* @param accountName name of account
* @param key key to get value for
* @return Integer specified by account and key
*/
public Integer getIntegerValue(String accountName, String key) {
String value = getValue(accountName, key);
if (value.isEmpty()) {
return -1;
} else {
return Integer.valueOf(value);
}
}
/**
* Returns stored value as string or empty string
*
* @return string if value found or empty string
*/
@NonNull
public String getValue(@Nullable User user, String key) {
return user != null ? getValue(user.getAccountName(), key) : "";
}
public String getValue(String accountName, String key) {
Cursor cursor = contentResolver.query(
ProviderMeta.ProviderTableMeta.CONTENT_URI_ARBITRARY_DATA,
null,
ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID + " = ? and " +
ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_KEY + " = ?",
new String[]{accountName, key},
null
);
if (cursor != null) {
if (cursor.moveToFirst()) {
String value = cursor.getString(cursor.getColumnIndexOrThrow(
ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_VALUE));
if (value == null) {
Log_OC.e(TAG, "Arbitrary value could not be created from cursor");
} else {
cursor.close();
return value;
}
}
cursor.close();
return "";
} else {
Log_OC.e(TAG, "DB error restoring arbitrary values.");
}
return "";
}
private ArbitraryDataSet getArbitraryDataSet(String accountName, String key) {
Cursor cursor = contentResolver.query(
ProviderMeta.ProviderTableMeta.CONTENT_URI_ARBITRARY_DATA,
null,
ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID + " = ? and " +
ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_KEY + " = ?",
new String[]{accountName, key},
null
);
ArbitraryDataSet dataSet = null;
if (cursor != null) {
if (cursor.moveToFirst()) {
int id = cursor.getInt(cursor.getColumnIndexOrThrow(ProviderMeta.ProviderTableMeta._ID));
String dbAccount = cursor.getString(cursor.getColumnIndexOrThrow(
ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID));
String dbKey = cursor.getString(cursor.getColumnIndexOrThrow(
ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_KEY));
String dbValue = cursor.getString(cursor.getColumnIndexOrThrow(
ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_VALUE));
if (id == -1) {
Log_OC.e(TAG, "Arbitrary value could not be created from cursor");
} else {
if (dbValue == null) {
dbValue = "";
}
dataSet = new ArbitraryDataSet(id, dbAccount, dbKey, dbValue);
}
}
cursor.close();
} else {
Log_OC.e(TAG, "DB error restoring arbitrary values.");
}
return dataSet;
}
}

View file

@ -0,0 +1,47 @@
/*
* Nextcloud Android client application
*
* @author Álvaro Brey
* Copyright (C) 2022 Álvaro Brey
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.owncloud.android.datamodel
import com.nextcloud.client.account.User
@Suppress("Detekt.TooManyFunctions") // legacy interface, will get rid of `accountName` methods in the future
interface ArbitraryDataProvider {
fun deleteKeyForAccount(account: String, key: String)
fun storeOrUpdateKeyValue(accountName: String, key: String, newValue: Long)
fun storeOrUpdateKeyValue(accountName: String, key: String, newValue: Boolean)
fun storeOrUpdateKeyValue(accountName: String, key: String, newValue: String)
fun getLongValue(accountName: String, key: String): Long
fun getLongValue(user: User, key: String): Long
fun getBooleanValue(accountName: String, key: String): Boolean
fun getBooleanValue(user: User, key: String): Boolean
fun getIntegerValue(accountName: String, key: String): Int
fun getValue(user: User?, key: String): String
fun getValue(accountName: String, key: String): String
companion object {
const val DIRECT_EDITING = "DIRECT_EDITING"
const val DIRECT_EDITING_ETAG = "DIRECT_EDITING_ETAG"
const val PREDEFINED_STATUS = "PREDEFINED_STATUS"
}
}

View file

@ -0,0 +1,148 @@
/*
* Nextcloud Android client application
*
* Copyright (C) 2017 Tobias Kaminsky
* Copyright (C) 2017 Mario Danic
* Copyright (C) 2017 Nextcloud.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.owncloud.android.datamodel;
import android.content.Context;
import com.nextcloud.client.account.User;
import com.nextcloud.client.database.NextcloudDatabase;
import com.nextcloud.client.database.dao.ArbitraryDataDao;
import com.nextcloud.client.database.entity.ArbitraryDataEntity;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* Database provider for handling the persistence aspects of arbitrary data table.
* <p>
* Don't instantiate this class, inject the interface instead.
*/
public class ArbitraryDataProviderImpl implements ArbitraryDataProvider {
private static final String TRUE = "true";
private final ArbitraryDataDao arbitraryDataDao;
/**
* @deprecated inject interface instead
*/
@Deprecated
public ArbitraryDataProviderImpl(final Context context) {
this(NextcloudDatabase.getInstance(context).arbitraryDataDao());
}
public ArbitraryDataProviderImpl(@NonNull final ArbitraryDataDao dao) {
this.arbitraryDataDao = dao;
}
@Override
public void deleteKeyForAccount(@NonNull String account, @NonNull String key) {
arbitraryDataDao.deleteValue(account, key);
}
@Override
public void storeOrUpdateKeyValue(@NonNull String accountName, @NonNull String key, long newValue) {
storeOrUpdateKeyValue(accountName, key, String.valueOf(newValue));
}
@Override
public void storeOrUpdateKeyValue(@NonNull final String accountName, @NonNull final String key, final boolean newValue) {
storeOrUpdateKeyValue(accountName, key, String.valueOf(newValue));
}
@Override
public void storeOrUpdateKeyValue(@NonNull String accountName,
@NonNull String key,
@Nullable String newValue) {
final ArbitraryDataEntity currentValue = arbitraryDataDao.getByAccountAndKey(accountName, key);
if (currentValue != null) {
arbitraryDataDao.updateValue(accountName, key, newValue);
} else {
arbitraryDataDao.insertValue(accountName, key, newValue);
}
}
@Override
public long getLongValue(@NonNull String accountName, @NonNull String key) {
String value = getValue(accountName, key);
if (value.isEmpty()) {
return -1L;
} else {
return Long.parseLong(value);
}
}
@Override
public long getLongValue(User user, @NonNull String key) {
return getLongValue(user.getAccountName(), key);
}
@Override
public boolean getBooleanValue(@NonNull String accountName, @NonNull String key) {
return TRUE.equalsIgnoreCase(getValue(accountName, key));
}
@Override
public boolean getBooleanValue(User user, @NonNull String key) {
return getBooleanValue(user.getAccountName(), key);
}
/**
* returns integer if found else -1
*
* @param accountName name of account
* @param key key to get value for
* @return Integer specified by account and key
*/
@Override
public int getIntegerValue(@NonNull String accountName, @NonNull String key) {
String value = getValue(accountName, key);
if (value.isEmpty()) {
return -1;
} else {
return Integer.parseInt(value);
}
}
/**
* Returns stored value as string or empty string
*
* @return string if value found or empty string
*/
@Override
@NonNull
public String getValue(@Nullable User user, @NonNull String key) {
return user != null ? getValue(user.getAccountName(), key) : "";
}
@Override
@NonNull
public String getValue(@NonNull String accountName, @NonNull String key) {
final ArbitraryDataEntity entity = arbitraryDataDao.getByAccountAndKey(accountName, key);
if (entity == null || entity.getValue() == null) {
return "";
}
return entity.getValue();
}
}

View file

@ -1,27 +0,0 @@
/*
*
* Nextcloud Android client application
*
* @author Tobias Kaminsky
* Copyright (C) 2020 Tobias Kaminsky
* Copyright (C) 2020 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.owncloud.android.datamodel
/**
* Data set for [ArbitraryDataProvider]
*/
internal class ArbitraryDataSet(val id: Int, val cloudId: String, val key: String, val value: String)

View file

@ -1047,7 +1047,7 @@ public final class ThumbnailsCacheManager {
String accountName = mUserId + "@" + mServerName;
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(mContext.getContentResolver());
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(mContext);
String eTag = arbitraryDataProvider.getValue(accountName, ThumbnailsCacheManager.AVATAR);
long timestamp = arbitraryDataProvider.getLongValue(accountName, ThumbnailsCacheManager.AVATAR_TIMESTAMP);

View file

@ -72,8 +72,6 @@ public class ProviderMeta {
+ MainApp.getAuthority() + "/synced_folders");
public static final Uri CONTENT_URI_EXTERNAL_LINKS = Uri.parse(CONTENT_PREFIX
+ MainApp.getAuthority() + "/external_links");
public static final Uri CONTENT_URI_ARBITRARY_DATA = Uri.parse(CONTENT_PREFIX
+ MainApp.getAuthority() + "/arbitrary_data");
public static final Uri CONTENT_URI_VIRTUAL = Uri.parse(CONTENT_PREFIX + MainApp.getAuthority() + "/virtual");
public static final Uri CONTENT_URI_FILESYSTEM = Uri.parse(CONTENT_PREFIX
+ MainApp.getAuthority() + "/filesystem");

View file

@ -26,6 +26,7 @@ import android.util.Pair;
import com.nextcloud.client.account.User;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.datamodel.DecryptedFolderMetadata;
import com.owncloud.android.datamodel.EncryptedFolderMetadata;
import com.owncloud.android.datamodel.FileDataStorageManager;
@ -106,7 +107,7 @@ public class CreateFolderOperation extends SyncOperation implements OnRemoteOper
}
private RemoteOperationResult encryptedCreate(OCFile parent, OwnCloudClient client) {
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver());
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(context);
String privateKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PRIVATE_KEY);
String publicKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PUBLIC_KEY);

View file

@ -28,6 +28,7 @@ import com.nextcloud.android.lib.resources.directediting.DirectEditingObtainRemo
import com.nextcloud.client.account.User;
import com.nextcloud.common.NextcloudClient;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.datamodel.DecryptedFolderMetadata;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
@ -296,7 +297,7 @@ public class RefreshFolderOperation extends RemoteOperation {
}
private void updateCapabilities() {
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(mContext.getContentResolver());
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(mContext);
String oldDirectEditingEtag = arbitraryDataProvider.getValue(user,
ArbitraryDataProvider.DIRECT_EDITING_ETAG);

View file

@ -26,6 +26,7 @@ import android.content.Context;
import com.google.gson.reflect.TypeToken;
import com.nextcloud.client.account.User;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.datamodel.DecryptedFolderMetadata;
import com.owncloud.android.datamodel.EncryptedFolderMetadata;
import com.owncloud.android.lib.common.OwnCloudClient;
@ -84,7 +85,7 @@ public class RemoveRemoteEncryptedFileOperation extends RemoteOperation {
this.user = user;
this.fileName = fileName;
arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver());
arbitraryDataProvider = new ArbitraryDataProviderImpl(context);
}
/**

View file

@ -33,6 +33,7 @@ import com.nextcloud.client.device.PowerManagementService;
import com.nextcloud.client.network.Connectivity;
import com.nextcloud.client.network.ConnectivityService;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.datamodel.DecryptedFolderMetadata;
import com.owncloud.android.datamodel.EncryptedFolderMetadata;
import com.owncloud.android.datamodel.FileDataStorageManager;
@ -455,7 +456,7 @@ public class UploadFileOperation extends SyncOperation {
boolean metadataExists = false;
String token = null;
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContext().getContentResolver());
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(getContext());
String privateKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PRIVATE_KEY);
String publicKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PUBLIC_KEY);

View file

@ -75,14 +75,12 @@ public class FileContentProvider extends ContentProvider {
private static final int UPLOADS = 6;
private static final int SYNCED_FOLDERS = 7;
private static final int EXTERNAL_LINKS = 8;
private static final int ARBITRARY_DATA = 9;
private static final int VIRTUAL = 10;
private static final int FILESYSTEM = 11;
private static final String TAG = FileContentProvider.class.getSimpleName();
// todo avoid string concatenation and use string formatting instead later.
private static final String ERROR = "ERROR ";
private static final int SINGLE_PATH_SEGMENT = 1;
public static final int ARBITRARY_DATA_TABLE_INTRODUCTION_VERSION = 20;
public static final int MINIMUM_PATH_SEGMENTS_SIZE = 1;
private static final String[] PROJECTION_CONTENT_TYPE = new String[]{
@ -160,9 +158,6 @@ public class FileContentProvider extends ContentProvider {
case EXTERNAL_LINKS:
count = db.delete(ProviderTableMeta.EXTERNAL_LINKS_TABLE_NAME, where, whereArgs);
break;
case ARBITRARY_DATA:
count = db.delete(ProviderTableMeta.ARBITRARY_DATA_TABLE_NAME, where, whereArgs);
break;
case VIRTUAL:
count = db.delete(ProviderTableMeta.VIRTUAL_TABLE_NAME, where, whereArgs);
break;
@ -361,16 +356,6 @@ public class FileContentProvider extends ContentProvider {
}
return insertedExternalLinkUri;
case ARBITRARY_DATA:
Uri insertedArbitraryDataUri;
long arbitraryDataId = db.insert(ProviderTableMeta.ARBITRARY_DATA_TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values);
if (arbitraryDataId > 0) {
insertedArbitraryDataUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_ARBITRARY_DATA,
arbitraryDataId);
} else {
throw new SQLException("ERROR " + uri);
}
return insertedArbitraryDataUri;
case VIRTUAL:
Uri insertedVirtualUri;
long virtualId = db.insert(ProviderTableMeta.VIRTUAL_TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values);
@ -456,7 +441,6 @@ public class FileContentProvider extends ContentProvider {
mUriMatcher.addURI(authority, "uploads/#", UPLOADS);
mUriMatcher.addURI(authority, "synced_folders", SYNCED_FOLDERS);
mUriMatcher.addURI(authority, "external_links", EXTERNAL_LINKS);
mUriMatcher.addURI(authority, "arbitrary_data", ARBITRARY_DATA);
mUriMatcher.addURI(authority, "virtual", VIRTUAL);
mUriMatcher.addURI(authority, "filesystem", FILESYSTEM);
@ -525,9 +509,6 @@ public class FileContentProvider extends ContentProvider {
case EXTERNAL_LINKS:
tableName = ProviderTableMeta.EXTERNAL_LINKS_TABLE_NAME;
break;
case ARBITRARY_DATA:
tableName = ProviderTableMeta.ARBITRARY_DATA_TABLE_NAME;
break;
case VIRTUAL:
tableName = ProviderTableMeta.VIRTUAL_TABLE_NAME;
break;
@ -567,9 +548,6 @@ public class FileContentProvider extends ContentProvider {
case EXTERNAL_LINKS:
order = ProviderTableMeta.EXTERNAL_LINKS_NAME;
break;
case ARBITRARY_DATA:
order = ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID;
break;
case VIRTUAL:
order = ProviderTableMeta.VIRTUAL_TYPE;
break;
@ -659,8 +637,6 @@ public class FileContentProvider extends ContentProvider {
return db.update(ProviderTableMeta.UPLOADS_TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values, selection, selectionArgs);
case SYNCED_FOLDERS:
return db.update(ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values, selection, selectionArgs);
case ARBITRARY_DATA:
return db.update(ProviderTableMeta.ARBITRARY_DATA_TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values, selection, selectionArgs);
case FILESYSTEM:
return db.update(ProviderTableMeta.FILESYSTEM_TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values, selection, selectionArgs);
default:
@ -699,7 +675,6 @@ public class FileContentProvider extends ContentProvider {
case UPLOADS:
case SYNCED_FOLDERS:
case EXTERNAL_LINKS:
case ARBITRARY_DATA:
case VIRTUAL:
case FILESYSTEM:
String callingPackage = mContext.getPackageManager().getNameForUid(Binder.getCallingUid());

View file

@ -40,6 +40,7 @@ import com.nextcloud.client.account.User;
import com.nextcloud.client.account.UserAccountManager;
import com.owncloud.android.R;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
@ -377,7 +378,7 @@ public class UsersAndGroupsSearchProvider extends ContentProvider {
@Override
@SuppressFBWarnings("IOI_USE_OF_FILE_STREAM_CONSTRUCTORS") // TODO remove with API26
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContext().getContentResolver());
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(getContext());
String userId = uri.getQueryParameter("shareWith");
String displayName = uri.getQueryParameter("displayName");

View file

@ -51,7 +51,6 @@ import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
import android.webkit.URLUtil;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@ -78,6 +77,7 @@ import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.authentication.PassCodeManager;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.datamodel.ExternalLinksProvider;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.common.ExternalLink;
@ -907,7 +907,7 @@ public abstract class DrawerActivity extends ToolbarActivity
}
externalLinksProvider = new ExternalLinksProvider(getContentResolver());
arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver());
arbitraryDataProvider = new ArbitraryDataProviderImpl(this);
}
@Override

View file

@ -50,6 +50,7 @@ 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.OCFile;
import com.owncloud.android.files.services.FileDownloader;
import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
@ -644,7 +645,7 @@ public abstract class FileActivity extends DrawerActivity
public void checkForNewDevVersionNecessary(Context context) {
if (getResources().getBoolean(R.bool.dev_version_direct_download_enabled)) {
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver());
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(this);
int count = arbitraryDataProvider.getIntegerValue(FilesSyncHelper.GLOBAL, APP_OPENED_COUNT);
if (count > 10 || count == -1) {

View file

@ -48,6 +48,7 @@ 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.FileDataStorageManager;
import com.owncloud.android.files.services.FileDownloader;
import com.owncloud.android.files.services.FileUploader;
@ -142,7 +143,7 @@ public class ManageAccountsActivity extends FileActivity implements UserListAdap
originalCurrentUser = currentUser.get().getAccountName();
}
arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver());
arbitraryDataProvider = new ArbitraryDataProviderImpl(this);
multipleAccountsSupported = getResources().getBoolean(R.bool.multiaccount_support);
@ -426,7 +427,7 @@ public class ManageAccountsActivity extends FileActivity implements UserListAdap
}
// store pending account removal
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver());
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(this);
arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(), PENDING_FOR_REMOVAL, String.valueOf(true));
// Cancel transfers

View file

@ -38,6 +38,7 @@ import com.nextcloud.java.util.Optional;
import com.owncloud.android.R;
import com.owncloud.android.databinding.NotificationsLayoutBinding;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
@ -146,7 +147,7 @@ public class NotificationsActivity extends DrawerActivity implements Notificatio
R.string.push_notifications_not_implemented,
Snackbar.LENGTH_INDEFINITE);
} else {
final ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver());
final ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(this);
final String accountName = optionalUser.isPresent() ? optionalUser.get().getAccountName() : "";
final boolean usesOldLogin = arbitraryDataProvider.getBooleanValue(accountName,
UserAccountManager.ACCOUNT_USES_STANDARD_PASSWORD);

View file

@ -62,6 +62,7 @@ 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;
@ -606,7 +607,7 @@ public class SettingsActivity extends PreferenceActivity
preferenceScreen.removePreference(preferenceCategorySyncedFolders);
} else {
// Upload on WiFi
final ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver());
final ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(this);
final SwitchPreference pUploadOnWifiCheckbox = (SwitchPreference) findPreference("synced_folder_on_wifi");
pUploadOnWifiCheckbox.setChecked(
@ -872,7 +873,7 @@ public class SettingsActivity extends PreferenceActivity
RequestCredentialsActivity.KEY_CHECK_RESULT_FALSE) ==
RequestCredentialsActivity.KEY_CHECK_RESULT_TRUE) {
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver());
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(this);
String mnemonic = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.MNEMONIC);
AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.FallbackTheming_Dialog);

View file

@ -48,7 +48,7 @@ import com.owncloud.android.BuildConfig
import com.owncloud.android.MainApp
import com.owncloud.android.R
import com.owncloud.android.databinding.SyncedFoldersLayoutBinding
import com.owncloud.android.datamodel.ArbitraryDataProvider
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl
import com.owncloud.android.datamodel.MediaFolder
import com.owncloud.android.datamodel.MediaFolderType
import com.owncloud.android.datamodel.MediaProvider
@ -696,14 +696,16 @@ class SyncedFoldersActivity :
backgroundJobManager.startImmediateFilesSyncJob(skipCustomFolders = false, overridePowerSaving = false)
} else {
val syncedFolderInitiatedKey = KEY_SYNCED_FOLDER_INITIATED_PREFIX + item.id
val arbitraryDataProvider = ArbitraryDataProvider(MainApp.getAppContext().contentResolver)
val arbitraryDataProvider =
ArbitraryDataProviderImpl(MainApp.getAppContext())
arbitraryDataProvider.deleteKeyForAccount("global", syncedFolderInitiatedKey)
}
}
}
private fun storeSyncedFolder(item: SyncedFolderDisplayItem) {
val arbitraryDataProvider = ArbitraryDataProvider(MainApp.getAppContext().contentResolver)
val arbitraryDataProvider =
ArbitraryDataProviderImpl(MainApp.getAppContext())
val storedId = syncedFolderProvider.storeSyncedFolder(item)
if (storedId != -1L) {
item.id = storedId

View file

@ -36,6 +36,7 @@ import com.nextcloud.client.di.Injectable;
import com.owncloud.android.R;
import com.owncloud.android.databinding.SetupEncryptionDialogBinding;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
@ -132,7 +133,7 @@ public class SetupEncryptionDialogFragment extends DialogFragment implements Inj
public Dialog onCreateDialog(Bundle savedInstanceState) {
user = getArguments().getParcelable(ARG_USER);
arbitraryDataProvider = new ArbitraryDataProvider(getContext().getContentResolver());
arbitraryDataProvider = new ArbitraryDataProviderImpl(getContext());
// Inflate the layout for the dialog
LayoutInflater inflater = getActivity().getLayoutInflater();

View file

@ -33,6 +33,7 @@ import com.owncloud.android.R;
import com.owncloud.android.databinding.FileListActionsBottomSheetCreatorBinding;
import com.owncloud.android.databinding.FileListActionsBottomSheetFragmentBinding;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.common.Creator;
import com.owncloud.android.lib.common.DirectEditing;
@ -103,7 +104,7 @@ public class OCFileListBottomSheetDialog extends BottomSheetDialog implements In
binding.templates.setVisibility(View.VISIBLE);
}
String json = new ArbitraryDataProvider(getContext().getContentResolver())
String json = new ArbitraryDataProviderImpl(getContext())
.getValue(user, ArbitraryDataProvider.DIRECT_EDITING);
if (!json.isEmpty() &&

View file

@ -55,6 +55,7 @@ import com.nextcloud.utils.EditorUtils;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.files.StreamMediaFileOperation;
@ -1120,7 +1121,7 @@ public class FileOperationsHelper {
}
public static boolean isEndToEndEncryptionSetup(Context context, User user) {
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver());
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(context);
String publicKey = arbitraryDataProvider.getValue(user, EncryptionUtils.PUBLIC_KEY);
String privateKey = arbitraryDataProvider.getValue(user, EncryptionUtils.PRIVATE_KEY);

View file

@ -69,6 +69,7 @@ import com.nextcloud.client.preferences.AppPreferences;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
@ -491,7 +492,7 @@ public final class DisplayUtils {
((View) callContext).setContentDescription(String.valueOf(user.toPlatformAccount().hashCode()));
}
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver());
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(context);
final String accountName = user.getAccountName();
String serverName = accountName.substring(accountName.lastIndexOf('@') + 1);

View file

@ -31,6 +31,7 @@ import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.nextcloud.client.account.User;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.datamodel.DecryptedFolderMetadata;
import com.owncloud.android.datamodel.EncryptedFolderMetadata;
import com.owncloud.android.datamodel.OCFile;
@ -231,7 +232,7 @@ public final class EncryptionUtils {
}
// decrypt metadata
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver());
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(context);
String serializedEncryptedMetadata = (String) getMetadataOperationResult.getData().get(0);
String privateKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PRIVATE_KEY);

View file

@ -262,6 +262,7 @@
<string name="error_comment_file">Грешка при коментиране на файл</string>
<string name="error_crash_title">%1$s крах</string>
<string name="error_creating_file_from_template">Грешка при създаването на файл от шаблон</string>
<string name="error_file_actions">Грешка при показване на действията на файла</string>
<string name="error_file_lock">Грешка при промяна на състоянието на заключване на файла</string>
<string name="error_report_issue_action">Докладване за грешка</string>
<string name="error_report_issue_text">Подаване на сигнал за проблем към тракера? (изисква се профил в Github)</string>

View file

@ -261,6 +261,7 @@
<string name="error_comment_file">Chyba při přidávání komentáře k souboru</string>
<string name="error_crash_title">%1$s zhavarovalo</string>
<string name="error_creating_file_from_template">Chyba při vytváření souboru ze šablony</string>
<string name="error_file_actions">Chyba při zobrazování akcí ohledně souboru.</string>
<string name="error_file_lock">Chyba při změně stavu zámku souboru</string>
<string name="error_report_issue_action">Hlášení</string>
<string name="error_report_issue_text">Nahlásit problém? (vyžaduje GitHub účet)</string>

View file

@ -261,6 +261,7 @@
<string name="error_comment_file">Fehler beim Kommentieren der Datei</string>
<string name="error_crash_title">%1$s abgestürzt</string>
<string name="error_creating_file_from_template">Fehler beim Erzeugen einer Datei aus der Vorlage</string>
<string name="error_file_actions">Fehler beim Anzeigen von Dateiaktionen</string>
<string name="error_file_lock">Fehler beim Ändern des Sperr-Status</string>
<string name="error_report_issue_action">Melden</string>
<string name="error_report_issue_text">Problem melden? (benötigt ein GitHub-Konto)</string>

View file

@ -261,6 +261,7 @@
<string name="error_comment_file">Error al comentar el archivo</string>
<string name="error_crash_title">%1$s se bloqueó</string>
<string name="error_creating_file_from_template">Error al crear el archivo desde la plantilla</string>
<string name="error_file_actions">Error al mostrar las acciones de archivo</string>
<string name="error_file_lock">Error al cambiar el estado de bloqueo de archivo</string>
<string name="error_report_issue_action">Informe</string>
<string name="error_report_issue_text">¿Informar del problema al tracker? (requiere una cuenta de GitHub)</string>

View file

@ -14,6 +14,7 @@
<string name="action_send_share">ارسال/اشتراک گذاری</string>
<string name="action_switch_grid_view">نمایش گرید</string>
<string name="action_switch_list_view">نمایش لیست</string>
<string name="actionbar_calendar_contacts_restore">بازگردانی آشنایان و تقویم</string>
<string name="actionbar_copy">کپی کردن</string>
<string name="actionbar_mkdir">پوشه جدید</string>
<string name="actionbar_move">انتقال</string>
@ -25,14 +26,16 @@
<string name="actionbar_sort">مرتب‌سازی</string>
<string name="active_user">کاربر فعال</string>
<string name="activities_no_results_headline">هنوز فعّالیت ندارد</string>
<string name="activities_no_results_message">رویدادی نظیر افزودن، تغییرات و به‌اشتراک‌گذاری هنوز وجود ندارد.</string>
<string name="activities_no_results_message">هیچ رویدادی چون افزودن، تغییرات و هم‌رسانی‌ها وجود ندارد.</string>
<string name="activity_chooser_send_file_title">ارسال</string>
<string name="activity_chooser_title">ارسال لینک به ....</string>
<string name="activity_icon">فعّالیت</string>
<string name="add_another_public_share_link">پیوند دیگری اضافه کنید</string>
<string name="add_another_public_share_link">افزودن پیوندی دیگر</string>
<string name="add_new_public_share">افزودن پیوند هم‌رسانی عمومی جدید</string>
<string name="add_to_cloud">افزودن به %1$s</string>
<string name="advanced_settings">تنظمیات پیش‌رفته</string>
<string name="allow_resharing">مجاز به اشتراک گذاری مجدد</string>
<string name="app_widget_description">نمایش یک ابزارک از پیشخان</string>
<string name="appbar_search_in">جستجو در %s</string>
<string name="associated_account_not_found">حساب مرتبط یافت نشد!</string>
<string name="auth_access_failed">دسترسی خطای %1$s</string>
@ -74,19 +77,24 @@
<string name="autoupload_hide_folder">مخفی کردن پوشه</string>
<string name="avatar">آواتار</string>
<string name="away">دور</string>
<string name="backup_settings">تنظیمات پشتیبان</string>
<string name="backup_title">پشتیبان از آشنایان و تقویم</string>
<string name="battery_optimization_close">بستن</string>
<string name="battery_optimization_disable">غیرفعال کردن</string>
<string name="battery_optimization_message">ممکن است وسیله شما بهینه ساز باطری را فعال کرده باشد. بارگذاری خودکار به درستی کار می کند اگر این برنامه را از آن حذف کنید.</string>
<string name="battery_optimization_title">بهینه سازی باتری</string>
<string name="brute_force_delay">به دلیل تلاش‌های نادرست متعدد، به تاخیر افتاد</string>
<string name="calendar">تقویم</string>
<string name="calendars">تقویم‌ها</string>
<string name="certificate_load_problem">در حال بارگذاری گواهینامه، یک مشکل وجود دارد.</string>
<string name="changelog_dev_version">تغییرات نسخه در حال توسعه</string>
<string name="checkbox">جعبه علامت</string>
<string name="choose_local_folder">پوشه محلی را انتخاب کردن...</string>
<string name="choose_location">گزینش مکان</string>
<string name="choose_remote_folder">پوشه راه دور را انتخاب کردن ...</string>
<string name="choose_template_helper_text">لطفا یک قالب را انتخاب و نامی برای پرونده وارد کنید.</string>
<string name="choose_which_file">انتخاب کنید کدام فایل را نگه‌ دارید!</string>
<string name="choose_widget">گزینش ابزارک</string>
<string name="clear_notifications_failed">ناموفق بودن در پاک کردن اعلانها</string>
<string name="clear_status_message">پیام وضعیت را پاک کن</string>
<string name="clear_status_message_after">"بعد از آن پیام وضعیت را پاک کن "</string>
@ -144,7 +152,9 @@
<string name="confirmation_remove_local">فقط محلی</string>
<string name="conflict_dialog_error">هنگام ساختن دیالوگ مغایرت‌ها خطایی رخ داده است!</string>
<string name="conflict_file_headline">فایل متناقض %1$s</string>
<string name="conflict_local_file">پروندهٔ محلّی</string>
<string name="conflict_message_description">اگر هردو نسخه را انتخاب کنید، یک شماره به نام فایل محلی اضافه خواهد شد.</string>
<string name="conflict_server_file">پروندهٔ کارساز</string>
<string name="contactlist_item_icon">آیکون کاربر برای لیست تماس</string>
<string name="contactlist_no_permission">هیچ مجوزی داده نشده ، هیچ چیز وارد نشده است.</string>
<string name="contacts">مخاطبین</string>
@ -174,12 +184,16 @@
<string name="create_rich_workspace">اطلاعات پوشه را اضافه کنید</string>
<string name="creates_rich_workspace">ایجاد اطلاعات پوشه</string>
<string name="credentials_disabled">اعتبارنامه غیرفعال است</string>
<string name="daily_backup">پشتیبان روزانه</string>
<string name="data_to_back_up">داده برای پشتیبان‌گیری</string>
<string name="date_unknown">ناشناخته</string>
<string name="default_credentials_wrong">اعتبارنامه نادرست است</string>
<string name="delete_account">حذف حساب کاربری</string>
<string name="delete_account_warning">برداشتن حساب %s و حذف تمام پرونده‌ّای محلّی؟\n\nحذف قابل بازگردانی نیست.</string>
<string name="delete_entries">ورودی ها را حذف کنید</string>
<string name="delete_link">حذف پیوند</string>
<string name="deselect_all">لغو انتخاب همه</string>
<string name="destination_filename">نام پروندهٔ مقصد</string>
<string name="dev_version_new_version_available">نسخه جدید در دسترس است</string>
<string name="dev_version_no_information_available">هیچ اطلاعاتی در دسترس نیست</string>
<string name="dev_version_no_new_version_available">نسخه جدید موجود نیست</string>
@ -358,6 +372,9 @@
<string name="hint_password">گذرواژه</string>
<string name="host_not_available">سرور در دسترس نیست</string>
<string name="host_your_own_server">میزبان سرور خود باشید</string>
<string name="icon_for_empty_list">نقشک برای سیاههٔ خالی</string>
<string name="icon_of_dashboard_widget">نقشک ابزارک پیشخان</string>
<string name="icon_of_widget_entry">نقشک ورودی ابزارک</string>
<string name="in_folder">در پوشه %1$s</string>
<string name="instant_upload_existing">فایل های موجود را نیز بارگذاری کنید</string>
<string name="instant_upload_on_charging">فقط هنگامی که گوشی شارژ می شود آپلود کنید</string>
@ -365,10 +382,15 @@
<string name="invalid_url">نشانی وب نامعتبر است</string>
<string name="invisible">نامرئی</string>
<string name="label_empty">برچسب نمی تواند خالی باشد</string>
<string name="last_backup">آخرین پشتیبان: %1$s</string>
<string name="link">پیوند</string>
<string name="link_name">نام پیوند</string>
<string name="link_share_allow_upload_and_editing">مجاز به بارگذاری و ویرایش</string>
<string name="link_share_editing">ویرایش کردن</string>
<string name="link_share_file_drop">انداختن فایل (فقط آپلود)</string>
<string name="link_share_view_only">تنها مشاهده</string>
<string name="list_layout">Listed layout</string>
<string name="load_more_results">بار کردن نتیحه‌های بیش‌تر</string>
<string name="local_file_list_empty">هیچ فایلی در این پوشه نیست.</string>
<string name="local_file_not_found_message">فایل در فایل‌های سیستمی محلی یافت نشد</string>
<string name="local_folder_friendly_path">%1$s/%2$s</string>
@ -576,54 +598,62 @@
<string name="set_status">تنظیم وضعیت</string>
<string name="set_status_message">تنظیم پیام وضعیت</string>
<string name="share">هم‌رسانی</string>
<string name="share_copy_link">هم‌رسانی و رونوشت پیوند</string>
<string name="share_dialog_title">اشتراک گذاری</string>
<string name="share_expiration_date_label">منقضی می شود %1$s</string>
<string name="share_file">اشتراک گذاری%1$s</string>
<string name="share_expiration_date_format">%1$s</string>
<string name="share_expiration_date_label">منقضی در%1$s</string>
<string name="share_file">هم‌رسانی %1$s</string>
<string name="share_group_clarification">%1$s (گروه)</string>
<string name="share_internal_link">پیوند داخلی را به اشتراک بگذارید</string>
<string name="share_internal_link_to_file_text">پیوند اشتراک‌گذاری داخلی فقط برای کاربرانی که به این فایل دسترسی دارند کار می‌کند</string>
<string name="share_internal_link_to_folder_text">پیوند اشتراک‌گذاری داخلی فقط برای کاربرانی که به این پوشه دسترسی دارند کار می‌کند</string>
<string name="share_known_remote_on_clarification">بر روی1 %1$s</string>
<string name="share_link">لینک را به اشتراک بگذارید</string>
<string name="share_internal_link">هم‌رسانی پیوند داخلی</string>
<string name="share_internal_link_to_file_text">پیوند هم‌رسانی داخلی تنها برای کاربرای کار می‌کند که به این پرونده دسترسی داشته باشند</string>
<string name="share_internal_link_to_folder_text">پیوند هم‌رسانی داخلی تنها برای کاربرای کار می‌کند که به این شاخه دسترسی داشته باشند</string>
<string name="share_known_remote_on_clarification">روی %1$s</string>
<string name="share_link">هم‌رسانی پیوند</string>
<string name="share_link_empty_password">شما باید یک رمزعبور را وارد کنید</string>
<string name="share_link_file_error">هنگام اشتراک‌گذاری این فایل یا پوشه خطایی رخ داده است.</string>
<string name="share_link_file_no_exist">امکان اشتراک‌گذاری وجود ندارد. لطفا موجود بودن فایل را بررسی کنید.</string>
<string name="share_link_file_error">هنگام تلاش برای هم‌رسانی این پرونده یا شاخه خطایی رخ داد.</string>
<string name="share_link_file_no_exist">ناتوان در هم‌رسانی. لطفاً وجود پرونده را بررسی کنید.</string>
<string name="share_link_forbidden_permissions">اشتراک‌گذاری این فایل</string>
<string name="share_link_optional_password_title">یک رمزعبور اختیاری وارد کنید</string>
<string name="share_link_optional_password_title">گذرواژه‌ای اختیاری وارد کنید</string>
<string name="share_link_password_title">یک رمزعبور را وارد کنید</string>
<string name="share_link_with_label">به‌اشتراک‌گذاری پیوند (%1$s)</string>
<string name="share_link_with_label">هم‌رسانی پیوند (%1$s)</string>
<string name="share_no_expiration_date_label">تنظیم تاریخ انقضا</string>
<string name="share_no_password_title">تنظیم رمز عبور</string>
<string name="share_password_title">رمز عبور محافظت شده</string>
<string name="share_permission_can_edit">می‌تواند ویرایش کند</string>
<string name="share_remote_clarification">%1$s (از راه دور)</string>
<string name="share_room_clarification">%1$s (گفتگو)</string>
<string name="share_search">نام، Federated cloud ID یا ایمیل</string>
<string name="share_no_password_title">تنظیم گذرواژه</string>
<string name="share_password_title">محافظت شده با گذرواژه</string>
<string name="share_permission_can_edit">توانایی ویرایش</string>
<string name="share_permission_file_drop">انداختن پرونده</string>
<string name="share_permission_view_only">تنها مشاهده</string>
<string name="share_permissions">اجازه‌های هم‌رسانی</string>
<string name="share_remote_clarification">%1$s (دوردست)</string>
<string name="share_room_clarification">%1$s (گفت‌وگو)</string>
<string name="share_search">نام، شناسهٔ ابری خودگردان یا نشانی رایانامه…</string>
<string name="share_send_new_email">فرستادن رایانامهٔ جدید</string>
<string name="share_send_note">یادداشت به گیرنده</string>
<string name="share_settings">تنظیمات</string>
<string name="share_via_link_hide_download">پنهان کردن دریافت</string>
<string name="share_via_link_hide_download">نهفتن بارگیری</string>
<string name="share_via_link_section_title">اشتراک گذاشتن لینک</string>
<string name="share_via_link_send_link_label">ارسال پیوند</string>
<string name="share_via_link_unset_password">تنظیم نشده است</string>
<string name="share_with_title">به اشتراک گذاشتن با…</string>
<string name="shared_avatar_desc">آواتار از کاربر مشترک</string>
<string name="shared_icon_share">اشتراک گذاری</string>
<string name="shared_icon_shared">به اشتراک گذاشته شد</string>
<string name="shared_icon_shared_via_link">از طریق پیوند به اشتراک گذاشته شد</string>
<string name="shared_with_you_by">به اشتراک گذاشه با شما توسط %1$s</string>
<string name="sharee_add_failed">افزودن اشتراک‌گذاری انجام نشد</string>
<string name="share_via_link_send_link_label">فرستادن پیوند</string>
<string name="share_via_link_unset_password">ناتنظیم</string>
<string name="share_with_title">هم‌رسانی با…</string>
<string name="shared_avatar_desc">چهرک از کاربر مشترک</string>
<string name="shared_icon_share">هم‌رسانی</string>
<string name="shared_icon_shared">هم‌رسانده</string>
<string name="shared_icon_shared_via_link">هم‌رسانده با پیوند</string>
<string name="shared_with_you_by">هم‌رسانده با شما به دست %1$s</string>
<string name="sharee_add_failed">شکست در افزودن هم‌رسانی</string>
<string name="show_images">نمایش عکس‌ها</string>
<string name="show_video">نمایش ویدیوها</string>
<string name="signup_with_provider">ثبت‌نام با ارائه دهنده</string>
<string name="single_sign_on_request_token" formatted="true">اجازه %1$s برای دسترسی به اکانت نکست‌کلود %2$s؟</string>
<string name="single_sign_on_request_token" formatted="true">اجازه به %1$s برای دسترسی به حساب نکست‌کلود %2$s؟</string>
<string name="sort_by">مرتب سازی بر اساس</string>
<string name="sort_by_modification_date_ascending">"تازه‌ترین‌ها اول "</string>
<string name="sort_by_modification_date_descending">"قدیمی‌ترین‌ها اول "</string>
<string name="sort_by_name_ascending">الف تا ی</string>
<string name="sort_by_name_descending">ی تا الف</string>
<string name="sort_by_size_ascending">کوچک‌ترین اول</string>
<string name="sort_by_size_descending">بزرگترین اول</string>
<string name="sort_by_modification_date_ascending">نخست جدیدترین</string>
<string name="sort_by_modification_date_descending">نخست قدیمی‌ترین</string>
<string name="sort_by_name_ascending">آ - ی</string>
<string name="sort_by_name_descending">ی - آ</string>
<string name="sort_by_size_ascending">نخست کوچک‌ترین</string>
<string name="sort_by_size_descending">نخست بزرگترین</string>
<string name="ssl_validator_btn_details_hide">پنهان کردن</string>
<string name="ssl_validator_btn_details_see">جزییات</string>
<string name="ssl_validator_header">هویت سرور قابل تأیید نیست</string>
<string name="ssl_validator_header">هویت کارساز نتوانست تأیید شود</string>
<string name="ssl_validator_label_C">کشور:</string>
<string name="ssl_validator_label_CN">نام مشترک:</string>
<string name="ssl_validator_label_L">مکان:</string>
@ -639,28 +669,30 @@
<string name="ssl_validator_label_validity_from">از:</string>
<string name="ssl_validator_label_validity_to">به:</string>
<string name="ssl_validator_no_info_about_error">هیچ گونه اطلاعاتی درباره خطا وجود ندارد</string>
<string name="ssl_validator_not_saved">گواهی ذخیره نشد</string>
<string name="ssl_validator_not_saved">نتوانست گواهی را ذخیره کند</string>
<string name="ssl_validator_null_cert">نمایش گواهینامه امکانپذیر نمی باشد</string>
<string name="ssl_validator_question">آیا می خواهید در هر صورت به این مدرک اطمینان کنید؟</string>
<string name="ssl_validator_reason_cert_expired">- گواهی سرور انقضا یافته</string>
<string name="ssl_validator_reason_cert_not_trusted">- گواهی سرور نامعتبر است</string>
<string name="ssl_validator_reason_cert_not_yet_valid">- زمان اعتبار گواهی نامه سرویس دهنده فرانرسیده است</string>
<string name="ssl_validator_reason_hostname_not_verified">- URL با آدرس هاست موجود در گواهی نامه مطابقت ندارد.</string>
<string name="status_message">پیغام وضعیت</string>
<string name="status_message">پیام وضعیت</string>
<string name="storage_camera">دوربین</string>
<string name="storage_choose_location">محل ذخیره را انتخاب کنید</string>
<string name="storage_description_default">پیش‌فرض</string>
<string name="storage_documents">اسناد</string>
<string name="storage_downloads">دریافت‌ها</string>
<string name="storage_internal_storage">حافظه داخلی</string>
<string name="storage_movies">فیلم ها</string>
<string name="storage_music">موسیقی</string>
<string name="storage_pictures">تصاویر</string>
<string name="storage_choose_location">گزینش مکان ذخیره‌سازی</string>
<string name="storage_description_default">پیش‌گزیده</string>
<string name="storage_documents">سندها</string>
<string name="storage_downloads">بارگیری‌ها</string>
<string name="storage_internal_storage">ذخیره‌ساز داخلی</string>
<string name="storage_movies">فیلم‌ها</string>
<string name="storage_music">آهنگ‌ها</string>
<string name="storage_permission_full_access">دسترسی کامل</string>
<string name="storage_permission_media_read_only">رسانه فقط‌خواندنی</string>
<string name="storage_pictures">نگاره‌ها</string>
<string name="store_full_desc">"بستر خودمیزبانی که به شما اختیارات کامل می‌دهد. امکانات: * سهولت، رابط مدرن، مناسب برای زمینه سرور شما * بارگذاری فایل‌ها در سرور نکست‌کلود شما * اشتراک‌گذاری آن‌ها با دیگران * همگام‌سازی فایل‌ها و پوشه‌های مورد‌علاقه شما * جستجو در تمامی پوشه‌های سرور شما * بارگذاری خودکار فیلم‌ها و عکس‌های گرفته شده توسط دستگاه شما * همواره به‌روز بودن توسط اعلان‌ها * پشتیبانی از چند حساب * دسترسی امن به داده‌‌های شما توسط اثر انگشت یا رمز پین * یکپارچه‌سازی با برنامک DAVx5(که قبلاً با نام DAVdroid شناخته می‌شد) برای راحتی در قرار دادن تقویم &amp; همگام‌سازی مخاطبین. لطفاً تمامی مسائل و مشکلات خود را در https://github.com/nextcloud/android/issues گزارش دهید و برای بحث در مورد این برنامک به آدرس https://help.nextcloud.com/c/clients/android مراجعه کنید. کاربر جدید نکست‌کلود هستید؟ نکست‌کلود یک سرور شخصی برای همگام‌سازی &amp; به‌اشتراک‌گذاری و ارتباطات است. این یک نرم‌افزار آزاد است، که شما می‌توانید خودتان میزبانی آن را انجام دهید و یا با پرداخت هزینه، از یک شرکت درخواست میزبانی کنید. به این ترتیب، شما کنترل عکس‌‌ها و فیلم‌هایتان، تقدیم و اطلاعات مخاطبین شما، فایل‌‌ها و اسنادتان و هرچیز دیگری را دارید. برای بررسی نکست‌کلود به https://nextcloud.com مراجعه کنید. "</string>
<string name="store_full_dev_desc">"بستر خودمیزبانی که به شما اختیارات کامل می‌دهد. این نسخه رسمی درحال توسعه می‌باشد، شامل یک نمونه روزانه از هر قابلیت جدید آزمایش نشده است، چیزی که ممکن است باعث ناپایداری و از دست دادن اطلاعات شود. این برنامک برای کاربرانی می‌باشد که قصد آزمایش دارند، و در صورت بروز مشکل آن را گزارش دهند. از آن برای کار تولیدی استفاده نکنید! هردو نسخه عادی و در حال توسعه در F-droid در دسترس هستند و به صورت هم‌زمان قابل نصب هستند. "</string>
<string name="store_short_desc">بستر بهره‌وری خود میزبان که به شما اختیار کامل می‌دهد</string>
<string name="store_short_dev_desc">بستر خودمیزبانی که به شما اختیار کامل می‌دهد</string>
<string name="stream">در جریان با…</string>
<string name="stream">جریان با…</string>
<string name="stream_not_possible_headline">جریان داخلی امکان پذیر نیست</string>
<string name="stream_not_possible_message">لطفاً به جای آن رسانه را دریافت کنید یا از برنامه خارجی استفاده کنید.</string>
<string name="strict_mode">حالت سخت‌گیر: اتصال HTTP مجاز نیست!</string>

View file

@ -261,6 +261,7 @@
<string name="error_comment_file">Hiba a fájlhoz hozzászóláskor</string>
<string name="error_crash_title">A(z) %1$s összeomlott</string>
<string name="error_creating_file_from_template">Hiba a fájl sablonból történő létrehozása során</string>
<string name="error_file_actions">Hiba a fájlműveletek megjelenítése során</string>
<string name="error_file_lock">Hiba a fájl zárolási állapotának módosítása során</string>
<string name="error_report_issue_action">Jelentés</string>
<string name="error_report_issue_text">Jelenti a hibát a követőbe? (GitHub-fiók szükséges)</string>

View file

@ -35,6 +35,7 @@
<string name="add_to_cloud">%1$s(으)로 추가</string>
<string name="advanced_settings">고급 설정</string>
<string name="allow_resharing">재공유 허용</string>
<string name="app_widget_description">대시보드로부터 위젯 1개 보기</string>
<string name="appbar_search_in">%s 검색</string>
<string name="associated_account_not_found">관련 계정을 찾을 수 없습니다!</string>
<string name="auth_access_failed">접근 실패: %1$s</string>
@ -87,11 +88,14 @@
<string name="calendars">달력</string>
<string name="certificate_load_problem">인증서를 불러올 수 없습니다.</string>
<string name="changelog_dev_version">Changelog 개발 버전</string>
<string name="check_back_later_or_reload">잠시후 다시 확인하거나 새로고침 하십시오.</string>
<string name="checkbox">체크 상자</string>
<string name="choose_local_folder">로컬 폴더 선택</string>
<string name="choose_location">위치 선택</string>
<string name="choose_remote_folder">대상 폴더 선택</string>
<string name="choose_template_helper_text">템플릿을 선택하고 파일 이름을 입력하세요.</string>
<string name="choose_which_file">보관할 파일을 선택하세요!</string>
<string name="choose_widget">위젯 선택</string>
<string name="clear_notifications_failed">알림을 지우는데 실패했습니다.</string>
<string name="clear_status_message">상태 메시지 지움</string>
<string name="clear_status_message_after">상태 메시지 지우기 예약</string>
@ -232,6 +236,7 @@
<string name="drawer_quota">%2$s 중 %1$s 사용됨</string>
<string name="drawer_quota_unlimited">%1$s 사용됨</string>
<string name="drawer_synced_folders">자동 업로드</string>
<string name="e2e_not_yet_setup">E2E가 아직 설정되지 않음</string>
<string name="encrypted">암호화하도록 설정</string>
<string name="end_to_end_encryption_confirm_button">암호화 설정</string>
<string name="end_to_end_encryption_decrypting">복호화중…</string>
@ -255,10 +260,14 @@
<string name="error_choosing_date">날짜 선택 오류</string>
<string name="error_comment_file">파일 주석 처리 중 오류 발생</string>
<string name="error_crash_title">%1$s충돌</string>
<string name="error_creating_file_from_template">템플릿으로부터 파일을 생성하는 중 오류 발생</string>
<string name="error_file_lock">파일 잠금 상태 변경중 오류 발생</string>
<string name="error_report_issue_action">보고</string>
<string name="error_report_issue_text">트래커에 문제를 보고하시겠습니까? (GitHub 계정 필요함)</string>
<string name="error_retrieving_file">파일 가져오는 중 오류 발생</string>
<string name="error_retrieving_templates">템플릿 검색 중 오류 발생</string>
<string name="error_starting_direct_camera_upload">카메라 시작 중 오류 발생</string>
<string name="error_starting_scan_doc">문서 스캔 중 오류 발생</string>
<string name="etm_accounts">계정</string>
<string name="etm_background_job_name">작업 이름</string>
<string name="etm_background_job_progress">진행 상황</string>
@ -289,6 +298,7 @@
<string name="failed_update_ui">UI 업데이트 실패</string>
<string name="favorite">즐겨찾기에 추가</string>
<string name="favorite_icon">즐겨찾기</string>
<string name="file_already_exists">파일 이름이 이미 존재함</string>
<string name="file_delete">삭제</string>
<string name="file_detail_activity_error">파일에 대한 작업 검색 중 오류 발생</string>
<string name="file_details_no_content">자세한 정보를 불러오는데 실패했습니다.</string>
@ -313,6 +323,9 @@
<string name="file_list_no_app_for_file_type">이 파일을 위한 앱이 존재하지 않습니다.</string>
<string name="file_list_seconds_ago">초 전</string>
<string name="file_management_permission">권한 필요함</string>
<string name="file_management_permission_optional">저장소 권한</string>
<string name="file_management_permission_optional_text">%1$s은(는) 저장소에 접근할 권한이 있어야 원활하게 동작합니다. 모든 파일에 대한 완전한 접근 혹은 사진과 동영상에 대한 읽기 전용 접근 중 하나를 선택할 수 있습니다.</string>
<string name="file_management_permission_text">%1$s은(는) 파일 권한이 있어야 원활하게 작동합니다. 모든 파일에 대한 완전한 접근 혹은 사진과 동영상에 대한 읽기 전용 접근 중 하나를 선택할 수 있습니다.</string>
<string name="file_migration_checking_destination">대상 점검 중…</string>
<string name="file_migration_cleaning">정리 중…</string>
<string name="file_migration_dialog_title">데이터 저장 폴더 업데이트 중</string>
@ -371,11 +384,15 @@
<string name="foreign_files_success">모든 파일이 이동됨</string>
<string name="forward">앞으로</string>
<string name="fourHours">4 시간</string>
<string name="hidden_file_name_warning">이 이름은 파일을 숨김처리 할 것입니다</string>
<string name="hint_name">이름</string>
<string name="hint_note">참고</string>
<string name="hint_password">암호</string>
<string name="host_not_available">서버를 사용할 수 없음</string>
<string name="host_your_own_server">자체 서버 호스팅</string>
<string name="icon_for_empty_list">빈 목록에 대한 아이콘</string>
<string name="icon_of_dashboard_widget">대시보드 위젯에 대한 아이콘</string>
<string name="icon_of_widget_entry">위젯 항목에 대한 아이콘</string>
<string name="in_folder">%1$s 폴더 안에</string>
<string name="instant_upload_existing">이미 존재하는 파일도 올리기</string>
<string name="instant_upload_on_charging">충전 중에만 업로드</string>
@ -396,7 +413,11 @@
<string name="local_file_not_found_message">내부 파일 시스템에서 파일을 찾을 수 없음</string>
<string name="local_folder_friendly_path">%1$s/%2$s</string>
<string name="local_folder_list_empty">더 이상의 폴더가 없습니다.</string>
<string name="locate_folder">폴더 위치 지정</string>
<string name="lock_expiration_info">만료시점: %1$s</string>
<string name="lock_file">파일 잠금</string>
<string name="locked_by">%1$s에 의해 잠김</string>
<string name="locked_by_app">%1$s앱에 의해 잠김</string>
<string name="log_send_mail_subject">%1$s Android 앱 로그</string>
<string name="log_send_no_mail_app">로그 전송용 앱이 없습니다. 이메일 클라이언트를 설치하십시오.</string>
<string name="login">로그인</string>
@ -450,8 +471,10 @@
<string name="no_browser_available">이 링크를 실행할 앱이 없습니다.</string>
<string name="no_calendar_exists">달력 없음</string>
<string name="no_email_app_available">메일 주소를 처리할 앱이 없음</string>
<string name="no_items">항목 없음</string>
<string name="no_mutliple_accounts_allowed">하나의 계정만 허용</string>
<string name="no_pdf_app_available">PDF를 실행할 앱이 없습니다.</string>
<string name="no_send_app">선택한 파일을 전송할 앱이 없음</string>
<string name="no_share_permission_selected">공유를 위해서 적어도 하나 이상의 권한 설정을 하세요.</string>
<string name="note_confirm">전송</string>
<string name="note_could_not_sent">메모를 보낼 수 없습니다.</string>
@ -487,6 +510,7 @@
<string name="pass_code_removed">암호 삭제됨</string>
<string name="pass_code_stored">암호 저장됨</string>
<string name="pass_code_wrong">암호가 잘못됨</string>
<string name="pdf_zoom_tip">페이지를 탭하여 확대</string>
<string name="permission_allow">허용</string>
<string name="permission_deny">거부</string>
<string name="permission_storage_access">파일을 업로드 및 다운로드하려면 추가 권한이 필요합니다.</string>
@ -564,6 +588,7 @@
<string name="recommend_subject">사용하고 있는 장치에서 %1$s을(를) 사용해 보세요!</string>
<string name="recommend_text">사용하고 있는 장치에서 %1$s을(를) 사용해 보세요.\n다운로드 링크: %2$s</string>
<string name="recommend_urls">%1$s 및 %2$s</string>
<string name="refresh_content">컨텐츠 새로고침</string>
<string name="reload">새로 고침</string>
<string name="remote">(원격)</string>
<string name="remote_file_fetch_failed">파일을 찾을 수 없음</string>
@ -598,6 +623,7 @@
<string name="screenshot_06_davdroid_subline">DAVx5와 동기화</string>
<string name="search_error">검색 결과 조회 오류</string>
<string name="select_all">모두 선택</string>
<string name="select_media_folder">미디어 폴더 설정</string>
<string name="select_one_template">하나의 템플릿을 선택하십시오</string>
<string name="select_template">템플릿 선택</string>
<string name="send">보내기</string>
@ -652,6 +678,8 @@
<string name="shared_icon_shared_via_link">링크로 공유됨</string>
<string name="shared_with_you_by">다음을 이용해 나와 공유됨 %1$s</string>
<string name="sharee_add_failed">공유사용자 추가 실패</string>
<string name="show_images">사진 보기</string>
<string name="show_video">비디오 접근</string>
<string name="signup_with_provider">공급자로 가입</string>
<string name="single_sign_on_request_token" formatted="true">%1$s가 Nextcloud 계정%2$s에 액세스하도록 허용 하시겠습니까?</string>
<string name="sort_by">정렬</string>
@ -695,6 +723,8 @@
<string name="storage_internal_storage">내부 저장소</string>
<string name="storage_movies">영화</string>
<string name="storage_music">음악</string>
<string name="storage_permission_full_access">완전한 접근</string>
<string name="storage_permission_media_read_only">읽기 전용 미디어 접근</string>
<string name="storage_pictures">그림</string>
<string name="store_full_desc">당신이 관리할 수 있는 자체 운영되는 생산성 플랫폼.
@ -727,6 +757,9 @@ Nextcloud를 여기서 확인하십시오: https://nextcloud.com</string>
<string name="strict_mode">엄격 모드 : http 연결이 허용되지 않습니다!</string>
<string name="subject_shared_with_you">\"%1$s\"을(를) 여러분과 공유했습니다</string>
<string name="subject_user_shared_with_you">%1$s 님이 \"%2$s\" 항목을 여러분과 공유했습니다</string>
<string name="subtitle_photos_only">사진만</string>
<string name="subtitle_photos_videos">사진 &amp; 동영상</string>
<string name="subtitle_videos_only">동영상만</string>
<string name="suggest">제안</string>
<string name="sync_conflicts_in_favourites_ticker">충돌하는 항목 발견됨</string>
<string name="sync_current_folder_was_removed">폴더 %1$s이(가) 더 이상 존재하지 않음</string>
@ -804,6 +837,7 @@ Nextcloud를 여기서 확인하십시오: https://nextcloud.com</string>
<string name="upload_lock_failed">폴더 잠금 실패</string>
<string name="upload_old_android">암호화는 안드로이드 5.0 이상 가능합니다</string>
<string name="upload_query_move_foreign_files">공간이 부족해서 선택한 파일을 %1$s 폴더로 복사할 수 없습니다. 대신 이동하시겠습니까?</string>
<string name="upload_scan_doc_upload">카메라로부터 문서 스캔</string>
<string name="upload_sync_conflict">동기화 충돌, 수동으로 해결해주세요</string>
<string name="upload_unknown_error">알 수 없는 오류</string>
<string name="uploader_btn_alternative_text">선택</string>
@ -867,6 +901,7 @@ Nextcloud를 여기서 확인하십시오: https://nextcloud.com</string>
<string name="userinfo_no_info_text">프로필 페이지에 이름, 사진, 연락처를 추가하십시오.</string>
<string name="username">사용자 이름</string>
<string name="version_dev_download">다운로드</string>
<string name="video_overlay_icon">동영상 오버레이 아이콘</string>
<string name="wait_a_moment">잠시 기다려 주십시오…</string>
<string name="wait_checking_credentials">저장된 인증 정보 확인 중</string>
<string name="wait_for_tmp_copy_from_private_storage">개인 저장소에서 파일 복사</string>
@ -896,6 +931,18 @@ Nextcloud를 여기서 확인하십시오: https://nextcloud.com</string>
<plurals name="found_n_duplicates">
<item quantity="other">%d 중복 항목 발견</item>
</plurals>
<plurals name="export_successful">
<item quantity="other">%d개의 파일을 내보냄 </item>
</plurals>
<plurals name="export_failed">
<item quantity="other">%d개의 파일을 내보내지 못함</item>
</plurals>
<plurals name="export_partially_failed">
<item quantity="other">%d개의 파일을 내보냈으며, 나머지는 오류로 인해 내보내지 못함</item>
</plurals>
<plurals name="export_start">
<item quantity="other">%d개의 파일이 내보내질 것입니다. 자세한 사항은 알림을 참고하십시오.</item>
</plurals>
<plurals name="file_list__footer__folder">
<item quantity="other">폴더 %1$d개</item>
</plurals>

View file

@ -236,6 +236,7 @@
<string name="drawer_quota">использовано %1$s из %2$s </string>
<string name="drawer_quota_unlimited">%1$s использовано</string>
<string name="drawer_synced_folders">Автоматическая передача</string>
<string name="e2e_not_yet_setup">Сквозное шифрование не настроено</string>
<string name="encrypted">Зашифровать</string>
<string name="end_to_end_encryption_confirm_button">Параметры шифрования</string>
<string name="end_to_end_encryption_decrypting">Расшифровка…</string>
@ -260,6 +261,7 @@
<string name="error_comment_file">Ошибка комментирования файла</string>
<string name="error_crash_title">сбой %1$s</string>
<string name="error_creating_file_from_template">Не удалось создать файл на основе шаблона</string>
<string name="error_file_actions">Не удалось показать доступные действия с файлом</string>
<string name="error_file_lock">Не удалось изменить состояние блокировки</string>
<string name="error_report_issue_action">Сообщить</string>
<string name="error_report_issue_text">Сообщить о проблеме? (требуется учётная запись на GitHub)</string>
@ -929,6 +931,18 @@
<item quantity="many">Найдено %d повторяющихся объектов.</item>
<item quantity="other">Найдено %d повторяющихся объекта.</item>
</plurals>
<plurals name="export_successful">
<item quantity="one">Экспортирован %d файл</item>
<item quantity="few">Экспортировано %d файлов</item>
<item quantity="many">Экспортировано %d файлов</item>
<item quantity="other">Экспортировано %d файлов</item>
</plurals>
<plurals name="export_failed">
<item quantity="one">Не удалось экспортировать %d файл</item>
<item quantity="few">Не удалось экспортировать %d файла</item>
<item quantity="many">Не удалось экспортировать %d файлов</item>
<item quantity="other">Не удалось экспортировать %d файла</item>
</plurals>
<plurals name="file_list__footer__folder">
<item quantity="one">%1$d каталог</item>
<item quantity="few">%1$d каталога</item>

View file

@ -261,6 +261,7 @@
<string name="error_comment_file">Chyba pri pridaní komentáru k súboru</string>
<string name="error_crash_title">%1$s zhavarovalo</string>
<string name="error_creating_file_from_template">Chyba pri vytváraní súboru zo šablóny</string>
<string name="error_file_actions">Chyba pri zobrazovaní akcií súboru</string>
<string name="error_file_lock">Chyba pri zmene stavu uzamknutia súboru</string>
<string name="error_report_issue_action">Hlásenie</string>
<string name="error_report_issue_text">Nahlásiť chybu? (vyžaduje účet na Githube)</string>

View file

@ -261,6 +261,7 @@
<string name="error_comment_file">Dosyaya yorum yapılırken sorun çıktı</string>
<string name="error_crash_title">%1$s çöktü</string>
<string name="error_creating_file_from_template">Kalıptan dosya oluşturulurken sorun çıktı</string>
<string name="error_file_actions">Dosya işlemleri görüntülenirken sorun çıktı</string>
<string name="error_file_lock">Dosya kilidi durumu değiştirilirken sorun çıktı</string>
<string name="error_report_issue_action">Hata bildirin</string>
<string name="error_report_issue_text">İzleyici üzerine bir hata bildirmek ister misiniz? (GitHub hesabı gereklidir)</string>

View file

@ -35,6 +35,7 @@
<string name="add_to_cloud">添加到%1$s</string>
<string name="advanced_settings">高级设置</string>
<string name="allow_resharing">允许二次共享</string>
<string name="app_widget_description">显示仪表盘中的一个小工具</string>
<string name="appbar_search_in">在 %s 中搜索</string>
<string name="associated_account_not_found">相关账号未找到!</string>
<string name="auth_access_failed">访问已失败: %1$s</string>
@ -235,6 +236,7 @@
<string name="drawer_quota">%1$s 中 %2$s 已使用</string>
<string name="drawer_quota_unlimited">%1$s 已使用</string>
<string name="drawer_synced_folders">自动上传</string>
<string name="e2e_not_yet_setup">尚未安装端到端加密</string>
<string name="encrypted">设置为加密</string>
<string name="end_to_end_encryption_confirm_button">设置加密</string>
<string name="end_to_end_encryption_decrypting">正在解密…</string>
@ -259,6 +261,7 @@
<string name="error_comment_file">评论文件发生错误</string>
<string name="error_crash_title">%1$s 崩溃了</string>
<string name="error_creating_file_from_template">从模板创建文件时出错</string>
<string name="error_file_actions">显示文件操作时出错</string>
<string name="error_file_lock">修改文件锁定状态时发生错误</string>
<string name="error_report_issue_action">报告</string>
<string name="error_report_issue_text">是否向跟踪器报告问题需要GitHub账号</string>
@ -388,6 +391,8 @@
<string name="hint_password">密码</string>
<string name="host_not_available">服务器不可用</string>
<string name="host_your_own_server">自己搭建服务器</string>
<string name="icon_for_empty_list">空列表的图标</string>
<string name="icon_of_dashboard_widget">仪表盘小工具的图标</string>
<string name="in_folder">在文件夹 %1$s 内 </string>
<string name="instant_upload_existing">同时上传现有文件</string>
<string name="instant_upload_on_charging">仅在充电时上传</string>

View file

@ -261,6 +261,7 @@
<string name="error_comment_file">留言錯誤</string>
<string name="error_crash_title">%1$s失敗</string>
<string name="error_creating_file_from_template">從模板創建檔案時出錯</string>
<string name="error_file_actions">顯示檔案操作時出錯</string>
<string name="error_file_lock">更改檔案上鎖狀態時出錯</string>
<string name="error_report_issue_action">舉報</string>
<string name="error_report_issue_text">想舉報問題需要GitHub賬戶</string>