Simplify the PassClassifier
This commit is contained in:
parent
6206bcbb23
commit
62fa6a8941
8 changed files with 74 additions and 91 deletions
|
@ -1,11 +1,12 @@
|
|||
package org.ligi.passandroid.injections;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
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 java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -40,6 +41,7 @@ public class FixedPassListPassStore implements PassStore {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Pass getPassbookForId(String id) {
|
||||
for (Pass pass : passes) {
|
||||
if (pass.getId().equals(id)) {
|
||||
|
@ -62,7 +64,7 @@ public class FixedPassListPassStore implements PassStore {
|
|||
@Override
|
||||
public PassClassifier getClassifier() {
|
||||
if (passClassifier == null) {
|
||||
passClassifier = new PassClassifier(new HashMap<String, Collection<String>>());
|
||||
passClassifier = new PassClassifier(new HashMap<String, String>(), this);
|
||||
}
|
||||
return passClassifier;
|
||||
}
|
||||
|
|
|
@ -27,8 +27,8 @@ public class AndroidFileSystemPassStore implements PassStore {
|
|||
path = settings.getPassesDir();
|
||||
|
||||
refreshPassesList();
|
||||
final File classificationFile = new File(settings.getStateDir(), "classification_state.json");
|
||||
passClassifier = new FileBackedPassClassifier(classificationFile);
|
||||
final File classificationFile = new File(settings.getStateDir(), "classifier_state.json");
|
||||
passClassifier = new FileBackedPassClassifier(classificationFile, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -5,7 +5,6 @@ import com.squareup.moshi.Moshi;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -17,8 +16,8 @@ public class FileBackedPassClassifier extends PassClassifier {
|
|||
private final JsonAdapter<Map> adapter;
|
||||
private final File backed_file;
|
||||
|
||||
public FileBackedPassClassifier(final File backed_file) {
|
||||
super(getBase(backed_file));
|
||||
public FileBackedPassClassifier(final File backed_file, final PassStore passStore) {
|
||||
super(getBase(backed_file), passStore);
|
||||
|
||||
this.backed_file = backed_file;
|
||||
adapter = getAdapter();
|
||||
|
@ -30,11 +29,11 @@ public class FileBackedPassClassifier extends PassClassifier {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, Collection<String>> getBase(final File backed_file) {
|
||||
private static Map<String, String> getBase(final File backed_file) {
|
||||
|
||||
if (backed_file.exists()) {
|
||||
try {
|
||||
return (Map<String, Collection<String>>) getAdapter().fromJson(Okio.buffer(Okio.source(backed_file)));
|
||||
return (Map<String, String>) getAdapter().fromJson(Okio.buffer(Okio.source(backed_file)));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
@ -71,7 +70,7 @@ public class FileBackedPassClassifier extends PassClassifier {
|
|||
|
||||
if (buffer != null) {
|
||||
try {
|
||||
adapter.toJson(buffer, pass_id_list_by_topic);
|
||||
adapter.toJson(buffer, topic_by_id);
|
||||
buffer.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
package org.ligi.passandroid.model;
|
||||
|
||||
import java.util.Collection;
|
||||
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 {
|
||||
|
@ -18,65 +16,36 @@ public class PassClassifier {
|
|||
|
||||
public final static String DEFAULT_TOPIC = "active";
|
||||
|
||||
protected final Map<String, Collection<String>> pass_id_list_by_topic;
|
||||
private final Map<String, String> topic_by_id = new HashMap<>();
|
||||
protected final Map<String, String> topic_by_id;
|
||||
|
||||
public PassClassifier(Map<String, Collection<String>> pass_id_list_by_topic) {
|
||||
this.pass_id_list_by_topic = pass_id_list_by_topic;
|
||||
private final PassStore passStore;
|
||||
|
||||
public PassClassifier(Map<String, String> topic_by_id, PassStore passStore) {
|
||||
this.topic_by_id = topic_by_id;
|
||||
this.passStore = passStore;
|
||||
|
||||
processDataChange();
|
||||
}
|
||||
|
||||
public void processDataChange() {
|
||||
calculateReverseMapping();
|
||||
removeEmpty();
|
||||
makeSureDefaultTopicExists();
|
||||
}
|
||||
|
||||
private void calculateReverseMapping() {
|
||||
topic_by_id.clear();
|
||||
for (Map.Entry<String, Collection<String>> stringListEntry : pass_id_list_by_topic.entrySet()) {
|
||||
final String topic = stringListEntry.getKey();
|
||||
for (String id : stringListEntry.getValue()) {
|
||||
topic_by_id.put(id, topic);
|
||||
final Set<String> keysToRemove = new HashSet<>();
|
||||
|
||||
for (String key : topic_by_id.keySet()) {
|
||||
if (passStore.getPassbookForId(key) == null) {
|
||||
keysToRemove.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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, Collection<String>> stringListEntry : pass_id_list_by_topic.entrySet()) {
|
||||
if (stringListEntry.getValue().isEmpty()) {
|
||||
toRemove.add(stringListEntry.getKey());
|
||||
}
|
||||
for (String key : keysToRemove) {
|
||||
topic_by_id.remove(key);
|
||||
}
|
||||
|
||||
for (String s : toRemove) {
|
||||
pass_id_list_by_topic.remove(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 Collection<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);
|
||||
topic_by_id.put(pass.getId(), newTopic);
|
||||
|
||||
processDataChange();
|
||||
|
||||
|
@ -89,29 +58,25 @@ public class PassClassifier {
|
|||
}
|
||||
}
|
||||
|
||||
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>());
|
||||
|
||||
public Collection<String> getTopics() {
|
||||
final Collection<String> res = new HashSet<>();
|
||||
|
||||
res.addAll(topic_by_id.values());
|
||||
|
||||
if (res.isEmpty()) {
|
||||
res.add(DEFAULT_TOPIC);
|
||||
}
|
||||
|
||||
final Collection<String> strings = pass_id_list_by_topic.get(newTopic);
|
||||
if (!strings.contains(pass.getId())) {
|
||||
strings.add(pass.getId());
|
||||
processDataChange();
|
||||
}
|
||||
}
|
||||
|
||||
public String[] getTopics() {
|
||||
final Set<String> strings = pass_id_list_by_topic.keySet();
|
||||
return pass_id_list_by_topic.keySet().toArray(new String[strings.size()]);
|
||||
return res;
|
||||
}
|
||||
|
||||
public String getTopic(Pass pass) {
|
||||
if(topic_by_id.containsKey(pass.getId())) {
|
||||
if (topic_by_id.containsKey(pass.getId())) {
|
||||
return topic_by_id.get(pass.getId());
|
||||
}
|
||||
|
||||
upsertPassToTopic(pass,DEFAULT_TOPIC);
|
||||
topic_by_id.put(pass.getId(),DEFAULT_TOPIC);
|
||||
|
||||
return DEFAULT_TOPIC;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ public interface PassStore {
|
|||
|
||||
void refreshPassesList();
|
||||
|
||||
@Nullable
|
||||
Pass getPassbookForId(String id);
|
||||
|
||||
boolean deletePassWithId(String id);
|
||||
|
|
|
@ -41,6 +41,8 @@ import org.ligi.snackengage.snacks.DefaultRateSnack;
|
|||
import org.ligi.tracedroid.TraceDroid;
|
||||
import org.ligi.tracedroid.sending.TraceDroidEmailSender;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
|
@ -294,7 +296,8 @@ public class PassListActivity extends PassAndroidActivity implements PassClassif
|
|||
|
||||
@Override
|
||||
public void notifyDataSetChanged() {
|
||||
topic_array = passClassifier.getTopics();
|
||||
final Collection<String> topics = passClassifier.getTopics();
|
||||
topic_array = topics.toArray(new String[topics.size()]);
|
||||
super.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@ import org.ligi.passandroid.model.PassStore;
|
|||
import org.ligi.passandroid.model.PassStoreProjection;
|
||||
import org.ligi.passandroid.model.Settings;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.support.v7.widget.helper.ItemTouchHelper.LEFT;
|
||||
|
@ -96,7 +98,7 @@ public class PassListFragment extends Fragment implements OnClassificationChange
|
|||
|
||||
@Nullable
|
||||
private String calculateNextTopic(final int swipeDir, final FiledPass pass) {
|
||||
final String[] topics = passStore.getClassifier().getTopics();
|
||||
final Collection<String> topics = passStore.getClassifier().getTopics();
|
||||
|
||||
switch (swipeDir) {
|
||||
case LEFT:
|
||||
|
@ -109,7 +111,7 @@ public class PassListFragment extends Fragment implements OnClassificationChange
|
|||
}
|
||||
|
||||
@Nullable
|
||||
private String getNextTopicRight(FiledPass pass, String[] topics) {
|
||||
private String getNextTopicRight(FiledPass pass, Collection<String> topics) {
|
||||
|
||||
boolean nextIsCandidate = false;
|
||||
|
||||
|
@ -126,7 +128,7 @@ public class PassListFragment extends Fragment implements OnClassificationChange
|
|||
|
||||
|
||||
@Nullable
|
||||
private String getNextTopicLeft(FiledPass pass, String[] topics) {
|
||||
private String getNextTopicLeft(FiledPass pass, Collection<String> topics) {
|
||||
String prev = null;
|
||||
|
||||
for (String topic : topics) {
|
||||
|
|
|
@ -2,46 +2,57 @@ package org.ligi.passandroid.unittest;
|
|||
|
||||
import org.junit.Test;
|
||||
import org.ligi.passandroid.model.FiledPass;
|
||||
import org.ligi.passandroid.model.Pass;
|
||||
import org.ligi.passandroid.model.PassClassifier;
|
||||
import org.ligi.passandroid.model.PassImpl;
|
||||
import org.mockito.internal.util.collections.Sets;
|
||||
import org.ligi.passandroid.model.PassStore;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class ThePassClassifier {
|
||||
|
||||
public static final String ID_1 = "ID1";
|
||||
public static final String TOPIC_1 = "topic1";
|
||||
|
||||
private PassStore getMockedPassStore() {
|
||||
final PassStore mock = mock(PassStore.class);
|
||||
|
||||
when(mock.getPassbookForId(anyString())).thenReturn(mock(Pass.class));
|
||||
|
||||
return mock;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatPassIsInactiveByDefault() {
|
||||
|
||||
final PassClassifier tested = new PassClassifier(new HashMap<String, Collection<String>>());
|
||||
final PassClassifier tested = new PassClassifier(new HashMap<String, String>(), getMockedPassStore());
|
||||
|
||||
assertThat(tested.getTopic(getPassWithId(ID_1)).equals(PassClassifier.DEFAULT_TOPIC));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatOnlyNonDefaultTopicInTopicListWhenOnePassWithNonDefaultTopic() {
|
||||
final PassClassifier tested = new PassClassifier(new HashMap<String, Collection<String>>() {
|
||||
final PassClassifier tested = new PassClassifier(new HashMap<String, String>() {
|
||||
{
|
||||
put(TOPIC_1, Sets.newSet(ID_1));
|
||||
put(ID_1, TOPIC_1);
|
||||
}
|
||||
});
|
||||
}, getMockedPassStore());
|
||||
|
||||
assertThat(tested.getTopics()).containsExactly(TOPIC_1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatAfterMovingFromOnlyOneTopicToDefaultTopicOnly() {
|
||||
final PassClassifier tested = new PassClassifier(new HashMap<String, Collection<String>>() {
|
||||
final PassClassifier tested = new PassClassifier(new HashMap<String, String>() {
|
||||
{
|
||||
put(TOPIC_1, Sets.newSet(ID_1));
|
||||
put(ID_1, TOPIC_1);
|
||||
}
|
||||
});
|
||||
}, getMockedPassStore());
|
||||
|
||||
tested.moveToTopic(getPassWithId(ID_1), PassClassifier.DEFAULT_TOPIC);
|
||||
assertThat(tested.getTopics()).containsExactly(PassClassifier.DEFAULT_TOPIC);
|
||||
|
@ -50,11 +61,11 @@ public class ThePassClassifier {
|
|||
|
||||
@Test
|
||||
public void testThatTopicIsGoneAfterMove() {
|
||||
final PassClassifier tested = new PassClassifier(new HashMap<String, Collection<String>>() {
|
||||
final PassClassifier tested = new PassClassifier(new HashMap<String, String>() {
|
||||
{
|
||||
put(TOPIC_1, Sets.newSet(ID_1));
|
||||
put(ID_1, TOPIC_1);
|
||||
}
|
||||
});
|
||||
}, getMockedPassStore());
|
||||
|
||||
tested.moveToTopic(getPassWithId(ID_1), PassClassifier.DEFAULT_TOPIC);
|
||||
assertThat(tested.getTopics()).containsExactly(PassClassifier.DEFAULT_TOPIC);
|
||||
|
@ -62,11 +73,11 @@ public class ThePassClassifier {
|
|||
|
||||
@Test
|
||||
public void testThatPassIsInTopicAsExpected() {
|
||||
final PassClassifier tested = new PassClassifier(new HashMap<String, Collection<String>>() {
|
||||
final PassClassifier tested = new PassClassifier(new HashMap<String, String>() {
|
||||
{
|
||||
put(TOPIC_1, Sets.newSet(ID_1));
|
||||
put(ID_1, TOPIC_1);
|
||||
}
|
||||
});
|
||||
}, getMockedPassStore());
|
||||
|
||||
assertThat(tested.getTopic(getPassWithId(ID_1)).equals(PassClassifier.DEFAULT_TOPIC));
|
||||
}
|
||||
|
@ -74,7 +85,7 @@ public class ThePassClassifier {
|
|||
|
||||
@Test
|
||||
public void testHasAtLeastOneTopic() {
|
||||
final PassClassifier tested = new PassClassifier(new HashMap<String, Collection<String>>());
|
||||
final PassClassifier tested = new PassClassifier(new HashMap<String, String>(), getMockedPassStore());
|
||||
|
||||
assertThat(tested.getTopics()).containsExactly(PassClassifier.DEFAULT_TOPIC);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue