/Sky/src/org/jsharkey/sky/TinyAppWidget.java
Java | 169 lines | 107 code | 27 blank | 35 comment | 19 complexity | 606031f570a7c8763f68ce74ca0c851f MD5 | raw file
1/* 2 * Copyright (C) 2009 Jeff Sharkey, http://jsharkey.org/ 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package org.jsharkey.sky; 18 19import org.jsharkey.sky.ForecastProvider.AppWidgets; 20import org.jsharkey.sky.ForecastProvider.AppWidgetsColumns; 21import org.jsharkey.sky.ForecastProvider.ForecastsColumns; 22 23import android.app.PendingIntent; 24import android.appwidget.AppWidgetManager; 25import android.appwidget.AppWidgetProvider; 26import android.content.ComponentName; 27import android.content.ContentResolver; 28import android.content.ContentUris; 29import android.content.Context; 30import android.content.Intent; 31import android.content.res.Resources; 32import android.database.Cursor; 33import android.net.Uri; 34import android.util.Log; 35import android.view.View; 36import android.widget.RemoteViews; 37 38/** 39 * Definition of a tiny-sized forecast widget. Passes any requested updates to 40 * {@link UpdateService} to perform on background thread and prevent ANR. 41 */ 42public class TinyAppWidget extends AppWidgetProvider { 43 private static final String TAG = "TinyAppWidget"; 44 45 private static final String[] PROJECTION_APPWIDGETS = new String[] { 46 AppWidgetsColumns.UNITS, 47 }; 48 49 private static final int COL_UNITS = 0; 50 51 private static final String[] PROJECTION_FORECASTS = new String[] { 52 ForecastsColumns.CONDITIONS, 53 ForecastsColumns.TEMP_HIGH, 54 ForecastsColumns.TEMP_LOW, 55 }; 56 57 private static final int COL_CONDITIONS = 0; 58 private static final int COL_TEMP_HIGH = 1; 59 private static final int COL_TEMP_LOW = 2; 60 61 /** 62 * {@inheritDoc} 63 */ 64 @Override 65 public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { 66 // If no specific widgets requested, collect list of all 67 if (appWidgetIds == null) { 68 appWidgetIds = appWidgetManager.getAppWidgetIds( 69 new ComponentName(context, TinyAppWidget.class)); 70 } 71 72 // Request update for these widgets and launch updater service 73 UpdateService.requestUpdate(appWidgetIds); 74 context.startService(new Intent(context, UpdateService.class)); 75 } 76 77 /** 78 * {@inheritDoc} 79 */ 80 @Override 81 public void onDeleted(Context context, int[] appWidgetIds) { 82 ContentResolver resolver = context.getContentResolver(); 83 for (int appWidgetId : appWidgetIds) { 84 Log.d(TAG, "Deleting appWidgetId=" + appWidgetId); 85 Uri appWidgetUri = ContentUris.withAppendedId(AppWidgets.CONTENT_URI, appWidgetId); 86 resolver.delete(appWidgetUri, null, null); 87 } 88 } 89 90 /** 91 * Build an update for the given tiny widget. Should only be called from a 92 * service or thread to prevent ANR during database queries. 93 */ 94 public static RemoteViews buildUpdate(Context context, Uri appWidgetUri) { 95 Log.d(TAG, "Building tiny widget update"); 96 97 RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_tiny); 98 99 boolean daytime = ForecastUtils.isDaytime(); 100 boolean forecastFilled = false; 101 102 ContentResolver resolver = context.getContentResolver(); 103 Resources res = context.getResources(); 104 105 Cursor cursor = null; 106 int tempUnits = AppWidgetsColumns.UNITS_FAHRENHEIT; 107 108 // Pull out desired temperature units 109 try { 110 cursor = resolver.query(appWidgetUri, PROJECTION_APPWIDGETS, null, null, null); 111 if (cursor != null && cursor.moveToFirst()) { 112 tempUnits = cursor.getInt(COL_UNITS); 113 } 114 } finally { 115 if (cursor != null) { 116 cursor.close(); 117 } 118 } 119 120 // Find the forecast nearest now and build update using it 121 try { 122 Uri forecastAtUri = Uri.withAppendedPath(appWidgetUri, AppWidgets.TWIG_FORECAST_AT); 123 Uri forecastAtNowUri = Uri.withAppendedPath(forecastAtUri, 124 Long.toString(System.currentTimeMillis())); 125 cursor = resolver.query(forecastAtNowUri, PROJECTION_FORECASTS, null, null, null); 126 if (cursor != null && cursor.moveToFirst()) { 127 128 String conditions = cursor.getString(COL_CONDITIONS); 129 int iconResource = ForecastUtils.getIconForForecast(conditions, daytime); 130 int tempHigh = cursor.getInt(COL_TEMP_HIGH); 131 int tempLow = cursor.getInt(COL_TEMP_LOW); 132 133 views.setImageViewResource(R.id.icon, iconResource); 134 135 if (tempHigh == Integer.MIN_VALUE || tempLow == Integer.MIN_VALUE) { 136 views.setViewVisibility(R.id.temp_block, View.GONE); 137 } else { 138 views.setViewVisibility(R.id.temp_block, View.VISIBLE); 139 views.setTextViewText(R.id.high, 140 ForecastUtils.formatTemp(res, tempHigh, tempUnits)); 141 views.setTextViewText(R.id.low, 142 ForecastUtils.formatTemp(res, tempLow, tempUnits)); 143 } 144 145 forecastFilled = true; 146 } 147 } finally { 148 if (cursor != null) { 149 cursor.close(); 150 } 151 } 152 153 // If not filled correctly, show error message and hide other fields 154 if (!forecastFilled) { 155 views = new RemoteViews(context.getPackageName(), R.layout.widget_loading); 156 views.setTextViewText(R.id.loading, res.getString(R.string.widget_error)); 157 } 158 159 // Connect click intent to launch details 160 Intent detailIntent = new Intent(context, DetailsActivity.class); 161 detailIntent.setData(appWidgetUri); 162 163 PendingIntent pending = PendingIntent.getActivity(context, 0, detailIntent, 0); 164 165 views.setOnClickPendingIntent(R.id.widget, pending); 166 167 return views; 168 } 169}