Add server-based localization of subscription names and badge information.

This commit is contained in:
Alex Hart 2021-11-03 09:38:42 -03:00 committed by Greyson Parrelli
parent 56c502c9bf
commit 70355aa70e
10 changed files with 40 additions and 29 deletions

View file

@ -10,6 +10,7 @@ import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
import org.whispersystems.signalservice.api.subscriptions.SubscriptionLevels import org.whispersystems.signalservice.api.subscriptions.SubscriptionLevels
import org.whispersystems.signalservice.internal.ServiceResponse import org.whispersystems.signalservice.internal.ServiceResponse
import java.util.Currency import java.util.Currency
import java.util.Locale
/** /**
* Repository which can query for the user's active subscription as well as a list of available subscriptions, * Repository which can query for the user's active subscription as well as a list of available subscriptions,
@ -27,12 +28,13 @@ class SubscriptionsRepository(private val donationsService: DonationsService) {
} }
} }
fun getSubscriptions(currency: Currency): Single<List<Subscription>> = donationsService.subscriptionLevels fun getSubscriptions(currency: Currency): Single<List<Subscription>> = donationsService.getSubscriptionLevels(Locale.getDefault())
.flatMap(ServiceResponse<SubscriptionLevels>::flattenResult) .flatMap(ServiceResponse<SubscriptionLevels>::flattenResult)
.map { subscriptionLevels -> .map { subscriptionLevels ->
subscriptionLevels.levels.map { (code, level) -> subscriptionLevels.levels.map { (code, level) ->
Subscription( Subscription(
id = code, id = code,
name = level.name,
badge = Badges.fromServiceBadge(level.badge), badge = Badges.fromServiceBadge(level.badge),
price = FiatMoney(level.currencies[currency.currencyCode]!!, currency), price = FiatMoney(level.currencies[currency.currencyCode]!!, currency),
level = code.toInt() level = code.toInt()

View file

@ -56,7 +56,7 @@ class BoostFragment : DSLSettingsBottomSheetFragment(
.append(" ") .append(" ")
.append( .append(
SpanUtil.learnMore(requireContext(), ContextCompat.getColor(requireContext(), R.color.signal_accent_primary)) { SpanUtil.learnMore(requireContext(), ContextCompat.getColor(requireContext(), R.color.signal_accent_primary)) {
CommunicationActions.openBrowserLink(requireContext(), R.string.sustainer_boost_and_badges) CommunicationActions.openBrowserLink(requireContext(), getString(R.string.sustainer_boost_and_badges))
} }
) )
} }

View file

@ -9,6 +9,7 @@ import org.whispersystems.signalservice.api.services.DonationsService
import org.whispersystems.signalservice.internal.ServiceResponse import org.whispersystems.signalservice.internal.ServiceResponse
import java.math.BigDecimal import java.math.BigDecimal
import java.util.Currency import java.util.Currency
import java.util.Locale
class BoostRepository(private val donationsService: DonationsService) { class BoostRepository(private val donationsService: DonationsService) {
@ -22,7 +23,7 @@ class BoostRepository(private val donationsService: DonationsService) {
} }
fun getBoostBadge(): Single<Badge> { fun getBoostBadge(): Single<Badge> {
return donationsService.boostBadge return donationsService.getBoostBadge(Locale.getDefault())
.flatMap(ServiceResponse<SignalServiceProfile.Badge>::flattenResult) .flatMap(ServiceResponse<SignalServiceProfile.Badge>::flattenResult)
.map(Badges::fromServiceBadge) .map(Badges::fromServiceBadge)
} }

View file

@ -45,7 +45,7 @@ object ActiveSubscriptionPreference {
override fun bind(model: Model) { override fun bind(model: Model) {
badge.setBadge(model.subscription.badge) badge.setBadge(model.subscription.badge)
title.text = model.subscription.getTitle(context) title.text = model.subscription.name
price.text = context.getString( price.text = context.getString(
R.string.MySupportPreference__s_per_month, R.string.MySupportPreference__s_per_month,

View file

@ -199,7 +199,7 @@ class SubscribeViewModel(
store.update { it.copy(stage = SubscribeState.Stage.TOKEN_REQUEST) } store.update { it.copy(stage = SubscribeState.Stage.TOKEN_REQUEST) }
subscriptionToPurchase = snapshot.selectedSubscription subscriptionToPurchase = snapshot.selectedSubscription
donationPaymentRepository.requestTokenFromGooglePay(snapshot.selectedSubscription.price, snapshot.selectedSubscription.getTitle(context), fetchTokenRequestCode) donationPaymentRepository.requestTokenFromGooglePay(snapshot.selectedSubscription.price, snapshot.selectedSubscription.name, fetchTokenRequestCode)
} }
fun setSelectedSubscription(subscription: Subscription) { fun setSelectedSubscription(subscription: Subscription) {

View file

@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.subscription package org.thoughtcrime.securesms.subscription
import android.content.Context
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
@ -21,20 +20,12 @@ import java.util.Locale
*/ */
data class Subscription( data class Subscription(
val id: String, val id: String,
val name: String,
val badge: Badge, val badge: Badge,
val price: FiatMoney, val price: FiatMoney,
val level: Int, val level: Int,
) { ) {
fun getTitle(context: Context): String {
return when (level) {
500 -> context.getString(R.string.SubscribeFragment__sustainer_i)
1000 -> context.getString(R.string.SubscribeFragment__sustainer_ii)
2000 -> context.getString(R.string.SubscribeFragment__sustainer_iii)
else -> ""
}
}
companion object { companion object {
fun register(adapter: MappingAdapter) { fun register(adapter: MappingAdapter) {
adapter.registerFactory(Model::class.java, MappingAdapter.LayoutFactory({ ViewHolder(it) }, R.layout.subscription_preference)) adapter.registerFactory(Model::class.java, MappingAdapter.LayoutFactory({ ViewHolder(it) }, R.layout.subscription_preference))
@ -90,7 +81,7 @@ data class Subscription(
badge.setBadge(model.subscription.badge) badge.setBadge(model.subscription.badge)
} }
title.text = model.subscription.getTitle(context) title.text = model.subscription.name
tagline.text = context.getString(R.string.Subscription__earn_a_s_badge, model.subscription.badge.name) tagline.text = context.getString(R.string.Subscription__earn_a_s_badge, model.subscription.badge.name)
val formattedPrice = FiatMoneyUtil.format( val formattedPrice = FiatMoneyUtil.format(

View file

@ -3947,9 +3947,6 @@
<string name="SubscribeFragment__update">Update</string> <string name="SubscribeFragment__update">Update</string>
<string name="SubscribeFragment__you_will_be_charged_the_full_amount">You will be charged the full amount of the new subscription price today. Your subscription will renew %1$s.</string> <string name="SubscribeFragment__you_will_be_charged_the_full_amount">You will be charged the full amount of the new subscription price today. Your subscription will renew %1$s.</string>
<string name="SubscribeFragment__you_will_be_charged_the_full_amount_s_of">You will be charged the full amount (%1$s) of the new subscription price today. Your subscription will renew monthly.</string> <string name="SubscribeFragment__you_will_be_charged_the_full_amount_s_of">You will be charged the full amount (%1$s) of the new subscription price today. Your subscription will renew monthly.</string>
<string name="SubscribeFragment__sustainer_i">Sustainer I</string>
<string name="SubscribeFragment__sustainer_ii">Sustainer II</string>
<string name="SubscribeFragment__sustainer_iii">Sustainer III</string>
<string name="Subscription__s_per_month">%s/month</string> <string name="Subscription__s_per_month">%s/month</string>
<string name="Subscription__s_per_month_dot_renews_s">%1$s/month · Renews %2$s</string> <string name="Subscription__s_per_month_dot_renews_s">%1$s/month · Renews %2$s</string>

View file

@ -22,6 +22,7 @@ import org.whispersystems.signalservice.internal.push.PushServiceSocket;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.annotations.NonNull;
@ -102,15 +103,15 @@ public class DonationsService {
/** /**
* @return The badge configuration for signal boost. Expect for right now only a single level numbered 1. * @return The badge configuration for signal boost. Expect for right now only a single level numbered 1.
*/ */
public Single<ServiceResponse<SignalServiceProfile.Badge>> getBoostBadge() { public Single<ServiceResponse<SignalServiceProfile.Badge>> getBoostBadge(Locale locale) {
return createServiceResponse(() -> new Pair<>(pushServiceSocket.getBoostLevels().getLevels().get("1").getBadge(), 200)); return createServiceResponse(() -> new Pair<>(pushServiceSocket.getBoostLevels(locale).getLevels().get("1").getBadge(), 200));
} }
/** /**
* Returns the subscription levels that are available for the client to choose from along with currencies and current prices * Returns the subscription levels that are available for the client to choose from along with currencies and current prices
*/ */
public Single<ServiceResponse<SubscriptionLevels>> getSubscriptionLevels() { public Single<ServiceResponse<SubscriptionLevels>> getSubscriptionLevels(Locale locale) {
return createServiceResponse(() -> new Pair<>(pushServiceSocket.getSubscriptionLevels(), 200)); return createServiceResponse(() -> new Pair<>(pushServiceSocket.getSubscriptionLevels(locale), 200));
} }
/** /**

View file

@ -28,17 +28,24 @@ public final class SubscriptionLevels {
* An available subscription level * An available subscription level
*/ */
public static final class Level { public static final class Level {
private final String name;
private final SignalServiceProfile.Badge badge; private final SignalServiceProfile.Badge badge;
private final Map<String, BigDecimal> currencies; private final Map<String, BigDecimal> currencies;
@JsonCreator @JsonCreator
public Level(@JsonProperty("badge") SignalServiceProfile.Badge badge, public Level(@JsonProperty("name") String name,
@JsonProperty("badge") SignalServiceProfile.Badge badge,
@JsonProperty("currencies") Map<String, BigDecimal> currencies) @JsonProperty("currencies") Map<String, BigDecimal> currencies)
{ {
this.name = name;
this.badge = badge; this.badge = badge;
this.currencies = currencies; this.currencies = currencies;
} }
public String getName() {
return name;
}
public SignalServiceProfile.Badge getBadge() { public SignalServiceProfile.Badge getBadge() {
return badge; return badge;
} }

View file

@ -888,8 +888,8 @@ public class PushServiceSocket {
return JsonUtil.fromJsonResponse(result, typeRef); return JsonUtil.fromJsonResponse(result, typeRef);
} }
public SubscriptionLevels getBoostLevels() throws IOException { public SubscriptionLevels getBoostLevels(Locale locale) throws IOException {
String result = makeServiceRequestWithoutAuthentication(BOOST_BADGES, "GET", null); String result = makeServiceRequestWithoutAuthentication(BOOST_BADGES, "GET", null, AcceptLanguagesUtil.getHeadersWithAcceptLanguage(locale));
return JsonUtil.fromJsonResponse(result, SubscriptionLevels.class); return JsonUtil.fromJsonResponse(result, SubscriptionLevels.class);
} }
@ -912,8 +912,8 @@ public class PushServiceSocket {
} }
public SubscriptionLevels getSubscriptionLevels() throws IOException { public SubscriptionLevels getSubscriptionLevels(Locale locale) throws IOException {
String result = makeServiceRequestWithoutAuthentication(SUBSCRIPTION_LEVELS, "GET", null); String result = makeServiceRequestWithoutAuthentication(SUBSCRIPTION_LEVELS, "GET", null, AcceptLanguagesUtil.getHeadersWithAcceptLanguage(locale));
return JsonUtil.fromJsonResponse(result, SubscriptionLevels.class); return JsonUtil.fromJsonResponse(result, SubscriptionLevels.class);
} }
@ -1530,7 +1530,19 @@ public class PushServiceSocket {
private String makeServiceRequestWithoutAuthentication(String urlFragment, String method, String jsonBody, ResponseCodeHandler responseCodeHandler) private String makeServiceRequestWithoutAuthentication(String urlFragment, String method, String jsonBody, ResponseCodeHandler responseCodeHandler)
throws NonSuccessfulResponseCodeException, PushNetworkException, MalformedResponseException throws NonSuccessfulResponseCodeException, PushNetworkException, MalformedResponseException
{ {
ResponseBody responseBody = makeServiceRequest(urlFragment, method, jsonRequestBody(jsonBody), NO_HEADERS, responseCodeHandler, Optional.absent(), true).body(); return makeServiceRequestWithoutAuthentication(urlFragment, method, jsonBody, NO_HEADERS, responseCodeHandler);
}
private String makeServiceRequestWithoutAuthentication(String urlFragment, String method, String jsonBody, Map<String, String> headers)
throws NonSuccessfulResponseCodeException, PushNetworkException, MalformedResponseException
{
return makeServiceRequestWithoutAuthentication(urlFragment, method, jsonBody, headers, NO_HANDLER);
}
private String makeServiceRequestWithoutAuthentication(String urlFragment, String method, String jsonBody, Map<String, String> headers, ResponseCodeHandler responseCodeHandler)
throws NonSuccessfulResponseCodeException, PushNetworkException, MalformedResponseException
{
ResponseBody responseBody = makeServiceRequest(urlFragment, method, jsonRequestBody(jsonBody), headers, responseCodeHandler, Optional.absent(), true).body();
try { try {
return responseBody.string(); return responseBody.string();
} catch (IOException e) { } catch (IOException e) {