From e1230c74f5c7a539237d8ca845f505d9701ce54b Mon Sep 17 00:00:00 2001 From: A117870935 Date: Wed, 7 Jul 2021 17:21:49 +0530 Subject: [PATCH] Implemented privacy settings --- .../nmc/android/ui/ClickableSpanTestHelper.kt | 115 ++++++++++++++++ .../ui/LoginPrivacySettingsActivityIT.kt | 109 +++++++++++++++ .../android/ui/PrivacySettingsActivityIT.kt | 93 +++++++++++++ app/src/main/AndroidManifest.xml | 9 ++ .../nextcloud/client/di/ComponentsModule.java | 8 ++ .../client/preferences/AppPreferences.java | 18 +++ .../preferences/AppPreferencesImpl.java | 25 ++++ .../com/nmc/android/ui/LauncherActivity.kt | 40 ++++-- .../ui/LoginPrivacySettingsActivity.kt | 107 +++++++++++++++ .../nmc/android/ui/PrivacySettingsActivity.kt | 87 ++++++++++++ .../android/ui/PrivacySettingsInterface.kt | 13 ++ .../ui/PrivacySettingsInterfaceImpl.kt | 14 ++ .../com/nmc/android/ui/PrivacyUserAction.kt | 9 ++ .../nmc/android/utils/CheckableThemeUtils.kt | 86 ++++++++++++ .../java/com/nmc/android/utils/Extensions.kt | 42 ++++++ .../authentication/AuthenticatorActivity.java | 9 +- .../android/ui/activity/SettingsActivity.java | 15 ++ .../main/res/drawable/ic_privacy_settings.xml | 12 ++ .../activity_login_privacy_settings.xml | 84 ++++++++++++ .../res/layout/activity_privacy_settings.xml | 128 ++++++++++++++++++ app/src/main/res/values-de/strings.xml | 29 ++++ app/src/main/res/values-night/colors.xml | 66 +++++++++ app/src/main/res/values/colors.xml | 91 +++++++++++++ app/src/main/res/values/dimens.xml | 31 +++++ app/src/main/res/values/setup.xml | 2 +- app/src/main/res/values/strings.xml | 27 ++++ 26 files changed, 1252 insertions(+), 17 deletions(-) create mode 100644 app/src/androidTest/java/com/nmc/android/ui/ClickableSpanTestHelper.kt create mode 100644 app/src/androidTest/java/com/nmc/android/ui/LoginPrivacySettingsActivityIT.kt create mode 100644 app/src/androidTest/java/com/nmc/android/ui/PrivacySettingsActivityIT.kt create mode 100644 app/src/main/java/com/nmc/android/ui/LoginPrivacySettingsActivity.kt create mode 100644 app/src/main/java/com/nmc/android/ui/PrivacySettingsActivity.kt create mode 100644 app/src/main/java/com/nmc/android/ui/PrivacySettingsInterface.kt create mode 100644 app/src/main/java/com/nmc/android/ui/PrivacySettingsInterfaceImpl.kt create mode 100644 app/src/main/java/com/nmc/android/ui/PrivacyUserAction.kt create mode 100644 app/src/main/java/com/nmc/android/utils/CheckableThemeUtils.kt create mode 100644 app/src/main/java/com/nmc/android/utils/Extensions.kt create mode 100644 app/src/main/res/drawable/ic_privacy_settings.xml create mode 100644 app/src/main/res/layout/activity_login_privacy_settings.xml create mode 100644 app/src/main/res/layout/activity_privacy_settings.xml create mode 100644 app/src/main/res/values/dimens.xml diff --git a/app/src/androidTest/java/com/nmc/android/ui/ClickableSpanTestHelper.kt b/app/src/androidTest/java/com/nmc/android/ui/ClickableSpanTestHelper.kt new file mode 100644 index 000000000000..59c91cd42dbf --- /dev/null +++ b/app/src/androidTest/java/com/nmc/android/ui/ClickableSpanTestHelper.kt @@ -0,0 +1,115 @@ +package com.nmc.android.ui + +import android.text.Spannable +import android.text.style.ClickableSpan +import android.view.View +import android.widget.TextView +import androidx.test.espresso.UiController +import androidx.test.espresso.ViewAction +import androidx.test.espresso.ViewInteraction +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.BoundedMatcher +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import org.hamcrest.Description +import org.hamcrest.Matcher + +object ClickableSpanTestHelper { + + /** + * method to get clickable span form a text view + * example: val clickableSpan = getClickableSpan("Link text", onView(withId(R.id.text_id))) + */ + fun getClickableSpan(spanText: String, matcher: ViewInteraction?): ClickableSpan? { + val clickableSpans = arrayOf(null) + + // Get the SpannableString from the TextView + matcher?.check(matches(isDisplayed())) + matcher?.perform(object : ViewAction { + override fun getConstraints(): Matcher { + return isAssignableFrom(TextView::class.java) + } + + override fun getDescription(): String { + return "get text from TextView" + } + + override fun perform(uiController: UiController, view: View) { + val textView = view as TextView + val text = textView.text + if (text is Spannable) { + val spans = text.getSpans( + 0, text.length, + ClickableSpan::class.java + ) + for (span in spans) { + val start = text.getSpanStart(span) + val end = text.getSpanEnd(span) + val spanString = text.subSequence(start, end).toString() + if (spanString == spanText) { + clickableSpans[0] = span + return + } + } + } + throw java.lang.RuntimeException("ClickableSpan not found") + } + }) + return clickableSpans[0] + } + + /** + * perform click on the spanned string + * @link getClickableSpan() method to get clickable span + */ + fun performClickSpan(clickableSpan: ClickableSpan?): ViewAction { + return object : ViewAction { + override fun getConstraints(): Matcher { + return ViewMatchers.isAssignableFrom(TextView::class.java) + } + + override fun getDescription(): String { + return "clicking on a span" + } + + override fun perform(uiController: UiController, view: View) { + val textView = view as TextView + val spannable = textView.text as Spannable + val spans = spannable.getSpans( + 0, spannable.length, + ClickableSpan::class.java + ) + for (span in spans) { + if (span == clickableSpan) { + span.onClick(textView) + return + } + } + throw RuntimeException("ClickableSpan not found") + } + } + } + + fun verifyClickSpan(clickableSpan: ClickableSpan?): Matcher { + return object : BoundedMatcher(TextView::class.java) { + override fun describeTo(description: Description) { + description.appendText("clickable span") + } + + override fun matchesSafely(textView: TextView): Boolean { + val spannable = textView.text as Spannable + val spans = spannable.getSpans( + 0, spannable.length, + ClickableSpan::class.java + ) + for (span in spans) { + if (span == clickableSpan) { + return true + } + } + return false + } + } + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/nmc/android/ui/LoginPrivacySettingsActivityIT.kt b/app/src/androidTest/java/com/nmc/android/ui/LoginPrivacySettingsActivityIT.kt new file mode 100644 index 000000000000..3232d588769e --- /dev/null +++ b/app/src/androidTest/java/com/nmc/android/ui/LoginPrivacySettingsActivityIT.kt @@ -0,0 +1,109 @@ +package com.nmc.android.ui + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.Espresso.pressBack +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.intent.Intents +import androidx.test.espresso.intent.Intents.intended +import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent +import androidx.test.espresso.matcher.ViewMatchers.isClickable +import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.ext.junit.rules.ActivityScenarioRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.nextcloud.client.preferences.AppPreferencesImpl +import com.nmc.android.ui.ClickableSpanTestHelper.getClickableSpan +import com.owncloud.android.AbstractIT +import com.owncloud.android.R +import com.owncloud.android.ui.activity.ExternalSiteWebView +import com.owncloud.android.ui.activity.FileDisplayActivity +import org.junit.Assert.* +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class LoginPrivacySettingsActivityIT : AbstractIT() { + + @get:Rule + val activityRule = ActivityScenarioRule(LoginPrivacySettingsActivity::class.java) + + @Test + fun verifyNothingHappensOnBackPress() { + pressBack() + shortSleep() + + //check any one view to check the activity is not destroyed + onView(withId(R.id.tv_privacy_setting_title)).check(matches(isCompletelyDisplayed())) + } + + @Test + fun verifyUIElements() { + onView(withId(R.id.ic_privacy)).check(matches(isCompletelyDisplayed())) + + onView(withId(R.id.tv_privacy_setting_title)).check(matches(isCompletelyDisplayed())) + + onView(withId(R.id.tv_login_privacy_intro_text)).check(matches(isCompletelyDisplayed())) + + onView(withId(R.id.privacy_accept_btn)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.privacy_accept_btn)).check(matches(isClickable())) + } + + @Test + fun verifyAcceptButtonRedirection() { + Intents.init() + onView(withId(R.id.privacy_accept_btn)).perform(click()) + + //check if the policy action saved correct --> 2 for Accept action + assertEquals(2, AppPreferencesImpl.fromContext(targetContext).privacyPolicyAction) + + intended(hasComponent(FileDisplayActivity::class.java.canonicalName)) + Intents.release() + } + + @Test + fun verifySettingsTextClick() { + Intents.init() + val settingsClickableSpan = getClickableSpan("Settings", onView(withId(R.id.tv_login_privacy_intro_text))) + onView(withId(R.id.tv_login_privacy_intro_text)).perform( + ClickableSpanTestHelper.performClickSpan( + settingsClickableSpan + ) + ) + intended(hasComponent(PrivacySettingsActivity::class.java.canonicalName)) + Intents.release() + } + + @Test + fun verifyPrivacyPolicyTextClick() { + Intents.init() + val privacyPolicyClickableSpan = + getClickableSpan("Privacy Policy", onView(withId(R.id.tv_login_privacy_intro_text))) + onView(withId(R.id.tv_login_privacy_intro_text)).perform( + ClickableSpanTestHelper.performClickSpan( + privacyPolicyClickableSpan + ) + ) + intended(hasComponent(ExternalSiteWebView::class.java.canonicalName)) + Intents.release() + } + + @Test + fun verifyRejectTextClick() { + Intents.init() + val rejectClickableSpan = + getClickableSpan("reject", onView(withId(R.id.tv_login_privacy_intro_text))) + onView(withId(R.id.tv_login_privacy_intro_text)).perform( + ClickableSpanTestHelper.performClickSpan( + rejectClickableSpan + ) + ) + + //check if the policy action saved correct --> 1 for Reject action + assertEquals(1, AppPreferencesImpl.fromContext(targetContext).privacyPolicyAction) + + intended(hasComponent(FileDisplayActivity::class.java.canonicalName)) + Intents.release() + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/nmc/android/ui/PrivacySettingsActivityIT.kt b/app/src/androidTest/java/com/nmc/android/ui/PrivacySettingsActivityIT.kt new file mode 100644 index 000000000000..5de5d075991b --- /dev/null +++ b/app/src/androidTest/java/com/nmc/android/ui/PrivacySettingsActivityIT.kt @@ -0,0 +1,93 @@ +package com.nmc.android.ui + +import android.content.Intent +import androidx.test.core.app.ActivityScenario +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isChecked +import androidx.test.espresso.matcher.ViewMatchers.isClickable +import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isEnabled +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.owncloud.android.AbstractIT +import com.owncloud.android.R +import org.hamcrest.CoreMatchers.not +import org.junit.After +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class PrivacySettingsActivityIT : AbstractIT() { + + private fun getIntent(showSettingsButton: Boolean): Intent = + Intent(targetContext, PrivacySettingsActivity::class.java) + .putExtra("show_settings_button", showSettingsButton) + + lateinit var activityRule: ActivityScenario + + @Before + fun setUp() { + activityRule = launchActivity(getIntent(false)) + } + + @Test + fun verifyUIElements() { + onView(withId(R.id.tv_privacy_intro_text)).check(matches(isCompletelyDisplayed())) + + onView(withId(R.id.switch_data_collection)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.switch_data_collection)).check(matches(not(isEnabled()))) + onView(withId(R.id.switch_data_collection)).check(matches(isChecked())) + + onView(withId(R.id.switch_data_analysis)).check(matches(isCompletelyDisplayed())) + onView(withId(R.id.switch_data_analysis)).check(matches(isEnabled())) + //by-default the analysis switch will be checked as per #AppPreferences.isDataAnalysisEnabled will return true + onView(withId(R.id.switch_data_analysis)).check(matches(isChecked())) + onView(withId(R.id.switch_data_analysis)).check(matches(isClickable())) + + onView(withId(R.id.privacy_save_settings_btn)).check(matches(not(isDisplayed()))) + } + + @Test + fun verifyDataCollectionSwitchToggle() { + //since this button is disabled performing click operation should do nothing + //and switch will be in checked state only + onView(withId(R.id.switch_data_collection)).perform(click()) + onView(withId(R.id.switch_data_collection)).check(matches(isChecked())) + + onView(withId(R.id.switch_data_collection)).perform(click()) + onView(withId(R.id.switch_data_collection)).check(matches(isChecked())) + } + + @Test + fun verifyDataAnalysisSwitchToggle() { + onView(withId(R.id.switch_data_analysis)).perform(click()) + onView(withId(R.id.switch_data_analysis)).check(matches(not(isChecked()))) + + onView(withId(R.id.switch_data_analysis)).perform(click()) + onView(withId(R.id.switch_data_analysis)).check(matches(isChecked())) + } + + @Test + fun verifySaveSettingsButton() { + //button not shown on the basis of extras passed to intent + onView(withId(R.id.privacy_save_settings_btn)).check(matches(not(isDisplayed()))) + //close the activity already open + activityRule.close() + + //launch activity with extras as true + activityRule = launchActivity(getIntent(true)) + //button will be shown if extras is true + onView(withId(R.id.privacy_save_settings_btn)).check(matches(isDisplayed())) + } + + @After + fun tearDown() { + activityRule.close() + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cb3523a3dc90..e37c9ad61583 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -483,6 +483,15 @@ android:name=".ui.preview.PreviewBitmapActivity" android:exported="false" android:theme="@style/Theme.ownCloud.OverlayGrey" /> + + + + preferences.setDataAnalysis(isChecked) + } + binding.privacySaveSettingsBtn.setOnClickListener { + //finish the activity as we are changing the setting on switch check change + finish() + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + android.R.id.home -> { + finish() + } + } + return super.onOptionsItemSelected(item) + } +} 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/nmc/android/ui/PrivacySettingsInterfaceImpl.kt b/app/src/main/java/com/nmc/android/ui/PrivacySettingsInterfaceImpl.kt new file mode 100644 index 000000000000..e087bc888e6f --- /dev/null +++ b/app/src/main/java/com/nmc/android/ui/PrivacySettingsInterfaceImpl.kt @@ -0,0 +1,14 @@ +package com.nmc.android.ui + +import android.content.Context + +/** + * interface impl to launch PrivacySettings Activity + * this class will have the implementation for it since it has the PrivacySettingsActivity in place + * calling of this method will be done from nmc/1921-settings + */ +class PrivacySettingsInterfaceImpl : PrivacySettingsInterface { + override fun openPrivacySettingsActivity(context: Context) { + PrivacySettingsActivity.openPrivacySettingsActivity(context, false) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nmc/android/ui/PrivacyUserAction.kt b/app/src/main/java/com/nmc/android/ui/PrivacyUserAction.kt new file mode 100644 index 000000000000..6dca412cc29b --- /dev/null +++ b/app/src/main/java/com/nmc/android/ui/PrivacyUserAction.kt @@ -0,0 +1,9 @@ +package com.nmc.android.ui + +//class to handle user action for privacy +object PrivacyUserAction { + //privacy user action to maintain the state of privacy policy + const val NO_ACTION = 0 //user has taken no action + const val REJECT_ACTION = 1 //user rejected the privacy policy + const val ACCEPT_ACTION = 2 //user has accepted the privacy policy +} \ No newline at end of file diff --git a/app/src/main/java/com/nmc/android/utils/CheckableThemeUtils.kt b/app/src/main/java/com/nmc/android/utils/CheckableThemeUtils.kt new file mode 100644 index 000000000000..7c86042db7cb --- /dev/null +++ b/app/src/main/java/com/nmc/android/utils/CheckableThemeUtils.kt @@ -0,0 +1,86 @@ +package com.nmc.android.utils + +import android.content.res.ColorStateList +import androidx.appcompat.widget.AppCompatCheckBox +import androidx.appcompat.widget.SwitchCompat +import androidx.core.graphics.drawable.DrawableCompat +import androidx.core.widget.CompoundButtonCompat +import com.owncloud.android.MainApp +import com.owncloud.android.R + +object CheckableThemeUtils { + @JvmStatic + fun tintCheckbox(vararg checkBoxes: AppCompatCheckBox) { + for (checkBox in checkBoxes) { + val checkEnabled = MainApp.getAppContext().resources.getColor(R.color.checkbox_checked_enabled) + val checkDisabled = MainApp.getAppContext().resources.getColor(R.color.checkbox_checked_disabled) + val uncheckEnabled = MainApp.getAppContext().resources.getColor(R.color.checkbox_unchecked_enabled) + val uncheckDisabled = MainApp.getAppContext().resources.getColor(R.color.checkbox_unchecked_disabled) + + val states = arrayOf( + intArrayOf(android.R.attr.state_enabled, android.R.attr.state_checked), + intArrayOf(-android.R.attr.state_enabled, android.R.attr.state_checked), + intArrayOf(android.R.attr.state_enabled, -android.R.attr.state_checked), + intArrayOf(-android.R.attr.state_enabled, -android.R.attr.state_checked) + ) + val colors = intArrayOf( + checkEnabled, + checkDisabled, + uncheckEnabled, + uncheckDisabled + ) + val checkColorStateList = ColorStateList(states, colors) + CompoundButtonCompat.setButtonTintList(checkBox, checkColorStateList) + } + } + + @JvmStatic + @JvmOverloads + fun tintSwitch(switchView: SwitchCompat, color: Int = 0, colorText: Boolean = false) { + if (colorText) { + switchView.setTextColor(color) + } + val thumbColorCheckedEnabled = MainApp.getAppContext().resources.getColor(R.color.switch_thumb_checked_enabled) + val thumbColorUncheckedEnabled = + MainApp.getAppContext().resources.getColor(R.color.switch_thumb_unchecked_enabled) + val thumbColorCheckedDisabled = + MainApp.getAppContext().resources.getColor(R.color.switch_thumb_checked_disabled) + val thumbColorUncheckedDisabled = + MainApp.getAppContext().resources.getColor(R.color.switch_thumb_unchecked_disabled) + + val states = arrayOf( + intArrayOf(android.R.attr.state_enabled, android.R.attr.state_checked), + intArrayOf(-android.R.attr.state_enabled, android.R.attr.state_checked), + intArrayOf(android.R.attr.state_enabled, -android.R.attr.state_checked), + intArrayOf(-android.R.attr.state_enabled, -android.R.attr.state_checked) + ) + val thumbColors = intArrayOf( + thumbColorCheckedEnabled, + thumbColorCheckedDisabled, + thumbColorUncheckedEnabled, + thumbColorUncheckedDisabled + ) + val thumbColorStateList = ColorStateList(states, thumbColors) + val trackColorCheckedEnabled = MainApp.getAppContext().resources.getColor(R.color.switch_track_checked_enabled) + val trackColorUncheckedEnabled = + MainApp.getAppContext().resources.getColor(R.color.switch_track_unchecked_enabled) + val trackColorCheckedDisabled = + MainApp.getAppContext().resources.getColor(R.color.switch_track_checked_disabled) + val trackColorUncheckedDisabled = + MainApp.getAppContext().resources.getColor(R.color.switch_track_unchecked_disabled) + + val trackColors = intArrayOf( + trackColorCheckedEnabled, + trackColorCheckedDisabled, + trackColorUncheckedEnabled, + trackColorUncheckedDisabled + ) + val trackColorStateList = ColorStateList(states, trackColors) + + // setting the thumb color + DrawableCompat.setTintList(switchView.thumbDrawable, thumbColorStateList) + + // setting the track color + DrawableCompat.setTintList(switchView.trackDrawable, trackColorStateList) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nmc/android/utils/Extensions.kt b/app/src/main/java/com/nmc/android/utils/Extensions.kt new file mode 100644 index 000000000000..035690e7af04 --- /dev/null +++ b/app/src/main/java/com/nmc/android/utils/Extensions.kt @@ -0,0 +1,42 @@ +package com.nmc.android.utils + +import android.text.Selection +import android.text.Spannable +import android.text.SpannableString +import android.text.Spanned +import android.text.TextPaint +import android.text.method.LinkMovementMethod +import android.text.style.ClickableSpan +import android.view.View +import android.widget.TextView + +fun TextView.makeLinks(vararg links: Pair) { + val spannableString = SpannableString(this.text) + var startIndexOfLink = -1 + for (link in links) { + val clickableSpan = object : ClickableSpan() { + override fun updateDrawState(textPaint: TextPaint) { + // use this to change the link color + textPaint.color = textPaint.linkColor + // toggle below value to enable/disable + // the underline shown below the clickable text + //textPaint.isUnderlineText = true + } + + override fun onClick(view: View) { + Selection.setSelection((view as TextView).text as Spannable, 0) + view.invalidate() + link.second.onClick(view) + } + } + startIndexOfLink = this.text.toString().indexOf(link.first, startIndexOfLink + 1) + spannableString.setSpan( + clickableSpan, startIndexOfLink, startIndexOfLink + link.first.length, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + } + this.movementMethod = + LinkMovementMethod.getInstance() // without LinkMovementMethod, link can not click + this.setText(spannableString, TextView.BufferType.SPANNABLE) +} + diff --git a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java index 777b3214a323..f0f6879ed344 100644 --- a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java +++ b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java @@ -83,6 +83,7 @@ import com.nextcloud.client.device.DeviceInfo; import com.nextcloud.client.di.Injectable; import com.nextcloud.client.onboarding.FirstRunActivity; +import com.nmc.android.ui.LoginPrivacySettingsActivity; import com.nextcloud.client.onboarding.OnboardingService; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.java.util.Optional; @@ -118,7 +119,6 @@ import com.owncloud.android.services.OperationsService; import com.owncloud.android.services.OperationsService.OperationsServiceBinder; import com.owncloud.android.ui.NextcloudWebViewClient; -import com.owncloud.android.ui.activity.FileDisplayActivity; import com.owncloud.android.ui.dialog.IndeterminateProgressDialog; import com.owncloud.android.ui.dialog.SslUntrustedCertDialog; import com.owncloud.android.ui.dialog.SslUntrustedCertDialog.OnSslUntrustedCertListener; @@ -1208,10 +1208,9 @@ private void endSuccess() { if (onlyAdd) { finish(); } else { - Intent i = new Intent(this, FileDisplayActivity.class); - i.setAction(FileDisplayActivity.RESTART); - i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(i); + //login privacy settings to accept/reject by the user after login + LoginPrivacySettingsActivity.openPrivacySettingsActivity(this); + finish(); } } 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 3971756f67b7..0e45401c4e0e 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 @@ -57,11 +57,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.owncloud.android.BuildConfig; import com.owncloud.android.MainApp; +import com.nmc.android.ui.PrivacySettingsInterfaceImpl; import com.owncloud.android.R; import com.owncloud.android.authentication.AuthenticatorActivity; import com.owncloud.android.datamodel.ArbitraryDataProvider; @@ -151,6 +153,16 @@ 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 @@ -163,6 +175,9 @@ public void onCreate(Bundle savedInstanceState) { setupActionBar(); + //NMC customization will be initialised in nmc/1878-privacy + privacySettingsInterface = new PrivacySettingsInterfaceImpl(); + // Register context menu for list of preferences. registerForContextMenu(getListView()); diff --git a/app/src/main/res/drawable/ic_privacy_settings.xml b/app/src/main/res/drawable/ic_privacy_settings.xml new file mode 100644 index 000000000000..c3bb3fb01899 --- /dev/null +++ b/app/src/main/res/drawable/ic_privacy_settings.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/layout/activity_login_privacy_settings.xml b/app/src/main/res/layout/activity_login_privacy_settings.xml new file mode 100644 index 000000000000..3a51e9457b3d --- /dev/null +++ b/app/src/main/res/layout/activity_login_privacy_settings.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_privacy_settings.xml b/app/src/main/res/layout/activity_privacy_settings.xml new file mode 100644 index 000000000000..1cf55ac1493a --- /dev/null +++ b/app/src/main/res/layout/activity_privacy_settings.xml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 6dd98744145a..95d7e9cee78d 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -596,6 +596,27 @@ Dunkel Hell Systemvorgaben verwenden + + + Zur Optimierung unserer App erfassen wir anonymisierte Daten. Hierzu nutzen wir Software Lösungen verschiedener Partner. Wir möchten Ihnen volle Transparenz und Entscheidungsgewalt über die Verarbeitung und Erfassung Ihrer anonymisierten Nutzungsdaten geben. Ihre Einstellungen können Sie auch später jederzeit in den Einstellungen unter Datenschutz ändern. Bitte beachten Sie jedoch, dass die Datenerfassungen einen erheblichen Beitrag zur Optimierung dieser App leisten und Sie diese Optimierungen durch die Unterbindung der Datenübermittlung verhindern. + Erfoderliche Datenerfassung + Die Erfassung dieser Daten ist notwendig, um wesentliche Funktionen der App nutzen zu können. + Analyse-Datenerfassung zur bedarfsgerechten Gestaltung + Diese Daten helfen uns, die App Nutzung für Sie zu optimieren und Systemabstürze und Fehler schneller zu identifizieren. + Diese App verwendet Cookies und ähnliche Technologien (Tools). + Mit einem Klick auf Zustimmen akzeptieren Sie die Verarbeitung und auch die Weitergabe Ihrer Daten an + Drittanbieter. Die Daten werden für Analysen, Retargeting und zur Ausspielung von personalisierten Inhalten + und Werbung auf Seiten der Telekom, sowie auf Drittanbieterseiten genutzt. Weitere Informationen, auch zur + Datenverarbeitung durch Drittanbieter, finden Sie in den Einstellungen sowie in unseren %s. Sie können die + Verwendung der Tools %s oder jederzeit über ihre %s anpassen. + Datenschutz-Einstellungen + Akzeptieren + Einstellungen speichern + Datenschutzhinweise + ablehnen + Einstellungen + + Bildvorschau Keine lokale Datei für die Vorschau vorhanden Bild kann nicht angezeigt werden @@ -855,6 +876,14 @@ Diese Datei kann nicht hochgeladen werden Keine Datei zum Hochladen Ordnername + 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 08bf64d552ad..88a29668c4cd 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -48,4 +48,70 @@ #1E1E1E @android:color/white + + + #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_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 f844b3156f33..1ddf1a93ca08 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -81,4 +81,95 @@ @android:color/white #666666 #A5A5A5 + + + #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 + @color/grey_0 + @color/grey_0 + #FFFFFF + @color/grey_30 + @color/grey_0 + @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 1dc7a9fd8786..43d3c39cb7d5 100644 --- a/app/src/main/res/values/setup.xml +++ b/app/src/main/res/values/setup.xml @@ -74,7 +74,7 @@ true false true - https://github.com/nextcloud/android + https://static.magentacloud.de/licences/android.html true https://www.gnu.org/licenses/gpl-2.0.html diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8af02a33a2e5..7cc5b633ff35 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -194,6 +194,27 @@ Created %1$d fresh UID Created %1$d fresh UIDs + + To optimize your app, we collect anonymous data. For this we use + software solutions of different partners. We would like to give you full transparency and decision-making power over the processing and collection of your anonymized usage data. You can also change your settings at any time later in the app settings under data protection. Please note, however, that data collection makes a considerable contribution to the optimization of this app and you prevent this optimization by preventing data transmission. + Required data collection + The collection of this data is necessary to be able to use essential + functions of the app. + Analysis-data acquisition for the design + This data helps us to optimize the app usage for you and to identify system crashes and errors more quickly. + This app uses Cookies and similar technologies (tools). By + clicking Accept, you accept the processing and also the Transfer of your data to third parties. The data will + be used for Analysis, retargeting and to Display personalized Content and Advertising on sites and + third-party sites. You can find further information, including Information on data processing by third-party + Providers, in the Settings and in our %s. You can %s the use of the Tools or customize them at any time in the + %s. + Privacy Settings + Accept + Save Settings + Privacy Policy + reject + Settings + Processed %d entry. Processed %d entries. @@ -1071,6 +1092,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