Author: Adam Pioterek <adam.pioterek@protonmail.ch>
refactoring (string constants and static functions) & fixed vm when timetable is full mode
%!v(PANIC=String method: strings: negative Repeat count)
diff --git a/app/src/main/java/ml/adamsprogs/bimba/MessageReceiver.kt b/app/src/main/java/ml/adamsprogs/bimba/MessageReceiver.kt index bc53ddb2579afcedda4f27b10e3ba572a7c31ec5..74c0a57f9d4a927528767567f96afe77bb248194 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/MessageReceiver.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/MessageReceiver.kt @@ -4,29 +4,27 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import ml.adamsprogs.bimba.models.Departure -import ml.adamsprogs.bimba.models.fromString -class MessageReceiver: BroadcastReceiver() { +class MessageReceiver : BroadcastReceiver() { val onTimetableDownloadListeners: HashSet<OnTimetableDownloadListener> = HashSet() val onVmListeners: HashSet<OnVmListener> = HashSet() override fun onReceive(context: Context?, intent: Intent?) { - if (intent?.action == "ml.adamsprogs.bimba.timetableDownloaded") { - val result = intent.getStringExtra("result") + if (intent?.action == TimetableDownloader.ACTION_DOWNLOADED) { + val result = intent.getStringExtra(TimetableDownloader.EXTRA_RESULT) for (listener in onTimetableDownloadListeners) { listener.onTimetableDownload(result) } } - if (intent?.action == "ml.adamsprogs.bimba.departuresCreated") { - val workdays = intent.getStringArrayListExtra("workdays").map { fromString(it)} as ArrayList<Departure> - val saturdays = intent.getStringArrayListExtra("saturdays").map { fromString(it)} as ArrayList<Departure> - val sundays = intent.getStringArrayListExtra("sundays").map { fromString(it)} as ArrayList<Departure> - val departures = HashMap<String, ArrayList<Departure>>() - departures["workdays"] = workdays - departures["saturdays"] = saturdays - departures["sundays"] = sundays + if (intent?.action == VmClient.ACTION_DEPARTURES_CREATED) { + val departures = intent.getStringArrayListExtra(VmClient.EXTRA_DEPARTURES).map { Departure.fromString(it) } as ArrayList<Departure> for (listener in onVmListeners) { listener.onVm(departures) + } + } + if (intent?.action == VmClient.ACTION_NO_DEPARTURES) { + for (listener in onVmListeners) { + listener.onVm(null) } } } @@ -52,6 +50,6 @@ fun onTimetableDownload(result: String?) } interface OnVmListener { - fun onVm(departures: HashMap<String, ArrayList<Departure>>) + fun onVm(vmDepartures: ArrayList<Departure>?) } } \ No newline at end of file diff --git a/app/src/main/java/ml/adamsprogs/bimba/NetworkStateReceiver.kt b/app/src/main/java/ml/adamsprogs/bimba/NetworkStateReceiver.kt index cd17f1cdc5ed38f6452844e51000d72fdc21e380..71ef786ec8e6cfec1495edf62cdbca6f904f51dd 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/NetworkStateReceiver.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/NetworkStateReceiver.kt @@ -5,12 +5,6 @@ import android.content.Intent import android.content.BroadcastReceiver import android.content.Context -fun isNetworkAvailable(context: Context): Boolean { - val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - val activeNetworkInfo = connectivityManager.activeNetworkInfo - return activeNetworkInfo != null && activeNetworkInfo.isConnected -} - class NetworkStateReceiver : BroadcastReceiver() { val onConnectivityChangeListeners = HashSet<OnConnectivityChangeListener>() @@ -40,5 +34,13 @@ } interface OnConnectivityChangeListener { fun onConnectivityChange(connected: Boolean) + } + + companion object { + fun isNetworkAvailable(context: Context): Boolean { + val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + val activeNetworkInfo = connectivityManager.activeNetworkInfo + return activeNetworkInfo != null && activeNetworkInfo.isConnected + } } } \ No newline at end of file diff --git a/app/src/main/java/ml/adamsprogs/bimba/TimetableDownloader.kt b/app/src/main/java/ml/adamsprogs/bimba/TimetableDownloader.kt index 39d9ee191aac7f3939939acebd03bcc59e93413e..ac1a62fa93168ced7d984c0ba40a2c68bea52fc4 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/TimetableDownloader.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/TimetableDownloader.kt @@ -12,9 +12,20 @@ import java.security.MessageDigest import kotlin.experimental.and import android.util.Log import android.app.NotificationManager +import ml.adamsprogs.bimba.models.Timetable class TimetableDownloader : IntentService("TimetableDownloader") { + companion object { + val ACTION_DOWNLOADED = "ml.adamsprogs.bimba.timetableDownloaded" + val EXTRA_FORCE = "force" + val EXTRA_RESULT = "result" + val RESULT_NO_CONNECTIVITY = "no connectivity" + val RESULT_VERSION_MISMATCH = "version mismatch" + val RESULT_UP_TO_DATE = "up-to-date" + val RESULT_DOWNLOADED = "downloaded" + val RESULT_VALIDITY_FAILED = "validity failed" + } lateinit var notificationManager: NotificationManager var size: Int = 0 @@ -23,28 +34,30 @@ if (intent != null) { notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val prefs = this.getSharedPreferences("ml.adamsprogs.bimba.prefs", Context.MODE_PRIVATE)!! - if (!isNetworkAvailable(this)) { - sendResult("no connectivity") + if (!NetworkStateReceiver.isNetworkAvailable(this)) { + sendResult(RESULT_NO_CONNECTIVITY) return } val metadataUrl = URL("https://adamsprogs.ml/w/_media/programmes/bimba/timetable.db.meta") var httpCon = metadataUrl.openConnection() as HttpURLConnection - if (httpCon.responseCode != HttpURLConnection.HTTP_OK) - throw Exception("Failed to connect") + if (httpCon.responseCode != HttpURLConnection.HTTP_OK){ + sendResult(RESULT_NO_CONNECTIVITY) + return + } Log.i("Downloader", "Got metadata") val reader = BufferedReader(InputStreamReader(httpCon.inputStream)) val lastModified = reader.readLine() val checksum = reader.readLine() size = Integer.parseInt(reader.readLine()) / 1024 val dbVersion = reader.readLine() - if (Integer.parseInt(dbVersion.split(".")[0]) > 1) { //todo version to const - sendResult("version mismatch") + if (Integer.parseInt(dbVersion.split(".")[0]) > Timetable.version) { + sendResult(RESULT_VERSION_MISMATCH) return } val dbFilename = reader.readLine() val currentLastModified = prefs.getString("timetableLastModified", "19791012") - if (lastModified <= currentLastModified && !intent.getBooleanExtra("force", false)) { - sendResult("up-to-date") + if (lastModified <= currentLastModified && !intent.getBooleanExtra(EXTRA_FORCE, false)) { + sendResult(RESULT_UP_TO_DATE) return } Log.i("Downloader", "timetable is newer ($lastModified > $currentLastModified)") @@ -53,8 +66,10 @@ notify(0) val xzDbUrl = URL("https://adamsprogs.ml/w/_media/programmes/bimba/$dbFilename") httpCon = xzDbUrl.openConnection() as HttpURLConnection - if (httpCon.responseCode != HttpURLConnection.HTTP_OK) - throw Exception("Failed to connect") + if (httpCon.responseCode != HttpURLConnection.HTTP_OK){ + sendResult(RESULT_NO_CONNECTIVITY) + return + } Log.i("Downloader", "connected to db") val xzIn = XZInputStream(httpCon.inputStream) val file = File(this.filesDir, "new_timetable.db") @@ -66,10 +81,10 @@ file.renameTo(oldFile) val prefsEditor = prefs.edit() prefsEditor.putString("timetableLastModified", lastModified) prefsEditor.apply() - sendResult("downloaded") + sendResult(RESULT_DOWNLOADED) } else { Log.i("Downloader", "downloaded but is wrong") - sendResult("validity failed") + sendResult(RESULT_VALIDITY_FAILED) } cancelNotification() @@ -78,9 +93,9 @@ } private fun sendResult(result: String) { val broadcastIntent = Intent() - broadcastIntent.action = "ml.adamsprogs.bimba.timetableDownloaded" + broadcastIntent.action = ACTION_DOWNLOADED broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT) - broadcastIntent.putExtra("result", result) + broadcastIntent.putExtra(EXTRA_RESULT, result) sendBroadcast(broadcastIntent) } diff --git a/app/src/main/java/ml/adamsprogs/bimba/VmClient.kt b/app/src/main/java/ml/adamsprogs/bimba/VmClient.kt index 77a60772f12835221c39d198b292738d3f4ffe00..86ab7c5b3c2becd0e5031c1af21e2930be46a83d 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/VmClient.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/VmClient.kt @@ -10,47 +10,50 @@ import java.util.* class VmClient : IntentService("VmClient") { + companion object { + val ACTION_DEPARTURES_CREATED = "ml.adamsprogs.bimba.departuresCreated" + val ACTION_NO_DEPARTURES = "ml.adamsprogs.bimba.noVM" + val EXTRA_STOP_SYMBOL = "stopSymbol" + val EXTRA_LINE_NUMBER = "lineNumber" + val EXTRA_DEPARTURES = "departures" + } override fun onHandleIntent(intent: Intent?) { if (intent != null) { - val stopId = intent.getStringExtra("stopId") - if (!isNetworkAvailable(this)) { - sendResult(createDepartures(stopId)) - } else { - val stopSymbol = intent.getStringExtra("stopSymbol") - val departures = createDepartures(stopId) + if (!NetworkStateReceiver.isNetworkAvailable(this)) { + sendNullResult() + return + } + + val stopSymbol = intent.getStringExtra(EXTRA_STOP_SYMBOL) + val lineNumber = intent.getStringExtra(EXTRA_LINE_NUMBER) - val client = OkHttpClient() - val url = "http://www.peka.poznan.pl/vm/method.vm?ts=${Calendar.getInstance().timeInMillis}" - val formBody = FormBody.Builder() - .add("method", "getTimes") - .add("p0", "{\"symbol\": \"$stopSymbol\"}") - .build() - val request = Request.Builder() - .url(url) - .post(formBody) - .build() - val responseBody : String? - try { - responseBody = client.newCall(request).execute().body()?.string() - } catch(e: IOException) { - sendResult(departures) - return - } - val javaRootMapObject = Gson().fromJson(responseBody, HashMap::class.java) - val times = (javaRootMapObject["success"] as Map<*, *>)["times"] as List<*> - val date = Calendar.getInstance() - val today = date.get(Calendar.DAY_OF_WEEK) - val todayDay = "${date.get(Calendar.DATE)}" - val todayMode: String - when (today) { - Calendar.SATURDAY -> todayMode = "saturdays" - Calendar.SUNDAY -> todayMode = "sundays" - else -> todayMode = "workdays" - } - val departuresToday = ArrayList<Departure>() - for (time in times) { - val t = time as Map<*, *> + val client = OkHttpClient() + val url = "http://www.peka.poznan.pl/vm/method.vm?ts=${Calendar.getInstance().timeInMillis}" + val formBody = FormBody.Builder() + .add("method", "getTimes") + .add("p0", "{\"symbol\": \"$stopSymbol\"}") + .build() + val request = Request.Builder() + .url(url) + .post(formBody) + .build() + val responseBody: String? + try { + responseBody = client.newCall(request).execute().body()?.string() + } catch(e: IOException) { + sendNullResult() + return + } + val javaRootMapObject = Gson().fromJson(responseBody, HashMap::class.java) + val times = (javaRootMapObject["success"] as Map<*, *>)["times"] as List<*> + val date = Calendar.getInstance() + val todayDay = "${date.get(Calendar.DATE)}" + val todayMode = date.getMode() + val departuresToday = ArrayList<Departure>() + for (time in times) { + val t = time as Map<*, *> + if (lineNumber == null || t["line"] == lineNumber) { val departureDay = (t["departure"] as String).split("T")[0].split("-")[2] val departureTimeRaw = (t["departure"] as String).split("T")[1].split(":") @@ -60,19 +63,27 @@ null, t["direction"] as String, t["realTime"] as Boolean, departureDay != todayDay, t["onStopPoint"] as Boolean) departuresToday.add(departure) } - departures[todayMode] = departuresToday - sendResult(departures) } + if (departuresToday.isEmpty()) + sendNullResult() + else + sendResult(departuresToday) + } } - private fun sendResult(departures: HashMap<String, ArrayList<Departure>>) { + private fun sendNullResult() { val broadcastIntent = Intent() - broadcastIntent.action = "ml.adamsprogs.bimba.departuresCreated" + broadcastIntent.action = ACTION_NO_DEPARTURES broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT) - broadcastIntent.putStringArrayListExtra("workdays", departures["workdays"]?.map { it.toString() } as java.util.ArrayList<String>) - broadcastIntent.putStringArrayListExtra("saturdays", departures["saturdays"]?.map { it.toString() } as java.util.ArrayList<String>) - broadcastIntent.putStringArrayListExtra("sundays", departures["sundays"]?.map { it.toString() } as java.util.ArrayList<String>) + sendBroadcast(broadcastIntent) + } + + private fun sendResult(departures: ArrayList<Departure>) { + val broadcastIntent = Intent() + broadcastIntent.action = ACTION_DEPARTURES_CREATED + broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT) + broadcastIntent.putStringArrayListExtra(EXTRA_DEPARTURES, departures.map { it.toString() } as java.util.ArrayList<String>) sendBroadcast(broadcastIntent) } } diff --git a/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt b/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt index 04934cf7346636bed7760e4df87a249705ee3187..b58059fb156c426130e6b4e0b23fb5d85119effe 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt @@ -85,8 +85,8 @@ } imm.hideSoftInputFromWindow(view.windowToken, 0) intent = Intent(context, StopActivity::class.java) searchSuggestion as StopSuggestion - intent.putExtra("stopId", searchSuggestion.id) - intent.putExtra("stopSymbol", searchSuggestion.symbol) + intent.putExtra(StopActivity.EXTRA_STOP_ID, searchSuggestion.id) + intent.putExtra(StopActivity.EXTRA_STOP_SYMBOL, searchSuggestion.symbol) startActivity(intent) } @@ -141,7 +141,7 @@ stops = timetable.getStops() } private fun prepareOnDownloadListener() { - val filter = IntentFilter("ml.adamsprogs.bimba.timetableDownloaded") + val filter = IntentFilter(TimetableDownloader.ACTION_DOWNLOADED) filter.addCategory(Intent.CATEGORY_DEFAULT) registerReceiver(receiver, filter) receiver.addOnTimetableDownloadListener(context as MessageReceiver.OnTimetableDownloadListener) @@ -205,10 +205,10 @@ override fun onTimetableDownload(result: String?) { Log.i("Refresh", "downloaded: $result") val message: String when (result) { - "downloaded" -> message = getString(R.string.timetable_downloaded) - "no connectivity" -> message = getString(R.string.no_connectivity) - "up-to-date" -> message = getString(R.string.timetable_up_to_date) - "validity failed" -> message = getString(R.string.validity_failed) + TimetableDownloader.RESULT_DOWNLOADED -> message = getString(R.string.timetable_downloaded) + TimetableDownloader.RESULT_NO_CONNECTIVITY -> message = getString(R.string.no_connectivity) + TimetableDownloader.RESULT_UP_TO_DATE -> message = getString(R.string.timetable_up_to_date) + TimetableDownloader.RESULT_VALIDITY_FAILED -> message = getString(R.string.validity_failed) else -> message = getString(R.string.error_try_later) } timetable.refresh() @@ -218,7 +218,7 @@ } override fun edit(name: String): Boolean { val intent = Intent(this, EditFavouriteActivity::class.java) - intent.putExtra("favourite", favourites.favourites[name]) + intent.putExtra(EditFavouriteActivity.EXTRA_FAVOURITE, favourites.favourites[name]) startActivity(intent) (favouritesList.adapter as FavouritesAdapter).favourites = favourites.favouritesList favouritesList.adapter.notifyDataSetChanged() diff --git a/app/src/main/java/ml/adamsprogs/bimba/activities/EditFavouriteActivity.kt b/app/src/main/java/ml/adamsprogs/bimba/activities/EditFavouriteActivity.kt index 9a044a18bb4329a09eca5bbf1002070d68f604b5..e8d0b94f23b4029ad6379ebd7e0b6f016aec1293 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/activities/EditFavouriteActivity.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/activities/EditFavouriteActivity.kt @@ -13,6 +13,9 @@ import ml.adamsprogs.bimba.models.FavouriteEditRowAdapter import ml.adamsprogs.bimba.models.FavouriteStorage class EditFavouriteActivity : AppCompatActivity() { + companion object { + val EXTRA_FAVOURITE = "favourite" + } lateinit var favourites: FavouriteStorage lateinit var nameEdit: EditText @@ -22,7 +25,7 @@ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_edit_favourite) - favourite = intent.getParcelableExtra<Favourite>("favourite") + favourite = intent.getParcelableExtra<Favourite>(EXTRA_FAVOURITE) if (favourite == null) finish() favourites = FavouriteStorage.getFavouriteStorage(this) diff --git a/app/src/main/java/ml/adamsprogs/bimba/activities/NoDbActivity.kt b/app/src/main/java/ml/adamsprogs/bimba/activities/NoDbActivity.kt index 84182ce84b7908f81cd6836fdadcd6745b47f68b..b35bf71c9670673bb7ec6a368874560476b3fac0 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/activities/NoDbActivity.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/activities/NoDbActivity.kt @@ -17,12 +17,12 @@ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_nodb) - var filter: IntentFilter = IntentFilter("ml.adamsprogs.bimba.timetableDownloaded") + var filter: IntentFilter = IntentFilter(TimetableDownloader.ACTION_DOWNLOADED) filter.addCategory(Intent.CATEGORY_DEFAULT) registerReceiver(timetableDownloadReceiver, filter) timetableDownloadReceiver.addOnTimetableDownloadListener(this) - if (!isNetworkAvailable(this)) { + if (!NetworkStateReceiver.isNetworkAvailable(this)) { askedForNetwork = true (findViewById(R.id.noDbCaption) as TextView).text = getString(R.string.no_db_connect) filter = IntentFilter("android.net.conn.CONNECTIVITY_CHANGE") @@ -34,10 +34,10 @@ } override fun onResume() { super.onResume() - var filter: IntentFilter = IntentFilter("ml.adamsprogs.bimba.timetableDownloaded") + var filter: IntentFilter = IntentFilter(TimetableDownloader.ACTION_DOWNLOADED) filter.addCategory(Intent.CATEGORY_DEFAULT) registerReceiver(timetableDownloadReceiver, filter) - if (!isNetworkAvailable(this)) { + if (!NetworkStateReceiver.isNetworkAvailable(this)) { askedForNetwork = true (findViewById(R.id.noDbCaption) as TextView).text = getString(R.string.no_db_connect) filter = IntentFilter("android.net.conn.CONNECTIVITY_CHANGE") @@ -51,7 +51,7 @@ fun downloadTimetable() { (findViewById(R.id.noDbCaption) as TextView).text = getString(R.string.no_db_downloading) serviceRunning = true intent = Intent(this, TimetableDownloader::class.java) - intent.putExtra("force", true) + intent.putExtra(TimetableDownloader.EXTRA_FORCE, true) startService(intent) } @@ -64,7 +64,7 @@ } override fun onTimetableDownload(result: String?) { when (result) { - "downloaded" -> { + TimetableDownloader.RESULT_DOWNLOADED -> { timetableDownloadReceiver.removeOnTimetableDownloadListener(this) networkStateReceiver.removeOnConnectivityChangeListener(this) startActivity(Intent(this, DashActivity::class.java)) diff --git a/app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt b/app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt index c9ff27e2028878e4a1d07fdc8da4fbf07b8cb0c8..3f1c27bd741334890ffc27d29a3015314e7d8b7c 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt @@ -12,13 +12,17 @@ import android.support.v7.widget.* import android.support.v4.app.* import android.support.v4.view.* import android.support.v4.content.res.ResourcesCompat -import android.util.Log import ml.adamsprogs.bimba.models.* import ml.adamsprogs.bimba.* class StopActivity : AppCompatActivity(), MessageReceiver.OnVmListener { + + companion object { + val EXTRA_STOP_ID = "stopId" + val EXTRA_STOP_SYMBOL = "stopSymbol" + } private lateinit var stopId: String private lateinit var stopSymbol: String @@ -32,18 +36,15 @@ private var timer = Timer() private lateinit var timerTask: TimerTask private val context = this private val receiver = MessageReceiver() - private lateinit var sharedPreferences: SharedPreferences override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_stop) - stopId = intent.getStringExtra("stopId") - stopSymbol = intent.getStringExtra("stopSymbol") + stopId = intent.getStringExtra(EXTRA_STOP_ID) + stopSymbol = intent.getStringExtra(EXTRA_STOP_SYMBOL) val toolbar = findViewById(R.id.toolbar) as Toolbar setSupportActionBar(toolbar) - - sharedPreferences = this.getSharedPreferences("ml.adamsprogs.bimba.prefs", Context.MODE_PRIVATE) createTimerTask() @@ -55,7 +56,7 @@ viewPager = findViewById(R.id.container) as ViewPager tabLayout = findViewById(R.id.tabs) as TabLayout - sectionsPagerAdapter = SectionsPagerAdapter(supportFragmentManager, createDepartures(stopId)) + sectionsPagerAdapter = SectionsPagerAdapter(supportFragmentManager, Departure.createDepartures(stopId)) viewPager!!.adapter = sectionsPagerAdapter viewPager!!.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabLayout)) @@ -73,19 +74,15 @@ fab.setImageDrawable(ResourcesCompat.getDrawable(context.resources, R.drawable.ic_favourite_empty, this.theme)) } fab.setOnClickListener { - Log.i("FAB", "Click") if (!favourites.has(stopSymbol)) { - Log.i("FAB", "Add") val items = ArrayList<HashMap<String, String>>() timetable.getLines(stopId).forEach { val o = HashMap<String, String>() - o["stop"] = stopId - o["line"] = it + o[Favourite.TAG_STOP] = stopId + o[Favourite.TAG_LINE] = it items.add(o) } - Log.i("FAB", "Say") favourites.add(stopSymbol, items) - Log.i("FAB", "Change") fab.setImageDrawable(ResourcesCompat.getDrawable(context.resources, R.drawable.ic_favourite, this.theme)) } else { Snackbar.make(it, getString(R.string.stop_already_fav), Snackbar.LENGTH_LONG) @@ -98,23 +95,29 @@ private fun createTimerTask() { timerTask = object : TimerTask() { override fun run() { val vmIntent = Intent(context, VmClient::class.java) - vmIntent.putExtra("stopId", stopId) - vmIntent.putExtra("stopSymbol", stopSymbol) + vmIntent.putExtra(VmClient.EXTRA_STOP_SYMBOL, stopSymbol) startService(vmIntent) } } } private fun prepareOnDownloadListener() { - val filter = IntentFilter("ml.adamsprogs.bimba.departuresCreated") + val filter = IntentFilter(VmClient.ACTION_DEPARTURES_CREATED) + filter.addAction(VmClient.ACTION_NO_DEPARTURES) filter.addCategory(Intent.CATEGORY_DEFAULT) registerReceiver(receiver, filter) receiver.addOnVmListener(context as MessageReceiver.OnVmListener) } - override fun onVm(departures: HashMap<String, ArrayList<Departure>>) { - sectionsPagerAdapter?.departures = departures - sectionsPagerAdapter?.notifyDataSetChanged() + override fun onVm(vmDepartures: ArrayList<Departure>?) { + if (timetableType == "departure") { + val fullDepartures = Departure.createDepartures(stopId) + if (vmDepartures != null) { + fullDepartures[today.getMode()] = vmDepartures + } + sectionsPagerAdapter?.departures = fullDepartures + sectionsPagerAdapter?.notifyDataSetChanged() + } } private fun selectTodayPage() { @@ -151,7 +154,7 @@ timer.cancel() } else { timetableType = "departure" item.icon = (ResourcesCompat.getDrawable(resources, R.drawable.ic_timetable_full, this.theme)) - sectionsPagerAdapter?.departures = createDepartures(stopId) + sectionsPagerAdapter?.departures = Departure.createDepartures(stopId) sectionsPagerAdapter?.relativeTime = true sectionsPagerAdapter?.notifyDataSetChanged() scheduleRefresh() @@ -179,7 +182,7 @@ val layoutManager = LinearLayoutManager(activity) val departuresList: RecyclerView = rootView.findViewById(R.id.departuresList) as RecyclerView val dividerItemDecoration = DividerItemDecoration(departuresList.context, layoutManager.orientation) departuresList.addItemDecoration(dividerItemDecoration) - val adapter = DeparturesAdapter(activity, arguments.getStringArrayList("departures").map { fromString(it) }, + val adapter = DeparturesAdapter(activity, arguments.getStringArrayList("departures").map { Departure.fromString(it) }, arguments["relativeTime"] as Boolean) departuresList.adapter = adapter departuresList.layoutManager = layoutManager @@ -216,9 +219,9 @@ override fun getItem(position: Int): Fragment { var mode: String? = null when (position) { - 0 -> mode = "workdays" - 1 -> mode = "saturdays" - 2 -> mode = "sundays" + 0 -> mode = Timetable.MODE_WORKDAYS + 1 -> mode = Timetable.MODE_SATURDAYS + 2 -> mode = Timetable.MODE_SUNDAYS } return PlaceholderFragment.newInstance(position + 1, stopId, departures[mode], relativeTime) } diff --git a/app/src/main/java/ml/adamsprogs/bimba/extensions.kt b/app/src/main/java/ml/adamsprogs/bimba/extensions.kt new file mode 100644 index 0000000000000000000000000000000000000000..59572d65b5f42dcd6f63cfaa7bda08380ff22c3a --- /dev/null +++ b/app/src/main/java/ml/adamsprogs/bimba/extensions.kt @@ -0,0 +1,12 @@ +package ml.adamsprogs.bimba + +import ml.adamsprogs.bimba.models.Timetable +import java.util.* + +internal fun Calendar.getMode(): String { + when (this.get(Calendar.DAY_OF_WEEK)) { + Calendar.SUNDAY -> return Timetable.MODE_SUNDAYS + Calendar.SATURDAY -> return Timetable.MODE_SATURDAYS + else -> return Timetable.MODE_WORKDAYS + } +} \ No newline at end of file diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/Departure.kt b/app/src/main/java/ml/adamsprogs/bimba/models/Departure.kt index aac1cd0c15f12c8e01e03ab340e87b4c23ca52d1..97a303f74d08250922cae1bc1c2d08b16a0ad609 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/Departure.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/Departure.kt @@ -1,10 +1,6 @@ package ml.adamsprogs.bimba.models -fun fromString(string: String): Departure { - val array = string.split("|") - return Departure(array[0], array[1], array[2], array[3] == "1", array[4], array[5], - array[6] == "true", array[7] == "true", array[8] == "true") -} +import java.util.* data class Departure(val line: String, val mode: String, val time: String, val lowFloor: Boolean, val modification: String?, val direction: String, val vm: Boolean = false, @@ -12,5 +8,58 @@ var tomorrow: Boolean = false, val onStop: Boolean = false) { override fun toString(): String { return "$line|$mode|$time|$lowFloor|$modification|$direction|$vm|$tomorrow|$onStop" + } + + fun copy(): Departure { + return Departure.fromString(this.toString()) + } + + companion object { + private fun filterDepartures(departures: List<Departure>?): ArrayList<Departure> { + val filtered = ArrayList<Departure>() + val lines = HashMap<String, Int>() + val now = Calendar.getInstance() + for (departure in departures!!) { + val time = Calendar.getInstance() + time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(departure.time.split(":")[0])) + time.set(Calendar.MINUTE, Integer.parseInt(departure.time.split(":")[1])) + time.set(Calendar.SECOND, 0) + time.set(Calendar.MILLISECOND, 0) + if (departure.tomorrow) + time.add(Calendar.DAY_OF_MONTH, 1) + var lineExistedTimes = lines[departure.line] + if ((now.before(time) || now == time) && lineExistedTimes ?: 0 < 3) { + lineExistedTimes = (lineExistedTimes ?: 0) + 1 + lines[departure.line] = lineExistedTimes + filtered.add(departure) + } + } + return filtered + } + + fun createDepartures(stopId: String): HashMap<String, ArrayList<Departure>> { + val timetable = Timetable.getTimetable() + val departures = timetable.getStopDepartures(stopId) + val moreDepartures = timetable.getStopDepartures(stopId) + val rolledDepartures = HashMap<String, ArrayList<Departure>>() + + for ((_, tomorrowDepartures) in moreDepartures!!) { + tomorrowDepartures.forEach { it.tomorrow = true } + } + + for ((mode, _) in departures!!) { + rolledDepartures[mode] = (departures[mode] as ArrayList<Departure> + + moreDepartures[mode] as ArrayList<Departure>) as ArrayList<Departure> + rolledDepartures[mode] = filterDepartures(rolledDepartures[mode]) + } + + return rolledDepartures + } + + fun fromString(string: String): Departure { + val array = string.split("|") + return Departure(array[0], array[1], array[2], array[3] == "1", array[4], array[5], + array[6] == "true", array[7] == "true", array[8] == "true") + } } } \ No newline at end of file diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/DeparturesAdapter.kt b/app/src/main/java/ml/adamsprogs/bimba/models/DeparturesAdapter.kt index 0b9376f1f9ef371b34511a3682bec683a909c3b6..c175a6e11d1209aa827274f6484acc3e8ce1c561 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/DeparturesAdapter.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/DeparturesAdapter.kt @@ -12,47 +12,6 @@ import android.view.LayoutInflater import ml.adamsprogs.bimba.Declinator import java.util.* -fun filterDepartures(departures: List<Departure>?): ArrayList<Departure> { - val filtered = ArrayList<Departure>() - val lines = HashMap<String, Int>() - val now = Calendar.getInstance() - for (departure in departures!!) { - val time = Calendar.getInstance() - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(departure.time.split(":")[0])) - time.set(Calendar.MINUTE, Integer.parseInt(departure.time.split(":")[1])) - time.set(Calendar.SECOND, 0) - time.set(Calendar.MILLISECOND, 0) - if (departure.tomorrow) - time.add(Calendar.DAY_OF_MONTH, 1) - var lineExistedTimes = lines[departure.line] - if ((now.before(time) || now == time) && lineExistedTimes ?: 0 < 3) { - lineExistedTimes = (lineExistedTimes ?: 0) + 1 - lines[departure.line] = lineExistedTimes - filtered.add(departure) - } - } - return filtered -} - -fun createDepartures(stopId: String): HashMap<String, ArrayList<Departure>> { - val timetable = Timetable.getTimetable() - val departures = timetable.getStopDepartures(stopId) - val moreDepartures = timetable.getStopDepartures(stopId) - val rolledDepartures = HashMap<String, ArrayList<Departure>>() - - for ((_, tomorrowDepartures) in moreDepartures!!) { - tomorrowDepartures.forEach { it.tomorrow = true } - } - - for ((mode, _) in departures!!) { - rolledDepartures[mode] = (departures[mode] as ArrayList<Departure> + - moreDepartures[mode] as ArrayList<Departure>) as ArrayList<Departure> - rolledDepartures[mode] = filterDepartures(rolledDepartures[mode]) - } - - return rolledDepartures -} - class DeparturesAdapter(val context: Context, val departures: List<Departure>, val relativeTime: Boolean) : RecyclerView.Adapter<DeparturesAdapter.ViewHolder>() { override fun getItemCount(): Int { diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/Favourite.kt b/app/src/main/java/ml/adamsprogs/bimba/models/Favourite.kt index cf116c5fd6d84d9c9505a2d76a1edced6d53f8a0..976c01548a1079143e8f4ccbe5e8270fa4e1d462 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/Favourite.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/Favourite.kt @@ -3,6 +3,7 @@ import android.os.Parcel import android.os.Parcelable import android.util.Log +import ml.adamsprogs.bimba.getMode import java.util.* import kotlin.collections.ArrayList import kotlin.collections.HashMap @@ -10,7 +11,7 @@ class Favourite : Parcelable { var name: String var timetables: ArrayList<HashMap<String, String>> - private var oneDayDepartures:ArrayList<HashMap<String, ArrayList<Departure>>>? = null + private var oneDayDepartures: ArrayList<HashMap<String, ArrayList<Departure>>>? = null constructor(parcel: Parcel) { val array = ArrayList<String>() @@ -18,8 +19,8 @@ parcel.readStringList(array) val timetables = ArrayList<HashMap<String, String>>() for (row in array) { val element = HashMap<String, String>() - element["stop"] = row.split("|")[0] - element["line"] = row.split("|")[1] + element[TAG_STOP] = row.split("|")[0] + element[TAG_LINE] = row.split("|")[1] timetables.add(element) } this.name = parcel.readString() @@ -36,7 +37,7 @@ return 105 } override fun writeToParcel(dest: Parcel?, flags: Int) { - val parcel = timetables.map { "${it["stop"]}|${it["line"]}" } + val parcel = timetables.map { "${it[TAG_STOP]}|${it[TAG_LINE]}" } dest?.writeStringList(parcel) dest?.writeString(name) } @@ -49,41 +50,34 @@ var nextDeparture: Departure? = null get() { if (timetables.isEmpty()) return null - val today: String - val tomorrow: String val twoDayDepartures = ArrayList<Departure>() val now = Calendar.getInstance() val departureTime = Calendar.getInstance() - when (Calendar.getInstance().get(Calendar.DAY_OF_WEEK)) { - Calendar.SUNDAY -> today = "sundays" - Calendar.SATURDAY -> today = "saturdays" - else -> today = "workdays" - } + val today = now.getMode() val tomorrowCal = Calendar.getInstance() tomorrowCal.add(Calendar.DAY_OF_MONTH, 1) - when (tomorrowCal.get(Calendar.DAY_OF_WEEK)) { - Calendar.SUNDAY -> tomorrow = "sundays" - Calendar.SATURDAY -> tomorrow = "saturdays" - else -> tomorrow = "workdays" - } + val tomorrow = tomorrowCal.getMode() if (oneDayDepartures == null) { oneDayDepartures = ArrayList<HashMap<String, ArrayList<Departure>>>() - timetables.mapTo(oneDayDepartures!!) { timetable.getStopDepartures(it["stop"] as String, it["line"])!! } + timetables.mapTo(oneDayDepartures!!) { timetable.getStopDepartures(it[TAG_STOP] as String, it[TAG_LINE])!! } } oneDayDepartures!!.forEach { it[today]!!.forEach { - twoDayDepartures.add(fromString(it.toString())) + twoDayDepartures.add(it.copy()) } } oneDayDepartures!!.forEach { it[tomorrow]!!.forEach { - val d = fromString(it.toString()) + val d = it.copy() d.tomorrow = true twoDayDepartures.add(d) } } + + if (twoDayDepartures.isEmpty()) + return null var minDeparture: Departure = twoDayDepartures[0] var minInterval = 24 * 60L @@ -106,7 +100,7 @@ private set fun delete(stop: String, line: String) { Log.i("ROW", "Favourite deleting $stop, $line") - timetables.remove(timetables.find { it["stop"] == stop && it["line"] == line }) + timetables.remove(timetables.find { it[TAG_STOP] == stop && it[TAG_LINE] == line }) Log.i("ROW", timetables.toString()) } @@ -118,5 +112,8 @@ override fun newArray(size: Int): Array<Favourite?> { return arrayOfNulls(size) } + + val TAG_STOP = "stop" + val TAG_LINE = "line" } } diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/FavouriteEditRowAdapter.kt b/app/src/main/java/ml/adamsprogs/bimba/models/FavouriteEditRowAdapter.kt index b46c27f10bf33ea327cd12ab933e81f578a5c289..336c547c2784a8796b9a6a02c2840ecca6016eb0 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/FavouriteEditRowAdapter.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/FavouriteEditRowAdapter.kt @@ -17,18 +17,18 @@ override fun onBindViewHolder(holder: ViewHolder?, position: Int) { val timetable = Timetable.getTimetable() val favourites = FavouriteStorage.getFavouriteStorage() - val favouriteElement = timetable.getFavouriteElement(favourite.timetables[position]["stop"]!!, - favourite.timetables[position]["line"]!!) + val favouriteElement = timetable.getFavouriteElement(favourite.timetables[position][Favourite.TAG_STOP]!!, + favourite.timetables[position][Favourite.TAG_LINE]!!) holder?.rowTextView?.text = favouriteElement holder?.splitButton?.setOnClickListener { - favourites.detach(favourite.name, favourite.timetables[position]["stop"]!!, - favourite.timetables[position]["line"]!!, favouriteElement!!) + favourites.detach(favourite.name, favourite.timetables[position][Favourite.TAG_STOP]!!, + favourite.timetables[position][Favourite.TAG_LINE]!!, favouriteElement!!) favourite = favourites.favourites[favourite.name]!! notifyDataSetChanged() } holder?.deleteButton?.setOnClickListener { - favourites.delete(favourite.name, favourite.timetables[position]["stop"]!!, - favourite.timetables[position]["line"]!!) + favourites.delete(favourite.name, favourite.timetables[position][Favourite.TAG_STOP]!!, + favourite.timetables[position][Favourite.TAG_LINE]!!) favourite = favourites.favourites[favourite.name]!! notifyDataSetChanged() } diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/FavouriteStorage.kt b/app/src/main/java/ml/adamsprogs/bimba/models/FavouriteStorage.kt index 7be084c70a618c7078fa944e579a7e9a8dfcd8f0..533ed3c316d3fbec75ca55c2163f07da656a54f9 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/FavouriteStorage.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/FavouriteStorage.kt @@ -37,8 +37,8 @@ for ((name, jsonTimetables) in favouritesMap.entrySet()) { val timetables = ArrayList<HashMap<String, String>>() for (jsonTimetable in jsonTimetables.asJsonArray) { val timetable = HashMap<String, String>() - timetable["stop"] = jsonTimetable.asJsonObject["stop"].asString - timetable["line"] = jsonTimetable.asJsonObject["line"].asString + timetable[Favourite.TAG_STOP] = jsonTimetable.asJsonObject[Favourite.TAG_STOP].asString + timetable[Favourite.TAG_LINE] = jsonTimetable.asJsonObject[Favourite.TAG_LINE].asString timetables.add(timetable) } favourites[name] = Favourite(name, timetables) @@ -77,8 +77,8 @@ for ((name, favourite) in favourites) { val timetables = JsonArray() for (timetable in favourite.timetables) { val element = JsonObject() - element.addProperty("stop", timetable["stop"]) - element.addProperty("line", timetable["line"]) + element.addProperty(Favourite.TAG_STOP, timetable[Favourite.TAG_STOP]) + element.addProperty(Favourite.TAG_LINE, timetable[Favourite.TAG_LINE]) timetables.add(element) } rootObject.add(name, timetables) @@ -92,8 +92,8 @@ } fun detach(name: String, stop: String, line: String, newName: String) { val element = HashMap<String, String>() - element["stop"] = stop - element["line"] = line + element[Favourite.TAG_STOP] = stop + element[Favourite.TAG_LINE] = line val array = ArrayList<HashMap<String, String>>() array.add(element) favourites[newName] = Favourite(newName, array) diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/FavouritesAdapter.kt b/app/src/main/java/ml/adamsprogs/bimba/models/FavouritesAdapter.kt index fd1c29d7ed811d15bd86d656a267d47037cb5e71..2a3f07ea66f19f36ce4f26f53fc6ffe600ac762c 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/FavouritesAdapter.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/FavouritesAdapter.kt @@ -69,7 +69,7 @@ if (nextDeparture.tomorrow) departureTime.add(Calendar.DAY_OF_MONTH, 1) val interval = ((departureTime.timeInMillis - now.timeInMillis) / (1000 * 60)) Log.i("Interval", "$interval") - nextDepartureText = context.getString(Declinator.decline(interval), interval.toString()) // fixme -1 + nextDepartureText = context.getString(Declinator.decline(interval), interval.toString()) nextDepartureLineText = context.getString(R.string.departure_to_line, nextDeparture.line, nextDeparture.direction) } else { nextDepartureText = context.getString(R.string.no_next_departure) diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt b/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt index a72e4d39081641a2cd05c084aea9b01cbd285c34..fb449ce4a9022308d23a7305aa12aeae0546c4cf 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt @@ -8,9 +8,13 @@ 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 { + fun getTimetable(context: Context? = null, force: Boolean = false): Timetable{ if (timetable == null || force) if (context != null) { val db: SQLiteDatabase? @@ -83,9 +87,9 @@ "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("workdays", ArrayList()) - departures.put("saturdays", ArrayList()) - departures.put("sundays", ArrayList()) + 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, diff --git a/app/src/main/res/layout/activity_dash.xml b/app/src/main/res/layout/activity_dash.xml index 37ba2c615b1ac3bc0bbb913935e0aa79e5f562b0..cc390f75452ce75bfe33423cf398c1477d683924 100644 --- a/app/src/main/res/layout/activity_dash.xml +++ b/app/src/main/res/layout/activity_dash.xml @@ -29,20 +29,6 @@ app:layout_scrollFlags="scroll|enterAlways" app:popupTheme="@style/AppTheme.PopupOverlay" /> </android.support.design.widget.AppBarLayout> - <com.arlib.floatingsearchview.FloatingSearchView - android:id="@+id/search_view" - android:layout_width="match_parent" - android:layout_height="match_parent" - app:floatingSearch_close_search_on_keyboard_dismiss="true" - app:floatingSearch_leftActionMode="showHamburger" - app:floatingSearch_searchBarMarginLeft="16dp" - app:floatingSearch_searchBarMarginRight="16dp" - app:floatingSearch_searchBarMarginTop="16dp" - app:floatingSearch_searchHint="@string/search_placeholder" - app:floatingSearch_showSearchKey="false" - app:floatingSearch_suggestionsListAnimDuration="250" /> - - <android.support.v7.widget.RecyclerView android:id="@+id/favouritesList" android:layout_width="match_parent" @@ -56,6 +42,19 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + <com.arlib.floatingsearchview.FloatingSearchView + android:id="@+id/search_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:floatingSearch_close_search_on_keyboard_dismiss="true" + app:floatingSearch_leftActionMode="showHamburger" + app:floatingSearch_searchBarMarginLeft="16dp" + app:floatingSearch_searchBarMarginRight="16dp" + app:floatingSearch_searchBarMarginTop="16dp" + app:floatingSearch_searchHint="@string/search_placeholder" + app:floatingSearch_showSearchKey="false" + app:floatingSearch_suggestionsListAnimDuration="250" /> </android.support.constraint.ConstraintLayout>