Skip to content

Commit

Permalink
Subscriptions design and copy changes (#4288)
Browse files Browse the repository at this point in the history
<!--
Note: This checklist is a reminder of our shared engineering
expectations.
The items in Bold are required
If your PR involves UI changes:
1. Upload screenshots or screencasts that illustrate the changes before
/ after
2. Add them under the UI changes section (feel free to add more columns
if needed)
If your PR does not involve UI changes, you can remove the **UI
changes** section

At a minimum, make sure your changes are tested in API 23 and one of the
more recent API levels available.
-->

Task/Issue URL:
https://app.asana.com/0/488551667048375/1206362079735328/f

### Description

See task.
  • Loading branch information
lmac012 authored Mar 12, 2024
1 parent 4b8a77d commit 47a2ce2
Show file tree
Hide file tree
Showing 19 changed files with 372 additions and 202 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ 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
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
Expand All @@ -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,
Expand Down Expand Up @@ -78,18 +79,4 @@ class ItrSettingViewModel @Inject constructor(
command.send(newCommand)
}
}

@Suppress("UNCHECKED_CAST")
class Factory @Inject constructor(
private val itrSettingViewModel: Provider<ItrSettingViewModel>,
) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return with(modelClass) {
when {
isAssignableFrom(ItrSettingViewModel::class.java) -> itrSettingViewModel.get()
else -> throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
}
} as T
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ 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
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
Expand All @@ -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,
Expand Down Expand Up @@ -78,18 +79,4 @@ class PirSettingViewModel @Inject constructor(
command.send(newCommand)
}
}

@Suppress("UNCHECKED_CAST")
class Factory @Inject constructor(
private val pirSettingViewModel: Provider<PirSettingViewModel>,
) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return with(modelClass) {
when {
isAssignableFrom(PirSettingViewModel::class.java) -> pirSettingViewModel.get()
else -> throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
}
} as T
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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()
}
}
}

Expand All @@ -156,6 +165,9 @@ class ProSettingView @JvmOverloads constructor(
),
)
}
is OpenRestoreScreen -> {
globalActivityStarter.start(context, RestoreSubscriptionScreenWithEmptyParams)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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<Command>(1, BufferOverflow.DROP_OLDEST)
Expand All @@ -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()) {
Expand All @@ -76,18 +83,4 @@ class ProSettingViewModel @Inject constructor(
command.send(newCommand)
}
}

@Suppress("UNCHECKED_CAST")
class Factory @Inject constructor(
private val proSettingViewModel: Provider<ProSettingViewModel>,
) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return with(modelClass) {
when {
isAssignableFrom(ProSettingViewModel::class.java) -> proSettingViewModel.get()
else -> throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
}
} as T
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -68,7 +69,7 @@ class AddDeviceActivity : DuckDuckGoActivity() {
.onEach { processCommand(it) }
.launchIn(lifecycleScope)

binding.email.setPrimaryButtonClickListener {
binding.manageEmailCard.emailButton.setOnClickListener {
viewModel.useEmail()
}
}
Expand All @@ -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)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
}

Expand Down Expand Up @@ -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()
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class RestoreSubscriptionViewModel @Inject constructor(
}
else -> {
pixelSender.reportRestoreUsingStoreFailureOther()
command.send(Error(response.message))
command.send(Error)
}
}
}
Expand All @@ -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()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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" ->
Expand Down
Loading

0 comments on commit 47a2ce2

Please sign in to comment.