[android] Implemented 'Yearly product' button

This commit is contained in:
Alexander Zatsepin 2018-09-17 21:23:38 +03:00 committed by yoksnod
parent cb15e71b9d
commit d263e23485
8 changed files with 212 additions and 15 deletions

View file

@ -22,6 +22,13 @@
android:textAppearance="@style/MwmTextAppearance.Title"
android:visibility="invisible"
tools:visibility="visible"/>
<include
layout="@layout/ads_removal_pay_button_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_below="@id/title"
android:layout_marginTop="@dimen/margin_double_plus"/>
<LinearLayout
android:id="@+id/progress_layout"
android:layout_width="match_parent"

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/pay_button_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/MwmWidget.Button.Primary"
android:minWidth="@dimen/ads_removal_pay_button_min_width"
android:layout_centerHorizontal="true"
android:paddingBottom="@dimen/margin_half_double_plus"
android:paddingTop="@dimen/margin_half_double_plus"
android:orientation="vertical"
tools:showIn="@layout/fragment_ads_removal_purchase_dialog">
<TextView
android:id="@+id/price"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="?attr/accentButtonTextColor"
android:textSize="@dimen/text_size_toolbar"
android:textAllCaps="true"
android:ellipsize="end"
android:gravity="center"/>
<TextView
android:id="@+id/saving"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/margin_quarter"
android:textColor="?attr/menuBackground"
android:textSize="@dimen/text_size_body_3"
android:textAllCaps="true"
android:ellipsize="end"
android:gravity="center"/>
</LinearLayout>

View file

@ -33,6 +33,13 @@
android:src="@drawable/img_mappy_heart"
android:visibility="invisible"
tools:visibility="visible"/>
<include
layout="@layout/ads_removal_pay_button_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_below="@id/image"
android:layout_marginTop="@dimen/margin_base"/>
<LinearLayout
android:id="@+id/progress_layout"
android:layout_width="match_parent"

View file

@ -254,4 +254,5 @@
<!-- Purchases-->
<dimen name="ads_removal_dialog_min_height">508dp</dimen>
<dimen name="ads_removal_pay_button_min_width">252dp</dimen>
</resources>

View file

@ -13,10 +13,10 @@ import com.android.billingclient.api.SkuDetails;
import com.mapswithme.maps.R;
import com.mapswithme.maps.base.BaseMwmDialogFragment;
import com.mapswithme.util.UiUtils;
import com.mapswithme.util.Utils;
import com.mapswithme.util.log.Logger;
import com.mapswithme.util.log.LoggerFactory;
import java.util.Collections;
import java.util.List;
public class AdsRemovalPurchaseDialog extends BaseMwmDialogFragment
@ -24,13 +24,20 @@ public class AdsRemovalPurchaseDialog extends BaseMwmDialogFragment
private final static Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.BILLING);
private final static String TAG = AdsRemovalPurchaseDialog.class.getSimpleName();
private final static String EXTRA_CURRENT_STATE = "extra_current_state";
@Nullable
private List<SkuDetails> mDetails;
private final static String EXTRA_PRODUCT_DETAILS = "extra_product_details";
private final static int WEEKS_IN_YEAR = 52;
private final static int WEEKS_IN_MONTH = 4;
@NonNull
private ProductDetails[] mProductDetails = new ProductDetails[Period.values().length];
@NonNull
private State mState = State.NONE;
@SuppressWarnings("NullableProblems")
@NonNull
private PurchaseController<AdsRemovalPurchaseCallback> mController;
@SuppressWarnings("NullableProblems")
@NonNull
private View mPayButtonContainer;
@Override
public void onCreate(@Nullable Bundle savedInstanceState)
{
@ -54,6 +61,8 @@ public class AdsRemovalPurchaseDialog extends BaseMwmDialogFragment
{
LOGGER.d(TAG, "onCreateView savedInstanceState = " + savedInstanceState + "this " + this);
View view = inflater.inflate(R.layout.fragment_ads_removal_purchase_dialog, container, false);
mPayButtonContainer = view.findViewById(R.id.pay_button_container);
mPayButtonContainer.setOnClickListener(v -> onYearlyProductClicked());
return view;
}
@ -65,13 +74,25 @@ public class AdsRemovalPurchaseDialog extends BaseMwmDialogFragment
if (savedInstanceState != null)
{
State savedState = State.values()[savedInstanceState.getInt(EXTRA_CURRENT_STATE)];
ProductDetails[] productDetails
= (ProductDetails[]) savedInstanceState.getParcelableArray(EXTRA_PRODUCT_DETAILS);
if (productDetails != null && productDetails.length > 0)
{
mProductDetails = productDetails;
updateYearlyButton();
}
activateState(savedState);
return;
}
else
{
activateState(State.LOADING);
mController.queryPurchaseDetails();
}
activateState(State.LOADING);
mController.queryPurchaseDetails();
}
void onYearlyProductClicked()
{
ProductDetails details = getProductDetailsForPeriod(Period.P1Y);
mController.launchPurchaseFlow(details.getProductId());
}
private void activateState(@NonNull State state)
@ -86,6 +107,7 @@ public class AdsRemovalPurchaseDialog extends BaseMwmDialogFragment
super.onSaveInstanceState(outState);
LOGGER.d(TAG, "onSaveInstanceState");
outState.putInt(EXTRA_CURRENT_STATE, mState.ordinal());
outState.putParcelableArray(EXTRA_PRODUCT_DETAILS, mProductDetails);
}
@Override
@ -96,6 +118,37 @@ public class AdsRemovalPurchaseDialog extends BaseMwmDialogFragment
mController.removeCallback();
}
private void updateYearlyButton()
{
ProductDetails details = getProductDetailsForPeriod(Period.P1Y);
String price = Utils.formatCurrencyString(details.getPrice(), details.getCurrencyCode());
TextView priceView = mPayButtonContainer.findViewById(R.id.price);
priceView.setText(getString(R.string.paybtn_title, price));
TextView savingView = mPayButtonContainer.findViewById(R.id.saving);
String saving = Utils.formatCurrencyString(calculateYearlySaving(), details.getCurrencyCode());
savingView.setText(getString(R.string.paybtn_subtitle, saving));
}
@NonNull
private ProductDetails getProductDetailsForPeriod(@NonNull Period period)
{
return mProductDetails[period.ordinal()];
}
private float calculateYearlySaving()
{
float pricePerWeek = getProductDetailsForPeriod(Period.P1W).getPrice();
float pricePerYear = getProductDetailsForPeriod(Period.P1Y).getPrice();
return pricePerWeek * WEEKS_IN_YEAR - pricePerYear;
}
private float calculateMonthlySaving()
{
float pricePerWeek = getProductDetailsForPeriod(Period.P1W).getPrice();
float pricePerMonth = getProductDetailsForPeriod(Period.P1M).getPrice();
return pricePerWeek * WEEKS_IN_MONTH - pricePerMonth;
}
public enum State
{
NONE
@ -111,7 +164,7 @@ public class AdsRemovalPurchaseDialog extends BaseMwmDialogFragment
@Override
void activate(@NonNull View view)
{
UiUtils.hide(view, R.id.title, R.id.image);
UiUtils.hide(view, R.id.title, R.id.image, R.id.pay_button_container);
UiUtils.show(view, R.id.progress_layout);
}
},
@ -121,7 +174,7 @@ public class AdsRemovalPurchaseDialog extends BaseMwmDialogFragment
void activate(@NonNull View view)
{
UiUtils.hide(view, R.id.progress_layout);
UiUtils.show(view, R.id.title, R.id.image);
UiUtils.show(view, R.id.title, R.id.image, R.id.pay_button_container);
TextView title = view.findViewById(R.id.title);
title.setText(R.string.remove_ads_title);
}
@ -151,7 +204,15 @@ public class AdsRemovalPurchaseDialog extends BaseMwmDialogFragment
@Override
public void onProductDetailsLoaded(@NonNull List<SkuDetails> details)
{
mDetails = Collections.unmodifiableList(details);
for (SkuDetails sku: details)
{
float price = sku.getPriceAmountMicros() / 1000000;
String currencyCode = sku.getPriceCurrencyCode();
Period period = Period.valueOf(sku.getSubscriptionPeriod());
mProductDetails[period.ordinal()] = new ProductDetails(sku.getSku(), price, currencyCode);
}
updateYearlyButton();
activateState(State.PRICE_SELECTION);
}
@ -161,4 +222,12 @@ public class AdsRemovalPurchaseDialog extends BaseMwmDialogFragment
// Coming soon.
}
}
enum Period
{
// Order is important.
P1Y,
P1M,
P1W
}
}

View file

@ -0,0 +1,72 @@
package com.mapswithme.maps.purchase;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
class ProductDetails implements Parcelable
{
@NonNull
private final String mProductId;
private final float mPrice;
@NonNull
private final String mCurrencyCode;
ProductDetails(@NonNull String productId, float price, @NonNull String currencyCode)
{
mProductId = productId;
mPrice = price;
mCurrencyCode = currencyCode;
}
private ProductDetails(Parcel in)
{
this(in.readString(), in.readFloat(), in.readString());
}
float getPrice()
{
return mPrice;
}
@NonNull
String getCurrencyCode()
{
return mCurrencyCode;
}
@NonNull
String getProductId()
{
return mProductId;
}
public static final Creator<ProductDetails> CREATOR = new Creator<ProductDetails>()
{
@Override
public ProductDetails createFromParcel(Parcel in)
{
return new ProductDetails(in);
}
@Override
public ProductDetails[] newArray(int size)
{
return new ProductDetails[size];
}
};
@Override
public int describeContents()
{
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags)
{
dest.writeString(mProductId);
dest.writeFloat(mPrice);
dest.writeString(mCurrencyCode);
}
}

View file

@ -17,7 +17,7 @@ public class PurchaseFactory
BillingManager<PlayStoreBillingCallback> billingManager
= new PlayStoreBillingManager(BillingClient.SkuType.SUBS);
PurchaseValidator<AdsRemovalValidationCallback> validator = new AdsRemovalPurchaseValidator();
return new AdsRemovalPurchaseController(validator, billingManager,
"ads.removal.monthly.test");
return new AdsRemovalPurchaseController(validator, billingManager, "ads.removal.yearly.test",
"ads.removal.monthly.test", "ads.removal.weekly.test");
}
}

View file

@ -518,11 +518,18 @@ public class Utils
@NonNull
public static String formatCurrencyString(@NonNull String price, @NonNull String currencyCode)
{
float value = Float.valueOf(price);
return formatCurrencyString(value, currencyCode);
}
@NonNull
public static String formatCurrencyString(float price, @NonNull String currencyCode)
{
String text;
try
{
float value = Float.valueOf(price);
Locale locale = Locale.getDefault();
Currency currency = Utils.getCurrencyForLocale(locale);
// If the currency cannot be obtained for the default locale we will use Locale.US.
@ -531,7 +538,7 @@ public class Utils
NumberFormat formatter = NumberFormat.getCurrencyInstance(locale);
if (!TextUtils.isEmpty(currencyCode))
formatter.setCurrency(Currency.getInstance(currencyCode));
return formatter.format(value);
return formatter.format(price);
}
catch (Throwable e)
{