package com.vaguehope.onosendai.update;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompat.Builder;
import com.vaguehope.onosendai.R;
import com.vaguehope.onosendai.notifications.NotificationIds;
import com.vaguehope.onosendai.ui.MainActivity;
import com.vaguehope.onosendai.util.FileHelper;
import com.vaguehope.onosendai.util.LogWrapper;
public class BatteryNotify extends BroadcastReceiver {
private static final int OVERRIDE_DURATION_HOURS = 1;
private static final long OVERRIDE_DURATION_MILLIS = TimeUnit.HOURS.toMillis(OVERRIDE_DURATION_HOURS);
private static final long GRACE_MILLIS = TimeUnit.MINUTES.toMillis(1L);
private static final String FILE_PREFIX = "battery-";
private static final String EXTRA_REQUEST_CODE = "request_code";
private static final int NOT_UPDATING_NOTIFICATION_ID = NotificationIds.BASE_BATTERY_ID + 1;
private static final int RC_SHOW_MAIN_ACT = NotificationIds.BASE_BATTERY_ID + 2;
private static final int RC_PLUS_TIME = NotificationIds.BASE_BATTERY_ID + 3;
private static final LogWrapper LOG = new LogWrapper("BN");
private static volatile Long overrideLastModifiedCache;
private static final Object[] OVERRIDE_LAST_MODIFIED_CACHE_LOCK = new Object[0];
private static volatile Boolean notifyNotUpdatingCache;
private static final Object[] NOTIFY_NOT_UPDATING_CACHE_LOCK = new Object[0];
private static File getOverrideFile (final Context context) {
return new File(context.getCacheDir(), FILE_PREFIX + "override");
}
private static File getUpdateNotifiyFile (final Context context) {
return new File(context.getCacheDir(), FILE_PREFIX + "update-notify");
}
public static boolean isOverrideEnabled (final Context context) {
Long lastModified;
synchronized (OVERRIDE_LAST_MODIFIED_CACHE_LOCK) {
lastModified = overrideLastModifiedCache;
if (lastModified == null) {
final File file = getOverrideFile(context);
if (file.exists()) {
lastModified = file.lastModified();
}
else {
lastModified = 0L;
}
overrideLastModifiedCache = lastModified;
}
}
return (System.currentTimeMillis() - lastModified) <= OVERRIDE_DURATION_MILLIS;
}
public static void plusTime (final Context context) {
enableOverride(context);
getManager(context).cancel(NOT_UPDATING_NOTIFICATION_ID);
// Run any updates that were missed now as the user has just clicked a thing.
context.startService(new Intent(context, UpdateService.class));
}
// Visible for testing.
static void enableOverride (final Context context) {
final File file = getOverrideFile(context);
try {
synchronized (OVERRIDE_LAST_MODIFIED_CACHE_LOCK) {
FileHelper.touchFile(file, GRACE_MILLIS);
overrideLastModifiedCache = null;
}
LOG.i("Override enabled.");
}
catch (final IOException e) {
LOG.w("Override marker file error: " + file.getAbsolutePath(), e);
}
}
public static void notifyNotUpdating (final Context context) {
if (shouldNotifyNotUpdating(context)) showNotification(context);
}
// Visible for testing.
static boolean shouldNotifyNotUpdating (final Context context) {
synchronized (NOTIFY_NOT_UPDATING_CACHE_LOCK) {
if (notifyNotUpdatingCache != null && notifyNotUpdatingCache) return false;
final File file = getUpdateNotifiyFile(context);
if (file.exists()) {
notifyNotUpdatingCache = true;
return false;
}
try {
FileHelper.touchFile(file, GRACE_MILLIS);
notifyNotUpdatingCache = true;
}
catch (final IOException e) {
LOG.w("NotUpdating marker file error: " + file.getAbsolutePath(), e);
}
}
return true;
}
public static void clearNotUpdating (final Context context) {
synchronized (NOTIFY_NOT_UPDATING_CACHE_LOCK) {
if (notifyNotUpdatingCache == null || notifyNotUpdatingCache) {
final File file = getUpdateNotifiyFile(context);
if (file.exists()) {
if (file.delete()) {
notifyNotUpdatingCache = false;
}
else {
notifyNotUpdatingCache = null;
LOG.w("Failed to rm: " + file.getAbsolutePath());
}
}
else {
notifyNotUpdatingCache = false;
}
}
}
}
private static void showNotification (final Context context) {
final Builder nb = new NotificationCompat.Builder(context)
.setOnlyAlertOnce(true)
.setSmallIcon(R.drawable.exclamation_red) // TODO better icon.
.setContentTitle(context.getString(R.string.background_updating_disabled_notification_title))
.setContentText(context.getString(R.string.background_updating_disabled_notification_msg))
.setTicker(context.getString(R.string.background_updating_disabled_notification_title))
.setAutoCancel(true)
.setWhen(System.currentTimeMillis())
.setContentIntent(makeShowMainActPi(context))
.addAction(android.R.drawable.ic_menu_add,
String.format("+%s Hour", OVERRIDE_DURATION_HOURS), //ES
makePlusTimePi(context));
getManager(context).notify(NOT_UPDATING_NOTIFICATION_ID, nb.build());
}
private static NotificationManager getManager (final Context context) {
return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
}
private static PendingIntent makeShowMainActPi (final Context context) {
return PendingIntent.getActivity(context, RC_SHOW_MAIN_ACT,
new Intent(context, MainActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
PendingIntent.FLAG_CANCEL_CURRENT);
}
private static PendingIntent makePlusTimePi (final Context context) {
return PendingIntent.getBroadcast(context, RC_PLUS_TIME,
new Intent(context, BatteryNotify.class)
.putExtra(EXTRA_REQUEST_CODE, RC_PLUS_TIME),
PendingIntent.FLAG_UPDATE_CURRENT);
}
public BatteryNotify () {
super();
}
@Override
public void onReceive (final Context context, final Intent intent) {
try {
final Bundle extras = intent.getExtras();
final int requestCode = extras != null ? extras.getInt(EXTRA_REQUEST_CODE, -1) : -1;
switch (requestCode) {
case RC_PLUS_TIME:
plusTime(context);
break;
default:
LOG.w("Intent with unknown request code %s: %s", requestCode, intent);
}
}
catch (final Exception e) { // NOSONAR record all errors.
LOG.e("Failed enable override.", e);
}
}
}