From 77af8edfdf998245314ddd9caea851d75c4fb818 Mon Sep 17 00:00:00 2001 From: A117870935 Date: Thu, 4 May 2023 20:12:31 +0530 Subject: [PATCH 1/2] Customized Settings as per MC with test cases. --- .../nmc/android/ui/SettingsPreferenceIT.kt | 212 +++++++++++ .../android/ui/PrivacySettingsInterface.kt | 13 + .../android/ui/AppVersionPreference.kt | 51 +++ .../android/ui/PreferenceCustomCategory.kt | 30 ++ .../android/ui/ThemeableSwitchPreference.java | 62 ++- .../android/ui/activity/SettingsActivity.java | 357 ++++++++++++------ .../owncloud/android/utils/StringUtils.java | 14 + app/src/main/res/drawable/item_divider.xml | 6 + .../layout/custom_app_preference_layout.xml | 59 +++ .../res/layout/custom_preference_layout.xml | 67 ++++ app/src/main/res/values-de/strings.xml | 12 +- app/src/main/res/values-night/colors.xml | 64 ++++ app/src/main/res/values/colors.xml | 89 +++++ app/src/main/res/values/dimens.xml | 31 ++ app/src/main/res/values/setup.xml | 3 + app/src/main/res/values/strings.xml | 10 + app/src/main/res/xml/preferences.xml | 234 +++++++----- 17 files changed, 1100 insertions(+), 214 deletions(-) create mode 100644 app/src/androidTest/java/com/nmc/android/ui/SettingsPreferenceIT.kt create mode 100644 app/src/main/java/com/nmc/android/ui/PrivacySettingsInterface.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/AppVersionPreference.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/PreferenceCustomCategory.kt create mode 100644 app/src/main/res/drawable/item_divider.xml create mode 100644 app/src/main/res/layout/custom_app_preference_layout.xml create mode 100644 app/src/main/res/layout/custom_preference_layout.xml create mode 100644 app/src/main/res/values/dimens.xml 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/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..ee0e0b43402a --- /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 7fad98aa344f..1d98859a2650 100644 --- a/app/src/main/java/com/owncloud/android/ui/ThemeableSwitchPreference.java +++ b/app/src/main/java/com/owncloud/android/ui/ThemeableSwitchPreference.java @@ -8,6 +8,7 @@ package com.owncloud.android.ui; import android.content.Context; +import android.content.res.ColorStateList; import android.preference.SwitchPreference; import android.util.AttributeSet; import android.view.View; @@ -15,10 +16,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 */ @@ -51,11 +55,65 @@ 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 Switch switchView) { - viewThemeUtils.platform.colorSwitch(switchView); + if (child instanceof Switch) { + Switch switchView = (Switch) child; + + 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 bcbed6b84d77..bb492040aaea 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 @@ -18,8 +18,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; @@ -40,6 +38,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; @@ -50,11 +49,13 @@ 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.ViewExtensionsKt; import com.nextcloud.utils.mdm.MDMConfig; +import com.owncloud.android.BuildConfig; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.AuthenticatorActivity; @@ -75,17 +76,18 @@ import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.EncryptionUtils; import com.owncloud.android.utils.MimeTypeUtil; +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; @@ -144,6 +146,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) { @@ -164,12 +177,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(); @@ -177,26 +193,40 @@ 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); // 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(); @@ -363,21 +393,108 @@ public void onBackPressed() { startActivity(i); } - private void setupSyncCategory() { + private void setupSyncCategory(int titleColor) { final PreferenceCategory preferenceCategorySync = (PreferenceCategory) findPreference("sync"); - viewThemeUtils.files.themePreferenceCategory(preferenceCategorySync); - setupAutoUploadPreference(preferenceCategorySync); - setupInternalTwoWaySyncPreference(); + setupAutoUploadPreference(preferenceCategorySync, titleColor); + setupInternalTwoWaySyncPreference(titleColor); } - private void setupMoreCategory() { + /** + * 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); + intent.putExtra(ExternalSiteWebView.EXTRA_MENU_ITEM_ID, -1); + } + + 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); + intent.putExtra(ExternalSiteWebView.EXTRA_MENU_ITEM_ID, -1); + startActivity(intent); + return true; + }); + } else { + preferenceCategoryAbout.removePreference(sourcecodePreference); + } + } + } + + private void setUpInfoCategory(int titleColor) { + PreferenceCategory preferenceCategoryAbout = (PreferenceCategory) findPreference("info"); + preferenceCategoryAbout.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_category_info), + titleColor)); + } + + 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); + setupCalendarPreference(preferenceCategoryMore); - setupBackupPreference(); + setupBackupPreference(titleColor); setupE2EPreference(preferenceCategoryMore); @@ -387,40 +504,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); - - 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, int titleColor) { - 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); @@ -577,23 +673,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 { @@ -605,8 +688,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.drawer_synced_folders), + titleColor)); twoWaySync.setOnPreferenceClickListener(preference -> { Intent intent = new Intent(this, InternalTwoWaySyncActivity.class); @@ -615,13 +700,12 @@ private void setupInternalTwoWaySyncPreference() { }); } - 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)); @@ -655,9 +739,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); @@ -666,13 +751,13 @@ 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); - setupHiddenFilesPreference(preferenceCategoryDetails, fShowHiddenFilesEnabled); + setupHiddenFilesPreference(preferenceCategoryDetails, fShowHiddenFilesEnabled, titleColor); setupShowEcosystemAppsPreference(preferenceCategoryDetails, fShowEcosystemAppsEnabled); - setupShowMediaScanNotifications(preferenceCategoryDetails, fShowMediaScanNotifications); + setupShowMediaScanNotifications(preferenceCategoryDetails, fShowMediaScanNotifications, titleColor); if (!fPassCodeEnabled && !fDeviceCredentialsEnabled && !fShowHiddenFilesEnabled && fSyncedFolderLightEnabled && fShowMediaScanNotifications) { @@ -681,18 +766,20 @@ private void setupDetailsCategory(PreferenceScreen preferenceScreen) { } 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_storage_path), + 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()); @@ -718,9 +805,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; @@ -776,10 +865,11 @@ private void setupLockPreference(PreferenceCategory preferenceCategoryDetails, } } - private void setupAutoUploadCategory(PreferenceScreen preferenceScreen) { - final PreferenceCategory preferenceCategorySyncedFolders = + private void setupAutoUploadCategory(int titleColor, PreferenceScreen preferenceScreen) { + 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); @@ -788,6 +878,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)); @@ -813,6 +905,57 @@ 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); + 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 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); + externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_MENU_ITEM_ID, -1); + startActivity(externalWebViewIntent); + } + private void enableLock(String lock) { pendingLock = LOCK_NONE; if (LOCK_PASSCODE.equals(lock)) { @@ -846,10 +989,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); @@ -862,52 +1015,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(); 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..9184eeab87a1 --- /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/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index bc5f9fc46acb..397e03af6f58 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 @@ -710,6 +711,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 @@ -1074,6 +1076,14 @@ Wir konnten die Datei auf dem Server nicht finden. Ein anderer Benutzer hat möglicherweise die Datei gelöscht. Ordnername Fehlgeschlagene lokale Dateien erneut hochladen + Kontoinformationen + Datenschutz + Datenschutz + Datenschutzbestimmungen + 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/setup.xml b/app/src/main/res/values/setup.xml index afed2dafe23a..11938891010a 100644 --- a/app/src/main/res/values/setup.xml +++ b/app/src/main/res/values/setup.xml @@ -99,6 +99,9 @@ "https://play.google.com/store/apps/details?id=com.nextcloud.client" https://nextcloud.com/install + + 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 3341599e790d..37845e66aac5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -117,6 +117,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 Help Recommend to a friend @@ -125,6 +126,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 @@ -644,6 +646,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. @@ -1217,6 +1220,7 @@ Storage permissions %1$s needs file management permissions to upload files. You can choose full access to all files, or read-only access to photos and videos. %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. @@ -1275,6 +1279,12 @@ Not possible without internet connection Scan page Done + Info + Data Privacy + Privacy Settings + Privacy Policy + 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 63a1902c5264..b36934671ee5 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -9,134 +9,190 @@ ~ SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) --> - + android:key="preference_screen"> + + + android:key="user_name" + android:layout="@layout/custom_preference_layout" + android:title="@string/prefs_category_account_info" /> + + + + - - - + android:key="storage_path" + android:layout="@layout/custom_preference_layout" + android:title="@string/prefs_storage_path" /> + + + + + - + android:id="@+id/synced_folders_configure_folders" + android:key="synced_folders_configure_folders" + android:layout="@layout/custom_preference_layout" + android:title="@string/synced_folders_configure_folders" /> + - - - + + + - + 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" /> + + + + + android:id="@+id/privacy_settings" + android:key="privacy_settings" + android:layout="@layout/custom_preference_layout" + android:title="@string/privacy_settings" /> + android:id="@+id/privacy_policy" + android:key="privacy_policy" + android:layout="@layout/custom_preference_layout" + android:title="@string/privacy_policy" /> + android:id="@+id/sourcecode" + android:key="sourcecode" + android:layout="@layout/custom_preference_layout" + android:title="@string/prefs_open_source" /> + + + + + - - - - - - - + android:key="imprint" + android:layout="@layout/custom_preference_layout" + android:title="@string/prefs_imprint" /> + + + + + - - + From 04275128e508f6f653d4826da1b3673fe43f8abb Mon Sep 17 00:00:00 2001 From: A117870935 Date: Fri, 19 Apr 2024 13:04:23 +0530 Subject: [PATCH 2/2] Added delete account menu under settings. NMC-3041 NMC-4681 -- enable logs for debug builds NMC-4850 -- use appcompat text appearance instead of material for separating headings NMC-4888 -- add option to save logs --- app/src/debug/res/values/log_config.xml | 11 ++ .../client/logger/ui/LogsActivity.kt | 1 + .../client/logger/ui/LogsSaveHandler.kt | 139 ++++++++++++++++++ .../client/logger/ui/LogsViewModel.kt | 7 + .../android/ui/AppVersionPreference.kt | 2 +- .../android/ui/activity/SettingsActivity.java | 37 +++-- .../res/layout/custom_preference_layout.xml | 2 +- app/src/main/res/menu/activity_logs.xml | 9 +- .../main/res/values-de/nmc_logs_strings.xml | 12 ++ app/src/main/res/values-de/strings.xml | 3 +- app/src/main/res/values/nmc_logs_strings.xml | 12 ++ app/src/main/res/values/setup.xml | 1 + app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/preferences.xml | 46 +++--- app/src/release/res/values/log_config.xml | 11 ++ 15 files changed, 256 insertions(+), 38 deletions(-) create mode 100644 app/src/debug/res/values/log_config.xml create mode 100644 app/src/main/java/com/nextcloud/client/logger/ui/LogsSaveHandler.kt create mode 100644 app/src/main/res/values-de/nmc_logs_strings.xml create mode 100644 app/src/main/res/values/nmc_logs_strings.xml create mode 100644 app/src/release/res/values/log_config.xml 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 c773758aa4b7..635fc89fa6d5 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 @@ -30,6 +30,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 @@ -46,6 +47,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/owncloud/android/ui/AppVersionPreference.kt b/app/src/main/java/com/owncloud/android/ui/AppVersionPreference.kt index ee0e0b43402a..1bcf709dce25 100644 --- a/app/src/main/java/com/owncloud/android/ui/AppVersionPreference.kt +++ b/app/src/main/java/com/owncloud/android/ui/AppVersionPreference.kt @@ -37,7 +37,7 @@ class AppVersionPreference : Preference { var temp: String try { val pkg = context.packageManager.getPackageInfo(context.packageName, 0) - temp = pkg.versionName + temp = pkg.versionName ?: "" } catch (e: PackageManager.NameNotFoundException) { temp = "" Log_OC.e(TAG, "Error while showing about dialog", e) 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 bb492040aaea..c5af20507d7a 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 @@ -213,7 +213,7 @@ public void onCreate(Bundle savedInstanceState) { // About // Not required in NMC - //setupAboutCategory(appVersion); + // setupAboutCategory(appVersion); // Data Privacy setupDataPrivacyCategory(titleColor); @@ -395,9 +395,10 @@ public void onBackPressed() { private void setupSyncCategory(int titleColor) { final PreferenceCategory preferenceCategorySync = (PreferenceCategory) findPreference("sync"); - + preferenceCategorySync.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_category_sync), + titleColor)); setupAutoUploadPreference(preferenceCategorySync, titleColor); - setupInternalTwoWaySyncPreference(titleColor); + // setupInternalTwoWaySyncPreference(titleColor); } /** @@ -444,7 +445,6 @@ private void setupDataPrivacyCategory(int titleColor) { getResources().getString(R.string.privacy_policy)); intent.putExtra(ExternalSiteWebView.EXTRA_URL, privacyUrl.toString()); intent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, false); - intent.putExtra(ExternalSiteWebView.EXTRA_MENU_ITEM_ID, -1); } startActivity(intent); @@ -471,7 +471,6 @@ private void setupDataPrivacyCategory(int titleColor) { 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); - intent.putExtra(ExternalSiteWebView.EXTRA_MENU_ITEM_ID, -1); startActivity(intent); return true; }); @@ -492,7 +491,7 @@ private void setupMoreCategory(int titleColor) { preferenceCategoryMore.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_category_more), titleColor)); - setupCalendarPreference(preferenceCategoryMore); + setupCalendarPreference(preferenceCategoryMore); setupBackupPreference(titleColor); @@ -690,7 +689,7 @@ private void setupAutoUploadPreference(PreferenceCategory preferenceCategoryMore private void setupInternalTwoWaySyncPreference(int titleColor) { Preference twoWaySync = findPreference("internal_two_way_sync"); - twoWaySync.setTitle(StringUtils.getColorSpan(getString(R.string.drawer_synced_folders), + twoWaySync.setTitle(StringUtils.getColorSpan(getString(R.string.internal_two_way_sync), titleColor)); twoWaySync.setOnPreferenceClickListener(preference -> { @@ -704,7 +703,7 @@ private void setupBackupPreference(int titleColor) { Preference pContactsBackup = findPreference("backup"); if (pContactsBackup != null) { boolean showCalendarBackup = getResources().getBoolean(R.bool.show_calendar_backup); - //NMC Customization + // NMC Customization pContactsBackup.setTitle(StringUtils.getColorSpan(getString(R.string.actionbar_contacts), titleColor)); pContactsBackup.setSummary(showCalendarBackup ? getString(R.string.prefs_daily_backup_summary) @@ -768,7 +767,7 @@ private void setupDetailsCategory(int titleColor, PreferenceScreen preferenceScr private void setupShowMediaScanNotifications(PreferenceCategory preferenceCategoryDetails, boolean fShowMediaScanNotifications, int titleColor) { SwitchPreference mShowMediaScanNotifications = (SwitchPreference) findPreference(PREFERENCE_SHOW_MEDIA_SCAN_NOTIFICATIONS); - mShowMediaScanNotifications.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_storage_path), + mShowMediaScanNotifications.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_enable_media_scan_notifications), titleColor)); if (fShowMediaScanNotifications) { preferenceCategoryDetails.removePreference(mShowMediaScanNotifications); @@ -866,7 +865,7 @@ private void setupLockPreference(PreferenceCategory preferenceCategoryDetails, } private void setupAutoUploadCategory(int titleColor, PreferenceScreen preferenceScreen) { - PreferenceCategory preferenceCategorySyncedFolders = + final PreferenceCategory preferenceCategorySyncedFolders = (PreferenceCategory) findPreference("synced_folders_category"); preferenceCategorySyncedFolders.setTitle(StringUtils.getColorSpan(getString(R.string.drawer_synced_folders), titleColor)); @@ -910,6 +909,7 @@ private void setUpServiceCategory(int titleColor) { preferenceCategoryService.setTitle(StringUtils.getColorSpan(getString(R.string.prefs_category_service), titleColor)); setupHelpPreference(titleColor); + setupDeleteAccountPreference(titleColor); setupImprintPreference(titleColor); } @@ -929,6 +929,22 @@ private void setupHelpPreference(int titleColor) { } } + 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) { @@ -952,7 +968,6 @@ private void openLinkInWebView(String url, @StringRes int title) { getResources().getString(title)); externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_URL, url); externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, false); - externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_MENU_ITEM_ID, -1); startActivity(externalWebViewIntent); } diff --git a/app/src/main/res/layout/custom_preference_layout.xml b/app/src/main/res/layout/custom_preference_layout.xml index 9184eeab87a1..1f6991c06e56 100644 --- a/app/src/main/res/layout/custom_preference_layout.xml +++ b/app/src/main/res/layout/custom_preference_layout.xml @@ -44,7 +44,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:maxLines="3" - android:textAppearance="?attr/textAppearanceListItem" + android:textAppearance="@style/TextAppearance.AppCompat.Subhead" android:ellipsize="end" /> 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 397e03af6f58..ef4e9e77e0ac 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -1078,8 +1078,9 @@ Fehlgeschlagene lokale Dateien erneut hochladen Kontoinformationen Datenschutz - Datenschutz + Datenschutz-Einstellungen Datenschutzbestimmungen + Konto endgültig löschen Verwendete OpenSource Software Info Bedienung 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 11938891010a..4c492d82600a 100644 --- a/app/src/main/res/values/setup.xml +++ b/app/src/main/res/values/setup.xml @@ -99,6 +99,7 @@ "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 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 37845e66aac5..f021f09ad0e9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1283,6 +1283,7 @@ Data Privacy Privacy Settings Privacy Policy + Delete account permanently Used OpenSource Software Service Generating PDF… diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index b36934671ee5..2544e448a08b 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -1,5 +1,4 @@ - - + android:summary="@string/prefs_data_storage_location_summary" + android:title="@string/prefs_data_storage_location" /> - + android:summary="@string/prefs_show_ecosystem_apps_summary" + android:title="@string/prefs_show_ecosystem_apps" /> + android:key="sync" + android:title="@string/prefs_category_sync"> + android:summary="@string/prefs_sycned_folders_summary" + android:title="@string/drawer_synced_folders" /> - + + android:key="more" + android:title="@string/prefs_category_more"> + + + + + false + \ No newline at end of file