From 7ec710824728676726f72c85082da8611eca7433 Mon Sep 17 00:00:00 2001 From: Dan Galpin Date: Mon, 25 May 2015 04:13:31 -0700 Subject: [PATCH] Load real data into the 1x1 Today widget, moving our work to an IntentService --- app/src/main/AndroidManifest.xml | 2 + .../app/sync/SunshineSyncAdapter.java | 11 ++ .../app/widget/TodayWidgetIntentService.java | 108 ++++++++++++++++++ .../app/widget/TodayWidgetProvider.java | 54 +++------ 4 files changed, 140 insertions(+), 35 deletions(-) create mode 100644 app/src/main/java/com/example/android/sunshine/app/widget/TodayWidgetIntentService.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4659c95..d41a398 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -116,10 +116,12 @@ android:label="@string/title_widget_today" > + + diff --git a/app/src/main/java/com/example/android/sunshine/app/sync/SunshineSyncAdapter.java b/app/src/main/java/com/example/android/sunshine/app/sync/SunshineSyncAdapter.java index e248235..c9275d0 100644 --- a/app/src/main/java/com/example/android/sunshine/app/sync/SunshineSyncAdapter.java +++ b/app/src/main/java/com/example/android/sunshine/app/sync/SunshineSyncAdapter.java @@ -52,6 +52,8 @@ import java.util.concurrent.ExecutionException; public class SunshineSyncAdapter extends AbstractThreadedSyncAdapter { public final String LOG_TAG = SunshineSyncAdapter.class.getSimpleName(); + public static final String ACTION_DATA_UPDATED = + "com.example.android.sunshine.app.ACTION_DATA_UPDATED"; // Interval at which to sync with the weather, in seconds. // 60 seconds (1 minute) * 180 = 3 hours public static final int SYNC_INTERVAL = 60 * 180; @@ -338,6 +340,7 @@ public class SunshineSyncAdapter extends AbstractThreadedSyncAdapter { WeatherContract.WeatherEntry.COLUMN_DATE + " <= ?", new String[] {Long.toString(dayTime.setJulianDay(julianStartDay-1))}); + updateWidgets(); notifyWeather(); } Log.d(LOG_TAG, "Sync Complete. " + cVVector.size() + " Inserted"); @@ -350,6 +353,14 @@ public class SunshineSyncAdapter extends AbstractThreadedSyncAdapter { } } + private void updateWidgets() { + Context context = getContext(); + // Setting the package ensures that only components in our app will receive the broadcast + Intent dataUpdatedIntent = new Intent(ACTION_DATA_UPDATED) + .setPackage(context.getPackageName()); + context.sendBroadcast(dataUpdatedIntent); + } + private void notifyWeather() { Context context = getContext(); //checking the last update and notify if it' the first of the day diff --git a/app/src/main/java/com/example/android/sunshine/app/widget/TodayWidgetIntentService.java b/app/src/main/java/com/example/android/sunshine/app/widget/TodayWidgetIntentService.java new file mode 100644 index 0000000..c07739a --- /dev/null +++ b/app/src/main/java/com/example/android/sunshine/app/widget/TodayWidgetIntentService.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * 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.example.android.sunshine.app.widget; + +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.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.widget.RemoteViews; + +import com.example.android.sunshine.app.MainActivity; +import com.example.android.sunshine.app.R; +import com.example.android.sunshine.app.Utility; +import com.example.android.sunshine.app.data.WeatherContract; + +/** + * IntentService which handles updating all Today widgets with the latest data + */ +public class TodayWidgetIntentService extends IntentService { + private static final String[] FORECAST_COLUMNS = { + WeatherContract.WeatherEntry.COLUMN_WEATHER_ID, + WeatherContract.WeatherEntry.COLUMN_SHORT_DESC, + WeatherContract.WeatherEntry.COLUMN_MAX_TEMP + }; + // these indices must match the projection + private static final int INDEX_WEATHER_ID = 0; + private static final int INDEX_SHORT_DESC = 1; + private static final int INDEX_MAX_TEMP = 2; + + public TodayWidgetIntentService() { + super("TodayWidgetIntentService"); + } + + @Override + protected void onHandleIntent(Intent intent) { + // Retrieve all of the Today widget ids: these are the widgets we need to update + AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this); + int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(this, + TodayWidgetProvider.class)); + + // Get today's data from the ContentProvider + String location = Utility.getPreferredLocation(this); + Uri weatherForLocationUri = WeatherContract.WeatherEntry.buildWeatherLocationWithStartDate( + location, System.currentTimeMillis()); + Cursor data = getContentResolver().query(weatherForLocationUri, FORECAST_COLUMNS, null, + null, WeatherContract.WeatherEntry.COLUMN_DATE + " ASC"); + if (data == null) { + return; + } + if (!data.moveToFirst()) { + data.close(); + return; + } + + // Extract the weather data from the Cursor + int weatherId = data.getInt(INDEX_WEATHER_ID); + int weatherArtResourceId = Utility.getArtResourceForWeatherCondition(weatherId); + String description = data.getString(INDEX_SHORT_DESC); + double maxTemp = data.getDouble(INDEX_MAX_TEMP); + String formattedMaxTemperature = Utility.formatTemperature(this, maxTemp); + data.close(); + + // Perform this loop procedure for each Today widget + for (int appWidgetId : appWidgetIds) { + int layoutId = R.layout.widget_today_small; + RemoteViews views = new RemoteViews(getPackageName(), layoutId); + + // Add the data to the RemoteViews + views.setImageViewResource(R.id.widget_icon, weatherArtResourceId); + // Content Descriptions for RemoteViews were only added in ICS MR1 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { + setRemoteContentDescription(views, description); + } + views.setTextViewText(R.id.widget_high_temperature, formattedMaxTemperature); + + // Create an Intent to launch MainActivity + Intent launchIntent = new Intent(this, MainActivity.class); + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, launchIntent, 0); + views.setOnClickPendingIntent(R.id.widget, pendingIntent); + + // Tell the AppWidgetManager to perform an update on the current app widget + appWidgetManager.updateAppWidget(appWidgetId, views); + } + } + + @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) + private void setRemoteContentDescription(RemoteViews views, String description) { + views.setContentDescription(R.id.widget_icon, description); + } +} diff --git a/app/src/main/java/com/example/android/sunshine/app/widget/TodayWidgetProvider.java b/app/src/main/java/com/example/android/sunshine/app/widget/TodayWidgetProvider.java index 04ef261..ee74909 100644 --- a/app/src/main/java/com/example/android/sunshine/app/widget/TodayWidgetProvider.java +++ b/app/src/main/java/com/example/android/sunshine/app/widget/TodayWidgetProvider.java @@ -15,55 +15,39 @@ */ package com.example.android.sunshine.app.widget; -import android.annotation.TargetApi; -import android.app.PendingIntent; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.Context; import android.content.Intent; -import android.os.Build; -import android.widget.RemoteViews; +import android.os.Bundle; +import android.support.annotation.NonNull; -import com.example.android.sunshine.app.MainActivity; -import com.example.android.sunshine.app.R; -import com.example.android.sunshine.app.Utility; +import com.example.android.sunshine.app.sync.SunshineSyncAdapter; /** * Provider for a widget showing today's weather. + * + * Delegates widget updating to {@link TodayWidgetIntentService} to ensure that + * data retrieval is done on a background thread */ public class TodayWidgetProvider extends AppWidgetProvider { + @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { - int weatherArtResourceId = R.drawable.art_clear; - String description = "Clear"; - double maxTemp = 24; - String formattedMaxTemperature = Utility.formatTemperature(context, maxTemp); + context.startService(new Intent(context, TodayWidgetIntentService.class)); + } - // Perform this loop procedure for each Today widget - for (int appWidgetId : appWidgetIds) { - int layoutId = R.layout.widget_today_small; - RemoteViews views = new RemoteViews(context.getPackageName(), layoutId); + @Override + public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, + int appWidgetId, Bundle newOptions) { + context.startService(new Intent(context, TodayWidgetIntentService.class)); + } - // Add the data to the RemoteViews - views.setImageViewResource(R.id.widget_icon, weatherArtResourceId); - // Content Descriptions for RemoteViews were only added in ICS MR1 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { - setRemoteContentDescription(views, description); - } - views.setTextViewText(R.id.widget_high_temperature, formattedMaxTemperature); - - // Create an Intent to launch MainActivity - Intent launchIntent = new Intent(context, MainActivity.class); - PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, launchIntent, 0); - views.setOnClickPendingIntent(R.id.widget, pendingIntent); - - // Tell the AppWidgetManager to perform an update on the current app widget - appWidgetManager.updateAppWidget(appWidgetId, views); + @Override + public void onReceive(@NonNull Context context, @NonNull Intent intent) { + super.onReceive(context, intent); + if (SunshineSyncAdapter.ACTION_DATA_UPDATED.equals(intent.getAction())) { + context.startService(new Intent(context, TodayWidgetIntentService.class)); } } - - @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) - private void setRemoteContentDescription(RemoteViews views, String description) { - views.setContentDescription(R.id.widget_icon, description); - } }