diff --git a/clients/android/NewsBlur/src/com/newsblur/database/BlurDatabaseHelper.java b/clients/android/NewsBlur/src/com/newsblur/database/BlurDatabaseHelper.java index c48e60c08..a2dbbd0cb 100644 --- a/clients/android/NewsBlur/src/com/newsblur/database/BlurDatabaseHelper.java +++ b/clients/android/NewsBlur/src/com/newsblur/database/BlurDatabaseHelper.java @@ -22,6 +22,7 @@ import com.newsblur.util.FeedUtils; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * Utility class for executing DB operations on the local, private NB database. @@ -175,14 +176,20 @@ public class BlurDatabaseHelper { bulkInsertValues(DatabaseConstants.SOCIALFEED_STORY_MAP_TABLE, socialStoryValues); // handle classifiers - // NOTE: only handles top-level classifiers, which only show up for single-feed requests if (apiResponse.classifiers != null) { - List classifierValues = apiResponse.classifiers.getContentValues(); - for (ContentValues values : classifierValues) { - values.put(DatabaseConstants.CLASSIFIER_ID, impliedFeedId); + for (Map.Entry entry : apiResponse.classifiers.entrySet()) { + // the API might not have included a feed ID, in which case it deserialized as -1 and must be implied + String classifierFeedId = entry.getKey(); + if (classifierFeedId.equals("-1")) { + classifierFeedId = impliedFeedId; + } + List classifierValues = entry.getValue().getContentValues(); + for (ContentValues values : classifierValues) { + values.put(DatabaseConstants.CLASSIFIER_ID, classifierFeedId); + } + dbRW.delete(DatabaseConstants.CLASSIFIER_TABLE, DatabaseConstants.CLASSIFIER_ID + " = ?", new String[] { classifierFeedId }); + bulkInsertValues(DatabaseConstants.CLASSIFIER_TABLE, classifierValues); } - dbRW.delete(DatabaseConstants.CLASSIFIER_TABLE, DatabaseConstants.CLASSIFIER_ID + " = ?", new String[] { impliedFeedId }); - bulkInsertValues(DatabaseConstants.CLASSIFIER_TABLE, classifierValues); } // handle comments diff --git a/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java b/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java index a869fac81..6581870b8 100644 --- a/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java +++ b/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java @@ -47,6 +47,7 @@ import com.newsblur.network.domain.StoriesResponse; import com.newsblur.network.domain.StoryTextResponse; import com.newsblur.network.domain.UnreadStoryHashesResponse; import com.newsblur.serialization.BooleanTypeAdapter; +import com.newsblur.serialization.ClassifierMapTypeAdapter; import com.newsblur.serialization.DateStringTypeAdapter; import com.newsblur.serialization.FeedListTypeAdapter; import com.newsblur.serialization.StoryTypeAdapter; @@ -75,6 +76,7 @@ public class APIManager { .registerTypeAdapter(boolean.class, new BooleanTypeAdapter()) .registerTypeAdapter(Story.class, new StoryTypeAdapter()) .registerTypeAdapter(new TypeToken>(){}.getType(), new FeedListTypeAdapter()) + .registerTypeAdapter(new TypeToken>(){}.getType(), new ClassifierMapTypeAdapter()) .create(); String appVersion = context.getSharedPreferences(PrefConstants.PREFERENCES, 0).getString(AppConstants.LAST_APP_VERSION, "unknown_version"); @@ -261,8 +263,7 @@ public class APIManager { } else if (fs.isAllSaved()) { uri = Uri.parse(APIConstants.URL_STARRED_STORIES); } else { - Log.wtf(this.getClass().getName(), "Asked to get stories for FeedSet of unknown type."); - return null; + throw new IllegalStateException("Asked to get stories for FeedSet of unknown type."); } // request params common to all stories diff --git a/clients/android/NewsBlur/src/com/newsblur/network/domain/StoriesResponse.java b/clients/android/NewsBlur/src/com/newsblur/network/domain/StoriesResponse.java index 8f12e1780..2aac76985 100644 --- a/clients/android/NewsBlur/src/com/newsblur/network/domain/StoriesResponse.java +++ b/clients/android/NewsBlur/src/com/newsblur/network/domain/StoriesResponse.java @@ -1,6 +1,7 @@ package com.newsblur.network.domain; import java.util.List; +import java.util.Map; import com.google.gson.annotations.SerializedName; @@ -17,7 +18,7 @@ public class StoriesResponse extends NewsBlurResponse { @SerializedName("user_profiles") public UserProfile[] users; - public Classifier classifiers; + public Map classifiers; // some stories responses (like those from social feeds) also include feed data for non-subscribed feeds @SerializedName("feeds") diff --git a/clients/android/NewsBlur/src/com/newsblur/serialization/ClassifierMapTypeAdapter.java b/clients/android/NewsBlur/src/com/newsblur/serialization/ClassifierMapTypeAdapter.java new file mode 100644 index 000000000..cb3503fa5 --- /dev/null +++ b/clients/android/NewsBlur/src/com/newsblur/serialization/ClassifierMapTypeAdapter.java @@ -0,0 +1,45 @@ +package com.newsblur.serialization; + +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.newsblur.domain.Classifier; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; + +/** + * Special handler for the "classifiers" field that appears in API responses that is supposed to be + * a map of feed IDs to classifier objects, but sometimes is just a bare object with no feed ID if + * the API thinks we can imply it from context. This adapter re-inserts a -1 feed ID when the latter + * happens so that we don't have to write two different bindings for responses to different requests. + */ +public class ClassifierMapTypeAdapter implements JsonDeserializer> { + + @Override + public Map deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + + Map result = new HashMap(); + + if (jsonElement.isJsonObject()) { + JsonObject o = jsonElement.getAsJsonObject(); + if (o.get("authors") != null) { // this is our hint that this is a bare classifiers object + Classifier c = (Classifier) jsonDeserializationContext.deserialize(jsonElement, Classifier.class); + result.put( "-1", c); + } else { // otherwise, we have a map of IDs to classifiers + for (Map.Entry entry : o.entrySet()) { + Classifier c = (Classifier) jsonDeserializationContext.deserialize(entry.getValue(), Classifier.class); + result.put(entry.getKey(), c); + } + } + } else { + throw new IllegalStateException("classifiers object is not an object"); + } + + return result; + } +} diff --git a/clients/android/NewsBlur/src/com/newsblur/serialization/FeedListTypeAdapter.java b/clients/android/NewsBlur/src/com/newsblur/serialization/FeedListTypeAdapter.java index b23101bfc..99605f7b9 100644 --- a/clients/android/NewsBlur/src/com/newsblur/serialization/FeedListTypeAdapter.java +++ b/clients/android/NewsBlur/src/com/newsblur/serialization/FeedListTypeAdapter.java @@ -9,7 +9,6 @@ import com.newsblur.domain.Feed; import java.lang.reflect.Type; import java.util.ArrayList; -import java.util.Date; import java.util.List; /** @@ -28,7 +27,7 @@ public class FeedListTypeAdapter implements JsonDeserializer> { result.add(feed); } else if (jsonElement.isJsonArray()) { for (JsonElement arrayMember : jsonElement.getAsJsonArray()) { - Feed feed = (Feed) jsonDeserializationContext.deserialize(jsonElement, Feed.class); + Feed feed = (Feed) jsonDeserializationContext.deserialize(arrayMember, Feed.class); result.add(feed); } }