start working on subscriptions

This commit is contained in:
Phillip Thelen 2017-01-10 18:05:03 +01:00
parent 8d1ab45424
commit b575287f85
21 changed files with 639 additions and 60 deletions

View file

@ -9,7 +9,7 @@
<uses-sdk
android:minSdkVersion="15"
android:targetSdkVersion="24" />
android:targetSdkVersion="25" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

View file

@ -93,7 +93,7 @@ dependencies {
compile 'org.greenrobot:eventbus:3.0.0'
// IAP Handling / Verification
compile 'org.solovyev.android:checkout:0.7.5@aar'
compile 'org.solovyev.android:checkout:0.9.1@aar'
//Facebook
compile('com.facebook.android:facebook-android-sdk:4.17.0') {
@ -130,10 +130,10 @@ dependencies {
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
//Push Notifications
compile 'com.google.firebase:firebase-core:9.4.0'
compile 'com.google.firebase:firebase-messaging:9.4.0'
compile 'com.google.firebase:firebase-core:10.0.1'
compile 'com.google.firebase:firebase-messaging:10.0.1'
compile 'com.google.android.gms:play-services-auth:9.4.0'
compile 'com.google.android.gms:play-services-auth:10.0.1'
compile(project(':seeds-sdk')) {
exclude group: 'com.google.android.gms'

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="oval">
<corners android:radius="@dimen/daily_checkbox_corner_radius"/>
<solid android:color="@color/brand_100" />
<stroke android:color="@color/transparent" android:width="5dp" />
</shape>
</item>
<item>
<shape android:shape="oval">
<corners android:radius="@dimen/daily_checkbox_corner_radius"/>
<stroke android:color="@color/brand_100" android:width="1dp" />
<size android:height="16dp" android:width="16dp" />
</shape>
</item>
</layer-list>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<corners android:radius="@dimen/daily_checkbox_corner_radius"/>
<stroke android:color="@color/brand_100" android:width="1dp" />
<size android:height="16dp" android:width="16dp" />
<padding android:left="0dip" android:top="0dip" android:right="0dip" android:bottom="0dip" />
</shape>

View file

@ -19,10 +19,11 @@
android:elevation="0dp"
tools:context=".ui.activities.MainActivity">
<FrameLayout
android:id="@+id/fragment_container"
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<android.support.design.widget.AppBarLayout
@ -63,7 +64,7 @@
</android.support.design.widget.CollapsingToolbarLayout>
<android.support.design.widget.TabLayout
android:id="@+id/detail_tabs"
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="54dp"
android:layout_gravity="bottom"
@ -78,8 +79,7 @@
app:layout_collapseMode="pin"
app:tabGravity="fill"
app:tabIndicatorColor="@android:color/white"
app:tabMode="fixed"
android:visibility="gone"/>
app:tabMode="fixed" />
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>

View file

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbarSize="3dp"
android:scrollbarThumbVertical="@color/md_grey_500"
android:scrollbars="vertical">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp">
<TextView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingBottom="20dp"
android:text="@string/subscribe.title"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:textColor="@color/best_10"
android:drawableTop="@drawable/ic_header_heart"
android:textSize="14sp"
android:lineSpacingExtra="4dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.habitrpg.android.habitica.ui.SubscriptionOptionView
android:id="@+id/subscription1month"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:recurringText="month" />
<com.habitrpg.android.habitica.ui.SubscriptionOptionView
android:id="@+id/subscription3month"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:recurringText="3 months" />
<com.habitrpg.android.habitica.ui.SubscriptionOptionView
android:id="@+id/subscription6month"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:recurringText="6 months" />
<com.habitrpg.android.habitica.ui.SubscriptionOptionView
android:id="@+id/subscription12month"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:recurringText="12 months" />
</LinearLayout>
<Button
android:text="@string/subscribe"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/subscribeButton"
android:textColor="@color/white"
android:background="@drawable/rounded_purple_square"
android:enabled="false" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/subscribe.subtitle"
android:gravity="center"
android:textStyle="normal|bold"
android:textColor="@color/brand_100"
android:textSize="16sp"
android:lineSpacingExtra="4dp"
android:layout_marginTop="23dp"
android:layout_marginBottom="12dp"/>
<TextView
android:text="@string/subscribe.listitem1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/GemPurchaseListItem"
/>
<TextView
android:text="@string/subscribe.listitem1.description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/GemPurchaseListItemDescription" />
<TextView
android:text="@string/subscribe.listitem2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/GemPurchaseListItem"
/>
<TextView
android:text="@string/subscribe.listitem2.description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/GemPurchaseListItemDescription" />
<TextView
android:text="@string/subscribe.listitem3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/GemPurchaseListItem"
/>
<TextView
android:text="@string/subscribe.listitem3.description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/GemPurchaseListItemDescription" />
<TextView
android:text="@string/subscribe.listitem4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/GemPurchaseListItem"
/>
<TextView
android:text="@string/subscribe.listitem4.description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/GemPurchaseListItemDescription" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>

View file

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/habit_inactive_gray"
android:gravity="center"
android:minHeight="80dp"
android:layout_marginBottom="12dp">
<FrameLayout
android:layout_width="50dp"
android:layout_height="match_parent"
android:background="@color/brand_700">
<View
android:id="@+id/subscriptionSelectedView"
android:layout_width="16dp"
android:layout_height="16dp"
android:background="@drawable/subscription_unselected"
android:layout_gravity="center"/>
</FrameLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/priceLabel"
android:textColor="@color/brand_100"
android:gravity="center"
android:textSize="36sp"
tools:text="$ 21"
android:layout_marginStart="8dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/descriptionTextView"
android:textColor="@color/brand_100"
tools:text="recurring every month"
android:layout_marginStart="16dp"/>
</LinearLayout>

View file

@ -14,5 +14,7 @@
<declare-styleable name="MaxHeightScrollView">
<attr name="maxHeightMultiplier" format="float" />
<declare-styleable name="SubscriptionOptionView">
<attr name="recurringText" format="string" />
</declare-styleable>
</resources>

View file

@ -109,4 +109,5 @@
<color name="task_secondary_text">#B2B2B2</color>
<color name="header_class_name">#CCBEED</color>
<color name="gem_icon_color">#24cc8f</color>
<color name="subscription_description_text">#b1000000</color>
</resources>

View file

@ -91,5 +91,5 @@
<dimen name="task_icon_space">12dp</dimen>
<dimen name="task_icon_size">18dp</dimen>
</resources>
<dimen name="task_info_bar_bottom_space">-8dp</dimen>
</resources>

View file

@ -18,5 +18,5 @@
<string name="qr_album_name" translatable="false">habitica</string>
<string name="qr_file_name" translatable="false"> habitrpg-qr-code.jpg</string>
<string name="habitica_user_count">2,000,000</string>
</resources>
<string name="habitica_user_count" translatable="false">2,000,000</string>
</resources>

View file

@ -448,6 +448,19 @@ To start, which parts of your life do you want to improve?</string>
<string name="belongs_to_challenge">Belongs to Challenge</string>
<string name="has_reminder">Has Reminder</string>
<string name="has_tag">Has Tag</string>
<string name="subscribe.title">Subscribing supports the developers and helps keep Habitica running</string>
<string name="subscribe.subtitle">Become a subscriber and youll get these useful benefits:</string>
<string name="subscribe.listitem1">Buy gems with gold</string>
<string name="subscribe.listitem2">Exclusive monthly items</string>
<string name="subscribe.listitem3">Retain additional history entries</string>
<string name="subscribe.listitem4">Daily drop-caps doubled</string>
<string name="subscriptions">Subscriptions</string>
<string name="subscription_duration" formatted="false">Recurring every %s</string>
<string name="subscribe">Subscribe</string>
<string name="subscribe.listitem1.description">Alexander the Merchant will sell you Gems at a cost of 20 gold per gem. His monthly shipments are initially capped at 25 Gems per month, but this cap increases by 5 Gems for every three months of consecutive subscription, up to a maximum of 50 Gems per month!</string>
<string name="subscribe.listitem2.description">Makes completed To-Dos and task history available for longer.</string>
<string name="subscribe.listitem3.description">Complete your stable faster!</string>
<string name="subscribe.listitem4.description">Each month you will receive a unique cosmetic item for your avatar! Plus, for every three months of consecutive subscription, the Mysterious Time Travelers will grant you access to historic (and futuristic!) cosmetic items.</string>
<string name="byLeader" formatted="false">by %s</string>
<string name="challenge_details">Challenge Details</string>

View file

@ -200,4 +200,11 @@
<item name="android:paddingTop">@dimen/section_top_padding</item>
<item name="android:paddingBottom">@dimen/section_top_padding</item>
</style>
<style name="GemPurchaseListItemDescription">
<item name="android:layout_gravity">center_horizontal</item>
<item name="android:gravity">center_horizontal</item>
<item name="android:textSize">12sp</item>
<item name="android:textColor">@color/subscription_description_text</item>
</style>
</resources>

View file

@ -33,8 +33,8 @@ import com.squareup.leakcanary.LeakCanary;
import org.solovyev.android.checkout.Billing;
import org.solovyev.android.checkout.Cache;
import org.solovyev.android.checkout.Checkout;
import org.solovyev.android.checkout.Inventory;
import org.solovyev.android.checkout.ProductTypes;
import org.solovyev.android.checkout.Products;
import org.solovyev.android.checkout.PurchaseVerifier;
import java.io.File;
@ -336,7 +336,7 @@ public abstract class HabiticaBaseApplication extends MultiDexApplication {
});
checkout = Checkout.forApplication(billing, Products.create().add(ProductTypes.IN_APP, PurchaseTypes.allTypes));
checkout = Checkout.forApplication(billing);
}
@NonNull
@ -344,6 +344,10 @@ public abstract class HabiticaBaseApplication extends MultiDexApplication {
return checkout;
}
public Billing getBilling() {
return billing;
}
// endregion
public static AppComponent getComponent() {

View file

@ -32,6 +32,7 @@ import com.habitrpg.android.habitica.ui.adapter.tasks.RewardsRecyclerViewAdapter
import com.habitrpg.android.habitica.ui.adapter.tasks.TodosRecyclerViewAdapter;
import com.habitrpg.android.habitica.ui.fragments.GemsPurchaseFragment;
import com.habitrpg.android.habitica.ui.fragments.NewsFragment;
import com.habitrpg.android.habitica.ui.fragments.SubscriptionFragment;
import com.habitrpg.android.habitica.ui.fragments.faq.FAQDetailFragment;
import com.habitrpg.android.habitica.ui.fragments.faq.FAQOverviewFragment;
import com.habitrpg.android.habitica.ui.fragments.inventory.customization.AvatarCustomizationFragment;
@ -220,6 +221,7 @@ public interface AppComponent {
void inject(TodosRecyclerViewAdapter todosRecyclerViewAdapter);
void inject(SubscriptionFragment subscriptionFragment);
void inject(ChallengeTasksRecyclerViewFragment.ChallengeTasksRecyclerViewAdapter challengeTasksRecyclerViewAdapter);
void inject(ChallengeTasksRecyclerViewFragment challengeTasksRecyclerViewFragment);

View file

@ -11,5 +11,12 @@ public class PurchaseTypes {
public static String Purchase42Gems = "com.habitrpg.android.habitica.iap.42gems";
public static String Purchase84Gems = "com.habitrpg.android.habitica.iap.84gems";
public static List<String> allTypes = Arrays.asList(Purchase4Gems, Purchase21Gems, Purchase42Gems, Purchase84Gems);
public static List<String> allGemTypes = Arrays.asList(Purchase4Gems, Purchase21Gems, Purchase42Gems, Purchase84Gems);
public static String Subscription1Month = "com.habitrpg.android.habitica.subscription.1month";
public static String Subscription3Month = "com.habitrpg.android.habitica.subscription.3month";
public static String Subscription6Month = "com.habitrpg.android.habitica.subscription.6month";
public static String Subscription12Month = "com.habitrpg.android.habitica.subscription.12month";
public static List<String> allSubscriptionTypes = Arrays.asList(Subscription1Month, Subscription3Month, Subscription6Month, Subscription12Month);
}

View file

@ -0,0 +1,72 @@
package com.habitrpg.android.habitica.ui;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import com.habitrpg.android.habitica.R;
import butterknife.BindView;
import butterknife.ButterKnife;
public class SubscriptionOptionView extends FrameLayout {
@BindView(R.id.priceLabel)
TextView priceTextView;
@BindView(R.id.descriptionTextView)
TextView descriptionTextView;
@BindView(R.id.subscriptionSelectedView)
View subscriptionSelectedView;
private String sku;
public SubscriptionOptionView(Context context, AttributeSet attrs) {
super(context, attrs);
inflate(context, R.layout.purchase_subscription_view, this);
ButterKnife.bind(this);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.SubscriptionOptionView,
0, 0);
descriptionTextView.setText(context.getString(R.string.subscription_duration, a.getText(R.styleable.SubscriptionOptionView_recurringText)));
}
public void setOnPurchaseClickListener(Button.OnClickListener listener) {
this.setOnClickListener(listener);
}
public void setPriceText(String text) {
this.priceTextView.setText(text);
}
public void setSku(String sku) {
this.sku = sku;
}
public String getSku() {
return sku;
}
public void setIsPurchased(boolean purchased) {
if (purchased) {
subscriptionSelectedView.setBackgroundResource(R.drawable.subscription_selected);
} else {
subscriptionSelectedView.setBackgroundResource(R.drawable.subscription_unselected);
}
}
}

View file

@ -6,6 +6,8 @@ import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.helpers.PurchaseTypes;
import com.habitrpg.android.habitica.ui.fragments.GemsPurchaseFragment;
import com.habitrpg.android.habitica.ui.fragments.SubscriptionFragment;
import com.habitrpg.android.habitica.ui.fragments.social.party.PartyInviteFragment;
import com.playseeds.android.sdk.Seeds;
import com.playseeds.android.sdk.inappmessaging.InAppMessageListener;
@ -14,13 +16,30 @@ import org.solovyev.android.checkout.Checkout;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
public class GemPurchaseActivity extends BaseActivity implements GemsPurchaseFragment.Listener, InAppMessageListener {
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
public class GemPurchaseActivity extends BaseActivity implements InAppMessageListener {
private ActivityCheckout checkout;
@BindView(R.id.tab_layout)
TabLayout tabLayout;
@BindView(R.id.view_pager)
ViewPager viewPager;
List<CheckoutFragment> fragments = new ArrayList<>();
@Override
protected int getLayoutResId() {
return R.layout.activity_gem_purchase;
@ -58,9 +77,9 @@ public class GemPurchaseActivity extends BaseActivity implements GemsPurchaseFra
getSupportActionBar().setTitle(R.string.gem_purchase_toolbartitle);
}
GemsPurchaseFragment firstFragment = new GemsPurchaseFragment();
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
viewPager.setCurrentItem(0);
setViewPagerAdapter();
}
@Override
@ -81,11 +100,10 @@ public class GemPurchaseActivity extends BaseActivity implements GemsPurchaseFra
}
private void setupCheckout() {
checkout = Checkout.forActivity(this, HabiticaApplication.getInstance(this).getCheckout());
checkout = Checkout.forActivity(this, HabiticaApplication.getInstance(this).getBilling());
checkout.start();
}
@Override
public ActivityCheckout getActivityCheckout() {
return checkout;
}
@ -135,4 +153,55 @@ public class GemPurchaseActivity extends BaseActivity implements GemsPurchaseFra
System.out.println("Exception: " + e);
}
}
public void setViewPagerAdapter() {
android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager();
viewPager.setAdapter(new FragmentPagerAdapter(fragmentManager) {
@Override
public Fragment getItem(int position) {
CheckoutFragment fragment;
if (position == 0) {
fragment = new GemsPurchaseFragment();
} else {
fragment = new SubscriptionFragment();
}
if (fragments.size() > position) {
fragments.set(position, fragment);
} else {
fragments.add(fragment);
}
fragment.setListener(GemPurchaseActivity.this);
fragment.setupCheckout();
return (Fragment)fragment;
}
@Override
public int getCount() {
return 2;
}
@Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return getString(R.string.gems);
case 1:
return getString(R.string.subscriptions);
}
return "";
}
});
if (tabLayout != null && viewPager != null) {
tabLayout.setupWithViewPager(viewPager);
}
}
public interface CheckoutFragment {
void setupCheckout();
void setListener(GemPurchaseActivity listener);
}
}

View file

@ -7,8 +7,6 @@ import com.habitrpg.android.habitica.helpers.PurchaseTypes;
import com.habitrpg.android.habitica.proxy.ifce.CrashlyticsProxy;
import com.habitrpg.android.habitica.ui.GemPurchaseOptionsView;
import com.habitrpg.android.habitica.ui.activities.GemPurchaseActivity;
import com.habitrpg.android.habitica.ui.helpers.ViewHelper;
import com.playseeds.android.sdk.Seeds;
import org.greenrobot.eventbus.EventBus;
import org.solovyev.android.checkout.ActivityCheckout;
@ -25,6 +23,7 @@ import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -36,7 +35,7 @@ import javax.inject.Inject;
import butterknife.BindView;
public class GemsPurchaseFragment extends BaseFragment {
public class GemsPurchaseFragment extends BaseFragment implements GemPurchaseActivity.CheckoutFragment {
@BindView(R.id.gems_4_view)
GemPurchaseOptionsView gems4View;
@ -50,19 +49,11 @@ public class GemsPurchaseFragment extends BaseFragment {
@Inject
CrashlyticsProxy crashlyticsProxy;
private HashMap<String, String> priceMap;
private static final int GEMS_TO_ADD = 21;
Button btnPurchaseGems;
private Listener listener;
private GemPurchaseActivity listener;
private BillingRequests billingRequests;
@Override
public void onAttach(Context context) {
super.onAttach(context);
listener = (Listener) context;
}
private Inventory inventory;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
@ -70,8 +61,6 @@ public class GemsPurchaseFragment extends BaseFragment {
super.onCreateView(inflater, container, savedInstanceState);
priceMap = new HashMap<>();
return inflater.inflate(R.layout.fragment_gem_purchase, container, false);
}
@ -90,16 +79,20 @@ public class GemsPurchaseFragment extends BaseFragment {
gems84View.setOnPurchaseClickListener(v -> purchaseGems(PurchaseTypes.Purchase84Gems));
gems84View.seedsImageButton.setOnClickListener(v -> ((GemPurchaseActivity)this.getActivity()).showSeedsPromo(getString(R.string.seeds_interstitial_gems), "store"));
}
@Override
public void setupCheckout() {
final ActivityCheckout checkout = listener.getActivityCheckout();
if (checkout != null) {
checkout.destroyPurchaseFlow();
inventory = checkout.makeInventory();
checkout.createPurchaseFlow(new RequestListener<Purchase>() {
@Override
public void onSuccess(@NonNull Purchase purchase) {
if (PurchaseTypes.allTypes.contains(purchase.sku)) {
if (PurchaseTypes.allGemTypes.contains(purchase.sku)) {
billingRequests.consume(purchase.token, new RequestListener<Object>() {
@Override
public void onSuccess(@NonNull Object o) {
@ -139,24 +132,29 @@ public class GemsPurchaseFragment extends BaseFragment {
@Override
public void onReady(@NonNull BillingRequests billingRequests, @NonNull String s, boolean b) {
checkout.loadInventory().whenLoaded(products -> {
Inventory.Product gems = products.get(ProductTypes.IN_APP);
java.util.List<Sku> skus = gems.getSkus();
for (Sku sku : skus) {
priceMap.put(sku.id, sku.price);
updateButtonLabel(sku.id, sku.price);
}
});
}
});
inventory.load(Inventory.Request.create()
.loadAllPurchases().loadSkus(ProductTypes.IN_APP, PurchaseTypes.allGemTypes),
products -> {
Inventory.Product gems = products.get(ProductTypes.IN_APP);
if (!gems.supported) {
// billing is not supported, user can't purchase anything
return;
}
java.util.List<Sku> skus = gems.getSkus();
for (Sku sku : skus) {
updateButtonLabel(sku.id.code, sku.price);
}
});
}
}
@Override
public void setListener(GemPurchaseActivity listener) {
this.listener = listener;
}
private void updateButtonLabel(String sku, String price) {
GemPurchaseOptionsView matchingView;
if (sku.equals(PurchaseTypes.Purchase4Gems)) {
@ -179,7 +177,7 @@ public class GemsPurchaseFragment extends BaseFragment {
@Override
public void onSuccess(@NonNull Purchases purchases) {
for (Purchase purchase : purchases.list) {
if (PurchaseTypes.allTypes.contains(purchase.sku)) {
if (PurchaseTypes.allGemTypes.contains(purchase.sku)) {
billingRequests.consume(purchase.token, new RequestListener<Object>() {
@Override
public void onSuccess(@NonNull Object o) {
@ -223,8 +221,4 @@ public class GemsPurchaseFragment extends BaseFragment {
});
}
public interface Listener {
ActivityCheckout getActivityCheckout();
}
}

View file

@ -0,0 +1,219 @@
package com.habitrpg.android.habitica.ui.fragments;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.events.BoughtGemsEvent;
import com.habitrpg.android.habitica.helpers.PurchaseTypes;
import com.habitrpg.android.habitica.proxy.ifce.CrashlyticsProxy;
import com.habitrpg.android.habitica.ui.SubscriptionOptionView;
import com.habitrpg.android.habitica.ui.activities.GemPurchaseActivity;
import org.greenrobot.eventbus.EventBus;
import org.solovyev.android.checkout.ActivityCheckout;
import org.solovyev.android.checkout.BillingRequests;
import org.solovyev.android.checkout.Checkout;
import org.solovyev.android.checkout.Inventory;
import org.solovyev.android.checkout.ProductTypes;
import org.solovyev.android.checkout.Purchase;
import org.solovyev.android.checkout.Purchases;
import org.solovyev.android.checkout.RequestListener;
import org.solovyev.android.checkout.Sku;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import javax.inject.Inject;
import butterknife.BindView;
public class SubscriptionFragment extends BaseFragment implements GemPurchaseActivity.CheckoutFragment {
@Inject
CrashlyticsProxy crashlyticsProxy;
@BindView(R.id.subscription1month)
SubscriptionOptionView subscription1MonthView;
@BindView(R.id.subscription3month)
SubscriptionOptionView subscription3MonthView;
@BindView(R.id.subscription6month)
SubscriptionOptionView subscription6MonthView;
@BindView(R.id.subscription12month)
SubscriptionOptionView subscription12MonthView;
@BindView(R.id.subscribeButton)
Button subscriptionButton;
@Nullable
String selectedSubscriptionSku;
private GemPurchaseActivity listener;
private BillingRequests billingRequests;
private Inventory inventory;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
return inflater.inflate(R.layout.fragment_subscription, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
this.subscription1MonthView.setOnPurchaseClickListener(view1 -> selectSubscription(PurchaseTypes.Subscription1Month));
this.subscription3MonthView.setOnPurchaseClickListener(view1 -> selectSubscription(PurchaseTypes.Subscription3Month));
this.subscription6MonthView.setOnPurchaseClickListener(view1 -> selectSubscription(PurchaseTypes.Subscription6Month));
this.subscription12MonthView.setOnPurchaseClickListener(view1 -> selectSubscription(PurchaseTypes.Subscription12Month));
}
@Override
public void injectFragment(AppComponent component) {
component.inject(this);
}
@Override
public void setupCheckout() {
final ActivityCheckout checkout = listener.getActivityCheckout();
if (checkout != null) {
inventory = checkout.makeInventory();
checkout.destroyPurchaseFlow();
checkout.createPurchaseFlow(new RequestListener<Purchase>() {
@Override
public void onSuccess(@NonNull Purchase purchase) {
if (PurchaseTypes.allSubscriptionTypes.contains(purchase.sku)) {
billingRequests.consume(purchase.token, new RequestListener<Object>() {
@Override
public void onSuccess(@NonNull Object o) {
if (purchase.sku.equals(PurchaseTypes.Purchase84Gems)) {
((GemPurchaseActivity)getActivity()).showSeedsPromo(getString(R.string.seeds_interstitial_sharing), "store");
}
}
@Override
public void onError(int i, @NonNull Exception e) {
crashlyticsProxy.fabricLogE("Purchase", "Consume", e);
}
});
}
}
@Override
public void onError(int i, @NonNull Exception e) {
crashlyticsProxy.fabricLogE("Purchase", "Error", e);
}
});
checkout.whenReady(new Checkout.Listener() {
@Override
public void onReady(@NonNull final BillingRequests billingRequests) {
SubscriptionFragment.this.billingRequests = billingRequests;
checkIfPendingPurchases();
}
@Override
public void onReady(@NonNull BillingRequests billingRequests, @NonNull String s, boolean b) {
inventory.load(Inventory.Request.create()
.loadAllPurchases().loadSkus(ProductTypes.SUBSCRIPTION, PurchaseTypes.allSubscriptionTypes),
products -> {
Inventory.Product subscriptions = products.get(ProductTypes.SUBSCRIPTION);
java.util.List<Sku> skus = subscriptions.getSkus();
for (Sku sku : skus) {
updateButtonLabel(sku, sku.price, subscriptions);
}
selectSubscription(PurchaseTypes.Subscription1Month);
});
}
});
}
}
private void updateButtonLabel(Sku sku, String price, Inventory.Product subscriptions) {
SubscriptionOptionView matchingView = buttonForSku(sku);
if (matchingView != null) {
matchingView.setPriceText(price);
matchingView.setSku(sku.id.code);
matchingView.setIsPurchased(subscriptions.isPurchased(sku));
}
}
private void selectSubscription(String sku) {
if (this.selectedSubscriptionSku != null) {
SubscriptionOptionView oldButton = buttonForSku(this.selectedSubscriptionSku);
if (oldButton != null) {
oldButton.setIsPurchased(false);
}
}
this.selectedSubscriptionSku = sku;
SubscriptionOptionView subscriptionOptionButton = buttonForSku(this.selectedSubscriptionSku);
if (subscriptionOptionButton != null) {
subscriptionOptionButton.setIsPurchased(true);
}
this.subscriptionButton.setEnabled(true);
}
@Nullable
private SubscriptionOptionView buttonForSku(Sku sku) {
return buttonForSku(sku.id.code);
}
@Nullable
private SubscriptionOptionView buttonForSku(String sku) {
if (sku.equals(PurchaseTypes.Subscription1Month)) {
return subscription1MonthView;
} else if (sku.equals(PurchaseTypes.Subscription3Month)) {
return subscription3MonthView;
} else if (sku.equals(PurchaseTypes.Subscription6Month)) {
return subscription6MonthView;
} else if (sku.equals(PurchaseTypes.Subscription12Month)) {
return subscription12MonthView;
} else {
return null;
}
}
@Override
public void setListener(GemPurchaseActivity listener) {
this.listener = listener;
}
private void checkIfPendingPurchases() {
billingRequests.getAllPurchases(ProductTypes.SUBSCRIPTION, new RequestListener<Purchases>() {
@Override
public void onSuccess(@NonNull Purchases purchases) {
for (Purchase purchase : purchases.list) {
if (PurchaseTypes.allGemTypes.contains(purchase.sku)) {
billingRequests.consume(purchase.token, new RequestListener<Object>() {
@Override
public void onSuccess(@NonNull Object o) {
}
@Override
public void onError(int i, @NonNull Exception e) {
crashlyticsProxy.fabricLogE("Purchase", "Consume", e);
}
});
}
}
}
@Override
public void onError(int i, @NonNull Exception e) {
crashlyticsProxy.fabricLogE("Purchase", "getAllPurchases", e);
}
});
}
}

View file

@ -46,11 +46,11 @@ android {
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'org.glassfish:javax.json:1.0.4'
compile 'com.google.android.gms:play-services:9.2.0'
compile 'com.google.android.gms:play-services:10.0.1'
compile 'com.google.code.gson:gson:2.4'
compile 'com.android.support:multidex:1.0.1'
compile 'com.loopj.android:android-async-http:1.4.9'
compile 'org.solovyev.android:checkout:0.7.5@aar'
compile 'org.solovyev.android:checkout:0.9.1@aar'
androidTestCompile 'org.mockito:mockito-core:1.9.5'
androidTestCompile 'com.google.dexmaker:dexmaker:1.0'
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.0'