Merge branch 'develop' into ability-change-checklist

Conflicts:
	Habitica/src/com/habitrpg/android/habitica/HabiticaApplication.java
	Habitica/src/com/habitrpg/android/habitica/MainActivity.java
	Habitica/src/com/habitrpg/android/habitica/TaskFormActivity.java
	Habitica/src/com/habitrpg/android/habitica/ui/AvatarWithBarsViewModel.java
	Habitica/src/com/habitrpg/android/habitica/ui/adapter/HabitItemRecyclerViewAdapter.java
This commit is contained in:
Negue 2015-12-01 01:22:09 +01:00
commit 41ef6e59d1
21 changed files with 426 additions and 106 deletions

View file

@ -1,4 +1,21 @@
[
{
"name": "Version 0.0.11",
"items":[
{
"type": "F",
"title": "Ability to Change/See/Add checklists"
},
{
"type": "F",
"title": "Settings Screen with logout options and account details."
},
{
"type": "B",
"title": "fix clear database"
}
]
},
{
"name": "Version 0.0.10",
"items":[
@ -204,4 +221,4 @@
}
]
}
]
]

View file

@ -88,7 +88,7 @@ dependencies {
compile 'de.greenrobot:eventbus:2.4.0'
// IAP Handling / Verification
compile 'org.solovyev.android:checkout:0.7.4@aar'
compile 'org.solovyev.android:checkout:0.7.5@aar'
compile 'com.facebook.android:facebook-android-sdk:4.7.0'
//Material Dialogs

View file

@ -3,10 +3,22 @@
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginTop="5dp"
android:text="Help support Habitica"
android:layout_gravity="center_horizontal">
</TextView>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Buy"
android:text="+ 21"
android:id="@+id/btn.purchase.gems"
android:layout_gravity="center_horizontal" />
android:layout_gravity="center_horizontal"
android:drawableRight="@drawable/ic_header_gem"
android:drawablePadding="5dp"
android:textColor="@color/white"/>
</LinearLayout>

View file

@ -20,8 +20,11 @@ import com.habitrpg.android.habitica.callbacks.TaskDeletionCallback;
import com.habitrpg.android.habitica.callbacks.TaskScoringCallback;
import com.habitrpg.android.habitica.database.CheckListItemExcludeStrategy;
import com.magicmicky.habitrpgwrapper.lib.api.ApiService;
import com.magicmicky.habitrpgwrapper.lib.api.InAppPurchasesApiService;
import com.magicmicky.habitrpgwrapper.lib.api.Server;
import com.magicmicky.habitrpgwrapper.lib.api.TypeAdapter.TagsAdapter;
import com.magicmicky.habitrpgwrapper.lib.models.PurchaseValidationRequest;
import com.magicmicky.habitrpgwrapper.lib.models.PurchaseValidationResult;
import com.magicmicky.habitrpgwrapper.lib.models.SkillList;
import com.magicmicky.habitrpgwrapper.lib.models.TaskDirection;
import com.magicmicky.habitrpgwrapper.lib.models.UserAuth;
@ -53,20 +56,21 @@ public class APIHelper implements ErrorHandler, Profiler {
private static final String TAG = "ApiHelper";
// I think we don't need the APIHelper anymore we could just use ApiService
public final ApiService apiService;
private final InAppPurchasesApiService inAppPurchasesService;
private Context mContext;
private HostConfig cfg;
//private OnHabitsAPIResult mResultListener;
//private HostConfig mConfig;
public APIHelper(Context c, final HostConfig cfg) {
this.mContext = c;
this.cfg = cfg;
RequestInterceptor requestInterceptor = new RequestInterceptor() {
@Override
public void intercept(RequestInterceptor.RequestFacade request) {
request.addHeader("x-api-key", cfg.getApi());
request.addHeader("x-api-user", cfg.getUser());
}
};
@ -108,6 +112,19 @@ public class APIHelper implements ErrorHandler, Profiler {
.build();
this.apiService = adapter.create(ApiService.class);
server = new Server(cfg.getAddress(), false);
adapter = new RestAdapter.Builder()
.setEndpoint(server.toString())
.setErrorHandler(this)
.setProfiler(this)
.setLogLevel(BuildConfig.DEBUG ? RestAdapter.LogLevel.FULL : RestAdapter.LogLevel.NONE)
.setRequestInterceptor(requestInterceptor)
.setConverter(new GsonConverter(gson))
.build();
this.inAppPurchasesService = adapter.create(InAppPurchasesApiService.class);
}
private static final TypeAdapter<Boolean> booleanAsIntAdapter = new TypeAdapter<Boolean>() {
@ -252,4 +269,9 @@ public class APIHelper implements ErrorHandler, Profiler {
public void reviveUser(HabitRPGUserCallback cb) {
apiService.revive(cb);
}
public PurchaseValidationResult validatePurchase(PurchaseValidationRequest request)
{
return inAppPurchasesService.validatePurchase(cfg.getUser(), cfg.getApi(), request);
}
}

View file

@ -18,33 +18,34 @@ import com.raizlabs.android.dbflow.config.FlowManager;
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.Purchase;
import org.solovyev.android.checkout.PurchaseVerifier;
import org.solovyev.android.checkout.RequestListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
/**
* Created by Negue on 14.06.2015.
*/
public class HabiticaApplication extends Application {
public static String Purchase20Gems = "com.habitrpg.android.habitica.iap.20.gems";
public static HabiticaApplication Instance;
public static HabitRPGUser User;
public static APIHelper ApiHelper;
@Override
public void onCreate() {
super.onCreate();
Instance = this;
createBillingAndCheckout();
FlowManager.init(this);
FacebookSdk.sdkInitialize(getApplicationContext());
@ -69,6 +70,9 @@ public class HabiticaApplication extends Application {
@Override
public boolean deleteDatabase(String name) {
if(!name.endsWith(".db")){
name += ".db";
}
return super.deleteDatabase(getDatabasePath(name).getAbsolutePath());
}
@ -77,7 +81,7 @@ public class HabiticaApplication extends Application {
return new File(getExternalFilesDir(null), "HabiticaDatabase/" + name);
}
public static void logout(Context context){
public static void logout(Context context) {
Instance.deleteDatabase(HabitDatabase.NAME);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = preferences.edit();
@ -86,7 +90,7 @@ public class HabiticaApplication extends Application {
context.startActivity(new Intent(context, LoginActivity.class));
}
public static void checkUserAuthentication(Context context, HostConfig hostConfig){
public static void checkUserAuthentication(Context context, HostConfig hostConfig) {
if (hostConfig == null || hostConfig.getApi() == null || hostConfig.getApi().equals("") || hostConfig.getUser() == null || hostConfig.getUser().equals("")) {
context.startActivity(new Intent(context, LoginActivity.class));
}
@ -94,65 +98,43 @@ public class HabiticaApplication extends Application {
// endregion
// region IAP - Specific
private void createBillingAndCheckout() {
billing = new Billing(this, new Billing.DefaultConfiguration() {
@NonNull
@Override
public String getPublicKey() {
return "DONT-NEED-IT";
}
@Nullable
@Override
public Cache getCache() {
return Billing.newCache();
}
@Override
public PurchaseVerifier getPurchaseVerifier() {
return new HabiticaPurchaseVerifier(HabiticaApplication.this);
}
});
checkout = Checkout.forApplication(billing, Products.create().add(ProductTypes.IN_APP, Arrays.asList(Purchase20Gems)));
}
/**
* For better performance billing class should be used as singleton
*/
@NonNull
private final Billing billing = new Billing(this, new Billing.Configuration() {
@NonNull
@Override
public String getPublicKey() {
return "sdfsdfdfsd";
}
@Nullable
@Override
public Cache getCache() {
return Billing.newCache();
}
@Override
public PurchaseVerifier getPurchaseVerifier() {
return new PurchaseVerifier() {
@Override
public void verify(List<Purchase> purchases, RequestListener<List<Purchase>> requestListener) {
final List<Purchase> verifiedPurchases = new ArrayList<Purchase>(purchases.size());
/* for (Purchase purchase : purchases) {
if (Security.verifyPurchase(publicKey, purchase.data, purchase.signature)) {
verifiedPurchases.add(purchase);
} else {
if (isEmpty(purchase.signature)) {
Billing.error("Cannot verify purchase: " + purchase + ". Signature is empty");
} else {
Billing.error("Cannot verify purchase: " + purchase + ". Wrong signature");
}
}
}*/
//requestListener.onSuccess(verifiedPurchases);
}
};
}
@Override
public Inventory getFallbackInventory(Checkout checkout, Executor executor) {
return null;
}
@Override
public boolean isAutoConnect() {
return false;
}
});
public static String Purchase20Gems = "com.habitrpg.android.habitica.iap.20.gems";
private Billing billing;
/**
* Application wide {@link org.solovyev.android.checkout.Checkout} instance (can be used anywhere in the app).
* This instance contains all available products in the app.
*/
@NonNull
private final Checkout checkout = Checkout.forApplication(billing, Products.create().add(ProductTypes.IN_APP, Arrays.asList(Purchase20Gems)));
private Checkout checkout;
@NonNull
@ -160,4 +142,5 @@ public class HabiticaApplication extends Application {
return checkout;
}
// endregion
}

View file

@ -0,0 +1,68 @@
package com.habitrpg.android.habitica;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import com.magicmicky.habitrpgwrapper.lib.models.PurchaseValidationRequest;
import com.magicmicky.habitrpgwrapper.lib.models.PurchaseValidationResult;
import com.magicmicky.habitrpgwrapper.lib.models.Transaction;
import org.solovyev.android.checkout.BasePurchaseVerifier;
import org.solovyev.android.checkout.Purchase;
import org.solovyev.android.checkout.RequestListener;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Created by Negue on 26.11.2015.
*/
public class HabiticaPurchaseVerifier extends BasePurchaseVerifier {
private Set<String> purchasedOrderList = new HashSet<>();
static String PURCHASED_PRODUCTS_KEY = "PURCHASED_PRODUCTS";
private SharedPreferences preferences;
public HabiticaPurchaseVerifier(Context context) {
preferences = PreferenceManager.getDefaultSharedPreferences(context);
preferences.getStringSet(PURCHASED_PRODUCTS_KEY, purchasedOrderList);
}
@Override
protected void doVerify(final List<Purchase> purchases, final RequestListener<List<Purchase>> requestListener) {
final List<Purchase> verifiedPurchases = new ArrayList<>(purchases.size());
for (final Purchase purchase : purchases) {
if (purchasedOrderList.contains(purchase.orderId)) {
verifiedPurchases.add(purchase);
requestListener.onSuccess(verifiedPurchases);
} else {
PurchaseValidationRequest validationRequest = new PurchaseValidationRequest();
validationRequest.transaction = new Transaction();
validationRequest.transaction.receipt = purchase.data;
validationRequest.transaction.signature = purchase.signature;
PurchaseValidationResult purchaseValidationResult = HabiticaApplication.ApiHelper.validatePurchase(validationRequest);
if (purchaseValidationResult.ok) {
purchasedOrderList.add(purchase.orderId);
verifiedPurchases.add(purchase);
requestListener.onSuccess(verifiedPurchases);
} else {
requestListener.onError(purchases.indexOf(purchase), new Exception(purchaseValidationResult.data.toString()));
}
}
}
SharedPreferences.Editor edit = preferences.edit();
edit.putStringSet(PURCHASED_PRODUCTS_KEY, purchasedOrderList);
edit.apply();
}
}

View file

@ -58,6 +58,9 @@ public class LoginActivity extends AppCompatActivity
public Boolean isRegistering;
private Menu menu;
private String apiAddress = getString(R.string.SP_address_default);
//private String apiAddress = "http://192.168.2.155:8080/"; // local testing
private CallbackManager callbackManager;
protected void onCreate(Bundle savedInstanceState) {
@ -107,7 +110,7 @@ public class LoginActivity extends AppCompatActivity
HostConfig hc= PrefsActivity.fromContext(this);
if(hc ==null) {
hc = new HostConfig(getString(R.string.SP_address_default), "80", "", "");
hc = new HostConfig(apiAddress, "80", "", "");
}
mApiHelper = new APIHelper(this,hc);

View file

@ -1,5 +1,6 @@
package com.habitrpg.android.habitica;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.os.Bundle;
@ -18,6 +19,7 @@ import android.widget.TextView;
import com.crashlytics.android.Crashlytics;
import com.crashlytics.android.core.CrashlyticsCore;
import com.habitrpg.android.habitica.callbacks.HabitRPGUserCallback;
import com.habitrpg.android.habitica.events.commands.OpenGemPurchaseFragmentCommand;
import com.habitrpg.android.habitica.prefs.PrefsActivity;
import com.habitrpg.android.habitica.ui.AvatarWithBarsViewModel;
import com.habitrpg.android.habitica.ui.MainDrawerBuilder;
@ -34,8 +36,19 @@ import com.raizlabs.android.dbflow.runtime.transaction.TransactionListener;
import com.raizlabs.android.dbflow.sql.builder.Condition;
import com.raizlabs.android.dbflow.sql.language.Select;
import org.solovyev.android.checkout.ActivityCheckout;
import org.solovyev.android.checkout.Checkout;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import butterknife.ButterKnife;
import butterknife.InjectView;
import de.greenrobot.event.EventBus;
import io.fabric.sdk.android.Fabric;
public class MainActivity extends InstabugAppCompatActivity implements HabitRPGUserCallback.OnUserReceived {
@ -68,6 +81,9 @@ public class MainActivity extends InstabugAppCompatActivity implements HabitRPGU
APIHelper mAPIHelper;
// Checkout needs to be in the Activity..
public static ActivityCheckout checkout = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -84,7 +100,7 @@ public class MainActivity extends InstabugAppCompatActivity implements HabitRPGU
this.hostConfig = PrefsActivity.fromContext(this);
HabiticaApplication.checkUserAuthentication(this, hostConfig);
this.mAPIHelper = new APIHelper(this, hostConfig);
HabiticaApplication.ApiHelper = this.mAPIHelper = new APIHelper(this, hostConfig);
new Select().from(HabitRPGUser.class).where(Condition.column("id").eq(hostConfig.getUser())).async().querySingle(userTransactionListener);
@ -110,8 +126,14 @@ public class MainActivity extends InstabugAppCompatActivity implements HabitRPGU
.build();
drawer.setSelectionAtPosition(1);
// Create Checkout
mAPIHelper.retrieveUser(new HabitRPGUserCallback(this));
checkout = Checkout.forActivity(this, HabiticaApplication.Instance.getCheckout());
checkout.start();
EventBus.getDefault().register(this);
}
@ -132,6 +154,10 @@ public class MainActivity extends InstabugAppCompatActivity implements HabitRPGU
}
}
public void onEvent(OpenGemPurchaseFragmentCommand cmd){
drawer.setSelection(MainDrawerBuilder.SIDEBAR_PURCHASE);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
return true;
@ -172,6 +198,14 @@ public class MainActivity extends InstabugAppCompatActivity implements HabitRPGU
private void setUserData() {
if (user != null) {
Calendar mCalendar = new GregorianCalendar();
TimeZone mTimeZone = mCalendar.getTimeZone();
long offset = -TimeUnit.MINUTES.convert(mTimeZone.getRawOffset(), TimeUnit.MILLISECONDS);
if (offset != user.getPreferences().getTimezoneOffset()) {
Map<String, String> updateData = new HashMap<String, String>();
updateData.put("preferences.timezoneOffset", String.valueOf(offset));
mAPIHelper.apiService.updateUser(updateData, new HabitRPGUserCallback(this));
}
runOnUiThread(new Runnable() {
@Override
public void run() {
@ -241,6 +275,21 @@ public class MainActivity extends InstabugAppCompatActivity implements HabitRPGU
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
checkout.onActivityResult(requestCode, resultCode, data);
}
@Override
public void onDestroy() {
if(checkout != null)
checkout.stop();
super.onDestroy();
}
public void showSnackbar(String content) {
showSnackbar(content, SnackbarDisplayType.NORMAL);
}

View file

@ -3,6 +3,11 @@ package com.habitrpg.android.habitica.callbacks;
import com.crashlytics.android.Crashlytics;
import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import retrofit.Callback;
import retrofit.RetrofitError;
import retrofit.client.Response;
@ -22,7 +27,6 @@ public class HabitRPGUserCallback implements Callback<HabitRPGUser> {
public void success(HabitRPGUser habitRPGUser, Response response) {
habitRPGUser.async().save();
mCallback.onUserReceived(habitRPGUser);
}

View file

@ -0,0 +1,12 @@
package com.habitrpg.android.habitica.events;
/**
* Created by Negue on 29.11.2015.
*/
public class BoughtGemsEvent {
public int NewGemsToAdd;
public BoughtGemsEvent(int newGemsToAdd) {
NewGemsToAdd = newGemsToAdd;
}
}

View file

@ -0,0 +1,7 @@
package com.habitrpg.android.habitica.events.commands;
/**
* Created by Negue on 29.11.2015.
*/
public class OpenGemPurchaseFragmentCommand {
}

View file

@ -12,15 +12,19 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.databinding .ValueBarBinding;
import com.habitrpg.android.habitica.databinding.ValueBarBinding;
import com.habitrpg.android.habitica.events.BoughtGemsEvent;
import com.habitrpg.android.habitica.events.commands.OpenGemPurchaseFragmentCommand;
import com.habitrpg.android.habitica.userpicture.UserPicture;
import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser;
import com.magicmicky.habitrpgwrapper.lib.models.Stats;
import de.greenrobot.event.EventBus;
/**
* Created by Negue on 14.06.2015.
*/
public class AvatarWithBarsViewModel {
public class AvatarWithBarsViewModel implements View.OnClickListener {
private ValueBarBinding hpBar;
private ValueBarBinding xpBar;
private ValueBarBinding mpBar;
@ -32,6 +36,7 @@ public class AvatarWithBarsViewModel {
private Context context;
private TextView lvlText, goldText, silverText, gemsText;
private HabitRPGUser userObject;
public AvatarWithBarsViewModel(Context context, View v) {
this.context = context;
@ -58,10 +63,17 @@ public class AvatarWithBarsViewModel {
setValueBar(hpBar, 50, 50, context.getString(R.string.HP_default), R.color.hpColor, R.drawable.ic_header_heart);
setValueBar(xpBar, 1, 1, context.getString(R.string.XP_default), R.color.xpColor, R.drawable.ic_header_exp);
setValueBar(mpBar, 100, 100, context.getString(R.string.MP_default), R.color.mpColor, R.drawable.ic_header_magic);
EventBus.getDefault().register(this);
gemsText.setClickable(true);
gemsText.setOnClickListener(this);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public void updateData(HabitRPGUser user) {
userObject = user;
Stats stats = user.getStats();
char classShort;
String userClass = "";
@ -138,4 +150,17 @@ public class AvatarWithBarsViewModel {
valueBar.setBarForegroundColor(color);
valueBar.icHeader.setImageResource(icon);
}
public void onEvent(BoughtGemsEvent gemsEvent){
Double gems = new Double(userObject.getBalance() * 4);
gems += gemsEvent.NewGemsToAdd;
gemsText.setText(gems.intValue() + "");
}
@Override
public void onClick(View view) {
// Gems Clicked
EventBus.getDefault().post(new OpenGemPurchaseFragmentCommand());
}
}

View file

@ -33,13 +33,13 @@ import com.mikepenz.materialdrawer.model.interfaces.IProfile;
public class MainDrawerBuilder {
// Change the identificationIDs to the position IDs so that its easier to set the selected entry
static final int SIDEBAR_TASKS = 0;
static final int SIDEBAR_SKILLS = 1;
static final int SIDEBAR_TAVERN = 3;
static final int SIDEBAR_PARTY = 4;
static final int SIDEBAR_PURCHASE = 5;
static final int SIDEBAR_SETTINGS = 7;
static final int SIDEBAR_ABOUT = 8;
public static final int SIDEBAR_TASKS = 0;
public static final int SIDEBAR_SKILLS = 1;
public static final int SIDEBAR_TAVERN = 3;
public static final int SIDEBAR_PARTY = 4;
public static final int SIDEBAR_PURCHASE = 5;
public static final int SIDEBAR_SETTINGS = 7;
public static final int SIDEBAR_ABOUT = 8;

View file

@ -588,6 +588,7 @@ public class HabitItemRecyclerViewAdapter<THabitItem extends Task>
// endregion
public void loadContent(HabitRPGUser user) {
Log.d("setting content", this.taskType);
this.observableContent = new ObservableArrayList<>();
if (this.taskType.equals(Task.TYPE_HABIT)) {
this.observableContent.addAll(user.getHabits());
@ -612,6 +613,7 @@ public class HabitItemRecyclerViewAdapter<THabitItem extends Task>
public void loadContent(boolean forced) {
if (this.observableContent == null || forced) {
Log.d("Loading content", this.taskType);
this.observableContent = new ObservableArrayList<>();
new Select().from(Task.class)
.where(Condition.column("type").eq(this.taskType))

View file

@ -8,30 +8,37 @@ import android.view.ViewGroup;
import android.widget.Button;
import com.habitrpg.android.habitica.HabiticaApplication;
import com.habitrpg.android.habitica.MainActivity;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.events.BoughtGemsEvent;
import com.habitrpg.android.habitica.ui.helpers.ViewHelper;
import org.solovyev.android.checkout.ActivityCheckout;
import org.solovyev.android.checkout.BillingRequests;
import org.solovyev.android.checkout.Checkout;
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 butterknife.ButterKnife;
import butterknife.InjectView;
import butterknife.OnClick;
import de.greenrobot.event.EventBus;
import io.fabric.sdk.android.Fabric;
/**
* Created by Negue on 24.11.2015.
*/
public class GemsPurchaseFragment extends BaseFragment {
static final int GEMS_TO_ADD = 21;
private BillingRequests billingRequests;
@InjectView(R.id.btn_purchase_gems)
Button btnPurchaseGems;
private ActivityCheckout checkout = null;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@ -42,29 +49,44 @@ public class GemsPurchaseFragment extends BaseFragment {
ButterKnife.inject(this, v);
checkout = Checkout.forActivity(this.activity, HabiticaApplication.Instance.getCheckout());
btnPurchaseGems.setEnabled(false);
ViewHelper.SetBackgroundTint(btnPurchaseGems, container.getResources().getColor(R.color.brand));
checkout.start();
// you only need this if this activity starts purchase process
checkout.createPurchaseFlow(new RequestListener<Purchase>() {
MainActivity.checkout.destroyPurchaseFlow();
MainActivity.checkout.createPurchaseFlow(new RequestListener<Purchase>() {
@Override
public void onSuccess(Purchase purchase) {
if(purchase.sku.equals(HabiticaApplication.Purchase20Gems)){
billingRequests.consume(purchase.token, new RequestListener<Object>() {
@Override
public void onSuccess(Object o) {
EventBus.getDefault().post(new BoughtGemsEvent(GEMS_TO_ADD));
}
@Override
public void onError(int i, Exception e) {
Fabric.getLogger().e("Purchase", "Consume", e);
}
});
}
}
@Override
public void onError(int i, Exception e) {
Fabric.getLogger().e("Purchase", "Error", e);
}
});
checkout.whenReady(new Checkout.Listener() {
MainActivity.checkout.whenReady(new Checkout.Listener() {
@Override
public void onReady(BillingRequests billingRequests) {
billingRequests.purchase(ProductTypes.IN_APP, HabiticaApplication.Purchase20Gems, null, checkout.getPurchaseFlow());
public void onReady(final BillingRequests billingRequests) {
GemsPurchaseFragment.this.billingRequests = billingRequests;
btnPurchaseGems.setEnabled(true);
checkIfPendingPurchases();
}
@Override
@ -76,16 +98,55 @@ public class GemsPurchaseFragment extends BaseFragment {
return v;
}
private void checkIfPendingPurchases(){
billingRequests.getAllPurchases(ProductTypes.IN_APP, new RequestListener<Purchases>() {
@Override
public void onSuccess(Purchases purchases) {
for(Purchase purchase : purchases.list){
if(purchase.sku.equals(HabiticaApplication.Purchase20Gems)) {
billingRequests.consume(purchase.token, new RequestListener<Object>() {
@Override
public void onSuccess(Object o) {
EventBus.getDefault().post(new BoughtGemsEvent(GEMS_TO_ADD));
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
checkout.onActivityResult(requestCode, resultCode, data);
@Override
public void onError(int i, Exception e) {
Fabric.getLogger().e("Purchase", "Consume", e);
}
});
}
}
}
@Override
public void onError(int i, Exception e) {
Fabric.getLogger().e("Purchase","getAllPurchases", e);
}
});
}
@Override
public void onDestroy() {
checkout.stop();
super.onDestroy();
@OnClick(R.id.btn_purchase_gems)
public void doPurchaseGems(Button button) {
// check if the user already bought and if it hasn't validated yet
billingRequests.isPurchased(ProductTypes.IN_APP, HabiticaApplication.Purchase20Gems, new RequestListener<Boolean>() {
@Override
public void onSuccess(Boolean aBoolean) {
if (!aBoolean) {
// no current product exist
billingRequests.purchase(ProductTypes.IN_APP, HabiticaApplication.Purchase20Gems, null, MainActivity.checkout.getPurchaseFlow());
}
else{
checkIfPendingPurchases();
}
}
@Override
public void onError(int i, Exception e) {
Fabric.getLogger().e("Purchase", "Error", e);
}
});
}
}

View file

@ -15,7 +15,9 @@ import com.magicmicky.habitrpgwrapper.lib.models.tasks.ItemData;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.Task;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.List;
import java.util.Map;
import retrofit.Callback;
import retrofit.http.Body;
@ -41,6 +43,9 @@ public interface ApiService {
@GET("/user/")
void getUser(Callback<HabitRPGUser> habitRPGUserCallback);
@PUT("/user/")
void updateUser(@Body Map<String, String> updateDictionary, Callback<HabitRPGUser> habitRPGUserCallback);
@GET("/user/inventory/buy")
void getInventoryBuyableGear(Callback<List<ItemData>> buyableGearCallback);

View file

@ -0,0 +1,17 @@
package com.magicmicky.habitrpgwrapper.lib.api;
import com.magicmicky.habitrpgwrapper.lib.models.PurchaseValidationRequest;
import com.magicmicky.habitrpgwrapper.lib.models.PurchaseValidationResult;
import retrofit.Callback;
import retrofit.http.Body;
import retrofit.http.POST;
import retrofit.http.Query;
/**
* Created by Negue on 27.11.2015.
*/
public interface InAppPurchasesApiService {
@POST("/iap/android/verify")
PurchaseValidationResult validatePurchase(@Query("_id") String id, @Query("apiToken") String apiToken, @Body PurchaseValidationRequest request);
}

View file

@ -4,19 +4,29 @@ package com.magicmicky.habitrpgwrapper.lib.api;
* Created by MagicMicky on 15/06/2014.
*/
public class Server {
public final static Server NORMAL = new Server("https://habitrpg.com");
public final static Server BETA = new Server("https://beta.habitrpg.com");
private String addr;
public Server(String addr) {
if(addr.endsWith("/api/v2") || addr.endsWith("/api/v2/"))
this.addr=addr;
else if(addr.endsWith("/"))
this.addr=addr + "api/v2";
else
this.addr = addr + "/api/v2";
public Server(String addr)
{
this(addr, true);
}
public Server(String addr, boolean attachPrefix)
{
if(attachPrefix){
if(addr.endsWith("/api/v2") || addr.endsWith("/api/v2/"))
this.addr=addr;
else if(addr.endsWith("/"))
this.addr=addr + "api/v2";
else
this.addr = addr + "/api/v2";
}
else
{
this.addr = addr;
}
}
public Server(Server s) {
this.addr = s.toString();
}

View file

@ -0,0 +1,9 @@
package com.magicmicky.habitrpgwrapper.lib.models;
/**
* Created by Negue on 26.11.2015.
*/
public class PurchaseValidationRequest {
public Transaction transaction;
}

View file

@ -0,0 +1,6 @@
package com.magicmicky.habitrpgwrapper.lib.models;
public class PurchaseValidationResult{
public boolean ok;
public Object data;
}

View file

@ -0,0 +1,8 @@
package com.magicmicky.habitrpgwrapper.lib.models;
public class Transaction
{
public String receipt;
public String signature;
}