forked from organicmaps/organicmaps
[android] Implemented 'Yearly product' button
This commit is contained in:
parent
cb15e71b9d
commit
d263e23485
8 changed files with 212 additions and 15 deletions
|
@ -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"
|
||||
|
|
34
android/res/layout/ads_removal_pay_button_container.xml
Normal file
34
android/res/layout/ads_removal_pay_button_container.xml
Normal 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>
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
72
android/src/com/mapswithme/maps/purchase/ProductDetails.java
Normal file
72
android/src/com/mapswithme/maps/purchase/ProductDetails.java
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue