Page MenuHomeGitPull.it

D243.1783783170.diff
No OneTemporary

Authored By
Unknown
Size
106 KB
Referenced Files
None
Subscribers
None

D243.1783783170.diff

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 <http://www.gnu.org/licenses/>.
+ */
+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 ");
+ }
+ //add left margin only to the first column
+ if(position%columns==0){
+ outRect.left = margin;
+ sb.append("left ");
+ }
+ if(position%columns==columns-1){
+ outRect.right = margin;
+ sb.append("right ");
+ }
+ }
+ if(BuildConfig.DEBUG)
+ Log.d(DEBUG_TAG, "margins put: " + sb.toString());
+
+ }
+}
+
+/* leftover code from my poor trial
+
+var adapter = parent.getAdapter();
+ int nrows = adapter!=null ? (int)Math.ceil( (double) adapter.getItemCount() / columns) : -2;
+ int position = parent.getChildLayoutPosition(view);
+ 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 ");
+ }
+ if(position % columns != 0){
+ outRect.left = margin;
+ sb.append("left ");
+ }
+ 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<CardMenuItem>
+
+ 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<ImageView>(R.id.cardIcon).setImageResource(item.iconRes)
+ cardView.findViewById<TextView>(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<ButtonsFragment.CardMenuItem>,
+ val listener: (ButtonsFragment.CardMenuItem) -> Unit) :
+ RecyclerView.Adapter<ActionsCardAdapter.ViewHolder>() {
+ 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<TextView>(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<Stop> 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<BarcodeScanOptions> 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<String[]> 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,21 +2,20 @@
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;
import androidx.activity.result.ActivityResultCallback;
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 +43,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 +156,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 7337fed6ef223388e380ab5eb665da0d3f3abd02..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
literal 761
zc$@+G0tWqwP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm00006VoOIv03HB{
z0Oc9g&QkyY010qNS#tmY4c7nw4c7reD4Tcy000McNliru-U|;4Ffcxu9|iyb0)|OM
zK~!ko?U=o56hRoqpSeUZ<Ot?W;`u?020LwR%n=lY5VSKAkh4mezaoD`T1B)BS|tG+
zG1qBj3b9EMga`p8T7*F0JbpHBbXo6X?`~&r7w>^#mtppu=b3qT=9!WD=p(%VazM^!
zA30CNPhKBe3D#w51}Z=$tLZZZ99WskObP{3r7WpnrJR{G0t6I*UH)R%>N1l)v;2$M
zOpLxg*XsyaPjwmv$S=?s4^#gy02@sNY@|Jfiokvo0sBBP&C6(6QZs_ZlO;8cqdx(B
zr8V||i9}~0;!k%~(!~hvE=jr)Q=f8ej`CSa4<wZ&osx9gcG_`C=OkT@80w3pZ<4-C
z`XT99QdQD>NpGBU2T?h>5WxXS_axnvG%9IE(%l|sXGPKnNk1ihl(glXtF<y=+oTWM
zkxd~m46LMx{0cDKt|1!)o&vQV60aGD!H#!tV7Z3^mgDgC0!lzNLE=@Q)b5hn%B0<~
zO~^O)fZMT}zJQ1ez@|s?o4|Q5jx``+2w3w#ehnB(%x(j(V<i77F*h0*otH6=$BRx!
zz8gEe5Tk%ciOqm>f9%us7zNx2#r8V`K;9f^To|{QiKWn}q<oA5@{-OZDd35uu^0u6
zS<FOMp#eL6MZsGuQ7~<>6S+3rX|b;V0v3V1l|`#-hMh#NAML<%*nq?_umw!l`R*F<
z&f3(&q0d|FAd#5{1U><C4cu(-ea_m}!>uF)EY<0_42(3hzabGL_DvW|NeFmuogG<9
zOtxzS0>*ICVjNk_XFiELdjfn0=DRs?gl1&kViN4jhm~Hn&I)mymO>&5z<prV*LlEo
ra&y+n&7Zd!@Ik)YtHnP0_}}pxDUm#v1xOpi00000NkvXXu0mjfqBukf
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 0f91201f5dc0aa926540bdf929ef2dd31a0979ca..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
literal 484
zc$@*`0UQ2_P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F800006VoOIv03HB{
z0Oc9g&QkyY010qNS#tmY4c7nw4c7reD4Tcy000McNliru-U|;4Fc1azKh6LE0dYx0
zK~z}7?UuhzLNOG^f4x_ti#iyM%HnKD*d@Bljt>AXxbX%Ky6OwybMOGh!GVPbAZ{wb
zU8m2b#s)4>djGKSB~6pI=X|H9=TBjc6p&PsR7#^GN@+vOj+J?!FR8x@VkA}ckBX$?
zp1>Fb&~%oXcCE16?`j%5Azp|AZ{JSXx+&5NqXZ9(iXy+(Y%HCU8y<w`z#*^&906Bz
zj<+T40q?*!@L`O3p8KQ@(I)T&oB*|)9QXpf0WD+9Pf~OrOByOa3@yjp6x)_G$bo!d
zbuZ3yJNGgq@433YijAG`Ga$cPiaf!XdJ6K(ta85=Kz^1<DYmZL53u{d$L*14?XLzH
zQ1$5zzRl<~zyL29;~4`%A#6Nc5P9_eiy7Nvpl-)_Rv!sYKSxO?A;NDZMb4Snu1%to
zMGAHug|CvzlZC}8TxEOa58bRjA77Fhiz=F9NW;o}X7TfVkQ4{V$C4h?{FtY1mbc6r
aE5R>G5FzYk27y)p0000<MNUMnLSTX-`N`7&
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 6994f1591af42c5f0057eeac154512c6754922da..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
literal 1091
zc$@)41ibr+P)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF00006VoOIv03HB{
z0Oc9g&QkyY010qNS#tmY4c7nw4c7reD4Tcy000McNliru-U|;4FD;P8eL(;K1JFrC
zK~#9!?V3?*6jv0-e`gb7)vRd~78I*!SDQ42B3RpKu@7Qx#Z<&seUN+r^`+2)U&1dS
zl=_rlUwrmODMg8oBBE*YV1qA0)wmc<`}gHUhq&v`&fcAw?9LyCnO*M8Ip=@RJ?Gpr
z0|g2cD3Adp)xGXD5~`A_z3c_klBToNp+7_S9|FGe-$TF?GwZ3@Kcu%f)N{d4((MQV
zvpp3IC2iNc*xoC~o1|-X2)HK6dcpO#NqSs|fX9-yrM(nNX$dm36~ndy!*zxX16w7L
zW;U=4JmJ6h5dXiK^`)s#DP`k-X(F>E>F2H^KoTC{j>Q;0>4CEAg5bEMMK#Bwr15TC
z0!M(2F~)5KzU!JGxDwUXqEDNdrI8Uj?g4K6ZwZL|HH~yw>h$dUPVbupw;z-=)ahVY
zN@7%uG5wOtKn2(XY)+utX5b5;BB?B?KZI^gV?f+>Cutq9+h_M%V9@&u1AhIX@A)_Q
zISHNsH9!9gECMfqWxu`-`~loIv!%H4sA&-NHq~C>1E364frG%8Yb5K=0yls;;2H1&
zxNT;0aoxNu>7u#^7kx>_OM||a47dcG&Z>150L#E-U<y#ZrEM{S%8$VBYmkIx;3RPM
zeKD<R7BI7`z**q=8Z!PlaMpEiuM&(@a^t|C9u)K1xseCG2{XGJC!@7wh@=g`9biXJ
z377{yH?vysLde;y0b{_;92$QU7&Eh4D+N((0wS)oWM&f{81sTFuG7*gT{~Cf`*|t{
zIsQ!2GB^|o2P7?LVE8LZ``b0%+9km=#7^L^l->RlGyA*Uo<UpN;fUaQV8m<hPUC^g
znoniDx3UG5^nD7hf4H6PZ}5i5su?(*0_P_h$j-JS<g@Wv3Iqi07?%PXKkR*jPNOR6
zqflut2m$VjgMM@xgFrb{@C(@#9i{$D&(IilS-B&il49~13H6|0NmSATuq7n|{xP$U
zb3s7TH$MU5PLMZ8LFxcWQweT;2%PfcVFKdM<mR(FNi!{0b3aMi6uEzs|JMp;@?!je
zq+2nSo?R(r>wPOM>4=YsgIkgYGT#SCsz_Rhq40#HzD6~CgoHjH+YAdXJ97^hlk~KK
zk~RMx*y3(pB1D+qTWj#_r;<kVummO>DEJ_Rf|5oh%{7oQnTJAX^#<q>aL&wjnc2N|
zT-4LtJu|BU=l$7He3}Ojh~k2zO6VS4WLZ>P#v9~?fIE_Yhy=z?1>BYEqb^ff8$Tmy
zpQQDnXFY~Dte5n4X7xZMvRj4|K-M~t*39QYW`P0)3KZxzyaw8)I0MPWRjL30002ov
JPDHLkV1nl#^7;S(
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 bc99ac86917c0ce1c8ba594eea8caafb9db1a380..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
literal 1655
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
B07d`+
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 d0ff07eb8631535b7f731afd9440c0adb9f7b913..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
literal 1929
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
P00000NkvXXu0mjfSA22Y
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
--- /dev/null
+++ b/app/src/main/res/drawable/compass_3_fill.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="30dp" android:viewportHeight="24" android:viewportWidth="24" android:width="30dp">
+
+ <path android:fillColor="?colorOnPrimary" android:pathData="M12,22C6.477,22 2,17.523 2,12C2,6.477 6.477,2 12,2C17.523,2 22,6.477 22,12C22,17.523 17.523,22 12,22ZM16.5,7.5L10,10L7.5,16.5L14,14L16.5,7.5ZM12,13C11.448,13 11,12.552 11,12C11,11.448 11.448,11 12,11C12.552,11 13,11.448 13,12C13,12.552 12.552,13 12,13Z"/>
+
+</vector>
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">
<path
- android:fillColor="#FF000000"
+ android:fillColor="?colorOnPrimary"
android:pathData="M6.25,36.9 L3.25,33.9 14.65,22.5Q16.5,20.7 19.025,20.7Q21.55,20.7 23.35,22.5L25.65,24.8Q26.2,25.35 26.975,25.35Q27.75,25.35 28.3,24.8L37.45,15.65H32.5V11.35H44.8V23.65H40.5V18.7L31.3,27.85Q29.5,29.7 26.95,29.7Q24.4,29.7 22.6,27.85L20.25,25.5Q19.7,24.95 18.95,24.95Q18.2,24.95 17.65,25.5Z"/>
</vector>
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">
<path
- android:fillColor="#FFFFFF"
+ android:fillColor="@color/white"
android:pathData="M12,17.27L18.18,21L16.54,13.97L22,9.24L14.81,8.62L12,2L9.19,8.62L2,9.24L7.45,13.97L5.82,21L12,17.27Z"/>
</vector>
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 @@
+<!-- TODO: Remove this file if it is not needed any more -->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true"
android:height="28dp" android:viewportHeight="16"
android:viewportWidth="16" android:width="28dp">
-
<path android:fillColor="?attr/colorOnPrimary"
android:pathData="M13.073,12.291 L10.147,9.364a3.971,3.971 0,1 0,-0.783 0.783l2.927,2.927ZM7.009,9.839A2.829,2.829 0,1 1,9.839 7.009,2.833 2.833,0 0,1 7.009,9.839Z"/>
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 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="28dp"
+ android:height="28dp"
+ android:viewportWidth="16"
+ android:viewportHeight="16">
+ <path
+ android:pathData="M14.857,13.791 L10.872,9.805A5.409,5.409 0,1 0,9.805 10.871l3.987,3.987zM6.598,10.452A3.853,3.853 0,1 1,10.452 6.597,3.859 3.859,0 0,1 6.598,10.452Z"
+ android:strokeWidth="1.36203"
+ android:fillColor="?attr/colorOnPrimary"/>
+</vector>
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 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="16" android:viewportWidth="16" android:width="24dp">
+
+ <path android:fillColor="?colorAccent" android:pathData="M0,0.5A0.5,0.5 0,0 1,0.5 0h3a0.5,0.5 0,0 1,0 1L1,1v2.5a0.5,0.5 0,0 1,-1 0zM12,0.5a0.5,0.5 0,0 1,0.5 -0.5h3a0.5,0.5 0,0 1,0.5 0.5v3a0.5,0.5 0,0 1,-1 0L15,1h-2.5a0.5,0.5 0,0 1,-0.5 -0.5M0.5,12a0.5,0.5 0,0 1,0.5 0.5L1,15h2.5a0.5,0.5 0,0 1,0 1h-3a0.5,0.5 0,0 1,-0.5 -0.5v-3a0.5,0.5 0,0 1,0.5 -0.5m15,0a0.5,0.5 0,0 1,0.5 0.5v3a0.5,0.5 0,0 1,-0.5 0.5h-3a0.5,0.5 0,0 1,0 -1L15,15v-2.5a0.5,0.5 0,0 1,0.5 -0.5M4,4h1v1L4,5z"/>
+
+ <path android:fillColor="?colorAccent" android:pathData="M7,2L2,2v5h5zM3,3h3v3L3,6zM5,11L4,11v1h1z"/>
+
+ <path android:fillColor="?colorAccent" android:pathData="M7,9L2,9v5h5zM3,10h3v3L3,13zM11,4h1v1h-1z"/>
+
+ <path android:fillColor="?colorAccent" android:pathData="M9,2h5v5L9,7zM10,3v3h3L13,3zM8,8v2h1v1L8,11v1h2v-2h1v2h1v-1h2v-1h-3L11,8zM10,10L9,10L9,9h1zM14,12h-1v1h-2v1h3zM10,14v-1L8,13v1z"/>
+
+ <path android:fillColor="?colorAccent" android:pathData="M12,9h2V8h-2z"/>
+
+</vector>
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"
+>
<RelativeLayout
android:id="@+id/rootRelativeLayout"
android:layout_width="match_parent"
@@ -45,9 +47,9 @@
app:itemIconTint="@color/teal_500"
app:itemTextAppearance="?android:attr/textAppearanceMedium"
app:itemTextColor="@color/black_900"
- android:fitsSystemWindows="false"
+ android:fitsSystemWindows="true"
app:menu="@menu/drawer_main" />
</androidx.drawerlayout.widget.DrawerLayout>
\ 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".fragments.ButtonsFragment"
+ android:padding="5dp"
+>
+ <!--
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Home"
+ android:textSize="20sp"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentTop="true"
+ android:id="@+id/homeTitleTextView"
+ />
+ -->
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/buttonsRecyclerView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true"
+ android:visibility="visible"
+ android:nestedScrollingEnabled="false"
+ android:layout_marginTop="15dp"
+ android:layout_marginStart="10dp"
+ android:layout_marginEnd="10dp"
+ />
+
+
+
+</RelativeLayout>
\ 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 @@
<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".fragments.MainScreenFragment"
android:paddingTop="10dip"
>
- >
<ImageButton
android:id="@+id/QRButton"
style="?android:attr/borderlessButtonStyle"
android:layout_width="40dip"
android:layout_height="40dip"
- android:layout_alignBottom="@+id/searchButton"
- android:layout_marginLeft="16dp"
android:layout_marginStart="16dp"
android:background="@drawable/qrcode_button_custom"
- android:contentDescription="@string/qrcode"
- />
+ android:contentDescription="@string/scan_qr_code_stop"
+ android:scaleType="fitCenter"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ />
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/leftGuide"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintStart_toEndOf="@id/QRButton"
+ />
<EditText
android:id="@+id/busStopSearchByIDEditText"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginEnd="5dp"
- android:layout_marginLeft="5dip"
- android:layout_marginRight="5dip"
- android:layout_toEndOf="@+id/QRButton"
- android:layout_toLeftOf="@+id/searchButton"
- android:layout_toRightOf="@+id/QRButton"
- android:layout_toStartOf="@+id/searchButton"
+ android:layout_marginEnd="10dp"
+ android:layout_marginStart="10dp"
android:ems="10"
android:hint="@string/insert_bus_stop_number"
android:imeOptions="actionSearch"
+
android:inputType="number"
android:selectAllOnFocus="true"
android:singleLine="true"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toStartOf="@id/leftGuide"
+ app:layout_constraintEnd_toEndOf="@id/rightGuide"
>
-
- <requestFocus />
</EditText>
<!--android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"-->
<EditText
android:id="@+id/busStopSearchByNameEditText"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginEnd="5dp"
- android:layout_marginLeft="5dip"
- android:layout_marginRight="5dip"
- android:layout_toEndOf="@+id/QRButton"
- android:layout_toLeftOf="@+id/searchButton"
- android:layout_toRightOf="@+id/QRButton"
- android:layout_toStartOf="@+id/searchButton"
+ android:layout_marginEnd="10dp"
+ android:layout_marginStart="10dp"
android:inputType="text"
android:ems="10"
android:hint="@string/insert_bus_stop_name"
android:imeOptions="actionSearch"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toStartOf="@id/leftGuide"
+ app:layout_constraintEnd_toEndOf="@id/rightGuide"
android:singleLine="true"
android:visibility="gone">
-
- <requestFocus />
</EditText>
-
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/rightGuide"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintEnd_toStartOf="@id/searchButton"
+ />
<ImageButton
android:id="@+id/searchButton"
style="?android:attr/borderlessButtonStyle"
android:layout_width="40dip"
android:layout_height="40dip"
- android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
android:layout_alignTop="@+id/busStopSearchByIDEditText"
android:layout_marginEnd="16dp"
- android:layout_marginRight="16dp"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
android:background="@drawable/search_button_custom"
android:contentDescription="@string/search"
/>
@@ -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"
+ />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="0dp"
android:id="@+id/coord_layout"
- android:layout_alignParentBottom="true"
- android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
android:layout_gravity="bottom|end"
android:layout_below="@+id/searchButton"
- android:layout_marginTop="5dp"
-
+ android:layout_marginTop="8dp"
+ app:layout_constraintTop_toBottomOf="@id/searchButton"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:flow_verticalBias="0.0"
+ app:layout_constraintVertical_bias="0.0"
>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/listRefreshLayout"
@@ -121,7 +128,6 @@
android:layout_width="match_parent"
android:id="@+id/resultFrame"
android:layout_height="fill_parent"
- android:layout_alignParentLeft="true"
android:layout_marginTop="5dp"
android:layout_alignParentStart="true"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
@@ -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"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
-</RelativeLayout>
\ No newline at end of file
+</androidx.constraintlayout.widget.ConstraintLayout>
\ 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.google.android.material.card.MaterialCardView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/buttonCardView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:cardCornerRadius="10dp"
+ app:cardElevation="1dp"
+ app:cardBackgroundColor="?colorPrimary"
+ app:rippleColor="?attr/colorControlHighlight">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:padding="20dp">
+ <TextView
+ android:id="@+id/cardLabel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="18sp"
+ android:text="Trial"
+ android:textColor="@color/grey_100"
+ android:textAlignment="center"
+ android:fontFamily="@font/lato_bold"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/cardIcon"
+ app:layout_constraintTop_toTopOf="parent"
+ android:textStyle="bold"
+ android:gravity="center"
+ />
+ <ImageView
+ android:id="@+id/cardIcon"
+ app:layout_constraintTop_toBottomOf="@id/cardLabel"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:contentDescription="@null"
+ app:tint="@color/grey_100"
+ app:layout_constraintVertical_bias="1.0"
+ android:layout_marginTop="12dp"
+ />
+
+
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+
+</com.google.android.material.card.MaterialCardView>
\ 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 @@
<item
android:id="@+id/nav_arrivals"
android:icon="@drawable/ic_baseline_departure_board_24"
- android:title="@string/nav_arrivals_text" />
+ android:title="@string/nav_home_text" />
<item
android:id="@+id/nav_map_item"
android:icon="@drawable/ic_baseline_map_24"
diff --git a/app/src/main/res/menu/menu_search.xml b/app/src/main/res/menu/menu_search.xml
--- a/app/src/main/res/menu/menu_search.xml
+++ b/app/src/main/res/menu/menu_search.xml
@@ -4,8 +4,8 @@
>
<item android:id="@+id/searchMenuItem"
android:title="@string/search"
- android:icon="@drawable/magnifying_glass"
+ android:icon="@drawable/magnifying_glass_larger"
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="collapseActionView|ifRoom"/>
</menu>
\ 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 @@
<string name="app_description">Stai utilizzando l\'ultimo ritrovato in materia di rispetto della tua privacy.</string>
<string name="search">Cerca</string>
<string name="qrcode">Codice QR</string>
+ <string name="scan_qr_code_stop">Scansiona codice QR alla fermata</string>
<string name="yes">Si</string>
<string name="no">No</string>
<string name="next">Prossimo</string>
@@ -74,6 +75,7 @@
<string name="action_view_on_map">Visualizza sulla mappa</string>
<string name="cannot_show_on_map_no_activity">Non trovo un\'applicazione dove mostrarla</string>
<string name="cannot_show_on_map_no_position">Posizione della fermata non trovata</string>
+ <string name="nearby_message_home_card">Vicino a me</string>
<string name="nearby_stops_message">Fermate vicine</string>
<string name="position_searching_message">Ricerca della posizione</string>
<string name="no_stops_nearby">Nessuna fermata nei dintorni</string>
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 @@
<resources>
- <style name="AppBaseTheme" parent="Theme.AppCompat.Light.NoActionBar">
- <!--
- This forces the app to not use Edge-to-Edge. However, on API 36 it will be ignored!
- -->
- </style>
-
- <style name="AppMaterialTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
- <!--
- This forces the app to not use Edge-to-Edge. However, on API 36 it will be ignored!
- -->
- </style>
</resources>
\ 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 @@
<string name="vehicle_fill">Vehicle %1$s</string>
<string name="no_passages">No timetable found</string>
<string name="no_qrcode">No QR code found, try using another app to scan</string>
+ <string name="scan_qr_code_stop">Scan QR code at the bus stop</string>
<string name="internal_error">Unexpected internal error, cannot extract data from GTT/5T website</string>
<string name="action_help">Help</string>
<string name="action_about">About the app</string>
@@ -151,7 +152,8 @@
<string name="list_fragment_debug" translatable="false">ListFragment - BusTO</string>
<string name="mainSharedPreferences" translatable="false">it.reyboz.bustorino.preferences</string>
<string name="databaseUpdatingPref" translatable="false">db_is_updating</string>
- <!-- Settings -->
+ <!-- used in the buttons as generic title for "show what is near me" -->
+ <string name="nearby_message_home_card">Near me</string>
<string name="nearby_stops_message">Nearby stops</string>
<string name="nearby_arrivals_message">Nearby connections</string>
<string name="app_version">App version</string>
@@ -250,6 +252,7 @@
describe what you were doing before the crash: \n
</string>
<string name="nav_arrivals_text">Arrivals</string>
+ <string name="nav_home_text">Home</string>
<string name="nav_map_text">Map</string>
<string name="nav_favorites_text">Favorites</string>
<string name="drawer_open">Open navigation drawer</string>
@@ -289,7 +292,7 @@
<string name="long_press_stop_4_options">Long press the stop for options</string>
<array name="first_screen_shown">
- <item>@string/nav_arrivals_text</item>
+ <item>@string/nav_home_text</item>
<item>@string/nav_favorites_text</item>
<item>@string/nav_map_text</item>
<item>@string/lines</item>

File Metadata

Mime Type
text/plain
Expires
Sat, Jul 11, 17:19 (10 h, 39 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1997623
Default Alt Text
D243.1783783170.diff (106 KB)

Event Timeline