diff --git a/build.gradle b/build.gradle
index beae0ac..7e03257 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,175 +1,175 @@
buildscript {
repositories {
jcenter()
maven { url 'https://maven.google.com' }
google()
}
ext {
androidXTestVersion = "1.4.0"
//multidex
multidex_version = "2.0.1"
//libraries versions
fragment_version = "1.4.1"
activity_version = "1.4.0"
appcompat_version = "1.4.1"
preference_version = "1.2.0"
work_version = "2.7.1"
acra_version = "5.7.0"
lifecycle_version = "2.4.1"
arch_version = "2.1.0"
room_version = "2.4.1"
//kotlin
kotlin_version = '1.6.0'
coroutines_version = "1.5.0"
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
jcenter()
maven { url 'https://maven.google.com' }
google()
mavenCentral()
}
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 31
buildToolsVersion '30.0.3'
defaultConfig {
applicationId "it.reyboz.bustorino"
minSdkVersion 16
targetSdkVersion 31
- versionCode 40
- versionName "1.17"
+ versionCode 41
+ versionName "1.17.1"
vectorDrawables.useSupportLibrary = true
multiDexEnabled true
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/assets/schemas/".toString()]
}
}
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
sourceSets {
androidTest.assets.srcDirs += files("$projectDir/assets/schemas/".toString())
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
}
}
buildTypes {
debug {
applicationIdSuffix ".debug"
versionNameSuffix "-dev"
}
}
lintOptions {
abortOnError false
}
repositories {
jcenter()
mavenLocal()
}
dependencies {
//new libraries
implementation "androidx.fragment:fragment-ktx:$fragment_version"
implementation "androidx.activity:activity:$activity_version"
implementation "androidx.annotation:annotation:1.3.0"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation "androidx.appcompat:appcompat:$appcompat_version"
implementation "androidx.appcompat:appcompat-resources:$appcompat_version"
implementation "androidx.preference:preference:$preference_version"
implementation "androidx.work:work-runtime:$work_version"
implementation "com.google.android.material:material:1.5.0"
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
implementation 'org.jsoup:jsoup:1.13.1'
implementation 'com.readystatesoftware.sqliteasset:sqliteassethelper:2.0.1'
implementation 'com.android.volley:volley:1.2.1'
implementation 'org.osmdroid:osmdroid-android:6.1.10'
// ACRA
implementation "ch.acra:acra-mail:$acra_version"
implementation "ch.acra:acra-dialog:$acra_version"
// google transit realtime
implementation 'com.google.protobuf:protobuf-java:3.14.0'
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
// Lifecycles only (without ViewModel or LiveData)
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
// Legacy
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
// Room components
implementation "androidx.room:room-ktx:$room_version"
kapt "androidx.room:room-compiler:$room_version"
//multidex - we need this to build the app
implementation "androidx.multidex:multidex:$multidex_version"
implementation 'de.siegmar:fastcsv:2.0.0'
testImplementation 'junit:junit:4.12'
implementation 'junit:junit:4.12'
implementation "androidx.test.ext:junit:1.1.3"
implementation "androidx.test:core:$androidXTestVersion"
implementation "androidx.test:runner:$androidXTestVersion"
implementation "androidx.room:room-testing:$room_version"
androidTestImplementation "androidx.test.ext:junit:1.1.3"
androidTestImplementation "androidx.test:core:$androidXTestVersion"
androidTestImplementation "androidx.test:runner:$androidXTestVersion"
androidTestImplementation "androidx.test:rules:$androidXTestVersion"
androidTestImplementation "androidx.room:room-testing:$room_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"
}
diff --git a/metadata/en-US/changelogs/41.txt b/metadata/en-US/changelogs/41.txt
new file mode 100644
index 0000000..4b8356a
--- /dev/null
+++ b/metadata/en-US/changelogs/41.txt
@@ -0,0 +1 @@
+* fix database update
diff --git a/metadata/it/changelogs/41.txt b/metadata/it/changelogs/41.txt
new file mode 100644
index 0000000..65d7312
--- /dev/null
+++ b/metadata/it/changelogs/41.txt
@@ -0,0 +1 @@
+* fix aggiornamento database
diff --git a/src/it/reyboz/bustorino/ActivityPrincipal.java b/src/it/reyboz/bustorino/ActivityPrincipal.java
index 32f12d3..caebbc3 100644
--- a/src/it/reyboz/bustorino/ActivityPrincipal.java
+++ b/src/it/reyboz/bustorino/ActivityPrincipal.java
@@ -1,626 +1,628 @@
/*
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 .
*/
package it.reyboz.bustorino;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.widget.Toolbar;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.preference.PreferenceManager;
import androidx.work.WorkInfo;
import androidx.work.WorkManager;
import com.google.android.material.navigation.NavigationView;
import com.google.android.material.snackbar.Snackbar;
import java.util.Arrays;
import it.reyboz.bustorino.backend.Stop;
import it.reyboz.bustorino.data.DBUpdateWorker;
import it.reyboz.bustorino.data.DatabaseUpdate;
import it.reyboz.bustorino.data.PreferencesHolder;
import it.reyboz.bustorino.data.gtfs.GtfsDatabase;
import it.reyboz.bustorino.fragments.*;
import it.reyboz.bustorino.middleware.GeneralActivity;
import static it.reyboz.bustorino.backend.utils.getBusStopIDFromUri;
import static it.reyboz.bustorino.backend.utils.openIceweasel;
public class ActivityPrincipal extends GeneralActivity implements FragmentListenerMain {
private DrawerLayout mDrawer;
private NavigationView mNavView;
private ActionBarDrawerToggle drawerToggle;
private final static String DEBUG_TAG="BusTO Act Principal";
private final static String TAG_FAVORITES="favorites_frag";
private Snackbar snackbar;
private boolean showingMainFragmentFromOther = false;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_principal);
final SharedPreferences theShPr = getMainSharedPreferences();
//database check
GtfsDatabase gtfsDB = GtfsDatabase.Companion.getGtfsDatabase(this);
final int db_version = gtfsDB.getOpenHelper().getReadableDatabase().getVersion();
boolean dataUpdateRequested = false;
final int old_version = PreferencesHolder.getGtfsDBVersion(theShPr);
Log.d(DEBUG_TAG, "GTFS Database: old version is "+old_version+ ", new version is "+db_version);
if (old_version < db_version){
- //request db update
- dataUpdateRequested = true;
- DatabaseUpdate.requestDBUpdateWithWork(this, true, true);
+ //decide update conditions in the future
+ if(old_version < 2 && db_version >= 2) {
+ dataUpdateRequested = true;
+ DatabaseUpdate.requestDBUpdateWithWork(this, true, true);
+ }
PreferencesHolder.setGtfsDBVersion(theShPr, db_version);
}
Toolbar mToolbar = findViewById(R.id.default_toolbar);
setSupportActionBar(mToolbar);
if (getSupportActionBar()!=null)
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
else Log.w(DEBUG_TAG, "NO ACTION BAR");
mToolbar.setOnMenuItemClickListener(new ToolbarItemClickListener(this));
mDrawer = findViewById(R.id.drawer_layout);
drawerToggle = setupDrawerToggle(mToolbar);
// Setup toggle to display hamburger icon with nice animation
drawerToggle.setDrawerIndicatorEnabled(true);
drawerToggle.syncState();
mDrawer.addDrawerListener(drawerToggle);
mDrawer.addDrawerListener(new DrawerLayout.DrawerListener() {
@Override
public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {
}
@Override
public void onDrawerOpened(@NonNull View drawerView) {
hideKeyboard();
}
@Override
public void onDrawerClosed(@NonNull View drawerView) {
}
@Override
public void onDrawerStateChanged(int newState) {
}
});
mNavView = findViewById(R.id.nvView);
setupDrawerContent(mNavView);
/*View header = mNavView.getHeaderView(0);
*/
//mNavView.getMenu().findItem(R.id.versionFooter).
/// LEGACY CODE
//---------------------------- START INTENT CHECK QUEUE ------------------------------------
// Intercept calls from URL intent
boolean tryedFromIntent = false;
String busStopID = null;
Uri data = getIntent().getData();
if (data != null) {
busStopID = getBusStopIDFromUri(data);
tryedFromIntent = true;
}
// Intercept calls from other activities
if (!tryedFromIntent) {
Bundle b = getIntent().getExtras();
if (b != null) {
busStopID = b.getString("bus-stop-ID");
/*
* I'm not very sure if you are coming from an Intent.
* Some launchers work in strange ways.
*/
tryedFromIntent = busStopID != null;
}
}
//---------------------------- END INTENT CHECK QUEUE --------------------------------------
if (busStopID == null) {
// Show keyboard if can't start from intent
// JUST DON'T
// showKeyboard();
// You haven't obtained anything... from an intent?
if (tryedFromIntent) {
// This shows a luser warning
Toast.makeText(getApplicationContext(),
R.string.insert_bus_stop_number_error, Toast.LENGTH_SHORT).show();
}
} else {
// If you are here an intent has worked successfully
//setBusStopSearchByIDEditText(busStopID);
requestArrivalsForStopID(busStopID);
}
//Try (hopefully) database update
if(!dataUpdateRequested)
DatabaseUpdate.requestDBUpdateWithWork(this, false, false);
/*
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
if (workInfoList == null || workInfoList.isEmpty()) {
return;
}
Log.d(DEBUG_TAG, "WorkerInfo: "+workInfoList);
boolean showProgress = false;
for (WorkInfo workInfo : workInfoList) {
if (workInfo.getState() == WorkInfo.State.RUNNING) {
showProgress = true;
break;
}
}
if (showProgress) {
createDefaultSnackbar();
} else {
if(snackbar!=null) {
snackbar.dismiss();
snackbar = null;
}
}
});
// show the main fragment
showMainFragment();
}
private ActionBarDrawerToggle setupDrawerToggle(Toolbar toolbar) {
// NOTE: Make sure you pass in a valid toolbar reference. ActionBarDrawToggle() does not require it
// and will not render the hamburger icon without it.
return new ActionBarDrawerToggle(this, mDrawer, toolbar, R.string.drawer_open, R.string.drawer_close);
}
/**
* Setup drawer actions
* @param navigationView the navigation view on which to set the callbacks
*/
private void setupDrawerContent(NavigationView navigationView) {
navigationView.setNavigationItemSelectedListener(
menuItem -> {
if (menuItem.getItemId() == R.id.drawer_action_settings) {
Log.d("MAINBusTO", "Pressed button preferences");
closeDrawerIfOpen();
startActivity(new Intent(ActivityPrincipal.this, ActivitySettings.class));
return true;
} else if(menuItem.getItemId() == R.id.nav_favorites_item){
closeDrawerIfOpen();
//get Fragment
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
FavoritesFragment fragment = FavoritesFragment.newInstance();
ft.replace(R.id.mainActContentFrame,fragment, TAG_FAVORITES)
.addToBackStack("main");
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
return true;
} else if(menuItem.getItemId() == R.id.nav_arrivals){
closeDrawerIfOpen();
showMainFragment();
return true;
} else if(menuItem.getItemId() == R.id.nav_map_item){
closeDrawerIfOpen();
final String permission = Manifest.permission.WRITE_EXTERNAL_STORAGE;
int result = askForPermissionIfNeeded(permission, STORAGE_PERMISSION_REQ);
switch (result) {
case PERMISSION_OK:
createAndShowMapFragment(null);
break;
case PERMISSION_ASKING:
permissionDoneRunnables.put(permission,
() -> createAndShowMapFragment(null));
break;
case PERMISSION_NEG_CANNOT_ASK:
String storage_perm = getString(R.string.storage_permission);
String text = getString(R.string.too_many_permission_asks, storage_perm);
Toast.makeText(getApplicationContext(),text, Toast.LENGTH_LONG).show();
}
return true;
} else if (menuItem.getItemId() == R.id.nav_lines_item) {
closeDrawerIfOpen();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
Fragment f = getSupportFragmentManager().findFragmentByTag(LinesFragment.FRAGMENT_TAG);
if(f!=null){
ft.replace(R.id.mainActContentFrame, f, LinesFragment.FRAGMENT_TAG);
}else{
//use new method
ft.replace(R.id.mainActContentFrame,LinesFragment.class,null,LinesFragment.FRAGMENT_TAG);
}
ft.setReorderingAllowed(true)
.addToBackStack("lines")
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.commit();
return true;
}
//selectDrawerItem(menuItem);
Log.d(DEBUG_TAG, "pressed item "+menuItem);
return true;
});
}
private void closeDrawerIfOpen(){
if (mDrawer.isDrawerOpen(GravityCompat.START))
mDrawer.closeDrawer(GravityCompat.START);
}
// `onPostCreate` called when activity start-up is complete after `onStart()`
// NOTE 1: Make sure to override the method with only a single `Bundle` argument
// Note 2: Make sure you implement the correct `onPostCreate(Bundle savedInstanceState)` method.
// There are 2 signatures and only `onPostCreate(Bundle state)` shows the hamburger icon.
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
drawerToggle.syncState();
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Pass any configuration change to the drawer toggles
drawerToggle.onConfigurationChanged(newConfig);
}
@Override
public boolean onCreateOptionsMenu(Menu 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);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode==STORAGE_PERMISSION_REQ){
final String storagePerm = Manifest.permission.WRITE_EXTERNAL_STORAGE;
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(DEBUG_TAG, "Permissions check: " + Arrays.toString(permissions));
if (permissionDoneRunnables.containsKey(storagePerm)) {
Runnable toRun = permissionDoneRunnables.get(storagePerm);
if (toRun != null)
toRun.run();
permissionDoneRunnables.remove(storagePerm);
}
} else {
//permission denied
showToastMessage(R.string.permission_storage_maps_msg, false);
}
}
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
int[] cases = {R.id.nav_arrivals, R.id.nav_favorites_item};
Log.d(DEBUG_TAG, "Item pressed");
if (item.getItemId() == android.R.id.home) {
mDrawer.openDrawer(GravityCompat.START);
return true;
}
if (drawerToggle.onOptionsItemSelected(item)) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onBackPressed() {
boolean foundFragment = false;
Fragment shownFrag = getSupportFragmentManager().findFragmentById(R.id.mainActContentFrame);
if (mDrawer.isDrawerOpen(GravityCompat.START))
mDrawer.closeDrawer(GravityCompat.START);
else if(shownFrag != null && shownFrag.isVisible() && shownFrag.getChildFragmentManager().getBackStackEntryCount() > 0){
//if we have been asked to show a stop from another fragment, we should go back even in the main
if(shownFrag instanceof MainScreenFragment){
//we have to stop the arrivals reload
((MainScreenFragment) shownFrag).cancelReloadArrivalsIfNeeded();
}
shownFrag.getChildFragmentManager().popBackStackImmediate();
if(showingMainFragmentFromOther && getSupportFragmentManager().getBackStackEntryCount() > 0){
getSupportFragmentManager().popBackStack();
}
}
else if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStack();
}
else
super.onBackPressed();
}
/**
* Create and show the SnackBar with the message
*/
private void createDefaultSnackbar() {
View baseView = null;
final Fragment frag = getSupportFragmentManager().findFragmentById(R.id.mainActContentFrame);
if (frag instanceof ScreenBaseFragment){
baseView = ((ScreenBaseFragment) frag).getBaseViewForSnackBar();
}
if (baseView == null) baseView = findViewById(R.id.mainActContentFrame);
if (baseView == null) Log.e(DEBUG_TAG, "baseView null for default snackbar, probably exploding now");
snackbar = Snackbar.make(baseView, R.string.database_update_msg_inapp, Snackbar.LENGTH_INDEFINITE);
snackbar.show();
}
private MainScreenFragment createAndShowMainFragment(){
FragmentManager fraMan = getSupportFragmentManager();
MainScreenFragment fragment = MainScreenFragment.newInstance();
FragmentTransaction transaction = fraMan.beginTransaction();
transaction.replace(R.id.mainActContentFrame, fragment, MainScreenFragment.FRAGMENT_TAG);
transaction.commit();
return fragment;
}
/**
* Show the fragment by adding it to the backstack
* @param fraMan the fragmentManager
* @param fragment the fragment
*/
private static void showMainFragment(FragmentManager fraMan, MainScreenFragment fragment){
fraMan.beginTransaction().replace(R.id.mainActContentFrame, fragment)
.setReorderingAllowed(true)
.addToBackStack(null)
/*.setCustomAnimations(
R.anim.slide_in, // enter
R.anim.fade_out, // exit
R.anim.fade_in, // popEnter
R.anim.slide_out // popExit
)*/
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.commit();
}
private MainScreenFragment showMainFragment(){
FragmentManager fraMan = getSupportFragmentManager();
Fragment fragment = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG);
final MainScreenFragment mainScreenFragment;
if (fragment==null | !(fragment instanceof MainScreenFragment)){
mainScreenFragment = createAndShowMainFragment();
}
else if(!fragment.isVisible()){
mainScreenFragment = (MainScreenFragment) fragment;
showMainFragment(fraMan, mainScreenFragment);
Log.d(DEBUG_TAG, "Found the main fragment");
} else{
mainScreenFragment = (MainScreenFragment) fragment;
}
return mainScreenFragment;
}
@Nullable
private MainScreenFragment getMainFragmentIfVisible(){
FragmentManager fraMan = getSupportFragmentManager();
Fragment fragment = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG);
if (fragment!= null && fragment.isVisible()) return (MainScreenFragment) fragment;
else return null;
}
@Override
public void showFloatingActionButton(boolean yes) {
//TODO
}
/*
public void setDrawerSelectedItem(String fragmentTag){
switch (fragmentTag){
case MainScreenFragment.FRAGMENT_TAG:
mNavView.setCheckedItem(R.id.nav_arrivals);
break;
case MapFragment.FRAGMENT_TAG:
break;
case FavoritesFragment.FRAGMENT_TAG:
mNavView.setCheckedItem(R.id.nav_favorites_item);
break;
}
}*/
@Override
public void readyGUIfor(FragmentKind fragmentType) {
MainScreenFragment probableFragment = getMainFragmentIfVisible();
if (probableFragment!=null){
probableFragment.readyGUIfor(fragmentType);
}
int titleResId;
switch (fragmentType){
case MAP:
mNavView.setCheckedItem(R.id.nav_map_item);
titleResId = R.string.map;
break;
case FAVORITES:
mNavView.setCheckedItem(R.id.nav_favorites_item);
titleResId = R.string.nav_favorites_text;
break;
case ARRIVALS:
titleResId = R.string.nav_arrivals_text;
mNavView.setCheckedItem(R.id.nav_arrivals);
break;
case STOPS:
titleResId = R.string.stop_search_view_title;
mNavView.setCheckedItem(R.id.nav_arrivals);
break;
case MAIN_SCREEN_FRAGMENT:
case NEARBY_STOPS:
case NEARBY_ARRIVALS:
titleResId=R.string.app_name_full;
mNavView.setCheckedItem(R.id.nav_arrivals);
break;
case LINES:
titleResId=R.string.lines;
mNavView.setCheckedItem(R.id.nav_lines_item);
break;
default:
titleResId = 0;
}
if(getSupportActionBar()!=null && titleResId!=0)
getSupportActionBar().setTitle(titleResId);
}
@Override
public void requestArrivalsForStopID(String ID) {
//register if the request came from the main fragment or not
MainScreenFragment probableFragment = getMainFragmentIfVisible();
showingMainFragmentFromOther = (probableFragment==null);
if (showingMainFragmentFromOther){
FragmentManager fraMan = getSupportFragmentManager();
Fragment fragment = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG);
if(fragment!=null){
//the fragment is there but not shown
probableFragment = (MainScreenFragment) fragment;
// set the flag
probableFragment.setSuppressArrivalsReload(true);
showMainFragment(fraMan, probableFragment);
} else {
// we have no fragment
probableFragment = createAndShowMainFragment();
}
}
probableFragment.requestArrivalsForStopID(ID);
mNavView.setCheckedItem(R.id.nav_arrivals);
}
@Override
public void toggleSpinner(boolean state) {
MainScreenFragment probableFragment = getMainFragmentIfVisible();
if (probableFragment!=null){
probableFragment.toggleSpinner(state);
}
}
@Override
public void enableRefreshLayout(boolean yes) {
MainScreenFragment probableFragment = getMainFragmentIfVisible();
if (probableFragment!=null){
probableFragment.enableRefreshLayout(yes);
}
}
@Override
public void showMapCenteredOnStop(Stop stop) {
createAndShowMapFragment(stop);
}
//Map Fragment stuff
void createAndShowMapFragment(@Nullable Stop stop){
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
MapFragment fragment = stop == null? MapFragment.getInstance(): MapFragment.getInstance(stop);
ft.replace(R.id.mainActContentFrame, fragment, MapFragment.FRAGMENT_TAG);
ft.addToBackStack(null);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
}
class ToolbarItemClickListener implements Toolbar.OnMenuItemClickListener{
private final Context activityContext;
public ToolbarItemClickListener(Context activityContext) {
this.activityContext = activityContext;
}
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_about:
startActivity(new Intent(ActivityPrincipal.this, ActivityAbout.class));
return true;
case R.id.action_hack:
openIceweasel(getString(R.string.hack_url), activityContext);
return true;
case R.id.action_source:
openIceweasel("https://gitpull.it/source/libre-busto/", activityContext);
return true;
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/backend/Result.java b/src/it/reyboz/bustorino/backend/Result.java
index 273127b..c420425 100644
--- a/src/it/reyboz/bustorino/backend/Result.java
+++ b/src/it/reyboz/bustorino/backend/Result.java
@@ -1,38 +1,42 @@
package it.reyboz.bustorino.backend;
import androidx.annotation.Nullable;
public class Result {
@Nullable
public final T result;
@Nullable
public final Exception exception;
public boolean isSuccess() {
return exception == null;
}
public static Result success(@Nullable T result) {
return new Result<>(result);
}
/**
* Returns a failed response
*/
public static Result failure(Exception error) {
return new Result<>(error);
}
private Result(@Nullable T result) {
this.result = result;
this.exception = null;
}
private Result(Exception error) {
this.result = null;
this.exception = error;
}
+
+ public interface Callback{
+ void onComplete(Result result);
+ }
}
diff --git a/src/it/reyboz/bustorino/middleware/AppLocationManager.java b/src/it/reyboz/bustorino/middleware/AppLocationManager.java
index 9c5fc07..93264f6 100644
--- a/src/it/reyboz/bustorino/middleware/AppLocationManager.java
+++ b/src/it/reyboz/bustorino/middleware/AppLocationManager.java
@@ -1,269 +1,274 @@
/*
BusTO (middleware)
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.middleware;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import androidx.core.content.ContextCompat;
+import androidx.core.location.LocationManagerCompat;
+import androidx.core.location.LocationRequestCompat;
import it.reyboz.bustorino.util.LocationCriteria;
import it.reyboz.bustorino.util.Permissions;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.ListIterator;
/**
* Singleton class used to access location. Possibly extended with other location sources.
*/
public class AppLocationManager implements LocationListener {
public static final int LOCATION_GPS_AVAILABLE = 22;
public static final int LOCATION_UNAVAILABLE = -22;
private final Context appContext;
private final LocationManager locMan;
public static final String DEBUG_TAG = "BUSTO LocAdapter";
private final String BUNDLE_LOCATION = "location";
private static AppLocationManager instance;
private int oldGPSLocStatus = LOCATION_UNAVAILABLE;
private int minimum_time_milli = -1;
- private boolean isLocationPermissionGiven = false;
-
private final ArrayList> requestersRef = new ArrayList<>();
private AppLocationManager(Context context) {
this.appContext = context.getApplicationContext();
locMan = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
- isLocationPermissionGiven = checkLocationPermission(context);
+ boolean isLocationPermissionGiven = checkLocationPermission(context);
}
public static AppLocationManager getInstance(Context con) {
if(instance==null) instance = new AppLocationManager(con);
return instance;
}
public static boolean checkLocationPermission(Context context){
return ContextCompat.checkSelfPermission(context,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED;
}
private void requestGPSPositionUpdates() throws SecurityException{
final int timeinterval = (minimum_time_milli>0 && minimum_time_milli> iter = requestersRef.listIterator();
while(iter.hasNext()){
final LocationRequester cReq = iter.next().get();
if(cReq==null) iter.remove();
else {
minimum_time_milli = Math.min(cReq.getLocationCriteria().getTimeInterval(),minimum_time_milli);
}
}
Log.d(DEBUG_TAG,"Updated requesters, got "+requestersRef.size()+" listeners to update every "+minimum_time_milli+" ms at least");
}
public void addLocationRequestFor(LocationRequester req){
boolean present = false;
minimum_time_milli = Integer.MAX_VALUE;
int countNull = 0;
ListIterator> iter = requestersRef.listIterator();
while(iter.hasNext()){
final LocationRequester cReq = iter.next().get();
if(cReq==null) {
countNull++;
iter.remove();
} else if(cReq.equals(req)){
present = true;
minimum_time_milli = Math.min(cReq.getLocationCriteria().getTimeInterval(),minimum_time_milli);
}
}
Log.d(DEBUG_TAG, countNull+" listeners have been removed because null");
if(!present) {
WeakReference newref = new WeakReference<>(req);
requestersRef.add(newref);
minimum_time_milli = Math.min(req.getLocationCriteria().getTimeInterval(),minimum_time_milli);
Log.d(DEBUG_TAG,"Added new stop requester, instance of "+req.getClass().getSimpleName());
}
if(requestersRef.size()>0){
Log.d(DEBUG_TAG,"Requesting location updates");
requestGPSPositionUpdates();
}
}
public void removeLocationRequestFor(LocationRequester req){
minimum_time_milli = Integer.MAX_VALUE;
ListIterator> iter = requestersRef.listIterator();
while(iter.hasNext()){
final LocationRequester cReq = iter.next().get();
if(cReq==null || cReq.equals(req)) iter.remove();
else {
minimum_time_milli = Math.min(cReq.getLocationCriteria().getTimeInterval(),minimum_time_milli);
}
}
if(requestersRef.size()<=0){
locMan.removeUpdates(this);
} else {
requestGPSPositionUpdates();
}
}
private void sendLocationStatusToAll(int status){
ListIterator> iter = requestersRef.listIterator();
while(iter.hasNext()){
final LocationRequester cReq = iter.next().get();
if(cReq==null) iter.remove();
else cReq.onLocationStatusChanged(status);
}
}
public boolean isRequesterRegistered(LocationRequester requester){
for(WeakReference regRef: requestersRef){
if(regRef.get()!=null && regRef.get() ==requester) return true;
}
return false;
}
@Override
public void onLocationChanged(Location location) {
Log.d(DEBUG_TAG,"found location:\nlat: "+location.getLatitude()+" lon: "+location.getLongitude()+"\naccuracy: "+location.getAccuracy());
ListIterator> iter = requestersRef.listIterator();
int new_min_interval = Integer.MAX_VALUE;
while(iter.hasNext()){
final LocationRequester requester = iter.next().get();
if(requester==null) iter.remove();
else{
final long timeNow = System.currentTimeMillis();
final LocationCriteria criteria = requester.getLocationCriteria();
if(location.getAccuracy()criteria.getTimeInterval()){
requester.onLocationChanged(location);
Log.d("AppLocationManager","Updating position for instance of requester "+requester.getClass().getSimpleName());
}
//update minimum time interval
new_min_interval = Math.min(requester.getLocationCriteria().getTimeInterval(),new_min_interval);
}
}
minimum_time_milli = new_min_interval;
if(requestersRef.size()==0){
//stop requesting the position
locMan.removeUpdates(this);
}
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
//IF ANOTHER LOCATION SOURCE IS READY, USE IT
//OTHERWISE, SIGNAL THAT WE HAVE NO LOCATION
if(oldGPSLocStatus !=status){
if(status == LocationProvider.OUT_OF_SERVICE || status == LocationProvider.TEMPORARILY_UNAVAILABLE) {
sendLocationStatusToAll(LOCATION_UNAVAILABLE);
}else if(status == LocationProvider.AVAILABLE){
sendLocationStatusToAll(LOCATION_GPS_AVAILABLE);
}
oldGPSLocStatus = status;
}
Log.d(DEBUG_TAG, "Provider status changed: "+provider+" status: "+status);
}
@Override
public void onProviderEnabled(String provider) {
cleanAndUpdateRequesters();
requestGPSPositionUpdates();
Log.d(DEBUG_TAG, "Provider: "+provider+" enabled");
for(WeakReference req: requestersRef){
if(req.get()==null) continue;
req.get().onLocationProviderAvailable();
}
}
@Override
public void onProviderDisabled(String provider) {
cleanAndUpdateRequesters();
for(WeakReference req: requestersRef){
if(req.get()==null) continue;
req.get().onLocationDisabled();
}
//locMan.removeUpdates(this);
Log.d(DEBUG_TAG, "Provider: "+provider+" disabled");
}
public boolean anyLocationProviderMatchesCriteria(Criteria cr) {
return Permissions.anyLocationProviderMatchesCriteria(locMan, cr, true);
}
/**
* Interface to be implemented to get the location request
*/
public interface LocationRequester{
/**
* Do something with the newly obtained location
* @param loc the obtained location
*/
void onLocationChanged(Location loc);
/**
* Inform the requester that the GPS status has changed
* @param status new status
*/
void onLocationStatusChanged(int status);
/**
* We have a location provider available
*/
void onLocationProviderAvailable();
/**
* Called when location is disabled
*/
void onLocationDisabled();
/**
* Give the last time of update the requester has
* Set it to -1 in order to receive each new location
* @return the time for update in milliseconds since epoch
*/
long getLastUpdateTimeMillis();
/**
* Get the specifications for the location
* @return fully parsed LocationCriteria
*/
LocationCriteria getLocationCriteria();
}
}