diff options
author | Camil Staps | 2015-05-30 16:31:31 +0200 |
---|---|---|
committer | Camil Staps | 2015-05-30 16:31:31 +0200 |
commit | 91620ce7c5451a5e7538c7524440ee015a42fd81 (patch) | |
tree | 10b8bdae0a44e3532a28e1c12ccdfa05c15f6fd7 /app/src/main/java | |
parent | Fixed app title issue (diff) |
Using JSON for serialization of keywords, feeds and filters
Diffstat (limited to 'app/src/main/java')
17 files changed, 287 insertions, 40 deletions
diff --git a/app/src/main/java/org/rssin/android/FilterSettingsActivity.java b/app/src/main/java/org/rssin/android/FilterSettingsActivity.java index 0d4af45..11aed6d 100755 --- a/app/src/main/java/org/rssin/android/FilterSettingsActivity.java +++ b/app/src/main/java/org/rssin/android/FilterSettingsActivity.java @@ -101,7 +101,7 @@ public class FilterSettingsActivity extends ActionBarActivity { case R.id.filter_settings_action_delete: filtersList.getFilters().remove(filter); try { - filtersList.save(); + filter.delete(DefaultStorageProvider.getInstance()); finish(); } catch (Exception e) { Frontend.error(this, R.string.error_delete_filter, e); @@ -227,6 +227,7 @@ public class FilterSettingsActivity extends ActionBarActivity { /** * @todo nicer selected feed layout + * @todo deselecting feeds doesn't work */ feedsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override @@ -256,17 +257,23 @@ public class FilterSettingsActivity extends ActionBarActivity { try { URL url = new URL(value); Feed feed = new Feed(url); - FeedsList.getInstance().getFeeds().add(feed); - filter.getFeedHashCodes().add(feed.hashCode()); - try { - filter.store(DefaultStorageProvider.getInstance()); feed.store(DefaultStorageProvider.getInstance()); - feedAdapter.notifyDataSetChanged(); - editText.setText(""); + FeedsList.getInstance().getFeeds().add(feed); + filter.getFeedHashCodes().add(feed.hashCode()); + + try { + filter.store(DefaultStorageProvider.getInstance()); + feed.store(DefaultStorageProvider.getInstance()); + feedAdapter.notifyDataSetChanged(); + editText.setText(""); + } catch (Exception e) { + Frontend.error(getActivity(), R.string.error_save_filters, e); + filter.getFeeds().remove(feed); + } } catch (Exception e) { - Frontend.error(getActivity(), R.string.error_save_filters, e); - filter.getFeeds().remove(feed); + Frontend.error(getActivity(), R.string.error_save_feeds, e); + FeedsList.getInstance().getFeeds().remove(feed); } } catch (MalformedURLException e) { Frontend.info(getActivity(), R.string.error_invalid_url, e); @@ -275,12 +282,7 @@ public class FilterSettingsActivity extends ActionBarActivity { }); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setMessage(R.string.filter_settings_feeds) - .setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - // @todo FIRE ZE MISSILES! - } - }); + builder.setMessage(R.string.filter_settings_feeds).setPositiveButton(R.string.button_ok, null); builder.setView(view); return builder.create(); diff --git a/app/src/main/java/org/rssin/android/FiltersActivity.java b/app/src/main/java/org/rssin/android/FiltersActivity.java index 35bd533..6efb8a9 100755 --- a/app/src/main/java/org/rssin/android/FiltersActivity.java +++ b/app/src/main/java/org/rssin/android/FiltersActivity.java @@ -37,6 +37,7 @@ public class FiltersActivity extends ActionBarActivity { private FiltersList filtersList; private ListView filtersView; + private FilterAdapter filterAdapter; private AdapterView.OnItemClickListener onFilterClickListener; private AdapterView.OnItemLongClickListener onFilterLongClickListener; @@ -55,13 +56,20 @@ public class FiltersActivity extends ActionBarActivity { finish(); } - final FilterAdapter adapter = new FilterAdapter(this, R.layout.item_filter, filtersList.getFilters()); - filtersView.setAdapter(adapter); + filterAdapter = new FilterAdapter(this, R.layout.item_filter, filtersList.getFilters()); + filtersView.setAdapter(filterAdapter); setupListeners(); } @Override + protected void onResume() { + super.onResume(); + + filterAdapter.notifyDataSetChanged(); + } + + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_filters, menu); return true; diff --git a/app/src/main/java/org/rssin/android/InternalStorageProvider.java b/app/src/main/java/org/rssin/android/InternalStorageProvider.java index 91534b3..c5a7bc1 100644 --- a/app/src/main/java/org/rssin/android/InternalStorageProvider.java +++ b/app/src/main/java/org/rssin/android/InternalStorageProvider.java @@ -56,6 +56,11 @@ class InternalStorageProvider implements StorageProvider<String, Storable>, Filt return fetchFromFilename(getFilename(key, className)); } + @Override + public void remove(String key, Class className) throws Exception { + throw new UnsupportedOperationException("Not implemented yet."); + } + public Storable fetchFromFilename(String fileName) throws ClassCastException, FileNotFoundException, IOException { FileInputStream fis = context.openFileInput(fileName); ObjectInputStream ois = new ObjectInputStream(fis); @@ -118,6 +123,11 @@ class InternalStorageProvider implements StorageProvider<String, Storable>, Filt } @Override + public void storeFilter(String key, Filter filter) throws Exception { + throw new UnsupportedOperationException("Not implemented yet."); + } + + @Override public Filter getFilter(String key) { try { return (Filter) fetch(key, Filter.class); diff --git a/app/src/main/java/org/rssin/android/SharedPreferencesStorageProvider.java b/app/src/main/java/org/rssin/android/SharedPreferencesStorageProvider.java index 161e1c6..4cc9386 100644 --- a/app/src/main/java/org/rssin/android/SharedPreferencesStorageProvider.java +++ b/app/src/main/java/org/rssin/android/SharedPreferencesStorageProvider.java @@ -3,6 +3,7 @@ package org.rssin.android; import android.content.Context; import android.content.SharedPreferences; import android.util.Base64; +import android.util.Log; import org.rssin.rssin.Feed; import org.rssin.rssin.Filter; @@ -70,9 +71,10 @@ class SharedPreferencesStorageProvider implements StorageProvider, FilterStorage oos.close(); String string = Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT); - SharedPreferences.Editor editor = context.getSharedPreferences(key.toString(), Context.MODE_PRIVATE).edit(); - editor.putString(element.getClass().getName(), string); - boolean works = editor.commit(); + context.getSharedPreferences(key.toString(), Context.MODE_PRIVATE) + .edit() + .putString(element.getClass().getName(), string) + .apply(); if (element.getClass() == Filter.class) { storeFilterKey(key); @@ -88,19 +90,25 @@ class SharedPreferencesStorageProvider implements StorageProvider, FilterStorage throw new IOException("No sharedPreference with key " + key.toString() + " and class " + className.getName()); } ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(Base64.decode(serialized, Base64.DEFAULT))); - return (Storable) className.cast(ois.readObject()); + Object obj = ois.readObject(); + return (Storable) className.cast(obj); } - protected void remove(Object key, Class className) { + @Override + public void remove(Object key, Class className) { context.getSharedPreferences(key.toString(), Context.MODE_PRIVATE) .edit() .remove(className.getName()) .apply(); + + if (className == Feed.class) { + removeFeed(key); + } } @Override public Object uniqueKey() { - return "_" + Long.toString(System.currentTimeMillis()); + return Long.toString(System.currentTimeMillis()); } @Override @@ -119,6 +127,12 @@ class SharedPreferencesStorageProvider implements StorageProvider, FilterStorage } @Override + public void storeFilter(Object key, Filter filter) throws Exception { + storeFilterKey(key); + store(key, filter); + } + + @Override public Filter getFilter(Object key) { try { return (Filter) fetch(key.toString(), Filter.class); @@ -131,11 +145,17 @@ class SharedPreferencesStorageProvider implements StorageProvider, FilterStorage public void removeFilter(Object key) { SharedPreferences sharedPreferences = context.getSharedPreferences(ADMIN_PREF_KEY, Context.MODE_PRIVATE); Set<String> names = new HashSet<>(sharedPreferences.getStringSet("filters", new HashSet<String>())); + for (String name : names) + Log.d("SPSP", "Old name: " + name); + Log.d("SPSP", "Removing name " + key.toString()); names.remove(key.toString()); + for (String name : names) + Log.d("SPSP", "New name: " + name); sharedPreferences .edit() .putStringSet("filters", names) .apply(); + remove(key, Filter.class); } /** @@ -171,6 +191,7 @@ class SharedPreferencesStorageProvider implements StorageProvider, FilterStorage try { return (Feed) fetch(key.toString(), Feed.class); } catch (Exception e) { + Log.e("SPSP", "Couldn't get feed", e); return null; } } diff --git a/app/src/main/java/org/rssin/neurons/FeedSorter.java b/app/src/main/java/org/rssin/neurons/FeedSorter.java index fe1e998..3d420a6 100755 --- a/app/src/main/java/org/rssin/neurons/FeedSorter.java +++ b/app/src/main/java/org/rssin/neurons/FeedSorter.java @@ -2,7 +2,7 @@ package org.rssin.neurons; import org.rssin.rss.FeedItem;
import org.rssin.storage.Storable;
-import org.rssin.tools.SerializationTools;
+import org.rssin.serialization.SerializationTools;
import java.io.IOException;
import java.util.ArrayList;
diff --git a/app/src/main/java/org/rssin/neurons/MultiNeuralNetwork.java b/app/src/main/java/org/rssin/neurons/MultiNeuralNetwork.java index 0f5c31b..ece53a8 100755 --- a/app/src/main/java/org/rssin/neurons/MultiNeuralNetwork.java +++ b/app/src/main/java/org/rssin/neurons/MultiNeuralNetwork.java @@ -1,6 +1,6 @@ package org.rssin.neurons;
-import org.rssin.tools.SerializationTools;
+import org.rssin.serialization.SerializationTools;
import java.io.IOException;
import java.io.Serializable;
diff --git a/app/src/main/java/org/rssin/neurons/NeuralNetwork.java b/app/src/main/java/org/rssin/neurons/NeuralNetwork.java index 2cbe284..c990be9 100755 --- a/app/src/main/java/org/rssin/neurons/NeuralNetwork.java +++ b/app/src/main/java/org/rssin/neurons/NeuralNetwork.java @@ -2,7 +2,7 @@ package org.rssin.neurons; import android.annotation.SuppressLint;
-import org.rssin.tools.SerializationTools;
+import org.rssin.serialization.SerializationTools;
import java.io.IOException;
import java.io.Serializable;
diff --git a/app/src/main/java/org/rssin/neurons/Neuron.java b/app/src/main/java/org/rssin/neurons/Neuron.java index 6a4970a..203a450 100755 --- a/app/src/main/java/org/rssin/neurons/Neuron.java +++ b/app/src/main/java/org/rssin/neurons/Neuron.java @@ -1,6 +1,6 @@ package org.rssin.neurons;
-import org.rssin.tools.SerializationTools;
+import org.rssin.serialization.SerializationTools;
import java.io.IOException;
import java.io.Serializable;
diff --git a/app/src/main/java/org/rssin/rssin/Feed.java b/app/src/main/java/org/rssin/rssin/Feed.java index 77e58c0..55d0e5d 100755 --- a/app/src/main/java/org/rssin/rssin/Feed.java +++ b/app/src/main/java/org/rssin/rssin/Feed.java @@ -1,19 +1,25 @@ package org.rssin.rssin; +import android.util.Log; + +import org.json.JSONException; +import org.json.JSONObject; +import org.rssin.serialization.Jsonable; import org.rssin.storage.Storable; import org.rssin.storage.StorageProvider; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.net.MalformedURLException; import java.net.URL; /** * Feed holder * @author Camil Staps - * - * @todo Write read & writeObject with JSON or so to make it easier to change this class later */ -public class Feed implements Storable, Comparable<Feed> { - private static int serialVersionUID = 0; +public class Feed implements Storable, Comparable<Feed>, Jsonable { + private static final long serialVersionUID = 0; /** * This feed does not have any information about the current status of the feed. @@ -47,6 +53,14 @@ public class Feed implements Storable, Comparable<Feed> { this.title = title; } + private void writeObject(ObjectOutputStream stream) throws JSONException, IOException { + stream.writeObject(toJson().toString()); + } + + private void readObject(ObjectInputStream stream) throws JSONException, IOException, ClassNotFoundException { + fromJson(new JSONObject((String) stream.readObject())); + } + public String getTitle() { return title; } @@ -117,4 +131,24 @@ public class Feed implements Storable, Comparable<Feed> { public int compareTo(Feed another) { return title.compareTo(another.title); } + + @Override + public JSONObject toJson() throws JSONException { + JSONObject json = new JSONObject(); + json.put("title", title); + json.put("url", url.toString()); + json.put("storageKey", storageKey.toString()); + return json; + } + + @Override + public void fromJson(JSONObject json) throws JSONException { + title = json.getString("title"); + storageKey = json.getString("storageKey"); + try { + url = new URL(json.getString("url")); + } catch (MalformedURLException e) { + throw new JSONException("Invalid URL"); + } + } } diff --git a/app/src/main/java/org/rssin/rssin/FeedLoaderAndSorter.java b/app/src/main/java/org/rssin/rssin/FeedLoaderAndSorter.java index e1fbbab..745e944 100755 --- a/app/src/main/java/org/rssin/rssin/FeedLoaderAndSorter.java +++ b/app/src/main/java/org/rssin/rssin/FeedLoaderAndSorter.java @@ -81,6 +81,13 @@ public class FeedLoaderAndSorter { }
}
+ /**
+ * @todo Why do we check on author?
+ * @todo Should we check only on whole words / give whole word occurences higher relevance?
+ * @todo At least only whole words for categories; otherwise Port connaisseurs will read only Sport news
+ * @param item
+ * @return
+ */
private boolean matchesKeyword(FeedItem item)
{
for(Keyword keyword : filter.getKeywords())
diff --git a/app/src/main/java/org/rssin/rssin/Filter.java b/app/src/main/java/org/rssin/rssin/Filter.java index 514d845..7b2c54e 100755 --- a/app/src/main/java/org/rssin/rssin/Filter.java +++ b/app/src/main/java/org/rssin/rssin/Filter.java @@ -3,11 +3,20 @@ package org.rssin.rssin; import android.text.TextUtils; import android.util.Log; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; import org.rssin.neurons.FeedSorter; +import org.rssin.serialization.JsonSerializer; +import org.rssin.serialization.Jsonable; import org.rssin.storage.FeedStorageProvider; +import org.rssin.storage.FilterStorageProvider; import org.rssin.storage.Storable; import org.rssin.storage.StorageProvider; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.List; @@ -17,7 +26,7 @@ import java.util.List; * * @todo Write read & writeObject with JSON or so to make it easier to change this class later */ -public class Filter implements Storable, Comparable<Filter> { +public class Filter implements Storable, Comparable<Filter>, Jsonable { private static final long serialVersionUID = 0; @@ -25,9 +34,9 @@ public class Filter implements Storable, Comparable<Filter> { * A filter is a list of Feeds with a list of Keywords. A title can be added as well. * The List of Feeds is transient. We only store a list of Feed hashcodes */ - private final List<Integer> feedHashCodes; + private List<Integer> feedHashCodes; private transient List<Feed> feeds; - private final List<Keyword> keywords; + private List<Keyword> keywords; private String title = ""; /** @@ -76,6 +85,14 @@ public class Filter implements Storable, Comparable<Filter> { setTitle(title); } + private void writeObject(ObjectOutputStream stream) throws JSONException, IOException { + stream.writeObject(toJson().toString()); + } + + private void readObject(ObjectInputStream stream) throws JSONException, IOException, ClassNotFoundException { + fromJson(new JSONObject((String) stream.readObject())); + } + public List<Integer> getFeedHashCodes() { return feedHashCodes; } @@ -165,11 +182,15 @@ public class Filter implements Storable, Comparable<Filter> { * @param storageProvider * @throws Exception */ - public synchronized void store(StorageProvider<Object,Filter> storageProvider) throws Exception { + public synchronized void store(FilterStorageProvider storageProvider) throws Exception { if (storageKey == null) { storageKey = storageProvider.uniqueKey(); } - storageProvider.store(storageKey, this); + storageProvider.storeFilter(storageKey, this); + } + + public synchronized void delete(FilterStorageProvider storageProvider) throws Exception { + storageProvider.removeFilter(storageKey); } /** @@ -189,6 +210,29 @@ public class Filter implements Storable, Comparable<Filter> { return title.compareTo(another.title); } + @Override + public JSONObject toJson() throws JSONException { + JSONObject json = new JSONObject(); + json.put("title", title); + json.put("feedHashCodes", JsonSerializer.numbersListToJson(feedHashCodes)); + json.put("keywords", JsonSerializer.listToJson(keywords)); + json.put("storageKey", storageKey.toString()); + return json; + } + + @Override + public void fromJson(JSONObject json) throws JSONException { + title = json.getString("title"); + feedHashCodes = JsonSerializer.integersListFromJson(json.getJSONArray("feedHashCodes")); + JSONArray keywordsJson = json.getJSONArray("keywords"); + keywords = new ArrayList<>(); + for (int i = 0; i < keywordsJson.length(); i++) { + keywords.add(new Keyword("")); + } + JsonSerializer.listFromJson(keywordsJson, keywords); + storageKey = json.getString("storageKey"); + } + private class FeedSorterStorer implements Runnable { private final Object storageKey; private final StorageProvider storageProvider; @@ -210,4 +254,5 @@ public class Filter implements Storable, Comparable<Filter> { } } } + } diff --git a/app/src/main/java/org/rssin/rssin/Keyword.java b/app/src/main/java/org/rssin/rssin/Keyword.java index 9b5adfb..532bd59 100644 --- a/app/src/main/java/org/rssin/rssin/Keyword.java +++ b/app/src/main/java/org/rssin/rssin/Keyword.java @@ -1,24 +1,39 @@ package org.rssin.rssin; +import org.json.JSONException; +import org.json.JSONObject; +import org.rssin.serialization.Jsonable; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; /** * Keyword holder * @author Camil Staps */ -public class Keyword implements Serializable { +public class Keyword implements Serializable, Jsonable { - private static final long serialVersionUID = 0; + private static final long serialVersionUID = 1; /** * For now, Keywords only have a single String keyword. Later, more options may be added */ - private final String keyword; + private String keyword; public Keyword(String keyword) { this.keyword = keyword.trim(); } + private void writeObject(ObjectOutputStream stream) throws JSONException, IOException { + stream.writeObject(toJson().toString()); + } + + private void readObject(ObjectInputStream stream) throws JSONException, IOException, ClassNotFoundException { + fromJson(new JSONObject((String) stream.readObject())); + } + public String getKeyword() { return keyword; } @@ -28,4 +43,15 @@ public class Keyword implements Serializable { return keyword; } + @Override + public JSONObject toJson() throws JSONException { + JSONObject json = new JSONObject(); + json.put("keyword", keyword); + return json; + } + + @Override + public void fromJson(JSONObject json) throws JSONException { + keyword = json.getString("keyword"); + } } diff --git a/app/src/main/java/org/rssin/serialization/JsonSerializer.java b/app/src/main/java/org/rssin/serialization/JsonSerializer.java new file mode 100644 index 0000000..ca060a5 --- /dev/null +++ b/app/src/main/java/org/rssin/serialization/JsonSerializer.java @@ -0,0 +1,59 @@ +package org.rssin.serialization; + +import org.json.JSONArray; +import org.json.JSONException; + +import java.util.ArrayList; +import java.util.List; + +/** + * Common tools for JSON serialization + * @author + */ +public class JsonSerializer { + + /** + * Make a JSONArray from a List<? extends Jsonable> + * @param list the list to serialize + * @return the JSONArray + * @throws JSONException if this happens, it should be a problem in the Jsonable's toJson + */ + public static JSONArray listToJson(List<? extends Jsonable> list) throws JSONException { + JSONArray json = new JSONArray(); + for (Jsonable item : list) { + json.put(item.toJson()); + } + return json; + } + + public static JSONArray numbersListToJson(List<? extends Number> list) throws JSONException { + JSONArray json = new JSONArray(); + for (Number item : list) { + json.put(item); + } + return json; + } + + /** + * Read a JSONArray into a List<? extends Jsonable> + * Items are read until either the JSONArray is empty or the List is full. + * + * @param json the JSONArray + * @param list the list to read into + * @throws JSONException + */ + public static void listFromJson(JSONArray json, List<? extends Jsonable> list) throws JSONException { + for (int i = 0; i < json.length() && i < list.size(); i++) { + list.get(i).fromJson(json.getJSONObject(i)); + } + } + + public static List<Integer> integersListFromJson(JSONArray json) throws JSONException { + List<Integer> list = new ArrayList<>(); + for (int i = 0; i < json.length(); i++) { + list.add(json.getInt(i)); + } + return list; + } + +} diff --git a/app/src/main/java/org/rssin/serialization/Jsonable.java b/app/src/main/java/org/rssin/serialization/Jsonable.java new file mode 100644 index 0000000..d97f2fd --- /dev/null +++ b/app/src/main/java/org/rssin/serialization/Jsonable.java @@ -0,0 +1,15 @@ +package org.rssin.serialization; + +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Classes that can be stored with JSON + * @author Camil Staps + */ +public interface Jsonable { + + JSONObject toJson() throws JSONException; + void fromJson(JSONObject json) throws JSONException; + +} diff --git a/app/src/main/java/org/rssin/tools/SerializationTools.java b/app/src/main/java/org/rssin/serialization/SerializationTools.java index 0886ded..7b06f55 100755 --- a/app/src/main/java/org/rssin/tools/SerializationTools.java +++ b/app/src/main/java/org/rssin/serialization/SerializationTools.java @@ -1,4 +1,4 @@ -package org.rssin.tools;
+package org.rssin.serialization;
import java.io.IOException;
import java.io.ObjectInputStream;
diff --git a/app/src/main/java/org/rssin/storage/FilterStorageProvider.java b/app/src/main/java/org/rssin/storage/FilterStorageProvider.java index b1807d9..694bf70 100644 --- a/app/src/main/java/org/rssin/storage/FilterStorageProvider.java +++ b/app/src/main/java/org/rssin/storage/FilterStorageProvider.java @@ -17,6 +17,13 @@ public interface FilterStorageProvider<K> { List<Filter> allFilters(); /** + * Save a Filter + * @param key + * @param filter + */ + void storeFilter(K key, Filter filter) throws Exception; + + /** * Get the filter of a specific key * @param key * @return @@ -29,4 +36,10 @@ public interface FilterStorageProvider<K> { */ void removeFilter(K key); + /** + * Get a new, unique, usable key + * @return + */ + K uniqueKey(); + } diff --git a/app/src/main/java/org/rssin/storage/StorageProvider.java b/app/src/main/java/org/rssin/storage/StorageProvider.java index 7e382c2..56c8a4c 100644 --- a/app/src/main/java/org/rssin/storage/StorageProvider.java +++ b/app/src/main/java/org/rssin/storage/StorageProvider.java @@ -21,6 +21,13 @@ public interface StorageProvider<K,E extends Storable> { E fetch(K key, Class className) throws Exception; /** + * Remove an element + * @param key + * @param className + */ + void remove(K key, Class className) throws Exception; + + /** * Get a new, unique, usable key * @return */ |