diff --git a/app/src/main/java/it/reyboz/bustorino/ActivityExperiments.java b/app/src/main/java/it/reyboz/bustorino/ActivityExperiments.java
--- a/app/src/main/java/it/reyboz/bustorino/ActivityExperiments.java
+++ b/app/src/main/java/it/reyboz/bustorino/ActivityExperiments.java
@@ -76,9 +76,20 @@
}
@Override
- public void showMapCenteredOnStop(Stop stop) {
+ public void showMapCenteredOnStop(@Nullable Stop stop) {
}
+
+ @Override
+ public void openLinesFragment() {
+ Log.d(DEBUG_TAG, "Asked to open lines grid fragment");
+ }
+
+ @Override
+ public void openFavoritesFragment() {
+
+ }
+
@Override
public void openLineFromStop(String routeGtfsId, @Nullable String stopIDFrom){
@@ -101,4 +112,9 @@
tr.commit();
}
+ @Override
+ public void openNearbyStopsFragment() {
+ Log.d(DEBUG_TAG, "Requested to open nearby stops fragment");
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java b/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java
--- a/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java
+++ b/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java
@@ -242,21 +242,21 @@
Fragment f = getSupportFragmentManager().findFragmentById(R.id.mainActContentFrame);
Log.d(DEBUG_TAG, "OnCreate the fragment is "+f);
String vl = PreferenceManager.getDefaultSharedPreferences(this).getString(SettingsFragment.PREF_KEY_STARTUP_SCREEN, "");
- //if (vl.length() == 0 || vl.equals("arrivals")) {
- // showMainFragment();
+
Log.d(DEBUG_TAG, "The default screen to open is: "+vl);
if (showingArrivalsFromIntent){
//do nothing but exclude a case
}else if (savedInstanceState==null) {
+ var framan = getSupportFragmentManager();
//we are not restarting the activity from nothing
if (vl.equals("map")) {
requestMapFragment(false);
} else if (vl.equals("favorites")) {
- checkAndShowFavoritesFragment(getSupportFragmentManager(), false);
+ checkAndShowFavoritesFragment(framan, false);
} else if (vl.equals("lines")) {
- showLinesFragment(getSupportFragmentManager(), false, null);
+ showLinesFragment(framan, false, null);
} else {
- showMainFragment(false);
+ showMainFragmentFromClick(false);
}
}
onCreateComplete = true;
@@ -264,6 +264,7 @@
//last but not least, set the good default values
checkApplyDefaultSettingsValues();
// handle the device "insets"
+ /*
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.rootRelativeLayout), (v, windowInsets) -> {
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
// Apply the insets as a margin to the view. This solution sets only the
@@ -286,21 +287,7 @@
return WindowInsetsCompat.CONSUMED;
});
- /*
- ViewCompat.setOnApplyWindowInsetsListener(mToolbar, (v, windowInsets) -> {
- Insets statusBarInsets = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars());
- // Apply the insets as a margin to the view.
- ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
- mlp.topMargin = statusBarInsets.top;
- v.setLayoutParams(mlp);
- v.setPadding(0, statusBarInsets.top, 0, 0);
- // Return CONSUMED if you don't want the window insets to keep passing
- // down to descendant views.
- return WindowInsetsCompat.CONSUMED;
- });
-
- */
//to properly handle IME
WindowInsetsControllerCompat insetsController =
WindowCompat.getInsetsController(getWindow(), getWindow().getDecorView());
@@ -311,6 +298,23 @@
);
}
+ */
+ // Toolbar: solo inset superiore (status bar)
+ ViewCompat.setOnApplyWindowInsetsListener(mToolbar, (v, windowInsets) -> {
+ Insets statusBar = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars());
+ v.setPadding(0, statusBar.top, 0, 0);
+ return windowInsets; // NON consumare: passa gli insets ai figli
+ });
+
+ // Content frame: insets laterali e inferiori (navigation bar)
+ // I fragment figli riceveranno gli insets e potranno gestirli a loro volta
+ ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.mainActContentFrame), (v, windowInsets) -> {
+ Insets systemBars = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
+ // Solo left/right, bottom lo gestisce ogni fragment
+ v.setPadding(systemBars.left, 0, systemBars.right, 0);
+ return windowInsets; //WindowInsetsCompat.CONSUMED; // NON consumare: passa ai fragment
+ });
+
//check if first run activity (IntroActivity) has been started once or not
@@ -355,7 +359,7 @@
return true;
} else if(menuItem.getItemId() == R.id.nav_arrivals){
closeDrawerIfOpen();
- showMainFragment(true);
+ showMainFragmentFromClick(true);
return true;
} else if(menuItem.getItemId() == R.id.nav_map_item){
closeDrawerIfOpen();
@@ -529,7 +533,7 @@
}
/**
- * Show the fragment by adding it to the backstack
+ * Show the actual fragment by adding it to the backstack
* @param fraMan the fragmentManager
* @param fragment the fragment
*/
@@ -548,11 +552,12 @@
ft.commit();
}
/**
- * Show the fragment by adding it to the backstack
+ * Create a new MainFragment for the arguments provided and show it in the layout
* @param fraMan the fragmentManager
* @param arguments args for the fragment
*/
private static void createShowMainFragment(FragmentManager fraMan,@Nullable Bundle arguments, boolean addToBackStack){
+ //var frag = MainScreenFragment.newInstance();
FragmentTransaction ft = fraMan.beginTransaction()
.replace(R.id.mainActContentFrame, MainScreenFragment.class, arguments, MainScreenFragment.FRAGMENT_TAG)
.setReorderingAllowed(false)
@@ -566,6 +571,27 @@
if (addToBackStack) ft.addToBackStack(null);
ft.commit();
}
+ private void showMainFragmentFromClick(@Nullable Bundle argsToCreate, boolean addToBackStack){
+ FragmentManager fraMan = getSupportFragmentManager();
+ Fragment fragment = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG);
+ final MainScreenFragment mainScreenFragment;
+ if (fragment==null | !(fragment instanceof MainScreenFragment)){
+ createShowMainFragment(fraMan, argsToCreate, addToBackStack);
+ }
+ else if(!fragment.isVisible()){
+
+
+ mainScreenFragment = (MainScreenFragment) fragment;
+ showMainFragment(fraMan, mainScreenFragment, addToBackStack);
+ Log.d(DEBUG_TAG, "Found the main fragment");
+ } else{
+ mainScreenFragment = (MainScreenFragment) fragment;
+ }
+ }
+
+ private void showMainFragmentFromClick(boolean addToBackStack){
+ showMainFragmentFromClick(MainScreenFragment.makeArgsButtonsScreen(), addToBackStack);
+ }
private void requestMapFragment(final boolean allowReturn){
// starting from Android 11, we don't need to have the STORAGE permission anymore for the map cache
@@ -630,24 +656,6 @@
.commit();
}
- private void showMainFragment(boolean addToBackStack){
- FragmentManager fraMan = getSupportFragmentManager();
- Fragment fragment = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG);
- final MainScreenFragment mainScreenFragment;
- if (fragment==null | !(fragment instanceof MainScreenFragment)){
- createShowMainFragment(fraMan, null, addToBackStack);
- }
- else if(!fragment.isVisible()){
-
-
- mainScreenFragment = (MainScreenFragment) fragment;
- showMainFragment(fraMan, mainScreenFragment, addToBackStack);
- Log.d(DEBUG_TAG, "Found the main fragment");
- } else{
- mainScreenFragment = (MainScreenFragment) fragment;
- }
- //return mainScreenFragment;
- }
@Nullable
private MainScreenFragment getMainFragmentIfVisible(){
FragmentManager fraMan = getSupportFragmentManager();
@@ -661,6 +669,7 @@
public void showFloatingActionButton(boolean yes) {
//TODO
}
+
/*
public void setDrawerSelectedItem(String fragmentTag){
switch (fragmentTag){
@@ -683,7 +692,7 @@
if (mainFragmentIfVisible!=null){
mainFragmentIfVisible.readyGUIfor(fragmentType);
}
- int titleResId;
+ Integer titleResId = null;
switch (fragmentType){
case MAP:
mNavView.setCheckedItem(R.id.nav_map_item);
@@ -704,6 +713,7 @@
case MAIN_SCREEN_FRAGMENT:
case NEARBY_STOPS:
case NEARBY_ARRIVALS:
+ case HOME_BUTTONS:
titleResId=R.string.app_name_full;
mNavView.setCheckedItem(R.id.nav_arrivals);
break;
@@ -711,10 +721,8 @@
titleResId=R.string.lines;
mNavView.setCheckedItem(R.id.nav_lines_item);
break;
- default:
- titleResId = 0;
}
- if(getSupportActionBar()!=null && titleResId!=0)
+ if(getSupportActionBar()!=null && titleResId!=null)
getSupportActionBar().setTitle(titleResId);
}
@@ -737,9 +745,8 @@
probableFragment.requestArrivalsForStopID(ID);
} else {
// we have no fragment
- final Bundle args = new Bundle();
- args.putString(MainScreenFragment.PENDING_STOP_SEARCH, ID);
//if onCreate is complete, then we are not asking for the first showing fragment
+ final Bundle args = MainScreenFragment.makeArgsArrivals(ID);
boolean addtobackstack = onCreateComplete;
createShowMainFragment(fraMan, args ,addtobackstack);
}
@@ -773,6 +780,32 @@
tr.commit();
}
+ @Override
+ public void openNearbyStopsFragment() {
+ FragmentManager fraMan = getSupportFragmentManager();
+ var fragment = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG);
+ if(fragment instanceof MainScreenFragment mainFrag){
+ if(!mainFrag.isVisible()){
+ showMainFragment(fraMan, mainFrag, false);
+ }
+ mainFrag.openNearbyStopsFragment();
+ } else{
+ // there is no fragment and it is not visible
+ // add to back stack the main fragment, as the NearbyStopsFragment will not be added
+ createShowMainFragment(fraMan, MainScreenFragment.makeArgsNearby(), true);
+ }
+ }
+
+ @Override
+ public void openLinesFragment() {
+ showLinesFragment(getSupportFragmentManager(), true, null);
+ }
+
+ @Override
+ public void openFavoritesFragment() {
+ checkAndShowFavoritesFragment(getSupportFragmentManager(), true);
+ }
+
@Override
public void toggleSpinner(boolean state) {
MainScreenFragment probableFragment = getMainFragmentIfVisible();
@@ -791,10 +824,12 @@
@Override
- public void showMapCenteredOnStop(Stop stop) {
+ public void showMapCenteredOnStop(@Nullable Stop stop) {
createAndShowMapFragment(stop, true);
}
+
+
//Map Fragment stuff
void createAndShowMapFragment(@Nullable Stop stop, boolean addToBackStack){
final FragmentManager fm = getSupportFragmentManager();
diff --git a/app/src/main/java/it/reyboz/bustorino/adapters/RecyclerViewMargin.java b/app/src/main/java/it/reyboz/bustorino/adapters/RecyclerViewMargin.java
new file mode 100644
--- /dev/null
+++ b/app/src/main/java/it/reyboz/bustorino/adapters/RecyclerViewMargin.java
@@ -0,0 +1,145 @@
+/*
+ BusTO - UI 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.adapters;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.View;
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+import it.reyboz.bustorino.BuildConfig;
+import it.reyboz.bustorino.backend.utils;
+
+
+// based on the answer at https://stackoverflow.com/questions/37507937/margin-between-items-in-recycler-view-android
+
+/**
+ * Recycler view margin setter for the elements. If you call "addExternal", it will use the same margins on bordering elements
+ * towards the border (i.e., applying the margin on top for the first row, on right for the last columns, etc.)
+ */
+public class RecyclerViewMargin extends RecyclerView.ItemDecoration {
+
+ private final int margin;
+ private final int columns;
+
+ private boolean addExternal = false;
+ private static final String DEBUG_TAG = "BusTO-RecViewMargin";
+ /**
+ * constructor
+ * @param marginPx desirable margin size in px between the views in the recyclerView
+ * @param numColumns number of numColumns of the RecyclerView
+ */
+ public RecyclerViewMargin(@IntRange(from=0)int marginPx , @IntRange(from=0) int numColumns ) {
+ this.margin = marginPx;
+ this.columns=numColumns;
+
+ }
+ public static RecyclerViewMargin makeMarginsDip(@NonNull Context context,
+ @IntRange(from=0)int marginDip ,
+ @IntRange(from=0) int numColumns) {
+ return new RecyclerViewMargin(utils.convertDipToPixelInt(context, marginDip), numColumns);
+ }
+
+ public RecyclerViewMargin addExternal(){
+ addExternal = true;
+ return this;
+ }
+
+ /**
+ * Set different margins for the items inside the recyclerView: no top margin for the first row
+ * and no left margin for the first column.
+ */
+ @Override
+ public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
+ @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
+ var adapter = parent.getAdapter();
+ int nrows = adapter!=null ? (int)Math.ceil( (double) adapter.getItemCount() / columns) : -2;
+ int position = parent.getChildLayoutPosition(view);
+ if(BuildConfig.DEBUG)
+ Log.d(DEBUG_TAG, "getItemOffsets: position = " + position);
+ var sb = new StringBuilder();
+ //set right margin to all
+ if(position % columns != columns-1){
+ outRect.right = margin;
+ sb.append("right ");
+ }
+ int row = (int)((double) position / columns) ;
+ if(nrows == -2 || row < nrows-1){
+ outRect.bottom = margin;
+ sb.append("bottom ");
+ }
+ /*
+ //set right margin to all
+ outRect.right = margin;
+
+ //set bottom margin to all
+ outRect.bottom = margin;
+ //we only add top margin to the first row
+
+
+ */
+ if(addExternal){
+ if (position = columns){
+ outRect.top = margin;
+ sb.append("top ");
+ }
+ int row = (int)((double) position / columns) ;
+ if(nrows == -2 || row < nrows-1){
+ outRect.bottom = margin;
+ sb.append("bottom ");
+ }
+ Log.d(DEBUG_TAG, "margins put: " + sb.toString());
+ */
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/utils.java b/app/src/main/java/it/reyboz/bustorino/backend/utils.java
--- a/app/src/main/java/it/reyboz/bustorino/backend/utils.java
+++ b/app/src/main/java/it/reyboz/bustorino/backend/utils.java
@@ -67,9 +67,8 @@
return Math.toDegrees(distanceInMeters/ EARTH_RADIUS);
}
- public static int convertDipToPixelsInt(Context con,double dips)
- {
- return (int) (dips * con.getResources().getDisplayMetrics().density + 0.5f);
+ public static int convertDipToPixelInt(Context context, int dp) {
+ return Math.round(dp * context.getResources().getDisplayMetrics().density);
}
/**
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.kt
--- a/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.kt
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.kt
@@ -758,11 +758,15 @@
return null
}
- fun isFragmentForTheSameStop(p: Palina): Boolean {
- return if (tag != null) tag == getFragmentTag(p)
+ fun isFragmentForTheSameStop(stopID: String) : Boolean{
+ return if (tag != null) tag == getFragmentTag(stopID)
else false
}
+ fun isFragmentForTheSameStop(p: Palina): Boolean {
+ return isFragmentForTheSameStop(p.ID)
+ }
+
/**
* Request arrivals in the fragment
@@ -812,10 +816,13 @@
return fragment
}
+
+ //return "palina_" + p.ID
+
@JvmStatic
- fun getFragmentTag(p: Palina): String {
- return "palina_" + p.ID
- }
+ fun getFragmentTag(stopID: String) = "palina_$stopID"
+ @JvmStatic
+ fun getFragmentTag(p: Palina) = getFragmentTag(p.ID)
@JvmStatic
fun getArrivalsWorkID(stopID: String) = "arrivals_search_$stopID"
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/BarcodeFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/BarcodeFragment.kt
new file mode 100644
--- /dev/null
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/BarcodeFragment.kt
@@ -0,0 +1,52 @@
+package it.reyboz.bustorino.fragments
+
+import android.net.Uri
+import android.util.Log
+import android.widget.Toast
+import androidx.activity.result.ActivityResultCallback
+import androidx.core.net.toUri
+import it.reyboz.bustorino.R
+import it.reyboz.bustorino.backend.utils
+import it.reyboz.bustorino.middleware.BarcodeScanContract
+import it.reyboz.bustorino.middleware.BarcodeScanOptions
+import it.reyboz.bustorino.middleware.BarcodeScanUtils
+
+//TODO: This might be probably implemented as interface
+abstract class BarcodeFragment : ScreenBaseFragment(){
+
+ private val barcodeLauncher = registerForActivityResult(BarcodeScanContract(), ActivityResultCallback {
+ result ->
+ if (result != null && result.contents != null) {
+ //Toast.makeText(MyActivity.this, "Cancelled", Toast.LENGTH_LONG).show();
+ val uri: Uri
+ try {
+ uri = result.contents.toUri() // this apparently prevents NullPointerException. Somehow.
+ } catch (e: Exception) {
+ Log.w("BusTO-BarcodeFragment","Cannot read QR code",e)
+ if (context != null) Toast.makeText(
+ requireContext(),
+ R.string.no_qrcode, Toast.LENGTH_SHORT
+ ).show()
+ return@ActivityResultCallback
+ }
+ val busStopID = utils.getBusStopIDFromUri(uri)
+ onQrScanSuccess(busStopID)
+ } else {
+ if (context != null) Toast.makeText(
+ requireContext(), R.string.no_qrcode, Toast.LENGTH_SHORT
+ ).show()
+ }
+ })
+
+ abstract fun onQrScanSuccess(busIDToSearch: String)
+
+ protected fun launchBarcodeScan() {
+ val scanOptions = BarcodeScanOptions()
+ val intent = scanOptions.createScanIntent()
+ if (!BarcodeScanUtils.checkTargetPackageExists(getContext(), intent)) {
+ BarcodeScanUtils.showDownloadDialog(null, this)
+ } else {
+ barcodeLauncher.launch(scanOptions)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/ButtonsFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/ButtonsFragment.kt
new file mode 100644
--- /dev/null
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/ButtonsFragment.kt
@@ -0,0 +1,218 @@
+package it.reyboz.bustorino.fragments
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.fragment.app.Fragment
+import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.google.android.material.card.MaterialCardView
+import it.reyboz.bustorino.ActivitySettings
+import it.reyboz.bustorino.R
+import it.reyboz.bustorino.adapters.RecyclerViewMargin
+
+/**
+ * A simple [Fragment] subclass.
+ * Use the [ButtonsFragment.newInstance] factory method to
+ * create an instance of this fragment.
+ */
+class ButtonsFragment : BarcodeFragment() {
+
+ //private lateinit var gridLayout: GridLayout
+
+ private lateinit var recyclerView: RecyclerView
+
+ private var listener: CommonFragmentListener? = null
+ private lateinit var items: List
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ arguments?.let {
+
+ }
+ if(listener is FragmentListenerMain){
+ val ll = listener as FragmentListenerMain
+ ll.enableRefreshLayout(false)
+ }
+ }
+ private val marginHoriz = 30
+ private val margin = 22
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ // Inflate the layout for this fragment
+ val root = inflater.inflate(R.layout.fragment_buttons, container, false)
+ items = listOf(
+ CardMenuItem(CardAction.NEARBY, getString(R.string.nearby_message_home_card), R.drawable.compass_3_fill),
+ CardMenuItem(CardAction.MAP, getString(R.string.map), R.drawable.map),
+ CardMenuItem(CardAction.FAVORITES_STOPS, getString(R.string.action_favorites), R.drawable.ic_star_filled_white),
+ CardMenuItem(CardAction.LINES, getString(R.string.lines), R.drawable.ic_moving_emph),
+ CardMenuItem(CardAction.SETTINGS, getString(R.string.action_settings), R.drawable.ic_baseline_settings_24),
+ CardMenuItem(CardAction.QR_SCAN, getString(R.string.scan_qr_code_stop), R.drawable.qr_code_scan)
+ )
+
+ recyclerView = root.findViewById(R.id.buttonsRecyclerView)
+
+ val gridLayoutManager = GridLayoutManager(requireContext(), 2)
+ recyclerView.layoutManager = gridLayoutManager
+
+ recyclerView.adapter = ActionsCardAdapter(items, this::onCardClicked)
+ val margins = RecyclerViewMargin.makeMarginsDip(requireContext(), margin, 2)
+ recyclerView.addItemDecoration(margins)
+
+
+ /*gridLayout = root.findViewById(R.id.homeGridLayout)
+
+ items.forEach { item ->
+ // Inflate base layout
+ val cardView = LayoutInflater.from(requireContext())
+ .inflate(R.layout.item_card_button, gridLayout, false)
+
+ // Popola icona e testo
+ cardView.findViewById(R.id.cardIcon).setImageResource(item.iconRes)
+ cardView.findViewById(R.id.cardLabel).text = item.label
+ // Parametri griglia: colonna flessibile + margini
+ cardView.layoutParams = GridLayout.LayoutParams().apply {
+ width = 0
+ height = GridLayout.LayoutParams.WRAP_CONTENT
+ columnSpec = GridLayout.spec(GridLayout.UNDEFINED, 1f)
+ setMargins(marginHoriz, marginVer, marginHoriz, marginVer) // margini tra le card
+ }
+
+ // Click
+ cardView.setOnClickListener { onCardClicked(item) }
+
+ gridLayout.addView(cardView)
+ }
+
+ */
+
+ return root
+ }
+
+
+ private fun onCardClicked(item: CardMenuItem) {
+ Log.d(DEBUG_TAG, "onCardClicked - item: ${item}, listener: ${listener}")
+ // reagisci al tap
+ val list = listener
+ if(list == null){
+ Log.w(DEBUG_TAG, "onCardClicked - listener is null")
+ } else
+ when(item.action) {
+ CardAction.NEARBY -> {
+ list.openNearbyStopsFragment()
+ }
+ CardAction.MAP -> { list.showMapCenteredOnStop(null)}
+ CardAction.LINES -> { list.openLinesFragment();}
+ CardAction.SETTINGS -> { startActivity(Intent(requireContext(), ActivitySettings::class.java)) }
+ CardAction.FAVORITES_STOPS -> { list.openFavoritesFragment() }
+ CardAction.QR_SCAN -> {
+ launchBarcodeScan()
+ }
+ }
+ }
+
+ override fun onQrScanSuccess(busIDToSearch: String) {
+ listener?.let {
+ it.requestArrivalsForStopID(busIDToSearch)
+ } ?: Log.d(DEBUG_TAG, "onQrScanSuccess - listener is null")
+ }
+
+ override fun getBaseViewForSnackBar(): View? {
+ return null
+ }
+
+ override fun onAttach(context: Context) {
+ super.onAttach(context)
+ if (context is CommonFragmentListener) {
+ listener = context
+ Log.d(DEBUG_TAG, "onAttach")
+ } else{
+ throw RuntimeException("$context must implement CommonFragmentListener")
+ }
+ }
+
+ override fun onDetach() {
+ listener = null
+ Log.d(DEBUG_TAG, "onDetach")
+ super.onDetach()
+ }
+
+ override fun onResume() {
+ super.onResume()
+ listener?.readyGUIfor(FragmentKind.HOME_BUTTONS)
+ }
+
+ companion object {
+ /**
+ * @return A new instance of fragment ButtonsFragment.
+ */
+ @JvmStatic
+ fun newInstance() =
+ ButtonsFragment().apply {
+ arguments = Bundle().apply {
+ }
+ }
+ const val DEBUG_TAG = "BusTO-ButtonsFragment"
+
+
+ const val FRAGMENT_TAG = "HomeButtonsFragment"
+ }
+ data class CardMenuItem(
+ val action: CardAction,
+ val label: String,
+ val iconRes: Int
+ )
+ enum class CardAction {
+ NEARBY, MAP, FAVORITES_STOPS, LINES, SETTINGS, QR_SCAN
+ }
+}
+
+class ActionsCardAdapter(val actions: List,
+ val listener: (ButtonsFragment.CardMenuItem) -> Unit) :
+ RecyclerView.Adapter() {
+ override fun onCreateViewHolder(
+ parent: ViewGroup,
+ viewType: Int
+ ): ViewHolder {
+ val view = LayoutInflater.from(parent.context).inflate(R.layout.item_card_button, parent, false)
+ /*
+ // Altezza match_parent per uniformare le card della stessa riga
+ view.layoutParams = RecyclerView.LayoutParams(
+ RecyclerView.LayoutParams.MATCH_PARENT,
+ RecyclerView.LayoutParams.MATCH_PARENT
+ )
+ */
+
+ return ViewHolder(view)
+ }
+
+ override fun onBindViewHolder(
+ holder: ViewHolder,
+ position: Int
+ ) {
+ val item = actions[position]
+
+ holder.imgView.setImageResource(item.iconRes)
+ holder.textView.text = item.label
+
+ holder.cardView.setOnClickListener { listener(item) }
+ }
+
+ override fun getItemCount() = actions.size
+
+
+ inner class ViewHolder(val view: View): RecyclerView.ViewHolder(view) {
+ val textView = view.findViewById(R.id.cardLabel)
+ val imgView: ImageView = view.findViewById(R.id.cardIcon)
+ val cardView: MaterialCardView = view.findViewById(R.id.buttonCardView)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/CommonFragmentListener.java b/app/src/main/java/it/reyboz/bustorino/fragments/CommonFragmentListener.java
--- a/app/src/main/java/it/reyboz/bustorino/fragments/CommonFragmentListener.java
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/CommonFragmentListener.java
@@ -35,7 +35,7 @@
* We want to open the map on the specified stop
* @param stop needs to have location data (latitude, longitude)
*/
- void showMapCenteredOnStop(Stop stop);
+ void showMapCenteredOnStop(@Nullable Stop stop);
/**
* We want to show the line in detail for route coming from a stop
@@ -50,4 +50,16 @@
* @param args extra arguments given as Bundle
*/
void openLineFromVehicle(String routeGtfsId, @Nullable String optionalPatternId, @Nullable Bundle args);
+
+ /**
+ * Show the nearby stops fragment
+ */
+ void openNearbyStopsFragment();
+
+ /**
+ * Show the lines
+ */
+ void openLinesFragment();
+
+ void openFavoritesFragment();
}
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/FragmentHelper.java b/app/src/main/java/it/reyboz/bustorino/fragments/FragmentHelper.java
--- a/app/src/main/java/it/reyboz/bustorino/fragments/FragmentHelper.java
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/FragmentHelper.java
@@ -20,11 +20,12 @@
import android.content.Context;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
-import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;
@@ -50,7 +51,6 @@
public static final int NO_FRAME = -3;
private static final String DEBUG_TAG = "BusTO FragmHelper";
private final StopSearcher stopSearcher;
- private boolean shouldHaltAllActivities=false;
public FragmentHelper(FragmentListenerMain listener, FragmentManager framan, Context context, int mainFrame) {
@@ -84,48 +84,43 @@
* Called when you need to create a fragment for a specified Palina
* @param p the Stop that needs to be displayed
*/
- public void createOrUpdateStopFragment(Palina p, boolean addToBackStack){
- boolean sameFragment;
+ public void showArrivalsFragmentForStop(@NonNull Palina p, boolean addToBackStack){
+ boolean sameFragment = false;
ArrivalsFragment arrivalsFragment = null;
+ final FragmentManager fm = managerWeakRef.get();
+ if(fm == null) return;
- if(managerWeakRef.get()==null || shouldHaltAllActivities) {
- //SOMETHING WENT VERY WRONG
- Log.e(DEBUG_TAG, "We are asked for a new stop but we can't show anything");
- return;
- }
-
- FragmentManager fm = managerWeakRef.get();
-
- if(fm.findFragmentById(primaryFrameLayout) instanceof ArrivalsFragment) {
- arrivalsFragment = (ArrivalsFragment) fm.findFragmentById(primaryFrameLayout);
- //Log.d(DEBUG_TAG, "Arrivals are for fragment with same stop?");
- if (arrivalsFragment == null) sameFragment = false;
- else sameFragment = arrivalsFragment.isFragmentForTheSameStop(p);
- } else {
- sameFragment = false;
- Log.d(DEBUG_TAG, "We aren't showing an ArrivalsFragment");
+ if(fm.findFragmentById(primaryFrameLayout) instanceof ArrivalsFragment frag) {
+ sameFragment = frag.isFragmentForTheSameStop(p);
+ if(sameFragment) {
+ arrivalsFragment = frag;
+ Log.d("BusTO", "Same bus stop, accessing existing fragment");
+ }
}
- setLastSuccessfullySearchedBusStop(p);
- if (sameFragment){
- Log.d("BusTO", "Same bus stop, accessing existing fragment");
- arrivalsFragment = (ArrivalsFragment) fm.findFragmentById(primaryFrameLayout);
- if (arrivalsFragment == null) sameFragment = false;
- }
- if(!sameFragment) {
- //set the String to be displayed on the fragment
- String displayName = p.getStopDisplayName();
- if (displayName != null && displayName.length() > 0) {
- arrivalsFragment = ArrivalsFragment.newInstance(p.ID,displayName);
- } else {
- arrivalsFragment = ArrivalsFragment.newInstance(p.ID);
+ if(!sameFragment) {
+ // get old fragment
+ var frag = fm.findFragmentByTag(ArrivalsFragment.getFragmentTag(p));
+ if(frag instanceof ArrivalsFragment) {
+ attachFragmentToContainer(fm, frag, null, true, addToBackStack);
+ arrivalsFragment = (ArrivalsFragment) frag;
+ } else { // create new fragment
+ //set the String to be displayed on the fragment
+ String displayName = p.getStopDisplayName();
+ if (displayName != null && !displayName.isEmpty()) {
+ arrivalsFragment = ArrivalsFragment.newInstance(p.ID, displayName);
+ } else {
+ arrivalsFragment = ArrivalsFragment.newInstance(p.ID);
+ }
+ String probableTag = ArrivalsFragment.getFragmentTag(p);
+ attachFragmentToContainer(fm, arrivalsFragment, probableTag, true, addToBackStack);
}
- String probableTag = ArrivalsFragment.getFragmentTag(p);
- attachFragmentToContainer(fm,arrivalsFragment,new AttachParameters(probableTag, true, addToBackStack));
}
- // DO NOT CALL `setListAdapter` ever on arrivals fragment
- arrivalsFragment.updateFragmentData(p);
+ setLastSuccessfullySearchedBusStop(p);
+ // update the data only if I have information about the passaggi
+ if(p.getTotalNumberOfPassages() > 0)
+ arrivalsFragment.updateFragmentData(p);
// enable fragment auto refresh
arrivalsFragment.setReloadOnResume(true);
@@ -141,13 +136,13 @@
public void createStopListFragment(List resultList, String query, boolean addToBackStack){
listenerMain.hideKeyboard();
StopListFragment listfragment = StopListFragment.newInstance(query);
- if(managerWeakRef.get()==null || shouldHaltAllActivities) {
+ if(managerWeakRef.get()==null) {
//SOMETHING WENT VERY WRONG
Log.e(DEBUG_TAG, "We are asked for a new stop but we can't show anything");
return;
}
- attachFragmentToContainer(managerWeakRef.get(),listfragment,
- new AttachParameters("search_"+query, false,addToBackStack));
+ attachFragmentToContainer(managerWeakRef.get(),
+ listfragment, "search_"+query, false, addToBackStack);
listfragment.setStopList(resultList);
//listenerMain.readyGUIfor(FragmentKind.STOPS);
toggleSpinner(false);
@@ -163,37 +158,35 @@
}
/**
- * Attach a new fragment to a cointainer
+ * Attach a new fragment to the appropriate container
* @param fm the FragmentManager
* @param fragment the Fragment
- * @param parameters attach parameters
+ * @param tagAttach attach tag (can be null, the fragment's own tag has preference)
+ * @param addToBackStack if the transaction is to be added to the stack
+ * @param toSecondaryFrame if the fragment goes to the secondary frame
*/
- protected void attachFragmentToContainer(FragmentManager fm,Fragment fragment, AttachParameters parameters){
- if(shouldHaltAllActivities) //nothing to do
- return;
+ protected void attachFragmentToContainer(FragmentManager fm, Fragment fragment, @Nullable String tagAttach, boolean toSecondaryFrame, boolean addToBackStack){
+
FragmentTransaction ft = fm.beginTransaction();
int frameID;
- if(parameters.attachToSecondaryFrame && secondaryFrameLayout!=NO_FRAME)
- // ft.replace(secondaryFrameLayout,fragment,tag);
+ if(toSecondaryFrame && secondaryFrameLayout!=NO_FRAME)
frameID = secondaryFrameLayout;
- else frameID = primaryFrameLayout;
- switch (parameters.transaction){
- case REPLACE:
- ft.replace(frameID,fragment,parameters.tag);
-
- }
- if (parameters.addToBackStack)
- ft.addToBackStack("state_"+parameters.tag);
+ else
+ frameID = primaryFrameLayout;
+ var tag = fragment.getTag();
+ if(tag == null) tag = tagAttach;
+ // there is only one case
+ //switch (pars.transaction){
+ // case REPLACE:
+ ft.replace(frameID,fragment,tag);
+ //}
+ if (addToBackStack)
+ ft.addToBackStack("state_"+tag);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE);
- if(!fm.isDestroyed() && !shouldHaltAllActivities)
- ft.commit();
+ ft.commit();
//fm.executePendingTransactions();
}
- public synchronized void setBlockAllActivities(boolean shouldI) {
- this.shouldHaltAllActivities = shouldI;
- }
-
public void stopLastRequestIfNeeded(){
/*if(lastTaskRef == null) return;
AsyncTask task = lastTaskRef.get();
@@ -260,11 +253,12 @@
private void showShortToast(int messageID){
showToastMessage(messageID, true);
}
-
+ /*
+ // 18/05/2026: Commenting, do not remove, might be useful later
enum Transaction{
REPLACE,
}
- static final class AttachParameters {
+ private static final class AttachParameters {
String tag;
boolean attachToSecondaryFrame;
Transaction transaction;
@@ -284,4 +278,6 @@
this.transaction = Transaction.REPLACE;
}
}
+
+ */
}
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/FragmentKind.java b/app/src/main/java/it/reyboz/bustorino/fragments/FragmentKind.java
--- a/app/src/main/java/it/reyboz/bustorino/fragments/FragmentKind.java
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/FragmentKind.java
@@ -19,5 +19,5 @@
public enum FragmentKind {
STOPS,ARRIVALS,FAVORITES,NEARBY_STOPS,NEARBY_ARRIVALS, MAP, MAIN_SCREEN_FRAGMENT,
- LINES
+ LINES, HOME_BUTTONS
}
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java
--- a/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java
@@ -33,12 +33,13 @@
import androidx.appcompat.widget.AppCompatImageButton;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
+import androidx.lifecycle.ViewModelProvider;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
@@ -53,16 +54,17 @@
import com.google.android.material.floatingactionbutton.FloatingActionButton;
-import java.util.List;
+import java.security.InvalidParameterException;
import java.util.Map;
import it.reyboz.bustorino.R;
import it.reyboz.bustorino.backend.*;
-import it.reyboz.bustorino.data.PreferencesHolder;
import it.reyboz.bustorino.middleware.BarcodeScanContract;
import it.reyboz.bustorino.middleware.BarcodeScanOptions;
import it.reyboz.bustorino.middleware.BarcodeScanUtils;
import it.reyboz.bustorino.util.Permissions;
+import it.reyboz.bustorino.viewmodels.IntroViewModel;
+import org.jetbrains.annotations.NotNull;
import static it.reyboz.bustorino.backend.utils.getBusStopIDFromUri;
import static it.reyboz.bustorino.util.Permissions.LOCATION_PERMISSIONS;
@@ -73,17 +75,36 @@
* Use the {@link MainScreenFragment#newInstance} factory method to
* create an instance of this fragment.
*/
-public class MainScreenFragment extends ScreenBaseFragment implements FragmentListenerMain{
+public class MainScreenFragment extends BarcodeFragment implements FragmentListenerMain{
private static final String SAVED_FRAGMENT="saved_fragment";
private static final String DEBUG_TAG = "BusTO - MainFragment";
- public static final String PENDING_STOP_SEARCH="PendingStopSearch";
+ public static final String ARG_INITIAL_CONTENT = "initial_content";
+ public static final String ARG_STOP_ID = "pending_stop_id";
+ public static final String ARG_SEARCH_QUERY = "pending_search_query";
public final static String FRAGMENT_TAG = "MainScreenFragment";
+ private enum SearchMode {SEARCH_ID,SEARCH_NAME,INITIAL}
+ public enum InitialScreen {
+ HOME_BUTTONS(0),
+ NEARBY_STOPS(1),
+ ARRIVALS(2),
+ STOP_SEARCH(3);
+
+ public final int code;
+ InitialScreen(int code) { this.code = code; }
+
+ @Nullable
+ public static InitialScreen fromCode(int code) {
+ for (InitialScreen c : values()) if (c.code == code) return c;
+ return null;
+ }
+ }
+
private FragmentHelper fragmentHelper;
private SwipeRefreshLayout swipeRefreshLayout;
private EditText busStopSearchByIDEditText;
@@ -100,14 +121,12 @@
/*
* Search mode
*/
- private static final int SEARCH_BY_NAME = 0;
- private static final int SEARCH_BY_ID = 1;
- //private static final int SEARCH_BY_ROUTE = 2; // implement this -- DONE!
- private int searchMode;
+
+ private SearchMode searchMode = SearchMode.INITIAL;
//private ImageButton addToFavorites;
//// HIDDEN BUT IMPORTANT ELEMENTS ////
private FragmentManager childFragMan;
-
+ private IntroViewModel introViewModel;
private void refreshStop() {
if(getContext() == null){
Log.w(DEBUG_TAG,"Asked to refresh stop but context is null");
@@ -126,38 +145,12 @@
Log.w(DEBUG_TAG, "Asked to refresh stop when there is no fragment");
}
}
- //
- private final ActivityResultLauncher barcodeLauncher = registerForActivityResult(new BarcodeScanContract(),
- result -> {
- if(result!=null && result.getContents()!=null) {
- //Toast.makeText(MyActivity.this, "Cancelled", Toast.LENGTH_LONG).show();
- Uri uri;
- try {
- uri = Uri.parse(result.getContents()); // this apparently prevents NullPointerException. Somehow.
- } catch (NullPointerException e) {
- if (getContext()!=null)
- Toast.makeText(getContext().getApplicationContext(),
- R.string.no_qrcode, Toast.LENGTH_SHORT).show();
- return;
- }
- String busStopID = getBusStopIDFromUri(uri);
- busStopSearchByIDEditText.setText(busStopID);
- requestArrivalsForStopID(busStopID);
-
- } else {
- //Toast.makeText(MyActivity.this, "Scanned: " + result.getContents(), Toast.LENGTH_LONG).show();
- if (getContext()!=null)
- Toast.makeText(getContext().getApplicationContext(),
- R.string.no_qrcode, Toast.LENGTH_SHORT).show();
-
-
- }
- });
/// LOCATION STUFF ///
boolean pendingIntroRun = false;
boolean pendingNearbyStopsFragmentRequest = false;
+ boolean pendingNearbyAddToBackStack = false;
boolean locationPermissionGranted, locationPermissionAsked = false;
//AppLocationManager locationManager;
private final ActivityResultLauncher requestPermissionLauncher =
@@ -186,7 +179,7 @@
//showNearbyStopsFragment();
Log.d(DEBUG_TAG, "We have location permission");
if (pendingNearbyStopsFragmentRequest) {
- showNearbyFragmentIfPossible();
+ showNearbyFragmentIfPossible(pendingNearbyAddToBackStack);
pendingNearbyStopsFragmentRequest = false;
}
}
@@ -199,6 +192,8 @@
private CommonFragmentListener mListener;
private String pendingStopID = null;
+ private String pendingSearchQuery = null;
+ private InitialScreen initialScreen = InitialScreen.HOME_BUTTONS;
private CoordinatorLayout coordLayout;
public MainScreenFragment() {
@@ -206,19 +201,67 @@
}
- public static MainScreenFragment newInstance() {
- return new MainScreenFragment();
+ public static MainScreenFragment newInstance(@NonNull InitialScreen kind,
+ @Nullable String stopId,
+ @Nullable String query) {
+ MainScreenFragment f = new MainScreenFragment();
+ f.setArguments(makeArgs(kind, stopId, query));
+ return f;
+ }
+ public static MainScreenFragment newInstance(@NonNull InitialScreen kind, @Nullable Bundle args){
+ MainScreenFragment f = new MainScreenFragment();
+ if (args != null) {
+ f.setArguments(args);
+ }
+ return f;
}
+ /**
+ * Create the bundle for the arguments of the fragment
+ * @param kind the kind of initial screen
+ * @param stopId
+ * @param query
+ * @return
+ */
+ public static Bundle makeArgs(@NonNull InitialScreen kind, @Nullable String stopId, @Nullable String query) {
+ Bundle b = new Bundle();
+ b.putInt(ARG_INITIAL_CONTENT, kind.code);
+ if (stopId != null) b.putString(ARG_STOP_ID, stopId);
+ if (query != null) b.putString(ARG_SEARCH_QUERY, query);
+ return b;
+ }
+ public static Bundle makeArgsArrivals(@NonNull String stopID){
+ return makeArgs(InitialScreen.ARRIVALS, stopID, null);
+ }
+ public static Bundle makeArgsStops(@NonNull String query){
+ return makeArgs(InitialScreen.STOP_SEARCH, query, null);
+ }
+ public static Bundle makeArgsNearby(){
+ return makeArgs(InitialScreen.NEARBY_STOPS, null, null);
+ }
+ public static Bundle makeArgsButtonsScreen(){
+ return makeArgs(InitialScreen.HOME_BUTTONS, null, null);
+ }
+
+
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if (getArguments() != null) {
- //do nothing
- Log.d(DEBUG_TAG, "ARGS ARE NOT NULL: "+getArguments());
- if (getArguments().getString(PENDING_STOP_SEARCH)!=null)
- pendingStopID = getArguments().getString(PENDING_STOP_SEARCH);
+ Bundle args = getArguments();
+ if (args != null) {
+ Log.d(DEBUG_TAG, "ARGS ARE NOT NULL: "+ args);
+
+ if (args.containsKey(ARG_INITIAL_CONTENT)) {
+ int code = args.getInt(ARG_INITIAL_CONTENT, InitialScreen.HOME_BUTTONS.code);
+ InitialScreen parsed = InitialScreen.fromCode(code);
+ initialScreen = (parsed != null) ? parsed : InitialScreen.HOME_BUTTONS;
+ }
+ String stopId = args.getString(ARG_STOP_ID);
+ if (stopId != null) pendingStopID = stopId;
+ pendingSearchQuery = args.getString(ARG_SEARCH_QUERY);
}
+
}
@Override
@@ -259,9 +302,21 @@
swipeRefreshLayout.setColorSchemeResources(R.color.blue_500, R.color.orange_500);
coordLayout = root.findViewById(R.id.coord_layout);
-
+ floatingActionButton.setImageResource(R.drawable.magnifying_glass_larger);
floatingActionButton.setOnClickListener((this::onToggleKeyboardLayout));
+ busStopSearchByIDEditText.setOnFocusChangeListener((v, hasFocus) -> {
+ //Log.d(DEBUG_TAG, "stop search by ID has focus: " + hasFocus);
+ if(hasFocus)
+ setSearchModeBusStopID();
+ });
+
+ busStopSearchByNameEditText.setOnFocusChangeListener((v, hasFocus) -> {
+ //Log.d(DEBUG_TAG, "stop search by Name has focus: " + hasFocus);
+ if(hasFocus)
+ setSearchModeBusStopName();
+ });
+
AppCompatImageButton qrButton = root.findViewById(R.id.QRButton);
qrButton.setOnClickListener(this::onQRButtonClick);
@@ -273,7 +328,6 @@
childFragMan.addOnBackStackChangedListener(() -> Log.d("BusTO Main Fragment", "BACK STACK CHANGED"));
fragmentHelper = new FragmentHelper(this, getChildFragmentManager(), getContext(), R.id.resultFrame);
- setSearchModeBusStopID();
/*
cr.setAccuracy(Criteria.ACCURACY_FINE);
@@ -283,6 +337,10 @@
cr.setPowerRequirement(Criteria.NO_REQUIREMENT);
*/
//locationManager = AppLocationManager.getInstance(requireContext());
+ introViewModel = new ViewModelProvider(requireActivity()).get(IntroViewModel.class);
+ introViewModel.getIntroIsRunning().observe(getViewLifecycleOwner(), isRunning -> {
+ pendingIntroRun = isRunning;
+ });
Log.d(DEBUG_TAG, "OnCreateView, savedInstanceState null: "+(savedInstanceState==null));
@@ -307,8 +365,38 @@
*/
if (getChildFragmentManager().findFragmentById(R.id.resultFrame)!= null){
swipeRefreshLayout.setVisibility(View.VISIBLE);
+ // The child FragmentManager has restored its content — don't dispatch again
+ return;
}
+ if (savedInstanceState != null) return;
+
+ dispatchInitialContent();
+ }
+
+ /**
+ * Installs the initial child fragment based on the arguments supplied as arguments
+ */
+ private void dispatchInitialContent() {
+ switch (initialScreen) {
+ case NEARBY_STOPS:
+ showNearbyStopsFragmentChecking(false);
+ break;
+ case ARRIVALS:
+ // pendingStopID is consumed in onResume → requestArrivalsForStopID
+ break;
+ case STOP_SEARCH:
+ if (pendingSearchQuery != null && pendingSearchQuery.length() >= 2) {
+ fragmentHelper.requestStopSearch(pendingSearchQuery);
+ } else {
+ showButtonsFragment(true);
+ }
+ pendingSearchQuery = null;
+ break;
+ case HOME_BUTTONS:
+ default:
+ showButtonsFragment(true);
+ }
}
@Override
@@ -318,7 +406,7 @@
Fragment fragment = getChildFragmentManager().findFragmentById(R.id.resultFrame);
if (fragment!=null)
getChildFragmentManager().putFragment(outState, SAVED_FRAGMENT, fragment);
- if (fragmentHelper!=null) fragmentHelper.setBlockAllActivities(true);
+ //if (fragmentHelper!=null) fragmentHelper.setBlockAllActivities(true);
}
@@ -361,7 +449,6 @@
throw new RuntimeException(context
+ " must implement CommonFragmentListener");
}
-
}
@Override
public void onDetach() {
@@ -377,18 +464,9 @@
if (setupOnStart) {
if (pendingStopID==null){
- if(PreferencesHolder.hasIntroFinishedOneShot(requireContext())){
- Log.d(DEBUG_TAG, "Showing nearby stops");
- if(!checkLocationPermission()){
- requestLocationPermission();
- pendingNearbyStopsFragmentRequest = true;
- }
- else {
- showNearbyFragmentIfPossible();
- }
- } else {
- //The Introductory Activity is about to be started, hence pause the request and show later
- pendingIntroRun = true;
+ if(!pendingIntroRun){
+ //show the fragment
+ //showButtonsFragment();
}
}
@@ -400,48 +478,64 @@
}
}
+ private void showButtonsFragment(boolean addInsteadOfReplace){
+
+ swipeRefreshLayout.setVisibility(View.VISIBLE);
+ var ft = childFragMan.beginTransaction();
+ var frag = ButtonsFragment.newInstance();
+ if(addInsteadOfReplace)
+ ft.add(R.id.resultFrame,frag, ButtonsFragment.FRAGMENT_TAG);
+ else{
+ ft.replace(R.id.resultFrame, frag, ButtonsFragment.FRAGMENT_TAG);
+ ft.addToBackStack(null);
+ }
+ ft.commit();
+ }
+
+ private void showNearbyStopsFragmentChecking(boolean addToBackStack){
+ if(!checkLocationPermission()){
+ requestLocationPermission();
+ pendingNearbyStopsFragmentRequest = true;
+ pendingNearbyAddToBackStack = addToBackStack;
+ Log.d(DEBUG_TAG, "requesting location permission for nearby fragment");
+ }
+ else {
+ Log.d(DEBUG_TAG, "Showing nearby stops fragment");
+ showNearbyFragmentIfPossible(addToBackStack);
+ }
+ }
+
@Override
public void onResume() {
super.onResume();
final Context con = requireContext();
Log.w(DEBUG_TAG, "OnResume called, setupOnStart: "+ setupOnStart);
- //if (locationManager == null)
- // locationManager = AppLocationManager.getInstance(con);
//recheck the introduction activity has been run
- if(pendingIntroRun && PreferencesHolder.hasIntroFinishedOneShot(con)){
- //request position permission if needed
- if(!checkLocationPermission()){
- requestLocationPermission();
- pendingNearbyStopsFragmentRequest = true;
- }
- else {
- showNearbyFragmentIfPossible();
- }
- //deactivate flag
- pendingIntroRun = false;
- }
if(Permissions.bothLocationPermissionsGranted(con)){
Log.d(DEBUG_TAG, "Location permission OK");
- //if(!locationManager.isRequesterRegistered(requester))
- // locationManager.addLocationRequestFor(requester);
+
} //don't request permission
// if we have a pending stopID request, do it
Log.d(DEBUG_TAG, "Pending stop ID for arrivals: "+pendingStopID);
//this is the second time we are attaching this fragment ->
Log.d(DEBUG_TAG, "Waiting for new stop request: "+ suppressArrivalsReload);
- //TODO: if we come back to this from another fragment, and the user has given again the permission
- // for the Location, we should show the Nearby Stops
+
if(!suppressArrivalsReload && pendingStopID==null){
//none of the following cases are true
// check if we are showing any fragment
+ /*
+ //TODO: check if this is needed
final Fragment fragment = getChildFragmentManager().findFragmentById(R.id.resultFrame);
+
if(fragment==null || swipeRefreshLayout.getVisibility() != View.VISIBLE){
//we are not showing anything
if(Permissions.anyLocationPermissionsGranted(getContext())){
showNearbyFragmentIfPossible();
}
}
+
+ */
}
if (suppressArrivalsReload){
// we have to suppress the reloading of the (possible) ArrivalsFragment
@@ -462,7 +556,7 @@
}
mListener.readyGUIfor(FragmentKind.MAIN_SCREEN_FRAGMENT);
- fragmentHelper.setBlockAllActivities(false);
+ //fragmentHelper.setBlockAllActivities(false);
}
@@ -470,31 +564,29 @@
public void onPause() {
//mainHandler = null;
//locationManager.removeLocationRequestFor(requester);
- fragmentHelper.setBlockAllActivities(true);
+ //fragmentHelper.setBlockAllActivities(true);
fragmentHelper.stopLastRequestIfNeeded();
super.onPause();
}
-
-
/*
GUI METHODS
*/
+
+ @Override
+ public void onQrScanSuccess(@NotNull String busIDToSearch) {
+ busStopSearchByIDEditText.setText(busIDToSearch);
+ requestArrivalsForStopID(busIDToSearch);
+ }
+
/**
* QR scan button clicked
*
* @param v View QRButton clicked
*/
public void onQRButtonClick(View v) {
-
- BarcodeScanOptions scanOptions = new BarcodeScanOptions();
- Intent intent = scanOptions.createScanIntent();
- if(!BarcodeScanUtils.checkTargetPackageExists(getContext(), intent)){
- BarcodeScanUtils.showDownloadDialog(null, this);
- }else {
- barcodeLauncher.launch(scanOptions);
- }
+ launchBarcodeScan();
}
/**
@@ -504,11 +596,12 @@
*/
public void onSearchClick(View v) {
//final StopsFinderByName[] stopsFinderByNames = new StopsFinderByName[]{new GTTStopsFetcher(), new FiveTStopsFetcher()};
- if (searchMode == SEARCH_BY_ID) {
+ if (searchMode == SearchMode.SEARCH_ID) {
String busStopID = busStopSearchByIDEditText.getText().toString();
fragmentHelper.stopLastRequestIfNeeded();
requestArrivalsForStopID(busStopID);
- } else { // searchMode == SEARCH_BY_NAME
+ } else if (searchMode == SearchMode.SEARCH_NAME) {
+ // searchMode == SEARCH_BY_NAME
String query = busStopSearchByNameEditText.getText().toString();
query = query.trim();
if(getContext()!=null) {
@@ -525,17 +618,19 @@
}
public void onToggleKeyboardLayout(View v) {
-
- if (searchMode == SEARCH_BY_NAME) {
- setSearchModeBusStopID();
- if (busStopSearchByIDEditText.requestFocus()) {
- showKeyboard();
- }
- } else { // searchMode == SEARCH_BY_ID
- setSearchModeBusStopName();
- if (busStopSearchByNameEditText.requestFocus()) {
- showKeyboard();
- }
+ switch (searchMode){
+ case SEARCH_ID:
+ setSearchModeBusStopName();
+ if (busStopSearchByNameEditText.requestFocus()) {
+ showKeyboard();
+ }
+ break;
+ case SEARCH_NAME:
+ case INITIAL:
+ setSearchModeBusStopID();
+ if (busStopSearchByIDEditText.requestFocus()) {
+ showKeyboard();
+ }
}
}
@@ -548,12 +643,21 @@
public void showKeyboard() {
if(getActivity() == null) return;
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
- View view = searchMode == SEARCH_BY_ID ? busStopSearchByIDEditText : busStopSearchByNameEditText;
+ View view;
+ if(searchMode == SearchMode.SEARCH_ID)
+ view= busStopSearchByIDEditText;
+ else if(searchMode == SearchMode.SEARCH_NAME)
+ view = busStopSearchByNameEditText;
+ else{
+ Log.e(DEBUG_TAG, "Asking to show keyboard but SearchMode is "+searchMode+", ignoring");
+ return;
+ }
+
imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
}
private void setSearchModeBusStopID() {
- searchMode = SEARCH_BY_ID;
+ searchMode = SearchMode.SEARCH_ID;
busStopSearchByNameEditText.setVisibility(View.GONE);
busStopSearchByNameEditText.setText("");
busStopSearchByIDEditText.setVisibility(View.VISIBLE);
@@ -561,7 +665,7 @@
}
private void setSearchModeBusStopName() {
- searchMode = SEARCH_BY_NAME;
+ searchMode = SearchMode.SEARCH_NAME;
busStopSearchByIDEditText.setVisibility(View.GONE);
busStopSearchByIDEditText.setText("");
busStopSearchByNameEditText.setVisibility(View.VISIBLE);
@@ -613,25 +717,6 @@
//actionHelpMenuItem.setVisible(false);
}
- private void actuallyShowNearbyStopsFragment(){
- swipeRefreshLayout.setVisibility(View.VISIBLE);
- final Fragment existingFrag = childFragMan.findFragmentById(R.id.resultFrame);
- // fragment;
- if (!(existingFrag instanceof NearbyStopsFragment)){
- Log.d(DEBUG_TAG, "actually showing Nearby Stops Fragment");
- //there is no fragment showing
- final NearbyStopsFragment fragment = NearbyStopsFragment.newInstance(NearbyStopsFragment.FragType.STOPS);
-
- FragmentTransaction ft = childFragMan.beginTransaction();
-
- ft.replace(R.id.resultFrame, fragment, NearbyStopsFragment.FRAGMENT_TAG);
- if (getActivity()!=null && !getActivity().isFinishing())
- ft.commit();
- else Log.e(DEBUG_TAG, "Not showing nearby fragment because activity null or is finishing");
- }
- }
-
-
@Override
public void showFloatingActionButton(boolean yes) {
mListener.showFloatingActionButton(yes);
@@ -678,12 +763,27 @@
@Override
public void openLineFromStop(String routeGtfsId, @Nullable String stopIDFrom) {
//pass to activity
- mListener.openLineFromStop(routeGtfsId, stopIDFrom);
+ if(mListener!=null) mListener.openLineFromStop(routeGtfsId, stopIDFrom);
}
@Override
public void openLineFromVehicle(String routeGtfsId, @Nullable String optionalPatternId, @Nullable Bundle args) {
- mListener.openLineFromVehicle(routeGtfsId, optionalPatternId, args);
+ if(mListener!=null) mListener.openLineFromVehicle(routeGtfsId, optionalPatternId, args);
+ }
+
+ @Override
+ public void openNearbyStopsFragment() {
+ showNearbyStopsFragmentChecking(true);
+ }
+
+ @Override
+ public void openLinesFragment() {
+ if(mListener!=null) mListener.openLinesFragment();
+ }
+
+ @Override
+ public void openFavoritesFragment() {
+ if(mListener!=null) mListener.openFavoritesFragment();
}
@Override
@@ -709,29 +809,28 @@
Log.e(DEBUG_TAG, "Asked for arrivals with null context");
return;
}
- ArrivalsFetcher[] fetchers = utils.getDefaultArrivalsFetchers(getContext()).toArray(new ArrivalsFetcher[0]);
- if (ID == null || ID.length() <= 0) {
+ if (ID == null || ID.isEmpty()) {
// we're still in UI thread, no need to mess with Progress
showToastMessage(R.string.insert_bus_stop_number_error, true);
toggleSpinner(false);
- } else if (framan.findFragmentById(R.id.resultFrame) instanceof ArrivalsFragment) {
- ArrivalsFragment fragment = (ArrivalsFragment) framan.findFragmentById(R.id.resultFrame);
- if (fragment != null && fragment.getStopID() != null && fragment.getStopID().equals(ID)){
- // Run with previous fetchers
- //fragment.getCurrentFetchers().toArray()
- fragment.requestArrivalsForTheFragment();
- } else{
- //SHOW NEW ARRIVALS FRAGMENT
- //new AsyncArrivalsSearcher(fragmentHelper, fetchers, getContext()).execute(ID);
- fragmentHelper.createOrUpdateStopFragment(new Palina(ID), true);
+ } else{
+ var palinaTrial = new Palina(ID);
+ if (framan.findFragmentById(R.id.resultFrame) instanceof ArrivalsFragment fragment) {
+ if (fragment.isFragmentForTheSameStop(palinaTrial)){
+ // Run with previous fetchers
+ //fragment.getCurrentFetchers().toArray()
+ fragment.requestArrivalsForTheFragment();
+ } else{
+ // The rest of the case is handled by the fragment Helper
+ fragmentHelper.showArrivalsFragmentForStop(palinaTrial, true);
+ }
}
- }
- else {
- Log.d(DEBUG_TAG, "This is probably the first arrivals search, preparing GUI");
- //prepareGUIForArrivals();
- //new AsyncArrivalsSearcher(fragmentHelper,fetchers, getContext()).execute(ID);
- fragmentHelper.createOrUpdateStopFragment(new Palina(ID), true);
+ else {
+ // this is not needed any more
+ //prepareGUIForArrivals();
+ fragmentHelper.showArrivalsFragmentForStop(palinaTrial, true);
+ }
}
}
@@ -739,21 +838,22 @@
final Context context = getContext();
if(context==null) return false;
- final boolean isOldVersion = Build.VERSION.SDK_INT < Build.VERSION_CODES.M;
- final boolean noPermission = ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
- ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED;
+ final boolean noPermission = ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED;
- return isOldVersion || !noPermission;
+ return !noPermission;
}
private void requestLocationPermission(){
+ if(shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)){
+ makeToast(R.string.enable_position_message_nearby);
+ }
requestPermissionLauncher.launch(LOCATION_PERMISSIONS);
}
- private void showNearbyFragmentIfPossible() {
+ private void showNearbyFragmentIfPossible(boolean addToBackStack) {
if (isNearbyFragmentShown()) {
//nothing to do
- Log.w(DEBUG_TAG, "Asked to show nearby fragment but we already are showing it");
+ Log.d(DEBUG_TAG, "Asked to show nearby fragment but we already are showing it");
return;
}
if (getContext() == null) {
@@ -761,13 +861,26 @@
return;
}
- if (fragmentHelper.getLastSuccessfullySearchedBusStop() == null
- && !childFragMan.isDestroyed()) {
+ if (!childFragMan.isDestroyed()) {
//Go ahead with the request
-
- actuallyShowNearbyStopsFragment();
+ swipeRefreshLayout.setVisibility(View.VISIBLE);
+ final Fragment existingFrag = childFragMan.findFragmentById(R.id.resultFrame);
+ // fragment;
+ if (!(existingFrag instanceof NearbyStopsFragment)){
+ Log.d(DEBUG_TAG, "actually showing Nearby Stops Fragment");
+ //there is no fragment showing
+ final NearbyStopsFragment fragment = NearbyStopsFragment.newInstance(NearbyStopsFragment.FragType.STOPS);
+
+ FragmentTransaction ft = childFragMan.beginTransaction();
+
+ ft.replace(R.id.resultFrame, fragment, NearbyStopsFragment.FRAGMENT_TAG);
+ if(addToBackStack) ft.addToBackStack(null);
+ if (getActivity()!=null && !getActivity().isFinishing())
+ ft.commit();
+ else Log.e(DEBUG_TAG, "Not showing nearby fragment because activity null or is finishing");
+ }
pendingNearbyStopsFragmentRequest = false;
}
}
}
\ 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
--- a/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java
@@ -2,13 +2,10 @@
import android.Manifest;
import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
import android.content.SharedPreferences;
-import android.net.Uri;
-import android.provider.Settings;
import android.util.Log;
import android.view.View;
+import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;
@@ -17,6 +14,9 @@
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.core.graphics.Insets;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowInsetsCompat;
import androidx.fragment.app.Fragment;
import com.google.android.material.snackbar.Snackbar;
@@ -44,12 +44,18 @@
return getOption(mContext, optionName, optDefault);
}
- protected void showToastMessage(int messageID, boolean short_lenght) {
- final int length = short_lenght ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG;
+ protected void showToastMessage(int messageID, boolean shortT) {
+ final int length = shortT ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG;
final Context context = getContext();
if(context!=null)
Toast.makeText(context, messageID, length).show();
}
+ protected void makeToast(String message){
+ Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
+ }
+ protected void makeToast(int messageID){
+ Toast.makeText(getContext(), messageID, Toast.LENGTH_SHORT).show();
+ }
public void hideKeyboard() {
if (getActivity()==null) return;
@@ -151,6 +157,20 @@
}
*/
+ public static void applyBottomInsetAsPadding(ViewGroup scrollableView) {
+ final int originalPaddingBottom = scrollableView.getPaddingBottom();
+ scrollableView.setClipToPadding(false); // ora lo trova
+ ViewCompat.setOnApplyWindowInsetsListener(scrollableView, (v, insets) -> {
+ Insets bars = insets.getInsets(
+ WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.ime()
+ );
+ v.setPadding(
+ v.getPaddingLeft(), v.getPaddingTop(),
+ v.getPaddingRight(), originalPaddingBottom + bars.bottom
+ );
+ return insets;
+ });
+ }
public interface LocationRequestListener{
void onPermissionResult(boolean locationGranted);
diff --git a/app/src/main/java/it/reyboz/bustorino/middleware/StopSearcher.kt b/app/src/main/java/it/reyboz/bustorino/middleware/StopSearcher.kt
--- a/app/src/main/java/it/reyboz/bustorino/middleware/StopSearcher.kt
+++ b/app/src/main/java/it/reyboz/bustorino/middleware/StopSearcher.kt
@@ -1,6 +1,5 @@
package it.reyboz.bustorino.middleware
-import android.content.Context
import android.util.Log
import it.reyboz.bustorino.backend.Fetcher
import it.reyboz.bustorino.backend.FiveTStopsFetcher
@@ -11,8 +10,6 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
-import kotlinx.coroutines.SupervisorJob
-import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.lang.ref.WeakReference
diff --git a/app/src/main/java/it/reyboz/bustorino/viewmodels/FavoritesViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/FavoritesViewModel.kt
--- a/app/src/main/java/it/reyboz/bustorino/viewmodels/FavoritesViewModel.kt
+++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/FavoritesViewModel.kt
@@ -1,21 +1,15 @@
package it.reyboz.bustorino.viewmodels
import android.app.Application
-import android.util.Log
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MediatorLiveData
-import androidx.lifecycle.application
import androidx.lifecycle.map
import androidx.lifecycle.switchMap
-import androidx.lifecycle.viewModelScope
import androidx.work.WorkInfo
import it.reyboz.bustorino.backend.Stop
import it.reyboz.bustorino.backend.StopFavoritesData
import it.reyboz.bustorino.data.DBUpdateWorker.Companion.getWorkInfoLiveData
-import it.reyboz.bustorino.data.FavoritesLiveData
import it.reyboz.bustorino.data.OldDataRepository
-import it.reyboz.bustorino.data.QueryLiveData
-import kotlinx.coroutines.launch
import java.util.concurrent.Executors
class FavoritesViewModel(application: Application) : AndroidViewModel(application) {
diff --git a/app/src/main/java/it/reyboz/bustorino/viewmodels/IntroViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/IntroViewModel.kt
new file mode 100644
--- /dev/null
+++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/IntroViewModel.kt
@@ -0,0 +1,11 @@
+package it.reyboz.bustorino.viewmodels
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+
+class IntroViewModel: ViewModel() {
+
+
+ var introIsRunning = MutableLiveData(false)
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesViewModel.kt
--- a/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesViewModel.kt
+++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesViewModel.kt
@@ -3,12 +3,9 @@
import android.app.Application
import android.util.Log
import androidx.lifecycle.*
-import it.reyboz.bustorino.backend.Result
import it.reyboz.bustorino.backend.Stop
import it.reyboz.bustorino.data.GtfsRepository
-import it.reyboz.bustorino.data.NextGenDB
import it.reyboz.bustorino.data.OldDataRepository
-import it.reyboz.bustorino.data.gtfs.GtfsDatabase
import it.reyboz.bustorino.data.gtfs.GtfsRoute
import it.reyboz.bustorino.data.gtfs.MatoPatternWithStops
import it.reyboz.bustorino.data.gtfs.PatternStop
diff --git a/app/src/main/res/drawable-hdpi/ic_star.png b/app/src/main/res/drawable-hdpi/ic_star.png
deleted file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_moving_emph.xml b/app/src/main/res/drawable/ic_moving_emph.xml
--- a/app/src/main/res/drawable/ic_moving_emph.xml
+++ b/app/src/main/res/drawable/ic_moving_emph.xml
@@ -4,6 +4,6 @@
android:viewportWidth="48"
android:viewportHeight="48">
diff --git a/app/src/main/res/drawable/ic_star_filled_white.xml b/app/src/main/res/drawable/ic_star_filled_white.xml
--- a/app/src/main/res/drawable/ic_star_filled_white.xml
+++ b/app/src/main/res/drawable/ic_star_filled_white.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
diff --git a/app/src/main/res/drawable/magnifying_glass.xml b/app/src/main/res/drawable/magnifying_glass.xml
--- a/app/src/main/res/drawable/magnifying_glass.xml
+++ b/app/src/main/res/drawable/magnifying_glass.xml
@@ -1,7 +1,7 @@
+
-
diff --git a/app/src/main/res/drawable/magnifying_glass_larger.xml b/app/src/main/res/drawable/magnifying_glass_larger.xml
new file mode 100644
--- /dev/null
+++ b/app/src/main/res/drawable/magnifying_glass_larger.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/qr_code_scan.xml b/app/src/main/res/drawable/qr_code_scan.xml
new file mode 100644
--- /dev/null
+++ b/app/src/main/res/drawable/qr_code_scan.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_principal.xml b/app/src/main/res/layout/activity_principal.xml
--- a/app/src/main/res/layout/activity_principal.xml
+++ b/app/src/main/res/layout/activity_principal.xml
@@ -3,7 +3,9 @@
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
+>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_buttons.xml b/app/src/main/res/layout/fragment_buttons.xml
new file mode 100644
--- /dev/null
+++ b/app/src/main/res/layout/fragment_buttons.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_main_screen.xml b/app/src/main/res/layout/fragment_main_screen.xml
--- a/app/src/main/res/layout/fragment_main_screen.xml
+++ b/app/src/main/res/layout/fragment_main_screen.xml
@@ -1,79 +1,84 @@
-
- >
+ android:contentDescription="@string/scan_qr_code_stop"
+ android:scaleType="fitCenter"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ />
+
-
-
-
-
-
+
@@ -83,30 +88,32 @@
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="8dp"
- android:layout_alignParentEnd="true"
- android:layout_alignParentStart="true"
android:layout_below="@+id/QRButton"
android:layout_marginTop="3dp"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:indeterminateOnly="true"
android:minWidth="10dp"
- android:visibility="gone" />
-
-
+ android:visibility="gone"
+ app:layout_constraintTop_toBottomOf="@id/searchButton"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ />
@@ -131,15 +137,16 @@
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
android:layout_gravity="bottom|end"
android:layout_margin="13dp"
- android:src="@drawable/alphabetical"
- fabSize="normal"
- backgroundTint="@color/teal_500"
- rippleColor="@color/teal_300"
- elevation="13dp"
+ app:srcCompat="@drawable/alphabetical"
+ app:tint="@android:color/white"
+ app:fabSize="normal"
+ app:backgroundTint="@color/teal_500"
+ app:rippleColor="@color/teal_300"
+ app:elevation="8dp"
+ style="?attr/floatingActionButtonMediumStyle"
/>
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_card_button.xml b/app/src/main/res/layout/item_card_button.xml
new file mode 100644
--- /dev/null
+++ b/app/src/main/res/layout/item_card_button.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/drawer_main.xml b/app/src/main/res/menu/drawer_main.xml
--- a/app/src/main/res/menu/drawer_main.xml
+++ b/app/src/main/res/menu/drawer_main.xml
@@ -5,7 +5,7 @@
+ android:title="@string/nav_home_text" />
-
\ 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
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -3,6 +3,7 @@
Stai utilizzando l\'ultimo ritrovato in materia di rispetto della tua privacy.
Cerca
Codice QR
+ Scansiona codice QR alla fermata
Si
No
Prossimo
@@ -74,6 +75,7 @@
Visualizza sulla mappa
Non trovo un\'applicazione dove mostrarla
Posizione della fermata non trovata
+ Vicino a me
Fermate vicine
Ricerca della posizione
Nessuna fermata nei dintorni
diff --git a/app/src/main/res/values-v35/styles.xml b/app/src/main/res/values-v35/styles.xml
--- a/app/src/main/res/values-v35/styles.xml
+++ b/app/src/main/res/values-v35/styles.xml
@@ -1,15 +1,4 @@
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -51,6 +51,7 @@
Vehicle %1$s
No timetable found
No QR code found, try using another app to scan
+ Scan QR code at the bus stop
Unexpected internal error, cannot extract data from GTT/5T website
Help
About the app
@@ -151,7 +152,8 @@
ListFragment - BusTO
it.reyboz.bustorino.preferences
db_is_updating
-
+
+ Near me
Nearby stops
Nearby connections
App version
@@ -250,6 +252,7 @@
describe what you were doing before the crash: \n
Arrivals
+ Home
Map
Favorites
Open navigation drawer
@@ -289,7 +292,7 @@
Long press the stop for options
- - @string/nav_arrivals_text
+ - @string/nav_home_text
- @string/nav_favorites_text
- @string/nav_map_text
- @string/lines