diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/AlertsDialogFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/AlertsDialogFragment.kt index 00a0801..585af4e 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/AlertsDialogFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/AlertsDialogFragment.kt @@ -1,143 +1,160 @@ +/* + BusTO - Fragments components + Copyright (C) 2026 Fabio Mazza + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ package it.reyboz.bustorino.fragments import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageButton import android.widget.TextView import android.widget.Toast import androidx.cardview.widget.CardView import androidx.fragment.app.DialogFragment import androidx.fragment.app.activityViewModels import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.work.ExistingWorkPolicy import androidx.work.WorkManager import it.reyboz.bustorino.R import it.reyboz.bustorino.adapters.AlertLineFullAdapter import it.reyboz.bustorino.backend.gtfs.GtfsUtils import it.reyboz.bustorino.data.GtfsAlertDBDownloadWorker import it.reyboz.bustorino.data.gtfs.AlertWithDetails import it.reyboz.bustorino.data.gtfs.GtfsAlertsTranslation import it.reyboz.bustorino.viewmodels.ServiceAlertsViewModel import java.util.Locale import kotlin.getValue import kotlin.collections.HashMap class AlertsDialogFragment(private val gtfsLineShow: String) : DialogFragment() { private lateinit var titleTextView: TextView private lateinit var messageTextView: TextView private lateinit var statusCardView: CardView private lateinit var recyclerView: RecyclerView private val alertsViewModel: ServiceAlertsViewModel by activityViewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.d(DEBUG_TAG, "created DialogFragment for line ${gtfsLineShow}") } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { // Inflate the layout for this fragment val root = inflater.inflate(R.layout.fragment_dialog_alerts_line, container, false) titleTextView = root.findViewById(R.id.titleTextView) titleTextView.setText(getString(R.string.alert_line_fill,GtfsUtils.lineNameDisplayFromGtfsID(gtfsLineShow))) recyclerView = root.findViewById(R.id.alertsRecyclerView) recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) messageTextView = root.findViewById(R.id.alertMessageTextView) statusCardView = root.findViewById(R.id.statusCard) alertsViewModel.alertsByRouteLiveData.observe(viewLifecycleOwner){ alerts -> showAlerts(alerts) } val btnClose = root.findViewById(R.id.btnClose) btnClose.setOnClickListener { dismiss() } val btnRefresh = root.findViewById(R.id.btnRefresh) btnRefresh.setOnClickListener { val name = "manualUpdateAlerts" val req = GtfsAlertDBDownloadWorker.makeOneTimeRequest("manualUpdate$gtfsLineShow") WorkManager.getInstance(requireContext()).enqueueUniqueWork(name, ExistingWorkPolicy.KEEP,req) Toast.makeText(context, R.string.checking_alerts_update, Toast.LENGTH_SHORT).show() } return root } override fun onStart() { super.onStart() dialog?.window?.setLayout( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT ) } private fun showAlerts(alerts: List) { val currentLang = Locale.getDefault().language val ms = "language : $currentLang" val langs_msg = HashMap() for (a in alerts) { for (tr in a.translations){ if(tr.field == GtfsAlertsTranslation.FIELD_HEADER){ tr.language?.let{ if(langs_msg.containsKey(it)){ langs_msg[it] = langs_msg[it]!! + 1 } else{ langs_msg[it] = 1 } } //found the title, stop break } } } Log.d(DEBUG_TAG, "Lang $currentLang, alerts: $langs_msg, of lang: ${langs_msg[currentLang]}") val msgInLang = langs_msg[currentLang]?: 0 val langShow = if (msgInLang > 0){ currentLang } else if("en" in langs_msg.keys){ "en" } else{ "it" } // if there are no messages with "it", then it's over val count = langs_msg[langShow] ?: 0 if (count == 0){ messageTextView.text = "ERROR: NO ALERTS TO SHOW" statusCardView.visibility = View.VISIBLE } else if(msgInLang == 0){ val msgShow = if(langShow == "en") getString(R.string.english) else getString(R.string.italian) messageTextView.text = getString(R.string.no_alerts_in_your_language_fill, msgShow) statusCardView.visibility = View.VISIBLE } // put them in the adapter if(count>0){ recyclerView.adapter = AlertLineFullAdapter(alerts, langShow) } } companion object { /** * Use this factory method to create a new instance of * this fragment using the provided parameters. * * @param gtfsLine Line To show. * @return A new instance of fragment LineAlertsDialogFragment. */ @JvmStatic fun newInstance(gtfsLine: String) = AlertsDialogFragment(gtfsLine) private const val GTFS_LINE_ARG = "gtfsLine" private const val DEBUG_TAG = "BusTO-AlertsDialog" } } \ No newline at end of file diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/AlertsFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/AlertsFragment.kt index d89341f..85234b1 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/AlertsFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/AlertsFragment.kt @@ -1,156 +1,173 @@ +/* + BusTO - Fragments components + Copyright (C) 2026 Fabio Mazza + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ package it.reyboz.bustorino.fragments import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.cardview.widget.CardView import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.recyclerview.widget.RecyclerView import com.google.transit.realtime.GtfsRealtime import it.reyboz.bustorino.R import it.reyboz.bustorino.viewmodels.ServiceAlertsViewModel import java.text.SimpleDateFormat import java.util.Date import java.util.Locale import java.util.TimeZone /** * A simple [Fragment] subclass. * Use the [AlertsFragment.newInstance] factory method to * create an instance of this fragment. */ class AlertsFragment : ScreenBaseFragment() { private val alertsViewModel: ServiceAlertsViewModel by activityViewModels() private lateinit var textView: TextView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) arguments?.let { //param1 = it.getString(ARG_PARAM1) //param2 = it.getString(ARG_PARAM2) } } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { // Inflate the layout for this fragment val root = inflater.inflate(R.layout.fragment_alerts, container, false) textView = root.findViewById(R.id.simpleTextView) alertsViewModel.allAlertsLiveData.observe(viewLifecycleOwner, { alerts -> val sb = StringBuilder() val unixTimestamp = (System.currentTimeMillis() / 1000) for (x in alerts) { sb.append(x.longPrint()) sb.append("----- Alert active: ").append(x.isActive(unixTimestamp)).append("\n\n") } textView.text = sb.toString() }) alertsViewModel.setStopFilter("472") /*alertsViewModel.alertsForStop.observe(viewLifecycleOwner){ Log.d(DEBUG_TAG, "Got ${it.size} alerts") it?.let { showAlerts(it) } } */ /* alertsViewModel.alertsByRouteLiveData.observe(viewLifecycleOwner) { map -> Log.d(DEBUG_TAG, "Alerts for routes: ${map.keys}") val keys = map.keys if(keys.isNotEmpty()){ val sb = StringBuilder() for (key in keys.sorted()) { sb.append(" ======== Route: $key =======").append("\n") sb.append(makeAlertListText(map[key]!!)).append("\n") Log.d(DEBUG_TAG, "Route: $key len: ${map[key]!!.size}") } textView.text = sb.toString() } } */ return root } override fun getBaseViewForSnackBar(): View? { TODO("Not yet implemented") } private fun makeAlertListText(alerts: List) : String{ val sb = StringBuilder() for (al in alerts) { sb.append("=========== Alert ===========\n") sb.append("Title:\n") for (t in al.headerText.translationList) { sb.append(t.language).append(": ").append(t.text).append("\n") } sb.append("Description:\n") val transl = al.descriptionText.translationList for (t in transl) { sb.append(t.language).append(": ").append(t.text).append("\n") } val infE = al.informedEntityList sb.append("--- Active periods count: ${al.activePeriodCount}\n") val timeActive = al.getActivePeriod(0) sb.append("Start: ").append(getTimeStampToString(timeActive.start)).append(" ") sb.append("End: ").append(getTimeStampToString(timeActive.end)).append("\n") sb.append("--- Cause:\n") sb.append(al.cause.name).append("\n") sb.append("--- Informed entities:\n") for (e in infE) { if(e.hasTrip()){ sb.append("Trip: ${e.trip.tripId} for route ${e.trip.routeId}, ") } else{ sb.append("No Trip, ") } sb.append("Stop: ${e.stopId}, Route: ${e.routeId}\n") } sb.append("\n") } return sb.toString() } companion object { /** * Use this factory method to create a new instance of * this fragment using the provided parameters. * * @return A new instance of fragment AlertsFragment. */ @JvmStatic fun newInstance() = AlertsFragment().apply { arguments = Bundle().apply { //putString(ARG_PARAM1, param1) //putString(ARG_PARAM2, param2) } } fun getTimeStampToString(timestamp: Long): String? { val date = Date(timestamp*1000) val sdf= SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) sdf.timeZone = TimeZone.getTimeZone("Europe/Rome") return sdf.format(date) } private const val DEBUG_TAG = "BusTO-AlertsFragment" } } \ No newline at end of file diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/BackupImportFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/BackupImportFragment.kt index ed3573f..c5989c8 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/BackupImportFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/BackupImportFragment.kt @@ -1,297 +1,314 @@ +/* + BusTO - Fragments components + Copyright (C) 2024 Fabio Mazza + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ package it.reyboz.bustorino.fragments import android.app.Activity import android.content.Intent import android.net.Uri import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Button import android.widget.CheckBox import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.Fragment import de.siegmar.fastcsv.reader.CsvReader import de.siegmar.fastcsv.writer.CsvWriter import it.reyboz.bustorino.R import it.reyboz.bustorino.data.PreferencesHolder import it.reyboz.bustorino.data.UserDB import it.reyboz.bustorino.util.ImportExport import java.io.* import java.text.DateFormat import java.text.SimpleDateFormat import java.util.* import java.util.zip.ZipEntry import java.util.zip.ZipInputStream import java.util.zip.ZipOutputStream /** * A simple [Fragment] subclass. * Use the [BackupImportFragment.newInstance] factory method to * create an instance of this fragment. */ class BackupImportFragment : Fragment() { private val saveFileLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> if (result.resultCode == Activity.RESULT_OK) { result.data?.data?.also { uri -> writeDataZip(uri) } } } private val openFileLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> if (!(loadFavorites|| loadPreferences)){ Toast.makeText(context, R.string.message_check_at_least_one, Toast.LENGTH_SHORT).show() } else if (result.resultCode == Activity.RESULT_OK) { result.data?.data?.also { uri -> loadZipData(uri,loadFavorites, loadPreferences) } } } private lateinit var saveButton: Button private var loadFavorites = true private var loadPreferences = true private lateinit var checkFavorites: CheckBox private lateinit var checkPreferences: CheckBox override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) /*arguments?.let { param1 = it.getString(ARG_PARAM1) param2 = it.getString(ARG_PARAM2) }*/ } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { // Inflate the layout for this fragment val rootview= inflater.inflate(R.layout.fragment_test_saving, container, false) saveButton = rootview.findViewById(R.id.saveButton) saveButton.setOnClickListener { startFileSaveIntent() } checkFavorites = rootview.findViewById(R.id.favoritesCheckBox) checkFavorites.setOnCheckedChangeListener { _, isChecked -> loadFavorites = isChecked } checkPreferences = rootview.findViewById(R.id.preferencesCheckBox) checkPreferences.setOnCheckedChangeListener { _, isChecked -> loadPreferences = isChecked } val readFavoritesButton = rootview.findViewById