From 47a2ce2d4243f5f961f00b9ac8a997187e9566bf Mon Sep 17 00:00:00 2001 From: Lukasz Macionczyk Date: Tue, 12 Mar 2024 22:28:57 +0100 Subject: [PATCH] Subscriptions design and copy changes (#4288) Task/Issue URL: https://app.asana.com/0/488551667048375/1206362079735328/f ### Description See task. --- .../impl/settings/views/ItrSettingView.kt | 4 +- .../settings/views/ItrSettingViewModel.kt | 19 +--- .../impl/settings/views/PirSettingView.kt | 4 +- .../settings/views/PirSettingViewModel.kt | 19 +--- .../impl/settings/views/ProSettingView.kt | 32 ++++--- .../settings/views/ProSettingViewModel.kt | 25 ++---- .../impl/ui/AddDeviceActivity.kt | 14 +-- .../impl/ui/RestoreSubscriptionActivity.kt | 16 ++-- .../impl/ui/RestoreSubscriptionViewModel.kt | 4 +- .../impl/ui/SubscriptionSettingsActivity.kt | 7 +- .../src/main/res/drawable/ic_privacy_pro.xml | 55 ++++++++++++ .../main/res/layout/activity_add_device.xml | 38 +------- .../layout/activity_restore_subscription.xml | 51 ++++------- .../layout/activity_subscription_settings.xml | 83 ++++++++++++------ .../src/main/res/layout/card_manage_email.xml | 86 +++++++++++++++++++ .../src/main/res/layout/view_settings.xml | 74 +++++++++++++--- .../src/main/res/values/donottranslate.xml | 32 +++---- .../settings/views/ProSettingViewModelTest.kt | 10 +++ .../ui/RestoreSubscriptionViewModelTest.kt | 1 - 19 files changed, 372 insertions(+), 202 deletions(-) create mode 100644 subscriptions/subscriptions-impl/src/main/res/drawable/ic_privacy_pro.xml create mode 100644 subscriptions/subscriptions-impl/src/main/res/layout/card_manage_email.xml diff --git a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ItrSettingView.kt b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ItrSettingView.kt index bf74dbbbb77a..32cc7d68199d 100644 --- a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ItrSettingView.kt +++ b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ItrSettingView.kt @@ -28,13 +28,13 @@ import com.duckduckgo.common.ui.view.gone import com.duckduckgo.common.ui.view.show import com.duckduckgo.common.ui.viewbinding.viewBinding import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.navigation.api.GlobalActivityStarter import com.duckduckgo.subscriptions.impl.SubscriptionsConstants import com.duckduckgo.subscriptions.impl.databinding.ViewItrSettingsBinding import com.duckduckgo.subscriptions.impl.settings.views.ItrSettingViewModel.Command import com.duckduckgo.subscriptions.impl.settings.views.ItrSettingViewModel.Command.OpenItr -import com.duckduckgo.subscriptions.impl.settings.views.ItrSettingViewModel.Factory import com.duckduckgo.subscriptions.impl.settings.views.ItrSettingViewModel.ViewState import com.duckduckgo.subscriptions.impl.ui.SubscriptionsWebViewActivityWithParams import dagger.android.support.AndroidSupportInjection @@ -54,7 +54,7 @@ class ItrSettingView @JvmOverloads constructor( ) : FrameLayout(context, attrs, defStyle) { @Inject - lateinit var viewModelFactory: Factory + lateinit var viewModelFactory: ViewViewModelFactory @Inject lateinit var globalActivityStarter: GlobalActivityStarter diff --git a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ItrSettingViewModel.kt b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ItrSettingViewModel.kt index 0788b9c872bf..9e146ce150f1 100644 --- a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ItrSettingViewModel.kt +++ b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ItrSettingViewModel.kt @@ -20,9 +20,10 @@ import android.annotation.SuppressLint import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import com.duckduckgo.anvil.annotations.ContributesViewModel import com.duckduckgo.common.utils.DispatcherProvider +import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.subscriptions.api.Product.ITR import com.duckduckgo.subscriptions.api.Subscriptions import com.duckduckgo.subscriptions.api.Subscriptions.EntitlementStatus.Found @@ -30,7 +31,6 @@ import com.duckduckgo.subscriptions.api.Subscriptions.EntitlementStatus.NotFound import com.duckduckgo.subscriptions.impl.pixels.SubscriptionPixelSender import com.duckduckgo.subscriptions.impl.settings.views.ItrSettingViewModel.Command.OpenItr import javax.inject.Inject -import javax.inject.Provider import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow @@ -40,6 +40,7 @@ import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch @SuppressLint("NoLifecycleObserver") // we don't observe app lifecycle +@ContributesViewModel(ViewScope::class) class ItrSettingViewModel @Inject constructor( private val subscriptions: Subscriptions, private val dispatcherProvider: DispatcherProvider, @@ -78,18 +79,4 @@ class ItrSettingViewModel @Inject constructor( command.send(newCommand) } } - - @Suppress("UNCHECKED_CAST") - class Factory @Inject constructor( - private val itrSettingViewModel: Provider, - ) : ViewModelProvider.NewInstanceFactory() { - override fun create(modelClass: Class): T { - return with(modelClass) { - when { - isAssignableFrom(ItrSettingViewModel::class.java) -> itrSettingViewModel.get() - else -> throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}") - } - } as T - } - } } diff --git a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/PirSettingView.kt b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/PirSettingView.kt index ff80e95ca37c..d8142b428a44 100644 --- a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/PirSettingView.kt +++ b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/PirSettingView.kt @@ -28,13 +28,13 @@ import com.duckduckgo.common.ui.view.gone import com.duckduckgo.common.ui.view.show import com.duckduckgo.common.ui.viewbinding.viewBinding import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.navigation.api.GlobalActivityStarter import com.duckduckgo.subscriptions.impl.databinding.ViewPirSettingsBinding import com.duckduckgo.subscriptions.impl.pir.PirActivity.Companion.PirScreenWithEmptyParams import com.duckduckgo.subscriptions.impl.settings.views.PirSettingViewModel.Command import com.duckduckgo.subscriptions.impl.settings.views.PirSettingViewModel.Command.OpenPir -import com.duckduckgo.subscriptions.impl.settings.views.PirSettingViewModel.Factory import com.duckduckgo.subscriptions.impl.settings.views.PirSettingViewModel.ViewState import dagger.android.support.AndroidSupportInjection import javax.inject.Inject @@ -53,7 +53,7 @@ class PirSettingView @JvmOverloads constructor( ) : FrameLayout(context, attrs, defStyle) { @Inject - lateinit var viewModelFactory: Factory + lateinit var viewModelFactory: ViewViewModelFactory @Inject lateinit var globalActivityStarter: GlobalActivityStarter diff --git a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/PirSettingViewModel.kt b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/PirSettingViewModel.kt index 52c6f88def75..a48449045458 100644 --- a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/PirSettingViewModel.kt +++ b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/PirSettingViewModel.kt @@ -20,9 +20,10 @@ import android.annotation.SuppressLint import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import com.duckduckgo.anvil.annotations.ContributesViewModel import com.duckduckgo.common.utils.DispatcherProvider +import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.subscriptions.api.Product.PIR import com.duckduckgo.subscriptions.api.Subscriptions import com.duckduckgo.subscriptions.api.Subscriptions.EntitlementStatus.Found @@ -30,7 +31,6 @@ import com.duckduckgo.subscriptions.api.Subscriptions.EntitlementStatus.NotFound import com.duckduckgo.subscriptions.impl.pixels.SubscriptionPixelSender import com.duckduckgo.subscriptions.impl.settings.views.PirSettingViewModel.Command.OpenPir import javax.inject.Inject -import javax.inject.Provider import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow @@ -40,6 +40,7 @@ import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch @SuppressLint("NoLifecycleObserver") // we don't observe app lifecycle +@ContributesViewModel(ViewScope::class) class PirSettingViewModel @Inject constructor( private val subscriptions: Subscriptions, private val dispatcherProvider: DispatcherProvider, @@ -78,18 +79,4 @@ class PirSettingViewModel @Inject constructor( command.send(newCommand) } } - - @Suppress("UNCHECKED_CAST") - class Factory @Inject constructor( - private val pirSettingViewModel: Provider, - ) : ViewModelProvider.NewInstanceFactory() { - override fun create(modelClass: Class): T { - return with(modelClass) { - when { - isAssignableFrom(PirSettingViewModel::class.java) -> pirSettingViewModel.get() - else -> throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}") - } - } as T - } - } } diff --git a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ProSettingView.kt b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ProSettingView.kt index 032ebdde02b9..7ae761b2e2bf 100644 --- a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ProSettingView.kt +++ b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ProSettingView.kt @@ -36,6 +36,7 @@ import com.duckduckgo.common.ui.view.gone import com.duckduckgo.common.ui.view.show import com.duckduckgo.common.ui.viewbinding.viewBinding import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.common.utils.extensions.html import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.navigation.api.GlobalActivityStarter @@ -45,9 +46,10 @@ import com.duckduckgo.subscriptions.impl.databinding.ViewSettingsBinding import com.duckduckgo.subscriptions.impl.pixels.SubscriptionPixelSender import com.duckduckgo.subscriptions.impl.settings.views.ProSettingViewModel.Command import com.duckduckgo.subscriptions.impl.settings.views.ProSettingViewModel.Command.OpenBuyScreen +import com.duckduckgo.subscriptions.impl.settings.views.ProSettingViewModel.Command.OpenRestoreScreen import com.duckduckgo.subscriptions.impl.settings.views.ProSettingViewModel.Command.OpenSettings -import com.duckduckgo.subscriptions.impl.settings.views.ProSettingViewModel.Factory import com.duckduckgo.subscriptions.impl.settings.views.ProSettingViewModel.ViewState +import com.duckduckgo.subscriptions.impl.ui.RestoreSubscriptionActivity.Companion.RestoreSubscriptionScreenWithEmptyParams import com.duckduckgo.subscriptions.impl.ui.SubscriptionSettingsActivity.Companion.SubscriptionsSettingsScreenWithEmptyParams import com.duckduckgo.subscriptions.impl.ui.SubscriptionsWebViewActivityWithParams import dagger.android.support.AndroidSupportInjection @@ -67,7 +69,7 @@ class ProSettingView @JvmOverloads constructor( ) : FrameLayout(context, attrs, defStyle) { @Inject - lateinit var viewModelFactory: Factory + lateinit var viewModelFactory: ViewViewModelFactory @Inject lateinit var globalActivityStarter: GlobalActivityStarter @@ -121,23 +123,30 @@ class ProSettingView @JvmOverloads constructor( binding.subscriptionSetting.setOnTouchListener(null) binding.subscriptionBuy.setOnClickListener(null) binding.subscriptionBuy.setOnTouchListener(null) + binding.subscriptionGet.setOnClickListener(null) + binding.subscriptionGet.setOnTouchListener(null) + binding.subscriptionRestore.setOnTouchListener(null) + binding.subscriptionRestore.setOnClickListener(null) if (viewState.hasSubscription) { - binding.subscriptionBuy.gone() - binding.subscribeSecondary.gone() - binding.subscriptionSetting.show() - binding.settingContainer.setOnClickListener { + binding.subscriptionBuyContainer.gone() + binding.subscriptionRestoreContainer.gone() + binding.subscriptionSettingContainer.show() + binding.subscriptionSettingContainer.setOnClickListener { viewModel.onSettings() } } else { val htmlText = context.getString(R.string.subscriptionSettingFeaturesList).html(context) - binding.subscribeSecondary.show() binding.subscribeSecondary.text = htmlText - binding.subscriptionBuy.show() - binding.subscriptionSetting.gone() - binding.settingContainer.setOnClickListener { + binding.subscriptionBuyContainer.show() + binding.subscriptionSettingContainer.gone() + binding.subscriptionRestoreContainer.show() + binding.subscriptionBuyContainer.setOnClickListener { viewModel.onBuy() } + binding.subscriptionRestoreContainer.setOnClickListener { + viewModel.onRestore() + } } } @@ -156,6 +165,9 @@ class ProSettingView @JvmOverloads constructor( ), ) } + is OpenRestoreScreen -> { + globalActivityStarter.start(context, RestoreSubscriptionScreenWithEmptyParams) + } } } } diff --git a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ProSettingViewModel.kt b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ProSettingViewModel.kt index 92db577068c0..0ddfd2d827ca 100644 --- a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ProSettingViewModel.kt +++ b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ProSettingViewModel.kt @@ -20,14 +20,15 @@ import android.annotation.SuppressLint import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import com.duckduckgo.anvil.annotations.ContributesViewModel import com.duckduckgo.common.utils.DispatcherProvider +import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.subscriptions.impl.SubscriptionsManager import com.duckduckgo.subscriptions.impl.settings.views.ProSettingViewModel.Command.OpenBuyScreen +import com.duckduckgo.subscriptions.impl.settings.views.ProSettingViewModel.Command.OpenRestoreScreen import com.duckduckgo.subscriptions.impl.settings.views.ProSettingViewModel.Command.OpenSettings import javax.inject.Inject -import javax.inject.Provider import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow @@ -37,6 +38,7 @@ import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch @SuppressLint("NoLifecycleObserver") // we don't observe app lifecycle +@ContributesViewModel(ViewScope::class) class ProSettingViewModel @Inject constructor( private val subscriptionsManager: SubscriptionsManager, private val dispatcherProvider: DispatcherProvider, @@ -45,6 +47,7 @@ class ProSettingViewModel @Inject constructor( sealed class Command { data object OpenSettings : Command() data object OpenBuyScreen : Command() + data object OpenRestoreScreen : Command() } private val command = Channel(1, BufferOverflow.DROP_OLDEST) @@ -62,6 +65,10 @@ class ProSettingViewModel @Inject constructor( sendCommand(OpenBuyScreen) } + fun onRestore() { + sendCommand(OpenRestoreScreen) + } + override fun onResume(owner: LifecycleOwner) { super.onResume(owner) viewModelScope.launch(dispatcherProvider.io()) { @@ -76,18 +83,4 @@ class ProSettingViewModel @Inject constructor( command.send(newCommand) } } - - @Suppress("UNCHECKED_CAST") - class Factory @Inject constructor( - private val proSettingViewModel: Provider, - ) : ViewModelProvider.NewInstanceFactory() { - override fun create(modelClass: Class): T { - return with(modelClass) { - when { - isAssignableFrom(ProSettingViewModel::class.java) -> proSettingViewModel.get() - else -> throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}") - } - } as T - } - } } diff --git a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/ui/AddDeviceActivity.kt b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/ui/AddDeviceActivity.kt index 26ebf851426b..62be401f1815 100644 --- a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/ui/AddDeviceActivity.kt +++ b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/ui/AddDeviceActivity.kt @@ -18,6 +18,7 @@ package com.duckduckgo.subscriptions.impl.ui import android.os.Bundle import android.widget.Toast +import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope @@ -68,7 +69,7 @@ class AddDeviceActivity : DuckDuckGoActivity() { .onEach { processCommand(it) } .launchIn(lifecycleScope) - binding.email.setPrimaryButtonClickListener { + binding.manageEmailCard.emailButton.setOnClickListener { viewModel.useEmail() } } @@ -80,11 +81,14 @@ class AddDeviceActivity : DuckDuckGoActivity() { private fun renderView(viewState: ViewState) { if (viewState.email != null) { - binding.email.setSecondaryText(String.format(getString(R.string.useEmail), viewState.email)) - binding.email.setPrimaryButtonText(getString(R.string.manage)) + binding.manageEmailCard.emailAddress.isVisible = true + binding.manageEmailCard.emailAddress.text = viewState.email + binding.manageEmailCard.emailSubtitle.setText(R.string.useEmail) + binding.manageEmailCard.emailButton.setText(R.string.manage) } else { - binding.email.setPrimaryButtonText(getString(R.string.addEmailText)) - binding.email.setSecondaryText(getString(R.string.addEmail)) + binding.manageEmailCard.emailAddress.isVisible = false + binding.manageEmailCard.emailSubtitle.setText(R.string.addEmail) + binding.manageEmailCard.emailButton.setText(R.string.addEmailText) } } diff --git a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/ui/RestoreSubscriptionActivity.kt b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/ui/RestoreSubscriptionActivity.kt index 33bf35d8e920..b986f665f7ea 100644 --- a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/ui/RestoreSubscriptionActivity.kt +++ b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/ui/RestoreSubscriptionActivity.kt @@ -66,12 +66,16 @@ class RestoreSubscriptionActivity : DuckDuckGoActivity() { .onEach { processCommand(it) } .launchIn(lifecycleScope) - binding.googlePlay.setPrimaryButtonClickListener { + binding.googlePlay.setOnClickListener { viewModel.restoreFromStore() } - binding.email.setPrimaryButtonClickListener { - viewModel.restoreFromEmail() + with(binding.manageEmailCard) { + emailSubtitle.setText(string.restoreSubscriptionEmailDescription) + emailButton.setText(string.restoreSubscriptionEmailButton) + emailButton.setOnClickListener { + viewModel.restoreFromEmail() + } } } @@ -134,10 +138,10 @@ class RestoreSubscriptionActivity : DuckDuckGoActivity() { .show() } - private fun showError(message: String) { + private fun showError() { TextAlertDialogBuilder(this) .setTitle(string.somethingWentWrong) - .setMessage(message) + .setMessage(string.somethingWentWrongDescription) .setDestructiveButtons(false) .setPositiveButton(string.ok) .show() @@ -148,7 +152,7 @@ class RestoreSubscriptionActivity : DuckDuckGoActivity() { is RestoreFromEmail -> goToRestore() is Success -> onPurchaseRestored() is SubscriptionNotFound -> subscriptionNotFound() - is Error -> showError(command.message) + is Error -> showError() } } companion object { diff --git a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/ui/RestoreSubscriptionViewModel.kt b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/ui/RestoreSubscriptionViewModel.kt index 2049dcda3904..f75317354508 100644 --- a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/ui/RestoreSubscriptionViewModel.kt +++ b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/ui/RestoreSubscriptionViewModel.kt @@ -76,7 +76,7 @@ class RestoreSubscriptionViewModel @Inject constructor( } else -> { pixelSender.reportRestoreUsingStoreFailureOther() - command.send(Error(response.message)) + command.send(Error) } } } @@ -95,6 +95,6 @@ class RestoreSubscriptionViewModel @Inject constructor( object RestoreFromEmail : Command() object Success : Command() object SubscriptionNotFound : Command() - data class Error(val message: String) : Command() + data object Error : Command() } } diff --git a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/ui/SubscriptionSettingsActivity.kt b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/ui/SubscriptionSettingsActivity.kt index 4428b3e6c877..f7552ee721b0 100644 --- a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/ui/SubscriptionSettingsActivity.kt +++ b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/ui/SubscriptionSettingsActivity.kt @@ -120,12 +120,15 @@ class SubscriptionSettingsActivity : DuckDuckGoActivity() { } private fun renderView(viewState: ViewState) { - val duration = if (viewState.duration is Monthly) { getString(string.monthly) } else { getString(string.yearly) } + binding.subscriptionDuration.setText( + if (viewState.duration is Monthly) string.monthlySubscription else string.yearlySubscription, + ) + val status = when (viewState.status) { is AutoRenewable -> getString(string.renews) else -> getString(string.expires) } - binding.description.text = getString(string.subscriptionsData, duration, status, viewState.date) + binding.description.text = getString(string.subscriptionsData, status, viewState.date) when (viewState.platform?.lowercase()) { "apple", "ios" -> diff --git a/subscriptions/subscriptions-impl/src/main/res/drawable/ic_privacy_pro.xml b/subscriptions/subscriptions-impl/src/main/res/drawable/ic_privacy_pro.xml new file mode 100644 index 000000000000..1ed0e9e11885 --- /dev/null +++ b/subscriptions/subscriptions-impl/src/main/res/drawable/ic_privacy_pro.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + diff --git a/subscriptions/subscriptions-impl/src/main/res/layout/activity_add_device.xml b/subscriptions/subscriptions-impl/src/main/res/layout/activity_add_device.xml index 3cd11f9e2157..7e0647275465 100644 --- a/subscriptions/subscriptions-impl/src/main/res/layout/activity_add_device.xml +++ b/subscriptions/subscriptions-impl/src/main/res/layout/activity_add_device.xml @@ -68,42 +68,10 @@ android:text="@string/addDeviceDescription" tools:ignore="HardcodedText"/> - - - - - - - + - diff --git a/subscriptions/subscriptions-impl/src/main/res/layout/activity_restore_subscription.xml b/subscriptions/subscriptions-impl/src/main/res/layout/activity_restore_subscription.xml index 460f526f31c8..bd1f342112ec 100644 --- a/subscriptions/subscriptions-impl/src/main/res/layout/activity_restore_subscription.xml +++ b/subscriptions/subscriptions-impl/src/main/res/layout/activity_restore_subscription.xml @@ -68,43 +68,26 @@ android:text="@string/restoreSubscriptionDescription" tools:ignore="HardcodedText"/> - + - - - + - + - diff --git a/subscriptions/subscriptions-impl/src/main/res/layout/activity_subscription_settings.xml b/subscriptions/subscriptions-impl/src/main/res/layout/activity_subscription_settings.xml index 098eb1850994..7a21d377d7b1 100644 --- a/subscriptions/subscriptions-impl/src/main/res/layout/activity_subscription_settings.xml +++ b/subscriptions/subscriptions-impl/src/main/res/layout/activity_subscription_settings.xml @@ -36,16 +36,62 @@ android:layout_height="wrap_content" android:orientation="vertical"> + + + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:text="@string/privacyPro" + app:textType="primary" + app:typography="h1" /> + + + + + + + + + + + + - - - - - - + app:primaryText="Privacy Pro FAQ" + app:secondaryText="Get answers to frequently asked questions about Privacy Pro in our help pages."/> diff --git a/subscriptions/subscriptions-impl/src/main/res/layout/card_manage_email.xml b/subscriptions/subscriptions-impl/src/main/res/layout/card_manage_email.xml new file mode 100644 index 000000000000..9c27fae7ac73 --- /dev/null +++ b/subscriptions/subscriptions-impl/src/main/res/layout/card_manage_email.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/subscriptions/subscriptions-impl/src/main/res/layout/view_settings.xml b/subscriptions/subscriptions-impl/src/main/res/layout/view_settings.xml index 6a8dfb7bb109..5831aaa8fd0f 100644 --- a/subscriptions/subscriptions-impl/src/main/res/layout/view_settings.xml +++ b/subscriptions/subscriptions-impl/src/main/res/layout/view_settings.xml @@ -14,31 +14,51 @@ ~ limitations under the License. --> - + + + android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground" + android:visibility="gone" + tools:visibility="visible"> - + app:primaryText="@string/subscriptionSetting" + tools:ignore="RtlSymmetry" /> + + + - + + - - \ No newline at end of file + + + + + + + + + + + \ No newline at end of file diff --git a/subscriptions/subscriptions-impl/src/main/res/values/donottranslate.xml b/subscriptions/subscriptions-impl/src/main/res/values/donottranslate.xml index 2aa0d28c982b..48c8cc144a90 100644 --- a/subscriptions/subscriptions-impl/src/main/res/values/donottranslate.xml +++ b/subscriptions/subscriptions-impl/src/main/res/values/donottranslate.xml @@ -20,34 +20,32 @@ Cancel OK Privacy Pro + Email Manage Add Email - %1$s You can use this email to activate your subscription on your other devices. - Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription. + You can use this email to activate your subscription from browser settings in the DuckDuckGo app on your other devices. + Add an email address to access your subscription in DuckDuckGo on other devices. We’ll only use this address to verify your subscription. Manage Email - Email An error happened, try again Use your subscription on all your devices - Access your subscription on any of your devices Google Play or by adding an email address. + Access your Privacy Pro subscription on other devices via an email address. Google Play Privacy Pro is available on any device signed in to your Google Play Store account. Something Went Wrong + Google Play was unable to process your purchase. Please try again later. You\'re all set. View Plans Subscription Not Found - The subscription associated with this Google Account is no longer active or could not be found. - Your purchase has been restored. + The subscription associated with this Google Play account is no longer active. + Your purchases have been restored. Activate your subscription on this device Access your Privacy Pro subscription on this device via Google Play or an email address. - Access your subscription on any of your devices via Google Play or by adding an email address. - Google Play - Restore your purchase to activate your subscription on this device. + Your subscription is automatically available in DuckDuckGo on any device signed in to your Google Play account. Restore Purchase - Email Use your email to activate your subscription on this device. Enter Email Buy Subscription @@ -55,11 +53,11 @@ Remove Subscription Your subscription has been removed from this device - Remove From This Device? - You will no longer be able to access your subscription on this device. This will not cancel your subscription, and it will remain active on your other device. - Your %1$s Privacy Pro subscription %2$s on %3$s. - monthly - yearly + Remove from this device? + You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices. + Your subscription %1$s on %2$s. + Monthly Subscription + Yearly Subscription renews expires @@ -67,7 +65,9 @@ Subscription Settings Subscribe To Privacy Pro More seamless privacy with three new protections, including: -
  •  VPN (Virtual Private Network)
  •  Personal Information Removal
  •  Identity Theft Restoration

  • Learn More]]>
    +
  •  VPN
  •  Personal Information Removal
  •  Identity Theft Restoration
  • ]]>
    + Get Privacy Pro + I Have a Subscription You\'re all set. diff --git a/subscriptions/subscriptions-impl/src/test/java/com/duckduckgo/subscriptions/impl/settings/views/ProSettingViewModelTest.kt b/subscriptions/subscriptions-impl/src/test/java/com/duckduckgo/subscriptions/impl/settings/views/ProSettingViewModelTest.kt index a8420686d2e5..ba1c3586c739 100644 --- a/subscriptions/subscriptions-impl/src/test/java/com/duckduckgo/subscriptions/impl/settings/views/ProSettingViewModelTest.kt +++ b/subscriptions/subscriptions-impl/src/test/java/com/duckduckgo/subscriptions/impl/settings/views/ProSettingViewModelTest.kt @@ -4,6 +4,7 @@ import app.cash.turbine.test import com.duckduckgo.common.test.CoroutineTestRule import com.duckduckgo.subscriptions.impl.SubscriptionsManager import com.duckduckgo.subscriptions.impl.settings.views.ProSettingViewModel.Command.OpenBuyScreen +import com.duckduckgo.subscriptions.impl.settings.views.ProSettingViewModel.Command.OpenRestoreScreen import com.duckduckgo.subscriptions.impl.settings.views.ProSettingViewModel.Command.OpenSettings import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest @@ -44,6 +45,15 @@ class ProSettingViewModelTest { } } + @Test + fun whenOnRestoreThenCommandSent() = runTest { + viewModel.commands().test { + viewModel.onRestore() + assertTrue(awaitItem() is OpenRestoreScreen) + cancelAndConsumeRemainingEvents() + } + } + @Test fun whenOnResumeIfSubscriptionEmitViewState() = runTest { whenever(subscriptionsManager.hasSubscription).thenReturn(flowOf(true)) diff --git a/subscriptions/subscriptions-impl/src/test/java/com/duckduckgo/subscriptions/impl/ui/RestoreSubscriptionViewModelTest.kt b/subscriptions/subscriptions-impl/src/test/java/com/duckduckgo/subscriptions/impl/ui/RestoreSubscriptionViewModelTest.kt index 8bfba3d86876..72704528bbe0 100644 --- a/subscriptions/subscriptions-impl/src/test/java/com/duckduckgo/subscriptions/impl/ui/RestoreSubscriptionViewModelTest.kt +++ b/subscriptions/subscriptions-impl/src/test/java/com/duckduckgo/subscriptions/impl/ui/RestoreSubscriptionViewModelTest.kt @@ -56,7 +56,6 @@ class RestoreSubscriptionViewModelTest { viewModel.restoreFromStore() val result = awaitItem() assertTrue(result is Error) - assertEquals("error", (result as Error).message) } }