diff --git a/build.gradle b/build.gradle
index bfb2390..6344f9d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,117 +1,117 @@
buildscript {
repositories {
jcenter()
maven { url 'https://maven.google.com' }
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.2'
}
ext {
//libraries versions
fragment_version = "1.3.3"
activity_version = "1.1.0"
appcompat_version = "1.2.0"
preference_version = "1.1.1"
work_version = "2.5.0"
acra_version = "5.7.0"
lifecycle_version = "2.3.1"
arch_version = "2.1.0"
}
}
allprojects {
repositories {
jcenter()
maven { url 'https://maven.google.com' }
google()
}
}
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion '29.0.3'
defaultConfig {
applicationId "it.reyboz.bustorino"
minSdkVersion 14
targetSdkVersion 29
versionCode 32
versionName "1.14.1"
vectorDrawables.useSupportLibrary = true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
sourceSets {
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:$fragment_version"
implementation "androidx.activity:activity:$activity_version"
implementation "androidx.annotation:annotation:1.2.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.3.0'
implementation 'org.jsoup:jsoup:1.13.1'
implementation 'com.readystatesoftware.sqliteasset:sqliteassethelper:2.0.1'
implementation 'com.android.volley:volley:1.2.0'
- implementation 'org.osmdroid:osmdroid-android:6.1.8'
+ 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:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
// Lifecycles only (without ViewModel or LiveData)
implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"
}
}
diff --git a/src/it/reyboz/bustorino/ActivityMap.java b/src/it/reyboz/bustorino/ActivityMap.java
index 0a79fcd..c8b010d 100644
--- a/src/it/reyboz/bustorino/ActivityMap.java
+++ b/src/it/reyboz/bustorino/ActivityMap.java
@@ -1,429 +1,451 @@
/*
BusTO Activities
Copyright (C) 2020 Andrea Ugo e 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.pm.PackageManager;
import android.location.Location;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageButton;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.app.ActivityCompat;
import androidx.preference.PreferenceManager;
import it.reyboz.bustorino.middleware.GeneralActivity;
import it.reyboz.bustorino.data.NextGenDB;
import org.osmdroid.api.IMapController;
import org.osmdroid.config.Configuration;
import org.osmdroid.events.DelayedMapListener;
import org.osmdroid.events.MapListener;
import org.osmdroid.events.ScrollEvent;
import org.osmdroid.events.ZoomEvent;
import org.osmdroid.tileprovider.tilesource.TileSourceFactory;
import org.osmdroid.util.BoundingBox;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.MapView;
import org.osmdroid.views.overlay.FolderOverlay;
import org.osmdroid.views.overlay.Marker;
import org.osmdroid.views.overlay.Overlay;
import org.osmdroid.views.overlay.infowindow.InfoWindow;
import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider;
import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay;
import java.util.*;
import it.reyboz.bustorino.backend.Stop;
import it.reyboz.bustorino.map.CustomInfoWindow;
public class ActivityMap extends GeneralActivity {
private static final String TAG = "Busto-MapActivity";
private static final String MAP_CURRENT_ZOOM_KEY = "map-current-zoom";
private static final String MAP_CENTER_LAT_KEY = "map-center-lat";
private static final String MAP_CENTER_LON_KEY = "map-center-lon";
public static final String BUNDLE_LATIT = "lat";
public static final String BUNDLE_LONGIT = "lon";
public static final String BUNDLE_NAME = "name";
public static final String BUNDLE_ID = "ID";
private static final double DEFAULT_CENTER_LAT = 45.0708;
private static final double DEFAULT_CENTER_LON = 7.6858;
private static final double POSITION_FOUND_ZOOM = 18.3;
private HashSet shownStops = null;
private MapView map = null;
public Context ctx;
private MyLocationNewOverlay mLocationOverlay = null;
private FolderOverlay stopsFolderOverlay = null;
protected ImageButton btCenterMap;
protected ImageButton btFollowMe;
+ private final CustomInfoWindow.TouchResponder touchResponder = new CustomInfoWindow.TouchResponder() {
+ @Override
+ public void onActionUp(@NonNull String stopID, @Nullable String stopName) {
+ Intent intent = new Intent(ctx, ActivityMain.class);
+ Bundle b = new Bundle();
+ b.putString("bus-stop-ID", stopID);
+ b.putString("bus-stop-display-name", stopName);
+ intent.putExtras(b);
+ intent.setFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
+
+ // start ActivityMain with the previous intent
+ ctx.startActivity(intent);
+ }
+ };
+
//@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//handle permissions first, before map is created. not depicted here
//load/initialize the osmdroid configuration
ctx = getApplicationContext();
Configuration.getInstance().load(ctx, PreferenceManager.getDefaultSharedPreferences(ctx));
//setting this before the layout is inflated is a good idea
//it 'should' ensure that the map has a writable location for the map cache, even without permissions
//if no tiles are displayed, you can try overriding the cache path using Configuration.getInstance().setCachePath
//see also StorageUtils
//note, the load method also sets the HTTP User Agent to your application's package name, abusing osm's tile servers will get you banned based on this string
//inflate and create the map
setContentView(R.layout.activity_map);
map = (MapView) findViewById(R.id.map);
map.setTileSource(TileSourceFactory.MAPNIK);
//map.setTilesScaledToDpi(true);
map.setFlingEnabled(true);
// add ability to zoom with 2 fingers
map.setMultiTouchControls(true);
btCenterMap = (ImageButton) findViewById(R.id.ic_center_map);
btFollowMe = (ImageButton) findViewById(R.id.ic_follow_me);
//setup FolderOverlay
stopsFolderOverlay = new FolderOverlay();
// take the parameters if it's called from other Activities
Bundle b = getIntent().getExtras();
startMap(b, savedInstanceState);
// on drag and zoom reload the markers
map.addMapListener(new DelayedMapListener(new MapListener() {
@Override
public boolean onScroll(ScrollEvent paramScrollEvent) {
loadMarkers();
return true;
}
@Override
public boolean onZoom(ZoomEvent event) {
loadMarkers();
return true;
}
}));
btCenterMap.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i(TAG, "centerMap clicked ");
final GeoPoint myPosition = mLocationOverlay.getMyLocation();
map.getController().animateTo(myPosition);
}
});
btFollowMe.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i(TAG, "btFollowMe clicked ");
if (!mLocationOverlay.isFollowLocationEnabled()) {
mLocationOverlay.enableFollowLocation();
btFollowMe.setImageResource(R.drawable.ic_follow_me_on);
} else {
mLocationOverlay.disableFollowLocation();
btFollowMe.setImageResource(R.drawable.ic_follow_me);
}
}
});
}
public void startMap(Bundle incoming, Bundle savedInstanceState) {
//parse incoming bundle
GeoPoint marker = null;
String name = null;
String ID = null;
if (incoming != null) {
double lat = incoming.getDouble(BUNDLE_LATIT);
double lon = incoming.getDouble(BUNDLE_LONGIT);
marker = new GeoPoint(lat, lon);
name = incoming.getString(BUNDLE_NAME);
ID = incoming.getString(BUNDLE_ID);
}
shownStops = new HashSet<>();
// move the map on the marker position or on a default view point: Turin, Piazza Castello
// and set the start zoom
IMapController mapController = map.getController();
GeoPoint startPoint = null;
boolean havePositionPermission = true;
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
askForPermissionIfNeeded(Manifest.permission.ACCESS_FINE_LOCATION, PERMISSION_REQUEST_POSITION);
havePositionPermission = false;
}
if (marker != null) {
startPoint = marker;
mapController.setZoom(POSITION_FOUND_ZOOM);
} else if (savedInstanceState != null || !havePositionPermission) {
mapController.setZoom(savedInstanceState.getDouble(MAP_CURRENT_ZOOM_KEY));
mapController.setCenter(new GeoPoint(savedInstanceState.getDouble(MAP_CENTER_LAT_KEY),
savedInstanceState.getDouble(MAP_CENTER_LON_KEY)));
} else {
boolean found = false;
LocationManager locationManager =
(LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
if (locationManager != null) {
Location userLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (userLocation != null) {
mapController.setZoom(POSITION_FOUND_ZOOM);
startPoint = new GeoPoint(userLocation);
found = true;
}
}
if(!found){
startPoint = new GeoPoint(DEFAULT_CENTER_LAT, DEFAULT_CENTER_LON);
mapController.setZoom(16.0);
}
}
// set the minimum zoom level
map.setMinZoomLevel(15.0);
//add contingency check (shouldn't happen..., but)
if (startPoint != null) {
mapController.setCenter(startPoint);
}
// Location Overlay
// from OpenBikeSharing (THANK GOD)
GpsMyLocationProvider imlp = new GpsMyLocationProvider(this.getBaseContext());
imlp.setLocationUpdateMinDistance(5);
imlp.setLocationUpdateMinTime(2000);
this.mLocationOverlay = new MyLocationNewOverlay(imlp,map);
mLocationOverlay.enableMyLocation();
mLocationOverlay.enableFollowLocation();
btFollowMe.setImageResource(R.drawable.ic_follow_me_on);
mLocationOverlay.setOptionsMenuEnabled(true);
/*
mLocationOverlay.runOnFirstFix(() -> {
mapController.setCenter(mLocationOverlay.getMyLocation());
mapController.animateTo(mLocationOverlay.getMyLocation());
});
*/
map.getOverlays().add(this.mLocationOverlay);
//add stops overlay
map.getOverlays().add(this.stopsFolderOverlay);
loadMarkers();
if (marker != null) {
// make a marker with the info window open for the searched marker
makeMarker(startPoint, name , ID, true);
}
}
public Marker makeMarker(GeoPoint geoPoint, String stopName, String ID, boolean isStartMarker) {
// add a marker
Marker marker = new Marker(map);
// set custom info window as info window
- CustomInfoWindow popup = new CustomInfoWindow(map, ID, stopName);
+ CustomInfoWindow popup = new CustomInfoWindow(map, ID, stopName, touchResponder);
marker.setInfoWindow(popup);
// make the marker clickable
marker.setOnMarkerClickListener((thisMarker, mapView) -> {
if (thisMarker.isInfoWindowOpen()) {
// on second click
// create an intent with these extras
Intent intent = new Intent(ActivityMap.this, ActivityMain.class);
Bundle b = new Bundle();
b.putString("bus-stop-ID", ID);
b.putString("bus-stop-display-name", stopName);
intent.putExtras(b);
intent.setFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
// start ActivityMain with the previous intent
startActivity(intent);
} else {
// on first click
// hide all opened info window
InfoWindow.closeAllInfoWindowsOn(map);
// show this particular info window
thisMarker.showInfoWindow();
// move the map to its position
map.getController().animateTo(thisMarker.getPosition());
}
return true;
});
// set its position
marker.setPosition(geoPoint);
marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM);
// add to it an icon
marker.setIcon(getResources().getDrawable(R.drawable.bus_marker));
// add to it a title
marker.setTitle(stopName);
// set the description as the ID
marker.setSnippet(ID);
// show popup info window of the searched marker
if (isStartMarker) {
marker.showInfoWindow();
}
return marker;
}
public void loadMarkers() {
// get rid of the previous markers
//map.getOverlays().clear();
//stopsFolderOverlay = new FolderOverlay();
List stopsOverlays = stopsFolderOverlay.getItems();
/*if (stopsOverlays != null){
stopsOverlays.clear();
}*/
// get the top, bottom, left and right screen's coordinate
BoundingBox bb = map.getBoundingBox();
double latFrom = bb.getLatSouth();
double latTo = bb.getLatNorth();
double lngFrom = bb.getLonWest();
double lngTo = bb.getLonEast();
// get the stops located in those coordinates
/*
StopsDB stopsDB = new StopsDB(ctx);
stopsDB.openIfNeeded();
Stop[] stops = stopsDB.queryAllInsideMapView(latFrom, latTo, lngFrom, lngTo);
stopsDB.closeIfNeeded();
*/
NextGenDB dbHelper = new NextGenDB(ctx);
Stop[] stops = dbHelper.queryAllInsideMapView(latFrom, latTo, lngFrom, lngTo);
// add new markers of those stops
for (Stop stop : stops) {
if (shownStops.contains(stop.ID)){
continue;
}
try{
stop.getLatitude();
stop.getLongitude();
} catch (NullPointerException e) {
Log.e(TAG,"Stop "+stop.ID+ " gives null coordinates");
e.printStackTrace();
continue;
}
shownStops.add(stop.ID);
GeoPoint marker = new GeoPoint(stop.getLatitude(), stop.getLongitude());
Marker stopMarker = makeMarker(marker, stop.getStopDefaultName(), stop.ID, false);
stopsFolderOverlay.add(stopMarker);
}
}
+ @Override
+ protected void onPostResume() {
+ super.onPostResume();
+ ctx = this;
+ }
+
protected boolean detachMapFromPosition(){
if (mLocationOverlay.isFollowLocationEnabled()) {
mLocationOverlay.disableFollowLocation();
btFollowMe.setImageResource(R.drawable.ic_follow_me);
return true;
} return false;
}
public void onResume(){
super.onResume();
//this will refresh the osmdroid configuration on resuming.
//if you make changes to the configuration, use
//SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
//Configuration.getInstance().load(this, PreferenceManager.getDefaultSharedPreferences(this));
map.onResume(); //needed for compass, my location overlays, v6.0.0 and up
mLocationOverlay.enableMyLocation();
}
public void onPause(){
super.onPause();
//this will refresh the osmdroid configuration on resuming.
//if you make changes to the configuration, use
//SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
//Configuration.getInstance().save(this, prefs);
map.onPause(); //needed for compass, my location overlays, v6.0.0 and up
mLocationOverlay.disableMyLocation();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putDouble(MAP_CURRENT_ZOOM_KEY, map.getZoomLevelDouble());
outState.putDouble(MAP_CENTER_LAT_KEY, map.getMapCenter().getLatitude());
outState.putDouble(MAP_CENTER_LON_KEY, map.getMapCenter().getLongitude());
}
/**
* PERMISSION STUFF
**/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case PERMISSION_REQUEST_POSITION:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
setOption(LOCATION_PERMISSION_GIVEN, true);
//if we sent a request for a new NearbyStopsFragment
} else {
//permission denied
setOption(LOCATION_PERMISSION_GIVEN, false);
}
break;
//add other cases for permissions
}
}
}
\ No newline at end of file
diff --git a/src/it/reyboz/bustorino/ActivityPrincipal.java b/src/it/reyboz/bustorino/ActivityPrincipal.java
index fe85fe5..f900b2e 100644
--- a/src/it/reyboz/bustorino/ActivityPrincipal.java
+++ b/src/it/reyboz/bustorino/ActivityPrincipal.java
@@ -1,433 +1,480 @@
package it.reyboz.bustorino;
import android.content.Intent;
import android.content.SharedPreferences;
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.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.work.BackoffPolicy;
import androidx.work.Constraints;
import androidx.work.ExistingPeriodicWorkPolicy;
import androidx.work.NetworkType;
import androidx.work.PeriodicWorkRequest;
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.concurrent.TimeUnit;
+import it.reyboz.bustorino.backend.Stop;
import it.reyboz.bustorino.data.DBUpdateWorker;
import it.reyboz.bustorino.data.DatabaseUpdate;
import it.reyboz.bustorino.fragments.FavoritesFragment;
import it.reyboz.bustorino.fragments.FragmentKind;
import it.reyboz.bustorino.fragments.FragmentListenerMain;
import it.reyboz.bustorino.fragments.MainScreenFragment;
+import it.reyboz.bustorino.fragments.MapFragment;
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();
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());
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);
mNavView = findViewById(R.id.nvView);
setupDrawerContent(mNavView);
/// 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
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);
/*
Set database update
*/
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;
}
}
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);
}
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);
ft.addToBackStack(null);
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();
+ createAndShowMapFragment(null);
+ return true;
}
//selectDrawerItem(menuItem);
Log.d(DEBUG_TAG, "pressed item "+menuItem.toString());
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.extra_menu_items, menu);
return super.onCreateOptionsMenu(menu);
}
@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
shownFrag.getChildFragmentManager().popBackStackImmediate();
if(showingMainFragmentFromOther && getSupportFragmentManager().getBackStackEntryCount() > 0){
getSupportFragmentManager().popBackStack();
}
}
else if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStack();
}
else
super.onBackPressed();
}
private void createDefaultSnackbar() {
if (snackbar == null) {
snackbar = Snackbar.make(findViewById(R.id.searchButton), R.string.database_update_message, 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);
MainScreenFragment mainScreenFragment = null;
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);
}
+ switch (fragmentType){
+ case MAP:
+ mNavView.setCheckedItem(R.id.nav_map_item);
+ break;
+ case FAVORITES:
+ mNavView.setCheckedItem(R.id.nav_favorites_item);
+ break;
+ case ARRIVALS:
+ case NEARBY_STOPS:
+ case STOPS:
+ case MAIN_SCREEN_FRAGMENT:
+ case NEARBY_ARRIVALS:
+ mNavView.setCheckedItem(R.id.nav_arrivals);
+ break;
+ }
}
@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);
}
}
+ //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{
@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), getApplicationContext());
return true;
case R.id.action_source:
openIceweasel("https://gitpull.it/source/libre-busto/", getApplicationContext());
return true;
case R.id.action_licence:
openIceweasel("https://www.gnu.org/licenses/gpl-3.0.html", getApplicationContext());
return true;
default:
}
return false;
}
}
}
diff --git a/src/it/reyboz/bustorino/data/NextGenDB.java b/src/it/reyboz/bustorino/data/NextGenDB.java
index c917312..a0781eb 100644
--- a/src/it/reyboz/bustorino/data/NextGenDB.java
+++ b/src/it/reyboz/bustorino/data/NextGenDB.java
@@ -1,356 +1,355 @@
/*
BusTO (middleware)
Copyright (C) 2018 Fabio Mazza
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
package it.reyboz.bustorino.data;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;
import android.util.Log;
import it.reyboz.bustorino.backend.Route;
import it.reyboz.bustorino.backend.Stop;
import java.util.*;
import static it.reyboz.bustorino.data.NextGenDB.Contract.*;
public class NextGenDB extends SQLiteOpenHelper{
public static final String DATABASE_NAME = "bustodatabase.db";
public static final int DATABASE_VERSION = 2;
public static final String DEBUG_TAG = "NextGenDB-BusTO";
//NO Singleton instance
//private static volatile NextGenDB instance = null;
//Some generating Strings
private static final String SQL_CREATE_LINES_TABLE="CREATE TABLE "+Contract.LinesTable.TABLE_NAME+" ("+
Contract.LinesTable._ID +" INTEGER PRIMARY KEY AUTOINCREMENT, "+ Contract.LinesTable.COLUMN_NAME +" TEXT, "+
Contract.LinesTable.COLUMN_DESCRIPTION +" TEXT, "+Contract.LinesTable.COLUMN_TYPE +" TEXT, "+
"UNIQUE ("+LinesTable.COLUMN_NAME+","+LinesTable.COLUMN_DESCRIPTION+","+LinesTable.COLUMN_TYPE+" ) "+" )";
private static final String SQL_CREATE_BRANCH_TABLE="CREATE TABLE "+Contract.BranchesTable.TABLE_NAME+" ("+
Contract.BranchesTable._ID +" INTEGER, "+ Contract.BranchesTable.COL_BRANCHID +" INTEGER PRIMARY KEY, "+
Contract.BranchesTable.COL_LINE +" INTEGER, "+ Contract.BranchesTable.COL_DESCRIPTION +" TEXT, "+
Contract.BranchesTable.COL_DIRECTION+" TEXT, "+ Contract.BranchesTable.COL_TYPE +" INTEGER, "+
//SERVICE DAYS: 0 => FERIALE,1=>FESTIVO,-1=>UNKNOWN,add others if necessary
Contract.BranchesTable.COL_FESTIVO +" INTEGER, "+
//DAYS COLUMNS. IT'S SO TEDIOUS I TRIED TO KILL MYSELF
BranchesTable.COL_LUN+" INTEGER, "+BranchesTable.COL_MAR+" INTEGER, "+BranchesTable.COL_MER+" INTEGER, "+BranchesTable.COL_GIO+" INTEGER, "+
BranchesTable.COL_VEN+" INTEGER, "+ BranchesTable.COL_SAB+" INTEGER, "+BranchesTable.COL_DOM+" INTEGER, "+
"FOREIGN KEY("+ Contract.BranchesTable.COL_LINE +") references "+ Contract.LinesTable.TABLE_NAME+"("+ Contract.LinesTable._ID+") "
+")";
private static final String SQL_CREATE_CONNECTIONS_TABLE="CREATE TABLE "+Contract.ConnectionsTable.TABLE_NAME+" ("+
Contract.ConnectionsTable.COLUMN_BRANCH+" INTEGER, "+ Contract.ConnectionsTable.COLUMN_STOP_ID+" TEXT, "+
Contract.ConnectionsTable.COLUMN_ORDER+" INTEGER, "+
"PRIMARY KEY ("+ Contract.ConnectionsTable.COLUMN_BRANCH+","+ Contract.ConnectionsTable.COLUMN_STOP_ID + "), "+
"FOREIGN KEY("+ Contract.ConnectionsTable.COLUMN_BRANCH+") references "+ Contract.BranchesTable.TABLE_NAME+"("+ Contract.BranchesTable.COL_BRANCHID +"), "+
"FOREIGN KEY("+ Contract.ConnectionsTable.COLUMN_STOP_ID+") references "+ Contract.StopsTable.TABLE_NAME+"("+ Contract.StopsTable.COL_ID +") "
+")";
private static final String SQL_CREATE_STOPS_TABLE="CREATE TABLE "+Contract.StopsTable.TABLE_NAME+" ("+
Contract.StopsTable.COL_ID+" TEXT PRIMARY KEY, "+ Contract.StopsTable.COL_TYPE+" INTEGER, "+Contract.StopsTable.COL_LAT+" REAL NOT NULL, "+
Contract.StopsTable.COL_LONG+" REAL NOT NULL, "+ Contract.StopsTable.COL_NAME+" TEXT NOT NULL, "+
Contract.StopsTable.COL_LOCATION+" TEXT, "+Contract.StopsTable.COL_PLACE+" TEXT, "+
Contract.StopsTable.COL_LINES_STOPPING +" TEXT )";
private static final String SQL_CREATE_STOPS_TABLE_TO_COMPLETE = " ("+
Contract.StopsTable.COL_ID+" TEXT PRIMARY KEY, "+ Contract.StopsTable.COL_TYPE+" INTEGER, "+Contract.StopsTable.COL_LAT+" REAL NOT NULL, "+
Contract.StopsTable.COL_LONG+" REAL NOT NULL, "+ Contract.StopsTable.COL_NAME+" TEXT NOT NULL, "+
Contract.StopsTable.COL_LOCATION+" TEXT, "+Contract.StopsTable.COL_PLACE+" TEXT, "+
Contract.StopsTable.COL_LINES_STOPPING +" TEXT )";
public static final String[] QUERY_COLUMN_stops_all = {
StopsTable.COL_ID, StopsTable.COL_NAME, StopsTable.COL_LOCATION,
StopsTable.COL_TYPE, StopsTable.COL_LAT, StopsTable.COL_LONG, StopsTable.COL_LINES_STOPPING};
public static final String QUERY_WHERE_LAT_AND_LNG_IN_RANGE = StopsTable.COL_LAT + " >= ? AND " +
StopsTable.COL_LAT + " <= ? AND "+ StopsTable.COL_LONG +
" >= ? AND "+ StopsTable.COL_LONG + " <= ?";
public static String QUERY_WHERE_ID = StopsTable.COL_ID+" = ?";
private final Context appContext;
public NextGenDB(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
appContext = context.getApplicationContext();
}
@Override
public void onCreate(SQLiteDatabase db) {
Log.d("BusTO-AppDB","Lines creating database:\n"+SQL_CREATE_LINES_TABLE+"\n"+
SQL_CREATE_STOPS_TABLE+"\n"+SQL_CREATE_BRANCH_TABLE+"\n"+SQL_CREATE_CONNECTIONS_TABLE);
db.execSQL(SQL_CREATE_LINES_TABLE);
db.execSQL(SQL_CREATE_STOPS_TABLE);
//tables with constraints
db.execSQL(SQL_CREATE_BRANCH_TABLE);
db.execSQL(SQL_CREATE_CONNECTIONS_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if(oldVersion<2 && newVersion == 2){
//DROP ALL TABLES
db.execSQL("DROP TABLE "+ConnectionsTable.TABLE_NAME);
db.execSQL("DROP TABLE "+BranchesTable.TABLE_NAME);
db.execSQL("DROP TABLE "+LinesTable.TABLE_NAME);
db.execSQL("DROP TABLE "+ StopsTable.TABLE_NAME);
//RECREATE THE TABLES WITH THE NEW SCHEMA
db.execSQL(SQL_CREATE_LINES_TABLE);
db.execSQL(SQL_CREATE_STOPS_TABLE);
//tables with constraints
db.execSQL(SQL_CREATE_BRANCH_TABLE);
db.execSQL(SQL_CREATE_CONNECTIONS_TABLE);
DatabaseUpdateService.startDBUpdate(appContext,0,true);
}
}
@Override
public void onConfigure(SQLiteDatabase db) {
super.onConfigure(db);
db.execSQL("PRAGMA foreign_keys=ON");
}
public static String getSqlCreateStopsTable(String tableName){
return "CREATE TABLE "+tableName+" ("+
Contract.StopsTable.COL_ID+" TEXT PRIMARY KEY, "+ Contract.StopsTable.COL_TYPE+" INTEGER, "+Contract.StopsTable.COL_LAT+" REAL NOT NULL, "+
Contract.StopsTable.COL_LONG+" REAL NOT NULL, "+ Contract.StopsTable.COL_NAME+" TEXT NOT NULL, "+
Contract.StopsTable.COL_LOCATION+" TEXT, "+Contract.StopsTable.COL_PLACE+" TEXT, "+
Contract.StopsTable.COL_LINES_STOPPING +" TEXT )";
}
/**
* Query some bus stops inside a map view
*
* You can obtain the coordinates from OSMDroid using something like this:
* BoundingBoxE6 bb = mMapView.getBoundingBox();
* double latFrom = bb.getLatSouthE6() / 1E6;
* double latTo = bb.getLatNorthE6() / 1E6;
* double lngFrom = bb.getLonWestE6() / 1E6;
* double lngTo = bb.getLonEastE6() / 1E6;
*/
public synchronized Stop[] queryAllInsideMapView(double minLat, double maxLat, double minLng, double maxLng) {
Stop[] stops = new Stop[0];
SQLiteDatabase db = this.getReadableDatabase();
- Cursor result;
+ //Cursor result=null;
int count;
// coordinates must be strings in the where condition
String minLatRaw = String.valueOf(minLat);
String maxLatRaw = String.valueOf(maxLat);
String minLngRaw = String.valueOf(minLng);
String maxLngRaw = String.valueOf(maxLng);
if(db == null) {
return stops;
}
try {
- result = db.query(StopsTable.TABLE_NAME, QUERY_COLUMN_stops_all, QUERY_WHERE_LAT_AND_LNG_IN_RANGE,
+ final Cursor result = db.query(StopsTable.TABLE_NAME, QUERY_COLUMN_stops_all, QUERY_WHERE_LAT_AND_LNG_IN_RANGE,
new String[] {minLatRaw, maxLatRaw, minLngRaw, maxLngRaw},
null, null, null);
stops = getStopsFromCursorAllFields(result);
-
+ result.close();
} catch(SQLiteException e) {
Log.e(DEBUG_TAG, "SQLiteException occurred");
e.printStackTrace();
return stops;
+ }finally {
+ db.close();
}
- result.close();
- db.close();
-
return stops;
}
/**
* Get the list of stop in the query, with all the possible fields {NextGenDB.QUERY_COLUMN_stops_all}
* @param result cursor from query
* @return an Array of the stops found in the query
*/
public static Stop[] getStopsFromCursorAllFields(Cursor result){
int colID = result.getColumnIndex(StopsTable.COL_ID);
int colName = result.getColumnIndex(StopsTable.COL_NAME);
int colLocation = result.getColumnIndex(StopsTable.COL_LOCATION);
int colType = result.getColumnIndex(StopsTable.COL_TYPE);
int colLat = result.getColumnIndex(StopsTable.COL_LAT);
int colLon = result.getColumnIndex(StopsTable.COL_LONG);
int colLines = result.getColumnIndex(StopsTable.COL_LINES_STOPPING);
int count = result.getCount();
Stop[] stops = new Stop[count];
int i = 0;
while(result.moveToNext()) {
final String stopID = result.getString(colID).trim();
final Route.Type type = Route.getTypeFromSymbol(result.getString(colType));
String lines = result.getString(colLines).trim();
String locationSometimesEmpty = result.getString(colLocation);
if (locationSometimesEmpty!= null && locationSometimesEmpty.length() <= 0) {
locationSometimesEmpty = null;
}
stops[i++] = new Stop(stopID, result.getString(colName), null,
locationSometimesEmpty, type, splitLinesString(lines),
result.getDouble(colLat), result.getDouble(colLon));
}
return stops;
}
/**
* Insert batch content, already prepared as
* @param content ContentValues array
* @return number of lines inserted
*/
public int insertBatchContent(ContentValues[] content,String tableName) throws SQLiteException {
final SQLiteDatabase db = this.getWritableDatabase();
int success = 0;
db.beginTransaction();
for (final ContentValues cv : content) {
try {
db.replaceOrThrow(tableName, null, cv);
success++;
} catch (SQLiteConstraintException d){
Log.w("NextGenDB_Insert","Failed insert with FOREIGN KEY... \n"+d.getMessage());
} catch (Exception e) {
Log.w("NextGenDB_Insert", e);
}
}
db.setTransactionSuccessful();
db.endTransaction();
return success;
}
public static List splitLinesString(String linesStr){
return Arrays.asList(linesStr.split("\\s*,\\s*"));
}
public static final class Contract{
//Ok, I get it, it really is a pain in the ass..
// But it's the only way to have maintainable code
public interface DataTables {
String getTableName();
String[] getFields();
}
public static final class LinesTable implements BaseColumns, DataTables {
//The fields
public static final String TABLE_NAME = "lines";
public static final String COLUMN_NAME = "line_name";
public static final String COLUMN_DESCRIPTION = "line_description";
public static final String COLUMN_TYPE = "line_bacino";
@Override
public String getTableName() {
return TABLE_NAME;
}
@Override
public String[] getFields() {
return new String[]{COLUMN_NAME,COLUMN_DESCRIPTION,COLUMN_TYPE};
}
}
public static final class BranchesTable implements BaseColumns, DataTables {
public static final String TABLE_NAME = "branches";
public static final String COL_BRANCHID = "branchid";
public static final String COL_LINE = "lineid";
public static final String COL_DESCRIPTION = "branch_description";
public static final String COL_DIRECTION = "branch_direzione";
public static final String COL_FESTIVO = "branch_festivo";
public static final String COL_TYPE = "branch_type";
public static final String COL_LUN="runs_lun";
public static final String COL_MAR="runs_mar";
public static final String COL_MER="runs_mer";
public static final String COL_GIO="runs_gio";
public static final String COL_VEN="runs_ven";
public static final String COL_SAB="runs_sab";
public static final String COL_DOM="runs_dom";
@Override
public String getTableName() {
return TABLE_NAME;
}
@Override
public String[] getFields() {
return new String[]{COL_BRANCHID,COL_LINE,COL_DESCRIPTION,
COL_DIRECTION,COL_FESTIVO,COL_TYPE,
COL_LUN,COL_MAR,COL_MER,COL_GIO,COL_VEN,COL_SAB,COL_DOM
};
}
}
public static final class ConnectionsTable implements DataTables {
public static final String TABLE_NAME = "connections";
public static final String COLUMN_BRANCH = "branchid";
public static final String COLUMN_STOP_ID = "stopid";
public static final String COLUMN_ORDER = "ordine";
@Override
public String getTableName() {
return TABLE_NAME;
}
@Override
public String[] getFields() {
return new String[]{COLUMN_STOP_ID,COLUMN_BRANCH,COLUMN_ORDER};
}
}
public static final class StopsTable implements DataTables {
public static final String TABLE_NAME = "stops";
public static final String COL_ID = "stopid"; //integer
public static final String COL_TYPE = "stop_type";
public static final String COL_NAME = "stop_name";
public static final String COL_LAT = "stop_latitude";
public static final String COL_LONG = "stop_longitude";
public static final String COL_LOCATION = "stop_location";
public static final String COL_PLACE = "stop_placeName";
public static final String COL_LINES_STOPPING = "stop_lines";
@Override
public String getTableName() {
return TABLE_NAME;
}
@Override
public String[] getFields() {
return new String[]{COL_ID,COL_TYPE,COL_NAME,COL_LAT,COL_LONG,COL_LOCATION,COL_PLACE,COL_LINES_STOPPING};
}
}
}
public static final class DBUpdatingException extends Exception{
public DBUpdatingException(String message) {
super(message);
}
}
}
diff --git a/src/it/reyboz/bustorino/fragments/CommonScrollListener.java b/src/it/reyboz/bustorino/fragments/CommonScrollListener.java
index f72703a..530d658 100644
--- a/src/it/reyboz/bustorino/fragments/CommonScrollListener.java
+++ b/src/it/reyboz/bustorino/fragments/CommonScrollListener.java
@@ -1,85 +1,85 @@
/*
BusTO - Fragments components
Copyright (C) 2018 Fabio Mazza
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
package it.reyboz.bustorino.fragments;
import androidx.recyclerview.widget.RecyclerView;
import android.util.Log;
import android.widget.AbsListView;
import java.lang.ref.WeakReference;
public class CommonScrollListener extends RecyclerView.OnScrollListener implements AbsListView.OnScrollListener{
WeakReference listenerWeakReference;
//enable swipeRefreshLayout when scrolling down or not
boolean enableRefreshLayout;
int lastvisibleitem;
public CommonScrollListener(FragmentListenerMain lis, boolean enableRefreshLayout){
listenerWeakReference = new WeakReference<>(lis);
this.enableRefreshLayout = enableRefreshLayout;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
/*
* This seems to be a totally useless method
*/
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
FragmentListenerMain listener = listenerWeakReference.get();
if(listener==null){
//can't do anything, sorry
Log.i(this.getClass().getName(),"called onScroll but FragmentListener is null");
return;
}
if (firstVisibleItem>=0) {
if (lastvisibleitem < firstVisibleItem) {
- Log.i("Busto", "Scrolling DOWN");
+ //Log.i("Busto", "Scrolling DOWN");
listener.showFloatingActionButton(false);
//lastScrollUp = true;
} else if (lastvisibleitem > firstVisibleItem) {
- Log.i("Busto", "Scrolling UP");
+ //Log.i("Busto", "Scrolling UP");
listener.showFloatingActionButton(true);
//lastScrollUp = false;
}
lastvisibleitem = firstVisibleItem;
}
if(enableRefreshLayout){
boolean enable = false;
if(view != null && view.getChildCount() > 0){
// check if the first item of the list is visible
boolean firstItemVisible = view.getFirstVisiblePosition() == 0;
// check if the top of the first item is visible
boolean topOfFirstItemVisible = view.getChildAt(0).getTop() == 0;
// enabling or disabling the refresh layout
enable = firstItemVisible && topOfFirstItemVisible;
}
listener.enableRefreshLayout(enable);
//Log.d(getString(R.string.list_fragment_debug),"onScroll active, first item visible: "+firstVisibleItem+", refreshlayout enabled: "+enable);
}}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
FragmentListenerMain listener = listenerWeakReference.get();
if(newState!=SCROLL_STATE_IDLE) listener.showFloatingActionButton(false);
else listener.showFloatingActionButton(true);
}
}
diff --git a/src/it/reyboz/bustorino/fragments/FavoritesFragment.java b/src/it/reyboz/bustorino/fragments/FavoritesFragment.java
index 22e79ca..247f697 100644
--- a/src/it/reyboz/bustorino/fragments/FavoritesFragment.java
+++ b/src/it/reyboz/bustorino/fragments/FavoritesFragment.java
@@ -1,272 +1,281 @@
package it.reyboz.bustorino.fragments;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;
import java.util.List;
import it.reyboz.bustorino.ActivityFavorites;
import it.reyboz.bustorino.ActivityMain;
import it.reyboz.bustorino.ActivityMap;
import it.reyboz.bustorino.R;
import it.reyboz.bustorino.adapters.StopAdapter;
import it.reyboz.bustorino.backend.Stop;
import it.reyboz.bustorino.data.FavoritesViewModel;
import it.reyboz.bustorino.data.UserDB;
import it.reyboz.bustorino.middleware.AsyncStopFavoriteAction;
public class FavoritesFragment extends BaseFragment {
private ListView favoriteListView;
private EditText busStopNameText;
private TextView favoriteTipTextView;
private ImageView angeryBusImageView;
@Nullable
private CommonFragmentListener mListener;
+ public static final String FRAGMENT_TAG = "BusTOFavFragment";
+
public static FavoritesFragment newInstance() {
FavoritesFragment fragment = new FavoritesFragment();
Bundle args = new Bundle();
//args.putString(ARG_PARAM1, param1);
//args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
- private FavoritesFragment(){
+ public FavoritesFragment(){
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
//do nothing
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_favorites, container, false);
favoriteListView = root.findViewById(R.id.favoriteListView);
favoriteListView.setOnItemClickListener((parent, view, position, id) -> {
/**
* Casting because of Javamerda
* @url http://stackoverflow.com/questions/30549485/androids-list-view-parameterized-type-in-adapterview-onitemclicklistener
*/
Stop busStop = (Stop) parent.getItemAtPosition(position);
if(mListener!=null){
mListener.requestArrivalsForStopID(busStop.ID);
}
});
angeryBusImageView = root.findViewById(R.id.angeryBusImageView);
favoriteTipTextView = root.findViewById(R.id.favoriteTipTextView);
registerForContextMenu(favoriteListView);
FavoritesViewModel model = new ViewModelProvider(this).get(FavoritesViewModel.class);
model.getFavorites().observe(getViewLifecycleOwner(), this::showStops);
return root;
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof CommonFragmentListener) {
mListener = (CommonFragmentListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement CommonFragmentListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
@Override
public void onCreateContextMenu(@NonNull ContextMenu menu, @NonNull View v,
ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
if (v.getId() == R.id.favoriteListView) {
// if we aren't attached to activity, return null
if (getActivity()==null) return;
MenuInflater inflater = getActivity().getMenuInflater();
inflater.inflate(R.menu.menu_favourites_entry, menu);
}
}
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (mListener!=null) mListener.readyGUIfor(FragmentKind.FAVORITES);
+ }
+
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item
.getMenuInfo();
Stop busStop = (Stop) favoriteListView.getItemAtPosition(info.position);
switch (item.getItemId()) {
case R.id.action_favourite_entry_delete:
if (getContext()!=null)
new AsyncStopFavoriteAction(getContext().getApplicationContext(), AsyncStopFavoriteAction.Action.REMOVE,
result -> {
}).execute(busStop);
return true;
case R.id.action_rename_bus_stop_username:
showBusStopUsernameInputDialog(busStop);
return true;
case R.id.action_view_on_map:
final String theGeoUrl = busStop.getGeoURL();
/*
if(theGeoUrl==null){
//doesn't have a position
Toast.makeText(getContext(),R.string.cannot_show_on_map_no_position,Toast.LENGTH_SHORT).show();
return true;
}
// start ActivityMap with these extras in intent
Intent intent = new Intent(getContext(), ActivityMap.class);
Bundle b = new Bundle();
double lat, lon;
if (busStop.getLatitude()!=null)
lat = busStop.getLatitude();
else lat = 200;
if (busStop.getLongitude()!=null)
lon = busStop.getLongitude();
else lon = 200;
b.putDouble("lat", lat);
b.putDouble("lon",lon);
b.putString("name", busStop.getStopDefaultName());
b.putString("ID", busStop.ID);
intent.putExtras(b);
startActivity(intent);
TODO: start map on button press
*/
return true;
default:
return super.onContextItemSelected(item);
}
}
+
void showStops(List busStops){
// If no data is found show a friendly message
if (busStops.size() == 0) {
favoriteListView.setVisibility(View.INVISIBLE);
// TextView favoriteTipTextView = (TextView) findViewById(R.id.favoriteTipTextView);
//assert favoriteTipTextView != null;
favoriteTipTextView.setVisibility(View.VISIBLE);
//ImageView angeryBusImageView = (ImageView) findViewById(R.id.angeryBusImageView);
angeryBusImageView.setVisibility(View.VISIBLE);
} else {
favoriteListView.setVisibility(View.VISIBLE);
favoriteTipTextView.setVisibility(View.INVISIBLE);
angeryBusImageView.setVisibility(View.INVISIBLE);
}
/* There's a nice method called notifyDataSetChanged() to avoid building the ListView
* all over again. This method exists in a billion answers on Stack Overflow, but
* it's nowhere to be seen around here, Android Studio can't find it no matter what.
* Anyway, it only works from Android 2.3 onward (which is why it refuses to appear, I
* guess) and requires to modify the list with .add() and .clear() and some other
* methods, so to update a single stop we need to completely rebuild the list for no
* reason. It would probably end up as "slow" as throwing away the old ListView and
* redrwaing everything.
*/
// Show results
favoriteListView.setAdapter(new StopAdapter(getContext(), busStops));
}
public void showBusStopUsernameInputDialog(final Stop busStop) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
LayoutInflater inflater = this.getLayoutInflater();
View renameDialogLayout = inflater.inflate(R.layout.rename_dialog, null);
busStopNameText = (EditText) renameDialogLayout.findViewById(R.id.rename_dialog_bus_stop_name);
busStopNameText.setText(busStop.getStopDisplayName());
busStopNameText.setHint(busStop.getStopDefaultName());
builder.setTitle(getString(R.string.dialog_rename_bus_stop_username_title));
builder.setView(renameDialogLayout);
builder.setPositiveButton(getString(android.R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String busStopUsername = busStopNameText.getText().toString();
String oldUserName = busStop.getStopUserName();
// changed to none
if(busStopUsername.length() == 0) {
// unless it was already empty, set new
if(oldUserName != null) {
busStop.setStopUserName(null);
}
} else { // changed to something
// something different?
if(!busStopUsername.equals(oldUserName)) {
busStop.setStopUserName(busStopUsername);
}
}
launchUpdate(busStop);
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.setNeutralButton(R.string.dialog_rename_bus_stop_username_reset_button, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// delete user name from database
busStop.setStopUserName(null);
launchUpdate(busStop);
}
});
builder.show();
}
private void launchUpdate(Stop busStop){
if (getContext()!=null)
new AsyncStopFavoriteAction(getContext().getApplicationContext(), AsyncStopFavoriteAction.Action.UPDATE,
new AsyncStopFavoriteAction.ResultListener() {
@Override
public void doStuffWithResult(Boolean result) {
//Toast.makeText(getApplicationContext(), R.string.tip_add_favorite, Toast.LENGTH_SHORT).show();
}
}).execute(busStop);
}
}
diff --git a/src/it/reyboz/bustorino/fragments/FragmentKind.java b/src/it/reyboz/bustorino/fragments/FragmentKind.java
index 07f5078..6aeac73 100644
--- a/src/it/reyboz/bustorino/fragments/FragmentKind.java
+++ b/src/it/reyboz/bustorino/fragments/FragmentKind.java
@@ -1,22 +1,22 @@
/*
BusTO (fragments)
Copyright (C) 2018 Fabio Mazza
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
package it.reyboz.bustorino.fragments;
public enum FragmentKind {
- STOPS,ARRIVALS,FAVORITES,NEARBY_STOPS,NEARBY_ARRIVALS
+ STOPS,ARRIVALS,FAVORITES,NEARBY_STOPS,NEARBY_ARRIVALS, MAP, MAIN_SCREEN_FRAGMENT
}
diff --git a/src/it/reyboz/bustorino/fragments/FragmentListenerMain.java b/src/it/reyboz/bustorino/fragments/FragmentListenerMain.java
index 4a3fd61..5f8a6b1 100644
--- a/src/it/reyboz/bustorino/fragments/FragmentListenerMain.java
+++ b/src/it/reyboz/bustorino/fragments/FragmentListenerMain.java
@@ -1,40 +1,33 @@
/*
BusTO - Fragments components
Copyright (C) 2018 Fabio Mazza
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
package it.reyboz.bustorino.fragments;
import it.reyboz.bustorino.backend.Stop;
public interface FragmentListenerMain extends CommonFragmentListener {
void toggleSpinner(boolean state);
-
- /*
- Unused method
- * Add the last successfully searched stop to the favorites
- */
-
- //void toggleLastStopToFavorites();
-
+ //TODO: implement void showStopOnMap()
/**
* Tell activity that we need to enable/disable the refreshLayout
* @param yes or no
*/
void enableRefreshLayout(boolean yes);
}
diff --git a/src/it/reyboz/bustorino/fragments/MainScreenFragment.java b/src/it/reyboz/bustorino/fragments/MainScreenFragment.java
index 7f5b2d6..5c82c99 100644
--- a/src/it/reyboz/bustorino/fragments/MainScreenFragment.java
+++ b/src/it/reyboz/bustorino/fragments/MainScreenFragment.java
@@ -1,636 +1,638 @@
package it.reyboz.bustorino.fragments;
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.Build;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageButton;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.zxing.integration.android.IntentIntegrator;
import it.reyboz.bustorino.R;
import it.reyboz.bustorino.backend.ArrivalsFetcher;
import it.reyboz.bustorino.backend.FiveTAPIFetcher;
import it.reyboz.bustorino.backend.FiveTScraperFetcher;
import it.reyboz.bustorino.backend.FiveTStopsFetcher;
import it.reyboz.bustorino.backend.GTTJSONFetcher;
import it.reyboz.bustorino.backend.GTTStopsFetcher;
import it.reyboz.bustorino.backend.StopsFinderByName;
import it.reyboz.bustorino.middleware.AsyncDataDownload;
import it.reyboz.bustorino.util.Permissions;
import static android.content.Context.LOCATION_SERVICE;
import static it.reyboz.bustorino.util.Permissions.LOCATION_PERMISSION_GIVEN;
/**
* A simple {@link Fragment} subclass.
* Use the {@link MainScreenFragment#newInstance} factory method to
* create an instance of this fragment.
*/
public class MainScreenFragment extends BaseFragment implements FragmentListenerMain{
private static final String OPTION_SHOW_LEGEND = "show_legend";
private static final String SAVED_FRAGMENT="saved_fragment";
private static final String DEBUG_TAG = "BusTO - MainFragment";
public final static String FRAGMENT_TAG = "MainScreenFragment";
/// UI ELEMENTS //
private ImageButton addToFavorites;
private FragmentHelper fragmentHelper;
private SwipeRefreshLayout swipeRefreshLayout;
private EditText busStopSearchByIDEditText;
private EditText busStopSearchByNameEditText;
private ProgressBar progressBar;
private TextView howDoesItWorkTextView;
private Button hideHintButton;
private MenuItem actionHelpMenuItem;
private FloatingActionButton floatingActionButton;
private boolean setupOnAttached = true;
private boolean suppressArrivalsReload = false;
//private Snackbar snackbar;
/*
* Search mode
*/
private static final int SEARCH_BY_NAME = 0;
private static final int SEARCH_BY_ID = 1;
private static final int SEARCH_BY_ROUTE = 2; // TODO: implement this -- https://gitpull.it/T12
private int searchMode;
//private ImageButton addToFavorites;
private final ArrivalsFetcher[] arrivalsFetchers = new ArrivalsFetcher[]{new FiveTAPIFetcher(), new GTTJSONFetcher(), new FiveTScraperFetcher()};
//// HIDDEN BUT IMPORTANT ELEMENTS ////
FragmentManager fragMan;
Handler mainHandler;
private final Runnable refreshStop = new Runnable() {
public void run() {
if (fragMan.findFragmentById(R.id.resultFrame) instanceof ArrivalsFragment) {
ArrivalsFragment fragment = (ArrivalsFragment) fragMan.findFragmentById(R.id.resultFrame);
if (fragment == null){
//we create a new fragment, which is WRONG
new AsyncDataDownload(fragmentHelper, arrivalsFetchers,getContext()).execute();
} else{
String stopName = fragment.getStopID();
new AsyncDataDownload(fragmentHelper, fragment.getCurrentFetchersAsArray(), getContext()).execute(stopName);
}
} else //we create a new fragment, which is WRONG
new AsyncDataDownload(fragmentHelper, arrivalsFetchers, getContext()).execute();
}
};
/// LOCATION STUFF ///
boolean pendingNearbyStopsRequest = false;
LocationManager locmgr;
private final Criteria cr = new Criteria();
//// ACTIVITY ATTACHED (LISTENER ///
private CommonFragmentListener mListener;
private String pendingStopID = null;
public MainScreenFragment() {
// Required empty public constructor
}
public static MainScreenFragment newInstance() {
MainScreenFragment fragment = new MainScreenFragment();
Bundle args = new Bundle();
//args.putString(ARG_PARAM1, param1);
//args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
//do nothing
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View root = inflater.inflate(R.layout.fragment_main_screen, container, false);
addToFavorites = (ImageButton) root.findViewById(R.id.addToFavorites);
busStopSearchByIDEditText = root.findViewById(R.id.busStopSearchByIDEditText);
busStopSearchByNameEditText = root.findViewById(R.id.busStopSearchByNameEditText);
progressBar = root.findViewById(R.id.progressBar);
howDoesItWorkTextView = root.findViewById(R.id.howDoesItWorkTextView);
hideHintButton = root.findViewById(R.id.hideHintButton);
swipeRefreshLayout = root.findViewById(R.id.listRefreshLayout);
floatingActionButton = root.findViewById(R.id.floatingActionButton);
busStopSearchByIDEditText.setSelectAllOnFocus(true);
busStopSearchByIDEditText
.setOnEditorActionListener((v, actionId, event) -> {
// IME_ACTION_SEARCH alphabetical option
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
onSearchClick(v);
return true;
}
return false;
});
busStopSearchByNameEditText
.setOnEditorActionListener((v, actionId, event) -> {
// IME_ACTION_SEARCH alphabetical option
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
onSearchClick(v);
return true;
}
return false;
});
swipeRefreshLayout
.setOnRefreshListener(() -> mainHandler.post(refreshStop));
swipeRefreshLayout.setColorSchemeResources(R.color.blue_500, R.color.orange_500);
floatingActionButton.setOnClickListener((this::onToggleKeyboardLayout));
hideHintButton.setOnClickListener(this::onHideHint);
AppCompatImageButton qrButton = root.findViewById(R.id.QRButton);
qrButton.setOnClickListener(this::onQRButtonClick);
AppCompatImageButton searchButton = root.findViewById(R.id.searchButton);
searchButton.setOnClickListener(this::onSearchClick);
// Fragment stuff
fragMan = getChildFragmentManager();
fragMan.addOnBackStackChangedListener(() -> Log.d("BusTO Main Fragment", "BACK STACK CHANGED"));
fragmentHelper = new FragmentHelper(this, getChildFragmentManager(), getContext(), R.id.resultFrame);
setSearchModeBusStopID();
cr.setAccuracy(Criteria.ACCURACY_FINE);
cr.setAltitudeRequired(false);
cr.setBearingRequired(false);
cr.setCostAllowed(true);
cr.setPowerRequirement(Criteria.NO_REQUIREMENT);
locmgr = (LocationManager) getContext().getSystemService(LOCATION_SERVICE);
Log.d(DEBUG_TAG, "OnCreateView, savedInstanceState null: "+(savedInstanceState==null));
return root;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Log.d(DEBUG_TAG, "onViewCreated, SwipeRefreshLayout visible: "+(swipeRefreshLayout.getVisibility()==View.VISIBLE));
Log.d(DEBUG_TAG, "Setup on attached: "+setupOnAttached);
//Restore instance state
if (savedInstanceState!=null){
Fragment fragment = getChildFragmentManager().getFragment(savedInstanceState, SAVED_FRAGMENT);
if (fragment!=null){
getChildFragmentManager().beginTransaction().add(R.id.resultFrame, fragment).commit();
setupOnAttached = false;
}
}
if (getChildFragmentManager().findFragmentById(R.id.resultFrame)!= null){
swipeRefreshLayout.setVisibility(View.VISIBLE);
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
Fragment fragment = getChildFragmentManager().findFragmentById(R.id.resultFrame);
if (fragment!=null)
getChildFragmentManager().putFragment(outState, SAVED_FRAGMENT, fragment);
}
public void setSuppressArrivalsReload(boolean value){
suppressArrivalsReload = value;
// we have to suppress the reloading of the (possible) ArrivalsFragment
/*if(value) {
Fragment fragment = getChildFragmentManager().findFragmentById(R.id.resultFrame);
if (fragment instanceof ArrivalsFragment) {
ArrivalsFragment frag = (ArrivalsFragment) fragment;
frag.setReloadOnResume(false);
}
}
*/
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
Log.d(DEBUG_TAG, "OnAttach called, setupOnAttach: "+setupOnAttached);
mainHandler = new Handler();
if (context instanceof CommonFragmentListener) {
mListener = (CommonFragmentListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement CommonFragmentListener");
}
if (setupOnAttached) {
if (pendingStopID==null)
//We want the nearby bus stops!
mainHandler.post(new NearbyStopsRequester(getContext(), cr, locListener));
else{
///TODO: if there is a stop displayed, we need to hold the update
}
//If there are no providers available, then, wait for them
setupOnAttached = false;
} else {
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
// setupOnAttached = true;
}
@Override
public void onResume() {
final Context con = getContext();
if (con != null)
locmgr = (LocationManager) getContext().getSystemService(LOCATION_SERVICE);
else {
Log.w(DEBUG_TAG, "Context is null at onResume");
}
super.onResume();
// if we have a pending stopID request, do it
Log.d(DEBUG_TAG, "Pending stop ID for arrivals: "+pendingStopID);
//this is the second time we are attaching this fragment
Log.d(DEBUG_TAG, "Waiting for new stop request: "+ suppressArrivalsReload);
if (suppressArrivalsReload){
// we have to suppress the reloading of the (possible) ArrivalsFragment
Fragment fragment = getChildFragmentManager().findFragmentById(R.id.resultFrame);
if (fragment instanceof ArrivalsFragment){
ArrivalsFragment frag = (ArrivalsFragment) fragment;
frag.setReloadOnResume(false);
}
suppressArrivalsReload = false;
}
if(pendingStopID!=null){
requestArrivalsForStopID(pendingStopID);
pendingStopID = null;
}
+ mListener.readyGUIfor(FragmentKind.MAIN_SCREEN_FRAGMENT);
}
@Override
public void onPause() {
//mainHandler = null;
locmgr = null;
super.onPause();
}
+
/*
GUI METHODS
*/
/**
* QR scan button clicked
*
* @param v View QRButton clicked
*/
public void onQRButtonClick(View v) {
IntentIntegrator integrator = new IntentIntegrator(getActivity());
integrator.initiateScan();
}
public void onHideHint(View v) {
hideHints();
setOption(OPTION_SHOW_LEGEND, false);
}
/**
* OK this is pure shit
*
* @param v View clicked
*/
public void onSearchClick(View v) {
final StopsFinderByName[] stopsFinderByNames = new StopsFinderByName[]{new GTTStopsFetcher(), new FiveTStopsFetcher()};
if (searchMode == SEARCH_BY_ID) {
String busStopID = busStopSearchByIDEditText.getText().toString();
requestArrivalsForStopID(busStopID);
} else { // searchMode == SEARCH_BY_NAME
String query = busStopSearchByNameEditText.getText().toString();
//new asyncWgetBusStopSuggestions(query, stopsDB, StopsFindersByNameRecursionHelper);
new AsyncDataDownload(fragmentHelper, stopsFinderByNames, getContext()).execute(query);
}
}
public void onToggleKeyboardLayout(View v) {
if (searchMode == SEARCH_BY_NAME) {
setSearchModeBusStopID();
if (busStopSearchByIDEditText.requestFocus()) {
showKeyboard();
}
} else { // searchMode == SEARCH_BY_ID
setSearchModeBusStopName();
if (busStopSearchByNameEditText.requestFocus()) {
showKeyboard();
}
}
}
@Override
public void enableRefreshLayout(boolean yes) {
swipeRefreshLayout.setEnabled(yes);
}
////////////////////////////////////// GUI HELPERS /////////////////////////////////////////////
public void showKeyboard() {
if (getActivity() == null)
return;
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
View view = searchMode == SEARCH_BY_ID ? busStopSearchByIDEditText : busStopSearchByNameEditText;
imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
}
private void setSearchModeBusStopID() {
searchMode = SEARCH_BY_ID;
busStopSearchByNameEditText.setVisibility(View.GONE);
busStopSearchByNameEditText.setText("");
busStopSearchByIDEditText.setVisibility(View.VISIBLE);
floatingActionButton.setImageResource(R.drawable.alphabetical);
}
private void setSearchModeBusStopName() {
searchMode = SEARCH_BY_NAME;
busStopSearchByIDEditText.setVisibility(View.GONE);
busStopSearchByIDEditText.setText("");
busStopSearchByNameEditText.setVisibility(View.VISIBLE);
floatingActionButton.setImageResource(R.drawable.numeric);
}
/**
* Having that cursor at the left of the edit text makes me cancer.
*
* @param busStopID bus stop ID
*/
private void setBusStopSearchByIDEditText(String busStopID) {
busStopSearchByIDEditText.setText(busStopID);
busStopSearchByIDEditText.setSelection(busStopID.length());
}
private void showHints() {
howDoesItWorkTextView.setVisibility(View.VISIBLE);
hideHintButton.setVisibility(View.VISIBLE);
//actionHelpMenuItem.setVisible(false);
}
private void hideHints() {
howDoesItWorkTextView.setVisibility(View.GONE);
hideHintButton.setVisibility(View.GONE);
//actionHelpMenuItem.setVisible(true);
}
@Override
public void toggleSpinner(boolean enable) {
if (enable) {
//already set by the RefreshListener when needed
//swipeRefreshLayout.setRefreshing(true);
progressBar.setVisibility(View.VISIBLE);
} else {
swipeRefreshLayout.setRefreshing(false);
progressBar.setVisibility(View.GONE);
}
}
private void prepareGUIForBusLines() {
swipeRefreshLayout.setEnabled(true);
swipeRefreshLayout.setVisibility(View.VISIBLE);
//actionHelpMenuItem.setVisible(true);
}
private void prepareGUIForBusStops() {
swipeRefreshLayout.setEnabled(false);
swipeRefreshLayout.setVisibility(View.VISIBLE);
//actionHelpMenuItem.setVisible(false);
}
@Override
public void showFloatingActionButton(boolean yes) {
mListener.showFloatingActionButton(yes);
}
/**
* This provides a temporary fix to make the transition
* to a single asynctask go smoother
*
* @param fragmentType the type of fragment created
*/
@Override
public void readyGUIfor(FragmentKind fragmentType) {
hideKeyboard();
//if we are getting results, already, stop waiting for nearbyStops
if (pendingNearbyStopsRequest && (fragmentType == FragmentKind.ARRIVALS || fragmentType == FragmentKind.STOPS)) {
locmgr.removeUpdates(locListener);
pendingNearbyStopsRequest = false;
}
if (fragmentType == null) Log.e("ActivityMain", "Problem with fragmentType");
else
switch (fragmentType) {
case ARRIVALS:
prepareGUIForBusLines();
if (getOption(OPTION_SHOW_LEGEND, true)) {
showHints();
}
break;
case STOPS:
prepareGUIForBusStops();
break;
default:
- Log.e("BusTO Activity", "Called readyGUI with unsupported type of Fragment");
+ Log.d(DEBUG_TAG, "Fragment type is unknown");
return;
}
// Shows hints
}
/**
* Main method for stops requests
* @param ID the Stop ID
*/
@Override
public void requestArrivalsForStopID(String ID) {
if (!isResumed()){
//defer request
pendingStopID = ID;
Log.d(DEBUG_TAG, "Deferring update for stop "+ID);
return;
}
final boolean delayedRequest = !(pendingStopID==null);
final FragmentManager framan = getChildFragmentManager();
if (getContext()==null){
Log.e(DEBUG_TAG, "Asked for arrivals with null context");
return;
}
if (ID == null || ID.length() <= 0) {
// we're still in UI thread, no need to mess with Progress
showToastMessage(R.string.insert_bus_stop_number_error, true);
toggleSpinner(false);
} else if (framan.findFragmentById(R.id.resultFrame) instanceof ArrivalsFragment) {
ArrivalsFragment fragment = (ArrivalsFragment) framan.findFragmentById(R.id.resultFrame);
if (fragment != null && fragment.getStopID() != null && fragment.getStopID().equals(ID)){
// Run with previous fetchers
//fragment.getCurrentFetchers().toArray()
new AsyncDataDownload(fragmentHelper,fragment.getCurrentFetchersAsArray(), getContext()).execute(ID);
} else{
new AsyncDataDownload(fragmentHelper, arrivalsFetchers, getContext()).execute(ID);
}
}
else {
new AsyncDataDownload(fragmentHelper,arrivalsFetchers, getContext()).execute(ID);
Log.d(DEBUG_TAG, "Started search for arrivals of stop " + ID);
}
}
/////////// LOCATION METHODS //////////
final LocationListener locListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
Log.d(DEBUG_TAG, "Location changed");
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
Log.d(DEBUG_TAG, "Location provider status: " + status);
if (status == LocationProvider.AVAILABLE) {
resolveStopRequest(provider);
}
}
@Override
public void onProviderEnabled(String provider) {
resolveStopRequest(provider);
}
@Override
public void onProviderDisabled(String provider) {
}
};
private void resolveStopRequest(String provider) {
Log.d(DEBUG_TAG, "Provider " + provider + " got enabled");
if (locmgr != null && mainHandler != null && pendingNearbyStopsRequest && locmgr.getProvider(provider).meetsCriteria(cr)) {
pendingNearbyStopsRequest = false;
mainHandler.post(new NearbyStopsRequester(getContext(), cr, locListener));
}
}
/**
* Run location requests separately and asynchronously
*/
class NearbyStopsRequester implements Runnable {
Context appContext;
Criteria cr;
LocationListener listener;
public NearbyStopsRequester(Context appContext, Criteria criteria, LocationListener listener) {
this.appContext = appContext.getApplicationContext();
this.cr = criteria;
this.listener = listener;
}
@Override
public void run() {
final boolean canRunPosition = Build.VERSION.SDK_INT < Build.VERSION_CODES.M || getOption(LOCATION_PERMISSION_GIVEN, false);
final boolean noPermission = ActivityCompat.checkSelfPermission(appContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(appContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED;
//if we don't have the permission, we have to ask for it, if we haven't
// asked too many times before
if (noPermission) {
if (!canRunPosition) {
pendingNearbyStopsRequest = true;
Permissions.assertLocationPermissions(appContext,getActivity());
Log.w(DEBUG_TAG, "Cannot get position: Asking permission, noPositionFromSys: " + noPermission);
return;
} else {
Toast.makeText(appContext, "Asked for permission position too many times", Toast.LENGTH_LONG).show();
}
} else setOption(LOCATION_PERMISSION_GIVEN, true);
LocationManager locManager = (LocationManager) appContext.getSystemService(LOCATION_SERVICE);
if (locManager == null) {
Log.e(DEBUG_TAG, "location manager is nihil, cannot create NearbyStopsFragment");
return;
}
if (Permissions.anyLocationProviderMatchesCriteria(locManager, cr, true)
&& fragmentHelper.getLastSuccessfullySearchedBusStop() == null
&& !fragMan.isDestroyed()) {
//Go ahead with the request
Log.d("mainActivity", "Recreating stop fragment");
swipeRefreshLayout.setVisibility(View.VISIBLE);
NearbyStopsFragment fragment = NearbyStopsFragment.newInstance(NearbyStopsFragment.TYPE_STOPS);
Fragment oldFrag = fragMan.findFragmentById(R.id.resultFrame);
FragmentTransaction ft = fragMan.beginTransaction();
if (oldFrag != null)
ft.remove(oldFrag);
ft.add(R.id.resultFrame, fragment, "nearbyStop_correct");
ft.commit();
//fragMan.executePendingTransactions();
pendingNearbyStopsRequest = false;
} else if (!Permissions.anyLocationProviderMatchesCriteria(locManager, cr, true)) {
//Wait for the providers
Log.d(DEBUG_TAG, "Queuing position request");
pendingNearbyStopsRequest = true;
locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10, 0.1f, listener);
}
}
}
}
\ No newline at end of file
diff --git a/src/it/reyboz/bustorino/fragments/MapFragment.java b/src/it/reyboz/bustorino/fragments/MapFragment.java
new file mode 100644
index 0000000..f7630e0
--- /dev/null
+++ b/src/it/reyboz/bustorino/fragments/MapFragment.java
@@ -0,0 +1,532 @@
+package it.reyboz.bustorino.fragments;
+
+import android.Manifest;
+import android.content.Context;
+
+import android.content.pm.PackageManager;
+import android.location.Location;
+import android.location.LocationManager;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.res.ResourcesCompat;
+import androidx.preference.PreferenceManager;
+
+import org.osmdroid.api.IGeoPoint;
+import org.osmdroid.api.IMapController;
+import org.osmdroid.config.Configuration;
+import org.osmdroid.events.DelayedMapListener;
+import org.osmdroid.events.MapListener;
+import org.osmdroid.events.ScrollEvent;
+import org.osmdroid.events.ZoomEvent;
+import org.osmdroid.tileprovider.tilesource.TileSourceFactory;
+import org.osmdroid.util.BoundingBox;
+import org.osmdroid.util.GeoPoint;
+import org.osmdroid.views.MapView;
+import org.osmdroid.views.overlay.FolderOverlay;
+import org.osmdroid.views.overlay.Marker;
+import org.osmdroid.views.overlay.infowindow.InfoWindow;
+import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider;
+
+import java.lang.ref.WeakReference;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+import it.reyboz.bustorino.R;
+import it.reyboz.bustorino.backend.Stop;
+import it.reyboz.bustorino.data.NextGenDB;
+import it.reyboz.bustorino.map.CustomInfoWindow;
+import it.reyboz.bustorino.map.LocationOverlay;
+import it.reyboz.bustorino.middleware.GeneralActivity;
+
+import static it.reyboz.bustorino.util.Permissions.PERMISSION_REQUEST_POSITION;
+
+public class MapFragment extends BaseFragment {
+
+ private static final String TAG = "Busto-MapActivity";
+ private static final String MAP_CURRENT_ZOOM_KEY = "map-current-zoom";
+ private static final String MAP_CENTER_LAT_KEY = "map-center-lat";
+ private static final String MAP_CENTER_LON_KEY = "map-center-lon";
+ private static final String FOLLOWING_LOCAT_KEY ="following";
+
+ public static final String BUNDLE_LATIT = "lat";
+ public static final String BUNDLE_LONGIT = "lon";
+ public static final String BUNDLE_NAME = "name";
+ public static final String BUNDLE_ID = "ID";
+
+ public static final String FRAGMENT_TAG="BusTOMapFragment";
+
+
+ private static final double DEFAULT_CENTER_LAT = 45.0708;
+ private static final double DEFAULT_CENTER_LON = 7.6858;
+ private static final double POSITION_FOUND_ZOOM = 18.3;
+
+ private static final String DEBUG_TAG=FRAGMENT_TAG;
+
+ protected FragmentListenerMain listenerMain;
+
+ private HashSet shownStops = null;
+
+
+ private MapView map = null;
+ public Context ctx;
+ private LocationOverlay mLocationOverlay = null;
+ private FolderOverlay stopsFolderOverlay = null;
+ private Bundle savedMapState = null;
+ protected ImageButton btCenterMap;
+ protected ImageButton btFollowMe;
+ private boolean followingLocation = false;
+
+ protected final CustomInfoWindow.TouchResponder responder = new CustomInfoWindow.TouchResponder() {
+ @Override
+ public void onActionUp(@NonNull String stopID, @Nullable String stopName) {
+ if (listenerMain!= null){
+ listenerMain.requestArrivalsForStopID(stopID);
+ }
+ }
+ };
+ protected final LocationOverlay.OverlayCallbacks locationCallbacks = new LocationOverlay.OverlayCallbacks() {
+ @Override
+ public void onDisableFollowMyLocation() {
+ updateGUIForLocationFollowing(false);
+ followingLocation=false;
+ }
+
+ @Override
+ public void onEnableFollowMyLocation() {
+ updateGUIForLocationFollowing(true);
+ followingLocation=true;
+ }
+ };
+
+ public MapFragment() {
+ }
+ public static MapFragment getInstance(){
+ return new MapFragment();
+ }
+ public static MapFragment getInstance(double stopLatit, double stopLong, String stopName, String stopID){
+ MapFragment fragment= new MapFragment();
+ Bundle args = new Bundle();
+ args.putDouble(BUNDLE_LATIT, stopLatit);
+ args.putDouble(BUNDLE_LONGIT, stopLong);
+ args.putString(BUNDLE_NAME, stopName);
+ args.putString(BUNDLE_ID, stopID);
+ fragment.setArguments(args);
+
+ return fragment;
+ }
+ public static MapFragment getInstance(Stop stop){
+ return getInstance(stop.getLatitude(), stop.getLongitude(), stop.getStopDisplayName(), stop.ID);
+ }
+
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ //use the same layout as the activity
+ View root = inflater.inflate(R.layout.activity_map, container, false);
+ if (getContext() == null){
+ throw new IllegalStateException();
+ }
+ ctx = getContext().getApplicationContext();
+ Configuration.getInstance().load(ctx, PreferenceManager.getDefaultSharedPreferences(ctx));
+ map = root.findViewById(R.id.map);
+ map.setTileSource(TileSourceFactory.MAPNIK);
+ //map.setTilesScaledToDpi(true);
+ map.setFlingEnabled(true);
+
+ // add ability to zoom with 2 fingers
+ map.setMultiTouchControls(true);
+
+ btCenterMap = root.findViewById(R.id.ic_center_map);
+ btFollowMe = root.findViewById(R.id.ic_follow_me);
+
+ //setup FolderOverlay
+ stopsFolderOverlay = new FolderOverlay();
+
+
+ //Start map from bundle
+ if (savedInstanceState !=null)
+ startMap(getArguments(), savedInstanceState);
+ else startMap(getArguments(), savedMapState);
+ //set listeners
+ map.addMapListener(new DelayedMapListener(new MapListener() {
+
+ @Override
+ public boolean onScroll(ScrollEvent paramScrollEvent) {
+ requestStopsToShow();
+ //Log.d(DEBUG_TAG, "Scrolling");
+ //if (moveTriggeredByCode) moveTriggeredByCode =false;
+ //else setLocationFollowing(false);
+ return true;
+ }
+
+ @Override
+ public boolean onZoom(ZoomEvent event) {
+ requestStopsToShow();
+ return true;
+ }
+
+ }));
+
+
+ btCenterMap.setOnClickListener(v -> {
+ //Log.i(TAG, "centerMap clicked ");
+ final GeoPoint myPosition = mLocationOverlay.getMyLocation();
+ map.getController().animateTo(myPosition);
+ });
+
+ btFollowMe.setOnClickListener(v -> {
+ //Log.i(TAG, "btFollowMe clicked ");
+ switchLocationFollowing(!followingLocation);
+ });
+
+ return root;
+ }
+
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+
+ if (context instanceof FragmentListenerMain) {
+ listenerMain = (FragmentListenerMain) context;
+ } else {
+ throw new RuntimeException(context.toString()
+ + " must implement FragmentListenerMain");
+ }
+ }
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ listenerMain = null;
+ // setupOnAttached = true;
+ Log.w(DEBUG_TAG, "Fragment detached");
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ saveMapState();
+ }
+
+ /**
+ * Save the map state inside the fragment
+ * (calls saveMapState(bundle))
+ */
+ private void saveMapState(){
+ savedMapState = new Bundle();
+ saveMapState(savedMapState);
+
+ }
+
+ /**
+ * Save the state of the map to restore it to a later time
+ * @param bundle the bundle in which to save the data
+ */
+ private void saveMapState(Bundle bundle){
+ final IGeoPoint loc = map.getMapCenter();
+ bundle.putDouble(MAP_CENTER_LAT_KEY, loc.getLatitude());
+ bundle.putDouble(MAP_CENTER_LON_KEY, loc.getLongitude());
+ bundle.putDouble(MAP_CURRENT_ZOOM_KEY, map.getZoomLevelDouble());
+ Log.d(DEBUG_TAG, "Saving state, location following: "+followingLocation);
+ bundle.putBoolean(FOLLOWING_LOCAT_KEY, followingLocation);
+
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if(listenerMain!=null) listenerMain.readyGUIfor(FragmentKind.MAP);
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ saveMapState(outState);
+
+ super.onSaveInstanceState(outState);
+ }
+
+ //own methods
+
+ /**
+ * Switch following the location on and off
+ * @param value true if we want to follow location
+ */
+ public void switchLocationFollowing(Boolean value){
+ followingLocation = value;
+ if (value){
+ mLocationOverlay.enableFollowLocation();
+ } else {
+ mLocationOverlay.disableFollowLocation();
+ }
+ }
+
+ /**
+ * Do all the stuff you need to do on the gui, when parameter is changed to value
+ * @param following value
+ */
+ protected void updateGUIForLocationFollowing(boolean following){
+ if (following)
+ btFollowMe.setImageResource(R.drawable.ic_follow_me_on);
+ else
+ btFollowMe.setImageResource(R.drawable.ic_follow_me);
+
+ }
+
+ public void startMap(Bundle incoming, Bundle savedInstanceState) {
+ //Check that we're attached
+ GeneralActivity activity = getActivity() instanceof GeneralActivity ? (GeneralActivity) getActivity() : null;
+ if(getContext()==null|| activity==null){
+ //we are not attached
+ Log.e(DEBUG_TAG, "Calling startMap when not attached");
+ return;
+ }else{
+ Log.d(DEBUG_TAG, "Starting map from scratch");
+ }
+
+
+ //parse incoming bundle
+ GeoPoint marker = null;
+ String name = null;
+ String ID = null;
+ if (incoming != null) {
+ double lat = incoming.getDouble(BUNDLE_LATIT);
+ double lon = incoming.getDouble(BUNDLE_LONGIT);
+ marker = new GeoPoint(lat, lon);
+ name = incoming.getString(BUNDLE_NAME);
+ ID = incoming.getString(BUNDLE_ID);
+ }
+
+ shownStops = new HashSet<>();
+ // move the map on the marker position or on a default view point: Turin, Piazza Castello
+ // and set the start zoom
+ IMapController mapController = map.getController();
+ GeoPoint startPoint = null;
+
+ boolean havePositionPermission = true;
+
+ if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
+ activity.askForPermissionIfNeeded(Manifest.permission.ACCESS_FINE_LOCATION, PERMISSION_REQUEST_POSITION);
+ havePositionPermission = false;
+ }
+ // Location Overlay
+ // from OpenBikeSharing (THANK GOD)
+ GpsMyLocationProvider imlp = new GpsMyLocationProvider(activity.getBaseContext());
+ imlp.setLocationUpdateMinDistance(5);
+ imlp.setLocationUpdateMinTime(2000);
+ this.mLocationOverlay = new LocationOverlay(imlp,map, locationCallbacks);
+ mLocationOverlay.enableMyLocation();
+ mLocationOverlay.setOptionsMenuEnabled(true);
+
+ if (marker != null) {
+ startPoint = marker;
+ mapController.setZoom(POSITION_FOUND_ZOOM);
+ switchLocationFollowing(false);
+ } else if (savedInstanceState != null) {
+ mapController.setZoom(savedInstanceState.getDouble(MAP_CURRENT_ZOOM_KEY));
+ mapController.setCenter(new GeoPoint(savedInstanceState.getDouble(MAP_CENTER_LAT_KEY),
+ savedInstanceState.getDouble(MAP_CENTER_LON_KEY)));
+ Log.d(DEBUG_TAG, "Location following from savedInstanceState: "+savedInstanceState.getBoolean(FOLLOWING_LOCAT_KEY));
+ switchLocationFollowing(savedInstanceState.getBoolean(FOLLOWING_LOCAT_KEY));
+ } else {
+ Log.d(DEBUG_TAG, "No position found from intent or saved state");
+ boolean found = false;
+ LocationManager locationManager =
+ (LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE);
+ if (locationManager != null) {
+
+ Location userLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
+ if (userLocation != null) {
+ mapController.setZoom(POSITION_FOUND_ZOOM);
+ startPoint = new GeoPoint(userLocation);
+ found = true;
+ switchLocationFollowing(true);
+ }
+ }
+ if(!found){
+ startPoint = new GeoPoint(DEFAULT_CENTER_LAT, DEFAULT_CENTER_LON);
+ mapController.setZoom(16.0);
+ switchLocationFollowing(false);
+ }
+ }
+
+ // set the minimum zoom level
+ map.setMinZoomLevel(15.0);
+ //add contingency check (shouldn't happen..., but)
+ if (startPoint != null) {
+ mapController.setCenter(startPoint);
+ }
+
+
+
+ map.getOverlays().add(this.mLocationOverlay);
+
+ //add stops overlay
+ map.getOverlays().add(this.stopsFolderOverlay);
+
+ Log.d(DEBUG_TAG, "Requesting stops load");
+ // This is not necessary, by setting the center we already move
+ // the map and we trigger a stop request
+ //requestStopsToShow();
+ if (marker != null) {
+ // make a marker with the info window open for the searched marker
+ makeMarker(startPoint, name , ID, true);
+ }
+
+ }
+
+ /**
+ * Start a request to load the stops that are in the current view
+ * from the database
+ */
+ private void requestStopsToShow(){
+ // get the top, bottom, left and right screen's coordinate
+ BoundingBox bb = map.getBoundingBox();
+ double latFrom = bb.getLatSouth();
+ double latTo = bb.getLatNorth();
+ double lngFrom = bb.getLonWest();
+ double lngTo = bb.getLonEast();
+
+ new AsyncStopFetcher(this).execute(
+ new AsyncStopFetcher.BoundingBoxLimit(lngFrom,lngTo,latFrom, latTo));
+ }
+
+ /**
+ * Add stops as Markers on the map
+ * @param stops the list of stops that must be included
+ */
+ protected void showStopsMarkers(List stops){
+
+ for (Stop stop : stops) {
+ if (shownStops.contains(stop.ID)){
+ continue;
+ }
+ if(stop.getLongitude()==null || stop.getLatitude()==null)
+ continue;
+
+ shownStops.add(stop.ID);
+ GeoPoint marker = new GeoPoint(stop.getLatitude(), stop.getLongitude());
+ Marker stopMarker = makeMarker(marker, stop.getStopDefaultName(), stop.ID, false);
+ stopsFolderOverlay.add(stopMarker);
+ if (!map.getOverlays().contains(stopsFolderOverlay)) {
+ Log.w(DEBUG_TAG, "Map doesn't have folder overlay");
+ }
+ }
+ //Log.d(DEBUG_TAG,"We have " +stopsFolderOverlay.getItems().size()+" stops in the folderOverlay");
+ //force redraw of markers
+ map.invalidate();
+ }
+
+ public Marker makeMarker(GeoPoint geoPoint, String stopName, String ID, boolean isStartMarker) {
+
+ // add a marker
+ Marker marker = new Marker(map);
+
+ // set custom info window as info window
+ CustomInfoWindow popup = new CustomInfoWindow(map, ID, stopName, responder);
+ marker.setInfoWindow(popup);
+
+ // make the marker clickable
+ marker.setOnMarkerClickListener((thisMarker, mapView) -> {
+ if (thisMarker.isInfoWindowOpen()) {
+ // on second click
+ //TODO: show the arrivals for the stop
+ Log.w(DEBUG_TAG, "Pressed on the click marker");
+ } else {
+ // on first click
+
+ // hide all opened info window
+ InfoWindow.closeAllInfoWindowsOn(map);
+ // show this particular info window
+ thisMarker.showInfoWindow();
+ // move the map to its position
+ map.getController().animateTo(thisMarker.getPosition());
+ }
+
+ return true;
+ });
+
+ // set its position
+ marker.setPosition(geoPoint);
+ marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM);
+ // add to it an icon
+ //marker.setIcon(getResources().getDrawable(R.drawable.bus_marker));
+
+ marker.setIcon(ResourcesCompat.getDrawable(getResources(), R.drawable.bus_marker, ctx.getTheme()));
+ // add to it a title
+ marker.setTitle(stopName);
+ // set the description as the ID
+ marker.setSnippet(ID);
+
+ // show popup info window of the searched marker
+ if (isStartMarker) {
+ marker.showInfoWindow();
+ }
+
+ return marker;
+ }
+
+ /**
+ * Simple asyncTask class to load the stops in the background
+ * Holds a weak reference to the fragment to do callbacks
+ */
+ static class AsyncStopFetcher extends AsyncTask>{
+
+ final WeakReference fragmentWeakReference;
+
+ public AsyncStopFetcher(MapFragment fragment) {
+ this.fragmentWeakReference = new WeakReference<>(fragment);
+ }
+
+ @Override
+ protected List doInBackground(BoundingBoxLimit... limits) {
+ if(fragmentWeakReference.get()==null || fragmentWeakReference.get().getContext() == null){
+ Log.w(DEBUG_TAG, "AsyncLoad fragmentWeakreference null");
+
+ return null;
+
+ }
+ final BoundingBoxLimit limit = limits[0];
+ //Log.d(DEBUG_TAG, "Async Stop Fetcher started working");
+
+ NextGenDB dbHelper = new NextGenDB(fragmentWeakReference.get().getContext());
+ Stop[] stops = dbHelper.queryAllInsideMapView(limit.latitFrom, limit.latitTo,
+ limit.longFrom, limit.latitTo);
+ dbHelper.close();
+ return Arrays.asList(stops);
+ }
+
+ @Override
+ protected void onPostExecute(List stops) {
+ super.onPostExecute(stops);
+ //Log.d(DEBUG_TAG, "Async Stop Fetcher has finished working");
+ if(fragmentWeakReference.get()==null) {
+ Log.w(DEBUG_TAG, "AsyncLoad fragmentWeakreference null");
+ return;
+ }
+ Log.d(DEBUG_TAG, "AsyncLoad number of stops: "+stops.size());
+ fragmentWeakReference.get().showStopsMarkers(stops);
+ }
+
+ private static class BoundingBoxLimit{
+ final double longFrom, longTo, latitFrom, latitTo;
+
+ public BoundingBoxLimit(double longFrom, double longTo, double latitFrom, double latitTo) {
+ this.longFrom = longFrom;
+ this.longTo = longTo;
+ this.latitFrom = latitFrom;
+ this.latitTo = latitTo;
+ }
+ }
+
+ }
+}
diff --git a/src/it/reyboz/bustorino/map/CustomInfoWindow.java b/src/it/reyboz/bustorino/map/CustomInfoWindow.java
index 8c4a9c7..57442cd 100644
--- a/src/it/reyboz/bustorino/map/CustomInfoWindow.java
+++ b/src/it/reyboz/bustorino/map/CustomInfoWindow.java
@@ -1,60 +1,64 @@
package it.reyboz.bustorino.map;
import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
+import android.os.Build;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
-import org.osmdroid.api.IMapView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import org.osmdroid.views.MapView;
import org.osmdroid.views.overlay.infowindow.BasicInfoWindow;
-import it.reyboz.bustorino.ActivityMain;
import it.reyboz.bustorino.R;
public class CustomInfoWindow extends BasicInfoWindow {
+ //TODO: Make the action on the Click customizable
+ private final TouchResponder touchResponder;
+ private final String stopID, name;
@Override
public void onOpen(Object item) {
super.onOpen(item);
-
TextView descr_textView = (TextView) mView.findViewById(R.id.bubble_description);
CharSequence text = descr_textView.getText();
if (text==null || !text.toString().isEmpty()){
descr_textView.setVisibility(View.VISIBLE);
} else
descr_textView.setVisibility(View.GONE);
- mView.setElevation(3.2f);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ mView.setElevation(3.2f);
+ }
}
@SuppressLint("ClickableViewAccessibility")
- public CustomInfoWindow(MapView mapView, String ID, String stopName) {
+ public CustomInfoWindow(MapView mapView, String stopID, String name, TouchResponder responder) {
// get the personalized layout
super(R.layout.map_popup, mapView);
+ touchResponder =responder;
+ this.stopID = stopID;
+ this.name = name;
// make clickable
mView.setOnTouchListener((View v, MotionEvent e) -> {
if (e.getAction() == MotionEvent.ACTION_UP) {
// on click
-
- // create an intent with these extras
- Intent intent = new Intent(mapView.getContext(), ActivityMain.class);
- Bundle b = new Bundle();
- b.putString("bus-stop-ID", ID);
- b.putString("bus-stop-display-name", stopName);
- intent.putExtras(b);
- intent.setFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
-
- // start ActivityMain with the previous intent
- mapView.getContext().startActivity(intent);
+ touchResponder.onActionUp(stopID, name);
}
return true;
});
}
+ public interface TouchResponder{
+ /**
+ * React to a click on the stop View
+ * @param stopID the stop id
+ * @param stopName the stop name
+ */
+ void onActionUp(@NonNull String stopID, @Nullable String stopName);
+ }
}
diff --git a/src/it/reyboz/bustorino/map/LocationOverlay.java b/src/it/reyboz/bustorino/map/LocationOverlay.java
new file mode 100644
index 0000000..483a87f
--- /dev/null
+++ b/src/it/reyboz/bustorino/map/LocationOverlay.java
@@ -0,0 +1,62 @@
+/*
+ BusTO (middleware)
+ 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.map;
+
+
+import org.osmdroid.views.MapView;
+import org.osmdroid.views.overlay.mylocation.IMyLocationProvider;
+import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay;
+
+public class LocationOverlay extends MyLocationNewOverlay {
+
+ final OverlayCallbacks callbacks;
+
+ public LocationOverlay(MapView mapView, OverlayCallbacks callbacks) {
+ super(mapView);
+ this.callbacks = callbacks;
+ }
+
+ public LocationOverlay(IMyLocationProvider myLocationProvider, MapView mapView, OverlayCallbacks callbacks) {
+ super(myLocationProvider, mapView);
+ this.callbacks = callbacks;
+ }
+
+ @Override
+ public void enableFollowLocation() {
+ super.enableFollowLocation();
+ callbacks.onEnableFollowMyLocation();
+ }
+
+ @Override
+ public void disableFollowLocation() {
+ super.disableFollowLocation();
+ callbacks.onDisableFollowMyLocation();
+ }
+
+ public interface OverlayCallbacks{
+ /**
+ * Called right after disableFollowMyLocation
+ */
+ void onDisableFollowMyLocation();
+
+ /**
+ * Called right after enableFollowMyLocation
+ */
+ void onEnableFollowMyLocation();
+ }
+}
diff --git a/src/it/reyboz/bustorino/middleware/AsyncDataDownload.java b/src/it/reyboz/bustorino/middleware/AsyncDataDownload.java
index 047e62e..cb33819 100644
--- a/src/it/reyboz/bustorino/middleware/AsyncDataDownload.java
+++ b/src/it/reyboz/bustorino/middleware/AsyncDataDownload.java
@@ -1,329 +1,347 @@
/*
BusTO (middleware)
Copyright (C) 2018 Fabio Mazza
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
package it.reyboz.bustorino.middleware;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.SQLException;
import android.net.Uri;
import android.os.AsyncTask;
import androidx.annotation.NonNull;
import android.util.Log;
import it.reyboz.bustorino.backend.*;
import it.reyboz.bustorino.data.AppDataProvider;
import it.reyboz.bustorino.data.NextGenDB;
import it.reyboz.bustorino.fragments.FragmentHelper;
import it.reyboz.bustorino.data.NextGenDB.Contract.*;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.Calendar;
/**
* This should be used to download data, but not to display it
*/
public class AsyncDataDownload extends AsyncTask{
private static final String TAG = "BusTO-DataDownload";
+ private static final String DEBUG_TAG = TAG;
private boolean failedAll = false;
private final AtomicReference res;
private final RequestType t;
private String query;
WeakReference helperRef;
private final ArrayList otherActivities = new ArrayList<>();
private final Fetcher[] theFetchers;
private Context context;
private final boolean replaceFragment;
public AsyncDataDownload(FragmentHelper fh, @NonNull Fetcher[] fetchers, Context context) {
RequestType type;
helperRef = new WeakReference<>(fh);
fh.setLastTaskRef(new WeakReference<>(this));
res = new AtomicReference<>();
this.context = context.getApplicationContext();
this.replaceFragment = true;
theFetchers = fetchers;
if (theFetchers.length < 1){
throw new IllegalArgumentException("You have to put at least one Fetcher, idiot!");
}
if (theFetchers[0] instanceof ArrivalsFetcher){
type = RequestType.ARRIVALS;
} else if (theFetchers[0] instanceof StopsFinderByName){
type = RequestType.STOPS;
} else{
type = null;
}
t = type;
}
@Override
protected Object doInBackground(String... params) {
RecursionHelper r = new RecursionHelper<>(theFetchers);
boolean success=false;
Object result;
FragmentHelper fh = helperRef.get();
//If the FragmentHelper is null, that means the activity doesn't exist anymore
if (fh == null){
return null;
}
//Log.d(TAG,"refresh layout reference is: "+fh.isRefreshLayoutReferenceTrue());
while(r.valid()) {
if(this.isCancelled()) {
return null;
}
//get the data from the fetcher
switch (t){
case ARRIVALS:
ArrivalsFetcher f = (ArrivalsFetcher) r.getAndMoveForward();
Log.d(TAG,"Using the ArrivalsFetcher: "+f.getClass());
Stop lastSearchedBusStop = fh.getLastSuccessfullySearchedBusStop();
Palina p;
String stopID;
if(params.length>0)
stopID=params[0]; //(it's a Palina)
else if(lastSearchedBusStop!=null)
stopID = lastSearchedBusStop.ID; //(it's a Palina)
else {
publishProgress(Fetcher.result.QUERY_TOO_SHORT);
return null;
}
//Skip the FiveTAPIFetcher for the Metro Stops because it shows incomprehensible arrival times
if(f instanceof FiveTAPIFetcher && Integer.parseInt(stopID)>= 8200)
continue;
p= f.ReadArrivalTimesAll(stopID,res);
publishProgress(res.get());
if(f instanceof FiveTAPIFetcher){
AtomicReference gres = new AtomicReference<>();
List branches = ((FiveTAPIFetcher) f).getDirectionsForStop(stopID,gres);
if(gres.get() == Fetcher.result.OK){
p.addInfoFromRoutes(branches);
Thread t = new Thread(new BranchInserter(branches, context));
t.start();
otherActivities.add(t);
}
//put updated values into Database
}
if(lastSearchedBusStop != null && res.get()== Fetcher.result.OK) {
// check that we don't have the same stop
if(lastSearchedBusStop.ID.equals(p.ID)) {
// searched and it's the same
String sn = lastSearchedBusStop.getStopDisplayName();
if(sn != null) {
// "merge" Stop over Palina and we're good to go
p.mergeNameFrom(lastSearchedBusStop);
}
}
}
result = p;
//TODO: find a way to avoid overloading the user with toasts
break;
case STOPS:
StopsFinderByName finder = (StopsFinderByName) r.getAndMoveForward();
List resultList= finder.FindByName(params[0], this.res); //it's a List
Log.d(TAG,"Using the StopFinderByName: "+finder.getClass());
query =params[0];
result = resultList; //dummy result
break;
default:
result = null;
}
//find if it went well
if(res.get()== Fetcher.result.OK) {
//wait for other threads to finish
for(Thread t: otherActivities){
try {
t.join();
} catch (InterruptedException e) {
//do nothing
}
}
return result;
}
}
//at this point, we are sure that the result has been negative
failedAll=true;
return null;
}
@Override
protected void onProgressUpdate(Fetcher.result... values) {
FragmentHelper fh = helperRef.get();
if (fh!=null)
for (Fetcher.result r : values){
//TODO: make Toast
fh.showErrorMessage(r);
}
else {
Log.w(TAG,"We had to show some progress but activity was destroyed");
}
}
@Override
protected void onPostExecute(Object o) {
FragmentHelper fh = helperRef.get();
if(failedAll || o == null || fh == null){
//everything went bad
if(fh!=null) fh.toggleSpinner(false);
cancel(true);
//TODO: send message here
return;
}
if(isCancelled()) return;
switch (t){
case ARRIVALS:
Palina palina = (Palina) o;
fh.createOrUpdateStopFragment(palina, replaceFragment);
break;
case STOPS:
//this should never be a problem
- List stopList = (List) o;
+ if(!(o instanceof List>)){
+ throw new IllegalStateException();
+ }
+ List> list = (List>) o;
+ if (list.size() ==0) return;
+ Object firstItem = list.get(0);
+ if(!(firstItem instanceof Stop)) return;
+ ArrayList stops = new ArrayList<>();
+ for(Object x: list){
+ if(x instanceof Stop) stops.add((Stop) x);
+ }
+ if(list.size() != stops.size()){
+ Log.w(DEBUG_TAG, "Wrong stop list size:\n incoming: "+
+ list.size()+" out: "+stops.size());
+ }
+ //List stopList = (List) list;
if(query!=null && !isCancelled()) {
- fh.createStopListFragment(stopList,query, replaceFragment);
+ fh.createStopListFragment(stops,query, replaceFragment);
} else Log.e(TAG,"QUERY NULL, COULD NOT CREATE FRAGMENT");
break;
case DBUPDATE:
break;
}
}
@Override
protected void onCancelled() {
FragmentHelper fh = helperRef.get();
if (fh!=null) fh.toggleSpinner(false);
}
@Override
protected void onPreExecute() {
FragmentHelper fh = helperRef.get();
if (fh!=null) fh.toggleSpinner(true);
}
public enum RequestType {
ARRIVALS,STOPS,DBUPDATE
}
- public class BranchInserter implements Runnable{
+ public static class BranchInserter implements Runnable{
private final List routesToInsert;
private final Context context;
- private final NextGenDB nextGenDB;
+ //private final NextGenDB nextGenDB;
public BranchInserter(List routesToInsert,@NonNull Context con) {
this.routesToInsert = routesToInsert;
- this.context = con;
- nextGenDB = new NextGenDB(context);
+ this.context = con.getApplicationContext();
+ //nextGenDB = new NextGenDB(context);
}
@Override
public void run() {
+ final NextGenDB nextGenDB = new NextGenDB(context);
ContentValues[] values = new ContentValues[routesToInsert.size()];
ArrayList connectionsVals = new ArrayList<>(routesToInsert.size()*4);
long starttime,endtime;
for (Route r:routesToInsert){
//if it has received an interrupt, stop
if(Thread.interrupted()) return;
//otherwise, build contentValues
final ContentValues cv = new ContentValues();
cv.put(BranchesTable.COL_BRANCHID,r.branchid);
cv.put(LinesTable.COLUMN_NAME,r.getName());
cv.put(BranchesTable.COL_DIRECTION,r.destinazione);
cv.put(BranchesTable.COL_DESCRIPTION,r.description);
for (int day :r.serviceDays) {
switch (day){
case Calendar.MONDAY:
cv.put(BranchesTable.COL_LUN,1);
break;
case Calendar.TUESDAY:
cv.put(BranchesTable.COL_MAR,1);
break;
case Calendar.WEDNESDAY:
cv.put(BranchesTable.COL_MER,1);
break;
case Calendar.THURSDAY:
cv.put(BranchesTable.COL_GIO,1);
break;
case Calendar.FRIDAY:
cv.put(BranchesTable.COL_VEN,1);
break;
case Calendar.SATURDAY:
cv.put(BranchesTable.COL_SAB,1);
break;
case Calendar.SUNDAY:
cv.put(BranchesTable.COL_DOM,1);
break;
}
}
if(r.type!=null) cv.put(BranchesTable.COL_TYPE, r.type.getCode());
cv.put(BranchesTable.COL_FESTIVO, r.festivo.getCode());
values[routesToInsert.indexOf(r)] = cv;
for(int i=0; i