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

This commit is contained in:
Tobias Kaminsky 2024-11-06 03:39:15 +01:00
commit 8247a31e5b
41 changed files with 237 additions and 69 deletions

View file

@ -16,7 +16,7 @@ import org.gradle.internal.jvm.Jvm
buildscript {
dependencies {
classpath "com.android.tools.build:gradle:$androidPluginVersion"
classpath 'com.github.spotbugs.snom:spotbugs-gradle-plugin:6.0.25'
classpath 'com.github.spotbugs.snom:spotbugs-gradle-plugin:6.0.26'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.23.7"
classpath "commons-httpclient:commons-httpclient:3.1@jar" // remove after entire switch to lib v2
@ -269,7 +269,7 @@ dependencies {
}
// Jetpack Compose
implementation(platform("androidx.compose:compose-bom:2024.10.00"))
implementation(platform("androidx.compose:compose-bom:2024.10.01"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.material3:material3")
@ -280,7 +280,7 @@ dependencies {
// remove after entire switch to lib v2
implementation "commons-httpclient:commons-httpclient:3.1@jar" // remove after entire switch to lib v2
implementation 'org.apache.jackrabbit:jackrabbit-webdav:2.13.5' // remove after entire switch to lib v2
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.constraintlayout:constraintlayout:2.2.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'com.google.android.material:material:1.12.0'
implementation 'com.jakewharton:disklrucache:2.0.2'
@ -310,7 +310,7 @@ dependencies {
exclude group: "com.android.support"
}
implementation 'com.caverock:androidsvg:1.4'
implementation 'androidx.annotation:annotation:1.9.0'
implementation 'androidx.annotation:annotation:1.9.1'
implementation 'com.vanniktech:emoji-google:0.21.0'
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.6")

View file

@ -34,6 +34,10 @@
<issue id="UnusedQuantity">
<ignore path="**/values-**/strings.xml" />
</issue>
<issue id="StringFormatCount">
<ignore path="**/values-**/strings.xml" />
</issue>
<issue id="ExtraTranslation">
<ignore path="**/strings.xml"/>

View file

@ -183,7 +183,7 @@ public abstract class AbstractIT {
String darkModeParameter = arguments.getString("DARKMODE");
if (darkModeParameter != null) {
if (darkModeParameter.equalsIgnoreCase("dark")) {
if ("dark".equalsIgnoreCase(darkModeParameter)) {
DARK_MODE = "dark";
AppPreferencesImpl.fromContext(targetContext).setDarkThemeMode(DarkMode.DARK);
MainApp.setAppTheme(DarkMode.DARK);
@ -192,7 +192,7 @@ public abstract class AbstractIT {
}
}
if (DARK_MODE.equalsIgnoreCase("light") && COLOR.equalsIgnoreCase("blue")) {
if ("light".equalsIgnoreCase(DARK_MODE) && "blue".equalsIgnoreCase(COLOR)) {
// use already existing names
DARK_MODE = "";
COLOR = "";

View file

@ -515,7 +515,7 @@ public class UploadIT extends AbstractOnServerIT {
OCFile ocFile = null;
for (OCFile f : files) {
if (f.getFileName().equals("metadata.jpg")) {
if ("metadata.jpg".equals(f.getFileName())) {
ocFile = f;
break;
}

View file

@ -9,7 +9,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
@ -53,7 +52,6 @@
must request the FOREGROUND_SERVICE permission
-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <!-- Runtime permissions introduced in Android 13 (API level 33) -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <!-- Needed for Android 14 (API level 34) -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />

View file

@ -1,7 +1,7 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2023 TSI-mc
* SPDX-FileCopyrightText: 2023-2024 TSI-mc <surinder.kumar@t-systems.com>
* SPDX-FileCopyrightText: 2019 Chris Narkiewicz <hello@ezaquarii.com>
* SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
*/
@ -19,8 +19,10 @@ import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import com.nextcloud.client.onboarding.FirstRunActivity;
import com.nextcloud.common.NextcloudClient;
import com.nextcloud.utils.extensions.AccountExtensionsKt;
import com.nmc.android.ui.LauncherActivity;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.authentication.AuthenticatorActivity;
@ -179,19 +181,20 @@ public class UserAccountManagerImpl implements UserAccountManager {
*/
@Nullable
private User createUserFromAccount(@NonNull Account account) {
if (AccountExtensionsKt.isAnonymous(account, context)) {
Context safeContext = context != null ? context : MainApp.getAppContext();
if (safeContext == null) {
Log_OC.e(TAG, "Unable to obtain a valid context");
return null;
}
if (context == null) {
Log_OC.d(TAG, "Context is null MainApp.getAppContext() used");
context = MainApp.getAppContext();
if (AccountExtensionsKt.isAnonymous(account, safeContext)) {
return null;
}
OwnCloudAccount ownCloudAccount;
try {
ownCloudAccount = new OwnCloudAccount(account, context);
} catch (AccountUtils.AccountNotFoundException ex) {
ownCloudAccount = new OwnCloudAccount(account, safeContext);
} catch (Exception ex) {
return null;
}
@ -211,7 +214,7 @@ public class UserAccountManagerImpl implements UserAccountManager {
*/
String serverAddressStr = accountManager.getUserData(account, AccountUtils.Constants.KEY_OC_BASE_URL);
if (serverAddressStr == null || serverAddressStr.isEmpty()) {
return AnonymousUser.fromContext(context);
return AnonymousUser.fromContext(safeContext);
}
URI serverUri = URI.create(serverAddressStr); // TODO: validate
@ -397,6 +400,10 @@ public class UserAccountManagerImpl implements UserAccountManager {
@Override
public void startAccountCreation(final Activity activity) {
// skipping AuthenticatorActivity redirection when user is on Launcher or FirstRun Activity
if (activity instanceof LauncherActivity || activity instanceof FirstRunActivity) return;
Intent intent = new Intent(context, AuthenticatorActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

View file

@ -40,8 +40,15 @@ class OfflineOperationTypeAdapter : JsonSerializer<OfflineOperationType>, JsonDe
jsonObject.addProperty("mimeType", src.mimeType)
}
is OfflineOperationType.RenameFile -> {
jsonObject.addProperty("type", src.type)
jsonObject.addProperty("ocFileId", src.ocFileId)
jsonObject.addProperty("newName", src.newName)
}
null -> Unit
}
return jsonObject
}
@ -65,6 +72,12 @@ class OfflineOperationTypeAdapter : JsonSerializer<OfflineOperationType>, JsonDe
jsonObject.get("mimeType").asString
)
OfflineOperationRawType.RenameFile.name -> OfflineOperationType.RenameFile(
jsonObject.get("type").asString,
jsonObject.get("ocFileId").asLong,
jsonObject.get("newName").asString
)
else -> null
}
}

View file

@ -173,5 +173,4 @@ interface BackgroundJobManager {
fun startOfflineOperations()
fun startPeriodicallyOfflineOperation()
fun scheduleInternal2WaySync(intervalMinutes: Long)
fun cancelInternal2WaySyncJob()
}

View file

@ -711,8 +711,4 @@ internal class BackgroundJobManagerImpl(
workManager.enqueueUniquePeriodicWork(JOB_INTERNAL_TWO_WAY_SYNC, ExistingPeriodicWorkPolicy.UPDATE, request)
}
override fun cancelInternal2WaySyncJob() {
workManager.cancelJob(JOB_INTERNAL_TWO_WAY_SYNC)
}
}

View file

@ -57,6 +57,8 @@ class FilesExportWork(
}
private fun exportFiles(fileIDs: LongArray): Int {
val fileDownloadHelper = FileDownloadHelper.instance()
var successfulExports = 0
fileIDs
.asSequence()
@ -76,7 +78,11 @@ class FilesExportWork(
showErrorNotification(successfulExports)
}
} else {
downloadFile(ocFile)
fileDownloadHelper.downloadFile(
user,
ocFile,
downloadType = DownloadType.EXPORT
)
}
successfulExports++
@ -95,14 +101,6 @@ class FilesExportWork(
)
}
private fun downloadFile(ocFile: OCFile) {
FileDownloadHelper.instance().downloadFile(
user,
ocFile,
downloadType = DownloadType.EXPORT
)
}
private fun showErrorNotification(successfulExports: Int) {
val message = if (successfulExports == 0) {
appContext.resources.getQuantityString(R.plurals.export_failed, successfulExports, successfulExports)

View file

@ -25,6 +25,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.utils.Log_OC
import com.owncloud.android.lib.resources.files.UploadFileRemoteOperation
import com.owncloud.android.operations.CreateFolderOperation
import com.owncloud.android.operations.RenameFileOperation
import com.owncloud.android.utils.theme.ViewThemeUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.NonCancellable
@ -98,6 +99,7 @@ class OfflineOperationsWorker(
Result.success()
} catch (e: Exception) {
Log_OC.d(TAG, "OfflineOperationsWorker terminated: $e")
notificationManager.dismissNotification()
Result.failure()
}
}
@ -146,6 +148,21 @@ class OfflineOperationsWorker(
createFileOperation.execute(client) to createFileOperation
}
is OfflineOperationType.RenameFile -> {
val renameFileOperation = withContext(NonCancellable) {
val operationType = (operation.type as OfflineOperationType.RenameFile)
fileDataStorageManager.getFileById(operationType.ocFileId)?.remotePath?.let { updatedRemotePath ->
RenameFileOperation(
updatedRemotePath,
operationType.newName,
fileDataStorageManager
)
}
}
renameFileOperation?.execute(client) to renameFileOperation
}
else -> {
Log_OC.d(TAG, "Unsupported operation type: ${operation.type}")
null

View file

@ -2,6 +2,7 @@
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2019 Chris Narkiewicz <hello@ezaquarii.com>
* SPDX-FileCopyrightText: 2024 TSI-mc <surinder.kumar@t-systems.com>
* SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
*/
package com.nextcloud.client.onboarding
@ -42,7 +43,7 @@ internal class OnboardingServiceImpl constructor(
override val isFirstRun: Boolean
get() {
return accountProvider.currentAccount == null
return accountProvider.user.isAnonymous
}
override fun shouldShowWhatsNew(callingContext: Context): Boolean {

View file

@ -11,15 +11,23 @@ sealed class OfflineOperationType {
abstract val type: String
data class CreateFolder(override val type: String, var path: String) : OfflineOperationType()
data class CreateFile(
override val type: String,
val localPath: String,
var remotePath: String,
val mimeType: String
) : OfflineOperationType()
data class RenameFile(
override val type: String,
var ocFileId: Long,
val newName: String
) : OfflineOperationType()
}
enum class OfflineOperationRawType {
CreateFolder,
CreateFile
CreateFile,
RenameFile
}

View file

@ -3,7 +3,7 @@
*
* SPDX-FileCopyrightText: 2023 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-FileCopyrightText: 2023 Andy Scherzinger <info@andy-scherzinger.de>
* SPDX-FileCopyrightText: 2023 TSI-mc
* SPDX-FileCopyrightText: 2023-2024 TSI-mc <surinder.kumar@t-systems.com>
* SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
*/
package com.nmc.android.ui
@ -18,6 +18,7 @@ import androidx.annotation.VisibleForTesting
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import com.nextcloud.client.preferences.AppPreferences
import com.owncloud.android.R
import com.owncloud.android.authentication.AuthenticatorActivity
import com.owncloud.android.databinding.ActivitySplashBinding
import com.owncloud.android.ui.activity.BaseActivity
import com.owncloud.android.ui.activity.FileDisplayActivity
@ -65,6 +66,8 @@ class LauncherActivity : BaseActivity() {
Handler(Looper.getMainLooper()).postDelayed({
if (user.isPresent) {
startActivity(Intent(this, FileDisplayActivity::class.java))
} else {
startActivity(Intent(this, AuthenticatorActivity::class.java))
}
finish()
}, SPLASH_DURATION)

View file

@ -1,7 +1,7 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2023 TSI-mc
* SPDX-FileCopyrightText: 2023-2024 TSI-mc <surinder.kumar@t-systems.com>
* SPDX-FileCopyrightText: 2019-2021 Tobias Kaminsky <tobias@kaminsky.me>
* SPDX-FileCopyrightText: 2018 Andy Scherzinger <info@andy-scherzinger>
* SPDX-FileCopyrightText: 2017 Mario Danic <mario@lovelyhq.com>
@ -1364,14 +1364,13 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
}
private void endSuccess() {
if (onlyAdd) {
finish();
} else {
if (!onlyAdd) {
Intent i = new Intent(this, FileDisplayActivity.class);
i.setAction(FileDisplayActivity.RESTART);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
}
finish();
}
private void getUserCapabilitiesAndFinish() {

View file

@ -217,6 +217,35 @@ public class FileDataStorageManager {
offlineOperationsRepository.deleteOperation(file);
}
public void addRenameFileOfflineOperation(OCFile file, String newName) {
OfflineOperationEntity entity = new OfflineOperationEntity();
entity.setFilename(newName);
entity.setParentOCFileId(file.getParentId());
OfflineOperationType operationType = new OfflineOperationType.RenameFile(OfflineOperationRawType.RenameFile.name(), file.getFileId(), newName);
entity.setType(operationType);
entity.setPath(file.getDecryptedRemotePath());
long createdAt = System.currentTimeMillis();
long modificationTimestamp = System.currentTimeMillis();
entity.setCreatedAt(createdAt);
entity.setModifiedAt(modificationTimestamp / 1000);
offlineOperationDao.insert(entity);
}
public String getFilenameConsideringOfflineOperation(OCFile file) {
String filename = file.getDecryptedFileName();
OfflineOperationEntity renameEntity = offlineOperationDao.getByPath(file.getDecryptedRemotePath());
if (renameEntity != null && renameEntity.getType() instanceof OfflineOperationType.RenameFile renameFile) {
filename = renameFile.getNewName();
}
return filename;
}
public void renameOfflineOperation(OCFile file, String newFolderName) {
var entity = offlineOperationDao.getByPath(file.getDecryptedRemotePath());
if (entity == null) {

View file

@ -397,8 +397,10 @@ public class FileMenuFilter {
}
private boolean anyFileDownloading() {
final var fileDownloadHelper = FileDownloadHelper.Companion.instance();
for (OCFile file : files) {
if (FileDownloadHelper.Companion.instance().isDownloading(user, file)) {
if (fileDownloadHelper.isDownloading(user, file)) {
return true;
}
}

View file

@ -124,7 +124,7 @@ public class CreateShareWithShareeOperation extends SyncOperation {
try {
String publicKey = EncryptionUtils.getPublicKey(user, shareeName, arbitraryDataProvider);
if (publicKey.equals("")) {
if ("".equals(publicKey)) {
NextcloudClient nextcloudClient = new ClientFactoryImpl(context).createNextcloudClient(user);
RemoteOperationResult<String> result = new GetPublicKeyRemoteOperation(shareeName).execute(nextcloudClient);
if (result.isSuccess()) {

View file

@ -441,7 +441,8 @@ public class SynchronizeFolderOperation extends SyncOperation {
}
private void startDirectDownloads() {
mFilesForDirectDownload.forEach(file -> FileDownloadHelper.Companion.instance().downloadFile(user, file));
final var fileDownloadHelper = FileDownloadHelper.Companion.instance();
mFilesForDirectDownload.forEach(file -> fileDownloadHelper.downloadFile(user, file));
}
/**

View file

@ -2,6 +2,7 @@
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2024 TSI-mc <surinder.kumar@t-systems.com>
* SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
*/
package com.owncloud.android.ui.activity;
@ -115,7 +116,9 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
protected void onRestart() {
Log_OC.v(TAG, "onRestart() start");
super.onRestart();
mixinRegistry.onRestart();
if (enableAccountHandling) {
mixinRegistry.onRestart();
}
}
private void onThemeSettingsModeChanged() {

View file

@ -181,7 +181,7 @@ class InternalTwoWaySyncActivity :
if (isChecked) {
backgroundJobManager.scheduleInternal2WaySync(preferences.twoWaySyncInterval)
} else {
backgroundJobManager.cancelInternal2WaySyncJob()
backgroundJobManager.cancelTwoWaySyncJob()
}
}
}

View file

@ -142,7 +142,7 @@ public abstract class ToolbarActivity extends BaseActivity implements Injectable
String title;
boolean isRoot = isRoot(chosenFile);
title = isRoot ? themeUtils.getDefaultDisplayNameForRootFolder(this) : chosenFile.getFileName();
title = isRoot ? themeUtils.getDefaultDisplayNameForRootFolder(this) : fileDataStorageManager.getFilenameConsideringOfflineOperation(chosenFile);
updateActionBarTitleAndHomeButtonByString(title);
if (mAppBar != null) {

View file

@ -518,7 +518,7 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
}
private void bindListGridItemViewHolder(ListGridItemViewHolder holder, OCFile file) {
holder.getFileName().setText(file.getDecryptedFileName());
holder.getFileName().setText(mStorageManager.getFilenameConsideringOfflineOperation(file));
boolean gridImage = MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file);
if (gridView && gridImage) {

View file

@ -335,10 +335,11 @@ class OCFileListDelegate(
private fun showLocalFileIndicator(file: OCFile, gridViewHolder: ListViewHolder) {
val operationsServiceBinder = transferServiceGetter.operationsServiceBinder
val fileDownloadHelper = FileDownloadHelper.instance()
val icon: Int? = when {
operationsServiceBinder?.isSynchronizing(user, file) == true ||
FileDownloadHelper.instance().isDownloading(user, file) ||
fileDownloadHelper.isDownloading(user, file) ||
fileUploadHelper.isUploading(user, file) -> {
// synchronizing, downloading or uploading
R.drawable.ic_synchronizing

View file

@ -24,6 +24,7 @@ import com.google.common.collect.Sets
import com.nextcloud.client.account.CurrentAccountProvider
import com.nextcloud.client.di.Injectable
import com.nextcloud.utils.extensions.getParcelableArgument
import com.nextcloud.utils.extensions.typedActivity
import com.nextcloud.utils.fileNameValidator.FileNameValidator.checkFileName
import com.nextcloud.utils.fileNameValidator.FileNameValidator.isFileHidden
import com.owncloud.android.R
@ -147,12 +148,16 @@ class RenameFileDialogFragment : DialogFragment(), DialogInterface.OnClickListen
if (mTargetFile?.isOfflineOperation == true) {
fileDataStorageManager.renameOfflineOperation(mTargetFile, newFileName)
if (requireActivity() is FileDisplayActivity) {
val activity = requireActivity() as FileDisplayActivity
activity.refreshCurrentDirectory()
}
typedActivity<FileDisplayActivity>()?.refreshCurrentDirectory()
} else {
(requireActivity() as ComponentsGetter).fileOperationsHelper.renameFile(mTargetFile, newFileName)
typedActivity<FileDisplayActivity>()?.connectivityService?.isNetworkAndServerAvailable { result ->
if (result) {
typedActivity<ComponentsGetter>()?.fileOperationsHelper?.renameFile(mTargetFile, newFileName)
} else {
fileDataStorageManager.addRenameFileOfflineOperation(mTargetFile, newFileName)
typedActivity<FileDisplayActivity>()?.refreshCurrentDirectory()
}
}
}
}
}

View file

@ -653,13 +653,15 @@ class PreviewMediaActivity :
packageName: String? = null,
activityName: String? = null
) {
if (FileDownloadHelper.instance().isDownloading(user, file)) {
val fileDownloadHelper = FileDownloadHelper.instance()
if (fileDownloadHelper.isDownloading(user, file)) {
return
}
user?.let { user ->
file?.let { file ->
FileDownloadHelper.instance().downloadFile(
fileDownloadHelper.downloadFile(
user,
file,
downloadBehavior ?: "",

View file

@ -50,13 +50,13 @@ public class CalendarSource {
String protocol = mUrl.getProtocol();
String userPass = mUsername + ":" + mPassword;
if (protocol.equalsIgnoreCase("ftp") || protocol.equalsIgnoreCase("ftps")) {
if ("ftp".equalsIgnoreCase(protocol) || "ftps".equalsIgnoreCase(protocol)) {
String external = mUrl.toExternalForm();
String end = external.substring(protocol.length() + HTTP_SEP.length());
return new URL(protocol + HTTP_SEP + userPass + "@" + end).openConnection();
}
if (protocol.equalsIgnoreCase("http") || protocol.equalsIgnoreCase("https")) {
if ("http".equalsIgnoreCase(protocol) || "https".equalsIgnoreCase(protocol)) {
String encoded = new String(new Base64().encode(userPass.getBytes("UTF-8")));
URLConnection connection = mUrl.openConnection();
connection.setRequestProperty("Authorization", "Basic " + encoded);

View file

@ -567,7 +567,7 @@ public class ProcessVEvent {
String expected = parts.length > 1 ? parts[1] : "";
String got = c.getAsString(key);
if (expected.equals("<non-null>") && got != null) {
if ("<non-null>".equals(expected) && got != null) {
got = "<non-null>"; // Sentinel for testing present and non-null
}
if (got == null) {

View file

@ -478,7 +478,7 @@ public class SaveCalendar {
return true;
}
final String utz = tz.toUpperCase(Locale.US);
return utz.equals("UTC") || utz.equals("UTC-0") || utz.equals("UTC+0") || utz.endsWith("/UTC");
return "UTC".equals(utz) || "UTC-0".equals(utz) || "UTC+0".equals(utz) || utz.endsWith("/UTC");
}
private Date getDateTime(Cursor cur, String dbName, String dbTzName, Calendar cal) {

View file

@ -700,7 +700,9 @@
<string name="prefs_synced_folders_local_path_title">مجلد محلي</string>
<string name="prefs_synced_folders_remote_path_title">مجلد عن بعد</string>
<string name="prefs_theme_title">السمة</string>
<string name="prefs_two_way_sync_interval">الفسحة الزمنية</string>
<string name="prefs_two_way_sync_summary">إدارة المجلدات الداخلية للمزامنة في الاتجاهين</string>
<string name="prefs_two_way_sync_switch_title">تمكين المزامنة في الاتجاهين</string>
<string name="prefs_value_theme_dark">غامق</string>
<string name="prefs_value_theme_light">فاتح</string>
<string name="prefs_value_theme_system">اتبع النظام</string>
@ -1097,6 +1099,22 @@
<string name="write_email">إرسال بريد</string>
<string name="wrong_storage_path">مجلد التخزين غير موجود !</string>
<string name="wrong_storage_path_desc">قد يكون هذا بسبب استعادة نسخة احتياطية على جهاز آخر . العودة الى الافتراضي . يرجى التحقق من الإعدادات لضبط مجلد تخزين البيانات.</string>
<plurals name="hours">
<item quantity="zero">%d ساعة</item>
<item quantity="one">%d ساعة</item>
<item quantity="two">%d ساعة</item>
<item quantity="few">%d ساعات</item>
<item quantity="many">%d ساعة</item>
<item quantity="other">%d ساعة</item>
</plurals>
<plurals name="minutes">
<item quantity="zero">%d دقيقة</item>
<item quantity="one">%d دقيقة</item>
<item quantity="two">%d دقيقة</item>
<item quantity="few">%d دقائق</item>
<item quantity="many">%d دقيقة</item>
<item quantity="other">%d دقيقة</item>
</plurals>
<plurals name="sync_fail_in_favourites_content">
<item quantity="zero">تعذرت مزامنة الملفات %1$d (التعارضات:%2$d)</item>
<item quantity="one">تعذرت مزامنة الملفات %1$d (التعارضات:%2$d)</item>

View file

@ -696,7 +696,9 @@
<string name="prefs_synced_folders_local_path_title">Local folder</string>
<string name="prefs_synced_folders_remote_path_title">Remote folder</string>
<string name="prefs_theme_title">Theme</string>
<string name="prefs_two_way_sync_interval">Interval</string>
<string name="prefs_two_way_sync_summary">Manage internal folders for two way sync</string>
<string name="prefs_two_way_sync_switch_title">Enable two way sync</string>
<string name="prefs_value_theme_dark">Dark</string>
<string name="prefs_value_theme_light">Light</string>
<string name="prefs_value_theme_system">Follow system</string>
@ -1070,6 +1072,14 @@
<string name="write_email">Send email</string>
<string name="wrong_storage_path">Data storage folder does not exist!</string>
<string name="wrong_storage_path_desc">This might be due to a backup restore on another device. Falling back to default. Please check settings to adjust data storage folder.</string>
<plurals name="hours">
<item quantity="one">%d hour</item>
<item quantity="other">%d hours</item>
</plurals>
<plurals name="minutes">
<item quantity="one">%d minute</item>
<item quantity="other">%d minutes</item>
</plurals>
<plurals name="sync_fail_in_favourites_content">
<item quantity="one">Could not sync %1$d file (conflicts: %2$d)</item>
<item quantity="other">Could not sync %1$d files (conflicts: %2$d)</item>

View file

@ -696,7 +696,9 @@
<string name="prefs_synced_folders_local_path_title">Fillteán áitiúil</string>
<string name="prefs_synced_folders_remote_path_title">Fillteán cianda</string>
<string name="prefs_theme_title">Téama</string>
<string name="prefs_two_way_sync_interval">Eatramh</string>
<string name="prefs_two_way_sync_summary">Bainistigh fillteáin inmheánacha le haghaidh sioncronaithe dhá bhealach</string>
<string name="prefs_two_way_sync_switch_title">Cumasaigh sioncronú dhá bhealach</string>
<string name="prefs_value_theme_dark">Dorcha</string>
<string name="prefs_value_theme_light">Solas</string>
<string name="prefs_value_theme_system">Lean an córas</string>
@ -1070,6 +1072,20 @@
<string name="write_email">Seol ríomhphost</string>
<string name="wrong_storage_path">Níl fillteán stórála sonraí ann!</string>
<string name="wrong_storage_path_desc">D\'fhéadfadh sé seo a bheith mar gheall ar aischur cúltaca ar ghléas eile. Ag titim ar ais go réamhshocraithe. Seiceáil le do thoil socruithe chun fillteán stórála sonraí a choigeartú.</string>
<plurals name="hours">
<item quantity="one">%d uair</item>
<item quantity="two">%d uair an chloig</item>
<item quantity="few">%d uair an chloig</item>
<item quantity="many">%d uair an chloig</item>
<item quantity="other">%d uair an chloig</item>
</plurals>
<plurals name="minutes">
<item quantity="one">%d nóiméad</item>
<item quantity="two">%d nóiméad</item>
<item quantity="few">%d nóiméad</item>
<item quantity="many">%d nóiméad</item>
<item quantity="other">%d nóiméad</item>
</plurals>
<plurals name="sync_fail_in_favourites_content">
<item quantity="one">Níorbh fhéidir comhad %1$d a shioncronú (coimhlint: %2$d)</item>
<item quantity="two">Níorbh fhéidir %1$d comhad a shioncronú (coimhlint: %2$d)</item>

View file

@ -697,7 +697,9 @@
<string name="prefs_synced_folders_local_path_title">Cartafol local</string>
<string name="prefs_synced_folders_remote_path_title">Cartafol remoto</string>
<string name="prefs_theme_title">Tema</string>
<string name="prefs_two_way_sync_interval">Intervalo</string>
<string name="prefs_two_way_sync_summary">Xestionar os cartafoles internos para a sincronización bidireccional</string>
<string name="prefs_two_way_sync_switch_title">Activar a sincronización bidireccional</string>
<string name="prefs_value_theme_dark">Escuro</string>
<string name="prefs_value_theme_light">Claro</string>
<string name="prefs_value_theme_system">Seguir o sistema</string>
@ -1071,6 +1073,14 @@
<string name="write_email">Enviar o correo</string>
<string name="wrong_storage_path">O cartafol de almacenamento de datos non existe.</string>
<string name="wrong_storage_path_desc">Isto pode deberse a unha restauración de copia de seguranza noutro dispositivo. Volvendo ao predeterminado. Comprobe as opcións para axustar o cartafol de almacenamento de datos.</string>
<plurals name="hours">
<item quantity="one">%d hora</item>
<item quantity="other">%d horas</item>
</plurals>
<plurals name="minutes">
<item quantity="one">%d minuto</item>
<item quantity="other">%d minutos</item>
</plurals>
<plurals name="sync_fail_in_favourites_content">
<item quantity="one">Non foi posíbel sincronizar %1$d ficheiro (conflitos: %2$d)</item>
<item quantity="other">Non foi posíbel sincronizar %1$d ficheiros (conflitos: %2$d)</item>

View file

@ -696,7 +696,9 @@
<string name="prefs_synced_folders_local_path_title">Yerel klasör</string>
<string name="prefs_synced_folders_remote_path_title">Uzak klasör</string>
<string name="prefs_theme_title">Tema</string>
<string name="prefs_two_way_sync_interval">Aralık</string>
<string name="prefs_two_way_sync_summary">İç klasörleri iki yönlü eşitleme ile yönetin</string>
<string name="prefs_two_way_sync_switch_title">İki yönlü eşitlemeyi aç</string>
<string name="prefs_value_theme_dark">Koyu</string>
<string name="prefs_value_theme_light">ık</string>
<string name="prefs_value_theme_system">Sistem ayarı</string>
@ -1070,6 +1072,14 @@
<string name="write_email">E-posta gönder</string>
<string name="wrong_storage_path">Veri depolama klasörü bulunamadı!</string>
<string name="wrong_storage_path_desc">Bunun nedeni bir yedeğin başka bir aygıta geri yüklenmesi olabilir. Varsayılan değerlere geri dönülüyor. Lütfen veri depolama klasörünü ayarlamak için ayarları denetleyin.</string>
<plurals name="hours">
<item quantity="one">%d saat</item>
<item quantity="other">%d saat</item>
</plurals>
<plurals name="minutes">
<item quantity="one">%d dakika</item>
<item quantity="other">%d dakika</item>
</plurals>
<plurals name="sync_fail_in_favourites_content">
<item quantity="one">%1$d dosya eşitlenemedi (çakışma: %2$d)</item>
<item quantity="other">%1$d dosya eşitlenemedi (çakışma: %2$d)</item>

View file

@ -696,7 +696,9 @@
<string name="prefs_synced_folders_local_path_title">近端資料夾</string>
<string name="prefs_synced_folders_remote_path_title">遠端資料夾</string>
<string name="prefs_theme_title">佈景主題</string>
<string name="prefs_two_way_sync_interval">間距</string>
<string name="prefs_two_way_sync_summary">管理內部資料夾以進行雙向同步</string>
<string name="prefs_two_way_sync_switch_title">啟用雙向同步</string>
<string name="prefs_value_theme_dark">深色</string>
<string name="prefs_value_theme_light">淺色</string>
<string name="prefs_value_theme_system">跟隨系統</string>
@ -1070,6 +1072,12 @@
<string name="write_email">傳送電子郵件</string>
<string name="wrong_storage_path">資料儲存資料夾不存在!</string>
<string name="wrong_storage_path_desc">這可能是由於另一部設備上的備份還原所致。退回默認值。請檢查設置以調整數據存儲資料夾。</string>
<plurals name="hours">
<item quantity="other">%d 小時</item>
</plurals>
<plurals name="minutes">
<item quantity="other">%d 分鐘</item>
</plurals>
<plurals name="sync_fail_in_favourites_content">
<item quantity="other">無法同步 %1$d 檔案(抵觸: %2$d</item>
</plurals>

View file

@ -1176,7 +1176,7 @@
<string name="icon_of_widget_entry">Icon of widget entry</string>
<string name="choose_widget">Choose widget</string>
<string name="reload">Reload</string>
<string name="widgets_not_available">Widgets are only available on %1$s 25 or later</string>
<string name="widgets_not_available">Widgets are only available in %1$s 25 or later when the Dashboard app is enabled</string>
<string name="widgets_not_available_title">Not available</string>
<string name="icon_for_empty_list">Icon for empty list</string>
<string name="no_items">No items</string>

View file

@ -252,7 +252,7 @@
<!-- Launch screen -->
<style name="Theme.ownCloud.Launcher">
<item name="android:statusBarColor">@color/primary</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@color/primary</item>
<item name="android:windowBackground">@color/primary</item>
<item name="android:textColorHint">@color/secondary_text_color</item>

View file

@ -2,13 +2,15 @@
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2019 Chris Narkiewicz <hello@ezaquarii.com>
* SPDX-FileCopyrightText: 2024 TSI-mc <surinder.kumar@t-systems.com>
* SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
*/
package com.nextcloud.client.onboarding
import android.accounts.Account
import android.content.res.Resources
import com.nextcloud.client.account.AnonymousUser
import com.nextcloud.client.account.CurrentAccountProvider
import com.nextcloud.client.account.User
import com.nextcloud.client.preferences.AppPreferences
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@ -30,7 +32,7 @@ class OnboardingServiceTest {
private lateinit var currentAccountProvider: CurrentAccountProvider
@Mock
private lateinit var account: Account
private lateinit var user: User
private lateinit var onboardingService: OnboardingServiceImpl
@ -43,13 +45,16 @@ class OnboardingServiceTest {
@Test
fun `first run flag toggles with current current account`() {
// GIVEN
// current account is not set
// current account is anonymous
whenever(currentAccountProvider.user).thenReturn(AnonymousUser("dummy"))
// THEN
// first run flag is true
assertTrue(onboardingService.isFirstRun)
// WHEN
// current account is set
whenever(currentAccountProvider.currentAccount).thenReturn(account)
whenever(currentAccountProvider.user).thenReturn(user)
// THEN
// first run flag toggles

View file

@ -10,8 +10,8 @@
*/
buildscript {
ext {
androidLibraryVersion ="3ec02eb2aeb59185815a3a9c41c9dd98679f6fbe"
androidPluginVersion = '8.7.1'
androidLibraryVersion ="ceb3812b688f2288911acf4d917596283fab0ea5"
androidPluginVersion = '8.7.2'
androidxMediaVersion = '1.4.1'
androidxTestVersion = "1.6.1"
appCompatVersion = '1.7.0'

View file

@ -259,11 +259,13 @@
<trusting group="androidx.annotation"/>
<trusting group="androidx.appcompat"/>
<trusting group="androidx.collection"/>
<trusting group="androidx.constraintlayout"/>
<trusting group="androidx.core"/>
<trusting group="androidx.databinding"/>
<trusting group="androidx.fragment"/>
<trusting group="androidx.graphics"/>
<trusting group="androidx.lifecycle"/>
<trusting group="androidx.profileinstaller"/>
<trusting group="androidx.transition" name="transition" version="1.5.0"/>
<trusting group="androidx.webkit" name="webkit" version="1.11.0"/>
<trusting group="androidx.webkit" name="webkit" version="1.12.0"/>
@ -873,6 +875,11 @@
<sha256 value="61f162bcc78a43f88415bec61aa80060ab14a968aad73696a4373ddb17211ae2" origin="Generated by Gradle" reason="Artifact is not signed"/>
</artifact>
</component>
<component group="androidx.compose" name="compose-bom" version="2024.10.01">
<artifact name="compose-bom-2024.10.01.pom">
<sha256 value="e4b122da072402de71c8a20b85289d68e4d4446d7b1bee6dee4fef9d4677130b" origin="Generated by Gradle" reason="Artifact is not signed"/>
</artifact>
</component>
<component group="androidx.compose.animation" name="animation-core" version="1.0.0-beta03">
<artifact name="animation-core-1.0.0-beta03.aar">
<sha256 value="4626086855eb6582dda9c3050c05bb56632f5a0308a3bb71a0f58def958602ca" origin="Generated by Gradle" reason="Artifact is not signed"/>

View file

@ -18,6 +18,8 @@ ruby scripts/analysis/lint-up.rb
lintValue=$?
curl "https://www.kaminsky.me/nc-dev/$repository-findbugs/$stableBranch.xml" -o "/tmp/$stableBranch.xml"
[[ ! -e test1 ]] && exit 1
ruby scripts/analysis/spotbugs-up.rb "$stableBranch"
spotbugsValue=$?
@ -132,10 +134,6 @@ else
exit 1
fi
if [ $checkLibrary -eq 1 ]; then
exit 1
fi
if [ ! $lintValue -eq 2 ]; then
exit $lintValue
fi