diff --git a/src/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt b/src/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt
index ecfd6b0..ab97832 100644
--- a/src/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt
+++ b/src/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt
@@ -1,430 +1,430 @@
/*
BusTO - Backend components
Copyright (C) 2021 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.backend.mato
import android.content.Context
import android.util.Log
import com.android.volley.DefaultRetryPolicy
import com.android.volley.toolbox.RequestFuture
import it.reyboz.bustorino.BuildConfig
import it.reyboz.bustorino.backend.*
import it.reyboz.bustorino.data.gtfs.GtfsAgency
import it.reyboz.bustorino.data.gtfs.GtfsFeed
import it.reyboz.bustorino.data.gtfs.GtfsRoute
import it.reyboz.bustorino.data.gtfs.MatoPattern
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.util.*
import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
import java.util.concurrent.atomic.AtomicReference
import kotlin.collections.ArrayList
open class MatoAPIFetcher(val minNumPassaggi: Int) : ArrivalsFetcher {
var appContext: Context? = null
set(value) {
field = value!!.applicationContext
}
constructor(): this(2)
override fun ReadArrivalTimesAll(stopID: String?, res: AtomicReference?): Palina {
stopID!!
val now = Calendar.getInstance().time
var numMinutes = 0
var palina = Palina(stopID)
var numPassaggi = 0
var trials = 0
val numDepartures = 4
while (numPassaggi < minNumPassaggi && trials < 2) {
//numDepartures+=2
numMinutes += 20
val future = RequestFuture.newFuture()
val request = MapiArrivalRequest(stopID, now, numMinutes * 60, numDepartures, res, future, future)
if (appContext == null || res == null) {
Log.e("BusTO:MatoAPIFetcher", "ERROR: Given null context or null result ref")
return Palina(stopID)
}
val requestQueue = NetworkVolleyManager.getInstance(appContext).requestQueue
request.setTag(getVolleyReqTag(MatoQueries.QueryType.ARRIVALS))
requestQueue.add(request)
try {
val palinaResult = future.get(5, TimeUnit.SECONDS)
if (palinaResult!=null) {
- if (BuildConfig.DEBUG)
+ /*if (BuildConfig.DEBUG)
for (r in palinaResult.queryAllRoutes()){
Log.d(DEBUG_TAG, "route " + r.gtfsId + " has " + r.passaggi.size + " passaggi: "+ r.passaggiToString)
- }
+ }*/
palina = palinaResult
numPassaggi = palina.minNumberOfPassages
} else{
Log.d(DEBUG_TAG, "Result palina is null")
}
} catch (e: InterruptedException) {
e.printStackTrace()
res.set(Fetcher.Result.PARSER_ERROR)
} catch (e: ExecutionException) {
e.printStackTrace()
if (res.get() == Fetcher.Result.OK)
res.set(Fetcher.Result.SERVER_ERROR)
} catch (e: TimeoutException) {
res.set(Fetcher.Result.CONNECTION_ERROR)
e.printStackTrace()
}
trials++
}
return palina
}
override fun getSourceForFetcher(): Passaggio.Source {
return Passaggio.Source.MatoAPI
}
companion object{
const val VOLLEY_TAG = "MatoAPIFetcher"
const val DEBUG_TAG = "BusTO:MatoAPIFetcher"
val REQ_PARAMETERS = mapOf(
"Content-Type" to "application/json; charset=utf-8",
"DNT" to "1",
"Host" to "mapi.5t.torino.it")
private val longRetryPolicy = DefaultRetryPolicy(10000,5,DefaultRetryPolicy.DEFAULT_BACKOFF_MULT)
fun getVolleyReqTag(type: MatoQueries.QueryType): String{
return when (type){
MatoQueries.QueryType.ALL_STOPS -> VOLLEY_TAG +"_AllStops"
MatoQueries.QueryType.ARRIVALS -> VOLLEY_TAG+"_Arrivals"
MatoQueries.QueryType.FEEDS -> VOLLEY_TAG +"_Feeds"
MatoQueries.QueryType.ROUTES -> VOLLEY_TAG +"_AllRoutes"
MatoQueries.QueryType.PATTERNS_FOR_ROUTES -> VOLLEY_TAG + "_PatternsForRoute"
}
}
/**
* Get stops from the MatoAPI, set [res] accordingly
*/
fun getAllStopsGTT(context: Context, res: AtomicReference?): List{
val requestQueue = NetworkVolleyManager.getInstance(context).requestQueue
val future = RequestFuture.newFuture>()
val request = VolleyAllStopsRequest(future, future)
request.tag = getVolleyReqTag(MatoQueries.QueryType.ALL_STOPS)
request.retryPolicy = longRetryPolicy
requestQueue.add(request)
var palinaList:List = mutableListOf()
try {
palinaList = future.get(120, TimeUnit.SECONDS)
res?.set(Fetcher.Result.OK)
}catch (e: InterruptedException) {
e.printStackTrace()
res?.set(Fetcher.Result.PARSER_ERROR)
} catch (e: ExecutionException) {
e.printStackTrace()
res?.set(Fetcher.Result.SERVER_ERROR)
} catch (e: TimeoutException) {
res?.set(Fetcher.Result.CONNECTION_ERROR)
e.printStackTrace()
}
return palinaList
}
/*
fun makeRequest(type: QueryType?, variables: JSONObject) : String{
type.let {
val requestData = JSONObject()
when (it){
QueryType.ARRIVALS ->{
requestData.put("operationName","AllStopsDirect")
requestData.put("variables", variables)
requestData.put("query", MatoQueries.QUERY_ARRIVALS)
}
else -> {
//TODO all other cases
}
}
//todo make the request...
//https://pablobaxter.github.io/volley-docs/com/android/volley/toolbox/RequestFuture.html
//https://stackoverflow.com/questions/16904741/can-i-do-a-synchronous-request-with-volley
}
return ""
}
*/
fun parseStopJSON(jsonStop: JSONObject): Palina{
val latitude = jsonStop.getDouble("lat")
val longitude = jsonStop.getDouble("lon")
val palina = Palina(
jsonStop.getString("code"),
jsonStop.getString("name"),
null, null, latitude, longitude,
jsonStop.getString("gtfsId")
)
val routesStoppingJSON = jsonStop.getJSONArray("routes")
val baseRoutes = mutableListOf()
// get all the possible routes
for (i in 0 until routesStoppingJSON.length()){
val routeBaseInfo = routesStoppingJSON.getJSONObject(i)
val r = Route(routeBaseInfo.getString("shortName"), Route.Type.UNKNOWN,"")
r.setGtfsId(routeBaseInfo.getString("gtfsId").trim())
baseRoutes.add(r)
}
if (jsonStop.has("desc")){
palina.location = jsonStop.getString("desc")
}
//there is also "zoneId" which is the zone of the stop (0-> city, etc)
if(jsonStop.has("stoptimesForPatterns")) {
val routesStopTimes = jsonStop.getJSONArray("stoptimesForPatterns")
for (i in 0 until routesStopTimes.length()) {
val patternJSON = routesStopTimes.getJSONObject(i)
val mRoute = parseRouteStoptimesJSON(patternJSON)
//Log.d("BusTO-MapiFetcher")
//val directionId = patternJSON.getJSONObject("pattern").getInt("directionId")
//TODO: use directionId
palina.addRoute(mRoute)
for (r in baseRoutes) {
if (mRoute.gtfsId != null && r.gtfsId.equals(mRoute.gtfsId)) {
baseRoutes.remove(r)
break
}
}
}
}
for (noArrivalRoute in baseRoutes){
palina.addRoute(noArrivalRoute)
}
//val gtfsRoutes = mutableListOf<>()
return palina
}
fun parseRouteStoptimesJSON(jsonPatternWithStops: JSONObject): Route{
val patternJSON = jsonPatternWithStops.getJSONObject("pattern")
val routeJSON = patternJSON.getJSONObject("route")
val passaggiJSON = jsonPatternWithStops.getJSONArray("stoptimes")
val gtfsId = routeJSON.getString("gtfsId").trim()
val passages = mutableListOf()
for( i in 0 until passaggiJSON.length()){
val stoptime = passaggiJSON.getJSONObject(i)
val scheduledTime = stoptime.getInt("scheduledArrival")
val realtimeTime = stoptime.getInt("realtimeArrival")
val realtime = stoptime.getBoolean("realtime")
passages.add(
Passaggio(realtimeTime,realtime, realtimeTime-scheduledTime,
Passaggio.Source.MatoAPI)
)
}
var routeType = Route.Type.UNKNOWN
if (gtfsId[gtfsId.length-1] == 'E')
routeType = Route.Type.LONG_DISTANCE_BUS
else when( routeJSON.getString("mode").trim()){
"BUS" -> routeType = Route.Type.BUS
"TRAM" -> routeType = Route.Type.TRAM
}
val route = Route(
routeJSON.getString("shortName"),
patternJSON.getString("headsign"),
routeType,
passages,
)
route.setGtfsId(gtfsId)
return route
}
fun makeRequestParameters(requestName:String, variables: JSONObject, query: String): JSONObject{
val data = JSONObject()
data.put("operationName", requestName)
data.put("variables", variables)
data.put("query", query)
return data
}
fun getFeedsAndAgencies(context: Context, res: AtomicReference?):
Pair, ArrayList> {
val requestQueue = NetworkVolleyManager.getInstance(context).requestQueue
val future = RequestFuture.newFuture()
val request = MatoVolleyJSONRequest(MatoQueries.QueryType.FEEDS, JSONObject(), future, future)
request.setRetryPolicy(longRetryPolicy)
request.tag = getVolleyReqTag(MatoQueries.QueryType.FEEDS)
requestQueue.add(request)
val feeds = ArrayList()
val agencies = ArrayList()
var outObj = ""
try {
val resObj = future.get(120,TimeUnit.SECONDS)
outObj = resObj.toString(1)
val feedsJSON = resObj.getJSONArray("feeds")
for (i in 0 until feedsJSON.length()){
val resTup = ResponseParsing.parseFeedJSON(feedsJSON.getJSONObject(i))
feeds.add(resTup.first)
agencies.addAll(resTup.second)
}
} catch (e: InterruptedException) {
e.printStackTrace()
res?.set(Fetcher.Result.PARSER_ERROR)
} catch (e: ExecutionException) {
e.printStackTrace()
res?.set(Fetcher.Result.SERVER_ERROR)
} catch (e: TimeoutException) {
res?.set(Fetcher.Result.CONNECTION_ERROR)
e.printStackTrace()
} catch (e: JSONException){
e.printStackTrace()
res?.set(Fetcher.Result.PARSER_ERROR)
Log.e(DEBUG_TAG, "Downloading feeds: $outObj")
}
return Pair(feeds,agencies)
}
fun getRoutes(context: Context, res: AtomicReference?):
ArrayList{
val requestQueue = NetworkVolleyManager.getInstance(context).requestQueue
val future = RequestFuture.newFuture()
val params = JSONObject()
params.put("feeds","gtt")
val request = MatoVolleyJSONRequest(MatoQueries.QueryType.ROUTES, params, future, future)
request.tag = getVolleyReqTag(MatoQueries.QueryType.ROUTES)
request.retryPolicy = longRetryPolicy
requestQueue.add(request)
val routes = ArrayList()
var outObj = ""
try {
val resObj = future.get(120,TimeUnit.SECONDS)
outObj = resObj.toString(1)
val routesJSON = resObj.getJSONArray("routes")
for (i in 0 until routesJSON.length()){
val route = ResponseParsing.parseRouteJSON(routesJSON.getJSONObject(i))
routes.add(route)
}
} catch (e: InterruptedException) {
e.printStackTrace()
res?.set(Fetcher.Result.PARSER_ERROR)
} catch (e: ExecutionException) {
e.printStackTrace()
res?.set(Fetcher.Result.SERVER_ERROR)
} catch (e: TimeoutException) {
res?.set(Fetcher.Result.CONNECTION_ERROR)
e.printStackTrace()
} catch (e: JSONException){
e.printStackTrace()
res?.set(Fetcher.Result.PARSER_ERROR)
Log.e(DEBUG_TAG, "Downloading feeds: $outObj")
}
return routes
}
fun getPatternsWithStops(context: Context, routesGTFSIds: ArrayList, res: AtomicReference?): ArrayList{
val requestQueue = NetworkVolleyManager.getInstance(context).requestQueue
val future = RequestFuture.newFuture()
val params = JSONObject()
for (r in routesGTFSIds){
if(r.isEmpty()) routesGTFSIds.remove(r)
}
val routes = JSONArray(routesGTFSIds)
params.put("routes",routes)
val request = MatoVolleyJSONRequest(MatoQueries.QueryType.PATTERNS_FOR_ROUTES, params, future, future)
request.retryPolicy = longRetryPolicy
request.tag = getVolleyReqTag(MatoQueries.QueryType.PATTERNS_FOR_ROUTES)
requestQueue.add(request)
val patterns = ArrayList()
//var outObj = ""
try {
val resObj = future.get(60,TimeUnit.SECONDS)
//outObj = resObj.toString(1)
val routesJSON = resObj.getJSONArray("routes")
for (i in 0 until routesJSON.length()){
val patternList = ResponseParsing.parseRoutePatternsStopsJSON(routesJSON.getJSONObject(i))
patterns.addAll(patternList)
}
} catch (e: InterruptedException) {
e.printStackTrace()
res?.set(Fetcher.Result.PARSER_ERROR)
} catch (e: ExecutionException) {
e.printStackTrace()
res?.set(Fetcher.Result.SERVER_ERROR)
} catch (e: TimeoutException) {
res?.set(Fetcher.Result.CONNECTION_ERROR)
e.printStackTrace()
} catch (e: JSONException){
e.printStackTrace()
res?.set(Fetcher.Result.PARSER_ERROR)
//Log.e(DEBUG_TAG, "Downloading feeds: $outObj")
}
/*
var numRequests = 0
for(routeName in routesGTFSIds){
if (!routeName.isEmpty()) numRequests++
}
val countDownForRequests = CountDownLatch(numRequests)
val lockSave = ReentrantLock()
//val countDownFor
for (routeName in routesGTFSIds){
val pars = JSONObject()
pars.put("")
}
val goodResponseListener = Response.Listener { }
val errorResponseListener = Response.ErrorListener { }
*/
return patterns
}
}
}
\ No newline at end of file
diff --git a/src/it/reyboz/bustorino/backend/utils.java b/src/it/reyboz/bustorino/backend/utils.java
index 5996b14..9283f44 100644
--- a/src/it/reyboz/bustorino/backend/utils.java
+++ b/src/it/reyboz/bustorino/backend/utils.java
@@ -1,320 +1,342 @@
/*
BusTO (backend components)
Copyright (C) 2019 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.backend;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceManager;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import it.reyboz.bustorino.backend.mato.MatoAPIFetcher;
import it.reyboz.bustorino.fragments.SettingsFragment;
public abstract class utils {
private static final double EarthRadius = 6371e3;
public static Double measuredistanceBetween(double lat1,double long1,double lat2,double long2){
final double phi1 = Math.toRadians(lat1);
final double phi2 = Math.toRadians(lat2);
final double deltaPhi = Math.toRadians(lat2-lat1);
final double deltaTheta = Math.toRadians(long2-long1);
final double a = Math.sin(deltaPhi/2)*Math.sin(deltaPhi/2)+
Math.cos(phi1)*Math.cos(phi2)*Math.sin(deltaTheta/2)*Math.sin(deltaTheta/2);
final double c = 2*Math.atan2(Math.sqrt(a),Math.sqrt(1-a));
return Math.abs(EarthRadius*c);
}
public static Double angleRawDifferenceFromMeters(double distanceInMeters){
return Math.toDegrees(distanceInMeters/EarthRadius);
}
/*
public static int convertDipToPixels(Context con,float dips)
{
return (int) (dips * con.getResources().getDisplayMetrics().density + 0.5f);
}
*/
public static float convertDipToPixels(Context con, float dp){
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,con.getResources().getDisplayMetrics());
}
/*
public static int calculateNumColumnsFromSize(View containerView, int pixelsize){
int width = containerView.getWidth();
float ncols = ((float)width)/pixelsize;
return (int) Math.floor(ncols);
}
*/
/**
* Check if there is an internet connection
* @param con context object to get the system service
* @return true if we are
*/
public static boolean isConnected(Context con) {
ConnectivityManager connMgr = (ConnectivityManager) con.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
return networkInfo != null && networkInfo.isConnected();
}
///////////////////// INTENT HELPER ////////////////////////////////////////////////////////////
/**
* Try to extract the bus stop ID from a URi
*
* @param uri The URL
* @return bus stop ID or null
*/
public static String getBusStopIDFromUri(Uri uri) {
String busStopID;
// everithing catches fire when passing null to a switch.
String host = uri.getHost();
if (host == null) {
Log.e("ActivityMain", "Not an URL: " + uri);
return null;
}
switch (host) {
case "m.gtt.to.it":
// http://m.gtt.to.it/m/it/arrivi.jsp?n=1254
busStopID = uri.getQueryParameter("n");
if (busStopID == null) {
Log.e("ActivityMain", "Expected ?n from: " + uri);
}
break;
case "www.gtt.to.it":
case "gtt.to.it":
// http://www.gtt.to.it/cms/percorari/arrivi?palina=1254
busStopID = uri.getQueryParameter("palina");
if (busStopID == null) {
Log.e("ActivityMain", "Expected ?palina from: " + uri);
}
break;
default:
Log.e("ActivityMain", "Unexpected intent URL: " + uri);
busStopID = null;
}
return busStopID;
}
final static Pattern ROMAN_PATTERN = Pattern.compile(
"^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$");
private static boolean isRomanNumber(String str){
if(str.isEmpty()) return false;
final Matcher matcher = ROMAN_PATTERN.matcher(str);
return matcher.find();
}
public static String toTitleCase(String givenString, boolean lowercaseRest) {
String[] arr = givenString.trim().split(" ");
StringBuilder sb = new StringBuilder();
//Log.d("BusTO chars", "String parsing: "+givenString+" in array: "+ Arrays.toString(arr));
for (String s : arr) {
if (s.length() > 0) {
String[] allsubs = s.split("\\.");
boolean addPoint = s.contains(".");
/*if (s.contains(".lli")|| s.contains(".LLI")) //Fratelli
{
DOESN'T ALWAYS WORK
addPoint = false;
allsubs = new String[]{s};
}*/
boolean first = true;
for (String subs : allsubs) {
if(first) first=false;
else {
if (addPoint) sb.append(".");
sb.append(" ");
}
if(isRomanNumber(subs)){
//add and skip the rest
sb.append(subs);
continue;
}
//SPLIT ON ', check if contains "D'"
if(subs.toLowerCase(Locale.ROOT).startsWith("d'")){
sb.append("D'");
subs = subs.substring(2);
}
int index = 0;
char c = subs.charAt(index);
if(subs.length() > 1 && c=='('){
sb.append(c);
index += 1;
c = subs.charAt(index);
}
sb.append(Character.toUpperCase(c));
if (lowercaseRest)
sb.append(subs.substring(index+1).toLowerCase(Locale.ROOT));
else
sb.append(subs.substring(index+1));
}
if(addPoint && allsubs.length == 1) sb.append('.');
sb.append(" ");
/*sb.append(Character.toUpperCase(arr[i].charAt(0)));
if (lowercaseRest)
sb.append(arr[i].substring(1).toLowerCase(Locale.ROOT));
else
sb.append(arr[i].substring(1));
sb.append(" ");
*/
} else sb.append(s);
}
return sb.toString().trim();
}
/**
* Open an URL in the default browser.
*
* @param url URL
*/
public static void openIceweasel(String url, Context context) {
Intent browserIntent1 = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
if (browserIntent1.resolveActivity(context.getPackageManager()) != null) {
//check we have an activity ready to receive intents (otherwise, there will be a crash)
context.startActivity(browserIntent1);
} else{
Log.e("BusTO","openIceweasel can't find a browser");
}
}
/**
* Get the default list of fetchers for arrival times
* @return array of ArrivalsFetchers to use
*/
public static ArrivalsFetcher[] getDefaultArrivalsFetchers(){
return new ArrivalsFetcher[]{ new MatoAPIFetcher(),
new GTTJSONFetcher(), new FiveTScraperFetcher()};
}
/**
* Get the default list of fetchers for arrival times
* @return array of ArrivalsFetchers to use
*/
public static List getDefaultArrivalsFetchers(Context context){
SharedPreferences defSharPref = PreferenceManager.getDefaultSharedPreferences(context);
- final Set setSelected = defSharPref.getStringSet(SettingsFragment.KEY_ARRIVALS_FETCHERS_USE, new HashSet<>());
+ final Set setSelected = new HashSet<>();
+ setSelected.addAll(defSharPref.getStringSet(SettingsFragment.KEY_ARRIVALS_FETCHERS_USE,
+ new HashSet<>()));
if (setSelected.isEmpty()) {
return Arrays.asList(new MatoAPIFetcher(),
new GTTJSONFetcher(), new FiveTScraperFetcher());
}else{
ArrayList outFetchers = new ArrayList<>(4);
- for(String s: setSelected){
+ /*for(String s: setSelected){
switch (s){
case "matofetcher":
outFetchers.add(new MatoAPIFetcher());
break;
case "fivetapifetcher":
outFetchers.add(new FiveTAPIFetcher());
break;
case "gttjsonfetcher":
outFetchers.add(new GTTJSONFetcher());
break;
case "fivetscraper":
outFetchers.add(new FiveTScraperFetcher());
break;
default:
throw new IllegalArgumentException();
}
+ }*/
+ if (setSelected.contains("matofetcher")) {
+ outFetchers.add(new MatoAPIFetcher());
+ setSelected.remove("matofetcher");
}
- /*
- if (setSelected.contains("matofetcher"))
- outFetchers.add(new MatoAPIFetcher());
- if (setSelected.contains("fivetapifetcher"))
+ if (setSelected.contains("fivetapifetcher")) {
outFetchers.add(new FiveTAPIFetcher());
- if (setSelected.contains("gttjsonfetcher"))
+ setSelected.remove("fivetapifetcher");
+ }
+ if (setSelected.contains("gttjsonfetcher")){
outFetchers.add(new GTTJSONFetcher());
- if (setSelected.contains("fivetscraper"))
+ setSelected.remove("gttjsonfetcher");
+ }
+ if (setSelected.contains("fivetscraper")) {
outFetchers.add(new FiveTScraperFetcher());
- */
+ setSelected.remove("fivetscraper");
+ }
+ if(!setSelected.isEmpty()){
+ Log.e("BusTO-Utils","Getting some fetchers values which are not contemplated");
+ }
+
return outFetchers;
}
}
/**
* Print the first i lines of the the trace of an exception
* https://stackoverflow.com/questions/21706722/fetch-only-first-n-lines-of-a-stack-trace
*/
/*
public static String traceCaller(Exception ex, int i) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
StringBuilder sb = new StringBuilder();
ex.printStackTrace(pw);
String ss = sw.toString();
String[] splitted = ss.split("\n");
sb.append("\n");
if(splitted.length > 2 + i) {
for(int x = 2; x < i+2; x++) {
sb.append(splitted[x].trim());
sb.append("\n");
}
return sb.toString();
}
return "Trace too Short.";
}
*/
public static String joinList(@Nullable List dat, String separator){
StringBuilder sb = new StringBuilder();
if(dat==null || dat.size()==0)
return "";
else if(dat.size()==1)
return dat.get(0);
sb.append(dat.get(0));
for (int i=1; i Set convertArrayToSet(T[] array)
{
// Create an empty Set
Set set = new HashSet<>();
// Add each element into the set
set.addAll(Arrays.asList(array));
// Return the converted Set
return set;
}
+
+ public static String giveClassesForArray(T[] array){
+ StringBuilder sb = new StringBuilder();
+ for (T f: array){
+ sb.append("");
+ sb.append(f.getClass().getSimpleName());
+ sb.append("; ");
+ }
+ return sb.toString();
+ }
}
diff --git a/src/it/reyboz/bustorino/fragments/ArrivalsFragment.java b/src/it/reyboz/bustorino/fragments/ArrivalsFragment.java
index 47a203f..86b83ed 100644
--- a/src/it/reyboz/bustorino/fragments/ArrivalsFragment.java
+++ b/src/it/reyboz/bustorino/fragments/ArrivalsFragment.java
@@ -1,529 +1,532 @@
/*
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.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.annotation.NonNull;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.CursorLoader;
import androidx.loader.content.Loader;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import it.reyboz.bustorino.R;
import it.reyboz.bustorino.adapters.PalinaAdapter;
import it.reyboz.bustorino.backend.ArrivalsFetcher;
import it.reyboz.bustorino.backend.DBStatusManager;
import it.reyboz.bustorino.backend.Fetcher;
import it.reyboz.bustorino.backend.FiveTAPIFetcher;
import it.reyboz.bustorino.backend.FiveTNormalizer;
import it.reyboz.bustorino.backend.FiveTScraperFetcher;
import it.reyboz.bustorino.backend.GTTJSONFetcher;
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.backend.utils;
import it.reyboz.bustorino.data.AppDataProvider;
import it.reyboz.bustorino.data.NextGenDB;
import it.reyboz.bustorino.data.UserDB;
import it.reyboz.bustorino.middleware.AsyncStopFavoriteAction;
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_ALL = "BUSTOArrivalsFragment";
private String DEBUG_TAG = DEBUG_TAG_ALL;
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;
private boolean fetchersChangeRequestPending = false;
private boolean stopIsInFavorites = false;
//Views
protected ImageButton addToFavorites;
protected TextView timesSourceTextView;
private List fetchers = null; //new ArrayList<>(Arrays.asList(utils.getDefaultArrivalsFetchers()));
private boolean reloadOnResume = true;
public static ArrivalsFragment newInstance(String stopID){
return newInstance(stopID, null);
}
public static ArrivalsFragment newInstance(@NonNull String stopID, @Nullable String stopName){
ArrivalsFragment fragment = new ArrivalsFragment();
Bundle args = new Bundle();
args.putString(KEY_STOP_ID,stopID);
//parameter for ResultListFragmentrequestArrivalsForStopID
args.putSerializable(LIST_TYPE,FragmentKind.ARRIVALS);
if (stopName != null){
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);
DEBUG_TAG = DEBUG_TAG_ALL+" "+stopID;
//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 = root.findViewById(R.id.messageTextView);
addToFavorites = root.findViewById(R.id.addToFavorites);
resultsListView = root.findViewById(R.id.resultsListView);
timesSourceTextView = root.findViewById(R.id.timesSourceTextView);
timesSourceTextView.setOnLongClickListener(view -> {
if(!fetchersChangeRequestPending){
rotateFetchers();
//Show we are changing provider
timesSourceTextView.setText(R.string.arrival_source_changing);
mListener.requestArrivalsForStopID(stopID);
fetchersChangeRequestPending = true;
return true;
}
return false;
});
timesSourceTextView.setOnClickListener(view -> {
Toast.makeText(getContext(), R.string.change_arrivals_source_message, Toast.LENGTH_SHORT)
.show();
});
//Button
addToFavorites.setClickable(true);
addToFavorites.setOnClickListener(v -> {
// add/remove the stop in the favorites
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);
if(displayName!=null)
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();
Log.d(DEBUG_TAG, "OnResume, justCreated "+justCreated);
/*if(needUpdateOnAttach){
updateFragmentData(null);
needUpdateOnAttach=false;
}*/
if(stopID!=null){
//refresh the arrivals
if(!justCreated){
if (reloadOnResume)
mListener.requestArrivalsForStopID(stopID);
}
else justCreated = false;
//start the loader
if(prefs.isDBUpdating(true)){
prefs.registerListener();
} else {
Log.d(DEBUG_TAG, "Restarting loader for stop");
loaderManager.restartLoader(loaderFavId, getArguments(), this);
}
updateMessage();
}
}
@Override
public void onStart() {
super.onStart();
if (needUpdateOnAttach){
updateFragmentData(null);
needUpdateOnAttach = false;
}
}
@Override
public void onPause() {
if(listener!=null)
prefs.unregisterListener();
super.onPause();
LoaderManager loaderManager = getLoaderManager();
Log.d(DEBUG_TAG, "onPause, have running loaders: "+loaderManager.hasRunningLoaders());
loaderManager.destroyLoader(loaderFavId);
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
//get fetchers
fetchers = utils.getDefaultArrivalsFetchers(context);
}
@Nullable
public String getStopID() {
return stopID;
}
public boolean reloadsOnResume() {
return reloadOnResume;
}
public void setReloadOnResume(boolean reloadOnResume) {
this.reloadOnResume = reloadOnResume;
}
/**
* Give the fetchers
* @return the list of the fetchers
*/
public ArrayList getCurrentFetchers(){
return new ArrayList<>(this.fetchers);
}
public ArrivalsFetcher[] getCurrentFetchersAsArray(){
ArrivalsFetcher[] arr = new ArrivalsFetcher[fetchers.size()];
fetchers.toArray(arr);
return arr;
}
private void rotateFetchers(){
- Collections.rotate(fetchers, 1);
+ Log.d(DEBUG_TAG, "Rotating fetchers, before: "+fetchers);
+ Collections.rotate(fetchers, -1);
+ Log.d(DEBUG_TAG, "Rotating fetchers, afterwards: "+fetchers);
+
}
/**
* 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 {
final PalinaAdapter adapter = new PalinaAdapter(getContext(), lastUpdatedPalina);
showArrivalsSources(lastUpdatedPalina);
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();
if (source == null){
Log.e(DEBUG_TAG, "NULL SOURCE");
return;
}
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 MatoAPI:
source_txt = getString(R.string.source_mato);
break;
case UNDETERMINED:
//Don't show the view
source_txt = getString(R.string.undetermined_source);
break;
default:
throw new IllegalStateException("Unexpected value: " + source);
}
int count = 0;
if (source!= Passaggio.Source.UNDETERMINED)
while (source != fetchers.get(0).getSourceForFetcher() && count < 10){
//we need to update the fetcher that is requested
rotateFetchers();
count++;
}
if (count>10)
Log.w(DEBUG_TAG, "Tried to update the source fetcher but it didn't work");
final String base_message = getString(R.string.times_source_fmt, source_txt);
timesSourceTextView.setText(base_message);
if (p.getTotalNumberOfPassages() > 0) {
timesSourceTextView.setVisibility(View.VISIBLE);
} else {
timesSourceTextView.setVisibility(View.INVISIBLE);
}
fetchersChangeRequestPending = false;
}
@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
//updateStarIconFromLastBusStop();
}
@NonNull
@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){
// IT'S IN FAVORITES
data.moveToFirst();
final String probableName = data.getString(colUserName);
stopIsInFavorites = true;
stopName = probableName;
//update the message in the textview
updateMessage();
} else {
stopIsInFavorites =false;
}
updateStarIcon();
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();
int index = data.getColumnIndex(
NextGenDB.Contract.StopsTable.COL_NAME
);
if (index == -1){
Log.e(DEBUG_TAG, "Index is -1, column not present. App may explode now...");
}
stopName = data.getString(index);
updateMessage();
} else {
Log.w("ArrivalsFragment"+getTag(),"Stop is not inside the database... CLOISTER BELL");
}
}
}
@Override
public void onLoaderReset(Loader loader) {
//NOTHING TO DO
}
public void toggleLastStopToFavorites() {
Stop stop = lastUpdatedPalina;
if (stop != null) {
// toggle the status in background
new AsyncStopFavoriteAction(getContext().getApplicationContext(), AsyncStopFavoriteAction.Action.TOGGLE,
v->updateStarIconFromLastBusStop(v)).execute(stop);
} else {
// this case have no sense, but just immediately update the favorite icon
updateStarIconFromLastBusStop(true);
}
}
/**
* Update the star "Add to favorite" icon
*/
public void updateStarIconFromLastBusStop(Boolean toggleDone) {
if (stopIsInFavorites)
stopIsInFavorites = !toggleDone;
else stopIsInFavorites = toggleDone;
updateStarIcon();
// check if there is a last Stop
/*
if (stopID == null) {
addToFavorites.setVisibility(View.INVISIBLE);
} else {
// filled or outline?
if (isStopInFavorites(stopID)) {
addToFavorites.setImageResource(R.drawable.ic_star_filled);
} else {
addToFavorites.setImageResource(R.drawable.ic_star_outline);
}
addToFavorites.setVisibility(View.VISIBLE);
}
*/
}
/**
* Update the star icon according to `stopIsInFavorites`
*/
public void updateStarIcon() {
// no favorites no party!
// check if there is a last Stop
if (stopID == null) {
addToFavorites.setVisibility(View.INVISIBLE);
} else {
// filled or outline?
if (stopIsInFavorites) {
addToFavorites.setImageResource(R.drawable.ic_star_filled);
} else {
addToFavorites.setImageResource(R.drawable.ic_star_outline);
}
addToFavorites.setVisibility(View.VISIBLE);
}
}
}
diff --git a/src/it/reyboz/bustorino/middleware/AsyncArrivalsSearcher.java b/src/it/reyboz/bustorino/middleware/AsyncArrivalsSearcher.java
index ad8f980..709bc42 100644
--- a/src/it/reyboz/bustorino/middleware/AsyncArrivalsSearcher.java
+++ b/src/it/reyboz/bustorino/middleware/AsyncArrivalsSearcher.java
@@ -1,332 +1,339 @@
/*
BusTO (middleware)
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.middleware;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.SQLException;
import android.net.Uri;
import android.os.AsyncTask;
import androidx.annotation.NonNull;
import android.util.Log;
import it.reyboz.bustorino.backend.*;
import it.reyboz.bustorino.backend.mato.MatoAPIFetcher;
import it.reyboz.bustorino.data.AppDataProvider;
import it.reyboz.bustorino.data.NextGenDB;
import it.reyboz.bustorino.fragments.FragmentHelper;
import it.reyboz.bustorino.data.NextGenDB.Contract.*;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.Calendar;
/**
* This should be used to download data, but not to display it
*/
public class AsyncArrivalsSearcher extends AsyncTask{
private static final String TAG = "BusTO-DataDownload";
private static final String DEBUG_TAG = TAG;
private boolean failedAll = false;
private final AtomicReference finalResultRef;
private String query;
WeakReference helperRef;
private final ArrayList otherActivities = new ArrayList<>();
private final ArrivalsFetcher[] theFetchers;
@SuppressLint("StaticFieldLeak")
private final Context context;
private final boolean replaceFragment;
public AsyncArrivalsSearcher(FragmentHelper fh, @NonNull ArrivalsFetcher[] fetchers, Context context) {
helperRef = new WeakReference<>(fh);
fh.setLastTaskRef(this);
finalResultRef = new AtomicReference<>();
this.context = context.getApplicationContext();
this.replaceFragment = true;
theFetchers = fetchers;
if (theFetchers.length < 1){
throw new IllegalArgumentException("You have to put at least one Fetcher, idiot!");
}
}
@Override
protected Palina doInBackground(String... params) {
RecursionHelper r = new RecursionHelper<>(theFetchers);
Palina resultPalina = null;
FragmentHelper fh = helperRef.get();
ArrayList results = new ArrayList<>(theFetchers.length);
//If the FragmentHelper is null, that means the activity doesn't exist anymore
+ StringBuilder sb = new StringBuilder();
+ for (ArrivalsFetcher f: theFetchers){
+ sb.append("");
+ sb.append(f.getClass().getSimpleName());
+ sb.append("; ");
+ }
+ Log.d(DEBUG_TAG, "Using fetchers: "+sb.toString());
if (fh == null){
return null;
}
//Log.d(TAG,"refresh layout reference is: "+fh.isRefreshLayoutReferenceTrue());
while(r.valid()) {
if(this.isCancelled()) {
return null;
}
//get the data from the fetcher
ArrivalsFetcher f = r.getAndMoveForward();
AtomicReference resRef = new AtomicReference<>();
if (f instanceof MatoAPIFetcher){
((MatoAPIFetcher)f).setAppContext(context);
}
Log.d(TAG,"Using the ArrivalsFetcher: "+f.getClass());
Stop lastSearchedBusStop = fh.getLastSuccessfullySearchedBusStop();
Palina p;
String stopID;
if(params.length>0)
stopID=params[0]; //(it's a Palina)
else if(lastSearchedBusStop!=null)
stopID = lastSearchedBusStop.ID; //(it's a Palina)
else {
publishProgress(Fetcher.Result.QUERY_TOO_SHORT);
return null;
}
//Skip the FiveTAPIFetcher for the Metro Stops because it shows incomprehensible arrival times
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,resRef);
//if (res.get()!= Fetcher.Result.OK)
Log.d(DEBUG_TAG, "Arrivals fetcher: "+f+"\n\tProgress: "+resRef.get());
if(f instanceof FiveTAPIFetcher){
AtomicReference gres = new AtomicReference<>();
List branches = ((FiveTAPIFetcher) f).getDirectionsForStop(stopID,gres);
Log.d(DEBUG_TAG, "FiveTArrivals fetcher: "+f+"\n\tDetails req: "+gres.get());
if(gres.get() == Fetcher.Result.OK){
p.addInfoFromRoutes(branches);
Thread t = new Thread(new BranchInserter(branches, context));
t.start();
otherActivities.add(t);
} else{
resRef.set(Fetcher.Result.NOT_FOUND);
}
//put updated values into Database
}
if(lastSearchedBusStop != null && resRef.get()== Fetcher.Result.OK) {
// check that we don't have the same stop
if(lastSearchedBusStop.ID.equals(p.ID)) {
// searched and it's the same
String sn = lastSearchedBusStop.getStopDisplayName();
if(sn != null) {
// "merge" Stop over Palina and we're good to go
p.mergeNameFrom(lastSearchedBusStop);
}
}
}
p.mergeDuplicateRoutes(0);
if (resRef.get() == Fetcher.Result.OK && p.getTotalNumberOfPassages() == 0 ) {
resRef.set(Fetcher.Result.EMPTY_RESULT_SET);
Log.d(DEBUG_TAG, "Setting empty results");
}
publishProgress(resRef.get());
//TODO: find a way to avoid overloading the user with toasts
if (resultPalina == null && f instanceof MatoAPIFetcher && p.queryAllRoutes().size() > 0){
resultPalina = p;
}
//find if it went well
results.add(resRef.get());
if(resRef.get()== Fetcher.Result.OK) {
//wait for other threads to finish
for(Thread t: otherActivities){
try {
t.join();
} catch (InterruptedException e) {
//do nothing
}
}
return p;
}
finalResultRef.set(resRef.get());
}
/*
boolean emptyResults = true;
for (Fetcher.Result re: results){
if (!re.equals(Fetcher.Result.EMPTY_RESULT_SET)) {
emptyResults = false;
break;
}
}
*/
//at this point, we are sure that the result has been negative
failedAll=true;
return resultPalina;
}
@Override
protected void onProgressUpdate(Fetcher.Result... values) {
FragmentHelper fh = helperRef.get();
if (fh!=null)
for (Fetcher.Result r : values){
//TODO: make Toast
fh.showErrorMessage(r, SearchRequestType.ARRIVALS);
}
else {
Log.w(TAG,"We had to show some progress but activity was destroyed");
}
}
@Override
protected void onPostExecute(Palina p) {
FragmentHelper fh = helperRef.get();
if(p == null || fh == null){
//everything went bad
if(fh!=null) fh.toggleSpinner(false);
cancel(true);
//TODO: send message here
return;
}
if(isCancelled()) return;
fh.createOrUpdateStopFragment( p, replaceFragment);
}
@Override
protected void onCancelled() {
FragmentHelper fh = helperRef.get();
if (fh!=null) fh.toggleSpinner(false);
}
@Override
protected void onPreExecute() {
FragmentHelper fh = helperRef.get();
if (fh!=null) fh.toggleSpinner(true);
}
public static class BranchInserter implements Runnable{
private final List routesToInsert;
private final Context context;
//private final NextGenDB nextGenDB;
public BranchInserter(List routesToInsert,@NonNull Context con) {
this.routesToInsert = routesToInsert;
this.context = con.getApplicationContext();
//nextGenDB = new NextGenDB(context);
}
@Override
public void run() {
final NextGenDB nextGenDB = NextGenDB.getInstance(context);
//ContentValues[] values = new ContentValues[routesToInsert.size()];
ArrayList branchesValues = new ArrayList<>(routesToInsert.size()*4);
ArrayList connectionsVals = new ArrayList<>(routesToInsert.size()*4);
long starttime,endtime;
for (Route r:routesToInsert){
//if it has received an interrupt, stop
if(Thread.interrupted()) return;
//otherwise, build contentValues
final ContentValues cv = new ContentValues();
cv.put(BranchesTable.COL_BRANCHID,r.branchid);
cv.put(LinesTable.COLUMN_NAME,r.getName());
cv.put(BranchesTable.COL_DIRECTION,r.destinazione);
cv.put(BranchesTable.COL_DESCRIPTION,r.description);
for (int day :r.serviceDays) {
switch (day){
case Calendar.MONDAY:
cv.put(BranchesTable.COL_LUN,1);
break;
case Calendar.TUESDAY:
cv.put(BranchesTable.COL_MAR,1);
break;
case Calendar.WEDNESDAY:
cv.put(BranchesTable.COL_MER,1);
break;
case Calendar.THURSDAY:
cv.put(BranchesTable.COL_GIO,1);
break;
case Calendar.FRIDAY:
cv.put(BranchesTable.COL_VEN,1);
break;
case Calendar.SATURDAY:
cv.put(BranchesTable.COL_SAB,1);
break;
case Calendar.SUNDAY:
cv.put(BranchesTable.COL_DOM,1);
break;
}
}
if(r.type!=null) cv.put(BranchesTable.COL_TYPE, r.type.getCode());
cv.put(BranchesTable.COL_FESTIVO, r.festivo.getCode());
//values[routesToInsert.indexOf(r)] = cv;
branchesValues.add(cv);
if(r.getStopsList() != null)
for(int i=0; i0) {
starttime = System.currentTimeMillis();
ContentValues[] valArr = connectionsVals.toArray(new ContentValues[0]);
Log.d("DataDownloadInsert", "inserting " + valArr.length + " connections");
int rows = nextGenDB.insertBatchContent(valArr, ConnectionsTable.TABLE_NAME);
endtime = System.currentTimeMillis();
Log.d("DataDownload", "Inserted connections found, took " + (endtime - starttime) + " ms, inserted " + rows + " rows");
}
nextGenDB.close();
}
}
}