From 3cc74231c8e375845a291c2618e53ae49dbd945f Mon Sep 17 00:00:00 2001 From: Gleb Maliborsky Date: Mon, 25 Apr 2016 17:31:31 +0300 Subject: [PATCH 1/7] //Add drag & drop to SetListFragment --- .idea/.name | 2 +- .idea/gradle.xml | 7 +- .idea/misc.xml | 29 +----- .idea/modules.xml | 2 +- .idea/vcs.xml | 2 +- .../hikapro/com/backpack/MainActivity.java | 1 + .../hikapro/com/backpack/model/Model.java | 3 + .../hikapro/com/backpack/model/SetModel.java | 10 +- .../com/backpack/presenter/Presenter.java | 8 ++ .../backpack/presenter/SetListPresenter.java | 53 ++++++++++- .../presenter/adapters/SetListAdapter.java | 15 ++- .../helper/ItemTouchHelperAdapter.java | 12 +++ .../helper/ItemTouchHelperViewHolder.java | 12 +++ .../adapters/helper/OnStartDragListener.java | 12 +++ .../helper/SimpleItemTouchHelperCallback.java | 93 +++++++++++++++++++ .../java/hikapro/com/backpack/view/View.java | 4 + .../view/fragments/SetListFragment.java | 21 ++++- .../backpack/view/recycler/SetViewHolder.java | 28 +++++- 18 files changed, 266 insertions(+), 48 deletions(-) create mode 100644 app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/ItemTouchHelperAdapter.java create mode 100644 app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/ItemTouchHelperViewHolder.java create mode 100644 app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/OnStartDragListener.java create mode 100644 app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/SimpleItemTouchHelperCallback.java diff --git a/.idea/.name b/.idea/.name index 8225020..645af55 100644 --- a/.idea/.name +++ b/.idea/.name @@ -1 +1 @@ -BackPack \ No newline at end of file +packwithme \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 508b3d9..8d2df47 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -5,18 +5,13 @@ - diff --git a/.idea/misc.xml b/.idea/misc.xml index 477b86a..5d19981 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -37,37 +37,10 @@ - + - - - - - 1.8 - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index ea59753..53118bf 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,8 +2,8 @@ - + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..6564d52 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/app/src/main/java/hikapro/com/backpack/MainActivity.java b/app/src/main/java/hikapro/com/backpack/MainActivity.java index 2d27772..879fdfc 100644 --- a/app/src/main/java/hikapro/com/backpack/MainActivity.java +++ b/app/src/main/java/hikapro/com/backpack/MainActivity.java @@ -15,6 +15,7 @@ import hikapro.com.backpack.model.entities.Set; import hikapro.com.backpack.presenter.ItemDetailPresenter; import hikapro.com.backpack.presenter.ItemListPresenter; import hikapro.com.backpack.presenter.SetListPresenter; +import hikapro.com.backpack.presenter.adapters.helper.OnStartDragListener; import hikapro.com.backpack.view.View; import hikapro.com.backpack.view.fragments.ItemDetailFragment; import hikapro.com.backpack.view.fragments.ItemListFragment; diff --git a/app/src/main/java/hikapro/com/backpack/model/Model.java b/app/src/main/java/hikapro/com/backpack/model/Model.java index 0f89f31..2fa9305 100644 --- a/app/src/main/java/hikapro/com/backpack/model/Model.java +++ b/app/src/main/java/hikapro/com/backpack/model/Model.java @@ -25,6 +25,9 @@ public interface Model { void notifyDataSetChanged(); void setPresenter(Presenter.SetList presenter); Presenter.SetList getPresenter(); + + //GLM + List getSet(); } interface Item extends Base { diff --git a/app/src/main/java/hikapro/com/backpack/model/SetModel.java b/app/src/main/java/hikapro/com/backpack/model/SetModel.java index e796815..0e1365d 100644 --- a/app/src/main/java/hikapro/com/backpack/model/SetModel.java +++ b/app/src/main/java/hikapro/com/backpack/model/SetModel.java @@ -114,9 +114,11 @@ public class SetModel implements Model.Set { // other <-- + //GLM - - - - + @Override + public List getSet() + { + return iList; + } } diff --git a/app/src/main/java/hikapro/com/backpack/presenter/Presenter.java b/app/src/main/java/hikapro/com/backpack/presenter/Presenter.java index f7d927a..277232e 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/Presenter.java +++ b/app/src/main/java/hikapro/com/backpack/presenter/Presenter.java @@ -2,7 +2,10 @@ package hikapro.com.backpack.presenter; import android.content.Context; import android.os.Bundle; +import android.support.v7.widget.RecyclerView; +import android.view.DragEvent; import android.view.LayoutInflater; +import android.view.View; import android.view.ViewGroup; import hikapro.com.backpack.model.Model; @@ -34,6 +37,11 @@ public interface Presenter { void setModel(Model.Set model); void notifyDataSetChanged(); void showMessage(String message); + + //GLM_add_resources_SetList + void onItemDismiss(int position); + boolean onItemMove(int fromPosition, int toPosition); + void onStartDrag(RecyclerView.ViewHolder viewHolder); } interface ItemList extends Base { diff --git a/app/src/main/java/hikapro/com/backpack/presenter/SetListPresenter.java b/app/src/main/java/hikapro/com/backpack/presenter/SetListPresenter.java index 11cd528..3b61acd 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/SetListPresenter.java +++ b/app/src/main/java/hikapro/com/backpack/presenter/SetListPresenter.java @@ -2,21 +2,29 @@ package hikapro.com.backpack.presenter; import android.content.Context; import android.os.Bundle; +import android.support.v4.view.MotionEventCompat; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; +import android.view.DragEvent; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.ViewGroup; import android.widget.Toast; import java.lang.ref.WeakReference; +import java.util.Collections; import hikapro.com.backpack.R; import hikapro.com.backpack.model.SetModel; import hikapro.com.backpack.model.Model; import hikapro.com.backpack.model.entities.Set; import hikapro.com.backpack.presenter.adapters.SetListAdapter; +import hikapro.com.backpack.presenter.adapters.helper.OnStartDragListener; +import hikapro.com.backpack.presenter.adapters.helper.SimpleItemTouchHelperCallback; import hikapro.com.backpack.view.View; +import hikapro.com.backpack.view.fragments.SetListFragment; import hikapro.com.backpack.view.recycler.SetViewHolder; /** @@ -28,12 +36,33 @@ public class SetListPresenter implements Presenter.SetList { private Model.Set model; private SetListAdapter adapter; + + //GLM_add_resources_SetList + private OnStartDragListener mDragStartListener; + private ItemTouchHelper mItemTouchHelper; + public SetListPresenter() { this.adapter = new SetListAdapter(this); } // life cycle --> + + //GLM_add_resources_SetList + @Override + public void onItemDismiss(int position) { + model.getSet().remove(position); + adapter.notifyItemRemoved(position); + } + + //GLM_add_resources_SetList + @Override + public boolean onItemMove(int fromPosition, int toPosition) { + Collections.swap(model.getSet(), fromPosition, toPosition); + adapter.notifyItemMoved(fromPosition, toPosition); + return true; + } + @Override public void onDestroy(boolean isChangingConfiguration) { view = null; @@ -52,6 +81,12 @@ public class SetListPresenter implements Presenter.SetList { setRecycler.setAdapter(adapter); setRecycler.setItemAnimator(new DefaultItemAnimator()); model.executeQuery(); + + //GLM_add_resources_SetList + ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(adapter); + mItemTouchHelper = new ItemTouchHelper(callback); + mItemTouchHelper.attachToRecyclerView(setRecycler); + return view; } @@ -81,6 +116,17 @@ public class SetListPresenter implements Presenter.SetList { showMessage("There is no view in presenter"); } }); + + + //GLM_add_resources_SetList + holder.cardView.setOnLongClickListener(new android.view.View.OnLongClickListener() { + @Override + public boolean onLongClick(android.view.View v) { + + mDragStartListener.onStartDrag(holder); + return false; + } + }); } @Override public int getSetsCount() { @@ -128,6 +174,7 @@ public class SetListPresenter implements Presenter.SetList { @Override public void setView(View.SetList view) { this.view = new WeakReference<>(view); + this.mDragStartListener = getView().getOnStartDragListener(); } @Override public void setModel(Model.Set model) { @@ -147,5 +194,9 @@ public class SetListPresenter implements Presenter.SetList { throw new NullPointerException("View is unavailable"); } - + //GLM_add_resources_SetList + @Override + public void onStartDrag(RecyclerView.ViewHolder viewHolder) { + mItemTouchHelper.startDrag(viewHolder); + } } diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/SetListAdapter.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/SetListAdapter.java index 15034fb..42fe656 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/SetListAdapter.java +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/SetListAdapter.java @@ -4,12 +4,13 @@ import android.support.v7.widget.RecyclerView; import android.view.ViewGroup; import hikapro.com.backpack.presenter.SetListPresenter; +import hikapro.com.backpack.presenter.adapters.helper.ItemTouchHelperAdapter; import hikapro.com.backpack.view.recycler.SetViewHolder; /** * Created by tariel on 20/04/16. */ -public class SetListAdapter extends RecyclerView.Adapter { +public class SetListAdapter extends RecyclerView.Adapter implements ItemTouchHelperAdapter{ private SetListPresenter presenter; @@ -31,4 +32,16 @@ public class SetListAdapter extends RecyclerView.Adapter { public int getItemCount() { return presenter.getSetsCount(); } + + //GLM_add_resources_SetList + @Override + public void onItemDismiss(int position) { + presenter.onItemDismiss(position); + } + + //GLM_add_resources_SetList + @Override + public boolean onItemMove(int fromPosition, int toPosition) { + return presenter.onItemMove(fromPosition, toPosition); + } } diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/ItemTouchHelperAdapter.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/ItemTouchHelperAdapter.java new file mode 100644 index 0000000..bce3ead --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/ItemTouchHelperAdapter.java @@ -0,0 +1,12 @@ +package hikapro.com.backpack.presenter.adapters.helper; + +/** + * Created by N551 on 25.04.2016. + */ +//GLM_add_resources_SetList +public interface ItemTouchHelperAdapter { + + boolean onItemMove(int fromPosition, int toPosition); + + void onItemDismiss(int position); +} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/ItemTouchHelperViewHolder.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/ItemTouchHelperViewHolder.java new file mode 100644 index 0000000..0060002 --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/ItemTouchHelperViewHolder.java @@ -0,0 +1,12 @@ +package hikapro.com.backpack.presenter.adapters.helper; + +/** + * Created by N551 on 25.04.2016. + */ +//GLM_add_resources_SetList +public interface ItemTouchHelperViewHolder { + + void onItemSelected(); + + void onItemClear(); +} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/OnStartDragListener.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/OnStartDragListener.java new file mode 100644 index 0000000..aca2fbe --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/OnStartDragListener.java @@ -0,0 +1,12 @@ +package hikapro.com.backpack.presenter.adapters.helper; + +import android.support.v7.widget.RecyclerView; + +/** + * Created by N551 on 25.04.2016. + */ +//GLM_add_resources_SetList +public interface OnStartDragListener { + + void onStartDrag(RecyclerView.ViewHolder viewHolder); +} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/SimpleItemTouchHelperCallback.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/SimpleItemTouchHelperCallback.java new file mode 100644 index 0000000..c8774b3 --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/SimpleItemTouchHelperCallback.java @@ -0,0 +1,93 @@ +package hikapro.com.backpack.presenter.adapters.helper; + +import android.graphics.Canvas; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; + +/** + * Created by N551 on 25.04.2016. + */ +//GLM_add_resources_SetList +public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback { + public static final float ALPHA_FULL = 1.0f; + + 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) { + if (source.getItemViewType() != target.getItemViewType()) { + return false; + } + + // Notify the adapter of the move + mAdapter.onItemMove(source.getAdapterPosition(), target.getAdapterPosition()); + return true; + } + + @Override + public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) { + // Notify the adapter of the dismissal + mAdapter.onItemDismiss(viewHolder.getAdapterPosition()); + } + + @Override + public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { + if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { + // Fade out the view as it is swiped out of the parent's bounds + final float alpha = ALPHA_FULL - Math.abs(dX) / (float) viewHolder.itemView.getWidth(); + viewHolder.itemView.setAlpha(alpha); + viewHolder.itemView.setTranslationX(dX); + } else { + super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); + } + } + + @Override + public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { + // We only want the active item to change + if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) { + if (viewHolder instanceof ItemTouchHelperViewHolder) { + // Let the view holder know that this item is being moved or dragged + ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder; + itemViewHolder.onItemSelected(); + } + } + + super.onSelectedChanged(viewHolder, actionState); + } + + @Override + public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { + super.clearView(recyclerView, viewHolder); + + viewHolder.itemView.setAlpha(ALPHA_FULL); + + if (viewHolder instanceof ItemTouchHelperViewHolder) { + // Tell the view holder it's time to restore the idle state + ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder; + itemViewHolder.onItemClear(); + } + } +} diff --git a/app/src/main/java/hikapro/com/backpack/view/View.java b/app/src/main/java/hikapro/com/backpack/view/View.java index 547ce9f..9e02aba 100644 --- a/app/src/main/java/hikapro/com/backpack/view/View.java +++ b/app/src/main/java/hikapro/com/backpack/view/View.java @@ -6,6 +6,7 @@ import android.widget.Toast; import hikapro.com.backpack.model.entities.Item; import hikapro.com.backpack.model.entities.Set; import hikapro.com.backpack.presenter.Presenter; +import hikapro.com.backpack.presenter.adapters.helper.OnStartDragListener; /** * Created by tariel on 19/04/16. @@ -19,6 +20,9 @@ public interface View { interface SetList extends Base { void showItemList(Set set); void setPresenter(Presenter.SetList presenter); + + //GLM_add_resources_SetList + OnStartDragListener getOnStartDragListener(); } interface ItemList extends Base { diff --git a/app/src/main/java/hikapro/com/backpack/view/fragments/SetListFragment.java b/app/src/main/java/hikapro/com/backpack/view/fragments/SetListFragment.java index 79c88b6..d2b4985 100644 --- a/app/src/main/java/hikapro/com/backpack/view/fragments/SetListFragment.java +++ b/app/src/main/java/hikapro/com/backpack/view/fragments/SetListFragment.java @@ -5,18 +5,18 @@ import android.app.Activity; import android.app.Fragment; import android.content.Context; import android.os.Bundle; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.Toast; -import hikapro.com.backpack.R; import hikapro.com.backpack.model.entities.Set; import hikapro.com.backpack.presenter.Presenter; -import hikapro.com.backpack.presenter.SetListPresenter; +import hikapro.com.backpack.presenter.adapters.helper.OnStartDragListener; -public class SetListFragment extends Fragment implements hikapro.com.backpack.view.View.SetList { +public class SetListFragment extends Fragment implements hikapro.com.backpack.view.View.SetList, OnStartDragListener{ private Presenter.SetList presenter; private hikapro.com.backpack.view.View.ActivityCallback activityCallback; @@ -130,4 +130,17 @@ public class SetListFragment extends Fragment implements hikapro.com.backpack.vi public void setPresenter(Presenter.SetList presenter) { this.presenter = presenter; } + + + //GLM_add_resources_SetList + @Override + public void onStartDrag(RecyclerView.ViewHolder viewHolder) { + presenter.onStartDrag(viewHolder); + } + + //GLM_add_resources_SetList + @Override + public OnStartDragListener getOnStartDragListener() { + return this; + } } diff --git a/app/src/main/java/hikapro/com/backpack/view/recycler/SetViewHolder.java b/app/src/main/java/hikapro/com/backpack/view/recycler/SetViewHolder.java index c51778c..e57463f 100644 --- a/app/src/main/java/hikapro/com/backpack/view/recycler/SetViewHolder.java +++ b/app/src/main/java/hikapro/com/backpack/view/recycler/SetViewHolder.java @@ -1,27 +1,53 @@ package hikapro.com.backpack.view.recycler; +import android.graphics.Color; +import android.graphics.drawable.Drawable; import android.support.v7.widget.CardView; import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.TextView; import hikapro.com.backpack.R; +import hikapro.com.backpack.presenter.adapters.helper.ItemTouchHelperViewHolder; /** * Created by tariel on 20/04/16. */ -public class SetViewHolder extends RecyclerView.ViewHolder { +public class SetViewHolder extends RecyclerView.ViewHolder implements ItemTouchHelperViewHolder{ public CardView cardView; public TextView textView; + //GLM_add_resources_SetList + private View view; + private Drawable drawable; + public SetViewHolder(View v) { super(v); + // + this.view = v; + + // setupViews(v); } private void setupViews(View view) { cardView = (CardView) view.findViewById(R.id.card_view_set); textView = (TextView) view.findViewById(R.id.set_text); + + this.view = view; + drawable = view.getBackground(); + } + + //GLM_add_resources_SetList + @Override + public void onItemClear() { + view.setBackground(drawable); + } + + //GLM_add_resources_SetList + @Override + public void onItemSelected() { + view.setBackgroundColor(Color.GRAY); } } From b76ffeabd9e4f975f511fd44ead67f3e77e41e3b Mon Sep 17 00:00:00 2001 From: Tariel Hlontsi Date: Fri, 29 Apr 2016 12:45:57 +0300 Subject: [PATCH 2/7] Basic DAO implementation --- app/src/main/AndroidManifest.xml | 1 + .../main/java/hikapro/com/backpack/App.java | 22 + .../hikapro/com/backpack/MainActivity.java | 2 + .../java/hikapro/com/backpack/model/Api.java | 17 +- .../com/backpack/model/DetailModel.java | 7 + .../hikapro/com/backpack/model/ItemModel.java | 87 ++- .../hikapro/com/backpack/model/Model.java | 7 +- .../com/backpack/model/RestClient.java | 2 +- .../hikapro/com/backpack/model/SetModel.java | 85 +- .../com/backpack/model/database/Command.java | 32 + .../com/backpack/model/database/DAO.java | 725 +++++++++++++++++- .../com/backpack/model/database/Db.java | 108 ++- .../com/backpack/model/database/DbHelper.java | 2 + .../com/backpack/model/database/Event.java | 51 ++ .../com/backpack/model/database/Test.java | 15 + .../com/backpack/model/entities/Set.java | 18 +- .../backpack/model/entities/Timestamp.java | 20 + .../backpack/model/entities/UpdateLog.java | 29 + .../com/backpack/model/entities/Updates.java | 41 + 19 files changed, 1159 insertions(+), 112 deletions(-) create mode 100644 app/src/main/java/hikapro/com/backpack/App.java create mode 100644 app/src/main/java/hikapro/com/backpack/model/database/Command.java create mode 100644 app/src/main/java/hikapro/com/backpack/model/database/Event.java create mode 100644 app/src/main/java/hikapro/com/backpack/model/database/Test.java create mode 100644 app/src/main/java/hikapro/com/backpack/model/entities/Timestamp.java create mode 100644 app/src/main/java/hikapro/com/backpack/model/entities/UpdateLog.java create mode 100644 app/src/main/java/hikapro/com/backpack/model/entities/Updates.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ee72e5f..0a8ca4c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ > getItems(); - @GET("/api/v1/backpack/item_categories") + @GET("api/v1/backpack/item_categories") Call> getItemCategories(); - @GET("/api/v1/backpack/sets") + @GET("api/v1/backpack/sets") Call> getSets(); + + @GET("api/v1/backpack/updates/timestamp") + Call getTimestamp(); + + @GET("api/v1/backpack/updates/all") + Call getUpdates(@Query("timestamp") long timestamp); + + } diff --git a/app/src/main/java/hikapro/com/backpack/model/DetailModel.java b/app/src/main/java/hikapro/com/backpack/model/DetailModel.java index 0abe09f..d256ee2 100644 --- a/app/src/main/java/hikapro/com/backpack/model/DetailModel.java +++ b/app/src/main/java/hikapro/com/backpack/model/DetailModel.java @@ -1,5 +1,7 @@ package hikapro.com.backpack.model; +import android.os.Message; + import hikapro.com.backpack.model.entities.Item; import hikapro.com.backpack.presenter.Presenter; @@ -66,4 +68,9 @@ public class DetailModel implements Model.Detail { public Presenter.ItemDetail getPresenter() { return presenter; } + + @Override + public void onEvent(Message event) { + + } } diff --git a/app/src/main/java/hikapro/com/backpack/model/ItemModel.java b/app/src/main/java/hikapro/com/backpack/model/ItemModel.java index 2bff2bc..e13f99d 100644 --- a/app/src/main/java/hikapro/com/backpack/model/ItemModel.java +++ b/app/src/main/java/hikapro/com/backpack/model/ItemModel.java @@ -1,14 +1,20 @@ package hikapro.com.backpack.model; +import android.os.Message; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Hashtable; import java.util.List; +import hikapro.com.backpack.model.database.Command; +import hikapro.com.backpack.model.database.DAO; +import hikapro.com.backpack.model.database.Event; import hikapro.com.backpack.model.entities.Category; import hikapro.com.backpack.model.entities.Item; +import hikapro.com.backpack.model.entities.Set; import hikapro.com.backpack.presenter.Presenter; import retrofit2.Call; import retrofit2.Callback; @@ -19,19 +25,21 @@ import retrofit2.Response; */ public class ItemModel implements Model.Item { - private Api api; private Presenter.ItemList presenter; private List rawCategories; private List sortedCategories; private List rawItems; + private DAO dao; private Hashtable> items; + private Hashtable>> cache; public ItemModel() { - this.api = RestClient.getApi(); this.rawCategories = new ArrayList<>(); this.rawItems = new ArrayList<>(); this.sortedCategories = new ArrayList<>(); + this.cache = new Hashtable<>(16, 0.9f); + this.dao = DAO.getInstance(); } // categories --> @@ -111,46 +119,45 @@ public class ItemModel implements Model.Item { @Override public void executeQuery() { - if (rawCategories.isEmpty() || rawItems.isEmpty()) { - loadCategories(); - loadItems(); - } else { - notifyDataSetChanged(); + Message command = Message.obtain(); + command.what = Command.SET_GET_ITEMS; + command.arg1 = presenter.getCurrentSet().getId(); + dao.executeCommand(command); + } + @Override + public void onEvent(Message event) { + + switch (event.what) { + case Event.SET_ITEMS_LOAD_ERROR : + break; + case Event.ITEM_FROM_SET_ERROR : + break; + case Event.ITEM_DELETE_ERROR : + break; + case Event.ITEM_PACK_ERROR : + break; + case Event.ITEM_UNPACK_ERROR : + break; + case Event.ITEM_INSERT_ERROR : + break; + case Event.SET_ITEMS_LOAD_COMPLETED : + Hashtable> res = (Hashtable>) event.obj; + cache.put(event.arg1, res); + notifyDataSetChanged(); + break; + case Event.ITEM_FROM_SET_DELETED : + break; + case Event.ITEM_DELETED : + break; + case Event.ITEM_PACKED : + break; + case Event.ITEM_UNPACKED : + break; + case Event.ITEM_INSERTED : + break; } + } - } - private void loadCategories() { - Call> call = api.getItemCategories(); - call.enqueue(new Callback>() { - @Override - public void onResponse(Call> call, Response> response) { - int statusCode = response.code(); - rawCategories = response.body(); - } - @Override - public void onFailure(Call> call, Throwable t) { - - } - }); - } - private void loadItems() { - Call> call2 = api.getItems(); - call2.enqueue(new Callback>() { - @Override - public void onResponse(Call> call, Response> response) { - int statusCode = response.code(); - rawItems = response.body(); - initData(); - } - @Override - public void onFailure(Call> call, Throwable t) { - - } - }); - } - private void syncData() { - // TODO sync data here - } // process <-- // other --> diff --git a/app/src/main/java/hikapro/com/backpack/model/Model.java b/app/src/main/java/hikapro/com/backpack/model/Model.java index 0f89f31..6e06da9 100644 --- a/app/src/main/java/hikapro/com/backpack/model/Model.java +++ b/app/src/main/java/hikapro/com/backpack/model/Model.java @@ -1,5 +1,7 @@ package hikapro.com.backpack.model; +import android.os.Message; + import java.util.List; import hikapro.com.backpack.model.entities.Category; @@ -15,14 +17,15 @@ public interface Model { interface Base { void onDestroy(boolean isConfigurationChanging); void executeQuery(); + void notifyDataSetChanged(); void sendMessage(String message); + void onEvent(Message event); } interface Set extends Base { hikapro.com.backpack.model.entities.Set getSetByPosition(int position); hikapro.com.backpack.model.entities.Set findSet(int id); int getSetsCount(); - void notifyDataSetChanged(); void setPresenter(Presenter.SetList presenter); Presenter.SetList getPresenter(); } @@ -35,14 +38,12 @@ public interface Model { int getItemsCount(int categoryId); hikapro.com.backpack.model.entities.Category getCategoryByPosition(int position); int getCategoriesCount(); - void notifyDataSetChanged(); void setPresenter(Presenter.ItemList presenter); Presenter.ItemList getPresenter(); } interface Detail extends Base { int getCount(); hikapro.com.backpack.model.entities.Item findItem(int id); - void notifyDataSetChanged(); void setPresenter(Presenter.ItemDetail presenter); Presenter.ItemDetail getPresenter(); diff --git a/app/src/main/java/hikapro/com/backpack/model/RestClient.java b/app/src/main/java/hikapro/com/backpack/model/RestClient.java index 0561ec9..6ae2133 100644 --- a/app/src/main/java/hikapro/com/backpack/model/RestClient.java +++ b/app/src/main/java/hikapro/com/backpack/model/RestClient.java @@ -11,7 +11,7 @@ import retrofit2.converter.gson.GsonConverterFactory; */ public class RestClient { - public static final String BASE_URL = "http://hikapro.com"; + public static final String BASE_URL = "http://hikapro.com/"; public static Api getApi() { diff --git a/app/src/main/java/hikapro/com/backpack/model/SetModel.java b/app/src/main/java/hikapro/com/backpack/model/SetModel.java index e796815..aebc326 100644 --- a/app/src/main/java/hikapro/com/backpack/model/SetModel.java +++ b/app/src/main/java/hikapro/com/backpack/model/SetModel.java @@ -1,38 +1,43 @@ package hikapro.com.backpack.model; +import android.os.Message; + import java.util.ArrayList; import java.util.List; +import hikapro.com.backpack.model.database.Command; +import hikapro.com.backpack.model.database.DAO; +import hikapro.com.backpack.model.database.Event; import hikapro.com.backpack.model.entities.Set; import hikapro.com.backpack.presenter.Presenter; -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; + /** * Created by tariel on 20/04/16. */ public class SetModel implements Model.Set { - private List iList; - private Api api; + private List cache; private Presenter.SetList presenter; + private DAO dao; + public SetModel() { - this.api = RestClient.getApi(); - this.iList = new ArrayList<>(); + this.cache = new ArrayList<>(); + this.dao = DAO.getInstance(); + dao.registerObserver(this); } - // sets --> + //region sets @Override public hikapro.com.backpack.model.entities.Set getSetByPosition(int position) { - return iList.get(position); + return cache.get(position); } @Override public hikapro.com.backpack.model.entities.Set findSet(int id) { Set ret = null; - for (Set s : iList) { + for (Set s : cache) { if (s.getId() == id) { ret = s; break; @@ -43,19 +48,18 @@ public class SetModel implements Model.Set { @Override public int getSetsCount() { - return iList.size(); + return cache.size(); } - // sets <-- + //endregion - // events --> + //region events @Override public void onDestroy(boolean isConfigurationChanging) { if ( !isConfigurationChanging ) { presenter = null; } - } @Override public void notifyDataSetChanged() { @@ -67,39 +71,42 @@ public class SetModel implements Model.Set { presenter.showMessage(message); } - // events <-- + //endregion - // process --> + //region process @Override public void executeQuery() { - if (iList.isEmpty()) - loadSets(); - else - notifyDataSetChanged(); + Message command = Message.obtain(); + command.what = Command.SYNC_IF_NOT_EXISTS; + dao.executeCommand(command); + command = Message.obtain(); + command.what = Command.SET_GET_ALL; + dao.executeCommand(command); } - private void loadSets() { - Call> call = api.getSets(); - call.enqueue(new Callback>() { - @Override - public void onResponse(Call> call, Response> response) { - int statusCode = response.code(); - iList = response.body(); + @Override + public void onEvent(Message event) { + switch (event.what) { + case Event.SET_LOAD_ERROR : + break; + case Event.SET_ITEMS_LOAD_ERROR : + break; + case Event.SET_REORDER_ERROR : + break; + case Event.SET_LOAD_COMPLETED : + cache = (List) event.obj; notifyDataSetChanged(); - } - @Override - public void onFailure(Call> call, Throwable t) { - - } - }); + break; + case Event.SET_ITEMS_LOAD_COMPLETED : + break; + case Event.SET_REORDER_COMPLETED : + break; + } } - private void syncData() { + //endregion - } - // process <-- - - // other --> + //region other @Override public void setPresenter(Presenter.SetList presenter) { @@ -111,7 +118,7 @@ public class SetModel implements Model.Set { return presenter; } - // other <-- + //endregion diff --git a/app/src/main/java/hikapro/com/backpack/model/database/Command.java b/app/src/main/java/hikapro/com/backpack/model/database/Command.java new file mode 100644 index 0000000..15fdc8f --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/model/database/Command.java @@ -0,0 +1,32 @@ +package hikapro.com.backpack.model.database; + +/** + * Created by tariel on 27/04/16. + */ +public interface Command { + + int SET_SCOPE_END = 0x77; + int ITEM_SCOPE_END = 0x8B; + int MY_LIST_SCOPE_END = 0x9F; + + + int SET_GET_ALL = 0x64; + int SET_REORDER = 0x65; + int SET_GET_ITEMS = 0x66; + + int ITEM_DELETE_FROM_SET = 0x78; + int ITEM_INSERT = 0x79; + int ITEM_PACK = 0x7A; + int ITEM_UNPACK = 0x7B; + + int MY_LIST_POST = 0x8C; + int MY_LIST_ITEM_ADD = 0x8D; + int MY_LIST_ITEM_DELETE = 0x8E; + int MY_LIST_CLEAR = 0x8F; + + int SYNC = 0xA0; + int SYNC_IF_NOT_EXISTS = 0xA1; + + int TEST = 0xC8; + +} diff --git a/app/src/main/java/hikapro/com/backpack/model/database/DAO.java b/app/src/main/java/hikapro/com/backpack/model/database/DAO.java index 3f2e0c6..9d848be 100644 --- a/app/src/main/java/hikapro/com/backpack/model/database/DAO.java +++ b/app/src/main/java/hikapro/com/backpack/model/database/DAO.java @@ -1,21 +1,198 @@ package hikapro.com.backpack.model.database; +import android.content.ContentValues; import android.content.Context; +import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Process; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import hikapro.com.backpack.App; +import hikapro.com.backpack.model.Api; +import hikapro.com.backpack.model.Model; +import hikapro.com.backpack.model.RestClient; +import hikapro.com.backpack.model.SetModel; +import hikapro.com.backpack.model.entities.Category; +import hikapro.com.backpack.model.entities.Item; +import hikapro.com.backpack.model.entities.Set; +import hikapro.com.backpack.model.entities.Timestamp; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; /** * Created by tariel on 20/04/16. */ public class DAO { + //region Constants + private static final int CORE_POOL_SIZE = 1; + private static final int CORE_MAX_POOL_SIZE = 1; - private Context context; - private DbHelper helper; + private static final int KEEP_ALIVE_TIME = 1; + private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS; - public DAO(Context context) { - this.context = context; + private static final int MY_LIST_ID = 15; + //endregion + + private static DAO instance; + + private final ThreadPoolExecutor threadPool; + private final Handler handler; + private SetModel setModel; + private final Api api; + private Context context; + private DbHelper helper; + private Map observers; + + private DAO() { + this.context = App.getAppContext(); this.helper = new DbHelper(this.context); + this.api = RestClient.getApi(); + final BlockingQueue taskQueue = new LinkedBlockingQueue<>(); + this.threadPool = initPool(taskQueue); + this.observers = Collections.synchronizedMap(new HashMap()); + this.handler = initHandler(); } + static { + instance = new DAO(); + } + + public static DAO getInstance() { + return instance; + } + + public void registerObserver(Model.Base o) { + observers.put(o.getClass().getName(), o); + } + + private ThreadPoolExecutor initPool (BlockingQueue taskQueue) { + ThreadPoolExecutor ret = new ThreadPoolExecutor( + CORE_POOL_SIZE + ,CORE_MAX_POOL_SIZE + ,KEEP_ALIVE_TIME + ,KEEP_ALIVE_TIME_UNIT + ,taskQueue + ); + return ret; + } + + private Handler initHandler() { + Handler ret = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + for (Map.Entry entry : observers.entrySet()) { + entry.getValue().onEvent(msg); + } + } + }; + return ret; + } + + public void executeCommand(Message command) { + + SetTask setTask; + ItemTask itemTask; + + if (command != null) { + switch (command.what) { + + case Command.SET_GET_ALL : + setTask = new SetTask(Command.SET_GET_ALL, + Process.THREAD_PRIORITY_MORE_FAVORABLE); + threadPool.execute(setTask); + break; + + case Command.SET_REORDER : + setTask = new SetTask(Command.SET_REORDER, + Process.THREAD_PRIORITY_BACKGROUND); + setTask.setsToUpdate = (List) command.obj; + threadPool.execute(setTask); + break; + + case Command.SET_GET_ITEMS : + setTask = new SetTask(Command.SET_GET_ITEMS, + Process.THREAD_PRIORITY_MORE_FAVORABLE); + setTask.setId = command.arg1; + threadPool.execute(setTask); + break; + + case Command.ITEM_INSERT : + itemTask = new ItemTask(Command.ITEM_INSERT, + Process.THREAD_PRIORITY_BACKGROUND); + itemTask.item = (Item) command.obj; + itemTask.setId = command.arg1; + threadPool.execute(itemTask); + break; + + case Command.ITEM_DELETE_FROM_SET : + itemTask = new ItemTask(Command.ITEM_DELETE_FROM_SET, + Process.THREAD_PRIORITY_BACKGROUND); + itemTask.setId = command.arg1; + itemTask.itemId = command.arg2; + threadPool.execute(itemTask); + break; + + case Command.ITEM_PACK : + itemTask = new ItemTask(Command.ITEM_PACK, + Process.THREAD_PRIORITY_BACKGROUND); + itemTask.setId = command.arg1; + itemTask.itemId = command.arg2; + threadPool.execute(itemTask); + break; + + case Command.ITEM_UNPACK : + itemTask = new ItemTask(Command.ITEM_UNPACK, + Process.THREAD_PRIORITY_BACKGROUND); + itemTask.setId = command.arg1; + itemTask.itemId = command.arg2; + threadPool.execute(itemTask); + break; + + case Command.MY_LIST_ITEM_ADD : + break; + + case Command.MY_LIST_ITEM_DELETE : + break; + + case Command.MY_LIST_POST : + break; + + case Command.MY_LIST_CLEAR : + break; + + case Command.SYNC : + threadPool.execute(new SyncTask(Command.SYNC, + Process.THREAD_PRIORITY_BACKGROUND)); + break; + + case Command.SYNC_IF_NOT_EXISTS : + threadPool.execute(new SyncTask(Command.SYNC_IF_NOT_EXISTS, + Process.THREAD_PRIORITY_MORE_FAVORABLE)); + break; + } + } + } + + /////////////////////// DATABASE ///////////////////// + + //region Database private SQLiteDatabase getReadDB(){ return helper.getReadableDatabase(); } @@ -23,4 +200,544 @@ public class DAO { private SQLiteDatabase getWriteDB(){ return helper.getWritableDatabase(); } + + // inserts + private void insertTimestamp(Timestamp timestamp) { + if (timestamp != null && timestamp.timestamp > 0) { + ContentValues values; + SQLiteDatabase db = getWriteDB(); + try { + db.beginTransaction(); + values = Db.LogTable.toContentValues(timestamp); + db.insert(Db.LogTable.TABLE_NAME, null, values); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + db.close(); + } + } + } + private void insertItems(List items) { + if (items != null && !items.isEmpty()) { + ContentValues values; + SQLiteDatabase db = getWriteDB(); + try { + db.beginTransaction(); + for (Item item : items) { + values = Db.ItemsTable.toContentValues(item); + db.insert(Db.ItemsTable.TABLE_NAME, null, values); + } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + db.close(); + } + } + } + private void insertCategories(List categories) { + + if (categories != null && !categories.isEmpty()) { + ContentValues values; + SQLiteDatabase db = getWriteDB(); + try { + db.beginTransaction(); + for (Category category : categories) { + values = Db.CategoriesTable.toContentValues(category); + db.insert(Db.CategoriesTable.TABLE_NAME, null, values); + } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + db.close(); + } + } + } + private void insertSets(List sets) { + + if (sets != null && !sets.isEmpty()) { + ContentValues values; + int i = 0; + SQLiteDatabase db = getWriteDB(); + try { + db.beginTransaction(); + for (Set set : sets) { + values = Db.SetsTable.toContentValues(set, i); + db.insert(Db.SetsTable.TABLE_NAME, null, values); + insertSetItems(set, db); + ++i; + } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + db.close(); + } + } + } + private void insertSetItems(Set set, SQLiteDatabase db) { + if (set != null && db != null) { + if (!set.getItems().isEmpty()) { + ContentValues values; + int setId = set.getId(); + try { + db.beginTransaction(); + for (Integer itemid : set.getItems()) { + values = Db.SetItemsTable.toContentValues(setId, itemid); + db.insert(Db.SetItemsTable.TABLE_NAME, null, values); + } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + } + } + private void insertSetItem(int setId, int itemId) { + ContentValues values; + SQLiteDatabase db = getWriteDB(); + try { + db.beginTransaction(); + values = Db.SetItemsTable.toContentValues(setId, itemId); + db.insert(Db.SetItemsTable.TABLE_NAME, null, values); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + db.close(); + } + } + // reads + private boolean LogExist() { + boolean ret; + SQLiteDatabase db = getReadDB(); + Cursor cursor = db.query(Db.LogTable.TABLE_NAME, + new String[] {Db.LogTable.COLUMN_ID}, null, null, null, null, null, "1"); + ret = cursor.moveToNext(); + cursor.close(); + db.close(); + return ret; + } + private List readItems(int setId) { + List ret = new ArrayList<>(256); + Cursor cursor = null; + SQLiteDatabase db = null; + Item item; + String query = String.format( + "SELECT * FROM %s a INNER JOIN %s b ON a.%s = b.%s WHERE b.%s = ? AND b.%s <> 1 AND b.%s <> 1", + Db.ItemsTable.TABLE_NAME, + Db.SetItemsTable.TABLE_NAME, + Db.ItemsTable.COLUMN_ID, + Db.SetItemsTable.COLUMN_ITEM, + Db.SetItemsTable.COLUMN_SET, + Db.SetItemsTable.COLUMN_DELETED, + Db.SetItemsTable.COLUMN_PACKED); + try { + db = getReadDB(); + cursor = db.rawQuery(query, new String[]{String.valueOf(setId)}); + while (cursor.moveToNext()) { + item = Db.ItemsTable.parseCursor(cursor); + ret.add(item); + } + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + //TODO write to log here + } finally { + if (cursor != null) + cursor.close(); + if (db != null) + db.close(); + } + return ret; + } + private Hashtable readCategories() { + Hashtable ret = new Hashtable<>(20, 0.9f); + Cursor cursor = null; + SQLiteDatabase db = null; + Category category; + try { + db = getReadDB(); + cursor = db.query(Db.CategoriesTable.TABLE_NAME, + new String[]{Db.CategoriesTable.COLUMN_ID, + Db.CategoriesTable.COLUMN_NAME}, + null,null,null,null,null); + while (cursor.moveToNext()) { + category = Db.CategoriesTable.parseCursor(cursor); + ret.put(category.getId(), category.getName()); + } + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + //TODO write to log here + } finally { + if (cursor != null) + cursor.close(); + if (db != null) + db.close(); + } + return ret; + } + private List readSets() { + List ret = new ArrayList<>(12); + Cursor cursor = null; + SQLiteDatabase db = null; + Set set; + try { + db = getReadDB(); + cursor = db.query(Db.SetsTable.TABLE_NAME, + new String[]{Db.SetsTable.COLUMN_ID, + Db.SetsTable.COLUMN_ITEMS, + Db.SetsTable.COLUMN_LINE_NUMBER, + Db.SetsTable.COLUMN_NAME, + Db.SetsTable.COLUMN_PHOTO_LOCAL, + Db.SetsTable.COLUMN_PHOTO_THUMB_LOCAL, + Db.SetsTable.COLUMN_PHOTO_THUMB_URL, + Db.SetsTable.COLUMN_PHOTO_THUMBNAIL_LOCAL, + Db.SetsTable.COLUMN_PHOTO_THUMBNAIL_URL, + Db.SetsTable.COLUMN_PHOTO_URL}, + null,null,null,null, + Db.SetsTable.COLUMN_LINE_NUMBER); + while (cursor.moveToNext()) { + set = Db.SetsTable.parseCursor(cursor); + ret.add(set); + } + Collections.sort(ret); + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + //TODO write to log here + } finally { + if (cursor != null) + cursor.close(); + if (db != null) + db.close(); + } + return ret; + } + // updates + private int updateSetsOrder(List reorderedSet) { + int ret = 0; + SQLiteDatabase db = null; + ContentValues values; + try { + + db = getWriteDB(); + db.beginTransaction(); + for (Set set : reorderedSet) { + values = new ContentValues(); + values.put(Db.SetsTable.COLUMN_LINE_NUMBER, set.getLineNumber()); + ret += db.update(Db.SetsTable.TABLE_NAME, values, "_id = ?", + new String[]{String.valueOf(set.getId())}); + } + db.setTransactionSuccessful(); + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + //TODO write to log here + } finally { + if (db != null) { + db.endTransaction(); + db.close(); + } + } + return ret; + } + private int updateSetItemDeleted(int setId, int itemId, boolean del) { + int ret = 0; + SQLiteDatabase db = null; + ContentValues values; + try { + db = getWriteDB(); + db.beginTransaction(); + values = new ContentValues(); + values.put(Db.SetItemsTable.COLUMN_DELETED, del); + ret = db.update(Db.SetItemsTable.TABLE_NAME, values, String.format("%s = ? AND %s = ?", + Db.SetItemsTable.COLUMN_SET, Db.SetItemsTable.COLUMN_ITEM ), + new String[]{String.valueOf(setId), String.valueOf(itemId)}); + db.setTransactionSuccessful(); + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + //TODO write to log here + } finally { + if (db != null) { + db.endTransaction(); + db.close(); + } + } + return ret; + } + private int updateSetItemPacked(int setId, int itemId, boolean pack) { + int ret = 0; + SQLiteDatabase db = null; + ContentValues values; + try { + db = getWriteDB(); + db.beginTransaction(); + + values = new ContentValues(); + values.put(Db.SetItemsTable.COLUMN_PACKED, pack); + ret = db.update(Db.SetItemsTable.TABLE_NAME, values, String.format("%s = ? AND %s = ?", + Db.SetItemsTable.COLUMN_SET, Db.SetItemsTable.COLUMN_ITEM ), + new String[]{String.valueOf(setId), String.valueOf(itemId)}); + + if (pack) { + values = Db.SetItemsTable.toContentValues(MY_LIST_ID, itemId); + db.insert(Db.SetItemsTable.TABLE_NAME, null, values); + } else { + db.delete(Db.SetItemsTable.TABLE_NAME, String.format("%s = ? AND %s = ?", + Db.SetItemsTable.COLUMN_SET, Db.SetItemsTable.COLUMN_ITEM ), + new String[]{String.valueOf(MY_LIST_ID), String.valueOf(itemId) }); + } + db.setTransactionSuccessful(); + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + //TODO write to log here + } finally { + if (db != null) { + db.endTransaction(); + db.close(); + } + } + return ret; + } + //endregion + + /////////////////////// TASK CLASSES ////////////////// + + //region Task classes + + // MY LIST CLASS + private class MyListTask implements Runnable { + int currentCommand; + int priority; + + + public MyListTask(int command, int priority) { + this.currentCommand = command; + this.priority = priority; + } + + @Override + public void run() { + android.os.Process.setThreadPriority(priority); + Message message = Message.obtain(); + switch (currentCommand) { + + } + handler.sendMessage(message); + } + } + // ITEM CLASS + private class ItemTask implements Runnable { + int currentCommand; + int priority; + int setId; + int itemId; + Item item; + + public ItemTask(int command, int priority) { + this.currentCommand = command; + this.setId = -1; + this.itemId = -1; + this.priority = priority; + } + + @Override + public void run() { + android.os.Process.setThreadPriority(priority); + Message message = Message.obtain(); + switch (currentCommand) { + + case Command.ITEM_DELETE_FROM_SET : + message.arg1 = updateSetItemDeleted(setId, itemId, true); + if (message.arg1 > 0) + message.what = Event.ITEM_FROM_SET_DELETED; + else + message.what = Event.ITEM_FROM_SET_ERROR; + break; + + case Command.ITEM_INSERT : + List items = new ArrayList<>(); + items.add(item); + if (items.isEmpty()) + message.what = Event.ITEM_INSERT_ERROR; + else { + insertItems(items); + insertSetItem(setId, item.getId()); + message.what = Event.ITEM_INSERTED; + message.arg1 = setId; + message.arg2 = item.getId(); + } + break; + + case Command.ITEM_PACK : + message.arg1 = updateSetItemPacked(setId, itemId, true); + if (message.arg1 > 0) + message.what = Event.ITEM_PACKED; + else + message.what = Event.ITEM_PACK_ERROR; + break; + + case Command.ITEM_UNPACK : + message.arg1 = updateSetItemPacked(setId, itemId, false); + if (message.arg1 > 0) + message.what = Event.ITEM_UNPACKED; + else + message.what = Event.ITEM_UNPACK_ERROR; + break; + } + handler.sendMessage(message); + } + } + // SET CLASS + private class SetTask implements Runnable { + int currentCommand; + int priority; + int setId; + List setsToUpdate; + + public SetTask(int command, int priority) { + this.currentCommand = command; + this.setId = -1; + this.setsToUpdate = new ArrayList<>(); + this.priority = priority; + } + + @Override + public void run() { + android.os.Process.setThreadPriority(priority); + Message message = Message.obtain(); + switch (currentCommand) { + + case Command.SET_GET_ALL : + List sets = readSets(); + if (sets.isEmpty()) + message.what = Event.SET_LOAD_ERROR; + else { + message.what = Event.SET_LOAD_COMPLETED; + message.obj = sets; + } + break; + + case Command.SET_GET_ITEMS : + List items = readItems(setId); + if (items.isEmpty()) + message.what = Event.SET_ITEMS_LOAD_ERROR; + else { + Collections.sort(items); + Hashtable categories = readCategories(); + Hashtable> result = new Hashtable<>(20, 0.9f); + String categoryName; + + for (Item item : items) { + categoryName = categories.get(item.getCategory()); + if (result.containsKey(categoryName)) { + result.get(categoryName).add(item); + } else { + List innerList = new ArrayList<>(20); + innerList.add(item); + result.put(categoryName, innerList); + } + } + message.what = Event.SET_ITEMS_LOAD_COMPLETED; + message.obj = result; + message.arg1 = setId; + } + break; + + case Command.SET_REORDER : + message.arg1 = updateSetsOrder(setsToUpdate); + if (message.arg1 > 0) + message.what = Event.SET_REORDER_COMPLETED; + else + message.what = Event.SET_REORDER_ERROR; + break; + } + handler.sendMessage(message); + } + } + // SYNC CLASS + private class SyncTask implements Runnable { + int currentCommand; + int priority; + int statusCode; + + public SyncTask(int command, int priority) { + this.currentCommand = command; + this.priority = priority; + } + + @Override + public void run() { + android.os.Process.setThreadPriority(priority); + Message message = Message.obtain(); + + switch (currentCommand) { + case Command.SYNC: + try { + Call> call = api.getSets(); + call.enqueue(new Callback>() { + @Override + public void onResponse(Call> call, Response> response) { + statusCode = response.code(); + // TODO + // check if first time + // if not check for updates else + // insert into database here + insertSets(response.body()); + } + @Override + public void onFailure(Call> call, Throwable t) { + } + }); + message.what = Event.SYNC_COMPLETED; + + } catch (Exception e) { + message.what = Event.SYNC_FAILED; + } + finally { + message.arg1 = statusCode; + handler.sendMessage(message); + } + break; + + case Command.SYNC_IF_NOT_EXISTS: + if (LogExist()) { + message.what = Event.SYNC_COMPLETED; + } else { + + try { + Response> response0 = api.getSets().execute(); + insertSets(response0.body()); + statusCode = response0.code(); + Response> response1 = api.getItemCategories().execute(); + insertCategories(response1.body()); + statusCode = response1.code(); + Response> response2 = api.getItems().execute(); + insertItems(response2.body()); + statusCode = response2.code(); + Response response3 = api.getTimestamp().execute(); + insertTimestamp(response3.body()); + statusCode = response3.code(); + message.what = Event.SYNC_COMPLETED; + } catch (IOException e ){ + message.what = Event.SYNC_FAILED; + } finally { + message.arg1 = statusCode; + handler.sendMessage(message); + } + } + break; + } + } + } + //endregion + + } diff --git a/app/src/main/java/hikapro/com/backpack/model/database/Db.java b/app/src/main/java/hikapro/com/backpack/model/database/Db.java index de45801..06cd845 100644 --- a/app/src/main/java/hikapro/com/backpack/model/database/Db.java +++ b/app/src/main/java/hikapro/com/backpack/model/database/Db.java @@ -2,6 +2,7 @@ package hikapro.com.backpack.model.database; import android.content.ContentValues; import android.database.Cursor; +import android.util.Log; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -12,6 +13,8 @@ import java.util.List; import hikapro.com.backpack.model.entities.Category; import hikapro.com.backpack.model.entities.Item; import hikapro.com.backpack.model.entities.Set; +import hikapro.com.backpack.model.entities.Timestamp; +import hikapro.com.backpack.model.entities.UpdateLog; /** * Created by tariel on 20/04/16. @@ -23,10 +26,10 @@ public class Db { public abstract static class CategoriesTable { - public static final String TABLE_NAME = "CATEGORIES"; + public static final String TABLE_NAME = "categories"; public static final String COLUMN_ID = "_id"; - public static final String COLUMN_NAME = "NAME"; + public static final String COLUMN_NAME = "name"; public static final String CREATE = "CREATE TABLE " + TABLE_NAME + " (" + @@ -51,17 +54,17 @@ public class Db { } public abstract static class ItemsTable { - public static final String TABLE_NAME = "ITEMS"; + public static final String TABLE_NAME = "items"; public static final String COLUMN_ID = "_id"; - public static final String COLUMN_NAME = "NAME"; - public static final String COLUMN_CATEGORY = "CATEGORY"; - public static final String COLUMN_DESCRIPTION = "DESCRIPTION"; - public static final String COLUMN_BUY_URLS = "BUY_URLS"; - public static final String COLUMN_PHOTO_URL = "PHOTO_URL"; - public static final String COLUMN_PHOTO_THUMB_URL = "PHOTO_THUMB_URL"; - public static final String COLUMN_PHOTO_LOCAL = "PHOTO_LOCAL"; - public static final String COLUMN_PHOTO_THUMB_LOCAL = "PHOTO_THUMB_LOCAL"; + public static final String COLUMN_NAME = "name"; + public static final String COLUMN_CATEGORY = "category"; + public static final String COLUMN_DESCRIPTION = "description"; + public static final String COLUMN_BUY_URLS = "buy_urls"; + public static final String COLUMN_PHOTO_URL = "photo_url"; + public static final String COLUMN_PHOTO_THUMB_URL = "photo_thumb_url"; + public static final String COLUMN_PHOTO_LOCAL = "photo_local"; + public static final String COLUMN_PHOTO_THUMB_LOCAL = "photo_thumb_local"; public static final String CREATE = @@ -124,17 +127,19 @@ public class Db { } public abstract static class SetsTable { - public static final String TABLE_NAME = "SETS"; + public static final String TABLE_NAME = "sets"; public static final String COLUMN_ID = "_id"; - public static final String COLUMN_NAME = "NAME"; - public static final String COLUMN_ITEMS = "ITEMS"; - public static final String COLUMN_PHOTO_URL = "PHOTO_URL"; - public static final String COLUMN_PHOTO_THUMB_URL = "PHOTO_THUMB_URL"; - public static final String COLUMN_PHOTO_LOCAL = "PHOTO_LOCAL"; - public static final String COLUMN_PHOTO_THUMB_LOCAL = "PHOTO_THUMB_LOCAL"; - public static final String COLUMN_PHOTO_THUMBNAIL_URL = "PHOTO_THUMBNAIL_URL"; - public static final String COLUMN_PHOTO_THUMBNAIL_LOCAL = "PHOTO_THUMBNAIL_LOCAL"; + public static final String COLUMN_NAME = "name"; + public static final String COLUMN_ITEMS = "items"; + public static final String COLUMN_PHOTO_URL = "photo_url"; + public static final String COLUMN_PHOTO_THUMB_URL = "photo_thumb_url"; + public static final String COLUMN_PHOTO_LOCAL = "photo_local"; + public static final String COLUMN_PHOTO_THUMB_LOCAL = "photo_thumb_local"; + public static final String COLUMN_PHOTO_THUMBNAIL_URL = "photo_thumbnail_url"; + public static final String COLUMN_PHOTO_THUMBNAIL_LOCAL = "photo_thumbnail_local"; + public static final String COLUMN_LINE_NUMBER = "line_num"; + public static final String CREATE = "CREATE TABLE " + TABLE_NAME + " (" + @@ -146,10 +151,11 @@ public class Db { COLUMN_PHOTO_LOCAL + " TEXT, " + COLUMN_PHOTO_THUMB_LOCAL + " TEXT, " + COLUMN_PHOTO_THUMBNAIL_URL + " TEXT, " + + COLUMN_LINE_NUMBER + " INTEGER, " + COLUMN_PHOTO_THUMBNAIL_LOCAL + " TEXT" + " ); "; - public static ContentValues toContentValues(Set set) { + public static ContentValues toContentValues(Set set, int lineNumber) { ContentValues values = new ContentValues(); values.put(COLUMN_ID, set.getId()); values.put(COLUMN_NAME, set.getName()); @@ -164,6 +170,7 @@ public class Db { values.put(COLUMN_PHOTO_URL, set.getPhoto()); values.put(COLUMN_PHOTO_THUMB_URL, set.getPhotoThumb()); values.put(COLUMN_PHOTO_THUMBNAIL_URL, set.getPhotoThumbnail()); + values.put(COLUMN_LINE_NUMBER, lineNumber); /* values.put(COLUMN_PHOTO_LOCAL, ""); values.put(COLUMN_PHOTO_THUMB_LOCAL, ""); @@ -187,9 +194,68 @@ public class Db { set.setPhoto(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_PHOTO_URL))); set.setPhotoThumb(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_PHOTO_THUMB_URL))); set.setPhotoThumbnail(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_PHOTO_THUMBNAIL_URL))); + set.setLineNumber(cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_LINE_NUMBER))); return set; } + } + public abstract static class LogTable { + + public static final String TABLE_NAME = "update_log"; + + public static final String COLUMN_ID = "_id"; + public static final String COLUMN_MODIFIED_DATETIME = "modified_datetime"; + public static final String COLUMN_TIMESTAMP = "timestamp"; + + public static final String CREATE = + "CREATE TABLE " + TABLE_NAME + " (" + + COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + COLUMN_TIMESTAMP + " INTEGER NOT NULL, " + + COLUMN_MODIFIED_DATETIME + " INTEGER NOT NULL DEFAULT current_timestamp" + + " ); "; + + public static ContentValues toContentValues(Timestamp timestamp) { + ContentValues values = new ContentValues(); + + values.put(COLUMN_TIMESTAMP, timestamp.timestamp); + + return values; + } + public static UpdateLog parseCursor(Cursor cursor) { + UpdateLog log = new UpdateLog(); + log.setTimestamp(cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_TIMESTAMP))); + log.setModifiedDatetime(cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_MODIFIED_DATETIME))); + return log; + } + } + + public abstract static class SetItemsTable { + + public static final String TABLE_NAME = "set_items"; + + public static final String COLUMN_ID = "_id"; + public static final String COLUMN_SET = "setId"; + public static final String COLUMN_ITEM = "itemId"; + public static final String COLUMN_DELETED = "deleted"; + public static final String COLUMN_PACKED = "packed"; + + public static final String CREATE = + "CREATE TABLE " + TABLE_NAME + " (" + + COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + COLUMN_SET + " INTEGER NOT NULL, " + + COLUMN_DELETED + " NUMERIC, " + + COLUMN_PACKED + " NUMERIC, " + + COLUMN_ITEM + " INTEGER NOT NULL" + + " ); "; + + public static ContentValues toContentValues(int setId, int itemId) { + ContentValues values = new ContentValues(); + values.put(COLUMN_SET, setId); + values.put(COLUMN_ITEM, itemId); + values.put(COLUMN_DELETED, 0); + values.put(COLUMN_PACKED, 0); + return values; + } } } diff --git a/app/src/main/java/hikapro/com/backpack/model/database/DbHelper.java b/app/src/main/java/hikapro/com/backpack/model/database/DbHelper.java index 3e3668c..9d8d947 100644 --- a/app/src/main/java/hikapro/com/backpack/model/database/DbHelper.java +++ b/app/src/main/java/hikapro/com/backpack/model/database/DbHelper.java @@ -38,6 +38,8 @@ public class DbHelper extends SQLiteOpenHelper { db.execSQL(Db.ItemsTable.CREATE); db.execSQL(Db.CategoriesTable.CREATE); db.execSQL(Db.SetsTable.CREATE); + db.execSQL(Db.LogTable.CREATE); + db.execSQL(Db.SetItemsTable.CREATE); if (oldVersion < 2) { // place the logic here diff --git a/app/src/main/java/hikapro/com/backpack/model/database/Event.java b/app/src/main/java/hikapro/com/backpack/model/database/Event.java new file mode 100644 index 0000000..f2a4df9 --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/model/database/Event.java @@ -0,0 +1,51 @@ +package hikapro.com.backpack.model.database; + +/** + * Created by tariel on 27/04/16. + */ +public interface Event { + + int SET_SCOPE_END = 0x13; + int ITEM_SCOPE_END = 0x27; + int MY_LIST_SCOPE_END = 0x3B; + + int SET_LOAD_ERROR = -0x1; + int SET_REORDER_ERROR = -0x2; + int SET_ITEMS_LOAD_ERROR = -0x3; + + + int SET_LOAD_COMPLETED = 0x1; + int SET_REORDER_COMPLETED = 0x2; + int SET_ITEMS_LOAD_COMPLETED = 0x3; + + + int ITEM_FROM_SET_ERROR = -0x14; + int ITEM_INSERT_ERROR = -0x15; + int ITEM_DELETE_ERROR = -0x16; + int ITEM_PACK_ERROR = -0x17; + int ITEM_UNPACK_ERROR = -0x18; + + int ITEM_FROM_SET_DELETED = 0x14; + int ITEM_INSERTED = 0x15; + int ITEM_DELETED = 0x16; + int ITEM_PACKED = 0x17; + int ITEM_UNPACKED = 0x18; + + int MY_LIST_POST_ERROR = -0x28; + int MY_LIST_ITEM_ADD_ERROR = -0x29; + int MY_LIST_ITEM_DELETE_ERROR = -0x2A; + int MY_LIST_CLEAR_ERROR = -0x2B; + + int MY_LIST_POSTED = 0x28; + int MY_LIST_ITEM_ADDED = 0x29; + int MY_LIST_ITEM_DELETED = 0x2A; + int MY_LIST_CLEARED = 0x2B; + + int SYNC_FAILED = -0x3C; + int SYNC_COMPLETED = 0x3C; + + int NOT_IMPLEMENTED = 0x50; + int NOT_UNDERSTAND = 0x51; + + +} diff --git a/app/src/main/java/hikapro/com/backpack/model/database/Test.java b/app/src/main/java/hikapro/com/backpack/model/database/Test.java new file mode 100644 index 0000000..e666937 --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/model/database/Test.java @@ -0,0 +1,15 @@ +package hikapro.com.backpack.model.database; + +/** + * Created by tariel on 27/04/16. + */ +public class Test { + private static Test ourInstance = new Test(); + + public static Test getInstance() { + return ourInstance; + } + + private Test() { + } +} diff --git a/app/src/main/java/hikapro/com/backpack/model/entities/Set.java b/app/src/main/java/hikapro/com/backpack/model/entities/Set.java index b017060..194cb16 100644 --- a/app/src/main/java/hikapro/com/backpack/model/entities/Set.java +++ b/app/src/main/java/hikapro/com/backpack/model/entities/Set.java @@ -10,7 +10,7 @@ import java.util.List; /** * Created by tariel on 02/04/16. */ -public class Set implements Serializable { +public class Set implements Comparable, Serializable { @SerializedName("id") @Expose @@ -31,6 +31,8 @@ public class Set implements Serializable { @Expose private String photoThumbnail; + private int lineNumber; + public Set() { } @@ -91,6 +93,14 @@ public class Set implements Serializable { this.photoThumbnail = photoThumbnail; } + public int getLineNumber() { + return lineNumber; + } + + public void setLineNumber(int lineNumber) { + this.lineNumber = lineNumber; + } + @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; @@ -109,4 +119,10 @@ public class Set implements Serializable { return false; return true; } + + @Override + public int compareTo(Set another) { + int cmp = Integer.valueOf(lineNumber).compareTo(Integer.valueOf(another.lineNumber)); + return (cmp != 0 ? cmp : name.compareTo(another.name)); + } } diff --git a/app/src/main/java/hikapro/com/backpack/model/entities/Timestamp.java b/app/src/main/java/hikapro/com/backpack/model/entities/Timestamp.java new file mode 100644 index 0000000..01f5e90 --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/model/entities/Timestamp.java @@ -0,0 +1,20 @@ +package hikapro.com.backpack.model.entities; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * Created by tariel on 27/04/16. + */ +public class Timestamp { + + @SerializedName("timestamp") + @Expose + public long timestamp; + + public Timestamp() { + } + + + +} diff --git a/app/src/main/java/hikapro/com/backpack/model/entities/UpdateLog.java b/app/src/main/java/hikapro/com/backpack/model/entities/UpdateLog.java new file mode 100644 index 0000000..b314744 --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/model/entities/UpdateLog.java @@ -0,0 +1,29 @@ +package hikapro.com.backpack.model.entities; + +/** + * Created by tariel on 27/04/16. + */ +public class UpdateLog { + + private long timestamp; + private long modifiedDatetime; + + public UpdateLog() { + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + public long getModifiedDatetime() { + return modifiedDatetime; + } + + public void setModifiedDatetime(long modifiedDatetime) { + this.modifiedDatetime = modifiedDatetime; + } +} diff --git a/app/src/main/java/hikapro/com/backpack/model/entities/Updates.java b/app/src/main/java/hikapro/com/backpack/model/entities/Updates.java new file mode 100644 index 0000000..00d2689 --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/model/entities/Updates.java @@ -0,0 +1,41 @@ +package hikapro.com.backpack.model.entities; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by tariel on 27/04/16. + */ +public class Updates { + + @SerializedName("items") + @Expose + public List items = new ArrayList<>(); + + @SerializedName("items_deleted_ids") + @Expose + public List itemsDeletedIds = new ArrayList<>(); + + @SerializedName("item_categories") + @Expose + public List itemCategories = new ArrayList<>(); + + @SerializedName("item_categories_deleted_ids") + @Expose + public List itemCategoriesDeletedIds = new ArrayList<>(); + + @SerializedName("sets") + @Expose + public List sets = new ArrayList<>(); + + @SerializedName("sets_deleted_ids") + @Expose + public List setsDeletedIds = new ArrayList<>(); + + @SerializedName("timestamp") + @Expose + public long timestamp; +} From d68c6693963a468ecb62c76155aad7f479b78f78 Mon Sep 17 00:00:00 2001 From: Tariel Hlontsi Date: Fri, 29 Apr 2016 14:54:58 +0300 Subject: [PATCH 3/7] After merging --- .idea/gradle.xml | 6 ++++++ .idea/vcs.xml | 2 +- .../main/java/hikapro/com/backpack/model/database/DAO.java | 1 - 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 8d2df47..c3bf4f7 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -12,6 +12,12 @@ + diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 6564d52..94a25f7 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/app/src/main/java/hikapro/com/backpack/model/database/DAO.java b/app/src/main/java/hikapro/com/backpack/model/database/DAO.java index 9d848be..d5eb553 100644 --- a/app/src/main/java/hikapro/com/backpack/model/database/DAO.java +++ b/app/src/main/java/hikapro/com/backpack/model/database/DAO.java @@ -17,7 +17,6 @@ import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; From 03272e0712c1b59e8631722a4f49656ad4344dab Mon Sep 17 00:00:00 2001 From: Tariel Hlontsi Date: Fri, 29 Apr 2016 15:25:02 +0300 Subject: [PATCH 4/7] Code refactoring --- .../hikapro/com/backpack/model/DetailModel.java | 4 ++-- .../hikapro/com/backpack/model/ItemModel.java | 4 ++-- .../java/hikapro/com/backpack/model/Model.java | 5 ++--- .../hikapro/com/backpack/model/SetModel.java | 16 ++++++++++++---- .../com/backpack/presenter/SetListPresenter.java | 10 +++------- 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/hikapro/com/backpack/model/DetailModel.java b/app/src/main/java/hikapro/com/backpack/model/DetailModel.java index d256ee2..38eece3 100644 --- a/app/src/main/java/hikapro/com/backpack/model/DetailModel.java +++ b/app/src/main/java/hikapro/com/backpack/model/DetailModel.java @@ -42,8 +42,8 @@ public class DetailModel implements Model.Detail { presenter = null; } } - @Override - public void sendMessage(String message) { + + private void sendMessage(String message) { presenter.showMessage(message); } diff --git a/app/src/main/java/hikapro/com/backpack/model/ItemModel.java b/app/src/main/java/hikapro/com/backpack/model/ItemModel.java index e13f99d..268be0e 100644 --- a/app/src/main/java/hikapro/com/backpack/model/ItemModel.java +++ b/app/src/main/java/hikapro/com/backpack/model/ItemModel.java @@ -109,8 +109,8 @@ public class ItemModel implements Model.Item { presenter = null; } } - @Override - public void sendMessage(String message) { + + private void sendMessage(String message) { presenter.showMessage(message); } // events <-- diff --git a/app/src/main/java/hikapro/com/backpack/model/Model.java b/app/src/main/java/hikapro/com/backpack/model/Model.java index 6347622..f521607 100644 --- a/app/src/main/java/hikapro/com/backpack/model/Model.java +++ b/app/src/main/java/hikapro/com/backpack/model/Model.java @@ -18,7 +18,6 @@ public interface Model { void onDestroy(boolean isConfigurationChanging); void executeQuery(); void notifyDataSetChanged(); - void sendMessage(String message); void onEvent(Message event); } @@ -28,9 +27,9 @@ public interface Model { int getSetsCount(); void setPresenter(Presenter.SetList presenter); Presenter.SetList getPresenter(); - //GLM - List getSet(); + List getSets(); // tag renamed + void setsReorderNotify(); } interface Item extends Base { diff --git a/app/src/main/java/hikapro/com/backpack/model/SetModel.java b/app/src/main/java/hikapro/com/backpack/model/SetModel.java index 1390631..1757da9 100644 --- a/app/src/main/java/hikapro/com/backpack/model/SetModel.java +++ b/app/src/main/java/hikapro/com/backpack/model/SetModel.java @@ -66,8 +66,8 @@ public class SetModel implements Model.Set { presenter.notifyDataSetChanged(); } - @Override - public void sendMessage(String message) { + + private void sendMessage(String message) { presenter.showMessage(message); } @@ -104,6 +104,14 @@ public class SetModel implements Model.Set { break; } } + + @Override + public void setsReorderNotify() { + Message command = Message.obtain(); + command.what = Command.SET_REORDER; + command.obj = cache; + dao.executeCommand(command); + } //endregion //region other @@ -124,8 +132,8 @@ public class SetModel implements Model.Set { //GLM @Override - public List getSet() + public List getSets() { - return iList; + return cache; } } diff --git a/app/src/main/java/hikapro/com/backpack/presenter/SetListPresenter.java b/app/src/main/java/hikapro/com/backpack/presenter/SetListPresenter.java index 3b61acd..d1c6ad3 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/SetListPresenter.java +++ b/app/src/main/java/hikapro/com/backpack/presenter/SetListPresenter.java @@ -2,14 +2,11 @@ package hikapro.com.backpack.presenter; import android.content.Context; import android.os.Bundle; -import android.support.v4.view.MotionEventCompat; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; -import android.view.DragEvent; import android.view.LayoutInflater; -import android.view.MotionEvent; import android.view.ViewGroup; import android.widget.Toast; @@ -17,14 +14,12 @@ import java.lang.ref.WeakReference; import java.util.Collections; import hikapro.com.backpack.R; -import hikapro.com.backpack.model.SetModel; import hikapro.com.backpack.model.Model; import hikapro.com.backpack.model.entities.Set; import hikapro.com.backpack.presenter.adapters.SetListAdapter; import hikapro.com.backpack.presenter.adapters.helper.OnStartDragListener; import hikapro.com.backpack.presenter.adapters.helper.SimpleItemTouchHelperCallback; import hikapro.com.backpack.view.View; -import hikapro.com.backpack.view.fragments.SetListFragment; import hikapro.com.backpack.view.recycler.SetViewHolder; /** @@ -51,15 +46,16 @@ public class SetListPresenter implements Presenter.SetList { //GLM_add_resources_SetList @Override public void onItemDismiss(int position) { - model.getSet().remove(position); + model.getSets().remove(position); adapter.notifyItemRemoved(position); } //GLM_add_resources_SetList @Override public boolean onItemMove(int fromPosition, int toPosition) { - Collections.swap(model.getSet(), fromPosition, toPosition); + Collections.swap(model.getSets(), fromPosition, toPosition); adapter.notifyItemMoved(fromPosition, toPosition); + model.setsReorderNotify(); return true; } From feee5f20e939a3bf177b20288cc0c03234f3046a Mon Sep 17 00:00:00 2001 From: Tariel Hlontsi Date: Mon, 2 May 2016 14:17:47 +0300 Subject: [PATCH 5/7] Refactoring --- .idea/dictionaries/tariel.xml | 7 +++ .idea/misc.xml | 2 +- .idea/modules.xml | 2 +- app/build.gradle | 3 +- .../hikapro/com/backpack/model/ItemModel.java | 47 +++++++++------ .../hikapro/com/backpack/model/SetModel.java | 3 + .../com/backpack/model/database/DAO.java | 20 +++---- app/src/main/res/layout/item.xml | 6 +- app/src/main/res/layout/section.xml | 58 +++++++++++++------ 9 files changed, 96 insertions(+), 52 deletions(-) create mode 100644 .idea/dictionaries/tariel.xml diff --git a/.idea/dictionaries/tariel.xml b/.idea/dictionaries/tariel.xml new file mode 100644 index 0000000..4da906f --- /dev/null +++ b/.idea/dictionaries/tariel.xml @@ -0,0 +1,7 @@ + + + + tariel + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 5d19981..fbb6828 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -37,7 +37,7 @@ - + diff --git a/.idea/modules.xml b/.idea/modules.xml index 53118bf..ea59753 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,8 +2,8 @@ + - \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 0ae0a65..f7c9d6b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -28,13 +28,12 @@ dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' - - compile 'com.android.support:support-v4:23.3.0' compile 'com.google.code.gson:gson:2.6.2' compile 'com.squareup.retrofit2:retrofit:2.0.1' compile 'com.squareup.retrofit2:converter-gson:2.0.1' compile 'com.squareup.okhttp3:okhttp:3.2.0' compile 'com.squareup.okhttp:logging-interceptor:2.7.0' + compile 'com.android.support:support-v4:23.3.0' compile 'com.android.support:appcompat-v7:23.3.0' compile 'com.android.support:design:23.3.0' diff --git a/app/src/main/java/hikapro/com/backpack/model/ItemModel.java b/app/src/main/java/hikapro/com/backpack/model/ItemModel.java index 268be0e..679ab15 100644 --- a/app/src/main/java/hikapro/com/backpack/model/ItemModel.java +++ b/app/src/main/java/hikapro/com/backpack/model/ItemModel.java @@ -30,27 +30,33 @@ public class ItemModel implements Model.Item { private List sortedCategories; private List rawItems; private DAO dao; + private int currentSet; + private Category currentCategory; + private List categoriesCache; private Hashtable> items; - private Hashtable>> cache; + private Hashtable>> cache; public ItemModel() { this.rawCategories = new ArrayList<>(); this.rawItems = new ArrayList<>(); this.sortedCategories = new ArrayList<>(); - this.cache = new Hashtable<>(16, 0.9f); + this.categoriesCache = new ArrayList<>(20); + this.cache = new Hashtable<>(12, 0.9f); this.dao = DAO.getInstance(); + dao.registerObserver(this); } // categories --> @Override public Category getCategoryByPosition(int position) { - return sortedCategories.get(position); + currentCategory = categoriesCache.get(position); + return currentCategory; } @Override public int getCategoriesCount() { - return sortedCategories.size(); + return categoriesCache.size(); } // categories <-- @@ -78,20 +84,12 @@ public class ItemModel implements Model.Item { } @Override public Item getItemByPosition(int categoryId, int position) { - Item ret = null; - Category category = findSortedCategory(categoryId); - if (category != null) { - ret = items.get(category).get(position); - } + Item ret = cache.get(currentSet).get(currentCategory).get(position); return ret; } @Override public int getItemsCount(int categoryId) { - int ret = 0; - Category category = findSortedCategory(categoryId); - if (category != null) { - ret = items.get(category).size(); - } + int ret = cache.get(currentSet).get(currentCategory).size(); return ret; } // items <-- @@ -119,10 +117,18 @@ public class ItemModel implements Model.Item { @Override public void executeQuery() { - Message command = Message.obtain(); - command.what = Command.SET_GET_ITEMS; - command.arg1 = presenter.getCurrentSet().getId(); - dao.executeCommand(command); + + if (cache.contains(currentSet)) { + Hashtable> buff = cache.get(currentSet); + Category[] array = buff.keySet().toArray(new Category[buff.keySet().size()]); + categoriesCache = Arrays.asList(array); + notifyDataSetChanged(); + } else { + Message command = Message.obtain(); + command.what = Command.SET_GET_ITEMS; + command.arg1 = presenter.getCurrentSet().getId(); + dao.executeCommand(command); + } } @Override public void onEvent(Message event) { @@ -141,8 +147,10 @@ public class ItemModel implements Model.Item { case Event.ITEM_INSERT_ERROR : break; case Event.SET_ITEMS_LOAD_COMPLETED : - Hashtable> res = (Hashtable>) event.obj; + Hashtable> res = (Hashtable>) event.obj; cache.put(event.arg1, res); + Category[] array = res.keySet().toArray(new Category[res.keySet().size()]); + categoriesCache = Arrays.asList(array); notifyDataSetChanged(); break; case Event.ITEM_FROM_SET_DELETED : @@ -166,6 +174,7 @@ public class ItemModel implements Model.Item { @Override public void setPresenter(Presenter.ItemList presenter) { this.presenter = presenter; + this.currentSet = presenter.getCurrentSet().getId(); } @Override diff --git a/app/src/main/java/hikapro/com/backpack/model/SetModel.java b/app/src/main/java/hikapro/com/backpack/model/SetModel.java index 1757da9..d07cadb 100644 --- a/app/src/main/java/hikapro/com/backpack/model/SetModel.java +++ b/app/src/main/java/hikapro/com/backpack/model/SetModel.java @@ -107,6 +107,9 @@ public class SetModel implements Model.Set { @Override public void setsReorderNotify() { + for (int i = 0; i < cache.size(); ++i) { + cache.get(i).setLineNumber(i); + } Message command = Message.obtain(); command.what = Command.SET_REORDER; command.obj = cache; diff --git a/app/src/main/java/hikapro/com/backpack/model/database/DAO.java b/app/src/main/java/hikapro/com/backpack/model/database/DAO.java index d5eb553..8aa5054 100644 --- a/app/src/main/java/hikapro/com/backpack/model/database/DAO.java +++ b/app/src/main/java/hikapro/com/backpack/model/database/DAO.java @@ -348,8 +348,8 @@ public class DAO { } return ret; } - private Hashtable readCategories() { - Hashtable ret = new Hashtable<>(20, 0.9f); + private Hashtable readCategories() { + Hashtable ret = new Hashtable<>(20, 0.9f); Cursor cursor = null; SQLiteDatabase db = null; Category category; @@ -361,7 +361,7 @@ public class DAO { null,null,null,null,null); while (cursor.moveToNext()) { category = Db.CategoriesTable.parseCursor(cursor); - ret.put(category.getId(), category.getName()); + ret.put(category.getId(), category); } } catch (SQLiteException e) { //TODO write to log here @@ -630,18 +630,18 @@ public class DAO { message.what = Event.SET_ITEMS_LOAD_ERROR; else { Collections.sort(items); - Hashtable categories = readCategories(); - Hashtable> result = new Hashtable<>(20, 0.9f); - String categoryName; + Hashtable categories = readCategories(); + Hashtable> result = new Hashtable<>(20, 0.9f); + Category category; for (Item item : items) { - categoryName = categories.get(item.getCategory()); - if (result.containsKey(categoryName)) { - result.get(categoryName).add(item); + category = categories.get(item.getCategory()); + if (result.containsKey(category)) { + result.get(category).add(item); } else { List innerList = new ArrayList<>(20); innerList.add(item); - result.put(categoryName, innerList); + result.put(category, innerList); } } message.what = Event.SET_ITEMS_LOAD_COMPLETED; diff --git a/app/src/main/res/layout/item.xml b/app/src/main/res/layout/item.xml index 773bfbf..f10833f 100644 --- a/app/src/main/res/layout/item.xml +++ b/app/src/main/res/layout/item.xml @@ -5,6 +5,8 @@ android:layout_height="wrap_content" android:singleLine="true" android:background="@android:color/transparent" - android:textSize="10sp" + android:textSize="16sp" android:id="@+id/item_text" - android:textStyle="bold" /> \ No newline at end of file + android:textStyle="bold" + android:height="30dp" + /> \ No newline at end of file diff --git a/app/src/main/res/layout/section.xml b/app/src/main/res/layout/section.xml index b722a57..973fa15 100644 --- a/app/src/main/res/layout/section.xml +++ b/app/src/main/res/layout/section.xml @@ -1,25 +1,49 @@ - - + > - + android:orientation="horizontal"> - \ No newline at end of file + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 501a3f80cac0dafa24751f4681c69a66217cb321 Mon Sep 17 00:00:00 2001 From: Tariel Hlontsi Date: Tue, 3 May 2016 12:40:48 +0300 Subject: [PATCH 6/7] Initial interface development --- .../hikapro/com/backpack/MainActivity.java | 2 - .../hikapro/com/backpack/model/ItemModel.java | 140 ++++++++-- .../hikapro/com/backpack/model/Model.java | 16 +- .../hikapro/com/backpack/model/SetModel.java | 6 +- .../model/{database => dao}/Command.java | 3 +- .../backpack/model/{database => dao}/DAO.java | 34 +-- .../backpack/model/{database => dao}/Db.java | 3 +- .../model/{database => dao}/DbHelper.java | 2 +- .../model/{database => dao}/Event.java | 4 +- .../model/{database => dao}/Test.java | 2 +- .../backpack/presenter/ItemListPresenter.java | 110 +++----- .../com/backpack/presenter/Presenter.java | 12 +- .../backpack/presenter/SetListPresenter.java | 4 +- .../adapters/CategoryListAdapter.java | 50 ---- .../presenter/adapters/ItemListAdapter.java | 161 ++++++++++- .../presenter/adapters/SetListAdapter.java | 2 +- .../helper/items/DimensionCalculator.java | 45 +++ .../helper/items/DividerDecoration.java | 96 +++++++ .../items/HeaderPositionCalculator.java | 256 ++++++++++++++++++ .../adapters/helper/items/HeaderProvider.java | 24 ++ .../adapters/helper/items/HeaderRenderer.java | 70 +++++ .../helper/items/HeaderViewCache.java | 56 ++++ .../helper/items/ItemSwipeCallback.java | 104 +++++++ .../helper/items/ItemSwipeDecoration.java | 88 ++++++ .../helper/items/ItemVisibilityAdapter.java | 19 ++ .../helper/items/StickyHeaderAdapter.java | 15 + .../helper/items/StickyHeaderDecoration.java | 158 +++++++++++ .../{ => sets}/ItemTouchHelperAdapter.java | 2 +- .../{ => sets}/ItemTouchHelperViewHolder.java | 2 +- .../{ => sets}/OnStartDragListener.java | 2 +- .../SimpleItemTouchHelperCallback.java | 2 +- .../java/hikapro/com/backpack/view/View.java | 3 +- .../view/fragments/ItemListFragment.java | 36 ++- .../view/fragments/SetListFragment.java | 3 +- .../view/recycler/CategoryViewHolder.java | 31 --- .../view/recycler/HeaderViewHolder.java | 25 ++ .../view/recycler/ItemViewHolder.java | 10 +- .../backpack/view/recycler/SetViewHolder.java | 2 +- app/src/main/res/drawable/ic_clear_24dp.xml | 9 + .../main/res/layout/fragment_item_list.xml | 2 +- app/src/main/res/layout/item.xml | 12 - app/src/main/res/layout/section.xml | 49 ---- .../main/res/layout/sticky_header_layout.xml | 5 + app/src/main/res/layout/view_header.xml | 16 ++ app/src/main/res/layout/view_item.xml | 30 ++ app/src/main/res/menu/menu_main.xml | 20 ++ app/src/main/res/values/dimens.xml | 1 + 47 files changed, 1437 insertions(+), 307 deletions(-) rename app/src/main/java/hikapro/com/backpack/model/{database => dao}/Command.java (90%) rename app/src/main/java/hikapro/com/backpack/model/{database => dao}/DAO.java (96%) rename app/src/main/java/hikapro/com/backpack/model/{database => dao}/Db.java (99%) rename app/src/main/java/hikapro/com/backpack/model/{database => dao}/DbHelper.java (96%) rename app/src/main/java/hikapro/com/backpack/model/{database => dao}/Event.java (91%) rename app/src/main/java/hikapro/com/backpack/model/{database => dao}/Test.java (82%) delete mode 100644 app/src/main/java/hikapro/com/backpack/presenter/adapters/CategoryListAdapter.java create mode 100644 app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/DimensionCalculator.java create mode 100644 app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/DividerDecoration.java create mode 100644 app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderPositionCalculator.java create mode 100644 app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderProvider.java create mode 100644 app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderRenderer.java create mode 100644 app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderViewCache.java create mode 100644 app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/ItemSwipeCallback.java create mode 100644 app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/ItemSwipeDecoration.java create mode 100644 app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/ItemVisibilityAdapter.java create mode 100644 app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/StickyHeaderAdapter.java create mode 100644 app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/StickyHeaderDecoration.java rename app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/{ => sets}/ItemTouchHelperAdapter.java (77%) rename app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/{ => sets}/ItemTouchHelperViewHolder.java (73%) rename app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/{ => sets}/OnStartDragListener.java (78%) rename app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/{ => sets}/SimpleItemTouchHelperCallback.java (98%) delete mode 100644 app/src/main/java/hikapro/com/backpack/view/recycler/CategoryViewHolder.java create mode 100644 app/src/main/java/hikapro/com/backpack/view/recycler/HeaderViewHolder.java create mode 100644 app/src/main/res/drawable/ic_clear_24dp.xml delete mode 100644 app/src/main/res/layout/item.xml delete mode 100644 app/src/main/res/layout/section.xml create mode 100644 app/src/main/res/layout/sticky_header_layout.xml create mode 100644 app/src/main/res/layout/view_header.xml create mode 100644 app/src/main/res/layout/view_item.xml create mode 100644 app/src/main/res/menu/menu_main.xml diff --git a/app/src/main/java/hikapro/com/backpack/MainActivity.java b/app/src/main/java/hikapro/com/backpack/MainActivity.java index b053626..8034220 100644 --- a/app/src/main/java/hikapro/com/backpack/MainActivity.java +++ b/app/src/main/java/hikapro/com/backpack/MainActivity.java @@ -10,13 +10,11 @@ import android.util.Log; import hikapro.com.backpack.model.DetailModel; import hikapro.com.backpack.model.ItemModel; import hikapro.com.backpack.model.SetModel; -import hikapro.com.backpack.model.database.DAO; import hikapro.com.backpack.model.entities.Item; import hikapro.com.backpack.model.entities.Set; import hikapro.com.backpack.presenter.ItemDetailPresenter; import hikapro.com.backpack.presenter.ItemListPresenter; import hikapro.com.backpack.presenter.SetListPresenter; -import hikapro.com.backpack.presenter.adapters.helper.OnStartDragListener; import hikapro.com.backpack.view.View; import hikapro.com.backpack.view.fragments.ItemDetailFragment; import hikapro.com.backpack.view.fragments.ItemListFragment; diff --git a/app/src/main/java/hikapro/com/backpack/model/ItemModel.java b/app/src/main/java/hikapro/com/backpack/model/ItemModel.java index 679ab15..e63a1b4 100644 --- a/app/src/main/java/hikapro/com/backpack/model/ItemModel.java +++ b/app/src/main/java/hikapro/com/backpack/model/ItemModel.java @@ -9,16 +9,12 @@ import java.util.Collections; import java.util.Hashtable; import java.util.List; -import hikapro.com.backpack.model.database.Command; -import hikapro.com.backpack.model.database.DAO; -import hikapro.com.backpack.model.database.Event; +import hikapro.com.backpack.model.dao.Command; +import hikapro.com.backpack.model.dao.DAO; +import hikapro.com.backpack.model.dao.Event; import hikapro.com.backpack.model.entities.Category; import hikapro.com.backpack.model.entities.Item; -import hikapro.com.backpack.model.entities.Set; import hikapro.com.backpack.presenter.Presenter; -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; /** * Created by tariel on 22/04/16. @@ -29,21 +25,27 @@ public class ItemModel implements Model.Item { private List rawCategories; private List sortedCategories; private List rawItems; + private DAO dao; - private int currentSet; - private Category currentCategory; - private List categoriesCache; + private int currentSet; + private Hashtable categoriesCache; + private List itemsCache; + private List itemsDiscardCache; private Hashtable> items; - private Hashtable>> cache; + private Hashtable> cache; public ItemModel() { this.rawCategories = new ArrayList<>(); this.rawItems = new ArrayList<>(); this.sortedCategories = new ArrayList<>(); - this.categoriesCache = new ArrayList<>(20); + + this.categoriesCache = new Hashtable<>(20, 0.9f); + + this.itemsCache = new ArrayList<>(); + this.itemsDiscardCache = new ArrayList<>(); this.cache = new Hashtable<>(12, 0.9f); - this.dao = DAO.getInstance(); + this.dao = DAO.getInstance(); dao.registerObserver(this); } @@ -51,8 +53,11 @@ public class ItemModel implements Model.Item { @Override public Category getCategoryByPosition(int position) { - currentCategory = categoriesCache.get(position); - return currentCategory; + Category ret = null; + if (cache.containsKey(currentSet)) + ret = categoriesCache.get(cache.get(currentSet).get(position).getCategory()); + return ret; + } @Override public int getCategoriesCount() { @@ -65,12 +70,23 @@ public class ItemModel implements Model.Item { @Override public int insertItem(Item item) { + List iList = cache.get(currentSet); + iList.add(item); + Collections.sort(iList); + cache.put(currentSet, iList); + // TODO write to database return 0; } @Override - public boolean deleteItem(int id) { + public boolean deleteItem(Item item) { + if (isPendingRemoval(item)) + itemsDiscardCache.remove(item); + cache.get(currentSet).remove(item); + //TODO write to ds + return false; } + @Override public Item findItem(int id) { Item item = null; @@ -83,15 +99,75 @@ public class ItemModel implements Model.Item { return item; } @Override - public Item getItemByPosition(int categoryId, int position) { - Item ret = cache.get(currentSet).get(currentCategory).get(position); + public Item getItemByPosition(int position) { + Item ret = null; + if (cache.containsKey(currentSet)) + ret = cache.get(currentSet).get(position); return ret; } + + @Override - public int getItemsCount(int categoryId) { - int ret = cache.get(currentSet).get(currentCategory).size(); - return ret; + public void filter(String query) { + + if (query.isEmpty()) { + Message command = Message.obtain(); + command.what = Command.SET_GET_ITEMS; + command.arg1 = presenter.getCurrentSet().getId(); + dao.executeCommand(command); + } else { + query = query.toLowerCase(); + String name; + List newList = new ArrayList<>(20); + List oldList = cache.get(currentSet); + for (Item item : oldList) { + name = item.getName().toLowerCase(); + if (name.contains(query)) { + newList.add(item); + } + } + cache.put(currentSet, newList); + } } + + @Override + public int getHeaderId(int position) { + return cache.containsKey(currentSet) ? cache.get(currentSet).get(position).getCategory() : -1; + } + + @Override + public int getItemId(int position) { + return cache.containsKey(currentSet) ? cache.get(currentSet).get(position).getId() : -1; + } + + @Override + public void clear() { + if (cache.containsKey(currentSet)) + cache.get(currentSet).clear(); + } + + @Override + public boolean isPendingRemoval(Item item) { + return itemsDiscardCache.contains(item); + } + + @Override + public void pendingRemoveCancel(Item item) { + if (itemsDiscardCache.contains(item)) + itemsDiscardCache.remove(item); + } + + @Override + public void pendingRemove(Item item) { + itemsDiscardCache.add(item); + } + + @Override + public int getItemsCount() { + boolean is = cache.containsKey(currentSet); + return is ? cache.get(currentSet).size() : 0; + } + // items <-- // events --> @@ -117,17 +193,21 @@ public class ItemModel implements Model.Item { @Override public void executeQuery() { + Message command; if (cache.contains(currentSet)) { - Hashtable> buff = cache.get(currentSet); - Category[] array = buff.keySet().toArray(new Category[buff.keySet().size()]); - categoriesCache = Arrays.asList(array); notifyDataSetChanged(); } else { - Message command = Message.obtain(); + if (categoriesCache.isEmpty()) { + command = Message.obtain(); + command.what = Command.ITEM_GET_CATEGORIES; + dao.executeCommand(command); + } + command = Message.obtain(); command.what = Command.SET_GET_ITEMS; command.arg1 = presenter.getCurrentSet().getId(); dao.executeCommand(command); + } } @Override @@ -146,13 +226,16 @@ public class ItemModel implements Model.Item { break; case Event.ITEM_INSERT_ERROR : break; + case Event.ITEM_CATEGORY_LOAD_ERROR : + break; case Event.SET_ITEMS_LOAD_COMPLETED : - Hashtable> res = (Hashtable>) event.obj; + List res = (List) event.obj; cache.put(event.arg1, res); - Category[] array = res.keySet().toArray(new Category[res.keySet().size()]); - categoriesCache = Arrays.asList(array); notifyDataSetChanged(); break; + case Event.ITEM_CATEGORY_LOAD_COMPLETED : + categoriesCache = (Hashtable)event.obj; + break; case Event.ITEM_FROM_SET_DELETED : break; case Event.ITEM_DELETED : @@ -220,7 +303,6 @@ public class ItemModel implements Model.Item { } } return category; - } private Category findCategory(int categoryId) { Category category = null; diff --git a/app/src/main/java/hikapro/com/backpack/model/Model.java b/app/src/main/java/hikapro/com/backpack/model/Model.java index f521607..08f0bdb 100644 --- a/app/src/main/java/hikapro/com/backpack/model/Model.java +++ b/app/src/main/java/hikapro/com/backpack/model/Model.java @@ -34,10 +34,20 @@ public interface Model { interface Item extends Base { int insertItem(hikapro.com.backpack.model.entities.Item item); - boolean deleteItem(int id); + boolean deleteItem(hikapro.com.backpack.model.entities.Item item); + void filter(String query); + int getHeaderId(int position);//TODO review + int getItemId(int position);//TODO review + void clear(); + boolean isPendingRemoval(hikapro.com.backpack.model.entities.Item item); + void pendingRemove(hikapro.com.backpack.model.entities.Item item); + void pendingRemoveCancel(hikapro.com.backpack.model.entities.Item item); + int getItemsCount(); + + hikapro.com.backpack.model.entities.Item findItem(int id); - hikapro.com.backpack.model.entities.Item getItemByPosition(int categoryId, int position); - int getItemsCount(int categoryId); + hikapro.com.backpack.model.entities.Item getItemByPosition(int position); + hikapro.com.backpack.model.entities.Category getCategoryByPosition(int position); int getCategoriesCount(); void setPresenter(Presenter.ItemList presenter); diff --git a/app/src/main/java/hikapro/com/backpack/model/SetModel.java b/app/src/main/java/hikapro/com/backpack/model/SetModel.java index d07cadb..cea372c 100644 --- a/app/src/main/java/hikapro/com/backpack/model/SetModel.java +++ b/app/src/main/java/hikapro/com/backpack/model/SetModel.java @@ -5,9 +5,9 @@ import android.os.Message; import java.util.ArrayList; import java.util.List; -import hikapro.com.backpack.model.database.Command; -import hikapro.com.backpack.model.database.DAO; -import hikapro.com.backpack.model.database.Event; +import hikapro.com.backpack.model.dao.Command; +import hikapro.com.backpack.model.dao.DAO; +import hikapro.com.backpack.model.dao.Event; import hikapro.com.backpack.model.entities.Set; import hikapro.com.backpack.presenter.Presenter; diff --git a/app/src/main/java/hikapro/com/backpack/model/database/Command.java b/app/src/main/java/hikapro/com/backpack/model/dao/Command.java similarity index 90% rename from app/src/main/java/hikapro/com/backpack/model/database/Command.java rename to app/src/main/java/hikapro/com/backpack/model/dao/Command.java index 15fdc8f..5542a54 100644 --- a/app/src/main/java/hikapro/com/backpack/model/database/Command.java +++ b/app/src/main/java/hikapro/com/backpack/model/dao/Command.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.model.database; +package hikapro.com.backpack.model.dao; /** * Created by tariel on 27/04/16. @@ -18,6 +18,7 @@ public interface Command { int ITEM_INSERT = 0x79; int ITEM_PACK = 0x7A; int ITEM_UNPACK = 0x7B; + int ITEM_GET_CATEGORIES = 0x7C; int MY_LIST_POST = 0x8C; int MY_LIST_ITEM_ADD = 0x8D; diff --git a/app/src/main/java/hikapro/com/backpack/model/database/DAO.java b/app/src/main/java/hikapro/com/backpack/model/dao/DAO.java similarity index 96% rename from app/src/main/java/hikapro/com/backpack/model/database/DAO.java rename to app/src/main/java/hikapro/com/backpack/model/dao/DAO.java index 8aa5054..44a87e9 100644 --- a/app/src/main/java/hikapro/com/backpack/model/database/DAO.java +++ b/app/src/main/java/hikapro/com/backpack/model/dao/DAO.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.model.database; +package hikapro.com.backpack.model.dao; import android.content.ContentValues; import android.content.Context; @@ -132,6 +132,12 @@ public class DAO { threadPool.execute(setTask); break; + case Command.ITEM_GET_CATEGORIES : + itemTask = new ItemTask(Command.ITEM_GET_CATEGORIES, + Process.THREAD_PRIORITY_MORE_FAVORABLE); + threadPool.execute(itemTask); + break; + case Command.ITEM_INSERT : itemTask = new ItemTask(Command.ITEM_INSERT, Process.THREAD_PRIORITY_BACKGROUND); @@ -590,6 +596,16 @@ public class DAO { else message.what = Event.ITEM_UNPACK_ERROR; break; + + case Command.ITEM_GET_CATEGORIES : + Hashtable res = readCategories(); + if (res.isEmpty()) + message.what = Event.ITEM_CATEGORY_LOAD_ERROR; + else { + message.what = Event.ITEM_CATEGORY_LOAD_COMPLETED; + message.obj = res; + } + break; } handler.sendMessage(message); } @@ -630,22 +646,8 @@ public class DAO { message.what = Event.SET_ITEMS_LOAD_ERROR; else { Collections.sort(items); - Hashtable categories = readCategories(); - Hashtable> result = new Hashtable<>(20, 0.9f); - Category category; - - for (Item item : items) { - category = categories.get(item.getCategory()); - if (result.containsKey(category)) { - result.get(category).add(item); - } else { - List innerList = new ArrayList<>(20); - innerList.add(item); - result.put(category, innerList); - } - } message.what = Event.SET_ITEMS_LOAD_COMPLETED; - message.obj = result; + message.obj = items; message.arg1 = setId; } break; diff --git a/app/src/main/java/hikapro/com/backpack/model/database/Db.java b/app/src/main/java/hikapro/com/backpack/model/dao/Db.java similarity index 99% rename from app/src/main/java/hikapro/com/backpack/model/database/Db.java rename to app/src/main/java/hikapro/com/backpack/model/dao/Db.java index 06cd845..26c643b 100644 --- a/app/src/main/java/hikapro/com/backpack/model/database/Db.java +++ b/app/src/main/java/hikapro/com/backpack/model/dao/Db.java @@ -1,8 +1,7 @@ -package hikapro.com.backpack.model.database; +package hikapro.com.backpack.model.dao; import android.content.ContentValues; import android.database.Cursor; -import android.util.Log; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; diff --git a/app/src/main/java/hikapro/com/backpack/model/database/DbHelper.java b/app/src/main/java/hikapro/com/backpack/model/dao/DbHelper.java similarity index 96% rename from app/src/main/java/hikapro/com/backpack/model/database/DbHelper.java rename to app/src/main/java/hikapro/com/backpack/model/dao/DbHelper.java index 9d8d947..e4e6a03 100644 --- a/app/src/main/java/hikapro/com/backpack/model/database/DbHelper.java +++ b/app/src/main/java/hikapro/com/backpack/model/dao/DbHelper.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.model.database; +package hikapro.com.backpack.model.dao; import android.content.Context; import android.database.sqlite.SQLiteDatabase; diff --git a/app/src/main/java/hikapro/com/backpack/model/database/Event.java b/app/src/main/java/hikapro/com/backpack/model/dao/Event.java similarity index 91% rename from app/src/main/java/hikapro/com/backpack/model/database/Event.java rename to app/src/main/java/hikapro/com/backpack/model/dao/Event.java index f2a4df9..e4ada30 100644 --- a/app/src/main/java/hikapro/com/backpack/model/database/Event.java +++ b/app/src/main/java/hikapro/com/backpack/model/dao/Event.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.model.database; +package hikapro.com.backpack.model.dao; /** * Created by tariel on 27/04/16. @@ -24,12 +24,14 @@ public interface Event { int ITEM_DELETE_ERROR = -0x16; int ITEM_PACK_ERROR = -0x17; int ITEM_UNPACK_ERROR = -0x18; + int ITEM_CATEGORY_LOAD_ERROR = -0x19; int ITEM_FROM_SET_DELETED = 0x14; int ITEM_INSERTED = 0x15; int ITEM_DELETED = 0x16; int ITEM_PACKED = 0x17; int ITEM_UNPACKED = 0x18; + int ITEM_CATEGORY_LOAD_COMPLETED = 0x19; int MY_LIST_POST_ERROR = -0x28; int MY_LIST_ITEM_ADD_ERROR = -0x29; diff --git a/app/src/main/java/hikapro/com/backpack/model/database/Test.java b/app/src/main/java/hikapro/com/backpack/model/dao/Test.java similarity index 82% rename from app/src/main/java/hikapro/com/backpack/model/database/Test.java rename to app/src/main/java/hikapro/com/backpack/model/dao/Test.java index e666937..f1464cf 100644 --- a/app/src/main/java/hikapro/com/backpack/model/database/Test.java +++ b/app/src/main/java/hikapro/com/backpack/model/dao/Test.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.model.database; +package hikapro.com.backpack.model.dao; /** * Created by tariel on 27/04/16. diff --git a/app/src/main/java/hikapro/com/backpack/presenter/ItemListPresenter.java b/app/src/main/java/hikapro/com/backpack/presenter/ItemListPresenter.java index 97e6193..28652f3 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/ItemListPresenter.java +++ b/app/src/main/java/hikapro/com/backpack/presenter/ItemListPresenter.java @@ -2,27 +2,24 @@ package hikapro.com.backpack.presenter; import android.content.Context; import android.os.Bundle; -import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; import android.view.LayoutInflater; import android.view.ViewGroup; import android.widget.Toast; import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; import hikapro.com.backpack.R; -import hikapro.com.backpack.model.ItemModel; import hikapro.com.backpack.model.Model; import hikapro.com.backpack.model.entities.Item; import hikapro.com.backpack.model.entities.Set; -import hikapro.com.backpack.presenter.adapters.CategoryListAdapter; +import hikapro.com.backpack.presenter.adapters.helper.items.DividerDecoration; import hikapro.com.backpack.presenter.adapters.ItemListAdapter; +import hikapro.com.backpack.presenter.adapters.helper.items.ItemSwipeCallback; +import hikapro.com.backpack.presenter.adapters.helper.items.StickyHeaderDecoration; import hikapro.com.backpack.view.View; -import hikapro.com.backpack.view.recycler.CategoryViewHolder; -import hikapro.com.backpack.view.recycler.ItemViewHolder; /** * Created by tariel on 20/04/16. @@ -34,10 +31,12 @@ public class ItemListPresenter implements Presenter.ItemList { private WeakReference view; private Model.Item model; private Set set; - private CategoryListAdapter categoryListAdapter; + private ItemListAdapter adapter; + RecyclerView recycler; public ItemListPresenter() { - this.categoryListAdapter = new CategoryListAdapter(this); + this.adapter = new ItemListAdapter(this); + adapter.setHasStableIds(true); } // life cycle --> @@ -56,10 +55,25 @@ public class ItemListPresenter implements Presenter.ItemList { set = (Set) savedInstanceState.getSerializable(BUNDLE_SET_LIST_KEY); android.view.View view = inflater.inflate(R.layout.fragment_item_list, container, false); LinearLayoutManager llm = new LinearLayoutManager(getActivityContext()); - RecyclerView mainRecycler = (RecyclerView) view.findViewById(R.id.categories_main_recycler); - mainRecycler.setLayoutManager(llm); - mainRecycler.setAdapter(categoryListAdapter); - mainRecycler.setItemAnimator(new DefaultItemAnimator()); + recycler = (RecyclerView) view.findViewById(R.id.items_recycler); + recycler.setLayoutManager(llm); + recycler.setAdapter(adapter); + + final StickyHeaderDecoration decoration = new StickyHeaderDecoration(adapter); + recycler.addItemDecoration(decoration); + recycler.addItemDecoration(new DividerDecoration(getActivityContext())); + adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { + @Override + public void onChanged() { + decoration.invalidateHeaders(); + } + }); + ItemSwipeCallback itemSwipeCallback = new ItemSwipeCallback(0, ItemTouchHelper.LEFT, adapter, getActivityContext()); + + ItemTouchHelper itemTouchHelper = new ItemTouchHelper(itemSwipeCallback); + itemTouchHelper.attachToRecyclerView(recycler); + adapter.setUndoOn(true); + model.executeQuery(); return view; } @@ -71,68 +85,11 @@ public class ItemListPresenter implements Presenter.ItemList { // life cycle <-- - // recycler --> - - @Override - public CategoryViewHolder createViewHolderCategory(ViewGroup parent, int viewType) { - CategoryViewHolder viewHolder; - android.view.View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.section, - parent, false); - viewHolder = new CategoryViewHolder(v); - return viewHolder; - } - @Override - public void bindViewHolderCategory(CategoryViewHolder holder, int position) { - final hikapro.com.backpack.model.entities.Category category = model.getCategoryByPosition(position); - holder.sectionText.setText(category.getName()); - LinearLayoutManager llm = new LinearLayoutManager(getActivityContext()); - holder.itemsRecycler.setLayoutManager(llm); - ItemListAdapter itemListAdapter = new ItemListAdapter(this); - itemListAdapter.setCategoryId(category.getId()); - holder.itemsRecycler.setAdapter(itemListAdapter); - holder.itemsRecycler.setItemAnimator(new DefaultItemAnimator()); - categoryListAdapter.addItemAdapter(itemListAdapter); - } - - @Override - public ItemViewHolder createViewHolderItem(ViewGroup parent, int viewType) { - ItemViewHolder viewHolder; - android.view.View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, - parent, false); - viewHolder = new ItemViewHolder(v); - return viewHolder; - } - @Override - public void bindViewHolderItem(final ItemViewHolder holder, final int position, int categoryId) { - - hikapro.com.backpack.model.entities.Item item = model.getItemByPosition(categoryId, position); - holder.title.setText(item.getName()); - holder.id = item.getId(); - holder.title.setOnClickListener(new android.view.View.OnClickListener() { - @Override - public void onClick(android.view.View view) { - clickItem(holder.id); - } - }); - } - @Override - public int getItemsCount(int categoryId) { - return model.getItemsCount(categoryId);//TODO category Id - } - @Override - public int getCategoriesCount() { - return model.getCategoriesCount(); - } - - - // recycler <-- - // process --> @Override public void notifyDataSetChanged() { - categoryListAdapter.notifyDataSetChanged(); - categoryListAdapter.notifyItemAdapters(); + adapter.notifyDataSetChanged(); } @Override @@ -156,6 +113,12 @@ public class ItemListPresenter implements Presenter.ItemList { public void setModel(Model.Item model) { this.model = model; } + + @Override + public Model.Item getModel() { + return model; + } + @Override public Context getAppContext() { try { @@ -186,6 +149,11 @@ public class ItemListPresenter implements Presenter.ItemList { throw new NullPointerException("View is unavailable"); } + @Override + public void filter(String query) { + adapter.filter(query); + recycler.scrollToPosition(0); + } @Override public Set getCurrentSet() { diff --git a/app/src/main/java/hikapro/com/backpack/presenter/Presenter.java b/app/src/main/java/hikapro/com/backpack/presenter/Presenter.java index 277232e..347c886 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/Presenter.java +++ b/app/src/main/java/hikapro/com/backpack/presenter/Presenter.java @@ -3,15 +3,12 @@ package hikapro.com.backpack.presenter; import android.content.Context; import android.os.Bundle; import android.support.v7.widget.RecyclerView; -import android.view.DragEvent; import android.view.LayoutInflater; -import android.view.View; import android.view.ViewGroup; import hikapro.com.backpack.model.Model; import hikapro.com.backpack.model.entities.Item; import hikapro.com.backpack.model.entities.Set; -import hikapro.com.backpack.view.recycler.CategoryViewHolder; import hikapro.com.backpack.view.recycler.DetailViewHolder; import hikapro.com.backpack.view.recycler.ItemViewHolder; import hikapro.com.backpack.view.recycler.SetViewHolder; @@ -45,21 +42,18 @@ public interface Presenter { } interface ItemList extends Base { - ItemViewHolder createViewHolderItem(ViewGroup parent, int viewType); - void bindViewHolderItem(ItemViewHolder holder, int position, int categoryId); - int getItemsCount(int categoryId); - CategoryViewHolder createViewHolderCategory(ViewGroup parent, int viewType); - void bindViewHolderCategory(CategoryViewHolder holder, int position); - int getCategoriesCount(); + void onDestroy(boolean isChangingConfiguration); android.view.View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState); void setView(hikapro.com.backpack.view.View.ItemList view); void setModel(Model.Item model); + Model.Item getModel(); void notifyDataSetChanged(); Set getCurrentSet(); void showMessage(String message); void onSaveInstanceState(Bundle outState); void clickItem(int itemId); + void filter(String query); } interface ItemDetail extends Base { diff --git a/app/src/main/java/hikapro/com/backpack/presenter/SetListPresenter.java b/app/src/main/java/hikapro/com/backpack/presenter/SetListPresenter.java index d1c6ad3..02604a1 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/SetListPresenter.java +++ b/app/src/main/java/hikapro/com/backpack/presenter/SetListPresenter.java @@ -17,8 +17,8 @@ import hikapro.com.backpack.R; import hikapro.com.backpack.model.Model; import hikapro.com.backpack.model.entities.Set; import hikapro.com.backpack.presenter.adapters.SetListAdapter; -import hikapro.com.backpack.presenter.adapters.helper.OnStartDragListener; -import hikapro.com.backpack.presenter.adapters.helper.SimpleItemTouchHelperCallback; +import hikapro.com.backpack.presenter.adapters.helper.sets.OnStartDragListener; +import hikapro.com.backpack.presenter.adapters.helper.sets.SimpleItemTouchHelperCallback; import hikapro.com.backpack.view.View; import hikapro.com.backpack.view.recycler.SetViewHolder; diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/CategoryListAdapter.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/CategoryListAdapter.java deleted file mode 100644 index d3d2a6c..0000000 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/CategoryListAdapter.java +++ /dev/null @@ -1,50 +0,0 @@ -package hikapro.com.backpack.presenter.adapters; - -import android.support.v7.widget.RecyclerView; -import android.view.ViewGroup; - -import java.util.ArrayList; -import java.util.List; - -import hikapro.com.backpack.presenter.Presenter; -import hikapro.com.backpack.view.recycler.CategoryViewHolder; - -/** - * Created by tariel on 20/04/16. - */ -public class CategoryListAdapter extends RecyclerView.Adapter { - - private Presenter.ItemList presenter; - private List itemAdapters; - - public CategoryListAdapter(Presenter.ItemList presenter) { - this.presenter = presenter; - this.itemAdapters = new ArrayList<>(); - } - - @Override - public int getItemCount() { - return presenter.getCategoriesCount(); - } - - @Override - public void onBindViewHolder(CategoryViewHolder holder, int position) { - presenter.bindViewHolderCategory(holder, position); - } - - @Override - public CategoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - return presenter.createViewHolderCategory(parent, viewType); - } - - public void addItemAdapter(ItemListAdapter adapter) { - itemAdapters.add(adapter); - adapter.notifyDataSetChanged(); - } - public void notifyItemAdapters() { - for (ItemListAdapter adapter : itemAdapters) { - adapter.notifyDataSetChanged(); - } - } - -} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/ItemListAdapter.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/ItemListAdapter.java index 19fc89a..bdc93d4 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/ItemListAdapter.java +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/ItemListAdapter.java @@ -1,18 +1,35 @@ package hikapro.com.backpack.presenter.adapters; +import android.graphics.Color; +import android.os.Handler; import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; import android.view.ViewGroup; +import java.util.HashMap; + +import hikapro.com.backpack.R; +import hikapro.com.backpack.model.Model; +import hikapro.com.backpack.model.entities.Category; +import hikapro.com.backpack.model.entities.Item; +import hikapro.com.backpack.presenter.ItemListPresenter; import hikapro.com.backpack.presenter.Presenter; +import hikapro.com.backpack.presenter.adapters.helper.items.StickyHeaderAdapter; +import hikapro.com.backpack.view.recycler.HeaderViewHolder; import hikapro.com.backpack.view.recycler.ItemViewHolder; /** - * Created by tariel on 20/04/16. + * Created by tariel on 01/05/16. */ -public class ItemListAdapter extends RecyclerView.Adapter { +public class ItemListAdapter extends RecyclerView.Adapter implements StickyHeaderAdapter { + private static final int PENDING_REMOVAL_TIMEOUT = 4000; // 4sec + + boolean undoOn; // is undo on, you can turn it on from the toolbar menu + private Handler handler = new Handler(); // hanlder for running delayed runnables + HashMap pendingRunables = new HashMap<>(); // map of items to pending runnables, so we can cancel a removal if need be private Presenter.ItemList presenter; - private int categoryId; public ItemListAdapter(Presenter.ItemList presenter) { this.presenter = presenter; @@ -20,19 +37,145 @@ public class ItemListAdapter extends RecyclerView.Adapter { @Override public int getItemCount() { - return presenter.getItemsCount(categoryId); + int res = presenter.getModel().getItemsCount(); + return res; + } + + @Override + public long getItemId(int position) { + return presenter.getModel().getItemByPosition(position).getId(); + } + + @Override + public void onBindViewHolder(ItemViewHolder holder, final int position) { + + final Item item = presenter.getModel().getItemByPosition(position); + if (presenter.getModel().isPendingRemoval(item)) { + // we need to show the "undo" state of the row + holder.itemView.setBackgroundColor(Color.RED); + holder.title.setVisibility(View.GONE); + holder.undoButton.setVisibility(View.VISIBLE); + holder.undoButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // user wants to undo the removal, let's cancel the pending task + Runnable pendingRemovalRunnable = pendingRunables.get(item); + pendingRunables.remove(item); + if (pendingRemovalRunnable != null) handler.removeCallbacks(pendingRemovalRunnable); + presenter.getModel().pendingRemoveCancel(item); + // this will rebind the row in "normal" state + notifyItemChanged(position); + } + }); + } else { + holder.title.setVisibility(View.VISIBLE); + holder.title.setText(item.getName()); + holder.id = item.getId(); + holder.categoryId = item.getCategory(); + holder.itemView.setBackgroundColor(0x33FF99); + holder.undoButton.setVisibility(View.GONE); + holder.undoButton.setOnClickListener(null); + } + } @Override public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - return presenter.createViewHolderItem(parent, viewType); + ItemViewHolder viewHolder; + View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_item, + parent, false); + viewHolder = new ItemViewHolder(v); + return viewHolder; + } + + public void setUndoOn(boolean undoOn) { + this.undoOn = undoOn; + } + + public boolean isUndoOn() { + return undoOn; + } + + public void add(Item item) { + presenter.getModel().insertItem(item); + notifyDataSetChanged(); + } + + + public void clear() { + presenter.getModel().clear(); + notifyDataSetChanged(); + } + + public void filter(String query) { + presenter.getModel().filter(query); + notifyDataSetChanged(); } @Override - public void onBindViewHolder(ItemViewHolder holder, int position) { - presenter.bindViewHolderItem(holder, position, categoryId); + public void setHasStableIds(boolean hasStableIds) { + super.setHasStableIds(hasStableIds); } - public void setCategoryId(int categoryId) { - this.categoryId = categoryId; + + @Override + public long getHeaderId(int position) { + /*if (position == 0) { + return -1; + } else {*/ + return presenter.getModel().getHeaderId(position); + //} } + + @Override + public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent) { + HeaderViewHolder viewHolder; + View v = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.view_header, parent, false); + viewHolder = new HeaderViewHolder(v); + return viewHolder; + } + + @Override + public void onBindHeaderViewHolder(HeaderViewHolder holder, int position) { + Category category = presenter.getModel().getCategoryByPosition(position); + holder.id = category.getId(); + holder.title.setText(category.getName()); + holder.title.setBackgroundColor(0x2B1E15); + } + + public void pendingRemoval(final int position) { + final Item item = presenter.getModel().getItemByPosition(position); + + if (! presenter.getModel().isPendingRemoval(item)) { + presenter.getModel().pendingRemove(item); + // this will redraw row in "undo" state + notifyItemChanged(position); + // let's create, store and post a runnable to remove the item + Runnable pendingRemovalRunnable = new Runnable() { + @Override + public void run() { + remove(item, position); + } + }; + handler.postDelayed(pendingRemovalRunnable, PENDING_REMOVAL_TIMEOUT); + pendingRunables.put(item, pendingRemovalRunnable); + } + } + + public void remove(Item item, int position) { + presenter.getModel().deleteItem(item); + notifyItemRemoved(position); + } + + public void remove(int position) { + Item item = presenter.getModel().getItemByPosition(position); + presenter.getModel().deleteItem(item); + notifyItemRemoved(position); + } + + public boolean isPendingRemoval(int position) { + Item item = presenter.getModel().getItemByPosition(position); + return presenter.getModel().isPendingRemoval(item); + } + } diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/SetListAdapter.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/SetListAdapter.java index 42fe656..5293c81 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/SetListAdapter.java +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/SetListAdapter.java @@ -4,7 +4,7 @@ import android.support.v7.widget.RecyclerView; import android.view.ViewGroup; import hikapro.com.backpack.presenter.SetListPresenter; -import hikapro.com.backpack.presenter.adapters.helper.ItemTouchHelperAdapter; +import hikapro.com.backpack.presenter.adapters.helper.sets.ItemTouchHelperAdapter; import hikapro.com.backpack.view.recycler.SetViewHolder; /** diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/DimensionCalculator.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/DimensionCalculator.java new file mode 100644 index 0000000..cf459ed --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/DimensionCalculator.java @@ -0,0 +1,45 @@ +package hikapro.com.backpack.presenter.adapters.helper.items; + +import android.graphics.Rect; +import android.view.View; +import android.view.ViewGroup; + +/** + * Created by tariel on 30/04/16. + */ +public class DimensionCalculator { + + /** + * Populates {@link Rect} with margins for any view. + * + * + * @param margins rect to populate + * @param view for which to get margins + */ + public void initMargins(Rect margins, View view) { + ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); + + if (layoutParams instanceof ViewGroup.MarginLayoutParams) { + ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) layoutParams; + initMarginRect(margins, marginLayoutParams); + } else { + margins.set(0, 0, 0, 0); + } + } + + /** + * Converts {@link ViewGroup.MarginLayoutParams} into a representative {@link Rect}. + * + * @param marginRect Rect to be initialized with margins coordinates, where + * {@link ViewGroup.MarginLayoutParams#leftMargin} is equivalent to {@link Rect#left}, etc. + * @param marginLayoutParams margins to populate the Rect with + */ + private void initMarginRect(Rect marginRect, ViewGroup.MarginLayoutParams marginLayoutParams) { + marginRect.set( + marginLayoutParams.leftMargin, + marginLayoutParams.topMargin, + marginLayoutParams.rightMargin, + marginLayoutParams.bottomMargin + ); + } +} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/DividerDecoration.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/DividerDecoration.java new file mode 100644 index 0000000..7076da7 --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/DividerDecoration.java @@ -0,0 +1,96 @@ +package hikapro.com.backpack.presenter.adapters.helper.items; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.View; + + +public class DividerDecoration extends RecyclerView.ItemDecoration { + + private static final int[] ATTRS = new int[]{ + android.R.attr.listDivider + }; + + public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL; + + public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL; + + private Drawable mDivider; + + public DividerDecoration(Context context) { + final TypedArray a = context.obtainStyledAttributes(ATTRS); + mDivider = a.getDrawable(0); + a.recycle(); + } + + private int getOrientation(RecyclerView parent) { + LinearLayoutManager layoutManager; + try { + layoutManager = (LinearLayoutManager) parent.getLayoutManager(); + } catch (ClassCastException e) { + throw new IllegalStateException("DividerDecoration can only be used with a " + + "LinearLayoutManager.", e); + } + return layoutManager.getOrientation(); + } + + @Override + public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { + super.onDraw(c, parent, state); + + if (getOrientation(parent) == VERTICAL_LIST) { + drawVertical(c, parent); + } else { + drawHorizontal(c, parent); + } + } + + public void drawVertical(Canvas c, RecyclerView parent) { + final int left = parent.getPaddingLeft(); + final int right = parent.getWidth() - parent.getPaddingRight(); + final int recyclerViewTop = parent.getPaddingTop(); + final int recyclerViewBottom = parent.getHeight() - parent.getPaddingBottom(); + final int childCount = parent.getChildCount(); + for (int i = 0; i < childCount; i++) { + final View child = parent.getChildAt(i); + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child + .getLayoutParams(); + final int top = Math.max(recyclerViewTop, child.getBottom() + params.bottomMargin); + final int bottom = Math.min(recyclerViewBottom, top + mDivider.getIntrinsicHeight()); + mDivider.setBounds(left, top, right, bottom); + mDivider.draw(c); + } + } + + public void drawHorizontal(Canvas c, RecyclerView parent) { + final int top = parent.getPaddingTop(); + final int bottom = parent.getHeight() - parent.getPaddingBottom(); + final int recyclerViewLeft = parent.getPaddingLeft(); + final int recyclerViewRight = parent.getWidth() - parent.getPaddingRight(); + final int childCount = parent.getChildCount(); + for (int i = 0; i < childCount; i++) { + final View child = parent.getChildAt(i); + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child + .getLayoutParams(); + final int left = Math.max(recyclerViewLeft, child.getRight() + params.rightMargin); + final int right = Math.min(recyclerViewRight, left + mDivider.getIntrinsicHeight()); + mDivider.setBounds(left, top, right, bottom); + mDivider.draw(c); + } + } + + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { + super.getItemOffsets(outRect, view, parent, state); + if (getOrientation(parent) == VERTICAL_LIST) { + outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); + } else { + outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderPositionCalculator.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderPositionCalculator.java new file mode 100644 index 0000000..2eab5be --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderPositionCalculator.java @@ -0,0 +1,256 @@ +package hikapro.com.backpack.presenter.adapters.helper.items; + +import android.graphics.Rect; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +/** + * Created by tariel on 30/04/16. + */ +public class HeaderPositionCalculator { + + private final StickyHeaderAdapter adapter; + private final HeaderProvider headerProvider; + private final DimensionCalculator dimensionCalculator; + + /** + * The following fields are used as buffers for internal calculations. Their sole purpose is to avoid + * allocating new Rect every time we need one. + */ + private final Rect tempRect1 = new Rect(); + private final Rect tempRect2 = new Rect(); + + public HeaderPositionCalculator(StickyHeaderAdapter adapter, HeaderProvider headerProvider, + DimensionCalculator dimensionCalculator) { + this.adapter = adapter; + this.headerProvider = headerProvider; + this.dimensionCalculator = dimensionCalculator; + } + + /** + * Determines if a view should have a sticky header. + * The view has a sticky header if: + * 1. It is the first element in the recycler view + * 2. It has a valid ID associated to its position + * + * @param itemView given by the RecyclerView + * @param orientation of the Recyclerview + * @param position of the list item in question + * @return True if the view should have a sticky header + */ + public boolean hasStickyHeader(View itemView, int orientation, int position) { + int offset, margin; + dimensionCalculator.initMargins(tempRect1, itemView); + if (orientation == LinearLayout.VERTICAL) { + offset = itemView.getTop(); + margin = tempRect1.top; + } else { + offset = itemView.getLeft(); + margin = tempRect1.left; + } + boolean ret = offset <= margin; + ret = ret && adapter.getHeaderId(position) >= 0; + + return ret; + } + + /** + * Determines if an item in the list should have a header that is different than the item in the + * list that immediately precedes it. Items with no headers will always return false. + * + * @param position of the list item in questions + * @param isReverseLayout TRUE if layout manager has flag isReverseLayout + * @return true if this item has a different header than the previous item in the list + */ + public boolean hasNewHeader(int position) { + if (indexOutOfBounds(position)) { + return false; + } + + long headerId = adapter.getHeaderId(position); + + if (headerId < 0) { + return false; + } + + long nextItemHeaderId = -1;//TODO -1 + int nextItemPosition = position - 1; // TODO -1 + if (!indexOutOfBounds(nextItemPosition)) { + nextItemHeaderId = adapter.getHeaderId(nextItemPosition); + } + int firstItemPosition = 0; + + return position == firstItemPosition || headerId != nextItemHeaderId; + } + + private boolean indexOutOfBounds(int position) { + return position < 0 || position >= adapter.getItemCount(); + } + + public void initHeaderBounds(Rect bounds, RecyclerView recyclerView, View header, View firstView, boolean firstHeader) { + initDefaultHeaderOffset(bounds, recyclerView, header, firstView, LinearLayout.VERTICAL); + + if (firstHeader && isStickyHeaderBeingPushedOffscreen(recyclerView, header)) { + View viewAfterNextHeader = getFirstViewUnobscuredByHeader(recyclerView, header); + int firstViewUnderHeaderPosition = recyclerView.getChildAdapterPosition(viewAfterNextHeader); + View secondHeader = headerProvider.getHeader(recyclerView, firstViewUnderHeaderPosition); + translateHeaderWithNextHeader(recyclerView, LinearLayout.VERTICAL, bounds, + header, viewAfterNextHeader, secondHeader); + } + } + + private void initDefaultHeaderOffset(Rect headerMargins, RecyclerView recyclerView, View header, View firstView, int orientation) { + int translationX, translationY; + dimensionCalculator.initMargins(tempRect1, header); + + ViewGroup.LayoutParams layoutParams = firstView.getLayoutParams(); + int leftMargin = 0; + int topMargin = 0; + if (layoutParams instanceof ViewGroup.MarginLayoutParams) { + ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) layoutParams; + leftMargin = marginLayoutParams.leftMargin; + topMargin = marginLayoutParams.topMargin; + } + + if (orientation == LinearLayoutManager.VERTICAL) { + translationX = firstView.getLeft() - leftMargin + tempRect1.left; + translationY = Math.max( + firstView.getTop() - topMargin - header.getHeight() - tempRect1.bottom, + getListTop(recyclerView) + tempRect1.top); + } else { + translationY = firstView.getTop() - topMargin + tempRect1.top; + translationX = Math.max( + firstView.getLeft() - leftMargin - header.getWidth() - tempRect1.right, + getListLeft(recyclerView) + tempRect1.left); + } + + headerMargins.set(translationX, translationY, translationX + header.getWidth(), + translationY + header.getHeight()); + } + + private boolean isStickyHeaderBeingPushedOffscreen(RecyclerView recyclerView, View stickyHeader) { + View viewAfterHeader = getFirstViewUnobscuredByHeader(recyclerView, stickyHeader); + int firstViewUnderHeaderPosition = recyclerView.getChildAdapterPosition(viewAfterHeader); + if (firstViewUnderHeaderPosition == RecyclerView.NO_POSITION) { + return false; + } + + int orientation = LinearLayoutManager.VERTICAL; + + if (firstViewUnderHeaderPosition > 0 && hasNewHeader(firstViewUnderHeaderPosition)) { + View nextHeader = headerProvider.getHeader(recyclerView, firstViewUnderHeaderPosition); + dimensionCalculator.initMargins(tempRect1, nextHeader); + dimensionCalculator.initMargins(tempRect2, stickyHeader); + + if (orientation == LinearLayoutManager.VERTICAL) { + int topOfNextHeader = viewAfterHeader.getTop() - tempRect1.bottom - nextHeader.getHeight() - tempRect1.top; + int bottomOfThisHeader = recyclerView.getPaddingTop() + stickyHeader.getBottom() + tempRect2.top + tempRect2.bottom; + if (topOfNextHeader < bottomOfThisHeader) { + return true; + } + } else { + int leftOfNextHeader = viewAfterHeader.getLeft() - tempRect1.right - nextHeader.getWidth() - tempRect1.left; + int rightOfThisHeader = recyclerView.getPaddingLeft() + stickyHeader.getRight() + tempRect2.left + tempRect2.right; + if (leftOfNextHeader < rightOfThisHeader) { + return true; + } + } + } + + return false; + } + + private void translateHeaderWithNextHeader(RecyclerView recyclerView, int orientation, Rect translation, + View currentHeader, View viewAfterNextHeader, View nextHeader) { + dimensionCalculator.initMargins(tempRect1, nextHeader); + dimensionCalculator.initMargins(tempRect2, currentHeader); + if (orientation == LinearLayoutManager.VERTICAL) { + int topOfStickyHeader = getListTop(recyclerView) + tempRect2.top + tempRect2.bottom; + int shiftFromNextHeader = viewAfterNextHeader.getTop() - nextHeader.getHeight() - tempRect1.bottom - tempRect1.top - currentHeader.getHeight() - topOfStickyHeader; + if (shiftFromNextHeader < topOfStickyHeader) { + translation.top += shiftFromNextHeader; + } + } else { + int leftOfStickyHeader = getListLeft(recyclerView) + tempRect2.left + tempRect2.right; + int shiftFromNextHeader = viewAfterNextHeader.getLeft() - nextHeader.getWidth() - tempRect1.right - tempRect1.left - currentHeader.getWidth() - leftOfStickyHeader; + if (shiftFromNextHeader < leftOfStickyHeader) { + translation.left += shiftFromNextHeader; + } + } + } + + /** + * Returns the first item currently in the RecyclerView that is not obscured by a header. + * + * @param parent Recyclerview containing all the list items + * @return first item that is fully beneath a header + */ + private View getFirstViewUnobscuredByHeader(RecyclerView parent, View firstHeader) { + int step = 1; + int from = 0; + for (int i = from; i >= 0 && i <= parent.getChildCount() - 1; i += step) { + View child = parent.getChildAt(i); + if (!itemIsObscuredByHeader(parent, child, firstHeader, LinearLayout.VERTICAL)) { + return child; + } + } + return null; + } + + /** + * Determines if an item is obscured by a header + * + * + * @param parent + * @param item to determine if obscured by header + * @param header that might be obscuring the item + * @param orientation of the {@link RecyclerView} + * @return true if the item view is obscured by the header view + */ + private boolean itemIsObscuredByHeader(RecyclerView parent, View item, View header, int orientation) { + RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) item.getLayoutParams(); + dimensionCalculator.initMargins(tempRect1, header); + + int adapterPosition = parent.getChildAdapterPosition(item); + if (adapterPosition == RecyclerView.NO_POSITION || headerProvider.getHeader(parent, adapterPosition) != header) { + // Resolves https://github.com/timehop/sticky-headers-recyclerview/issues/36 + // Handles an edge case where a trailing header is smaller than the current sticky header. + return false; + } + + if (orientation == LinearLayoutManager.VERTICAL) { + int itemTop = item.getTop() - layoutParams.topMargin; + int headerBottom = getListTop(parent) + header.getBottom() + tempRect1.bottom + tempRect1.top; + if (itemTop >= headerBottom) { + return false; + } + } else { + int itemLeft = item.getLeft() - layoutParams.leftMargin; + int headerRight = getListLeft(parent) + header.getRight() + tempRect1.right + tempRect1.left; + if (itemLeft >= headerRight) { + return false; + } + } + + return true; + } + + private int getListTop(RecyclerView view) { + if (view.getLayoutManager().getClipToPadding()) { + return view.getPaddingTop(); + } else { + return 0; + } + } + + private int getListLeft(RecyclerView view) { + if (view.getLayoutManager().getClipToPadding()) { + return view.getPaddingLeft(); + } else { + return 0; + } + } +} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderProvider.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderProvider.java new file mode 100644 index 0000000..d760882 --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderProvider.java @@ -0,0 +1,24 @@ +package hikapro.com.backpack.presenter.adapters.helper.items; + +import android.support.v7.widget.RecyclerView; +import android.view.View; + +/** + * Created by tariel on 30/04/16. + */ +public interface HeaderProvider { + + /** + * Will provide a header view for a given position in the RecyclerView + * + * @param recyclerView that will display the header + * @param position that will be headed by the header + * @return a header view for the given position and list + */ + View getHeader(RecyclerView recyclerView, int position); + + /** + * TODO: describe this functionality and its necessity + */ + void invalidate(); +} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderRenderer.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderRenderer.java new file mode 100644 index 0000000..6bde81a --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderRenderer.java @@ -0,0 +1,70 @@ +package hikapro.com.backpack.presenter.adapters.helper.items; + +import android.graphics.Canvas; +import android.graphics.Rect; +import android.support.v7.widget.RecyclerView; +import android.view.View; + +/** + * Created by tariel on 30/04/16. + */ +public class HeaderRenderer { + + private final DimensionCalculator dimensionCalculator; + + /** + * The following field is used as a buffer for internal calculations. Its sole purpose is to avoid + * allocating new Rect every time we need one. + */ + private final Rect mTempRect = new Rect(); + + public HeaderRenderer() { + this.dimensionCalculator = new DimensionCalculator(); + } + + /** + * Draws a header to a canvas, offsetting by some x and y amount + * + * @param recyclerView the parent recycler view for drawing the header into + * @param canvas the canvas on which to draw the header + * @param header the view to draw as the header + * @param offset a Rect used to define the x/y offset of the header. Specify x/y offset by setting + * the {@link Rect#left} and {@link Rect#top} properties, respectively. + */ + public void drawHeader(RecyclerView recyclerView, Canvas canvas, View header, Rect offset) { + canvas.save(); + + if (recyclerView.getLayoutManager().getClipToPadding()) { + // Clip drawing of headers to the padding of the RecyclerView. Avoids drawing in the padding + initClipRectForHeader(mTempRect, recyclerView, header); + canvas.clipRect(mTempRect); + } + + canvas.translate(offset.left, offset.top); + + header.draw(canvas); + canvas.restore(); + } + + /** + * Initializes a clipping rect for the header based on the margins of the header and the padding of the + * recycler. + * FIXME: Currently right margin in VERTICAL orientation and bottom margin in HORIZONTAL + * orientation are clipped so they look accurate, but the headers are not being drawn at the + * correctly smaller width and height respectively. + * + * @param clipRect {@link Rect} for clipping a provided header to the padding of a recycler view + * @param recyclerView for which to provide a header + * @param header for clipping + */ + private void initClipRectForHeader(Rect clipRect, RecyclerView recyclerView, View header) { + dimensionCalculator.initMargins(clipRect, header); + + clipRect.set( + recyclerView.getPaddingLeft(), + recyclerView.getPaddingTop(), + recyclerView.getWidth() - recyclerView.getPaddingRight() - clipRect.right, + recyclerView.getHeight() - recyclerView.getPaddingBottom()); + + } +} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderViewCache.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderViewCache.java new file mode 100644 index 0000000..118aeb3 --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderViewCache.java @@ -0,0 +1,56 @@ +package hikapro.com.backpack.presenter.adapters.helper.items; + +import android.support.v4.util.LongSparseArray; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.view.ViewGroup; + +/** + * Created by tariel on 30/04/16. + */ +public class HeaderViewCache implements HeaderProvider { + + private final StickyHeaderAdapter adapter; + private final LongSparseArray headerViews = new LongSparseArray<>(); + + public HeaderViewCache(StickyHeaderAdapter adapter) { + this.adapter = adapter; + } + + @Override + public View getHeader(RecyclerView parent, int position) { + long headerId = adapter.getHeaderId(position); + + View header = headerViews.get(headerId); + if (header == null) { + //TODO - recycle views + RecyclerView.ViewHolder viewHolder = adapter.onCreateHeaderViewHolder(parent); + adapter.onBindHeaderViewHolder(viewHolder, position); + header = viewHolder.itemView; + if (header.getLayoutParams() == null) { + header.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + } + + int widthSpec; + int heightSpec; + + widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY); + heightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight(), View.MeasureSpec.UNSPECIFIED); + + int childWidth = ViewGroup.getChildMeasureSpec(widthSpec, + parent.getPaddingLeft() + parent.getPaddingRight(), header.getLayoutParams().width); + int childHeight = ViewGroup.getChildMeasureSpec(heightSpec, + parent.getPaddingTop() + parent.getPaddingBottom(), header.getLayoutParams().height); + header.measure(childWidth, childHeight); + header.layout(0, 0, header.getMeasuredWidth(), header.getMeasuredHeight()); + headerViews.put(headerId, header); + } + return header; + } + + @Override + public void invalidate() { + headerViews.clear(); + } +} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/ItemSwipeCallback.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/ItemSwipeCallback.java new file mode 100644 index 0000000..13bc750 --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/ItemSwipeCallback.java @@ -0,0 +1,104 @@ +package hikapro.com.backpack.presenter.adapters.helper.items; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; +import android.view.View; + +import hikapro.com.backpack.R; +import hikapro.com.backpack.presenter.adapters.ItemListAdapter; + +/** + * Created by tariel on 02/05/16. + */ +public class ItemSwipeCallback extends ItemTouchHelper.SimpleCallback { + + // we want to cache these and not allocate anything repeatedly in the onChildDraw method + Drawable background; + Drawable xMark; + int xMarkMargin; + boolean initiated; + ItemListAdapter adapter; + Context context; + + public ItemSwipeCallback(int dragDirs, int swipeDirs, ItemListAdapter adapter, Context context) { + super(dragDirs, swipeDirs); + this.adapter = adapter; + this.context = context; + } + + private void init() { + background = new ColorDrawable(Color.RED); + xMark = ContextCompat.getDrawable(context, R.drawable.ic_clear_24dp); + xMark.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP); + xMarkMargin = (int) context.getResources().getDimension(R.dimen.ic_clear_margin); + initiated = true; + } + + // not important, we don't want drag & drop + @Override + public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { + return false; + } + + @Override + public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { + int position = viewHolder.getAdapterPosition(); + if (adapter.isUndoOn() && adapter.isPendingRemoval(position)) { + return 0; + } + return super.getSwipeDirs(recyclerView, viewHolder); + } + + @Override + public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) { + int swipedPosition = viewHolder.getAdapterPosition(); + + boolean undoOn = adapter.isUndoOn(); + if (undoOn) { + adapter.pendingRemoval(swipedPosition); + } else { + adapter.remove(swipedPosition); + } + } + + @Override + public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { + View itemView = viewHolder.itemView; + + // not sure why, but this method get's called for viewholder that are already swiped away + if (viewHolder.getAdapterPosition() == -1) { + // not interested in those + return; + } + + if (!initiated) { + init(); + } + + // draw red background + background.setBounds(itemView.getRight() + (int) dX, itemView.getTop(), itemView.getRight(), itemView.getBottom()); + background.draw(c); + + // draw x mark + int itemHeight = itemView.getBottom() - itemView.getTop(); + int intrinsicWidth = xMark.getIntrinsicWidth(); + int intrinsicHeight = xMark.getIntrinsicWidth(); + + int xMarkLeft = itemView.getRight() - xMarkMargin - intrinsicWidth; + int xMarkRight = itemView.getRight() - xMarkMargin; + int xMarkTop = itemView.getTop() + (itemHeight - intrinsicHeight)/2; + int xMarkBottom = xMarkTop + intrinsicHeight; + xMark.setBounds(xMarkLeft, xMarkTop, xMarkRight, xMarkBottom); + + xMark.draw(c); + + super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); + } +} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/ItemSwipeDecoration.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/ItemSwipeDecoration.java new file mode 100644 index 0000000..784595e --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/ItemSwipeDecoration.java @@ -0,0 +1,88 @@ +package hikapro.com.backpack.presenter.adapters.helper.items; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.support.v7.widget.RecyclerView; +import android.view.View; + +/** + * Created by tariel on 02/05/16. + */ +public class ItemSwipeDecoration extends RecyclerView.ItemDecoration { + + // we want to cache this and not allocate anything repeatedly in the onDraw method + Drawable background; + boolean initiated; + + private void init() { + background = new ColorDrawable(Color.RED); + initiated = true; + } + + @Override + public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { + + if (!initiated) { + init(); + } + + // only if animation is in progress + if (parent.getItemAnimator().isRunning()) { + + // some items might be animating down and some items might be animating up to close the gap left by the removed item + // this is not exclusive, both movement can be happening at the same time + // to reproduce this leave just enough items so the first one and the last one would be just a little off screen + // then remove one from the middle + + // find first child with translationY > 0 + // and last one with translationY < 0 + // we're after a rect that is not covered in recycler-view views at this point in time + View lastViewComingDown = null; + View firstViewComingUp = null; + + // this is fixed + int left = 0; + int right = parent.getWidth(); + + // this we need to find out + int top = 0; + int bottom = 0; + + // find relevant translating views + int childCount = parent.getLayoutManager().getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = parent.getLayoutManager().getChildAt(i); + if (child.getTranslationY() < 0) { + // view is coming down + lastViewComingDown = child; + } else if (child.getTranslationY() > 0) { + // view is coming up + if (firstViewComingUp == null) { + firstViewComingUp = child; + } + } + } + + if (lastViewComingDown != null && firstViewComingUp != null) { + // views are coming down AND going up to fill the void + top = lastViewComingDown.getBottom() + (int) lastViewComingDown.getTranslationY(); + bottom = firstViewComingUp.getTop() + (int) firstViewComingUp.getTranslationY(); + } else if (lastViewComingDown != null) { + // views are going down to fill the void + top = lastViewComingDown.getBottom() + (int) lastViewComingDown.getTranslationY(); + bottom = lastViewComingDown.getBottom(); + } else if (firstViewComingUp != null) { + // views are coming up to fill the void + top = firstViewComingUp.getTop(); + bottom = firstViewComingUp.getTop() + (int) firstViewComingUp.getTranslationY(); + } + + background.setBounds(left, top, right, bottom); + background.draw(c); + + } + super.onDraw(c, parent, state); + } +} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/ItemVisibilityAdapter.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/ItemVisibilityAdapter.java new file mode 100644 index 0000000..2d6547d --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/ItemVisibilityAdapter.java @@ -0,0 +1,19 @@ +package hikapro.com.backpack.presenter.adapters.helper.items; + +/** + * Created by tariel on 30/04/16. + */ +public interface ItemVisibilityAdapter { + + /** + * + * Return true the specified adapter position is visible, false otherwise + * + * The implementation of this method will typically return true if + * the position is between the layout manager's findFirstVisibleItemPosition + * and findLastVisibleItemPosition (inclusive). + * + * @param position the adapter position + */ + boolean isPositionVisible(final int position); +} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/StickyHeaderAdapter.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/StickyHeaderAdapter.java new file mode 100644 index 0000000..f21ef78 --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/StickyHeaderAdapter.java @@ -0,0 +1,15 @@ +package hikapro.com.backpack.presenter.adapters.helper.items; + +import android.support.v7.widget.RecyclerView; +import android.view.ViewGroup; + +/** + * Created by tariel on 01/05/16. + */ +public interface StickyHeaderAdapter { + + long getHeaderId(int position); + VH onCreateHeaderViewHolder(ViewGroup parent); + void onBindHeaderViewHolder(VH holder, int position); + int getItemCount(); +} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/StickyHeaderDecoration.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/StickyHeaderDecoration.java new file mode 100644 index 0000000..074b954 --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/StickyHeaderDecoration.java @@ -0,0 +1,158 @@ +package hikapro.com.backpack.presenter.adapters.helper.items; + +import android.graphics.Canvas; +import android.graphics.Rect; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.SparseArray; +import android.view.View; +import android.widget.LinearLayout; + +/** + * Created by tariel on 01/05/16. + */ +public class StickyHeaderDecoration extends RecyclerView.ItemDecoration { + + private final StickyHeaderAdapter adapter; + private final ItemVisibilityAdapter visibilityAdapter; + private final SparseArray headerRects = new SparseArray<>(); + private final HeaderProvider headerProvider; + private final HeaderPositionCalculator headerPositionCalculator; + private final HeaderRenderer renderer; + private final DimensionCalculator dimensionCalculator; + + private final Rect rect = new Rect(); + + + public StickyHeaderDecoration(StickyHeaderAdapter adapter) { + this(adapter, new DimensionCalculator(), null); + } + + private StickyHeaderDecoration(StickyHeaderAdapter adapter, + DimensionCalculator dimensionCalculator, ItemVisibilityAdapter visibilityAdapter) { + this(adapter, dimensionCalculator, new HeaderRenderer(), new HeaderViewCache(adapter), visibilityAdapter); + } + + private StickyHeaderDecoration(StickyHeaderAdapter adapter, DimensionCalculator dimensionCalculator, + HeaderRenderer headerRenderer, HeaderProvider headerProvider, + ItemVisibilityAdapter visibilityAdapter) { + this(adapter, headerRenderer, dimensionCalculator, headerProvider, + new HeaderPositionCalculator(adapter, headerProvider, + dimensionCalculator), visibilityAdapter); + } + + private StickyHeaderDecoration(StickyHeaderAdapter adapter, HeaderRenderer headerRenderer, + DimensionCalculator dimensionCalculator, HeaderProvider headerProvider, + HeaderPositionCalculator headerPositionCalculator, ItemVisibilityAdapter visibilityAdapter) { + this.adapter = adapter; + this.headerProvider = headerProvider; + this.renderer = headerRenderer; + this.dimensionCalculator = dimensionCalculator; + this.headerPositionCalculator = headerPositionCalculator; + this.visibilityAdapter = visibilityAdapter; + } + + + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { + super.getItemOffsets(outRect, view, parent, state); + + int itemPosition = parent.getChildAdapterPosition(view); + if (itemPosition == RecyclerView.NO_POSITION) { + return; + } + if (headerPositionCalculator.hasNewHeader(itemPosition)) { + View header = getHeaderView(parent, itemPosition); + setItemOffsetsForHeader(outRect, header, LinearLayout.VERTICAL); + } + } + + /** + * Sets the offsets for the first item in a section to make room for the header view + * + * @param itemOffsets rectangle to define offsets for the item + * @param header view used to calculate offset for the item + * @param orientation used to calculate offset for the item + */ + private void setItemOffsetsForHeader(Rect itemOffsets, View header, int orientation) { + dimensionCalculator.initMargins(rect, header); + if (orientation == LinearLayoutManager.VERTICAL) { + itemOffsets.top = header.getHeight() + rect.top + rect.bottom; + } else { + itemOffsets.left = header.getWidth() + rect.left + + rect.right; + } + } + + @Override + public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { + super.onDrawOver(c, parent, state); + + final int childCount = parent.getChildCount(); + if (childCount <= 0 || adapter.getItemCount() <= 0) { + return; + } + + for (int i = 0; i < childCount; i++) { + View itemView = parent.getChildAt(i); + int position = parent.getChildAdapterPosition(itemView); + if (position == RecyclerView.NO_POSITION) { + continue; + } + + boolean hasStickyHeader = headerPositionCalculator.hasStickyHeader(itemView, LinearLayout.VERTICAL, position); + if (hasStickyHeader || headerPositionCalculator.hasNewHeader(position)) { + View header = headerProvider.getHeader(parent, position); + //re-use existing Rect, if any. + Rect headerOffset = headerRects.get(position); + if (headerOffset == null) { + headerOffset = new Rect(); + headerRects.put(position, headerOffset); + } + headerPositionCalculator.initHeaderBounds(headerOffset, parent, header, itemView, hasStickyHeader); + renderer.drawHeader(parent, c, header, headerOffset); + } + } + } + + /** + * Gets the position of the header under the specified (x, y) coordinates. + * + * @param x x-coordinate + * @param y y-coordinate + * @return position of header, or -1 if not found + */ + public int findHeaderPositionUnder(int x, int y) { + for (int i = 0; i < headerRects.size(); i++) { + Rect rect = headerRects.get(headerRects.keyAt(i)); + if (rect.contains(x, y)) { + int position = headerRects.keyAt(i); + if (visibilityAdapter == null || visibilityAdapter.isPositionVisible(position)) { + return position; + } + } + } + return -1; + } + + /** + * Gets the header view for the associated position. If it doesn't exist yet, it will be + * created, measured, and laid out. + * + * @param parent the recyclerview + * @param position the position to get the header view for + * @return Header view + */ + public View getHeaderView(RecyclerView parent, int position) { + return headerProvider.getHeader(parent, position); + } + + /** + * Invalidates cached headers. This does not invalidate the recyclerview, you should do that manually after + * calling this method. + */ + public void invalidateHeaders() { + headerProvider.invalidate(); + headerRects.clear(); + } +} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/ItemTouchHelperAdapter.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/ItemTouchHelperAdapter.java similarity index 77% rename from app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/ItemTouchHelperAdapter.java rename to app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/ItemTouchHelperAdapter.java index bce3ead..a1377aa 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/ItemTouchHelperAdapter.java +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/ItemTouchHelperAdapter.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.presenter.adapters.helper; +package hikapro.com.backpack.presenter.adapters.helper.sets; /** * Created by N551 on 25.04.2016. diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/ItemTouchHelperViewHolder.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/ItemTouchHelperViewHolder.java similarity index 73% rename from app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/ItemTouchHelperViewHolder.java rename to app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/ItemTouchHelperViewHolder.java index 0060002..96085d0 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/ItemTouchHelperViewHolder.java +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/ItemTouchHelperViewHolder.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.presenter.adapters.helper; +package hikapro.com.backpack.presenter.adapters.helper.sets; /** * Created by N551 on 25.04.2016. diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/OnStartDragListener.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/OnStartDragListener.java similarity index 78% rename from app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/OnStartDragListener.java rename to app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/OnStartDragListener.java index aca2fbe..02770ec 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/OnStartDragListener.java +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/OnStartDragListener.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.presenter.adapters.helper; +package hikapro.com.backpack.presenter.adapters.helper.sets; import android.support.v7.widget.RecyclerView; diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/SimpleItemTouchHelperCallback.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/SimpleItemTouchHelperCallback.java similarity index 98% rename from app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/SimpleItemTouchHelperCallback.java rename to app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/SimpleItemTouchHelperCallback.java index c8774b3..0e70a70 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/SimpleItemTouchHelperCallback.java +++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/SimpleItemTouchHelperCallback.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.presenter.adapters.helper; +package hikapro.com.backpack.presenter.adapters.helper.sets; import android.graphics.Canvas; import android.support.v7.widget.RecyclerView; diff --git a/app/src/main/java/hikapro/com/backpack/view/View.java b/app/src/main/java/hikapro/com/backpack/view/View.java index 9e02aba..45148c1 100644 --- a/app/src/main/java/hikapro/com/backpack/view/View.java +++ b/app/src/main/java/hikapro/com/backpack/view/View.java @@ -1,12 +1,11 @@ package hikapro.com.backpack.view; import android.content.Context; -import android.widget.Toast; import hikapro.com.backpack.model.entities.Item; import hikapro.com.backpack.model.entities.Set; import hikapro.com.backpack.presenter.Presenter; -import hikapro.com.backpack.presenter.adapters.helper.OnStartDragListener; +import hikapro.com.backpack.presenter.adapters.helper.sets.OnStartDragListener; /** * Created by tariel on 19/04/16. diff --git a/app/src/main/java/hikapro/com/backpack/view/fragments/ItemListFragment.java b/app/src/main/java/hikapro/com/backpack/view/fragments/ItemListFragment.java index 36bef71..67a4c3a 100644 --- a/app/src/main/java/hikapro/com/backpack/view/fragments/ItemListFragment.java +++ b/app/src/main/java/hikapro/com/backpack/view/fragments/ItemListFragment.java @@ -7,8 +7,12 @@ import android.content.Context; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.SearchView; import android.widget.Toast; import hikapro.com.backpack.R; @@ -18,7 +22,8 @@ import hikapro.com.backpack.presenter.ItemListPresenter; import hikapro.com.backpack.presenter.Presenter; -public class ItemListFragment extends Fragment implements hikapro.com.backpack.view.View.ItemList { +public class ItemListFragment extends Fragment implements hikapro.com.backpack.view.View.ItemList, + SearchView.OnQueryTextListener { private static final String BUNDLE_SET_KEY = "BUNDLE_SET_KEY"; private hikapro.com.backpack.view.View.ActivityCallback activityCallback; @@ -40,6 +45,34 @@ public class ItemListFragment extends Fragment implements hikapro.com.backpack.v return ret; } + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + + inflater.inflate(R.menu.menu_main, menu); + + final MenuItem item = menu.findItem(R.id.action_search); + final SearchView searchView = (SearchView) item.getActionView(); + searchView.setOnQueryTextListener(this); + super.onCreateOptionsMenu(menu, inflater); + + } + + @Override + public boolean onQueryTextSubmit(String query) { + return false; + } + + @Override + public boolean onQueryTextChange(String newText) { + presenter.filter(newText); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + return super.onOptionsItemSelected(item); + } + // life cycle --> @Override public void onAttach(Context context) { @@ -65,6 +98,7 @@ public class ItemListFragment extends Fragment implements hikapro.com.backpack.v @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setHasOptionsMenu(true); Log.i(this.toString(), "onCreate"); } @Override diff --git a/app/src/main/java/hikapro/com/backpack/view/fragments/SetListFragment.java b/app/src/main/java/hikapro/com/backpack/view/fragments/SetListFragment.java index d2b4985..9537794 100644 --- a/app/src/main/java/hikapro/com/backpack/view/fragments/SetListFragment.java +++ b/app/src/main/java/hikapro/com/backpack/view/fragments/SetListFragment.java @@ -6,7 +6,6 @@ import android.app.Fragment; import android.content.Context; import android.os.Bundle; import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.helper.ItemTouchHelper; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -14,7 +13,7 @@ import android.view.ViewGroup; import hikapro.com.backpack.model.entities.Set; import hikapro.com.backpack.presenter.Presenter; -import hikapro.com.backpack.presenter.adapters.helper.OnStartDragListener; +import hikapro.com.backpack.presenter.adapters.helper.sets.OnStartDragListener; public class SetListFragment extends Fragment implements hikapro.com.backpack.view.View.SetList, OnStartDragListener{ diff --git a/app/src/main/java/hikapro/com/backpack/view/recycler/CategoryViewHolder.java b/app/src/main/java/hikapro/com/backpack/view/recycler/CategoryViewHolder.java deleted file mode 100644 index 62352e9..0000000 --- a/app/src/main/java/hikapro/com/backpack/view/recycler/CategoryViewHolder.java +++ /dev/null @@ -1,31 +0,0 @@ -package hikapro.com.backpack.view.recycler; - -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.view.View; -import android.widget.LinearLayout; -import android.widget.TextView; - -import hikapro.com.backpack.R; - -/** - * Created by tariel on 20/04/16. - */ -public class CategoryViewHolder extends RecyclerView.ViewHolder { - - public LinearLayout section; - public TextView sectionText; - public RecyclerView itemsRecycler; - - public CategoryViewHolder(View v) { - super(v); - setupViews(v); - } - - private void setupViews(View view) { - section = (LinearLayout)view.findViewById(R.id.linear); - sectionText = (TextView)view.findViewById(R.id.section_text); - itemsRecycler = (RecyclerView) view.findViewById(R.id.category_inner_recycler); - } - -} diff --git a/app/src/main/java/hikapro/com/backpack/view/recycler/HeaderViewHolder.java b/app/src/main/java/hikapro/com/backpack/view/recycler/HeaderViewHolder.java new file mode 100644 index 0000000..95de0c9 --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/view/recycler/HeaderViewHolder.java @@ -0,0 +1,25 @@ +package hikapro.com.backpack.view.recycler; + +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.TextView; + +import hikapro.com.backpack.R; + +/** + * Created by tariel on 01/05/16. + */ +public class HeaderViewHolder extends RecyclerView.ViewHolder { + + public int id; + public TextView title; + + public HeaderViewHolder(View v) { + super(v); + setupViews(v); + } + + private void setupViews(View view) { + title = (TextView) view.findViewById(R.id.header); + } +} diff --git a/app/src/main/java/hikapro/com/backpack/view/recycler/ItemViewHolder.java b/app/src/main/java/hikapro/com/backpack/view/recycler/ItemViewHolder.java index 5a86246..b448f3f 100644 --- a/app/src/main/java/hikapro/com/backpack/view/recycler/ItemViewHolder.java +++ b/app/src/main/java/hikapro/com/backpack/view/recycler/ItemViewHolder.java @@ -2,18 +2,21 @@ package hikapro.com.backpack.view.recycler; import android.support.v7.widget.RecyclerView; import android.view.View; +import android.widget.Button; import android.widget.TextView; import hikapro.com.backpack.R; + /** - * Created by tariel on 20/04/16. + * Created by tariel on 01/05/16. */ public class ItemViewHolder extends RecyclerView.ViewHolder { - public TextView title; public int id; public int categoryId; + public TextView title; + public Button undoButton; public ItemViewHolder(View v) { super(v); @@ -21,6 +24,7 @@ public class ItemViewHolder extends RecyclerView.ViewHolder { } private void setupViews(View view) { - title = (TextView) view.findViewById(R.id.item_text); + title = (TextView) view.findViewById(R.id.item_txt); + undoButton = (Button) view.findViewById(R.id.undo_button); } } diff --git a/app/src/main/java/hikapro/com/backpack/view/recycler/SetViewHolder.java b/app/src/main/java/hikapro/com/backpack/view/recycler/SetViewHolder.java index e57463f..ed82162 100644 --- a/app/src/main/java/hikapro/com/backpack/view/recycler/SetViewHolder.java +++ b/app/src/main/java/hikapro/com/backpack/view/recycler/SetViewHolder.java @@ -8,7 +8,7 @@ import android.view.View; import android.widget.TextView; import hikapro.com.backpack.R; -import hikapro.com.backpack.presenter.adapters.helper.ItemTouchHelperViewHolder; +import hikapro.com.backpack.presenter.adapters.helper.sets.ItemTouchHelperViewHolder; /** * Created by tariel on 20/04/16. diff --git a/app/src/main/res/drawable/ic_clear_24dp.xml b/app/src/main/res/drawable/ic_clear_24dp.xml new file mode 100644 index 0000000..ede4b71 --- /dev/null +++ b/app/src/main/res/drawable/ic_clear_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/fragment_item_list.xml b/app/src/main/res/layout/fragment_item_list.xml index 361ca3b..ca43fc4 100644 --- a/app/src/main/res/layout/fragment_item_list.xml +++ b/app/src/main/res/layout/fragment_item_list.xml @@ -6,7 +6,7 @@ android:orientation="vertical"> - - \ No newline at end of file diff --git a/app/src/main/res/layout/section.xml b/app/src/main/res/layout/section.xml deleted file mode 100644 index 973fa15..0000000 --- a/app/src/main/res/layout/section.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/sticky_header_layout.xml b/app/src/main/res/layout/sticky_header_layout.xml new file mode 100644 index 0000000..dc7a091 --- /dev/null +++ b/app/src/main/res/layout/sticky_header_layout.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_header.xml b/app/src/main/res/layout/view_header.xml new file mode 100644 index 0000000..115b9c6 --- /dev/null +++ b/app/src/main/res/layout/view_header.xml @@ -0,0 +1,16 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_item.xml b/app/src/main/res/layout/view_item.xml new file mode 100644 index 0000000..d7557bf --- /dev/null +++ b/app/src/main/res/layout/view_item.xml @@ -0,0 +1,30 @@ + + + + +