Added new subscriptions - merge

This commit is contained in:
crimean 2018-11-01 19:16:58 +03:00
parent 566e2fe544
commit 2724df5f0e
36 changed files with 2033 additions and 606 deletions

View file

@ -24,7 +24,12 @@
<string name="hint_tag">Tag</string>
<string name="hint_value">Value</string>
<!-- DEVELOPMENT -->
<string name="osm_live_default_price">€1,49</string>
<string name="osm_live_default_price">€1,49</string>
<string name="osm_live_monthly_price">€1,99</string>
<string name="osm_live_3_months_price">€3,99</string>
<string name="osm_live_3_months_monthly_price">€1,33</string>
<string name="osm_live_annual_price">€7,99</string>
<string name="osm_live_annual_monthly_price">€2,66</string>
<string name="twitter_address">https://twitter.com/osmandapp</string>
<string name="facebook_address">https://www.facebook.com/osmandapp</string>
<string name="vk_address">https://vk.com/osmandapp</string>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<stroke android:color="@color/color_dialog_buttons_dark" android:width="2dp"/>
<corners android:radius="@dimen/map_button_rect_rad" />
</shape>
</item>
</selector>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<stroke android:color="@color/color_dialog_buttons_light" android:width="2dp"/>
<corners android:radius="@dimen/map_button_rect_rad" />
</shape>
</item>
</selector>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_transparent" />
<stroke
android:width="1dp"
android:color="@color/wikivoyage_active_dark" />
<corners android:radius="@dimen/map_button_rect_rad" />
</shape>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_transparent" />
<stroke
android:width="1dp"
android:color="@color/osmand_orange" />
<corners android:radius="@dimen/map_button_rect_rad" />
</shape>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/wikivoyage_card_divider_dark" />
<corners android:radius="@dimen/map_button_rect_rad" />
</shape>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/wikivoyage_card_divider_light" />
<corners android:radius="@dimen/map_button_rect_rad" />
</shape>

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:osmand="http://schemas.android.com/tools"
android:orientation="vertical"
android:paddingLeft="6dp"
android:paddingRight="6dp"
@ -182,6 +182,37 @@
</LinearLayout>
<View
android:id="@+id/div_button_settings"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/dashboard_divider"
android:visibility="gone"/>
<LinearLayout
android:id="@+id/button_settings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:minHeight="48dp"
android:visibility="gone">
<net.osmand.plus.widgets.TextViewEx
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:layout_marginBottom="4dp"
android:layout_marginRight="16dp"
android:layout_marginTop="4dp"
android:gravity="right"
app:textAllCapsCompat="true"
android:text="@string/shared_string_settings"
android:textColor="?attr/color_dialog_buttons"
android:textSize="@dimen/default_list_text_size"
app:typeface="@string/font_roboto_medium"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View file

@ -1,52 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:osmand="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/list_header_padding"
android:layout_marginLeft="@dimen/card_padding"
android:layout_marginRight="@dimen/card_padding"
android:background="?attr/purchase_dialog_active_card_bg"
android:orientation="vertical">
<include layout="@layout/purchase_dialog_card_header"/>
<View
android:id="@+id/header_div"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="1dp"
android:layout_marginRight="1dp"
android:background="?attr/wikivoyage_card_divider_color"/>
<LinearLayout
android:id="@+id/rows_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/card_descr"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/list_content_padding_large"
android:layout_marginRight="@dimen/list_content_padding_large"
android:layout_marginTop="@dimen/list_header_padding"
android:gravity="center"
android:textColor="?attr/card_description_text_color"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_regular"
tools:text="@string/osm_live_payment_desc"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/card_padding">
<include layout="@layout/purchase_dialog_card_button"/>
</FrameLayout>
</LinearLayout>

View file

@ -0,0 +1,151 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:osmand="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
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:paddingLeft="@dimen/card_padding"
android:paddingRight="@dimen/card_padding">
<LinearLayout
android:layout_width="0dp"
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"
android:layout_height="wrap_content"
android:orientation="horizontal">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/description"
android:layout_width="wrap_content"
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="Monthly payment" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/button_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="?attr/wikivoyage_secondary_btn_bg">
<LinearLayout
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="@dimen/dialog_button_height"
android:gravity="center"
android:orientation="horizontal"
android:padding="@dimen/list_header_padding">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/button_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:letterSpacing="@dimen/text_button_letter_spacing"
android:maxWidth="@dimen/dialog_button_ex_max_width"
android:minWidth="@dimen/dialog_button_ex_min_width"
android:textColor="@color/wikivoyage_secondary_text"
android:textSize="@dimen/text_button_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:text="7,99€" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/button_cancel_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="?attr/wikivoyage_primary_btn_bg">
<LinearLayout
android:id="@+id/button_cancel"
android:layout_width="match_parent"
android:layout_height="@dimen/dialog_button_height"
android:gravity="center"
android:orientation="horizontal"
android:padding="@dimen/list_header_padding">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/button_cancel_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
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:textColor="@color/color_white"
android:textSize="@dimen/text_button_text_size"
osmand:typeface="@string/font_roboto_medium" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<View
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:visibility="gone" />
</LinearLayout>

View file

@ -0,0 +1,134 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:osmand="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="1dp"
android:layout_marginRight="1dp"
android:orientation="vertical">
<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:paddingLeft="@dimen/card_padding"
android:paddingRight="@dimen/card_padding">
<LinearLayout
android:layout_width="0dp"
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"
android:layout_height="wrap_content"
android:orientation="horizontal">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/description"
android:layout_width="wrap_content"
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="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_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_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>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="?attr/btn_round_border_2">
<LinearLayout
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="@dimen/dialog_button_height"
android:gravity="center"
android:orientation="horizontal"
android:padding="@dimen/list_header_padding">
<ProgressBar
android:id="@+id/button_progress"
android:layout_width="@dimen/card_button_progress_size_small"
android:layout_height="@dimen/card_button_progress_size_small"
android:indeterminate="true"
android:visibility="gone" />
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/button_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:letterSpacing="@dimen/text_button_letter_spacing"
android:maxWidth="@dimen/dialog_button_ex_max_width"
android:minWidth="@dimen/dialog_button_ex_min_width"
android:textColor="?attr/color_dialog_buttons"
android:textSize="@dimen/text_button_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:text="7,99€" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<View
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/wikivoyage_card_divider_color"
android:visibility="gone" />
</LinearLayout>

View file

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:osmand="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/card_padding"
android:layout_marginRight="@dimen/card_padding"
android:layout_marginBottom="@dimen/list_header_padding"
android:background="?attr/purchase_dialog_active_card_bg"
android:orientation="vertical">
<include layout="@layout/purchase_dialog_card_header" />
<View
android:id="@+id/header_div"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="1dp"
android:layout_marginRight="1dp"
android:background="?attr/wikivoyage_card_divider_color" />
<LinearLayout
android:id="@+id/rows_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
<View
android:id="@+id/buttons_div"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="1dp"
android:layout_marginRight="1dp"
android:background="?attr/wikivoyage_card_divider_color"/>
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/card_button_subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/card_padding"
android:layout_marginLeft="@dimen/card_padding"
android:layout_marginRight="@dimen/card_padding"
android:layout_marginBottom="@dimen/list_header_padding"
android:text="@string/osm_live_payment_header"
android:textColor="@color/dialog_text_description_color"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_regular" />
<ProgressBar
android:id="@+id/card_progress"
android:layout_width="@dimen/card_button_progress_size"
android:layout_height="@dimen/card_button_progress_size"
android:layout_gravity="center"
android:layout_marginTop="@dimen/card_padding"
android:layout_marginLeft="@dimen/card_padding"
android:layout_marginRight="@dimen/card_padding"
android:layout_marginBottom="@dimen/list_content_padding_large"
android:indeterminate="true"
android:visibility="visible" />
<LinearLayout
android:id="@+id/card_buttons_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone" />
</LinearLayout>

View file

@ -277,7 +277,7 @@
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginTop="16dp"
android:visibility="gone">
android:visibility="visible">
<Button
android:id="@+id/saveChangesButton"
@ -297,105 +297,6 @@
<include layout="@layout/card_bottom_divider"/>
<View
android:layout_width="match_parent"
android:layout_height="8dp"/>
<include layout="@layout/card_top_divider"/>
<LinearLayout
android:id="@+id/purchaseCard"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/bg_color"
android:orientation="vertical"
android:visibility="visible">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal">
<android.support.v7.widget.AppCompatImageView
android:layout_width="56dp"
android:layout_height="48dp"
android:scaleType="center"
android:tint="?attr/color_dialog_buttons"
android:src="@drawable/ic_action_payment_card"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/priceTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:text="@string/osm_live_default_price"
android:textColor="?attr/color_dialog_buttons"
android:textSize="@dimen/default_list_text_size_large"
android:textStyle="bold"/>
<net.osmand.plus.widgets.TextViewEx
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" - "
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/default_list_text_size_large"
android:textStyle="bold"/>
<net.osmand.plus.widgets.TextViewEx
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/osm_live_month_cost"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/default_list_text_size_large"
android:textStyle="bold"/>
</LinearLayout>
<net.osmand.plus.widgets.TextViewEx
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/osm_live_payment_desc"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/default_desc_text_size"/>
<android.support.v7.widget.AppCompatButton
android:id="@+id/subscribeButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="20dp"
android:background="@drawable/blue_button_drawable"
android:paddingBottom="4dp"
android:paddingLeft="14dp"
android:paddingRight="14dp"
android:paddingTop="4dp"
android:text="@string/osm_live_subscribe_btn"
android:textColor="@color/color_white"
android:visibility="visible"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<include layout="@layout/card_bottom_divider"/>
<View
android:layout_width="match_parent"
android:layout_height="16dp"/>

View file

@ -19,6 +19,7 @@
<attr name="expandable_list_background" format="color"/>
<attr name="bg_color" format="reference" />
<attr name="btn_round_border" format="reference" />
<attr name="btn_round_border_2" format="reference" />
<attr name="bg_card" format="reference" />
<attr name="context_menu_card" format="reference" />
<attr name="ctx_menu_card_btn" format="reference" />
@ -74,6 +75,9 @@
<attr name="dialog_text_description_color" format="reference"/>
<attr name="card_description_text_color" format="reference"/>
<attr name="subscription_active_bg_color" format="reference"/>
<attr name="subscription_active_div_color" format="reference"/>
<attr name="wikivoyage_bg_color" format="reference"/>
<attr name="wikivoyage_card_bg_color" format="reference"/>
<attr name="wikivoyage_card_divider_color" format="reference"/>
@ -96,7 +100,10 @@
<attr name="bottom_nav_shadow" format="reference"/>
<attr name="purchase_dialog_active_card_bg" format="reference"/>
</declare-styleable>
<attr name="text_rounded_bg_regular" format="reference" />
<attr name="text_rounded_bg_active" format="reference" />
</declare-styleable>
<declare-styleable name="PagerSlidingTabStrip">
<attr name="pstsTextColor" format="color"/>

View file

@ -1,6 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="subscription_active_bg_color_light">#fff9b2</color>
<color name="subscription_active_bg_color_dark">#43453b</color>
<color name="subscription_active_div_color_light">#f3edae</color>
<color name="subscription_active_div_color_dark">#32332c</color>
<color name="feature_purchased_bg">#78cc5c</color>
<color name="dialog_bg_color_light">#f0f0f0</color>

View file

@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="dialog_button_ex_height">60dp</dimen>
<dimen name="dialog_button_ex_min_width">72dp</dimen>
<dimen name="dialog_button_ex_max_width">120dp</dimen>
<dimen name="wpt_list_category_height">60dp</dimen>
<dimen name="wpt_list_item_height">56dp</dimen>
<dimen name="list_item_button_padding">4dp</dimen>
@ -12,6 +15,7 @@
<dimen name="card_row_min_height">48dp</dimen>
<dimen name="card_button_min_size">58dp</dimen>
<dimen name="card_button_progress_size">32dp</dimen>
<dimen name="card_button_progress_size_small">24dp</dimen>
<dimen name="card_padding">12dp</dimen>
<dimen name="widget_turn_lane_size">36dp</dimen>

View file

@ -10,6 +10,19 @@
- For wording and consistency, please note https://osmand.net/help-online?id=technical-articles#Creating_a_Consistent_User_Experience
Thx - Hardy
-->
<string name="osm_live_payment_monthly_title">Pay monthly</string>
<string name="osm_live_payment_3_months_title">Pay once in 3 months</string>
<string name="osm_live_payment_annual_title">Pay once a year</string>
<string name="osm_live_payment_month_cost_descr">%1$s / month</string>
<string name="osm_live_payment_month_cost_descr_ex">%1$.2f %2$s / month</string>
<string name="osm_live_payment_discount_descr">Save %1$s!</string>
<string name="osm_live_payment_current_subscription">Your current subscription</string>
<string name="osm_live_payment_renews_monthly">Renews monthly</string>
<string name="osm_live_payment_renews_quarterly">Renews quarterly</string>
<string name="osm_live_payment_renews_annually">Renews annually</string>
<string name="default_price_currency_format">%1$.2f %2$s</string>
<string name="osm_live_payment_header">Select the payment period convenient for you:</string>
<string name="osm_live_payment_contribute_descr">Part of income goes to OpenStreetMap contributors.</string>
<string name="markers_remove_dialog_msg">Delete Map marker \'%s\'?</string>
<string name="edit_map_marker">Edit map marker</string>
<string name="third_party_application">Third-party application</string>

View file

@ -130,6 +130,7 @@
<item name="actionBarStyle">@style/Widget.Styled.ActionBarLight</item>
<item name="bg_color">@color/bg_color_light</item>
<item name="btn_round_border">@drawable/btn_round_border_light</item>
<item name="btn_round_border_2">@drawable/btn_round_border_light_2</item>
<item name="bg_card">@drawable/bg_card_light</item>
<item name="context_menu_card">@drawable/context_menu_card_light</item>
<item name="bg_map_context_menu">@drawable/bg_map_context_menu_light</item>
@ -199,6 +200,9 @@
<item name="coordinate_input_edit_text_normal">@color/coordinate_input_edit_text_normal_light</item>
<item name="subscription_active_bg_color">@color/subscription_active_bg_color_light</item>
<item name="subscription_active_div_color">@color/subscription_active_div_color_light</item>
<item name="dialog_bg_color">@color/dialog_bg_color_light</item>
<item name="dialog_transparent_bg_color">@color/dialog_transparent_bg_color_light</item>
<item name="dialog_title_color">@color/dialog_title_color_light</item>
@ -228,6 +232,8 @@
<item name="bottom_nav_shadow">@drawable/bg_bottom_bar_shadow_with_line_day</item>
<item name="text_rounded_bg_active">@drawable/text_rounded_bg_active_light</item>
<item name="text_rounded_bg_regular">@drawable/text_rounded_bg_regular_light</item>
</style>
<style name="OverflowMenuButton" parent="@style/Widget.AppCompat.ActionButton.Overflow">
@ -358,6 +364,7 @@
<item name="actionBarStyle">@style/Widget.Styled.ActionBarDark</item>
<item name="bg_color">@color/bg_color_dark</item>
<item name="btn_round_border">@drawable/btn_round_border_dark</item>
<item name="btn_round_border_2">@drawable/btn_round_border_dark_2</item>
<item name="bg_card">@drawable/bg_card_dark</item>
<item name="context_menu_card">@drawable/context_menu_card_dark</item>
<item name="bg_map_context_menu">@drawable/bg_map_context_menu_dark</item>
@ -375,8 +382,7 @@
<item name="switch_ex_background">@drawable/switch_ex_background_dark</item>
<item name="switch_ex_text_color">@color/switch_ex_button_text_dark</item>
<item name="bg_plugin_logo_disabled">@drawable/bg_plugin_logo_disabled_dark</item>
<item name="plugin_details_install_header_bg">@color/plugin_details_install_header_bg_dark
</item>
<item name="plugin_details_install_header_bg">@color/plugin_details_install_header_bg_dark</item>
<item name="expandable_list_item_background">
@drawable/expandable_list_item_background_dark
</item>
@ -384,8 +390,7 @@
<item name="size_progress_bar">@drawable/size_progressbar_dark</item>
<item name="dash_parking_bg">@drawable/dash_parking_dark</item>
<item name="dashboard_subheader_text_color">@color/dashboard_subheader_text_dark</item>
<item name="dashboard_general_button_text_color">@color/dashboard_general_button_text_dark
</item>
<item name="dashboard_general_button_text_color">@color/dashboard_general_button_text_dark</item>
<item name="android:listChoiceIndicatorMultiple">@drawable/check_dark</item>
<item name="android:textColorPrimary">@color/color_white</item>
<item name="spinnerItemTextColor">@color/color_white</item>
@ -414,6 +419,9 @@
<item name="coordinate_input_edit_text_normal">@color/coordinate_input_edit_text_normal_dark</item>
<item name="subscription_active_bg_color">@color/subscription_active_bg_color_dark</item>
<item name="subscription_active_div_color">@color/subscription_active_div_color_dark</item>
<item name="circle_dialog_bg">@drawable/circle_dialog_bg_night</item>
<item name="dialog_bg_color">@color/dialog_bg_color_night</item>
<item name="dialog_transparent_bg_color">@color/dialog_transparent_bg_color_night</item>
@ -444,6 +452,8 @@
<item name="bottom_nav_shadow">@drawable/bg_bottom_bar_shadow_with_line_night</item>
<item name="text_rounded_bg_active">@drawable/text_rounded_bg_active_dark</item>
<item name="text_rounded_bg_regular">@drawable/text_rounded_bg_regular_dark</item>
</style>
<style name="FreeVersionBanner" parent="OsmandDarkTheme">

View file

@ -7,9 +7,7 @@ import android.os.AsyncTask;
import net.osmand.osm.io.NetworkUtils;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.SQLiteTileSource;
import net.osmand.plus.Version;
import net.osmand.plus.resources.ResourceManager;
import net.osmand.util.Algorithms;
import org.apache.commons.logging.Log;
@ -17,8 +15,6 @@ import org.apache.commons.logging.Log;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@ -29,6 +25,7 @@ import java.net.URLConnection;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.text.MessageFormat;
import java.util.List;
import java.util.Map;
public class AndroidNetworkUtils {
@ -40,6 +37,35 @@ public class AndroidNetworkUtils {
void onResult(String result);
}
public static void sendRequestsAsync(final OsmandApplication ctx,
final List<Request> requests,
final OnRequestResultListener listener) {
new AsyncTask<Void, Void, String>() {
@Override
protected String doInBackground(Void... params) {
for (Request request : requests) {
try {
return sendRequest(ctx, request.getUrl(), request.getParameters(),
request.getUserOperation(), request.isToastAllowed(), request.isPost());
} catch (Exception e) {
// ignore
}
}
return null;
}
@Override
protected void onPostExecute(String response) {
if (listener != null) {
listener.onResult(response);
}
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
}
public static void sendRequestAsync(final OsmandApplication ctx,
final String url,
final Map<String, String> parameters,
@ -193,4 +219,39 @@ public class AndroidNetworkUtils {
ctx.showToastMessage(message);
}
public static class Request {
private String url;
private Map<String, String> parameters;
private String userOperation;
private boolean toastAllowed;
private boolean post;
public Request(String url, Map<String, String> parameters, String userOperation, boolean toastAllowed, boolean post) {
this.url = url;
this.parameters = parameters;
this.userOperation = userOperation;
this.toastAllowed = toastAllowed;
this.post = post;
}
public String getUrl() {
return url;
}
public Map<String, String> getParameters() {
return parameters;
}
public String getUserOperation() {
return userOperation;
}
public boolean isToastAllowed() {
return toastAllowed;
}
public boolean isPost() {
return post;
}
}
}

View file

@ -180,7 +180,7 @@ public class AppInitializer implements IProgress {
if(prevAppVersion < VERSION_2_3) {
startPrefs.edit().putInt(VERSION_INSTALLED_NUMBER, VERSION_2_3).commit();
} else if (prevAppVersion < VERSION_3_2) {
app.getSettings().BILLING_PURCHASE_TOKEN_SENT.set(false);
app.getSettings().BILLING_PURCHASE_TOKENS_SENT.set("");
startPrefs.edit().putInt(VERSION_INSTALLED_NUMBER, VERSION_3_2).commit();
}
startPrefs.edit().putString(VERSION_INSTALLED, Version.getFullVersion(app)).commit();

View file

@ -970,6 +970,7 @@ public class OsmandSettings {
public final OsmandPreference<String> BILLING_USER_COUNTRY_DOWNLOAD_NAME = new StringPreference("billing_user_country_download_name", BILLING_USER_DONATION_NONE_PARAMETER).makeGlobal();
public final OsmandPreference<Boolean> BILLING_HIDE_USER_NAME = new BooleanPreference("billing_hide_user_name", false).makeGlobal();
public final OsmandPreference<Boolean> BILLING_PURCHASE_TOKEN_SENT = new BooleanPreference("billing_purchase_token_sent", false).makeGlobal();
public final OsmandPreference<String> BILLING_PURCHASE_TOKENS_SENT = new StringPreference("billing_purchase_tokens_sent", "").makeGlobal();
public final OsmandPreference<Boolean> LIVE_UPDATES_PURCHASED = new BooleanPreference("billing_live_updates_purchased", false).makeGlobal();
public final OsmandPreference<Long> LIVE_UPDATES_PURCHASE_CANCELLED_TIME = new LongPreference("live_updates_purchase_cancelled_time", 0).makeGlobal();
public final OsmandPreference<Boolean> LIVE_UPDATES_PURCHASE_CANCELLED_FIRST_DLG_SHOWN = new BooleanPreference("live_updates_purchase_cancelled_first_dlg_shown", false).makeGlobal();

View file

@ -176,7 +176,7 @@ public class OsmandInAppPurchaseActivity extends AppCompatActivity implements In
@Override
public void onItemPurchased(String sku, boolean active) {
if (purchaseHelper != null && purchaseHelper.getSkuLiveUpdates().equals(sku)) {
if (purchaseHelper != null && purchaseHelper.getLiveUpdates().containsSku(sku)) {
getMyApplication().logEvent(this, "live_osm_subscription_purchased");
if (!active) {

View file

@ -1,9 +1,12 @@
package net.osmand.plus.chooseplan;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.ColorRes;
@ -11,6 +14,7 @@ import android.support.annotation.DrawableRes;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.content.ContextCompat;
@ -25,6 +29,7 @@ import android.view.Window;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import net.osmand.AndroidUtils;
import net.osmand.PlatformUtil;
@ -39,8 +44,11 @@ import net.osmand.plus.download.DownloadValidationManager;
import net.osmand.plus.inapp.InAppPurchaseHelper;
import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseListener;
import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseTaskType;
import net.osmand.plus.inapp.InAppPurchases.InAppSubscription;
import net.osmand.plus.liveupdates.SubscriptionFragment;
import net.osmand.plus.srtmplugin.SRTMPlugin;
import net.osmand.plus.widgets.TextViewEx;
import net.osmand.util.Algorithms;
import org.apache.commons.logging.Log;
@ -53,7 +61,8 @@ public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment
private boolean nightMode;
private View osmLiveCardButton;
private ViewGroup osmLiveCardButtonsContainer;
private ProgressBar osmLiveCardProgress;
private View planTypeCardButton;
public interface ChoosePlanDialogListener {
@ -61,26 +70,32 @@ public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment
}
public enum OsmAndFeature {
WIKIVOYAGE_OFFLINE(R.string.wikivoyage_offline),
DAILY_MAP_UPDATES(R.string.daily_map_updates),
MONTHLY_MAP_UPDATES(R.string.monthly_map_updates),
UNLIMITED_DOWNLOADS(R.string.unlimited_downloads),
WIKIPEDIA_OFFLINE(R.string.wikipedia_offline),
CONTOUR_LINES_HILLSHADE_MAPS(R.string.contour_lines_hillshade_maps),
SEA_DEPTH_MAPS(R.string.index_item_depth_contours_osmand_ext),
UNLOCK_ALL_FEATURES(R.string.unlock_all_features),
DONATION_TO_OSM(R.string.donation_to_osm);
WIKIVOYAGE_OFFLINE(R.string.wikivoyage_offline, R.drawable.ic_action_explore),
DAILY_MAP_UPDATES(R.string.daily_map_updates, R.drawable.ic_action_time_span),
MONTHLY_MAP_UPDATES(R.string.monthly_map_updates, R.drawable.ic_action_sand_clock),
UNLIMITED_DOWNLOADS(R.string.unlimited_downloads, R.drawable.ic_action_unlimited_download),
WIKIPEDIA_OFFLINE(R.string.wikipedia_offline, R.drawable.ic_plugin_wikipedia),
CONTOUR_LINES_HILLSHADE_MAPS(R.string.contour_lines_hillshade_maps, R.drawable.ic_plugin_srtm),
SEA_DEPTH_MAPS(R.string.index_item_depth_contours_osmand_ext, R.drawable.ic_action_nautical_depth),
UNLOCK_ALL_FEATURES(R.string.unlock_all_features, R.drawable.ic_action_osmand_logo),
DONATION_TO_OSM(R.string.donation_to_osm, 0);
private final int key;
private final int iconId;
OsmAndFeature(int key) {
OsmAndFeature(int key, int iconId) {
this.key = key;
this.iconId = iconId;
}
public String toHumanString(Context ctx) {
return ctx.getString(key);
}
public int getIconId() {
return iconId;
}
public boolean isFeaturePurchased(OsmandApplication ctx) {
if (InAppPurchaseHelper.isSubscribedToLiveUpdates(ctx)) {
return true;
@ -205,7 +220,7 @@ public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
Activity activity = getActivity();
if (activity != null && activity instanceof ChoosePlanDialogListener) {
if (activity instanceof ChoosePlanDialogListener) {
((ChoosePlanDialogListener) activity).onChoosePlanDialogDismiss();
}
}
@ -252,7 +267,7 @@ public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment
}
private ViewGroup buildOsmLiveCard(@NonNull Context ctx, ViewGroup container) {
ViewGroup cardView = (ViewGroup) inflate(R.layout.purchase_dialog_active_card, container);
ViewGroup cardView = (ViewGroup) inflate(R.layout.purchase_dialog_osm_live_card, container);
TextView headerTitle = (TextView) cardView.findViewById(R.id.header_title);
TextView headerDescr = (TextView) cardView.findViewById(R.id.header_descr);
headerTitle.setText(R.string.osm_live);
@ -269,6 +284,7 @@ public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment
imgView.setVisibility(View.GONE);
imgPurchasedView.setVisibility(View.VISIBLE);
} else {
imgView.setImageResource(feature.getIconId());
imgView.setVisibility(View.VISIBLE);
imgPurchasedView.setVisibility(View.GONE);
}
@ -283,54 +299,187 @@ public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment
if (featureRowDiv != null) {
featureRowDiv.setVisibility(View.GONE);
}
TextViewEx cardDescription = (TextViewEx) cardView.findViewById(R.id.card_descr);
cardDescription.setText(R.string.osm_live_payment_desc);
osmLiveCardButton = cardView.findViewById(R.id.card_button);
osmLiveCardButtonsContainer = (ViewGroup) cardView.findViewById(R.id.card_buttons_container);
osmLiveCardProgress = (ProgressBar) cardView.findViewById(R.id.card_progress);
if (osmLiveCardProgress != null) {
int color = ContextCompat.getColor(ctx, nightMode ? R.color.wikivoyage_active_dark : R.color.wikivoyage_active_light);
osmLiveCardProgress.getIndeterminateDrawable().setColorFilter(color, android.graphics.PorterDuff.Mode.MULTIPLY);
}
return cardView;
}
private void setupOsmLiveCardButton(boolean progress) {
if (osmLiveCardButton != null) {
ProgressBar progressBar = (ProgressBar) osmLiveCardButton.findViewById(R.id.card_button_progress);
TextViewEx buttonTitle = (TextViewEx) osmLiveCardButton.findViewById(R.id.card_button_title);
TextViewEx buttonSubtitle = (TextViewEx) osmLiveCardButton.findViewById(R.id.card_button_subtitle);
if (purchaseHelper == null || !purchaseHelper.hasPrices()) {
buttonTitle.setText(getString(R.string.purchase_subscription_title, getString(R.string.osm_live_default_price)));
} else {
buttonTitle.setText(getString(R.string.purchase_subscription_title, purchaseHelper.getLiveUpdatesPrice()));
@SuppressLint("CutPasteId")
private void setupOsmLiveCardButtons(boolean progress) {
final Context ctx = getContext();
if (ctx == null) {
return;
}
if (progress) {
if (osmLiveCardButtonsContainer != null) {
osmLiveCardButtonsContainer.setVisibility(View.GONE);
}
buttonSubtitle.setText(R.string.osm_live_month_cost_desc);
if (progress) {
buttonTitle.setVisibility(View.GONE);
buttonSubtitle.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
osmLiveCardButton.setOnClickListener(null);
} else {
buttonTitle.setVisibility(View.VISIBLE);
buttonSubtitle.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
osmLiveCardButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
subscript();
if (osmLiveCardProgress != null) {
osmLiveCardProgress.setVisibility(View.VISIBLE);
}
} else if (osmLiveCardButtonsContainer != null) {
osmLiveCardButtonsContainer.removeAllViews();
View lastBtn = null;
InAppSubscription monthlyLiveUpdates = purchaseHelper.getMonthlyLiveUpdates();
double regularMonthlyPrice = monthlyLiveUpdates.getPriceValue();
for (final InAppSubscription s : purchaseHelper.getLiveUpdates().getVisibleSubscriptions()) {
if (s.isPurchased()) {
if (lastBtn != null) {
View lastBtnDiv = lastBtn.findViewById(R.id.div);
if (lastBtnDiv != null) {
lastBtnDiv.setVisibility(View.GONE);
}
}
});
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 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);
title.setText(s.getTitle(ctx));
description.setText(s.getDescription(ctx));
buttonTitle.setText(s.getPrice(ctx));
buttonView.setVisibility(View.VISIBLE);
buttonCancelView.setVisibility(View.GONE);
buttonPurchased.setOnClickListener(null);
divTop.setVisibility(View.VISIBLE);
div.setVisibility(View.VISIBLE);
divBottom.setVisibility(View.GONE);
if (s.isDonationSupported()) {
buttonPurchased.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
showDonationSettings();
dismiss();
}
});
} else {
buttonPurchased.setOnClickListener(null);
}
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);
buttonContainer.setBackgroundDrawable(null);
title.setText(getString(R.string.osm_live_payment_current_subscription));
description.setText(s.getRenewDescription(ctx));
buttonView.setVisibility(View.GONE);
buttonCancelView.setVisibility(View.VISIBLE);
buttonCancel.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
manageSubscription(ctx, s.getSku());
}
});
divTop.setVisibility(View.GONE);
div.setVisibility(View.GONE);
divBottom.setVisibility(View.VISIBLE);
osmLiveCardButtonsContainer.addView(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 buttonTitle = (TextViewEx) button.findViewById(R.id.button_title);
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));
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());
}
});
div.setVisibility(View.VISIBLE);
osmLiveCardButtonsContainer.addView(button);
lastBtn = button;
}
}
if (lastBtn != null) {
View div = lastBtn.findViewById(R.id.div);
if (div != null) {
div.setVisibility(View.GONE);
}
}
if (osmLiveCardProgress != null) {
osmLiveCardProgress.setVisibility(View.GONE);
}
osmLiveCardButtonsContainer.setVisibility(View.VISIBLE);
}
}
private void showDonationSettings() {
FragmentActivity activity = getActivity();
if (activity != null) {
SubscriptionFragment subscriptionFragment = new SubscriptionFragment();
subscriptionFragment.show(activity.getSupportFragmentManager(), SubscriptionFragment.TAG);
}
}
private void subscribe(String sku) {
if (!app.getSettings().isInternetConnectionAvailable(true)) {
Toast.makeText(app, R.string.internet_not_available, Toast.LENGTH_LONG).show();
} else {
FragmentActivity ctx = getActivity();
if (ctx != null && purchaseHelper != null) {
OsmandSettings settings = app.getSettings();
purchaseHelper.purchaseLiveUpdates(ctx, sku,
settings.BILLING_USER_EMAIL.get(),
settings.BILLING_USER_NAME.get(),
settings.BILLING_USER_COUNTRY_DOWNLOAD_NAME.get(),
settings.BILLING_HIDE_USER_NAME.get());
}
}
}
private void subscript() {
FragmentActivity ctx = getActivity();
if (ctx != null && purchaseHelper != null) {
OsmandSettings settings = app.getSettings();
purchaseHelper.purchaseLiveUpdates(ctx,
settings.BILLING_USER_EMAIL.get(),
settings.BILLING_USER_NAME.get(),
settings.BILLING_USER_COUNTRY_DOWNLOAD_NAME.get(),
settings.BILLING_HIDE_USER_NAME.get());
private void manageSubscription(@NonNull Context ctx, @Nullable String sku) {
String url = "https://play.google.com/store/account/subscriptions?package=" + ctx.getPackageName();
if (!Algorithms.isEmpty(sku)) {
url += "&sku=" + sku;
}
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
}
private ViewGroup buildPlanTypeCard(@NonNull Context ctx, ViewGroup container) {
@ -366,6 +515,7 @@ public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment
imgView.setVisibility(View.GONE);
imgPurchasedView.setVisibility(View.VISIBLE);
} else {
imgView.setImageResource(feature.getIconId());
imgView.setVisibility(View.VISIBLE);
imgPurchasedView.setVisibility(View.GONE);
}
@ -408,7 +558,7 @@ public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment
@Nullable
public MapActivity getMapActivity() {
Activity activity = getActivity();
if (activity != null && activity instanceof MapActivity) {
if (activity instanceof MapActivity) {
return (MapActivity) activity;
}
return null;
@ -424,8 +574,8 @@ public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment
}
boolean requestingInventory = purchaseHelper != null && purchaseHelper.getActiveTask() == InAppPurchaseTaskType.REQUEST_INVENTORY;
if (osmLiveCardButton != null) {
setupOsmLiveCardButton(requestingInventory);
if (osmLiveCardButtonsContainer != null) {
setupOsmLiveCardButtons(requestingInventory);
}
if (planTypeCardButton != null) {
setupPlanTypeCardButton(requestingInventory);
@ -445,7 +595,7 @@ public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment
@Override
public void onError(InAppPurchaseTaskType taskType, String error) {
if (taskType == InAppPurchaseTaskType.REQUEST_INVENTORY) {
setupOsmLiveCardButton(false);
setupOsmLiveCardButtons(false);
setupPlanTypeCardButton(false);
}
}
@ -456,13 +606,19 @@ public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment
@Override
public void onItemPurchased(String sku, boolean active) {
if (purchaseHelper != null) {
InAppSubscription s = purchaseHelper.getLiveUpdates().getSubscriptionBySku(sku);
if (s != null && s.isDonationSupported()) {
showDonationSettings();
}
}
dismiss();
}
@Override
public void showProgress(InAppPurchaseTaskType taskType) {
if (taskType == InAppPurchaseTaskType.REQUEST_INVENTORY) {
setupOsmLiveCardButton(true);
setupOsmLiveCardButtons(true);
setupPlanTypeCardButton(true);
}
}
@ -470,7 +626,7 @@ public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment
@Override
public void dismissProgress(InAppPurchaseTaskType taskType) {
if (taskType == InAppPurchaseTaskType.REQUEST_INVENTORY) {
setupOsmLiveCardButton(false);
setupOsmLiveCardButtons(false);
setupPlanTypeCardButton(false);
}
}

View file

@ -19,7 +19,6 @@ public class ChoosePlanFreeBannerDialogFragment extends ChoosePlanDialogFragment
OsmAndFeature.CONTOUR_LINES_HILLSHADE_MAPS,
OsmAndFeature.SEA_DEPTH_MAPS,
OsmAndFeature.UNLOCK_ALL_FEATURES,
OsmAndFeature.DONATION_TO_OSM,
};
private final OsmAndFeature[] selectedOsmLiveFeatures = {
OsmAndFeature.DAILY_MAP_UPDATES,
@ -76,11 +75,7 @@ public class ChoosePlanFreeBannerDialogFragment extends ChoosePlanDialogFragment
@Override
public String getPlanTypeButtonTitle() {
InAppPurchaseHelper purchaseHelper = getOsmandApplication().getInAppPurchaseHelper();
if (purchaseHelper == null || !purchaseHelper.hasPrices()) {
return getString(R.string.purchase_unlim_title, getString(R.string.full_version_price));
} else {
return getString(R.string.purchase_unlim_title, purchaseHelper.getFullVersionPrice());
}
return getString(R.string.purchase_unlim_title, purchaseHelper.getFullVersion().getPrice(getContext()));
}
@Override

View file

@ -18,7 +18,6 @@ public class ChoosePlanHillshadeSrtmDialogFragment extends ChoosePlanDialogFragm
OsmAndFeature.WIKIPEDIA_OFFLINE,
OsmAndFeature.WIKIVOYAGE_OFFLINE,
OsmAndFeature.UNLOCK_ALL_FEATURES,
OsmAndFeature.DONATION_TO_OSM,
};
private final OsmAndFeature[] selectedOsmLiveFeatures = {
OsmAndFeature.CONTOUR_LINES_HILLSHADE_MAPS,
@ -68,11 +67,7 @@ public class ChoosePlanHillshadeSrtmDialogFragment extends ChoosePlanDialogFragm
@Override
public String getPlanTypeButtonTitle() {
InAppPurchaseHelper purchaseHelper = getOsmandApplication().getInAppPurchaseHelper();
if (purchaseHelper == null || !purchaseHelper.hasPrices()) {
return getString(R.string.purchase_unlim_title, getString(R.string.srtm_plugin_price));
} else {
return getString(R.string.purchase_unlim_title, purchaseHelper.getContourLinesPrice());
}
return getString(R.string.purchase_unlim_title, purchaseHelper.getContourLines().getPrice(getContext()));
}
@Override

View file

@ -18,7 +18,6 @@ public class ChoosePlanSeaDepthMapsDialogFragment extends ChoosePlanDialogFragme
OsmAndFeature.DAILY_MAP_UPDATES,
OsmAndFeature.UNLIMITED_DOWNLOADS,
OsmAndFeature.UNLOCK_ALL_FEATURES,
OsmAndFeature.DONATION_TO_OSM,
};
private final OsmAndFeature[] selectedOsmLiveFeatures = {
OsmAndFeature.CONTOUR_LINES_HILLSHADE_MAPS,
@ -68,11 +67,7 @@ public class ChoosePlanSeaDepthMapsDialogFragment extends ChoosePlanDialogFragme
@Override
public String getPlanTypeButtonTitle() {
InAppPurchaseHelper purchaseHelper = getOsmandApplication().getInAppPurchaseHelper();
if (purchaseHelper == null || !purchaseHelper.hasPrices()) {
return getString(R.string.purchase_unlim_title, getString(R.string.sea_depth_maps_price));
} else {
return getString(R.string.purchase_unlim_title, purchaseHelper.getDepthContoursPrice());
}
return getString(R.string.purchase_unlim_title, purchaseHelper.getDepthContours().getPrice(getContext()));
}
@Override

View file

@ -11,7 +11,6 @@ public class ChoosePlanWikipediaDialogFragment extends ChoosePlanFreeBannerDialo
OsmAndFeature.CONTOUR_LINES_HILLSHADE_MAPS,
OsmAndFeature.SEA_DEPTH_MAPS,
OsmAndFeature.UNLOCK_ALL_FEATURES,
OsmAndFeature.DONATION_TO_OSM,
};
private final OsmAndFeature[] selectedOsmLiveFeatures = {
OsmAndFeature.WIKIPEDIA_OFFLINE,

View file

@ -8,7 +8,6 @@ import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.ColorRes;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
@ -56,7 +55,6 @@ public class OsmLiveCancelledDialog extends BaseOsmAndDialogFragment implements
OsmAndFeature.CONTOUR_LINES_HILLSHADE_MAPS,
OsmAndFeature.SEA_DEPTH_MAPS,
OsmAndFeature.UNLOCK_ALL_FEATURES,
OsmAndFeature.DONATION_TO_OSM,
};
@Override
@ -93,7 +91,9 @@ public class OsmLiveCancelledDialog extends BaseOsmAndDialogFragment implements
if (ctx == null) {
return null;
}
View view = inflate(R.layout.osmlive_cancelled_dialog_fragment, container);
int themeRes = nightMode ? R.style.OsmandDarkTheme_DarkActionbar : R.style.OsmandLightTheme_DarkActionbar_LightStatusBar;
View view = LayoutInflater.from(new ContextThemeWrapper(getContext(), themeRes))
.inflate(R.layout.osmlive_cancelled_dialog_fragment, container, false);
view.findViewById(R.id.button_close).setOnClickListener(new View.OnClickListener() {
@Override
@ -118,7 +118,7 @@ public class OsmLiveCancelledDialog extends BaseOsmAndDialogFragment implements
@Nullable
public MapActivity getMapActivity() {
Activity activity = getActivity();
if (activity != null && activity instanceof MapActivity) {
if (activity instanceof MapActivity) {
return (MapActivity) activity;
}
return null;
@ -194,11 +194,13 @@ public class OsmLiveCancelledDialog extends BaseOsmAndDialogFragment implements
ProgressBar progressBar = (ProgressBar) osmLiveButton.findViewById(R.id.card_button_progress);
TextViewEx buttonTitle = (TextViewEx) osmLiveButton.findViewById(R.id.card_button_title);
TextViewEx buttonSubtitle = (TextViewEx) osmLiveButton.findViewById(R.id.card_button_subtitle);
/*
if (!purchaseHelper.hasPrices()) {
buttonTitle.setText(getString(R.string.purchase_subscription_title, getString(R.string.osm_live_default_price)));
} else {
buttonTitle.setText(getString(R.string.purchase_subscription_title, purchaseHelper.getLiveUpdatesPrice()));
}
*/
buttonSubtitle.setText(R.string.osm_live_month_cost_desc);
if (progress) {
buttonTitle.setVisibility(View.GONE);
@ -224,7 +226,7 @@ public class OsmLiveCancelledDialog extends BaseOsmAndDialogFragment implements
FragmentActivity ctx = getActivity();
if (ctx != null && purchaseHelper != null) {
OsmandSettings settings = app.getSettings();
purchaseHelper.purchaseLiveUpdates(ctx,
purchaseHelper.purchaseLiveUpdates(ctx, "",
settings.BILLING_USER_EMAIL.get(),
settings.BILLING_USER_NAME.get(),
settings.BILLING_USER_COUNTRY_DOWNLOAD_NAME.get(),
@ -232,12 +234,6 @@ public class OsmLiveCancelledDialog extends BaseOsmAndDialogFragment implements
}
}
private View inflate(@LayoutRes int layoutId, @Nullable ViewGroup container) {
int themeRes = nightMode ? R.style.OsmandDarkTheme_DarkActionbar : R.style.OsmandLightTheme_DarkActionbar_LightStatusBar;
return LayoutInflater.from(new ContextThemeWrapper(getContext(), themeRes))
.inflate(layoutId, container, false);
}
public static boolean shouldShowDialog(OsmandApplication app) {
OsmandSettings settings = app.getSettings();
long cancelledTime = settings.LIVE_UPDATES_PURCHASE_CANCELLED_TIME.get();

View file

@ -29,6 +29,8 @@ import net.osmand.plus.Version;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.chooseplan.ChoosePlanDialogFragment;
import net.osmand.plus.inapp.InAppPurchaseHelper;
import net.osmand.plus.inapp.InAppPurchases.InAppPurchase;
import net.osmand.plus.inapp.InAppPurchases.InAppSubscription;
import net.osmand.plus.poi.PoiUIFilter;
import net.osmand.plus.search.QuickSearchHelper;
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarController;
@ -128,9 +130,15 @@ public class DiscountHelper {
if (data.url.startsWith(INAPP_PREFIX) && data.url.length() > INAPP_PREFIX.length()) {
String inAppSku = data.url.substring(INAPP_PREFIX.length());
InAppPurchaseHelper purchaseHelper = app.getInAppPurchaseHelper();
if (purchaseHelper != null
&& (purchaseHelper.isPurchased(inAppSku) || InAppPurchaseHelper.isSubscribedToLiveUpdates(app))) {
return;
if (purchaseHelper != null) {
if (purchaseHelper.isPurchased(inAppSku) || InAppPurchaseHelper.isSubscribedToLiveUpdates(app)) {
return;
} else {
InAppSubscription discountSubscription = purchaseHelper.getLiveUpdates().applyDiscountSubscription(inAppSku);
if (discountSubscription != null && discountSubscription.fetchRequired()) {
purchaseHelper.requestInventory();
}
}
}
}
@ -251,15 +259,20 @@ public class DiscountHelper {
private static void openUrl(final MapActivity mapActivity, String url) {
if (url.startsWith(INAPP_PREFIX)) {
if (url.contains(InAppPurchaseHelper.SKU_FULL_VERSION_PRICE)) {
OsmandApplication app = mapActivity.getMyApplication();
app.logEvent(mapActivity, "in_app_purchase_redirect");
InAppPurchaseHelper purchaseHelper = app.getInAppPurchaseHelper();
if (purchaseHelper != null) {
OsmandApplication app = mapActivity.getMyApplication();
InAppPurchaseHelper purchaseHelper = app.getInAppPurchaseHelper();
if (purchaseHelper != null) {
if (url.contains(purchaseHelper.getFullVersion().getSku())) {
app.logEvent(mapActivity, "in_app_purchase_redirect");
purchaseHelper.purchaseFullVersion(mapActivity);
} else {
for (InAppPurchase p : purchaseHelper.getLiveUpdates().getAllSubscriptions()) {
if (url.contains(p.getSku())) {
ChoosePlanDialogFragment.showOsmLiveInstance(mapActivity.getSupportFragmentManager());
break;
}
}
}
} else if (url.contains(InAppPurchaseHelper.SKU_LIVE_UPDATES)) {
ChoosePlanDialogFragment.showOsmLiveInstance(mapActivity.getSupportFragmentManager());
}
} else if (url.startsWith(SEARCH_QUERY_PREFIX)) {
String query = url.substring(SEARCH_QUERY_PREFIX.length());

View file

@ -5,6 +5,8 @@ import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import net.osmand.AndroidNetworkUtils;
@ -14,6 +16,11 @@ import net.osmand.plus.OsmandSettings;
import net.osmand.plus.OsmandSettings.OsmandPreference;
import net.osmand.plus.R;
import net.osmand.plus.Version;
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.InAppSubscriptionList;
import net.osmand.plus.inapp.util.IabHelper;
import net.osmand.plus.inapp.util.IabHelper.OnIabPurchaseFinishedListener;
import net.osmand.plus.inapp.util.IabHelper.QueryInventoryFinishedListener;
@ -30,35 +37,25 @@ import org.json.JSONObject;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static net.osmand.plus.inapp.util.IabHelper.ITEM_TYPE_SUBS;
public class InAppPurchaseHelper {
// Debug tag, for logging
private static final String TAG = InAppPurchaseHelper.class.getSimpleName();
boolean mDebugLog = true;
private boolean mDebugLog = true;
public static final long SUBSCRIPTION_HOLDING_TIME_MSEC = 1000 * 60 * 60 * 24 * 3; // 3 days
private InAppPurchases purchases;
private long lastValidationCheckTime;
private String liveUpdatesPrice;
private String fullVersionPrice;
private String depthContoursPrice;
private String contourLinesPrice;
public static final String SKU_FULL_VERSION_PRICE = "osmand_full_version_price";
private static final String SKU_LIVE_UPDATES_FULL = "osm_live_subscription_2";
private static final String SKU_LIVE_UPDATES_FREE = "osm_free_live_subscription_2";
private static final String SKU_DEPTH_CONTOURS_FULL = "net.osmand.seadepth_plus";
private static final String SKU_DEPTH_CONTOURS_FREE = "net.osmand.seadepth";
private static final String SKU_CONTOUR_LINES_FULL = "net.osmand.contourlines_plus";
private static final String SKU_CONTOUR_LINES_FREE = "net.osmand.contourlines";
public static String SKU_LIVE_UPDATES;
public static String SKU_DEPTH_CONTOURS;
public static String SKU_CONTOUR_LINES;
private static final long PURCHASE_VALIDATION_PERIOD_MSEC = 1000 * 60 * 60 * 24; // daily
// (arbitrary) request code for the purchase flow
@ -136,59 +133,30 @@ public class InAppPurchaseHelper {
return Version.isDeveloperBuild(ctx) || ctx.getSettings().DEPTH_CONTOURS_PURCHASED.get();
}
public String getLiveUpdatesPrice() {
return liveUpdatesPrice;
public InAppSubscriptionList getLiveUpdates() {
return purchases.getLiveUpdates();
}
public String getFullVersionPrice() {
return fullVersionPrice;
public InAppPurchase getFullVersion() {
return purchases.getFullVersion();
}
public String getDepthContoursPrice() {
return depthContoursPrice;
public InAppPurchase getDepthContours() {
return purchases.getDepthContours();
}
public String getContourLinesPrice() {
return contourLinesPrice;
public InAppPurchase getContourLines() {
return purchases.getContourLines();
}
public String getSkuLiveUpdates() {
return SKU_LIVE_UPDATES;
}
public boolean hasPrices() {
return !Algorithms.isEmpty(liveUpdatesPrice)
&& (!Version.isFreeVersion(ctx) || !Algorithms.isEmpty(fullVersionPrice));
}
private void initialize() {
if (SKU_LIVE_UPDATES == null) {
if (Version.isFreeVersion(ctx)) {
SKU_LIVE_UPDATES = SKU_LIVE_UPDATES_FREE;
} else {
SKU_LIVE_UPDATES = SKU_LIVE_UPDATES_FULL;
}
}
if (SKU_DEPTH_CONTOURS == null) {
if (Version.isFreeVersion(ctx)) {
SKU_DEPTH_CONTOURS = SKU_DEPTH_CONTOURS_FREE;
} else {
SKU_DEPTH_CONTOURS = SKU_DEPTH_CONTOURS_FULL;
}
}
if (SKU_CONTOUR_LINES == null) {
if (Version.isFreeVersion(ctx)) {
SKU_CONTOUR_LINES = SKU_CONTOUR_LINES_FREE;
} else {
SKU_CONTOUR_LINES = SKU_CONTOUR_LINES_FULL;
}
}
public InAppSubscription getMonthlyLiveUpdates() {
return purchases.getMonthlyLiveUpdates();
}
public InAppPurchaseHelper(OsmandApplication ctx) {
this.ctx = ctx;
isDeveloperVersion = Version.isDeveloperVersion(ctx);
initialize();
purchases = new InAppPurchases(ctx);
}
public boolean hasInventory() {
@ -196,11 +164,11 @@ public class InAppPurchaseHelper {
}
public boolean isPurchased(String inAppSku) {
if (inAppSku.equals(SKU_FULL_VERSION_PRICE)) {
if (purchases.isFullVersion(inAppSku)) {
return isFullVersionPurchased(ctx);
} else if (inAppSku.equals(SKU_LIVE_UPDATES_FULL) || inAppSku.equals(SKU_LIVE_UPDATES_FREE)) {
} else if (purchases.isLiveUpdates(inAppSku)) {
return isSubscribedToLiveUpdates(ctx);
} else if (inAppSku.equals(SKU_DEPTH_CONTOURS_FULL) || inAppSku.equals(SKU_DEPTH_CONTOURS_FREE)) {
} else if (purchases.isDepthContours(inAppSku)) {
return isDepthContoursPurchased(ctx);
}
return false;
@ -257,7 +225,7 @@ public class InAppPurchaseHelper {
}
public boolean needRequestInventory() {
return (isSubscribedToLiveUpdates(ctx) && !ctx.getSettings().BILLING_PURCHASE_TOKEN_SENT.get())
return (isSubscribedToLiveUpdates(ctx) && Algorithms.isEmpty(ctx.getSettings().BILLING_PURCHASE_TOKENS_SENT.get()))
|| System.currentTimeMillis() - lastValidationCheckTime > PURCHASE_VALIDATION_PERIOD_MSEC;
}
@ -268,10 +236,9 @@ public class InAppPurchaseHelper {
public boolean run(InAppPurchaseHelper helper) {
logDebug("Setup successful. Querying inventory.");
List<String> skus = new ArrayList<>();
skus.add(SKU_LIVE_UPDATES);
skus.add(SKU_DEPTH_CONTOURS);
skus.add(SKU_CONTOUR_LINES);
skus.add(SKU_FULL_VERSION_PRICE);
for (InAppPurchase purchase : purchases.getAllInAppPurchases()) {
skus.add(purchase.getSku());
}
try {
mHelper.queryInventoryAsync(true, skus, mGotInventoryListener);
return false;
@ -292,7 +259,7 @@ public class InAppPurchaseHelper {
public boolean run(InAppPurchaseHelper helper) {
try {
mHelper.launchPurchaseFlow(activity,
SKU_FULL_VERSION_PRICE, RC_REQUEST, mPurchaseFinishedListener);
getFullVersion().getSku(), RC_REQUEST, mPurchaseFinishedListener);
return false;
} catch (Exception e) {
complain("Cannot launch full version purchase!");
@ -304,10 +271,10 @@ public class InAppPurchaseHelper {
});
}
public void purchaseLiveUpdates(Activity activity, String email, String userName,
public void purchaseLiveUpdates(Activity activity, String sku, String email, String userName,
String countryDownloadName, boolean hideUserName) {
notifyShowProgress(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES);
new LiveUpdatesPurchaseTask(activity, email, userName, countryDownloadName, hideUserName)
new LiveUpdatesPurchaseTask(activity, sku, email, userName, countryDownloadName, hideUserName)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
}
@ -318,7 +285,7 @@ public class InAppPurchaseHelper {
public boolean run(InAppPurchaseHelper helper) {
try {
mHelper.launchPurchaseFlow(activity,
SKU_DEPTH_CONTOURS, RC_REQUEST, mPurchaseFinishedListener);
getDepthContours().getSku(), RC_REQUEST, mPurchaseFinishedListener);
return false;
} catch (Exception e) {
complain("Cannot launch depth contours purchase!");
@ -358,8 +325,17 @@ public class InAppPurchaseHelper {
*/
// Do we have the live updates?
Purchase liveUpdatesPurchase = inventory.getPurchase(SKU_LIVE_UPDATES);
boolean subscribedToLiveUpdates = (liveUpdatesPurchase != null && liveUpdatesPurchase.getPurchaseState() == 0);
boolean subscribedToLiveUpdates = false;
List<Purchase> liveUpdatesPurchases = new ArrayList<>();
for (InAppPurchase p : getLiveUpdates().getAllSubscriptions()) {
Purchase purchase = inventory.getPurchase(p.getSku());
if (purchase != null) {
liveUpdatesPurchases.add(purchase);
if (!subscribedToLiveUpdates && purchase.getPurchaseState() == 0) {
subscribedToLiveUpdates = true;
}
}
}
OsmandPreference<Long> subscriptionCancelledTime = ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_TIME;
if (!subscribedToLiveUpdates && ctx.getSettings().LIVE_UPDATES_PURCHASED.get()) {
if (subscriptionCancelledTime.get() == 0) {
@ -377,13 +353,15 @@ public class InAppPurchaseHelper {
ctx.getSettings().LIVE_UPDATES_PURCHASED.set(true);
}
Purchase fullVersionPurchase = inventory.getPurchase(SKU_FULL_VERSION_PRICE);
InAppPurchase fullVersion = getFullVersion();
Purchase fullVersionPurchase = inventory.getPurchase(fullVersion.getSku());
boolean fullVersionPurchased = (fullVersionPurchase != null && fullVersionPurchase.getPurchaseState() == 0);
if (fullVersionPurchased) {
ctx.getSettings().FULL_VERSION_PURCHASED.set(true);
}
Purchase depthContoursPurchase = inventory.getPurchase(SKU_DEPTH_CONTOURS);
InAppPurchase depthContours = getDepthContours();
Purchase depthContoursPurchase = inventory.getPurchase(depthContours.getSku());
boolean depthContoursPurchased = (depthContoursPurchase != null && depthContoursPurchase.getPurchaseState() == 0);
if (depthContoursPurchased) {
ctx.getSettings().DEPTH_CONTOURS_PURCHASED.set(true);
@ -393,43 +371,64 @@ public class InAppPurchaseHelper {
logDebug("User " + (subscribedToLiveUpdates ? "HAS" : "DOES NOT HAVE")
+ " live updates purchased.");
if (inventory.hasDetails(SKU_LIVE_UPDATES)) {
SkuDetails liveUpdatesDetails = inventory.getSkuDetails(SKU_LIVE_UPDATES);
liveUpdatesPrice = liveUpdatesDetails.getPrice();
List<String> allOwnedSubscriptionSkus = inventory.getAllOwnedSkus(ITEM_TYPE_SUBS);
for (InAppPurchase p : getLiveUpdates().getAllSubscriptions()) {
if (inventory.hasDetails(p.getSku())) {
Purchase purchase = inventory.getPurchase(p.getSku());
SkuDetails liveUpdatesDetails = inventory.getSkuDetails(p.getSku());
fetchInAppPurchase(p, liveUpdatesDetails, purchase);
allOwnedSubscriptionSkus.remove(p.getSku());
}
}
if (inventory.hasDetails(SKU_FULL_VERSION_PRICE)) {
SkuDetails fullPriceDetails = inventory.getSkuDetails(SKU_FULL_VERSION_PRICE);
fullVersionPrice = fullPriceDetails.getPrice();
for (String sku : allOwnedSubscriptionSkus) {
Purchase purchase = inventory.getPurchase(sku);
SkuDetails liveUpdatesDetails = inventory.getSkuDetails(sku);
InAppSubscription s = getLiveUpdates().applyDiscountSubscription(sku);
if (s == null) {
s = new InAppPurchaseLiveUpdatesOldSubscription(liveUpdatesDetails);
}
fetchInAppPurchase(s, liveUpdatesDetails, purchase);
}
if (inventory.hasDetails(SKU_DEPTH_CONTOURS)) {
SkuDetails depthContoursDetails = inventory.getSkuDetails(SKU_DEPTH_CONTOURS);
depthContoursPrice = depthContoursDetails.getPrice();
if (inventory.hasDetails(fullVersion.getSku())) {
Purchase purchase = inventory.getPurchase(fullVersion.getSku());
SkuDetails fullPriceDetails = inventory.getSkuDetails(fullVersion.getSku());
fetchInAppPurchase(fullVersion, fullPriceDetails, purchase);
}
if (inventory.hasDetails(SKU_CONTOUR_LINES)) {
SkuDetails contourLinesDetails = inventory.getSkuDetails(SKU_CONTOUR_LINES);
contourLinesPrice = contourLinesDetails.getPrice();
if (inventory.hasDetails(depthContours.getSku())) {
Purchase purchase = inventory.getPurchase(depthContours.getSku());
SkuDetails depthContoursDetails = inventory.getSkuDetails(depthContours.getSku());
fetchInAppPurchase(depthContours, depthContoursDetails, purchase);
}
InAppPurchase contourLines = getContourLines();
if (inventory.hasDetails(contourLines.getSku())) {
Purchase purchase = inventory.getPurchase(contourLines.getSku());
SkuDetails contourLinesDetails = inventory.getSkuDetails(contourLines.getSku());
fetchInAppPurchase(contourLines, contourLinesDetails, purchase);
}
OsmandSettings settings = ctx.getSettings();
settings.INAPPS_READ.set(true);
boolean needSendToken = false;
if (liveUpdatesPurchase != null) {
if ((Algorithms.isEmpty(settings.BILLING_USER_ID.get()) || Algorithms.isEmpty(settings.BILLING_USER_TOKEN.get()))
&& !Algorithms.isEmpty(liveUpdatesPurchase.getDeveloperPayload())) {
String payload = liveUpdatesPurchase.getDeveloperPayload();
if (!Algorithms.isEmpty(payload)) {
String[] arr = payload.split(" ");
if (arr.length > 0) {
settings.BILLING_USER_ID.set(arr[0]);
}
if (arr.length > 1) {
token = arr[1];
settings.BILLING_USER_TOKEN.set(token);
List<Purchase> tokensToSend = new ArrayList<>();
if (liveUpdatesPurchases.size() > 0) {
List<String> tokensSent = Arrays.asList(settings.BILLING_PURCHASE_TOKENS_SENT.get().split(";"));
for (Purchase purchase : liveUpdatesPurchases) {
if ((Algorithms.isEmpty(settings.BILLING_USER_ID.get()) || Algorithms.isEmpty(settings.BILLING_USER_TOKEN.get()))
&& !Algorithms.isEmpty(purchase.getDeveloperPayload())) {
String payload = purchase.getDeveloperPayload();
if (!Algorithms.isEmpty(payload)) {
String[] arr = payload.split(" ");
if (arr.length > 0) {
settings.BILLING_USER_ID.set(arr[0]);
}
if (arr.length > 1) {
token = arr[1];
settings.BILLING_USER_TOKEN.set(token);
}
}
}
}
if (!settings.BILLING_PURCHASE_TOKEN_SENT.get()) {
needSendToken = true;
if (!tokensSent.contains(purchase.getSku())) {
tokensToSend.add(purchase);
}
}
}
@ -443,19 +442,41 @@ public class InAppPurchaseHelper {
}
};
if (needSendToken) {
sendToken(liveUpdatesPurchase.getToken(), listener);
if (tokensToSend.size() > 0) {
sendTokens(tokensToSend, listener);
} else {
listener.onResult("OK");
}
}
};
private void fetchInAppPurchase(@NonNull InAppPurchase inAppPurchase, @NonNull SkuDetails skuDetails, @Nullable Purchase purchase) {
if (purchase != null) {
inAppPurchase.setPurchaseState(purchase.getPurchaseState() == 0
? PurchaseState.PURCHASED : PurchaseState.NOT_PURCHASED);
inAppPurchase.setPurchaseTime(purchase.getPurchaseTime());
} else {
inAppPurchase.setPurchaseState(PurchaseState.NOT_PURCHASED);
}
inAppPurchase.setPrice(skuDetails.getPrice());
inAppPurchase.setPriceCurrencyCode(skuDetails.getPriceCurrencyCode());
if (skuDetails.getPriceAmountMicros() > 0) {
inAppPurchase.setPriceValue(skuDetails.getPriceAmountMicros() / 1000000d);
}
String subscriptionPeriod = skuDetails.getSubscriptionPeriod();
if (!Algorithms.isEmpty(subscriptionPeriod)) {
if (inAppPurchase instanceof InAppSubscription) {
((InAppSubscription) inAppPurchase).setSubscriptionPeriod(subscriptionPeriod);
}
}
}
@SuppressLint("StaticFieldLeak")
private class LiveUpdatesPurchaseTask extends AsyncTask<Void, Void, String> {
private WeakReference<Activity> activity;
private String sku;
private String email;
private String userName;
private String countryDownloadName;
@ -463,10 +484,11 @@ public class InAppPurchaseHelper {
private String userId;
LiveUpdatesPurchaseTask(Activity activity, String email, String userName,
LiveUpdatesPurchaseTask(Activity activity, String sku, String email, String userName,
String countryDownloadName, boolean hideUserName) {
this.activity = new WeakReference<>(activity);
this.sku = sku;
this.email = email;
this.userName = userName;
this.countryDownloadName = countryDownloadName;
@ -476,35 +498,43 @@ public class InAppPurchaseHelper {
@Override
protected String doInBackground(Void... params) {
userId = ctx.getSettings().BILLING_USER_ID.get();
try {
Map<String, String> parameters = new HashMap<>();
parameters.put("visibleName", hideUserName ? "" : userName);
parameters.put("preferredCountry", countryDownloadName);
parameters.put("email", email);
if (Algorithms.isEmpty(userId)) {
parameters.put("status", "new");
if (Algorithms.isEmpty(userId) || Algorithms.isEmpty(token)) {
try {
Map<String, String> parameters = new HashMap<>();
parameters.put("visibleName", hideUserName ? "" : userName);
parameters.put("preferredCountry", countryDownloadName);
parameters.put("email", email);
return AndroidNetworkUtils.sendRequest(ctx,
"https://osmand.net/subscription/register",
parameters, "Requesting userId...", true, true);
} catch (Exception e) {
logError("sendRequest Error", e);
}
return AndroidNetworkUtils.sendRequest(ctx,
"https://osmand.net/subscription/register",
parameters, "Requesting userId...", true, true);
} catch (Exception e) {
logError("sendRequest Error", e);
return null;
}
return null;
}
@Override
protected void onPostExecute(String response) {
logDebug("Response=" + response);
if (response == null) {
complain("Cannot retrieve userId from server.");
notifyDismissProgress(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES);
notifyError(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES, "Cannot retrieve userId from server.");
stop(true);
return;
if (!Algorithms.isEmpty(userId)) {
if (Algorithms.isEmpty(token)) {
complain("User token is empty.");
notifyDismissProgress(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES);
notifyError(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES, "User token is empty.");
stop(true);
return;
}
} else {
complain("Cannot retrieve userId from server.");
notifyDismissProgress(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES);
notifyError(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES, "Cannot retrieve userId from server.");
stop(true);
return;
}
} else {
try {
JSONObject obj = new JSONObject(response);
@ -524,7 +554,7 @@ public class InAppPurchaseHelper {
}
notifyDismissProgress(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES);
if (!Algorithms.isEmpty(userId)) {
if (!Algorithms.isEmpty(userId) && !Algorithms.isEmpty(token)) {
logDebug("Launching purchase flow for live updates subscription for userId=" + userId);
final String payload = userId + " " + token;
exec(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES, new InAppRunnable() {
@ -534,7 +564,7 @@ public class InAppPurchaseHelper {
Activity a = activity.get();
if (a != null) {
mHelper.launchPurchaseFlow(a,
SKU_LIVE_UPDATES, IabHelper.ITEM_TYPE_SUBS,
sku, ITEM_TYPE_SUBS,
RC_REQUEST, mPurchaseFinishedListener, payload);
return false;
} else {
@ -548,7 +578,7 @@ public class InAppPurchaseHelper {
}
});
} else {
notifyError(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES,"Empty userId");
notifyError(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES, "Empty userId");
stop(true);
}
}
@ -597,10 +627,12 @@ public class InAppPurchaseHelper {
logDebug("Purchase successful.");
if (purchase.getSku().equals(SKU_LIVE_UPDATES)) {
InAppPurchase liveUpdatesPurchase = getLiveUpdates().getSubscriptionBySku(purchase.getSku());
if (liveUpdatesPurchase != null) {
// bought live updates
logDebug("Live updates subscription purchased.");
sendToken(purchase.getToken(), new OnRequestResultListener() {
final String sku = liveUpdatesPurchase.getSku();
sendTokens(Collections.singletonList(purchase), new OnRequestResultListener() {
@Override
public void onResult(String result) {
boolean active = ctx.getSettings().LIVE_UPDATES_PURCHASED.get();
@ -612,22 +644,22 @@ public class InAppPurchaseHelper {
ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_SECOND_DLG_SHOWN.set(false);
notifyDismissProgress(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES);
notifyItemPurchased(SKU_LIVE_UPDATES, active);
notifyItemPurchased(sku, active);
stop(true);
}
});
} else if (purchase.getSku().equals(SKU_FULL_VERSION_PRICE)) {
} else if (purchase.getSku().equals(getFullVersion().getSku())) {
// bought full version
logDebug("Full version purchased.");
showToast(ctx.getString(R.string.full_version_thanks));
ctx.getSettings().FULL_VERSION_PURCHASED.set(true);
notifyDismissProgress(InAppPurchaseTaskType.PURCHASE_FULL_VERSION);
notifyItemPurchased(SKU_FULL_VERSION_PRICE, false);
notifyItemPurchased(getFullVersion().getSku(), false);
stop(true);
} else if (purchase.getSku().equals(SKU_DEPTH_CONTOURS)) {
} else if (purchase.getSku().equals(getDepthContours().getSku())) {
// bought sea depth contours
logDebug("Sea depth contours purchased.");
showToast(ctx.getString(R.string.sea_depth_thanks));
@ -635,7 +667,7 @@ public class InAppPurchaseHelper {
ctx.getSettings().getCustomRenderBooleanProperty("depthContours").set(true);
notifyDismissProgress(InAppPurchaseTaskType.PURCHASE_DEPTH_CONTOURS);
notifyItemPurchased(SKU_DEPTH_CONTOURS, false);
notifyItemPurchased(getDepthContours().getSku(), false);
stop(true);
} else {
@ -667,71 +699,80 @@ public class InAppPurchaseHelper {
}
}
private void sendToken(String purchaseToken, final OnRequestResultListener listener) {
private void sendTokens(@NonNull final List<Purchase> purchases, final OnRequestResultListener listener) {
final String userId = ctx.getSettings().BILLING_USER_ID.get();
final String token = ctx.getSettings().BILLING_USER_TOKEN.get();
final String email = ctx.getSettings().BILLING_USER_EMAIL.get();
try {
Map<String, String> parameters = new HashMap<>();
parameters.put("userid", userId);
parameters.put("sku", SKU_LIVE_UPDATES);
parameters.put("purchaseToken", purchaseToken);
parameters.put("email", email);
parameters.put("token", token);
AndroidNetworkUtils.sendRequestAsync(ctx,
"https://osmand.net/subscription/purchased",
parameters, "Sending purchase info...", true, true, new OnRequestResultListener() {
@Override
public void onResult(String result) {
if (result != null) {
try {
JSONObject obj = new JSONObject(result);
if (!obj.has("error")) {
ctx.getSettings().BILLING_PURCHASE_TOKEN_SENT.set(true);
if (obj.has("visibleName") && !Algorithms.isEmpty(obj.getString("visibleName"))) {
ctx.getSettings().BILLING_USER_NAME.set(obj.getString("visibleName"));
ctx.getSettings().BILLING_HIDE_USER_NAME.set(false);
} else {
ctx.getSettings().BILLING_HIDE_USER_NAME.set(true);
}
if (obj.has("preferredCountry")) {
String prefferedCountry = obj.getString("preferredCountry");
if (!ctx.getSettings().BILLING_USER_COUNTRY_DOWNLOAD_NAME.get().equals(prefferedCountry)) {
ctx.getSettings().BILLING_USER_COUNTRY_DOWNLOAD_NAME.set(prefferedCountry);
CountrySelectionFragment countrySelectionFragment = new CountrySelectionFragment();
countrySelectionFragment.initCountries(ctx);
CountryItem countryItem = null;
if (Algorithms.isEmpty(prefferedCountry)) {
countryItem = countrySelectionFragment.getCountryItems().get(0);
} else if (!prefferedCountry.equals(OsmandSettings.BILLING_USER_DONATION_NONE_PARAMETER)) {
countryItem = countrySelectionFragment.getCountryItem(prefferedCountry);
}
if (countryItem != null) {
ctx.getSettings().BILLING_USER_COUNTRY.set(countryItem.getLocalName());
}
}
}
if (obj.has("email")) {
ctx.getSettings().BILLING_USER_EMAIL.set(obj.getString("email"));
}
} else {
complain("SendToken Error: "
+ obj.getString("error")
+ " (userId=" + userId + " token=" + token + " response=" + result + ")");
}
} catch (JSONException e) {
logError("SendToken", e);
complain("SendToken Error: "
+ (e.getMessage() != null ? e.getMessage() : "JSONException")
+ " (userId=" + userId + " token=" + token + " response=" + result + ")");
String url = "https://osmand.net/subscription/purchased";
String userOperation = "Sending purchase info...";
final List<AndroidNetworkUtils.Request> requests = new ArrayList<>();
for (Purchase purchase : purchases) {
Map<String, String> parameters = new HashMap<>();
parameters.put("userid", userId);
parameters.put("sku", purchase.getSku());
parameters.put("purchaseToken", purchase.getToken());
parameters.put("email", email);
parameters.put("token", token);
requests.add(new AndroidNetworkUtils.Request(url, parameters, userOperation, true, true));
}
AndroidNetworkUtils.sendRequestsAsync(ctx, requests, new OnRequestResultListener() {
@Override
public void onResult(String result) {
if (result != null) {
try {
JSONObject obj = new JSONObject(result);
if (!obj.has("error")) {
String tokensSentStr = ctx.getSettings().BILLING_PURCHASE_TOKENS_SENT.get();
Set<String> tokensSent = new HashSet<>(Arrays.asList(tokensSentStr.split(";")));
for (Purchase purchase : purchases) {
tokensSent.add(purchase.getSku());
}
ctx.getSettings().BILLING_PURCHASE_TOKENS_SENT.set(TextUtils.join(";", tokensSent));
if (obj.has("visibleName") && !Algorithms.isEmpty(obj.getString("visibleName"))) {
ctx.getSettings().BILLING_USER_NAME.set(obj.getString("visibleName"));
ctx.getSettings().BILLING_HIDE_USER_NAME.set(false);
} else {
ctx.getSettings().BILLING_HIDE_USER_NAME.set(true);
}
if (obj.has("preferredCountry")) {
String prefferedCountry = obj.getString("preferredCountry");
if (!ctx.getSettings().BILLING_USER_COUNTRY_DOWNLOAD_NAME.get().equals(prefferedCountry)) {
ctx.getSettings().BILLING_USER_COUNTRY_DOWNLOAD_NAME.set(prefferedCountry);
CountrySelectionFragment countrySelectionFragment = new CountrySelectionFragment();
countrySelectionFragment.initCountries(ctx);
CountryItem countryItem = null;
if (Algorithms.isEmpty(prefferedCountry)) {
countryItem = countrySelectionFragment.getCountryItems().get(0);
} else if (!prefferedCountry.equals(OsmandSettings.BILLING_USER_DONATION_NONE_PARAMETER)) {
countryItem = countrySelectionFragment.getCountryItem(prefferedCountry);
}
if (countryItem != null) {
ctx.getSettings().BILLING_USER_COUNTRY.set(countryItem.getLocalName());
}
}
}
if (obj.has("email")) {
ctx.getSettings().BILLING_USER_EMAIL.set(obj.getString("email"));
}
} else {
complain("SendToken Error: "
+ obj.getString("error")
+ " (userId=" + userId + " token=" + token + " response=" + result + ")");
}
if (listener != null) {
listener.onResult("OK");
}
} catch (JSONException e) {
logError("SendToken", e);
complain("SendToken Error: "
+ (e.getMessage() != null ? e.getMessage() : "JSONException")
+ " (userId=" + userId + " token=" + token + " response=" + result + ")");
}
});
}
if (listener != null) {
listener.onResult("OK");
}
}
});
} catch (Exception e) {
logError("SendToken Error", e);
if (listener != null) {

View file

@ -0,0 +1,859 @@
package net.osmand.plus.inapp;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.StyleSpan;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.Version;
import net.osmand.plus.inapp.util.SkuDetails;
import net.osmand.util.Algorithms;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Currency;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class InAppPurchases {
private static final InAppPurchase FULL_VERSION = new InAppPurchaseFullVersion();
private static final InAppPurchaseDepthContoursFull DEPTH_CONTOURS_FULL = new InAppPurchaseDepthContoursFull();
private static final InAppPurchaseDepthContoursFree DEPTH_CONTOURS_FREE = new InAppPurchaseDepthContoursFree();
private static final InAppPurchaseContourLinesFull CONTOUR_LINES_FULL = new InAppPurchaseContourLinesFull();
private static final InAppPurchaseContourLinesFree CONTOUR_LINES_FREE = new InAppPurchaseContourLinesFree();
private static final InAppSubscription[] LIVE_UPDATES_FULL = new InAppSubscription[]{
new InAppPurchaseLiveUpdatesOldMonthlyFull(),
new InAppPurchaseLiveUpdatesMonthlyFull(),
new InAppPurchaseLiveUpdates3MonthsFull(),
new InAppPurchaseLiveUpdatesAnnualFull()
};
private static final InAppSubscription[] LIVE_UPDATES_FREE = new InAppSubscription[]{
new InAppPurchaseLiveUpdatesOldMonthlyFree(),
new InAppPurchaseLiveUpdatesMonthlyFree(),
new InAppPurchaseLiveUpdates3MonthsFree(),
new InAppPurchaseLiveUpdatesAnnualFree()
};
private InAppPurchase fullVersion;
private InAppPurchase depthContours;
private InAppPurchase contourLines;
private InAppSubscription monthlyLiveUpdates;
private InAppSubscriptionList liveUpdates;
InAppPurchases(OsmandApplication ctx) {
fullVersion = FULL_VERSION;
if (Version.isFreeVersion(ctx)) {
liveUpdates = new LiveUpdatesInAppPurchasesFree();
} else {
liveUpdates = new LiveUpdatesInAppPurchasesFull();
}
for (InAppSubscription s : liveUpdates.getAllSubscriptions()) {
if (s instanceof InAppPurchaseLiveUpdatesMonthly) {
monthlyLiveUpdates = s;
break;
}
}
if (Version.isFreeVersion(ctx)) {
depthContours = DEPTH_CONTOURS_FREE;
} else {
depthContours = DEPTH_CONTOURS_FULL;
}
if (Version.isFreeVersion(ctx)) {
contourLines = CONTOUR_LINES_FREE;
} else {
contourLines = CONTOUR_LINES_FULL;
}
}
public InAppPurchase getFullVersion() {
return fullVersion;
}
public InAppPurchase getDepthContours() {
return depthContours;
}
public InAppPurchase getContourLines() {
return contourLines;
}
public InAppSubscription getMonthlyLiveUpdates() {
return monthlyLiveUpdates;
}
public InAppSubscriptionList getLiveUpdates() {
return liveUpdates;
}
public List<InAppPurchase> getAllInAppPurchases() {
List<InAppPurchase> purchases = new ArrayList<>();
purchases.add(fullVersion);
purchases.add(depthContours);
purchases.add(contourLines);
purchases.addAll(liveUpdates.getAllSubscriptions());
return purchases;
}
public boolean isFullVersion(String sku) {
return FULL_VERSION.getSku().equals(sku);
}
public boolean isDepthContours(String sku) {
return DEPTH_CONTOURS_FULL.getSku().equals(sku) || DEPTH_CONTOURS_FREE.getSku().equals(sku);
}
public boolean isContourLines(String sku) {
return CONTOUR_LINES_FULL.getSku().equals(sku) || CONTOUR_LINES_FREE.getSku().equals(sku);
}
public boolean isLiveUpdates(String sku) {
for (InAppPurchase p : LIVE_UPDATES_FULL) {
if (p.getSku().equals(sku)) {
return true;
}
}
for (InAppPurchase p : LIVE_UPDATES_FREE) {
if (p.getSku().equals(sku)) {
return true;
}
}
return false;
}
public abstract static class InAppSubscriptionList {
private List<InAppSubscription> subscriptions;
InAppSubscriptionList(@NonNull InAppSubscription[] subscriptionsArray) {
this.subscriptions = Arrays.asList(subscriptionsArray);;
}
private List<InAppSubscription> getSubscriptions() {
return new ArrayList<>(subscriptions);
}
public List<InAppSubscription> getAllSubscriptions() {
List<InAppSubscription> res = new ArrayList<>();
for (InAppSubscription s : getSubscriptions()) {
res.add(s);
res.addAll(s.getDiscounts());
}
return res;
}
public List<InAppSubscription> getVisibleSubscriptions() {
List<InAppSubscription> res = new ArrayList<>();
for (InAppSubscription s : getSubscriptions()) {
boolean added = false;
if (s.isPurchased()) {
res.add(s);
added = true;
} else {
for (InAppSubscription discount : s.getDiscounts()) {
if (discount.isPurchased()) {
res.add(discount);
added = true;
}
}
}
if (!added) {
for (InAppSubscription discount : s.getDiscounts()) {
res.add(discount);
added = true;
}
}
if (!added && !s.isDiscounted()) {
res.add(s);
}
}
return res;
}
@Nullable
public InAppSubscription getSubscriptionBySku(@NonNull String sku) {
for (InAppSubscription s : getAllSubscriptions()) {
if (s.getSku().equals(sku)) {
return s;
}
}
return null;
}
public boolean containsSku(@NonNull String sku) {
return getSubscriptionBySku(sku) != null;
}
@Nullable
public InAppSubscription applyDiscountSubscription(String sku) {
List<InAppSubscription> subscriptions = getAllSubscriptions();
for (InAppSubscription s : subscriptions) {
InAppSubscription discount = s.applyDiscountSubscription(sku);
if (discount != null) {
return discount;
}
}
return null;
}
}
public static class LiveUpdatesInAppPurchasesFree extends InAppSubscriptionList {
public LiveUpdatesInAppPurchasesFree() {
super(LIVE_UPDATES_FREE);
}
}
public static class LiveUpdatesInAppPurchasesFull extends InAppSubscriptionList {
public LiveUpdatesInAppPurchasesFull() {
super(LIVE_UPDATES_FULL);
}
}
public abstract static class InAppPurchase {
public enum PurchaseState {
UNKNOWN,
PURCHASED,
NOT_PURCHASED
}
private String sku;
private String price;
private double priceValue;
private String priceCurrencyCode;
private PurchaseState purchaseState = PurchaseState.UNKNOWN;
private long purchaseTime;
double monthlyPriceValue;
boolean donationSupported = false;
boolean discounted = false;
private NumberFormat currencyFormatter;
private InAppPurchase(String sku) {
this.sku = sku;
}
private InAppPurchase(String sku, boolean discounted) {
this(sku);
this.discounted = discounted;
}
public String getSku() {
return sku;
}
public String getPrice(Context ctx) {
if (!Algorithms.isEmpty(price)) {
return price;
} else {
return getDefaultPrice(ctx);
}
}
public void setPrice(String price) {
this.price = price;
}
public long getPurchaseTime() {
return purchaseTime;
}
public void setPurchaseTime(long purchaseTime) {
this.purchaseTime = purchaseTime;
}
public String getDefaultPrice(Context ctx) {
return "";
}
public String getDefaultMonthlyPrice(Context ctx) {
return "";
}
public PurchaseState getPurchaseState() {
return purchaseState;
}
public void setPurchaseState(PurchaseState purchaseState) {
this.purchaseState = purchaseState;
}
public boolean isPurchased() {
return purchaseState == PurchaseState.PURCHASED;
}
public boolean fetchRequired() {
return purchaseState == PurchaseState.UNKNOWN;
}
public boolean isDiscounted() {
return discounted;
}
public CharSequence getTitle(Context ctx) {
return "";
}
public CharSequence getDescription(@NonNull Context ctx) {
NumberFormat currencyFormatter = getCurrencyFormatter();
if (currencyFormatter != null) {
return currencyFormatter.format(getPriceValue());
} else {
return ctx.getString(R.string.default_price_currency_format, getPriceValue(), getPriceCurrencyCode());
}
}
public double getPriceValue() {
return priceValue;
}
public void setPriceValue(double priceValue) {
this.priceValue = priceValue;
}
public double getMonthlyPriceValue() {
return monthlyPriceValue;
}
public boolean isDonationSupported() {
return donationSupported;
}
public String getPriceCurrencyCode() {
return priceCurrencyCode;
}
public void setPriceCurrencyCode(String priceCurrencyCode) {
this.priceCurrencyCode = priceCurrencyCode;
Locale currencyLocale = null;
Locale defaultLocale = Locale.getDefault();
try {
Currency defaultCurrency = Currency.getInstance(defaultLocale);
if (defaultCurrency != null && defaultCurrency.getCurrencyCode().equals(priceCurrencyCode)) {
currencyLocale = defaultLocale;
}
} catch (Exception e) {
// ignore
}
if (currencyLocale == null) {
if ("USD".equals(priceCurrencyCode)) {
currencyLocale = Locale.US;
} else if ("EUR".equals(priceCurrencyCode)) {
currencyLocale = Locale.GERMANY;
} else if ("GBP".equals(priceCurrencyCode)) {
currencyLocale = Locale.UK;
} else if ("JPY".equals(priceCurrencyCode)) {
currencyLocale = Locale.JAPAN;
} else if ("CNY".equals(priceCurrencyCode)) {
currencyLocale = Locale.CHINA;
} else if ("UAH".equals(priceCurrencyCode)) {
currencyLocale = new Locale("ukr", "UA");
} else if ("RUB".equals(priceCurrencyCode)) {
currencyLocale = new Locale("rus", "RU");
}
}
if (currencyLocale != null) {
currencyFormatter = NumberFormat.getCurrencyInstance(currencyLocale);
}
}
NumberFormat getCurrencyFormatter() {
return currencyFormatter;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (obj instanceof InAppPurchase) {
InAppPurchase purchase = (InAppPurchase) obj;
return sku.equals(purchase.sku);
} else {
return false;
}
}
@Override
public int hashCode() {
return sku.hashCode();
}
}
public static abstract class InAppSubscription extends InAppPurchase {
private Map<String, InAppSubscription> discounts = new ConcurrentHashMap<>();
private String skuNoVersion;
private String subscriptionPeriod;
protected boolean discount = false;
InAppSubscription(@NonNull String skuNoVersion, int version) {
super(skuNoVersion + "_v" + version);
this.skuNoVersion = skuNoVersion;
}
InAppSubscription(@NonNull String sku, boolean discounted) {
super(sku, discounted);
this.skuNoVersion = sku;
}
@NonNull
public List<InAppSubscription> getDiscounts() {
return new ArrayList<>(discounts.values());
}
@Nullable
public InAppSubscription applyDiscountSubscription(@NonNull String sku) {
InAppSubscription s = null;
if (!discount) {
s = discounts.get(sku);
if (s == null) {
s = newInstance(sku);
if (s != null) {
s.discount = true;
discounts.put(sku, s);
}
}
}
return s;
}
public boolean isAnyPurchased() {
if (isPurchased()) {
return true;
} else {
for (InAppSubscription s : getDiscounts()) {
if (s.isPurchased()) {
return true;
}
}
}
return false;
}
public boolean isDiscount() {
return discount;
}
public String getSkuNoVersion() {
return skuNoVersion;
}
public String getSubscriptionPeriod() {
return subscriptionPeriod;
}
public void setSubscriptionPeriod(String subscriptionPeriod) {
this.subscriptionPeriod = subscriptionPeriod;
}
@Override
public CharSequence getDescription(@NonNull Context ctx) {
if (getMonthlyPriceValue() == 0) {
return ctx.getString(R.string.osm_live_payment_month_cost_descr, getDefaultMonthlyPrice(ctx));
} else {
NumberFormat currencyFormatter = getCurrencyFormatter();
if (currencyFormatter != null) {
return ctx.getString(R.string.osm_live_payment_month_cost_descr, currencyFormatter.format(getMonthlyPriceValue()));
} else {
return ctx.getString(R.string.osm_live_payment_month_cost_descr_ex, getMonthlyPriceValue(), getPriceCurrencyCode());
}
}
}
public CharSequence getRenewDescription(@NonNull Context ctx) {
return "";
}
@Nullable
protected abstract InAppSubscription newInstance(@NonNull String sku);
}
public static class InAppPurchaseFullVersion extends InAppPurchase {
private static final String SKU_FULL_VERSION_PRICE = "osmand_full_version_price";
InAppPurchaseFullVersion() {
super(SKU_FULL_VERSION_PRICE);
}
@Override
public String getDefaultPrice(Context ctx) {
return ctx.getString(R.string.full_version_price);
}
}
public static class InAppPurchaseDepthContours extends InAppPurchase {
private InAppPurchaseDepthContours(String sku) {
super(sku);
}
@Override
public String getDefaultPrice(Context ctx) {
return ctx.getString(R.string.sea_depth_maps_price);
}
}
public static class InAppPurchaseDepthContoursFull extends InAppPurchaseDepthContours {
private static final String SKU_DEPTH_CONTOURS_FULL = "net.osmand.seadepth_plus";
InAppPurchaseDepthContoursFull() {
super(SKU_DEPTH_CONTOURS_FULL);
}
}
public static class InAppPurchaseDepthContoursFree extends InAppPurchaseDepthContours {
private static final String SKU_DEPTH_CONTOURS_FREE = "net.osmand.seadepth";
InAppPurchaseDepthContoursFree() {
super(SKU_DEPTH_CONTOURS_FREE);
}
}
public static class InAppPurchaseContourLines extends InAppPurchase {
private InAppPurchaseContourLines(String sku) {
super(sku);
}
@Override
public String getDefaultPrice(Context ctx) {
return ctx.getString(R.string.srtm_plugin_price);
}
}
public static class InAppPurchaseContourLinesFull extends InAppPurchaseContourLines {
private static final String SKU_CONTOUR_LINES_FULL = "net.osmand.contourlines_plus";
InAppPurchaseContourLinesFull() {
super(SKU_CONTOUR_LINES_FULL);
}
}
public static class InAppPurchaseContourLinesFree extends InAppPurchaseContourLines {
private static final String SKU_CONTOUR_LINES_FREE = "net.osmand.contourlines";
InAppPurchaseContourLinesFree() {
super(SKU_CONTOUR_LINES_FREE);
}
}
public static abstract class InAppPurchaseLiveUpdatesMonthly extends InAppSubscription {
InAppPurchaseLiveUpdatesMonthly(String skuNoVersion, int version) {
super(skuNoVersion, version);
}
InAppPurchaseLiveUpdatesMonthly(@NonNull String sku) {
super(sku, false);
}
@Override
public void setPriceValue(double priceValue) {
super.setPriceValue(priceValue);
monthlyPriceValue = priceValue;
donationSupported = true;
}
@Override
public String getDefaultPrice(Context ctx) {
return ctx.getString(R.string.osm_live_monthly_price);
}
@Override
public String getDefaultMonthlyPrice(Context ctx) {
return ctx.getString(R.string.osm_live_monthly_price);
}
@Override
public CharSequence getTitle(Context ctx) {
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);
}
}
public static class InAppPurchaseLiveUpdatesMonthlyFull extends InAppPurchaseLiveUpdatesMonthly {
private static final String SKU_LIVE_UPDATES_MONTHLY_FULL = "osm_live_subscription_monthly_full";
InAppPurchaseLiveUpdatesMonthlyFull() {
super(SKU_LIVE_UPDATES_MONTHLY_FULL, 1);
}
private InAppPurchaseLiveUpdatesMonthlyFull(@NonNull String sku) {
super(sku);
}
@Nullable
@Override
protected InAppSubscription newInstance(@NonNull String sku) {
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesMonthlyFull(sku) : null;
}
}
public static class InAppPurchaseLiveUpdatesMonthlyFree extends InAppPurchaseLiveUpdatesMonthly {
private static final String SKU_LIVE_UPDATES_MONTHLY_FREE = "osm_live_subscription_monthly_free";
InAppPurchaseLiveUpdatesMonthlyFree() {
super(SKU_LIVE_UPDATES_MONTHLY_FREE, 1);
}
private InAppPurchaseLiveUpdatesMonthlyFree(@NonNull String sku) {
super(sku);
}
@Nullable
@Override
protected InAppSubscription newInstance(@NonNull String sku) {
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesMonthlyFree(sku) : null;
}
}
public static abstract class InAppPurchaseLiveUpdates3Months extends InAppSubscription {
InAppPurchaseLiveUpdates3Months(String skuNoVersion, int version) {
super(skuNoVersion, version);
}
InAppPurchaseLiveUpdates3Months(@NonNull String sku) {
super(sku, false);
}
@Override
public void setPriceValue(double priceValue) {
super.setPriceValue(priceValue);
monthlyPriceValue = priceValue / 3d;
}
@Override
public String getDefaultPrice(Context ctx) {
return ctx.getString(R.string.osm_live_3_months_price);
}
@Override
public String getDefaultMonthlyPrice(Context ctx) {
return ctx.getString(R.string.osm_live_3_months_monthly_price);
}
@Override
public CharSequence getTitle(Context ctx) {
return ctx.getString(R.string.osm_live_payment_3_months_title);
}
@Override
public CharSequence getRenewDescription(@NonNull Context ctx) {
return ctx.getString(R.string.osm_live_payment_renews_quarterly);
}
}
public static class InAppPurchaseLiveUpdates3MonthsFull extends InAppPurchaseLiveUpdates3Months {
private static final String SKU_LIVE_UPDATES_3_MONTHS_FULL = "osm_live_subscription_3_months_full";
InAppPurchaseLiveUpdates3MonthsFull() {
super(SKU_LIVE_UPDATES_3_MONTHS_FULL, 1);
}
private InAppPurchaseLiveUpdates3MonthsFull(@NonNull String sku) {
super(sku);
}
@Nullable
@Override
protected InAppSubscription newInstance(@NonNull String sku) {
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdates3MonthsFull(sku) : null;
}
}
public static class InAppPurchaseLiveUpdates3MonthsFree extends InAppPurchaseLiveUpdates3Months {
private static final String SKU_LIVE_UPDATES_3_MONTHS_FREE = "osm_live_subscription_3_months_free";
InAppPurchaseLiveUpdates3MonthsFree() {
super(SKU_LIVE_UPDATES_3_MONTHS_FREE, 1);
}
private InAppPurchaseLiveUpdates3MonthsFree(@NonNull String sku) {
super(sku);
}
@Nullable
@Override
protected InAppSubscription newInstance(@NonNull String sku) {
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdates3MonthsFree(sku) : null;
}
}
public static abstract class InAppPurchaseLiveUpdatesAnnual extends InAppSubscription {
InAppPurchaseLiveUpdatesAnnual(String skuNoVersion, int version) {
super(skuNoVersion, version);
}
InAppPurchaseLiveUpdatesAnnual(@NonNull String sku) {
super(sku, false);
}
@Override
public void setPriceValue(double priceValue) {
super.setPriceValue(priceValue);
monthlyPriceValue = priceValue / 12d;
}
@Override
public String getDefaultPrice(Context ctx) {
return ctx.getString(R.string.osm_live_annual_price);
}
@Override
public String getDefaultMonthlyPrice(Context ctx) {
return ctx.getString(R.string.osm_live_annual_monthly_price);
}
@Override
public CharSequence getTitle(Context ctx) {
return ctx.getString(R.string.osm_live_payment_annual_title);
}
@Override
public CharSequence getRenewDescription(@NonNull Context ctx) {
return ctx.getString(R.string.osm_live_payment_renews_annually);
}
}
public static class InAppPurchaseLiveUpdatesAnnualFull extends InAppPurchaseLiveUpdatesAnnual {
private static final String SKU_LIVE_UPDATES_ANNUAL_FULL = "osm_live_subscription_annual_full";
InAppPurchaseLiveUpdatesAnnualFull() {
super(SKU_LIVE_UPDATES_ANNUAL_FULL, 1);
}
private InAppPurchaseLiveUpdatesAnnualFull(@NonNull String sku) {
super(sku);
}
@Nullable
@Override
protected InAppSubscription newInstance(@NonNull String sku) {
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesAnnualFull(sku) : null;
}
}
public static class InAppPurchaseLiveUpdatesAnnualFree extends InAppPurchaseLiveUpdatesAnnual {
private static final String SKU_LIVE_UPDATES_ANNUAL_FREE = "osm_live_subscription_annual_free";
InAppPurchaseLiveUpdatesAnnualFree() {
super(SKU_LIVE_UPDATES_ANNUAL_FREE, 1);
}
private InAppPurchaseLiveUpdatesAnnualFree(@NonNull String sku) {
super(sku);
}
@Nullable
@Override
protected InAppSubscription newInstance(@NonNull String sku) {
return sku.startsWith(getSkuNoVersion()) ? new InAppPurchaseLiveUpdatesAnnualFree(sku) : null;
}
}
public static class InAppPurchaseLiveUpdatesOldMonthly extends InAppSubscription {
private InAppPurchaseLiveUpdatesOldMonthly(String sku) {
super(sku, true);
}
@Override
public String getDefaultPrice(Context ctx) {
return ctx.getString(R.string.osm_live_default_price);
}
@Nullable
@Override
protected InAppSubscription newInstance(@NonNull String sku) {
return null;
}
}
public static class InAppPurchaseLiveUpdatesOldMonthlyFull extends InAppPurchaseLiveUpdatesOldMonthly {
private static final String SKU_LIVE_UPDATES_OLD_MONTHLY_FULL = "osm_live_subscription_2";
InAppPurchaseLiveUpdatesOldMonthlyFull() {
super(SKU_LIVE_UPDATES_OLD_MONTHLY_FULL);
}
}
public static class InAppPurchaseLiveUpdatesOldMonthlyFree extends InAppPurchaseLiveUpdatesOldMonthly {
private static final String SKU_LIVE_UPDATES_OLD_MONTHLY_FREE = "osm_free_live_subscription_2";
InAppPurchaseLiveUpdatesOldMonthlyFree() {
super(SKU_LIVE_UPDATES_OLD_MONTHLY_FREE);
}
}
public static class InAppPurchaseLiveUpdatesOldSubscription extends InAppSubscription {
private SkuDetails details;
InAppPurchaseLiveUpdatesOldSubscription(@NonNull SkuDetails details) {
super(details.getSku(), true);
this.details = details;
}
@Override
public String getDefaultPrice(Context ctx) {
return "";
}
@Override
public CharSequence getTitle(Context ctx) {
return details.getTitle();
}
@Override
public CharSequence getDescription(@NonNull Context ctx) {
return details.getDescription();
}
@Nullable
@Override
protected InAppSubscription newInstance(@NonNull String sku) {
return null;
}
}
}

View file

@ -63,12 +63,12 @@ public class Inventory {
}
/** Returns a list of all owned product IDs. */
List<String> getAllOwnedSkus() {
public List<String> getAllOwnedSkus() {
return new ArrayList<String>(mPurchaseMap.keySet());
}
/** Returns a list of all owned product IDs of a given type */
List<String> getAllOwnedSkus(String itemType) {
public List<String> getAllOwnedSkus(String itemType) {
List<String> result = new ArrayList<String>();
for (Purchase p : mPurchaseMap.values()) {
if (p.getItemType().equals(itemType)) result.add(p.getSku());

View file

@ -26,6 +26,9 @@ public class SkuDetails {
String mSku;
String mType;
String mPrice;
long mPriceAmountMicros;
String mPriceCurrencyCode;
String mSubscriptionPeriod;
String mTitle;
String mDescription;
String mJson;
@ -41,6 +44,9 @@ public class SkuDetails {
mSku = o.optString("productId");
mType = o.optString("type");
mPrice = o.optString("price");
mPriceAmountMicros = o.optLong("price_amount_micros");
mPriceCurrencyCode = o.optString("price_currency_code");
mSubscriptionPeriod = o.optString("subscriptionPeriod");
mTitle = o.optString("title");
mDescription = o.optString("description");
}
@ -48,6 +54,15 @@ public class SkuDetails {
public String getSku() { return mSku; }
public String getType() { return mType; }
public String getPrice() { return mPrice; }
public long getPriceAmountMicros() {
return mPriceAmountMicros;
}
public String getPriceCurrencyCode() {
return mPriceCurrencyCode;
}
public String getSubscriptionPeriod() {
return mSubscriptionPeriod;
}
public String getTitle() { return mTitle; }
public String getDescription() { return mDescription; }

View file

@ -14,6 +14,7 @@ import android.os.Bundle;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBar;
@ -47,10 +48,12 @@ import net.osmand.plus.activities.LocalIndexInfo;
import net.osmand.plus.activities.OsmandBaseExpandableListAdapter;
import net.osmand.plus.activities.OsmandInAppPurchaseActivity;
import net.osmand.plus.base.BaseOsmAndFragment;
import net.osmand.plus.chooseplan.ChoosePlanDialogFragment;
import net.osmand.plus.download.ui.AbstractLoadLocalIndexTask;
import net.osmand.plus.inapp.InAppPurchaseHelper;
import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseListener;
import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseTaskType;
import net.osmand.plus.inapp.InAppPurchases.InAppSubscription;
import net.osmand.plus.resources.IncrementalChangesManager;
import net.osmand.util.Algorithms;
@ -153,9 +156,14 @@ public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurc
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (position == 0 && !processing && InAppPurchaseHelper.isSubscribedToLiveUpdates(getMyApplication())) {
SubscriptionFragment subscriptionFragment = new SubscriptionFragment();
subscriptionFragment.setEditMode(true);
subscriptionFragment.show(getChildFragmentManager(), SubscriptionFragment.TAG);
FragmentActivity activity = getActivity();
if (activity != null) {
if (isDonationSupported()) {
showDonationSettings();
} else {
ChoosePlanDialogFragment.showOsmLiveInstance(activity.getSupportFragmentManager());
}
}
}
}
});
@ -179,18 +187,47 @@ public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurc
TextView regionNameTextView = (TextView) subscriptionHeader.findViewById(R.id.regionTextView);
statusTextView.setText(getString(R.string.osm_live_active));
statusIcon.setImageDrawable(getMyApplication().getUIUtilities().getThemedIcon(R.drawable.ic_action_done));
regionNameHeaderTextView.setText(R.string.osm_live_support_region);
String countryName = getSettings().BILLING_USER_COUNTRY.get();
if (Algorithms.isEmpty(countryName)) {
if (getSettings().BILLING_USER_COUNTRY_DOWNLOAD_NAME.get().equals(OsmandSettings.BILLING_USER_DONATION_NONE_PARAMETER)) {
InAppPurchaseHelper purchaseHelper = getInAppPurchaseHelper();
if (purchaseHelper != null) {
InAppSubscription s = purchaseHelper.getMonthlyLiveUpdates();
if (s.isDonationSupported() && s.isAnyPurchased()) {
if (Algorithms.isEmpty(countryName)) {
if (getSettings().BILLING_USER_COUNTRY_DOWNLOAD_NAME.get().equals(OsmandSettings.BILLING_USER_DONATION_NONE_PARAMETER)) {
regionNameHeaderTextView.setText(R.string.default_buttons_support);
countryName = getString(R.string.osmand_team);
} else {
countryName = getString(R.string.shared_string_world);
}
}
} else {
regionNameHeaderTextView.setText(R.string.default_buttons_support);
countryName = getString(R.string.osmand_team);
} else {
countryName = getString(R.string.shared_string_world);
}
} else {
regionNameHeaderTextView.setText(R.string.default_buttons_support);
countryName = getString(R.string.osmand_team);
}
regionNameTextView.setText(countryName);
View divButtonSettings = subscriptionHeader.findViewById(R.id.div_button_settings);
View buttonSettings = subscriptionHeader.findViewById(R.id.button_settings);
if (isDonationSupported()) {
buttonSettings.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showDonationSettings();
}
});
divButtonSettings.setVisibility(View.VISIBLE);
buttonSettings.setVisibility(View.VISIBLE);
} else {
divButtonSettings.setVisibility(View.GONE);
buttonSettings.setVisibility(View.GONE);
}
subscriptionBanner.setVisibility(View.GONE);
subscriptionInfo.setVisibility(View.VISIBLE);
} else {
@ -209,8 +246,10 @@ public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurc
subscriptionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SubscriptionFragment subscriptionFragment = new SubscriptionFragment();
subscriptionFragment.show(getChildFragmentManager(), SubscriptionFragment.TAG);
FragmentActivity activity = getActivity();
if (activity != null) {
ChoosePlanDialogFragment.showOsmLiveInstance(activity.getSupportFragmentManager());
}
}
});
@ -229,10 +268,6 @@ public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurc
enableProgress();
}
}
if (((OsmLiveActivity) getActivity()).shouldOpenSubscription()) {
SubscriptionFragment subscriptionFragment = new SubscriptionFragment();
subscriptionFragment.show(getChildFragmentManager(), SubscriptionFragment.TAG);
}
}
@Override
@ -273,14 +308,33 @@ public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurc
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == SUBSCRIPTION_SETTINGS && !processing) {
SubscriptionFragment subscriptionFragment = new SubscriptionFragment();
subscriptionFragment.setEditMode(true);
subscriptionFragment.show(getChildFragmentManager(), SubscriptionFragment.TAG);
FragmentActivity activity = getActivity();
if (activity != null) {
if (isDonationSupported()) {
showDonationSettings();
} else {
ChoosePlanDialogFragment.showOsmLiveInstance(activity.getSupportFragmentManager());
}
}
return true;
}
return super.onOptionsItemSelected(item);
}
private boolean isDonationSupported() {
InAppPurchaseHelper purchaseHelper = getInAppPurchaseHelper();
if (purchaseHelper != null) {
InAppSubscription s = purchaseHelper.getMonthlyLiveUpdates();
return s.isDonationSupported() && s.isAnyPurchased();
}
return false;
}
private void showDonationSettings() {
SubscriptionFragment subscriptionFragment = new SubscriptionFragment();
subscriptionFragment.show(getChildFragmentManager(), SubscriptionFragment.TAG);
}
protected class LocalIndexesAdapter extends OsmandBaseExpandableListAdapter {
public static final int SHOULD_UPDATE_GROUP_POSITION = 0;
public static final int SHOULD_NOT_UPDATE_GROUP_POSITION = 1;
@ -715,7 +769,7 @@ public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurc
@Override
public void onItemPurchased(String sku, boolean active) {
InAppPurchaseHelper purchaseHelper = getInAppPurchaseHelper();
if (purchaseHelper != null && purchaseHelper.getSkuLiveUpdates().equals(sku)) {
if (purchaseHelper != null && purchaseHelper.getLiveUpdates().containsSku(sku)) {
updateSubscriptionHeader();
}

View file

@ -34,11 +34,9 @@ import java.util.TimeZone;
public class OsmLiveActivity extends AbstractDownloadActivity implements DownloadEvents, ChoosePlanDialogListener {
private final static Log LOG = PlatformUtil.getLog(OsmLiveActivity.class);
public final static String OPEN_SUBSCRIPTION_INTENT_PARAM = "open_subscription_intent_param";
public final static String SHOW_SETTINGS_ONLY_INTENT_PARAM = "show_settings_only_intent_param";
private LiveUpdatesFragmentPagerAdapter pagerAdapter;
private boolean openSubscription;
private boolean showSettingOnly;
private GetLastUpdateDateTask getLastUpdateDateTask;
private static final String URL = "https://osmand.net/api/osmlive_status";
@ -51,10 +49,8 @@ public class OsmLiveActivity extends AbstractDownloadActivity implements Downloa
Intent intent = getIntent();
if (intent != null && intent.getExtras() != null) {
openSubscription = intent.getExtras().getBoolean(OPEN_SUBSCRIPTION_INTENT_PARAM, false);
showSettingOnly = intent.getExtras().getBoolean(SHOW_SETTINGS_ONLY_INTENT_PARAM, false);
} else if (savedInstanceState != null) {
openSubscription = savedInstanceState.getBoolean(OPEN_SUBSCRIPTION_INTENT_PARAM, false);
showSettingOnly = savedInstanceState.getBoolean(SHOW_SETTINGS_ONLY_INTENT_PARAM, false);
}
@ -109,13 +105,12 @@ public class OsmLiveActivity extends AbstractDownloadActivity implements Downloa
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(OPEN_SUBSCRIPTION_INTENT_PARAM, openSubscription);
outState.putBoolean(SHOW_SETTINGS_ONLY_INTENT_PARAM, showSettingOnly);
}
@Override
public void onChoosePlanDialogDismiss() {
finish();
//finish();
}
public boolean isShowSettingOnly() {
@ -140,10 +135,6 @@ public class OsmLiveActivity extends AbstractDownloadActivity implements Downloa
((LiveUpdatesFragment) pagerAdapter.fragments[0]).notifyLiveUpdatesChanged();
}
public boolean shouldOpenSubscription() {
return openSubscription;
}
private static class GetLastUpdateDateTask extends AsyncTask<Void, Void, String> {
private OsmandApplication app;

View file

@ -42,7 +42,6 @@ import java.util.Map;
public class SubscriptionFragment extends BaseOsmAndDialogFragment implements InAppPurchaseListener, OnFragmentInteractionListener {
public static final String TAG = "SubscriptionFragment";
private static final String EDIT_MODE_ID = "edit_mode_id";
private static final String USER_NAME_ID = "user_name_id";
private static final String EMAIL_ID = "email_id";
private static final String COUNTRY_ITEM_ID = "country_id";
@ -51,7 +50,6 @@ public class SubscriptionFragment extends BaseOsmAndDialogFragment implements In
private OsmandSettings settings;
private ProgressDialog dlg;
private boolean editMode;
private boolean donation;
private String prevEmail;
@ -59,10 +57,6 @@ public class SubscriptionFragment extends BaseOsmAndDialogFragment implements In
private CountrySelectionFragment countrySelectionFragment = new CountrySelectionFragment();
public void setEditMode(boolean editMode) {
this.editMode = editMode;
}
@Nullable
public InAppPurchaseHelper getInAppPurchaseHelper() {
Activity activity = getActivity();
@ -75,8 +69,6 @@ public class SubscriptionFragment extends BaseOsmAndDialogFragment implements In
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putBoolean(EDIT_MODE_ID, editMode);
View view = getView();
if (view != null) {
EditText userNameEdit = (EditText) view.findViewById(R.id.userNameEdit);
@ -91,7 +83,6 @@ public class SubscriptionFragment extends BaseOsmAndDialogFragment implements In
outState.putSerializable(COUNTRY_ITEM_ID, selectedCountryItem);
}
}
super.onSaveInstanceState(outState);
}
@ -99,10 +90,6 @@ public class SubscriptionFragment extends BaseOsmAndDialogFragment implements In
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
editMode = savedInstanceState.getBoolean(EDIT_MODE_ID);
}
settings = getMyApplication().getSettings();
prevEmail = settings.BILLING_USER_EMAIL.get();
}
@ -134,11 +121,7 @@ public class SubscriptionFragment extends BaseOsmAndDialogFragment implements In
View view = inflater.inflate(R.layout.subscription_fragment, container, false);
ImageButton closeButton = (ImageButton) view.findViewById(R.id.closeButton);
if (editMode) {
closeButton.setImageDrawable(getMyApplication().getUIUtilities().getIcon(R.drawable.ic_action_mode_back));
} else {
closeButton.setImageDrawable(getMyApplication().getUIUtilities().getIcon(R.drawable.ic_action_remove_dark));
}
closeButton.setImageDrawable(getMyApplication().getUIUtilities().getIcon(R.drawable.ic_action_mode_back));
closeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -147,11 +130,7 @@ public class SubscriptionFragment extends BaseOsmAndDialogFragment implements In
});
TextView title = (TextView) view.findViewById(R.id.titleTextView);
if (editMode) {
title.setText(getString(R.string.osm_live_subscription_settings));
} else {
title.setText(getString(R.string.osm_live_subscription));
}
title.setText(getString(R.string.osm_live_subscription_settings));
final View headerLayout = view.findViewById(R.id.headerLayout);
final View paramsLayout = view.findViewById(R.id.paramsLayout);
@ -204,100 +183,69 @@ public class SubscriptionFragment extends BaseOsmAndDialogFragment implements In
final CheckBox hideUserNameCheckbox = (CheckBox) view.findViewById(R.id.hideUserNameCheckbox);
hideUserNameCheckbox.setChecked(hideUserName);
View editModeBottomView = view.findViewById(R.id.editModeBottomView);
View purchaseCard = view.findViewById(R.id.purchaseCard);
if (editMode) {
editModeBottomView.setVisibility(View.VISIBLE);
purchaseCard.setVisibility(View.GONE);
Button saveChangesButton = (Button) view.findViewById(R.id.saveChangesButton);
saveChangesButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
InAppPurchaseHelper purchaseHelper = getInAppPurchaseHelper();
if (purchaseHelper != null && applySettings(userNameEdit.getText().toString().trim(),
emailEdit.getText().toString().trim(), hideUserNameCheckbox.isChecked())) {
Button saveChangesButton = (Button) view.findViewById(R.id.saveChangesButton);
saveChangesButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
InAppPurchaseHelper purchaseHelper = getInAppPurchaseHelper();
if (purchaseHelper != null && applySettings(userNameEdit.getText().toString().trim(),
emailEdit.getText().toString().trim(), hideUserNameCheckbox.isChecked())) {
final Map<String, String> parameters = new HashMap<>();
parameters.put("visibleName", settings.BILLING_HIDE_USER_NAME.get() ? "" : settings.BILLING_USER_NAME.get());
parameters.put("preferredCountry", settings.BILLING_USER_COUNTRY_DOWNLOAD_NAME.get());
parameters.put("email", settings.BILLING_USER_EMAIL.get());
parameters.put("cemail", prevEmail);
parameters.put("userid", settings.BILLING_USER_ID.get());
parameters.put("token", settings.BILLING_USER_TOKEN.get());
final Map<String, String> parameters = new HashMap<>();
parameters.put("visibleName", settings.BILLING_HIDE_USER_NAME.get() ? "" : settings.BILLING_USER_NAME.get());
parameters.put("preferredCountry", settings.BILLING_USER_COUNTRY_DOWNLOAD_NAME.get());
parameters.put("email", settings.BILLING_USER_EMAIL.get());
parameters.put("cemail", prevEmail);
parameters.put("userid", settings.BILLING_USER_ID.get());
parameters.put("token", settings.BILLING_USER_TOKEN.get());
showProgress(null);
showProgress(null);
AndroidNetworkUtils.sendRequestAsync(getMyApplication(),
"https://osmand.net/subscription/update",
parameters, "Sending data...", true, true, new AndroidNetworkUtils.OnRequestResultListener() {
@Override
public void onResult(String result) {
dismissProgress(null);
OsmandApplication app = getMyApplication();
if (result != null) {
try {
JSONObject obj = new JSONObject(result);
if (!obj.has("error")) {
String userId = obj.getString("userid");
app.getSettings().BILLING_USER_ID.set(userId);
String email = obj.getString("email");
app.getSettings().BILLING_USER_EMAIL.set(email);
String visibleName = obj.getString("visibleName");
if (!Algorithms.isEmpty(visibleName)) {
app.getSettings().BILLING_USER_NAME.set(visibleName);
app.getSettings().BILLING_HIDE_USER_NAME.set(false);
} else {
app.getSettings().BILLING_HIDE_USER_NAME.set(true);
}
String preferredCountry = obj.getString("preferredCountry");
app.getSettings().BILLING_USER_COUNTRY_DOWNLOAD_NAME.set(preferredCountry);
Fragment parent = getParentFragment();
if (parent != null && parent instanceof LiveUpdatesFragment) {
((LiveUpdatesFragment) parent).updateSubscriptionHeader();
}
dismiss();
AndroidNetworkUtils.sendRequestAsync(getMyApplication(),
"https://osmand.net/subscription/update",
parameters, "Sending data...", true, true, new AndroidNetworkUtils.OnRequestResultListener() {
@Override
public void onResult(String result) {
dismissProgress(null);
OsmandApplication app = getMyApplication();
if (result != null) {
try {
JSONObject obj = new JSONObject(result);
if (!obj.has("error")) {
String userId = obj.getString("userid");
app.getSettings().BILLING_USER_ID.set(userId);
String email = obj.getString("email");
app.getSettings().BILLING_USER_EMAIL.set(email);
String visibleName = obj.getString("visibleName");
if (!Algorithms.isEmpty(visibleName)) {
app.getSettings().BILLING_USER_NAME.set(visibleName);
app.getSettings().BILLING_HIDE_USER_NAME.set(false);
} else {
app.showToastMessage("Error: " + obj.getString("error"));
app.getSettings().BILLING_HIDE_USER_NAME.set(true);
}
} catch (JSONException e) {
app.showToastMessage(getString(R.string.shared_string_io_error));
String preferredCountry = obj.getString("preferredCountry");
app.getSettings().BILLING_USER_COUNTRY_DOWNLOAD_NAME.set(preferredCountry);
Fragment parent = getParentFragment();
if (parent instanceof LiveUpdatesFragment) {
((LiveUpdatesFragment) parent).updateSubscriptionHeader();
}
dismiss();
} else {
app.showToastMessage("Error: " + obj.getString("error"));
}
} else {
} catch (JSONException e) {
app.showToastMessage(getString(R.string.shared_string_io_error));
}
} else {
app.showToastMessage(getString(R.string.shared_string_io_error));
}
});
}
}
});
}
});
} else {
editModeBottomView.setVisibility(View.GONE);
purchaseCard.setVisibility(View.VISIBLE);
updatePrice(view);
final Button subscribeButton = (Button) view.findViewById(R.id.subscribeButton);
subscribeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
InAppPurchaseHelper purchaseHelper = getInAppPurchaseHelper();
if (purchaseHelper != null) {
if (applySettings(userNameEdit.getText().toString().trim(),
emailEdit.getText().toString().trim(), hideUserNameCheckbox.isChecked())) {
purchaseHelper.purchaseLiveUpdates(getActivity(),
settings.BILLING_USER_EMAIL.get(),
settings.BILLING_USER_NAME.get(),
settings.BILLING_USER_COUNTRY_DOWNLOAD_NAME.get(),
settings.BILLING_HIDE_USER_NAME.get());
}
}
}
});
}
}
});
setThemedDrawable((ImageView) view.findViewById(R.id.userNameIcon), R.drawable.ic_person);
setThemedDrawable((ImageView) view.findViewById(R.id.emailIcon), R.drawable.ic_action_message);
@ -351,7 +299,6 @@ public class SubscriptionFragment extends BaseOsmAndDialogFragment implements In
@Override
public void onGetItems() {
updatePrice(getView());
}
@Override
@ -390,17 +337,4 @@ public class SubscriptionFragment extends BaseOsmAndDialogFragment implements In
}
}
}
private void updatePrice(View view) {
if (view == null) {
view = getView();
}
if (view != null) {
TextView priceTextView = (TextView) view.findViewById(R.id.priceTextView);
InAppPurchaseHelper purchaseHelper = getInAppPurchaseHelper();
if (purchaseHelper.getLiveUpdatesPrice() != null) {
priceTextView.setText(purchaseHelper.getLiveUpdatesPrice());
}
}
}
}