/******************************************************************************* * Copyright 2013 PAR Works, Inc * * 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.parworks.mars.model.sync; import java.io.IOException; import java.util.ArrayList; import java.util.List; import android.content.ContentProviderOperation; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.OperationApplicationException; import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.RemoteException; import android.util.Log; import com.parworks.androidlibrary.ar.ARErrorListener; import com.parworks.androidlibrary.ar.ARListener; import com.parworks.androidlibrary.ar.ARSite; import com.parworks.androidlibrary.response.AugmentedImage; import com.parworks.androidlibrary.response.SiteComment; import com.parworks.androidlibrary.response.SiteInfo; import com.parworks.androidlibrary.response.SiteInfoOverview; import com.parworks.mars.model.db.AugmentedImagesTable; import com.parworks.mars.model.db.CommentsTable; import com.parworks.mars.model.db.SiteInfoTable; import com.parworks.mars.model.db.TrendingSitesTable; import com.parworks.mars.model.provider.MarsContentProvider; import com.parworks.mars.utils.JsonMapper; import com.parworks.mars.utils.SiteTags; import com.parworks.mars.utils.User; public class SyncHandler { private static final String TAG = "MarsSyncHandler"; private static ContentResolver mContentResolver; private static Context mContext; public static void initSyncHandler(Context context) { mContentResolver = context.getContentResolver(); mContext = context; } public static void syncSiteInfo(final String siteId, final boolean syncAllRelatedInfo) { Log.d(TAG, "Syncing site info: " + siteId); User.getARSites().getSiteInfo(siteId, new ARListener<SiteInfo>() { @Override public void handleResponse(SiteInfo resp) { // store the SiteInfo locally storeSiteInfo(resp); // we might also need to sync other related info // such as AugmentedImages, Comments, etc. if (syncAllRelatedInfo) { syncSiteAugmentedImages(siteId); syncSiteComments(siteId); } } }, new ARErrorListener() { @Override public void handleError(Exception error) { Log.e(TAG, "Failed to get the site info", error); } }); } /** * Sync the SiteInfo for a list of siteIds * * @param siteIds */ public static void syncListSiteInfo(List<String> siteIds) { // TODO: this should be done with thread control for(String id : siteIds) { syncSiteInfo(id, false); } } /** * Sync all the recently augmented images for a given site * * @param siteId */ public static void syncSiteAugmentedImages(String siteId) { User.getARSites().getExisting(siteId, new ARListener<ARSite>() { @Override public void handleResponse(ARSite resp) { resp.getAugmentedImages(new ARListener<List<AugmentedImage>>() { @Override public void handleResponse(final List<AugmentedImage> resp) { new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { try { storeAugmentedImages(resp); } catch (Exception e) { Log.e(TAG, "Failed to sync trending sites", e); } return null; } }.execute(); } }, new ARErrorListener() { @Override public void handleError(Exception error) { Log.e(TAG, "Failed to get the augmented images", error); } }); } }, new ARErrorListener() { @Override public void handleError(Exception error) { Log.e(TAG, "Failed to get site when getting augmented images", error); } }); } /** * Sync site comments * * @param siteId */ public static void syncSiteComments(final String siteId) { User.getARSites().getExisting(siteId, new ARListener<ARSite>() { @Override public void handleResponse(ARSite resp) { resp.getSiteComments(siteId, new ARListener<List<SiteComment>>() { @Override public void handleResponse(final List<SiteComment> resp) { new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { try { storeSiteComments(resp); } catch (Exception e) { Log.e(TAG, "Failed to sync trending sites", e); } return null; } }.execute(); } }, new ARErrorListener() { @Override public void handleError(Exception error) { Log.e(TAG, "Failed to load site comments", error); } }); } }, new ARErrorListener() { @Override public void handleError(Exception error) { Log.e(TAG, "Failed to get the site when loading site comments", error); } }); } /** * Sync trending sites */ public static void syncTrendingSites() { User.getARSites().getTrendingSites(new ARListener<List<SiteInfoOverview>>() { @Override public void handleResponse(final List<SiteInfoOverview> resp) { try { Log.i(TAG, "Sync for TrendingSites: "); new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { try { storeTrendingSite(resp); } catch (Exception e) { Log.e(TAG, "Failed to sync trending sites", e); } return null; } }.execute(); } catch (Exception e) { Log.e(TAG, "Failed to sync trending sites", e); } } }, new ARErrorListener() { @Override public void handleError(Exception error) { Log.e(TAG, "Failed to sync trending sites", error); } }); } /** * Sync suggested tags and all tags */ public static void syncTags() { Log.d(TAG, "Sync suggested tags"); // Sync suggested User.getARSites().getSuggestedTags(new ARListener<List<String>>() { @Override public void handleResponse(final List<String> resp) { new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { try { storeTags(mContext, "suggestedTags", resp); } catch (Exception e) { Log.e(TAG, "Failed to sync trending sites", e); } return null; } }.execute(); } }, new ARErrorListener() { @Override public void handleError(Exception error) { Log.e(TAG, "Failed to sync suggested tags", error); } }); // sync all tags User.getARSites().getAllTags(new ARListener<List<String>>() { @Override public void handleResponse(final List<String> resp) { new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { try { storeTags(mContext, "allTags", resp); } catch (Exception e) { Log.e(TAG, "Failed to sync trending sites", e); } return null; } }.execute(); } }, new ARErrorListener() { @Override public void handleError(Exception error) { Log.e(TAG, "Failed to sync all tags", error); } }); } private static void storeSiteInfo(SiteInfo info) { ContentValues values = new ContentValues(); values.put(SiteInfoTable.COLUMN_ADDRESS, info.getAddress()); values.put(SiteInfoTable.COLUMN_CHANNEL, info.getChannel()); values.put(SiteInfoTable.COLUMN_DESC, info.getDescription()); values.put(SiteInfoTable.COLUMN_FEATURE_DESC, info.getFeatureType()); values.put(SiteInfoTable.COLUMN_LAT, info.getLat()); values.put(SiteInfoTable.COLUMN_LON, info.getLon()); values.put(SiteInfoTable.COLUMN_NAME, info.getName()); values.put(SiteInfoTable.COLUMN_POSTER_IMAGE_CONTENT, info.getPosterImageOverlayContent()); values.put(SiteInfoTable.COLUMN_POSTER_IMAGE_URL, info.getPosterImageURL()); values.put(SiteInfoTable.COLUMN_PROFILE, info.getProcessingProfile()); values.put(SiteInfoTable.COLUMN_SITE_ID, info.getId()); values.put(SiteInfoTable.COLUMN_STATE, info.getSiteState().name()); values.put(SiteInfoTable.COLUMN_TAG_LIST, SiteTags.toJson(info.getTags())); if (info.getAugmentedPosterImage() != null) { values.put(SiteInfoTable.COLUMN_AUG_POSTER_IMAGE_CONTENT, info.getAugmentedPosterImage().getOutput()); values.put(SiteInfoTable.COLUMN_AUG_POSTER_IMAGE_URL, info.getAugmentedPosterImage().getImgContentPath()); values.put(SiteInfoTable.COLUMN_AUG_POSTER_IMAGE_WIDTH, info.getAugmentedPosterImage().getFullSizeWidth()); values.put(SiteInfoTable.COLUMN_AUG_POSTER_IMAGE_HEIGHT, info.getAugmentedPosterImage().getFullSizeHeight()); } // update or insert if not exist // FIXME: not thread-safe here if (mContentResolver.update(MarsContentProvider.getSiteUri(info.getId()), values, null, null) == 0) { mContentResolver.insert(MarsContentProvider.CONTENT_URI_ALL_SITES, values); } } private static void storeAugmentedImages(List<AugmentedImage> augmentedImages) { boolean hasInserted = false; // only insert/update up to 5 records for(int i = 0; i < augmentedImages.size() && i < 5; i++) { AugmentedImage image = augmentedImages.get(i); ContentValues values = new ContentValues(); values.put(AugmentedImagesTable.COLUMN_SITE_ID, image.getSiteId()); values.put(AugmentedImagesTable.COLUMN_IMAGE_ID, image.getImgId()); values.put(AugmentedImagesTable.COLUMN_USER_ID, image.getUserId()); values.put(AugmentedImagesTable.COLUMN_WIDTH, image.getFullSizeWidth()); values.put(AugmentedImagesTable.COLUMN_HIEGHT, image.getFullSizeHeight()); values.put(AugmentedImagesTable.COLUMN_FULL_SIZE_URL, image.getImgPath()); values.put(AugmentedImagesTable.COLUMN_GALLERY_SIZE_URL, image.getImgGalleryPath()); values.put(AugmentedImagesTable.COLUMN_CONTENT_SIZE_URL, image.getImgContentPath()); values.put(AugmentedImagesTable.COLUMN_TIMESTAMP, image.getTime()); values.put(AugmentedImagesTable.COLUMN_CONTENT, image.getOutput()); // update or insert if not exist // FIXME: not thread-safe here if (mContentResolver.update(MarsContentProvider.getAugmentedImageUri(image.getImgId()), values, null, null) == 0) { hasInserted = true; mContentResolver.insert(MarsContentProvider.CONTENT_URI_ALL_AUGMENTED_IMAGES, values); } // TODO: Cut the records if there are too many records for the site } // notify changes if (hasInserted) { mContentResolver.notifyChange(MarsContentProvider.CONTENT_URI_ALL_AUGMENTED_IMAGES_FOR_SITE, null); } } private static void storeSiteComments(List<SiteComment> comments) { for(int i = 0; i < comments.size() && i < 20; i++) { SiteComment comment = comments.get(i); ContentValues values = new ContentValues(); values.put(CommentsTable.COLUMN_SITE_ID, comment.getSiteId()); values.put(CommentsTable.COLUMN_COMMENT, comment.getComment()); values.put(CommentsTable.COLUMN_USER_ID, comment.getUserId()); values.put(CommentsTable.COLUMN_TIMESTAMP, comment.getTimeStamp()); values.put(CommentsTable.COLUMN_USER_NAME, comment.getUserName()); // update or insert if not exist // FIXME: not thread-safe here if (mContentResolver.update(MarsContentProvider.getCommentsUri(comment.getSiteId()), values, null, null) == 0) { mContentResolver.insert(MarsContentProvider.CONTENT_URI_ALL_COMMENTS, values); } // TODO: Cut the records if there are too many records for the site } } private static void storeTrendingSite(List<SiteInfoOverview> sites) throws RemoteException, OperationApplicationException { ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); // delete the old trending sites ContentProviderOperation.Builder op = ContentProviderOperation.newDelete(MarsContentProvider.CONTENT_URI_ALL_TRENDING_SITES); ops.add(op.build()); // insert the new trending sites for(SiteInfoOverview siteInfoOverview : sites) { ContentValues values = new ContentValues(); values.put(TrendingSitesTable.COLUMN_ADDRESS, siteInfoOverview.getAddress()); values.put(TrendingSitesTable.COLUMN_DESC, siteInfoOverview.getDescription()); values.put(TrendingSitesTable.COLUMN_LAT, siteInfoOverview.getLat()); values.put(TrendingSitesTable.COLUMN_LON, siteInfoOverview.getLon()); values.put(TrendingSitesTable.COLUMN_NAME, siteInfoOverview.getName()); values.put(TrendingSitesTable.COLUMN_POSTER_IMAGE_CONTENT, siteInfoOverview.getPosterImageOverlayContent()); values.put(TrendingSitesTable.COLUMN_POSTER_IMAGE_URL, siteInfoOverview.getPosterImageURL()); if (siteInfoOverview.getAugmentedPosterImage() != null) { values.put(TrendingSitesTable.COLUMN_AUG_POSTER_IMAGE_CONTENT, siteInfoOverview.getAugmentedPosterImage().getOutput()); values.put(TrendingSitesTable.COLUMN_AUG_POSTER_IMAGE_URL, siteInfoOverview.getAugmentedPosterImage().getImgContentPath()); // FIXME values.put(TrendingSitesTable.COLUMN_AUG_POSTER_BLURRED_IMAGE_URL, siteInfoOverview.getPosterImageBlurredURL()); values.put(TrendingSitesTable.COLUMN_AUG_POSTER_IMAGE_WIDTH, siteInfoOverview.getAugmentedPosterImage().getFullSizeWidth()); values.put(TrendingSitesTable.COLUMN_AUG_POSTER_IMAGE_HEIGHT, siteInfoOverview.getAugmentedPosterImage().getFullSizeHeight()); } values.put(TrendingSitesTable.COLUMN_SITE_ID, siteInfoOverview.getId()); values.put(TrendingSitesTable.COLUMN_STATE, siteInfoOverview.getSiteState()); values.put(TrendingSitesTable.COLUMN_NUM_AUGMENTED_IMAGES, siteInfoOverview.getNumAugmentedImages()); values.put(TrendingSitesTable.COLUMN_POSTER_BLURRED_IMAGE_URL, siteInfoOverview.getPosterImageBlurredURL()); op = ContentProviderOperation.newInsert(MarsContentProvider.CONTENT_URI_ALL_TRENDING_SITES) .withValues(values); ops.add(op.build()); } mContentResolver.applyBatch(MarsContentProvider.AUTHORITY, ops); mContentResolver.notifyChange(MarsContentProvider.CONTENT_URI_ALL_TRENDING_SITES, null); // update the augmented image table for(SiteInfoOverview siteInfoOverview : sites) { storeAugmentedImages(siteInfoOverview.getRecentlyAugmentedImages()); } } private static void storeTags(Context context, String key, List<String> tags) { // store the tags try { SharedPreferences myPrefs = context.getSharedPreferences("MARSTAGS", 0); SharedPreferences.Editor prefsEditor = myPrefs.edit(); prefsEditor.putString(key, JsonMapper.get().writeValueAsString(tags)); prefsEditor.commit(); } catch (IOException e) { Log.e(TAG, "Failed to write the tags value into shared preferences", e); } } }