diff --git a/res/layout/fragment_arrivals.xml b/res/layout/fragment_arrivals.xml
index 190516a..ce184fe 100644
--- a/res/layout/fragment_arrivals.xml
+++ b/res/layout/fragment_arrivals.xml
@@ -1,49 +1,98 @@
+ fab:cardElevation="2dp">
+ android:layout_toLeftOf="@+id/addToFavorites"
+
+ android:foreground="?attr/selectableItemBackground"
+ android:gravity="center_vertical"
+ android:minHeight="40dp"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+ tools:ignore="OnClick" />
-
+ android:orientation="vertical"
+
+ android:layout_height="wrap_content">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 5172f0a..e52be70 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -1,118 +1,126 @@
Stai utilizzando l\'ultimo ritrovato in materia di rispetto della tua privacy.
Cerca
QR Code
Numero fermata
Nome fermata
Inserisci il numero della fermata
Inserisci il nome della fermata
Verifica l\'accesso ad Internet!
Sembra che nessuna fermata abbia questo nome
Errore di lettura del sito 5T/GTT (dannato sito!)
Fermata: %1$s
Linee: %1$s
Scegli la fermata…
Nessun passaggio
Nessun QR code
Preferiti
Aiuto
Informazioni
Più informazioni
News
Invia bug
Codice sorgente
Licenza
Incontra l\'autore
Fermata aggiunta ai preferiti
Impossibile aggiungere ai preferiti (memoria piena o database corrotto?)!
Preferiti
Mappa
Nessun preferito? Arghh! Schiaccia sulla stella di una fermata per aggiungerla a questa lista!
Rimuovi
Rinomina
Rinomina fermata
Reset
Informazioni
Tocca la stella per aggiungere la fermata ai preferiti\n\nCome leggere gli orari:\n 12:56* Orario in tempo reale\n 12:56 Orario programmato\n\nTrascina giù per aggiornare l\'orario.
OK!
Benvenuto!
Grazie per aver scelto BusTO, un\'app indipendente da GTT/5T, per spostarsi a Torino attraverso software libero:
Perché usare BusTO?
- Non sei monitorato
- Non ci sono pubblicità
- La tua privacy è al sicuro
- Inoltre l\'app è molto leggera!
Come Funziona?
Quest\'app ottiene i passaggi dei bus in tempo reale filtrando i dati forniti pubblicamente sul sito www.gtt.to.it o www.5t.torino.it "per uso personale".
Ingredienti:
- Fabio Mazza attuale rockstar developer anziano.
- Andrea Ugo attuale rockstar developer in formazione.
- Ludovico Pavesi ex rockstar developer anziano.
- Valerio Bozzolan attuale manutentore.
- Marco Gagino apprezzato ex collaboratore, ideatore icona e grafica.
- JSoup libreria per "web scaping".
- Google icone e libreria di supporto per il Material Design.
- Tutti i contributori!
Licenze
L\'app e il relativo codice sorgente sono distribuiti sotto la licenza GNU General Public License v3+.
Ciò significa che puoi usare, studiare, migliorare e ricondividere quest\'app con qualunque mezzo e per qualsiasi scopo: a patto di mantenere sempre questi diritti a tua volta e di dare credito a Valerio Bozzolan.
Note
Quest\'applicazione è rilasciata nella speranza che sia utile a tutti ma senza NESSUNA garanzia.
Buon utilizzo! :)
]]>
Nome troppo corto, digita più caratteri e riprova
%1$s verso %2$s
%s (destinazione sconosciuta)
Errore interno inaspettato, impossibile estrarre dati dal sito GTT/5T
Visualizza sulla mappa
Non trovo un\'applicazione dove mostrarla
Posizione della fermata non trovata
Fermate vicine
Ricerca della posizione in corso…
Nessuna fermata nei dintorni
Preferenze
Aggiornamento del database…
Numero di fermate
Impostazioni
Impostazioni
Fermate recenti
Impostazioni generali
Gestione del database
Comincia aggiornamento manuale del database
Abilitare il GPS
Raggio di ricerca
Funzionalità sperimentali
arriva alle
alla fermata
Mostra arrivi
Mostra fermate
Arrivi qui vicino
Fermata rimossa dai preferiti
La mia posizione
Segui posizione
+
+ Fonte orari: %1$s
+ App GTT
+ Sito GTT
+ Sito 5T Torino
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f78d599..525f6c2 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1,129 +1,136 @@
BusTO
You\'re using the latest in technology when it comes to respecting your privacy.
Search
Scan QR Code
Bus stop number
Bus stop name
Insert bus stop number
Insert bus stop name
%1$s towards %2$s
%s (unknown destination)
Verify your Internet connection!
Seems that no bus stop have this name
Error parsing the 5T/GTT website (damn site!)
Name too short, type more characters and retry
Arrivals at: %1$s
Choose the bus stop…
Lines: %1$s
No timetable found
No QR code
Unexpected internal error, cannot extract data from GTT/5T website
Help
About
More about
News releases
Bug submission
Source code
Licence
Meet the author
Bus stop is now in your favorites
Bus stop removed from your favorites
Favorites
Favorites
Map
No favorites? Arghh! Press on a bus stop star to populate this list!
Delete
Rename
Rename the bus stop
Reset
About
Tap the star
to add the bus stop to the favourites\n\nHow to read timelines:\n 12:56* Real-time
arrivals\n 12:56 Scheduled arrivals\n\nPull down to refresh the timetable
GOT IT!
Welcome!
Thanks for using BusTO, a "politically" independent app useful to move around Torino using a Free/Libre software.
Why use this app?
- You\'ll never be tracked
- You\'ll never see boring ads
- We\'ll always respect your privacy
- Moreover, it\'s lightweight!
How does it work?
This app will show you bus timetables gathering data from www.gtt.to.it or www.5t.torino.it "for personal use".
Who worked on BusTO:
- Fabio Mazza current senior rockstar developer.
- Andrea Ugo current junior rockstar developer.
- Ludovico Pavesi previous senior rockstar developer.
- Valerio Bozzolan maintainer and infrastructure sponsor.
- Marco Gagino contributor and icon creator.
- JSoup web scraper library.
- makovkastar floating buttons.
- Google Material Design icons.
- All the contributors!
Licenses
The app and the related source code are released by Valerio Bozzolan under the terms of the GNU General Public License v3+).
So everyone is allowed to use, to study, to improve and to share this app by any kind of means and for any purpose: under the conditions of maintaining this rights and of attributing the original work to Valerio Bozzolan.
Notes
This app has been developed hoping to be useful to everyone but without ANY warranty.
This translation is kindly provided by Riccardo Caniato and Marco Gagino.
Get involved! :)
]]>
Cannot add to favorites (storage full or corrupted database?)!
View on a map
Cannot find any application to show it in
Cannot find the position of the stop
ListFragment - BusTO
it.reyboz.bustorino.preferences
db_is_updating
Nearby stops
Nearby connections
Finding the position…
No stops nearby
Number of stops
Preferences
Settings
Settings
Experimental features
Search radius
Recent stops
General settings
Database management
Launch manual database update
Please enable GPS
Database update in progress…
is arriving at
at the stop
%1$s - %2$s
Show arrivals
Show stops
Center on my location
Follow me
+
+ Arrivals source: %1$s
+ GTT App
+ GTT Website
+ 5T Torino website
diff --git a/src/it/reyboz/bustorino/backend/FiveTScraperFetcher.java b/src/it/reyboz/bustorino/backend/FiveTScraperFetcher.java
index 5f39aa6..d5da78f 100644
--- a/src/it/reyboz/bustorino/backend/FiveTScraperFetcher.java
+++ b/src/it/reyboz/bustorino/backend/FiveTScraperFetcher.java
@@ -1,206 +1,206 @@
/*
BusTO - Arrival times for Turin public transports.
Copyright (C) 2014 Valerio Bozzolan
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
package it.reyboz.bustorino.backend;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.net.URL;
import java.net.URLEncoder;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
//import android.util.Log;
/**
* Contains large chunks of code taken from the old GTTSiteSucker, AsyncWget and AsyncWgetBusStopFromBusStopID classes.
*
* «BusTO, because sucking happens»
*
* @author Valerio Bozzolan
*/
public class FiveTScraperFetcher implements ArrivalsFetcher {
/**
* Execute regexes.
*
* @param needle Regex
* @param haystack Entire string
* @return Matched string
*/
private static String grep(String needle, String haystack) {
String matched = null;
Matcher matcher = Pattern.compile(
needle).matcher(haystack);
if (matcher.find()) {
matched = matcher.group(1);
}
return matched;
}
@Override
public Palina ReadArrivalTimesAll(final String stopID, final AtomicReference res) {
Palina p = new Palina(stopID);
int routeIndex;
String responseInDOMFormatBecause5THaveAbsolutelyNoIdeaWhatJSONWas = null;
try {
responseInDOMFormatBecause5THaveAbsolutelyNoIdeaWhatJSONWas = networkTools.getDOM(new URL("http://www.5t.torino.it/5t/trasporto/arrival-times-byline.jsp?action=getTransitsByLine&shortName=" + URLEncoder.encode(stopID, "utf-8")), res);
} catch (Exception e) {
res.set(result.PARSER_ERROR);
}
if(responseInDOMFormatBecause5THaveAbsolutelyNoIdeaWhatJSONWas == null) {
// result already set in getDOM()
return p;
}
Document doc = Jsoup.parse(responseInDOMFormatBecause5THaveAbsolutelyNoIdeaWhatJSONWas);
// Tried in rete Edisu (it does Man In The Middle... asd)
Element span = doc.select("span").first();
if(span == null) {
res.set(result.SERVER_ERROR);
return p;
}
String busStopID = grep("^(.+) ", span.html());
if (busStopID == null) {
//Log.e("BusStop", "Empty busStopID from " + span.html());
res.set(result.EMPTY_RESULT_SET);
return p;
}
// this also appears when no stops are found, but that case has already been handled above
Element error = doc.select("p.errore").first();
if (error != null) {
res.set(result.SERVER_ERROR);
return p;
}
String busStopName = grep("^.+ (.+)", span.html()); // The first "dot" is the single strange space character in the middle of "39{HERE→} {←HERE}PORTA NUOVA"
if (busStopName == null) {
//Log.e("BusStop", "Empty busStopName from " + span.html());
res.set(result.SERVER_ERROR);
return p;
}
p.setStopName(busStopName.trim());
// Every table row is a busLine
Elements trs = doc.select("table tr");
for (Element tr : trs) {
Element line = tr.select("td.line a").first();
if (!line.hasText()) {
res.set(result.SERVER_ERROR);
return p;
}
String busLineName = line.text();
// this is yet another ID, that has no known use so we can safely ignore it
// Integer busLineID = string2Integer(
// grep(
// "([0-9]+)$",
// line.attr("href")
// )
// );
if (busLineName == null) {
res.set(result.SERVER_ERROR);
return p;
}
// this fetcher doesn't support railways and probably they've removed METRO too, but anyway...
if(busLineName.equals("METRO")) {
routeIndex = p.addRoute(busLineName, "", Route.Type.METRO);
} else {
if(busLineName.length() >= 4) {
boolean isExtraurbano = true;
for(int ch = 0; ch < busLineName.length(); ch++) {
if(!Character.isDigit(busLineName.charAt(ch))) {
isExtraurbano = false;
break;
}
}
if(isExtraurbano) {
routeIndex = p.addRoute(busLineName, "", Route.Type.LONG_DISTANCE_BUS);
} else {
routeIndex = p.addRoute(busLineName, "", Route.Type.BUS);
}
} else {
routeIndex = p.addRoute(busLineName, "", Route.Type.BUS);
}
}
// Every busLine have passages
Elements tds = tr.select("td:not(.line)");
for (Element td : tds) {
//boolean isInRealTime = td.select("i").size() > 0;
//td.select("i").remove(); // Stripping "*"
String time = td.text().trim();
if (time.equals("")) {
// Yes... Sometimes there is an EMPTY td ._.
continue;
}
- p.addPassaggio(time, Passaggio.Source.FiveTScraper,routeIndex);
+ p.addPassaggio(time, Passaggio.Source.FiveTScraper, routeIndex);
}
}
p.sortRoutes();
res.set(result.OK);
return p;
}
// preserved for future generations:
// /*
// * I've sent many emails to the public email info@5t.torino.it to write down something like:
// * «YOUR SITE EXPLODE IF I USE **YOUR** BUS LINE IDs STARTING WITH ZERO!!!!!»
// * So, waiting for a response, I must purge the busStopID from "0"s .__.
// * IN YOUR FACE 5T/GTT. IN YOUR FACE.
// *
// * @param busStopID
// * @return parseInt(busStopID)
// * @antifeatured yep
// * @notabug yep
// * @wontfix yep
// */
// protected final String getFilteredBusStopID(String busStopID) {
// /*
// * OK leds me ezplain why 'm dong this shot of shittt. OK zo swhy?
// * Bhumm thads because the GTT/5T site-"developer" ids obviusli drunk.
// */
// String enableGTTDeveloperSimulator = "on"; // DRUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUNK
// final char ZZZZZZZEEEEROOOOOO = '0'; // DRUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUNK
// char[] cinquettiBarraGtt = busStopID.toCharArray(); // DRUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUNK
// int merda = 0; // DRUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUNK
// while (merda < cinquettiBarraGtt.length && cinquettiBarraGtt[merda] == ZZZZZZZEEEEROOOOOO) {
// // COMPLETELELELLELEEELY DRUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUNK
// Log.i("AsyncWgetBusStop", "scimmie ubriache assunte per tirar su il sito 5T/GTT"); // DR
// merda++; // DRUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUNK
// } // DRUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUNK
// String trenoDiMerda = ""; // DRUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUNK
// for (; merda < cinquettiBarraGtt.length; merda++) { // DRUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUNK
// trenoDiMerda += cinquettiBarraGtt[merda]; // DRUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUNK
// } // DRUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUNK
// enableGTTDeveloperSimulator = "off"; // DRUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUNK
//
// return trenoDiMerda;
// }
}
diff --git a/src/it/reyboz/bustorino/backend/Palina.java b/src/it/reyboz/bustorino/backend/Palina.java
index 62e0231..a67c86c 100644
--- a/src/it/reyboz/bustorino/backend/Palina.java
+++ b/src/it/reyboz/bustorino/backend/Palina.java
@@ -1,317 +1,349 @@
/*
BusTO (backend components)
Copyright (C) 2016 Ludovico Pavesi
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
package it.reyboz.bustorino.backend;
import android.util.Log;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
/**
* Timetable for multiple routes.
*
* Apparently "palina" and a bunch of other terms can't really be translated into English.
* Not in a way that makes sense and keeps the code readable, at least.
*/
public class Palina extends Stop {
private ArrayList routes = new ArrayList<>();
+ private boolean routesModified = false;
+ private Passaggio.Source allSource = null;
public Palina(String stopID) {
super(stopID);
}
public Palina(Stop s){
super(s.ID,s.getStopDefaultName(),s.getStopUserName(),s.location,s.type,
s.getRoutesThatStopHere(),s.getLatitude(),s.getLongitude());
}
/**
* Adds a timetable entry to a route.
*
* @param TimeGTT time in GTT format (e.g. "11:22*")
* @param arrayIndex position in the array for this route (returned by addRoute)
*/
public void addPassaggio(String TimeGTT, Passaggio.Source src,int arrayIndex) {
this.routes.get(arrayIndex).addPassaggio(TimeGTT,src);
+ routesModified = true;
}
/**
* Count routes with missing directions
* @return number
*/
public int countRoutesWithMissingDirections(){
int i = 0;
for (Route r : routes){
if(r.destinazione==null||r.destinazione.equals(""))
i++;
}
return i;
}
/**
* Adds a route to the timetable.
*
* @param routeID name
* @param type bus, underground, railway, ...
* @param destinazione end of line\terminus (underground stations have the same ID for both directions)
* @return array index for this route
*/
public int addRoute(String routeID, String destinazione, Route.Type type) {
- this.routes.add(new Route(routeID, destinazione, type, new ArrayList(6)));
+ this.routes.add(new Route(routeID, destinazione, type, new ArrayList<>(6)));
+ routesModified = true;
return this.routes.size() - 1; // last inserted element and pray that direct access to ArrayList elements really is direct
}
public int addRoute(Route r){
this.routes.add(r);
+ routesModified = true;
return this.routes.size()-1;
}
public void setRoutes(List routeList){
routes = new ArrayList<>(routeList);
}
// /**
// * Clears a route timetable (or creates an empty route) and returns its index
// *
// * @param routeID name
// * @param destinazione end of line\terminus
// * @return array index for this route
// */
// public int updateRoute(String routeID, String destinazione) {
// int s = this.routes.size();
// RouteInternal r;
//
// for(int i = 0; i < s; i++) {
// r = routes.get(i);
// if(r.name.compareTo(routeID) == 0 && r.destinazione.compareTo(destinazione) == 0) {
// // capire se è possibile che ci siano stessa linea e stessa destinazione su 2 righe diverse del sito e qui una sovrascrive l'altra (probabilmente no)
// r.updateFlag();
// r.deletePassaggio();
// return i;
// }
// }
//
// return this.addRoute(routeID, destinazione);
// }
//
// /**
// * Deletes routes marked as "not updated" (= disappeared from the GTT website\API\whatever).
// * Sets all remaining routes to "not updated" because that's how this contraption works.
// */
// public void finishUpdatingRoutes() {
// RouteInternal r;
//
// for(Iterator itr = this.routes.iterator(); itr.hasNext(); ) {
// r = itr.next();
// if(r.unupdateFlag()) {
// itr.remove();
// }
// }
// }
// /**
// * Gets the current timetable for a route. Returns null if the route doesn't exist.
// * This is slower than queryRouteByIndex.
// *
// * @return timetable (passaggi)
// */
// public List queryRoute(String routeID) {
// for(Route r : this.routes) {
// if(routeID.equals(r.name)) {
// return r.getPassaggi();
// }
// }
//
// return null;
// }
//
// /**
// * Gets the current timetable for this route, from its index in the array.
// *
// * @return timetable (passaggi)
// */
// public List queryRouteByIndex(int index) {
// return this.routes.get(index).getPassaggi();
// }
+ protected void checkPassaggi(){
+ Passaggio.Source mSource = null;
+ for (Route r: routes){
+ for(Passaggio pass: r.passaggi){
+ if (mSource == null) {
+ mSource = pass.source;
+ } else if (mSource != pass.source){
+ Log.w("BusTO-CheckPassaggi",
+ "Cannot determine the source, have got "+mSource +" so far, the next one is "+pass.source );
+ mSource = Passaggio.Source.UNKNOWN;
+
+ break;
+ }
+ }
+ if(mSource == Passaggio.Source.UNKNOWN)
+ break;
+ }
+ //finished with the check, setting flags
+ routesModified = false;
+ allSource = mSource;
+ }
+
+ public Passaggio.Source getPassaggiSourceIfAny(){
+ if(allSource==null || routesModified){
+ checkPassaggi();
+ }
+ assert allSource != null;
+ return allSource;
+ }
/**
* Gets every route and its timetable.
*
* @return routes and timetables.
*/
public List queryAllRoutes() {
return this.routes;
}
public void sortRoutes() {
Collections.sort(this.routes);
}
/**
* Add info about the routes already found from another source
* @param additionalRoutes ArrayList of routes to get the info from
* @return the number of routes modified
*/
public int addInfoFromRoutes(List additionalRoutes){
if(routes == null || routes.size()==0) {
this.routes = new ArrayList<>(additionalRoutes);
return routes.size();
}
int count=0;
final Calendar c = Calendar.getInstance();
final int todaysInt = c.get(Calendar.DAY_OF_WEEK);
for(Route r:routes) {
int j = 0;
boolean correct = false;
Route selected = null;
//TODO: rewrite this as a simple loop
//MADNESS begins here
while (!correct) {
//find the correct route to merge to
// scan routes and find the first which has the same name
while (j < additionalRoutes.size() && !r.getName().equals(additionalRoutes.get(j).getName())) {
j++;
}
if (j == additionalRoutes.size()) break; //no match has been found
//should have found the first occurrence of the line
selected = additionalRoutes.get(j);
//move forward
j++;
if (selected.serviceDays != null && selected.serviceDays.length > 0) {
//check if it is in service
for (int d : selected.serviceDays) {
if (d == todaysInt) {
correct = true;
break;
}
}
} else if (r.festivo != null) {
switch (r.festivo) {
case FERIALE:
//Domenica = 1 --> Saturday=7
if (todaysInt <= 7 && todaysInt > 1) correct = true;
break;
case FESTIVO:
if (todaysInt == 1) correct = true; //TODO: implement way to recognize all holidays
break;
case UNKNOWN:
correct = true;
}
} else {
//case a: there is no info because the line is always active
//case b: there is no info because the information is missing
correct = true;
}
}
if (!correct || selected == null) {
Log.w("Palina_mergeRoutes","Cannot match the route with name "+r.getName());
continue; //we didn't find any match
}
//found the correct correspondance
//MERGE INFO
if(r.mergeRouteWithAnother(selected)) count++;
}
return count;
}
-
-
// /**
// * Route with terminus (destinazione) and timetables (passaggi), internal implementation.
// *
// * Contains mostly the same data as the Route public class, but methods are quite different and extending Route doesn't really work, here.
// */
// private final class RouteInternal {
// public final String name;
// public final String destinazione;
// private boolean updated;
// private List passaggi;
//
// /**
// * Creates a new route and marks it as "updated", since it's new.
// *
// * @param routeID name
// * @param destinazione end of line\terminus
// */
// public RouteInternal(String routeID, String destinazione) {
// this.name = routeID;
// this.destinazione = destinazione;
// this.passaggi = new LinkedList<>();
// this.updated = true;
// }
//
// /**
// * Adds a time (passaggio) to the timetable for this route
// *
// * @param TimeGTT time in GTT format (e.g. "11:22*")
// */
// public void addPassaggio(String TimeGTT) {
// this.passaggi.add(new Passaggio(TimeGTT));
// }
//
// /**
// * Deletes al times (passaggi) from the timetable.
// */
// public void deletePassaggio() {
// this.passaggi = new LinkedList<>();
// this.updated = true;
// }
//
// /**
// * Sets the "updated" flag to false.
// *
// * @return previous state
// */
// public boolean unupdateFlag() {
// if(this.updated) {
// this.updated = false;
// return true;
// } else {
// return false;
// }
// }
//
// /**
// * Sets the "updated" flag to true.
// *
// * @return previous state
// */
// public boolean updateFlag() {
// if(this.updated) {
// return true;
// } else {
// this.updated = true;
// return false;
// }
// }
//
// /**
// * Exactly what it says on the tin.
// *
// * @return times from the timetable
// */
// public List getPassaggi() {
// return this.passaggi;
// }
// }
}
\ No newline at end of file
diff --git a/src/it/reyboz/bustorino/backend/Passaggio.java b/src/it/reyboz/bustorino/backend/Passaggio.java
index d4bdf72..e09901d 100644
--- a/src/it/reyboz/bustorino/backend/Passaggio.java
+++ b/src/it/reyboz/bustorino/backend/Passaggio.java
@@ -1,159 +1,159 @@
/*
BusTO (backend components)
Copyright (C) 2016 Ludovico Pavesi
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
package it.reyboz.bustorino.backend;
import android.support.annotation.NonNull;
import android.util.Log;
public final class Passaggio implements Comparable {
private static final int UNKNOWN_TIME = -3;
private static final String DEBUG_TAG = "BusTO-Passaggio";
private final String passaggioGTT;
public final int hh,mm;
public final boolean isInRealTime;
- public final Source passageSource;
+ public final Source source;
/**
* Useless constructor.
*
* //@param TimeGTT time in GTT format (e.g. "11:22*"), already trimmed from whitespace.
*/
// public Passaggio(@NonNull String TimeGTT) {
// this.passaggio = TimeGTT;
// }
@Override
public String toString() {
return this.passaggioGTT;
}
/**
* Constructs a time (passaggio) for the timetable.
*
* @param TimeGTT time in GTT format (e.g. "11:22*"), already trimmed from whitespace.
* @throws IllegalArgumentException if nothing reasonable can be extracted from the string
*/
public Passaggio(@NonNull String TimeGTT, @NonNull Source sorgente) {
passaggioGTT = TimeGTT;
- passageSource = sorgente;
+ source = sorgente;
String[] parts = TimeGTT.split(":");
String hh,mm;
boolean realtime;
if(parts.length != 2) {
//throw new IllegalArgumentException("The string " + TimeGTT + " doesn't follow the sacred format of time according to GTT!");
Log.w(DEBUG_TAG,"The string " + TimeGTT + " doesn't follow the sacred format of time according to GTT!");
this.hh = UNKNOWN_TIME;
this.mm = UNKNOWN_TIME;
this.isInRealTime = false;
return;
}
hh = parts[0];
if(parts[1].endsWith("*")) {
mm = parts[1].substring(0, parts[1].length() - 1);
realtime = true;
} else {
mm = parts[1];
realtime = false;
}
int hour=-3,min=-3;
try {
hour = Integer.parseInt(hh);
min = Integer.parseInt(mm);
} catch (NumberFormatException ex){
Log.w(DEBUG_TAG,"Cannot convert passaggio into hour and minutes");
hour = UNKNOWN_TIME;
min = UNKNOWN_TIME;
realtime = false;
} finally {
this.hh = hour;
this.mm = min;
this.isInRealTime = realtime;
}
}
public Passaggio(int hour, int minutes, boolean realtime, Source sorgente){
this.hh = hour;
this.mm = minutes;
this.isInRealTime = realtime;
- this.passageSource = sorgente;
+ this.source = sorgente;
//Build the passaggio string
StringBuilder sb = new StringBuilder();
sb.append(hour).append(":").append(minutes);
if(realtime) sb.append("*");
this.passaggioGTT = sb.toString();
}
public static String createPassaggioGTT(String timeInput, boolean realtime){
final String time = timeInput.trim();
if(time.contains("*")){
if(realtime) return time;
else return time.substring(0,time.length()-1);
} else{
if(realtime) return time.concat("*");
else return time;
}
}
@Override
public int compareTo(@NonNull Passaggio other) {
if(this.hh == UNKNOWN_TIME || other.hh == UNKNOWN_TIME)
return 0;
else {
int diff = this.hh - other.hh;
// an attempt to correctly sort arrival times around midnight (e.g. 23.59 should come before 00.01)
if (diff > 12) { // untested
diff -= 24;
} else if (diff < -12) {
diff += 24;
}
diff *= 60;
diff += this.mm - other.mm;
// we should take into account if one is in real time and the other isn't, shouldn't we?
if (other.isInRealTime) {
++diff;
}
if (this.isInRealTime) {
--diff;
}
//TODO: separate Realtime and Non-Realtime, especially for the GTTJSONFetcher
return diff;
}
}
//
// @Override
// public String toString() {
// String resultString = (this.hh).concat(":").concat(this.mm);
// if(this.isInRealTime) {
// return resultString.concat("*");
// } else {
// return resultString;
// }
// }
public enum Source{
- FiveTAPI,GTTJSON,FiveTScraper
+ FiveTAPI,GTTJSON,FiveTScraper, UNKNOWN
}
}
\ No newline at end of file
diff --git a/src/it/reyboz/bustorino/fragments/ArrivalsFragment.java b/src/it/reyboz/bustorino/fragments/ArrivalsFragment.java
index 2a6142e..3b31b66 100644
--- a/src/it/reyboz/bustorino/fragments/ArrivalsFragment.java
+++ b/src/it/reyboz/bustorino/fragments/ArrivalsFragment.java
@@ -1,285 +1,342 @@
/*
BusTO - Fragments components
Copyright (C) 2018 Fabio Mazza
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
package it.reyboz.bustorino.fragments;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ImageButton;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import it.reyboz.bustorino.R;
import it.reyboz.bustorino.adapters.PalinaAdapter;
import it.reyboz.bustorino.backend.DBStatusManager;
import it.reyboz.bustorino.backend.FiveTNormalizer;
import it.reyboz.bustorino.backend.Palina;
+import it.reyboz.bustorino.backend.Passaggio;
import it.reyboz.bustorino.backend.Route;
import it.reyboz.bustorino.backend.Stop;
import it.reyboz.bustorino.middleware.AppDataProvider;
import it.reyboz.bustorino.middleware.NextGenDB;
import it.reyboz.bustorino.middleware.UserDB;
public class ArrivalsFragment extends ResultListFragment implements LoaderManager.LoaderCallbacks {
private final static String KEY_STOP_ID = "stopid";
private final static String KEY_STOP_NAME = "stopname";
private final static String DEBUG_TAG = "BUSTOArrivalsFragment";
private final static int loaderFavId = 2;
private final static int loaderStopId = 1;
static final String STOP_TITLE = "messageExtra";
private @Nullable String stopID,stopName;
private DBStatusManager prefs;
private DBStatusManager.OnDBUpdateStatusChangeListener listener;
private boolean justCreated = false;
private Palina lastUpdatedPalina = null;
+ private boolean needUpdateOnAttach = false;
//Views
protected ImageButton addToFavorites;
+ protected TextView timesSourceTextView;
public static ArrivalsFragment newInstance(String stopID){
Bundle args = new Bundle();
args.putString(KEY_STOP_ID,stopID);
ArrivalsFragment fragment = new ArrivalsFragment();
//parameter for ResultListFragment
args.putSerializable(LIST_TYPE,FragmentKind.ARRIVALS);
fragment.setArguments(args);
return fragment;
}
public static ArrivalsFragment newInstance(String stopID,String stopName){
ArrivalsFragment fragment = newInstance(stopID);
Bundle args = fragment.getArguments();
args.putString(KEY_STOP_NAME,stopName);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
stopID = getArguments().getString(KEY_STOP_ID);
//this might really be null
stopName = getArguments().getString(KEY_STOP_NAME);
final ArrivalsFragment arrivalsFragment = this;
listener = new DBStatusManager.OnDBUpdateStatusChangeListener() {
@Override
public void onDBStatusChanged(boolean updating) {
if(!updating){
getLoaderManager().restartLoader(loaderFavId,getArguments(),arrivalsFragment);
} else {
final LoaderManager lm = getLoaderManager();
lm.destroyLoader(loaderFavId);
lm.destroyLoader(loaderStopId);
}
}
@Override
public boolean defaultStatusValue() {
return true;
}
};
prefs = new DBStatusManager(getContext().getApplicationContext(),listener);
justCreated = true;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_arrivals, container, false);
messageTextView = (TextView) root.findViewById(R.id.messageTextView);
addToFavorites = (ImageButton) root.findViewById(R.id.addToFavorites);
resultsListView = (ListView) root.findViewById(R.id.resultsListView);
+ timesSourceTextView = (TextView) root.findViewById(R.id.timesSourceTextView);
//Button
addToFavorites.setClickable(true);
addToFavorites.setOnClickListener(v -> {
// add/remove the stop in the favorites
mListener.toggleLastStopToFavorites();
});
resultsListView.setOnItemClickListener((parent, view, position, id) -> {
String routeName;
Route r = (Route) parent.getItemAtPosition(position);
routeName = FiveTNormalizer.routeInternalToDisplay(r.getNameForDisplay());
if (routeName == null) {
routeName = r.getNameForDisplay();
}
if (r.destinazione == null || r.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, r.destinazione), Toast.LENGTH_SHORT).show();
}
});
String displayName = getArguments().getString(STOP_TITLE);
setTextViewMessage(String.format(
getString(R.string.passages), displayName));
String probablemessage = getArguments().getString(MESSAGE_TEXT_VIEW);
if (probablemessage != null) {
//Log.d("BusTO fragment " + this.getTag(), "We have a possible message here in the savedInstaceState: " + probablemessage);
messageTextView.setText(probablemessage);
messageTextView.setVisibility(View.VISIBLE);
}
return root;
}
@Override
public void onResume() {
super.onResume();
LoaderManager loaderManager = getLoaderManager();
if(stopID!=null){
//refresh the arrivals
if(!justCreated)
mListener.createFragmentForStop(stopID);
else justCreated = false;
//start the loader
if(prefs.isDBUpdating(true)){
prefs.registerListener();
} else {
loaderManager.restartLoader(loaderFavId, getArguments(), this);
}
updateMessage();
}
}
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (needUpdateOnAttach){
+ updateFragmentData(null);
+ }
+ }
+
@Nullable
public String getStopID() {
return stopID;
}
+ /**
+ * Update the UI with the new data
+ * @param p the full Palina
+ */
+ public void updateFragmentData(@Nullable Palina p){
+ if (p!=null)
+ lastUpdatedPalina = p;
+
+ if (!isAdded()){
+ //defer update at next show
+ if (p==null)
+ Log.w(DEBUG_TAG, "Asked to update the data, but we're not attached and the data is null");
+ else needUpdateOnAttach = true;
+ } else {
- public void updateFragmentData(Palina p, PalinaAdapter adapter){
+ final PalinaAdapter adapter = new PalinaAdapter(getContext(), lastUpdatedPalina);
+ showArrivalsSources(lastUpdatedPalina);
+ super.resetListAdapter(adapter);
+ }
+ }
- super.resetListAdapter(adapter);
+ /**
+ * Set the message of the arrival times source
+ * @param p Palina with the arrival times
+ */
+ protected void showArrivalsSources(Palina p){
+ final Passaggio.Source source = p.getPassaggiSourceIfAny();
+
+ String source_txt;
+ switch (source){
+ case GTTJSON:
+ source_txt = getString(R.string.gttjsonfetcher);
+ break;
+ case FiveTAPI:
+ source_txt = getString(R.string.fivetapifetcher);
+ break;
+ case FiveTScraper:
+ source_txt = getString(R.string.fivetscraper);
+ break;
+ case UNKNOWN:
+ //Don't show the view
+ timesSourceTextView.setVisibility(View.GONE);
+ return;
+ default:
+ throw new IllegalStateException("Unexpected value: " + source);
+ }
+ final String base_message = getString(R.string.times_source_fmt, source_txt);
+ timesSourceTextView.setVisibility(View.VISIBLE);
+ timesSourceTextView.setText(base_message);
}
@Override
public void setNewListAdapter(ListAdapter adapter) {
throw new UnsupportedOperationException();
}
/**
* Update the message in the fragment
*
* It may eventually change the "Add to Favorite" icon
*/
private void updateMessage(){
String message = null;
if (stopName != null && stopID != null && stopName.length() > 0) {
message = (stopID.concat(" - ").concat(stopName));
} else if(stopID!=null) {
message = stopID;
} else {
Log.e("ArrivalsFragm"+getTag(),"NO ID FOR THIS FRAGMENT - something went horribly wrong");
}
if(message!=null) {
setTextViewMessage(getString(R.string.passages,message));
}
// whatever is the case, update the star icon
mListener.updateStarIconFromLastBusStop();
}
@Override
public Loader onCreateLoader(int id, Bundle args) {
if(args.getString(KEY_STOP_ID)==null) return null;
final String stopID = args.getString(KEY_STOP_ID);
final Uri.Builder builder = AppDataProvider.getUriBuilderToComplete();
CursorLoader cl;
switch (id){
case loaderFavId:
builder.appendPath("favorites").appendPath(stopID);
cl = new CursorLoader(getContext(),builder.build(),UserDB.getFavoritesColumnNamesAsArray,null,null,null);
break;
case loaderStopId:
builder.appendPath("stop").appendPath(stopID);
cl = new CursorLoader(getContext(),builder.build(),new String[]{NextGenDB.Contract.StopsTable.COL_NAME},
null,null,null);
break;
default:
return null;
}
cl.setUpdateThrottle(500);
return cl;
}
@Override
public void onLoadFinished(Loader loader, Cursor data) {
switch (loader.getId()){
case loaderFavId:
final int colUserName = data.getColumnIndex(UserDB.getFavoritesColumnNamesAsArray[1]);
if(data.getCount()>0){
data.moveToFirst();
final String probableName = data.getString(colUserName);
if(probableName!=null && !probableName.isEmpty()){
stopName = probableName;
updateMessage();
}
}
if(stopName == null){
//stop is not inside the favorites and wasn't provided
Log.d("ArrivalsFragment"+getTag(),"Stop wasn't in the favorites and has no name, looking in the DB");
getLoaderManager().restartLoader(loaderStopId,getArguments(),this);
}
break;
case loaderStopId:
if(data.getCount()>0){
data.moveToFirst();
stopName = data.getString(data.getColumnIndex(
NextGenDB.Contract.StopsTable.COL_NAME
));
updateMessage();
} else {
Log.w("ArrivalsFragment"+getTag(),"Stop is not inside the database... CLOISTER BELL");
}
}
}
@Override
public void onPause() {
if(listener!=null)
prefs.unregisterListener();
super.onPause();
}
@Override
public void onLoaderReset(Loader loader) {
//NOTHING TO DO
}
}
diff --git a/src/it/reyboz/bustorino/fragments/FragmentHelper.java b/src/it/reyboz/bustorino/fragments/FragmentHelper.java
index 105fe04..5dfc492 100644
--- a/src/it/reyboz/bustorino/fragments/FragmentHelper.java
+++ b/src/it/reyboz/bustorino/fragments/FragmentHelper.java
@@ -1,232 +1,232 @@
/*
BusTO (fragments)
Copyright (C) 2018 Fabio Mazza
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
package it.reyboz.bustorino.fragments;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.sqlite.SQLiteException;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.Log;
import it.reyboz.bustorino.R;
import it.reyboz.bustorino.adapters.PalinaAdapter;
import it.reyboz.bustorino.backend.Fetcher;
import it.reyboz.bustorino.backend.Palina;
import it.reyboz.bustorino.backend.Stop;
import it.reyboz.bustorino.middleware.*;
import java.lang.ref.WeakReference;
import java.util.List;
/**
* Helper class to manage the fragments and their needs
*/
public class FragmentHelper {
GeneralActivity act;
private Stop lastSuccessfullySearchedBusStop;
//support for multiple frames
private int primaryFrameLayout,secondaryFrameLayout, swipeRefID;
public static final int NO_FRAME = -3;
private WeakReference lastTaskRef;
private NextGenDB newDBHelper;
private boolean shouldHaltAllActivities=false;
public FragmentHelper(GeneralActivity act, int swipeRefID, int mainFrame) {
this(act,swipeRefID,mainFrame,NO_FRAME);
}
public FragmentHelper(GeneralActivity act, int swipeRefID, int primaryFrameLayout, int secondaryFrameLayout) {
this.act = act;
this.swipeRefID = swipeRefID;
this.primaryFrameLayout = primaryFrameLayout;
this.secondaryFrameLayout = secondaryFrameLayout;
newDBHelper = new NextGenDB(act.getApplicationContext());
}
/**
* Get the last successfully searched bus stop or NULL
*
* @return the stop
*/
public Stop getLastSuccessfullySearchedBusStop() {
return lastSuccessfullySearchedBusStop;
}
public void setLastSuccessfullySearchedBusStop(Stop stop) {
this.lastSuccessfullySearchedBusStop = stop;
}
public void setLastTaskRef(WeakReference lastTaskRef) {
this.lastTaskRef = lastTaskRef;
}
/**
* 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 sameFragment;
ArrivalsFragment arrivalsFragment;
if(act==null || shouldHaltAllActivities) {
//SOMETHING WENT VERY WRONG
return;
}
FragmentManager fm = act.getSupportFragmentManager();
if(fm.findFragmentById(R.id.resultFrame) instanceof ArrivalsFragment) {
arrivalsFragment = (ArrivalsFragment) fm.findFragmentById(R.id.resultFrame);
sameFragment = arrivalsFragment.isFragmentForTheSameStop(p);
} else
sameFragment = false;
setLastSuccessfullySearchedBusStop(p);
if(!sameFragment) {
//set the String to be displayed on the fragment
String displayName = p.getStopDisplayName();
String displayStuff;
if (displayName != null && displayName.length() > 0) {
arrivalsFragment = ArrivalsFragment.newInstance(p.ID,displayName);
} else {
arrivalsFragment = ArrivalsFragment.newInstance(p.ID);
}
attachFragmentToContainer(fm,arrivalsFragment,true,ResultListFragment.getFragmentTag(p));
} else {
Log.d("BusTO", "Same bus stop, accessing existing fragment");
arrivalsFragment = (ArrivalsFragment) fm.findFragmentById(R.id.resultFrame);
}
final PalinaAdapter adapter = new PalinaAdapter(act.getApplicationContext(), p);
// DO NOT CALL `setListAdapter` ever on arrivals fragment
- arrivalsFragment.updateFragmentData(p, adapter);
+ arrivalsFragment.updateFragmentData(p);
act.hideKeyboard();
toggleSpinner(false);
}
/**
* Called when you need to display the results of a search of stops
* @param resultList the List of stops found
* @param query String queried
*/
public void createFragmentFor(List resultList,String query){
act.hideKeyboard();
StopListFragment listfragment = StopListFragment.newInstance(query);
attachFragmentToContainer(act.getSupportFragmentManager(),listfragment,false,"search_"+query);
listfragment.setStopList(resultList);
toggleSpinner(false);
}
/**
* Wrapper for toggleSpinner in Activity
* @param on new status of spinner system
*/
public void toggleSpinner(boolean on){
if (act instanceof FragmentListener)
((FragmentListener) act).toggleSpinner(on);
else {
SwipeRefreshLayout srl = (SwipeRefreshLayout) act.findViewById(swipeRefID);
srl.setRefreshing(false);
}
}
/**
* Attach a new fragment to a cointainer
* @param fm the FragmentManager
* @param fragment the Fragment
* @param sendToSecondaryFrame needs to be displayed in secondary frame or not
* @param tag tag for the fragment
*/
public void attachFragmentToContainer(FragmentManager fm,Fragment fragment, boolean sendToSecondaryFrame, String tag){
FragmentTransaction ft = fm.beginTransaction();
if(sendToSecondaryFrame && secondaryFrameLayout!=NO_FRAME)
ft.replace(secondaryFrameLayout,fragment,tag);
else ft.replace(primaryFrameLayout,fragment,tag);
ft.addToBackStack("state_"+tag);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE);
ft.commit();
//fm.executePendingTransactions();
}
synchronized public int insertBatchDataInNextGenDB(ContentValues[] valuesArr,String tableName){
if(newDBHelper !=null)
try {
return newDBHelper.insertBatchContent(valuesArr, tableName);
} catch (SQLiteException exc){
Log.w("DB Batch inserting: ","ERROR Inserting the data batch: ",exc.fillInStackTrace());
return -2;
}
else return -1;
}
synchronized public ContentResolver getContentResolver(){
return act.getContentResolver();
}
public void setBlockAllActivities(boolean shouldI) {
this.shouldHaltAllActivities = shouldI;
}
public void stopLastRequestIfNeeded(){
if(lastTaskRef == null) return;
AsyncDataDownload task = lastTaskRef.get();
if(task!=null){
task.cancel(true);
}
}
/**
* Wrapper to show the errors/status that happened
* @param res result from Fetcher
*/
public void showErrorMessage(Fetcher.result res){
//TODO: implement a common set of errors for all fragments
switch (res){
case OK:
break;
case CLIENT_OFFLINE:
act.showMessage(R.string.network_error);
break;
case SERVER_ERROR:
if (act.isConnected()) {
act.showMessage(R.string.parsing_error);
} else {
act.showMessage(R.string.network_error);
}
case PARSER_ERROR:
default:
act.showMessage(R.string.internal_error);
break;
case QUERY_TOO_SHORT:
act.showMessage(R.string.query_too_short);
break;
case EMPTY_RESULT_SET:
act.showMessage(R.string.no_bus_stop_have_this_name);
break;
}
}
}