/* * Copyright 2014 sonaive.com. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.sonaive.v2ex.io; import android.content.ContentProviderOperation; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.provider.BaseColumns; import android.text.TextUtils; import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.sonaive.v2ex.io.model.Review; import com.sonaive.v2ex.provider.V2exContract; import com.sonaive.v2ex.sync.api.Api; import com.sonaive.v2ex.util.ModelUtils; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import static com.sonaive.v2ex.util.LogUtils.LOGD; import static com.sonaive.v2ex.util.LogUtils.LOGE; import static com.sonaive.v2ex.util.LogUtils.LOGW; import static com.sonaive.v2ex.util.LogUtils.makeLogTag; /** * Created by liutao on 12/15/14. */ public class ReviewsHandler extends JSONHandler { private static final String TAG = makeLogTag(ReviewsHandler.class); private HashMap<String, Review> mReviews = new HashMap<>(); private int page = 1; private String topicId; public ReviewsHandler(Context context) { super(context); } @Override public void makeContentProviderOperations(ArrayList<ContentProviderOperation> list) { Uri uri = V2exContract.Reviews.CONTENT_URI; HashMap<String, String> reviewHashcodes = loadReviewHashcodes(); HashSet<String> reviewsToKeep = new HashSet<>(); boolean isIncrementalUpdate = reviewHashcodes != null && reviewHashcodes.size() > 0; if (isIncrementalUpdate) { LOGD(TAG, "Doing incremental update for reviews."); } else { LOGD(TAG, "Doing FULL (non incremental) update for reviews."); list.add(ContentProviderOperation.newDelete(uri).build()); } int updatedReviews = 0; for (Review review : mReviews.values()) { String hashCode = review.getImportHashcode(); reviewsToKeep.add(String.valueOf(review.id)); // add review, if necessary if (!isIncrementalUpdate || !reviewHashcodes.containsKey(String.valueOf(review.id)) || !reviewHashcodes.get(String.valueOf(review.id)).equals(hashCode)) { ++updatedReviews; boolean isNew = !isIncrementalUpdate || !reviewHashcodes.containsKey(String.valueOf(review.id)); buildReview(isNew, review, list); } } int deletedReviews = 0; if (isIncrementalUpdate) { for (String posterId : reviewHashcodes.keySet()) { if (!reviewsToKeep.contains(posterId)) { buildDeleteOperation(posterId, list); ++deletedReviews; } } } LOGD(TAG, "Reviews: " + (isIncrementalUpdate ? "INCREMENTAL" : "FULL") + " update. " + updatedReviews + " to update, " + deletedReviews + " to delete, New total: " + mReviews.size()); } @Override public void process(JsonElement element) { if (element.isJsonArray()) { for (Review review : new Gson().fromJson(element, Review[].class)) { mReviews.put(String.valueOf(review.id), review); } } // Due to api hasn't supported pagination.The following code will be commented. // if (mReviews.size() > 0) { // EventBus.getDefault().postSticky(new UpdateLoadingStateEvent(LoadingStatus.FINISH)); // } else { // EventBus.getDefault().postSticky(new UpdateLoadingStateEvent(LoadingStatus.NO_MORE_DATA)); // } } @Override public String getBody(Bundle data) { if (data != null) { String requestParams = data.getString(Api.ARG_API_PARAMS); JsonObject jo = new Gson().fromJson(requestParams, JsonObject.class); JsonElement topicIdJsonElement = jo.get("topic_id"); if (topicIdJsonElement != null) { topicId = topicIdJsonElement.getAsString(); } return data.getString(Api.ARG_RESULT); } return ""; } private void buildReview(boolean isInsert, Review review, ArrayList<ContentProviderOperation> list) { Uri allReviewsUri = V2exContract.Reviews.CONTENT_URI; Uri thisReviewUri = V2exContract.Reviews.buildReviewTopicUri(String.valueOf(review.id)); ContentProviderOperation.Builder builder; if (isInsert) { builder = ContentProviderOperation.newInsert(allReviewsUri); } else { builder = ContentProviderOperation.newUpdate(thisReviewUri); } if (TextUtils.isEmpty(String.valueOf(review.id))) { LOGW(TAG, "Ignoring feed with missing feed ID."); return; } list.add(builder.withValue(V2exContract.Reviews.REVIEW_ID, review.id) .withValue(V2exContract.Reviews.REVIEW_TOPIC_ID, topicId) .withValue(V2exContract.Reviews.REVIEW_THANKS, review.thanks) .withValue(V2exContract.Reviews.REVIEW_CONTENT, review.content) .withValue(V2exContract.Reviews.REVIEW_CONTENT_RENDERED, review.content_rendered) .withValue(V2exContract.Reviews.REVIEW_MEMBER, ModelUtils.serializeMember(review.member)) .withValue(V2exContract.Reviews.REVIEW_CREATED, review.created) .withValue(V2exContract.Reviews.REVIEW_LAST_MODIFIED, review.last_modified) .withValue(V2exContract.Reviews.REVIEW_IMPORT_HASHCODE, review.getImportHashcode()) .build()); } private void buildDeleteOperation(String reviewId, ArrayList<ContentProviderOperation> list) { Uri uri = V2exContract.Reviews.buildReviewTopicUri(reviewId); list.add(ContentProviderOperation.newDelete(uri).build()); } private HashMap<String, String> loadReviewHashcodes() { // int limit = page * Config.PAGE_SIZE; // int offset = (page - 1) * Config.PAGE_SIZE; // // Put the limit clause as a query parameter using the syntax 'limit = offset, limit' // Uri uri = V2exContract.Reviews.CONTENT_URI.buildUpon().encodedQuery("limit=" + offset + "," + limit).build(); Uri uri = V2exContract.Reviews.CONTENT_URI; Cursor cursor = mContext.getContentResolver().query(uri, ReviewHashcodeQuery.PROJECTION, "review_topic_id = ?", new String[] {topicId}, V2exContract.Reviews.REVIEW_LAST_MODIFIED + " DESC"); if (cursor == null) { LOGE(TAG, "Error querying REVIEW hashcodes (got null cursor)"); return null; } if (cursor.getCount() < 1) { LOGE(TAG, "Error querying REVIEW hashcodes (no records returned)"); cursor.close(); return null; } HashMap<String, String> result = new HashMap<>(); while (cursor.moveToNext()) { String reviewId = cursor.getString(ReviewHashcodeQuery.REVIEW_ID); String hashcode = cursor.getString(ReviewHashcodeQuery.REVIEW_IMPORT_HASHCODE); result.put(reviewId, hashcode == null ? "" : hashcode); } cursor.close(); return result; } private interface ReviewHashcodeQuery { String[] PROJECTION = { BaseColumns._ID, V2exContract.Reviews.REVIEW_ID, V2exContract.Reviews.REVIEW_IMPORT_HASHCODE }; final int _ID = 0; final int REVIEW_ID = 1; final int REVIEW_IMPORT_HASHCODE = 2; } }