diff --git a/app/src/main/java/it/reyboz/bustorino/adapters/StringListAdapter.kt b/app/src/main/java/it/reyboz/bustorino/adapters/StringListAdapter.kt new file mode 100644 index 0000000..c9c3e61 --- /dev/null +++ b/app/src/main/java/it/reyboz/bustorino/adapters/StringListAdapter.kt @@ -0,0 +1,32 @@ +package it.reyboz.bustorino.adapters + + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView + +class StringListAdapter(private val stringList: List) : + RecyclerView.Adapter() { + + // ViewHolder class to hold the TextView for each item + class StringViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val textView: TextView = itemView.findViewById(android.R.id.text1) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StringViewHolder { + // Inflate the layout for each item in the RecyclerView (simple_list_item_1) + val view = LayoutInflater.from(parent.context) + .inflate(android.R.layout.simple_list_item_1, parent, false) + return StringViewHolder(view) + } + + override fun onBindViewHolder(holder: StringViewHolder, position: Int) { + // Bind the string from stringList at this position to the TextView + holder.textView.text = stringList[position] + } + + override fun getItemCount(): Int = stringList.size + +} \ No newline at end of file diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/LinesGridShowingFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/LinesGridShowingFragment.kt index 7ab74d9..e234f2e 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/LinesGridShowingFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/LinesGridShowingFragment.kt @@ -1,337 +1,403 @@ package it.reyboz.bustorino.fragments import android.content.Context import android.os.Bundle import android.util.Log -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.view.* import android.view.animation.Animation import android.view.animation.LinearInterpolator import android.view.animation.RotateAnimation import android.widget.ImageView import android.widget.TextView +import androidx.appcompat.widget.SearchView +import androidx.core.view.MenuHost +import androidx.core.view.MenuProvider import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle import androidx.recyclerview.widget.RecyclerView import it.reyboz.bustorino.R import it.reyboz.bustorino.adapters.RouteAdapter import it.reyboz.bustorino.adapters.RouteOnlyLineAdapter +import it.reyboz.bustorino.adapters.StringListAdapter import it.reyboz.bustorino.backend.utils import it.reyboz.bustorino.data.PreferencesHolder import it.reyboz.bustorino.data.gtfs.GtfsRoute import it.reyboz.bustorino.middleware.AutoFitGridLayoutManager import it.reyboz.bustorino.util.LinesNameSorter import it.reyboz.bustorino.util.ViewUtils import it.reyboz.bustorino.viewmodels.LinesGridShowingViewModel class LinesGridShowingFragment : ScreenBaseFragment() { private val viewModel: LinesGridShowingViewModel by viewModels() //private lateinit var gridLayoutManager: AutoFitGridLayoutManager private lateinit var favoritesRecyclerView: RecyclerView private lateinit var urbanRecyclerView: RecyclerView private lateinit var extraurbanRecyclerView: RecyclerView private lateinit var touristRecyclerView: RecyclerView private lateinit var favoritesTitle: TextView private lateinit var urbanLinesTitle: TextView private lateinit var extrurbanLinesTitle: TextView private lateinit var touristLinesTitle: TextView + //private lateinit var searchBar: SearchView private var routesByAgency = HashMap>() /*hashMapOf( AG_URBAN to ArrayList(), AG_EXTRAURB to ArrayList(), AG_TOUR to ArrayList() )*/ private lateinit var fragmentListener: CommonFragmentListener private val linesNameSorter = LinesNameSorter() private val linesComparator = Comparator { a,b -> return@Comparator linesNameSorter.compare(a.shortName, b.shortName) } private val routeClickListener = RouteAdapter.ItemClicker { fragmentListener.showLineOnMap(it.gtfsId) } private val arrows = HashMap() private val durations = HashMap() + //private val recyclerViewAdapters= HashMap() + private val lastQueryEmptyForAgency = HashMap(3) private var openRecyclerView = "AG_URBAN" override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val rootView = inflater.inflate(R.layout.fragment_lines_grid, container, false) favoritesRecyclerView = rootView.findViewById(R.id.favoritesRecyclerView) urbanRecyclerView = rootView.findViewById(R.id.urbanLinesRecyclerView) extraurbanRecyclerView = rootView.findViewById(R.id.extraurbanLinesRecyclerView) touristRecyclerView = rootView.findViewById(R.id.touristLinesRecyclerView) favoritesTitle = rootView.findViewById(R.id.favoritesTitleView) urbanLinesTitle = rootView.findViewById(R.id.urbanLinesTitleView) extrurbanLinesTitle = rootView.findViewById(R.id.extraurbanLinesTitleView) touristLinesTitle = rootView.findViewById(R.id.touristLinesTitleView) - arrows[AG_URBAN] = rootView.findViewById(R.id.arrowUrb) arrows[AG_TOUR] = rootView.findViewById(R.id.arrowTourist) arrows[AG_EXTRAURB] = rootView.findViewById(R.id.arrowExtraurban) arrows[AG_FAV] = rootView.findViewById(R.id.arrowFavorites) //show urban expanded by default val recViews = listOf(urbanRecyclerView, extraurbanRecyclerView, touristRecyclerView) for (recyView in recViews) { val gridLayoutManager = AutoFitGridLayoutManager( requireContext().applicationContext, (utils.convertDipToPixels(context, COLUMN_WIDTH_DP.toFloat())).toInt() ) recyView.layoutManager = gridLayoutManager } //init favorites recyclerview val gridLayoutManager = AutoFitGridLayoutManager( requireContext().applicationContext, (utils.convertDipToPixels(context, 70f)).toInt() ) favoritesRecyclerView.layoutManager = gridLayoutManager - viewModel.routesLiveData.observe(viewLifecycleOwner){ + viewModel.getLinesLiveData().observe(viewLifecycleOwner){ //routesList = ArrayList(it) //routesList.sortWith(linesComparator) routesByAgency.clear() + for (k in AGENCIES){ + routesByAgency[k] = ArrayList() + } for(route in it){ val agency = route.agencyID - if(!routesByAgency.containsKey(agency)){ - routesByAgency[agency] = ArrayList() + if(agency !in routesByAgency.keys){ + Log.e(DEBUG_TAG, "The agency $agency is not present in the predefined agencies (${routesByAgency.keys})") } routesByAgency[agency]?.add(route) - } - //val adapter = RouteOnlyLineAdapter(routesByAgency.map { route-> route.shortName }) //zip agencies and recyclerviews Companion.AGENCIES.zip(recViews) { ag, recView -> routesByAgency[ag]?.let { routeList -> - routeList.sortWith(linesComparator) - //val adapter = RouteOnlyLineAdapter(it.map { rt -> rt.shortName }) - val adapter = RouteAdapter(routeList,routeClickListener) - recView.adapter = adapter + if (routeList.size > 0) { + routeList.sortWith(linesComparator) + //val adapter = RouteOnlyLineAdapter(it.map { rt -> rt.shortName }) + val adapter = RouteAdapter(routeList, routeClickListener) + val lastQueryEmpty = if(ag in lastQueryEmptyForAgency.keys) lastQueryEmptyForAgency[ag]!! else true + if (lastQueryEmpty) + recView.adapter = adapter + else recView.swapAdapter(adapter, false) + lastQueryEmptyForAgency[ag] = false + } else { + val messageString = if(viewModel.getLineQueryValue().isNotEmpty()) getString(R.string.no_lines_found_query) else getString(R.string.no_lines_found) + val extraAdapter = StringListAdapter(listOf(messageString)) + recView.adapter = extraAdapter + lastQueryEmptyForAgency[ag] = true + + } durations[ag] = if(routeList.size < 20) ViewUtils.DEF_DURATION else 1000 } } } viewModel.favoritesLines.observe(viewLifecycleOwner){ routes-> val routesNames = routes.map { it.shortName } //create new item click listener every time val adapter = RouteOnlyLineAdapter(routesNames){ pos, _ -> val r = routes[pos] fragmentListener.showLineOnMap(r.gtfsId) } favoritesRecyclerView.adapter = adapter } //onClicks urbanLinesTitle.setOnClickListener { openLinesAndCloseOthersIfNeeded(AG_URBAN) } extrurbanLinesTitle.setOnClickListener { openLinesAndCloseOthersIfNeeded(AG_EXTRAURB) } touristLinesTitle.setOnClickListener { openLinesAndCloseOthersIfNeeded(AG_TOUR) } favoritesTitle.setOnClickListener { closeOpenFavorites() } arrows[AG_FAV]?.setOnClickListener { closeOpenFavorites() } //arrows onClicks for(k in Companion.AGENCIES){ //k is either AG_TOUR, AG_EXTRAURBAN, AG_URBAN arrows[k]?.setOnClickListener { openLinesAndCloseOthersIfNeeded(k) } } return rootView } + fun setUserSearch(textSearch:String){ + viewModel.setLineQuery(textSearch) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val menuHost: MenuHost = requireActivity() + + // Add menu items without using the Fragment Menu APIs + // Note how we can tie the MenuProvider to the viewLifecycleOwner + // and an optional Lifecycle.State (here, RESUMED) to indicate when + // the menu should be visible + menuHost.addMenuProvider(object : MenuProvider { + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + // Add menu items here + menuInflater.inflate(R.menu.menu_search, menu) + + val search = menu.findItem(R.id.searchMenuItem).actionView as SearchView + search.setOnQueryTextListener(object : SearchView.OnQueryTextListener{ + override fun onQueryTextSubmit(query: String?): Boolean { + setUserSearch(query ?: "") + return true + } + + override fun onQueryTextChange(query: String?): Boolean { + setUserSearch(query ?: "") + return true + } + + }) + + search.queryHint = getString(R.string.search_box_lines_suggestion_filter) + } + + override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + // Handle the menu selection + if (menuItem.itemId == R.id.searchMenuItem){ + Log.d(DEBUG_TAG, "Clicked on search menu") + } + else{ + Log.d(DEBUG_TAG, "Clicked on something else") + } + return false + } + }, viewLifecycleOwner, Lifecycle.State.RESUMED) + } + + private fun closeOpenFavorites(){ if(favoritesRecyclerView.visibility == View.VISIBLE){ //close it favoritesRecyclerView.visibility = View.GONE setOpen(arrows[AG_FAV]!!, false) viewModel.favoritesExpanded.value = false } else{ favoritesRecyclerView.visibility = View.VISIBLE setOpen(arrows[AG_FAV]!!, true) viewModel.favoritesExpanded.value = true } } private fun openLinesAndCloseOthersIfNeeded(agency: String){ if(openRecyclerView!="" && openRecyclerView!= agency) { switchRecyclerViewStatus(openRecyclerView) } switchRecyclerViewStatus(agency) } private fun switchRecyclerViewStatus(agency: String){ val recyclerView = when(agency){ AG_TOUR -> touristRecyclerView AG_EXTRAURB -> extraurbanRecyclerView AG_URBAN -> urbanRecyclerView else -> throw IllegalArgumentException("$DEBUG_TAG: Agency Invalid") } val expandedLiveData = when(agency){ AG_TOUR -> viewModel.isTouristExpanded AG_URBAN -> viewModel.isUrbanExpanded AG_EXTRAURB -> viewModel.isExtraUrbanExpanded else -> throw IllegalArgumentException("$DEBUG_TAG: Agency Invalid") } val duration = durations[agency] val arrow = arrows[agency] val durArrow = if(duration == null || duration==ViewUtils.DEF_DURATION) 500 else duration if(duration!=null&&arrow!=null) when (recyclerView.visibility){ View.GONE -> { Log.d(DEBUG_TAG, "Open recyclerview $agency") //val a =ViewUtils.expand(recyclerView, duration, 0) recyclerView.visibility = View.VISIBLE expandedLiveData.value = true Log.d(DEBUG_TAG, "Arrow for $agency has rotation: ${arrow.rotation}") setOpen(arrow, true) //arrow.startAnimation(rotateArrow(true,durArrow)) openRecyclerView = agency } View.VISIBLE -> { Log.d(DEBUG_TAG, "Close recyclerview $agency") //ViewUtils.collapse(recyclerView, duration) recyclerView.visibility = View.GONE expandedLiveData.value = false //arrow.rotation = 90f Log.d(DEBUG_TAG, "Arrow for $agency has rotation ${arrow.rotation} pre-rotate") setOpen(arrow, false) //arrow.startAnimation(rotateArrow(false,durArrow)) openRecyclerView = "" } View.INVISIBLE -> { TODO() } } } override fun onAttach(context: Context) { super.onAttach(context) if(context is CommonFragmentListener){ fragmentListener = context } else throw RuntimeException("$context must implement CommonFragmentListener") } override fun getBaseViewForSnackBar(): View? { return null } override fun onResume() { super.onResume() val pref = PreferencesHolder.getMainSharedPreferences(requireContext()) val res = pref.getStringSet(PreferencesHolder.PREF_FAVORITE_LINES, HashSet()) res?.let { viewModel.setFavoritesLinesIDs(HashSet(it))} //restore state viewModel.favoritesExpanded.value?.let { if(!it){ //close it favoritesRecyclerView.visibility = View.GONE setOpen(arrows[AG_FAV]!!, false) } else{ favoritesRecyclerView.visibility = View.VISIBLE setOpen(arrows[AG_FAV]!!, true) } } viewModel.isUrbanExpanded.value?.let { if(it) { urbanRecyclerView.visibility = View.VISIBLE arrows[AG_URBAN]?.rotation= 90f openRecyclerView = AG_URBAN Log.d(DEBUG_TAG, "RecyclerView gtt:U is expanded") } else { urbanRecyclerView.visibility = View.GONE arrows[AG_URBAN]?.rotation= 0f } } viewModel.isTouristExpanded.value?.let { val recview = touristRecyclerView if(it) { recview.visibility = View.VISIBLE arrows[AG_TOUR]?.rotation=90f openRecyclerView = AG_TOUR } else { recview.visibility = View.GONE arrows[AG_TOUR]?.rotation= 0f } } viewModel.isExtraUrbanExpanded.value?.let { val recview = extraurbanRecyclerView if(it) { openRecyclerView = AG_EXTRAURB recview.visibility = View.VISIBLE arrows[AG_EXTRAURB]?.rotation=90f } else { recview.visibility = View.GONE arrows[AG_EXTRAURB]?.rotation=0f } } fragmentListener.readyGUIfor(FragmentKind.LINES) } companion object { private const val COLUMN_WIDTH_DP=200 private const val AG_FAV = "fav" private const val AG_URBAN = "gtt:U" private const val AG_EXTRAURB ="gtt:E" private const val AG_TOUR ="gtt:T" private const val DEBUG_TAG ="BusTO-LinesGridFragment" const val FRAGMENT_TAG = "LinesGridShowingFragment" private val AGENCIES = listOf(AG_URBAN, AG_EXTRAURB, AG_TOUR) fun newInstance() = LinesGridShowingFragment() @JvmStatic fun setOpen(imageView: ImageView, value: Boolean){ if(value) imageView.rotation = 90f else imageView.rotation = 0f } @JvmStatic fun rotateArrow(toOpen: Boolean, duration: Long): RotateAnimation{ val start = if (toOpen) 0f else 90f val stop = if(toOpen) 90f else 0f Log.d(DEBUG_TAG, "Rotate arrow from $start to $stop") val rotate = RotateAnimation(start, stop, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f) rotate.duration = duration rotate.interpolator = LinearInterpolator() //rotate.fillAfter = true rotate.fillBefore = false return rotate } } } \ No newline at end of file diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java index 48834f5..b99ff4f 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java +++ b/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java @@ -1,90 +1,91 @@ package it.reyboz.bustorino.fragments; import android.Manifest; import android.content.Context; import android.content.SharedPreferences; +import android.os.Bundle; import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.Toast; import androidx.activity.result.ActivityResultCallback; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import it.reyboz.bustorino.BuildConfig; import java.util.Map; import static android.content.Context.MODE_PRIVATE; public abstract class ScreenBaseFragment extends Fragment { protected final static String PREF_FILE= BuildConfig.APPLICATION_ID+".fragment_prefs"; protected void setOption(String optionName, boolean value) { Context mContext = getContext(); SharedPreferences.Editor editor = mContext.getSharedPreferences(PREF_FILE, MODE_PRIVATE).edit(); editor.putBoolean(optionName, value); editor.commit(); } protected boolean getOption(String optionName, boolean optDefault) { Context mContext = getContext(); assert mContext != null; return getOption(mContext, optionName, optDefault); } protected void showToastMessage(int messageID, boolean short_lenght) { final int length = short_lenght ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG; Toast.makeText(getContext(), messageID, length).show(); } public void hideKeyboard() { if (getActivity()==null) return; View view = getActivity().getCurrentFocus(); if (view != null) { ((InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE)) .hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); } } /** * Find the view on which the snackbar should be shown * @return a view or null if you don't want the snackbar shown */ @Nullable public abstract View getBaseViewForSnackBar(); public static boolean getOption(Context context, String optionName, boolean optDefault){ SharedPreferences preferences = context.getSharedPreferences(PREF_FILE, MODE_PRIVATE); return preferences.getBoolean(optionName, optDefault); } public static void setOption(Context context,String optionName, boolean value) { SharedPreferences.Editor editor = context.getSharedPreferences(PREF_FILE, MODE_PRIVATE).edit(); editor.putBoolean(optionName, value); editor.apply(); } public ActivityResultLauncher getPositionRequestLauncher(LocationRequestListener listener){ return registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<>() { @Override public void onActivityResult(Map result) { if (result == null) return; if (result.get(Manifest.permission.ACCESS_COARSE_LOCATION) == null || result.get(Manifest.permission.ACCESS_FINE_LOCATION) == null) return; final boolean coarseGranted = Boolean.TRUE.equals(result.get(Manifest.permission.ACCESS_COARSE_LOCATION)); final boolean fineGranted = Boolean.TRUE.equals(result.get(Manifest.permission.ACCESS_FINE_LOCATION)); listener.onPermissionResult(coarseGranted, fineGranted); } }); } public interface LocationRequestListener{ void onPermissionResult(boolean isCoarseGranted, boolean isFineGranted); } } diff --git a/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesGridShowingViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesGridShowingViewModel.kt index 23a7d24..21eab54 100644 --- a/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesGridShowingViewModel.kt +++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesGridShowingViewModel.kt @@ -1,51 +1,83 @@ package it.reyboz.bustorino.viewmodels import android.app.Application +import android.util.Log import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.map import it.reyboz.bustorino.data.GtfsRepository import it.reyboz.bustorino.data.gtfs.GtfsDatabase import it.reyboz.bustorino.data.gtfs.GtfsRoute import it.reyboz.bustorino.util.LinesNameSorter class LinesGridShowingViewModel(application: Application) : AndroidViewModel(application) { - private val gtfsRepo: GtfsRepository - - init { - val gtfsDao = GtfsDatabase.getGtfsDatabase(application).gtfsDao() - gtfsRepo = GtfsRepository(gtfsDao) - + private val linesNameSorter = LinesNameSorter() + private val linesComparator = Comparator { a,b -> + return@Comparator linesNameSorter.compare(a.shortName, b.shortName) } - val routesLiveData = gtfsRepo.getAllRoutes() + private val gtfsRepo: GtfsRepository + + private val routesLiveData: LiveData> //= gtfsRepo.getAllRoutes() val isUrbanExpanded = MutableLiveData(true) val isExtraUrbanExpanded = MutableLiveData(false) val isTouristExpanded = MutableLiveData(false) val favoritesExpanded = MutableLiveData(true) val favoritesLinesIDs = MutableLiveData>() - private val linesNameSorter = LinesNameSorter() - private val linesComparator = Comparator { a,b -> - return@Comparator linesNameSorter.compare(a.shortName, b.shortName) + private val queryLiveData = MutableLiveData("") + fun setLineQuery(query: String){ + if(query!=queryLiveData.value) + queryLiveData.value = query + } + fun getLineQueryValue():String{ + return queryLiveData.value ?: "" + } + private val filteredLinesLiveData = MediatorLiveData>() + fun getLinesLiveData(): LiveData> { + return filteredLinesLiveData + } + + private fun filterLinesForQuery(lines: List, query: String): List{ + val result= lines.filter { r-> query.lowercase() in r.shortName.lowercase() } + + return result + } + + init { + val gtfsDao = GtfsDatabase.getGtfsDatabase(application).gtfsDao() + gtfsRepo = GtfsRepository(gtfsDao) + routesLiveData = gtfsRepo.getAllRoutes() + + filteredLinesLiveData.addSource(routesLiveData){ + filteredLinesLiveData.value = filterLinesForQuery(it,queryLiveData.value ?: "" ) + } + filteredLinesLiveData.addSource(queryLiveData){ + routesLiveData.value?.let { routes -> + filteredLinesLiveData.value = filterLinesForQuery(routes, it) + } + } } fun setFavoritesLinesIDs(linesIds: HashSet){ favoritesLinesIDs.value = linesIds } + val favoritesLines = favoritesLinesIDs.map {lineIds -> val linesList = ArrayList() if (lineIds.size == 0 || routesLiveData.value==null) return@map linesList for(line in routesLiveData.value!!){ if(lineIds.contains(line.gtfsId)) linesList.add(line) } linesList.sortWith(linesComparator) return@map linesList } } \ No newline at end of file diff --git a/app/src/main/res/drawable/magnifying_glass.xml b/app/src/main/res/drawable/magnifying_glass.xml new file mode 100644 index 0000000..630d02d --- /dev/null +++ b/app/src/main/res/drawable/magnifying_glass.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/app/src/main/res/layout/fragment_lines_grid.xml b/app/src/main/res/layout/fragment_lines_grid.xml index 60889a7..99df4c5 100644 --- a/app/src/main/res/layout/fragment_lines_grid.xml +++ b/app/src/main/res/layout/fragment_lines_grid.xml @@ -1,176 +1,177 @@ + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_search.xml b/app/src/main/res/menu/menu_search.xml new file mode 100644 index 0000000..84a90f8 --- /dev/null +++ b/app/src/main/res/menu/menu_search.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index ee594f0..3b77f47 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -1,309 +1,312 @@ Stai utilizzando l\'ultimo ritrovato in materia di rispetto della tua privacy. Cerca QR Code Si No Prossimo Precedente Installare Barcode Scanner? Questa azione richiede un\'altra app per scansionare i codici QR. Vuoi installare Barcode Scanner? Numero fermata Nome fermata Inserisci il numero della fermata Inserisci il nome della fermata Verifica l\'accesso ad Internet! Sembra che nessuna fermata abbia questo nome Nessun passaggio trovato alla fermata Errore di lettura del sito 5T/GTT (dannato sito!) Fermata: %1$s Linea Linee Linee urbane Linee extraurbane Linee turistiche Direzione: + Nessuna linea in questa categoria + Nessuna linea corrisponde alla ricerca + Filtra per nome Linea: %1$s Linee: %1$s Scegli la fermata… Nessun passaggio Nessun QR code trovato, prova ad usare un\'altra app Preferiti Aiuto Informazioni Più informazioni Contribuisci https://gitpull.it/w/librebusto/it/ Codice sorgente Licenza Incontra l\'autore Fermata aggiunta ai preferiti Impossibile aggiungere ai preferiti (memoria piena o database corrotto?)! Preferiti Mappa Nessun preferito? Arghh!\nSchiaccia sulla stella di una fermata per aggiungerla a questa lista! Rimuovi Rinomina Rinomina fermata Reset Informazioni Tocca la stella per aggiungere la fermata ai preferiti\n\nCome leggere gli orari: \n   12:56* Orario in tempo reale\n   12:56   Orario programmato\n\nTrascina giù per aggiornare l\'orario. \nTocca a lungo su Fonte Orari per cambiare sorgente degli orari di arrivo. OK! Benvenuto/a/ə!

Grazie per aver scelto BusTO, un\'app open source e indipendente da GTT/5T, per spostarsi a Torino attraverso software libero!

BusTO rispetta la tua privacy non raccogliendo nessun dato sull\'utilizzo, ed è leggera e senza pubblicità!


Qui puoi trovare più informazioni e link riguardo al progetto.


Schermata iniziale

Se vuoi rivedere la schermata iniziale, usa il pulsante qui sotto: ]]> Notizie e aggiornamenti

Nel canale Telegram puoi trovare informazioni sugli ultimi aggiornamenti dell\'app

]]>
Ma come funziona?

Quest\'app ottiene i passaggi dei bus, le fermate e altre informazioni utili unendo dati forniti dal sito www.gtt.to.it, www.5t.torino.it, muoversiatorino.it "per uso personale" e altre fonti Open Data (aperto.comune.torino.it).


Ingredienti:
- Fabio Mazza attuale rockstar developer anziano.
- Andrea Ugo attuale rockstar developer in formazione.
- Silviu Chiriac designer del logo 2021.
- Marco M formidabile tester e cacciatore di bug.
- Ludovico Pavesi ex rockstar developer anziano asd.
- Valerio Bozzolan attuale manutentore.
- Marco Gagino apprezzato ex collaboratore, ideatore icona e grafica.
- JSoup libreria per "web scaping".
- Google icone e librerie di supporto e design.
- Altre icone da Bootstrap, Feather e Hero Icons
- Tutti i contributori e i beta tester!


Se vuoi avere più informazioni tecniche e contribuire allo sviluppo, usa i pulsanti qui sotto! ]]>
Licenze

L\'app e il relativo codice sorgente sono distribuiti sotto la licenza GNU General Public License v3 (https://www.gnu.org/licenses/gpl-3.0.html). Ciò significa che puoi usare, studiare, migliorare e ricondividere quest\'app con qualunque mezzo e per qualsiasi scopo: a patto di mantenere sempre questi diritti a tua volta e di dare credito a Valerio Bozzolan e agli altri autori del codice dell\'app.


Note

Quest\'applicazione è rilasciata nella speranza che sia utile a tutti ma senza NESSUNA garanzia sul suo funzionamento attuale e/o futuro.

Tutti i dati utilizzati dall\'app provengono direttamente da GTT o da simili agenzie pubbliche: se trovi che sono inesatti per qualche motivo, ti invitiamo a rivolgerti a loro.

Buon utilizzo! :)

]]>
Nome troppo corto, digita più caratteri e riprova %1$s verso %2$s %s (destinazione sconosciuta) Errore interno inaspettato, impossibile estrarre dati dal sito GTT/5T Visualizza sulla mappa Non trovo un\'applicazione dove mostrarla Posizione della fermata non trovata Fermate vicine Ricerca della posizione Nessuna fermata nei dintorni Preferenze Aggiornamento del database… Aggiornamento del database Aggiornamento database forzato Tocca per aggiornare ora il database Numero minimo di fermate Il numero di fermate da ricercare non è valido Valore errato, inserisci un numero Impostazioni Distanza massima di ricerca (m) Funzionalità sperimentali Impostazioni Generali Fermate recenti Impostazioni generali Gestione del database Comincia aggiornamento manuale del database Consenti l\'accesso alla posizione per mostrarla sulla mappa Consenti l\'accesso alla posizione per mostrare le fermate vicine Abilitare il GPS arriva alle alla fermata Mostra arrivi Mostra fermate Arrivi qui vicino Fermata rimossa dai preferiti Canale telegram Mostra introduzione La mia posizione Segui posizione Attiva o disattiva posizione Posizione attivata Posizione disattivata La posizione è disabilitata sul dispositivo Fonte orari: %1$s App GTT Sito GTT Sito 5T Torino App Muoversi a Torino Sconosciuta Fonti orari di arrivo Scegli le fonti di orari da usare Cambiamento sorgente orari… Premi a lungo per cambiare la sorgente degli orari Nessun passaggio per le linee: Canale default delle notifiche Operazioni sul database Informazioni sul database (aggiornamento) BusTO - posizioni in tempo reale Posizioni in tempo reale Attività del servizio delle posizioni in tempo reale Servizio posizioni MaTO in tempo reale attivo Downloading trips from MaTO server Chiesto troppe volte per il permesso %1$s Non si può usare questa funzionalità senza il permesso di archivio di archivio Un bug ha fatto crashare l\'app! \nPremi \"OK\" per inviare il report agli sviluppatori via email, così potranno scovare e risolvere il tuo bug! \nIl report contiene piccole informazioni non sensibili sulla configurazione del tuo telefono e sullo stato dell\'app al momento del crash. L\'applicazione è crashata, e il crash report è stato messo negli allegati. Se vuoi, descrivi cosa stavi facendo prima che si interrompesse: \n Arrivi Mappa Preferiti Apri drawer Chiudi drawer Esperimenti Offrici un caffè Mappa Ricerca fermate Versione app Orari di arrivo Richiesto aggiornamento del database Download dati dal server MaTO Mostra direzioni in maiuscolo Non cambiare Tutto in maiuscolo Solo la prima lettera maiuscola Mostra arrivi quando tocchi una fermata Abilita esperimenti Schermata da mostrare all\'avvio Tocca per cambiare Fonte posizioni in tempo reale di bus e tram MaTO (aggiornate più spesso, può non funzionare) GTFS RT (più stabile) Linea aggiunta ai preferiti Linea rimossa dai preferiti Preferite Tocca a lungo la fermata per le opzioni Rimuovi i dati dei trip (libera spazio) Tutti i trip GTFS sono rimossi dal database Mostra introduzione open source per il trasporto pubblico di Torino. Stai usando un\'app indipendente, senza pubblicità e senza nessun tracciamento. ]]> Se ti trovi a una fermata, puoi scansionare il codice QR presente sulla palina toccando l\'icona a sinistra della barra di ricerca.]]> preferiti toccando la stella a fianco del nome.]]> fermate più vicine a te direttamente nella schermata principale...]]> posizioni in tempo reale dei bus e tram (in blu)]]> Guarda nelle Impostazioni per personalizzare l\'app come preferisci, e su Informazioni per sapere di più sull\'app e il team di sviluppo.]]> Capito, chiudi introduzione Chiudi introduzione Abilita accesso alla posizione Accesso alla posizione abilitato Accesso alla posizione non consentito dall\'utente Abilita notifiche Notifiche abilitate Backup e ripristino Importa / esporta dati Dati salvati Salva backup Importa i dati dal backup Backup importato Seleziona almeno un elemento da importare! Importa preferiti dal backup Importa preferenze dal backup
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c928bfc..1425f66 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,336 +1,340 @@ BusTO Libre BusTO BusTO dev BusTO git You\'re using the latest in technology when it comes to respecting your privacy. Search Scan QR Code Yes No Next Previous Install Barcode Scanner? This application requires an app to scan the QR codes. Would you like to install Barcode Scanner now? Bus stop number Bus stop name Insert bus stop number Insert bus stop name %1$s towards %2$s %s (unknown destination) Verify your Internet connection! Seems that no bus stop have this name No arrivals found for this stop Error parsing the 5T/GTT website (damn site!) Name too short, type more characters and retry Arrivals at: %1$s Choose the bus stop… Line Lines Urban lines Extra urban lines Tourist lines + No lines found in this category + No lines match the searched name + Destination: Lines: %1$s Line: %1$s No timetable found No QR code found, try using another app to scan Unexpected internal error, cannot extract data from GTT/5T website Help About the app More about Contribute https://gitpull.it/w/librebusto/en/ Source code Licence11 Meet the author Bus stop is now in your favorites Bus stop removed from your favorites Added line to favorites Remove line from favorites Favorites Favorites Favorites Map No favorites? Arghh! Press on a bus stop star to populate this list! Delete Rename Rename the bus stop Reset About the app Tap the star to add the bus stop to the favourites\n\nHow to read timelines:\n   12:56* Real-time arrivals\n   12:56   Scheduled arrivals\n\nPull down to refresh the timetable \n Long press on Arrivals source to change the source of the arrival times GOT IT! Arrival times No arrivals found for lines: Welcome!

Thanks for using BusTO, an open source and independent app useful to move around Torino using a Free/Libre software.


Why use this app?

- You\'ll never be tracked
- You\'ll never see boring ads
- We\'ll always respect your privacy
- Moreover, it\'s lightweight!


Introductory tutorial

If you want to see the introduction again, use the button below:

]]>
News and Updates

On the Telegram channel, you can find information about the latest app updates

]]>
How does it work?

This app is able to do all the amazing things it does by pulling data from www.gtt.to.it, www.5t.torino.it or muoversiatorino.it "for personal use", along with open data from the AperTO (aperto.comune.torino.it) website.


The work of several people is behind this app, in particular:
- Fabio Mazza, current senior rockstar developer.
- Andrea Ugo, current junior rockstar developer.
- Silviu Chiriac, designer of the 2021 logo.
- Marco M, rockstar tester and bug hunter.
- Ludovico Pavesi, previous senior rockstar developer (asd).
- Valerio Bozzolan, maintainer and infrastructure (sponsor).
- Marco Gagino, contributor and first icon creator.
- JSoup web scraper library.
- makovkastar floating buttons.
- Google for icons and support and design libraries.
- Other icons from Bootstrap, Feather, and Hero Icons.
- All the contributors, and the beta testers, too!


If you want more technical information or to contribute to development, use the buttons below! ]]>
Licenses

The app and the related source code are released by Valerio Bozzolan and the other authors under the terms of the GNU General Public License v3+). So everyone is allowed to use, to study, to improve and to share this app by any kind of means and for any purpose: under the conditions of maintaining this rights and of attributing the original work to Valerio Bozzolan.


Notes

This app has been developed with the hope to be useful to everyone, but comes without ANY warranty of any kind.

The data used by the app comes directly from GTT and other public agencies: if you find any errors, please take it up to them, not to us.

This translation is kindly provided by Riccardo Caniato, Marco Gagino and Fabio Mazza.

Now you can hack public transport, too! :)

]]>
Cannot add to favorites (storage full or corrupted database?)! View on a map Cannot find any application to show it in Cannot find the position of the stop ListFragment - BusTO it.reyboz.bustorino.preferences db_is_updating Nearby stops Nearby connections App version The number of stops to show in the recent stops is invalid Invalid value, put a valid number Finding location No stops nearby Minimum number of stops Preferences Settings Settings General Experimental features Maximum distance (meters) Recent stops General settings Database management Launch manual database update Allow access to location to show it on the map Allow access to location to show stops nearby Please enable location on the device Database update in progress… Updating the database Force database update Touch to update the app database now is arriving at at the stop %1$s - %2$s Show arrivals Show stops Join Telegram channel Show introduction Center on my location Follow me Enable or disable location Location enabled Location disabled Location is disabled on device Arrivals source: %1$s GTT App GTT Website 5T Torino website Muoversi a Torino app Undetermined Changing arrival times source… Long press to change the source of arrivals @string/source_mato @string/fivetapifetcher @string/gttjsonfetcher @string/fivetscraper Sources of arrival times Select which sources of arrival times to use Default Default channel for notifications Database operations Updates of the app database BusTO - live position service Live positions Showing activity related to the live positions service MaTO live bus positions service is running Downloading trips from MaTO server Asked for %1$s permission too many times Cannot use the map with the storage permission! storage The application has crashed because you encountered a bug. \nIf you want, you can help the developers by sending the crash report via email. \nNote that no sensitive data is contained in the report, just small bits of info on your phone and app configuration/state. The application crashed and the crash report is in the attachments. Please describe what you were doing before the crash: \n Arrivals Map Favorites Open navigation drawer Close navigation drawer Experiments Buy us a coffee Map Search by stop + Filter by name Launching database update Downloading data from MaTO server Capitalize directions Do not change arrivals directions Capitalize everything Capitalize only first letter KEEP CAPITALIZE_ALL CAPITALIZE_FIRST Section to show on startup Touch to change it Show arrivals touching on stop Enable experiments Long press the stop for options @string/nav_arrivals_text @string/nav_favorites_text @string/nav_map_text @string/lines Source of real time positions for buses and trams MaTO (updated more frequently, might be offline) GTFS RT (more stable, less frequently updated) Remove trips data (free up space) All GTFS trips have been removed from the database Show tutorial open source app for Turin public transport. This is an independent app, with no ads and no tracking whatsoever.]]> favorites by touching the star next to its name]]> blue)]]> Settings to customize the app behaviour, and in the About the app section if you want to know more about the app and the developers.]]> Notifications permission to show the information about background processing. Press the button below to grant it]]> Grant location permission Location permission granted Location permission has not been granted OK, close the tutorial Close the tutorial Enable notifications Notifications enabled Backup and restore Import/export preferences Data saved Backup to file Import data from backup Backup has been imported Check at least one item to import! Import favorites from backup Import preferences from backup