diff --git a/Habitica/AndroidManifest.xml b/Habitica/AndroidManifest.xml
index 48932a209..d8d7ad509 100644
--- a/Habitica/AndroidManifest.xml
+++ b/Habitica/AndroidManifest.xml
@@ -9,7 +9,7 @@
+ android:targetSdkVersion="25" />
diff --git a/Habitica/build.gradle b/Habitica/build.gradle
index 4feb852f5..2fb81d47f 100644
--- a/Habitica/build.gradle
+++ b/Habitica/build.gradle
@@ -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'
diff --git a/Habitica/res/drawable/subscription_selected.xml b/Habitica/res/drawable/subscription_selected.xml
new file mode 100644
index 000000000..da78dfc66
--- /dev/null
+++ b/Habitica/res/drawable/subscription_selected.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
diff --git a/Habitica/res/drawable/subscription_unselected.xml b/Habitica/res/drawable/subscription_unselected.xml
new file mode 100644
index 000000000..c290a3fdd
--- /dev/null
+++ b/Habitica/res/drawable/subscription_unselected.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/Habitica/res/layout/activity_gem_purchase.xml b/Habitica/res/layout/activity_gem_purchase.xml
index 7776fe9a0..4c0e82efc 100644
--- a/Habitica/res/layout/activity_gem_purchase.xml
+++ b/Habitica/res/layout/activity_gem_purchase.xml
@@ -19,10 +19,11 @@
android:elevation="0dp"
tools:context=".ui.activities.MainActivity">
-
+ app:tabMode="fixed" />
diff --git a/Habitica/res/layout/fragment_subscription.xml b/Habitica/res/layout/fragment_subscription.xml
new file mode 100644
index 000000000..881cf9e92
--- /dev/null
+++ b/Habitica/res/layout/fragment_subscription.xml
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/purchase_subscription_view.xml b/Habitica/res/layout/purchase_subscription_view.xml
new file mode 100644
index 000000000..3df53f8e3
--- /dev/null
+++ b/Habitica/res/layout/purchase_subscription_view.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/values/attrs.xml b/Habitica/res/values/attrs.xml
index 1a650745c..69ce1ea97 100644
--- a/Habitica/res/values/attrs.xml
+++ b/Habitica/res/values/attrs.xml
@@ -14,5 +14,7 @@
+
+
diff --git a/Habitica/res/values/colors.xml b/Habitica/res/values/colors.xml
index da169231f..fde09fd31 100644
--- a/Habitica/res/values/colors.xml
+++ b/Habitica/res/values/colors.xml
@@ -109,4 +109,5 @@
#B2B2B2
#CCBEED
#24cc8f
+ #b1000000
diff --git a/Habitica/res/values/dimens.xml b/Habitica/res/values/dimens.xml
index 8894adca2..a20567454 100644
--- a/Habitica/res/values/dimens.xml
+++ b/Habitica/res/values/dimens.xml
@@ -91,5 +91,5 @@
12dp
18dp
-
-
\ No newline at end of file
+ -8dp
+
diff --git a/Habitica/res/values/strings.constants.xml b/Habitica/res/values/strings.constants.xml
index 57e14d57c..1b8b6428e 100644
--- a/Habitica/res/values/strings.constants.xml
+++ b/Habitica/res/values/strings.constants.xml
@@ -18,5 +18,5 @@
habitica
habitrpg-qr-code.jpg
- 2,000,000
-
\ No newline at end of file
+ 2,000,000
+
diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml
index 2113e6d5d..96c7c58ec 100644
--- a/Habitica/res/values/strings.xml
+++ b/Habitica/res/values/strings.xml
@@ -448,6 +448,19 @@ To start, which parts of your life do you want to improve?
Belongs to Challenge
Has Reminder
Has Tag
+ Subscribing supports the developers and helps keep Habitica running
+ Become a subscriber and you’ll get these useful benefits:
+ Buy gems with gold
+ Exclusive monthly items
+ Retain additional history entries
+ Daily drop-caps doubled
+ Subscriptions
+ Recurring every %s
+ Subscribe
+ 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!
+ Makes completed To-Dos and task history available for longer.
+ Complete your stable faster!
+ 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.
by %s
Challenge Details
diff --git a/Habitica/res/values/styles.xml b/Habitica/res/values/styles.xml
index 36f54eea9..9049a207e 100644
--- a/Habitica/res/values/styles.xml
+++ b/Habitica/res/values/styles.xml
@@ -200,4 +200,11 @@
- @dimen/section_top_padding
- @dimen/section_top_padding
+
+
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/HabiticaBaseApplication.java b/Habitica/src/main/java/com/habitrpg/android/habitica/HabiticaBaseApplication.java
index 2655c180c..0afd18362 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/HabiticaBaseApplication.java
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/HabiticaBaseApplication.java
@@ -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() {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/components/AppComponent.java b/Habitica/src/main/java/com/habitrpg/android/habitica/components/AppComponent.java
index a6e93ed59..3fcf97f50 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/components/AppComponent.java
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/components/AppComponent.java
@@ -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);
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/PurchaseTypes.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/PurchaseTypes.java
index b7befef4b..7286f7711 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/PurchaseTypes.java
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/PurchaseTypes.java
@@ -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 allTypes = Arrays.asList(Purchase4Gems, Purchase21Gems, Purchase42Gems, Purchase84Gems);
+ public static List 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 allSubscriptionTypes = Arrays.asList(Subscription1Month, Subscription3Month, Subscription6Month, Subscription12Month);
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/SubscriptionOptionView.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/SubscriptionOptionView.java
new file mode 100644
index 000000000..5a6f29207
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/SubscriptionOptionView.java
@@ -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);
+ }
+ }
+}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GemPurchaseActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GemPurchaseActivity.java
index a4e309fbf..ecca75079 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GemPurchaseActivity.java
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GemPurchaseActivity.java
@@ -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 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);
+ }
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/GemsPurchaseFragment.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/GemsPurchaseFragment.java
index fc83ab8c1..68e93cf1d 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/GemsPurchaseFragment.java
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/GemsPurchaseFragment.java
@@ -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 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() {
@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