diff --git a/AndroidManifest.xml b/AndroidManifest.xml --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2,8 +2,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="it.reyboz.bustorino"> - - <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> @@ -14,13 +12,19 @@ <uses-permission android:name="android.permission.READ_PHONE_STATE"/> <application - android:name=".BustoApp" - android:networkSecurityConfig="@xml/networks_security_config" - android:allowBackup="true" - android:icon="@mipmap/ic_launcher" - android:roundIcon="@mipmap/ic_launcher_round" - android:label="@string/app_name" - android:theme="@style/AppTheme.NoActionBar"> + android:name=".BustoApp" + android:allowBackup="true" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:networkSecurityConfig="@xml/networks_security_config" + android:roundIcon="@mipmap/ic_launcher_round" + android:theme="@style/AppTheme.NoActionBar"> + + <activity + android:name=".ActivityExperiments" + android:label="@string/experiments" + android:theme="@style/MapTheme"> + </activity> <activity android:name=".ActivityPrincipal" android:label="@string/app_name" @@ -88,21 +92,20 @@ android:value=".ActivityMain"/> </activity> <activity - android:name=".ActivityMap" - android:label="@string/title_activity_map" - android:parentActivityName=".ActivityMain" - android:theme="@style/MapTheme"> + android:name=".ActivityMap" + android:label="@string/title_activity_map" + android:parentActivityName=".ActivityMain" + android:theme="@style/MapTheme"> <!-- API < 16: --> <meta-data - android:name="android.support.PARENT_ACTIVITY" - android:value=".ActivityMain"/> + android:name="android.support.PARENT_ACTIVITY" + android:value=".ActivityMain"/> </activity> - <activity android:name=".ActivityMain" - android:screenOrientation="portrait" - android:label="@string/app_name" > + android:label="@string/app_name" + android:screenOrientation="portrait"> </activity> <provider @@ -115,8 +118,7 @@ <service android:name=".data.DatabaseUpdateService" android:exported="false"> - </service> - <!-- Don't show the additional frame on samsung phones --> + </service> <!-- Don't show the additional frame on samsung phones --> <meta-data android:name="com.samsung.android.icon_container.has_icon_container" android:value="true"/> @@ -132,4 +134,4 @@ </activity> </application> -</manifest> +</manifest> \ No newline at end of file diff --git a/build.gradle b/build.gradle --- a/build.gradle +++ b/build.gradle @@ -6,11 +6,9 @@ } - dependencies { - - classpath 'com.android.tools.build:gradle:4.1.3' - } ext { + //multidex + multidex_version = "2.0.1" //libraries versions fragment_version = "1.3.6" activity_version = "1.2.4" @@ -21,8 +19,17 @@ acra_version = "5.7.0" lifecycle_version = "2.3.1" arch_version = "2.1.0" + room_version = "2.3.0" + //kotlin + kotlin_version = '1.5.0' + coroutines_version = "1.5.0" } + dependencies { + + classpath 'com.android.tools.build:gradle:4.1.3' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } } allprojects { repositories { @@ -34,6 +41,8 @@ } apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-kapt' android { compileSdkVersion 29 @@ -41,11 +50,12 @@ defaultConfig { applicationId "it.reyboz.bustorino" - minSdkVersion 14 + minSdkVersion 15 targetSdkVersion 29 versionCode 35 versionName "1.15.4" vectorDrawables.useSupportLibrary = true + multiDexEnabled true } compileOptions { @@ -91,8 +101,8 @@ implementation "androidx.preference:preference:$preference_version" implementation "androidx.work:work-runtime:$work_version" - - implementation "com.google.android.material:material:1.3.0" + implementation "com.google.android.material:material:1.4.0" + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'org.jsoup:jsoup:1.13.1' @@ -113,5 +123,18 @@ // Lifecycles only (without ViewModel or LiveData) implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version" + // Room components + implementation "androidx.room:room-ktx:$room_version" + kapt "androidx.room:room-compiler:$room_version" + androidTestImplementation "androidx.room:room-testing:$room_version" + //multidex - we need this to build the app + implementation "androidx.multidex:multidex:$multidex_version" } } + +dependencies { + + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" + api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" +} \ No newline at end of file diff --git a/res/layout/activity_experiments.xml b/res/layout/activity_experiments.xml new file mode 100644 --- /dev/null +++ b/res/layout/activity_experiments.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".ActivityExperiments"> + + <Button + android:text="Download GTFS data" + android:layout_width="wrap_content" + android:layout_height="wrap_content" android:id="@+id/button" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + android:onClick="runExp" + /> + + <Button + android:text="Delete temporary GTFS file" + android:layout_width="wrap_content" + android:layout_height="wrap_content" android:id="@+id/deleteButton" + app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/button"/> +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/res/menu/extra_menu_items.xml b/res/menu/principal_menu.xml rename from res/menu/extra_menu_items.xml rename to res/menu/principal_menu.xml --- a/res/menu/extra_menu_items.xml +++ b/res/menu/principal_menu.xml @@ -21,4 +21,9 @@ android:orderInCategory="8" android:title="@string/action_licence" app:showAsAction="never" /> + <item + android:id="@+id/action_experiments" + android:title="@string/experiments" + android:orderInCategory="10" + /> </menu> \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -157,7 +157,8 @@ <string name="storage_permission">storage</string> <string name="message_crash">The application has crashed because you encountered a bug. \nIf you want, you can help the developers by sending the crash report via email. - \nNote that no sensitive data is contained in the report, just small bits of info on your phone and app configuration/state. + \nNote that no sensitive data is contained in the report, just small bits of info on your phone and app + configuration/state. </string> <string name="acra_email_message">The application crashed and the crash report is in the attachments. Please describe what you were doing before the crash: \n @@ -171,4 +172,6 @@ <string name="donate_now">Buy us a coffee</string> <string name="map">Map</string> <string name="stop_search_view_title">Search by stop</string> + + </resources> diff --git a/res/values/theme.xml b/res/values/theme.xml --- a/res/values/theme.xml +++ b/res/values/theme.xml @@ -21,4 +21,6 @@ <style name="preferenceTheme" parent="PreferenceThemeOverlay"></style> + + </resources> \ No newline at end of file diff --git a/res/xml/networks_security_config.xml b/res/xml/networks_security_config.xml --- a/res/xml/networks_security_config.xml +++ b/res/xml/networks_security_config.xml @@ -3,6 +3,6 @@ <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">5t.torino.it</domain> <domain includeSubdomains="true">gtt.to.it</domain> - + <domain includeSubdomains="true">comune.torino.it</domain> </domain-config> </network-security-config> \ No newline at end of file diff --git a/src/it/reyboz/bustorino/ActivityExperiments.java b/src/it/reyboz/bustorino/ActivityExperiments.java new file mode 100644 --- /dev/null +++ b/src/it/reyboz/bustorino/ActivityExperiments.java @@ -0,0 +1,141 @@ +/* + BusTO - Data 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 <http://www.gnu.org/licenses/>. + */ +package it.reyboz.bustorino; + +import android.content.Context; +import android.os.AsyncTask; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.Toast; +import androidx.appcompat.app.AppCompatActivity; +import android.os.Bundle; +import it.reyboz.bustorino.backend.Fetcher; +import it.reyboz.bustorino.backend.gtfs.GtfsDataParser; +import it.reyboz.bustorino.backend.networkTools; +import it.reyboz.bustorino.backend.utils; +import it.reyboz.bustorino.middleware.GeneralActivity; + +import java.io.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipInputStream; + +public class ActivityExperiments extends GeneralActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_experiments); + Button deleteButton = findViewById(R.id.deleteButton); + if(deleteButton!=null) + deleteButton.setOnClickListener(view -> { + File saveFile = new File(getFilesDir(), "gtfs_data.zip"); + if(!saveFile.isDirectory() && saveFile.exists()){ + //delete the file + if(saveFile.delete()) + Toast.makeText(this, "Gtfs zip deleted", Toast.LENGTH_SHORT).show(); + else + Toast.makeText(this, "Cannot delete gtfs zip", Toast.LENGTH_SHORT).show(); + } else + Toast.makeText(this, "Gtfs data zip not present", Toast.LENGTH_SHORT).show(); + }); + } + + public void runExp(View v){ + + final Context appContext = v.getContext().getApplicationContext(); + + Runnable run = new Runnable() { + @Override + public void run() { + final String DEBUG_TAG = "ExperimentsGTFS"; + AtomicReference<Fetcher.Result> res = new AtomicReference<>(); + //List<String> files = GtfsDataParser.readFilesList(res); + Date updateDate = GtfsDataParser.getLastGTFSUpdateDate(res); + Log.w( + "ExperimentGTFS", "Last update date is " + updateDate//utils.joinList(files, "\n") + ); + //Toast.makeText(v.getContext(), "Gtfs data already downloaded", Toast.LENGTH_SHORT).show(); + + File saveFile = new File(getFilesDir(), "gtfs_data.zip"); + if (!saveFile.isDirectory() && saveFile.exists()) { + Log.w(DEBUG_TAG, "Zip exists: " + saveFile); + try (FileInputStream fileStream = new FileInputStream(saveFile)) { + ZipInputStream stream = new ZipInputStream(fileStream); + // now iterate through each item in the stream. The get next + // entry call will return a ZipEntry for each file in the + // stream + ZipEntry entry; + String line; + final BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + while ((entry = stream.getNextEntry()) != null) { + String s = String.format(Locale.ENGLISH, "Entry: %s len %d added", + entry.getName(), + entry.getSize() + ); + //Toast.makeText(v.getContext(), "File: " + entry.getName(), Toast.LENGTH_SHORT).show(); + Log.d(DEBUG_TAG, s); + //read data in table + final String tableName = entry.getName().split("\\.")[0].trim(); + GtfsDataParser.readCSVWithColumns(reader, tableName, v.getContext().getApplicationContext()); + + + // Once we get the entry from the stream, the stream is + // positioned read to read the raw data, and we keep + // reading until read returns 0 or less. + //result.add(entry.getName()); + } + stream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + //saveFile.delete(); + } else + try { + //Toast.makeText(v.getContext(), "Downloading gtfs data", Toast.LENGTH_SHORT).show(); + + networkTools.saveFileInCache(saveFile, new URL(GtfsDataParser.GTFS_ADDRESS)); + Log.w(DEBUG_TAG, "File saved"); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + + } + }; + ExecutorService executorService = Executors.newFixedThreadPool(2); + //Looper looper = new Looper(true); + //Handler handler = new Handler(); + //handler.post(run); + executorService.execute(run); + + + } + + +} \ No newline at end of file diff --git a/src/it/reyboz/bustorino/ActivityPrincipal.java b/src/it/reyboz/bustorino/ActivityPrincipal.java --- a/src/it/reyboz/bustorino/ActivityPrincipal.java +++ b/src/it/reyboz/bustorino/ActivityPrincipal.java @@ -1,3 +1,20 @@ +/* + BusTO - Arrival times for Turin public transport. + 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 <http://www.gnu.org/licenses/>. + */ package it.reyboz.bustorino; import android.Manifest; @@ -23,6 +40,7 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; +import androidx.preference.PreferenceManager; import androidx.work.BackoffPolicy; import androidx.work.Constraints; import androidx.work.ExistingPeriodicWorkPolicy; @@ -165,22 +183,11 @@ requestArrivalsForStopID(busStopID); } //Try (hopefully) database update - PeriodicWorkRequest wr = new PeriodicWorkRequest.Builder(DBUpdateWorker.class, 1, TimeUnit.DAYS) - .setBackoffCriteria(BackoffPolicy.LINEAR, 30, TimeUnit.MINUTES) - .setConstraints(new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED) - .build()) - .build(); - final WorkManager workManager = WorkManager.getInstance(this); - - final int version = theShPr.getInt(DatabaseUpdate.DB_VERSION_KEY, -10); - if (version >= 0) - workManager.enqueueUniquePeriodicWork(DBUpdateWorker.DEBUG_TAG, - ExistingPeriodicWorkPolicy.KEEP, wr); - else workManager.enqueueUniquePeriodicWork(DBUpdateWorker.DEBUG_TAG, - ExistingPeriodicWorkPolicy.REPLACE, wr); + DatabaseUpdate.requestDBUpdateWithWork(this, false); /* - Set database update + Watch for database update */ + final WorkManager workManager = WorkManager.getInstance(this); workManager.getWorkInfosForUniqueWorkLiveData(DBUpdateWorker.DEBUG_TAG) .observe(this, workInfoList -> { // If there are no matching work info, do nothing @@ -300,7 +307,11 @@ @Override public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.extra_menu_items, menu); + getMenuInflater().inflate(R.menu.principal_menu, menu); + MenuItem experimentsMenuItem = menu.findItem(R.id.action_experiments); + SharedPreferences shPr = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + boolean exper_On = shPr.getBoolean(getString(R.string.pref_key_experimental), false); + experimentsMenuItem.setVisible(exper_On); return super.onCreateOptionsMenu(menu); } @@ -569,6 +580,8 @@ case R.id.action_licence: openIceweasel("https://www.gnu.org/licenses/gpl-3.0.html", activityContext); return true; + case R.id.action_experiments: + startActivity(new Intent(ActivityPrincipal.this, ActivityExperiments.class)); default: } return false; diff --git a/src/it/reyboz/bustorino/BustoApp.java b/src/it/reyboz/bustorino/BustoApp.java --- a/src/it/reyboz/bustorino/BustoApp.java +++ b/src/it/reyboz/bustorino/BustoApp.java @@ -1,8 +1,25 @@ +/* + BusTO - Arrival times for Turin public transport. + 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 <http://www.gnu.org/licenses/>. + */ package it.reyboz.bustorino; -import android.app.Application; import android.content.Context; +import androidx.multidex.MultiDexApplication; import org.acra.ACRA; import org.acra.BuildConfig; import org.acra.ReportField; @@ -14,7 +31,7 @@ import static org.acra.ReportField.*; -public class BustoApp extends Application { +public class BustoApp extends MultiDexApplication { private static final ReportField[] REPORT_FIELDS = {REPORT_ID, APP_VERSION_CODE, APP_VERSION_NAME, PACKAGE_NAME, PHONE_MODEL, BRAND, PRODUCT, ANDROID_VERSION, BUILD_CONFIG, CUSTOM_DATA, IS_SILENT, STACK_TRACE, INITIAL_CONFIGURATION, CRASH_CONFIGURATION, DISPLAY, USER_COMMENT, diff --git a/src/it/reyboz/bustorino/backend/Fetcher.java b/src/it/reyboz/bustorino/backend/Fetcher.java --- a/src/it/reyboz/bustorino/backend/Fetcher.java +++ b/src/it/reyboz/bustorino/backend/Fetcher.java @@ -31,6 +31,7 @@ * QUERY_TOO_SHORT: input more characters and retry. */ enum Result { - OK, CLIENT_OFFLINE, SERVER_ERROR, SETUP_ERROR,PARSER_ERROR, EMPTY_RESULT_SET, QUERY_TOO_SHORT,SERVER_ERROR_404 + OK, CLIENT_OFFLINE, SERVER_ERROR, SETUP_ERROR,PARSER_ERROR, EMPTY_RESULT_SET, QUERY_TOO_SHORT, SERVER_ERROR_404, + CONNECTION_ERROR } } diff --git a/src/it/reyboz/bustorino/backend/gtfs/GtfsDataParser.java b/src/it/reyboz/bustorino/backend/gtfs/GtfsDataParser.java new file mode 100644 --- /dev/null +++ b/src/it/reyboz/bustorino/backend/gtfs/GtfsDataParser.java @@ -0,0 +1,256 @@ +package it.reyboz.bustorino.backend.gtfs; + +import android.content.Context; +import android.util.Log; +import androidx.annotation.NonNull; +import it.reyboz.bustorino.backend.Fetcher; +import it.reyboz.bustorino.backend.networkTools; +import it.reyboz.bustorino.data.gtfs.CsvTableInserter; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Attributes; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +abstract public class GtfsDataParser { + public static final String GTFS_ADDRESS="https://www.gtt.to.it/open_data/gtt_gtfs.zip"; + public static final String GTFS_PAGE_ADDRESS="http://aperto.comune.torino.it/dataset/feed-gtfs-trasporti-gtt"; + + private static final String DEBUG_TAG = "BusTO-GTFSDataParser"; + + private static final Pattern quotePattern = Pattern.compile("^\\s*\"((?:[^\"]|(?:\"\"))*?)\"\\s*,"); + + /** + * First trial for a function to download the zip + * @param res Fetcher.result + * @return the list of files inside the ziè + */ + public static ArrayList<String> readFilesList(AtomicReference<Fetcher.Result> res){ + + HttpURLConnection urlConnection; + InputStream in; + ArrayList<String> result = new ArrayList<>(); + try { + final URL gtfsUrl = new URL(GTFS_ADDRESS); + urlConnection = (HttpURLConnection) gtfsUrl.openConnection(); + } catch(IOException e) { + //e.printStackTrace(); + res.set(Fetcher.Result.SERVER_ERROR); // even when offline, urlConnection works fine. WHY. + return null; + } + urlConnection.setConnectTimeout(4000); + urlConnection.setReadTimeout(50*1000); + + try { + in = urlConnection.getInputStream(); + } catch (Exception e) { + try { + if(urlConnection.getResponseCode()==404) + res.set(Fetcher.Result.SERVER_ERROR_404); + } catch (IOException e2) { + e2.printStackTrace(); + } + return null; + + } + try (ZipInputStream stream = new ZipInputStream(in)) { + + // now iterate through each item in the stream. The get next + // entry call will return a ZipEntry for each file in the + // stream + ZipEntry entry; + while ((entry = stream.getNextEntry()) != null) { + String s = String.format(Locale.ENGLISH, "Entry: %s len %d added", + entry.getName(), + entry.getSize() + ); + System.out.println(s); + + // Once we get the entry from the stream, the stream is + // positioned read to read the raw data, and we keep + // reading until read returns 0 or less. + result.add(entry.getName()); + } + } catch (IOException e) { + e.printStackTrace(); + } + // we must always close the zip file. + return result; + } + + public static Date getLastGTFSUpdateDate(AtomicReference<Fetcher.Result> res) { + URL theURL; + final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.ENGLISH); + //final Date baseDate = dateFormat.parse("1970-00-00T00:00:00+0000"); + final Date nullDate = new Date(0); + try{ + theURL = new URL(GTFS_PAGE_ADDRESS); + } catch (IOException ex){ + Log.e(DEBUG_TAG, "Fixed URL is null, this is a real issue"); + return nullDate; + } + res.set(Fetcher.Result.OK); + final String fullPageDOM = networkTools.getDOM(theURL, res); + if(fullPageDOM== null){ + //Something wrong happend + Log.e(DEBUG_TAG, "Cannot get URL"); + return nullDate; + } + res.set(Fetcher.Result.OK); + Document doc = Jsoup.parse(fullPageDOM); + + Elements sections = doc.select("section.additional-info"); + Date finalDate = new Date(0); + for (Element sec: sections){ + Element head = sec.select("h3").first(); + String headTitle = head.text(); + if(!headTitle.trim().toLowerCase(Locale.ITALIAN).equals("informazioni supplementari")) + continue; + + for (Element row: sec.select("tr")){ + if(!row.selectFirst("th").text().trim() + .toLowerCase(Locale.ITALIAN).equals("ultimo aggiornamento")) + continue; + + Attributes spanAttributes = row.selectFirst("td > span").attributes(); + String dateAsString = spanAttributes.get("data-datetime"); + try { + finalDate = dateFormat.parse(dateAsString); + return finalDate; + }catch (ParseException ex){ + Log.e(DEBUG_TAG, "Wrong date for the last update of GTFS Data: "+dateAsString); + res.set(Fetcher.Result.PARSER_ERROR); + ex.printStackTrace(); + } + break; + } + } + res.set(Fetcher.Result.PARSER_ERROR); + return finalDate; + + } + + public static void readCSVWithColumns(BufferedReader reader, String tableName, Context con) throws IOException { + + //String[] elements; + List<String> lineElements; + + String line; + + final String header = reader.readLine(); + if (header == null){ + throw new IOException(); + } + + //elements = header.split("\n")[0].split(","); + //System.out.println(Arrays.toString(elements)); + + lineElements = readCsvLine(header); + + + final HashMap<Integer,String> columnMap = new HashMap<>(); + + final CsvTableInserter inserter = new CsvTableInserter(tableName,con); + + for (int i=0; i< lineElements.size(); i++){ + //columnMap.put(i, fixStringIfItHasQuotes(elements[i].trim()) ); + columnMap.put(i, lineElements.get(i).trim() ); + + } + Log.d(DEBUG_TAG, "Columns for the file: "+columnMap); + boolean first = true; + while((line = reader.readLine())!=null){ + //there is a line of data + //elements = line.split("\n")[0].split(","); + if(first) Log.d(DEBUG_TAG, "Element line: "+line); + lineElements = readCsvLine(line); + + final Map<String,String> rowsMap = getColumnsAsString(lineElements.toArray(new String[0]), columnMap); + if (first){ + Log.d(DEBUG_TAG, " in map:"+rowsMap); + first=false; + } + inserter.addElement(rowsMap); + } + + //commit data + inserter.insertDataInDatabase(); + } + @NonNull + private static Map<String,String> getColumnsAsString(@NonNull String[] lineElements, Map<Integer,String> colsIndices) + { + final HashMap<String,String> theMap = new HashMap<>(); + for(int l=0; l<lineElements.length; l++){ + if(!colsIndices.containsKey(l)) + continue; + //theMap.put(colsIndices.get(l), fixStringIfItHasQuotes(lineElements[l].trim())); + theMap.put(colsIndices.get(l), lineElements[l].trim()); + } + return theMap; + } + + private static String fixStringIfItHasQuotes(String item) { + if(item.length()==0){ + return item; + } + final String[] elements=item.split("\""); + /* + Log.d(DEBUG_TAG,"Splitting quotes length:"+elements.length); + for (int i=0; i<elements.length; i++){ + Log.d(DEBUG_TAG,"Elements: "+i+" "+elements[i]); + } + */ + if(elements.length>1){ + //if(elements.length<3) throw new IllegalArgumentException("Malformed string"); + return elements[1]; + } else if(elements.length > 0) + return elements[0]; + else + return item; + } + //https://stackoverflow.com/questions/7800494/parse-csv-with-double-quote-in-some-cases#7800519 + public static List<String> readCsvLine(String line) throws IllegalArgumentException + { + + List<String> list = new ArrayList<String>(); + line += ","; + + for (int x = 0; x < line.length(); x++) + { + String s = line.substring(x); + if (s.trim().startsWith("\"")) + { + Matcher m = quotePattern.matcher(s); + if (!m.find()) { + Log.e(DEBUG_TAG, "Cannot find pattern, "+s+" , line: "+line); + throw new IllegalArgumentException("CSV is malformed"); + } + list.add(m.group(1).replace("\"\"", "\"")); + x += m.end() - 1; + } + else + { + int y = s.indexOf(","); + if (y == -1) + throw new IllegalArgumentException("CSV is malformed"); + list.add(s.substring(0, y)); + x += y; + } + } + return list; + } +} diff --git a/src/it/reyboz/bustorino/backend/networkTools.java b/src/it/reyboz/bustorino/backend/networkTools.java --- a/src/it/reyboz/bustorino/backend/networkTools.java +++ b/src/it/reyboz/bustorino/backend/networkTools.java @@ -18,22 +18,20 @@ package it.reyboz.bustorino.backend; +import android.content.Context; import androidx.annotation.Nullable; import android.util.Log; -import java.io.BufferedInputStream; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; +import java.io.*; import java.net.HttpURLConnection; import java.net.URL; +import java.util.ArrayList; import java.util.Map; import java.util.Scanner; import java.util.concurrent.atomic.AtomicReference; public abstract class networkTools { - static String getDOM(final URL url, final AtomicReference<Fetcher.Result> res) { + public static String getDOM(final URL url, final AtomicReference<Fetcher.Result> res) { //Log.d("asyncwget", "Catching URL in background: " + uri[0]); HttpURLConnection urlConnection; StringBuilder result = null; @@ -70,6 +68,52 @@ res.set(Fetcher.Result.PARSER_ERROR); // will be set to "OK" later, this is a safety net in case StringBuilder returns null, the website returns an HTTP 204 or something like that. return result.toString(); } + + public static Fetcher.Result saveFileInCache(File outputFile, URL url) { + HttpURLConnection urlConnection; + try { + urlConnection = (HttpURLConnection) url.openConnection(); + } catch (IOException e) { + //e.printStackTrace(); + return Fetcher.Result.CONNECTION_ERROR; + } + urlConnection.setConnectTimeout(4000); + urlConnection.setReadTimeout(50 * 1000); + + Log.d("BusTO net Tools", "Download file "+url); + try (InputStream inputStream = urlConnection.getInputStream()) { + //File outputFile = new File(con.getFilesDir(), fileName); + //BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream); + FileOutputStream outputStream = new FileOutputStream(outputFile); + byte buffer[] = new byte[16384]; + boolean inProgress = true; + while(inProgress){ + int numread = inputStream.read(buffer); + inProgress = (numread > 0); + if(inProgress) outputStream.write(buffer, 0, numread); + } + outputStream.close(); + //while (bufferedInputStream.available()) + } catch (IOException e) { + e.printStackTrace(); + try { + final Fetcher.Result res; + if(urlConnection.getResponseCode()==404) + res= Fetcher.Result.SERVER_ERROR_404; + else if(urlConnection.getResponseCode()!=200) + res= Fetcher.Result.SERVER_ERROR; + else res= Fetcher.Result.PARSER_ERROR; + urlConnection.disconnect(); + return res; + } catch (IOException ioException) { + ioException.printStackTrace(); + urlConnection.disconnect(); + return Fetcher.Result.PARSER_ERROR; + } + } + urlConnection.disconnect(); + return Fetcher.Result.OK; + } @Nullable static String queryURL(URL url, AtomicReference<Fetcher.Result> res){ return queryURL(url,res,null); diff --git a/src/it/reyboz/bustorino/backend/utils.java b/src/it/reyboz/bustorino/backend/utils.java --- a/src/it/reyboz/bustorino/backend/utils.java +++ b/src/it/reyboz/bustorino/backend/utils.java @@ -8,10 +8,12 @@ import android.util.Log; import android.util.TypedValue; import android.view.View; +import androidx.annotation.Nullable; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Arrays; +import java.util.List; public abstract class utils { private static final double EarthRadius = 6371e3; @@ -153,4 +155,17 @@ return "Trace too Short."; } */ + public static String joinList(@Nullable List<String> 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<dat.size(); i++){ + sb.append(separator); + sb.append(dat.get(i)); + } + return sb.toString(); + } } diff --git a/src/it/reyboz/bustorino/data/AppRepository.java b/src/it/reyboz/bustorino/data/AppRepository.java new file mode 100644 --- /dev/null +++ b/src/it/reyboz/bustorino/data/AppRepository.java @@ -0,0 +1,23 @@ +/* + BusTO - Data 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 <http://www.gnu.org/licenses/>. + */ +package it.reyboz.bustorino.data; + +public class AppRepository { + + +} diff --git a/src/it/reyboz/bustorino/data/DBUpdateWorker.java b/src/it/reyboz/bustorino/data/DBUpdateWorker.java --- a/src/it/reyboz/bustorino/data/DBUpdateWorker.java +++ b/src/it/reyboz/bustorino/data/DBUpdateWorker.java @@ -1,3 +1,20 @@ +/* + BusTO - Data 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 <http://www.gnu.org/licenses/>. + */ package it.reyboz.bustorino.data; import android.annotation.SuppressLint; diff --git a/src/it/reyboz/bustorino/data/DatabaseUpdate.java b/src/it/reyboz/bustorino/data/DatabaseUpdate.java --- a/src/it/reyboz/bustorino/data/DatabaseUpdate.java +++ b/src/it/reyboz/bustorino/data/DatabaseUpdate.java @@ -1,3 +1,20 @@ +/* + BusTO - Data 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 <http://www.gnu.org/licenses/>. + */ package it.reyboz.bustorino.data; import android.content.ContentValues; @@ -5,6 +22,8 @@ import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; import android.util.Log; +import androidx.core.content.ContextCompat; +import androidx.work.*; import it.reyboz.bustorino.R; import it.reyboz.bustorino.backend.Fetcher; import it.reyboz.bustorino.backend.FiveTAPIFetcher; @@ -14,6 +33,7 @@ import org.json.JSONObject; import java.util.ArrayList; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import static android.content.Context.MODE_PRIVATE; @@ -155,4 +175,25 @@ editor.putBoolean(con.getString(R.string.databaseUpdatingPref),value); return editor.commit(); } + + /** + * Request update using workmanager framework + * @param con the context to use + * @param forced if you want to force the request to go now + */ + public static void requestDBUpdateWithWork(Context con, boolean forced){ + final SharedPreferences theShPr = PreferencesHolder.getMainSharedPreferences(con); + final WorkManager workManager = WorkManager.getInstance(con); + PeriodicWorkRequest wr = new PeriodicWorkRequest.Builder(DBUpdateWorker.class, 1, TimeUnit.DAYS) + .setBackoffCriteria(BackoffPolicy.LINEAR, 30, TimeUnit.MINUTES) + .setConstraints(new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED) + .build()) + .build(); + final int version = theShPr.getInt(DatabaseUpdate.DB_VERSION_KEY, -10); + if (version >= 0 && !forced) + workManager.enqueueUniquePeriodicWork(DBUpdateWorker.DEBUG_TAG, + ExistingPeriodicWorkPolicy.KEEP, wr); + else workManager.enqueueUniquePeriodicWork(DBUpdateWorker.DEBUG_TAG, + ExistingPeriodicWorkPolicy.REPLACE, wr); + } } diff --git a/src/it/reyboz/bustorino/data/FavoritesLiveData.java b/src/it/reyboz/bustorino/data/FavoritesLiveData.java --- a/src/it/reyboz/bustorino/data/FavoritesLiveData.java +++ b/src/it/reyboz/bustorino/data/FavoritesLiveData.java @@ -1,3 +1,20 @@ +/* + BusTO - Data 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 <http://www.gnu.org/licenses/>. + */ package it.reyboz.bustorino.data; diff --git a/src/it/reyboz/bustorino/data/GTTInfoInject.java b/src/it/reyboz/bustorino/data/GTTInfoInject.java --- a/src/it/reyboz/bustorino/data/GTTInfoInject.java +++ b/src/it/reyboz/bustorino/data/GTTInfoInject.java @@ -1,3 +1,20 @@ +/* + BusTO - Data 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 <http://www.gnu.org/licenses/>. + */ package it.reyboz.bustorino.data; import java.util.Locale; diff --git a/src/it/reyboz/bustorino/data/NextGenDB.java b/src/it/reyboz/bustorino/data/NextGenDB.java --- a/src/it/reyboz/bustorino/data/NextGenDB.java +++ b/src/it/reyboz/bustorino/data/NextGenDB.java @@ -123,7 +123,7 @@ db.execSQL(SQL_CREATE_BRANCH_TABLE); db.execSQL(SQL_CREATE_CONNECTIONS_TABLE); - DatabaseUpdateService.startDBUpdate(appContext,0,true); + DatabaseUpdate.requestDBUpdateWithWork(appContext, true); } } diff --git a/src/it/reyboz/bustorino/data/PreferencesHolder.java b/src/it/reyboz/bustorino/data/PreferencesHolder.java new file mode 100644 --- /dev/null +++ b/src/it/reyboz/bustorino/data/PreferencesHolder.java @@ -0,0 +1,34 @@ +/* + BusTO - Data 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 <http://www.gnu.org/licenses/>. + */ +package it.reyboz.bustorino.data; + +import android.content.Context; +import android.content.SharedPreferences; +import it.reyboz.bustorino.R; + +import static android.content.Context.MODE_PRIVATE; + +/** + * Static class for commonly used SharedPreference operations + */ +public abstract class PreferencesHolder { + + public static SharedPreferences getMainSharedPreferences(Context context){ + return context.getSharedPreferences(context.getString(R.string.mainSharedPreferences), MODE_PRIVATE); + } +} diff --git a/src/it/reyboz/bustorino/data/gtfs/Converters.kt b/src/it/reyboz/bustorino/data/gtfs/Converters.kt new file mode 100644 --- /dev/null +++ b/src/it/reyboz/bustorino/data/gtfs/Converters.kt @@ -0,0 +1,103 @@ +/* + BusTO - Data 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 <http://www.gnu.org/licenses/>. + */ +package it.reyboz.bustorino.data.gtfs + +import androidx.room.TypeConverter +import java.text.SimpleDateFormat +import java.util.* + +/** + * Class to convert values for objects into + * the needed columns + * + * handled automatically by Room with TypeConverter + */ +class Converters { + + + @TypeConverter + fun fromString(value: String?): Date? { + return dateFromFmtString(value) + } + + @TypeConverter + fun dateToString(date: Date?): String? { + return date?.let { stringFormat.format(it)} + } + + @TypeConverter + fun exceptionToInt(type: GtfsServiceDate.ExceptionType?): Int? { + return type?.value + } + @TypeConverter + fun fromInt(value: Int?): GtfsServiceDate.ExceptionType? { + return value?.let { GtfsServiceDate.ExceptionType.getByValue(it) } + } + + companion object{ + const val DATE_FMT_STRING = "yyyyMMdd" + val stringFormat = SimpleDateFormat(DATE_FMT_STRING, Locale.US) + + fun fromStringNum(string: String?): Boolean?{ + string?.let { if (it.trim() == "1") + return true + else if(it.trim() == "0") + return false + else throw Exception("Cannot convert $string to numeric value") } + return null + + } + fun fromStringNum(string: String?, defaultVal: Boolean): Boolean{ + string?.let { if (it.trim() == "1") + return true + else if(it.trim() == "0") + return false + else return defaultVal } + return defaultVal + + } + fun dateFromFmtString(value: String?): Date?{ + return value?.let { + + stringFormat.parse(it) + } + } + + fun wheelchairFromString(string: String?): GtfsStop.WheelchairAccess?{ + string?.let { if (it.trim() == "1") + return GtfsStop.WheelchairAccess.SOMETIMES + else if(it.trim() == "0") + return GtfsStop.WheelchairAccess.UNKNOWN + else if(it.trim() == "2") + return GtfsStop.WheelchairAccess.IMPOSSIBLE + else //throw Exception("Cannot convert $string to wheelchair access") } + return GtfsStop.WheelchairAccess.UNKNOWN + + } + return null + } + @TypeConverter + fun wheelchairToInt(access:GtfsStop.WheelchairAccess): Int{ + return access.value; + } + @TypeConverter + fun wheelchairFromInt(value: Int): GtfsStop.WheelchairAccess{ + return GtfsStop.WheelchairAccess.getByValue(value)?: GtfsStop.WheelchairAccess.UNKNOWN + } + } +} \ No newline at end of file diff --git a/src/it/reyboz/bustorino/data/gtfs/CsvTableInserter.kt b/src/it/reyboz/bustorino/data/gtfs/CsvTableInserter.kt new file mode 100644 --- /dev/null +++ b/src/it/reyboz/bustorino/data/gtfs/CsvTableInserter.kt @@ -0,0 +1,82 @@ +/* + BusTO - Data 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 <http://www.gnu.org/licenses/>. + */ +package it.reyboz.bustorino.data.gtfs + +import android.content.Context +import android.util.Log +import java.util.ArrayList + +class CsvTableInserter( + val tableName: String, context: Context +) { + private val database: GtfsDatabase = GtfsDatabase.getGtfsDatabase(context) + private val dao: StaticGtfsDao = database.gtfsDao() + + private val elementsList: MutableList< in GtfsTable> = mutableListOf() + + fun addElement(csvLineElements: Map<String,String>) { + + when(tableName){ + "stops" -> + elementsList.add(GtfsStop(csvLineElements)) + "routes" -> + elementsList.add(GtfsRoute(csvLineElements)) + "calendar" -> + elementsList.add(GtfsService(csvLineElements)) + "calendar_dates" -> + elementsList.add(GtfsServiceDate(csvLineElements)) + "trips" -> + elementsList.add(GtfsTrip(csvLineElements)) + "shapes" -> + elementsList.add(GtfsShape(csvLineElements)) + "stop_times" -> + elementsList.add(GtfsStopTime(csvLineElements)) + + + } + if(elementsList.size >= MAX_ELEMENTS){ + //have to insert + Log.d(DEBUG_TAG, "Inserting first batch of elements now, list size: "+elementsList.size) + if (tableName == "routes") + dao.insertRoutes(elementsList.filterIsInstance<GtfsRoute>()) + else + insertDataInDatabase() + + elementsList.clear() + + } + } + fun insertDataInDatabase(){ + when(tableName){ + "stops" -> dao.updateStops(elementsList.filterIsInstance<GtfsStop>()) + "routes" -> dao.clearAndInsertRoutes(elementsList.filterIsInstance<GtfsRoute>()) + "calendar" -> dao.insertServices(elementsList.filterIsInstance<GtfsService>()) + "calendar_dates" -> dao.insertDates(elementsList.filterIsInstance<GtfsServiceDate>()) + "trips" -> dao.insertTrips(elementsList.filterIsInstance<GtfsTrip>()) + "stop_times"-> dao.insertStopTimes(elementsList.filterIsInstance<GtfsStopTime>()) + "shapes" -> dao.insertShapes(elementsList.filterIsInstance<GtfsShape>()) + + } + } + + companion object{ + val MAX_ELEMENTS = 5000 + + val DEBUG_TAG="BusTO - TableInserter" + } +} \ No newline at end of file diff --git a/src/it/reyboz/bustorino/data/gtfs/GtfsDatabase.kt b/src/it/reyboz/bustorino/data/gtfs/GtfsDatabase.kt new file mode 100644 --- /dev/null +++ b/src/it/reyboz/bustorino/data/gtfs/GtfsDatabase.kt @@ -0,0 +1,57 @@ +/* + BusTO - Data 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 <http://www.gnu.org/licenses/>. + */ +package it.reyboz.bustorino.data.gtfs + +import android.content.Context +import androidx.room.* + +@Database( + entities = [ + GtfsServiceDate::class, + GtfsStop::class, + GtfsService::class, + GtfsRoute::class, + GtfsStopTime::class, + GtfsTrip::class, + GtfsShape::class], + version = GtfsDatabase.VERSION, + exportSchema = false, +) +@TypeConverters(Converters::class) +public abstract class GtfsDatabase : RoomDatabase() { + + abstract fun gtfsDao() : StaticGtfsDao + + companion object{ + @Volatile + private var INSTANCE: GtfsDatabase? =null + + fun getGtfsDatabase(context: Context): GtfsDatabase{ + return INSTANCE ?: synchronized(this){ + val instance = Room.databaseBuilder(context.applicationContext, + GtfsDatabase::class.java, + "gtfs_database").build() + INSTANCE = instance + instance + } + } + + const val VERSION = 1 + const val FOREIGNKEY_ONDELETE = ForeignKey.NO_ACTION + } +} \ No newline at end of file diff --git a/src/it/reyboz/bustorino/data/gtfs/GtfsRoute.kt b/src/it/reyboz/bustorino/data/gtfs/GtfsRoute.kt new file mode 100644 --- /dev/null +++ b/src/it/reyboz/bustorino/data/gtfs/GtfsRoute.kt @@ -0,0 +1,79 @@ +/* + BusTO - Data 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 <http://www.gnu.org/licenses/>. + */ +package it.reyboz.bustorino.data.gtfs + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName=GtfsRoute.DB_TABLE) +data class GtfsRoute( + @PrimaryKey @ColumnInfo(name = COL_ROUTE_ID) + val ID: String, + @ColumnInfo(name = "agency_id") + val agencyID: String, + @ColumnInfo(name = "route_short_name") + val shortName: String, + @ColumnInfo(name = "route_long_name") + val longName: String, + @ColumnInfo(name = "route_desc") + val description: String, + @ColumnInfo(name ="route_type") + val type: String, + //@ColumnInfo(name ="route_url") + //val url: String, + @ColumnInfo(name ="route_color") + val color: String, + @ColumnInfo(name ="route_text_color") + val textColor: String, + @ColumnInfo(name = COL_SORT_ORDER) + val sortOrder: Int +): GtfsTable { + + constructor(valuesByColumn: Map<String,String>) : this( + valuesByColumn[COL_ROUTE_ID]!!, + valuesByColumn["agency_id"]!!, + valuesByColumn["route_short_name"]!!, + valuesByColumn["route_long_name"]!!, + valuesByColumn["route_desc"]!!, + valuesByColumn["route_type"]!!, + valuesByColumn["route_color"]!!, + valuesByColumn["route_text_color"]!!, + valuesByColumn[COL_SORT_ORDER]?.toInt()!! + ) + companion object { + const val DB_TABLE: String="routes_table" + const val COL_SORT_ORDER: String="route_sort_order" + const val COL_ROUTE_ID = "route_id" + + val COLUMNS = arrayOf(COL_ROUTE_ID, + "agency_id", + "route_short_name", + "route_long_name", + "route_desc", + "route_type", + "route_color", + "route_text_color", + COL_SORT_ORDER + ) + } + + override fun getColumns(): Array<String> { + return COLUMNS + } +} diff --git a/src/it/reyboz/bustorino/data/gtfs/GtfsService.kt b/src/it/reyboz/bustorino/data/gtfs/GtfsService.kt new file mode 100644 --- /dev/null +++ b/src/it/reyboz/bustorino/data/gtfs/GtfsService.kt @@ -0,0 +1,94 @@ +/* + BusTO - Data 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 <http://www.gnu.org/licenses/>. + */ +package it.reyboz.bustorino.data.gtfs + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.util.Date + +@Entity(tableName = GtfsService.DB_TABLE) +data class GtfsService( + @PrimaryKey + @ColumnInfo(name = COL_SERVICE_ID) + val serviceID: String, + @ColumnInfo(name = COL_MONDAY) + val onMonday: Boolean, + @ColumnInfo(name = COL_TUESDAY) + val onTuesday: Boolean, + @ColumnInfo(name = COL_WEDNESDAY) + val onWednesday: Boolean, + @ColumnInfo(name = COL_THURSDAY) + val onThursday: Boolean, + @ColumnInfo(name = COL_FRIDAY) + val onFriday: Boolean, + @ColumnInfo(name = COL_SATURDAY) + val onSaturday: Boolean, + @ColumnInfo(name = COL_SUNDAY) + val onSunday: Boolean, + @ColumnInfo(name = COL_START_DATE) + val startDate: Date, + @ColumnInfo(name = COL_END_DATE) + val endDate: Date, +): GtfsTable { + + constructor(valuesByColumn: Map<String,String>) : this( + valuesByColumn[COL_SERVICE_ID]!!, + Converters.fromStringNum(valuesByColumn[COL_MONDAY])!!, + Converters.fromStringNum(valuesByColumn[COL_TUESDAY])!!, + Converters.fromStringNum(valuesByColumn[COL_WEDNESDAY])!!, + Converters.fromStringNum(valuesByColumn[COL_THURSDAY])!!, + Converters.fromStringNum(valuesByColumn[COL_FRIDAY])!!, + Converters.fromStringNum(valuesByColumn[COL_SATURDAY])!!, + Converters.fromStringNum(valuesByColumn[COL_SUNDAY])!!, + Converters.dateFromFmtString(valuesByColumn[COL_START_DATE])!!, + Converters.dateFromFmtString(valuesByColumn[COL_END_DATE])!! + ) + companion object{ + const val DB_TABLE="gtfs_calendar" + const val COL_SERVICE_ID="service_id" + const val COL_MONDAY="monday" + const val COL_TUESDAY="tuesday" + const val COL_WEDNESDAY="wednesday" + const val COL_THURSDAY="thursday" + const val COL_FRIDAY="friday" + const val COL_SATURDAY="saturday" + const val COL_SUNDAY="sunday" + const val COL_START_DATE="start_date" + + const val COL_END_DATE="end_date" + + val COLUMNS = arrayOf( + COL_SERVICE_ID, + COL_MONDAY, + COL_TUESDAY, + COL_WEDNESDAY, + COL_THURSDAY, + COL_FRIDAY, + COL_SATURDAY, + COL_SUNDAY, + COL_START_DATE + ) + } + + + + override fun getColumns(): Array<String> { + return COLUMNS + } +} diff --git a/src/it/reyboz/bustorino/data/gtfs/GtfsServiceDate.kt b/src/it/reyboz/bustorino/data/gtfs/GtfsServiceDate.kt new file mode 100644 --- /dev/null +++ b/src/it/reyboz/bustorino/data/gtfs/GtfsServiceDate.kt @@ -0,0 +1,72 @@ +/* + BusTO - Data 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 <http://www.gnu.org/licenses/>. + */ +package it.reyboz.bustorino.data.gtfs + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.ForeignKey +import java.util.Date + + +@Entity( + tableName = GtfsServiceDate.DB_TABLE, + primaryKeys = [GtfsServiceDate.COL_SERVICE_ID, GtfsServiceDate.COL_DATE], + foreignKeys = [ForeignKey(entity = GtfsService::class, + parentColumns = [GtfsService.COL_SERVICE_ID], + childColumns = [GtfsServiceDate.COL_SERVICE_ID], + onDelete = GtfsDatabase.FOREIGNKEY_ONDELETE)] +) +data class GtfsServiceDate( + @ColumnInfo(name= COL_SERVICE_ID) + val serviceID: String, + @ColumnInfo(name=COL_DATE) + val date: Date, + @ColumnInfo(name=COL_EXCEPTION) + val exceptionType: ExceptionType, +): GtfsTable { + companion object{ + const val DB_TABLE="gtfs_calendar_dates" + const val COL_SERVICE_ID="service_id" + const val COL_DATE="date" + const val COL_EXCEPTION="exception_type" + + val COLUMNS = arrayOf(COL_SERVICE_ID, COL_DATE, COL_SERVICE_ID) + + val converter = Converters() + + } + constructor(valuesByColumn: Map<String,String>) : this( + valuesByColumn[COL_SERVICE_ID]!!, + converter.fromString(valuesByColumn[COL_DATE])!!, + valuesByColumn[COL_EXCEPTION]?.let { ExceptionType.getByValue(it.toInt()) }!! + ) + enum class ExceptionType(val value: Int){ + ADDED(1), + REMOVED(2); + + companion object { + private val VALUES = values() + fun getByValue(value: Int) = VALUES.firstOrNull { it.value == value } + } + } + + override fun getColumns(): Array<String> { + return COLUMNS + } +} + diff --git a/src/it/reyboz/bustorino/data/gtfs/GtfsShape.kt b/src/it/reyboz/bustorino/data/gtfs/GtfsShape.kt new file mode 100644 --- /dev/null +++ b/src/it/reyboz/bustorino/data/gtfs/GtfsShape.kt @@ -0,0 +1,61 @@ +/* + BusTO - Data 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 <http://www.gnu.org/licenses/>. + */ +package it.reyboz.bustorino.data.gtfs + +import androidx.room.ColumnInfo +import androidx.room.Entity + +@Entity(tableName = GtfsShape.DB_TABLE, +primaryKeys = [GtfsShape.COL_SHAPE_ID, GtfsShape.COL_POINT_SEQ]) +data class GtfsShape( + @ColumnInfo(name = COL_SHAPE_ID) + val shapeID: String, + @ColumnInfo(name = COL_POINT_LAT) + val pointLat: Double, + @ColumnInfo(name = COL_POINT_LON) + val pointLon: Double, + @ColumnInfo(name = COL_POINT_SEQ) + val pointSequence: Int, +): GtfsTable { + + constructor(valuesByColumn: Map<String,String>) : this( + valuesByColumn[COL_SHAPE_ID]!!, + valuesByColumn[COL_POINT_LAT]?.toDoubleOrNull()!!, + valuesByColumn[COL_POINT_LON]?.toDoubleOrNull()!!, + valuesByColumn[COL_POINT_SEQ]?.toIntOrNull()!! + ) + + companion object{ + const val DB_TABLE="gtfs_shapes" + const val COL_SHAPE_ID = "shape_id" + const val COL_POINT_LAT="shape_pt_lat" + const val COL_POINT_LON="shape_pt_lon" + const val COL_POINT_SEQ="shape_pt_sequence" + + val COLUMNS= arrayOf( + COL_SHAPE_ID, + COL_POINT_LAT, + COL_POINT_LON, + COL_POINT_SEQ + ) + } + + override fun getColumns(): Array<String> { + return COLUMNS + } +} diff --git a/src/it/reyboz/bustorino/data/gtfs/GtfsStop.kt b/src/it/reyboz/bustorino/data/gtfs/GtfsStop.kt new file mode 100644 --- /dev/null +++ b/src/it/reyboz/bustorino/data/gtfs/GtfsStop.kt @@ -0,0 +1,90 @@ +/* + BusTO - Data 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 <http://www.gnu.org/licenses/>. + */ +package it.reyboz.bustorino.data.gtfs + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = GtfsStop.DB_TABLE) +data class GtfsStop( + @PrimaryKey + @ColumnInfo(name= COL_STOP_ID) + val internalID: Int, + @ColumnInfo(name= COL_STOP_CODE) + val gttStopID: String, + @ColumnInfo(name= COL_STOP_NAME) + val stopName: String, + @ColumnInfo(name= COL_GTT_PLACE) + val gttPlaceName: String, + @ColumnInfo(name= COL_LATITUDE) + val latitude: Double, + @ColumnInfo(name= COL_LONGITUDE) + val longitude: Double, + //@ColumnInfo(name="zone_id") + //val zoneID: Int, + @ColumnInfo(name= COL_WHEELCHAIR) + val wheelchair: WheelchairAccess, +): GtfsTable { + + constructor(valuesByColumn: Map<String,String>) : this( + valuesByColumn[COL_STOP_ID]?.toIntOrNull()!!, + valuesByColumn[COL_STOP_CODE]!!, + valuesByColumn[COL_STOP_NAME]!!, + valuesByColumn[COL_GTT_PLACE]!!, + valuesByColumn[COL_LATITUDE]?.toDoubleOrNull()!!, + valuesByColumn[COL_LONGITUDE]?.toDoubleOrNull()!!, + //valuesByColumn["zone_id"]?.toIntOrNull()!!, + Converters.wheelchairFromString(valuesByColumn[COL_WHEELCHAIR])!! + ) + companion object{ + const val DB_TABLE="stops_gtfs" + const val COL_STOP_CODE="stop_code" + const val COL_STOP_ID = "stop_id" + const val COL_GTT_PLACE="stop_desc" + const val COL_STOP_NAME="stop_name" + const val COL_LATITUDE="stop_lat" + const val COL_LONGITUDE="stop_lon" + const val COL_WHEELCHAIR="wheelchair_boarding" + val COLUMNS = arrayOf( + COL_STOP_CODE, + COL_STOP_ID, + COL_GTT_PLACE, + COL_STOP_NAME, + COL_LATITUDE, + COL_LONGITUDE, + //"zone_id", + COL_WHEELCHAIR + ) + } + + override fun getColumns(): Array<String> { + return COLUMNS + } + + enum class WheelchairAccess(val value: Int){ + UNKNOWN(0), + SOMETIMES(1), + IMPOSSIBLE(2); + + companion object { + private val VALUES = values() + fun getByValue(value: Int) = VALUES.firstOrNull { it.value == value } + } + } +} diff --git a/src/it/reyboz/bustorino/data/gtfs/GtfsStopTime.kt b/src/it/reyboz/bustorino/data/gtfs/GtfsStopTime.kt new file mode 100644 --- /dev/null +++ b/src/it/reyboz/bustorino/data/gtfs/GtfsStopTime.kt @@ -0,0 +1,78 @@ +/* + BusTO - Data 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 <http://www.gnu.org/licenses/>. + */ +package it.reyboz.bustorino.data.gtfs + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.Index + +@Entity(tableName = GtfsStopTime.DB_TABLE, + primaryKeys = [GtfsStopTime.COL_TRIP_ID, GtfsStopTime.COL_STOP_ID], + foreignKeys = [ + ForeignKey(entity = GtfsStop::class, + parentColumns = [GtfsStop.COL_STOP_ID], + childColumns = [GtfsStopTime.COL_STOP_ID], + onDelete = GtfsDatabase.FOREIGNKEY_ONDELETE), + ForeignKey(entity = GtfsTrip::class, + parentColumns = [GtfsTrip.COL_TRIP_ID], + childColumns = [GtfsStopTime.COL_TRIP_ID], + onDelete = GtfsDatabase.FOREIGNKEY_ONDELETE), + ], + indices = [Index(GtfsStopTime.COL_STOP_ID)] +) +data class GtfsStopTime( + @ColumnInfo(name= COL_TRIP_ID) + val tripID: String, + @ColumnInfo(name= COL_ARRIVAL_TIME) + val arrivalTime: String, + @ColumnInfo(name= COL_DEPARTURE_TIME) + val departureTime:String, + @ColumnInfo(name= COL_STOP_ID) + val stopID: Int, + @ColumnInfo(name= COL_STOP_SEQUENCE) + val stopSequence: Int, +): GtfsTable { + constructor(valuesByColumn: Map<String,String>) : this( + valuesByColumn[COL_TRIP_ID]!!, + valuesByColumn[COL_ARRIVAL_TIME]!!, + valuesByColumn[COL_DEPARTURE_TIME]!!, + valuesByColumn[COL_STOP_ID]?.toIntOrNull()!!, + valuesByColumn[COL_STOP_SEQUENCE]?.toIntOrNull()!! + ) + companion object{ + const val DB_TABLE="gtfs_stop_times" + const val COL_TRIP_ID="trip_id" + const val COL_ARRIVAL_TIME="arrival_time" + const val COL_DEPARTURE_TIME="departure_time" + const val COL_STOP_ID="stop_id" + const val COL_STOP_SEQUENCE="stop_sequence" + + val COLUMNS = arrayOf( + COL_TRIP_ID, + COL_ARRIVAL_TIME, + COL_DEPARTURE_TIME, + COL_STOP_ID, + COL_STOP_SEQUENCE + ) + } + + override fun getColumns(): Array<String> { + return COLUMNS + } +} diff --git a/src/it/reyboz/bustorino/data/gtfs/GtfsTable.java b/src/it/reyboz/bustorino/data/gtfs/GtfsTable.java new file mode 100644 --- /dev/null +++ b/src/it/reyboz/bustorino/data/gtfs/GtfsTable.java @@ -0,0 +1,24 @@ +/* + BusTO - Data 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 <http://www.gnu.org/licenses/>. + */ +package it.reyboz.bustorino.data.gtfs; + +public interface GtfsTable { + + String[] getColumns(); + +} diff --git a/src/it/reyboz/bustorino/data/gtfs/GtfsTrip.kt b/src/it/reyboz/bustorino/data/gtfs/GtfsTrip.kt new file mode 100644 --- /dev/null +++ b/src/it/reyboz/bustorino/data/gtfs/GtfsTrip.kt @@ -0,0 +1,107 @@ +/* + BusTO - Data 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 <http://www.gnu.org/licenses/>. + */ +package it.reyboz.bustorino.data.gtfs + +import androidx.room.* + +@Entity(tableName = GtfsTrip.DB_TABLE, + foreignKeys=[ + ForeignKey(entity = GtfsRoute::class, + parentColumns = [GtfsRoute.COL_ROUTE_ID], + childColumns = [GtfsTrip.COL_ROUTE_ID], + onDelete = GtfsDatabase.FOREIGNKEY_ONDELETE), + // The service_id: ID referencing calendar.service_id or calendar_dates.service_id + /* + ForeignKey(entity = GtfsService::class, + parentColumns = [GtfsService.COL_SERVICE_ID], + childColumns = [GtfsTrips.COL_SERVICE_ID], + onDelete = GtfsDatabase.FOREIGNKEY_ONDELETE), + */ + ], + indices = [Index(GtfsTrip.COL_ROUTE_ID)] +) +data class GtfsTrip( + @ColumnInfo(name = COL_ROUTE_ID ) + val routeID: String, + @ColumnInfo(name = COL_SERVICE_ID) + val serviceID: String, + @PrimaryKey + @ColumnInfo(name = COL_TRIP_ID) + val tripID: String, + @ColumnInfo(name = COL_HEADSIGN) + val tripHeadsign: String, + @ColumnInfo(name = COL_DIRECTION_ID) + val directionID: Int, + @ColumnInfo(name = COL_BLOCK_ID) + val blockID: String, + @ColumnInfo(name = COL_SHAPE_ID) + val shapeID: String, + @ColumnInfo(name = COL_WHEELCHAIR) + val isWheelchairAccess: Boolean, + @ColumnInfo(name = COL_LIMITED_R) + val isLimitedRoute: Boolean, + +): GtfsTable { + + constructor(valuesByColumn: Map<String,String>) : this( + valuesByColumn[COL_ROUTE_ID]!!, + valuesByColumn[COL_SERVICE_ID]!!, + valuesByColumn[COL_TRIP_ID]!!, + valuesByColumn[COL_HEADSIGN]!!, + valuesByColumn[COL_DIRECTION_ID]?.toIntOrNull()?: 0, + valuesByColumn[COL_BLOCK_ID]!!, + valuesByColumn[COL_SHAPE_ID]!!, + Converters.fromStringNum(valuesByColumn[COL_WHEELCHAIR], false), + Converters.fromStringNum(valuesByColumn[COL_LIMITED_R], false) + ) + + companion object{ + const val DB_TABLE="gtfs_trips" + const val COL_ROUTE_ID="route_id" + const val COL_SERVICE_ID="service_id" + const val COL_TRIP_ID = "trip_id" + const val COL_HEADSIGN="trip_headsign" + //const val COL_SHORT_NAME="trip_short_name", + const val COL_DIRECTION_ID="direction_id" + const val COL_BLOCK_ID="block_id" + const val COL_SHAPE_ID = "shape_id" + const val COL_WHEELCHAIR="wheelchair_accessible" + const val COL_LIMITED_R="limited_route" + + val COLUMNS= arrayOf( + COL_ROUTE_ID, + COL_SERVICE_ID, + COL_TRIP_ID, + COL_HEADSIGN, + COL_DIRECTION_ID, + COL_BLOCK_ID, + COL_SHAPE_ID, + COL_WHEELCHAIR, + COL_LIMITED_R + ) + /* + open fun fromContentValues(values: ContentValues) { + val tripItem = GtfsTrips(); + } + */ + } + + override fun getColumns(): Array<String> { + return COLUMNS + } +} \ No newline at end of file diff --git a/src/it/reyboz/bustorino/data/gtfs/StaticGtfsDao.kt b/src/it/reyboz/bustorino/data/gtfs/StaticGtfsDao.kt new file mode 100644 --- /dev/null +++ b/src/it/reyboz/bustorino/data/gtfs/StaticGtfsDao.kt @@ -0,0 +1,77 @@ +/* + BusTO - Data 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 <http://www.gnu.org/licenses/>. + */ +package it.reyboz.bustorino.data.gtfs + +import androidx.lifecycle.LiveData +import androidx.room.* + +@Dao +interface StaticGtfsDao { + @Query("SELECT * FROM "+GtfsRoute.DB_TABLE+" ORDER BY "+GtfsRoute.COL_SORT_ORDER) + fun getAllRoutes() : LiveData<List<GtfsRoute>> + + @Query("SELECT * FROM "+GtfsStop.DB_TABLE+" WHERE "+GtfsStop.COL_STOP_CODE+" LIKE :queryID") + fun getStopByStopID(queryID: String): LiveData<List<GtfsStop>> + + @Query("SELECT * FROM "+GtfsShape.DB_TABLE+ + " WHERE "+GtfsShape.COL_SHAPE_ID+" LIKE :shapeID"+ + " ORDER BY "+GtfsShape.COL_POINT_SEQ+ " ASC" + ) + fun getShapeByID(shapeID: String) : LiveData<List<GtfsShape>> + + @Transaction + fun clearAndInsertRoutes(routes: List<GtfsRoute>){ + deleteAllRoutes() + insertRoutes(routes) + } + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertRoutes(users: List<GtfsRoute>) + @Insert + fun insertStops(stops: List<GtfsStop>) + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertCalendarServices(services: List<GtfsService>) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertShapes(shapes: List<GtfsShape>) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertDates(dates: List<GtfsServiceDate>) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertServices(services: List<GtfsService>) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertTrips(trips: List<GtfsTrip>) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertStopTimes(stopTimes: List<GtfsStopTime>) + + @Query("DELETE FROM "+GtfsRoute.DB_TABLE) + fun deleteAllRoutes() + @Query("DELETE FROM "+GtfsStop.DB_TABLE) + fun deleteAllStops() + @Update(onConflict = OnConflictStrategy.REPLACE) + fun updateShapes(shapes: List<GtfsShape>) : Int + + @Transaction + fun updateStops(stops: List<GtfsStop>){ + deleteAllStops() + insertStops(stops) + } +} \ No newline at end of file diff --git a/src/it/reyboz/bustorino/fragments/FavoritesFragment.java b/src/it/reyboz/bustorino/fragments/FavoritesFragment.java --- a/src/it/reyboz/bustorino/fragments/FavoritesFragment.java +++ b/src/it/reyboz/bustorino/fragments/FavoritesFragment.java @@ -1,3 +1,20 @@ +/* + BusTO - Fragments 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 <http://www.gnu.org/licenses/>. + */ package it.reyboz.bustorino.fragments; import android.app.AlertDialog; diff --git a/src/it/reyboz/bustorino/fragments/MapFragment.java b/src/it/reyboz/bustorino/fragments/MapFragment.java --- a/src/it/reyboz/bustorino/fragments/MapFragment.java +++ b/src/it/reyboz/bustorino/fragments/MapFragment.java @@ -1,3 +1,21 @@ +/* + BusTO - Fragments components + Copyright (C) 2020 Andrea Ugo + 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 <http://www.gnu.org/licenses/>. + */ package it.reyboz.bustorino.fragments; import android.Manifest; diff --git a/src/it/reyboz/bustorino/fragments/NearbyStopsFragment.java b/src/it/reyboz/bustorino/fragments/NearbyStopsFragment.java --- a/src/it/reyboz/bustorino/fragments/NearbyStopsFragment.java +++ b/src/it/reyboz/bustorino/fragments/NearbyStopsFragment.java @@ -328,15 +328,16 @@ @Override - public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor data) { + public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor cursor) { if (0 > MAX_DISTANCE) throw new AssertionError(); //Cursor might be null - Log.d(DEBUG_TAG, "Num stops found: "+data.getCount()+", Current distance: "+distance); - if(data==null){ + if(cursor==null){ Log.e(DEBUG_TAG,"Null cursor, something really wrong happened"); return; } - if(!isDBUpdating() && (data.getCount()<MIN_NUM_STOPS && distance<=MAX_DISTANCE)){ + Log.d(DEBUG_TAG, "Num stops found: "+cursor.getCount()+", Current distance: "+distance); + + if(!isDBUpdating() && (cursor.getCount()<MIN_NUM_STOPS && distance<=MAX_DISTANCE)){ distance = distance*2; Bundle d = new Bundle(); d.putParcelable(BUNDLE_LOCATION,lastReceivedLocation); @@ -344,11 +345,11 @@ //Log.d(DEBUG_TAG, "Doubling distance now!"); return; } - Log.d("LoadFromCursor","Number of nearby stops: "+data.getCount()); + Log.d("LoadFromCursor","Number of nearby stops: "+cursor.getCount()); //////// - if(data.getCount()>0) { - ArrayList<Stop> stopList = createStopListFromCursor(data); + if(cursor.getCount()>0) { + ArrayList<Stop> stopList = createStopListFromCursor(cursor); double minDistance = Double.POSITIVE_INFINITY; for(Stop s: stopList){ minDistance = Math.min(minDistance, s.getDistanceFromLocation(lastReceivedLocation)); diff --git a/src/it/reyboz/bustorino/fragments/SettingsFragment.java b/src/it/reyboz/bustorino/fragments/SettingsFragment.java --- a/src/it/reyboz/bustorino/fragments/SettingsFragment.java +++ b/src/it/reyboz/bustorino/fragments/SettingsFragment.java @@ -1,3 +1,20 @@ +/* + BusTO - Fragments components + Copyright (C) 2020 Fabio Mazza + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ package it.reyboz.bustorino.fragments; import android.content.Context; diff --git a/src/it/reyboz/bustorino/map/CustomInfoWindow.java b/src/it/reyboz/bustorino/map/CustomInfoWindow.java --- a/src/it/reyboz/bustorino/map/CustomInfoWindow.java +++ b/src/it/reyboz/bustorino/map/CustomInfoWindow.java @@ -1,3 +1,21 @@ +/* + BusTO - Map components + Copyright (C) 2020 Andrea Ugo + 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 <http://www.gnu.org/licenses/>. + */ package it.reyboz.bustorino.map; import android.annotation.SuppressLint; diff --git a/src/it/reyboz/bustorino/map/LocationOverlay.java b/src/it/reyboz/bustorino/map/LocationOverlay.java --- a/src/it/reyboz/bustorino/map/LocationOverlay.java +++ b/src/it/reyboz/bustorino/map/LocationOverlay.java @@ -1,5 +1,5 @@ /* - BusTO (middleware) + BusTO - Map components Copyright (C) 2021 Fabio Mazza This program is free software: you can redistribute it and/or modify diff --git a/src/it/reyboz/bustorino/middleware/GeneralActivity.java b/src/it/reyboz/bustorino/middleware/GeneralActivity.java --- a/src/it/reyboz/bustorino/middleware/GeneralActivity.java +++ b/src/it/reyboz/bustorino/middleware/GeneralActivity.java @@ -25,6 +25,7 @@ import it.reyboz.bustorino.R; import it.reyboz.bustorino.backend.utils; +import it.reyboz.bustorino.data.PreferencesHolder; /** * Activity class that contains all the generally useful methods @@ -58,7 +59,7 @@ } protected SharedPreferences getMainSharedPreferences(){ - return getSharedPreferences(getString(R.string.mainSharedPreferences),MODE_PRIVATE); + return PreferencesHolder.getMainSharedPreferences(this); } public void hideKeyboard() { View view = getCurrentFocus();