Merge branch 'tag_items_list_interface_1' into dev
This commit is contained in:
commit
899be44eda
|
@ -1 +1 @@
|
|||
BackPack
|
||||
packwithme
|
|
@ -0,0 +1,7 @@
|
|||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="tariel">
|
||||
<words>
|
||||
<w>tariel</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
|
@ -5,6 +5,7 @@
|
|||
<GradleProjectSettings>
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="1.8" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
|
|
|
@ -37,37 +37,10 @@
|
|||
<ConfirmationsSetting value="0" id="Add" />
|
||||
<ConfirmationsSetting value="0" id="Remove" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.7" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
<component name="masterDetails">
|
||||
<states>
|
||||
<state key="ProjectJDKs.UI">
|
||||
<settings>
|
||||
<last-edited>1.8</last-edited>
|
||||
<splitter-proportions>
|
||||
<option name="proportions">
|
||||
<list>
|
||||
<option value="0.2" />
|
||||
</list>
|
||||
</option>
|
||||
</splitter-proportions>
|
||||
</settings>
|
||||
</state>
|
||||
<state key="ScopeChooserConfigurable.UI">
|
||||
<settings>
|
||||
<splitter-proportions>
|
||||
<option name="proportions">
|
||||
<list>
|
||||
<option value="0.2" />
|
||||
</list>
|
||||
</option>
|
||||
</splitter-proportions>
|
||||
</settings>
|
||||
</state>
|
||||
</states>
|
||||
</component>
|
||||
</project>
|
|
@ -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'
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:name=".App"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package hikapro.com.backpack;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* Created by tariel on 27/04/16.
|
||||
*/
|
||||
public class App extends Application {
|
||||
|
||||
private static Context context;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
App.context = getApplicationContext();
|
||||
}
|
||||
|
||||
public static Context getAppContext() {
|
||||
return App.context;
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ public class MainActivity extends Activity implements View.ActivityCallback {
|
|||
|
||||
private FragmentManager fragmentManager;
|
||||
|
||||
|
||||
private final StateMaintainer stateMaintainer =
|
||||
new StateMaintainer(getFragmentManager(), MainActivity.class.getName());
|
||||
|
||||
|
@ -37,10 +38,10 @@ public class MainActivity extends Activity implements View.ActivityCallback {
|
|||
|
||||
stateMaintainer.init();
|
||||
|
||||
if (fragmentManager.getBackStackEntryCount() == 0) {
|
||||
|
||||
if (fragmentManager.getBackStackEntryCount() == 0 && savedInstanceState == null) {
|
||||
startSetListFragment();
|
||||
} else {
|
||||
|
||||
Fragment fragment = fragmentManager.findFragmentByTag(SetListFragment.class.getName());
|
||||
if (fragment != null) {
|
||||
SetListFragment view = (SetListFragment) fragment;
|
||||
|
@ -51,7 +52,6 @@ public class MainActivity extends Activity implements View.ActivityCallback {
|
|||
presenter.setView(view);
|
||||
presenter.setModel(model);
|
||||
model.setPresenter(presenter);
|
||||
|
||||
}
|
||||
|
||||
fragment = fragmentManager.findFragmentByTag(ItemListFragment.class.getName());
|
||||
|
@ -128,12 +128,11 @@ public class MainActivity extends Activity implements View.ActivityCallback {
|
|||
SetModel model = new SetModel();
|
||||
|
||||
view.setPresenter(presenter);
|
||||
presenter.setView(view);
|
||||
//presenter.setView(view);
|
||||
presenter.setModel(model);
|
||||
model.setPresenter(presenter);
|
||||
|
||||
replaceFragment(view, true, SetListFragment.class.getName());
|
||||
|
||||
replaceFragment(view, false, SetListFragment.class.getName());
|
||||
stateMaintainer.put(presenter);
|
||||
stateMaintainer.put(model);
|
||||
|
||||
|
|
|
@ -6,20 +6,31 @@ 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.Updates;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
/**
|
||||
* Created by tariel on 19/04/16.
|
||||
*/
|
||||
public interface Api {
|
||||
|
||||
@GET("/api/v1/backpack/items")
|
||||
@GET("api/v1/backpack/items")
|
||||
Call<List<Item>> getItems();
|
||||
|
||||
@GET("/api/v1/backpack/item_categories")
|
||||
@GET("api/v1/backpack/item_categories")
|
||||
Call<List<Category>> getItemCategories();
|
||||
|
||||
@GET("/api/v1/backpack/sets")
|
||||
@GET("api/v1/backpack/sets")
|
||||
Call<List<Set>> getSets();
|
||||
|
||||
@GET("api/v1/backpack/updates/timestamp")
|
||||
Call<Timestamp> getTimestamp();
|
||||
|
||||
@GET("api/v1/backpack/updates/all")
|
||||
Call<Updates> getUpdates(@Query("timestamp") long timestamp);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Category> rawCategories;
|
||||
private List<Category> sortedCategories;
|
||||
private List<Item> rawItems;
|
||||
|
||||
private DAO dao;
|
||||
private int currentSet;
|
||||
private Hashtable<Integer, Category> categoriesCache;
|
||||
private List<Item> itemsCache;
|
||||
private List<Item> itemsDiscardCache;
|
||||
|
||||
private Hashtable<Category, List<Item>> items;
|
||||
private Hashtable<Integer, List<Item>> 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<Item> 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<Item> items = cache.get(currentSet);
|
||||
Item item = null;
|
||||
for (Item i : rawItems) {
|
||||
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<Item> newList = new ArrayList<>(20);
|
||||
List<Item> oldList = cache.get(currentSet);
|
||||
for (Item item : oldList) {
|
||||
name = item.getName().toLowerCase();
|
||||
if (name.contains(query)) {
|
||||
newList.add(item);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
cache.put(currentSet, newList);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeaderId(int position) {
|
||||
return cache.containsKey(currentSet) ? cache.get(currentSet).get(position).getCategory() : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemId(int position) {
|
||||
return cache.containsKey(currentSet) ? cache.get(currentSet).get(position).getId() : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
if (cache.containsKey(currentSet))
|
||||
cache.get(currentSet).clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPendingRemoval(Item item) {
|
||||
return itemsDiscardCache.contains(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pendingRemoveCancel(Item item) {
|
||||
if (itemsDiscardCache.contains(item))
|
||||
itemsDiscardCache.remove(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pendingRemove(Item item) {
|
||||
itemsDiscardCache.add(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemsCount() {
|
||||
boolean is = cache.containsKey(currentSet);
|
||||
return is ? cache.get(currentSet).size() : 0;
|
||||
}
|
||||
|
||||
// items <--
|
||||
|
||||
// events -->
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
private void loadCategories() {
|
||||
Call<List<Category>> call = api.getItemCategories();
|
||||
call.enqueue(new Callback<List<Category>>() {
|
||||
@Override
|
||||
public void onResponse(Call<List<Category>> call, Response<List<Category>> response) {
|
||||
int statusCode = response.code();
|
||||
rawCategories = response.body();
|
||||
}
|
||||
@Override
|
||||
public void onFailure(Call<List<Category>> call, Throwable t) {
|
||||
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<Item> res = (List<Item>) event.obj;
|
||||
cache.put(event.arg1, res);
|
||||
notifyDataSetChanged();
|
||||
break;
|
||||
case Event.ITEM_CATEGORY_LOAD_COMPLETED :
|
||||
categoriesCache = (Hashtable<Integer, Category>)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 loadItems() {
|
||||
Call<List<Item>> call2 = api.getItems();
|
||||
call2.enqueue(new Callback<List<Item>>() {
|
||||
@Override
|
||||
public void onResponse(Call<List<Item>> call, Response<List<Item>> response) {
|
||||
int statusCode = response.code();
|
||||
rawItems = response.body();
|
||||
initData();
|
||||
}
|
||||
@Override
|
||||
public void onFailure(Call<List<Item>> 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;
|
||||
|
|
|
@ -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<hikapro.com.backpack.model.entities.Set> 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();
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
||||
|
|
|
@ -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<hikapro.com.backpack.model.entities.Set> iList;
|
||||
private Api api;
|
||||
private List<hikapro.com.backpack.model.entities.Set> 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<List<hikapro.com.backpack.model.entities.Set>> call = api.getSets();
|
||||
call.enqueue(new Callback<List<hikapro.com.backpack.model.entities.Set>>() {
|
||||
@Override
|
||||
public void onResponse(Call<List<hikapro.com.backpack.model.entities.Set>> call, Response<List<hikapro.com.backpack.model.entities.Set>> response) {
|
||||
int statusCode = response.code();
|
||||
iList = response.body();
|
||||
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<Set>) event.obj;
|
||||
notifyDataSetChanged();
|
||||
break;
|
||||
case Event.SET_ITEMS_LOAD_COMPLETED :
|
||||
break;
|
||||
case Event.SET_REORDER_COMPLETED :
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<List<hikapro.com.backpack.model.entities.Set>> call, Throwable t) {
|
||||
|
||||
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);
|
||||
}
|
||||
private void syncData() {
|
||||
//endregion
|
||||
|
||||
}
|
||||
// process <--
|
||||
|
||||
// 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<hikapro.com.backpack.model.entities.Set> getSets()
|
||||
{
|
||||
return cache;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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<String, Model.Base> observers;
|
||||
|
||||
private DAO() {
|
||||
this.context = App.getAppContext();
|
||||
this.helper = new DbHelper(this.context);
|
||||
this.api = RestClient.getApi();
|
||||
final BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
|
||||
this.threadPool = initPool(taskQueue);
|
||||
this.observers = Collections.synchronizedMap(new HashMap<String, Model.Base>());
|
||||
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<Runnable> 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<String, Model.Base> 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<Set>) 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<Item> 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<Category> 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<Set> 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<Item> readItems(int setId) {
|
||||
List<Item> 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<Integer, Category> readCategories() {
|
||||
Hashtable<Integer, Category> 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<Set> readSets() {
|
||||
List<Set> 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<Set> 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<Item> 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<Integer, Category> 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<Set> 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<Set> 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<Item> 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<List<Set>> call = api.getSets();
|
||||
call.enqueue(new Callback<List<Set>>() {
|
||||
@Override
|
||||
public void onResponse(Call<List<hikapro.com.backpack.model.entities.Set>> call, Response<List<Set>> 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<List<hikapro.com.backpack.model.entities.Set>> 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<List<Set>> response0 = api.getSets().execute();
|
||||
insertSets(response0.body());
|
||||
statusCode = response0.code();
|
||||
Response<List<Category>> response1 = api.getItemCategories().execute();
|
||||
insertCategories(response1.body());
|
||||
statusCode = response1.code();
|
||||
Response<List<Item>> response2 = api.getItems().execute();
|
||||
insertItems(response2.body());
|
||||
statusCode = response2.code();
|
||||
Response<Timestamp> 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
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
|
||||
|
||||
}
|
|
@ -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() {
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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<Set>, 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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<Integer> items = new ArrayList<>();
|
||||
|
||||
@SerializedName("items_deleted_ids")
|
||||
@Expose
|
||||
public List<Integer> itemsDeletedIds = new ArrayList<>();
|
||||
|
||||
@SerializedName("item_categories")
|
||||
@Expose
|
||||
public List<Integer> itemCategories = new ArrayList<>();
|
||||
|
||||
@SerializedName("item_categories_deleted_ids")
|
||||
@Expose
|
||||
public List<Integer> itemCategoriesDeletedIds = new ArrayList<>();
|
||||
|
||||
@SerializedName("sets")
|
||||
@Expose
|
||||
public List<Integer> sets = new ArrayList<>();
|
||||
|
||||
@SerializedName("sets_deleted_ids")
|
||||
@Expose
|
||||
public List<Integer> setsDeletedIds = new ArrayList<>();
|
||||
|
||||
@SerializedName("timestamp")
|
||||
@Expose
|
||||
public long timestamp;
|
||||
}
|
|
@ -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.ItemList> 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() {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<CategoryViewHolder> {
|
||||
|
||||
private Presenter.ItemList presenter;
|
||||
private List<ItemListAdapter> 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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<ItemViewHolder> {
|
||||
public class ItemListAdapter extends RecyclerView.Adapter<ItemViewHolder> implements StickyHeaderAdapter<HeaderViewHolder> {
|
||||
|
||||
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<Item, Runnable> 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<ItemViewHolder> {
|
|||
|
||||
@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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<SetViewHolder> {
|
||||
public class SetListAdapter extends RecyclerView.Adapter<SetViewHolder> implements ItemTouchHelperAdapter{
|
||||
|
||||
private SetListPresenter presenter;
|
||||
|
||||
|
@ -31,4 +32,16 @@ public class SetListAdapter extends RecyclerView.Adapter<SetViewHolder> {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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());
|
||||
|
||||
}
|
||||
}
|
|
@ -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<View> 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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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<VH extends RecyclerView.ViewHolder> {
|
||||
|
||||
long getHeaderId(int position);
|
||||
VH onCreateHeaderViewHolder(ViewGroup parent);
|
||||
void onBindHeaderViewHolder(VH holder, int position);
|
||||
int getItemCount();
|
||||
}
|
|
@ -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<Rect> 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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||
</vector>
|
|
@ -6,7 +6,7 @@
|
|||
android:orientation="vertical">
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/categories_main_recycler"
|
||||
android:id="@+id/items_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:scrollbars="vertical"
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:background="@android:color/transparent"
|
||||
android:textSize="10sp"
|
||||
android:id="@+id/item_text"
|
||||
android:textStyle="bold" />
|
|
@ -1,25 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/linear">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingLeft="16dp"
|
||||
android:singleLine="true"
|
||||
android:textAllCaps="true"
|
||||
android:background="@android:color/transparent"
|
||||
android:textSize="16sp"
|
||||
android:id="@+id/section_text"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/category_inner_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/sticky_header_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<TextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:background="#001F3F"
|
||||
android:textSize="28sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@android:color/white"
|
||||
tools:text="Animals starting with A"
|
||||
android:id="@+id/header"
|
||||
tools:context=".MainActivity"/>
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
>
|
||||
<TextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="horizontal"
|
||||
android:clickable="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
android:textSize="14sp"
|
||||
tools:text="Aardvark"
|
||||
android:id="@+id/item_txt"
|
||||
tools:context=".MainActivity"/>
|
||||
<Button
|
||||
android:id="@+id/undo_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Undo"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="@android:color/white"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
style="@style/Base.Widget.AppCompat.Button.Borderless"/>
|
||||
</FrameLayout>
|
|
@ -0,0 +1,20 @@
|
|||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".MainActivity">
|
||||
<item
|
||||
android:id="@+id/action_settings"
|
||||
android:orderInCategory="1"
|
||||
android:title="Settings"
|
||||
android:showAsAction="never" />
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/action_search"
|
||||
android:icon="@android:drawable/ic_menu_search"
|
||||
android:showAsAction="always|collapseActionView"
|
||||
app:showAsAction="always|collapseActionView"
|
||||
android:actionViewClass="android.widget.SearchView"
|
||||
android:visible="true"
|
||||
android:title="Search"/>
|
||||
|
||||
</menu>
|
|
@ -2,4 +2,5 @@
|
|||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
<dimen name="ic_clear_margin">16dp</dimen>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue