Fix merge

This commit is contained in:
max-klaus 2019-08-18 19:25:24 +03:00
parent ef52f119d7
commit 30056e5dea
10 changed files with 596 additions and 182 deletions

View file

@ -0,0 +1,171 @@
package net.osmand;
import java.text.ParseException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Period {
public enum PeriodUnit {
YEAR("Y"),
MONTH("M"),
WEEK("W"),
DAY("D");
private String unitStr;
PeriodUnit(String unitStr) {
this.unitStr = unitStr;
}
public String getUnitStr() {
return unitStr;
}
public double getMonthsValue() {
switch (this) {
case YEAR:
return 12d;
case MONTH:
return 1d;
case WEEK:
return 1/4d;
case DAY:
return 1/30d;
}
return 0d;
}
public static PeriodUnit parseUnit(String unitStr) {
for (PeriodUnit unit : values()) {
if (unit.unitStr.equals(unitStr)) {
return unit;
}
}
return null;
}
}
private static final Pattern PATTERN =
Pattern.compile("^P(?:([-+]?[0-9]+)([YMWD]))?$", Pattern.CASE_INSENSITIVE);
private PeriodUnit unit;
private final int numberOfUnits;
public static Period ofYears(int years) {
return new Period(PeriodUnit.YEAR, years);
}
public static Period ofMonths(int months) {
return new Period(PeriodUnit.MONTH, months);
}
public static Period ofWeeks(int weeks) {
return new Period(PeriodUnit.WEEK, weeks);
}
public static Period ofDays(int days) {
return new Period(PeriodUnit.DAY, days);
}
public PeriodUnit getUnit() {
return unit;
}
public int getNumberOfUnits() {
return numberOfUnits;
}
/**
* Obtains a {@code Period} from a text string such as {@code PnY PnM PnD PnW}.
* <p>
* This will parse the string produced by {@code toString()} which is
* based on the ISO-8601 period formats {@code PnY PnM PnD PnW}.
* <p>
* The string cannot start with negative sign.
* The ASCII letter "P" is next in upper or lower case.
* <p>
* For example, the following are valid inputs:
* <pre>
* "P2Y" -- Period.ofYears(2)
* "P3M" -- Period.ofMonths(3)
* "P4W" -- Period.ofWeeks(4)
* "P5D" -- Period.ofDays(5)
* </pre>
*
* @param text the text to parse, not null
* @return the parsed period, not null
* @throws ParseException if the text cannot be parsed to a period
*/
public static Period parse(CharSequence text) throws ParseException {
Matcher matcher = PATTERN.matcher(text);
if (matcher.matches()) {
String numberOfUnitsMatch = matcher.group(1);
String unitMatch = matcher.group(2);
if (numberOfUnitsMatch != null && unitMatch != null) {
try {
int numberOfUnits = parseNumber(numberOfUnitsMatch);
PeriodUnit unit = PeriodUnit.parseUnit(unitMatch);
return new Period(unit, numberOfUnits);
} catch (IllegalArgumentException ex) {
throw new ParseException("Text cannot be parsed to a Period: " + text, 0);
}
}
}
throw new ParseException("Text cannot be parsed to a Period: " + text, 0);
}
private static int parseNumber(String str) throws ParseException {
if (str == null) {
return 0;
}
return Integer.parseInt(str);
}
public Period(PeriodUnit unit, int numberOfUnits) {
if (unit == null) {
throw new IllegalArgumentException("PeriodUnit cannot be null");
}
this.unit = unit;
this.numberOfUnits = numberOfUnits;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof Period) {
Period other = (Period) obj;
return unit.ordinal() == other.unit.ordinal() && numberOfUnits == other.numberOfUnits;
}
return false;
}
@Override
public int hashCode() {
return unit.ordinal() + Integer.rotateLeft(numberOfUnits, 8);
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append('P').append(numberOfUnits);
switch (unit) {
case YEAR:
buf.append('Y');
break;
case MONTH:
buf.append('M');
break;
case WEEK:
buf.append('W');
break;
case DAY:
buf.append('D');
break;
}
return buf.toString();
}
}

View file

@ -6,52 +6,51 @@
android:layout_height="wrap_content"
android:layout_marginLeft="1dp"
android:layout_marginRight="1dp"
android:background="?attr/subscription_active_bg_color"
android:orientation="vertical">
<View
android:id="@+id/div_top"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/subscription_active_div_color"
android:visibility="gone" />
<LinearLayout
android:id="@+id/button_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:baselineAligned="false"
android:minHeight="@dimen/dialog_button_ex_height"
android:orientation="horizontal"
android:orientation="vertical"
android:paddingLeft="@dimen/card_padding"
android:paddingRight="@dimen/card_padding">
<LinearLayout
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/list_header_padding"
android:layout_marginEnd="@dimen/list_content_padding"
android:layout_marginRight="@dimen/list_content_padding"
android:layout_marginBottom="@dimen/list_header_padding"
android:layout_weight="1"
android:orientation="vertical">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="?attr/dialog_title_color"
android:textSize="@dimen/default_list_text_size"
osmand:typeface="@string/font_roboto_regular"
tools:text="Pay monthly" />
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:layout_weight="1"
android:orientation="vertical">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="?attr/dialog_title_color"
android:textSize="@dimen/default_list_text_size"
osmand:typeface="@string/font_roboto_regular"
tools:text="Pay monthly" />
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/description_contribute"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/osm_live_payment_contribute_descr"
android:textColor="?attr/card_description_text_color"
android:textSize="@dimen/default_desc_text_size"
android:visibility="gone"
osmand:typeface="@string/font_roboto_regular"
tools:visibility="visible" />
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/description"
@ -64,11 +63,20 @@
</LinearLayout>
<android.support.v7.widget.AppCompatImageView
android:id="@+id/right_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="@dimen/list_header_padding"
android:layout_marginLeft="@dimen/list_header_padding"
android:src="@drawable/ic_action_singleselect" />
</LinearLayout>
<LinearLayout
android:id="@+id/button_view"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="?attr/wikivoyage_secondary_btn_bg">
@ -76,7 +84,7 @@
<LinearLayout
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="@dimen/dialog_button_height"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:padding="@dimen/list_header_padding">
@ -100,7 +108,7 @@
<LinearLayout
android:id="@+id/button_cancel_view"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="?attr/wikivoyage_primary_btn_bg">
@ -108,7 +116,8 @@
<LinearLayout
android:id="@+id/button_cancel"
android:layout_width="match_parent"
android:layout_height="@dimen/dialog_button_height"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:gravity="center"
android:orientation="horizontal"
android:padding="@dimen/list_header_padding">
@ -121,7 +130,7 @@
android:letterSpacing="@dimen/text_button_letter_spacing"
android:maxWidth="@dimen/dialog_button_ex_max_width"
android:minWidth="@dimen/dialog_button_ex_min_width"
android:text="@string/shared_string_cancel"
android:text="@string/cancel_subscription"
android:textColor="@color/color_white"
android:textSize="@dimen/text_button_text_size"
osmand:typeface="@string/font_roboto_medium" />
@ -136,16 +145,8 @@
android:id="@+id/div"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="@dimen/card_padding"
android:layout_marginRight="@dimen/card_padding"
android:background="?attr/subscription_active_div_color"
android:visibility="gone" />
<View
android:id="@+id/div_bottom"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/subscription_active_div_color"
android:layout_marginTop="@dimen/content_padding_small"
android:background="?attr/wikivoyage_card_divider_color"
android:visibility="gone" />
</LinearLayout>

View file

@ -12,82 +12,52 @@
android:id="@+id/button_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:baselineAligned="false"
android:minHeight="@dimen/dialog_button_ex_height"
android:orientation="horizontal"
android:orientation="vertical"
android:paddingLeft="@dimen/card_padding"
android:paddingRight="@dimen/card_padding">
<LinearLayout
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/list_header_padding"
android:layout_marginEnd="@dimen/list_content_padding"
android:layout_marginRight="@dimen/list_content_padding"
android:layout_marginBottom="@dimen/list_header_padding"
android:layout_weight="1"
android:orientation="vertical">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="?attr/dialog_title_color"
android:textSize="@dimen/default_list_text_size"
osmand:typeface="@string/font_roboto_regular"
tools:text="Pay monthly" />
<LinearLayout
android:layout_width="wrap_content"
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/description_contribute"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:text="@string/osm_live_payment_contribute_descr"
android:textColor="?attr/card_description_text_color"
android:textSize="@dimen/default_desc_text_size"
android:visibility="gone"
osmand:typeface="@string/font_roboto_regular"
tools:visibility="visible" />
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="?attr/card_description_text_color"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_regular"
tools:text="Monthly payment" />
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/discount_banner_regular"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginStart="@dimen/list_item_button_padding"
android:layout_marginLeft="@dimen/list_item_button_padding"
android:background="?attr/text_rounded_bg_regular"
android:textColor="?attr/card_description_text_color"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_regular"
tools:text=" Save 20%! " />
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/discount_banner_active"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginStart="@dimen/list_item_button_padding"
android:layout_marginLeft="@dimen/list_item_button_padding"
android:background="?attr/text_rounded_bg_active"
android:textColor="@color/osmand_orange"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_regular"
tools:text=" Save 20%! " />
</LinearLayout>
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?attr/card_description_text_color"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_regular"
tools:text="$0.62 / month • Save 68%" />
</LinearLayout>
<LinearLayout
android:id="@+id/button_view"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="?attr/btn_round_border_2">
@ -95,7 +65,7 @@
<LinearLayout
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="@dimen/dialog_button_height"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:gravity="center"
android:orientation="horizontal"
@ -119,7 +89,7 @@
android:textColor="?attr/color_dialog_buttons"
android:textSize="@dimen/text_button_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:text="7,99€" />
tools:text="7,99€ / year" />
</LinearLayout>
@ -127,7 +97,7 @@
<LinearLayout
android:id="@+id/button_ex_view"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="?attr/wikivoyage_primary_btn_bg">
@ -135,7 +105,7 @@
<LinearLayout
android:id="@+id/button_ex"
android:layout_width="match_parent"
android:layout_height="@dimen/dialog_button_height"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:gravity="center"
android:orientation="horizontal"
@ -152,7 +122,7 @@
android:textColor="@color/color_white"
android:textSize="@dimen/text_button_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:text="7,99€" />
tools:text="$3.99 for six month\nthan $7.49 / year" />
</LinearLayout>
@ -164,8 +134,7 @@
android:id="@+id/div"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="@dimen/card_padding"
android:layout_marginRight="@dimen/card_padding"
android:layout_marginTop="@dimen/content_padding_small"
android:background="?attr/wikivoyage_card_divider_color"
android:visibility="gone" />

View file

@ -64,6 +64,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:paddingBottom="@dimen/content_padding_small"
android:orientation="vertical"
android:visibility="gone" />

View file

@ -1,5 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="day">День</string>
<string name="days_2_4">Дня</string>
<string name="days_5">Дней</string>
<string name="week">Неделя</string>
<string name="weeks_2_4">Недели</string>
<string name="weeks_5">Недель</string>
<string name="month">Месяц</string>
<string name="months_2_4">Месяца</string>
<string name="months_5">Месяцев</string>
<string name="year">Год</string>
<string name="years_2_4">Года</string>
<string name="years_5">Лет</string>
<string name="months_3">Три месяца</string>
<string name="price_free">Бесплатно</string>
<string name="get_discount_title">Получите %1$d %2$s со скидкой %3$s.</string>
<string name="get_free_trial_title">Начать бесплатный период в %1$d %2$s.</string>
<string name="get_discount_first_part">%1$s за первый %2$s</string>
<string name="get_discount_first_few_part">%1$s за первые %2$s</string>
<string name="get_discount_second_part">затем %1$s</string>
<string name="cancel_subscription">Отменить подписку</string>
<string name="price_and_discount">%1$s • Экономия %2$s</string>
<string name="rendering_attr_winter_road_name">Автозимник</string>
<string name="rendering_attr_ice_road_name">Ледовый автозимник</string>
<string name="routeInfo_winter_ice_road_name">Зимники</string>

View file

@ -11,6 +11,30 @@
Thx - Hardy
-->
<string name="day">Day</string>
<string name="days_2_4">Days</string>
<string name="days_5">Days</string>
<string name="week">Week</string>
<string name="weeks_2_4">Weeks</string>
<string name="weeks_5">Weeks</string>
<string name="month">Month</string>
<string name="months_2_4">Months</string>
<string name="months_5">Months</string>
<string name="year">Year</string>
<string name="years_2_4">Years</string>
<string name="years_5">Years</string>
<string name="months_3">Three months</string>
<string name="price_free">Free</string>
<string name="get_discount_title">Get %1$d %2$s at %3$s off.</string>
<string name="get_free_trial_title">Start your %1$d %2$s free trial.</string>
<string name="get_discount_first_part">%1$s for first %2$s</string>
<string name="get_discount_first_few_part">%1$s for first %2$s</string>
<string name="get_discount_second_part">then %1$s</string>
<string name="cancel_subscription">Cancel subscription</string>
<string name="price_and_discount">%1$s • Save %2$s</string>
<string name="app_mode_wagon">Wagon</string>
<string name="app_mode_pickup_truck">Pickup truck</string>
<string name="shared_string_default">Default</string>
<string name="gpx_join_gaps">Join gaps</string>
<string name="app_mode_camper">Camper</string>
<string name="app_mode_campervan">Campervan</string>

View file

@ -30,6 +30,9 @@ import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.support.v4.text.TextUtilsCompat;
import android.support.v4.view.ViewCompat;
import android.text.ParcelableSpan;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
@ -60,6 +63,7 @@ import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static android.content.Context.POWER_SERVICE;
@ -576,4 +580,8 @@ public class AndroidUtils {
return baseString;
}
}
public static boolean isRTL() {
return TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL;
}
}

View file

@ -45,6 +45,7 @@ import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseListener;
import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseTaskType;
import net.osmand.plus.inapp.InAppPurchases.InAppPurchase;
import net.osmand.plus.inapp.InAppPurchases.InAppSubscription;
import net.osmand.plus.inapp.InAppPurchases.InAppSubscriptionIntroductoryInfo;
import net.osmand.plus.liveupdates.SubscriptionFragment;
import net.osmand.plus.srtmplugin.SRTMPlugin;
import net.osmand.plus.widgets.TextViewEx;
@ -341,38 +342,40 @@ public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment
} else if (osmLiveCardButtonsContainer != null) {
osmLiveCardButtonsContainer.removeAllViews();
View lastBtn = null;
InAppSubscription monthlyLiveUpdates = purchaseHelper.getMonthlyLiveUpdates();
double regularMonthlyPrice = monthlyLiveUpdates.getPriceValue();
List<InAppSubscription> visibleSubscriptions = purchaseHelper.getLiveUpdates().getVisibleSubscriptions();
boolean anyPurchased = false;
boolean anyPurchasedOrIntroducted = false;
for (final InAppSubscription s : visibleSubscriptions) {
if (s.isPurchased()) {
anyPurchased = true;
if (s.isPurchased() || s.getIntroductoryInfo() != null) {
anyPurchasedOrIntroducted = true;
break;
}
}
for (final InAppSubscription s : visibleSubscriptions) {
InAppSubscriptionIntroductoryInfo introductoryInfo = s.getIntroductoryInfo();
boolean hasIntroductoryInfo = introductoryInfo != null;
CharSequence priceTitle = hasIntroductoryInfo ? introductoryInfo.getFormattedDescription(ctx) : s.getPrice(ctx);
CharSequence descriptionText = hasIntroductoryInfo ?
introductoryInfo.getDescriptionTitle(ctx) : s.getDescription(ctx, purchaseHelper.getMonthlyLiveUpdates());
if (s.isPurchased()) {
View buttonPurchased = inflate(R.layout.purchase_dialog_card_button_active_ex, osmLiveCardButtonsContainer);
View buttonContainer = buttonPurchased.findViewById(R.id.button_container);
TextViewEx title = (TextViewEx) buttonPurchased.findViewById(R.id.title);
TextViewEx description = (TextViewEx) buttonPurchased.findViewById(R.id.description);
TextViewEx descriptionContribute = (TextViewEx) buttonPurchased.findViewById(R.id.description_contribute);
descriptionContribute.setVisibility(s.isDonationSupported() ? View.VISIBLE : View.GONE);
TextViewEx buttonTitle = (TextViewEx) buttonPurchased.findViewById(R.id.button_title);
View buttonView = buttonPurchased.findViewById(R.id.button_view);
View buttonCancelView = buttonPurchased.findViewById(R.id.button_cancel_view);
View divTop = buttonPurchased.findViewById(R.id.div_top);
View divBottom = buttonPurchased.findViewById(R.id.div_bottom);
View div = buttonPurchased.findViewById(R.id.div);
AppCompatImageView rightImage = buttonPurchased.findViewById(R.id.right_image);
title.setText(s.getTitle(ctx));
description.setText(s.getDescription(ctx));
buttonTitle.setText(s.getPrice(ctx));
description.setText(descriptionText);
buttonTitle.setText(priceTitle);
buttonView.setVisibility(View.VISIBLE);
buttonCancelView.setVisibility(View.GONE);
buttonPurchased.setOnClickListener(null);
divTop.setVisibility(View.VISIBLE);
div.setVisibility(View.VISIBLE);
divBottom.setVisibility(View.GONE);
div.setVisibility(View.GONE);
rightImage.setVisibility(View.GONE);
if (s.isDonationSupported()) {
buttonPurchased.setOnClickListener(new OnClickListener() {
@Override
@ -387,14 +390,12 @@ public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment
osmLiveCardButtonsContainer.addView(buttonPurchased);
View buttonCancel = inflate(R.layout.purchase_dialog_card_button_active_ex, osmLiveCardButtonsContainer);
buttonContainer = buttonCancel.findViewById(R.id.button_container);
title = (TextViewEx) buttonCancel.findViewById(R.id.title);
description = (TextViewEx) buttonCancel.findViewById(R.id.description);
buttonView = buttonCancel.findViewById(R.id.button_view);
buttonCancelView = buttonCancel.findViewById(R.id.button_cancel_view);
divTop = buttonCancel.findViewById(R.id.div_top);
divBottom = buttonCancel.findViewById(R.id.div_bottom);
div = buttonCancel.findViewById(R.id.div);
rightImage = buttonPurchased.findViewById(R.id.right_image);
title.setText(getString(R.string.osm_live_payment_current_subscription));
description.setText(s.getRenewDescription(ctx));
@ -406,70 +407,34 @@ public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment
manageSubscription(ctx, s.getSku());
}
});
divTop.setVisibility(View.GONE);
div.setVisibility(View.GONE);
divBottom.setVisibility(View.VISIBLE);
div.setVisibility(View.VISIBLE);
rightImage.setVisibility(View.VISIBLE);
osmLiveCardButtonsContainer.addView(buttonCancel);
if (lastBtn != null) {
View lastBtnDiv = lastBtn.findViewById(R.id.div);
if (lastBtnDiv != null) {
lastBtnDiv.setVisibility(View.GONE);
}
View lastBtnDivBottom = lastBtn.findViewById(R.id.div_bottom);
if (lastBtnDivBottom != null) {
lastBtnDivBottom.setVisibility(View.GONE);
}
}
lastBtn = buttonCancel;
} else {
View button = inflate(R.layout.purchase_dialog_card_button_ex, osmLiveCardButtonsContainer);
TextViewEx title = (TextViewEx) button.findViewById(R.id.title);
TextViewEx description = (TextViewEx) button.findViewById(R.id.description);
TextViewEx descriptionContribute = (TextViewEx) button.findViewById(R.id.description_contribute);
descriptionContribute.setVisibility(s.isDonationSupported() ? View.VISIBLE : View.GONE);
View buttonView = button.findViewById(R.id.button_view);
View buttonExView = button.findViewById(R.id.button_ex_view);
TextViewEx buttonTitle = (TextViewEx) button.findViewById(R.id.button_title);
TextViewEx buttonExTitle = (TextViewEx) button.findViewById(R.id.button_ex_title);
buttonView.setVisibility(anyPurchased ? View.VISIBLE : View.GONE);
buttonExView.setVisibility(!anyPurchased ? View.VISIBLE : View.GONE);
boolean showSolidButton = !anyPurchasedOrIntroducted || hasIntroductoryInfo;
buttonView.setVisibility(!showSolidButton ? View.VISIBLE : View.GONE);
buttonExView.setVisibility(showSolidButton ? View.VISIBLE : View.GONE);
TextViewEx discountRegular = (TextViewEx) button.findViewById(R.id.discount_banner_regular);
TextViewEx discountActive = (TextViewEx) button.findViewById(R.id.discount_banner_active);
View div = button.findViewById(R.id.div);
title.setText(s.getTitle(ctx));
description.setText(s.getDescription(ctx));
buttonTitle.setText(s.getPrice(ctx));
buttonExTitle.setText(s.getPrice(ctx));
description.setText(descriptionText);
buttonTitle.setText(priceTitle);
buttonExTitle.setText(priceTitle);
if (regularMonthlyPrice > 0 && s.getMonthlyPriceValue() > 0 && s.getMonthlyPriceValue() < regularMonthlyPrice) {
int discount = (int) ((1 - s.getMonthlyPriceValue() / regularMonthlyPrice) * 100d);
String discountStr = discount + "%";
if (discount > 50) {
discountActive.setText(String.format(" %s ", getString(R.string.osm_live_payment_discount_descr, discountStr)));
discountActive.setVisibility(View.VISIBLE);
discountRegular.setVisibility(View.GONE);
} else if (discount > 0) {
discountActive.setVisibility(View.GONE);
discountRegular.setText(String.format(" %s ", getString(R.string.osm_live_payment_discount_descr, discountStr)));
discountRegular.setVisibility(View.VISIBLE);
} else {
discountActive.setVisibility(View.GONE);
discountRegular.setVisibility(View.GONE);
}
} else {
discountActive.setVisibility(View.GONE);
discountRegular.setVisibility(View.GONE);
}
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
subscribe(s.getSku());
}
});
if (anyPurchased) {
if (!showSolidButton) {
buttonView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@ -494,10 +459,6 @@ public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment
if (div != null) {
div.setVisibility(View.GONE);
}
View divBottom = lastBtn.findViewById(R.id.div_bottom);
if (divBottom != null) {
divBottom.setVisibility(View.GONE);
}
}
if (osmLiveCardProgress != null) {
osmLiveCardProgress.setVisibility(View.GONE);

View file

@ -27,6 +27,7 @@ import net.osmand.plus.inapp.InAppPurchases.InAppPurchase;
import net.osmand.plus.inapp.InAppPurchases.InAppPurchase.PurchaseState;
import net.osmand.plus.inapp.InAppPurchases.InAppPurchaseLiveUpdatesOldSubscription;
import net.osmand.plus.inapp.InAppPurchases.InAppSubscription;
import net.osmand.plus.inapp.InAppPurchases.InAppSubscriptionIntroductoryInfo;
import net.osmand.plus.inapp.InAppPurchases.InAppSubscriptionList;
import net.osmand.plus.inapp.util.BillingManager;
import net.osmand.plus.inapp.util.BillingManager.BillingUpdatesListener;
@ -39,6 +40,7 @@ import org.json.JSONException;
import org.json.JSONObject;
import java.lang.ref.WeakReference;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -603,7 +605,26 @@ public class InAppPurchaseHelper {
String subscriptionPeriod = skuDetails.getSubscriptionPeriod();
if (!Algorithms.isEmpty(subscriptionPeriod)) {
if (inAppPurchase instanceof InAppSubscription) {
((InAppSubscription) inAppPurchase).setSubscriptionPeriod(subscriptionPeriod);
try {
((InAppSubscription) inAppPurchase).setSubscriptionPeriodString(subscriptionPeriod);
} catch (ParseException e) {
LOG.error(e);
}
}
}
if (inAppPurchase instanceof InAppSubscription) {
String introductoryPrice = skuDetails.getIntroductoryPrice();
String introductoryPricePeriod = skuDetails.getIntroductoryPricePeriod();
String introductoryPriceCycles = skuDetails.getIntroductoryPriceCycles();
long introductoryPriceAmountMicros = skuDetails.getIntroductoryPriceAmountMicros();
if (!Algorithms.isEmpty(introductoryPrice)) {
InAppSubscription s = (InAppSubscription) inAppPurchase;
try {
s.setIntroductoryInfo(new InAppSubscriptionIntroductoryInfo(s, introductoryPrice,
introductoryPriceAmountMicros, introductoryPricePeriod, introductoryPriceCycles));
} catch (ParseException e) {
LOG.error(e);
}
}
}
}

View file

@ -1,20 +1,28 @@
package net.osmand.plus.inapp;
import android.content.Context;
import android.graphics.Typeface;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.StyleSpan;
import android.text.style.ForegroundColorSpan;
import com.android.billingclient.api.SkuDetails;
import net.osmand.AndroidUtils;
import net.osmand.Period;
import net.osmand.Period.PeriodUnit;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.Version;
import net.osmand.plus.helpers.FontCache;
import net.osmand.plus.widgets.style.CustomTypefaceSpan;
import net.osmand.util.Algorithms;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Currency;
@ -169,7 +177,7 @@ public class InAppPurchases {
private List<InAppSubscription> subscriptions;
InAppSubscriptionList(@NonNull InAppSubscription[] subscriptionsArray) {
this.subscriptions = Arrays.asList(subscriptionsArray);;
this.subscriptions = Arrays.asList(subscriptionsArray);
}
private List<InAppSubscription> getSubscriptions() {
@ -427,13 +435,204 @@ public class InAppPurchases {
}
}
public static class InAppSubscriptionIntroductoryInfo {
private InAppSubscription subscription;
private String introductoryPrice;
private long introductoryPriceAmountMicros;
private String introductoryPeriodString;
private int introductoryCycles;
private double introductoryPriceValue;
private Period introductoryPeriod;
public InAppSubscriptionIntroductoryInfo(@NonNull InAppSubscription subscription,
String introductoryPrice,
long introductoryPriceAmountMicros,
String introductoryPeriodString,
String introductoryCycles) throws ParseException {
this.subscription = subscription;
this.introductoryPrice = introductoryPrice;
this.introductoryPriceAmountMicros = introductoryPriceAmountMicros;
this.introductoryPeriodString = introductoryPeriodString;
try {
this.introductoryCycles = Integer.parseInt(introductoryCycles);
} catch (NumberFormatException e) {
throw new ParseException("Cannot parse introductoryCycles = " + introductoryCycles, 0);
}
introductoryPriceValue = introductoryPriceAmountMicros / 1000000d;
introductoryPeriod = Period.parse(introductoryPeriodString);
}
public String getIntroductoryPrice() {
return introductoryPrice;
}
public long getIntroductoryPriceAmountMicros() {
return introductoryPriceAmountMicros;
}
public String getIntroductoryPeriodString() {
return introductoryPeriodString;
}
public int getIntroductoryCycles() {
return introductoryCycles;
}
public double getIntroductoryPriceValue() {
return introductoryPriceValue;
}
public double getIntroductoryMonthlyPriceValue() {
return introductoryPriceValue /
(introductoryPeriod.getUnit().getMonthsValue() * introductoryPeriod.getNumberOfUnits());
}
public Period getIntroductoryPeriod() {
return introductoryPeriod;
}
public long getTotalPeriods() {
return introductoryPeriod.getNumberOfUnits() * introductoryCycles;
}
private String getTotalUnitsString(@NonNull Context ctx, boolean original) {
String unitStr = "";
Period subscriptionPeriod = subscription.getSubscriptionPeriod();
PeriodUnit unit = original && subscriptionPeriod != null ? subscriptionPeriod.getUnit() : introductoryPeriod.getUnit();
long totalPeriods = original && subscriptionPeriod != null ? subscriptionPeriod.getNumberOfUnits() : getTotalPeriods();
switch (unit) {
case YEAR:
unitStr = ctx.getString(R.string.year);
break;
case MONTH:
if (totalPeriods == 1) {
unitStr = ctx.getString(R.string.month);
} else if (totalPeriods < 5) {
unitStr = ctx.getString(R.string.months_2_4);
} else {
unitStr = ctx.getString(R.string.months_5);
}
break;
case WEEK:
if (totalPeriods == 1) {
unitStr = ctx.getString(R.string.week);
} else if (totalPeriods < 5) {
unitStr = ctx.getString(R.string.weeks_2_4);
} else {
unitStr = ctx.getString(R.string.weeks_5);
}
break;
case DAY:
if (totalPeriods == 1) {
unitStr = ctx.getString(R.string.day);
} else if (totalPeriods < 5) {
unitStr = ctx.getString(R.string.days_2_4);
} else {
unitStr = ctx.getString(R.string.days_5);
}
break;
}
return unitStr;
}
private String getUnitString(@NonNull Context ctx) {
PeriodUnit unit = introductoryPeriod.getUnit();
switch (unit) {
case YEAR:
return ctx.getString(R.string.year);
case MONTH:
return ctx.getString(R.string.month);
case WEEK:
return ctx.getString(R.string.week);
case DAY:
return ctx.getString(R.string.day);
}
return "";
}
private String getDisountPeriodString(String unitStr, long totalPeriods) {
if (totalPeriods == 1)
return unitStr;
if (AndroidUtils.isRTL()) {
return unitStr + " " + totalPeriods;
} else {
return totalPeriods + " " + unitStr;
}
}
public CharSequence getDescriptionTitle(@NonNull Context ctx) {
long totalPeriods = getTotalPeriods();
String unitStr = getTotalUnitsString(ctx, false).toLowerCase();
int discountPercent = subscription.getDiscountPercent(null);
return ctx.getString(R.string.get_discount_title, totalPeriods, unitStr, discountPercent + "%");
}
public CharSequence getFormattedDescription(@NonNull Context ctx) {
long totalPeriods = getTotalPeriods();
String singleUnitStr = getUnitString(ctx).toLowerCase();
String unitStr = getTotalUnitsString(ctx, false).toLowerCase();
long numberOfUnits = introductoryPeriod.getNumberOfUnits();
Period subscriptionPeriod = subscription.getSubscriptionPeriod();
long originalNumberOfUnits = subscriptionPeriod != null ? subscriptionPeriod.getNumberOfUnits() : 1;
String originalUnitsStr = getTotalUnitsString(ctx, true).toLowerCase();
String originalPriceStr = subscription.getPrice(ctx);
String priceStr = introductoryPrice;
String pricePeriod;
String originalPricePeriod;
if (AndroidUtils.isRTL()) {
pricePeriod = singleUnitStr + " / " + priceStr;
originalPricePeriod = originalUnitsStr + " / " + originalPriceStr;
if (numberOfUnits > 1) {
pricePeriod = unitStr + " " + numberOfUnits + " / " + priceStr;
}
if (originalNumberOfUnits == 3 && subscriptionPeriod.getUnit() == PeriodUnit.MONTH) {
originalPricePeriod = ctx.getString(R.string.months_3).toLowerCase() + " / " + originalPriceStr;
} else if (originalNumberOfUnits > 1) {
originalPricePeriod = originalUnitsStr + " " + originalNumberOfUnits + " / " + originalPriceStr;
}
} else {
pricePeriod = priceStr + " / " + singleUnitStr;
originalPricePeriod = originalPriceStr + " / " + originalUnitsStr;
if (numberOfUnits > 1) {
pricePeriod = priceStr + " / " + numberOfUnits + " " + unitStr;
}
if (originalNumberOfUnits == 3 && subscriptionPeriod.getUnit() == PeriodUnit.MONTH) {
originalPricePeriod = originalPriceStr + " / " + ctx.getString(R.string.months_3).toLowerCase();
} else if (originalNumberOfUnits > 1) {
originalPricePeriod = originalPriceStr + " / " + originalNumberOfUnits + " " + originalUnitsStr;
}
}
String periodPriceStr = introductoryCycles == 1 ? priceStr : pricePeriod;
int firstPartRes = totalPeriods == 1 ? R.string.get_discount_first_part : R.string.get_discount_first_few_part;
SpannableStringBuilder mainPart = new SpannableStringBuilder(ctx.getString(firstPartRes, periodPriceStr, getDisountPeriodString(unitStr, totalPeriods)));
SpannableStringBuilder thenPart = new SpannableStringBuilder(ctx.getString(R.string.get_discount_second_part, originalPricePeriod));
Typeface typefaceRegular = FontCache.getRobotoRegular(ctx);
Typeface typefaceBold = FontCache.getRobotoMedium(ctx);
mainPart.setSpan(new CustomTypefaceSpan(typefaceBold), 0, mainPart.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
int textColor = ContextCompat.getColor(ctx, R.color.white_50_transparent);
thenPart.setSpan(new ForegroundColorSpan(textColor), 0, thenPart.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
thenPart.setSpan(new CustomTypefaceSpan(typefaceRegular), 0, thenPart.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return new SpannableStringBuilder(mainPart).append("\n").append(thenPart);
}
}
public static abstract class InAppSubscription extends InAppPurchase {
private Map<String, InAppSubscription> upgrades = new ConcurrentHashMap<>();
private String skuNoVersion;
private String subscriptionPeriod;
private String subscriptionPeriodString;
private Period subscriptionPeriod;
private boolean upgrade = false;
private InAppSubscriptionIntroductoryInfo introductoryInfo;
InAppSubscription(@NonNull String skuNoVersion, int version) {
super(skuNoVersion + "_v" + version);
this.skuNoVersion = skuNoVersion;
@ -482,12 +681,36 @@ public class InAppPurchases {
return skuNoVersion;
}
public String getSubscriptionPeriod() {
@Nullable
public String getSubscriptionPeriodString() {
return subscriptionPeriodString;
}
@Nullable
public Period getSubscriptionPeriod() {
return subscriptionPeriod;
}
public void setSubscriptionPeriod(String subscriptionPeriod) {
this.subscriptionPeriod = subscriptionPeriod;
public void setSubscriptionPeriodString(String subscriptionPeriodString) throws ParseException {
this.subscriptionPeriodString = subscriptionPeriodString;
this.subscriptionPeriod = Period.parse(subscriptionPeriodString);
}
public InAppSubscriptionIntroductoryInfo getIntroductoryInfo() {
/*
try {
if (subscriptionPeriod != null && subscriptionPeriod.getUnit() == PeriodUnit.YEAR) {
introductoryInfo = new InAppSubscriptionIntroductoryInfo(this, "30 грн.", 30000000L, "P1Y", "1");
}
} catch (ParseException e) {
//
}
*/
return introductoryInfo;
}
public void setIntroductoryInfo(InAppSubscriptionIntroductoryInfo introductoryInfo) {
this.introductoryInfo = introductoryInfo;
}
@Override
@ -504,12 +727,34 @@ public class InAppPurchases {
}
}
public CharSequence getDescription(@NonNull Context ctx, @Nullable InAppSubscription monthlyLiveUpdates) {
CharSequence descr = getDescription(ctx);
int discountPercent = getDiscountPercent(monthlyLiveUpdates);
return discountPercent > 0 ? ctx.getString(R.string.price_and_discount, descr, discountPercent + "%") : descr;
}
public CharSequence getRenewDescription(@NonNull Context ctx) {
return "";
}
@Nullable
protected abstract InAppSubscription newInstance(@NonNull String sku);
public int getDiscountPercent(@Nullable InAppSubscription monthlyLiveUpdates) {
double monthlyPriceValue = getMonthlyPriceValue();
if (monthlyLiveUpdates != null) {
double regularMonthlyPrice = monthlyLiveUpdates.getPriceValue();
if (regularMonthlyPrice > 0 && monthlyPriceValue > 0 && monthlyPriceValue < regularMonthlyPrice) {
return (int) ((1 - monthlyPriceValue / regularMonthlyPrice) * 100d);
}
} else if (introductoryInfo != null) {
double introductoryMonthlyPrice = introductoryInfo.getIntroductoryMonthlyPriceValue();
if (introductoryMonthlyPrice > 0 && monthlyPriceValue > 0 && monthlyPriceValue > introductoryMonthlyPrice) {
return (int) ((1 - introductoryMonthlyPrice / monthlyPriceValue) * 100d);
}
}
return 0;
}
}
public static class InAppPurchaseFullVersion extends InAppPurchase {
@ -623,14 +868,6 @@ public class InAppPurchases {
return ctx.getString(R.string.osm_live_payment_monthly_title);
}
@Override
public CharSequence getDescription(@NonNull Context ctx) {
CharSequence descr = super.getDescription(ctx);
SpannableStringBuilder text = new SpannableStringBuilder(descr).append(". ").append(ctx.getString(R.string.osm_live_payment_contribute_descr));
text.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), descr.length() + 1, text.length() - 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return text;
}
@Override
public CharSequence getRenewDescription(@NonNull Context ctx) {
return ctx.getString(R.string.osm_live_payment_renews_monthly);