diff --git a/app/src/androidTest/java/com/nmc/android/ui/SettingsPreferenceIT.kt b/app/src/androidTest/java/com/nmc/android/ui/SettingsPreferenceIT.kt new file mode 100644 index 000000000000..dde7af362273 --- /dev/null +++ b/app/src/androidTest/java/com/nmc/android/ui/SettingsPreferenceIT.kt @@ -0,0 +1,212 @@ +package com.nmc.android.ui + +import android.preference.ListPreference +import android.preference.Preference +import androidx.test.espresso.Espresso.onData +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.PreferenceMatchers +import androidx.test.espresso.matcher.PreferenceMatchers.withKey +import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed +import androidx.test.ext.junit.rules.ActivityScenarioRule +import com.owncloud.android.AbstractIT +import com.owncloud.android.R +import com.owncloud.android.ui.AppVersionPreference +import com.owncloud.android.ui.PreferenceCustomCategory +import com.owncloud.android.ui.ThemeableSwitchPreference +import com.owncloud.android.ui.activity.SettingsActivity +import org.hamcrest.Matchers.allOf +import org.hamcrest.Matchers.instanceOf +import org.hamcrest.Matchers.`is` +import org.junit.Assert.assertEquals +import org.junit.Rule +import org.junit.Test + +class SettingsPreferenceIT : AbstractIT() { + + @get:Rule + val activityRule = ActivityScenarioRule(SettingsActivity::class.java) + + @Test + fun verifyPreferenceSectionCustomClass() { + activityRule.scenario.onActivity { + val preferenceAccountInfo = it.findPreference("account_info") + val preferenceGeneral = it.findPreference("general") + val preferenceDetails = it.findPreference("details") + val preferenceMore = it.findPreference("more") + val preferenceDataProtection = it.findPreference("data_protection") + val preferenceInfo = it.findPreference("info") + + val preferenceCategoryList = listOf( + preferenceAccountInfo, + preferenceGeneral, + preferenceDetails, + preferenceMore, + preferenceDataProtection, + preferenceInfo + ) + + for (preference in preferenceCategoryList) { + assertEquals(PreferenceCustomCategory::class.java, preference.javaClass) + } + } + } + + @Test + fun verifySwitchPreferenceCustomClass() { + activityRule.scenario.onActivity { + val preferenceShowHiddenFiles = it.findPreference("show_hidden_files") + assertEquals(ThemeableSwitchPreference::class.java, preferenceShowHiddenFiles.javaClass) + } + } + + @Test + fun verifyAppVersionPreferenceCustomClass() { + activityRule.scenario.onActivity { + val preferenceAboutApp = it.findPreference("about_app") + assertEquals(AppVersionPreference::class.java, preferenceAboutApp.javaClass) + } + } + + @Test + fun verifyPreferenceChildCustomLayout() { + activityRule.scenario.onActivity { + val userName = it.findPreference("user_name") + val storagePath = it.findPreference("storage_path") + val lock = it.findPreference("lock") + val showHiddenFiles = it.findPreference("show_hidden_files") + val syncedFolders = it.findPreference("syncedFolders") + val backup = it.findPreference("backup") + val mnemonic = it.findPreference("mnemonic") + val privacySettings = it.findPreference("privacy_settings") + val privacyPolicy = it.findPreference("privacy_policy") + val sourceCode = it.findPreference("sourcecode") + val help = it.findPreference("help") + val imprint = it.findPreference("imprint") + + val preferenceList = listOf( + userName, + storagePath, + lock, + showHiddenFiles, + syncedFolders, + backup, + mnemonic, + privacySettings, + privacyPolicy, + sourceCode, + help, + imprint + ) + + for (preference in preferenceList) { + assertEquals(R.layout.custom_preference_layout, preference.layoutResource) + } + + val aboutApp = it.findPreference("about_app") + assertEquals(R.layout.custom_app_preference_layout, aboutApp.layoutResource) + + } + } + + @Test + fun verifyPreferencesTitleText() { + onData(allOf(`is`(instanceOf(PreferenceCustomCategory::class.java)), withKey("account_info"), + PreferenceMatchers.withTitleText("Account Information"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(Preference::class.java)), withKey("user_name"), + PreferenceMatchers.withTitleText("test"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(PreferenceCustomCategory::class.java)), withKey("general"), + PreferenceMatchers.withTitleText("General"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(ListPreference::class.java)), withKey("storage_path"), + PreferenceMatchers.withTitleText("Data storage folder"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(PreferenceCustomCategory::class.java)), withKey("details"), + PreferenceMatchers.withTitleText("Details"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(ListPreference::class.java)), withKey("lock"), + PreferenceMatchers.withTitleText("App passcode"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(ThemeableSwitchPreference::class.java)), withKey("show_hidden_files"), + PreferenceMatchers.withTitleText("Show hidden files"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(PreferenceCustomCategory::class.java)), withKey("more"), + PreferenceMatchers.withTitleText("More"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(Preference::class.java)), withKey("syncedFolders"), + PreferenceMatchers.withTitleText("Auto upload"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(Preference::class.java)), withKey("backup"), + PreferenceMatchers.withTitleText("Back up contacts"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(Preference::class.java)), withKey("mnemonic"), + PreferenceMatchers.withTitleText("E2E mnemonic"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(Preference::class.java)), withKey("logger"), + PreferenceMatchers.withTitleText("Logs"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(PreferenceCustomCategory::class.java)), withKey("data_protection"), + PreferenceMatchers.withTitleText("Data Privacy"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(Preference::class.java)), withKey("privacy_settings"), + PreferenceMatchers.withTitleText("Privacy Settings"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(Preference::class.java)), withKey("privacy_policy"), + PreferenceMatchers.withTitleText("Privacy Policy"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(Preference::class.java)), withKey("sourcecode"), + PreferenceMatchers.withTitleText("Used OpenSource Software"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(PreferenceCustomCategory::class.java)), withKey("service"), + PreferenceMatchers.withTitleText("Service"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(Preference::class.java)), withKey("help"), + PreferenceMatchers.withTitleText("Help"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(Preference::class.java)), withKey("imprint"), + PreferenceMatchers.withTitleText("Imprint"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(PreferenceCustomCategory::class.java)), withKey("info"), + PreferenceMatchers.withTitleText("Info"))) + .check(matches(isCompletelyDisplayed())) + } + + @Test + fun verifyPreferencesSummaryText() { + onData(allOf(`is`(instanceOf(Preference::class.java)), withKey("lock"), + PreferenceMatchers.withSummaryText("None"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(Preference::class.java)), withKey("syncedFolders"), + PreferenceMatchers.withSummaryText("Manage folders for auto upload"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(Preference::class.java)), withKey("backup"), + PreferenceMatchers.withSummaryText("Daily backup of your calendar & contacts"))) + .check(matches(isCompletelyDisplayed())) + + onData(allOf(`is`(instanceOf(Preference::class.java)), withKey("mnemonic"), + PreferenceMatchers.withSummaryText("To show mnemonic please enable device credentials."))) + .check(matches(isCompletelyDisplayed())) + } +} \ No newline at end of file diff --git a/app/src/debug/res/values/log_config.xml b/app/src/debug/res/values/log_config.xml new file mode 100644 index 000000000000..db470a2d25c4 --- /dev/null +++ b/app/src/debug/res/values/log_config.xml @@ -0,0 +1,11 @@ + + + + + true + \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/client/logger/ui/LogsActivity.kt b/app/src/main/java/com/nextcloud/client/logger/ui/LogsActivity.kt index 214750cbcffc..3a537887148c 100644 --- a/app/src/main/java/com/nextcloud/client/logger/ui/LogsActivity.kt +++ b/app/src/main/java/com/nextcloud/client/logger/ui/LogsActivity.kt @@ -85,6 +85,7 @@ class LogsActivity : ToolbarActivity() { android.R.id.home -> finish() R.id.action_delete_logs -> vm.deleteAll() R.id.action_send_logs -> vm.send() + R.id.action_save_logs -> vm.save() R.id.action_refresh_logs -> vm.load() else -> retval = super.onOptionsItemSelected(item) } diff --git a/app/src/main/java/com/nextcloud/client/logger/ui/LogsSaveHandler.kt b/app/src/main/java/com/nextcloud/client/logger/ui/LogsSaveHandler.kt new file mode 100644 index 000000000000..1daef16c5d6c --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/logger/ui/LogsSaveHandler.kt @@ -0,0 +1,139 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + */ +package com.nextcloud.client.logger.ui + +import android.app.DownloadManager +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.content.Intent.FLAG_ACTIVITY_NEW_TASK +import androidx.core.app.NotificationCompat +import com.nextcloud.client.core.AsyncRunner +import com.nextcloud.client.core.Cancellable +import com.nextcloud.client.core.Clock +import com.nextcloud.client.logger.LogEntry +import com.owncloud.android.R +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.ui.notifications.NotificationUtils +import com.owncloud.android.utils.DisplayUtils +import com.owncloud.android.utils.FileExportUtils +import java.io.File +import java.io.FileWriter +import java.security.SecureRandom +import java.util.TimeZone + +// NMC-4888 task +class LogsSaveHandler(private val context: Context, private val clock: Clock, private val runner: AsyncRunner) { + + private companion object { + private const val LOGS_MIME_TYPE = "text/plain" + private const val LOGS_DATE_FORMAT = "yyyyMMdd_HHmmssZ" + private val notificationId = SecureRandom().nextInt() + } + + private class Task( + private val logs: List, + private val file: File, + private val tz: TimeZone + ) : Function0 { + + override fun invoke(): File { + file.parentFile?.mkdirs() + val fo = FileWriter(file, false) + logs.forEach { + fo.write(it.toString(tz)) + fo.write("\n") + } + fo.close() + return file + } + } + + private var task: Cancellable? = null + + fun save(logs: List) { + if (task == null) { + val timestamp = DisplayUtils.getDateByPattern(System.currentTimeMillis(), context, LOGS_DATE_FORMAT) + val logFileName = "logs_${context.resources.getString(R.string.app_name)}_${timestamp}.txt" + val outFile = File(context.cacheDir, logFileName) + task = runner.postQuickTask(Task(logs, outFile, clock.tz), onResult = { + task = null + export(it) + }) + } + } + + fun stop() { + if (task != null) { + task?.cancel() + task = null + } + } + + private fun export(file: File) { + task = null + try { + FileExportUtils().exportFile( + file.name, + LOGS_MIME_TYPE, + context.contentResolver, + null, + file + ) + showSuccessNotification() + } catch (e: IllegalStateException) { + Log_OC.e("LogsSaveHandler", "Error saving logs to file", e) + showErrorNotification() + } + } + + private fun showErrorNotification() { + showNotification(false, context.resources.getString(R.string.logs_export_failed)) + } + + private fun showSuccessNotification() { + showNotification(true, context.resources.getString(R.string.logs_export_success)) + } + + private fun showNotification(isSuccess: Boolean, message: String) { + val notificationBuilder = NotificationCompat.Builder( + context, + NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD + ) + .setSmallIcon(R.drawable.notification_icon) + .setContentTitle(message) + .setAutoCancel(true) + + // NMC Customization + notificationBuilder.color = context.resources.getColor(R.color.primary, null) + + if (isSuccess) { + val actionIntent = Intent(DownloadManager.ACTION_VIEW_DOWNLOADS).apply { + flags = FLAG_ACTIVITY_NEW_TASK + } + val actionPendingIntent = PendingIntent.getActivity( + context, + notificationId, + actionIntent, + PendingIntent.FLAG_CANCEL_CURRENT or + PendingIntent.FLAG_IMMUTABLE + ) + notificationBuilder.addAction( + NotificationCompat.Action( + null, + context.getString(R.string.locate_folder), + actionPendingIntent + ) + ) + } + + val notificationManager = context + .getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.notify(notificationId, notificationBuilder.build()) + } +} diff --git a/app/src/main/java/com/nextcloud/client/logger/ui/LogsViewModel.kt b/app/src/main/java/com/nextcloud/client/logger/ui/LogsViewModel.kt index 46e4a8cbd566..12030e451e7d 100644 --- a/app/src/main/java/com/nextcloud/client/logger/ui/LogsViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/logger/ui/LogsViewModel.kt @@ -32,6 +32,7 @@ class LogsViewModel @Inject constructor( private val asyncFilter = AsyncFilter(asyncRunner) private val sender = LogsEmailSender(context, clock, asyncRunner) + private val logsSaver = LogsSaveHandler(context, clock, asyncRunner) private var allEntries = emptyList() private var logsSize = -1L private var filterDurationMs = 0L @@ -48,6 +49,12 @@ class LogsViewModel @Inject constructor( } } + fun save() { + entries.value?.let { + logsSaver.save(it) + } + } + fun load() { if (isLoading.value != true) { logsRepository.load(this::onLoaded) diff --git a/app/src/main/java/com/nmc/android/ui/PrivacySettingsInterface.kt b/app/src/main/java/com/nmc/android/ui/PrivacySettingsInterface.kt new file mode 100644 index 000000000000..860ffada08ab --- /dev/null +++ b/app/src/main/java/com/nmc/android/ui/PrivacySettingsInterface.kt @@ -0,0 +1,13 @@ +package com.nmc.android.ui + +import android.content.Context + +/** + * interface to open privacy settings activity from nmc/1921-settings branch + * for implementation look nmc/1878-privacy branch + * this class will have the declaration for it since it has the PrivacySettingsActivity.java in place + * since we don't have privacy settings functionality in this branch so to handle the redirection we have used interface + */ +interface PrivacySettingsInterface { + fun openPrivacySettingsActivity(context: Context) +} \ No newline at end of file diff --git a/app/src/main/java/com/owncloud/android/ui/AppVersionPreference.kt b/app/src/main/java/com/owncloud/android/ui/AppVersionPreference.kt new file mode 100644 index 000000000000..1bcf709dce25 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/AppVersionPreference.kt @@ -0,0 +1,51 @@ +package com.owncloud.android.ui + +import android.content.Context +import android.content.pm.PackageManager +import android.preference.Preference +import android.util.AttributeSet +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import com.owncloud.android.R +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.utils.StringUtils + +class AppVersionPreference : Preference { + constructor(context: Context?) : super(context) + constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) + constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle) + + override fun getView(convertView: View?, parent: ViewGroup?): View { + val v = super.getView(convertView, parent) + updatePreferenceView(v.findViewById(R.id.title), v.findViewById(R.id.summary)) + return v + } + + private fun updatePreferenceView(title: TextView, summary: TextView) { + val appVersion = appVersion + val titleColor: Int = context.resources.getColor(R.color.fontAppbar, null) + title.text = StringUtils.getColorSpan( + context.getString(R.string.app_name), + titleColor + ) + summary.text = String.format(context.getString(R.string.about_version), appVersion) + } + + private val appVersion: String + get() { + var temp: String + try { + val pkg = context.packageManager.getPackageInfo(context.packageName, 0) + temp = pkg.versionName ?: "" + } catch (e: PackageManager.NameNotFoundException) { + temp = "" + Log_OC.e(TAG, "Error while showing about dialog", e) + } + return temp + } + + companion object { + private val TAG = AppVersionPreference::class.java.simpleName + } +} \ No newline at end of file diff --git a/app/src/main/java/com/owncloud/android/ui/PreferenceCustomCategory.kt b/app/src/main/java/com/owncloud/android/ui/PreferenceCustomCategory.kt new file mode 100644 index 000000000000..ec6b920585e1 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/PreferenceCustomCategory.kt @@ -0,0 +1,30 @@ +package com.owncloud.android.ui + +import android.content.Context +import android.graphics.Typeface +import android.preference.PreferenceCategory +import android.util.AttributeSet +import android.util.TypedValue +import android.view.View +import android.widget.TextView +import com.owncloud.android.R + +class PreferenceCustomCategory : PreferenceCategory { + constructor(context: Context?) : super(context) + constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) + constructor( + context: Context?, attrs: AttributeSet?, + defStyle: Int + ) : super(context, attrs, defStyle) + + override fun onBindView(view: View) { + super.onBindView(view) + val titleView = view.findViewById(android.R.id.title) + titleView.setTextColor(context.resources.getColor(R.color.text_color)) + titleView.setTextSize( + TypedValue.COMPLEX_UNIT_PX, + context.resources.getDimensionPixelSize(R.dimen.txt_size_16sp).toFloat() + ) + titleView.setTypeface(null, Typeface.BOLD) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/owncloud/android/ui/ThemeableSwitchPreference.java b/app/src/main/java/com/owncloud/android/ui/ThemeableSwitchPreference.java index 279529622c43..683c88d214b7 100644 --- a/app/src/main/java/com/owncloud/android/ui/ThemeableSwitchPreference.java +++ b/app/src/main/java/com/owncloud/android/ui/ThemeableSwitchPreference.java @@ -9,6 +9,7 @@ import android.annotation.SuppressLint; import android.content.Context; +import android.content.res.ColorStateList; import android.preference.SwitchPreference; import android.util.AttributeSet; import android.view.View; @@ -16,10 +17,13 @@ import android.widget.Switch; import com.owncloud.android.MainApp; +import com.owncloud.android.R; import com.owncloud.android.utils.theme.ViewThemeUtils; import javax.inject.Inject; +import androidx.core.content.res.ResourcesCompat; + /** * Themeable switch preference TODO Migrate to androidx */ @@ -52,11 +56,64 @@ protected void onBindView(View view) { } private void findSwitch(ViewGroup viewGroup) { + ColorStateList thumbColorStateList; + ColorStateList trackColorStateList; + for (int i = 0; i < viewGroup.getChildCount(); i++) { View child = viewGroup.getChildAt(i); if (child instanceof @SuppressLint("UseSwitchCompatOrMaterialCode") Switch switchView) { - viewThemeUtils.platform.colorSwitch(switchView); + + int[][] states = new int[][]{ + new int[]{android.R.attr.state_enabled, android.R.attr.state_checked}, // enabled and checked + new int[]{android.R.attr.state_enabled, -android.R.attr.state_checked}, // enabled and unchecked + new int[]{-android.R.attr.state_enabled} // disabled + }; + + int thumbColorCheckedEnabled = ResourcesCompat.getColor( + switchView.getContext().getResources(), + R.color.switch_thumb_checked_enabled, + switchView.getContext().getTheme()); + int thumbColorUncheckedEnabled = ResourcesCompat.getColor( + switchView.getContext().getResources(), + R.color.switch_thumb_unchecked_enabled, + switchView.getContext().getTheme()); + int thumbColorDisabled = + ResourcesCompat.getColor( + switchView.getContext().getResources(), + R.color.switch_thumb_disabled, + switchView.getContext().getTheme()); + + int[] thumbColors = new int[]{ + thumbColorCheckedEnabled, + thumbColorUncheckedEnabled, + thumbColorDisabled + }; + + thumbColorStateList = new ColorStateList(states, thumbColors); + + int trackColorCheckedEnabled = ResourcesCompat.getColor( + switchView.getContext().getResources(), + R.color.switch_track_checked_enabled, + switchView.getContext().getTheme()); + int trackColorUncheckedEnabled = ResourcesCompat.getColor( + switchView.getContext().getResources(), + R.color.switch_track_unchecked_enabled, + switchView.getContext().getTheme()); + int trackColorDisabled = ResourcesCompat.getColor( + switchView.getContext().getResources(), + R.color.switch_track_disabled, + switchView.getContext().getTheme()); + + int[] trackColors = new int[]{ + trackColorCheckedEnabled, + trackColorUncheckedEnabled, + trackColorDisabled + }; + trackColorStateList = new ColorStateList(states, trackColors); + + switchView.setThumbTintList(thumbColorStateList); + switchView.setTrackTintList(trackColorStateList); break; } else if (child instanceof ViewGroup) { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java index ffdbfd72ae9a..b11101e96286 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java @@ -19,8 +19,6 @@ import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -43,6 +41,7 @@ import android.view.ViewGroup; import android.view.WindowManager; import android.webkit.URLUtil; +import android.widget.ListView; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.nextcloud.android.common.ui.util.extensions.WindowExtensionsKt; @@ -53,9 +52,9 @@ import com.nextcloud.client.logger.ui.LogsActivity; import com.nextcloud.client.network.ClientFactory; import com.nextcloud.client.network.ConnectivityService; +import com.nmc.android.ui.PrivacySettingsInterface; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.client.preferences.AppPreferencesImpl; -import com.nextcloud.client.preferences.DarkMode; import com.nextcloud.utils.extensions.ContextExtensionsKt; import com.nextcloud.utils.extensions.ViewExtensionsKt; import com.nextcloud.utils.mdm.MDMConfig; @@ -80,17 +79,18 @@ import com.owncloud.android.utils.EncryptionUtils; import com.owncloud.android.utils.MimeTypeUtil; import com.owncloud.android.utils.PermissionUtil; +import com.owncloud.android.utils.StringUtils; import com.owncloud.android.utils.theme.CapabilityUtils; import com.owncloud.android.utils.theme.ViewThemeUtils; import java.util.ArrayList; -import java.util.List; import java.util.Objects; import javax.inject.Inject; import androidx.annotation.LayoutRes; import androidx.annotation.NonNull; +import androidx.annotation.StringRes; import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatDelegate; @@ -151,6 +151,17 @@ public class SettingsActivity extends PreferenceActivity @Inject ViewThemeUtils viewThemeUtils; @Inject ConnectivityService connectivityService; + /** + * Things to note about both the branches. + * 1. nmc/1921-settings branch: + * --> interface won't be initialised + * --> calling of interface method will be done here + * 2. nmc/1878-privacy + * --> interface will be initialised + * --> calling of interface method won't be done here + */ + private PrivacySettingsInterface privacySettingsInterface; + @SuppressWarnings("deprecation") @Override public void onCreate(Bundle savedInstanceState) { @@ -171,12 +182,15 @@ public void onCreate(Bundle savedInstanceState) { getDelegate().onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); + ListView listView = getListView(); + listView.setDivider(ResourcesCompat.getDrawable(getResources(), R.drawable.item_divider, null)); + setupActionBar(); // Register context menu for list of preferences. registerForContextMenu(getListView()); - String appVersion = getAppVersion(); + int titleColor = getResources().getColor(R.color.fontAppbar, null); PreferenceScreen preferenceScreen = (PreferenceScreen) findPreference("preference_screen"); user = accountManager.getUser(); @@ -184,29 +198,43 @@ public void onCreate(Bundle savedInstanceState) { // retrieve user's base uri setupBaseUri(); + // Account Information + setupAccountInfoCategory(titleColor); + // General - setupGeneralCategory(); + setupGeneralCategory(titleColor); // Synced folders - setupAutoUploadCategory(preferenceScreen); + setupAutoUploadCategory(titleColor, preferenceScreen); // Files - setupFilesCategory(); + setupFilesCategory(titleColor); // Details - setupDetailsCategory(preferenceScreen); + setupDetailsCategory(titleColor, preferenceScreen); // Sync - setupSyncCategory(); + setupSyncCategory(titleColor); // More - setupMoreCategory(); + setupMoreCategory(titleColor); // About - setupAboutCategory(appVersion); + // Not required in NMC + // setupAboutCategory(appVersion); + + // Data Privacy + setupDataPrivacyCategory(titleColor); + + //Service + setUpServiceCategory(titleColor); + + //Info + setUpInfoCategory(titleColor); + // Not required for NMC // Dev - setupDevCategory(preferenceScreen); + // setupDevCategory(preferenceScreen); // workaround for mismatched color when app dark mode and system dark mode don't agree setListBackground(); @@ -377,22 +405,107 @@ private void setupAboutCategory(String appVersion) { } } - private void setupSyncCategory() { + private void setupSyncCategory(int titleColor) { final PreferenceCategory preferenceCategorySync = (PreferenceCategory) findPreference("sync"); - viewThemeUtils.files.themePreferenceCategory(preferenceCategorySync); + preferenceCategorySync.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_category_sync), + titleColor)); + setupAutoUploadPreference(preferenceCategorySync, titleColor); + // setupInternalTwoWaySyncPreference(titleColor); + setupAllFilesAccessPreference(preferenceCategorySync, titleColor); + } + /** + * NMC customization + */ + private void setupDataPrivacyCategory(int titleColor) { + PreferenceCategory preferenceCategoryAbout = (PreferenceCategory) findPreference("data_protection"); + preferenceCategoryAbout.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_category_data_privacy), + titleColor)); + + //privacy settings + Preference privacySettingPreference = findPreference("privacy_settings"); + if (privacySettingPreference != null) { + privacySettingPreference.setTitle(StringUtils.getColorSpan(getString(R.string.privacy_settings), + titleColor)); + privacySettingPreference.setOnPreferenceClickListener(preference -> { + //implementation and logic will be available in nmc/1878-privacy + if (privacySettingsInterface != null) { + privacySettingsInterface.openPrivacySettingsActivity(SettingsActivity.this); + } + return true; + }); + } + + // privacy policy + Preference privacyPolicyPreference = findPreference("privacy_policy"); + + if (privacyPolicyPreference != null) { + privacyPolicyPreference.setTitle(StringUtils.getColorSpan(getString(R.string.privacy_policy), + titleColor)); + if (URLUtil.isValidUrl(getString(R.string.privacy_url))) { + privacyPolicyPreference.setOnPreferenceClickListener(preference -> { + try { + Uri privacyUrl = Uri.parse(getString(R.string.privacy_url)); + String mimeType = MimeTypeUtil.getBestMimeTypeByFilename(privacyUrl.getLastPathSegment()); + + Intent intent; + if (MimeTypeUtil.isPDF(mimeType)) { + intent = new Intent(Intent.ACTION_VIEW, privacyUrl); + DisplayUtils.startIntentIfAppAvailable(intent, this, R.string.no_pdf_app_available); + } else { + intent = new Intent(getApplicationContext(), ExternalSiteWebView.class); + intent.putExtra(ExternalSiteWebView.EXTRA_TITLE, + getResources().getString(R.string.privacy_policy)); + intent.putExtra(ExternalSiteWebView.EXTRA_URL, privacyUrl.toString()); + intent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, false); + } + + startActivity(intent); + } catch (Exception e) { + Log_OC.e(TAG, "Could not parse privacy policy url"); + preferenceCategoryAbout.removePreference(privacyPolicyPreference); + } + return true; + }); + } else { + preferenceCategoryAbout.removePreference(privacyPolicyPreference); + } + } + + // source code + Preference sourcecodePreference = findPreference("sourcecode"); + if (sourcecodePreference != null) { + sourcecodePreference.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_open_source), + titleColor)); + if (URLUtil.isValidUrl(getString(R.string.sourcecode_url))) { + sourcecodePreference.setOnPreferenceClickListener(preference -> { + Intent intent = new Intent(getApplicationContext(), ExternalSiteWebView.class); + intent.putExtra(ExternalSiteWebView.EXTRA_TITLE, + getResources().getString(R.string.prefs_open_source)); + intent.putExtra(ExternalSiteWebView.EXTRA_URL, getResources().getString(R.string.sourcecode_url)); + intent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, false); + startActivity(intent); + return true; + }); + } else { + preferenceCategoryAbout.removePreference(sourcecodePreference); + } + } + } - setupAutoUploadPreference(preferenceCategorySync); - setupInternalTwoWaySyncPreference(); - setupAllFilesAccessPreference(preferenceCategorySync); + private void setUpInfoCategory(int titleColor) { + PreferenceCategory preferenceCategoryAbout = (PreferenceCategory) findPreference("info"); + preferenceCategoryAbout.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_category_info), + titleColor)); } - private void setupMoreCategory() { + private void setupMoreCategory(int titleColor) { final PreferenceCategory preferenceCategoryMore = (PreferenceCategory) findPreference("more"); - viewThemeUtils.files.themePreferenceCategory(preferenceCategoryMore); + preferenceCategoryMore.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_category_more), + titleColor)); setupCalendarPreference(preferenceCategoryMore); - setupBackupPreference(); + setupBackupPreference(titleColor); setupE2EPreference(preferenceCategoryMore); @@ -402,40 +515,19 @@ private void setupMoreCategory() { removeE2E(preferenceCategoryMore); - setupHelpPreference(preferenceCategoryMore); - setupRecommendPreference(preferenceCategoryMore); - setupLoggingPreference(preferenceCategoryMore); - - setupImprintPreference(preferenceCategoryMore); + setupLoggingPreference(preferenceCategoryMore, titleColor); loadExternalSettingLinks(preferenceCategoryMore); } - private void setupImprintPreference(PreferenceCategory preferenceCategoryMore) { - boolean imprintEnabled = getResources().getBoolean(R.bool.imprint_enabled); - Preference pImprint = findPreference("imprint"); - if (pImprint != null) { - if (imprintEnabled) { - pImprint.setOnPreferenceClickListener(preference -> { - String imprintWeb = getString(R.string.url_imprint); + private void setupLoggingPreference(PreferenceCategory preferenceCategoryMore, int titleColor) { - if (!imprintWeb.isEmpty()) { - DisplayUtils.startLinkIntent(this, imprintWeb); - } - //ImprintDialog.newInstance(true).show(preference.get, "IMPRINT_DIALOG"); - return true; - }); - } else { - preferenceCategoryMore.removePreference(pImprint); - } - } - } - - private void setupLoggingPreference(PreferenceCategory preferenceCategoryMore) { Preference pLogger = findPreference("logger"); if (pLogger != null) { + pLogger.setTitle(StringUtils.getColorSpan(getString(R.string.logs_title), + titleColor)); if (MDMConfig.INSTANCE.isLogEnabled(this)) { pLogger.setOnPreferenceClickListener(preference -> { Intent loggerIntent = new Intent(getApplicationContext(), LogsActivity.class); @@ -592,23 +684,10 @@ private void showRemoveE2EAlertDialog(PreferenceCategory preferenceCategoryMore, .show(); } - private void setupHelpPreference(PreferenceCategory preferenceCategoryMore) { - boolean helpEnabled = getResources().getBoolean(R.bool.help_enabled); - Preference pHelp = findPreference("help"); - if (pHelp != null) { - if (helpEnabled) { - pHelp.setOnPreferenceClickListener(preference -> { - DisplayUtils.startLinkIntent(this, R.string.url_help); - return true; - }); - } else { - preferenceCategoryMore.removePreference(pHelp); - } - } - } - - private void setupAutoUploadPreference(PreferenceCategory preferenceCategoryMore) { + private void setupAutoUploadPreference(PreferenceCategory preferenceCategoryMore, int titleColor) { Preference autoUpload = findPreference("syncedFolders"); + autoUpload.setTitle(StringUtils.getColorSpan(getString(R.string.drawer_synced_folders), + titleColor)); if (getResources().getBoolean(R.bool.syncedFolder_light)) { preferenceCategoryMore.removePreference(autoUpload); } else { @@ -620,8 +699,10 @@ private void setupAutoUploadPreference(PreferenceCategory preferenceCategoryMore } } - private void setupInternalTwoWaySyncPreference() { + private void setupInternalTwoWaySyncPreference(int titleColor) { Preference twoWaySync = findPreference("internal_two_way_sync"); + twoWaySync.setTitle(StringUtils.getColorSpan(getString(R.string.internal_two_way_sync), + titleColor)); twoWaySync.setOnPreferenceClickListener(preference -> { Intent intent = new Intent(this, InternalTwoWaySyncActivity.class); @@ -630,8 +711,9 @@ private void setupInternalTwoWaySyncPreference() { }); } - private void setupAllFilesAccessPreference(PreferenceCategory category) { + private void setupAllFilesAccessPreference(PreferenceCategory category, int titleColor) { Preference allFilesAccess = findPreference("allFilesAccess"); + allFilesAccess.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_all_files_access_title), titleColor)); if (PermissionUtil.checkAllFilesAccess()) { category.removePreference(allFilesAccess); @@ -647,13 +729,12 @@ private void setupAllFilesAccessPreference(PreferenceCategory category) { }); } - private void setupBackupPreference() { + private void setupBackupPreference(int titleColor) { Preference pContactsBackup = findPreference("backup"); if (pContactsBackup != null) { boolean showCalendarBackup = getResources().getBoolean(R.bool.show_calendar_backup); - pContactsBackup.setTitle(showCalendarBackup - ? getString(R.string.backup_title) - : getString(R.string.contact_backup_title)); + // NMC Customization + pContactsBackup.setTitle(StringUtils.getColorSpan(getString(R.string.actionbar_contacts), titleColor)); pContactsBackup.setSummary(showCalendarBackup ? getString(R.string.prefs_daily_backup_summary) : getString(R.string.prefs_daily_contact_backup_summary)); @@ -687,9 +768,10 @@ private void setupCalendarPreference(PreferenceCategory preferenceCategoryMore) } } - private void setupDetailsCategory(PreferenceScreen preferenceScreen) { + private void setupDetailsCategory(int titleColor, PreferenceScreen preferenceScreen) { PreferenceCategory preferenceCategoryDetails = (PreferenceCategory) findPreference("details"); - viewThemeUtils.files.themePreferenceCategory(preferenceCategoryDetails); + preferenceCategoryDetails.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_category_details), + titleColor)); boolean fPassCodeEnabled = getResources().getBoolean(R.bool.passcode_enabled); boolean fDeviceCredentialsEnabled = getResources().getBoolean(R.bool.device_credentials_enabled); @@ -697,11 +779,11 @@ private void setupDetailsCategory(PreferenceScreen preferenceScreen) { boolean fSyncedFolderLightEnabled = getResources().getBoolean(R.bool.syncedFolder_light); boolean fShowMediaScanNotifications = preferences.isShowMediaScanNotifications(); - setupLockPreference(preferenceCategoryDetails, fPassCodeEnabled, fDeviceCredentialsEnabled); + setupLockPreference(preferenceCategoryDetails, fPassCodeEnabled, fDeviceCredentialsEnabled, titleColor); setupShowEcosystemAppsPreference(preferenceCategoryDetails, fShowEcosystemAppsEnabled); - setupShowMediaScanNotifications(preferenceCategoryDetails, fShowMediaScanNotifications); + setupShowMediaScanNotifications(preferenceCategoryDetails, fShowMediaScanNotifications, titleColor); if (!fPassCodeEnabled && !fDeviceCredentialsEnabled && fSyncedFolderLightEnabled && fShowMediaScanNotifications) { @@ -709,30 +791,33 @@ private void setupDetailsCategory(PreferenceScreen preferenceScreen) { } } - private void setupFilesCategory() { + private void setupFilesCategory(int titleColor) { PreferenceCategory preferenceCategoryDetails = (PreferenceCategory) findPreference("files"); - viewThemeUtils.files.themePreferenceCategory(preferenceCategoryDetails); + preferenceCategoryDetails.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_category_files), + titleColor)); boolean fShowHiddenFilesEnabled = getResources().getBoolean(R.bool.show_hidden_files_enabled); - setupHiddenFilesPreference(preferenceCategoryDetails, fShowHiddenFilesEnabled); - setupFoldersBeforeFilesPreference(); - setupSortFavoritesFirstPreference(); + setupHiddenFilesPreference(preferenceCategoryDetails, fShowHiddenFilesEnabled, titleColor); + setupFoldersBeforeFilesPreference(titleColor); + setupSortFavoritesFirstPreference(titleColor); } private void setupShowMediaScanNotifications(PreferenceCategory preferenceCategoryDetails, - boolean fShowMediaScanNotifications) { - ThemeableSwitchPreference mShowMediaScanNotifications = - (ThemeableSwitchPreference) findPreference(PREFERENCE_SHOW_MEDIA_SCAN_NOTIFICATIONS); - + boolean fShowMediaScanNotifications, int titleColor) { + SwitchPreference mShowMediaScanNotifications = (SwitchPreference) findPreference(PREFERENCE_SHOW_MEDIA_SCAN_NOTIFICATIONS); + mShowMediaScanNotifications.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_enable_media_scan_notifications), + titleColor)); if (fShowMediaScanNotifications) { preferenceCategoryDetails.removePreference(mShowMediaScanNotifications); } } private void setupHiddenFilesPreference(PreferenceCategory preferenceCategoryDetails, - boolean fShowHiddenFilesEnabled) { + boolean fShowHiddenFilesEnabled, int titleColor) { showHiddenFiles = (ThemeableSwitchPreference) findPreference("show_hidden_files"); + showHiddenFiles.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_show_hidden_files), + titleColor)); if (fShowHiddenFilesEnabled) { showHiddenFiles.setOnPreferenceClickListener(preference -> { preferences.setShowHiddenFilesEnabled(showHiddenFiles.isChecked()); @@ -743,16 +828,20 @@ private void setupHiddenFilesPreference(PreferenceCategory preferenceCategoryDet } } - private void setupFoldersBeforeFilesPreference() { + private void setupFoldersBeforeFilesPreference(int titleColor) { ThemeableSwitchPreference preference = (ThemeableSwitchPreference) findPreference("sort_folders_before_files"); + preference.setTitle(StringUtils.getColorSpan(getString(R.string.sort_folders_before_files), + titleColor)); preference.setOnPreferenceClickListener(p -> { preferences.setSortFoldersBeforeFiles(preference.isChecked()); return true; }); } - private void setupSortFavoritesFirstPreference() { + private void setupSortFavoritesFirstPreference(int titleColor) { ThemeableSwitchPreference preference = (ThemeableSwitchPreference) findPreference("sort_favorites_first"); + preference.setTitle(StringUtils.getColorSpan(getString(R.string.sort_favorites_first), + titleColor)); preference.setOnPreferenceClickListener(p -> { preferences.setSortFavoritesFirst(preference.isChecked()); return true; @@ -774,9 +863,11 @@ private void setupShowEcosystemAppsPreference(PreferenceCategory preferenceCateg private void setupLockPreference(PreferenceCategory preferenceCategoryDetails, boolean passCodeEnabled, - boolean deviceCredentialsEnabled) { + boolean deviceCredentialsEnabled, int titleColor) { boolean enforceProtection = MDMConfig.INSTANCE.enforceProtection(this); lock = (ListPreferenceDialog) findPreference(PREFERENCE_LOCK); + lock.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_lock), + titleColor)); int optionSize = 3; if (enforceProtection) { optionSize = 2; @@ -832,10 +923,11 @@ private void setupLockPreference(PreferenceCategory preferenceCategoryDetails, } } - private void setupAutoUploadCategory(PreferenceScreen preferenceScreen) { + private void setupAutoUploadCategory(int titleColor, PreferenceScreen preferenceScreen) { final PreferenceCategory preferenceCategorySyncedFolders = (PreferenceCategory) findPreference("synced_folders_category"); - viewThemeUtils.files.themePreferenceCategory(preferenceCategorySyncedFolders); + preferenceCategorySyncedFolders.setTitle(StringUtils.getColorSpan(getString(R.string.drawer_synced_folders), + titleColor)); if (!getResources().getBoolean(R.bool.syncedFolder_light)) { preferenceScreen.removePreference(preferenceCategorySyncedFolders); @@ -844,6 +936,8 @@ private void setupAutoUploadCategory(PreferenceScreen preferenceScreen) { final ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(this); final SwitchPreference pUploadOnWifiCheckbox = (SwitchPreference) findPreference("synced_folder_on_wifi"); + pUploadOnWifiCheckbox.setTitle(StringUtils.getColorSpan(getString(R.string.auto_upload_on_wifi), + titleColor)); pUploadOnWifiCheckbox.setChecked( arbitraryDataProvider.getBooleanValue(user, SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI)); @@ -869,6 +963,73 @@ private void setupAutoUploadCategory(PreferenceScreen preferenceScreen) { } } + private void setUpServiceCategory(int titleColor) { + PreferenceCategory preferenceCategoryService = (PreferenceCategory) findPreference("service"); + preferenceCategoryService.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_category_service), + titleColor)); + setupHelpPreference(titleColor); + setupDeleteAccountPreference(titleColor); + setupImprintPreference(titleColor); + } + + private void setupHelpPreference(int titleColor) { + Preference pHelp = findPreference("help"); + if (pHelp != null) { + pHelp.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_help), + titleColor)); + pHelp.setOnPreferenceClickListener(preference -> { + String helpWeb = getString(R.string.url_help); + if (!helpWeb.isEmpty()) { + openLinkInWebView(helpWeb, R.string.prefs_help); + } + return true; + }); + + } + } + + private void setupDeleteAccountPreference(int titleColor) { + Preference pHelp = findPreference("delete_account"); + if (pHelp != null) { + pHelp.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_delete_account), + titleColor)); + pHelp.setOnPreferenceClickListener(preference -> { + String helpWeb = getString(R.string.url_delete_account); + if (!helpWeb.isEmpty()) { + openLinkInWebView(helpWeb, R.string.prefs_delete_account); + } + return true; + }); + + } + } + + private void setupImprintPreference(int titleColor) { + Preference pImprint = findPreference("imprint"); + if (pImprint != null) { + pImprint.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_imprint), + titleColor)); + pImprint.setOnPreferenceClickListener(preference -> { + String imprintWeb = getString(R.string.url_imprint_nmc); + if (!imprintWeb.isEmpty()) { + openLinkInWebView(imprintWeb, R.string.prefs_imprint); + } + //ImprintDialog.newInstance(true).show(preference.get, "IMPRINT_DIALOG"); + return true; + }); + } + + } + + private void openLinkInWebView(String url, @StringRes int title) { + Intent externalWebViewIntent = new Intent(getApplicationContext(), ExternalSiteWebView.class); + externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, + getResources().getString(title)); + externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_URL, url); + externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, false); + startActivity(externalWebViewIntent); + } + private void enableLock(String lock) { pendingLock = LOCK_NONE; if (LOCK_PASSCODE.equals(lock)) { @@ -902,10 +1063,20 @@ private void disableLock(String lock) { } } - private void setupGeneralCategory() { - final PreferenceCategory preferenceCategoryGeneral = (PreferenceCategory) findPreference("general"); - viewThemeUtils.files.themePreferenceCategory(preferenceCategoryGeneral); + private void setupAccountInfoCategory(int titleColor) { + PreferenceCategory preferenceCategoryAccountInfo = (PreferenceCategory) findPreference("account_info"); + preferenceCategoryAccountInfo.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_category_account_info), + titleColor)); + Preference autoUpload = findPreference("user_name"); + autoUpload.setTitle(StringUtils.getColorSpan(accountManager.getUser().toOwnCloudAccount().getDisplayName(), + titleColor)); + } + + private void setupGeneralCategory(int titleColor) { + PreferenceCategory preferenceCategoryGeneral = (PreferenceCategory) findPreference("general"); + preferenceCategoryGeneral.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_category_general), + titleColor)); readStoragePath(); prefDataLoc = findPreference(AppPreferencesImpl.DATA_STORAGE_LOCATION); @@ -918,52 +1089,12 @@ private void setupGeneralCategory() { }); } - ListPreference themePref = (ListPreference) findPreference("darkMode"); - - List themeEntries = new ArrayList<>(3); - themeEntries.add(getString(R.string.prefs_value_theme_light)); - themeEntries.add(getString(R.string.prefs_value_theme_dark)); - themeEntries.add(getString(R.string.prefs_value_theme_system)); - - List themeValues = new ArrayList<>(3); - themeValues.add(DarkMode.LIGHT.name()); - themeValues.add(DarkMode.DARK.name()); - themeValues.add(DarkMode.SYSTEM.name()); - - themePref.setEntries(themeEntries.toArray(new String[0])); - themePref.setEntryValues(themeValues.toArray(new String[0])); - - if (TextUtils.isEmpty(themePref.getEntry())) { - themePref.setValue(DarkMode.SYSTEM.name()); - themePref.setSummary(TextUtils.isEmpty(themePref.getEntry()) ? DarkMode.SYSTEM.name() : themePref.getEntry()); - } - - themePref.setOnPreferenceChangeListener((preference, newValue) -> { - DarkMode mode = DarkMode.valueOf((String) newValue); - preferences.setDarkThemeMode(mode); - MainApp.setAppTheme(mode); - setListBackground(); - - return true; - }); } private void setListBackground() { getListView().setBackgroundColor(ContextCompat.getColor(this, R.color.bg_default)); } - private String getAppVersion() { - String temp; - try { - PackageInfo pkg = getPackageManager().getPackageInfo(getPackageName(), 0); - temp = pkg.versionName; - } catch (NameNotFoundException e) { - temp = ""; - Log_OC.e(TAG, "Error while showing about dialog", e); - } - return temp; - } - @Override public boolean onOptionsItemSelected(MenuItem item) { finish(); @@ -1094,7 +1225,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { } } else if (requestCode == REQ_ALL_FILES_ACCESS) { final PreferenceCategory preferenceCategorySync = (PreferenceCategory) findPreference("sync"); - setupAllFilesAccessPreference(preferenceCategorySync); + setupAllFilesAccessPreference(preferenceCategorySync, getResources().getColor(R.color.fontAppbar, null)); } } diff --git a/app/src/main/java/com/owncloud/android/utils/StringUtils.java b/app/src/main/java/com/owncloud/android/utils/StringUtils.java index d4339f0003eb..3f67854ef0dd 100644 --- a/app/src/main/java/com/owncloud/android/utils/StringUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/StringUtils.java @@ -7,6 +7,11 @@ */ package com.owncloud.android.utils; + +import android.text.Spannable; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; + import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -55,6 +60,15 @@ String searchAndColor(@Nullable String text, @Nullable String searchText, } } + public static Spannable getColorSpan(@NonNull String title, @ColorInt int color) { + Spannable text = new SpannableString(title); + text.setSpan(new ForegroundColorSpan(color), + 0, + text.length(), + Spannable.SPAN_INCLUSIVE_INCLUSIVE); + return text; + } + public static @NonNull String removePrefix(@NonNull String s, @NonNull String prefix) { diff --git a/app/src/main/res/drawable/item_divider.xml b/app/src/main/res/drawable/item_divider.xml new file mode 100644 index 000000000000..9f742e91d67c --- /dev/null +++ b/app/src/main/res/drawable/item_divider.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/layout/custom_app_preference_layout.xml b/app/src/main/res/layout/custom_app_preference_layout.xml new file mode 100644 index 000000000000..5c8ab83fae4c --- /dev/null +++ b/app/src/main/res/layout/custom_app_preference_layout.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/custom_preference_layout.xml b/app/src/main/res/layout/custom_preference_layout.xml new file mode 100644 index 000000000000..1f6991c06e56 --- /dev/null +++ b/app/src/main/res/layout/custom_preference_layout.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/activity_logs.xml b/app/src/main/res/menu/activity_logs.xml index 08a14cf62093..0e6e6dc40559 100644 --- a/app/src/main/res/menu/activity_logs.xml +++ b/app/src/main/res/menu/activity_logs.xml @@ -27,10 +27,17 @@ android:orderInCategory="200" android:icon="@drawable/ic_send"/> + + diff --git a/app/src/main/res/values-de/nmc_logs_strings.xml b/app/src/main/res/values-de/nmc_logs_strings.xml new file mode 100644 index 000000000000..223e2215231c --- /dev/null +++ b/app/src/main/res/values-de/nmc_logs_strings.xml @@ -0,0 +1,12 @@ + + + + Protokolle speichern + Protokolle erfolgreich gespeichert + Fehler beim Speichern der Protokolle + \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 63e97d1a9c78..4f62bd2b6a26 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -13,7 +13,8 @@ Senden/Teilen Kachelansicht Listenansicht - Kontakte und Kalender wiederherstellen + Kontakte & Kalender wiederherstellen + Kontakte sichern Neuer Ordner Verschieben oder kopieren Öffnen mit @@ -720,6 +721,7 @@ Ende-zu-Ende-Verschlüsselung ist eingerichtet! E2E-Gedächtnisstütze Um die Gedächtnisstütze anzuzeigen, aktivieren Sie bitte Geräte-Zugangsdaten + 12-Wort-Schlüssel anzeigen (Passphrase) Benachrichtigungen der Mediensuche anzeigen Über neu gefundene Medienordner informieren GNU General Public Lizenz, version 2 @@ -1085,6 +1087,15 @@ Datei nicht gefunden. Sind Sie sicher, dass diese Datei existiert oder wurde ein früherer Konflikt nicht gelöst? Wir konnten die Datei auf dem Server nicht finden. Ein anderer Benutzer hat möglicherweise die Datei gelöscht. Ordnername + Kontoinformationen + Datenschutz + Datenschutz-Einstellungen + Datenschutzbestimmungen + Konto endgültig löschen + Verwendete OpenSource Software + Info + Bedienung + Die Ende-zu-Ende Verschlüsselung wurde bereits auf einem anderen Gerät eingerichtet. Bitte geben Sie Ihre Passphrase ein, damit die Dateien synchronisiert und entschlüsselt werden. Hochladeordner auswählen Konnte %1$s nicht hochladen Hochladen fehlgeschlagen, bitte erneut anmelden diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index db1e1d218038..c5134bdaad77 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -39,4 +39,68 @@ @android:color/white #101418 + + + #FFFFFF + @color/grey_30 + @color/grey_30 + #CCCCCC + @color/grey_70 + @color/grey_80 + #2D2D2D + @color/grey_70 + @color/grey_70 + + + @color/grey_80 + @color/grey_0 + + + @color/grey_80 + @color/grey_0 + + + @color/grey_60 + @color/grey_0 + @color/grey_0 + @color/grey_30 + #FFFFFF + @color/grey_30 + @color/grey_80 + #FFFFFF + + + @color/grey_80 + @color/grey_30 + @color/grey_0 + + + @color/grey_80 + @color/grey_0 + @color/grey_80 + + + @color/grey_70 + @color/grey_60 + + + @color/grey_70 + @color/grey_70 + + + #FFFFFF + @color/grey_30 + @color/grey_0 + @color/grey_0 + @color/grey_0 + @color/grey_0 + @color/grey_60 + @color/grey_0 + #FFFFFF + + + #121212 + @color/grey_0 + @color/grey_80 + @color/grey_80 diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 36d7459ecdaf..90c40fb1a4ad 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -89,4 +89,93 @@ #A5A5A5 #F7F9FF + + + #191919 + @color/primary + #191919 + #191919 + @color/grey_30 + @android:color/white + #FFFFFF + @color/grey_0 + #CCCCCC + #77c4ff + #B3FFFFFF + @color/grey_10 + + + #101010 + #F2F2F2 + #E5E5E5 + #B2B2B2 + #666666 + #4C4C4C + #333333 + + + @color/design_snackbar_background_color + @color/white + + + #FFFFFF + #191919 + + + @color/grey_0 + #191919 + @color/primary + #191919 + @color/primary + @color/grey_30 + @color/white + #191919 + + + #FFFFFF + #191919 + #191919 + + + #FFFFFF + #191919 + #FFFFFF + + + @color/primary + #F399C7 + #FFFFFF + @color/grey_30 + @color/grey_10 + @color/grey_0 + + + @color/primary + @color/grey_30 + @color/grey_30 + #CCCCCC + + + #191919 + @color/grey_30 + #191919 + #191919 + #191919 + #191919 + @color/grey_30 + #191919 + #000000 + #191919 + #F6E5EB + #C16F81 + #0D39DF + #0099ff + + + @color/grey_0 + #191919 + @color/grey_0 + @color/grey_30 + #77b6bb + #5077b6bb diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 000000000000..cc9e25255a10 --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,31 @@ + + + 4dp + 16dp + 24dp + 6dp + 18sp + 15sp + 15dp + 56dp + 86dp + 80dp + 11sp + 30dp + 55dp + 258dp + 17sp + 20dp + 160dp + 50dp + 150dp + 55dp + 48dp + 48dp + 24dp + 26dp + 20sp + 145dp + 1dp + 13sp + \ No newline at end of file diff --git a/app/src/main/res/values/nmc_logs_strings.xml b/app/src/main/res/values/nmc_logs_strings.xml new file mode 100644 index 000000000000..c71e7a804eb3 --- /dev/null +++ b/app/src/main/res/values/nmc_logs_strings.xml @@ -0,0 +1,12 @@ + + + + Save logs + Logs saved successfully + Failed to save logs + \ No newline at end of file diff --git a/app/src/main/res/values/setup.xml b/app/src/main/res/values/setup.xml index afed2dafe23a..4c492d82600a 100644 --- a/app/src/main/res/values/setup.xml +++ b/app/src/main/res/values/setup.xml @@ -99,6 +99,10 @@ "https://play.google.com/store/apps/details?id=com.nextcloud.client" https://nextcloud.com/install + https://www.telekom.de/hilfe/vertrag-rechnung/login-daten-passwoerter/telekom-login-loeschen + + https://www.telekom.de/impressum false diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a48e76517e0a..6c50db967e6e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -127,6 +127,7 @@ Calendar and contacts sync set up Daily backup of your calendar and contacts Daily backup of your contacts + Displays your 12 word key (passhprase) Manage folders for auto upload All files access @@ -139,6 +140,7 @@ Dark Follow system Theme + End-to-end encryption was already set up on another client. Please enter your mnemonic to allow this client to sync and decrypt the files. Enable two way sync Interval @@ -724,6 +726,7 @@ Grid view List view + Back up contacts Manage space Settings, database and server certificates from %1$s\'s data will be deleted permanently. \n\nDownloaded files will be kept untouched.\n\nThis process can take a while. @@ -1298,6 +1301,7 @@ Storage permission is required for file uploads. Storage permissions %1$s works best with permissions to access storage. You can choose full access to all files, or read-only access to photos and videos. + Account Information No results found for your query Start your search Type in the search bar above to find files, contacts, calendar events, and more across your account. @@ -1356,6 +1360,13 @@ Not possible without internet connection Scan page Done + Info + Data Privacy + Privacy Settings + Privacy Policy + Delete account permanently + Used OpenSource Software + Service Generating PDF… Error starting document scan PDF generation failed diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index adada5180474..58ad525d90c0 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -1,5 +1,4 @@ - - - + android:key="preference_screen"> + + + + + + + - - - - - - - - + + + + + + + + - - - - + + + + + android:layout="@layout/custom_preference_layout" + android:summary="@string/prefs_show_ecosystem_apps_summary" + android:title="@string/prefs_show_ecosystem_apps" /> - - - - + android:title="@string/prefs_enable_media_scan_notifications" /> + + + + - + - - + + android:layout="@layout/custom_preference_layout" + android:summary="@string/prefs_daily_backup_summary" + android:title="@string/backup_title" /> + android:layout="@layout/custom_preference_layout" + android:summary="@string/setup_e2e" + android:title="@string/prefs_setup_e2e" /> + android:layout="@layout/custom_preference_layout" + android:summary="@string/prefs_keys_exist_summary" + android:title="@string/prefs_keys_exist" /> + android:layout="@layout/custom_preference_layout" + android:summary="@string/prefs_mnemonic_summary" + android:title="@string/prefs_e2e_mnemonic" /> - - - - - - - - - - - - - - - - - - - - + android:layout="@layout/custom_preference_layout" + android:summary="@string/remove_e2e" + android:title="@string/prefs_remove_e2e" /> + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/release/res/values/log_config.xml b/app/src/release/res/values/log_config.xml new file mode 100644 index 000000000000..11b54b011564 --- /dev/null +++ b/app/src/release/res/values/log_config.xml @@ -0,0 +1,11 @@ + + + + + false + \ No newline at end of file