package org.michenux.yourappidea.tutorial.sync;
import android.accounts.Account;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.OperationApplicationException;
import android.content.SyncResult;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import org.michenux.drodrolib.content.ContentProviderUtils;
import org.michenux.drodrolib.db.utils.CursorUtils;
import org.michenux.drodrolib.info.AppUsageUtils;
import org.michenux.drodrolib.wordpress.json.WPJsonPost;
import org.michenux.drodrolib.wordpress.json.WPJsonResponse;
import org.michenux.yourappidea.BuildConfig;
import org.michenux.yourappidea.R;
import org.michenux.yourappidea.YourApplication;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.ExecutionException;
import javax.inject.Inject;
import rx.Observable;
public class TutorialSyncAdapter extends AbstractThreadedSyncAdapter {
public static final String SYNC_FINISHED = "sync_finished";
public static final String SYNC_STARTED = "sync_started";
@Inject
TutorialSyncHelper mSyncHelper;
/**
* Set up the sync adapter
*/
public TutorialSyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
((YourApplication) context.getApplicationContext()).inject(this);
if (BuildConfig.DEBUG) {
Log.d(YourApplication.LOG_TAG, "tutorialSyncAdapter()");
}
}
@Override
public void onPerformSync(
Account account,
Bundle extras,
String authority,
ContentProviderClient provider,
SyncResult syncResult) {
((YourApplication) getContext().getApplicationContext()).setSyncAdapterRunning(true);
LocalBroadcastManager.getInstance(this.getContext()).sendBroadcast(new Intent(SYNC_STARTED));
try {
boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
WPJsonPost newPost = retrievePosts(AppUsageUtils.getLastSync(this.getContext()), provider);
if (BuildConfig.DEBUG) {
Log.d(YourApplication.LOG_TAG, "tutorialSyncAdapter.onPerformSync() - manual:" + manualSync);
}
if (newPost != null) {
// notification only if not manual sync
if (!manualSync) {
sendNotification(newPost);
}
} else {
if (BuildConfig.DEBUG) {
Log.d(YourApplication.LOG_TAG, " no new post");
}
}
if (!manualSync) {
mSyncHelper.adjustSyncInterval(TutorialSyncAdapter.this.getContext());
}
AppUsageUtils.updateLastSync(TutorialSyncAdapter.this.getContext());
} catch (ParseException e) {
Log.e(YourApplication.LOG_TAG, "tutorialSyncAdapter.onPerformSync()", e);
syncResult.stats.numParseExceptions++;
} catch (InterruptedException | ExecutionException | RemoteException | OperationApplicationException e) {
Log.e(YourApplication.LOG_TAG, "tutorialSyncAdapter.onPerformSync()", e);
syncResult.stats.numIoExceptions++;
} finally {
LocalBroadcastManager.getInstance(this.getContext()).sendBroadcast(new Intent(SYNC_FINISHED));
((YourApplication) getContext().getApplicationContext()).setSyncAdapterRunning(false);
}
}
private void sendNotification(WPJsonPost newPost) {
Intent notificationIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(newPost.getUrl()));
PendingIntent contentIntent = PendingIntent.getActivity(this.getContext(), 0, notificationIntent, 0);
NotificationManager notificationManager = (NotificationManager) this.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder notifBuilder = new NotificationCompat.Builder(this.getContext());
notifBuilder.setContentTitle(getContext().getString(R.string.tutorial_notification_title));
notifBuilder.setContentText(newPost.getTitle());
notifBuilder.setSmallIcon(R.drawable.ic_stat_notify_newtuto);
notifBuilder.setAutoCancel(true);
notifBuilder.setDefaults(Notification.DEFAULT_SOUND);
notifBuilder.setLights(0xff00ff00, 300, 1000);
notifBuilder.setContentIntent(contentIntent);
//notifBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(newPost.title));
Notification noti = notifBuilder.build();
//noti.ledARGB = 0xff00ff00;
//noti.ledOnMS = 300;
//noti.ledOffMS = 1000;
//noti.flags |= Notification.FLAG_AUTO_CANCEL;
//noti.flags |= Notification.FLAG_SHOW_LIGHTS;
notificationManager.notify(0, noti);
}
private WPJsonPost retrievePosts(long lastSync, ContentProviderClient provider) throws InterruptedException, ExecutionException, ParseException, RemoteException, OperationApplicationException {
if (BuildConfig.DEBUG) {
Log.d(YourApplication.LOG_TAG, "tutorialSyncAdapter.retrievePosts()");
}
WordpressService wordpressService = WordpressServiceFactory.create(getContext());
Observable<WPJsonResponse> observable =
wordpressService.query("get_recent_posts", "android", "android_desc", "android", 9999);
WPJsonResponse response = observable.toBlocking().first();
WPJsonPost newPost = null;
if (response.getStatus().equals(WPJsonResponse.STATUS_OK) &&
response.getPosts() != null &&
!response.getPosts().isEmpty()) {
final WPJsonPost lastPost = response.getPosts().get(0);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.FRENCH);
sdf.setTimeZone(TimeZone.getTimeZone("Europe/Paris"));
Date lastPostDate = sdf.parse(lastPost.getDate());
Date lastSyncDate = new Date(lastSync);
if (BuildConfig.DEBUG) {
Log.d(YourApplication.LOG_TAG, "title:" + lastPost.getTitle());
Log.d(YourApplication.LOG_TAG, "date: " + lastPost.getDate());
Log.d(YourApplication.LOG_TAG, "lastSync: " + sdf.format(lastSync));
}
if (lastPostDate.after(lastSyncDate)) {
newPost = lastPost;
}
updateDatabase(response.getPosts(), provider);
//if (ConnectivityUtils.isConnectedWifi(TutorialSyncAdapter.this.getContext()) &&
// BatteryUtils.isChargingOrFull(TutorialSyncAdapter.this.getContext())) {
//load image
//}
} else if (BuildConfig.DEBUG) {
Log.d(YourApplication.LOG_TAG, "result is null or empty");
}
return newPost;
}
private void updateDatabase(List<WPJsonPost> posts, ContentProviderClient provider) throws RemoteException, OperationApplicationException, ParseException {
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.FRENCH);
sdf.setTimeZone(TimeZone.getTimeZone("Europe/Paris"));
for (WPJsonPost post : posts) {
String thumbnail = "";
if (post.getThumbnailImages() != null && post.getThumbnailImages().getFoundationFeaturedImage() != null) {
thumbnail = post.getThumbnailImages().getFoundationFeaturedImage().getUrl();
}
Date postCreationDate = sdf.parse(post.getDate());
Date postModifDate = sdf.parse(post.getDate());
Cursor cursor = getContext().getContentResolver().query(
TutorialContentProvider.CONTENT_URI, // The content URI of the words table
new String[]{TutorialContentProvider.DATEMODIFICATION_COLUMN}, // The columns to return for each row
TutorialContentProvider.POSTID_COLUMN + "= ?", // Selection criteria
new String[]{Integer.toString(post.getId())}, // Selection criteria
null);
boolean insertOrModified = false;
if (cursor != null) {
if (cursor.moveToFirst()) {
long modificationDate = CursorUtils.getLong(TutorialContentProvider.DATEMODIFICATION_COLUMN, cursor);
if (modificationDate != (postModifDate.getTime() / 1000)) {
if (BuildConfig.DEBUG) {
Log.d(YourApplication.LOG_TAG, "updated post: " + post.getId());
}
// delete the old one if modified
ops.add(ContentProviderOperation.newDelete(TutorialContentProvider.CONTENT_URI).withSelection(TutorialContentProvider.POSTID_COLUMN + " = ?",
new String[]{Integer.toString(post.getId())}).build());
insertOrModified = true;
} else {
if (BuildConfig.DEBUG) {
Log.d(YourApplication.LOG_TAG, "unchanged post: " + post.getId() + ", not inserting.");
}
}
} else {
if (BuildConfig.DEBUG) {
Log.d(YourApplication.LOG_TAG, "new post: " + post.getId());
}
insertOrModified = true;
}
cursor.close();
}
if (insertOrModified) {
// insert if new or modified
ops.add(
ContentProviderOperation.newInsert(TutorialContentProvider.CONTENT_URI)
.withValue(TutorialContentProvider.POSTID_COLUMN, post.getId())
.withValue(TutorialContentProvider.TITLE_COLUMN, post.getTitle())
.withValue(TutorialContentProvider.DESCRIPTION_COLUMN, post.getExcerpt())
.withValue(TutorialContentProvider.THUMBNAIL_COLMUN, thumbnail)
.withValue(TutorialContentProvider.URL_COLUMN, post.getUrl())
.withValue(TutorialContentProvider.CONTENT_COLUMN, post.getContent())
.withValue(TutorialContentProvider.AUTHOR_COLUMN, post.getAuthor().getName())
.withValue(TutorialContentProvider.DATECREATION_COLUMN, postCreationDate.getTime() / 1000)
.withValue(TutorialContentProvider.DATEMODIFICATION_COLUMN, postModifDate.getTime() / 1000)
.withYieldAllowed(false)
.build());
}
}
// Keep last 50 posts
String deleteSelection = TutorialContentProvider.ID_COLUMN + " not in ( select " +
TutorialContentProvider.ID_COLUMN + " from " + TutorialContentProvider.TABLE_NAME +
" order by " + TutorialContentProvider.DATECREATION_COLUMN + " desc limit 50 )";
String[] deleteParams = new String[]{};
int actuToDelete = ContentProviderUtils.count(TutorialContentProvider.CONTENT_URI, deleteSelection, deleteParams, this.getContext().getContentResolver());
if (BuildConfig.DEBUG) {
Log.d(YourApplication.LOG_TAG, actuToDelete + " old posts to delete");
}
if (actuToDelete > 0) {
ops.add(ContentProviderOperation.newDelete(TutorialContentProvider.CONTENT_URI).withSelection(deleteSelection, deleteParams).build());
}
if (!ops.isEmpty()) {
if (BuildConfig.DEBUG) {
Log.d(YourApplication.LOG_TAG, "execute batch");
}
provider.applyBatch(ops);
} else {
if (BuildConfig.DEBUG) {
Log.d(YourApplication.LOG_TAG, "ignore batch, empty");
}
}
}
}