Merge pull request #53 from HabitRPG/ability-change-checklist

Fixes #40 - Add ability to change checklist
This commit is contained in:
negue 2015-12-01 01:23:12 +01:00
commit d0d7cf8e8d
19 changed files with 659 additions and 34 deletions

View file

@ -68,9 +68,9 @@
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="72dp"
android:layout_height="140dp"
android:layout_gravity="center_horizontal"
android:id="@+id/task_startdate_layout">
@ -82,12 +82,12 @@
android:id="@+id/textView"
android:layout_gravity="center_vertical" />
<TextView
android:layout_width="match_parent"
<DatePicker
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/task_startdate_textview"
android:textAlignment="gravity"
android:layout_gravity="center_vertical|right" />
android:id="@+id/task_startdate_picker"
android:datePickerMode="spinner"
android:calendarViewShown="false" />
</LinearLayout>
</LinearLayout>
@ -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"/>
<CheckBox
android:layout_width="wrap_content"
@ -144,7 +145,8 @@
android:id="@+id/textView8"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginTop="11dp" />
android:layout_marginTop="11dp"
android:layout_toLeftOf="@+id/task_negative_checkbox"/>
<CheckBox
android:layout_width="wrap_content"
@ -170,7 +172,33 @@
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="@string/checklist"
android:id="@+id/textView9" />
android:id="@+id/textView9"
android:layout_marginBottom="8dp"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/checklist_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/new_checklist"
android:hint="@string/new_checklist_item"
android:layout_weight="1" />
<Button
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_checklist_item"
android:id="@+id/add_checklist_button" />
</LinearLayout>
</LinearLayout>
@ -203,6 +231,5 @@
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:foreground="?selectableItemBackground"
android:orientation="horizontal"
android:paddingTop="4dp"
android:paddingBottom="4dp">
<Button
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/delete_item_button"
android:text="x" />
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/item_edittext"
android:layout_weight="1"
android:layout_marginLeft="4dp" />
</LinearLayout>

View file

@ -183,4 +183,6 @@
<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>
<string name="new_checklist_item">new checklist item</string>
<string name="add_checklist_item">Add</string>
</resources>

View file

@ -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) {

View file

@ -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;

View file

@ -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;

View file

@ -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<String> 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<List<Tag>> tagsSearchingListener = new TransactionListener<List<Tag>>() {

View file

@ -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;

View file

@ -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;
}
}

View file

@ -0,0 +1,8 @@
package com.habitrpg.android.habitica.database;
/**
* Created by franzejr on 29/11/15.
*/
public @interface ExcludeCheckListItem {
}

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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<CheckListAdapter.ItemViewHolder>
implements ItemTouchHelperAdapter{
private final List<ChecklistItem> mItems = new ArrayList<>();
public CheckListAdapter(List<ChecklistItem> 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<ChecklistItem> 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());
}
}
}
}

View file

@ -138,7 +138,9 @@ public class HabitItemRecyclerViewAdapter<THabitItem extends Task>
break;
}
}
observableContent.set(i, task);
if (i < observableContent.size()) {
observableContent.set(i, task);
}
}
private void filter() {
@ -274,13 +276,11 @@ public class HabitItemRecyclerViewAdapter<THabitItem extends Task>
@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<Task> {
@ -371,6 +371,7 @@ public class HabitItemRecyclerViewAdapter<THabitItem extends Task>
isClickable = true;
}
checklistIndicatorWrapper.setClickable(isClickable);
displayChecklist = false;
}
public void setDisplayChecklist(Boolean displayChecklist) {
@ -382,11 +383,11 @@ public class HabitItemRecyclerViewAdapter<THabitItem extends Task>
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<THabitItem extends Task>
}
} 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);

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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.<br/>
* </br/>
* Expects the <code>RecyclerView.Adapter</code> to react to {@link
* ItemTouchHelperAdapter} callbacks and the <code>RecyclerView.ViewHolder</code> 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();
}
}

View file

@ -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> 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();
}
}

View file

@ -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();
}
}