diff --git a/Habitica/res/layout/activity_task_form.xml b/Habitica/res/layout/activity_task_form.xml
index 81463559e..a3b029395 100644
--- a/Habitica/res/layout/activity_task_form.xml
+++ b/Habitica/res/layout/activity_task_form.xml
@@ -68,9 +68,9 @@
@@ -82,12 +82,12 @@
android:id="@+id/textView"
android:layout_gravity="center_vertical" />
-
+ android:id="@+id/task_startdate_picker"
+ android:datePickerMode="spinner"
+ android:calendarViewShown="false" />
@@ -120,7 +120,8 @@
android:id="@+id/textView7"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
- android:layout_marginTop="11dp" />
+ android:layout_marginTop="11dp"
+ android:layout_toLeftOf="@+id/task_positive_checkbox"/>
+ android:layout_marginTop="11dp"
+ android:layout_toLeftOf="@+id/task_negative_checkbox"/>
+ android:id="@+id/textView9"
+ android:layout_marginBottom="8dp"/>
+
+
+
+
+
+
+
+
+
@@ -203,6 +231,5 @@
-
\ No newline at end of file
diff --git a/Habitica/res/layout/checklist_item.xml b/Habitica/res/layout/checklist_item.xml
new file mode 100644
index 000000000..63cf050a9
--- /dev/null
+++ b/Habitica/res/layout/checklist_item.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml
index 521830ebc..d673aa3ea 100644
--- a/Habitica/res/values/strings.xml
+++ b/Habitica/res/values/strings.xml
@@ -183,4 +183,6 @@
%d MP
You used %1$s for %2$d mana.
+ new checklist item
+ Add
\ No newline at end of file
diff --git a/Habitica/src/com/habitrpg/android/habitica/APIHelper.java b/Habitica/src/com/habitrpg/android/habitica/APIHelper.java
index 9c0e8fdda..7f94c7adf 100644
--- a/Habitica/src/com/habitrpg/android/habitica/APIHelper.java
+++ b/Habitica/src/com/habitrpg/android/habitica/APIHelper.java
@@ -18,6 +18,7 @@ import com.google.gson.stream.JsonWriter;
import com.habitrpg.android.habitica.callbacks.HabitRPGUserCallback;
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;
@@ -30,10 +31,12 @@ 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.UserAuthSocialTokens;
+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.SkillDeserializer;
import com.raizlabs.android.dbflow.structure.ModelAdapter;
+import com.raizlabs.android.dbflow.structure.container.ForeignKeyContainer;
import java.io.IOException;
import java.lang.reflect.Type;
@@ -77,6 +80,7 @@ public class APIHelper implements ErrorHandler, Profiler {
//Exclusion stratety needed for DBFlow https://github.com/Raizlabs/DBFlow/issues/121
Gson gson = new GsonBuilder()
+ .setExclusionStrategies(new CheckListItemExcludeStrategy())
.setExclusionStrategies(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
diff --git a/Habitica/src/com/habitrpg/android/habitica/HabiticaApplication.java b/Habitica/src/com/habitrpg/android/habitica/HabiticaApplication.java
index 0f6af27f2..ed4e15b37 100644
--- a/Habitica/src/com/habitrpg/android/habitica/HabiticaApplication.java
+++ b/Habitica/src/com/habitrpg/android/habitica/HabiticaApplication.java
@@ -21,6 +21,7 @@ import org.solovyev.android.checkout.Checkout;
import org.solovyev.android.checkout.ProductTypes;
import org.solovyev.android.checkout.Products;
import org.solovyev.android.checkout.PurchaseVerifier;
+import org.solovyev.android.checkout.RequestListener;
import java.io.File;
import java.util.Arrays;
diff --git a/Habitica/src/com/habitrpg/android/habitica/MainActivity.java b/Habitica/src/com/habitrpg/android/habitica/MainActivity.java
index 01bbd98d2..0a7aac68e 100644
--- a/Habitica/src/com/habitrpg/android/habitica/MainActivity.java
+++ b/Habitica/src/com/habitrpg/android/habitica/MainActivity.java
@@ -158,13 +158,6 @@ public class MainActivity extends InstabugAppCompatActivity implements HabitRPGU
drawer.setSelection(MainDrawerBuilder.SIDEBAR_PURCHASE);
}
- @Override
- protected void onResume() {
- super.onResume();
- mAPIHelper.retrieveUser(new HabitRPGUserCallback(this));
- setUserData();
- }
-
@Override
public boolean onCreateOptionsMenu(Menu menu) {
return true;
diff --git a/Habitica/src/com/habitrpg/android/habitica/TaskFormActivity.java b/Habitica/src/com/habitrpg/android/habitica/TaskFormActivity.java
index bedb86b24..b9c607add 100644
--- a/Habitica/src/com/habitrpg/android/habitica/TaskFormActivity.java
+++ b/Habitica/src/com/habitrpg/android/habitica/TaskFormActivity.java
@@ -1,13 +1,20 @@
package com.habitrpg.android.habitica;
+import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
+import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.LinearLayout;
@@ -16,7 +23,11 @@ import android.widget.Spinner;
import android.widget.TextView;
import com.habitrpg.android.habitica.events.TaskSaveEvent;
+import com.habitrpg.android.habitica.ui.WrapContentRecyclerViewLayoutManager;
+import com.habitrpg.android.habitica.ui.adapter.CheckListAdapter;
+import com.habitrpg.android.habitica.ui.helpers.SimpleItemTouchHelperCallback;
import com.magicmicky.habitrpgwrapper.lib.models.Tag;
+import com.magicmicky.habitrpgwrapper.lib.models.tasks.ChecklistItem;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.Days;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.Task;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.TaskTag;
@@ -44,6 +55,8 @@ public class TaskFormActivity extends AppCompatActivity implements AdapterView.O
private NumberPicker frequencyPicker;
private LinearLayout frequencyContainer;
private List tags;
+ private CheckListAdapter checklistAdapter;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -94,12 +107,12 @@ public class TaskFormActivity extends AppCompatActivity implements AdapterView.O
this.dailyFrequencySpinner.setAdapter(frequencyAdapter);
this.dailyFrequencySpinner.setOnItemSelectedListener(this);
+
this.frequencyContainer = (LinearLayout) weekdayWrapper.findViewById(R.id.task_frequency_container);
} else {
mainWrapper.removeView(weekdayWrapper);
}
-
if (taskId != null) {
Task task = new Select().from(Task.class).byIds(taskId).querySingle();
this.task = task;
@@ -108,6 +121,41 @@ public class TaskFormActivity extends AppCompatActivity implements AdapterView.O
} else {
setTitle((Task) null);
}
+
+ if(taskType.equals("todo") || taskType.equals("daily")){
+ createCheckListRecyclerView();
+ }
+ }
+
+ private void createCheckListRecyclerView() {
+ checklistAdapter = new CheckListAdapter(task.getChecklist());
+ RecyclerView recyclerView = (RecyclerView) findViewById(R.id.checklist_recycler_view);
+
+ LinearLayoutManager llm = new LinearLayoutManager(this);
+ llm.setOrientation(LinearLayoutManager.VERTICAL);
+
+ recyclerView.setLayoutManager(llm);
+ recyclerView.setAdapter(checklistAdapter);
+ int i = checklistAdapter.getItemCount();
+
+ recyclerView.setLayoutManager(new WrapContentRecyclerViewLayoutManager(this));
+
+ ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(checklistAdapter);
+ ItemTouchHelper mItemTouchHelper = new ItemTouchHelper(callback);
+ mItemTouchHelper.attachToRecyclerView(recyclerView);
+
+ final EditText newCheckListEditText = (EditText)findViewById(R.id.new_checklist);
+
+ Button button = (Button)findViewById(R.id.add_checklist_button);
+ button.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ String checklist = newCheckListEditText.getText().toString();
+ ChecklistItem item = new ChecklistItem(checklist);
+ checklistAdapter.addItem(item);
+ newCheckListEditText.setText("");
+ }
+ });
}
private void setTitle(Task task) {
@@ -197,6 +245,7 @@ public class TaskFormActivity extends AppCompatActivity implements AdapterView.O
//noinspection SimplifiableIfStatement
if (id == R.id.action_discard_changes) {
finish();
+ dismissKeyboard();
return true;
}
@@ -249,6 +298,8 @@ public class TaskFormActivity extends AppCompatActivity implements AdapterView.O
private boolean saveTask(Task task) {
task.text = taskText.getText().toString();
+ task.setChecklist(checklistAdapter.getCheckListItems());
+
if (task.text.isEmpty())
return false;
@@ -319,15 +370,27 @@ public class TaskFormActivity extends AppCompatActivity implements AdapterView.O
@Override
public boolean onSupportNavigateUp() {
- this.prepareSave();
- finish();
- return super.onSupportNavigateUp();
+ this.finishActivitySuccessfuly();
+ return true;
}
@Override
public void onBackPressed() {
+ this.finishActivitySuccessfuly();
+ }
+
+ private void finishActivitySuccessfuly() {
this.prepareSave();
finish();
+ dismissKeyboard();
+ }
+
+ private void dismissKeyboard() {
+ InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
+ View currentFocus = getCurrentFocus();
+ if (currentFocus != null) {
+ imm.hideSoftInputFromWindow(currentFocus.getWindowToken(), 0);
+ }
}
private TransactionListener> tagsSearchingListener = new TransactionListener>() {
diff --git a/Habitica/src/com/habitrpg/android/habitica/callbacks/HabitRPGUserCallback.java b/Habitica/src/com/habitrpg/android/habitica/callbacks/HabitRPGUserCallback.java
index e714ae35f..6d37b57e0 100644
--- a/Habitica/src/com/habitrpg/android/habitica/callbacks/HabitRPGUserCallback.java
+++ b/Habitica/src/com/habitrpg/android/habitica/callbacks/HabitRPGUserCallback.java
@@ -1,7 +1,5 @@
package com.habitrpg.android.habitica.callbacks;
-import android.util.Log;
-
import com.crashlytics.android.Crashlytics;
import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser;
diff --git a/Habitica/src/com/habitrpg/android/habitica/database/CheckListItemExcludeStrategy.java b/Habitica/src/com/habitrpg/android/habitica/database/CheckListItemExcludeStrategy.java
new file mode 100644
index 000000000..68a6e04cb
--- /dev/null
+++ b/Habitica/src/com/habitrpg/android/habitica/database/CheckListItemExcludeStrategy.java
@@ -0,0 +1,20 @@
+package com.habitrpg.android.habitica.database;
+
+import com.google.gson.ExclusionStrategy;
+import com.google.gson.FieldAttributes;
+
+/**
+ * Created by franzejr on 29/11/15.
+ */
+public class CheckListItemExcludeStrategy implements ExclusionStrategy {
+
+ @Override
+ public boolean shouldSkipField(FieldAttributes f) {
+ return f.getAnnotation(ExcludeCheckListItem.class) != null;
+ }
+
+ @Override
+ public boolean shouldSkipClass(Class> clazz) {
+ return clazz.getAnnotation(ExcludeCheckListItem.class) != null;
+ }
+}
diff --git a/Habitica/src/com/habitrpg/android/habitica/database/ExcludeCheckListItem.java b/Habitica/src/com/habitrpg/android/habitica/database/ExcludeCheckListItem.java
new file mode 100644
index 000000000..30f8e29f4
--- /dev/null
+++ b/Habitica/src/com/habitrpg/android/habitica/database/ExcludeCheckListItem.java
@@ -0,0 +1,8 @@
+package com.habitrpg.android.habitica.database;
+
+/**
+ * Created by franzejr on 29/11/15.
+ */
+public @interface ExcludeCheckListItem {
+
+}
diff --git a/Habitica/src/com/habitrpg/android/habitica/events/TaskLongPressedEvent.java b/Habitica/src/com/habitrpg/android/habitica/events/TaskLongPressedEvent.java
index 9e2dcf1ac..e6806b7da 100644
--- a/Habitica/src/com/habitrpg/android/habitica/events/TaskLongPressedEvent.java
+++ b/Habitica/src/com/habitrpg/android/habitica/events/TaskLongPressedEvent.java
@@ -1,8 +1,17 @@
package com.habitrpg.android.habitica.events;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.habitrpg.android.habitica.TaskFormActivity;
+
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Created by Negue on 10.07.2015.
*/
public class TaskLongPressedEvent {
- public com.magicmicky.habitrpgwrapper.lib.models.tasks.Task Task;
+ public com.magicmicky.habitrpgwrapper.lib.models.tasks.Task task;
}
diff --git a/Habitica/src/com/habitrpg/android/habitica/ui/WrapContentRecyclerViewLayoutManager.java b/Habitica/src/com/habitrpg/android/habitica/ui/WrapContentRecyclerViewLayoutManager.java
new file mode 100644
index 000000000..d63fe2771
--- /dev/null
+++ b/Habitica/src/com/habitrpg/android/habitica/ui/WrapContentRecyclerViewLayoutManager.java
@@ -0,0 +1,201 @@
+package com.habitrpg.android.habitica.ui;
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.view.View;
+
+import com.habitrpg.android.habitica.BuildConfig;
+import com.raizlabs.android.dbflow.sql.index.Index;
+
+/**
+ * http://stackoverflow.com/a/29945693/1315039
+ */
+public class WrapContentRecyclerViewLayoutManager extends android.support.v7.widget.LinearLayoutManager {
+
+ private static final int CHILD_WIDTH = 0;
+ private static final int CHILD_HEIGHT = 1;
+ private static final int DEFAULT_CHILD_SIZE = 100;
+
+ private final int[] childDimensions = new int[2];
+
+ private int childSize = DEFAULT_CHILD_SIZE;
+ private boolean hasChildSize;
+
+ @SuppressWarnings("UnusedDeclaration")
+ public WrapContentRecyclerViewLayoutManager(Context context) {
+ super(context);
+ }
+
+ @SuppressWarnings("UnusedDeclaration")
+ public WrapContentRecyclerViewLayoutManager(Context context, int orientation, boolean reverseLayout) {
+ super(context, orientation, reverseLayout);
+ }
+
+ public static int makeUnspecifiedSpec() {
+ return View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
+ }
+
+ @Override
+ public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
+ final int widthMode = View.MeasureSpec.getMode(widthSpec);
+ final int heightMode = View.MeasureSpec.getMode(heightSpec);
+
+ final int widthSize = View.MeasureSpec.getSize(widthSpec);
+ final int heightSize = View.MeasureSpec.getSize(heightSpec);
+
+ final boolean exactWidth = widthMode == View.MeasureSpec.EXACTLY;
+ final boolean exactHeight = heightMode == View.MeasureSpec.EXACTLY;
+
+ final int unspecified = makeUnspecifiedSpec();
+
+ if (exactWidth && exactHeight) {
+ // in case of exact calculations for both dimensions let's use default "onMeasure" implementation
+ super.onMeasure(recycler, state, widthSpec, heightSpec);
+ return;
+ }
+
+ final boolean vertical = getOrientation() == VERTICAL;
+
+ initChildDimensions(widthSize, heightSize, vertical);
+
+ int width = 0;
+ int height = 0;
+
+ // it's possible to get scrap views in recycler which are bound to old (invalid) adapter entities. This
+ // happens because their invalidation happens after "onMeasure" method. As a workaround let's clear the
+ // recycler now (it should not cause any performance issues while scrolling as "onMeasure" is never
+ // called whiles scrolling)
+ recycler.clear();
+
+ final int stateItemCount = state.getItemCount();
+ final int adapterItemCount = getItemCount();
+ // adapter always contains actual data while state might contain old data (f.e. data before the animation is
+ // done). As we want to measure the view with actual data we must use data from the adapter and not from the
+ // state
+ for (int i = 0; i < adapterItemCount; i++) {
+ if (vertical) {
+ if (!hasChildSize) {
+ if (i < stateItemCount) {
+ // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
+ // we will use previously calculated dimensions
+ measureChild(recycler, i, widthSpec, unspecified, childDimensions);
+ } else {
+ logMeasureWarning(i);
+ }
+ }
+ height += childDimensions[CHILD_HEIGHT];
+ if (i == 0) {
+ width = childDimensions[CHILD_WIDTH];
+ }
+ } else {
+ if (!hasChildSize) {
+ if (i < stateItemCount) {
+ // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
+ // we will use previously calculated dimensions
+ measureChild(recycler, i, unspecified, heightSpec, childDimensions);
+ } else {
+ logMeasureWarning(i);
+ }
+ }
+ width += childDimensions[CHILD_WIDTH];
+ if (i == 0) {
+ height = childDimensions[CHILD_HEIGHT];
+ }
+ }
+ }
+
+ // we really should wrap the contents of the view, let's do it
+
+ if (exactWidth) {
+ width = widthSize;
+ } else {
+ width += getPaddingLeft() + getPaddingRight();
+ }
+
+ if (exactHeight) {
+ height = heightSize;
+ } else {
+ height += getPaddingTop() + getPaddingBottom();
+ }
+
+ setMeasuredDimension(width, height);
+ }
+
+ private void logMeasureWarning(int child) {
+ if (BuildConfig.DEBUG) {
+ Log.w("LinearLayoutManager", "Can't measure child #" + child + ", previously used dimensions will be reused." +
+ "To remove this message either use #setChildSize() method or don't run RecyclerView animations");
+ }
+ }
+
+ private void initChildDimensions(int width, int height, boolean vertical) {
+ if (childDimensions[CHILD_WIDTH] != 0 || childDimensions[CHILD_HEIGHT] != 0) {
+ // already initialized, skipping
+ return;
+ }
+ if (vertical) {
+ childDimensions[CHILD_WIDTH] = width;
+ childDimensions[CHILD_HEIGHT] = childSize;
+ } else {
+ childDimensions[CHILD_WIDTH] = childSize;
+ childDimensions[CHILD_HEIGHT] = height;
+ }
+ }
+
+ @Override
+ public void setOrientation(int orientation) {
+ // might be called before the constructor of this class is called
+ //noinspection ConstantConditions
+ if (childDimensions != null) {
+ if (getOrientation() != orientation) {
+ childDimensions[CHILD_WIDTH] = 0;
+ childDimensions[CHILD_HEIGHT] = 0;
+ }
+ }
+ super.setOrientation(orientation);
+ }
+
+ public void clearChildSize() {
+ hasChildSize = false;
+ setChildSize(DEFAULT_CHILD_SIZE);
+ }
+
+ public void setChildSize(int childSize) {
+ hasChildSize = true;
+ if (this.childSize != childSize) {
+ this.childSize = childSize;
+ requestLayout();
+ }
+ }
+
+ private void measureChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] dimensions) {
+ final View child;
+ try {
+ child = recycler.getViewForPosition(position);
+ } catch (IndexOutOfBoundsException e) {
+ return;
+ }
+
+ final RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) child.getLayoutParams();
+
+ final int hPadding = getPaddingLeft() + getPaddingRight();
+ final int vPadding = getPaddingTop() + getPaddingBottom();
+
+ final int hMargin = p.leftMargin + p.rightMargin;
+ final int vMargin = p.topMargin + p.bottomMargin;
+
+ final int hDecoration = getRightDecorationWidth(child) + getLeftDecorationWidth(child);
+ final int vDecoration = getTopDecorationHeight(child) + getBottomDecorationHeight(child);
+
+ final int childWidthSpec = getChildMeasureSpec(widthSpec, hPadding + hMargin + hDecoration, p.width, canScrollHorizontally());
+ final int childHeightSpec = getChildMeasureSpec(heightSpec, vPadding + vMargin + vDecoration, p.height, canScrollVertically());
+
+ child.measure(childWidthSpec, childHeightSpec);
+
+ dimensions[CHILD_WIDTH] = getDecoratedMeasuredWidth(child) + p.leftMargin + p.rightMargin;
+ dimensions[CHILD_HEIGHT] = getDecoratedMeasuredHeight(child) + p.bottomMargin + p.topMargin;
+
+ recycler.recycleView(child);
+ }
+}
diff --git a/Habitica/src/com/habitrpg/android/habitica/ui/adapter/CheckListAdapter.java b/Habitica/src/com/habitrpg/android/habitica/ui/adapter/CheckListAdapter.java
new file mode 100644
index 000000000..6136071bf
--- /dev/null
+++ b/Habitica/src/com/habitrpg/android/habitica/ui/adapter/CheckListAdapter.java
@@ -0,0 +1,124 @@
+package com.habitrpg.android.habitica.ui.adapter;
+
+import android.graphics.Color;
+import android.support.v7.widget.RecyclerView;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import com.habitrpg.android.habitica.R;
+import com.habitrpg.android.habitica.ui.helpers.ItemTouchHelperAdapter;
+import com.habitrpg.android.habitica.ui.helpers.ItemTouchHelperViewHolder;
+import com.magicmicky.habitrpgwrapper.lib.models.tasks.ChecklistItem;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import butterknife.ButterKnife;
+import butterknife.InjectView;
+
+/**
+ * Created by franzejr on 15/11/15.
+ */
+public class CheckListAdapter extends RecyclerView.Adapter
+ implements ItemTouchHelperAdapter{
+
+ private final List mItems = new ArrayList<>();
+
+ public CheckListAdapter(List checklistItems) {
+ mItems.addAll(checklistItems);
+ }
+
+ @Override
+ public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.checklist_item, parent, false);
+ ItemViewHolder itemViewHolder = new ItemViewHolder(view);
+ return itemViewHolder;
+ }
+
+ @Override
+ public void onBindViewHolder(final ItemViewHolder holder, int position) {
+ holder.checkListTextView.setText(mItems.get(position).getText());
+ }
+
+ public void addItem(ChecklistItem item){
+ mItems.add(item);
+ notifyItemInserted(mItems.size() - 1);
+ }
+
+ public List getCheckListItems(){
+ return mItems;
+ }
+
+ @Override
+ public int getItemCount() {
+ return mItems.size();
+ }
+
+
+ @Override
+ public void onItemDismiss(int position) {
+ mItems.remove(position);
+ notifyItemRemoved(position);
+ }
+
+
+ @Override
+ public void onItemMove(int fromPosition, int toPosition) {
+ Collections.swap(mItems, fromPosition, toPosition);
+ notifyItemMoved(fromPosition, toPosition);
+ }
+
+ public class ItemViewHolder extends RecyclerView.ViewHolder implements
+ ItemTouchHelperViewHolder, Button.OnClickListener {
+
+ @InjectView(R.id.item_edittext)
+ EditText checkListTextView;
+
+ @InjectView(R.id.delete_item_button)
+ Button deleteButton;
+
+ public ItemViewHolder(View itemView) {
+ super(itemView);
+ ButterKnife.inject(this, itemView);
+ deleteButton.setOnClickListener(this);
+
+ checkListTextView.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ mItems.get(getAdapterPosition()).setText(checkListTextView.getText().toString());
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ }
+ });
+ }
+
+ @Override
+ public void onItemSelected() {
+ itemView.setBackgroundColor(Color.LTGRAY);
+ }
+
+ @Override
+ public void onItemClear() {
+ itemView.setBackgroundColor(0);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v == deleteButton) {
+ CheckListAdapter.this.onItemDismiss(getAdapterPosition());
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Habitica/src/com/habitrpg/android/habitica/ui/adapter/HabitItemRecyclerViewAdapter.java b/Habitica/src/com/habitrpg/android/habitica/ui/adapter/HabitItemRecyclerViewAdapter.java
index a3bc1a8b9..6e76c20d3 100644
--- a/Habitica/src/com/habitrpg/android/habitica/ui/adapter/HabitItemRecyclerViewAdapter.java
+++ b/Habitica/src/com/habitrpg/android/habitica/ui/adapter/HabitItemRecyclerViewAdapter.java
@@ -138,7 +138,9 @@ public class HabitItemRecyclerViewAdapter
break;
}
}
- observableContent.set(i, task);
+ if (i < observableContent.size()) {
+ observableContent.set(i, task);
+ }
}
private void filter() {
@@ -274,13 +276,11 @@ public class HabitItemRecyclerViewAdapter
@Override
public boolean onLongClick(View v) {
TaskLongPressedEvent event = new TaskLongPressedEvent();
- event.Task = Item;
+ event.task = Item;
EventBus.getDefault().post(event);
-
return true;
}
-
}
public class HabitViewHolder extends ViewHolder {
@@ -371,6 +371,7 @@ public class HabitItemRecyclerViewAdapter
isClickable = true;
}
checklistIndicatorWrapper.setClickable(isClickable);
+ displayChecklist = false;
}
public void setDisplayChecklist(Boolean displayChecklist) {
@@ -382,11 +383,11 @@ public class HabitItemRecyclerViewAdapter
for (ChecklistItem item : this.Item.checklist) {
LinearLayout itemView = (LinearLayout) layoutInflater.inflate(R.layout.checklist_item_row, null);
CheckBox checkbox = (CheckBox) itemView.findViewById(R.id.checkBox);
- checkbox.setOnCheckedChangeListener(this);
TextView textView = (TextView) itemView.findViewById(R.id.checkedTextView);
// Populate the data into the template view using the data object
textView.setText(item.getText());
checkbox.setChecked(item.getCompleted());
+ checkbox.setOnCheckedChangeListener(this);
this.checklistView.addView(itemView);
}
} else {
@@ -411,7 +412,11 @@ public class HabitItemRecyclerViewAdapter
}
} else {
- Integer position = (Integer) ((ViewGroup) checkbox.getParent().getParent()).indexOfChild((View) checkbox.getParent());
+ View v = (View) buttonView.getParent();
+ while (v.getParent() != this.checklistView) {
+ v = (View) v.getParent();
+ }
+ Integer position = (Integer) ((ViewGroup) v.getParent()).indexOfChild(v);
if (isChecked != Item.checklist.get(position).getCompleted()) {
TaskSaveEvent event = new TaskSaveEvent();
Item.checklist.get(position).setCompleted(isChecked);
diff --git a/Habitica/src/com/habitrpg/android/habitica/ui/helpers/ItemTouchHelperAdapter.java b/Habitica/src/com/habitrpg/android/habitica/ui/helpers/ItemTouchHelperAdapter.java
new file mode 100644
index 000000000..a2f73eb50
--- /dev/null
+++ b/Habitica/src/com/habitrpg/android/habitica/ui/helpers/ItemTouchHelperAdapter.java
@@ -0,0 +1,33 @@
+package com.habitrpg.android.habitica.ui.helpers;
+
+import android.support.v7.widget.RecyclerView;
+
+/**
+ * Interface to notify a {@link RecyclerView.Adapter} of moving and dismissal event from a {@link
+ * android.support.v7.widget.helper.ItemTouchHelper.Callback}.
+ *
+ * @author Paul Burke (ipaulpro)
+ */
+public interface ItemTouchHelperAdapter {
+
+ /**
+ * Called when an item has been dragged far enough to trigger a move. This is called every time
+ * an item is shifted, and not at the end of a "drop" event.
+ *
+ * @param fromPosition The start position of the moved item.
+ * @param toPosition Then end position of the moved item.
+ * @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder)
+ * @see RecyclerView.ViewHolder#getAdapterPosition()
+ */
+ void onItemMove(int fromPosition, int toPosition);
+
+
+ /**
+ * Called when an item has been dismissed by a swipe.
+ *
+ * @param position The position of the item dismissed.
+ * @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder)
+ * @see RecyclerView.ViewHolder#getAdapterPosition()
+ */
+ void onItemDismiss(int position);
+}
diff --git a/Habitica/src/com/habitrpg/android/habitica/ui/helpers/ItemTouchHelperViewHolder.java b/Habitica/src/com/habitrpg/android/habitica/ui/helpers/ItemTouchHelperViewHolder.java
new file mode 100644
index 000000000..4b91a123d
--- /dev/null
+++ b/Habitica/src/com/habitrpg/android/habitica/ui/helpers/ItemTouchHelperViewHolder.java
@@ -0,0 +1,25 @@
+package com.habitrpg.android.habitica.ui.helpers;
+
+import android.support.v7.widget.helper.ItemTouchHelper;
+
+/**
+ * Interface to notify an item ViewHolder of relevant callbacks from {@link
+ * android.support.v7.widget.helper.ItemTouchHelper.Callback}.
+ *
+ * @author Paul Burke (ipaulpro)
+ */
+public interface ItemTouchHelperViewHolder {
+
+ /**
+ * Called when the {@link ItemTouchHelper} first registers an item as being moved or swiped.
+ * Implementations should update the item view to indicate it's active state.
+ */
+ void onItemSelected();
+
+
+ /**
+ * Called when the {@link ItemTouchHelper} has completed the move or swipe, and the active item
+ * state should be cleared.
+ */
+ void onItemClear();
+}
diff --git a/Habitica/src/com/habitrpg/android/habitica/ui/helpers/SimpleItemTouchHelperCallback.java b/Habitica/src/com/habitrpg/android/habitica/ui/helpers/SimpleItemTouchHelperCallback.java
new file mode 100644
index 000000000..afecfe2e6
--- /dev/null
+++ b/Habitica/src/com/habitrpg/android/habitica/ui/helpers/SimpleItemTouchHelperCallback.java
@@ -0,0 +1,69 @@
+package com.habitrpg.android.habitica.ui.helpers;
+
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
+
+/**
+ * An implementation of {@link ItemTouchHelper.Callback} that enables basic drag & drop and
+ * swipe-to-dismiss. Drag events are automatically started by an item long-press.
+ *
+ * Expects the RecyclerView.Adapter to react to {@link
+ * ItemTouchHelperAdapter} callbacks and the RecyclerView.ViewHolder to implement
+ * {@link ItemTouchHelperViewHolder}.
+ *
+ * @author Paul Burke (ipaulpro)
+ */
+public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
+
+ private final ItemTouchHelperAdapter mAdapter;
+
+ public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {
+ mAdapter = adapter;
+ }
+
+ @Override
+ public boolean isLongPressDragEnabled() {
+ return true;
+ }
+
+ @Override
+ public boolean isItemViewSwipeEnabled() {
+ return false;
+ }
+
+ @Override
+ public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
+ final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
+ final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
+ return makeMovementFlags(dragFlags, swipeFlags);
+ }
+
+ @Override
+ public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
+ mAdapter.onItemMove(source.getAdapterPosition(), target.getAdapterPosition());
+ return true;
+ }
+
+ @Override
+ public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) {
+ mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
+ }
+
+ @Override
+ public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
+ if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
+ ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
+ itemViewHolder.onItemSelected();
+ }
+
+ super.onSelectedChanged(viewHolder, actionState);
+ }
+
+ @Override
+ public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
+ super.clearView(recyclerView, viewHolder);
+
+ ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
+ itemViewHolder.onItemClear();
+ }
+}
diff --git a/Habitica/src/com/magicmicky/habitrpgwrapper/lib/models/tasks/ChecklistItem.java b/Habitica/src/com/magicmicky/habitrpgwrapper/lib/models/tasks/ChecklistItem.java
index f3762c21a..1074dab9f 100644
--- a/Habitica/src/com/magicmicky/habitrpgwrapper/lib/models/tasks/ChecklistItem.java
+++ b/Habitica/src/com/magicmicky/habitrpgwrapper/lib/models/tasks/ChecklistItem.java
@@ -1,6 +1,7 @@
package com.magicmicky.habitrpgwrapper.lib.models.tasks;
import com.habitrpg.android.habitica.HabitDatabase;
+import com.habitrpg.android.habitica.database.ExcludeCheckListItem;
import com.raizlabs.android.dbflow.annotation.Column;
import com.raizlabs.android.dbflow.annotation.ForeignKey;
import com.raizlabs.android.dbflow.annotation.ForeignKeyReference;
@@ -31,13 +32,14 @@ public class ChecklistItem extends BaseModel {
columnType = String.class,
foreignColumnName = "id")},
saveForeignKeyModel = false)
+ @ExcludeCheckListItem
ForeignKeyContainer task;
public ChecklistItem() {
this(null,null);
}
public ChecklistItem(String id, String text) {
- this(id,text,false);
+ this(id, text, false);
}
public ChecklistItem(String id,String text, boolean completed) {
this.setText(text);
@@ -72,7 +74,11 @@ public class ChecklistItem extends BaseModel {
}
public Task getTask() {
- return task.toModel();
+ if (task != null) {
+ return task.toModel();
+ } else {
+ return null;
+ }
}
public void setTask(Task task) {
@@ -80,4 +86,12 @@ public class ChecklistItem extends BaseModel {
this.task.setModel(task);
this.task.put("id", task.id);
}
+
+ @Override
+ public void save() {
+ if (this.getId() == null || this.getId().isEmpty()) {
+ return;
+ }
+ super.save();
+ }
}
diff --git a/Habitica/src/com/magicmicky/habitrpgwrapper/lib/models/tasks/Task.java b/Habitica/src/com/magicmicky/habitrpgwrapper/lib/models/tasks/Task.java
index ddb177f7d..1eda7ec2d 100644
--- a/Habitica/src/com/magicmicky/habitrpgwrapper/lib/models/tasks/Task.java
+++ b/Habitica/src/com/magicmicky/habitrpgwrapper/lib/models/tasks/Task.java
@@ -374,7 +374,9 @@ public class Task extends BaseModel {
}
if (this.checklist != null) {
for (ChecklistItem item : this.checklist) {
- item.setTask(this);
+ if(item.getTask() == null){
+ item.setTask(this);
+ }
item.async().save();
}
}