PageRenderTime 69ms CodeModel.GetById 20ms app.highlight 43ms RepoModel.GetById 1ms app.codeStats 1ms

/Sky/Sky/src/org/jsharkey/sky/ForecastProvider.java

http://android-sky.googlecode.com/
Java | 417 lines | 268 code | 56 blank | 93 comment | 20 complexity | 615457206fbd9852793081e0fda2cbc7 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 android.appwidget.AppWidgetManager;
 20import android.content.ContentProvider;
 21import android.content.ContentResolver;
 22import android.content.ContentUris;
 23import android.content.ContentValues;
 24import android.content.Context;
 25import android.content.UriMatcher;
 26import android.database.Cursor;
 27import android.database.sqlite.SQLiteDatabase;
 28import android.database.sqlite.SQLiteOpenHelper;
 29import android.database.sqlite.SQLiteQueryBuilder;
 30import android.net.Uri;
 31import android.provider.BaseColumns;
 32import android.util.Log;
 33
 34/**
 35 * Provider that holds widget configuration details, and any cached forecast
 36 * data. Provides easy {@link ContentResolver} access to the data when building
 37 * widget updates or showing detailed lists.
 38 */
 39public class ForecastProvider extends ContentProvider {
 40    private static final String TAG = "ForecastProvider";
 41    private static final boolean LOGD = true;
 42
 43    public static final String AUTHORITY = "org.jsharkey.sky";
 44
 45    public interface AppWidgetsColumns {
 46        /**
 47         * Title given by user to this widget, usually shown in medium widgets
 48         * and details title bar.
 49         */
 50        public static final String TITLE = "title";
 51        public static final String LAT = "lat";
 52        public static final String LON = "lon";
 53
 54        /**
 55         * Temperature units to use when displaying forecasts for this widget,
 56         * usually defaults to {@link #UNITS_FAHRENHEIT}.
 57         */
 58        public static final String UNITS = "units";
 59        public static final int UNITS_FAHRENHEIT = 1;
 60        public static final int UNITS_CELSIUS = 2;
 61
 62        /**
 63         * Last system time when forecasts for this widget were updated, usually
 64         * as read from {@link System#currentTimeMillis()}.
 65         */
 66        public static final String LAST_UPDATED = "lastUpdated";
 67
 68        /**
 69         * Flag specifying if this widget has been configured yet, used to skip
 70         * building widget updates.
 71         */
 72        public static final String CONFIGURED = "configured";
 73
 74        public static final int CONFIGURED_TRUE = 1;
 75    }
 76
 77    public static class AppWidgets implements BaseColumns, AppWidgetsColumns {
 78        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/appwidgets");
 79
 80        /**
 81         * Directory twig to request all forecasts for a specific widget.
 82         */
 83        public static final String TWIG_FORECASTS = "forecasts";
 84
 85        /**
 86         * Directory twig to request the forecast nearest the requested time.
 87         */
 88        public static final String TWIG_FORECAST_AT = "forecast_at";
 89
 90        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/appwidget";
 91        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/appwidget";
 92
 93    }
 94
 95    public interface ForecastsColumns {
 96        /**
 97         * The parent {@link AppWidgetManager#EXTRA_APPWIDGET_ID} of this
 98         * forecast.
 99         */
100        public static final String APPWIDGET_ID = "widgetId";
101
102        /**
103         * Flag if this forecast is an alert.
104         */
105        public static final String ALERT = "alert";
106        public static final int ALERT_TRUE = 1;
107
108        /**
109         * Timestamp when this forecast becomes valid, in base ready for
110         * comparison with {@link System#currentTimeMillis()}.
111         */
112        public static final String VALID_START = "validStart";
113
114        /**
115         * High temperature during this forecast period, stored in Fahrenheit.
116         */
117        public static final String TEMP_HIGH = "tempHigh";
118
119        /**
120         * Low temperature during this forecast period, stored in Fahrenheit.
121         */
122        public static final String TEMP_LOW = "tempLow";
123
124        /**
125         * String describing the weather conditions.
126         */
127        public static final String CONDITIONS = "conditions";
128
129        /**
130         * Web link where more details can be found about this forecast.
131         */
132        public static final String URL = "url";
133
134    }
135
136    public static class Forecasts implements BaseColumns, ForecastsColumns {
137        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/forecasts");
138
139        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/forecast";
140        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/forecast";
141        
142    }
143
144    private static final String TABLE_APPWIDGETS = "appwidgets";
145    private static final String TABLE_FORECASTS = "forecasts";
146
147    private DatabaseHelper mOpenHelper;
148
149    /**
150     * Helper to manage upgrading between versions of the forecast database.
151     */
152    private static class DatabaseHelper extends SQLiteOpenHelper {
153        private static final String DATABASE_NAME = "forecasts.db";
154
155        private static final int DATABASE_VERSION = 2;
156
157        public DatabaseHelper(Context context) {
158            super(context, DATABASE_NAME, null, DATABASE_VERSION);
159        }
160
161        @Override
162        public void onCreate(SQLiteDatabase db) {
163            db.execSQL("CREATE TABLE " + TABLE_APPWIDGETS + " ("
164                    + BaseColumns._ID + " INTEGER PRIMARY KEY,"
165                    + AppWidgetsColumns.TITLE + " TEXT,"
166                    + AppWidgetsColumns.LAT + " REAL,"
167                    + AppWidgetsColumns.LON + " REAL,"
168                    + AppWidgetsColumns.UNITS + " INTEGER,"
169                    + AppWidgetsColumns.LAST_UPDATED + " INTEGER,"
170                    + AppWidgetsColumns.CONFIGURED + " INTEGER);");
171
172            db.execSQL("CREATE TABLE " + TABLE_FORECASTS + " ("
173                    + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
174                    + ForecastsColumns.APPWIDGET_ID + " INTEGER,"
175                    + ForecastsColumns.ALERT + " INTEGER DEFAULT 0,"
176                    + ForecastsColumns.VALID_START + " INTEGER,"
177                    + ForecastsColumns.TEMP_HIGH + " INTEGER,"
178                    + ForecastsColumns.TEMP_LOW + " INTEGER,"
179                    + ForecastsColumns.CONDITIONS + " TEXT,"
180                    + ForecastsColumns.URL + " TEXT);");
181        }
182
183        @Override
184        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
185            int version = oldVersion;
186
187            if (version != DATABASE_VERSION) {
188                Log.w(TAG, "Destroying old data during upgrade.");
189                db.execSQL("DROP TABLE IF EXISTS " + TABLE_APPWIDGETS);
190                db.execSQL("DROP TABLE IF EXISTS " + TABLE_FORECASTS);
191                onCreate(db);
192            }
193        }
194    }
195
196    /**
197     * {@inheritDoc}
198     */
199    @Override
200    public int delete(Uri uri, String selection, String[] selectionArgs) {
201        if (LOGD) Log.d(TAG, "delete() with uri=" + uri);
202        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
203
204        int count = 0;
205
206        switch (sUriMatcher.match(uri)) {
207            case APPWIDGETS: {
208                count = db.delete(TABLE_APPWIDGETS, selection, selectionArgs);
209                break;
210            }
211            case APPWIDGETS_ID: {
212                // Delete a specific widget and all its forecasts
213                long appWidgetId = Long.parseLong(uri.getPathSegments().get(1));
214                count = db.delete(TABLE_APPWIDGETS, BaseColumns._ID + "=" + appWidgetId, null);
215                count += db.delete(TABLE_FORECASTS, ForecastsColumns.APPWIDGET_ID + "="
216                        + appWidgetId, null);
217                break;
218            }
219            case APPWIDGETS_FORECASTS: {
220                // Delete all the forecasts for a specific widget
221                long appWidgetId = Long.parseLong(uri.getPathSegments().get(1));
222                if (selection == null) {
223                    selection = "";
224                } else {
225                    selection = "(" + selection + ") AND ";
226                }
227                selection += ForecastsColumns.APPWIDGET_ID + "=" + appWidgetId;
228                count = db.delete(TABLE_FORECASTS, selection, selectionArgs);
229                break;
230            }
231            case FORECASTS: {
232                count = db.delete(TABLE_FORECASTS, selection, selectionArgs);
233                break;
234            }
235            default:
236                throw new UnsupportedOperationException();
237        }
238
239        return count;
240    }
241
242    /**
243     * {@inheritDoc}
244     */
245    @Override
246    public String getType(Uri uri) {
247        switch (sUriMatcher.match(uri)) {
248            case APPWIDGETS:
249                return AppWidgets.CONTENT_TYPE;
250            case APPWIDGETS_ID:
251                return AppWidgets.CONTENT_ITEM_TYPE;
252            case APPWIDGETS_FORECASTS:
253                return Forecasts.CONTENT_TYPE;
254            case FORECASTS:
255                return Forecasts.CONTENT_TYPE;
256            case FORECASTS_ID:
257                return Forecasts.CONTENT_ITEM_TYPE;
258        }
259        throw new IllegalStateException();
260    }
261
262    /**
263     * {@inheritDoc}
264     */
265    @Override
266    public Uri insert(Uri uri, ContentValues values) {
267        if (LOGD) Log.d(TAG, "insert() with uri=" + uri);
268        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
269
270        Uri resultUri = null;
271
272        switch (sUriMatcher.match(uri)) {
273            case APPWIDGETS: {
274                long rowId = db.insert(TABLE_APPWIDGETS, AppWidgetsColumns.TITLE, values);
275                if (rowId != -1) {
276                    resultUri = ContentUris.withAppendedId(AppWidgets.CONTENT_URI, rowId);
277                }
278                break;
279            }
280            case APPWIDGETS_FORECASTS: {
281                // Insert a forecast into a specific widget
282                long appWidgetId = Long.parseLong(uri.getPathSegments().get(1));
283                values.put(ForecastsColumns.APPWIDGET_ID, appWidgetId);
284                long rowId = db.insert(TABLE_FORECASTS, ForecastsColumns.CONDITIONS, values);
285                if (rowId != -1) {
286                    resultUri = ContentUris.withAppendedId(AppWidgets.CONTENT_URI, rowId);
287                }
288                break;
289            }
290            case FORECASTS: {
291                long rowId = db.insert(TABLE_FORECASTS, ForecastsColumns.CONDITIONS, values);
292                if (rowId != -1) {
293                    resultUri = ContentUris.withAppendedId(Forecasts.CONTENT_URI, rowId);
294                }
295                break;
296            }
297            default:
298                throw new UnsupportedOperationException();
299        }
300
301        return resultUri;
302    }
303
304    @Override
305    public boolean onCreate() {
306        mOpenHelper = new DatabaseHelper(getContext());
307        return true;
308    }
309
310    /**
311     * {@inheritDoc}
312     */
313    @Override
314    public Cursor query(Uri uri, String[] projection, String selection,
315            String[] selectionArgs, String sortOrder) {
316        if (LOGD) Log.d(TAG, "query() with uri=" + uri);
317        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
318        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
319
320        String limit = null;
321
322        switch (sUriMatcher.match(uri)) {
323            case APPWIDGETS: {
324                qb.setTables(TABLE_APPWIDGETS);
325                break;
326            }
327            case APPWIDGETS_ID: {
328                String appWidgetId = uri.getPathSegments().get(1);
329                qb.setTables(TABLE_APPWIDGETS);
330                qb.appendWhere(BaseColumns._ID + "=" + appWidgetId);
331                break;
332            }
333            case APPWIDGETS_FORECASTS: {
334                // Pick all the forecasts for given widget, sorted by date and
335                // importance
336                String appWidgetId = uri.getPathSegments().get(1);
337                qb.setTables(TABLE_FORECASTS);
338                qb.appendWhere(ForecastsColumns.APPWIDGET_ID + "=" + appWidgetId);
339                sortOrder = ForecastsColumns.VALID_START + " ASC, " + ForecastsColumns.ALERT
340                        + " DESC";
341                break;
342            }
343            case APPWIDGETS_FORECAST_AT: {
344                // Pick the forecast nearest for given widget nearest the given
345                // timestamp
346                String appWidgetId = uri.getPathSegments().get(1);
347                String atTime = uri.getPathSegments().get(3);
348                qb.setTables(TABLE_FORECASTS);
349                qb.appendWhere(ForecastsColumns.APPWIDGET_ID + "=" + appWidgetId);
350                sortOrder = "ABS(" + ForecastsColumns.VALID_START + " - " + atTime + ") ASC, "
351                        + ForecastsColumns.ALERT + " DESC";
352                limit = "1";
353                break;
354            }
355            case FORECASTS: {
356                qb.setTables(TABLE_FORECASTS);
357                break;
358            }
359            case FORECASTS_ID: {
360                String forecastId = uri.getPathSegments().get(1);
361                qb.setTables(TABLE_FORECASTS);
362                qb.appendWhere(BaseColumns._ID + "=" + forecastId);
363                break;
364            }
365        }
366
367        return qb.query(db, projection, selection, selectionArgs, null, null, sortOrder, limit);
368    }
369
370    /**
371     * {@inheritDoc}
372     */
373    @Override
374    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
375        if (LOGD) Log.d(TAG, "update() with uri=" + uri);
376        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
377
378        switch (sUriMatcher.match(uri)) {
379            case APPWIDGETS: {
380                return db.update(TABLE_APPWIDGETS, values, selection, selectionArgs);
381            }
382            case APPWIDGETS_ID: {
383                long appWidgetId = Long.parseLong(uri.getPathSegments().get(1));
384                return db.update(TABLE_APPWIDGETS, values, BaseColumns._ID + "=" + appWidgetId,
385                        null);
386            }
387            case FORECASTS: {
388                return db.update(TABLE_FORECASTS, values, selection, selectionArgs);
389            }
390        }
391
392        throw new UnsupportedOperationException();
393    }
394
395    /**
396     * Matcher used to filter an incoming {@link Uri}. 
397     */
398    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
399
400    private static final int APPWIDGETS = 101;
401    private static final int APPWIDGETS_ID = 102;
402    private static final int APPWIDGETS_FORECASTS = 103;
403    private static final int APPWIDGETS_FORECAST_AT = 104;
404
405    private static final int FORECASTS = 201;
406    private static final int FORECASTS_ID = 202;
407
408    static {
409        sUriMatcher.addURI(AUTHORITY, "appwidgets", APPWIDGETS);
410        sUriMatcher.addURI(AUTHORITY, "appwidgets/#", APPWIDGETS_ID);
411        sUriMatcher.addURI(AUTHORITY, "appwidgets/#/forecasts", APPWIDGETS_FORECASTS);
412        sUriMatcher.addURI(AUTHORITY, "appwidgets/#/forecast_at/*", APPWIDGETS_FORECAST_AT);
413
414        sUriMatcher.addURI(AUTHORITY, "forecasts", FORECASTS);
415        sUriMatcher.addURI(AUTHORITY, "forecasts/#", FORECASTS_ID);
416    }
417}