diff --git a/app/build.gradle b/app/build.gradle --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,7 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' +apply plugin: 'kotlin-android' +apply plugin: 'com.android.application' + android { compileSdkVersion 33 @@ -23,10 +24,6 @@ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } testOptions { unitTests.returnDefaultValues = true } @@ -62,7 +59,6 @@ dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" diff --git a/app/src/main/java/it/reyboz/bustorino/adapters/ArrivalsStopAdapter.java b/app/src/main/java/it/reyboz/bustorino/adapters/ArrivalsStopAdapter.java --- a/app/src/main/java/it/reyboz/bustorino/adapters/ArrivalsStopAdapter.java +++ b/app/src/main/java/it/reyboz/bustorino/adapters/ArrivalsStopAdapter.java @@ -95,7 +95,7 @@ holder.stopNameView.setText(stopText); //final String routeName = String.format(context.getResources().getString(R.string.two_strings_format),r.getNameForDisplay(),r.destinazione); if (r!=null) { - holder.lineNameTextView.setText(r.getNameForDisplay()); + holder.lineNameTextView.setText(r.getDisplayCode()); holder.lineDirectionTextView.setText(NameCapitalize.capitalizePass(r.destinazione, capit)); holder.arrivalsTextView.setText(r.getPassaggiToString(0,2,true)); } else { diff --git a/app/src/main/java/it/reyboz/bustorino/adapters/PalinaAdapter.java b/app/src/main/java/it/reyboz/bustorino/adapters/PalinaAdapter.java --- a/app/src/main/java/it/reyboz/bustorino/adapters/PalinaAdapter.java +++ b/app/src/main/java/it/reyboz/bustorino/adapters/PalinaAdapter.java @@ -18,7 +18,10 @@ package it.reyboz.bustorino.adapters; import android.content.Context; +import android.content.res.Resources; import androidx.annotation.NonNull; +import androidx.cardview.widget.CardView; +import androidx.core.content.res.ResourcesCompat; import androidx.preference.PreferenceManager; import android.content.SharedPreferences; @@ -49,6 +52,7 @@ * * @author Valerio Bozzolan * @author Ludovico Pavesi + * @author Fabio Mazza */ public class PalinaAdapter extends RecyclerView.Adapter implements SharedPreferences.OnSharedPreferenceChangeListener { @@ -56,6 +60,7 @@ private static final int metroBg = R.drawable.route_background_metro; private static final int busBg = R.drawable.route_background_bus; private static final int extraurbanoBg = R.drawable.route_background_bus_long_distance; + private static final int busIcon = R.drawable.bus; private static final int trainIcon = R.drawable.subway; private static final int tramIcon = R.drawable.tram; @@ -64,7 +69,7 @@ private Capitalize capit; private final List mRoutes; - private final AdapterClickListener mRouteListener; + private final PalinaClickListener mRouteListener; @NonNull @NotNull @@ -78,8 +83,11 @@ @Override public void onBindViewHolder(@NonNull @NotNull PalinaViewHolder vh, int position) { final Route route = mRoutes.get(position); + final Context con = vh.itemView.getContext(); + final Resources res = con.getResources(); - vh.rowStopIcon.setText(route.getNameForDisplay()); + vh.routeIDTextView.setText(route.getDisplayCode()); + vh.routeCard.setOnClickListener(view -> mRouteListener.requestShowingRoute(route)); if(route.destinazione==null || route.destinazione.length() == 0) { vh.rowRouteDestination.setVisibility(View.GONE); // move around the route timetable @@ -109,7 +117,7 @@ //set click listener vh.itemView.setOnClickListener(view -> { - mRouteListener.onAdapterClickListener(route); + mRouteListener.showRouteFullDirection(route); }); } @@ -119,24 +127,28 @@ case BUS: default: // convertView could contain another background, reset it - vh.rowStopIcon.setBackgroundResource(busBg); + //vh.rowStopIcon.setBackgroundResource(busBg); + vh.rowRouteDestination.setCompoundDrawablesWithIntrinsicBounds(busIcon, 0, 0, 0); break; case LONG_DISTANCE_BUS: - vh.rowStopIcon.setBackgroundResource(extraurbanoBg); + //vh.rowStopIcon.setBackgroundResource(extraurbanoBg); + vh.routeCard.setCardBackgroundColor(ResourcesCompat.getColor(res, R.color.extraurban_bus_bg, null)); vh.rowRouteDestination.setCompoundDrawablesWithIntrinsicBounds(busIcon, 0, 0, 0); break; case METRO: - vh.rowStopIcon.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); - vh.rowStopIcon.setBackgroundResource(metroBg); + //vh.rowStopIcon.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); + //vh.rowStopIcon.setBackgroundResource(metroBg); + vh.routeIDTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); + vh.routeCard.setCardBackgroundColor(ResourcesCompat.getColor(res, R.color.metro_bg, null)); vh.rowRouteDestination.setCompoundDrawablesWithIntrinsicBounds(trainIcon, 0, 0, 0); break; case RAILWAY: - vh.rowStopIcon.setBackgroundResource(busBg); + //vh.rowStopIcon.setBackgroundResource(busBg); vh.rowRouteDestination.setCompoundDrawablesWithIntrinsicBounds(trainIcon, 0, 0, 0); break; case TRAM: // never used but whatever. - vh.rowStopIcon.setBackgroundResource(busBg); + //vh.rowStopIcon.setBackgroundResource(busBg); vh.rowRouteDestination.setCompoundDrawablesWithIntrinsicBounds(tramIcon, 0, 0, 0); break; } @@ -161,7 +173,9 @@ // hey look, a pattern! public static class PalinaViewHolder extends RecyclerView.ViewHolder { - final TextView rowStopIcon; + //final TextView rowStopIcon; + final TextView routeIDTextView; + final CardView routeCard; final TextView rowRouteDestination; final TextView rowRouteTimetable; @@ -172,7 +186,9 @@ vh.rowRouteDestination = (TextView) convertView.findViewById(R.id.routeDestination); vh.rowRouteTimetable = (TextView) convertView.findViewById(R.id.routesThatStopHere); */ - rowStopIcon = view.findViewById(R.id.routeID); + //rowStopIcon = view.findViewById(R.id.routeID); + routeIDTextView = view.findViewById(R.id.routeNameTextView); + routeCard = view.findViewById(R.id.routeCard); rowRouteDestination = view.findViewById(R.id.routeDestination); rowRouteTimetable = view.findViewById(R.id.routesThatStopHere); } @@ -192,7 +208,7 @@ return Capitalize.DO_NOTHING; } - public PalinaAdapter(Context context, Palina p, AdapterClickListener listener, boolean hideEmptyRoutes) { + public PalinaAdapter(Context context, Palina p, PalinaClickListener listener, boolean hideEmptyRoutes) { Comparator sorter = null; if (p.getPassaggiSourceIfAny()== Passaggio.Source.GTTJSON){ sorter = new PassaggiSorter(); @@ -239,4 +255,18 @@ enum Capitalize{ DO_NOTHING, ALL, FIRST } + + public interface PalinaClickListener{ + /** + * Simple click listener for the whole line (show info) + * @param route for toast + */ + void showRouteFullDirection(Route route); + + /** + * Show the line with all the stops in the app + * @param route partial line info + */ + void requestShowingRoute(Route route); + } } diff --git a/app/src/main/java/it/reyboz/bustorino/backend/FiveTNormalizer.java b/app/src/main/java/it/reyboz/bustorino/backend/FiveTNormalizer.java --- a/app/src/main/java/it/reyboz/bustorino/backend/FiveTNormalizer.java +++ b/app/src/main/java/it/reyboz/bustorino/backend/FiveTNormalizer.java @@ -200,9 +200,9 @@ // I wonder why GTT calls this "SE1" while other absurd names have a human readable name too. return "1 Settimo"; case "16CD": - return "16 Circolare Destra"; + return "16 CD"; case "16CS": - return "16 Circolare Sinistra"; + return "16 CS"; case "79": return "Cremagliera Sassi-Superga"; case "W01": @@ -247,10 +247,38 @@ return "44 Scolastico"; case "46N": return "46 Navetta"; + case "M1S": + return "MetroBus sostitutivo"; default: return null; } } + public static String fixShortNameForDisplay(String routeID, boolean withBarratoSpace) { + /*if (routeID.length() == 3 && routeID.charAt(2) == 'B') { + return routeID.substring(0, 2).concat("/"); + } else if (routeID.charAt(routeID.length() - 1) == '/' && routeID.charAt(routeID.length() - 2) == ' ') { + //remove last space + return routeID.substring(0, routeID.length() - 2).concat("/"); + } else return routeID; + */ + int len = routeID.length(); + final boolean isBarrato = (routeID.charAt(len-1) == 'B') || (routeID.charAt(len-1) == '/'); + if(isBarrato) { + String output; + if ((routeID.charAt(len - 2) == ' ')) + output = routeID.substring(0, len - 2); + else output = routeID.substring(0, len - 1); + if(withBarratoSpace) + output = output.concat(" /"); + else + output = output.concat("/"); + return output; + } else return routeID; + } + + public static String fixShortNameForDisplay(String routeID){ + return fixShortNameForDisplay(routeID, false); + } public static String routeDisplayToInternal(String displayName){ String name = displayName.trim(); @@ -316,4 +344,35 @@ return name.replace(" ",""); } + public static String getGtfsRouteID(Route route){ + String routeName = route.getName(); + String cutName = routeName.replace("\\s", ""); + + int len = cutName.length(); + StringBuilder sb = new StringBuilder("gtt:"); + if (cutName.charAt(len-1) == '/'){ + sb.append(cutName.substring(0, len-2)); + sb.append("B"); + //cutName = cutName.substring(0, len-2).concat("B"); + } else { + sb.append(cutName); + } + //determine service kind + switch (route.type){ + case UNKNOWN: + case BUS: + case TRAM: + //tourist lines have "U" in the routeid + sb.append("U"); + break; + case RAILWAY: + sb.append("F"); + break; + case LONG_DISTANCE_BUS: + sb.append("E"); + } + + + return sb.toString(); + } } diff --git a/app/src/main/java/it/reyboz/bustorino/backend/Palina.java b/app/src/main/java/it/reyboz/bustorino/backend/Palina.java --- a/app/src/main/java/it/reyboz/bustorino/backend/Palina.java +++ b/app/src/main/java/it/reyboz/bustorino/backend/Palina.java @@ -408,10 +408,10 @@ } for(Route r: routes){ if(r.numPassaggi()==0) - mList.add(r.getNameForDisplay()); + mList.add(r.getDisplayCode()); } return mList; } //private void mergeRoute } \ No newline at end of file diff --git a/app/src/main/java/it/reyboz/bustorino/backend/Route.java b/app/src/main/java/it/reyboz/bustorino/backend/Route.java --- a/app/src/main/java/it/reyboz/bustorino/backend/Route.java +++ b/app/src/main/java/it/reyboz/bustorino/backend/Route.java @@ -35,6 +35,7 @@ private final static int BRANCHID_MISSING = -1; private final String name; + private @Nullable String displayCode = null; public String destinazione; public final List passaggi; //create a copy of the list, so that @@ -213,10 +214,18 @@ return name; } - public String getNameForDisplay(){ + /*public String getNameForDisplay(){ if(name.trim().equals("101Metrobus")) return "101 Metrobus"; else return name; } + + */ + public String getDisplayCode(){ + if(displayCode!=null) return displayCode; + + displayCode = FiveTNormalizer.fixShortNameForDisplay(name); + return displayCode; + } /** * Get all passaggi in a single string * @return the string diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.java --- a/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.java +++ b/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.java @@ -18,7 +18,6 @@ package it.reyboz.bustorino.fragments; -import android.annotation.SuppressLint; import android.content.Context; import android.database.Cursor; import android.net.Uri; @@ -27,7 +26,6 @@ import android.widget.*; import androidx.annotation.Nullable; import androidx.annotation.NonNull; -import androidx.core.widget.NestedScrollView; import androidx.loader.app.LoaderManager; import androidx.loader.content.CursorLoader; import androidx.loader.content.Loader; @@ -46,7 +44,6 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import it.reyboz.bustorino.R; -import it.reyboz.bustorino.adapters.AdapterClickListener; import it.reyboz.bustorino.adapters.PalinaAdapter; import it.reyboz.bustorino.adapters.RouteOnlyLineAdapter; import it.reyboz.bustorino.backend.ArrivalsFetcher; @@ -63,7 +60,6 @@ import it.reyboz.bustorino.data.UserDB; import it.reyboz.bustorino.middleware.AsyncStopFavoriteAction; import it.reyboz.bustorino.util.LinesNameSorter; -import it.reyboz.bustorino.util.ViewUtils; import static it.reyboz.bustorino.fragments.ScreenBaseFragment.setOption; @@ -110,25 +106,42 @@ private boolean reloadOnResume = true; - private final AdapterClickListener mRouteClickListener = route -> { - String routeName; + private final PalinaAdapter.PalinaClickListener palinaClickListener = new PalinaAdapter.PalinaClickListener() { + @Override + public void showRouteFullDirection(Route route) { + String routeName; + Log.d(DEBUG_TAG, "Make toast for line "+route.getName()); - routeName = FiveTNormalizer.routeInternalToDisplay(route.getNameForDisplay()); - if (routeName == null) { - routeName = route.getNameForDisplay(); - } - if(getContext()==null) - Log.e(DEBUG_TAG, "Touched on a route but Context is null"); - else if (route.destinazione == null || route.destinazione.length() == 0) { - Toast.makeText(getContext(), - getString(R.string.route_towards_unknown, routeName), Toast.LENGTH_SHORT).show(); - } else { - Toast.makeText(getContext(), - getString(R.string.route_towards_destination, routeName, route.destinazione), Toast.LENGTH_SHORT).show(); + + routeName = FiveTNormalizer.routeInternalToDisplay(route.getName()); + if (routeName == null) { + routeName = route.getDisplayCode(); + } + if(getContext()==null) + Log.e(DEBUG_TAG, "Touched on a route but Context is null"); + else if (route.destinazione == null || route.destinazione.length() == 0) { + Toast.makeText(getContext(), + getString(R.string.route_towards_unknown, routeName), Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(getContext(), + getString(R.string.route_towards_destination, routeName, route.destinazione), Toast.LENGTH_SHORT).show(); + } } + @Override + public void requestShowingRoute(Route route) { + Log.d(DEBUG_TAG, "Need to show line for route:\ngtfsID "+route.getGtfsId()+ " name "+route.getName()); + if(route.getGtfsId()!=null){ + mListener.showLineOnMap(route.getGtfsId()); + } else { + String gtfsID = FiveTNormalizer.getGtfsRouteID(route); + Log.d(DEBUG_TAG, "GtfsID for route is: " + gtfsID); + mListener.showLineOnMap(gtfsID); + } + } }; + public static ArrivalsFragment newInstance(String stopID){ return newInstance(stopID, null); } @@ -403,7 +416,7 @@ else needUpdateOnAttach = true; } else { - final PalinaAdapter adapter = new PalinaAdapter(getContext(), lastUpdatedPalina, mRouteClickListener, true); + final PalinaAdapter adapter = new PalinaAdapter(getContext(), lastUpdatedPalina, palinaClickListener, true); showArrivalsSources(lastUpdatedPalina); resetListAdapter(adapter); diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt --- a/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt @@ -40,6 +40,7 @@ import it.reyboz.bustorino.adapters.NameCapitalize import it.reyboz.bustorino.adapters.StopAdapterListener import it.reyboz.bustorino.adapters.StopRecyclerAdapter +import it.reyboz.bustorino.backend.FiveTNormalizer import it.reyboz.bustorino.backend.Stop import it.reyboz.bustorino.backend.gtfs.GtfsUtils import it.reyboz.bustorino.backend.gtfs.LivePositionUpdate @@ -175,7 +176,8 @@ descripTextView.visibility = View.INVISIBLE val titleTextView = rootView.findViewById(R.id.titleTextView) - titleTextView.text = getString(R.string.line)+" "+GtfsUtils.getLineNameFromGtfsID(lineID) + titleTextView.text = getString(R.string.line)+" "+FiveTNormalizer.fixShortNameForDisplay( + GtfsUtils.getLineNameFromGtfsID(lineID), true) favoritesButton?.isClickable = true favoritesButton?.setOnClickListener { @@ -239,6 +241,11 @@ } } viewModel.gtfsRoute.observe(viewLifecycleOwner){route-> + if(route == null){ + //need to close the fragment + activity?.supportFragmentManager?.popBackStack() + return@observe + } descripTextView.text = route.longName descripTextView.visibility = View.VISIBLE } diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/ResultListFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/ResultListFragment.java --- a/app/src/main/java/it/reyboz/bustorino/fragments/ResultListFragment.java +++ b/app/src/main/java/it/reyboz/bustorino/fragments/ResultListFragment.java @@ -152,9 +152,9 @@ String routeName; Route r = (Route) parent.getItemAtPosition(position); - routeName = FiveTNormalizer.routeInternalToDisplay(r.getNameForDisplay()); + routeName = FiveTNormalizer.routeInternalToDisplay(r.getName()); if (routeName == null) { - routeName = r.getNameForDisplay(); + routeName = r.getDisplayCode(); } if (r.destinazione == null || r.destinazione.length() == 0) { Toast.makeText(getContext(), diff --git a/app/src/main/res/layout/entry_bus_line_passage.xml b/app/src/main/res/layout/entry_bus_line_passage.xml --- a/app/src/main/res/layout/entry_bus_line_passage.xml +++ b/app/src/main/res/layout/entry_bus_line_passage.xml @@ -1,24 +1,49 @@ - - + + + + + + Tocca a lungo la fermata per le opzioni - Rimuovi tutti i trip GTFS + Rimuovi i dati dei trip (libera spazio) Tutti i trip GTFS sono rimossi dal database diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -55,4 +55,7 @@ #00000000 + @color/orange_500 + @color/blue_extraurbano + @color/metro_red \ 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 @@ -258,7 +258,7 @@ GTFS RT (more stable, less frequently updated) - Remove all GTFS trips info + Remove trips data (free up space) All GTFS trips have been removed from the database diff --git a/build.gradle b/build.gradle --- a/build.gradle +++ b/build.gradle @@ -11,8 +11,8 @@ } dependencies { - classpath 'com.android.tools.build:gradle:7.3.1' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.20" + classpath 'com.android.tools.build:gradle:7.4.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.22" } } @@ -33,7 +33,7 @@ arch_version = "2.1.0" room_version = "2.5.2" //kotlin - kotlin_version = '1.8.22' + kotlin_version = '1.8.0' coroutines_version = "1.7.0" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Sat Apr 24 16:03:07 CEST 2021 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME