Compare commits

...

5 commits

Author SHA1 Message Date
ligi
d1776c9c20 Use gradle:2.0.0-alpha8 2016-02-03 02:11:17 +01:00
ligi
8ea083418a WIP 2016-02-01 01:12:02 +01:00
ligi
9e1d5a0fb7 Swipe to Trash feature 2016-01-29 19:20:30 +01:00
ligi
3e252f9f5b Toast -> SnackBar 2016-01-29 19:20:30 +01:00
ligi
83dbc828f5 Use com.android.tools.build:gradle:2.0.0-alpha7 2016-01-29 18:57:18 +01:00
17 changed files with 644 additions and 61 deletions

1
.gitignore vendored
View file

@ -4,7 +4,6 @@ build
#assets
bin
gen
proguard-project.txt
project.properties
gradle.properties
local.properties

View file

@ -6,7 +6,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:2.0.0-alpha6'
classpath 'com.android.tools.build:gradle:2.0.0-alpha9'
classpath 'de.felixschulze.gradle:gradle-spoon-plugin:2.6.1'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.12.0'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

View file

@ -0,0 +1,110 @@
# To enable ProGuard in your project, edit project.properties
# to define the proguard.config property as described in that file.
#
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in ${sdk.dir}/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
#http://stackoverflow.com/questions/19274974/android-badparcelableexception-only-with-signed-apk
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
public *;
}
# optimize
-optimizationpasses 2
-optimizations !code/simplification/arithmetic
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
# AppCompat
-dontwarn android.support.v7.**
-keep class android.support.v7.** { *; }
-keep interface android.support.v7.** { *; }
# Keep line numbers to alleviate debugging stack traces
-renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable
### for api client
-keepattributes Signature,RuntimeVisibleAnnotations,AnnotationDefault
-keepclassmembers class * {
@com.google.api.client.util.Key <fields>;
}
# Needed by Guava
# See https://groups.google.com/forum/#!topic/guava-discuss/YCZzeCiIVoI
-dontwarn sun.misc.Unsafe
-dontwarn com.google.common.collect.MinMaxPriorityQueue
# Needed by google-http-client-android when linking against an older platform version
-dontwarn com.google.api.client.extensions.android.**
# Needed by google-api-client-android when linking against an older platform version
-dontwarn com.google.api.client.googleapis.extensions.android.**
#### for butterknife
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }
-keepnames class * { @butterknife.Bind *;}
#### for support 22
-dontwarn android.support.**
#### for guava
-dontwarn javax.annotation.**
-dontwarn javax.inject.**
-dontwarn sun.misc.Unsafe
-dontwarn com.google.common.collect.MinMaxPriorityQueue
-keep,allowoptimization class com.google.inject.** { *; }
-keep,allowoptimization class javax.inject.** { *; }
-keep,allowoptimization class javax.annotation.** { *; }
-keep,allowoptimization class com.google.inject.Binder
-keepclasseswithmembers public class * {
public static void main(java.lang.String[]);
}
-keepclassmembers,allowoptimization class com.google.common.* {
void finalizeReferent();
void startFinalizer(java.lang.Class,java.lang.Object);
}
-keepclassmembers class * {
@com.google.common.eventbus.Subscribe *;
}
-dontwarn java.nio.file.Files
-dontwarn java.nio.file.Path
-dontwarn java.nio.file.OpenOption
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-keep public class com.google.android.gms.**
-dontwarn com.google.android.gms.**

View file

@ -34,7 +34,7 @@ public class ThePassListActivity extends BaseIntegration<PassListActivity> {
@MediumTest
public void testListIsThere() {
onView(withId(R.id.content_list)).check(matches(isDisplayed()));
onView(withId(R.id.pass_recyclerview)).check(matches(isDisplayed()));
Spoon.screenshot(getActivity(), "list");
}

View file

@ -2,10 +2,12 @@ package org.ligi.passandroid.injections;
import org.ligi.passandroid.model.FiledPass;
import org.ligi.passandroid.model.Pass;
import org.ligi.passandroid.model.PassClassifier;
import org.ligi.passandroid.model.PassStore;
import org.ligi.passandroid.model.comparator.PassSortOrder;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
public class FixedPassListPassStore implements PassStore {
@ -46,10 +48,6 @@ public class FixedPassListPassStore implements PassStore {
return null;
}
@Override
public void sort(PassSortOrder order) {
}
@Override
public Pass getCurrentPass() {
return actPass;
@ -60,6 +58,11 @@ public class FixedPassListPassStore implements PassStore {
actPass = pass;
}
@Override
public PassClassifier getClassifier() {
return new PassClassifier(new HashMap<String, Set<String>>());
}
@Override
public boolean deletePassWithId(String id) {
return false;

View file

@ -8,6 +8,7 @@ import org.ligi.passandroid.ui.PassAndroidActivity;
import org.ligi.passandroid.ui.PassEditActivity;
import org.ligi.passandroid.ui.PassImportActivity;
import org.ligi.passandroid.ui.PassListActivity;
import org.ligi.passandroid.ui.PassListFragment;
import org.ligi.passandroid.ui.PassMenuOptions;
import org.ligi.passandroid.ui.PassViewActivityBase;
import org.ligi.passandroid.ui.SearchPassesIntentService;
@ -53,6 +54,8 @@ public interface AppComponent {
void inject(PassAndroidActivity passAndroidActivity);
void inject(PassListFragment passListFragment);
PassStore passStore();
Tracker tracker();

View file

@ -4,7 +4,6 @@ import android.content.Context;
import org.ligi.axt.AXT;
import org.ligi.passandroid.helper.DirectoryFileFilter;
import org.ligi.passandroid.model.comparator.PassSortOrder;
import org.ligi.passandroid.reader.AppleStylePassReader;
import org.ligi.passandroid.reader.PassReader;
import org.ligi.tracedroid.logging.Log;
@ -12,8 +11,9 @@ import org.ligi.tracedroid.logging.Log;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
public class AndroidFileSystemPassStore implements PassStore {
@ -22,12 +22,14 @@ public class AndroidFileSystemPassStore implements PassStore {
private List<FiledPass> passList = new ArrayList<>();
private Pass actPass;
private final PassClassifier passClassifier;
public AndroidFileSystemPassStore(final Context context, final Settings settings) {
this.context = context;
path = settings.getPassesDir();
refreshPassesList();
passClassifier = new PassClassifier(new HashMap<String, Set<String>>());
}
@Override
@ -124,11 +126,6 @@ public class AndroidFileSystemPassStore implements PassStore {
return getCachedPassOrLoad(id);
}
@Override
public void sort(final PassSortOrder order) {
Collections.sort(passList, order.toComparator());
}
@Override
public Pass getCurrentPass() {
return actPass;
@ -139,9 +136,18 @@ public class AndroidFileSystemPassStore implements PassStore {
actPass = pass;
}
@Override
public PassClassifier getClassifier() {
return passClassifier;
}
@Override
public boolean deletePassWithId(final String id) {
return AXT.at(new File(getPathForID(id))).deleteRecursive();
final boolean result = AXT.at(new File(getPathForID(id))).deleteRecursive();
if (result) {
refreshPassesList();
}
return result;
}
public String getPathForID(final String id) {

View file

@ -0,0 +1,128 @@
package org.ligi.passandroid.model;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CopyOnWriteArraySet;
public class PassClassifier {
public interface OnClassificationChangeListener {
void OnClassificationChange();
}
public Set<OnClassificationChangeListener> onClassificationChangeListeners = new CopyOnWriteArraySet<>();
public final static String DEFAULT_TOPIC = "active";
private final HashMap<String, Set<String>> pass_id_list_by_topic;
private final HashMap<String, String> topic_by_id = new HashMap<>();
public PassClassifier(HashMap<String, Set<String>> pass_id_list_by_topic) {
this.pass_id_list_by_topic = pass_id_list_by_topic;
processDataChange();
}
private void processDataChange() {
calculateReverseMapping();
removeEmpty();
makeSureDefaultTopicExists();
for (OnClassificationChangeListener onClassificationChangeListener : onClassificationChangeListeners) {
onClassificationChangeListener.OnClassificationChange();
}
}
private void calculateReverseMapping() {
topic_by_id.clear();
for (Map.Entry<String, Set<String>> stringListEntry : pass_id_list_by_topic.entrySet()) {
final String topic = stringListEntry.getKey();
for (String id : stringListEntry.getValue()) {
topic_by_id.put(id, topic);
}
}
}
private void makeSureDefaultTopicExists() {
if (pass_id_list_by_topic.isEmpty()) {
pass_id_list_by_topic.put(DEFAULT_TOPIC, new TreeSet<String>());
}
}
private void removeEmpty() {
final Set<String> toRemove = new HashSet<>();
for (Map.Entry<String, Set<String>> stringListEntry : pass_id_list_by_topic.entrySet()) {
if (stringListEntry.getValue().isEmpty()) {
toRemove.add(stringListEntry.getKey());
}
}
for (String s : toRemove) {
pass_id_list_by_topic.remove(s);
}
}
/*public PassClassifier(final Context context) {
final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
deleted_passes = Arrays.asList(sharedPreferences.getString("deleted_passes", "").split(";;"));
//deleted_passes
//deleted_passes.co
}*/
public void moveToTopic(final Pass pass, final String newTopic) {
if (topic_by_id.containsKey(pass.getId())) {
final String oldTopic = topic_by_id.get(pass.getId());
final Set<String> idsForOldTopic = pass_id_list_by_topic.get(oldTopic);
idsForOldTopic.remove(pass.getId());
if (idsForOldTopic.isEmpty()) {
pass_id_list_by_topic.remove(oldTopic);
}
}
upsertPassToTopic(pass, newTopic);
processDataChange();
}
private void upsertPassToTopic(Pass pass, String newTopic) {
if (!pass_id_list_by_topic.containsKey(newTopic)) {
pass_id_list_by_topic.put(newTopic, new TreeSet<String>());
}
pass_id_list_by_topic.get(newTopic).add(pass.getId());
}
public Set<String> getTopics() {
return pass_id_list_by_topic.keySet();
}
public boolean isPassOnTopic(FiledPass pass, String topic) {
final boolean passIsClassified = topic_by_id.containsKey(pass.getId());
if (passIsClassified) {
return topic_by_id.get(pass.getId()).equals(topic);
}
if (topic.equals(DEFAULT_TOPIC)) {
topic_by_id.put(pass.getId(), DEFAULT_TOPIC);
if (pass_id_list_by_topic.get(DEFAULT_TOPIC) == null) {
pass_id_list_by_topic.put(DEFAULT_TOPIC, new HashSet<String>());
}
final String id = pass.getId();
final Set<String> strings = pass_id_list_by_topic.get(DEFAULT_TOPIC);
if (id!=null) {
strings.add(id);
}
return true;
}
return false;
}
}

View file

@ -2,33 +2,28 @@ package org.ligi.passandroid.model;
import android.support.annotation.Nullable;
import org.ligi.passandroid.model.comparator.PassSortOrder;
import java.util.List;
public interface PassStore {
boolean deletePassWithId(String id);
String getPathForID(String id);
List<FiledPass> getPassList();
void preCachePassesList();
void deleteCacheForId(String id);
void refreshPassesList();
Pass getPassbookForId(String id);
boolean deletePassWithId(String id);
void sort(PassSortOrder order);
String getPathForID(String id);
void preCachePassesList();
List<FiledPass> getPassList();
@Nullable
Pass getCurrentPass();
void setCurrentPass(@Nullable Pass pass);
PassClassifier getClassifier();
}

View file

@ -0,0 +1,43 @@
package org.ligi.passandroid.model;
import org.ligi.passandroid.model.comparator.PassSortOrder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class PassStoreProjection {
private List<FiledPass> passList = new ArrayList<>();
private final PassStore passStore;
private final String topic;
private final PassSortOrder passSortOrder;
public PassStoreProjection(final PassStore passStore, final String topic, PassSortOrder order) {
this.passStore = passStore;
this.topic = topic;
this.passSortOrder = order;
refresh();
}
public List<FiledPass> getPassList() {
return passList;
}
public void refresh() {
ArrayList<FiledPass> newPassList = new ArrayList<>();
for (FiledPass filedPass : passStore.getPassList()) {
if (passStore.getClassifier().isPassOnTopic(filedPass, topic)) {
newPassList.add(filedPass);
}
}
Collections.sort(newPassList, passSortOrder.toComparator());
passList = newPassList;
}
}

View file

@ -1,5 +1,6 @@
package org.ligi.passandroid.ui;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.view.ActionMode;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
@ -15,6 +16,7 @@ import org.ligi.passandroid.R;
import org.ligi.passandroid.model.FiledPass;
import org.ligi.passandroid.model.Pass;
import org.ligi.passandroid.model.PassStore;
import org.ligi.passandroid.model.PassStoreProjection;
import java.util.List;
@ -25,10 +27,13 @@ public class PassAdapter extends RecyclerView.Adapter<PassViewHolder> {
@Inject
PassStore passStore;
protected final PassListActivity passListActivity;
protected final AppCompatActivity passListActivity;
private final PassStoreProjection passStoreProjection;
ActionMode actionMode;
public PassAdapter(PassListActivity passListActivity) {
public PassAdapter(AppCompatActivity passListActivity, PassStoreProjection passStoreProjection) {
this.passStoreProjection = passStoreProjection;
App.component().inject(this);
this.passListActivity = passListActivity;
}
@ -42,10 +47,10 @@ public class PassAdapter extends RecyclerView.Adapter<PassViewHolder> {
}
@Override
public void onBindViewHolder(PassViewHolder viewHolder, final int longClickedCardPosition) {
final Pass pass = passStore.getPassList().get(longClickedCardPosition);
public void onBindViewHolder(final PassViewHolder viewHolder, int longClickedCardPosition) {
final Pass pass = passStoreProjection.getPassList().get(longClickedCardPosition);
viewHolder.apply(pass,passListActivity);
viewHolder.apply(pass, passListActivity);
final CardView root = viewHolder.root;
@ -62,7 +67,7 @@ public class PassAdapter extends RecyclerView.Adapter<PassViewHolder> {
@Override
public boolean onLongClick(View v) {
final Pass pass = getList().get(longClickedCardPosition);
final Pass pass = getList().get(viewHolder.getAdapterPosition());
if (actionMode != null) {
final boolean clickedOnDifferentItem = actionMode.getTag() == null || !actionMode.getTag().equals(pass);
@ -88,7 +93,7 @@ public class PassAdapter extends RecyclerView.Adapter<PassViewHolder> {
@Override
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
if (new PassMenuOptions(passListActivity, getList().get(longClickedCardPosition)).process(menuItem)) {
if (new PassMenuOptions(passListActivity, getList().get(viewHolder.getAdapterPosition())).process(menuItem)) {
actionMode.finish();
return true;
}
@ -103,7 +108,7 @@ public class PassAdapter extends RecyclerView.Adapter<PassViewHolder> {
}
});
actionMode.setTag(getList().get(longClickedCardPosition));
actionMode.setTag(getList().get(viewHolder.getAdapterPosition()));
root.setCardElevation(v.getContext().getResources().getDimension(R.dimen.card_longclick_elevation));
@ -119,8 +124,8 @@ public class PassAdapter extends RecyclerView.Adapter<PassViewHolder> {
return position;
}
public List<FiledPass> getList() {
return passStore.getPassList();
private List<FiledPass> getList() {
return passStoreProjection.getPassList();
}
@Override

View file

@ -8,10 +8,13 @@ import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@ -31,6 +34,7 @@ import org.ligi.passandroid.events.SortOrderChangeEvent;
import org.ligi.passandroid.events.TypeFocusEvent;
import org.ligi.passandroid.helper.PassUtil;
import org.ligi.passandroid.model.FiledPass;
import org.ligi.passandroid.model.PassClassifier;
import org.ligi.snackengage.SnackEngage;
import org.ligi.snackengage.snacks.DefaultRateSnack;
import org.ligi.tracedroid.TraceDroid;
@ -40,15 +44,18 @@ import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class PassListActivity extends PassAndroidActivity {
public class PassListActivity extends PassAndroidActivity implements PassClassifier.OnClassificationChangeListener {
private static final int OPEN_FILE_READ_REQUEST_CODE = 1000;
private PassAdapter passAdapter;
private ActionBarDrawerToggle drawerToggle;
@Bind(R.id.content_list)
RecyclerView recyclerView;
@Bind(R.id.tab_layout)
TabLayout tabLayout;
@Bind(R.id.view_pager)
ViewPager viewPager;
@Bind(R.id.drawer_layout)
DrawerLayout drawer;
@ -59,6 +66,8 @@ public class PassListActivity extends PassAndroidActivity {
@Bind(R.id.fam)
FloatingActionsMenu floatingActionsMenu;
private PassTopicFragmentPagerAdapter adapter;
@OnClick(R.id.fab_action_create_pass)
void onFABClick() {
final FiledPass pass = PassUtil.createEmptyPass();
@ -86,7 +95,6 @@ public class PassListActivity extends PassAndroidActivity {
@Bind(R.id.fab_action_open_file)
FloatingActionButton openFileFAB;
public final static int VERSION_STARTING_TO_SUPPORT_STORAGE_FRAMEWORK = 19;
@OnClick(R.id.fab_action_open_file)
@ -131,12 +139,9 @@ public class PassListActivity extends PassAndroidActivity {
public void run() {
passStore.refreshPassesList();
passStore.sort(settings.getSortOrder());
AXT.at(emptyView).setVisibility(passStore.getPassList().isEmpty());
passAdapter.notifyDataSetChanged();
}
});
@ -157,10 +162,6 @@ public class PassListActivity extends PassAndroidActivity {
AXT.at(openFileFAB).setVisibility(Build.VERSION.SDK_INT >= VERSION_STARTING_TO_SUPPORT_STORAGE_FRAMEWORK);
final LinearLayoutManager llm = new LinearLayoutManager(this);
llm.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(llm);
// don't want too many windows in worst case - so check for errors first
if (TraceDroid.getStackTraceFiles().length > 0) {
tracker.trackEvent("ui_event", "send", "stacktraces", null);
@ -185,18 +186,23 @@ public class PassListActivity extends PassAndroidActivity {
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
passAdapter = new PassAdapter(this);
recyclerView.setAdapter(passAdapter);
adapter = new PassTopicFragmentPagerAdapter(passStore.getClassifier(), getSupportFragmentManager());
viewPager.setAdapter(adapter);
tabLayout.setupWithViewPager(viewPager);
}
private void scrollToType(String type) {
/*
for (int i = 0; i < passAdapter.getItemCount(); i++) {
if (passStore.getPassList().get(i).getTypeNotNull().equals(type)) {
recyclerView.scrollToPosition(i);
return; // we are done
}
}
*/
}
@ -222,6 +228,9 @@ public class PassListActivity extends PassAndroidActivity {
bus.register(this);
refreshPasses();
adapter.notifyDataSetChanged();
passStore.getClassifier().onClassificationChangeListeners.add(this);
}
@Override
@ -245,8 +254,54 @@ public class PassListActivity extends PassAndroidActivity {
@Override
protected void onPause() {
passStore.getClassifier().onClassificationChangeListeners.remove(this);
bus.unregister(this);
super.onPause();
}
@Override
public void OnClassificationChange() {
refreshPasses();
adapter.notifyDataSetChanged();
tabLayout.setupWithViewPager(viewPager);
}
private static class PassTopicFragmentPagerAdapter extends FragmentStatePagerAdapter {
private String[] topic_array;
private final PassClassifier passClassifier;
public PassTopicFragmentPagerAdapter(PassClassifier passClassifier, FragmentManager fragmentManager) {
super(fragmentManager);
this.passClassifier = passClassifier;
notifyDataSetChanged();
}
@Override
public void notifyDataSetChanged() {
topic_array = passClassifier.getTopics().toArray(new String[passClassifier.getTopics().size()]);
super.notifyDataSetChanged();
}
@Override
public Fragment getItem(int position) {
return PassListFragment.newInstance(topic_array[position]);
}
@Override
public int getItemPosition(Object object) {
return POSITION_NONE; // TODO - return POSITION_UNCHANGED in some cases
}
@Override
public int getCount() {
return topic_array.length;
}
@Override
public CharSequence getPageTitle(int position) {
return topic_array[position];
}
}
}

View file

@ -0,0 +1,113 @@
package org.ligi.passandroid.ui;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.ligi.passandroid.App;
import org.ligi.passandroid.R;
import org.ligi.passandroid.model.FiledPass;
import org.ligi.passandroid.model.PassClassifier;
import org.ligi.passandroid.model.PassStore;
import org.ligi.passandroid.model.PassStoreProjection;
import org.ligi.passandroid.model.Settings;
import javax.inject.Inject;
public class PassListFragment extends Fragment implements PassClassifier.OnClassificationChangeListener {
private static final String BUNDLE_KEY_TOPIC = "topic";
private PassStoreProjection passStoreProjection;
private PassAdapter adapter;
public static PassListFragment newInstance(final String topic) {
PassListFragment myFragment = new PassListFragment();
Bundle args = new Bundle();
args.putString(BUNDLE_KEY_TOPIC, topic);
myFragment.setArguments(args);
return myFragment;
}
@Inject
PassStore passStore;
@Inject
Settings settings;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
final View inflate = inflater.inflate(R.layout.pass_recycler, container, false);
final RecyclerView recyclerView = (RecyclerView) inflate.findViewById(R.id.pass_recyclerview);
App.component().inject(this);
passStoreProjection = new PassStoreProjection(passStore, getArguments().getString(BUNDLE_KEY_TOPIC), settings.getSortOrder());
adapter = new PassAdapter((AppCompatActivity) getActivity(), passStoreProjection);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
final FiledPass pass = passStoreProjection.getPassList().get(viewHolder.getAdapterPosition());
if (passStore.getClassifier().isPassOnTopic(pass, "TRASH")) {
passStore.getClassifier().moveToTopic(pass, PassClassifier.DEFAULT_TOPIC);
} else {
passStore.getClassifier().moveToTopic(pass, "TRASH");
}
//Remove swiped item from list and notify the RecyclerView
/*final Pass passbookAt = passStoreProjection.getPassList().get(viewHolder.getAdapterPosition());
passStore.deletePassWithId(passbookAt.getId());
adapter.notifyItemRemoved(viewHolder.getAdapterPosition());
Snackbar.make(getView(),"Deleted", Snackbar.LENGTH_LONG)
.setAction("Undo", new View.OnClickListener() {
@Override
public void onClick(View v) {
}
}).show();
*/
}
};
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
itemTouchHelper.attachToRecyclerView(recyclerView);
passStore.getClassifier().onClassificationChangeListeners.add(this);
return inflate;
}
@Override
public void onDestroyView() {
super.onDestroyView();
passStore.getClassifier().onClassificationChangeListeners.remove(this);
}
@Override
public void OnClassificationChange() {
passStoreProjection.refresh();
adapter.notifyDataSetChanged();
}
}

View file

@ -21,13 +21,23 @@
android:singleLine="false"
android:text="@string/empty_text_view" />
<android.support.v7.widget.RecyclerView
android:id="@+id/content_list"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:choiceMode="singleChoice"
android:divider="@null"
android:listSelector="@android:color/transparent" />
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>
<net.i2p.android.ext.floatingactionbutton.FloatingActionsMenu
android:id="@+id/fam"

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pass_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:choiceMode="singleChoice"
android:divider="@null"
android:listSelector="@android:color/transparent" />

View file

@ -39,7 +39,7 @@ The app is offline usable once you downloaded the pass!
<h2>Why this app?</h2>
The tickets for the Chaos Congress were only for print and iPhone passbooks.
Ligi - the original author - didn\'t want to print the ticket or buy an Apple device, so he programmed PassAndroid.
<a href="https://twitter.com/mr_ligi/status/678708731190841346">Source</a>
<a href="https://twitter.com/mr_ligi/status/678708731190841346">Source</a>
<H1>Credits</H1>
Big thanks to <a href="http://ltorrecilla.com/">Luis Torrecilla</a> for the Design & <a href="https://github.com/cketti">Cketti</a> for pull-requests!

View file

@ -0,0 +1,105 @@
package org.ligi.passandroid.unittest;
import org.junit.Test;
import org.ligi.passandroid.model.FiledPass;
import org.ligi.passandroid.model.PassClassifier;
import org.ligi.passandroid.model.PassImpl;
import org.mockito.internal.util.collections.Sets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
public class ThePassClassifier {
public static final String ID_1 = "ID1";
public static final String TOPIC_1 = "topic1";
@Test
public void testThatPassIsInactiveByDefault() {
final PassClassifier tested = new PassClassifier(new HashMap<String, Set<String>>());
assertThat(tested.isPassOnTopic(getPassWithId(ID_1), PassClassifier.DEFAULT_TOPIC)).isTrue();
}
@Test
public void testThatOnlyNonDefaultTopicInTopicListWhenOnePassWithNonDefaultTopic() {
final PassClassifier tested = new PassClassifier(new HashMap<String, Set<String>>() {
{
put(TOPIC_1, Sets.newSet(ID_1));
}
});
assertThat(tested.getTopics()).containsExactly(TOPIC_1);
}
@Test
public void testThatAfterMovingFromOnlyOneTopicToDefaultTopicOnly() {
final PassClassifier tested = new PassClassifier(new HashMap<String, Set<String>>() {
{
put(TOPIC_1, Sets.newSet(ID_1));
}
});
tested.moveToTopic(getPassWithId(ID_1), PassClassifier.DEFAULT_TOPIC);
assertThat(tested.getTopics()).containsExactly(PassClassifier.DEFAULT_TOPIC);
}
@Test
public void testThatTopicIsGoneAfterMove() {
final PassClassifier tested = new PassClassifier(new HashMap<String, Set<String>>() {
{
put(TOPIC_1, Sets.newSet(ID_1));
}
});
tested.moveToTopic(getPassWithId(ID_1), PassClassifier.DEFAULT_TOPIC);
assertThat(tested.getTopics()).containsExactly(PassClassifier.DEFAULT_TOPIC);
}
@Test
public void testThatPassIsNotActiveWhenInTopic() {
final PassClassifier tested = new PassClassifier(new HashMap<String, Set<String>>() {
{
put(TOPIC_1, Sets.newSet(ID_1));
}
});
assertThat(tested.isPassOnTopic(getPassWithId(ID_1), PassClassifier.DEFAULT_TOPIC)).isFalse();
}
@Test
public void testThatPassIsInTopicAsExpected() {
final PassClassifier tested = new PassClassifier(new HashMap<String, Set<String>>() {
{
put(TOPIC_1, Sets.newSet(ID_1));
}
});
assertThat(tested.isPassOnTopic(getPassWithId(ID_1), TOPIC_1)).isTrue();
}
@Test
public void testHasAtLeastOneTopic() {
final PassClassifier tested = new PassClassifier(new HashMap<String, Set<String>>());
assertThat(tested.getTopics()).containsExactly(PassClassifier.DEFAULT_TOPIC);
}
public FiledPass getPassWithId(String id) {
PassImpl result = new PassImpl();
result.setId(id);
return result;
}
}