package com.ianhanniballake.contractiontimer.appwidget;
import android.annotation.TargetApi;
import android.app.IntentService;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.Build;
import android.preference.PreferenceManager;
import android.provider.BaseColumns;
import android.support.annotation.NonNull;
import android.support.annotation.StringDef;
import android.support.v4.app.TaskStackBuilder;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.View;
import android.widget.RemoteViews;
import com.ianhanniballake.contractiontimer.BuildConfig;
import com.ianhanniballake.contractiontimer.R;
import com.ianhanniballake.contractiontimer.provider.ContractionContract;
import com.ianhanniballake.contractiontimer.ui.MainActivity;
import com.ianhanniballake.contractiontimer.ui.Preferences;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Handles updates of the 'Detail' style App Widgets
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class DetailAppWidgetService extends IntentService {
private final static String TAG = DetailAppWidgetProvider.class.getSimpleName();
/**
* Identifier for the keyguard (lockscreen) version of this widget to be used in Google Analytics
*/
private final static String KEYGUARD_WIDGET_IDENTIFIER = "DetailWidgetKeyguard";
/**
* Identifier for this widget to be used in Analytics
*/
private final static String WIDGET_IDENTIFIER = "widget_detail";
/**
* Creates a new DetailAppWidgetService
*/
public DetailAppWidgetService() {
super(TAG);
}
@Override
protected void onHandleIntent(final Intent intent) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Updating Detail App Widgets");
final String[] projection = {BaseColumns._ID, ContractionContract.Contractions.COLUMN_NAME_START_TIME,
ContractionContract.Contractions.COLUMN_NAME_END_TIME};
final String selection = ContractionContract.Contractions.COLUMN_NAME_START_TIME + ">?";
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
final long averagesTimeFrame = Long.parseLong(preferences.getString(
Preferences.AVERAGE_TIME_FRAME_PREFERENCE_KEY,
getString(R.string.pref_average_time_frame_default)));
final long timeCutoff = System.currentTimeMillis() - averagesTimeFrame;
final String[] selectionArgs = {Long.toString(timeCutoff)};
final Cursor data = getContentResolver().query(ContractionContract.Contractions.CONTENT_URI, projection,
selection, selectionArgs, null);
final String appwidgetBackground = preferences.getString(Preferences.APPWIDGET_BACKGROUND_PREFERENCE_KEY,
getString(R.string.pref_appwidget_background_default));
// Set the average duration and frequency
String formattedAverageDuration = "";
String formattedAverageFrequency = "";
if (data != null && data.moveToFirst()) {
double averageDuration = 0;
double averageFrequency = 0;
int numDurations = 0;
int numFrequencies = 0;
while (!data.isAfterLast()) {
final int startTimeColumnIndex = data
.getColumnIndex(ContractionContract.Contractions.COLUMN_NAME_START_TIME);
final long startTime = data.getLong(startTimeColumnIndex);
final int endTimeColumnIndex = data
.getColumnIndex(ContractionContract.Contractions.COLUMN_NAME_END_TIME);
if (!data.isNull(endTimeColumnIndex)) {
final long endTime = data.getLong(endTimeColumnIndex);
final long curDuration = endTime - startTime;
averageDuration = (curDuration + numDurations * averageDuration) / (numDurations + 1);
numDurations++;
}
if (data.moveToNext()) {
final int prevContractionStartTimeColumnIndex = data
.getColumnIndex(ContractionContract.Contractions.COLUMN_NAME_START_TIME);
final long prevContractionStartTime = data.getLong(prevContractionStartTimeColumnIndex);
final long curFrequency = startTime - prevContractionStartTime;
averageFrequency = (curFrequency + numFrequencies * averageFrequency) / (numFrequencies + 1);
numFrequencies++;
}
}
final long averageDurationInSeconds = (long) (averageDuration / 1000);
formattedAverageDuration = DateUtils.formatElapsedTime(averageDurationInSeconds);
final long averageFrequencyInSeconds = (long) (averageFrequency / 1000);
formattedAverageFrequency = DateUtils.formatElapsedTime(averageFrequencyInSeconds);
}
// Determine whether a contraction is currently ongoing
// Need to use a separate cursor as there could be running contractions
// outside of the average time frame
final Cursor allData = getContentResolver().query(ContractionContract.Contractions.CONTENT_URI, projection,
null, null, null);
final boolean contractionOngoing = allData != null && allData.moveToFirst()
&& allData.isNull(allData.getColumnIndex(ContractionContract.Contractions.COLUMN_NAME_END_TIME));
// Close the cursors
if (data != null)
data.close();
if (allData != null)
allData.close();
// Get the list of app widgets to update
final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
final int[] detailAppWidgetIds;
if (intent.hasExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS))
detailAppWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
else
detailAppWidgetIds = appWidgetManager
.getAppWidgetIds(new ComponentName(this, DetailAppWidgetProvider.class));
// Build and update the views for each widget
// We need to do it widget by widget to allow changes for keyguard vs
// home screen widgets
for (final int appWidgetId : detailAppWidgetIds) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Updating detail widget with id " + appWidgetId);
// Note that all widgets share the same theme
RemoteViews views;
if (appwidgetBackground.equals("light"))
views = new RemoteViews(getPackageName(), R.layout.detail_appwidget_light);
else
views = new RemoteViews(getPackageName(), R.layout.detail_appwidget_dark);
// Add the intent to the Application Launch button
final Intent applicationLaunchIntent = new Intent(this, MainActivity.class);
applicationLaunchIntent.putExtra(MainActivity.LAUNCHED_FROM_WIDGET_EXTRA, WIDGET_IDENTIFIER);
applicationLaunchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
final PendingIntent applicationLaunchPendingIntent = PendingIntent.getActivity(this, 0,
applicationLaunchIntent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.application_launch, applicationLaunchPendingIntent);
// Add in the averages
views.setTextViewText(R.id.average_duration, formattedAverageDuration);
views.setTextViewText(R.id.average_frequency, formattedAverageFrequency);
// Add the intent for the toggle button
final Intent toggleContractionIntent = new Intent(this, AppWidgetToggleService.class);
toggleContractionIntent.putExtra(AppWidgetToggleService.WIDGET_NAME_EXTRA, WIDGET_IDENTIFIER);
final PendingIntent toggleContractionPendingIntent = PendingIntent.getService(this, 0,
toggleContractionIntent, PendingIntent.FLAG_UPDATE_CURRENT);
if (contractionOngoing) {
views.setViewVisibility(R.id.contraction_toggle_on, View.VISIBLE);
views.setOnClickPendingIntent(R.id.contraction_toggle_on, toggleContractionPendingIntent);
views.setViewVisibility(R.id.contraction_toggle_off, View.GONE);
} else {
views.setViewVisibility(R.id.contraction_toggle_off, View.VISIBLE);
views.setOnClickPendingIntent(R.id.contraction_toggle_off, toggleContractionPendingIntent);
views.setViewVisibility(R.id.contraction_toggle_on, View.GONE);
}
// Set up the collection
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
setRemoteAdapter(views);
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
setRemoteAdapterV11(views);
final Intent clickIntentTemplate = new Intent(Intent.ACTION_VIEW).setPackage(getPackageName());
clickIntentTemplate.putExtra(MainActivity.LAUNCHED_FROM_WIDGET_EXTRA, WIDGET_IDENTIFIER);
final PendingIntent clickPendingIntentTemplate = TaskStackBuilder.create(this)
.addNextIntentWithParentStack(clickIntentTemplate)
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
views.setPendingIntentTemplate(R.id.list_view, clickPendingIntentTemplate);
views.setEmptyView(R.id.list_view, R.id.empty_view);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
/**
* Sets the remote adapter used to fill in the list items
*
* @param views RemoteViews to set the RemoteAdapter
*/
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
private void setRemoteAdapter(@NonNull final RemoteViews views) {
views.setRemoteAdapter(R.id.list_view, new Intent(this, DetailAppWidgetRemoteViewsService.class));
}
/**
* Sets the remote adapter used to fill in the list items
*
* @param views RemoteViews to set the RemoteAdapter
*/
@SuppressWarnings("deprecation")
private void setRemoteAdapterV11(@NonNull final RemoteViews views) {
views.setRemoteAdapter(0, R.id.list_view, new Intent(this, DetailAppWidgetRemoteViewsService.class));
}
@Retention(RetentionPolicy.SOURCE)
@StringDef({KEYGUARD_WIDGET_IDENTIFIER, WIDGET_IDENTIFIER})
public @interface WidgetIdentifier {
}
}