add ability to use skills

This commit is contained in:
Phillip Thelen 2015-11-28 19:30:23 +01:00
parent 422b79a2a3
commit effdc76390
13 changed files with 598 additions and 2 deletions

View file

@ -48,7 +48,10 @@
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity
android:name=".SkillTasksActivity"
android:label="@string/app_name">
</activity>
<receiver
android:name=".widget.SimpleWidget"
android:icon="@mipmap/ic_launcher"

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="bottom"
android:background="?attr/colorPrimary"
android:elevation="0dp"
android:fillViewport="false"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:layout_anchor="@+id/collapsing_toolbar"
app:layout_anchorGravity="bottom"
app:layout_collapseMode="pin"
app:tabGravity="fill"
app:tabIndicatorColor="@android:color/white"
app:tabMode="fixed" />
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1"
android:background="@android:color/white" />
</LinearLayout>

View file

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="com.magicmicky.habitrpgwrapper.lib.models.tasks.Task" />
<import type="android.view.View"/>
<variable
name="task"
type="Task" />
</data>
<LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@color/white">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_centerVertical="true"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingEnd="16dp"
android:paddingBottom="20dp"
android:paddingTop="20dp">
<TextView
android:id="@+id/checkedTextView"
style="@style/CardTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{task.text}" />
<TextView
android:id="@+id/notesTextView"
style="@style/CardText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{task.notes}"
android:visibility="@{task.notes != null ? View.VISIBLE : View.GONE}"/>
</LinearLayout>
<View
android:id="@+id/rightBorderView"
android:layout_width="5dp"
android:layout_height="match_parent"
app:backgroundColor="@{task.getLightTaskColor}"
android:gravity="center"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />
</RelativeLayout>
</LinearLayout>
<View
android:id="@+id/bottomBorderView"
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="@color/cell_separator"/>
</LinearLayout>
</layout>

View file

@ -179,4 +179,5 @@
<string name="profile_image">Profile Image</string>
<string name="mana_price_button" formatted="false">%d MP</string>
<string name="used_skill" formatted="false">You used %1$s for %2$d mana.</string>
</resources>

View file

@ -0,0 +1,109 @@
package com.habitrpg.android.habitica;
import android.app.Activity;
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.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.habitrpg.android.habitica.ui.adapter.HabitItemRecyclerViewAdapter;
import com.habitrpg.android.habitica.ui.adapter.SkillTasksRecyclerViewAdapter;
import com.habitrpg.android.habitica.ui.fragments.SkillTasksRecyclerViewFragment;
import com.habitrpg.android.habitica.ui.fragments.TaskRecyclerViewFragment;
import com.habitrpg.android.habitica.ui.fragments.TasksFragment;
import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.Task;
import java.util.HashMap;
import java.util.Map;
import butterknife.ButterKnife;
import butterknife.InjectView;
/**
* Created by viirus on 28/11/15.
*/
public class SkillTasksActivity extends AppCompatActivity {
@InjectView(R.id.viewpager)
public ViewPager viewPager;
@InjectView(R.id.tab_layout)
public TabLayout tabLayout;
Map<Integer, SkillTasksRecyclerViewFragment> ViewFragmentsDictionary = new HashMap<>();
protected HabitRPGUser user;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_skill_tasks);
ButterKnife.inject(this);
loadTaskLists();
}
public void loadTaskLists() {
android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager();
viewPager.setAdapter(new FragmentPagerAdapter(fragmentManager) {
@Override
public Fragment getItem(int position) {
SkillTasksRecyclerViewFragment fragment;
switch (position) {
case 0:
fragment = SkillTasksRecyclerViewFragment.newInstance(new SkillTasksRecyclerViewAdapter(Task.TYPE_HABIT, SkillTasksActivity.this), Task.TYPE_HABIT);
break;
case 1:
fragment = SkillTasksRecyclerViewFragment.newInstance(new SkillTasksRecyclerViewAdapter(Task.TYPE_DAILY, SkillTasksActivity.this), Task.TYPE_DAILY);
break;
default:
fragment = SkillTasksRecyclerViewFragment.newInstance(new SkillTasksRecyclerViewAdapter(Task.TYPE_TODO, SkillTasksActivity.this), Task.TYPE_TODO);
}
ViewFragmentsDictionary.put(position, fragment);
return fragment;
}
@Override
public int getCount() {
return 3;
}
@Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "Habits";
case 1:
return "Dailies";
case 2:
return "Todos";
}
return "";
}
});
tabLayout.setupWithViewPager(viewPager);
}
public void taskSelected(String taskId) {
Intent resultIntent = new Intent();
resultIntent.putExtra("task_id", taskId);
setResult(Activity.RESULT_OK, resultIntent);
finish();
}
}

View file

@ -0,0 +1,42 @@
package com.habitrpg.android.habitica.callbacks;
import android.util.Log;
import com.crashlytics.android.Crashlytics;
import com.habitrpg.android.habitica.events.SkillUsedEvent;
import com.habitrpg.android.habitica.events.TaskCreatedEvent;
import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser;
import com.magicmicky.habitrpgwrapper.lib.models.Skill;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.Task;
import de.greenrobot.event.EventBus;
import retrofit.Callback;
import retrofit.RetrofitError;
import retrofit.client.Response;
/**
* Created by viirus on 28/11/15.
*/
public class SkillCallback implements Callback<HabitRPGUser> {
private Skill usedSkill;
private final HabitRPGUserCallback.OnUserReceived callback;
public SkillCallback(HabitRPGUserCallback.OnUserReceived callback, Skill usedSkill) {
this.callback = callback;
this.usedSkill = usedSkill;
}
@Override
public void success(HabitRPGUser habitRPGUser, Response response) {
habitRPGUser.async().save();
EventBus.getDefault().post(new SkillUsedEvent(this.usedSkill, habitRPGUser.getStats().getMp()));
callback.onUserReceived(habitRPGUser);
}
@Override
public void failure(RetrofitError error) {
Crashlytics.logException(error);
callback.onUserFail();
}
}

View file

@ -0,0 +1,17 @@
package com.habitrpg.android.habitica.events;
import com.magicmicky.habitrpgwrapper.lib.models.Skill;
/**
* Created by viirus on 28/11/15.
*/
public class SkillUsedEvent {
public Skill usedSkill;
public Double newMana;
public SkillUsedEvent(Skill usedSkill, Double newMana) {
this.usedSkill = usedSkill;
this.newMana = newMana;
}
}

View file

@ -0,0 +1,11 @@
package com.habitrpg.android.habitica.events.commands;
import com.magicmicky.habitrpgwrapper.lib.models.Skill;
/**
* Created by viirus on 28/11/15.
*/
public class UseSkillCommand {
public Skill skill;
}

View file

@ -0,0 +1,168 @@
package com.habitrpg.android.habitica.ui.adapter;
import android.app.Activity;
import android.content.Context;
import android.databinding.DataBindingUtil;
import android.databinding.ObservableArrayList;
import android.os.Handler;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.SkillTasksActivity;
import com.habitrpg.android.habitica.databinding.SkillTaskItemCardBinding;
import com.habitrpg.android.habitica.events.TaskLongPressedEvent;
import com.habitrpg.android.habitica.events.TaskTappedEvent;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.Task;
import com.raizlabs.android.dbflow.sql.builder.Condition;
import com.raizlabs.android.dbflow.sql.language.OrderBy;
import com.raizlabs.android.dbflow.sql.language.Select;
import com.raizlabs.android.dbflow.structure.BaseModel;
import com.raizlabs.android.dbflow.structure.Model;
import java.util.UUID;
import butterknife.ButterKnife;
import butterknife.InjectView;
import de.greenrobot.event.EventBus;
public class SkillTasksRecyclerViewAdapter extends RecyclerView.Adapter<SkillTasksRecyclerViewAdapter.ViewHolder> {
String taskType;
private ObservableArrayList<Task> observableContent;
SkillTasksActivity activity;
static final int TYPE_CELL = 1;
private RecyclerView.Adapter<ViewHolder> parentAdapter;
public SkillTasksRecyclerViewAdapter(String taskType, SkillTasksActivity activity) {
this.setHasStableIds(true);
this.taskType = taskType;
this.activity = activity;
this.loadContent();
}
public void setParentAdapter(RecyclerView.Adapter<SkillTasksRecyclerViewAdapter.ViewHolder> parentAdapter) {
this.parentAdapter = parentAdapter;
}
@Override
public int getItemViewType(int position) {
switch (position) {
default:
return TYPE_CELL;
}
}
@Override
public long getItemId(int position) {
Task task = observableContent.get(position);
if (task.getId() != null && task.getId().length() == 36) {
return UUID.fromString(task.getId()).getMostSignificantBits();
}
return UUID.randomUUID().getMostSignificantBits();
}
@Override
public int getItemCount() {
return observableContent.size();
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = null;
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.skill_task_item_card, parent, false);
return new SkillTasksRecyclerViewAdapter.ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Task item = observableContent.get(position);
holder.bindHolder(item, position);
}
// region ViewHolders
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
protected android.content.res.Resources resources;
public Task task;
SkillTaskItemCardBinding binding;
@InjectView(R.id.notesTextView)
TextView notesTextView;
public ViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
itemView.setClickable(true);
ButterKnife.inject(this, itemView);
binding = DataBindingUtil.bind(itemView);
resources = itemView.getResources();
}
public void bindHolder(Task habitItem, int position) {
double itemvalue = habitItem.getValue();
task = habitItem;
if (habitItem.notes == null || habitItem.notes.length() == 0) {
notesTextView.setHeight(0);
} else {
notesTextView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
}
binding.setTask(task);
}
@Override
public void onClick(View v) {
if (v != itemView)
return;
activity.taskSelected(task.getId());
}
}
// endregion
public void loadContent() {
this.loadContent(false);
}
public void loadContent(boolean forced) {
if (this.observableContent == null || forced) {
this.observableContent = new ObservableArrayList<>();
this.observableContent.addAll(new Select().from(Task.class)
.where(Condition.column("type").eq(this.taskType))
.and(Condition.CombinedCondition
.begin(Condition.column("completed").eq(false))
.or(Condition.column("type").eq("daily"))
)
.orderBy(OrderBy.columns("dateCreated").descending())
.queryList());
}
if (parentAdapter != null) {
parentAdapter.notifyDataSetChanged();
} else {
notifyDataSetChanged();
}
}
}

View file

@ -17,6 +17,7 @@ import android.widget.TextView;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.databinding.ValueBarBinding;
import com.habitrpg.android.habitica.events.TaskTappedEvent;
import com.habitrpg.android.habitica.events.commands.CopyChatAsTodoCommand;
import com.habitrpg.android.habitica.events.commands.DeleteChatMessageCommand;
import com.habitrpg.android.habitica.events.commands.FlagChatMessageCommand;
@ -24,6 +25,7 @@ import com.habitrpg.android.habitica.events.commands.OpenNewPMActivityCommand;
import com.habitrpg.android.habitica.events.commands.SendNewGroupMessageCommand;
import com.habitrpg.android.habitica.events.commands.ToggleInnCommand;
import com.habitrpg.android.habitica.events.commands.ToggleLikeMessageCommand;
import com.habitrpg.android.habitica.events.commands.UseSkillCommand;
import com.habitrpg.android.habitica.ui.AvatarWithBarsViewModel;
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils;
import com.habitrpg.android.habitica.ui.helpers.ViewHelper;
@ -59,6 +61,11 @@ public class SkillsRecyclerViewAdapter extends RecyclerView.Adapter<SkillsRecycl
this.notifyDataSetChanged();
}
public void setMana(Double mana) {
this.mana = mana;
this.notifyDataSetChanged();
}
@Override
public SkillViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
@ -79,7 +86,7 @@ public class SkillsRecyclerViewAdapter extends RecyclerView.Adapter<SkillsRecycl
return skillList == null ? 0 : skillList.size();
}
class SkillViewHolder extends RecyclerView.ViewHolder {
class SkillViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
@InjectView(R.id.skill_text)
TextView skillNameTextView;
@ -90,6 +97,8 @@ public class SkillsRecyclerViewAdapter extends RecyclerView.Adapter<SkillsRecycl
@InjectView(R.id.price_button)
Button priceButton;
Skill skill;
Resources resources;
public SkillViewHolder(View itemView) {
@ -98,9 +107,12 @@ public class SkillsRecyclerViewAdapter extends RecyclerView.Adapter<SkillsRecycl
ButterKnife.inject(this, itemView);
resources = itemView.getResources();
priceButton.setOnClickListener(this);
}
public void bind(Skill skill) {
this.skill = skill;
skillNameTextView.setText(skill.text);
skillNotesTextView.setText(skill.notes);
priceButton.setText(String.format(resources.getString(R.string.mana_price_button), skill.mana));
@ -115,7 +127,14 @@ public class SkillsRecyclerViewAdapter extends RecyclerView.Adapter<SkillsRecycl
skillNotesTextView.setTextColor(resources.getColor(android.R.color.black));
priceButton.setEnabled(true);
}
}
@Override
public void onClick(View v) {
UseSkillCommand event = new UseSkillCommand();
event.skill = this.skill;
EventBus.getDefault().post(event);
}
}
}

View file

@ -0,0 +1,80 @@
package com.habitrpg.android.habitica.ui.fragments;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.events.commands.AddNewTaskCommand;
import com.habitrpg.android.habitica.ui.adapter.HabitItemRecyclerViewAdapter;
import com.habitrpg.android.habitica.ui.adapter.SkillTasksRecyclerViewAdapter;
import de.greenrobot.event.EventBus;
/**
* Created by viirus on 28/11/15.
*/
public class SkillTasksRecyclerViewFragment extends Fragment implements View.OnClickListener {
public RecyclerView mRecyclerView;
public RecyclerView.Adapter mAdapter;
private String classType;
public void SetInnerAdapter(SkillTasksRecyclerViewAdapter adapter, String classType) {
this.classType = classType;
mAdapter = adapter;
}
private View view;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (view == null)
view = inflater.inflate(R.layout.fragment_recyclerview, container, false);
return view;
}
LinearLayoutManager layoutManager = null;
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
android.support.v4.app.FragmentActivity context = getActivity();
layoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
if (layoutManager == null) {
layoutManager = new LinearLayoutManager(context);
mRecyclerView.setLayoutManager(layoutManager);
}
mRecyclerView.setAdapter(mAdapter);
}
public static SkillTasksRecyclerViewFragment newInstance(SkillTasksRecyclerViewAdapter adapter, String classType) {
SkillTasksRecyclerViewFragment fragment = new SkillTasksRecyclerViewFragment();
fragment.setRetainInstance(true);
fragment.SetInnerAdapter(adapter, classType);
return fragment;
}
@Override
public void onClick(View v) {
AddNewTaskCommand event = new AddNewTaskCommand();
event.ClassType = this.classType;
EventBus.getDefault().post(event);
}
}

View file

@ -1,6 +1,8 @@
package com.habitrpg.android.habitica.ui.fragments;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.Nullable;
@ -14,12 +16,17 @@ import android.view.ViewGroup;
import com.habitrpg.android.habitica.APIHelper;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.SkillTasksActivity;
import com.habitrpg.android.habitica.callbacks.HabitRPGUserCallback;
import com.habitrpg.android.habitica.callbacks.SkillCallback;
import com.habitrpg.android.habitica.events.SkillUsedEvent;
import com.habitrpg.android.habitica.events.ToggledInnStateEvent;
import com.habitrpg.android.habitica.events.commands.DeleteChatMessageCommand;
import com.habitrpg.android.habitica.events.commands.FlagChatMessageCommand;
import com.habitrpg.android.habitica.events.commands.SendNewGroupMessageCommand;
import com.habitrpg.android.habitica.events.commands.ToggleInnCommand;
import com.habitrpg.android.habitica.events.commands.ToggleLikeMessageCommand;
import com.habitrpg.android.habitica.events.commands.UseSkillCommand;
import com.habitrpg.android.habitica.ui.adapter.ChatRecyclerViewAdapter;
import com.habitrpg.android.habitica.ui.adapter.SkillsRecyclerViewAdapter;
import com.magicmicky.habitrpgwrapper.lib.models.ChatMessage;
@ -44,11 +51,15 @@ import retrofit.client.Response;
*/
public class SkillsFragment extends BaseFragment {
private final int TASK_SELECTION_ACTIVITY = 10;
private View view;
private Skill selectedSkill;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
if (view == null)
view = inflater.inflate(R.layout.fragment_skills, container, false);
@ -83,4 +94,33 @@ public class SkillsFragment extends BaseFragment {
adapter.setSkillList(skills);
}
public void onEvent(UseSkillCommand command) {
Skill skill = command.skill;
if (skill.target.equals("task")) {
selectedSkill = skill;
Intent intent = new Intent(activity, SkillTasksActivity.class);
startActivityForResult(intent, TASK_SELECTION_ACTIVITY);
} else {
mAPIHelper.apiService.useSkill(skill.key, skill.target, new SkillCallback(activity, skill));
}
}
public void onEvent(SkillUsedEvent event) {
Skill skill = event.usedSkill;
adapter.setMana(event.newMana);
activity.showSnackbar(activity.getString(R.string.used_skill, skill.text, skill.mana));
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode) {
case (TASK_SELECTION_ACTIVITY) : {
if (resultCode == Activity.RESULT_OK) {
mAPIHelper.apiService.useSkill(selectedSkill.key, selectedSkill.target, data.getStringExtra("task_id"), new SkillCallback(activity, selectedSkill));
}
break;
}
}
}
}

View file

@ -91,6 +91,12 @@ public interface ApiService {
@POST("/user/revive")
void revive(Callback<HabitRPGUser> habitRPGUserCallback);
@POST("/user/class/cast/{skill}")
void useSkill(@Path("skill") String skillName, @Query("targetType") String targetType, @Query("targetId") String targetId, Callback<HabitRPGUser> habitRPGUserCallback);
@POST("/user/class/cast/{skill}")
void useSkill(@Path("skill") String skillName, @Query("targetType") String targetType, Callback<HabitRPGUser> habitRPGUserCallback);
/* Group API */
@GET("/groups")