commit e050d9cc5e6668f86851f646ffdf7af27e63fdb6 Author: Fabio Mazza Date: Wed May 20 19:32:51 2026 +0200 Add initial home fragment, fix layout issues of MainScreenFragment Summary: Also, fix scan by QR Code not working Reviewers: #libre_busto_hackers, valerio.bozzolan Reviewed By: #libre_busto_hackers, valerio.bozzolan Subscribers: valerio.bozzolan Project Tags: #libre_busto Differential Revision: https://gitpull.it/D243 diff --git a/app/src/main/java/it/reyboz/bustorino/ActivityExperiments.java b/app/src/main/java/it/reyboz/bustorino/ActivityExperiments.java index cf0a5e5..e573d42 100644 --- a/app/src/main/java/it/reyboz/bustorino/ActivityExperiments.java +++ b/app/src/main/java/it/reyboz/bustorino/ActivityExperiments.java @@ -76,9 +76,20 @@ public class ActivityExperiments extends GeneralActivity implements CommonFragme } @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 @@ public class ActivityExperiments extends GeneralActivity implements CommonFragme 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 index 817f8a4..c4ee229 100644 --- a/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java +++ b/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java @@ -242,21 +242,21 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen 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 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen //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 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen 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 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen ); } + */ + // 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 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen 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 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen } /** - * 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 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen 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 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen 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 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen .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 class ActivityPrincipal extends GeneralActivity implements FragmentListen public void showFloatingActionButton(boolean yes) { //TODO } + /* public void setDrawerSelectedItem(String fragmentTag){ switch (fragmentTag){ @@ -683,7 +692,7 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen 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 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen 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 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen 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 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen 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 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen 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 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen @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 index 0000000..d5a38b7 --- /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 index ebd050b..1b83eaf 100644 --- 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 @@ public abstract class utils { 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 index 07a8ce5..6b7854d 100644 --- 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 @@ class ArrivalsFragment : ResultBaseFragment(), LoaderManager.LoaderCallbacks + 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 index 0000000..3ae55e8 --- /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 index 7970bd1..c6a2f93 100644 --- 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 @@ public interface CommonFragmentListener { * 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 @@ public interface CommonFragmentListener { * @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 index 5d412b7..5dc985f 100644 --- 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 @@ package it.reyboz.bustorino.fragments; 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 class FragmentHelper { 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 @@ public class FragmentHelper { * 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 class FragmentHelper { 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 @@ public class FragmentHelper { } /** - * 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 @@ public class FragmentHelper { 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 @@ public class FragmentHelper { 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 index 721b1ae..10d6acb 100644 --- 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 @@ package it.reyboz.bustorino.fragments; 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 index 429830f..f98e7e5 100644 --- 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.annotation.Nullable; 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 android.widget.Toast; 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 @@ import static it.reyboz.bustorino.util.Permissions.LOCATION_PERMISSIONS; * 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 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL /* * 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 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL 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 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL //showNearbyStopsFragment(); Log.d(DEBUG_TAG, "We have location permission"); if (pendingNearbyStopsFragmentRequest) { - showNearbyFragmentIfPossible(); + showNearbyFragmentIfPossible(pendingNearbyAddToBackStack); pendingNearbyStopsFragmentRequest = false; } } @@ -199,6 +192,8 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL 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 class MainScreenFragment extends ScreenBaseFragment implements FragmentL } - 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 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL 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 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL 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 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL 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 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL */ 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 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL 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 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL throw new RuntimeException(context + " must implement CommonFragmentListener"); } - } @Override public void onDetach() { @@ -377,18 +464,9 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL 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 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL } } + 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 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL } mListener.readyGUIfor(FragmentKind.MAIN_SCREEN_FRAGMENT); - fragmentHelper.setBlockAllActivities(false); + //fragmentHelper.setBlockAllActivities(false); } @@ -470,31 +564,29 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL 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 class MainScreenFragment extends ScreenBaseFragment implements FragmentL */ 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 class MainScreenFragment extends ScreenBaseFragment implements FragmentL } 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 class MainScreenFragment extends ScreenBaseFragment implements FragmentL 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 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL } 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 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL //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 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL @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 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL 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 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL 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,11 +861,24 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL 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; } } 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 c81589f..6f5306f 100644 --- 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 @@ package it.reyboz.bustorino.fragments; 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.ActivityResultLauncher; 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 @@ public abstract class ScreenBaseFragment extends Fragment { 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 abstract class ScreenBaseFragment extends Fragment { } */ + 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 index 05727a4..89fdb21 100644 --- 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 it.reyboz.bustorino.fragments.FragmentHelper 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 index 2164ae7..52406ff 100644 --- 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 index 0000000..fc7d0a8 --- /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 index 4750784..922a45a 100644 --- 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 @@ package it.reyboz.bustorino.viewmodels 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 7337fed..0000000 Binary files a/app/src/main/res/drawable-hdpi/ic_star.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_star.png b/app/src/main/res/drawable-mdpi/ic_star.png deleted file mode 100644 index 0f91201..0000000 Binary files a/app/src/main/res/drawable-mdpi/ic_star.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_star.png b/app/src/main/res/drawable-xhdpi/ic_star.png deleted file mode 100644 index 6994f15..0000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_star.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_star.png b/app/src/main/res/drawable-xxhdpi/ic_star.png deleted file mode 100644 index bc99ac8..0000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_star.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_star.png b/app/src/main/res/drawable-xxxhdpi/ic_star.png deleted file mode 100644 index d0ff07e..0000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_star.png and /dev/null differ diff --git a/app/src/main/res/drawable/compass_3_fill.xml b/app/src/main/res/drawable/compass_3_fill.xml new file mode 100644 index 0000000..fe9bd72 --- /dev/null +++ b/app/src/main/res/drawable/compass_3_fill.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_moving_emph.xml b/app/src/main/res/drawable/ic_moving_emph.xml index 4dbee23..514b863 100644 --- 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 index 75d337d..87fb40f 100644 --- 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 index 630d02d..7fc5a10 100644 --- 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 index 0000000..105a295 --- /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 index 0000000..4f3559b --- /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 index 6815296..e439b05 100644 --- 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" +> diff --git a/app/src/main/res/layout/fragment_buttons.xml b/app/src/main/res/layout/fragment_buttons.xml new file mode 100644 index 0000000..dc65526 --- /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 index af18a3c..33c749c 100644 --- 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 index 0000000..4da1fa0 --- /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 index 3a62823..0c2f4a4 100644 --- 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" /> diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 36176ca..01dc903 100644 --- 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 index ae2e815..1646d1b 100644 --- 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 index 17942b4..b11345d 100644 --- 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