diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -94,18 +94,22 @@
Nessuna fermata nei dintorni
Preferenze
Aggiornamento del database…
- Numero di fermate
+ Numero minimo di fermate
+ Il numero di fermate da ricercare non è valido
+ Valore errato, inserisci un numero
Impostazioni
+ Distanza massima di ricerca (m)
+ Funzionalità sperimentali
Impostazioni
+ Generali
Fermate recenti
Impostazioni generali
Gestione del database
Comincia aggiornamento manuale del database
+
Consenti l\'accesso alla posizione per mostrarla sulla mappa
Abilitare il GPS
- Raggio di ricerca
- Funzionalità sperimentali
arriva alle
alla fermata
Mostra arrivi
diff --git a/res/values/strings.xml b/res/values/strings.xml
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -91,6 +91,7 @@
Get involved! :)
]]>
+
Cannot add to favorites (storage full or corrupted database?)!
View on a map
Cannot find any application to show it in
@@ -99,19 +100,22 @@
ListFragment - BusTO
it.reyboz.bustorino.preferences
db_is_updating
-
+
Nearby stops
Nearby connections
App version
+ The number of stops to show in the recents is invalid
+ Invalid value, put a valid number
Finding the position…
No stops nearby
- Number of stops
+ Minimum number of stops
Preferences
Settings
Settings
+ General
Experimental features
- Search radius
+ Maximum distance (meters)
Recent stops
General settings
Database management
diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml
--- a/res/xml/preferences.xml
+++ b/res/xml/preferences.xml
@@ -2,30 +2,44 @@
+ android:title="@string/general_settings">
+
+
diff --git a/src/it/reyboz/bustorino/backend/FiveTAPIFetcher.java b/src/it/reyboz/bustorino/backend/FiveTAPIFetcher.java
--- a/src/it/reyboz/bustorino/backend/FiveTAPIFetcher.java
+++ b/src/it/reyboz/bustorino/backend/FiveTAPIFetcher.java
@@ -130,7 +130,8 @@
public List parseDirectionsFromResponse(String response) throws IllegalArgumentException,JSONException{
- if(response == null || response.length()==0) throw new IllegalArgumentException("Response string is null or void");
+ if(response == null || response.equals("null") || response.length()==0)
+ throw new IllegalArgumentException("Response string is null or void");
ArrayList routes = new ArrayList<>(10);
JSONArray lines =new JSONArray(response);
for(int i=0; i.
- */
-
-package it.reyboz.bustorino.data;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.readystatesoftware.sqliteasset.SQLiteAssetHelper;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import it.reyboz.bustorino.backend.Route;
-import it.reyboz.bustorino.backend.Stop;
-import it.reyboz.bustorino.backend.StopsDBInterface;
-
-
-public class StopsDB extends SQLiteAssetHelper implements StopsDBInterface {
- private static String QUERY_TABLE_stops = "stops";
- private static String QUERY_WHERE_ID = "ID = ?";
- private static String QUERY_WHERE_LAT_AND_LNG_IN_RANGE = "lat >= ? AND lat <= ? AND lon >= ? AND lon <= ?";
- private static String[] QUERY_COLUMN_name = {"name"};
- private static final String[] QUERY_COLUMN_location = {"location"};
- private static final String[] QUERY_COLUMN_route = {"route"};
- private static final String[] QUERY_COLUMN_everything = {"name", "location", "type", "lat", "lon"};
- private static final String[] QUERY_COLUMN_everything_and_ID = {"ID", "name", "location", "type", "lat", "lon"};
-
- private static String DB_NAME = "stops.sqlite";
- private static int DB_VERSION = 1;
- private SQLiteDatabase db;
- private AtomicInteger openCounter = new AtomicInteger();
-
- public StopsDB(Context context) {
- super(context, DB_NAME, null, DB_VERSION);
- // WARNING: do not remove the following line, do not save anything in this database, it will be overwritten on every update!
- setForcedUpgrade();
-
- // remove old database (BusTo version 1.8.5 and below)
- File filename = new File(context.getFilesDir(), "busto.sqlite");
- if(filename.exists()) {
- //noinspection ResultOfMethodCallIgnored
- filename.delete();
- }
- }
-
- /**
- * Through the magic of an atomic counter, the database gets opened and closed without race
- * conditions between threads (HOPEFULLY).
- *
- * @return database or null if cannot be opened
- */
- @Nullable
- public synchronized SQLiteDatabase openIfNeeded() {
- openCounter.incrementAndGet();
- this.db = getReadableDatabase();
- return this.db;
- }
-
- /**
- * Through the magic of an atomic counter, the database gets really closed only when no thread
- * is using it anymore (HOPEFULLY).
- */
- public synchronized void closeIfNeeded() {
- // is anybody still using the database or can we close it?
- if(openCounter.decrementAndGet() <= 0) {
- super.close();
- this.db = null;
- }
- }
-
- public List getRoutesByStop(@NonNull String stopID) {
- String[] uselessArray = {stopID};
- int count;
- Cursor result;
-
- if(this.db == null) {
- return null;
- }
-
- try {
- result = this.db.query("routemap", QUERY_COLUMN_route, "stop = ?", uselessArray, null, null, null);
- } catch(SQLiteException e) {
- return null;
- }
-
- count = result.getCount();
- if(count == 0) {
- return null;
- }
-
- List routes = new ArrayList<>(count);
-
- while(result.moveToNext()) {
- routes.add(result.getString(0));
- }
-
- result.close();
-
- return routes;
- }
-
- public String getLocationFromID(@NonNull String stopID) {
- String[] uselessArray = {stopID};
- int count;
- String name;
- Cursor result;
-
- if(this.db == null) {
- return null;
- }
-
- try {
- result = this.db.query(QUERY_TABLE_stops, QUERY_COLUMN_location, QUERY_WHERE_ID, uselessArray, null, null, null);
- } catch(SQLiteException e) {
- return null;
- }
-
- count = result.getCount();
- if(count == 0) {
- return null;
- }
-
- result.moveToNext();
- name = result.getString(0);
-
- result.close();
-
- return name;
- }
-
- public Stop getAllFromID(@NonNull String stopID) {
- Cursor result;
- int count;
- Stop s;
-
- if(this.db == null) {
- return null;
- }
-
- try {
- result = this.db.query(QUERY_TABLE_stops, QUERY_COLUMN_everything, QUERY_WHERE_ID, new String[] {stopID}, null, null, null);
- int colName = result.getColumnIndex("name");
- int colLocation = result.getColumnIndex("location");
- int colType = result.getColumnIndex("type");
- int colLat = result.getColumnIndex("lat");
- int colLon = result.getColumnIndex("lon");
-
- count = result.getCount();
- if(count == 0) {
- return null;
- }
-
- result.moveToNext();
-
- Route.Type type = routeTypeFromSymbol(result.getString(colType));
-
- String locationWhichSometimesIsAnEmptyString = result.getString(colLocation);
- if(locationWhichSometimesIsAnEmptyString.length() <= 0) {
- locationWhichSometimesIsAnEmptyString = null;
- }
-
- s = new Stop(stopID, result.getString(colName), null, locationWhichSometimesIsAnEmptyString, type, getRoutesByStop(stopID), result.getDouble(colLat), result.getDouble(colLon));
- } catch(SQLiteException e) {
- return null;
- }
-
- result.close();
-
- return s;
- }
-
- /**
- * Query some bus stops inside a map view
- *
- * You can obtain the coordinates from OSMDroid using something like this:
- * BoundingBoxE6 bb = mMapView.getBoundingBox();
- * double latFrom = bb.getLatSouthE6() / 1E6;
- * double latTo = bb.getLatNorthE6() / 1E6;
- * double lngFrom = bb.getLonWestE6() / 1E6;
- * double lngTo = bb.getLonEastE6() / 1E6;
- */
- public Stop[] queryAllInsideMapView(double minLat, double maxLat, double minLng, double maxLng) {
- Stop[] stops = new Stop[0];
-
- Cursor result;
- int count;
-
- // coordinates must be strings in the where condition
- String minLatRaw = String.valueOf(minLat);
- String maxLatRaw = String.valueOf(maxLat);
- String minLngRaw = String.valueOf(minLng);
- String maxLngRaw = String.valueOf(maxLng);
-
- String stopID;
- Route.Type type;
-
- if(this.db == null) {
- return stops;
- }
-
- try {
- result = this.db.query(QUERY_TABLE_stops, QUERY_COLUMN_everything_and_ID, QUERY_WHERE_LAT_AND_LNG_IN_RANGE, new String[] {minLatRaw, maxLatRaw, minLngRaw, maxLngRaw}, null, null, null);
-
- int colID = result.getColumnIndex("ID");
- int colName = result.getColumnIndex("name");
- int colLocation = result.getColumnIndex("location");
- int colType = result.getColumnIndex("type");
- int colLat = result.getColumnIndex("lat");
- int colLon = result.getColumnIndex("lon");
-
- count = result.getCount();
- stops = new Stop[count];
-
- int i = 0;
- while(result.moveToNext()) {
-
- stopID = result.getString(colID);
- type = routeTypeFromSymbol(result.getString(colType));
-
- String locationWhichSometimesIsAnEmptyString = result.getString(colLocation);
- if (locationWhichSometimesIsAnEmptyString.length() <= 0) {
- locationWhichSometimesIsAnEmptyString = null;
- }
-
- stops[i++] = new Stop(stopID, result.getString(colName), null,
- locationWhichSometimesIsAnEmptyString, type, getRoutesByStop(stopID),
- result.getDouble(colLat), result.getDouble(colLon));
- }
-
- } catch(SQLiteException e) {
- // TODO: put a warning in the log
- return stops;
- }
-
- result.close();
-
- return stops;
- }
-
- /**
- * Get a Route Type from its char symbol
- *
- * @param route The route symbol (e.g. "B")
- * @return The related Route.Type (e.g. Route.Type.Bus)
- */
- public static Route.Type routeTypeFromSymbol(String route) {
- switch (route) {
- case "M":
- return Route.Type.METRO;
- case "T":
- return Route.Type.RAILWAY;
- }
-
- // default with case "B"
- return Route.Type.BUS;
- }
-}
diff --git a/src/it/reyboz/bustorino/fragments/MainScreenFragment.java b/src/it/reyboz/bustorino/fragments/MainScreenFragment.java
--- a/src/it/reyboz/bustorino/fragments/MainScreenFragment.java
+++ b/src/it/reyboz/bustorino/fragments/MainScreenFragment.java
@@ -131,8 +131,8 @@
if(status == AppLocationManager.LOCATION_GPS_AVAILABLE && !isNearbyFragmentShown()){
//request Stops
pendingNearbyStopsRequest = false;
-
- mainHandler.post(new NearbyStopsRequester(getContext(), cr));
+ if (getContext()!= null)
+ mainHandler.post(new NearbyStopsRequester(getContext(), cr));
}
}
@@ -149,7 +149,7 @@
@Override
public void onLocationProviderAvailable() {
//Log.w(DEBUG_TAG, "pendingNearbyStopRequest: "+pendingNearbyStopsRequest);
- if(!isNearbyFragmentShown()){
+ if(!isNearbyFragmentShown() && getContext()!=null){
pendingNearbyStopsRequest = false;
mainHandler.post(new NearbyStopsRequester(getContext(), cr));
}
diff --git a/src/it/reyboz/bustorino/fragments/NearbyStopsFragment.java b/src/it/reyboz/bustorino/fragments/NearbyStopsFragment.java
--- a/src/it/reyboz/bustorino/fragments/NearbyStopsFragment.java
+++ b/src/it/reyboz/bustorino/fragments/NearbyStopsFragment.java
@@ -44,6 +44,7 @@
import android.widget.ProgressBar;
import android.widget.TextView;
import com.android.volley.*;
+import it.reyboz.bustorino.BuildConfig;
import it.reyboz.bustorino.R;
import it.reyboz.bustorino.adapters.ArrivalsStopAdapter;
import it.reyboz.bustorino.backend.*;
@@ -272,7 +273,21 @@
SharedPreferences shpr = PreferenceManager.getDefaultSharedPreferences(getContext().getApplicationContext());
//For some reason, they are all saved as strings
MAX_DISTANCE = shpr.getInt(getString(R.string.pref_key_radius_recents),600);
- MIN_NUM_STOPS = Integer.parseInt(shpr.getString(getString(R.string.pref_key_num_recents),"10"));
+ boolean isMinStopInt = true;
+ try{
+ MIN_NUM_STOPS = shpr.getInt(getString(R.string.pref_key_num_recents), 10);
+ } catch (ClassCastException ex){
+ isMinStopInt = false;
+ }
+ if(!isMinStopInt)
+ try {
+ MIN_NUM_STOPS = Integer.parseInt(shpr.getString(getString(R.string.pref_key_num_recents), "10"));
+ } catch (NumberFormatException ex){
+ MIN_NUM_STOPS = 10;
+ }
+ if(BuildConfig.DEBUG)
+ Log.d(DEBUG_TAG, "Max distance for stops: "+MAX_DISTANCE+
+ ", Min number of stops: "+MIN_NUM_STOPS);
}
@@ -350,7 +365,7 @@
}
@Override
- public void onLoaderReset(Loader loader) {
+ public void onLoaderReset(@NonNull Loader loader) {
}
/**
diff --git a/src/it/reyboz/bustorino/fragments/SettingsFragment.java b/src/it/reyboz/bustorino/fragments/SettingsFragment.java
--- a/src/it/reyboz/bustorino/fragments/SettingsFragment.java
+++ b/src/it/reyboz/bustorino/fragments/SettingsFragment.java
@@ -3,34 +3,126 @@
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
+import android.os.Handler;
import android.util.Log;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceFragmentCompat;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.preference.*;
import it.reyboz.bustorino.R;
+import java.lang.ref.WeakReference;
+
public class SettingsFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final String TAG = SettingsFragment.class.getName();
- SharedPreferences preferences;
+ private static final String DIALOG_FRAGMENT_TAG =
+ "androidx.preference.PreferenceFragment.DIALOG";
+ //private static final
+ Handler mHandler;
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ mHandler = new Handler();
+ return super.onCreateView(inflater, container, savedInstanceState);
+
+ }
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ //getPreferenceManager().setSharedPreferencesName(getString(R.string.mainSharedPreferences));
+ convertStringPreferenceToInt(getString(R.string.pref_key_num_recents), getContext());
+
+ getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
setPreferencesFromResource(R.xml.preferences,rootKey);
- Context con = getContext();
- if (con == null){
- Log.w("SETTINGS FRAGMENT", "context is null");
- preferences = null;
- }
- else
- preferences = getContext().getSharedPreferences(getString(R.string.mainSharedPreferences), Context.MODE_PRIVATE);
+ /*EditTextPreference editPref = findPreference(getString(R.string.pref_key_num_recents));
+ editPref.setOnBindEditTextListener(editText -> {
+ editText.setInputType(InputType.TYPE_CLASS_NUMBER);
+ editText.setSelection(0,editText.getText().length());
+ });
+ */
+
+
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Preference pref = findPreference(key);
- //non so a cosa serva tutto questo
+ Log.d(TAG,"Preference key "+key+" changed");
+ //sometimes this happens
+ if(getContext()==null) return;
+ /*
+ THIS CODE STAYS COMMENTED FOR FUTURE REFERENCES
+ if (key.equals(getString(R.string.pref_key_num_recents))){
+ //check that is it an int
+
+ String value = sharedPreferences.getString(key,"");
+ boolean valid = value.length() != 0;
+ try{
+ Integer intValue = Integer.parseInt(value);
+ } catch (NumberFormatException ex){
+ valid = false;
+ }
+ if (!valid){
+ Toast.makeText(getContext(), R.string.invalid_number, Toast.LENGTH_SHORT).show();
+ if(pref instanceof EditTextPreference){
+ EditTextPreference prefEdit = (EditTextPreference) pref;
+ //Intent intent = prefEdit.getIntent();
+ Log.d(TAG, "opening preference, dialog showing "+
+ (getParentFragmentManager().findFragmentByTag(DIALOG_FRAGMENT_TAG)!=null) );
+ //getPreferenceManager().showDialog(pref);
+ //onDisplayPreferenceDialog(prefEdit);
+ mHandler.postDelayed(new DelayedDisplay(prefEdit), 500);
+ }
+
+ }
+ }
+ */
Log.d("BusTO Settings", "changed "+key+"\n "+sharedPreferences.getAll());
}
+
+ private void convertStringPreferenceToInt(String preferenceKey, Context con){
+ if (con == null) return;
+ SharedPreferences defaultSharedPref = PreferenceManager.getDefaultSharedPreferences(getContext());
+ try{
+
+ Integer val = defaultSharedPref.getInt(preferenceKey, 0);
+ } catch (NumberFormatException | ClassCastException ex){
+ //convert the preference
+ //final String preferenceNumRecents = getString(R.string.pref_key_num_recents);
+ Log.d("Preference - BusTO", "Converting to integer the string preference "+preferenceKey);
+ String currentValue = defaultSharedPref.getString(preferenceKey, "10");
+ int newValue;
+ try{
+ newValue = Integer.parseInt(currentValue);
+ } catch (NumberFormatException e){
+ newValue = 10;
+ }
+ final SharedPreferences.Editor editor = defaultSharedPref.edit();
+ editor.remove(preferenceKey);
+ editor.putInt(preferenceKey, newValue);
+ editor.apply();
+ }
+ }
+
+ class DelayedDisplay implements Runnable{
+ private final WeakReference preferenceWeakReference;
+
+ public DelayedDisplay(DialogPreference preference) {
+ this.preferenceWeakReference = new WeakReference<>(preference);
+ }
+
+ @Override
+ public void run() {
+ if(preferenceWeakReference.get()==null)
+ return;
+
+ getPreferenceManager().showDialog(preferenceWeakReference.get());
+ }
+ }
}
diff --git a/src/it/reyboz/bustorino/middleware/AsyncDataDownload.java b/src/it/reyboz/bustorino/middleware/AsyncDataDownload.java
--- a/src/it/reyboz/bustorino/middleware/AsyncDataDownload.java
+++ b/src/it/reyboz/bustorino/middleware/AsyncDataDownload.java
@@ -116,8 +116,12 @@
return null;
}
//Skip the FiveTAPIFetcher for the Metro Stops because it shows incomprehensible arrival times
- if(f instanceof FiveTAPIFetcher && Integer.parseInt(stopID)>= 8200)
- continue;
+ try {
+ if (f instanceof FiveTAPIFetcher && Integer.parseInt(stopID) >= 8200)
+ continue;
+ } catch (NumberFormatException ex){
+ Log.e(DEBUG_TAG, "The stop number is not a valid integer, expect failures");
+ }
p= f.ReadArrivalTimesAll(stopID,res);
publishProgress(res.get());
//if (res.get()!= Fetcher.Result.OK)