diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ee72e5f..0a8ca4c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ > getItems(); - @GET("/api/v1/backpack/item_categories") + @GET("api/v1/backpack/item_categories") Call> getItemCategories(); - @GET("/api/v1/backpack/sets") + @GET("api/v1/backpack/sets") Call> getSets(); + + @GET("api/v1/backpack/updates/timestamp") + Call getTimestamp(); + + @GET("api/v1/backpack/updates/all") + Call getUpdates(@Query("timestamp") long timestamp); + + } diff --git a/app/src/main/java/hikapro/com/backpack/model/DetailModel.java b/app/src/main/java/hikapro/com/backpack/model/DetailModel.java index 0abe09f..d256ee2 100644 --- a/app/src/main/java/hikapro/com/backpack/model/DetailModel.java +++ b/app/src/main/java/hikapro/com/backpack/model/DetailModel.java @@ -1,5 +1,7 @@ package hikapro.com.backpack.model; +import android.os.Message; + import hikapro.com.backpack.model.entities.Item; import hikapro.com.backpack.presenter.Presenter; @@ -66,4 +68,9 @@ public class DetailModel implements Model.Detail { public Presenter.ItemDetail getPresenter() { return presenter; } + + @Override + public void onEvent(Message event) { + + } } diff --git a/app/src/main/java/hikapro/com/backpack/model/ItemModel.java b/app/src/main/java/hikapro/com/backpack/model/ItemModel.java index 2bff2bc..e13f99d 100644 --- a/app/src/main/java/hikapro/com/backpack/model/ItemModel.java +++ b/app/src/main/java/hikapro/com/backpack/model/ItemModel.java @@ -1,14 +1,20 @@ package hikapro.com.backpack.model; +import android.os.Message; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Hashtable; import java.util.List; +import hikapro.com.backpack.model.database.Command; +import hikapro.com.backpack.model.database.DAO; +import hikapro.com.backpack.model.database.Event; import hikapro.com.backpack.model.entities.Category; import hikapro.com.backpack.model.entities.Item; +import hikapro.com.backpack.model.entities.Set; import hikapro.com.backpack.presenter.Presenter; import retrofit2.Call; import retrofit2.Callback; @@ -19,19 +25,21 @@ import retrofit2.Response; */ public class ItemModel implements Model.Item { - private Api api; private Presenter.ItemList presenter; private List rawCategories; private List sortedCategories; private List rawItems; + private DAO dao; private Hashtable> items; + private Hashtable>> cache; public ItemModel() { - this.api = RestClient.getApi(); this.rawCategories = new ArrayList<>(); this.rawItems = new ArrayList<>(); this.sortedCategories = new ArrayList<>(); + this.cache = new Hashtable<>(16, 0.9f); + this.dao = DAO.getInstance(); } // categories --> @@ -111,46 +119,45 @@ public class ItemModel implements Model.Item { @Override public void executeQuery() { - if (rawCategories.isEmpty() || rawItems.isEmpty()) { - loadCategories(); - loadItems(); - } else { - notifyDataSetChanged(); + Message command = Message.obtain(); + command.what = Command.SET_GET_ITEMS; + command.arg1 = presenter.getCurrentSet().getId(); + dao.executeCommand(command); + } + @Override + public void onEvent(Message event) { + + switch (event.what) { + case Event.SET_ITEMS_LOAD_ERROR : + break; + case Event.ITEM_FROM_SET_ERROR : + break; + case Event.ITEM_DELETE_ERROR : + break; + case Event.ITEM_PACK_ERROR : + break; + case Event.ITEM_UNPACK_ERROR : + break; + case Event.ITEM_INSERT_ERROR : + break; + case Event.SET_ITEMS_LOAD_COMPLETED : + Hashtable> res = (Hashtable>) event.obj; + cache.put(event.arg1, res); + notifyDataSetChanged(); + break; + case Event.ITEM_FROM_SET_DELETED : + break; + case Event.ITEM_DELETED : + break; + case Event.ITEM_PACKED : + break; + case Event.ITEM_UNPACKED : + break; + case Event.ITEM_INSERTED : + break; } + } - } - private void loadCategories() { - Call> call = api.getItemCategories(); - call.enqueue(new Callback>() { - @Override - public void onResponse(Call> call, Response> response) { - int statusCode = response.code(); - rawCategories = response.body(); - } - @Override - public void onFailure(Call> call, Throwable t) { - - } - }); - } - private void loadItems() { - Call> call2 = api.getItems(); - call2.enqueue(new Callback>() { - @Override - public void onResponse(Call> call, Response> response) { - int statusCode = response.code(); - rawItems = response.body(); - initData(); - } - @Override - public void onFailure(Call> call, Throwable t) { - - } - }); - } - private void syncData() { - // TODO sync data here - } // process <-- // other --> diff --git a/app/src/main/java/hikapro/com/backpack/model/Model.java b/app/src/main/java/hikapro/com/backpack/model/Model.java index 0f89f31..6e06da9 100644 --- a/app/src/main/java/hikapro/com/backpack/model/Model.java +++ b/app/src/main/java/hikapro/com/backpack/model/Model.java @@ -1,5 +1,7 @@ package hikapro.com.backpack.model; +import android.os.Message; + import java.util.List; import hikapro.com.backpack.model.entities.Category; @@ -15,14 +17,15 @@ public interface Model { interface Base { void onDestroy(boolean isConfigurationChanging); void executeQuery(); + void notifyDataSetChanged(); void sendMessage(String message); + void onEvent(Message event); } interface Set extends Base { hikapro.com.backpack.model.entities.Set getSetByPosition(int position); hikapro.com.backpack.model.entities.Set findSet(int id); int getSetsCount(); - void notifyDataSetChanged(); void setPresenter(Presenter.SetList presenter); Presenter.SetList getPresenter(); } @@ -35,14 +38,12 @@ public interface Model { int getItemsCount(int categoryId); hikapro.com.backpack.model.entities.Category getCategoryByPosition(int position); int getCategoriesCount(); - void notifyDataSetChanged(); void setPresenter(Presenter.ItemList presenter); Presenter.ItemList getPresenter(); } interface Detail extends Base { int getCount(); hikapro.com.backpack.model.entities.Item findItem(int id); - void notifyDataSetChanged(); void setPresenter(Presenter.ItemDetail presenter); Presenter.ItemDetail getPresenter(); diff --git a/app/src/main/java/hikapro/com/backpack/model/RestClient.java b/app/src/main/java/hikapro/com/backpack/model/RestClient.java index 0561ec9..6ae2133 100644 --- a/app/src/main/java/hikapro/com/backpack/model/RestClient.java +++ b/app/src/main/java/hikapro/com/backpack/model/RestClient.java @@ -11,7 +11,7 @@ import retrofit2.converter.gson.GsonConverterFactory; */ public class RestClient { - public static final String BASE_URL = "http://hikapro.com"; + public static final String BASE_URL = "http://hikapro.com/"; public static Api getApi() { diff --git a/app/src/main/java/hikapro/com/backpack/model/SetModel.java b/app/src/main/java/hikapro/com/backpack/model/SetModel.java index e796815..aebc326 100644 --- a/app/src/main/java/hikapro/com/backpack/model/SetModel.java +++ b/app/src/main/java/hikapro/com/backpack/model/SetModel.java @@ -1,38 +1,43 @@ package hikapro.com.backpack.model; +import android.os.Message; + import java.util.ArrayList; import java.util.List; +import hikapro.com.backpack.model.database.Command; +import hikapro.com.backpack.model.database.DAO; +import hikapro.com.backpack.model.database.Event; import hikapro.com.backpack.model.entities.Set; import hikapro.com.backpack.presenter.Presenter; -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; + /** * Created by tariel on 20/04/16. */ public class SetModel implements Model.Set { - private List iList; - private Api api; + private List cache; private Presenter.SetList presenter; + private DAO dao; + public SetModel() { - this.api = RestClient.getApi(); - this.iList = new ArrayList<>(); + this.cache = new ArrayList<>(); + this.dao = DAO.getInstance(); + dao.registerObserver(this); } - // sets --> + //region sets @Override public hikapro.com.backpack.model.entities.Set getSetByPosition(int position) { - return iList.get(position); + return cache.get(position); } @Override public hikapro.com.backpack.model.entities.Set findSet(int id) { Set ret = null; - for (Set s : iList) { + for (Set s : cache) { if (s.getId() == id) { ret = s; break; @@ -43,19 +48,18 @@ public class SetModel implements Model.Set { @Override public int getSetsCount() { - return iList.size(); + return cache.size(); } - // sets <-- + //endregion - // events --> + //region events @Override public void onDestroy(boolean isConfigurationChanging) { if ( !isConfigurationChanging ) { presenter = null; } - } @Override public void notifyDataSetChanged() { @@ -67,39 +71,42 @@ public class SetModel implements Model.Set { presenter.showMessage(message); } - // events <-- + //endregion - // process --> + //region process @Override public void executeQuery() { - if (iList.isEmpty()) - loadSets(); - else - notifyDataSetChanged(); + Message command = Message.obtain(); + command.what = Command.SYNC_IF_NOT_EXISTS; + dao.executeCommand(command); + command = Message.obtain(); + command.what = Command.SET_GET_ALL; + dao.executeCommand(command); } - private void loadSets() { - Call> call = api.getSets(); - call.enqueue(new Callback>() { - @Override - public void onResponse(Call> call, Response> response) { - int statusCode = response.code(); - iList = response.body(); + @Override + public void onEvent(Message event) { + switch (event.what) { + case Event.SET_LOAD_ERROR : + break; + case Event.SET_ITEMS_LOAD_ERROR : + break; + case Event.SET_REORDER_ERROR : + break; + case Event.SET_LOAD_COMPLETED : + cache = (List) event.obj; notifyDataSetChanged(); - } - @Override - public void onFailure(Call> call, Throwable t) { - - } - }); + break; + case Event.SET_ITEMS_LOAD_COMPLETED : + break; + case Event.SET_REORDER_COMPLETED : + break; + } } - private void syncData() { + //endregion - } - // process <-- - - // other --> + //region other @Override public void setPresenter(Presenter.SetList presenter) { @@ -111,7 +118,7 @@ public class SetModel implements Model.Set { return presenter; } - // other <-- + //endregion diff --git a/app/src/main/java/hikapro/com/backpack/model/database/Command.java b/app/src/main/java/hikapro/com/backpack/model/database/Command.java new file mode 100644 index 0000000..15fdc8f --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/model/database/Command.java @@ -0,0 +1,32 @@ +package hikapro.com.backpack.model.database; + +/** + * Created by tariel on 27/04/16. + */ +public interface Command { + + int SET_SCOPE_END = 0x77; + int ITEM_SCOPE_END = 0x8B; + int MY_LIST_SCOPE_END = 0x9F; + + + int SET_GET_ALL = 0x64; + int SET_REORDER = 0x65; + int SET_GET_ITEMS = 0x66; + + int ITEM_DELETE_FROM_SET = 0x78; + int ITEM_INSERT = 0x79; + int ITEM_PACK = 0x7A; + int ITEM_UNPACK = 0x7B; + + int MY_LIST_POST = 0x8C; + int MY_LIST_ITEM_ADD = 0x8D; + int MY_LIST_ITEM_DELETE = 0x8E; + int MY_LIST_CLEAR = 0x8F; + + int SYNC = 0xA0; + int SYNC_IF_NOT_EXISTS = 0xA1; + + int TEST = 0xC8; + +} diff --git a/app/src/main/java/hikapro/com/backpack/model/database/DAO.java b/app/src/main/java/hikapro/com/backpack/model/database/DAO.java index 3f2e0c6..9d848be 100644 --- a/app/src/main/java/hikapro/com/backpack/model/database/DAO.java +++ b/app/src/main/java/hikapro/com/backpack/model/database/DAO.java @@ -1,21 +1,198 @@ package hikapro.com.backpack.model.database; +import android.content.ContentValues; import android.content.Context; +import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Process; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import hikapro.com.backpack.App; +import hikapro.com.backpack.model.Api; +import hikapro.com.backpack.model.Model; +import hikapro.com.backpack.model.RestClient; +import hikapro.com.backpack.model.SetModel; +import hikapro.com.backpack.model.entities.Category; +import hikapro.com.backpack.model.entities.Item; +import hikapro.com.backpack.model.entities.Set; +import hikapro.com.backpack.model.entities.Timestamp; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; /** * Created by tariel on 20/04/16. */ public class DAO { + //region Constants + private static final int CORE_POOL_SIZE = 1; + private static final int CORE_MAX_POOL_SIZE = 1; - private Context context; - private DbHelper helper; + private static final int KEEP_ALIVE_TIME = 1; + private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS; - public DAO(Context context) { - this.context = context; + private static final int MY_LIST_ID = 15; + //endregion + + private static DAO instance; + + private final ThreadPoolExecutor threadPool; + private final Handler handler; + private SetModel setModel; + private final Api api; + private Context context; + private DbHelper helper; + private Map observers; + + private DAO() { + this.context = App.getAppContext(); this.helper = new DbHelper(this.context); + this.api = RestClient.getApi(); + final BlockingQueue taskQueue = new LinkedBlockingQueue<>(); + this.threadPool = initPool(taskQueue); + this.observers = Collections.synchronizedMap(new HashMap()); + this.handler = initHandler(); } + static { + instance = new DAO(); + } + + public static DAO getInstance() { + return instance; + } + + public void registerObserver(Model.Base o) { + observers.put(o.getClass().getName(), o); + } + + private ThreadPoolExecutor initPool (BlockingQueue taskQueue) { + ThreadPoolExecutor ret = new ThreadPoolExecutor( + CORE_POOL_SIZE + ,CORE_MAX_POOL_SIZE + ,KEEP_ALIVE_TIME + ,KEEP_ALIVE_TIME_UNIT + ,taskQueue + ); + return ret; + } + + private Handler initHandler() { + Handler ret = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + for (Map.Entry entry : observers.entrySet()) { + entry.getValue().onEvent(msg); + } + } + }; + return ret; + } + + public void executeCommand(Message command) { + + SetTask setTask; + ItemTask itemTask; + + if (command != null) { + switch (command.what) { + + case Command.SET_GET_ALL : + setTask = new SetTask(Command.SET_GET_ALL, + Process.THREAD_PRIORITY_MORE_FAVORABLE); + threadPool.execute(setTask); + break; + + case Command.SET_REORDER : + setTask = new SetTask(Command.SET_REORDER, + Process.THREAD_PRIORITY_BACKGROUND); + setTask.setsToUpdate = (List) command.obj; + threadPool.execute(setTask); + break; + + case Command.SET_GET_ITEMS : + setTask = new SetTask(Command.SET_GET_ITEMS, + Process.THREAD_PRIORITY_MORE_FAVORABLE); + setTask.setId = command.arg1; + threadPool.execute(setTask); + break; + + case Command.ITEM_INSERT : + itemTask = new ItemTask(Command.ITEM_INSERT, + Process.THREAD_PRIORITY_BACKGROUND); + itemTask.item = (Item) command.obj; + itemTask.setId = command.arg1; + threadPool.execute(itemTask); + break; + + case Command.ITEM_DELETE_FROM_SET : + itemTask = new ItemTask(Command.ITEM_DELETE_FROM_SET, + Process.THREAD_PRIORITY_BACKGROUND); + itemTask.setId = command.arg1; + itemTask.itemId = command.arg2; + threadPool.execute(itemTask); + break; + + case Command.ITEM_PACK : + itemTask = new ItemTask(Command.ITEM_PACK, + Process.THREAD_PRIORITY_BACKGROUND); + itemTask.setId = command.arg1; + itemTask.itemId = command.arg2; + threadPool.execute(itemTask); + break; + + case Command.ITEM_UNPACK : + itemTask = new ItemTask(Command.ITEM_UNPACK, + Process.THREAD_PRIORITY_BACKGROUND); + itemTask.setId = command.arg1; + itemTask.itemId = command.arg2; + threadPool.execute(itemTask); + break; + + case Command.MY_LIST_ITEM_ADD : + break; + + case Command.MY_LIST_ITEM_DELETE : + break; + + case Command.MY_LIST_POST : + break; + + case Command.MY_LIST_CLEAR : + break; + + case Command.SYNC : + threadPool.execute(new SyncTask(Command.SYNC, + Process.THREAD_PRIORITY_BACKGROUND)); + break; + + case Command.SYNC_IF_NOT_EXISTS : + threadPool.execute(new SyncTask(Command.SYNC_IF_NOT_EXISTS, + Process.THREAD_PRIORITY_MORE_FAVORABLE)); + break; + } + } + } + + /////////////////////// DATABASE ///////////////////// + + //region Database private SQLiteDatabase getReadDB(){ return helper.getReadableDatabase(); } @@ -23,4 +200,544 @@ public class DAO { private SQLiteDatabase getWriteDB(){ return helper.getWritableDatabase(); } + + // inserts + private void insertTimestamp(Timestamp timestamp) { + if (timestamp != null && timestamp.timestamp > 0) { + ContentValues values; + SQLiteDatabase db = getWriteDB(); + try { + db.beginTransaction(); + values = Db.LogTable.toContentValues(timestamp); + db.insert(Db.LogTable.TABLE_NAME, null, values); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + db.close(); + } + } + } + private void insertItems(List items) { + if (items != null && !items.isEmpty()) { + ContentValues values; + SQLiteDatabase db = getWriteDB(); + try { + db.beginTransaction(); + for (Item item : items) { + values = Db.ItemsTable.toContentValues(item); + db.insert(Db.ItemsTable.TABLE_NAME, null, values); + } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + db.close(); + } + } + } + private void insertCategories(List categories) { + + if (categories != null && !categories.isEmpty()) { + ContentValues values; + SQLiteDatabase db = getWriteDB(); + try { + db.beginTransaction(); + for (Category category : categories) { + values = Db.CategoriesTable.toContentValues(category); + db.insert(Db.CategoriesTable.TABLE_NAME, null, values); + } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + db.close(); + } + } + } + private void insertSets(List sets) { + + if (sets != null && !sets.isEmpty()) { + ContentValues values; + int i = 0; + SQLiteDatabase db = getWriteDB(); + try { + db.beginTransaction(); + for (Set set : sets) { + values = Db.SetsTable.toContentValues(set, i); + db.insert(Db.SetsTable.TABLE_NAME, null, values); + insertSetItems(set, db); + ++i; + } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + db.close(); + } + } + } + private void insertSetItems(Set set, SQLiteDatabase db) { + if (set != null && db != null) { + if (!set.getItems().isEmpty()) { + ContentValues values; + int setId = set.getId(); + try { + db.beginTransaction(); + for (Integer itemid : set.getItems()) { + values = Db.SetItemsTable.toContentValues(setId, itemid); + db.insert(Db.SetItemsTable.TABLE_NAME, null, values); + } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + } + } + private void insertSetItem(int setId, int itemId) { + ContentValues values; + SQLiteDatabase db = getWriteDB(); + try { + db.beginTransaction(); + values = Db.SetItemsTable.toContentValues(setId, itemId); + db.insert(Db.SetItemsTable.TABLE_NAME, null, values); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + db.close(); + } + } + // reads + private boolean LogExist() { + boolean ret; + SQLiteDatabase db = getReadDB(); + Cursor cursor = db.query(Db.LogTable.TABLE_NAME, + new String[] {Db.LogTable.COLUMN_ID}, null, null, null, null, null, "1"); + ret = cursor.moveToNext(); + cursor.close(); + db.close(); + return ret; + } + private List readItems(int setId) { + List ret = new ArrayList<>(256); + Cursor cursor = null; + SQLiteDatabase db = null; + Item item; + String query = String.format( + "SELECT * FROM %s a INNER JOIN %s b ON a.%s = b.%s WHERE b.%s = ? AND b.%s <> 1 AND b.%s <> 1", + Db.ItemsTable.TABLE_NAME, + Db.SetItemsTable.TABLE_NAME, + Db.ItemsTable.COLUMN_ID, + Db.SetItemsTable.COLUMN_ITEM, + Db.SetItemsTable.COLUMN_SET, + Db.SetItemsTable.COLUMN_DELETED, + Db.SetItemsTable.COLUMN_PACKED); + try { + db = getReadDB(); + cursor = db.rawQuery(query, new String[]{String.valueOf(setId)}); + while (cursor.moveToNext()) { + item = Db.ItemsTable.parseCursor(cursor); + ret.add(item); + } + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + //TODO write to log here + } finally { + if (cursor != null) + cursor.close(); + if (db != null) + db.close(); + } + return ret; + } + private Hashtable readCategories() { + Hashtable ret = new Hashtable<>(20, 0.9f); + Cursor cursor = null; + SQLiteDatabase db = null; + Category category; + try { + db = getReadDB(); + cursor = db.query(Db.CategoriesTable.TABLE_NAME, + new String[]{Db.CategoriesTable.COLUMN_ID, + Db.CategoriesTable.COLUMN_NAME}, + null,null,null,null,null); + while (cursor.moveToNext()) { + category = Db.CategoriesTable.parseCursor(cursor); + ret.put(category.getId(), category.getName()); + } + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + //TODO write to log here + } finally { + if (cursor != null) + cursor.close(); + if (db != null) + db.close(); + } + return ret; + } + private List readSets() { + List ret = new ArrayList<>(12); + Cursor cursor = null; + SQLiteDatabase db = null; + Set set; + try { + db = getReadDB(); + cursor = db.query(Db.SetsTable.TABLE_NAME, + new String[]{Db.SetsTable.COLUMN_ID, + Db.SetsTable.COLUMN_ITEMS, + Db.SetsTable.COLUMN_LINE_NUMBER, + Db.SetsTable.COLUMN_NAME, + Db.SetsTable.COLUMN_PHOTO_LOCAL, + Db.SetsTable.COLUMN_PHOTO_THUMB_LOCAL, + Db.SetsTable.COLUMN_PHOTO_THUMB_URL, + Db.SetsTable.COLUMN_PHOTO_THUMBNAIL_LOCAL, + Db.SetsTable.COLUMN_PHOTO_THUMBNAIL_URL, + Db.SetsTable.COLUMN_PHOTO_URL}, + null,null,null,null, + Db.SetsTable.COLUMN_LINE_NUMBER); + while (cursor.moveToNext()) { + set = Db.SetsTable.parseCursor(cursor); + ret.add(set); + } + Collections.sort(ret); + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + //TODO write to log here + } finally { + if (cursor != null) + cursor.close(); + if (db != null) + db.close(); + } + return ret; + } + // updates + private int updateSetsOrder(List reorderedSet) { + int ret = 0; + SQLiteDatabase db = null; + ContentValues values; + try { + + db = getWriteDB(); + db.beginTransaction(); + for (Set set : reorderedSet) { + values = new ContentValues(); + values.put(Db.SetsTable.COLUMN_LINE_NUMBER, set.getLineNumber()); + ret += db.update(Db.SetsTable.TABLE_NAME, values, "_id = ?", + new String[]{String.valueOf(set.getId())}); + } + db.setTransactionSuccessful(); + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + //TODO write to log here + } finally { + if (db != null) { + db.endTransaction(); + db.close(); + } + } + return ret; + } + private int updateSetItemDeleted(int setId, int itemId, boolean del) { + int ret = 0; + SQLiteDatabase db = null; + ContentValues values; + try { + db = getWriteDB(); + db.beginTransaction(); + values = new ContentValues(); + values.put(Db.SetItemsTable.COLUMN_DELETED, del); + ret = db.update(Db.SetItemsTable.TABLE_NAME, values, String.format("%s = ? AND %s = ?", + Db.SetItemsTable.COLUMN_SET, Db.SetItemsTable.COLUMN_ITEM ), + new String[]{String.valueOf(setId), String.valueOf(itemId)}); + db.setTransactionSuccessful(); + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + //TODO write to log here + } finally { + if (db != null) { + db.endTransaction(); + db.close(); + } + } + return ret; + } + private int updateSetItemPacked(int setId, int itemId, boolean pack) { + int ret = 0; + SQLiteDatabase db = null; + ContentValues values; + try { + db = getWriteDB(); + db.beginTransaction(); + + values = new ContentValues(); + values.put(Db.SetItemsTable.COLUMN_PACKED, pack); + ret = db.update(Db.SetItemsTable.TABLE_NAME, values, String.format("%s = ? AND %s = ?", + Db.SetItemsTable.COLUMN_SET, Db.SetItemsTable.COLUMN_ITEM ), + new String[]{String.valueOf(setId), String.valueOf(itemId)}); + + if (pack) { + values = Db.SetItemsTable.toContentValues(MY_LIST_ID, itemId); + db.insert(Db.SetItemsTable.TABLE_NAME, null, values); + } else { + db.delete(Db.SetItemsTable.TABLE_NAME, String.format("%s = ? AND %s = ?", + Db.SetItemsTable.COLUMN_SET, Db.SetItemsTable.COLUMN_ITEM ), + new String[]{String.valueOf(MY_LIST_ID), String.valueOf(itemId) }); + } + db.setTransactionSuccessful(); + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + //TODO write to log here + } finally { + if (db != null) { + db.endTransaction(); + db.close(); + } + } + return ret; + } + //endregion + + /////////////////////// TASK CLASSES ////////////////// + + //region Task classes + + // MY LIST CLASS + private class MyListTask implements Runnable { + int currentCommand; + int priority; + + + public MyListTask(int command, int priority) { + this.currentCommand = command; + this.priority = priority; + } + + @Override + public void run() { + android.os.Process.setThreadPriority(priority); + Message message = Message.obtain(); + switch (currentCommand) { + + } + handler.sendMessage(message); + } + } + // ITEM CLASS + private class ItemTask implements Runnable { + int currentCommand; + int priority; + int setId; + int itemId; + Item item; + + public ItemTask(int command, int priority) { + this.currentCommand = command; + this.setId = -1; + this.itemId = -1; + this.priority = priority; + } + + @Override + public void run() { + android.os.Process.setThreadPriority(priority); + Message message = Message.obtain(); + switch (currentCommand) { + + case Command.ITEM_DELETE_FROM_SET : + message.arg1 = updateSetItemDeleted(setId, itemId, true); + if (message.arg1 > 0) + message.what = Event.ITEM_FROM_SET_DELETED; + else + message.what = Event.ITEM_FROM_SET_ERROR; + break; + + case Command.ITEM_INSERT : + List items = new ArrayList<>(); + items.add(item); + if (items.isEmpty()) + message.what = Event.ITEM_INSERT_ERROR; + else { + insertItems(items); + insertSetItem(setId, item.getId()); + message.what = Event.ITEM_INSERTED; + message.arg1 = setId; + message.arg2 = item.getId(); + } + break; + + case Command.ITEM_PACK : + message.arg1 = updateSetItemPacked(setId, itemId, true); + if (message.arg1 > 0) + message.what = Event.ITEM_PACKED; + else + message.what = Event.ITEM_PACK_ERROR; + break; + + case Command.ITEM_UNPACK : + message.arg1 = updateSetItemPacked(setId, itemId, false); + if (message.arg1 > 0) + message.what = Event.ITEM_UNPACKED; + else + message.what = Event.ITEM_UNPACK_ERROR; + break; + } + handler.sendMessage(message); + } + } + // SET CLASS + private class SetTask implements Runnable { + int currentCommand; + int priority; + int setId; + List setsToUpdate; + + public SetTask(int command, int priority) { + this.currentCommand = command; + this.setId = -1; + this.setsToUpdate = new ArrayList<>(); + this.priority = priority; + } + + @Override + public void run() { + android.os.Process.setThreadPriority(priority); + Message message = Message.obtain(); + switch (currentCommand) { + + case Command.SET_GET_ALL : + List sets = readSets(); + if (sets.isEmpty()) + message.what = Event.SET_LOAD_ERROR; + else { + message.what = Event.SET_LOAD_COMPLETED; + message.obj = sets; + } + break; + + case Command.SET_GET_ITEMS : + List items = readItems(setId); + if (items.isEmpty()) + message.what = Event.SET_ITEMS_LOAD_ERROR; + else { + Collections.sort(items); + Hashtable categories = readCategories(); + Hashtable> result = new Hashtable<>(20, 0.9f); + String categoryName; + + for (Item item : items) { + categoryName = categories.get(item.getCategory()); + if (result.containsKey(categoryName)) { + result.get(categoryName).add(item); + } else { + List innerList = new ArrayList<>(20); + innerList.add(item); + result.put(categoryName, innerList); + } + } + message.what = Event.SET_ITEMS_LOAD_COMPLETED; + message.obj = result; + message.arg1 = setId; + } + break; + + case Command.SET_REORDER : + message.arg1 = updateSetsOrder(setsToUpdate); + if (message.arg1 > 0) + message.what = Event.SET_REORDER_COMPLETED; + else + message.what = Event.SET_REORDER_ERROR; + break; + } + handler.sendMessage(message); + } + } + // SYNC CLASS + private class SyncTask implements Runnable { + int currentCommand; + int priority; + int statusCode; + + public SyncTask(int command, int priority) { + this.currentCommand = command; + this.priority = priority; + } + + @Override + public void run() { + android.os.Process.setThreadPriority(priority); + Message message = Message.obtain(); + + switch (currentCommand) { + case Command.SYNC: + try { + Call> call = api.getSets(); + call.enqueue(new Callback>() { + @Override + public void onResponse(Call> call, Response> response) { + statusCode = response.code(); + // TODO + // check if first time + // if not check for updates else + // insert into database here + insertSets(response.body()); + } + @Override + public void onFailure(Call> call, Throwable t) { + } + }); + message.what = Event.SYNC_COMPLETED; + + } catch (Exception e) { + message.what = Event.SYNC_FAILED; + } + finally { + message.arg1 = statusCode; + handler.sendMessage(message); + } + break; + + case Command.SYNC_IF_NOT_EXISTS: + if (LogExist()) { + message.what = Event.SYNC_COMPLETED; + } else { + + try { + Response> response0 = api.getSets().execute(); + insertSets(response0.body()); + statusCode = response0.code(); + Response> response1 = api.getItemCategories().execute(); + insertCategories(response1.body()); + statusCode = response1.code(); + Response> response2 = api.getItems().execute(); + insertItems(response2.body()); + statusCode = response2.code(); + Response response3 = api.getTimestamp().execute(); + insertTimestamp(response3.body()); + statusCode = response3.code(); + message.what = Event.SYNC_COMPLETED; + } catch (IOException e ){ + message.what = Event.SYNC_FAILED; + } finally { + message.arg1 = statusCode; + handler.sendMessage(message); + } + } + break; + } + } + } + //endregion + + } diff --git a/app/src/main/java/hikapro/com/backpack/model/database/Db.java b/app/src/main/java/hikapro/com/backpack/model/database/Db.java index de45801..06cd845 100644 --- a/app/src/main/java/hikapro/com/backpack/model/database/Db.java +++ b/app/src/main/java/hikapro/com/backpack/model/database/Db.java @@ -2,6 +2,7 @@ package hikapro.com.backpack.model.database; import android.content.ContentValues; import android.database.Cursor; +import android.util.Log; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -12,6 +13,8 @@ import java.util.List; import hikapro.com.backpack.model.entities.Category; import hikapro.com.backpack.model.entities.Item; import hikapro.com.backpack.model.entities.Set; +import hikapro.com.backpack.model.entities.Timestamp; +import hikapro.com.backpack.model.entities.UpdateLog; /** * Created by tariel on 20/04/16. @@ -23,10 +26,10 @@ public class Db { public abstract static class CategoriesTable { - public static final String TABLE_NAME = "CATEGORIES"; + public static final String TABLE_NAME = "categories"; public static final String COLUMN_ID = "_id"; - public static final String COLUMN_NAME = "NAME"; + public static final String COLUMN_NAME = "name"; public static final String CREATE = "CREATE TABLE " + TABLE_NAME + " (" + @@ -51,17 +54,17 @@ public class Db { } public abstract static class ItemsTable { - public static final String TABLE_NAME = "ITEMS"; + public static final String TABLE_NAME = "items"; public static final String COLUMN_ID = "_id"; - public static final String COLUMN_NAME = "NAME"; - public static final String COLUMN_CATEGORY = "CATEGORY"; - public static final String COLUMN_DESCRIPTION = "DESCRIPTION"; - public static final String COLUMN_BUY_URLS = "BUY_URLS"; - public static final String COLUMN_PHOTO_URL = "PHOTO_URL"; - public static final String COLUMN_PHOTO_THUMB_URL = "PHOTO_THUMB_URL"; - public static final String COLUMN_PHOTO_LOCAL = "PHOTO_LOCAL"; - public static final String COLUMN_PHOTO_THUMB_LOCAL = "PHOTO_THUMB_LOCAL"; + public static final String COLUMN_NAME = "name"; + public static final String COLUMN_CATEGORY = "category"; + public static final String COLUMN_DESCRIPTION = "description"; + public static final String COLUMN_BUY_URLS = "buy_urls"; + public static final String COLUMN_PHOTO_URL = "photo_url"; + public static final String COLUMN_PHOTO_THUMB_URL = "photo_thumb_url"; + public static final String COLUMN_PHOTO_LOCAL = "photo_local"; + public static final String COLUMN_PHOTO_THUMB_LOCAL = "photo_thumb_local"; public static final String CREATE = @@ -124,17 +127,19 @@ public class Db { } public abstract static class SetsTable { - public static final String TABLE_NAME = "SETS"; + public static final String TABLE_NAME = "sets"; public static final String COLUMN_ID = "_id"; - public static final String COLUMN_NAME = "NAME"; - public static final String COLUMN_ITEMS = "ITEMS"; - public static final String COLUMN_PHOTO_URL = "PHOTO_URL"; - public static final String COLUMN_PHOTO_THUMB_URL = "PHOTO_THUMB_URL"; - public static final String COLUMN_PHOTO_LOCAL = "PHOTO_LOCAL"; - public static final String COLUMN_PHOTO_THUMB_LOCAL = "PHOTO_THUMB_LOCAL"; - public static final String COLUMN_PHOTO_THUMBNAIL_URL = "PHOTO_THUMBNAIL_URL"; - public static final String COLUMN_PHOTO_THUMBNAIL_LOCAL = "PHOTO_THUMBNAIL_LOCAL"; + public static final String COLUMN_NAME = "name"; + public static final String COLUMN_ITEMS = "items"; + public static final String COLUMN_PHOTO_URL = "photo_url"; + public static final String COLUMN_PHOTO_THUMB_URL = "photo_thumb_url"; + public static final String COLUMN_PHOTO_LOCAL = "photo_local"; + public static final String COLUMN_PHOTO_THUMB_LOCAL = "photo_thumb_local"; + public static final String COLUMN_PHOTO_THUMBNAIL_URL = "photo_thumbnail_url"; + public static final String COLUMN_PHOTO_THUMBNAIL_LOCAL = "photo_thumbnail_local"; + public static final String COLUMN_LINE_NUMBER = "line_num"; + public static final String CREATE = "CREATE TABLE " + TABLE_NAME + " (" + @@ -146,10 +151,11 @@ public class Db { COLUMN_PHOTO_LOCAL + " TEXT, " + COLUMN_PHOTO_THUMB_LOCAL + " TEXT, " + COLUMN_PHOTO_THUMBNAIL_URL + " TEXT, " + + COLUMN_LINE_NUMBER + " INTEGER, " + COLUMN_PHOTO_THUMBNAIL_LOCAL + " TEXT" + " ); "; - public static ContentValues toContentValues(Set set) { + public static ContentValues toContentValues(Set set, int lineNumber) { ContentValues values = new ContentValues(); values.put(COLUMN_ID, set.getId()); values.put(COLUMN_NAME, set.getName()); @@ -164,6 +170,7 @@ public class Db { values.put(COLUMN_PHOTO_URL, set.getPhoto()); values.put(COLUMN_PHOTO_THUMB_URL, set.getPhotoThumb()); values.put(COLUMN_PHOTO_THUMBNAIL_URL, set.getPhotoThumbnail()); + values.put(COLUMN_LINE_NUMBER, lineNumber); /* values.put(COLUMN_PHOTO_LOCAL, ""); values.put(COLUMN_PHOTO_THUMB_LOCAL, ""); @@ -187,9 +194,68 @@ public class Db { set.setPhoto(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_PHOTO_URL))); set.setPhotoThumb(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_PHOTO_THUMB_URL))); set.setPhotoThumbnail(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_PHOTO_THUMBNAIL_URL))); + set.setLineNumber(cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_LINE_NUMBER))); return set; } + } + public abstract static class LogTable { + + public static final String TABLE_NAME = "update_log"; + + public static final String COLUMN_ID = "_id"; + public static final String COLUMN_MODIFIED_DATETIME = "modified_datetime"; + public static final String COLUMN_TIMESTAMP = "timestamp"; + + public static final String CREATE = + "CREATE TABLE " + TABLE_NAME + " (" + + COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + COLUMN_TIMESTAMP + " INTEGER NOT NULL, " + + COLUMN_MODIFIED_DATETIME + " INTEGER NOT NULL DEFAULT current_timestamp" + + " ); "; + + public static ContentValues toContentValues(Timestamp timestamp) { + ContentValues values = new ContentValues(); + + values.put(COLUMN_TIMESTAMP, timestamp.timestamp); + + return values; + } + public static UpdateLog parseCursor(Cursor cursor) { + UpdateLog log = new UpdateLog(); + log.setTimestamp(cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_TIMESTAMP))); + log.setModifiedDatetime(cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_MODIFIED_DATETIME))); + return log; + } + } + + public abstract static class SetItemsTable { + + public static final String TABLE_NAME = "set_items"; + + public static final String COLUMN_ID = "_id"; + public static final String COLUMN_SET = "setId"; + public static final String COLUMN_ITEM = "itemId"; + public static final String COLUMN_DELETED = "deleted"; + public static final String COLUMN_PACKED = "packed"; + + public static final String CREATE = + "CREATE TABLE " + TABLE_NAME + " (" + + COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + COLUMN_SET + " INTEGER NOT NULL, " + + COLUMN_DELETED + " NUMERIC, " + + COLUMN_PACKED + " NUMERIC, " + + COLUMN_ITEM + " INTEGER NOT NULL" + + " ); "; + + public static ContentValues toContentValues(int setId, int itemId) { + ContentValues values = new ContentValues(); + values.put(COLUMN_SET, setId); + values.put(COLUMN_ITEM, itemId); + values.put(COLUMN_DELETED, 0); + values.put(COLUMN_PACKED, 0); + return values; + } } } diff --git a/app/src/main/java/hikapro/com/backpack/model/database/DbHelper.java b/app/src/main/java/hikapro/com/backpack/model/database/DbHelper.java index 3e3668c..9d8d947 100644 --- a/app/src/main/java/hikapro/com/backpack/model/database/DbHelper.java +++ b/app/src/main/java/hikapro/com/backpack/model/database/DbHelper.java @@ -38,6 +38,8 @@ public class DbHelper extends SQLiteOpenHelper { db.execSQL(Db.ItemsTable.CREATE); db.execSQL(Db.CategoriesTable.CREATE); db.execSQL(Db.SetsTable.CREATE); + db.execSQL(Db.LogTable.CREATE); + db.execSQL(Db.SetItemsTable.CREATE); if (oldVersion < 2) { // place the logic here diff --git a/app/src/main/java/hikapro/com/backpack/model/database/Event.java b/app/src/main/java/hikapro/com/backpack/model/database/Event.java new file mode 100644 index 0000000..f2a4df9 --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/model/database/Event.java @@ -0,0 +1,51 @@ +package hikapro.com.backpack.model.database; + +/** + * Created by tariel on 27/04/16. + */ +public interface Event { + + int SET_SCOPE_END = 0x13; + int ITEM_SCOPE_END = 0x27; + int MY_LIST_SCOPE_END = 0x3B; + + int SET_LOAD_ERROR = -0x1; + int SET_REORDER_ERROR = -0x2; + int SET_ITEMS_LOAD_ERROR = -0x3; + + + int SET_LOAD_COMPLETED = 0x1; + int SET_REORDER_COMPLETED = 0x2; + int SET_ITEMS_LOAD_COMPLETED = 0x3; + + + int ITEM_FROM_SET_ERROR = -0x14; + int ITEM_INSERT_ERROR = -0x15; + int ITEM_DELETE_ERROR = -0x16; + int ITEM_PACK_ERROR = -0x17; + int ITEM_UNPACK_ERROR = -0x18; + + int ITEM_FROM_SET_DELETED = 0x14; + int ITEM_INSERTED = 0x15; + int ITEM_DELETED = 0x16; + int ITEM_PACKED = 0x17; + int ITEM_UNPACKED = 0x18; + + int MY_LIST_POST_ERROR = -0x28; + int MY_LIST_ITEM_ADD_ERROR = -0x29; + int MY_LIST_ITEM_DELETE_ERROR = -0x2A; + int MY_LIST_CLEAR_ERROR = -0x2B; + + int MY_LIST_POSTED = 0x28; + int MY_LIST_ITEM_ADDED = 0x29; + int MY_LIST_ITEM_DELETED = 0x2A; + int MY_LIST_CLEARED = 0x2B; + + int SYNC_FAILED = -0x3C; + int SYNC_COMPLETED = 0x3C; + + int NOT_IMPLEMENTED = 0x50; + int NOT_UNDERSTAND = 0x51; + + +} diff --git a/app/src/main/java/hikapro/com/backpack/model/database/Test.java b/app/src/main/java/hikapro/com/backpack/model/database/Test.java new file mode 100644 index 0000000..e666937 --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/model/database/Test.java @@ -0,0 +1,15 @@ +package hikapro.com.backpack.model.database; + +/** + * Created by tariel on 27/04/16. + */ +public class Test { + private static Test ourInstance = new Test(); + + public static Test getInstance() { + return ourInstance; + } + + private Test() { + } +} diff --git a/app/src/main/java/hikapro/com/backpack/model/entities/Set.java b/app/src/main/java/hikapro/com/backpack/model/entities/Set.java index b017060..194cb16 100644 --- a/app/src/main/java/hikapro/com/backpack/model/entities/Set.java +++ b/app/src/main/java/hikapro/com/backpack/model/entities/Set.java @@ -10,7 +10,7 @@ import java.util.List; /** * Created by tariel on 02/04/16. */ -public class Set implements Serializable { +public class Set implements Comparable, Serializable { @SerializedName("id") @Expose @@ -31,6 +31,8 @@ public class Set implements Serializable { @Expose private String photoThumbnail; + private int lineNumber; + public Set() { } @@ -91,6 +93,14 @@ public class Set implements Serializable { this.photoThumbnail = photoThumbnail; } + public int getLineNumber() { + return lineNumber; + } + + public void setLineNumber(int lineNumber) { + this.lineNumber = lineNumber; + } + @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; @@ -109,4 +119,10 @@ public class Set implements Serializable { return false; return true; } + + @Override + public int compareTo(Set another) { + int cmp = Integer.valueOf(lineNumber).compareTo(Integer.valueOf(another.lineNumber)); + return (cmp != 0 ? cmp : name.compareTo(another.name)); + } } diff --git a/app/src/main/java/hikapro/com/backpack/model/entities/Timestamp.java b/app/src/main/java/hikapro/com/backpack/model/entities/Timestamp.java new file mode 100644 index 0000000..01f5e90 --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/model/entities/Timestamp.java @@ -0,0 +1,20 @@ +package hikapro.com.backpack.model.entities; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * Created by tariel on 27/04/16. + */ +public class Timestamp { + + @SerializedName("timestamp") + @Expose + public long timestamp; + + public Timestamp() { + } + + + +} diff --git a/app/src/main/java/hikapro/com/backpack/model/entities/UpdateLog.java b/app/src/main/java/hikapro/com/backpack/model/entities/UpdateLog.java new file mode 100644 index 0000000..b314744 --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/model/entities/UpdateLog.java @@ -0,0 +1,29 @@ +package hikapro.com.backpack.model.entities; + +/** + * Created by tariel on 27/04/16. + */ +public class UpdateLog { + + private long timestamp; + private long modifiedDatetime; + + public UpdateLog() { + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + public long getModifiedDatetime() { + return modifiedDatetime; + } + + public void setModifiedDatetime(long modifiedDatetime) { + this.modifiedDatetime = modifiedDatetime; + } +} diff --git a/app/src/main/java/hikapro/com/backpack/model/entities/Updates.java b/app/src/main/java/hikapro/com/backpack/model/entities/Updates.java new file mode 100644 index 0000000..00d2689 --- /dev/null +++ b/app/src/main/java/hikapro/com/backpack/model/entities/Updates.java @@ -0,0 +1,41 @@ +package hikapro.com.backpack.model.entities; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by tariel on 27/04/16. + */ +public class Updates { + + @SerializedName("items") + @Expose + public List items = new ArrayList<>(); + + @SerializedName("items_deleted_ids") + @Expose + public List itemsDeletedIds = new ArrayList<>(); + + @SerializedName("item_categories") + @Expose + public List itemCategories = new ArrayList<>(); + + @SerializedName("item_categories_deleted_ids") + @Expose + public List itemCategoriesDeletedIds = new ArrayList<>(); + + @SerializedName("sets") + @Expose + public List sets = new ArrayList<>(); + + @SerializedName("sets_deleted_ids") + @Expose + public List setsDeletedIds = new ArrayList<>(); + + @SerializedName("timestamp") + @Expose + public long timestamp; +}