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

This commit is contained in:
Tobias Kaminsky 2024-08-27 03:39:42 +02:00
commit 3cdd2f0fa2
45 changed files with 1914 additions and 34 deletions

View file

@ -46,5 +46,5 @@ jobs:
(If you believe you should not receive this message, you can add yourself to the [blocklist](https://github.com/nextcloud/.github/blob/master/non-community-usernames.txt).)
days-before-feedback: 14
start-date: '2024-04-30'
exempt-authors: '${{ steps.blocklist.outputs.blocklist }},${{ steps.scrape.outputs.users }},nextcloud-command,nextcloud-android-bot'
exempt-authors: '${{ steps.blocklist.outputs.blocklist }},${{ steps.scrape.outputs.users }}'
exempt-bots: true

View file

@ -15,8 +15,8 @@ jobs:
reuse-compliance-check:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: REUSE Compliance Check
uses: fsfe/reuse-action@3ae3c6bdf1257ab19397fab11fd3312144692083 # v4.0.0
- name: REUSE Compliance Check
uses: fsfe/reuse-action@3ae3c6bdf1257ab19397fab11fd3312144692083 # v4.0.0

View file

@ -1206,4 +1206,4 @@
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '082a63031678a67879428f688f02d3b5')"
]
}
}
}

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View file

@ -106,7 +106,6 @@ public class FileIT extends AbstractOnServerIT {
assertTrue(new SynchronizeFolderOperation(targetContext,
folderPath,
user,
System.currentTimeMillis(),
fileDataStorageManager)
.execute(targetContext)
.isSuccess());

View file

@ -255,6 +255,9 @@
<activity
android:name=".ui.activity.SyncedFoldersActivity"
android:exported="false" />
<activity
android:name=".ui.activity.InternalTwoWaySyncActivity"
android:exported="false" />
<activity
android:name="com.nextcloud.client.widget.DashboardWidgetConfigurationActivity"
android:exported="false">
@ -627,4 +630,4 @@
</activity>
</application>
</manifest>
</manifest>

View file

@ -60,7 +60,8 @@ import com.owncloud.android.db.ProviderMeta
AutoMigration(from = 78, to = 79, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class),
AutoMigration(from = 79, to = 80),
AutoMigration(from = 80, to = 81),
AutoMigration(from = 81, to = 82)
AutoMigration(from = 81, to = 82),
AutoMigration(from = 82, to = 83)
],
exportSchema = true
)

View file

@ -49,4 +49,10 @@ interface FileDao {
@Query("SELECT * FROM filelist where file_owner = :fileOwner AND etag_in_conflict IS NOT NULL")
fun getFilesWithSyncConflict(fileOwner: String): List<FileEntity>
@Query(
"SELECT * FROM filelist where file_owner = :fileOwner AND internal_two_way_sync_timestamp >= 0 " +
"ORDER BY internal_two_way_sync_timestamp DESC"
)
fun getInternalTwoWaySyncFolders(fileOwner: String): List<FileEntity>
}

View file

@ -115,5 +115,9 @@ data class FileEntity(
@ColumnInfo(name = ProviderTableMeta.FILE_METADATA_GPS)
val metadataGPS: String?,
@ColumnInfo(name = ProviderTableMeta.FILE_E2E_COUNTER)
val e2eCounter: Long?
val e2eCounter: Long?,
@ColumnInfo(name = ProviderTableMeta.FILE_INTERNAL_TWO_WAY_SYNC_TIMESTAMP)
val internalTwoWaySync: Long?,
@ColumnInfo(name = ProviderTableMeta.FILE_INTERNAL_TWO_WAY_SYNC_RESULT)
val internalTwoWaySyncResult: String?
)

View file

@ -54,6 +54,7 @@ import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.ui.activity.FileDisplayActivity;
import com.owncloud.android.ui.activity.FilePickerActivity;
import com.owncloud.android.ui.activity.FolderPickerActivity;
import com.owncloud.android.ui.activity.InternalTwoWaySyncActivity;
import com.owncloud.android.ui.activity.ManageAccountsActivity;
import com.owncloud.android.ui.activity.ManageSpaceActivity;
import com.owncloud.android.ui.activity.NotificationsActivity;
@ -476,4 +477,7 @@ abstract class ComponentsModule {
@ContributesAndroidInjector
abstract TestJob testJob();
@ContributesAndroidInjector
abstract InternalTwoWaySyncActivity internalTwoWaySyncActivity();
}

View file

@ -95,6 +95,7 @@ class BackgroundJobFactory @Inject constructor(
GeneratePdfFromImagesWork::class -> createPDFGenerateWork(context, workerParameters)
HealthStatusWork::class -> createHealthStatusWork(context, workerParameters)
TestJob::class -> createTestJob(context, workerParameters)
InternalTwoWaySyncWork::class -> createInternalTwoWaySyncWork(context, workerParameters)
else -> null // caller falls back to default factory
}
}
@ -277,4 +278,14 @@ class BackgroundJobFactory @Inject constructor(
backgroundJobManager.get()
)
}
private fun createInternalTwoWaySyncWork(context: Context, params: WorkerParameters): InternalTwoWaySyncWork {
return InternalTwoWaySyncWork(
context,
params,
accountManager,
powerManagementService,
connectivityService
)
}
}

View file

@ -168,4 +168,5 @@ interface BackgroundJobManager {
fun schedulePeriodicHealthStatus()
fun startHealthStatus()
fun bothFilesSyncJobsRunning(syncedFolderID: Long): Boolean
fun scheduleInternal2WaySync()
}

View file

@ -84,6 +84,8 @@ internal class BackgroundJobManagerImpl(
const val JOB_PERIODIC_HEALTH_STATUS = "periodic_health_status"
const val JOB_IMMEDIATE_HEALTH_STATUS = "immediate_health_status"
const val JOB_INTERNAL_TWO_WAY_SYNC = "internal_two_way_sync"
const val JOB_TEST = "test_job"
const val MAX_CONTENT_TRIGGER_DELAY_MS = 10000L
@ -647,4 +649,13 @@ internal class BackgroundJobManagerImpl(
request
)
}
override fun scheduleInternal2WaySync() {
val request = periodicRequestBuilder(
jobClass = InternalTwoWaySyncWork::class,
jobName = JOB_INTERNAL_TWO_WAY_SYNC
).build()
workManager.enqueueUniquePeriodicWork(JOB_INTERNAL_TWO_WAY_SYNC, ExistingPeriodicWorkPolicy.KEEP, request)
}
}

View file

@ -0,0 +1,91 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2024 Tobias Kaminsky <tobias.kaminsky@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.nextcloud.client.jobs
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.nextcloud.client.account.UserAccountManager
import com.nextcloud.client.device.PowerManagementService
import com.nextcloud.client.network.ConnectivityService
import com.owncloud.android.MainApp
import com.owncloud.android.datamodel.FileDataStorageManager
import com.owncloud.android.lib.common.utils.Log_OC
import com.owncloud.android.operations.SynchronizeFolderOperation
import com.owncloud.android.utils.FileStorageUtils
import java.io.File
@Suppress("Detekt.NestedBlockDepth")
class InternalTwoWaySyncWork(
private val context: Context,
params: WorkerParameters,
private val userAccountManager: UserAccountManager,
private val powerManagementService: PowerManagementService,
private val connectivityService: ConnectivityService
) : Worker(context, params) {
override fun doWork(): Result {
Log_OC.d(TAG, "Worker started!")
var result = true
if (powerManagementService.isPowerSavingEnabled ||
!connectivityService.isConnected || connectivityService.isInternetWalled
) {
Log_OC.d(TAG, "Not starting due to constraints!")
return Result.success()
}
val users = userAccountManager.allUsers
for (user in users) {
val fileDataStorageManager = FileDataStorageManager(user, context.contentResolver)
val folders = fileDataStorageManager.getInternalTwoWaySyncFolders(user)
for (folder in folders) {
val freeSpaceLeft = File(folder.storagePath).getFreeSpace()
val localFolderSize = FileStorageUtils.getFolderSize(File(folder.storagePath, MainApp.getDataFolder()))
val remoteFolderSize = folder.fileLength
if (freeSpaceLeft < (remoteFolderSize - localFolderSize)) {
Log_OC.d(TAG, "Not enough space left!")
result = false
}
Log_OC.d(TAG, "Folder ${folder.remotePath}: started!")
val operation = SynchronizeFolderOperation(context, folder.remotePath, user, fileDataStorageManager)
.execute(context)
if (operation.isSuccess) {
Log_OC.d(TAG, "Folder ${folder.remotePath}: finished!")
} else {
Log_OC.d(TAG, "Folder ${folder.remotePath} failed!")
result = false
}
folder.apply {
internalFolderSyncResult = operation.code.toString()
internalFolderSyncTimestamp = System.currentTimeMillis()
}
fileDataStorageManager.saveFile(folder)
}
}
return if (result) {
Log_OC.d(TAG, "Worker finished with success!")
Result.success()
} else {
Log_OC.d(TAG, "Worker finished with failure!")
Result.failure()
}
}
companion object {
const val TAG = "InternalTwoWaySyncWork"
}
}

View file

@ -371,6 +371,7 @@ public class MainApp extends Application implements HasAndroidInjector {
backgroundJobManager.scheduleMediaFoldersDetectionJob();
backgroundJobManager.startMediaFoldersDetectionJob();
backgroundJobManager.schedulePeriodicHealthStatus();
backgroundJobManager.scheduleInternal2WaySync();
}
registerGlobalPassCodeProtection();

View file

@ -77,6 +77,7 @@ import androidx.annotation.VisibleForTesting;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import kotlin.Pair;
@SuppressFBWarnings("CE")
public class FileDataStorageManager {
private static final String TAG = FileDataStorageManager.class.getSimpleName();
@ -558,6 +559,8 @@ public class FileDataStorageManager {
cv.put(ProviderTableMeta.FILE_SHAREES, gson.toJson(fileOrFolder.getSharees()));
cv.put(ProviderTableMeta.FILE_TAGS, gson.toJson(fileOrFolder.getTags()));
cv.put(ProviderTableMeta.FILE_RICH_WORKSPACE, fileOrFolder.getRichWorkspace());
cv.put(ProviderTableMeta.FILE_INTERNAL_TWO_WAY_SYNC_TIMESTAMP, fileOrFolder.getInternalFolderSyncTimestamp());
cv.put(ProviderTableMeta.FILE_INTERNAL_TWO_WAY_SYNC_RESULT, fileOrFolder.getInternalFolderSyncResult());
return cv;
}
@ -602,6 +605,8 @@ public class FileDataStorageManager {
cv.put(ProviderTableMeta.FILE_METADATA_GPS, gson.toJson(file.getGeoLocation()));
cv.put(ProviderTableMeta.FILE_METADATA_LIVE_PHOTO, file.getLinkedFileIdForLivePhoto());
cv.put(ProviderTableMeta.FILE_E2E_COUNTER, file.getE2eCounter());
cv.put(ProviderTableMeta.FILE_INTERNAL_TWO_WAY_SYNC_TIMESTAMP, file.getInternalFolderSyncTimestamp());
cv.put(ProviderTableMeta.FILE_INTERNAL_TWO_WAY_SYNC_RESULT, file.getInternalFolderSyncResult());
return cv;
}
@ -1035,6 +1040,7 @@ public class FileDataStorageManager {
ocFile.setLivePhoto(fileEntity.getMetadataLivePhoto());
ocFile.setHidden(nullToZero(fileEntity.getHidden()) == 1);
ocFile.setE2eCounter(fileEntity.getE2eCounter());
ocFile.setInternalFolderSyncTimestamp(fileEntity.getInternalTwoWaySync());
String sharees = fileEntity.getSharees();
// Surprisingly JSON deserialization causes significant overhead.
@ -2477,4 +2483,29 @@ public class FileDataStorageManager {
return files;
}
public List<OCFile> getInternalTwoWaySyncFolders(User user) {
List<FileEntity> fileEntities = fileDao.getInternalTwoWaySyncFolders(user.getAccountName());
List<OCFile> files = new ArrayList<>(fileEntities.size());
for (FileEntity fileEntity : fileEntities) {
files.add(createFileInstance(fileEntity));
}
return files;
}
public boolean isPartOfInternalTwoWaySync(OCFile file) {
if (file.isInternalFolderSync()) {
return true;
}
while (file != null && !OCFile.ROOT_PATH.equals(file.getDecryptedRemotePath())) {
if (file.isInternalFolderSync()) {
return true;
}
file = getFileById(file.getParentId());
}
return false;
}
}

View file

@ -117,6 +117,8 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
@Nullable
private GeoLocation geolocation;
private List<String> tags = new ArrayList<>();
private Long internalFolderSyncTimestamp = -1L;
private String internalFolderSyncResult = "";
/**
* URI to the local path of the file contents, if stored in the device; cached after first call to
@ -1051,6 +1053,26 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
this.e2eCounter = e2eCounter;
}
}
public boolean isInternalFolderSync() {
return internalFolderSyncTimestamp >= 0;
}
public Long getInternalFolderSyncTimestamp() {
return internalFolderSyncTimestamp;
}
public void setInternalFolderSyncTimestamp(Long internalFolderSyncTimestamp) {
this.internalFolderSyncTimestamp = internalFolderSyncTimestamp;
}
public String getInternalFolderSyncResult() {
return internalFolderSyncResult;
}
public void setInternalFolderSyncResult(String internalFolderSyncResult) {
this.internalFolderSyncResult = internalFolderSyncResult;
}
public boolean isAPKorAAB() {
if ("gplay".equals(BuildConfig.FLAVOR)) {

View file

@ -25,7 +25,7 @@ import java.util.List;
*/
public class ProviderMeta {
public static final String DB_NAME = "filelist";
public static final int DB_VERSION = 82;
public static final int DB_VERSION = 83;
private ProviderMeta() {
// No instance
@ -120,6 +120,8 @@ public class ProviderMeta {
public static final String FILE_LOCK_TOKEN = "lock_token";
public static final String FILE_TAGS = "tags";
public static final String FILE_E2E_COUNTER = "e2e_counter";
public static final String FILE_INTERNAL_TWO_WAY_SYNC_TIMESTAMP = "internal_two_way_sync_timestamp";
public static final String FILE_INTERNAL_TWO_WAY_SYNC_RESULT = "internal_two_way_sync_result";
public static final List<String> FILE_ALL_COLUMNS = Collections.unmodifiableList(Arrays.asList(
_ID,
@ -171,7 +173,9 @@ public class ProviderMeta {
FILE_METADATA_LIVE_PHOTO,
FILE_E2E_COUNTER,
FILE_TAGS,
FILE_METADATA_GPS));
FILE_METADATA_GPS,
FILE_INTERNAL_TWO_WAY_SYNC_TIMESTAMP,
FILE_INTERNAL_TWO_WAY_SYNC_RESULT));
public static final String FILE_DEFAULT_SORT_ORDER = FILE_NAME + " collate nocase asc";
// Columns of ocshares table

View file

@ -697,6 +697,7 @@ public class RefreshFolderOperation extends RemoteOperation {
if (localFile != null) {
updatedFile.setFileId(localFile.getFileId());
updatedFile.setLastSyncDateForData(localFile.getLastSyncDateForData());
updatedFile.setInternalFolderSyncTimestamp(localFile.getInternalFolderSyncTimestamp());
updatedFile.setModificationTimestampAtLastSyncForData(
localFile.getModificationTimestampAtLastSyncForData()
);

View file

@ -295,6 +295,8 @@ public class SynchronizeFileOperation extends SyncOperation {
}
private void requestForDownload(OCFile file) {
Log_OC.d("InternalTwoWaySyncWork", "download file: " + file.getFileName());
FileDownloadHelper.Companion.instance().downloadFile(
mUser,
file);

View file

@ -55,9 +55,6 @@ public class SynchronizeFolderOperation extends SyncOperation {
private static final String TAG = SynchronizeFolderOperation.class.getSimpleName();
/** Time stamp for the synchronization process in progress */
private long mCurrentSyncTime;
/** Remote path of the folder to synchronize */
private String mRemotePath;
@ -95,17 +92,14 @@ public class SynchronizeFolderOperation extends SyncOperation {
* @param context Application context.
* @param remotePath Path to synchronize.
* @param user Nextcloud account where the folder is located.
* @param currentSyncTime Time stamp for the synchronization process in progress.
*/
public SynchronizeFolderOperation(Context context,
String remotePath,
User user,
long currentSyncTime,
FileDataStorageManager storageManager) {
super(storageManager);
mRemotePath = remotePath;
mCurrentSyncTime = currentSyncTime;
this.user = user;
mContext = context;
mRemoteFolderChanged = false;
@ -365,7 +359,7 @@ public class SynchronizeFolderOperation extends SyncOperation {
}
private void updateLocalStateData(OCFile remoteFile, OCFile localFile, OCFile updatedFile) {
updatedFile.setLastSyncDateForProperties(mCurrentSyncTime);
updatedFile.setLastSyncDateForProperties(System.currentTimeMillis());
if (localFile != null) {
updatedFile.setFileId(localFile.getFileId());
updatedFile.setLastSyncDateForData(localFile.getLastSyncDateForData());
@ -393,8 +387,19 @@ public class SynchronizeFolderOperation extends SyncOperation {
}
}
private void classifyFileForLaterSyncOrDownload(OCFile remoteFile, OCFile localFile) {
if (!remoteFile.isFolder()) {
@SuppressFBWarnings("JLM")
private void classifyFileForLaterSyncOrDownload(OCFile remoteFile, OCFile localFile) throws OperationCancelledException {
if (remoteFile.isFolder()) {
/// to download children files recursively
synchronized (mCancellationRequested) {
if (mCancellationRequested.get()) {
throw new OperationCancelledException();
}
startSyncFolderOperation(remoteFile.getRemotePath());
}
} else {
/// prepare content synchronization for files (any file, not just favorites)
SynchronizeFileOperation operation = new SynchronizeFileOperation(
localFile,
remoteFile,

View file

@ -707,7 +707,6 @@ public class OperationsService extends Service {
this, // TODO remove this dependency from construction time
remotePath,
user,
System.currentTimeMillis(), // TODO remove this dependency from construction time
fileDataStorageManager
);
break;

View file

@ -0,0 +1,30 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2024 Tobias Kaminsky <tobias.kaminsky@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.owncloud.android.ui.activity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import com.nextcloud.client.di.Injectable
import com.owncloud.android.databinding.InternalTwoWaySyncLayoutBinding
import com.owncloud.android.ui.adapter.InternalTwoWaySyncAdapter
class InternalTwoWaySyncActivity : BaseActivity(), Injectable {
lateinit var binding: InternalTwoWaySyncLayoutBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = InternalTwoWaySyncLayoutBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.list.apply {
adapter = InternalTwoWaySyncAdapter(fileDataStorageManager, user.get(), context)
layoutManager = LinearLayoutManager(context)
}
}
}

View file

@ -171,6 +171,9 @@ public class SettingsActivity extends PreferenceActivity
// Details
setupDetailsCategory(preferenceScreen);
// Sync
setupSyncCategory();
// More
setupMoreCategory();
@ -310,13 +313,19 @@ public class SettingsActivity extends PreferenceActivity
}
}
}
private void setupSyncCategory() {
final PreferenceCategory preferenceCategorySync = (PreferenceCategory) findPreference("sync");
viewThemeUtils.files.themePreferenceCategory(preferenceCategorySync);
setupAutoUploadPreference(preferenceCategorySync);
setupInternalTwoWaySyncPreference(preferenceCategorySync);
}
private void setupMoreCategory() {
final PreferenceCategory preferenceCategoryMore = (PreferenceCategory) findPreference("more");
viewThemeUtils.files.themePreferenceCategory(preferenceCategoryMore);
setupAutoUploadPreference(preferenceCategoryMore);
setupCalendarPreference(preferenceCategoryMore);
setupBackupPreference();
@ -548,6 +557,16 @@ public class SettingsActivity extends PreferenceActivity
});
}
}
private void setupInternalTwoWaySyncPreference(PreferenceCategory preferenceCategorySync) {
Preference twoWaySync = findPreference("internal_two_way_sync");
twoWaySync.setOnPreferenceClickListener(preference -> {
Intent intent = new Intent(this, InternalTwoWaySyncActivity.class);
startActivity(intent);
return true;
});
}
private void setupBackupPreference() {
Preference pContactsBackup = findPreference("backup");

View file

@ -424,8 +424,12 @@ public class StorageMigration {
throw new MigrationException(R.string.file_migration_failed_dir_already_exists);
}
if (dstFile.getFreeSpace() < FileStorageUtils.getFolderSize(new File(srcFile, MainApp.getDataFolder()))) {
throw new MigrationException(R.string.file_migration_failed_not_enough_space);
try {
if (dstFile.getFreeSpace() < FileStorageUtils.getFolderSize(new File(srcFile, MainApp.getDataFolder()))) {
throw new MigrationException(R.string.file_migration_failed_not_enough_space);
}
} catch (MigrationException e) {
throw new RuntimeException(e);
}
}

View file

@ -0,0 +1,43 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2024 Tobias Kaminsky <tobias.kaminsky@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.owncloud.android.ui.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.nextcloud.client.account.User
import com.owncloud.android.databinding.InternalTwoWaySyncViewHolderBinding
import com.owncloud.android.datamodel.FileDataStorageManager
import com.owncloud.android.datamodel.OCFile
class InternalTwoWaySyncAdapter(
dataStorageManager: FileDataStorageManager,
user: User,
val context: Context
) : RecyclerView.Adapter<InternalTwoWaySyncViewHolder>() {
var folders: List<OCFile> = dataStorageManager.getInternalTwoWaySyncFolders(user)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): InternalTwoWaySyncViewHolder {
return InternalTwoWaySyncViewHolder(
InternalTwoWaySyncViewHolderBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun getItemCount(): Int {
return folders.size
}
override fun onBindViewHolder(holder: InternalTwoWaySyncViewHolder, position: Int) {
holder.bind(folders[position], context)
}
}

View file

@ -0,0 +1,44 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2024 Tobias Kaminsky <tobias.kaminsky@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.owncloud.android.ui.adapter
import android.content.Context
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import com.owncloud.android.R
import com.owncloud.android.databinding.InternalTwoWaySyncViewHolderBinding
import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.utils.DisplayUtils
class InternalTwoWaySyncViewHolder(val binding: InternalTwoWaySyncViewHolderBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(folder: OCFile, context: Context) {
binding.run {
size.text = DisplayUtils.bytesToHumanReadable(folder.fileLength)
name.text = folder.decryptedFileName
if (folder.internalFolderSyncResult.isEmpty()) {
syncResult.visibility = View.GONE
syncResultDivider.visibility = View.GONE
} else {
syncResult.visibility = View.VISIBLE
syncResultDivider.visibility = View.VISIBLE
syncResult.text = folder.internalFolderSyncResult
}
if (folder.internalFolderSyncTimestamp == 0L) {
syncTimestamp.text = context.getString(R.string.internal_two_way_sync_not_yet)
} else {
syncTimestamp.text = DisplayUtils.getRelativeTimestamp(
context,
folder.internalFolderSyncTimestamp
)
}
}
}
}

View file

@ -261,6 +261,7 @@ public class FileDetailFragment extends FileFragment implements OnClickListener,
binding.favorite.setOnClickListener(this);
binding.overflowMenu.setOnClickListener(this);
binding.lastModificationTimestamp.setOnClickListener(this);
binding.folderSyncButton.setOnClickListener(this);
updateFileDetails(false, false);
}
@ -471,8 +472,14 @@ public class FileDetailFragment extends FileFragment implements OnClickListener,
boolean showDetailedTimestamp = !preferences.isShowDetailedTimestampEnabled();
preferences.setShowDetailedTimestampEnabled(showDetailedTimestamp);
setFileModificationTimestamp(getFile(), showDetailedTimestamp);
Log_OC.e(TAG, "Incorrect view clicked!");
} else if (id == R.id.folder_sync_button) {
if (binding.folderSyncButton.isChecked()) {
getFile().setInternalFolderSyncTimestamp(0L);
} else {
getFile().setInternalFolderSyncTimestamp(-1L);
}
storageManager.saveFile(getFile());
} else {
Log_OC.e(TAG, "Incorrect view clicked!");
}
@ -556,6 +563,17 @@ public class FileDetailFragment extends FileFragment implements OnClickListener,
if (fabMain != null) {
fabMain.hide();
}
binding.syncBlock.setVisibility(file.isFolder() ? View.VISIBLE : View.GONE);
if (file.isInternalFolderSync()) {
binding.folderSyncButton.setChecked(file.isInternalFolderSync());
} else {
if (storageManager.isPartOfInternalTwoWaySync(file)) {
binding.folderSyncButton.setChecked(true);
binding.folderSyncButton.setEnabled(false);
}
}
}
setupViewPager();

View file

@ -170,6 +170,39 @@
</LinearLayout>
<LinearLayout
android:id="@+id/syncBlock"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/list_divider_background" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="@dimen/standard_padding"
android:paddingTop="@dimen/standard_half_padding"
android:paddingEnd="@dimen/zero"
android:paddingBottom="@dimen/standard_half_padding">
<CheckBox
android:id="@+id/folder_sync_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/sync" />
</LinearLayout>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Nextcloud - Android Client
~
~ SPDX-FileCopyrightText: 2024 Tobias Kaminsky <tobias@kaminsky.me>
~ SPDX-License-Identifier: AGPL-3.0-or-later
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Nextcloud - Android Client
~
~ SPDX-FileCopyrightText: 2024 Andy Scherzinger <info@andy-scherzinger.de>
~ SPDX-FileCopyrightText: 2024 Tobias Kaminsky <tobias@kaminsky.me>
~ SPDX-License-Identifier: AGPL-3.0-or-later
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="@dimen/min_list_item_size"
android:orientation="horizontal"
android:paddingStart="@dimen/standard_half_padding"
android:paddingEnd="@dimen/standard_half_padding">
<ImageView
android:layout_width="@dimen/file_icon_size"
android:layout_height="@dimen/file_icon_size"
android:layout_gravity="center_vertical"
android:contentDescription="@null"
android:src="@drawable/folder" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/min_list_item_size"
android:layout_marginStart="@dimen/standard_half_margin"
android:gravity="center_vertical"
android:orientation="vertical">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="middle"
android:gravity="center_vertical"
android:singleLine="true"
android:textColor="@color/text_color"
android:textSize="@dimen/two_line_primary_text_size"
tools:text="Folder abc" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:textColor="@color/list_item_lastmod_and_filesize_text"
android:textSize="@dimen/two_line_secondary_text_size"
tools:text="241 Mb" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="end"
android:paddingStart="@dimen/zero"
android:paddingEnd="@dimen/standard_quarter_padding"
android:text="@string/info_separator"
android:textColor="@color/list_item_lastmod_and_filesize_text"
android:textSize="@dimen/two_line_secondary_text_size" />
<TextView
android:id="@+id/sync_timestamp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:textColor="@color/list_item_lastmod_and_filesize_text"
android:textSize="@dimen/two_line_secondary_text_size"
tools:text="5 min ago" />
<TextView
android:id="@+id/sync_result_divider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="end"
android:paddingStart="@dimen/zero"
android:paddingEnd="@dimen/standard_quarter_padding"
android:text="@string/info_separator"
android:textColor="@color/list_item_lastmod_and_filesize_text"
android:textSize="@dimen/two_line_secondary_text_size" />
<TextView
android:id="@+id/sync_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:textColor="@color/list_item_lastmod_and_filesize_text"
android:textSize="@dimen/two_line_secondary_text_size"
tools:text="Success" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

View file

@ -279,8 +279,11 @@
<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_counter_too_old">العدَّاد قديم جداً</string>
<string name="e2e_hash_not_found">تعذّر العثور على التجزئة hash</string>
<string name="e2e_not_yet_setup">التشفير من الحدّ للحدّ E2E غير مُجهّز</string>
<string name="e2e_offline">غير ممكن بدون اتصال انترنت</string>
<string name="e2e_signature_does_not_match">التوقيع غير مُطابق</string>
<string name="ecosystem_apps_display_assistant">المُساعِد</string>
<string name="ecosystem_apps_display_more">المزيد</string>
<string name="ecosystem_apps_display_notes">الملاحظات Notes</string>
@ -406,6 +409,14 @@
<string name="file_migration_updating_index">تحديث الفهرس…</string>
<string name="file_migration_use_data_folder">استعمل</string>
<string name="file_migration_waiting_for_unfinished_sync">بإنتظار مزامنة كاملة…</string>
<string name="file_name_validator_current_path_is_invalid">اسم المجلد الحالي غير صحيح. قُم رجاءً بتغيير اسم المجلد. إعادة التوجيه إلى المجلد الجذر</string>
<string name="file_name_validator_error_contains_reserved_names_or_invalid_characters">مسار المجلد يحتوي على أسماء محجوزة أو حروف غير صالحة</string>
<string name="file_name_validator_error_ends_with_space_period">اسم ينتهي بفراغ أو نقطة</string>
<string name="file_name_validator_error_forbidden_file_extensions">.%s امتداد ملف غير مسموحٍ به</string>
<string name="file_name_validator_error_invalid_character">اسم يحتوي على حروف غير صالحة: %s</string>
<string name="file_name_validator_error_reserved_names">%s اسم ممنوع</string>
<string name="file_name_validator_rename_before_move_or_copy">%s. قُم رجاءً بتغيير اسم الملف قبل نقله أو نسخه</string>
<string name="file_name_validator_upload_content_error">بعض المحتويات لا يمكن تحميلها بسبب احتوائها على أسماء محجوزة أو حروف غير صالحة</string>
<string name="file_not_found">تعذر العثور على الملف</string>
<string name="file_not_synced">الملف لا يمكن مزامنته. عرض آخر إصدار متوفر.</string>
<string name="file_rename">إعادة التسمية</string>
@ -429,6 +440,7 @@
<string name="folder_already_exists">المجلد موجود مسبقاً</string>
<string name="folder_confirm_create">إنشاء</string>
<string name="folder_list_empty_headline">لا توجد مجلدات</string>
<string name="folder_name_empty">اسم المجلد لا يمكن أن يكون فارغاً</string>
<string name="folder_picker_choose_button_text">إختَر</string>
<string name="folder_picker_choose_caption_text">اختر مكان المجلد</string>
<string name="folder_picker_copy_button_text">إنسَخ</string>

View file

@ -279,8 +279,11 @@
<string name="drawer_quota">%1$s of %2$s used</string>
<string name="drawer_quota_unlimited">%1$s used</string>
<string name="drawer_synced_folders">Auto upload</string>
<string name="e2e_counter_too_old">Counter is too old</string>
<string name="e2e_hash_not_found">Hash not found</string>
<string name="e2e_not_yet_setup">E2E not yet setup</string>
<string name="e2e_offline">Not possible without internet connection</string>
<string name="e2e_signature_does_not_match">Signature does not match</string>
<string name="ecosystem_apps_display_assistant">Assistant</string>
<string name="ecosystem_apps_display_more">More</string>
<string name="ecosystem_apps_display_notes">Notes</string>
@ -406,6 +409,14 @@
<string name="file_migration_updating_index">Updating index…</string>
<string name="file_migration_use_data_folder">Use</string>
<string name="file_migration_waiting_for_unfinished_sync">Awaiting full sync…</string>
<string name="file_name_validator_current_path_is_invalid">Current folder name is invalid, please rename the folder. Redirecting to root</string>
<string name="file_name_validator_error_contains_reserved_names_or_invalid_characters">Folder path contains reserved names or invalid character</string>
<string name="file_name_validator_error_ends_with_space_period">Name ends with a space or a period</string>
<string name="file_name_validator_error_forbidden_file_extensions">.%s is a forbidden file extension</string>
<string name="file_name_validator_error_invalid_character">Name contains an invalid character: %s</string>
<string name="file_name_validator_error_reserved_names">%s is a forbidden name</string>
<string name="file_name_validator_rename_before_move_or_copy">%s. Please rename the file before moving or copying</string>
<string name="file_name_validator_upload_content_error">Some contents cannot able to uploaded due to contains reserved names or invalid character</string>
<string name="file_not_found">File not found</string>
<string name="file_not_synced">File could not be synced. Showing latest available version.</string>
<string name="file_rename">Rename</string>
@ -429,6 +440,7 @@
<string name="folder_already_exists">Folder already exists</string>
<string name="folder_confirm_create">Create</string>
<string name="folder_list_empty_headline">No folders here</string>
<string name="folder_name_empty">Folder name cannot be empty</string>
<string name="folder_picker_choose_button_text">Choose</string>
<string name="folder_picker_choose_caption_text">Choose target folder</string>
<string name="folder_picker_copy_button_text">Copy</string>

View file

@ -279,8 +279,11 @@
<string name="drawer_quota">%1$s von %2$s verwendet</string>
<string name="drawer_quota_unlimited">%1$s verwendet</string>
<string name="drawer_synced_folders">Automatisches Hochladen</string>
<string name="e2e_counter_too_old">Zähler ist zu alt</string>
<string name="e2e_hash_not_found">Hash nicht gefunden</string>
<string name="e2e_not_yet_setup">E2E bislang nicht eingerichtet</string>
<string name="e2e_offline">Ohne Internetverbindung nicht möglich</string>
<string name="e2e_signature_does_not_match">Signatur stimmt nicht überein</string>
<string name="ecosystem_apps_display_assistant">Assistent</string>
<string name="ecosystem_apps_display_more">Mehr</string>
<string name="ecosystem_apps_display_notes">Notizen</string>
@ -406,6 +409,14 @@
<string name="file_migration_updating_index">Aktualisiere Index…</string>
<string name="file_migration_use_data_folder">Verwende bestehenden</string>
<string name="file_migration_waiting_for_unfinished_sync">Warte auf die Fertigstellung aller Synchronisierungen …</string>
<string name="file_name_validator_current_path_is_invalid">Der aktuelle Ordnername ist ungültig. Bitte benennen Sie den Ordner um. Weiterleitung zum Stammverzeichnis</string>
<string name="file_name_validator_error_contains_reserved_names_or_invalid_characters">Der Ordnerpfad enthält reservierte Namen oder ungültige Zeichen</string>
<string name="file_name_validator_error_ends_with_space_period">Der Name endet mit einem Leerzeichen oder einem Punkt</string>
<string name="file_name_validator_error_forbidden_file_extensions">.%s ist eine verbotene Dateierweiterung</string>
<string name="file_name_validator_error_invalid_character">Der Name enthält ein ungültiges Zeichen: %s</string>
<string name="file_name_validator_error_reserved_names">%s ist ein verbotener Name</string>
<string name="file_name_validator_rename_before_move_or_copy">%s. Bitte Datei umbenennen, bevor diese verschoben oder kopiert wird</string>
<string name="file_name_validator_upload_content_error">Einige Inhalte können nicht hochgeladen werden, da sie reservierte Namen oder ungültige Zeichen enthalten</string>
<string name="file_not_found">Datei nicht gefunden</string>
<string name="file_not_synced">Die gewünschte Datei konnte nicht synchronisiert werden. Die letzte verfügbare Version wird angezeigt. </string>
<string name="file_rename">Umbenennen</string>
@ -429,6 +440,7 @@
<string name="folder_already_exists">Ordner existiert bereits</string>
<string name="folder_confirm_create">Erstellen</string>
<string name="folder_list_empty_headline">Keine Ordner vorhanden</string>
<string name="folder_name_empty">Der Ordnername darf nicht leer sein</string>
<string name="folder_picker_choose_button_text">Auswählen</string>
<string name="folder_picker_choose_caption_text">Zielordner auswählen</string>
<string name="folder_picker_copy_button_text">Kopieren</string>

View file

@ -279,8 +279,11 @@
<string name="drawer_quota">Usado %1$s de %2$s</string>
<string name="drawer_quota_unlimited">Usado %1$s</string>
<string name="drawer_synced_folders">Envío automático</string>
<string name="e2e_counter_too_old">O contador é demasiado antigo</string>
<string name="e2e_hash_not_found">Non se atopou o resumo criptográfico</string>
<string name="e2e_not_yet_setup">E2E aínda non está configurado</string>
<string name="e2e_offline">Non é posíbel sen conexión a internet</string>
<string name="e2e_signature_does_not_match">A sinatura non coincide</string>
<string name="ecosystem_apps_display_assistant">Asistente</string>
<string name="ecosystem_apps_display_more">Máis</string>
<string name="ecosystem_apps_display_notes">Notas</string>
@ -406,6 +409,14 @@
<string name="file_migration_updating_index">Actualizando o índice…</string>
<string name="file_migration_use_data_folder">Usar</string>
<string name="file_migration_waiting_for_unfinished_sync">Agardando que rematen as sincronizacións…</string>
<string name="file_name_validator_current_path_is_invalid">O nome actual do cartafol non é válido, cambie o nome do cartafol. Redireccionando á raíz</string>
<string name="file_name_validator_error_contains_reserved_names_or_invalid_characters">A ruta do cartafol contén nomes reservados ou caracteres non válidos</string>
<string name="file_name_validator_error_ends_with_space_period">O nome remata cun espazo ou un punto</string>
<string name="file_name_validator_error_forbidden_file_extensions">.%s é unha extensión de ficheiro prohibida.</string>
<string name="file_name_validator_error_invalid_character">O nome contén un carácter non válido: %s</string>
<string name="file_name_validator_error_reserved_names">%s é un nome prohibido.</string>
<string name="file_name_validator_rename_before_move_or_copy">%s. Cambie o nome do ficheiro antes de mover ou copiar</string>
<string name="file_name_validator_upload_content_error">Non é posíbel enviar algúns contidos porque conteñen nomes reservados ou caracteres non válidos</string>
<string name="file_not_found">Non se atopou o ficheiro</string>
<string name="file_not_synced">Non foi posíbel sincronizar o ficheiro. Amosase a última versión dispoñíbel.</string>
<string name="file_rename">Cambiar o nome</string>
@ -429,6 +440,7 @@
<string name="folder_already_exists">Xa existe o cartafol</string>
<string name="folder_confirm_create">Crear</string>
<string name="folder_list_empty_headline">Aquí non hai cartafoles</string>
<string name="folder_name_empty">O nome do cartafol non pode estar baleiro</string>
<string name="folder_picker_choose_button_text">Escoller</string>
<string name="folder_picker_choose_caption_text">Escoller o cartafol de destino</string>
<string name="folder_picker_copy_button_text">Copiar</string>

View file

@ -279,8 +279,11 @@
<string name="drawer_quota">%1$s av %2$s brukt</string>
<string name="drawer_quota_unlimited">%1$s brukt</string>
<string name="drawer_synced_folders">Auto-opplasting</string>
<string name="e2e_counter_too_old">Telleren er for gammel</string>
<string name="e2e_hash_not_found">Hash ikke funnet</string>
<string name="e2e_not_yet_setup">E2E er ikke satt opp enda</string>
<string name="e2e_offline">Ikke mulig uten internettilkobling</string>
<string name="e2e_signature_does_not_match">Signatur samsvarer ikke</string>
<string name="ecosystem_apps_display_assistant">Assistent</string>
<string name="ecosystem_apps_display_more">Mer</string>
<string name="ecosystem_apps_display_notes">Notater</string>
@ -406,6 +409,14 @@
<string name="file_migration_updating_index">Oppdaterer indeks…</string>
<string name="file_migration_use_data_folder">Bruk</string>
<string name="file_migration_waiting_for_unfinished_sync">Venter på full synkronisering…</string>
<string name="file_name_validator_current_path_is_invalid">Gjeldende mappenavn er ugyldig, vennligst gi nytt navn til mappen. Omdirigerer til rot</string>
<string name="file_name_validator_error_contains_reserved_names_or_invalid_characters">Mappebanen inneholder reserverte navn eller ugyldige tegn</string>
<string name="file_name_validator_error_ends_with_space_period">Navn slutter med et mellomrom eller et punktum</string>
<string name="file_name_validator_error_forbidden_file_extensions">.%s er en forbudt filtype</string>
<string name="file_name_validator_error_invalid_character">Navnet inneholder et ugyldig tegn: %s</string>
<string name="file_name_validator_error_reserved_names">%s er et forbudt navn</string>
<string name="file_name_validator_rename_before_move_or_copy">%s. Gi nytt navn til filen før du flytter eller kopierer</string>
<string name="file_name_validator_upload_content_error">Noe innhold kan ikke lastes opp på grunn av inneholder reserverte navn eller ugyldige tegn</string>
<string name="file_not_found">Finner ikke filen</string>
<string name="file_not_synced">Kunne ikke synkronisere fil. Viser sist tilgjengelige versjon.</string>
<string name="file_rename">Gi nytt navn</string>
@ -429,6 +440,7 @@
<string name="folder_already_exists">Mappe finnes allerede</string>
<string name="folder_confirm_create">Opprett</string>
<string name="folder_list_empty_headline">Ingen mapper her</string>
<string name="folder_name_empty">Mappenavnet kan ikke være tomt</string>
<string name="folder_picker_choose_button_text">Velg</string>
<string name="folder_picker_choose_caption_text">Velg målmappe</string>
<string name="folder_picker_copy_button_text">Kopi</string>

View file

@ -279,8 +279,10 @@
<string name="drawer_quota">%1$s av %2$s använt</string>
<string name="drawer_quota_unlimited">%1$s använt</string>
<string name="drawer_synced_folders">Automatisk uppladdning</string>
<string name="e2e_hash_not_found">Hash kunde inte hittas</string>
<string name="e2e_not_yet_setup">E2E ej inställt än</string>
<string name="e2e_offline">Inte möjligt utan internetuppkoppling</string>
<string name="e2e_signature_does_not_match">Signaturen matchar inte</string>
<string name="ecosystem_apps_display_assistant">Assistent</string>
<string name="ecosystem_apps_display_more">Mer</string>
<string name="ecosystem_apps_display_notes">Anteckning</string>
@ -406,6 +408,14 @@
<string name="file_migration_updating_index">Uppdaterar index…</string>
<string name="file_migration_use_data_folder">Använd</string>
<string name="file_migration_waiting_for_unfinished_sync">Väntar på fullständig synkronisering…</string>
<string name="file_name_validator_current_path_is_invalid">Aktuellt mappnamn är ogiltigt. Byt namn på mappen. Omdirigerar till root</string>
<string name="file_name_validator_error_contains_reserved_names_or_invalid_characters">Mappsökvägen innehåller reserverade namn eller ogiltiga tecken</string>
<string name="file_name_validator_error_ends_with_space_period">Namnet slutar med ett mellanslag eller en punkt</string>
<string name="file_name_validator_error_forbidden_file_extensions">.%s är ett förbjudet filtillägg</string>
<string name="file_name_validator_error_invalid_character">Namnet innehåller ett ogiltigt tecken: %s</string>
<string name="file_name_validator_error_reserved_names">%s är ett förbjudet namn</string>
<string name="file_name_validator_rename_before_move_or_copy">%s. Byt namn på filen innan du flyttar eller kopierar</string>
<string name="file_name_validator_upload_content_error">En del innehåll kan inte laddas upp på grund av att det innehåller reserverade namn eller ogiltiga tecken</string>
<string name="file_not_found">Filen hittades inte</string>
<string name="file_not_synced">Filen kunde inte synkroniseras. Visar senaste tillgängliga versionen.</string>
<string name="file_rename">Byt namn</string>
@ -429,6 +439,7 @@
<string name="folder_already_exists">Mapp finns redan</string>
<string name="folder_confirm_create">Skapa</string>
<string name="folder_list_empty_headline">Inga mappar här</string>
<string name="folder_name_empty">Mappnamnet får inte vara tomt</string>
<string name="folder_picker_choose_button_text">Välj</string>
<string name="folder_picker_choose_caption_text">Välj målmapp</string>
<string name="folder_picker_copy_button_text">Kopiera</string>

View file

@ -279,8 +279,11 @@
<string name="drawer_quota">%1$s / %2$s kullanıldı</string>
<string name="drawer_quota_unlimited">%1$s kullanılıyor</string>
<string name="drawer_synced_folders">Otomatik yükleme</string>
<string name="e2e_counter_too_old">Sayaç çok eski</string>
<string name="e2e_hash_not_found">Karma bulunamadı</string>
<string name="e2e_not_yet_setup">Uçtan uca şifreleme henüz kurulmamış</string>
<string name="e2e_offline">İnternet bağlantısı olmadan yapılamaz</string>
<string name="e2e_signature_does_not_match">İmza eşleşmiyor</string>
<string name="ecosystem_apps_display_assistant">Yardımcı</string>
<string name="ecosystem_apps_display_more">Diğer</string>
<string name="ecosystem_apps_display_notes">Notlar</string>
@ -406,6 +409,14 @@
<string name="file_migration_updating_index">Dizin güncelleniyor …</string>
<string name="file_migration_use_data_folder">Kullanılsın</string>
<string name="file_migration_waiting_for_unfinished_sync">Eşitleme işleminin tamamlanması bekleniyor …</string>
<string name="file_name_validator_current_path_is_invalid">Geçerli klasör adı geçersiz. Lütfen klasörü yeniden adlandırın. Kök klasöre yönlendiriliyorsunuz.</string>
<string name="file_name_validator_error_contains_reserved_names_or_invalid_characters">Klasör yolunda ayrılmış adlar veya geçersiz karakterler var</string>
<string name="file_name_validator_error_ends_with_space_period">Ad bir boşluk ya da nokta ile bitiyor</string>
<string name="file_name_validator_error_forbidden_file_extensions">.%s dosya uzantısına izin verilmiyor.</string>
<string name="file_name_validator_error_invalid_character">Ad içinde geçersiz bir karakter var: %s</string>
<string name="file_name_validator_error_reserved_names">%s adına izin verilmiyor</string>
<string name="file_name_validator_rename_before_move_or_copy">%s. Kopyalamadan ya da taşımadan önce lütfen dosyayı yeniden adlandırın</string>
<string name="file_name_validator_upload_content_error">Bazı içerikler, içinde ayrılmış adlar veya geçersiz karakterler bulunduğundan yüklenemedi</string>
<string name="file_not_found">Dosya bulunamadı</string>
<string name="file_not_synced">Dosya eşitlenemedi. Kullanılabilen son sürüm görüntüleniyor.</string>
<string name="file_rename">Yeniden adlandır</string>
@ -429,6 +440,7 @@
<string name="folder_already_exists">Klasör zaten var</string>
<string name="folder_confirm_create">Ekle</string>
<string name="folder_list_empty_headline">Burada herhangi bir klasör yok</string>
<string name="folder_name_empty">Klasör adı boş olamaz</string>
<string name="folder_picker_choose_button_text">Seçin</string>
<string name="folder_picker_choose_caption_text">Hedef klasörü seçin</string>
<string name="folder_picker_copy_button_text">Kopyala</string>

View file

@ -278,8 +278,11 @@
<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_counter_too_old">Лічильний застарілий</string>
<string name="e2e_hash_not_found">Не знайдено</string>
<string name="e2e_not_yet_setup">Наскрізне шифруванння ще не налаштовано</string>
<string name="e2e_offline">Дія не можлива без доступу до з\'єднання з мережею</string>
<string name="e2e_signature_does_not_match">Підпис не збігається</string>
<string name="ecosystem_apps_display_assistant">Помічник</string>
<string name="ecosystem_apps_display_more">Більше</string>
<string name="ecosystem_apps_display_notes">Нотатки</string>
@ -365,6 +368,7 @@
<string name="file_list_empty_headline">Тут відсутні файли</string>
<string name="file_list_empty_headline_search">Жодного збігу у цьому каталозі</string>
<string name="file_list_empty_headline_server_search">Жодного збігу</string>
<string name="file_list_empty_local_search">Не знайдено файли або каталоги згідно із запитом</string>
<string name="file_list_empty_moving">Тут нічого немає. Ви можете створити каталог.</string>
<string name="file_list_empty_on_device">Тут ти можете знайти каталоги та файли, які було звантажено.</string>
<string name="file_list_empty_recently_modified">Відсутні зміни серед файлів за останні 7 днів</string>
@ -402,6 +406,14 @@
<string name="file_migration_updating_index">Оновлення індексу...</string>
<string name="file_migration_use_data_folder">Використовувати</string>
<string name="file_migration_waiting_for_unfinished_sync">Очікування повної синхронізації…</string>
<string name="file_name_validator_current_path_is_invalid">Перейменуйте назву поточого каталогу, оскільки його назва не є дійсною. Переспрямування до кореневого каталогу.</string>
<string name="file_name_validator_error_contains_reserved_names_or_invalid_characters">Шлях до каталогу містить зарезервовані імена або недійсні символи</string>
<string name="file_name_validator_error_ends_with_space_period">Ім\'я закінчується на символ пробілу або крапку</string>
<string name="file_name_validator_error_forbidden_file_extensions">.%s не є дозволеним розширенням файлу</string>
<string name="file_name_validator_error_invalid_character">Ім\'я містить недійсний символ: %s</string>
<string name="file_name_validator_error_reserved_names">%s не є дозволеним ім\'ям</string>
<string name="file_name_validator_rename_before_move_or_copy">%s. Перейменуйте файл перед переміщенням або копіюванням</string>
<string name="file_name_validator_upload_content_error">Окремі дані неможливо завантажити, оскільки він містить зарезервовані імена або недійсні символи</string>
<string name="file_not_found">Файл не знайдено</string>
<string name="file_not_synced">Неможливо синхронізувати файл. Буде показано останню доступну версію.</string>
<string name="file_rename">Перейменувати</string>
@ -425,6 +437,7 @@
<string name="folder_already_exists">Каталог вже існує</string>
<string name="folder_confirm_create">Створити</string>
<string name="folder_list_empty_headline">Тут відсутні каталоги</string>
<string name="folder_name_empty">Ім\'я каталогу не може бути порожнім</string>
<string name="folder_picker_choose_button_text">Вибрати</string>
<string name="folder_picker_choose_caption_text">Виберіть каталог призначення</string>
<string name="folder_picker_copy_button_text">Копія</string>

View file

@ -279,8 +279,11 @@
<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_counter_too_old">計數器太舊</string>
<string name="e2e_hash_not_found">未找到哈希值</string>
<string name="e2e_not_yet_setup">尚未設定端到端加密</string>
<string name="e2e_offline">沒有互聯網連接無法使用</string>
<string name="e2e_signature_does_not_match">簽名不相符</string>
<string name="ecosystem_apps_display_assistant">助手</string>
<string name="ecosystem_apps_display_more">更多</string>
<string name="ecosystem_apps_display_notes">備註</string>
@ -406,6 +409,14 @@
<string name="file_migration_updating_index">更新索引中…</string>
<string name="file_migration_use_data_folder">使用</string>
<string name="file_migration_waiting_for_unfinished_sync">等待完整同步…</string>
<string name="file_name_validator_current_path_is_invalid">目前資料夾名稱無效,請重新命名該資料夾。重定向到根目錄</string>
<string name="file_name_validator_error_contains_reserved_names_or_invalid_characters">資料夾途徑包含保留名稱或無效字符</string>
<string name="file_name_validator_error_ends_with_space_period">名稱以空格或句點結尾。</string>
<string name="file_name_validator_error_forbidden_file_extensions">「.%s」是禁止使用的副檔名。</string>
<string name="file_name_validator_error_invalid_character">名字含有無效的字元:「%s」。</string>
<string name="file_name_validator_error_reserved_names">「%s」是禁止使用的名字。</string>
<string name="file_name_validator_rename_before_move_or_copy">%s。請在移動或複製之前重新命名檔案</string>
<string name="file_name_validator_upload_content_error">部分內容因包含保留名稱或無效字元而無法上傳</string>
<string name="file_not_found">找不到檔案</string>
<string name="file_not_synced">檔案不能同步,將顯示最新可用版本。</string>
<string name="file_rename">更名</string>
@ -429,6 +440,7 @@
<string name="folder_already_exists">文件夾已存在</string>
<string name="folder_confirm_create">建立</string>
<string name="folder_list_empty_headline">這裡沒有資料夾</string>
<string name="folder_name_empty">資料夾名稱不能為空</string>
<string name="folder_picker_choose_button_text">選擇</string>
<string name="folder_picker_choose_caption_text">選擇目標資料夾</string>
<string name="folder_picker_copy_button_text">複製</string>

View file

@ -279,8 +279,11 @@
<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_counter_too_old">計數器太舊</string>
<string name="e2e_hash_not_found">找不到雜湊值</string>
<string name="e2e_not_yet_setup">尚未設定端到端加密</string>
<string name="e2e_offline">沒有網際網路連線就無法使用</string>
<string name="e2e_signature_does_not_match">簽章不相符</string>
<string name="ecosystem_apps_display_assistant">助理</string>
<string name="ecosystem_apps_display_more">更多</string>
<string name="ecosystem_apps_display_notes">筆記</string>
@ -406,6 +409,14 @@
<string name="file_migration_updating_index">正在更新索引……</string>
<string name="file_migration_use_data_folder">使用</string>
<string name="file_migration_waiting_for_unfinished_sync">正在等待完全同步……</string>
<string name="file_name_validator_current_path_is_invalid">目前資料夾名稱無效,請重新命名資料夾。重新導向至根目錄</string>
<string name="file_name_validator_error_contains_reserved_names_or_invalid_characters">資料夾路徑包含保留名稱或無效字元</string>
<string name="file_name_validator_error_ends_with_space_period">名稱以空格或句點結尾</string>
<string name="file_name_validator_error_forbidden_file_extensions">.%s 是禁止使用的副檔名</string>
<string name="file_name_validator_error_invalid_character">名稱包含無效的字元:%s</string>
<string name="file_name_validator_error_reserved_names">%s 是禁止的名稱</string>
<string name="file_name_validator_rename_before_move_or_copy">%s。請在移動或複製前重新命名檔案</string>
<string name="file_name_validator_upload_content_error">部份內容包含保留名稱或無效字元而無法上傳</string>
<string name="file_not_found">找不到檔案</string>
<string name="file_not_synced">檔案無法同步,將顯示最新可用版本。</string>
<string name="file_rename">重新命名</string>
@ -429,6 +440,7 @@
<string name="folder_already_exists">資料夾已存在</string>
<string name="folder_confirm_create">建立</string>
<string name="folder_list_empty_headline">這裡沒有資料夾</string>
<string name="folder_name_empty">資料夾名稱不能為空</string>
<string name="folder_picker_choose_button_text">選擇</string>
<string name="folder_picker_choose_caption_text">選擇目標資料夾</string>
<string name="folder_picker_copy_button_text">複製</string>

View file

@ -1218,13 +1218,15 @@
<string name="sub_folder_rule_day">Year/Month/Day</string>
<string name="secure_share_not_set_up">Secure sharing is not set up for this user</string>
<string name="share_not_allowed_when_file_drop">Resharing is not allowed during secure file drop</string>
<string name="prefs_category_sync">Sync</string>
<string name="internal_two_way_sync">Internal two way sync</string>
<string name="prefs_two_way_sync_summary">Manage internal folders for two way sync</string>
<string name="internal_two_way_sync_not_yet">Not yet, soon to be synced</string>
<string name="gplay_restriction">Google restricted downloading APK/AAB files!</string>
<string name="file_list_empty_local_search">No file or folder matching your search</string>
<string name="unified_search_fragment_calendar_event_not_found">Event not found, you can always sync to update. Redirecting to web…</string>
<string name="unified_search_fragment_contact_not_found">Contact not found, you can always sync to update. Redirecting to web…</string>
<string name="unified_search_fragment_permission_needed">Permissions are required to open search result otherwise it will redirected to web…</string>
<string name="file_name_validator_current_path_is_invalid">Current folder name is invalid, please rename the folder. Redirecting to root</string>
<string name="file_name_validator_rename_before_move_or_copy">%s. Please rename the file before moving or copying</string>
<string name="file_name_validator_upload_content_error">Some contents cannot able to uploaded due to contains reserved names or invalid character</string>
@ -1233,4 +1235,5 @@
<string name="file_name_validator_error_reserved_names">%s is a forbidden name</string>
<string name="file_name_validator_error_forbidden_file_extensions">.%s is a forbidden file extension</string>
<string name="file_name_validator_error_ends_with_space_period">Name ends with a space or a period</string>
<string name="sync">Sync</string>
</resources>

View file

@ -53,13 +53,23 @@
android:key="show_media_scan_notifications"/>
</PreferenceCategory>
<PreferenceCategory
android:title="@string/prefs_category_sync"
android:key="sync">
<Preference
android:title="@string/drawer_synced_folders"
android:key="syncedFolders"
android:summary="@string/prefs_sycned_folders_summary" />
<Preference
android:title="@string/internal_two_way_sync"
android:key="internal_two_way_sync"
android:summary="@string/prefs_two_way_sync_summary" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/prefs_category_more"
android:key="more">
<Preference
android:title="@string/drawer_synced_folders"
android:key="syncedFolders"
android:summary="@string/prefs_sycned_folders_summary" />
<Preference
android:title="@string/prefs_calendar_contacts"
android:key="calendar_contacts"