aboutsummaryrefslogtreecommitdiff
path: root/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderUpdateService.java
diff options
context:
space:
mode:
Diffstat (limited to 'WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderUpdateService.java')
-rw-r--r--WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderUpdateService.java331
1 files changed, 331 insertions, 0 deletions
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderUpdateService.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderUpdateService.java
new file mode 100644
index 000000000..f3590ac78
--- /dev/null
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderUpdateService.java
@@ -0,0 +1,331 @@
+package org.wordpress.android.ui.reader.services;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.IBinder;
+
+import com.android.volley.VolleyError;
+import com.wordpress.rest.RestRequest;
+
+import org.json.JSONObject;
+import org.wordpress.android.WordPress;
+import org.wordpress.android.datasets.ReaderBlogTable;
+import org.wordpress.android.datasets.ReaderDatabase;
+import org.wordpress.android.datasets.ReaderPostTable;
+import org.wordpress.android.datasets.ReaderTagTable;
+import org.wordpress.android.models.ReaderBlogList;
+import org.wordpress.android.models.ReaderRecommendBlogList;
+import org.wordpress.android.models.ReaderTag;
+import org.wordpress.android.models.ReaderTagList;
+import org.wordpress.android.models.ReaderTagType;
+import org.wordpress.android.ui.reader.ReaderConstants;
+import org.wordpress.android.ui.reader.ReaderEvents;
+import org.wordpress.android.ui.reader.utils.ReaderUtils;
+import org.wordpress.android.util.AppLog;
+import org.wordpress.android.util.JSONUtils;
+
+import java.util.EnumSet;
+import java.util.Iterator;
+
+import de.greenrobot.event.EventBus;
+
+public class ReaderUpdateService extends Service {
+
+ /***
+ * service which updates followed/recommended tags and blogs for the Reader, relies
+ * on EventBus to notify of changes
+ */
+
+ public enum UpdateTask {
+ TAGS,
+ FOLLOWED_BLOGS,
+ RECOMMENDED_BLOGS
+ }
+
+ private EnumSet<UpdateTask> mCurrentTasks;
+ private static final String ARG_UPDATE_TASKS = "update_tasks";
+
+ public static void startService(Context context, EnumSet<UpdateTask> tasks) {
+ if (context == null || tasks == null || tasks.size() == 0) {
+ return;
+ }
+ Intent intent = new Intent(context, ReaderUpdateService.class);
+ intent.putExtra(ARG_UPDATE_TASKS, tasks);
+ context.startService(intent);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ AppLog.i(AppLog.T.READER, "reader service > created");
+ }
+
+ @Override
+ public void onDestroy() {
+ AppLog.i(AppLog.T.READER, "reader service > destroyed");
+ super.onDestroy();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (intent != null && intent.hasExtra(ARG_UPDATE_TASKS)) {
+ //noinspection unchecked
+ EnumSet<UpdateTask> tasks = (EnumSet<UpdateTask>) intent.getSerializableExtra(ARG_UPDATE_TASKS);
+ performTasks(tasks);
+ }
+
+ return START_NOT_STICKY;
+ }
+
+ private void performTasks(EnumSet<UpdateTask> tasks) {
+ mCurrentTasks = EnumSet.copyOf(tasks);
+
+ // perform in priority order - we want to update tags first since without them
+ // the Reader can't show anything
+ if (tasks.contains(UpdateTask.TAGS)) {
+ updateTags();
+ }
+ if (tasks.contains(UpdateTask.FOLLOWED_BLOGS)) {
+ updateFollowedBlogs();
+ }
+ if (tasks.contains(UpdateTask.RECOMMENDED_BLOGS)) {
+ updateRecommendedBlogs();
+ }
+ }
+
+ private void taskCompleted(UpdateTask task) {
+ mCurrentTasks.remove(task);
+ if (mCurrentTasks.isEmpty()) {
+ allTasksCompleted();
+ }
+ }
+
+ private void allTasksCompleted() {
+ AppLog.i(AppLog.T.READER, "reader service > all tasks completed");
+ stopSelf();
+ }
+
+ /***
+ * update the tags the user is followed - also handles recommended (popular) tags since
+ * they're included in the response
+ */
+ private void updateTags() {
+ com.wordpress.rest.RestRequest.Listener listener = new RestRequest.Listener() {
+ @Override
+ public void onResponse(JSONObject jsonObject) {
+ handleUpdateTagsResponse(jsonObject);
+ }
+ };
+
+ RestRequest.ErrorListener errorListener = new RestRequest.ErrorListener() {
+ @Override
+ public void onErrorResponse(VolleyError volleyError) {
+ AppLog.e(AppLog.T.READER, volleyError);
+ taskCompleted(UpdateTask.TAGS);
+ }
+ };
+ AppLog.d(AppLog.T.READER, "reader service > updating tags");
+ WordPress.getRestClientUtilsV1_2().get("read/menu", null, null, listener, errorListener);
+ }
+
+ private void handleUpdateTagsResponse(final JSONObject jsonObject) {
+ new Thread() {
+ @Override
+ public void run() {
+ // get server topics, both default & followed - but use "recommended" for logged-out
+ // reader since user won't have any followed tags
+ ReaderTagList serverTopics = new ReaderTagList();
+ serverTopics.addAll(parseTags(jsonObject, "default", ReaderTagType.DEFAULT));
+ if (ReaderUtils.isLoggedOutReader()) {
+ serverTopics.addAll(parseTags(jsonObject, "recommended", ReaderTagType.FOLLOWED));
+ } else {
+ serverTopics.addAll(parseTags(jsonObject, "subscribed", ReaderTagType.FOLLOWED));
+ }
+
+ // parse topics from the response, detect whether they're different from local
+ ReaderTagList localTopics = new ReaderTagList();
+ localTopics.addAll(ReaderTagTable.getDefaultTags());
+ localTopics.addAll(ReaderTagTable.getFollowedTags());
+ localTopics.addAll(ReaderTagTable.getCustomListTags());
+
+ if (!localTopics.isSameList(serverTopics)) {
+ AppLog.d(AppLog.T.READER, "reader service > followed topics changed");
+ // if any local topics have been removed from the server, make sure to delete
+ // them locally (including their posts)
+ deleteTags(localTopics.getDeletions(serverTopics));
+ // now replace local topics with the server topics
+ ReaderTagTable.replaceTags(serverTopics);
+ // broadcast the fact that there are changes
+ EventBus.getDefault().post(new ReaderEvents.FollowedTagsChanged());
+ }
+
+ // save changes to recommended topics
+ if (!ReaderUtils.isLoggedOutReader()) {
+ ReaderTagList serverRecommended = parseTags(jsonObject, "recommended", ReaderTagType.RECOMMENDED);
+ ReaderTagList localRecommended = ReaderTagTable.getRecommendedTags(false);
+ if (!serverRecommended.isSameList(localRecommended)) {
+ AppLog.d(AppLog.T.READER, "reader service > recommended topics changed");
+ ReaderTagTable.setRecommendedTags(serverRecommended);
+ EventBus.getDefault().post(new ReaderEvents.RecommendedTagsChanged());
+ }
+ }
+
+ taskCompleted(UpdateTask.TAGS);
+ }
+ }.start();
+ }
+
+ /*
+ * parse a specific topic section from the topic response
+ */
+ private static ReaderTagList parseTags(JSONObject jsonObject, String name, ReaderTagType tagType) {
+ ReaderTagList topics = new ReaderTagList();
+
+ if (jsonObject == null) {
+ return topics;
+ }
+
+ JSONObject jsonTopics = jsonObject.optJSONObject(name);
+ if (jsonTopics == null) {
+ return topics;
+ }
+
+ Iterator<String> it = jsonTopics.keys();
+ while (it.hasNext()) {
+ String internalName = it.next();
+ JSONObject jsonTopic = jsonTopics.optJSONObject(internalName);
+ if (jsonTopic != null) {
+ String tagTitle = JSONUtils.getStringDecoded(jsonTopic, ReaderConstants.JSON_TAG_TITLE);
+ String tagDisplayName = JSONUtils.getStringDecoded(jsonTopic, ReaderConstants.JSON_TAG_DISPLAY_NAME);
+ String tagSlug = JSONUtils.getStringDecoded(jsonTopic, ReaderConstants.JSON_TAG_SLUG);
+ String endpoint = JSONUtils.getString(jsonTopic, ReaderConstants.JSON_TAG_URL);
+
+ // if the endpoint contains `read/list` then this is a custom list - these are
+ // included in the response as default tags
+ if (tagType == ReaderTagType.DEFAULT && endpoint.contains("/read/list/")) {
+ topics.add(new ReaderTag(tagSlug, tagDisplayName, tagTitle, endpoint, ReaderTagType.CUSTOM_LIST));
+ } else {
+ topics.add(new ReaderTag(tagSlug, tagDisplayName, tagTitle, endpoint, tagType));
+ }
+ }
+ }
+
+ return topics;
+ }
+
+ private static void deleteTags(ReaderTagList tagList) {
+ if (tagList == null || tagList.size() == 0) {
+ return;
+ }
+
+ SQLiteDatabase db = ReaderDatabase.getWritableDb();
+ db.beginTransaction();
+ try {
+ for (ReaderTag tag: tagList) {
+ ReaderTagTable.deleteTag(tag);
+ ReaderPostTable.deletePostsWithTag(tag);
+ }
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+
+ /***
+ * request the list of blogs the current user is following
+ */
+ private void updateFollowedBlogs() {
+ RestRequest.Listener listener = new RestRequest.Listener() {
+ @Override
+ public void onResponse(JSONObject jsonObject) {
+ handleFollowedBlogsResponse(jsonObject);
+ }
+ };
+ RestRequest.ErrorListener errorListener = new RestRequest.ErrorListener() {
+ @Override
+ public void onErrorResponse(VolleyError volleyError) {
+ AppLog.e(AppLog.T.READER, volleyError);
+ taskCompleted(UpdateTask.FOLLOWED_BLOGS);
+ }
+ };
+
+ AppLog.d(AppLog.T.READER, "reader service > updating followed blogs");
+ // request using ?meta=site,feed to get extra info
+ WordPress.getRestClientUtilsV1_1().get("read/following/mine?meta=site%2Cfeed", listener, errorListener);
+ }
+
+ private void handleFollowedBlogsResponse(final JSONObject jsonObject) {
+ new Thread() {
+ @Override
+ public void run() {
+ ReaderBlogList serverBlogs = ReaderBlogList.fromJson(jsonObject);
+ ReaderBlogList localBlogs = ReaderBlogTable.getFollowedBlogs();
+
+ if (!localBlogs.isSameList(serverBlogs)) {
+ // always update the list of followed blogs if there are *any* changes between
+ // server and local (including subscription count, description, etc.)
+ ReaderBlogTable.setFollowedBlogs(serverBlogs);
+ // ...but only update the follow status and alert that followed blogs have
+ // changed if the server list doesn't have the same blogs as the local list
+ // (ie: a blog has been followed/unfollowed since local was last updated)
+ if (!localBlogs.hasSameBlogs(serverBlogs)) {
+ ReaderPostTable.updateFollowedStatus();
+ AppLog.i(AppLog.T.READER, "reader blogs service > followed blogs changed");
+ EventBus.getDefault().post(new ReaderEvents.FollowedBlogsChanged());
+ }
+ }
+
+ taskCompleted(UpdateTask.FOLLOWED_BLOGS);
+ }
+ }.start();
+ }
+
+ /***
+ * request the latest recommended blogs, replaces all local ones
+ */
+ private void updateRecommendedBlogs() {
+ RestRequest.Listener listener = new RestRequest.Listener() {
+ @Override
+ public void onResponse(JSONObject jsonObject) {
+ handleRecommendedBlogsResponse(jsonObject);
+ }
+ };
+ RestRequest.ErrorListener errorListener = new RestRequest.ErrorListener() {
+ @Override
+ public void onErrorResponse(VolleyError volleyError) {
+ AppLog.e(AppLog.T.READER, volleyError);
+ taskCompleted(UpdateTask.RECOMMENDED_BLOGS);
+ }
+ };
+
+ AppLog.d(AppLog.T.READER, "reader service > updating recommended blogs");
+ String path = "read/recommendations/mine/"
+ + "?source=mobile"
+ + "&number=" + Integer.toString(ReaderConstants.READER_MAX_RECOMMENDED_TO_REQUEST);
+ WordPress.getRestClientUtilsV1_1().get(path, listener, errorListener);
+ }
+ private void handleRecommendedBlogsResponse(final JSONObject jsonObject) {
+ new Thread() {
+ @Override
+ public void run() {
+ ReaderRecommendBlogList serverBlogs = ReaderRecommendBlogList.fromJson(jsonObject);
+ ReaderRecommendBlogList localBlogs = ReaderBlogTable.getRecommendedBlogs();
+
+ if (!localBlogs.isSameList(serverBlogs)) {
+ ReaderBlogTable.setRecommendedBlogs(serverBlogs);
+ EventBus.getDefault().post(new ReaderEvents.RecommendedBlogsChanged());
+ }
+
+ taskCompleted(UpdateTask.RECOMMENDED_BLOGS);
+ }
+ }.start();
+ }
+}