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

This commit is contained in:
Tobias Kaminsky 2024-05-11 03:38:43 +02:00
commit 7148238c7c
18 changed files with 279 additions and 12 deletions

View file

@ -34,7 +34,7 @@ jobs:
persist-credentials: false persist-credentials: false
- name: "Run analysis" - name: "Run analysis"
uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3
with: with:
results_file: results.sarif results_file: results.sarif
results_format: sarif results_format: sarif

View file

@ -0,0 +1,86 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.nextcloud.utils
import android.os.Bundle
import com.owncloud.android.AbstractIT
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory
import com.owncloud.android.utils.appConfig.AppConfigKeys
import com.owncloud.android.utils.appConfig.AppConfigManager
import org.junit.AfterClass
import org.junit.Test
class AppConfigManagerTests : AbstractIT() {
private val testBaseUrl = "nextcloud.cloud.cloud"
private val testProxyHost = "nextcloud.cloud.cloud.com"
@Suppress("MagicNumber")
private val testProxyPort = 8800
@Test
fun testSetProxyConfigWhenGivenClientBrandedPlusAndCorrectBundleDataProxyConfigurationShouldSet() {
val proxySetting = Bundle().apply {
putString(AppConfigKeys.ProxyHost.key, testProxyHost)
putInt(AppConfigKeys.ProxyPort.key, testProxyPort)
}
AppConfigManager(targetContext, proxySetting).run {
setProxyConfig(true)
}
val proxyHost = OwnCloudClientManagerFactory.getProxyHost()
val proxyPort = OwnCloudClientManagerFactory.getProxyPort()
assert(proxyHost.equals(testProxyHost))
assert(proxyPort == testProxyPort)
}
@Test
fun testSetProxyConfigWhenGivenClientNotBrandedPlusAndCorrectBundleDataProxyConfigurationShouldNotSet() {
val proxySetting = Bundle().apply {
putString(AppConfigKeys.ProxyHost.key, testProxyHost)
putInt(AppConfigKeys.ProxyPort.key, testProxyPort)
}
AppConfigManager(targetContext, proxySetting).run {
setProxyConfig(false)
}
val proxyHost = OwnCloudClientManagerFactory.getProxyHost()
val proxyPort = OwnCloudClientManagerFactory.getProxyPort()
assert(proxyHost.equals(""))
assert(proxyPort == -1)
}
@Test
fun testGetBaseUrlConfigWhenGivenClientBrandedPlusAndCorrectBundleDataBaseUrlConfigurationShouldSet() {
val baseUrlConfig = Bundle().apply {
putString(AppConfigKeys.BaseUrl.key, testBaseUrl)
}
val sut = AppConfigManager(targetContext, baseUrlConfig)
assert(!sut.getBaseUrl(true).isNullOrEmpty())
}
@Test
fun testGetBaseUrlConfigWhenGivenClientBrandedPlusAndBrokenBundleDataBaseUrlConfigurationShouldNotSet() {
val baseUrlConfig = Bundle()
val sut = AppConfigManager(targetContext, baseUrlConfig)
assert(sut.getBaseUrl(true).isNullOrEmpty())
}
companion object {
@JvmStatic
@AfterClass
fun tearDown() {
OwnCloudClientManagerFactory.setProxyHost("")
OwnCloudClientManagerFactory.setProxyPort(-1)
}
}
}

View file

@ -126,6 +126,10 @@
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
tools:ignore="UnusedAttribute" tools:ignore="UnusedAttribute"
tools:replace="android:allowBackup"> tools:replace="android:allowBackup">
<meta-data android:name="android.content.APP_RESTRICTIONS"
android:resource="@xml/app_config" />
<activity <activity
android:name="com.nextcloud.ui.composeActivity.ComposeActivity" android:name="com.nextcloud.ui.composeActivity.ComposeActivity"
android:exported="false" /> android:exported="false" />

View file

@ -21,9 +21,12 @@ import android.app.ActivityManager;
import android.app.Application; import android.app.Application;
import android.app.NotificationChannel; import android.app.NotificationChannel;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter;
import android.content.RestrictionsManager;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@ -55,6 +58,7 @@ import com.nextcloud.client.onboarding.OnboardingService;
import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.client.preferences.AppPreferences;
import com.nextcloud.client.preferences.AppPreferencesImpl; import com.nextcloud.client.preferences.AppPreferencesImpl;
import com.nextcloud.client.preferences.DarkMode; import com.nextcloud.client.preferences.DarkMode;
import com.nextcloud.utils.extensions.ContextExtensionsKt;
import com.nmc.android.ui.LauncherActivity; import com.nmc.android.ui.LauncherActivity;
import com.owncloud.android.authentication.AuthenticatorActivity; import com.owncloud.android.authentication.AuthenticatorActivity;
import com.owncloud.android.authentication.PassCodeManager; import com.owncloud.android.authentication.PassCodeManager;
@ -63,6 +67,7 @@ import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.datamodel.MediaFolder; import com.owncloud.android.datamodel.MediaFolder;
import com.owncloud.android.datamodel.MediaFolderType; import com.owncloud.android.datamodel.MediaFolderType;
import com.owncloud.android.datamodel.MediaProvider; import com.owncloud.android.datamodel.MediaProvider;
import com.owncloud.android.datamodel.ReceiverFlag;
import com.owncloud.android.datamodel.SyncedFolder; import com.owncloud.android.datamodel.SyncedFolder;
import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.datamodel.SyncedFolderProvider;
import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.datamodel.ThumbnailsCacheManager;
@ -75,6 +80,7 @@ import com.owncloud.android.lib.resources.status.NextcloudVersion;
import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import com.owncloud.android.ui.activity.SyncedFoldersActivity; import com.owncloud.android.ui.activity.SyncedFoldersActivity;
import com.owncloud.android.ui.notifications.NotificationUtils; import com.owncloud.android.ui.notifications.NotificationUtils;
import com.owncloud.android.utils.appConfig.AppConfigManager;
import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.FilesSyncHelper; import com.owncloud.android.utils.FilesSyncHelper;
import com.owncloud.android.utils.PermissionUtil; import com.owncloud.android.utils.PermissionUtil;
@ -191,6 +197,8 @@ public class MainApp extends MultiDexApplication implements HasAndroidInjector {
@SuppressWarnings("unused") @SuppressWarnings("unused")
private boolean mBound; private boolean mBound;
private AppConfigManager appConfigManager;
private static AppComponent appComponent; private static AppComponent appComponent;
/** /**
@ -281,6 +289,7 @@ public class MainApp extends MultiDexApplication implements HasAndroidInjector {
return appComponent; return appComponent;
} }
@SuppressFBWarnings("ST") @SuppressFBWarnings("ST")
@Override @Override
public void onCreate() { public void onCreate() {
@ -314,11 +323,15 @@ public class MainApp extends MultiDexApplication implements HasAndroidInjector {
OwnCloudClientManagerFactory.setUserAgent(getUserAgent()); OwnCloudClientManagerFactory.setUserAgent(getUserAgent());
try { if (isClientBrandedPlus()) {
OwnCloudClientManagerFactory.setProxyHost(getResources().getString(R.string.proxy_host)); RestrictionsManager restrictionsManager = (RestrictionsManager) getSystemService(Context.RESTRICTIONS_SERVICE);
OwnCloudClientManagerFactory.setProxyPort(getResources().getInteger(R.integer.proxy_port)); appConfigManager = new AppConfigManager(this, restrictionsManager.getApplicationRestrictions());
} catch (Resources.NotFoundException e) { appConfigManager.setProxyConfig(isClientBrandedPlus());
// no proxy set
// Listen app config changes
ContextExtensionsKt.registerBroadcastReceiver(this, restrictionsReceiver, restrictionsFilter, ReceiverFlag.NotExported);
} else {
setProxyForNonBrandedPlusClients();
} }
// initialise thumbnails cache on background thread // initialise thumbnails cache on background thread
@ -364,9 +377,35 @@ public class MainApp extends MultiDexApplication implements HasAndroidInjector {
} else if (event == Lifecycle.Event.ON_STOP) { } else if (event == Lifecycle.Event.ON_STOP) {
passCodeManager.setCanAskPin(true); passCodeManager.setCanAskPin(true);
Log_OC.d(TAG, "APP IN BACKGROUND"); Log_OC.d(TAG, "APP IN BACKGROUND");
} else if (event == Lifecycle.Event.ON_RESUME) {
if (appConfigManager == null) return;
appConfigManager.setProxyConfig(isClientBrandedPlus());
Log_OC.d(TAG, "APP ON RESUME");
} }
}); });
private void setProxyForNonBrandedPlusClients() {
try {
OwnCloudClientManagerFactory.setProxyHost(getResources().getString(R.string.proxy_host));
OwnCloudClientManagerFactory.setProxyPort(getResources().getInteger(R.integer.proxy_port));
} catch (Resources.NotFoundException e) {
Log_OC.d(TAG, "Error caught at setProxyForNonBrandedPlusClients: " + e);
}
}
public static boolean isClientBrandedPlus() {
return (getAppContext().getResources().getBoolean(R.bool.is_branded_plus_client));
}
private final IntentFilter restrictionsFilter = new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
private final BroadcastReceiver restrictionsReceiver = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
if (appConfigManager == null) return;
appConfigManager.setProxyConfig(isClientBrandedPlus());
}
};
private void registerGlobalPassCodeProtection() { private void registerGlobalPassCodeProtection() {
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {

View file

@ -21,6 +21,7 @@ import android.app.Activity;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.RestrictionsManager;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@ -83,6 +84,7 @@ import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
import com.owncloud.android.lib.common.OwnCloudCredentials; import com.owncloud.android.lib.common.OwnCloudCredentials;
import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; import com.owncloud.android.lib.common.OwnCloudCredentialsFactory;
import com.owncloud.android.lib.common.UserInfo; import com.owncloud.android.lib.common.UserInfo;
import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException; import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException;
import com.owncloud.android.lib.common.accounts.AccountUtils.Constants; import com.owncloud.android.lib.common.accounts.AccountUtils.Constants;
import com.owncloud.android.lib.common.network.CertificateCombinedException; import com.owncloud.android.lib.common.network.CertificateCombinedException;
@ -111,6 +113,7 @@ import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.ErrorMessageAdapter; import com.owncloud.android.utils.ErrorMessageAdapter;
import com.owncloud.android.utils.PermissionUtil; import com.owncloud.android.utils.PermissionUtil;
import com.owncloud.android.utils.WebViewUtil; import com.owncloud.android.utils.WebViewUtil;
import com.owncloud.android.utils.appConfig.AppConfigManager;
import com.owncloud.android.utils.theme.CapabilityUtils; import com.owncloud.android.utils.theme.CapabilityUtils;
import com.owncloud.android.utils.theme.ViewThemeUtils; import com.owncloud.android.utils.theme.ViewThemeUtils;
@ -318,9 +321,18 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
mIsFirstAuthAttempt = savedInstanceState.getBoolean(KEY_AUTH_IS_FIRST_ATTEMPT_TAG); mIsFirstAuthAttempt = savedInstanceState.getBoolean(KEY_AUTH_IS_FIRST_ATTEMPT_TAG);
} }
String webloginUrl = null;
boolean webViewLoginMethod; boolean webViewLoginMethod;
if (getIntent().getBooleanExtra(EXTRA_USE_PROVIDER_AS_WEBLOGIN, false)) { String webloginUrl = null;
if (MainApp.isClientBrandedPlus()) {
RestrictionsManager restrictionsManager = (RestrictionsManager) getSystemService(Context.RESTRICTIONS_SERVICE);
AppConfigManager appConfigManager = new AppConfigManager(this, restrictionsManager.getApplicationRestrictions());
webloginUrl = appConfigManager.getBaseUrl(MainApp.isClientBrandedPlus());
}
if (webloginUrl != null) {
webViewLoginMethod = true;
} else if (getIntent().getBooleanExtra(EXTRA_USE_PROVIDER_AS_WEBLOGIN, false)) {
webViewLoginMethod = true; webViewLoginMethod = true;
webloginUrl = getString(R.string.provider_registration_server); webloginUrl = getString(R.string.provider_registration_server);
} else { } else {
@ -1375,7 +1387,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
// can be anything: email, name, name with whitespaces // can be anything: email, name, name with whitespaces
String loginName = webViewUser; String loginName = webViewUser;
String accountName = com.owncloud.android.lib.common.accounts.AccountUtils.buildAccountName(uri, loginName); String accountName = AccountUtils.buildAccountName(uri, loginName);
Account newAccount = new Account(accountName, accountType); Account newAccount = new Account(accountName, accountType);
if (accountManager.exists(newAccount)) { if (accountManager.exists(newAccount)) {
// fail - not a new account, but an existing one; disallow // fail - not a new account, but an existing one; disallow
@ -1480,7 +1492,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
public void onRequestPermissionsResult(int requestCode, public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions, @NonNull String[] permissions,
@NonNull int[] grantResults) { @NonNull int[] grantResults) {
if (requestCode == PermissionUtil.PERMISSIONS_CAMERA) {// If request is cancelled, result arrays are empty. if (requestCode == PERMISSIONS_CAMERA) {// If request is cancelled, result arrays are empty.
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted // permission was granted
startQRScanner(); startQRScanner();

View file

@ -46,6 +46,7 @@ import com.nextcloud.client.network.ClientFactory;
import com.nextcloud.common.NextcloudClient; import com.nextcloud.common.NextcloudClient;
import com.nextcloud.ui.fileactions.FileActionsBottomSheet; import com.nextcloud.ui.fileactions.FileActionsBottomSheet;
import com.nextcloud.utils.extensions.BundleExtensionsKt; import com.nextcloud.utils.extensions.BundleExtensionsKt;
import com.owncloud.android.MainApp;
import com.owncloud.android.R; import com.owncloud.android.R;
import com.owncloud.android.databinding.FragmentPreviewMediaBinding; import com.owncloud.android.databinding.FragmentPreviewMediaBinding;
import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.OCFile;

View file

@ -11,6 +11,7 @@ import android.content.res.Resources;
import android.view.Menu; import android.view.Menu;
import com.nextcloud.client.account.User; import com.nextcloud.client.account.User;
import com.owncloud.android.MainApp;
import com.owncloud.android.R; import com.owncloud.android.R;
import com.owncloud.android.lib.resources.status.OCCapability; import com.owncloud.android.lib.resources.status.OCCapability;

View file

@ -0,0 +1,17 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.owncloud.android.utils.appConfig
/**
* These keys are connected to app_config.xml
*/
enum class AppConfigKeys(val key: String) {
BaseUrl("base_url"),
ProxyHost("proxy_host"),
ProxyPort("proxy_port")
}

View file

@ -0,0 +1,68 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.owncloud.android.utils.appConfig
import android.content.Context
import android.content.res.Resources
import android.os.Bundle
import android.text.TextUtils
import com.owncloud.android.R
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory
import com.owncloud.android.lib.common.utils.Log_OC
class AppConfigManager(private val context: Context, private val appRestrictions: Bundle) {
private val tag = "AppConfigManager"
fun setProxyConfig(isBrandedPlus: Boolean) {
if (!isBrandedPlus) {
Log_OC.d(tag, "Proxy configuration cannot be set. Client is not branded plus.")
return
}
val host = if (appRestrictions.containsKey(AppConfigKeys.ProxyHost.key)) {
appRestrictions.getString(AppConfigKeys.ProxyHost.key)
} else {
context.getString(R.string.proxy_host)
}
val port = if (appRestrictions.containsKey(AppConfigKeys.ProxyPort.key)) {
appRestrictions.getInt(AppConfigKeys.ProxyPort.key)
} else {
context.resources.getInteger(R.integer.proxy_port)
}
if (TextUtils.isEmpty(host) || port == -1) {
Log_OC.d(tag, "Proxy configuration cannot be found")
return
}
try {
OwnCloudClientManagerFactory.setProxyHost(host)
OwnCloudClientManagerFactory.setProxyPort(port)
Log_OC.d(tag, "Proxy configuration successfully set")
} catch (e: Resources.NotFoundException) {
Log_OC.e(tag, "Proxy config cannot able to set due to: $e")
}
}
fun getBaseUrl(isBrandedPlus: Boolean): String? {
if (!isBrandedPlus) {
Log_OC.d(tag, "Proxy configuration cannot be set. Client is not branded plus. Default url applied")
return null
}
return if (appRestrictions.containsKey(AppConfigKeys.BaseUrl.key)) {
appRestrictions.getString(AppConfigKeys.BaseUrl.key)
} else {
Log_OC.d(tag, "BaseUrl configuration cannot be found, default url applied")
null
}
}
}

View file

@ -665,6 +665,7 @@
<string name="preview_image_description">معاينة الصورة</string> <string name="preview_image_description">معاينة الصورة</string>
<string name="preview_image_error_no_local_file">لا يوجد ملف محلي للمعاينة</string> <string name="preview_image_error_no_local_file">لا يوجد ملف محلي للمعاينة</string>
<string name="preview_image_error_unknown_format">تعذرت عملية عرض الصورة</string> <string name="preview_image_error_unknown_format">تعذرت عملية عرض الصورة</string>
<string name="preview_media_unhandled_http_code_message">الملف مقفلٌ حالياً من قِبَل مستخدِمٍ أو عمليةٍ أخرى؛ وبالتالي لا يمكن حذفه. الرجاء معاودة المحاولة في وقتٍ لاحقٍ.</string>
<string name="preview_sorry">آسف</string> <string name="preview_sorry">آسف</string>
<string name="privacy">الخصوصية</string> <string name="privacy">الخصوصية</string>
<string name="public_share_name">اسم جديد</string> <string name="public_share_name">اسم جديد</string>

View file

@ -661,6 +661,7 @@
<string name="preview_image_description">Image preview</string> <string name="preview_image_description">Image preview</string>
<string name="preview_image_error_no_local_file">There is no local file to preview</string> <string name="preview_image_error_no_local_file">There is no local file to preview</string>
<string name="preview_image_error_unknown_format">Unable to show image</string> <string name="preview_image_error_unknown_format">Unable to show image</string>
<string name="preview_media_unhandled_http_code_message">File is currently locked by another user or process and therefore not deletable. Please try again later.</string>
<string name="preview_sorry">Sorry</string> <string name="preview_sorry">Sorry</string>
<string name="privacy">Privacy</string> <string name="privacy">Privacy</string>
<string name="public_share_name">New name</string> <string name="public_share_name">New name</string>

View file

@ -661,6 +661,7 @@
<string name="preview_image_description">Bildvorschau</string> <string name="preview_image_description">Bildvorschau</string>
<string name="preview_image_error_no_local_file">Keine lokale Datei für die Vorschau vorhanden</string> <string name="preview_image_error_no_local_file">Keine lokale Datei für die Vorschau vorhanden</string>
<string name="preview_image_error_unknown_format">Bild kann nicht angezeigt werden</string> <string name="preview_image_error_unknown_format">Bild kann nicht angezeigt werden</string>
<string name="preview_media_unhandled_http_code_message">Die Datei ist derzeit von einem anderen Benutzer oder Prozess gesperrt und kann daher nicht gelöscht werden. Bitte später noch einmal versuchen.</string>
<string name="preview_sorry">Entschuldigung</string> <string name="preview_sorry">Entschuldigung</string>
<string name="privacy">Datenschutz</string> <string name="privacy">Datenschutz</string>
<string name="public_share_name">Neuer Name</string> <string name="public_share_name">Neuer Name</string>

View file

@ -661,6 +661,7 @@
<string name="preview_image_description">Преглед слике</string> <string name="preview_image_description">Преглед слике</string>
<string name="preview_image_error_no_local_file">Нема локалног фајла за преглед</string> <string name="preview_image_error_no_local_file">Нема локалног фајла за преглед</string>
<string name="preview_image_error_unknown_format">Не могу да прикажем слику</string> <string name="preview_image_error_unknown_format">Не могу да прикажем слику</string>
<string name="preview_media_unhandled_http_code_message">Фајл је тренутно закључао други корисник или процес, па не може да се обрише. Молимо вас покушајте касније.</string>
<string name="preview_sorry">Извините</string> <string name="preview_sorry">Извините</string>
<string name="privacy">Приватност</string> <string name="privacy">Приватност</string>
<string name="public_share_name">Ново име</string> <string name="public_share_name">Ново име</string>

View file

@ -655,12 +655,13 @@
<string name="prefs_synced_folders_local_path_title">近端資料夾</string> <string name="prefs_synced_folders_local_path_title">近端資料夾</string>
<string name="prefs_synced_folders_remote_path_title">遠端資料夾</string> <string name="prefs_synced_folders_remote_path_title">遠端資料夾</string>
<string name="prefs_theme_title">佈景主題</string> <string name="prefs_theme_title">佈景主題</string>
<string name="prefs_value_theme_dark"></string> <string name="prefs_value_theme_dark"></string>
<string name="prefs_value_theme_light"></string> <string name="prefs_value_theme_light"></string>
<string name="prefs_value_theme_system">跟隨系統</string> <string name="prefs_value_theme_system">跟隨系統</string>
<string name="preview_image_description">圖像預覽</string> <string name="preview_image_description">圖像預覽</string>
<string name="preview_image_error_no_local_file">沒有可供預覽的近端檔案</string> <string name="preview_image_error_no_local_file">沒有可供預覽的近端檔案</string>
<string name="preview_image_error_unknown_format">無法顯示圖像</string> <string name="preview_image_error_unknown_format">無法顯示圖像</string>
<string name="preview_media_unhandled_http_code_message">檔案目前已被其他用戶或進程鎖定,因此無法刪除。 請稍後再試。</string>
<string name="preview_sorry">很抱歉</string> <string name="preview_sorry">很抱歉</string>
<string name="privacy">私隱政策</string> <string name="privacy">私隱政策</string>
<string name="public_share_name">新名稱</string> <string name="public_share_name">新名稱</string>

View file

@ -661,6 +661,7 @@
<string name="preview_image_description">圖片預覽</string> <string name="preview_image_description">圖片預覽</string>
<string name="preview_image_error_no_local_file">沒有可供預覽的本機檔案</string> <string name="preview_image_error_no_local_file">沒有可供預覽的本機檔案</string>
<string name="preview_image_error_unknown_format">無法顯示圖片</string> <string name="preview_image_error_unknown_format">無法顯示圖片</string>
<string name="preview_media_unhandled_http_code_message">檔案目前已被其他使用者或處理程序鎖定,因此無法刪除。請稍後再試。</string>
<string name="preview_sorry">很抱歉</string> <string name="preview_sorry">很抱歉</string>
<string name="privacy">隱私權</string> <string name="privacy">隱私權</string>
<string name="public_share_name">新名稱</string> <string name="public_share_name">新名稱</string>

View file

@ -43,6 +43,7 @@
<bool name="show_external_links">true</bool> <bool name="show_external_links">true</bool>
<bool name="show_outdated_server_warning">true</bool> <bool name="show_outdated_server_warning">true</bool>
<bool name="is_branded_client">false</bool> <bool name="is_branded_client">false</bool>
<bool name="is_branded_plus_client">false</bool>
<!-- Calendar & Contacts backup --> <!-- Calendar & Contacts backup -->
<string name="contacts_backup_folder">/.Contacts-Backup</string> <string name="contacts_backup_folder">/.Contacts-Backup</string>

View file

@ -26,6 +26,10 @@
<string name="ecosystem_apps_display_assistant">Assistant</string> <string name="ecosystem_apps_display_assistant">Assistant</string>
<string name="app_config_base_url_title">Base Url</string>
<string name="app_config_proxy_host_title">Proxy Host Name</string>
<string name="app_config_proxy_port_title">Proxy Port</string>
<string name="assistant_screen_task_types_error_state_message">Unable to fetch task types, please check your internet connection.</string> <string name="assistant_screen_task_types_error_state_message">Unable to fetch task types, please check your internet connection.</string>
<string name="assistant_screen_task_list_error_state_message">Unable to fetch task list, please check your internet connection.</string> <string name="assistant_screen_task_list_error_state_message">Unable to fetch task list, please check your internet connection.</string>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Nextcloud - Android Client
~
~ SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
~ SPDX-License-Identifier: AGPL-3.0-or-later
-->
<restrictions xmlns:android="http://schemas.android.com/apk/res/android">
<restriction
android:key="proxy_host"
android:defaultValue=""
android:restrictionType="string"
android:title="@string/app_config_proxy_host_title" />
<restriction
android:key="proxy_port"
android:defaultValue="-1"
android:restrictionType="integer"
android:title="@string/app_config_proxy_port_title" />
<restriction
android:key="base_url"
android:defaultValue=""
android:restrictionType="string"
android:title="@string/app_config_base_url_title" />
</restrictions>