package com.nuscomputing.ivle;
import com.nuscomputing.ivle.providers.AnnouncementsContract;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
/**
* The primary sync adapter for all announcements.
* @author yjwong
*/
public class IVLESyncService extends Service {
// {{{ properties
/** TAG for logging */
public static final String TAG = "IVLESyncService";
/** The sync adapter */
private static IVLESyncAdapter sSyncAdapter = null;
/** Sync started flag */
public static final String ACTION_SYNC_STARTED = "com.nuscomputing.ivle.intent.action.SYNC_STARTED";
/** Sync success flag */
public static final String ACTION_SYNC_SUCCESS = "com.nuscomputing.ivle.intent.action.SYNC_SUCCESS";
/** Sync completion flag */
public static final String ACTION_SYNC_COMPLETE = "com.nuscomputing.ivle.intent.action.SYNC_COMPLETE";
/** Sync canceled flag */
public static final String ACTION_SYNC_CANCELED = "com.nuscomputing.ivle.intent.action.SYNC_CANCELED";
/** Sync failure flag */
public static final String ACTION_SYNC_FAILED = "com.nuscomputing.ivle.intent.action.SYNC_FAILED";
/** SharedPreferences key for sync_in_progress */
public static final String KEY_SYNC_IN_PROGRESS = "sync_in_progress";
// }}}
// {{{ methods
@Override
public IBinder onBind(Intent intent) {
// For some reason, need to call setIsSyncable for Gingerbread.
if (Build.VERSION.SDK_INT < 11) {
AccountManager am = AccountManager.get(this);
Account[] accounts = am.getAccountsByType(Constants.ACCOUNT_TYPE);
for (Account account : accounts) {
ContentResolver.setIsSyncable(account, Constants.PROVIDER_AUTHORITY, 1);
}
}
IBinder ret = null;
ret = getSyncAdapter().getSyncAdapterBinder();
return ret;
}
/**
* Method: getSyncInProgressKey
* <p>
* Gets the shared preference key for sync status.
*/
public static String getSyncInProgressKey(Account account) {
return KEY_SYNC_IN_PROGRESS + "_" + account.name;
}
/**
* Method: getSyncAdapter
* <p>
* Gets the sync adapter for this service. If the sync adapter has not been
* initialized, initialize it and return.
*
* @return AnnouncementsSyncAdapter
*/
private IVLESyncAdapter getSyncAdapter() {
if (sSyncAdapter == null) {
sSyncAdapter = new IVLESyncAdapter(getApplicationContext(), false);
}
return sSyncAdapter;
}
/**
* Method: isSyncInProgress
* <p>
* Returns true if a sync is in progress for the specified account, false
* otherwise.
*/
public static boolean isSyncInProgress(Context context, Account account) {
return IVLESyncAdapter.isSyncInProgress(context, account);
}
/**
* Method: broadcastSyncStarted
* <p>
* Sends a system broadcast that our sync is started.
* This is made because there is not many ways of knowing the status of a
* sync operation in Android's current sync framework.
*/
public static void broadcastSyncStarted(Context context, Account account) {
Intent intent = new Intent(IVLESyncService.ACTION_SYNC_STARTED);
intent.putExtra("com.nuscomputing.ivle.Account", account);
context.sendBroadcast(intent);
}
/**
* Method: broadcastSyncCanceled
* <p>
* Sends a system broadcast that our sync has been canceled.
* This is made because there is not many ways of knowing the status of a
* sync operation in Android's current sync framework.
*/
public static void broadcastSyncCanceled(Context context, Account account) {
Intent intent = new Intent(IVLESyncService.ACTION_SYNC_CANCELED);
intent.putExtra("com.nuscomputing.ivle.Account", account);
context.sendBroadcast(intent);
}
/**
* Method: broadcastSyncSuccess
* <p>
* Sends a system broadcast that our sync has succeeded.
* This is made because there is not many ways of knowing the status of a
* sync operation in Android's current sync framework.
*/
public static void broadcastSyncSuccess(Context context, Account account) {
Intent intent = new Intent(IVLESyncService.ACTION_SYNC_SUCCESS);
intent.putExtra("com.nuscomputing.ivle.Account", account);
context.sendBroadcast(intent);
IVLESyncService.onSyncSuccess(context, account);
}
/**
* Method: broadcastSyncComplete
* <p>
* Sends a system broadcast that our sync has completed.
* This is made because there is not many ways of knowing the status of a
* sync operation in Android's current sync framework.
*/
public static void broadcastSyncComplete(Context context, Account account) {
Intent intent = new Intent(IVLESyncService.ACTION_SYNC_COMPLETE);
intent.putExtra("com.nuscomputing.ivle.Account", account);
context.sendBroadcast(intent);
}
/**
* Method: broadcastSyncFailed
* <p>
* Sends a system broadcast that our sync has failed.
* This is made because there is not many ways of knowing the status of a
* sync operation in Android's current sync framework.
*/
public static void broadcastSyncFailed(Context context, Account account) {
Intent intent = new Intent(IVLESyncService.ACTION_SYNC_FAILED);
intent.putExtra("com.nuscomputing.ivle.Account", account);
context.sendBroadcast(intent);
}
/**
* Method: onSyncSuccess
* <p>
* Called when the sync was successful. Currently used to display
* notifications.
*/
@SuppressWarnings("deprecation")
@TargetApi(16)
private static void onSyncSuccess(Context context, Account account) {
// Check if we enable notifications.
SharedPreferences prefs = context.getSharedPreferences("account_" + account.name, Context.MODE_PRIVATE);
boolean notifsEnabled = prefs.getBoolean("notifications", true);
boolean notifsAnnouncementsEnabled = prefs.getBoolean("notifications_announcements", true);
if (notifsEnabled) {
if (notifsAnnouncementsEnabled) {
// Get unread announcements.
ContentResolver resolver = context.getContentResolver();
ContentProviderClient provider = resolver.acquireContentProviderClient(AnnouncementsContract.CONTENT_URI);
try {
Cursor cursor = provider.query(
AnnouncementsContract.CONTENT_URI,
new String[] { AnnouncementsContract.ID, AnnouncementsContract.TITLE },
AnnouncementsContract.IS_READ.concat(" = ?").concat(" AND ")
.concat(DatabaseHelper.ANNOUNCEMENTS_TABLE_NAME).concat(".").concat(AnnouncementsContract.ACCOUNT).concat(" = ?"),
new String[] { "0", account.name },
null
);
// Get the notification manager.
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
int announcementCount = cursor.getCount();
// Determine how we show the notification based on the
// number of announcements.
if (announcementCount > 0) {
Notification notif = null;
cursor.moveToFirst();
if (announcementCount == 1) {
// Obtain the details for the only announcement.
String notifTitle = cursor.getString(cursor.getColumnIndex(AnnouncementsContract.TITLE));
long announcementId = cursor.getLong(cursor.getColumnIndex(AnnouncementsContract.ID));
// Create a pending intent.
Intent intent = NotificationDispatcher.createIntent(context, NotificationDispatcher.NOTIFICATION_ANNOUNCEMENT_SINGLE, account.name);
intent.putExtra("announcementId", announcementId);
PendingIntent pendingIntent = PendingIntent.getActivity(context, account.hashCode(), intent, 0);
// Create notification based on platform version.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
Notification.Builder builder = new Notification.Builder(context)
.setContentTitle(notifTitle)
.setContentText(account.name)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_notification);
notif = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) ? builder.build() : builder.getNotification();
} else {
notif = new Notification(R.drawable.ic_notification, notifTitle, System.currentTimeMillis());
notif.flags |= Notification.FLAG_AUTO_CANCEL;
notif.setLatestEventInfo(context, notifTitle, account.name, pendingIntent);
}
// Send the notification.
manager.notify(account.name, NotificationDispatcher.NOTIFICATION_ANNOUNCEMENT_SINGLE, notif);
} else {
String notifTitle = context.getString(R.string.notification_new_announcements, announcementCount);
// Create a pending intent.
Intent intent = NotificationDispatcher.createIntent(context, NotificationDispatcher.NOTIFICATION_ANNOUNCEMENT_MANY, account.name);
PendingIntent pendingIntent = PendingIntent.getActivity(context, account.hashCode(), intent, 0);
// Create notification based on platform version.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
Notification.Builder builder = new Notification.Builder(context)
.setContentTitle(notifTitle)
.setContentText(account.name)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_notification);
// Use the inbox style on Jellybean.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
Notification.InboxStyle builderInboxStyle = new Notification.InboxStyle(builder);
// We also want to limit the number of lines to 5.
int a = 0;
while (!cursor.isAfterLast() && a < 5) {
builderInboxStyle.addLine(cursor.getString(cursor.getColumnIndex(AnnouncementsContract.TITLE)));
cursor.moveToNext();
a++;
}
// Tap to show the rest.
if (announcementCount > 5) {
builderInboxStyle.setSummaryText(context.getString(R.string.notification_plus_more, (announcementCount - 5)));
}
notif = builderInboxStyle.build();
} else {
notif = builder.getNotification();
}
} else {
notif = new Notification(R.drawable.ic_notification, notifTitle, System.currentTimeMillis());
notif.flags |= Notification.FLAG_AUTO_CANCEL;
notif.setLatestEventInfo(context, notifTitle, account.name, pendingIntent);
}
// Send the notification.
manager.notify(account.name, NotificationDispatcher.NOTIFICATION_ANNOUNCEMENT_MANY, notif);
}
}
} catch (RemoteException e) {
Log.e(TAG, "Error trying to check for unread announcements");
}
}
}
}
// }}}
}