15
15
You should have received a copy of the GNU General Public License
16
16
along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19
package it.reyboz.bustorino.middleware;
20
import it.reyboz.bustorino.lab.GTTSiteSucker.BusStop;
21
import it.reyboz.bustorino.lab.GTTSiteSucker.BusLine;
23
21
import android.database.Cursor;
24
import android.database.SQLException;
25
import android.provider.BaseColumns;
26
import android.util.Log;
27
22
import android.database.sqlite.SQLiteDatabase;
23
import android.database.sqlite.SQLiteException;
28
24
import android.database.sqlite.SQLiteOpenHelper;
29
import android.content.ContentValues;
30
25
import android.content.Context;
26
import android.util.Log;
32
28
import java.util.ArrayList;
37
* @author Valerio Bozzolan
29
import java.util.List;
31
import it.reyboz.bustorino.backend.Stop;
39
33
public class UserDB extends SQLiteOpenHelper {
41
// If you change the database schema, you must increment the database
43
public static final int DATABASE_VERSION = 8;
44
public static final String DATABASE_NAME = "bustorino.db";
46
public static final String COMMA = ", ";
47
public static final String DOT = ".";
48
public static final String EQUALS = " = ";
49
public static final String CREATE_TABLE_IF_NOT_EXISTS = "CREATE TABLE IF NOT EXISTS ";
50
public static final String DROP_TABLE = "DROP TABLE IF EXISTS ";
51
public static final String TEXT_TYPE = " TEXT";
52
public static final String INTEGER_TYPE = " INTEGER";
53
public static final String REAL_TYPE = " REAL ";
54
public static final String PRIMARY_KEY = " PRIMARY KEY";
55
public static final String NOT_NULL = " NOT NULL";
56
public static final String WHERE = " WHERE ";
57
public static final String AND = " AND ";
58
public static final String SELECT = "SELECT ";
59
public static final String FROM = " FROM ";
60
public static final String SELECT_ALL_FROM = "SELECT * FROM ";
61
public static final String ORDER_BY = " ORDER BY ";
62
public static final String ASC = " ASC";
63
public static final String DESC = " DESC";
65
public static String getDefault(String i) {
66
return " DEFAULT " + String.valueOf(i);
70
* @deprecated CAUSES CRASHES IN PARAMETRIC QUERIES
74
public static String somethingEqualsString(String columnName) {
75
return " " + columnName + "=\"?\"";
78
public static String somethingEqualsWithoutQuotes(String columnName) {
79
return " " + columnName + "=?";
34
public static final int DATABASE_VERSION = 1;
35
private static final String DATABASE_NAME = "user.db";
36
private final Context c; // needed during upgrade
37
private static String[] usernameColumnNameAsArray = {"username"};
82
39
public UserDB(Context context) {
83
40
super(context, DATABASE_NAME, null, DATABASE_VERSION);
86
45
public void onCreate(SQLiteDatabase db) {
87
db.execSQL(DBBusStop.SQL_CREATE_ENTRIES);
88
db.execSQL(DBBusLine.SQL_CREATE_ENTRIES);
89
db.execSQL(DBBusStopServeLine.SQL_CREATE_ENTRIES);
46
// exception intentionally left unhandled
47
db.execSQL("CREATE TABLE favorites (ID TEXT PRIMARY KEY NOT NULL, standardname TEXT, username TEXT)");
49
if(OldDB.doesItExist(this.c)) {
50
upgradeFromOldDatabase();
54
private void upgradeFromOldDatabase() {
57
old = new OldDB(this.c);
58
} catch(IllegalStateException e) {
59
// can't create database => it doesn't really exist, no matter what doesItExist() says
63
int ver = old.getOldVersion();
65
/* version 8 was the previous version, OldDB "upgrades" itself to 1337 but unless the app
66
* has crashed midway through the upgrade and the user is retrying, that should never show
67
* up here. And if it does, try to recover favorites anyway.
68
* Versions < 8 already got dropped during the update process, so let's do the same.
71
ArrayList<String> ID = new ArrayList<>();
72
ArrayList<String> name = new ArrayList<>();
73
ArrayList<String> username = new ArrayList<>();
78
Cursor c = old.getReadableDatabase().rawQuery("SELECT busstop_ID, busstop_name, busstop_username FROM busstop WHERE busstop_isfavorite = 1 ORDER BY busstop_name ASC", new String[] {});
80
int zero = c.getColumnIndex("busstop_ID");
81
int one = c.getColumnIndex("busstop_name");
82
int two = c.getColumnIndex("busstop_username");
84
while(c.moveToNext()) {
85
ID.add(c.getString(zero));
87
if(c.getString(one).length() <= 0) {
90
name.add(c.getString(one));
93
if(c.getString(two).length() <= 0) {
96
username.add(c.getString(two));
102
} catch(Exception ignored) {
103
// there's no hope, go ahead and nuke old database.
111
len2 = username.size();
118
SQLiteDatabase newdb = this.getWritableDatabase();
119
newdb.beginTransaction();
122
for (int i = 0; i < len; i++) {
123
newdb.rawQuery("INSERT INTO favorites(ID, standardname, username) VALUES (?, ?, ?)", new String[]{ID.get(i), name.get(i), username.get(i)});
125
newdb.setTransactionSuccessful();
127
newdb.endTransaction();
134
if(!OldDB.destroy(this.c)) {
135
// TODO: notify user somehow?
136
Log.e("UserDB", "Failed to delete old database, you should really uninstall and reinstall the app. Unfortunately I have no way to tell the user.");
92
141
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
93
boolean somethingTerribleIsGoingToHappen = false;
95
if(oldVersion == 6 && newVersion == 7) {
96
// Added two tables. It's OK!
99
if(oldVersion == 7 && newVersion == 8) {
100
/****** TOO MANY CHANGES. SORRY ME! THIS WILL CAUSE THE DROP OF YOUR CURRENT DB! ******/
101
somethingTerribleIsGoingToHappen = true;
104
if (oldVersion != DATABASE_VERSION || somethingTerribleIsGoingToHappen){
105
// Do you hear these cries? :'(
106
db.execSQL(DBBusStop.SQL_DROP_TABLE);
107
db.execSQL(DBBusLine.SQL_DROP_TABLE);
108
db.execSQL(DBBusStopServeLine.SQL_DROP_TABLE);
114
146
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
115
onUpgrade(db, oldVersion, newVersion);
118
public static boolean rowExists(SQLiteDatabase db, String tableName, String columnName, String columnValue) {
119
Cursor c = db.rawQuery(SELECT_ALL_FROM + tableName + WHERE + somethingEqualsWithoutQuotes(columnName), new String[]{columnValue});
120
boolean success = c.moveToFirst();
128
public static abstract class DBBusStop implements BaseColumns {
129
public static final String TABLE_NAME = "busstop";
130
public static final String COLUMN_NAME_BUSSTOP_ID = "busstop_ID";
131
public static final String COLUMN_NAME_BUSSTOP_NAME = "busstop_name";
132
public static final String COLUMN_NAME_BUSSTOP_USERNAME = "busstop_username"; // User manually edit it
133
public static final String COLUMN_NAME_BUSSTOP_LOCALITY = "busstop_locality";
134
public static final String COLUMN_NAME_BUSSTOP_LATITUDE = "busstop_latitude";
135
public static final String COLUMN_NAME_BUSSTOP_LONGITUDE = "busstop_longitude";
136
public static final String COLUMN_NAME_BUSSTOP_ISFAVORITE = "busstop_isfavorite";
138
public static final String IS_FAVORITE = "1";
139
public static final String IS_NOT_FAVORITE = "0";
141
private static final String SQL_CREATE_ENTRIES = CREATE_TABLE_IF_NOT_EXISTS
144
+ COLUMN_NAME_BUSSTOP_ID + TEXT_TYPE + PRIMARY_KEY + NOT_NULL + COMMA
145
+ COLUMN_NAME_BUSSTOP_NAME + TEXT_TYPE + NOT_NULL + COMMA
146
+ COLUMN_NAME_BUSSTOP_USERNAME + TEXT_TYPE + COMMA
147
+ COLUMN_NAME_BUSSTOP_ISFAVORITE + INTEGER_TYPE + NOT_NULL + getDefault(IS_NOT_FAVORITE) + COMMA
148
+ COLUMN_NAME_BUSSTOP_LOCALITY + TEXT_TYPE + COMMA
149
+ COLUMN_NAME_BUSSTOP_LATITUDE + REAL_TYPE + COMMA
150
+ COLUMN_NAME_BUSSTOP_LONGITUDE + REAL_TYPE
153
private static final String SQL_DROP_TABLE = DROP_TABLE + TABLE_NAME;
155
public static boolean busStopExists(SQLiteDatabase db, String busStopID) {
156
return rowExists(db, TABLE_NAME, getColumn(COLUMN_NAME_BUSSTOP_ID), busStopID);
159
public final static int FORCE_NULL_BUSSTOP_USERNAME = 1;
162
* Add a bus stop or replace the existing bus stop.
163
* It also add its bus lines!
169
* @throws SQLException
171
public static void addBusStop(SQLiteDatabase db, BusStop busStop, int forceNULL) throws SQLException {
172
ContentValues values = new ContentValues();
174
if(busStop.getBusStopName() != null) {
175
values.put(COLUMN_NAME_BUSSTOP_NAME, busStop.getBusStopName());
177
if(busStop.getBusStopUsername() != null) {
178
values.put(COLUMN_NAME_BUSSTOP_USERNAME, busStop.getBusStopUsername());
179
} else if((forceNULL & FORCE_NULL_BUSSTOP_USERNAME) != 0) {
180
values.putNull(COLUMN_NAME_BUSSTOP_USERNAME);
182
if (busStop.getBusStopLocality() != null) {
183
values.put(COLUMN_NAME_BUSSTOP_LOCALITY, busStop.getBusStopLocality());
185
if (busStop.getIsFavorite() != null) {
186
values.put(COLUMN_NAME_BUSSTOP_ISFAVORITE, busStop.getIsFavorite() ? IS_FAVORITE : IS_NOT_FAVORITE);
188
if(busStop.getLatitude() != null && busStop.getLongitude() != null) {
189
values.put(COLUMN_NAME_BUSSTOP_LATITUDE, busStop.getLatitude());
190
values.put(COLUMN_NAME_BUSSTOP_LONGITUDE, busStop.getLongitude());
193
if(busStopExists(db, busStop.getBusStopID())) {
194
db.update(TABLE_NAME, values, somethingEqualsWithoutQuotes(COLUMN_NAME_BUSSTOP_ID), new String[]{busStop.getBusStopID()});
196
Log.d("MyDB", "busStop with busStopID " + busStop.getBusStopID() + " updated");
198
values.put(COLUMN_NAME_BUSSTOP_ID, busStop.getBusStopID());
200
long lastInserted = db.insert(TABLE_NAME, null,
203
Log.d("MyDB", "busStop with busStopID " + busStop.getBusStopID() + " inserted as " + lastInserted);
207
if(busStop.getBusLines() != null) {
208
for (BusLine busLine : busStop.getBusLines()) {
209
DBBusLine.addBusLine(db, busLine);
210
DBBusStopServeLine.addBusStopServeLine(db, busStop.getBusStopID(), busLine.getBusLineID());
215
public static void addBusStop(SQLiteDatabase db, BusStop busStop) throws SQLException {
216
addBusStop(db, busStop, 0);
219
public static BusStop[] getFavoriteBusStops(SQLiteDatabase db) {
220
ArrayList<BusStop> busStops = new ArrayList<BusStop>();
222
String query = UserDB.SELECT_ALL_FROM
225
+ somethingEqualsWithoutQuotes(COLUMN_NAME_BUSSTOP_ISFAVORITE)
226
+ ORDER_BY + COLUMN_NAME_BUSSTOP_NAME + ASC;
228
Cursor c = db.rawQuery(query, new String[]{DBBusStop.IS_FAVORITE});
230
while (c.moveToNext()) {
231
String busStopID = c.getString(c.getColumnIndex(COLUMN_NAME_BUSSTOP_ID));
232
String busStopName = c.getString(c.getColumnIndex(COLUMN_NAME_BUSSTOP_NAME));
233
String busStopUsername = c.getString(c.getColumnIndex(COLUMN_NAME_BUSSTOP_USERNAME));
234
String busStopLocality = c.getString(c.getColumnIndex(COLUMN_NAME_BUSSTOP_LOCALITY));
236
BusLine[] busLines = DBBusStopServeLine.getBusLinesServedByBusStopID(db, busStopID);
238
BusStop busStop = new BusStop(busStopID, busStopName, busLines);
239
busStop.setBusStopUsername(busStopUsername);
240
busStop.setBusStopLocality(busStopLocality);
242
busStops.add(busStop);
246
return busStops.toArray(new BusStop[busStops.size()]);
249
public static BusStop getBusStop(SQLiteDatabase db, String searchBusStopID) {
250
BusStop busStop = null;
252
String query = UserDB.SELECT_ALL_FROM
255
+ somethingEqualsWithoutQuotes(COLUMN_NAME_BUSSTOP_ID);
257
Cursor c = db.rawQuery(query, new String[]{searchBusStopID});
259
while (c.moveToNext()) {
260
String busStopID = c.getString(c.getColumnIndex(COLUMN_NAME_BUSSTOP_ID));
261
String busStopName = c.getString(c.getColumnIndex(COLUMN_NAME_BUSSTOP_NAME));
262
String busStopUsername = c.getString(c.getColumnIndex(COLUMN_NAME_BUSSTOP_USERNAME));
263
String busStopLocality = c.getString(c.getColumnIndex(COLUMN_NAME_BUSSTOP_LOCALITY));
265
BusLine[] busLines = DBBusStopServeLine.getBusLinesServedByBusStopID(db, busStopID);
267
busStop = new BusStop(busStopID, busStopName, busLines);
268
busStop.setBusStopUsername(busStopUsername);
269
busStop.setBusStopLocality(busStopLocality);
276
public static String getColumn(String columnName) {
277
return TABLE_NAME + DOT + columnName;
284
public static abstract class DBBusLine implements BaseColumns {
285
public static final String TABLE_NAME = "busline";
286
public static final String COLUMN_NAME_BUSLINE_ID = "busline_ID";
287
public static final String COLUMN_NAME_BUSLINE_NAME = "busline_name";
288
public static final String COLUMN_NAME_BUSLINE_USERNAME = "busline_username";
289
public static final String COLUMN_NAME_BUSLINE_TYPE = "busline_type";
291
private static final String SQL_CREATE_ENTRIES = CREATE_TABLE_IF_NOT_EXISTS
294
+ COLUMN_NAME_BUSLINE_ID + TEXT_TYPE + PRIMARY_KEY + NOT_NULL + COMMA
295
+ COLUMN_NAME_BUSLINE_NAME + TEXT_TYPE + NOT_NULL + COMMA
296
+ COLUMN_NAME_BUSLINE_USERNAME + TEXT_TYPE + COMMA
297
+ COLUMN_NAME_BUSLINE_TYPE + INTEGER_TYPE
300
private static final String SQL_DROP_TABLE = DROP_TABLE + TABLE_NAME;
302
public static String getColumn(String columnName) {
303
return TABLE_NAME + DOT + columnName;
306
public static boolean busLineExists(SQLiteDatabase db, Integer busLineID) {
307
return rowExists(db, TABLE_NAME, getColumn(COLUMN_NAME_BUSLINE_ID), busLineID.toString());
310
public static void addBusLine(SQLiteDatabase db, BusLine busLine) throws SQLException {
311
ContentValues values = new ContentValues();
313
values.put(COLUMN_NAME_BUSLINE_NAME, busLine.getBusLineName());
315
if(busLine.getBusLineUsername() != null) {
316
values.put(COLUMN_NAME_BUSLINE_USERNAME, busLine.getBusLineUsername());
318
if(busLine.getBusLineType() != null) {
319
values.put(COLUMN_NAME_BUSLINE_TYPE, busLine.getBusLineType());
322
if(busLineExists(db, busLine.getBusLineID())) {
323
db.update(TABLE_NAME, values, somethingEqualsWithoutQuotes(COLUMN_NAME_BUSLINE_ID), new String[] { String.valueOf(busLine.getBusLineID()) });
325
Log.d("MyDB", "BusLine with busLineID " + busLine.getBusLineID() + " updated");
327
values.put(COLUMN_NAME_BUSLINE_ID, busLine.getBusLineID());
329
long lastInserted = db.insert(TABLE_NAME, null, values);
330
Log.d("MyDB", "BusLine with busLineID " + busLine.getBusLineID() + " inserted as " + lastInserted);
336
* Associate every bus stop on every bus line served
338
public static abstract class DBBusStopServeLine {
339
public static final String TABLE_NAME = "busstopserveline";
340
public static final String COLUMN_NAME_BUSSTOP_ID = "busstop_ID";
341
public static final String COLUMN_NAME_BUSLINE_ID = "busline_ID";
343
private static final String SQL_CREATE_ENTRIES = CREATE_TABLE_IF_NOT_EXISTS
346
+ COLUMN_NAME_BUSSTOP_ID
350
+ COLUMN_NAME_BUSLINE_ID
356
+ COLUMN_NAME_BUSSTOP_ID
358
+ COLUMN_NAME_BUSLINE_ID
359
+ "))"; // Add primary key on two columns
361
private static final String SQL_DROP_TABLE = DROP_TABLE + TABLE_NAME;
364
* Serve a bus stop by a bus line (if it don't exists)
369
public static void addBusStopServeLine(SQLiteDatabase db,
370
String busStopID, Integer busLineID) {
372
String queryExists = SELECT
373
+ getColumn(COLUMN_NAME_BUSSTOP_ID) + COMMA
374
+ getColumn(COLUMN_NAME_BUSLINE_ID)
378
+ somethingEqualsWithoutQuotes(COLUMN_NAME_BUSSTOP_ID)
380
+ somethingEqualsWithoutQuotes(COLUMN_NAME_BUSLINE_ID);
381
Cursor cursor = db.rawQuery(queryExists,
382
new String[] { String.valueOf(busStopID), String.valueOf(busLineID) });
384
boolean exists = cursor.moveToNext();
388
Log.d("MyDB", "busStopServeLine relation between busStopID " + busStopID + " and busLineID " + busLineID + " already inserted");
390
ContentValues values = new ContentValues();
391
values.put(COLUMN_NAME_BUSSTOP_ID, busStopID);
392
values.put(COLUMN_NAME_BUSLINE_ID, busLineID);
393
long inserted = db.insert(TABLE_NAME, null, values);
394
Log.d("MyDB", "busStopServeLine relation between busStopID " + busStopID + " and busLineID " + busLineID + " inserted as " + inserted);
398
public static String getColumn(String columnName) {
399
return TABLE_NAME + DOT + columnName;
403
* Get bus lines server by a bus stop.
408
public static BusLine[] getBusLinesServedByBusStopID(SQLiteDatabase db, String busStopID) {
409
ArrayList<BusLine> busLines = new ArrayList<BusLine>();
411
String queryLines = SELECT
412
+ DBBusLine.getColumn(DBBusLine.COLUMN_NAME_BUSLINE_ID) + COMMA
413
+ DBBusLine.getColumn(DBBusLine.COLUMN_NAME_BUSLINE_NAME)
416
+ DBBusLine.TABLE_NAME
418
+ somethingEqualsWithoutQuotes(getColumn(COLUMN_NAME_BUSSTOP_ID))
420
+ getColumn(COLUMN_NAME_BUSLINE_ID)
422
+ DBBusLine.getColumn(DBBusLine.COLUMN_NAME_BUSLINE_ID)
424
+ DBBusLine.getColumn(DBBusLine.COLUMN_NAME_BUSLINE_NAME)
427
Cursor cursor = db.rawQuery(queryLines,
428
new String[] { String.valueOf(busStopID) });
430
while (cursor.moveToNext()) {
431
Integer busLineID = cursor.getInt(0);
432
String busLineName = cursor.getString(1);
433
busLines.add(new BusLine(busLineID, busLineName));
437
return busLines.toArray(new BusLine[busLines.size()]);
151
* Gets stop name set by the user.
153
* @param db readable database
154
* @param stopID stop ID
155
* @return name set by user, or null if not set\not found
157
public static String getStopUserName(SQLiteDatabase db, String stopID) {
158
String username = null;
161
Cursor c = db.query("favorites", usernameColumnNameAsArray, "ID = ?", new String[] {stopID}, null, null, null, null);
164
username = c.getString(c.getColumnIndex("username"));
167
} catch(SQLiteException ignored) {}
172
public static List<Stop> getFavorites(SQLiteDatabase db) {
173
List<Stop> l = new ArrayList<>();
176
l.add(new Stop("TEST asd", "123", null, null, null));
177
l.add(new Stop("TEST lel", "456", null, null, null));
178
l.add(new Stop("asdasdasd asdsaad", "67", null, null, null));
183
public boolean addOrUpdate(Stop s, SQLiteDatabase db) {
187
username = getStopUserName(db, s.ID);
188
String[] arr = {s.ID, s.getStopName(), username};
192
db.rawQuery("INSERT OR IGNORE INTO favorites(ID, standardname, username) VALUES (?, ?, ?)", arr).close();
193
db.rawQuery("UPDATE favorites SET standardname = ? WHERE ID = ?", new String[]{arr[1], arr[0]}).close();
194
} catch(SQLiteException e) {