Bimba.git

ref: v1.1.0

app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
package ml.adamsprogs.bimba.models

import android.content.Context
import android.database.CursorIndexOutOfBoundsException
import android.database.sqlite.SQLiteCantOpenDatabaseException
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteDatabaseCorruptException
import java.io.File


class Timetable private constructor() {
    companion object {
        val version = 1
        val MODE_WORKDAYS = "workdays"
        val MODE_SATURDAYS = "saturdays"
        val MODE_SUNDAYS = "sundays"
        private var timetable: Timetable? = null

        fun getTimetable(context: Context? = null, force: Boolean = false): Timetable {
            if (timetable == null || force)
                if (context != null) {
                    val db: SQLiteDatabase?
                    try {
                        db = SQLiteDatabase.openDatabase(File(context.filesDir, "timetable.db").path,
                                null, SQLiteDatabase.OPEN_READONLY)
                    } catch(e: NoSuchFileException) {
                        throw SQLiteCantOpenDatabaseException("no such file")
                    } catch(e: SQLiteCantOpenDatabaseException) {
                        throw SQLiteCantOpenDatabaseException("cannot open db")
                    } catch(e: SQLiteDatabaseCorruptException) {
                        throw SQLiteCantOpenDatabaseException("db corrupt")
                    }
                    timetable = Timetable()
                    timetable!!.db = db
                    return timetable as Timetable
                } else
                    throw IllegalArgumentException("new timetable requested and no context given")
            else
                return timetable as Timetable
        }
    }

    lateinit var db: SQLiteDatabase
    private var _stops: ArrayList<StopSuggestion>? = null
    private val _stopDepartures = HashMap<String, HashMap<String, ArrayList<Departure>>>()
    private val _stopDeparturesCount = HashMap<String, Int>()

    fun refresh(context: Context) {
        val db: SQLiteDatabase?
        try {
            db = SQLiteDatabase.openDatabase(File(context.filesDir, "timetable.db").path,
                    null, SQLiteDatabase.OPEN_READONLY)
        } catch(e: NoSuchFileException) {
            throw SQLiteCantOpenDatabaseException("no such file")
        } catch(e: SQLiteCantOpenDatabaseException) {
            throw SQLiteCantOpenDatabaseException("cannot open db")
        } catch(e: SQLiteDatabaseCorruptException) {
            throw SQLiteCantOpenDatabaseException("db corrupt")
        }
        this.db = db

        for ((k, _) in _stopDepartures)
            _stopDepartures.remove(k)
        //todo recreate cache
    }

    fun getStops(): ArrayList<StopSuggestion> {
        if (_stops != null)
            return _stops!!

        val stops = ArrayList<StopSuggestion>()
        val cursor = db.rawQuery("select name ||char(10)|| headsigns as suggestion, id, stops.symbol || number as stopSymbol from stops" +
                " join nodes on(stops.symbol = nodes.symbol) order by name, id;", null)
        while (cursor.moveToNext())
            stops.add(StopSuggestion(cursor.getString(0), cursor.getString(1), cursor.getString(2)))
        cursor?.close()
        _stops = stops
        return stops
    }

    fun getStopName(stopId: String): String {
        val cursor = db.rawQuery("select name from nodes join stops on(stops.symbol = nodes.symbol) where id = ?;",
                listOf(stopId).toTypedArray())
        val name: String
        cursor.moveToNext()
        name = cursor.getString(0)
        cursor.close()
        return name
    }

    fun getStopSymbol(stopId: String): String {
        val cursor = db.rawQuery("select symbol||number from stops where id = ?", listOf(stopId).toTypedArray())
        val symbol: String
        cursor.moveToNext()
        symbol = cursor.getString(0)
        cursor.close()
        return symbol
    }

    fun getLineNumber(lineId: String): String {
        val cursor = db.rawQuery("select number from lines where id = ?", listOf(lineId).toTypedArray())
        val number: String
        cursor.moveToNext()
        number = cursor.getString(0)
        cursor.close()
        return number
    }

    fun getStopDepartures(stopId: String, lineId: String? = null, tomorrow: Boolean = false): HashMap<String, ArrayList<Departure>> {
        val andLine: String = if (lineId == null)
            ""
        else
            "and line_id = '$lineId'"

        if (lineId == null && _stopDepartures.contains(stopId)) {
            _stopDeparturesCount[stopId] = _stopDeparturesCount[stopId]!! + 1
            return _stopDepartures[stopId]!!
        }
        _stopDeparturesCount[stopId] = _stopDeparturesCount[stopId]?:0 + 1
        val cursor = db.rawQuery("select lines.number, mode, substr('0'||hour, -2) || ':' || " +
                "substr('0'||minute, -2) as time, lowFloor, modification, headsign from departures join " +
                "timetables on(timetable_id = timetables.id) join lines on(line_id = lines.id) where " +
                "stop_id = ? $andLine order by mode, time;", listOf(stopId).toTypedArray())
        val departures = HashMap<String, ArrayList<Departure>>()
        departures.put(MODE_WORKDAYS, ArrayList())
        departures.put(MODE_SATURDAYS, ArrayList())
        departures.put(MODE_SUNDAYS, ArrayList())
        while (cursor.moveToNext()) {
            departures[cursor.getString(1)]?.add(Departure(cursor.getString(0),
                    cursor.getString(1), cursor.getString(2), cursor.getInt(3) == 1,
                    cursor.getString(4), cursor.getString(5), tomorrow = tomorrow))
        }
        cursor.close()
        if (lineId == null) {
            if (_stopDepartures.size < 10)
                _stopDepartures[stopId] = departures
            else {
                for ((key, value) in _stopDeparturesCount) {
                    if (value < _stopDeparturesCount[stopId]!!) {
                        _stopDepartures.remove(key)
                        _stopDepartures[stopId] = departures
                        break
                    }
                }
            }
        }
        return departures
    }

    fun getLines(stopId: String): ArrayList<String> {
        val cursor = db.rawQuery(" select distinct line_id from timetables join " +
                "stops on(stop_id = stops.id) where stops.id = ?;",
                listOf(stopId).toTypedArray())
        val lines = ArrayList<String>()
        while (cursor.moveToNext()) {
            lines.add(cursor.getString(0))
        }
        cursor.close()
        return lines
    }

    fun getFavouriteElement(stop: String, line: String): String {
        val cursor = db.rawQuery("select name || ' (' || stops.symbol || stops.number || '): \n' " +
                "|| lines.number || ' → ' || headsign from timetables join stops on (stops.id = stop_id) " +
                "join lines on(lines.id = line_id) join nodes on(nodes.symbol = stops.symbol) where " +
                "stop_id = ? and line_id = ?",
                listOf(stop, line).toTypedArray())
        val element: String
        cursor.moveToNext()
        element = cursor.getString(0)
        cursor.close()
        return element
    }

    fun isEmpty(): Boolean {
        val cursor = db.rawQuery("select * from metadata;", null)
        try {
            cursor.moveToNext()
            cursor.getString(0)
            cursor.close()
        } catch(e: CursorIndexOutOfBoundsException) {
            return true
        }
        return false
    }

    fun getValidity(): String {
        val cursor = db.rawQuery("select value from metadata where key = 'validFrom'", null)
        cursor.moveToNext()
        val validity = cursor.getString(0)
        cursor.close()
        return "%s-%s-%s".format(validity.substring(0..3), validity.substring(4..5), validity.substring(6..7))
    }
}