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/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/gradle.xml b/.idea/gradle.xml
index 508b3d9..c3bf4f7 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -5,6 +5,7 @@
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 477b86a..fbb6828 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -37,37 +37,10 @@
-
+
-
-
-
-
- 1.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/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..38eece3 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;
@@ -40,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);
}
@@ -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..459617b 100644
--- a/app/src/main/java/hikapro/com/backpack/model/ItemModel.java
+++ b/app/src/main/java/hikapro/com/backpack/model/ItemModel.java
@@ -1,48 +1,63 @@
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.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.presenter.Presenter;
-import retrofit2.Call;
-import retrofit2.Callback;
-import retrofit2.Response;
/**
* Created by tariel on 22/04/16.
*/
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 int currentSet;
+ private Hashtable
categoriesCache;
+ private List- itemsCache;
+ private List
- itemsDiscardCache;
+
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.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();
+ dao.registerObserver(this);
}
// categories -->
@Override
public Category getCategoryByPosition(int position) {
- return sortedCategories.get(position);
- }
- @Override
- public int getCategoriesCount() {
- return sortedCategories.size();
+ Category ret = null;
+ if (cache.containsKey(currentSet))
+ ret = categoriesCache.get(cache.get(currentSet).get(position).getCategory());
+ return ret;
+
}
// categories <--
@@ -51,41 +66,108 @@ 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) {
+ List
- items = cache.get(currentSet);
Item item = null;
- for (Item i : rawItems) {
- if (i.getId() == id) {
- item = i;
- break;
+ if (items != null)
+ {
+ for (Item i : items) {
+ if (i.getId() == id) {
+ item = i;
+ break;
+ }
}
}
return item;
}
@Override
- public Item getItemByPosition(int categoryId, int position) {
+ public Item getItemByPosition(int position) {
Item ret = null;
- Category category = findSortedCategory(categoryId);
- if (category != null) {
- ret = items.get(category).get(position);
- }
+ if (cache.containsKey(currentSet))
+ ret = cache.get(currentSet).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();
+ 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);
}
- return ret;
}
+
+ @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 -->
@@ -101,56 +183,73 @@ public class ItemModel implements Model.Item {
presenter = null;
}
}
- @Override
- public void sendMessage(String message) {
+
+ private void sendMessage(String message) {
presenter.showMessage(message);
}
+
// events <--
// process -->
@Override
public void executeQuery() {
- if (rawCategories.isEmpty() || rawItems.isEmpty()) {
- loadCategories();
- loadItems();
- } else {
+ Message command;
+
+ if (cache.contains(currentSet)) {
notifyDataSetChanged();
+ } else {
+ 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
+ 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.ITEM_CATEGORY_LOAD_ERROR :
+ break;
+ case Event.SET_ITEMS_LOAD_COMPLETED :
+ List
- res = (List
- ) event.obj;
+ cache.put(event.arg1, res);
+ notifyDataSetChanged();
+ break;
+ case Event.ITEM_CATEGORY_LOAD_COMPLETED :
+ categoriesCache = (Hashtable
)event.obj;
+ 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 -->
@@ -159,6 +258,7 @@ public class ItemModel implements Model.Item {
@Override
public void setPresenter(Presenter.ItemList presenter) {
this.presenter = presenter;
+ this.currentSet = presenter.getCurrentSet().getId();
}
@Override
@@ -204,7 +304,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 0f89f31..157a71b 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,34 +17,43 @@ public interface Model {
interface Base {
void onDestroy(boolean isConfigurationChanging);
void executeQuery();
- void sendMessage(String message);
+ void notifyDataSetChanged();
+ 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();
+ //GLM
+ List getSets(); // tag renamed
+ void setsReorderNotify();
}
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 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..cea372c 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.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;
-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,63 +48,76 @@ 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() {
presenter.notifyDataSetChanged();
}
- @Override
- public void sendMessage(String message) {
+
+ private void sendMessage(String message) {
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() {
+ @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;
+ dao.executeCommand(command);
}
- // process <--
+ //endregion
- // other -->
+ //region other
@Override
public void setPresenter(Presenter.SetList presenter) {
@@ -111,12 +129,14 @@ public class SetModel implements Model.Set {
return presenter;
}
- // other <--
-
-
-
-
+ //endregion
+ //GLM
+ @Override
+ public List getSets()
+ {
+ return cache;
+ }
}
diff --git a/app/src/main/java/hikapro/com/backpack/model/dao/Command.java b/app/src/main/java/hikapro/com/backpack/model/dao/Command.java
new file mode 100644
index 0000000..5542a54
--- /dev/null
+++ b/app/src/main/java/hikapro/com/backpack/model/dao/Command.java
@@ -0,0 +1,33 @@
+package hikapro.com.backpack.model.dao;
+
+/**
+ * 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 ITEM_GET_CATEGORIES = 0x7C;
+
+ 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/dao/DAO.java b/app/src/main/java/hikapro/com/backpack/model/dao/DAO.java
new file mode 100644
index 0000000..44a87e9
--- /dev/null
+++ b/app/src/main/java/hikapro/com/backpack/model/dao/DAO.java
@@ -0,0 +1,744 @@
+package hikapro.com.backpack.model.dao;
+
+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.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 static final int KEEP_ALIVE_TIME = 1;
+ private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
+
+ 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_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);
+ 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();
+ }
+
+ 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);
+ }
+ } 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;
+
+ 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);
+ }
+ }
+ // 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);
+ message.what = Event.SET_ITEMS_LOAD_COMPLETED;
+ message.obj = items;
+ 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/dao/Db.java
similarity index 63%
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 de45801..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,4 +1,4 @@
-package hikapro.com.backpack.model.database;
+package hikapro.com.backpack.model.dao;
import android.content.ContentValues;
import android.database.Cursor;
@@ -12,6 +12,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 +25,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 +53,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 +126,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 +150,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 +169,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 +193,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/dao/DbHelper.java
similarity index 90%
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 3e3668c..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;
@@ -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/dao/Event.java b/app/src/main/java/hikapro/com/backpack/model/dao/Event.java
new file mode 100644
index 0000000..e4ada30
--- /dev/null
+++ b/app/src/main/java/hikapro/com/backpack/model/dao/Event.java
@@ -0,0 +1,53 @@
+package hikapro.com.backpack.model.dao;
+
+/**
+ * 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_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;
+ 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/dao/Test.java b/app/src/main/java/hikapro/com/backpack/model/dao/Test.java
new file mode 100644
index 0000000..f1464cf
--- /dev/null
+++ b/app/src/main/java/hikapro/com/backpack/model/dao/Test.java
@@ -0,0 +1,15 @@
+package hikapro.com.backpack.model.dao;
+
+/**
+ * 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/database/DAO.java b/app/src/main/java/hikapro/com/backpack/model/database/DAO.java
deleted file mode 100644
index 3f2e0c6..0000000
--- a/app/src/main/java/hikapro/com/backpack/model/database/DAO.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package hikapro.com.backpack.model.database;
-
-import android.content.Context;
-import android.database.sqlite.SQLiteDatabase;
-
-/**
- * Created by tariel on 20/04/16.
- */
-public class DAO {
-
- private Context context;
- private DbHelper helper;
-
- public DAO(Context context) {
- this.context = context;
- this.helper = new DbHelper(this.context);
- }
-
- private SQLiteDatabase getReadDB(){
- return helper.getReadableDatabase();
- }
-
- private SQLiteDatabase getWriteDB(){
- return helper.getWritableDatabase();
- }
-}
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;
+}
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..1ee0ebb 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 -->
@@ -52,14 +51,30 @@ public class ItemListPresenter implements Presenter.ItemList {
}
@Override
public android.view.View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ /*
if (savedInstanceState != null)
- set = (Set) savedInstanceState.getSerializable(BUNDLE_SET_LIST_KEY);
+ 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 +86,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 +114,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 +150,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 f7d927a..347c886 100644
--- a/app/src/main/java/hikapro/com/backpack/presenter/Presenter.java
+++ b/app/src/main/java/hikapro/com/backpack/presenter/Presenter.java
@@ -2,13 +2,13 @@ package hikapro.com.backpack.presenter;
import android.content.Context;
import android.os.Bundle;
+import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
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;
@@ -34,24 +34,26 @@ 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 {
- 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 11cd528..02604a1 100644
--- a/app/src/main/java/hikapro/com/backpack/presenter/SetListPresenter.java
+++ b/app/src/main/java/hikapro/com/backpack/presenter/SetListPresenter.java
@@ -5,17 +5,20 @@ 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.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.sets.OnStartDragListener;
+import hikapro.com.backpack.presenter.adapters.helper.sets.SimpleItemTouchHelperCallback;
import hikapro.com.backpack.view.View;
import hikapro.com.backpack.view.recycler.SetViewHolder;
@@ -28,12 +31,34 @@ 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.getSets().remove(position);
+ adapter.notifyItemRemoved(position);
+ }
+
+ //GLM_add_resources_SetList
+ @Override
+ public boolean onItemMove(int fromPosition, int toPosition) {
+ Collections.swap(model.getSets(), fromPosition, toPosition);
+ adapter.notifyItemMoved(fromPosition, toPosition);
+ model.setsReorderNotify();
+ return true;
+ }
+
@Override
public void onDestroy(boolean isChangingConfiguration) {
view = null;
@@ -52,6 +77,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 +112,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 +170,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 +190,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/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..230c723 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,151 @@ 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(final 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.title.setOnClickListener(new android.view.View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ presenter.clickItem(holder.id);
+ }
+ });
+ 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 15034fb..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,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.sets.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/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/sets/ItemTouchHelperAdapter.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/ItemTouchHelperAdapter.java
new file mode 100644
index 0000000..a1377aa
--- /dev/null
+++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/ItemTouchHelperAdapter.java
@@ -0,0 +1,12 @@
+package hikapro.com.backpack.presenter.adapters.helper.sets;
+
+/**
+ * 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/sets/ItemTouchHelperViewHolder.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/ItemTouchHelperViewHolder.java
new file mode 100644
index 0000000..96085d0
--- /dev/null
+++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/ItemTouchHelperViewHolder.java
@@ -0,0 +1,12 @@
+package hikapro.com.backpack.presenter.adapters.helper.sets;
+
+/**
+ * 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/sets/OnStartDragListener.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/OnStartDragListener.java
new file mode 100644
index 0000000..02770ec
--- /dev/null
+++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/OnStartDragListener.java
@@ -0,0 +1,12 @@
+package hikapro.com.backpack.presenter.adapters.helper.sets;
+
+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/sets/SimpleItemTouchHelperCallback.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/SimpleItemTouchHelperCallback.java
new file mode 100644
index 0000000..0e70a70
--- /dev/null
+++ b/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/SimpleItemTouchHelperCallback.java
@@ -0,0 +1,93 @@
+package hikapro.com.backpack.presenter.adapters.helper.sets;
+
+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..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,11 +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.sets.OnStartDragListener;
/**
* Created by tariel on 19/04/16.
@@ -19,6 +19,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/ItemListFragment.java b/app/src/main/java/hikapro/com/backpack/view/fragments/ItemListFragment.java
index 36bef71..ccb05a8 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,14 +98,15 @@ 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
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
- View view = presenter.onCreateView(inflater, container, savedInstanceState);
presenter.setView(this);
+ View view = presenter.onCreateView(inflater, container, savedInstanceState);
Log.i(this.toString(), "onCreateView");
return view;
}
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..4feaf63 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
@@ -1,22 +1,21 @@
package hikapro.com.backpack.view.fragments;
-
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
+import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.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.sets.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;
@@ -60,10 +59,12 @@ public class SetListFragment extends Fragment implements hikapro.com.backpack.vi
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- // Inflate the layout for this fragment
- View view = presenter.onCreateView(inflater, container, savedInstanceState);
- presenter.setView(this);
Log.i(this.toString(), "onCreateView");
+ // Inflate the layout for this fragment
+ presenter.setView(this);
+ View view = presenter.onCreateView(inflater, container, savedInstanceState);
+
+
return view;
}
@Override
@@ -130,4 +131,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/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 c51778c..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
@@ -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.sets.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);
}
}
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 b722a57..0000000
--- a/app/src/main/res/layout/section.xml
+++ /dev/null
@@ -1,25 +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 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..001b261
--- /dev/null
+++ b/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,20 @@
+
+
+ app:showAsAction="never" />
+
+
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 47c8224..361540a 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -2,4 +2,5 @@
16dp
16dp
+ 16dp