/* * Copyright (C) 2010-2012 Paul Watts (paulcwatts@gmail.com) * * 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 org.onebusaway.android.provider; import org.onebusaway.android.BuildConfig; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; import android.database.DatabaseUtils; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.util.Log; import java.io.File; import java.util.HashMap; import java.util.List; public class ObaProvider extends ContentProvider { public static final String TAG = "ObaProvider"; /** * The database name cannot be changed. It needs to remain the same to support backwards * compatibility with existing installed apps */ private static final String DATABASE_NAME = BuildConfig.APPLICATION_ID + ".db"; private class OpenHelper extends SQLiteOpenHelper { private static final int DATABASE_VERSION = 25; public OpenHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { bootstrapDatabase(db); onUpgrade(db, 12, DATABASE_VERSION); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.d(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion); if (oldVersion < 12) { dropTables(db); bootstrapDatabase(db); oldVersion = 12; } if (oldVersion == 12) { db.execSQL( "CREATE TABLE " + ObaContract.StopRouteFilters.PATH + " (" + ObaContract.StopRouteFilters.STOP_ID + " VARCHAR NOT NULL, " + ObaContract.StopRouteFilters.ROUTE_ID + " VARCHAR NOT NULL" + ");"); ++oldVersion; } if (oldVersion == 13) { db.execSQL( "ALTER TABLE " + ObaContract.Stops.PATH + " ADD COLUMN " + ObaContract.Stops.USER_NAME); db.execSQL( "ALTER TABLE " + ObaContract.Stops.PATH + " ADD COLUMN " + ObaContract.Stops.ACCESS_TIME); db.execSQL( "ALTER TABLE " + ObaContract.Stops.PATH + " ADD COLUMN " + ObaContract.Stops.FAVORITE); // These are being added to the routes database as well, // even though some of them aren't accessible though the UI yet // (we don't allow people to rename routes) db.execSQL( "ALTER TABLE " + ObaContract.Routes.PATH + " ADD COLUMN " + ObaContract.Routes.USER_NAME); db.execSQL( "ALTER TABLE " + ObaContract.Routes.PATH + " ADD COLUMN " + ObaContract.Routes.ACCESS_TIME); db.execSQL( "ALTER TABLE " + ObaContract.Routes.PATH + " ADD COLUMN " + ObaContract.Routes.FAVORITE); ++oldVersion; } if (oldVersion == 14) { db.execSQL( "ALTER TABLE " + ObaContract.Routes.PATH + " ADD COLUMN " + ObaContract.Routes.URL); ++oldVersion; } if (oldVersion == 15) { db.execSQL( "CREATE TABLE " + ObaContract.TripAlerts.PATH + " (" + ObaContract.TripAlerts._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + ObaContract.TripAlerts.TRIP_ID + " VARCHAR NOT NULL, " + ObaContract.TripAlerts.STOP_ID + " VARCHAR NOT NULL, " + ObaContract.TripAlerts.START_TIME + " INTEGER NOT NULL, " + ObaContract.TripAlerts.STATE + " INTEGER NOT NULL DEFAULT " + ObaContract.TripAlerts.STATE_SCHEDULED + ");"); db.execSQL("DROP TRIGGER IF EXISTS trip_alerts_cleanup"); db.execSQL( "CREATE TRIGGER trip_alerts_cleanup DELETE ON " + ObaContract.Trips.PATH + " BEGIN " + "DELETE FROM " + ObaContract.TripAlerts.PATH + " WHERE " + ObaContract.TripAlerts.TRIP_ID + " = old." + ObaContract.Trips._ID + " AND " + ObaContract.TripAlerts.STOP_ID + " = old." + ObaContract.Trips.STOP_ID + ";" + "END"); ++oldVersion; } if (oldVersion == 16) { db.execSQL( "CREATE TABLE " + ObaContract.ServiceAlerts.PATH + " (" + ObaContract.ServiceAlerts._ID + " VARCHAR PRIMARY KEY, " + ObaContract.ServiceAlerts.MARKED_READ_TIME + " INTEGER " + ");"); ++oldVersion; } if (oldVersion == 17) { db.execSQL( "CREATE TABLE " + ObaContract.Regions.PATH + " (" + ObaContract.Regions._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + ObaContract.Regions.NAME + " VARCHAR NOT NULL, " + ObaContract.Regions.OBA_BASE_URL + " VARCHAR NOT NULL, " + ObaContract.Regions.SIRI_BASE_URL + " VARCHAR NOT NULL, " + ObaContract.Regions.LANGUAGE + " VARCHAR NOT NULL, " + ObaContract.Regions.CONTACT_EMAIL + " VARCHAR NOT NULL, " + ObaContract.Regions.SUPPORTS_OBA_DISCOVERY + " INTEGER NOT NULL, " + ObaContract.Regions.SUPPORTS_OBA_REALTIME + " INTEGER NOT NULL, " + ObaContract.Regions.SUPPORTS_SIRI_REALTIME + " INTEGER NOT NULL " + ");"); db.execSQL( "CREATE TABLE " + ObaContract.RegionBounds.PATH + " (" + ObaContract.RegionBounds._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + ObaContract.RegionBounds.REGION_ID + " INTEGER NOT NULL, " + ObaContract.RegionBounds.LATITUDE + " REAL NOT NULL, " + ObaContract.RegionBounds.LONGITUDE + " REAL NOT NULL, " + ObaContract.RegionBounds.LAT_SPAN + " REAL NOT NULL, " + ObaContract.RegionBounds.LON_SPAN + " REAL NOT NULL " + ");"); db.execSQL("DROP TRIGGER IF EXISTS region_bounds_cleanup"); db.execSQL( "CREATE TRIGGER region_bounds_cleanup DELETE ON " + ObaContract.Regions.PATH + " BEGIN " + "DELETE FROM " + ObaContract.RegionBounds.PATH + " WHERE " + ObaContract.RegionBounds.REGION_ID + " = old." + ObaContract.Regions._ID + ";" + "END"); db.execSQL( "ALTER TABLE " + ObaContract.Stops.PATH + " ADD COLUMN " + ObaContract.Stops.REGION_ID + " INTEGER"); db.execSQL( "ALTER TABLE " + ObaContract.Routes.PATH + " ADD COLUMN " + ObaContract.Routes.REGION_ID + " INTEGER"); ++oldVersion; } if (oldVersion == 18) { db.execSQL( "ALTER TABLE " + ObaContract.Regions.PATH + " ADD COLUMN " + ObaContract.Regions.TWITTER_URL + " VARCHAR"); ++oldVersion; } if (oldVersion == 19) { db.execSQL( "ALTER TABLE " + ObaContract.Regions.PATH + " ADD COLUMN " + ObaContract.Regions.EXPERIMENTAL + " INTEGER"); ++oldVersion; } if (oldVersion == 20) { db.execSQL( "ALTER TABLE " + ObaContract.Regions.PATH + " ADD COLUMN " + ObaContract.Regions.STOP_INFO_URL + " VARCHAR"); ++oldVersion; } if (oldVersion == 21) { db.execSQL( "CREATE TABLE " + ObaContract.RouteHeadsignFavorites.PATH + " (" + ObaContract.RouteHeadsignFavorites.ROUTE_ID + " VARCHAR NOT NULL, " + ObaContract.RouteHeadsignFavorites.HEADSIGN + " VARCHAR NOT NULL, " + ObaContract.RouteHeadsignFavorites.STOP_ID + " VARCHAR NOT NULL, " + ObaContract.RouteHeadsignFavorites.EXCLUDE + " INTEGER NOT NULL " + ");"); ++oldVersion; } if (oldVersion == 22) { db.execSQL( "CREATE TABLE " + ObaContract.RegionOpen311Servers.PATH + " (" + ObaContract.RegionOpen311Servers._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + ObaContract.RegionOpen311Servers.REGION_ID + " INTEGER NOT NULL, " + ObaContract.RegionOpen311Servers.JURISDICTION + " VARCHAR, " + ObaContract.RegionOpen311Servers.API_KEY + " VARCHAR NOT NULL, " + ObaContract.RegionOpen311Servers.BASE_URL + " VARCHAR NOT NULL " + ");"); ++oldVersion; } if (oldVersion == 23) { db.execSQL( "ALTER TABLE " + ObaContract.Regions.PATH + " ADD COLUMN " + ObaContract.Regions.OTP_BASE_URL + " VARCHAR"); db.execSQL( "ALTER TABLE " + ObaContract.Regions.PATH + " ADD COLUMN " + ObaContract.Regions.OTP_CONTACT_EMAIL + " VARCHAR"); ++oldVersion; } if (oldVersion == 24) { db.execSQL( "ALTER TABLE " + ObaContract.ServiceAlerts.PATH + " ADD COLUMN " + ObaContract.ServiceAlerts.HIDDEN + " INTEGER"); } } @Override public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { /** * See #473 - This can happen if the user does a backup of stops/routes, updates to a * new version of the app that includes a database upgrade, and then tries to restore * the backup. This is because our current backup/restore mechanism is simply copying * the database. In this case, we should try to make sure the schema is current so no * code fails on missing tables or columns. */ Log.d(TAG, "Downgrading database from version " + oldVersion + " to " + newVersion); onUpgrade(db, newVersion, newVersion); } private void bootstrapDatabase(SQLiteDatabase db) { db.execSQL( "CREATE TABLE " + ObaContract.Stops.PATH + " (" + ObaContract.Stops._ID + " VARCHAR PRIMARY KEY, " + ObaContract.Stops.CODE + " VARCHAR NOT NULL, " + ObaContract.Stops.NAME + " VARCHAR NOT NULL, " + ObaContract.Stops.DIRECTION + " CHAR[2] NOT NULL," + ObaContract.Stops.USE_COUNT + " INTEGER NOT NULL," + ObaContract.Stops.LATITUDE + " DOUBLE NOT NULL," + ObaContract.Stops.LONGITUDE + " DOUBLE NOT NULL" + ");"); db.execSQL( "CREATE TABLE " + ObaContract.Routes.PATH + " (" + ObaContract.Routes._ID + " VARCHAR PRIMARY KEY, " + ObaContract.Routes.SHORTNAME + " VARCHAR NOT NULL, " + ObaContract.Routes.LONGNAME + " VARCHAR, " + ObaContract.Routes.USE_COUNT + " INTEGER NOT NULL" + ");"); db.execSQL( "CREATE TABLE " + ObaContract.Trips.PATH + " (" + ObaContract.Trips._ID + " VARCHAR NOT NULL, " + ObaContract.Trips.STOP_ID + " VARCHAR NOT NULL, " + ObaContract.Trips.ROUTE_ID + " VARCHAR NOT NULL, " + ObaContract.Trips.DEPARTURE + " INTEGER NOT NULL, " + ObaContract.Trips.HEADSIGN + " VARCHAR NOT NULL, " + ObaContract.Trips.NAME + " VARCHAR NOT NULL, " + ObaContract.Trips.REMINDER + " INTEGER NOT NULL, " + ObaContract.Trips.DAYS + " INTEGER NOT NULL" + ");"); } private void dropTables(SQLiteDatabase db) { db.execSQL("DROP TABLE IF EXISTS " + ObaContract.StopRouteFilters.PATH); db.execSQL("DROP TABLE IF EXISTS " + ObaContract.Routes.PATH); db.execSQL("DROP TABLE IF EXISTS " + ObaContract.Stops.PATH); db.execSQL("DROP TABLE IF EXISTS " + ObaContract.Trips.PATH); db.execSQL("DROP TABLE IF EXISTS " + ObaContract.TripAlerts.PATH); db.execSQL("DROP TABLE IF EXISTS " + ObaContract.ServiceAlerts.PATH); db.execSQL("DROP TABLE IF EXISTS " + ObaContract.Regions.PATH); db.execSQL("DROP TABLE IF EXISTS " + ObaContract.RegionBounds.PATH); db.execSQL("DROP TABLE IF EXISTS " + ObaContract.RegionOpen311Servers.PATH); db.execSQL("DROP TABLE IF EXISTS " + ObaContract.RouteHeadsignFavorites.PATH); } } private static final int STOPS = 1; private static final int STOPS_ID = 2; private static final int ROUTES = 3; private static final int ROUTES_ID = 4; private static final int TRIPS = 5; private static final int TRIPS_ID = 6; private static final int TRIP_ALERTS = 7; private static final int TRIP_ALERTS_ID = 8; private static final int STOP_ROUTE_FILTERS = 9; private static final int SERVICE_ALERTS = 10; private static final int SERVICE_ALERTS_ID = 11; private static final int REGIONS = 12; private static final int REGIONS_ID = 13; private static final int REGION_BOUNDS = 14; private static final int REGION_BOUNDS_ID = 15; private static final int ROUTE_HEADSIGN_FAVORITES = 16; private static final int REGION_OPEN311_SERVERS = 17; private static final int REGION_OPEN311_SERVERS_ID = 18; private static final UriMatcher sUriMatcher; private static final HashMap<String, String> sStopsProjectionMap; private static final HashMap<String, String> sRoutesProjectionMap; private static final HashMap<String, String> sTripsProjectionMap; private static final HashMap<String, String> sTripAlertsProjectionMap; private static final HashMap<String, String> sServiceAlertsProjectionMap; private static final HashMap<String, String> sRegionsProjectionMap; private static final HashMap<String, String> sRegionBoundsProjectionMap; private static final HashMap<String, String> sRegionOpen311ProjectionMap; // Insert helpers are useful. private DatabaseUtils.InsertHelper mStopsInserter; private DatabaseUtils.InsertHelper mRoutesInserter; private DatabaseUtils.InsertHelper mTripsInserter; private DatabaseUtils.InsertHelper mTripAlertsInserter; private DatabaseUtils.InsertHelper mFilterInserter; private DatabaseUtils.InsertHelper mServiceAlertsInserter; private DatabaseUtils.InsertHelper mRegionsInserter; private DatabaseUtils.InsertHelper mRegionBoundsInserter; private DatabaseUtils.InsertHelper mRegionOpen311ServersInserter; private DatabaseUtils.InsertHelper mRouteHeadsignFavoritesInserter; static { sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); sUriMatcher.addURI(ObaContract.AUTHORITY, ObaContract.Stops.PATH, STOPS); sUriMatcher.addURI(ObaContract.AUTHORITY, ObaContract.Stops.PATH + "/*", STOPS_ID); sUriMatcher.addURI(ObaContract.AUTHORITY, ObaContract.Routes.PATH, ROUTES); sUriMatcher.addURI(ObaContract.AUTHORITY, ObaContract.Routes.PATH + "/*", ROUTES_ID); sUriMatcher.addURI(ObaContract.AUTHORITY, ObaContract.Trips.PATH, TRIPS); sUriMatcher.addURI(ObaContract.AUTHORITY, ObaContract.Trips.PATH + "/*/*", TRIPS_ID); sUriMatcher.addURI(ObaContract.AUTHORITY, ObaContract.TripAlerts.PATH, TRIP_ALERTS); sUriMatcher .addURI(ObaContract.AUTHORITY, ObaContract.TripAlerts.PATH + "/#", TRIP_ALERTS_ID); sUriMatcher.addURI(ObaContract.AUTHORITY, ObaContract.StopRouteFilters.PATH, STOP_ROUTE_FILTERS); sUriMatcher.addURI(ObaContract.AUTHORITY, ObaContract.ServiceAlerts.PATH, SERVICE_ALERTS); sUriMatcher.addURI(ObaContract.AUTHORITY, ObaContract.ServiceAlerts.PATH + "/*", SERVICE_ALERTS_ID); sUriMatcher.addURI(ObaContract.AUTHORITY, ObaContract.Regions.PATH, REGIONS); sUriMatcher.addURI(ObaContract.AUTHORITY, ObaContract.Regions.PATH + "/#", REGIONS_ID); sUriMatcher.addURI(ObaContract.AUTHORITY, ObaContract.RegionBounds.PATH, REGION_BOUNDS); sUriMatcher.addURI(ObaContract.AUTHORITY, ObaContract.RegionBounds.PATH + "/#", REGION_BOUNDS_ID); sUriMatcher.addURI(ObaContract.AUTHORITY, ObaContract.RegionOpen311Servers.PATH, REGION_OPEN311_SERVERS); sUriMatcher.addURI(ObaContract.AUTHORITY, ObaContract.RegionOpen311Servers.PATH + "/#", REGION_OPEN311_SERVERS_ID); sUriMatcher.addURI(ObaContract.AUTHORITY, ObaContract.RouteHeadsignFavorites.PATH, ROUTE_HEADSIGN_FAVORITES); sStopsProjectionMap = new HashMap<String, String>(); sStopsProjectionMap.put(ObaContract.Stops._ID, ObaContract.Stops._ID); sStopsProjectionMap.put(ObaContract.Stops.CODE, ObaContract.Stops.CODE); sStopsProjectionMap.put(ObaContract.Stops.NAME, ObaContract.Stops.NAME); sStopsProjectionMap.put(ObaContract.Stops.DIRECTION, ObaContract.Stops.DIRECTION); sStopsProjectionMap.put(ObaContract.Stops.USE_COUNT, ObaContract.Stops.USE_COUNT); sStopsProjectionMap.put(ObaContract.Stops.LATITUDE, ObaContract.Stops.LATITUDE); sStopsProjectionMap.put(ObaContract.Stops.LONGITUDE, ObaContract.Stops.LONGITUDE); sStopsProjectionMap.put(ObaContract.Stops.USER_NAME, ObaContract.Stops.USER_NAME); sStopsProjectionMap.put(ObaContract.Stops.ACCESS_TIME, ObaContract.Stops.ACCESS_TIME); sStopsProjectionMap.put(ObaContract.Stops.FAVORITE, ObaContract.Stops.FAVORITE); sStopsProjectionMap.put(ObaContract.Stops._COUNT, "count(*)"); sStopsProjectionMap.put(ObaContract.Stops.UI_NAME, "CASE WHEN " + ObaContract.Stops.USER_NAME + " IS NOT NULL THEN " + ObaContract.Stops.USER_NAME + " ELSE " + ObaContract.Stops.NAME + " END AS " + ObaContract.Stops.UI_NAME); sRoutesProjectionMap = new HashMap<String, String>(); sRoutesProjectionMap.put(ObaContract.Routes._ID, ObaContract.Routes._ID); sRoutesProjectionMap.put(ObaContract.Routes.SHORTNAME, ObaContract.Routes.SHORTNAME); sRoutesProjectionMap.put(ObaContract.Routes.LONGNAME, ObaContract.Routes.LONGNAME); sRoutesProjectionMap.put(ObaContract.Routes.USE_COUNT, ObaContract.Routes.USE_COUNT); sRoutesProjectionMap.put(ObaContract.Routes.USER_NAME, ObaContract.Routes.USER_NAME); sRoutesProjectionMap.put(ObaContract.Routes.ACCESS_TIME, ObaContract.Routes.ACCESS_TIME); sRoutesProjectionMap.put(ObaContract.Routes.FAVORITE, ObaContract.Routes.FAVORITE); sRoutesProjectionMap.put(ObaContract.Routes.URL, ObaContract.Routes.URL); sRoutesProjectionMap.put(ObaContract.Routes._COUNT, "count(*)"); sTripsProjectionMap = new HashMap<String, String>(); sTripsProjectionMap.put(ObaContract.Trips._ID, ObaContract.Trips._ID); sTripsProjectionMap.put(ObaContract.Trips.STOP_ID, ObaContract.Trips.STOP_ID); sTripsProjectionMap.put(ObaContract.Trips.ROUTE_ID, ObaContract.Trips.ROUTE_ID); sTripsProjectionMap.put(ObaContract.Trips.DEPARTURE, ObaContract.Trips.DEPARTURE); sTripsProjectionMap.put(ObaContract.Trips.HEADSIGN, ObaContract.Trips.HEADSIGN); sTripsProjectionMap.put(ObaContract.Trips.NAME, ObaContract.Trips.NAME); sTripsProjectionMap.put(ObaContract.Trips.REMINDER, ObaContract.Trips.REMINDER); sTripsProjectionMap.put(ObaContract.Trips.DAYS, ObaContract.Trips.DAYS); sTripsProjectionMap.put(ObaContract.Trips._COUNT, "count(*)"); sTripAlertsProjectionMap = new HashMap<String, String>(); sTripAlertsProjectionMap.put(ObaContract.TripAlerts._ID, ObaContract.TripAlerts._ID); sTripAlertsProjectionMap .put(ObaContract.TripAlerts.TRIP_ID, ObaContract.TripAlerts.TRIP_ID); sTripAlertsProjectionMap .put(ObaContract.TripAlerts.STOP_ID, ObaContract.TripAlerts.STOP_ID); sTripAlertsProjectionMap .put(ObaContract.TripAlerts.START_TIME, ObaContract.TripAlerts.START_TIME); sTripAlertsProjectionMap.put(ObaContract.TripAlerts.STATE, ObaContract.TripAlerts.STATE); sTripAlertsProjectionMap.put(ObaContract.TripAlerts._COUNT, "count(*)"); sServiceAlertsProjectionMap = new HashMap<String, String>(); sServiceAlertsProjectionMap .put(ObaContract.ServiceAlerts._ID, ObaContract.ServiceAlerts._ID); sServiceAlertsProjectionMap.put(ObaContract.ServiceAlerts.MARKED_READ_TIME, ObaContract.ServiceAlerts.MARKED_READ_TIME); sServiceAlertsProjectionMap.put(ObaContract.ServiceAlerts.HIDDEN, ObaContract.ServiceAlerts.HIDDEN); sRegionsProjectionMap = new HashMap<String, String>(); sRegionsProjectionMap.put(ObaContract.Regions._ID, ObaContract.Regions._ID); sRegionsProjectionMap.put(ObaContract.Regions.NAME, ObaContract.Regions.NAME); sRegionsProjectionMap .put(ObaContract.Regions.OBA_BASE_URL, ObaContract.Regions.OBA_BASE_URL); sRegionsProjectionMap .put(ObaContract.Regions.SIRI_BASE_URL, ObaContract.Regions.SIRI_BASE_URL); sRegionsProjectionMap.put(ObaContract.Regions.LANGUAGE, ObaContract.Regions.LANGUAGE); sRegionsProjectionMap .put(ObaContract.Regions.CONTACT_EMAIL, ObaContract.Regions.CONTACT_EMAIL); sRegionsProjectionMap.put(ObaContract.Regions.SUPPORTS_OBA_DISCOVERY, ObaContract.Regions.SUPPORTS_OBA_DISCOVERY); sRegionsProjectionMap.put(ObaContract.Regions.SUPPORTS_OBA_REALTIME, ObaContract.Regions.SUPPORTS_OBA_REALTIME); sRegionsProjectionMap.put(ObaContract.Regions.SUPPORTS_SIRI_REALTIME, ObaContract.Regions.SUPPORTS_SIRI_REALTIME); sRegionsProjectionMap.put(ObaContract.Regions.TWITTER_URL, ObaContract.Regions.TWITTER_URL); sRegionsProjectionMap .put(ObaContract.Regions.EXPERIMENTAL, ObaContract.Regions.EXPERIMENTAL); sRegionsProjectionMap .put(ObaContract.Regions.STOP_INFO_URL, ObaContract.Regions.STOP_INFO_URL); sRegionsProjectionMap .put(ObaContract.Regions.OTP_BASE_URL, ObaContract.Regions.OTP_BASE_URL); sRegionsProjectionMap .put(ObaContract.Regions.OTP_CONTACT_EMAIL, ObaContract.Regions.OTP_CONTACT_EMAIL); sRegionBoundsProjectionMap = new HashMap<String, String>(); sRegionBoundsProjectionMap.put(ObaContract.RegionBounds._ID, ObaContract.RegionBounds._ID); sRegionBoundsProjectionMap .put(ObaContract.RegionBounds.REGION_ID, ObaContract.RegionBounds.REGION_ID); sRegionBoundsProjectionMap .put(ObaContract.RegionBounds.LATITUDE, ObaContract.RegionBounds.LATITUDE); sRegionBoundsProjectionMap .put(ObaContract.RegionBounds.LONGITUDE, ObaContract.RegionBounds.LONGITUDE); sRegionBoundsProjectionMap .put(ObaContract.RegionBounds.LAT_SPAN, ObaContract.RegionBounds.LAT_SPAN); sRegionBoundsProjectionMap .put(ObaContract.RegionBounds.LON_SPAN, ObaContract.RegionBounds.LON_SPAN); sRegionOpen311ProjectionMap = new HashMap<String, String>(); sRegionOpen311ProjectionMap .put(ObaContract.RegionOpen311Servers._ID, ObaContract.RegionOpen311Servers._ID); sRegionOpen311ProjectionMap .put(ObaContract.RegionOpen311Servers.REGION_ID, ObaContract.RegionOpen311Servers.REGION_ID); sRegionOpen311ProjectionMap .put(ObaContract.RegionOpen311Servers.JURISDICTION, ObaContract.RegionOpen311Servers.JURISDICTION); sRegionOpen311ProjectionMap .put(ObaContract.RegionOpen311Servers.API_KEY, ObaContract.RegionOpen311Servers.API_KEY); sRegionOpen311ProjectionMap .put(ObaContract.RegionOpen311Servers.BASE_URL, ObaContract.RegionOpen311Servers.BASE_URL); } private SQLiteDatabase mDb; private OpenHelper mOpenHelper; public static File getDatabasePath(Context context) { return context.getDatabasePath(DATABASE_NAME); } @Override public boolean onCreate() { mOpenHelper = new OpenHelper(getContext()); return true; } @Override public String getType(Uri uri) { int match = sUriMatcher.match(uri); switch (match) { case STOPS: return ObaContract.Stops.CONTENT_DIR_TYPE; case STOPS_ID: return ObaContract.Stops.CONTENT_TYPE; case ROUTES: return ObaContract.Routes.CONTENT_DIR_TYPE; case ROUTES_ID: return ObaContract.Routes.CONTENT_TYPE; case TRIPS: return ObaContract.Trips.CONTENT_DIR_TYPE; case TRIPS_ID: return ObaContract.Trips.CONTENT_TYPE; case TRIP_ALERTS: return ObaContract.TripAlerts.CONTENT_DIR_TYPE; case TRIP_ALERTS_ID: return ObaContract.TripAlerts.CONTENT_TYPE; case STOP_ROUTE_FILTERS: return ObaContract.StopRouteFilters.CONTENT_DIR_TYPE; case SERVICE_ALERTS: return ObaContract.ServiceAlerts.CONTENT_DIR_TYPE; case SERVICE_ALERTS_ID: return ObaContract.ServiceAlerts.CONTENT_TYPE; case REGIONS: return ObaContract.Regions.CONTENT_DIR_TYPE; case REGIONS_ID: return ObaContract.Regions.CONTENT_TYPE; case REGION_BOUNDS: return ObaContract.RegionBounds.CONTENT_DIR_TYPE; case REGION_BOUNDS_ID: return ObaContract.RegionBounds.CONTENT_TYPE; case REGION_OPEN311_SERVERS: return ObaContract.RegionOpen311Servers.CONTENT_DIR_TYPE; case REGION_OPEN311_SERVERS_ID: return ObaContract.RegionOpen311Servers.CONTENT_TYPE; case ROUTE_HEADSIGN_FAVORITES: return ObaContract.RouteHeadsignFavorites.CONTENT_DIR_TYPE; default: throw new IllegalArgumentException("Unknown URI: " + uri); } } @Override public Uri insert(Uri uri, ContentValues values) { final SQLiteDatabase db = getDatabase(); db.beginTransaction(); try { Uri result = insertInternal(db, uri, values); getContext().getContentResolver().notifyChange(uri, null); db.setTransactionSuccessful(); return result; } finally { db.endTransaction(); } } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { final SQLiteDatabase db = getDatabase(); return queryInternal(db, uri, projection, selection, selectionArgs, sortOrder); } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { final SQLiteDatabase db = getDatabase(); db.beginTransaction(); try { int result = updateInternal(db, uri, values, selection, selectionArgs); if (result > 0) { getContext().getContentResolver().notifyChange(uri, null); } db.setTransactionSuccessful(); return result; } finally { db.endTransaction(); } } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { final SQLiteDatabase db = getDatabase(); db.beginTransaction(); try { int result = deleteInternal(db, uri, selection, selectionArgs); if (result > 0) { getContext().getContentResolver().notifyChange(uri, null); } db.setTransactionSuccessful(); return result; } finally { db.endTransaction(); } } private Uri insertInternal(SQLiteDatabase db, Uri uri, ContentValues values) { final int match = sUriMatcher.match(uri); String id; Uri result; long longId; switch (match) { case STOPS: // Pull out the Stop ID from the values to construct the new URI // (And we'd better have a stop ID) id = values.getAsString(ObaContract.Stops._ID); if (id == null) { throw new IllegalArgumentException("Need a stop ID to insert! " + uri); } result = Uri.withAppendedPath(ObaContract.Stops.CONTENT_URI, id); mStopsInserter.insert(values); return result; case ROUTES: // Pull out the Route ID from the values to construct the new URI // (And we'd better have a route ID) id = values.getAsString(ObaContract.Routes._ID); if (id == null) { throw new IllegalArgumentException("Need a routes ID to insert! " + uri); } result = Uri.withAppendedPath(ObaContract.Routes.CONTENT_URI, id); mRoutesInserter.insert(values); return result; case TRIPS: // Pull out the Trip ID from the values to construct the new URI // (And we'd better have a trip ID) id = values.getAsString(ObaContract.Trips._ID); if (id == null) { throw new IllegalArgumentException("Need a trip ID to insert! " + uri); } result = Uri.withAppendedPath(ObaContract.Trips.CONTENT_URI, id); mTripsInserter.insert(values); return result; case TRIP_ALERTS: longId = mTripAlertsInserter.insert(values); result = ContentUris.withAppendedId(ObaContract.TripAlerts.CONTENT_URI, longId); return result; case STOP_ROUTE_FILTERS: // TODO: We should provide a "virtual" column that is an array, // so clients don't have to call the content provider for each item to insert. // Pull out the stop ID from the values to construct the new URI // (And we'd better have a stop ID) id = values.getAsString(ObaContract.StopRouteFilters.STOP_ID); if (id == null) { throw new IllegalArgumentException("Need a stop ID to insert! " + uri); } result = Uri.withAppendedPath(ObaContract.StopRouteFilters.CONTENT_URI, id); mFilterInserter.insert(values); return result; case SERVICE_ALERTS: // Pull out the Situation ID from the values to construct the new URI // (And we'd better have a situation ID) id = values.getAsString(ObaContract.ServiceAlerts._ID); if (id == null) { throw new IllegalArgumentException("Need a situation ID to insert! " + uri); } result = Uri.withAppendedPath(ObaContract.ServiceAlerts.CONTENT_URI, id); mServiceAlertsInserter.insert(values); return result; case REGIONS: longId = mRegionsInserter.insert(values); result = ContentUris.withAppendedId(ObaContract.Regions.CONTENT_URI, longId); return result; case REGION_BOUNDS: longId = mRegionBoundsInserter.insert(values); result = ContentUris.withAppendedId(ObaContract.RegionBounds.CONTENT_URI, longId); return result; case REGION_OPEN311_SERVERS: longId = mRegionOpen311ServersInserter.insert(values); result = ContentUris.withAppendedId(ObaContract.RegionOpen311Servers.CONTENT_URI, longId); return result; case ROUTE_HEADSIGN_FAVORITES: id = values.getAsString(ObaContract.RouteHeadsignFavorites.ROUTE_ID); if (id == null) { throw new IllegalArgumentException("Need a route ID to insert! " + uri); } result = Uri.withAppendedPath(ObaContract.RouteHeadsignFavorites.CONTENT_URI, id); mRouteHeadsignFavoritesInserter.insert(values); return result; // What would these mean, anyway?? case STOPS_ID: case ROUTES_ID: case TRIPS_ID: case TRIP_ALERTS_ID: case SERVICE_ALERTS_ID: case REGIONS_ID: case REGION_BOUNDS_ID: throw new UnsupportedOperationException("Cannot insert to this URI: " + uri); default: throw new IllegalArgumentException("Unknown URI: " + uri); } } private Cursor queryInternal(SQLiteDatabase db, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { final int match = sUriMatcher.match(uri); final String limit = uri.getQueryParameter("limit"); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); switch (match) { case STOPS: qb.setTables(ObaContract.Stops.PATH); qb.setProjectionMap(sStopsProjectionMap); return qb.query(mDb, projection, selection, selectionArgs, null, null, sortOrder, limit); case STOPS_ID: qb.setTables(ObaContract.Stops.PATH); qb.setProjectionMap(sStopsProjectionMap); qb.appendWhere(ObaContract.Stops._ID); qb.appendWhere("="); qb.appendWhereEscapeString(uri.getLastPathSegment()); return qb.query(mDb, projection, selection, selectionArgs, null, null, sortOrder, limit); case ROUTES: qb.setTables(ObaContract.Routes.PATH); qb.setProjectionMap(sRoutesProjectionMap); return qb.query(mDb, projection, selection, selectionArgs, null, null, sortOrder, limit); case ROUTES_ID: qb.setTables(ObaContract.Routes.PATH); qb.setProjectionMap(sRoutesProjectionMap); qb.appendWhere(ObaContract.Routes._ID); qb.appendWhere("="); qb.appendWhereEscapeString(uri.getLastPathSegment()); return qb.query(mDb, projection, selection, selectionArgs, null, null, sortOrder, limit); case TRIPS: qb.setTables(ObaContract.Trips.PATH); qb.setProjectionMap(sTripsProjectionMap); return qb.query(mDb, projection, selection, selectionArgs, null, null, sortOrder, limit); case TRIPS_ID: qb.setTables(ObaContract.Trips.PATH); qb.setProjectionMap(sTripsProjectionMap); qb.appendWhere(tripWhere(uri)); return qb.query(mDb, projection, selection, selectionArgs, null, null, sortOrder, limit); case TRIP_ALERTS: qb.setTables(ObaContract.TripAlerts.PATH); qb.setProjectionMap(sTripAlertsProjectionMap); return qb.query(mDb, projection, selection, selectionArgs, null, null, sortOrder, limit); case TRIP_ALERTS_ID: qb.setTables(ObaContract.TripAlerts.PATH); qb.setProjectionMap(sTripAlertsProjectionMap); qb.appendWhere(ObaContract.TripAlerts._ID); qb.appendWhere("="); qb.appendWhere(String.valueOf(ContentUris.parseId(uri))); return qb.query(mDb, projection, selection, selectionArgs, null, null, sortOrder, limit); case STOP_ROUTE_FILTERS: qb.setTables(ObaContract.StopRouteFilters.PATH); return qb.query(mDb, projection, selection, selectionArgs, null, null, sortOrder, limit); case SERVICE_ALERTS: qb.setTables(ObaContract.ServiceAlerts.PATH); qb.setProjectionMap(sServiceAlertsProjectionMap); return qb.query(mDb, projection, selection, selectionArgs, null, null, sortOrder, limit); case SERVICE_ALERTS_ID: qb.setTables(ObaContract.ServiceAlerts.PATH); qb.setProjectionMap(sServiceAlertsProjectionMap); qb.appendWhere(ObaContract.ServiceAlerts._ID); qb.appendWhere("="); qb.appendWhereEscapeString(uri.getLastPathSegment()); return qb.query(mDb, projection, selection, selectionArgs, null, null, sortOrder, limit); case REGIONS: qb.setTables(ObaContract.Regions.PATH); qb.setProjectionMap(sRegionsProjectionMap); return qb.query(mDb, projection, selection, selectionArgs, null, null, sortOrder, limit); case REGIONS_ID: qb.setTables(ObaContract.Regions.PATH); qb.setProjectionMap(sRegionsProjectionMap); qb.appendWhere(ObaContract.Regions._ID); qb.appendWhere("="); qb.appendWhere(String.valueOf(ContentUris.parseId(uri))); return qb.query(mDb, projection, selection, selectionArgs, null, null, sortOrder, limit); case REGION_BOUNDS: qb.setTables(ObaContract.RegionBounds.PATH); qb.setProjectionMap(sRegionBoundsProjectionMap); return qb.query(mDb, projection, selection, selectionArgs, null, null, sortOrder, limit); case REGION_BOUNDS_ID: qb.setTables(ObaContract.RegionBounds.PATH); qb.setProjectionMap(sRegionBoundsProjectionMap); qb.appendWhere(ObaContract.RegionBounds._ID); qb.appendWhere("="); qb.appendWhere(String.valueOf(ContentUris.parseId(uri))); return qb.query(mDb, projection, selection, selectionArgs, null, null, sortOrder, limit); case REGION_OPEN311_SERVERS: qb.setTables(ObaContract.RegionOpen311Servers.PATH); qb.setProjectionMap(sRegionOpen311ProjectionMap); return qb.query(mDb, projection, selection, selectionArgs, null, null, sortOrder, limit); case REGION_OPEN311_SERVERS_ID: qb.setTables(ObaContract.RegionOpen311Servers.PATH); qb.setProjectionMap(sRegionOpen311ProjectionMap); qb.appendWhere(ObaContract.RegionOpen311Servers._ID); qb.appendWhere("="); qb.appendWhere(String.valueOf(ContentUris.parseId(uri))); return qb.query(mDb, projection, selection, selectionArgs, null, null, sortOrder, limit); case ROUTE_HEADSIGN_FAVORITES: qb.setTables(ObaContract.RouteHeadsignFavorites.PATH); return qb.query(mDb, projection, selection, selectionArgs, null, null, sortOrder, limit); default: throw new IllegalArgumentException("Unknown URI: " + uri); } } private int updateInternal(SQLiteDatabase db, Uri uri, ContentValues values, String selection, String[] selectionArgs) { final int match = sUriMatcher.match(uri); switch (match) { case STOPS: return db.update(ObaContract.Stops.PATH, values, selection, selectionArgs); case STOPS_ID: return db.update(ObaContract.Stops.PATH, values, where(ObaContract.Stops._ID, uri), selectionArgs); case ROUTES: return db.update(ObaContract.Routes.PATH, values, selection, selectionArgs); case ROUTES_ID: return db.update(ObaContract.Routes.PATH, values, where(ObaContract.Routes._ID, uri), selectionArgs); case TRIPS: return db.update(ObaContract.Trips.PATH, values, selection, selectionArgs); case TRIPS_ID: return db.update(ObaContract.Trips.PATH, values, tripWhere(uri), selectionArgs); case TRIP_ALERTS: return db.update(ObaContract.TripAlerts.PATH, values, selection, selectionArgs); case TRIP_ALERTS_ID: return db.update(ObaContract.TripAlerts.PATH, values, whereLong(ObaContract.TripAlerts._ID, uri), selectionArgs); // Can we do anything here?? case STOP_ROUTE_FILTERS: return 0; case SERVICE_ALERTS: return db.update(ObaContract.ServiceAlerts.PATH, values, selection, selectionArgs); case SERVICE_ALERTS_ID: return db.update(ObaContract.ServiceAlerts.PATH, values, where(ObaContract.ServiceAlerts._ID, uri), selectionArgs); case REGIONS: return db.update(ObaContract.Regions.PATH, values, selection, selectionArgs); case REGIONS_ID: return db.update(ObaContract.Regions.PATH, values, whereLong(ObaContract.Regions._ID, uri), selectionArgs); case REGION_BOUNDS: return db.update(ObaContract.RegionBounds.PATH, values, selection, selectionArgs); case REGION_BOUNDS_ID: return db.update(ObaContract.RegionBounds.PATH, values, whereLong(ObaContract.RegionBounds._ID, uri), selectionArgs); case REGION_OPEN311_SERVERS: return db.update(ObaContract.RegionOpen311Servers.PATH, values, selection, selectionArgs); case REGION_OPEN311_SERVERS_ID: return db.update(ObaContract.RegionOpen311Servers.PATH, values, whereLong(ObaContract.RegionOpen311Servers._ID, uri), selectionArgs); case ROUTE_HEADSIGN_FAVORITES: return 0; default: throw new IllegalArgumentException("Unknown URI: " + uri); } } private int deleteInternal(SQLiteDatabase db, Uri uri, String selection, String[] selectionArgs) { final int match = sUriMatcher.match(uri); switch (match) { case STOPS: return db.delete(ObaContract.Stops.PATH, selection, selectionArgs); case STOPS_ID: return db.delete(ObaContract.Stops.PATH, where(ObaContract.Stops._ID, uri), selectionArgs); case ROUTES: return db.delete(ObaContract.Routes.PATH, selection, selectionArgs); case ROUTES_ID: return db.delete(ObaContract.Routes.PATH, where(ObaContract.Routes._ID, uri), selectionArgs); case TRIPS: return db.delete(ObaContract.Trips.PATH, selection, selectionArgs); case TRIPS_ID: return db.delete(ObaContract.Trips.PATH, tripWhere(uri), selectionArgs); case TRIP_ALERTS: return db.delete(ObaContract.TripAlerts.PATH, selection, selectionArgs); case TRIP_ALERTS_ID: return db.delete(ObaContract.TripAlerts.PATH, whereLong(ObaContract.TripAlerts._ID, uri), selectionArgs); case STOP_ROUTE_FILTERS: return db.delete(ObaContract.StopRouteFilters.PATH, selection, selectionArgs); case SERVICE_ALERTS: return db.delete(ObaContract.ServiceAlerts.PATH, selection, selectionArgs); case SERVICE_ALERTS_ID: return db.delete(ObaContract.ServiceAlerts.PATH, where(ObaContract.ServiceAlerts._ID, uri), selectionArgs); case REGIONS: return db.delete(ObaContract.Regions.PATH, selection, selectionArgs); case REGIONS_ID: return db.delete(ObaContract.Regions.PATH, whereLong(ObaContract.Regions._ID, uri), selectionArgs); case REGION_BOUNDS: return db.delete(ObaContract.RegionBounds.PATH, selection, selectionArgs); case REGION_BOUNDS_ID: return db.delete(ObaContract.RegionBounds.PATH, whereLong(ObaContract.RegionBounds._ID, uri), selectionArgs); case REGION_OPEN311_SERVERS: return db.delete(ObaContract.RegionOpen311Servers.PATH, selection, selectionArgs); case REGION_OPEN311_SERVERS_ID: return db.delete(ObaContract.RegionOpen311Servers.PATH, whereLong(ObaContract.RegionOpen311Servers._ID, uri), selectionArgs); case ROUTE_HEADSIGN_FAVORITES: return db.delete(ObaContract.RouteHeadsignFavorites.PATH, selection, selectionArgs); default: throw new IllegalArgumentException("Unknown URI: " + uri); } } private String where(String column, Uri uri) { StringBuilder sb = new StringBuilder(); sb.append(column); sb.append('='); DatabaseUtils.appendValueToSql(sb, uri.getLastPathSegment()); return sb.toString(); } private String whereLong(String column, Uri uri) { StringBuilder sb = new StringBuilder(); sb.append(column); sb.append('='); sb.append(String.valueOf(ContentUris.parseId(uri))); return sb.toString(); } private String tripWhere(Uri uri) { List<String> segments = uri.getPathSegments(); StringBuilder sb = new StringBuilder(); sb.append("("); sb.append(ObaContract.Trips._ID); sb.append("="); DatabaseUtils.appendValueToSql(sb, segments.get(1)); sb.append(" AND "); sb.append(ObaContract.Trips.STOP_ID); sb.append("="); DatabaseUtils.appendValueToSql(sb, segments.get(2)); sb.append(")"); return sb.toString(); } private SQLiteDatabase getDatabase() { if (mDb == null) { mDb = mOpenHelper.getWritableDatabase(); // Initialize the insert helpers mStopsInserter = new DatabaseUtils.InsertHelper(mDb, ObaContract.Stops.PATH); mRoutesInserter = new DatabaseUtils.InsertHelper(mDb, ObaContract.Routes.PATH); mTripsInserter = new DatabaseUtils.InsertHelper(mDb, ObaContract.Trips.PATH); mTripAlertsInserter = new DatabaseUtils.InsertHelper(mDb, ObaContract.TripAlerts.PATH); mFilterInserter = new DatabaseUtils.InsertHelper(mDb, ObaContract.StopRouteFilters.PATH); mServiceAlertsInserter = new DatabaseUtils.InsertHelper(mDb, ObaContract.ServiceAlerts.PATH); mRegionsInserter = new DatabaseUtils.InsertHelper(mDb, ObaContract.Regions.PATH); mRegionBoundsInserter = new DatabaseUtils.InsertHelper(mDb, ObaContract.RegionBounds.PATH); mRegionOpen311ServersInserter = new DatabaseUtils.InsertHelper(mDb, ObaContract.RegionOpen311Servers.PATH); mRouteHeadsignFavoritesInserter = new DatabaseUtils.InsertHelper(mDb, ObaContract.RouteHeadsignFavorites.PATH); } return mDb; } // // Closes the database // public void closeDB() { mOpenHelper.close(); mDb = null; } }