Allow customizations and sets to be purchased

This commit is contained in:
Phillip Thelen 2016-01-16 18:05:14 +01:00
parent d9d795fb3b
commit 5c527b8ad6
22 changed files with 564 additions and 102 deletions

View file

@ -3,22 +3,35 @@
android:id="@+id/card_view"
android:layout_height="wrap_content"
android:layout_width="match_parent">
<LinearLayout
<RelativeLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="90dp"
android:minHeight="60dp"
android:padding="5dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:orientation="vertical"
android:clickable="true"
android:background="@drawable/btn_habit_background">
<View
android:id="@+id/purchaseOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:background="@android:color/black" />
<ImageView
android:id="@+id/imageView"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_gravity="center_horizontal"
android:scaleType="fitEnd"/>
</LinearLayout>
android:scaleType="fitEnd"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
</android.support.v7.widget.CardView>

View file

@ -4,8 +4,15 @@
android:layout_height="match_parent">
<TextView
android:id="@+id/label"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_marginTop="12dp"/>
android:layout_weight="1"
android:layout_gravity="bottom"
android:textAppearance="?android:attr/textAppearanceMedium"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/purchaseSetButton"
android:layout_marginTop="12dp" />
</LinearLayout>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/imageView"
android:layout_gravity="center_horizontal" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:id="@+id/priceLabel"
tools:text="2"
android:layout_gravity="center_horizontal" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/imageButton"
android:src="@drawable/ic_header_gem"
android:layout_gravity="center"/>
</LinearLayout>
</LinearLayout>

View file

@ -155,4 +155,6 @@
<string name="avatar_size">Size</string>
<string name="avatar_size_slim">Slim</string>
<string name="avatar_size_broad">Broad</string>
<string name="purchase_customization">Purchase Customization</string>
<string name="purchase_set_button" formatted="false">Unlock set for %d gems</string>
</resources>

View file

@ -1,7 +1,5 @@
package com.habitrpg.android.habitica;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.support.v7.app.AlertDialog;
@ -9,11 +7,6 @@ import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
@ -26,9 +19,10 @@ 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.HabitRPGUser;
import com.magicmicky.habitrpgwrapper.lib.models.Customization;
import com.magicmicky.habitrpgwrapper.lib.models.PurchaseValidationRequest;
import com.magicmicky.habitrpgwrapper.lib.models.PurchaseValidationResult;
import com.magicmicky.habitrpgwrapper.lib.models.Purchases;
import com.magicmicky.habitrpgwrapper.lib.models.SkillList;
import com.magicmicky.habitrpgwrapper.lib.models.TaskDirection;
import com.magicmicky.habitrpgwrapper.lib.models.UserAuth;
@ -39,6 +33,8 @@ import com.magicmicky.habitrpgwrapper.lib.models.tasks.ChecklistItem;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.Task;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.TaskTag;
import com.magicmicky.habitrpgwrapper.lib.utils.ChecklistItemSerializer;
import com.magicmicky.habitrpgwrapper.lib.utils.CustomizationDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.PurchasedDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.SkillDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.TaskListDeserializer;
import com.raizlabs.android.dbflow.structure.ModelAdapter;
@ -53,7 +49,6 @@ import retrofit.Profiler;
import retrofit.RequestInterceptor;
import retrofit.RestAdapter;
import retrofit.RetrofitError;
import retrofit.converter.ConversionException;
import retrofit.converter.GsonConverter;
@ -84,6 +79,7 @@ public class APIHelper implements ErrorHandler, Profiler {
Type taskClassListType = new TypeToken<List<Task>>() {}.getType();
Type customizationListType = new TypeToken<List<Customization>>() {}.getType();
//Exclusion stratety needed for DBFlow https://github.com/Raizlabs/DBFlow/issues/121
Gson gson = new GsonBuilder()
@ -105,6 +101,8 @@ public class APIHelper implements ErrorHandler, Profiler {
.registerTypeAdapter(SkillList.class, new SkillDeserializer())
.registerTypeAdapter(ChecklistItem.class, new ChecklistItemSerializer())
.registerTypeAdapter(taskClassListType, new TaskListDeserializer())
.registerTypeAdapter(Purchases.class, new PurchasedDeserializer())
.registerTypeAdapter(customizationListType, new CustomizationDeserializer())
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.create();

View file

@ -0,0 +1,40 @@
package com.habitrpg.android.habitica.callbacks;
import com.crashlytics.android.Crashlytics;
import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser;
import com.magicmicky.habitrpgwrapper.lib.models.responses.UnlockResponse;
import retrofit.Callback;
import retrofit.RetrofitError;
import retrofit.client.Response;
/**
* Created by magicmicky on 18/02/15.
*/
public class UnlockCallback implements Callback<UnlockResponse> {
private final HabitRPGUserCallback.OnUserReceived mCallback;
private HabitRPGUser user;
public UnlockCallback(HabitRPGUserCallback.OnUserReceived callback, HabitRPGUser user) {
this.mCallback = callback;
this.user = user;
}
@Override
public void success(UnlockResponse unlockResponse, Response response) {
this.user.setPurchased(unlockResponse.purchased);
this.user.setItems(unlockResponse.items);
this.user.setPreferences(unlockResponse.preferences);
this.user.async().save();
mCallback.onUserReceived(this.user);
}
@Override
public void failure(RetrofitError error) {
mCallback.onUserFail();
}
}

View file

@ -0,0 +1,8 @@
package com.habitrpg.android.habitica.events.commands;
/**
* Created by viirus on 15/01/16.
*/
public class OpenMenuItemCommand {
public int identifier;
}

View file

@ -0,0 +1,11 @@
package com.habitrpg.android.habitica.events.commands;
/**
* Created by viirus on 15/01/16.
*/
public class UnlockPathCommand {
public String path;
public double balanceDiff;
}

View file

@ -31,12 +31,15 @@ import com.habitrpg.android.habitica.NotificationPublisher;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.callbacks.HabitRPGUserCallback;
import com.habitrpg.android.habitica.callbacks.TaskScoringCallback;
import com.habitrpg.android.habitica.callbacks.UnlockCallback;
import com.habitrpg.android.habitica.databinding.ValueBarBinding;
import com.habitrpg.android.habitica.events.TaskRemovedEvent;
import com.habitrpg.android.habitica.events.ToggledInnStateEvent;
import com.habitrpg.android.habitica.events.commands.BuyRewardCommand;
import com.habitrpg.android.habitica.events.commands.DeleteTaskCommand;
import com.habitrpg.android.habitica.events.commands.OpenGemPurchaseFragmentCommand;
import com.habitrpg.android.habitica.events.commands.OpenMenuItemCommand;
import com.habitrpg.android.habitica.events.commands.UnlockPathCommand;
import com.habitrpg.android.habitica.events.commands.UpdateUserCommand;
import com.habitrpg.android.habitica.ui.AvatarWithBarsViewModel;
import com.habitrpg.android.habitica.ui.MainDrawerBuilder;
@ -466,6 +469,15 @@ public class MainActivity extends BaseActivity implements HabitRPGUserCallback.O
mAPIHelper.apiService.updateUser(event.updateData, new HabitRPGUserCallback(this));
}
public void onEvent(UnlockPathCommand event) {
this.user.setBalance(this.user.getBalance() - event.balanceDiff);
mAPIHelper.apiService.unlockPath(event.path, new UnlockCallback(this, this.user));
}
public void onEvent(OpenMenuItemCommand event) {
drawer.setSelection(event.identifier);
}
public void onEvent(final BuyRewardCommand event) {
final String rewardKey = event.Reward.getId();

View file

@ -1,6 +1,6 @@
package com.habitrpg.android.habitica.ui.adapter;
import android.content.res.Resources;
import android.content.Context;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
@ -9,13 +9,19 @@ import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.GravityEnum;
import com.afollestad.materialdialogs.MaterialDialog;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.events.commands.OpenMenuItemCommand;
import com.habitrpg.android.habitica.events.commands.UnlockPathCommand;
import com.habitrpg.android.habitica.events.commands.UpdateUserCommand;
import com.habitrpg.android.habitica.ui.MainDrawerBuilder;
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils;
import com.magicmicky.habitrpgwrapper.lib.models.Customization;
import com.raizlabs.android.dbflow.sql.language.Update;
import java.util.ArrayList;
import java.util.HashMap;
@ -35,14 +41,15 @@ public class CustomizationRecyclerViewAdapter extends RecyclerView.Adapter<Recyc
private String activeCustomization;
public String userSize;
public String hairColor;
public double gemBalance;
public void setCustomizationList(List<Customization> newCustomizationList) {
this.customizationList = new ArrayList<Object>();
String lastSetName = null;
for (Customization customization : newCustomizationList) {
if (customization.getSet() != null && !customization.getSet().equals(lastSetName)) {
customizationList.add(customization.getSet());
lastSetName = customization.getSet();
if (customization.getCustomizationSet() != null && !customization.getCustomizationSet().equals(lastSetName)) {
customizationList.add(customization.getCustomizationSet());
lastSetName = customization.getCustomizationSet();
}
customizationList.add(customization);
}
@ -99,21 +106,28 @@ public class CustomizationRecyclerViewAdapter extends RecyclerView.Adapter<Recyc
class CustomizationViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
@Bind(R.id.card_view)
CardView cardView;
CardView cardView;
@Bind(R.id.linearLayout)
LinearLayout linearLayout;
RelativeLayout linearLayout;
@Bind(R.id.imageView)
ImageView imageView;
@Bind(R.id.purchaseOverlay)
View purchaseOverlay;
Customization customization;
Context context;
public CustomizationViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
linearLayout.setOnClickListener(this);
context = itemView.getContext();
}
public void bind(Customization customization) {
@ -124,25 +138,69 @@ public class CustomizationRecyclerViewAdapter extends RecyclerView.Adapter<Recyc
cardView.setCardBackgroundColor(android.R.color.white);
if (customization.isUsable()) {
imageView.setAlpha(1.0f);
purchaseOverlay.setAlpha(0.0f);
if (customization.getIdentifier().equals(activeCustomization)) {
cardView.setCardBackgroundColor(R.color.brand_500);
}
} else {
imageView.setAlpha(0.3f);
purchaseOverlay.setAlpha(0.8f);
}
}
@Override
public void onClick(View v) {
if (!customization.isUsable() && customization.getIdentifier().equals(activeCustomization)) {
if (!customization.isUsable()) {
LinearLayout dialogContent = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.dialog_purchase_customization, null);
ImageView imageView = (ImageView) dialogContent.findViewById(R.id.imageView);
DataBindingUtils.loadImage(imageView, customization.getImageName(userSize, hairColor));
TextView priceLabel = (TextView) dialogContent.findViewById(R.id.priceLabel);
priceLabel.setText(String.valueOf(customization.getPrice()));
MaterialDialog dialog = new MaterialDialog.Builder(context)
.onPositive(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) {
if (customization.getPrice() > gemBalance) {
OpenMenuItemCommand event = new OpenMenuItemCommand();
event.identifier = MainDrawerBuilder.SIDEBAR_PURCHASE;
EventBus.getDefault().post(event);
return;
}
UnlockPathCommand event = new UnlockPathCommand();
event.path = customization.getPath();
event.balanceDiff = customization.getPrice() / 4;
EventBus.getDefault().post(event);
}
})
.contentGravity(GravityEnum.CENTER)
.positiveColor(context.getResources().getColor(R.color.brand_200))
.positiveText(R.string.reward_dialog_buy)
.title(context.getString(R.string.purchase_customization))
.customView(dialogContent, true)
.negativeText(R.string.reward_dialog_dismiss)
.onNegative(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) {
materialDialog.dismiss();
}
}).build();
dialog.show();
return;
}
if (customization.getIdentifier().equals(activeCustomization)) {
return;
}
UpdateUserCommand command = new UpdateUserCommand();
Map<String, String> updateData = new HashMap<String, String>();
String updatePath = "preferences." + customization.getType();
if (customization.getGroup() != null) {
updatePath = updatePath + "." + customization.getGroup();
if (customization.getCategory() != null) {
updatePath = updatePath + "." + customization.getCategory();
}
updateData.put(updatePath, customization.getIdentifier());
command.updateData = updateData;
@ -151,22 +209,77 @@ public class CustomizationRecyclerViewAdapter extends RecyclerView.Adapter<Recyc
}
}
class SectionViewHolder extends RecyclerView.ViewHolder {
class SectionViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private String sectionName;
@Bind(R.id.label)
TextView label;
@Bind(R.id.purchaseSetButton)
Button purchaseSetButton;
Context context;
public SectionViewHolder(View itemView) {
super(itemView);
context = itemView.getContext();
ButterKnife.bind(this, itemView);
purchaseSetButton.setOnClickListener(this);
}
public void bind(String sectionName) {
this.sectionName = sectionName;
String uppercasedSectionName = sectionName.substring(0, 1).toUpperCase() + sectionName.substring(1);
this.label.setText(uppercasedSectionName);
this.purchaseSetButton.setText(context.getString(R.string.purchase_set_button, 5));
}
@Override
public void onClick(View v) {
LinearLayout dialogContent = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.dialog_purchase_customization, null);
TextView priceLabel = (TextView) dialogContent.findViewById(R.id.priceLabel);
priceLabel.setText("5");
MaterialDialog dialog = new MaterialDialog.Builder(context)
.onPositive(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) {
if (5 > gemBalance) {
OpenMenuItemCommand event = new OpenMenuItemCommand();
event.identifier = MainDrawerBuilder.SIDEBAR_PURCHASE;
EventBus.getDefault().post(event);
return;
}
UnlockPathCommand event = new UnlockPathCommand();
String path = "";
for (Object obj : customizationList) {
if (obj.getClass().equals(Customization.class)) {
Customization customization = (Customization) obj;
if (customization.getCustomizationSet() != null && customization.getCustomizationSet().equals(sectionName)) {
path = path + "," + customization.getPath();
}
}
}
event.path = path;
event.balanceDiff = 1.25;
EventBus.getDefault().post(event);
}
})
.contentGravity(GravityEnum.CENTER)
.positiveColor(context.getResources().getColor(R.color.brand_200))
.positiveText(R.string.reward_dialog_buy)
.title(context.getString(R.string.purchase_customization))
.customView(dialogContent, true)
.negativeText(R.string.reward_dialog_dismiss)
.onNegative(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) {
materialDialog.dismiss();
}
}).build();
dialog.show();
}
}
}

View file

@ -1,6 +1,5 @@
package com.habitrpg.android.habitica.ui.fragments;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
@ -14,11 +13,11 @@ import com.habitrpg.android.habitica.ui.helpers.MarginDecoration;
import com.magicmicky.habitrpgwrapper.lib.models.Customization;
import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser;
import com.magicmicky.habitrpgwrapper.lib.models.Preferences;
import com.magicmicky.habitrpgwrapper.lib.models.Skill;
import com.raizlabs.android.dbflow.sql.builder.Condition;
import com.raizlabs.android.dbflow.sql.language.Select;
import com.raizlabs.android.dbflow.sql.language.Where;
import java.util.Date;
import java.util.List;
import butterknife.Bind;
@ -30,7 +29,7 @@ import butterknife.ButterKnife;
public class AvatarCustomizationFragment extends BaseFragment {
public String type;
public String group;
public String category;
public String activeCustomization;
@Bind(R.id.recyclerView)
@ -69,6 +68,7 @@ public class AvatarCustomizationFragment extends BaseFragment {
this.updateActiveCustomization();
this.adapter.userSize = this.user.getPreferences().getSize();
this.adapter.hairColor = this.user.getPreferences().getHair().getColor();
this.adapter.gemBalance = user.getBalance() * 4;
return v;
}
@ -81,14 +81,21 @@ public class AvatarCustomizationFragment extends BaseFragment {
Where<Customization> select = new Select()
.from(Customization.class)
.where(Condition.column("type").eq(this.type))
.and(Condition.CombinedCondition.begin(Condition.column("purchasable").eq(true))
.or(Condition.column("purchased").eq(true))
.and(Condition.CombinedCondition.begin(Condition.column("purchased").eq(true))
.or(Condition.column("price").eq(0))
.or(Condition.column("price").isNull()));
if (this.group != null) {
select = select.and(Condition.column("group").eq(this.group));
.or(Condition.column("price").isNull())
.or(Condition.CombinedCondition.begin(
Condition.CombinedCondition.begin(Condition.column("availableUntil").isNull())
.or(Condition.column("availableUntil").greaterThanOrEq(new Date().getTime())))
.and(Condition.CombinedCondition.begin(Condition.column("availableFrom").isNull())
.or(Condition.column("availableFrom").lessThanOrEq(new Date().getTime()))
)
)
);
if (this.category != null) {
select = select.and(Condition.column("category").eq(this.category));
}
select.orderBy(true, "set", "identifier");
select.orderBy(true, "customizationSet", "identifier");
List<Customization> customizations = select.queryList();
adapter.setCustomizationList(customizations);
@ -102,7 +109,9 @@ public class AvatarCustomizationFragment extends BaseFragment {
@Override
public void updateUserData(HabitRPGUser user) {
super.updateUserData(user);
this.adapter.gemBalance = user.getBalance() * 4;
this.updateActiveCustomization();
this.loadCustomizations();
}
private void updateActiveCustomization() {
@ -115,7 +124,7 @@ public class AvatarCustomizationFragment extends BaseFragment {
this.activeCustomization = prefs.getShirt();
break;
case "hair":
switch (this.group) {
switch (this.category) {
case "bangs":
this.activeCustomization = String.valueOf(prefs.getHair().getBangs());
break;

View file

@ -1,10 +1,7 @@
package com.habitrpg.android.habitica.ui.fragments;
import android.databinding.DataBindingUtil;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.widget.CardView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -14,8 +11,7 @@ import android.widget.Spinner;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.callbacks.HabitRPGUserCallback;
import com.habitrpg.android.habitica.databinding.FragmentAvatarOverviewBinding;
import com.habitrpg.android.habitica.databinding.FragmentPartyInfoBinding;
import com.magicmicky.habitrpgwrapper.lib.models.Customization;
import com.magicmicky.habitrpgwrapper.lib.models.ContentResult;
import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser;
import java.util.HashMap;
@ -126,10 +122,10 @@ public class AvatarOverviewFragment extends BaseFragment implements AdapterView.
return v;
}
private void displayCustomizationFragment(String type, String group) {
private void displayCustomizationFragment(String type, String category) {
AvatarCustomizationFragment fragment = new AvatarCustomizationFragment();
fragment.type = type;
fragment.group = group;
fragment.category = category;
activity.displayFragment(fragment);
}

View file

@ -11,6 +11,7 @@ import com.magicmicky.habitrpgwrapper.lib.models.TaskDirectionData;
import com.magicmicky.habitrpgwrapper.lib.models.UserAuth;
import com.magicmicky.habitrpgwrapper.lib.models.UserAuthResponse;
import com.magicmicky.habitrpgwrapper.lib.models.UserAuthSocial;
import com.magicmicky.habitrpgwrapper.lib.models.responses.UnlockResponse;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.ItemData;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.Task;
@ -51,15 +52,15 @@ public interface ApiService {
@POST("/user/inventory/buy/{key}")
void buyItem(@Path("key") String itemKey, Callback<Void> voidCallback);
@POST("/user/unlock")
void unlockPath(@Query("path") String path, Callback<UnlockResponse> unlockResponseCallback);
@GET("/user/tasks/{id}")
void getTask(@Path("id") String id, Callback<Task> habitItemCallback);
@POST("/user/tasks/{id}/{direction}")
void postTaskDirection(@Path("id") String id, @Path("direction") String direction, Callback<TaskDirectionData> taskDirectionCallback);
@POST("/user/tasks")
void createItem(@Body Task item, Callback<Task> habitItemCallback);

View file

@ -3,6 +3,7 @@ package com.magicmicky.habitrpgwrapper.lib.models;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.ItemData;
import java.util.HashMap;
import java.util.List;
/**
* Created by Negue on 15.07.2015.
@ -18,5 +19,7 @@ public class ContentResult {
public HashMap<String, QuestContent> quests;
public SkillList spells;
public List<Customization> appearances;
}

View file

@ -2,16 +2,12 @@ package com.magicmicky.habitrpgwrapper.lib.models;
import com.habitrpg.android.habitica.HabitDatabase;
import com.raizlabs.android.dbflow.annotation.Column;
import com.raizlabs.android.dbflow.annotation.ConflictAction;
import com.raizlabs.android.dbflow.annotation.PrimaryKey;
import com.raizlabs.android.dbflow.annotation.Table;
import com.raizlabs.android.dbflow.annotation.Unique;
import com.raizlabs.android.dbflow.annotation.UniqueGroup;
import com.raizlabs.android.dbflow.structure.BaseModel;
/**
* Created by viirus on 13/01/16.
*/
import java.util.Date;
@Table(databaseName = HabitDatabase.NAME)
public class Customization extends BaseModel {
@ -20,16 +16,19 @@ public class Customization extends BaseModel {
private String id;
@Column
private String identifier, group, type, notes, set, text;
private String identifier, category, type, notes, customizationSet, text;
@Column
private boolean purchasable, purchased;
private boolean purchased;
@Column
private Integer price;
private Integer price, setPrice;
@Column
private Date availableFrom, availableUntil;
public void updateID() {
this.id = this.identifier + "_" + this.type + "_" + this.group;
this.id = this.identifier + "_" + this.type + "_" + this.category;
}
public void setIdentifier(String identifier) {
@ -42,29 +41,49 @@ public class Customization extends BaseModel {
this.updateID();
}
public void setGroup(String group) {
this.group = group;
public void setCategory(String category) {
this.category = category;
this.updateID();
}
public void setId(String id) {this.id = id;}
public void setNotes(String notes) {this.notes = notes;}
public void setSet(String set) {this.set = set;}
public void setCustomizationSet(String customizationSet) {this.customizationSet = customizationSet;}
public void setText(String text) {this.text = text;}
public void setPurchasable(boolean purchasable) {this.purchasable = purchasable;}
public void setPurchased(boolean purchased) {this.purchased = purchased;}
public void setPrice(Integer price) {this.price = price;}
public void setSetPrice(Integer setPrice) {this.setPrice = setPrice; }
public void setAvailableUntil(Date availableUntil) {this.availableUntil = availableUntil; }
public void setAvailableFrom(Date availableFrom) {this.availableFrom = availableFrom; }
public String getId() { return this.id; }
public String getIdentifier() { return this.identifier; }
public String getGroup() { return this.group; }
public String getCategory() { return this.category; }
public String getType() { return this.type; }
public String getNotes() { return this.notes; }
public String getSet() { return this.set; }
public String getCustomizationSet() { return this.customizationSet; }
public String getText() { return this.text; }
public boolean getPurchasable() { return this.purchasable; }
@SuppressWarnings("RedundantIfStatement")
public boolean getPurchasable() {
Date today = new Date();
if (this.availableFrom != null && !this.availableFrom.before(today)) {
//Not released yet
return false;
}
if (this.availableUntil != null && !this.availableUntil.after(today)) {
//Discontinued
return false;
}
return true;
}
public boolean getPurchased() { return this.purchased; }
public Integer getPrice() { return this.price; }
public Integer getSetPrice() { return this.setPrice; }
public Date getAvailableFrom() { return this.availableFrom; }
public Date getAvailableUntil() { return this.availableUntil; }
public String getImageName(String userSize, String hairColor) {
@ -78,13 +97,13 @@ public class Customization extends BaseModel {
return "head_0";
}
switch (this.group) {
switch (this.category) {
case "color":
return "hair_bangs_1_" + this.identifier;
case "flower":
return "hair_flower_" + this.identifier;
default:
return "hair_" + this.group + "_" + this.identifier + "_" + hairColor;
return "hair_" + this.category + "_" + this.identifier + "_" + hairColor;
}
}
return "";
@ -94,4 +113,16 @@ public class Customization extends BaseModel {
return this.price == null || this.price == 0 || this.purchased;
}
public String getPath() {
String path = this.type;
if (this.category != null) {
path = path + "." + this.category;
}
path = path + "." + this.identifier;
return path;
}
}

View file

@ -73,6 +73,8 @@ public class HabitRPGUser extends BaseModel {
@SerializedName("auth")
private Authentication authentication;
private Purchases purchased;
public Preferences getPreferences() {
return preferences;
}
@ -157,6 +159,9 @@ public class HabitRPGUser extends BaseModel {
this.authentication = authentication;
}
public Purchases getPurchased() { return purchased; }
public void setPurchased(Purchases purchased) { this.purchased = purchased; }
@OneToMany(methods = {OneToMany.Method.SAVE, OneToMany.Method.DELETE}, variableName = "habits")
public List<Task> getHabits() {
if (habits == null) {

View file

@ -0,0 +1,11 @@
package com.magicmicky.habitrpgwrapper.lib.models;
import java.util.List;
/**
* Created by viirus on 14/01/16.
*/
public class Purchases {
public List<Customization> customizations;
}

View file

@ -0,0 +1,18 @@
package com.magicmicky.habitrpgwrapper.lib.models.responses;
import com.magicmicky.habitrpgwrapper.lib.models.Items;
import com.magicmicky.habitrpgwrapper.lib.models.Preferences;
import com.magicmicky.habitrpgwrapper.lib.models.Purchases;
/**
* Created by viirus on 16/01/16.
*/
public class UnlockResponse {
public Preferences preferences;
public Purchases purchased;
public Items items;
}

View file

@ -0,0 +1,86 @@
package com.magicmicky.habitrpgwrapper.lib.utils;
import android.annotation.SuppressLint;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.magicmicky.habitrpgwrapper.lib.models.Customization;
import com.magicmicky.habitrpgwrapper.lib.models.Purchases;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.ChecklistItem;
import com.raizlabs.android.dbflow.runtime.TransactionManager;
import com.raizlabs.android.dbflow.runtime.transaction.process.ProcessModelInfo;
import com.raizlabs.android.dbflow.runtime.transaction.process.SaveModelTransaction;
import org.json.JSONObject;
import java.lang.reflect.Type;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* Created by viirus on 14/01/16.
*/
public class CustomizationDeserializer implements JsonDeserializer<List<Customization>> {
@Override
public List<Customization> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject object = json.getAsJsonObject();
List<Customization> customizations = new ArrayList<Customization>();
for (String type : Arrays.asList("shirt", "skin")) {
for (Map.Entry<String,JsonElement> entry : object.get(type).getAsJsonObject().entrySet()) {
customizations.add(this.parseCustomization(type, null, entry));
}
}
for (Map.Entry<String,JsonElement> categoryEntry : object.get("hair").getAsJsonObject().entrySet()) {
for (Map.Entry<String,JsonElement> entry : categoryEntry.getValue().getAsJsonObject().entrySet()) {
customizations.add(this.parseCustomization("hair", categoryEntry.getKey(), entry));
}
}
TransactionManager.getInstance().addTransaction(new SaveModelTransaction<>(ProcessModelInfo.withModels(customizations)));
return customizations;
}
private Customization parseCustomization(String type, String category, Map.Entry<String,JsonElement> entry) {
JsonObject obj = entry.getValue().getAsJsonObject();
Customization customization = new Customization();
customization.setType(type);
if (category != null) {
customization.setCategory(category);
}
customization.setIdentifier(entry.getKey());
if (obj.has("price")) {
customization.setPrice(obj.get("price").getAsInt());
}
if (obj.has("set")) {
JsonObject setInfo = obj.get("set").getAsJsonObject();
customization.setCustomizationSet(setInfo.get("key").getAsString());
@SuppressLint("SimpleDateFormat") SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
try {
if (setInfo.has("availableFrom")) {
customization.setAvailableFrom(format.parse(setInfo.get("availableFrom").getAsString()));
}
if (setInfo.has("availableUntil")) {
customization.setAvailableUntil(format.parse(setInfo.get("availableUntil").getAsString()));
}
} catch (Exception e) {
e.printStackTrace();
}
}
return customization;
}
}

View file

@ -0,0 +1,63 @@
package com.magicmicky.habitrpgwrapper.lib.utils;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.magicmicky.habitrpgwrapper.lib.models.Customization;
import com.magicmicky.habitrpgwrapper.lib.models.Purchases;
import com.magicmicky.habitrpgwrapper.lib.models.Skill;
import com.magicmicky.habitrpgwrapper.lib.models.SkillList;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.ChecklistItem;
import com.raizlabs.android.dbflow.runtime.TransactionManager;
import com.raizlabs.android.dbflow.runtime.transaction.process.ProcessModelInfo;
import com.raizlabs.android.dbflow.runtime.transaction.process.SaveModelTransaction;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* Created by viirus on 14/01/16.
*/
public class PurchasedDeserializer implements JsonDeserializer<Purchases> {
@Override
public Purchases deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject object = json.getAsJsonObject();
List<Customization> customizations = new ArrayList<Customization>();
Purchases purchases = new Purchases();
for (String type : Arrays.asList("background", "shirt", "skin")) {
for (Map.Entry<String,JsonElement> entry : object.get(type).getAsJsonObject().entrySet()) {
Customization customization = new Customization();
customization.setType(type);
customization.setIdentifier(entry.getKey());
customization.setPurchased(entry.getValue().getAsBoolean());
customizations.add(customization);
}
}
for (Map.Entry<String,JsonElement> categoryEntry : object.get("hair").getAsJsonObject().entrySet()) {
for (Map.Entry<String,JsonElement> entry : categoryEntry.getValue().getAsJsonObject().entrySet()) {
Customization customization = new Customization();
customization.setType("hair");
customization.setCategory(categoryEntry.getKey());
customization.setIdentifier(entry.getKey());
customization.setPurchased(entry.getValue().getAsBoolean());
customizations.add(customization);
}
}
TransactionManager.getInstance().addTransaction(new SaveModelTransaction<>(ProcessModelInfo.withModels(customizations)));
purchases.customizations = customizations;
return purchases;
}
}

34
gradlew vendored
View file

@ -12,7 +12,7 @@ DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
# Use the maximum available, or customizationSet MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
@ -42,7 +42,7 @@ case "`uname`" in
;;
esac
# Attempt to set APP_HOME
# Attempt to customizationSet APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
@ -71,16 +71,16 @@ if [ -n "$JAVA_HOME" ] ; then
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
die "ERROR: JAVA_HOME is customizationSet to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
Please customizationSet the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not customizationSet and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
Please customizationSet the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
@ -93,7 +93,7 @@ if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
warn "Could not customizationSet maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
@ -137,16 +137,16 @@ if $cygwin ; then
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
(0) customizationSet -- ;;
(1) customizationSet -- "$args0" ;;
(2) customizationSet -- "$args0" "$args1" ;;
(3) customizationSet -- "$args0" "$args1" "$args2" ;;
(4) customizationSet -- "$args0" "$args1" "$args2" "$args3" ;;
(5) customizationSet -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) customizationSet -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) customizationSet -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) customizationSet -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) customizationSet -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi

34
gradlew.bat vendored
View file

@ -9,38 +9,38 @@
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
customizationSet DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
customizationSet DIRNAME=%~dp0
if "%DIRNAME%" == "" customizationSet DIRNAME=.
customizationSet APP_BASE_NAME=%~n0
customizationSet APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
customizationSet JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo ERROR: JAVA_HOME is not customizationSet and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo Please customizationSet the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
customizationSet JAVA_HOME=%JAVA_HOME:"=%
customizationSet JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo ERROR: JAVA_HOME is customizationSet to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo Please customizationSet the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
@ -53,23 +53,23 @@ if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
customizationSet CMD_LINE_ARGS=
customizationSet _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
customizationSet CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
customizationSet CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
customizationSet CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%