Page Menu
Home
GitPull.it
Search
Configure Global Search
Log In
Files
F13558472
D243.1783783170.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Authored By
Unknown
Size
106 KB
Referenced Files
None
Subscribers
None
D243.1783783170.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D243: Add initial home fragment, fix layout issues of MainScreenFragment
Attached
Detach File
Event Timeline