diff --git a/build.gradle b/build.gradle
--- a/build.gradle
+++ b/build.gradle
@@ -12,13 +12,15 @@
}
ext {
//libraries versions
- fragment_version = "1.3.0"
+ 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"
}
}
@@ -82,7 +84,7 @@
//new libraries
implementation "androidx.fragment:fragment:$fragment_version"
implementation "androidx.activity:activity:$activity_version"
- implementation "androidx.annotation:annotation:1.1.0"
+ 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"
@@ -95,7 +97,7 @@
implementation 'org.jsoup:jsoup:1.13.1'
implementation 'com.readystatesoftware.sqliteasset:sqliteassethelper:2.0.1'
- implementation 'com.android.volley:volley:1.1.1'
+ implementation 'com.android.volley:volley:1.2.0'
implementation 'org.osmdroid:osmdroid-android:6.1.8'
// ACRA
@@ -104,5 +106,12 @@
// 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/res/layout/fragment_favorites.xml b/res/layout/fragment_favorites.xml
new file mode 100644
--- /dev/null
+++ b/res/layout/fragment_favorites.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/it/reyboz/bustorino/ActivityFavorites.java b/src/it/reyboz/bustorino/ActivityFavorites.java
--- a/src/it/reyboz/bustorino/ActivityFavorites.java
+++ b/src/it/reyboz/bustorino/ActivityFavorites.java
@@ -17,20 +17,18 @@
*/
package it.reyboz.bustorino;
-import android.database.Cursor;
-import androidx.loader.app.LoaderManager;
-import androidx.loader.content.Loader;
+import androidx.lifecycle.ViewModelProvider;
+
import android.widget.*;
import it.reyboz.bustorino.backend.Stop;
import it.reyboz.bustorino.adapters.StopAdapter;
-import it.reyboz.bustorino.middleware.AsyncStopFavoriteAction;
-import it.reyboz.bustorino.data.StopsDB;
+import it.reyboz.bustorino.data.FavoritesViewModel;
import it.reyboz.bustorino.data.UserDB;
+import it.reyboz.bustorino.middleware.AsyncStopFavoriteAction;
import android.app.AlertDialog;
-import android.content.Context;
import android.content.DialogInterface;
-import android.os.AsyncTask;
+
import androidx.core.app.NavUtils;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
@@ -47,10 +45,10 @@
import java.util.List;
-public class ActivityFavorites extends AppCompatActivity implements LoaderManager.LoaderCallbacks {
+public class ActivityFavorites extends AppCompatActivity {
private ListView favoriteListView;
private SQLiteDatabase userDB;
- private EditText bus_stop_name;
+ private EditText busStopNameText;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -66,8 +64,35 @@
ab.setDisplayHomeAsUpEnabled(true); // Back button
favoriteListView = (ListView) 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);
+
+ Intent intent = new Intent(ActivityFavorites.this,
+ ActivityMain.class);
+
+ Bundle b = new Bundle();
+ // TODO: is passing a serialized object a good idea? Or rather, is it reasonably fast?
+ //b.putSerializable("bus-stop-serialized", busStop);
+ b.putString("bus-stop-ID", busStop.ID);
+ b.putString("bus-stop-display-name", busStop.getStopDisplayName());
+ intent.putExtras(b);
+ //intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+ // Intent.FLAG_ACTIVITY_CLEAR_TASK isn't supported in API < 11 and we're targeting API 7...
+ intent.setFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
+
+ startActivity(intent);
+
+ finish();
+ });
+ registerForContextMenu(favoriteListView);
+
+ FavoritesViewModel model = new ViewModelProvider(this).get(FavoritesViewModel.class);
+ model.getFavorites().observe(this, this::showStops);
- createFavoriteList();
}
@Override
@@ -100,17 +125,16 @@
switch (item.getItemId()) {
case R.id.action_favourite_entry_delete:
+ new AsyncStopFavoriteAction(getApplicationContext(), AsyncStopFavoriteAction.Action.REMOVE,
+ new AsyncStopFavoriteAction.ResultListener() {
+ @Override
+ public void doStuffWithResult(Boolean result) {
- // remove the stop from the favorites in background
- new AsyncStopFavoriteAction(getApplicationContext(), AsyncStopFavoriteAction.Action.REMOVE, new AsyncStopFavoriteAction.ResultListener() {
- @Override
- public void doStuffWithResult(Boolean result) {
- //Update favorites list
- createFavoriteList();
- }
- }).execute(busStop);
+ }
+ }).execute(busStop);
return true;
+
case R.id.action_rename_bus_stop_username:
showBusStopUsernameInputDialog(busStop);
return true;
@@ -125,8 +149,15 @@
// start ActivityMap with these extras in intent
Intent intent = new Intent(ActivityFavorites.this, ActivityMap.class);
Bundle b = new Bundle();
- b.putDouble("lat", busStop.getLatitude());
- b.putDouble("lon", busStop.getLongitude());
+ 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);
@@ -138,9 +169,29 @@
}
}
- void createFavoriteList() {
- // TODO: memoize default list, query only user names every time?
- new AsyncGetFavorites(getApplicationContext(), this.userDB).execute();
+
+ 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);
+ }
+ /* 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(this, busStops));
}
public void showBusStopUsernameInputDialog(final Stop busStop) {
@@ -149,16 +200,16 @@
LayoutInflater inflater = this.getLayoutInflater();
View renameDialogLayout = inflater.inflate(R.layout.rename_dialog, null);
- bus_stop_name = (EditText) renameDialogLayout.findViewById(R.id.rename_dialog_bus_stop_name);
- bus_stop_name.setText(busStop.getStopDisplayName());
- bus_stop_name.setHint(busStop.getStopDefaultName());
+ 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 = bus_stop_name.getText().toString();
+ String busStopUsername = busStopNameText.getText().toString();
String oldUserName = busStop.getStopUserName();
// changed to none
@@ -166,17 +217,25 @@
// unless it was already empty, set new
if(oldUserName != null) {
busStop.setStopUserName(null);
- UserDB.updateStop(busStop, userDB);
- createFavoriteList();
+ //UserDB.updateStop(busStop, userDB);
+ //UserDB.notifyContentProvider(getApplicationContext());
}
} else { // changed to something
// something different?
- if(oldUserName == null || !busStopUsername.equals(oldUserName)) {
+ if(!busStopUsername.equals(oldUserName)) {
busStop.setStopUserName(busStopUsername);
- UserDB.updateStop(busStop, userDB);
- createFavoriteList();
+ //UserDB.updateStop(busStop, userDB);
+ //UserDB.notifyContentProvider(getApplicationContext());
}
}
+ new AsyncStopFavoriteAction(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);
+
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@@ -192,7 +251,6 @@
busStop.setStopUserName(null);
UserDB.updateStop(busStop, userDB);
- createFavoriteList();
}
});
builder.show();
@@ -208,22 +266,7 @@
super.onStop();
this.userDB.close();
}
-
- @Override
- public Loader onCreateLoader(int id, Bundle args) {
- return null;
- }
-
- @Override
- public void onLoadFinished(Loader loader, Cursor data) {
-
- }
-
- @Override
- public void onLoaderReset(Loader loader) {
-
- }
-
+/*
private class AsyncGetFavorites extends AsyncTask> {
private Context c;
private SQLiteDatabase userDB;
@@ -245,55 +288,9 @@
@Override
protected void onPostExecute(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);
- }
-
- /* 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(this.c, busStops));
- favoriteListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- public void onItemClick(AdapterView> parent, View view, int position, long 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);
-
- Intent intent = new Intent(ActivityFavorites.this,
- ActivityMain.class);
-
- Bundle b = new Bundle();
- // TODO: is passing a serialized object a good idea? Or rather, is it reasonably fast?
- //b.putSerializable("bus-stop-serialized", busStop);
- b.putString("bus-stop-ID", busStop.ID);
- b.putString("bus-stop-display-name", busStop.getStopDisplayName());
- intent.putExtras(b);
- //intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
- // Intent.FLAG_ACTIVITY_CLEAR_TASK isn't supported in API < 11 and we're targeting API 7...
- intent.setFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
-
- startActivity(intent);
-
- finish();
- }
- });
- registerForContextMenu(favoriteListView);
}
}
+*/
+
}
diff --git a/src/it/reyboz/bustorino/ActivityMain.java b/src/it/reyboz/bustorino/ActivityMain.java
--- a/src/it/reyboz/bustorino/ActivityMain.java
+++ b/src/it/reyboz/bustorino/ActivityMain.java
@@ -24,7 +24,6 @@
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Resources;
-import android.database.sqlite.SQLiteDatabase;
import android.location.*;
import android.net.Uri;
import android.os.Build;
@@ -67,7 +66,6 @@
import it.reyboz.bustorino.backend.*;
import it.reyboz.bustorino.data.DBUpdateWorker;
import it.reyboz.bustorino.data.DatabaseUpdate;
-import it.reyboz.bustorino.data.UserDB;
import it.reyboz.bustorino.fragments.*;
import it.reyboz.bustorino.middleware.*;
import it.reyboz.bustorino.util.Permissions;
diff --git a/src/it/reyboz/bustorino/ActivityPrincipal.java b/src/it/reyboz/bustorino/ActivityPrincipal.java
--- a/src/it/reyboz/bustorino/ActivityPrincipal.java
+++ b/src/it/reyboz/bustorino/ActivityPrincipal.java
@@ -34,6 +34,7 @@
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;
@@ -48,8 +49,11 @@
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) {
@@ -191,6 +195,20 @@
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;
}
//selectDrawerItem(menuItem);
Log.d(DEBUG_TAG, "pressed item "+menuItem.toString());
@@ -239,12 +257,9 @@
Log.d(DEBUG_TAG, "Item pressed");
- switch (item.getItemId()){
- case android.R.id.home:
- mDrawer.openDrawer(GravityCompat.START);
- return true;
- default:
-
+ if (item.getItemId() == android.R.id.home) {
+ mDrawer.openDrawer(GravityCompat.START);
+ return true;
}
if (drawerToggle.onOptionsItemSelected(item)) {
@@ -263,7 +278,11 @@
if (mDrawer.isDrawerOpen(GravityCompat.START))
mDrawer.closeDrawer(GravityCompat.START);
else if(shownFrag != null && shownFrag.isVisible() && shownFrag.getChildFragmentManager().getBackStackEntryCount() > 0){
- shownFrag.getChildFragmentManager().popBackStack();
+ //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();
@@ -279,7 +298,7 @@
snackbar.show();
}
- private MainScreenFragment showMainFragment(){
+ private MainScreenFragment createAndShowMainFragment(){
FragmentManager fraMan = getSupportFragmentManager();
MainScreenFragment fragment = MainScreenFragment.newInstance();
@@ -289,6 +308,44 @@
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();
@@ -297,6 +354,7 @@
else return null;
}
+
@Override
public void showFloatingActionButton(boolean yes) {
//TODO
@@ -312,24 +370,26 @@
@Override
public void requestArrivalsForStopID(String ID) {
- FragmentManager fraMan = getSupportFragmentManager();
- Fragment fragment = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG);
- MainScreenFragment mainScreenFragment = null;
- if (fragment==null | !(fragment instanceof MainScreenFragment)){
- mainScreenFragment = showMainFragment();
- }
- else if(!fragment.isVisible()){
-
- fraMan.beginTransaction().replace(R.id.mainActContentFrame, fragment)
- .addToBackStack(null)
- .commit();
- mainScreenFragment = (MainScreenFragment) fragment;
- Log.d(DEBUG_TAG, "Found the main fragment");
- } else{
- mainScreenFragment = (MainScreenFragment) fragment;
+ //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();
+ }
}
-
- mainScreenFragment.requestArrivalsForStopID(ID);
+ probableFragment.requestArrivalsForStopID(ID);
+ mNavView.setCheckedItem(R.id.nav_arrivals);
}
@Override
diff --git a/src/it/reyboz/bustorino/backend/Stop.java b/src/it/reyboz/bustorino/backend/Stop.java
--- a/src/it/reyboz/bustorino/backend/Stop.java
+++ b/src/it/reyboz/bustorino/backend/Stop.java
@@ -96,7 +96,7 @@
}
// no string yet? build it!
- return buildString();
+ return buildRoutesString();
}
@Nullable
@@ -117,7 +117,7 @@
return routesThatStopHere;
}
- private @Nullable String buildString() {
+ private @Nullable String buildRoutesString() {
// no routes => no string
if(this.routesThatStopHere == null || this.routesThatStopHere.size() == 0) {
return null;
diff --git a/src/it/reyboz/bustorino/backend/StopsDBInterface.java b/src/it/reyboz/bustorino/backend/StopsDBInterface.java
--- a/src/it/reyboz/bustorino/backend/StopsDBInterface.java
+++ b/src/it/reyboz/bustorino/backend/StopsDBInterface.java
@@ -36,15 +36,6 @@
*/
@Nullable List getRoutesByStop(@NonNull String stopID);
- /**
- * Stop ID goes in, stop name comes out.
- * GTT API doesn't return this useful piece of information, so here we go, get it from the database!
- *
- * @param stopID stop ID, in normalized form
- * @return stop name or null if not found (or database closed)
- */
- @Nullable String getNameFromID(@NonNull String stopID);
-
/**
* Stop ID goes in, stop location comes out.
* This is sometimes missing in GTT API, but database contains meaningful locations for nearly every stop...
diff --git a/src/it/reyboz/bustorino/data/AppDataProvider.java b/src/it/reyboz/bustorino/data/AppDataProvider.java
--- a/src/it/reyboz/bustorino/data/AppDataProvider.java
+++ b/src/it/reyboz/bustorino/data/AppDataProvider.java
@@ -26,10 +26,13 @@
import it.reyboz.bustorino.BuildConfig;
import it.reyboz.bustorino.backend.DBStatusManager;
+import it.reyboz.bustorino.backend.Stop;
import it.reyboz.bustorino.data.NextGenDB.Contract.*;
import java.util.List;
+import static it.reyboz.bustorino.data.UserDB.getFavoritesColumnNamesAsArray;
+
public class AppDataProvider extends ContentProvider {
public static final String AUTHORITY = BuildConfig.APPLICATION_ID +".provider";
@@ -42,6 +45,9 @@
private static final int LINE_INSERT_OP = 7;
private static final int CONNECTIONS = 8;
private static final int LOCATION_SEARCH = 9;
+ private static final int GET_ALL_FAVORITES =10;
+
+ public static final String FAVORITES = "favorites";
private static final String DEBUG_TAG="AppDataProvider";
private Context con;
@@ -74,6 +80,7 @@
sUriMatcher.addURI(AUTHORITY,"branches",ADD_UPDATE_BRANCHES);
sUriMatcher.addURI(AUTHORITY,"connections",CONNECTIONS);
sUriMatcher.addURI(AUTHORITY,"favorites/#",FAVORITES_OP);
+ sUriMatcher.addURI(AUTHORITY,FAVORITES,GET_ALL_FAVORITES);
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
@@ -231,7 +238,7 @@
}
case FAVORITES_OP:
- final String stopFavSelection = UserDB.getFavoritesColumnNamesAsArray[0]+" = ?";
+ final String stopFavSelection = getFavoritesColumnNamesAsArray[0]+" = ?";
db = userDBHelper.getReadableDatabase();
Log.d(DEBUG_TAG,"Asked information on Favorites about stop with id "+uri.getLastPathSegment());
return db.query(UserDB.TABLE_NAME,projection,stopFavSelection,new String[]{uri.getLastPathSegment()},null,null,sortOrder);
@@ -241,8 +248,15 @@
final String stopSelection = StopsTable.COL_ID+" = ?";
Log.d(DEBUG_TAG,"Asked information about stop with id "+selectionValues[0]);
return db.query(StopsTable.TABLE_NAME,projection,stopSelection,selectionValues,null,null,sortOrder);
+ case MANY_STOPS:
+ return db.query(StopsTable.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
+
+ case GET_ALL_FAVORITES:
+ db = userDBHelper.getReadableDatabase();
+ return db.query(UserDB.TABLE_NAME, projection, selection, selectionArgs, null, null,sortOrder);
+
default:
- Log.d("DataProvider","got request "+uri.getPath()+" which doesn't match anything");
+ Log.e("DataProvider","got request "+uri.getPath()+" which doesn't match anything");
}
throw new UnsupportedOperationException("Not yet implemented");
diff --git a/src/it/reyboz/bustorino/data/CustomAsyncQueryHandler.java b/src/it/reyboz/bustorino/data/CustomAsyncQueryHandler.java
new file mode 100644
--- /dev/null
+++ b/src/it/reyboz/bustorino/data/CustomAsyncQueryHandler.java
@@ -0,0 +1,45 @@
+package it.reyboz.bustorino.data;
+
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
+import android.database.Cursor;
+
+import java.lang.ref.WeakReference;
+
+public class CustomAsyncQueryHandler extends AsyncQueryHandler {
+
+ private WeakReference mListener;
+
+ public interface AsyncQueryListener {
+ void onQueryComplete(int token, Object cookie, Cursor cursor);
+ }
+
+ public CustomAsyncQueryHandler(ContentResolver cr, AsyncQueryListener listener) {
+ super(cr);
+ mListener = new WeakReference(listener);
+ }
+
+ public CustomAsyncQueryHandler(ContentResolver cr) {
+ super(cr);
+ }
+
+ /**
+ * Assign the given {@link AsyncQueryListener} to receive query events from
+ * asynchronous calls. Will replace any existing listener.
+ */
+ public void setQueryListener(AsyncQueryListener listener) {
+ mListener = new WeakReference(listener);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ final AsyncQueryListener listener = mListener.get();
+ if (listener != null) {
+ listener.onQueryComplete(token, cookie, cursor);
+ } else if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+}
diff --git a/src/it/reyboz/bustorino/data/FavoritesLiveData.java b/src/it/reyboz/bustorino/data/FavoritesLiveData.java
new file mode 100644
--- /dev/null
+++ b/src/it/reyboz/bustorino/data/FavoritesLiveData.java
@@ -0,0 +1,201 @@
+package it.reyboz.bustorino.data;
+
+import android.annotation.SuppressLint;
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.content.ContentResolverCompat;
+import androidx.core.os.CancellationSignal;
+import androidx.core.os.OperationCanceledException;
+import androidx.lifecycle.LiveData;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import it.reyboz.bustorino.backend.Route;
+import it.reyboz.bustorino.backend.Stop;
+
+import it.reyboz.bustorino.data.NextGenDB.Contract.*;
+
+public class FavoritesLiveData extends LiveData> implements CustomAsyncQueryHandler.AsyncQueryListener {
+ private static final String TAG = "FavoritesLiveData";
+ private final boolean notifyChangesDescendants;
+
+
+ @NonNull
+ private final Context mContext;
+
+ @NonNull
+ private final FavoritesLiveData.ForceLoadContentObserver mObserver;
+ private final CustomAsyncQueryHandler queryHandler;
+
+
+ private final Uri FAVORITES_URI = AppDataProvider.getUriBuilderToComplete().appendPath(
+ AppDataProvider.FAVORITES).build();
+
+
+ private final int FAV_TOKEN = 23, STOPS_TOKEN_BASE=90;
+
+
+ @Nullable
+ private List stopsFromFavorites, stopsDone;
+
+ private boolean isQueryRunning = false;
+ private int stopNeededCount = 0;
+
+ public FavoritesLiveData(@NonNull Context context, boolean notifyDescendantsChanges) {
+ super();
+ mContext = context.getApplicationContext();
+ mObserver = new FavoritesLiveData.ForceLoadContentObserver();
+ notifyChangesDescendants = notifyDescendantsChanges;
+ queryHandler = new CustomAsyncQueryHandler(mContext.getContentResolver(),this);
+
+ }
+
+ private void loadData() {
+ loadData(false);
+ }
+ private static Uri.Builder getStopsBuilder(){
+ return AppDataProvider.getUriBuilderToComplete().appendPath("stop");
+
+ }
+
+ private void loadData(boolean forceQuery) {
+ Log.d(TAG, "loadData()");
+
+ if (!forceQuery){
+ if (getValue()!= null){
+ //Data already loaded
+ return;
+ }
+ }
+ if (isQueryRunning){
+ //we are waiting for data, we will get an update soon
+ return;
+ }
+
+ isQueryRunning = true;
+ queryHandler.startQuery(FAV_TOKEN,null, FAVORITES_URI, UserDB.getFavoritesColumnNamesAsArray, null, null, null);
+
+
+ }
+
+ @Override
+ protected void onActive() {
+ Log.d(TAG, "onActive()");
+ loadData();
+ }
+
+ @Override
+ protected void onInactive() {
+ Log.d(TAG, "onInactive()");
+
+ }
+
+ /**
+ * Clear the data for the cursor
+ */
+ public void onClear(){
+
+ ContentResolver resolver = mContext.getContentResolver();
+ resolver.unregisterContentObserver(mObserver);
+
+ }
+
+
+ @Override
+ protected void setValue(List stops) {
+
+ ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(FAVORITES_URI, notifyChangesDescendants,mObserver);
+
+ super.setValue(stops);
+ }
+
+ @Override
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ if (token == FAV_TOKEN) {
+ stopsFromFavorites = UserDB.getFavoritesFromCursor(cursor, UserDB.getFavoritesColumnNamesAsArray);
+ cursor.close();
+
+ for (int i = 0; i < stopsFromFavorites.size(); i++) {
+ Stop s = stopsFromFavorites.get(i);
+ queryHandler.startQuery(STOPS_TOKEN_BASE + i, null, getStopsBuilder().appendPath(s.ID).build(),
+ NextGenDB.QUERY_COLUMN_stops_all, null, null, null);
+ }
+ stopNeededCount = stopsFromFavorites.size();
+ stopsDone = new ArrayList<>();
+
+
+ } else if(token >= STOPS_TOKEN_BASE){
+ final int index = token - STOPS_TOKEN_BASE;
+ assert stopsFromFavorites != null;
+ Stop stopUpdate = stopsFromFavorites.get(index);
+ Stop finalStop;
+
+ List result = Arrays.asList(NextGenDB.getStopsFromCursorAllFields(cursor));
+ cursor.close();
+ if (result.size() < 1){
+ // stop is not in the DB
+ finalStop = stopUpdate;
+ } else{
+ finalStop = result.get(0);
+ assert (finalStop.ID.equals(stopUpdate.ID));
+ finalStop.setStopUserName(stopUpdate.getStopUserName());
+ }
+ if (stopsDone!=null)
+ stopsDone.add(finalStop);
+
+ stopNeededCount--;
+ if (stopNeededCount == 0) {
+ // we have finished the queries
+ isQueryRunning = false;
+ Collections.sort(stopsDone);
+
+ setValue(stopsDone);
+ }
+
+ }
+ }
+
+
+ /**
+ * Content Observer that forces reload of cursor when data changes
+ * On different thread (new Handler)
+ */
+ public final class ForceLoadContentObserver
+ extends ContentObserver {
+
+ public ForceLoadContentObserver() {
+ super(new Handler());
+ }
+
+ @Override
+ public boolean deliverSelfNotifications() {
+ return true;
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ Log.d(TAG, "ForceLoadContentObserver.onChange()");
+ loadData(true);
+ }
+
+ }
+
+
+}
+
diff --git a/src/it/reyboz/bustorino/data/FavoritesViewModel.java b/src/it/reyboz/bustorino/data/FavoritesViewModel.java
new file mode 100644
--- /dev/null
+++ b/src/it/reyboz/bustorino/data/FavoritesViewModel.java
@@ -0,0 +1,36 @@
+package it.reyboz.bustorino.data;
+
+import android.app.Application;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.AndroidViewModel;
+import androidx.lifecycle.LiveData;
+
+import java.util.List;
+
+import it.reyboz.bustorino.backend.Stop;
+
+public class FavoritesViewModel extends AndroidViewModel {
+
+ FavoritesLiveData favoritesLiveData;
+ final Context appContext;
+
+ public FavoritesViewModel(@NonNull Application application) {
+ super(application);
+ appContext = application.getApplicationContext();
+ }
+
+ @Override
+ protected void onCleared() {
+ favoritesLiveData.onClear();
+ super.onCleared();
+ }
+
+ public LiveData> getFavorites(){
+ if (favoritesLiveData==null){
+ favoritesLiveData= new FavoritesLiveData(appContext, true);
+ }
+ return favoritesLiveData;
+ }
+}
diff --git a/src/it/reyboz/bustorino/data/NextGenDB.java b/src/it/reyboz/bustorino/data/NextGenDB.java
--- a/src/it/reyboz/bustorino/data/NextGenDB.java
+++ b/src/it/reyboz/bustorino/data/NextGenDB.java
@@ -76,16 +76,18 @@
Contract.StopsTable.COL_LOCATION+" TEXT, "+Contract.StopsTable.COL_PLACE+" TEXT, "+
Contract.StopsTable.COL_LINES_STOPPING +" TEXT )";
- private static final String[] QUERY_COLUMN_stops_all = {
+ 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};
- private static final String QUERY_WHERE_LAT_AND_LNG_IN_RANGE = StopsTable.COL_LAT + " >= ? AND " +
+ 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 Context appContext;
+
+ private final Context appContext;
public NextGenDB(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
@@ -164,9 +166,6 @@
String minLngRaw = String.valueOf(minLng);
String maxLngRaw = String.valueOf(maxLng);
- String[] queryColumns = {};
- String stopID;
- Route.Type type;
if(db == null) {
return stops;
@@ -176,34 +175,7 @@
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);
-
- 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);
-
- count = result.getCount();
- stops = new Stop[count];
-
- int i = 0;
- while(result.moveToNext()) {
-
- stopID = result.getString(colID);
- 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));
- }
+ stops = getStopsFromCursorAllFields(result);
} catch(SQLiteException e) {
Log.e(DEBUG_TAG, "SQLiteException occurred");
@@ -217,6 +189,42 @@
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
diff --git a/src/it/reyboz/bustorino/data/StopsDB.java b/src/it/reyboz/bustorino/data/StopsDB.java
--- a/src/it/reyboz/bustorino/data/StopsDB.java
+++ b/src/it/reyboz/bustorino/data/StopsDB.java
@@ -121,35 +121,6 @@
return routes;
}
- public String getNameFromID(@NonNull String stopID) {
- String[] uselessArray = {stopID};
- int count;
- String name;
- Cursor result;
-
- if(this.db == null) {
- return null;
- }
-
- try {
- result = this.db.query(QUERY_TABLE_stops, QUERY_COLUMN_name, QUERY_WHERE_ID, uselessArray, null, null, null);
- } catch(SQLiteException e) {
- return null;
- }
-
- count = result.getCount();
- if(count == 0) {
- return null;
- }
-
- result.moveToNext();
- name = result.getString(0);
-
- result.close();
-
- return name;
- }
-
public String getLocationFromID(@NonNull String stopID) {
String[] uselessArray = {stopID};
int count;
diff --git a/src/it/reyboz/bustorino/data/UserDB.java b/src/it/reyboz/bustorino/data/UserDB.java
--- a/src/it/reyboz/bustorino/data/UserDB.java
+++ b/src/it/reyboz/bustorino/data/UserDB.java
@@ -24,9 +24,11 @@
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.content.Context;
+import android.net.Uri;
import android.util.Log;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -41,7 +43,11 @@
private final static String[] usernameColumnNameAsArray = {"username"};
public final static String[] getFavoritesColumnNamesAsArray = {"ID", "username"};
- public UserDB(Context context) {
+ private static final Uri FAVORITES_URI = AppDataProvider.getUriBuilderToComplete().appendPath(
+ AppDataProvider.FAVORITES).build();
+
+
+ public UserDB(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
this.c = context;
}
@@ -224,7 +230,6 @@
l.add(s);
}
}
-
c.close();
} catch(SQLiteException ignored) {}
@@ -233,6 +238,31 @@
return l;
}
+ public static void notifyContentProvider(Context context){
+ context.
+ getContentResolver().
+ notifyChange(FAVORITES_URI, null);
+ }
+
+ public static ArrayList getFavoritesFromCursor(Cursor cursor, String[] columns){
+ List colsList = Arrays.asList(columns);
+ if (!colsList.contains(getFavoritesColumnNamesAsArray[0]) || !colsList.contains(getFavoritesColumnNamesAsArray[1])){
+ throw new IllegalArgumentException();
+ }
+ ArrayList l = new ArrayList<>();
+ final int colID = cursor.getColumnIndex("ID");
+ final int colUser = cursor.getColumnIndex("username");
+ while(cursor.moveToNext()) {
+ final String stopUserName = cursor.getString(colUser);
+ final String stopID = cursor.getString(colID);
+ final Stop s = new Stop(stopID.trim());
+ if (stopUserName!=null) s.setStopUserName(stopUserName);
+
+ l.add(s);
+ }
+ return l;
+
+ }
public static boolean addOrUpdateStop(Stop s, SQLiteDatabase db) {
ContentValues cv = new ContentValues();
diff --git a/src/it/reyboz/bustorino/fragments/ArrivalsFragment.java b/src/it/reyboz/bustorino/fragments/ArrivalsFragment.java
--- a/src/it/reyboz/bustorino/fragments/ArrivalsFragment.java
+++ b/src/it/reyboz/bustorino/fragments/ArrivalsFragment.java
@@ -65,7 +65,8 @@
private final static String KEY_STOP_ID = "stopid";
private final static String KEY_STOP_NAME = "stopname";
- private final static String DEBUG_TAG = "BUSTOArrivalsFragment";
+ private final static String DEBUG_TAG_ALL = "BUSTOArrivalsFragment";
+ private String DEBUG_TAG = DEBUG_TAG_ALL;
private final static int loaderFavId = 2;
private final static int loaderStopId = 1;
private final static ArrivalsFetcher[] defaultFetchers = new ArrivalsFetcher[]{new FiveTAPIFetcher(), new GTTJSONFetcher(), new FiveTScraperFetcher()};
@@ -86,6 +87,7 @@
private List fetchers = new ArrayList<>(Arrays.asList(defaultFetchers));
+ private boolean reloadOnResume = true;
public static ArrivalsFragment newInstance(String stopID){
return newInstance(stopID, null);
@@ -95,7 +97,7 @@
ArrivalsFragment fragment = new ArrivalsFragment();
Bundle args = new Bundle();
args.putString(KEY_STOP_ID,stopID);
- //parameter for ResultListFragment
+ //parameter for ResultListFragmentrequestArrivalsForStopID
args.putSerializable(LIST_TYPE,FragmentKind.ARRIVALS);
if (stopName != null){
args.putString(KEY_STOP_NAME,stopName);
@@ -108,6 +110,8 @@
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
stopID = getArguments().getString(KEY_STOP_ID);
+ DEBUG_TAG = DEBUG_TAG_ALL+" "+stopID;
+
//this might really be null
stopName = getArguments().getString(KEY_STOP_NAME);
final ArrivalsFragment arrivalsFragment = this;
@@ -198,22 +202,26 @@
public void onResume() {
super.onResume();
LoaderManager loaderManager = getLoaderManager();
-
+ Log.d(DEBUG_TAG, "OnResume, justCreated "+justCreated);
if(stopID!=null){
//refresh the arrivals
- if(!justCreated)
- mListener.requestArrivalsForStopID(stopID);
+ if(!justCreated){
+ if (reloadOnResume)
+ mListener.requestArrivalsForStopID(stopID);
+ }
else justCreated = false;
//start the loader
if(prefs.isDBUpdating(true)){
prefs.registerListener();
} else {
+ Log.d(DEBUG_TAG, "Restarting loader for stop");
loaderManager.restartLoader(loaderFavId, getArguments(), this);
}
updateMessage();
}
}
+
@Override
public void onStart() {
super.onStart();
@@ -221,12 +229,30 @@
updateFragmentData(null);
}
}
+ @Override
+ public void onPause() {
+ if(listener!=null)
+ prefs.unregisterListener();
+ super.onPause();
+ LoaderManager loaderManager = getLoaderManager();
+ Log.d(DEBUG_TAG, "onPause, have running loaders: "+loaderManager.hasRunningLoaders());
+ loaderManager.destroyLoader(loaderFavId);
+
+ }
@Nullable
public String getStopID() {
return stopID;
}
+ public boolean reloadsOnResume() {
+ return reloadOnResume;
+ }
+
+ public void setReloadOnResume(boolean reloadOnResume) {
+ this.reloadOnResume = reloadOnResume;
+ }
+
/**
* Give the fetchers
* @return the list of the fetchers
@@ -376,11 +402,10 @@
data.moveToFirst();
final String probableName = data.getString(colUserName);
stopIsInFavorites = true;
- if(probableName!=null && !probableName.isEmpty()){
- stopName = probableName;
- //update the message in the textview
- updateMessage();
- }
+ stopName = probableName;
+ //update the message in the textview
+ updateMessage();
+
} else {
stopIsInFavorites =false;
}
@@ -406,13 +431,6 @@
}
- @Override
- public void onPause() {
- if(listener!=null)
- prefs.unregisterListener();
- super.onPause();
- }
-
@Override
public void onLoaderReset(Loader loader) {
//NOTHING TO DO
diff --git a/src/it/reyboz/bustorino/fragments/FavoritesFragment.java b/src/it/reyboz/bustorino/fragments/FavoritesFragment.java
new file mode 100644
--- /dev/null
+++ b/src/it/reyboz/bustorino/fragments/FavoritesFragment.java
@@ -0,0 +1,272 @@
+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 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(){
+
+ }
+
+ @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 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/FragmentHelper.java b/src/it/reyboz/bustorino/fragments/FragmentHelper.java
--- a/src/it/reyboz/bustorino/fragments/FragmentHelper.java
+++ b/src/it/reyboz/bustorino/fragments/FragmentHelper.java
@@ -19,6 +19,7 @@
import android.content.Context;
+
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
@@ -30,7 +31,6 @@
import it.reyboz.bustorino.backend.Palina;
import it.reyboz.bustorino.backend.Stop;
import it.reyboz.bustorino.backend.utils;
-import it.reyboz.bustorino.data.NextGenDB;
import it.reyboz.bustorino.middleware.*;
import java.lang.ref.WeakReference;
@@ -87,7 +87,7 @@
* Called when you need to create a fragment for a specified Palina
* @param p the Stop that needs to be displayed
*/
- public void createOrUpdateStopFragment(Palina p){
+ public void createOrUpdateStopFragment(Palina p, boolean addToBackStack){
boolean sameFragment;
ArrivalsFragment arrivalsFragment;
@@ -102,6 +102,7 @@
if(fm.findFragmentById(primaryFrameLayout) instanceof ArrivalsFragment) {
arrivalsFragment = (ArrivalsFragment) fm.findFragmentById(primaryFrameLayout);
//Log.d(DEBUG_TAG, "Arrivals are for fragment with same stop?");
+ assert arrivalsFragment != null;
sameFragment = arrivalsFragment.isFragmentForTheSameStop(p);
} else {
sameFragment = false;
@@ -120,13 +121,16 @@
} else {
arrivalsFragment = ArrivalsFragment.newInstance(p.ID);
}
- attachFragmentToContainer(fm,arrivalsFragment,true,ResultListFragment.getFragmentTag(p));
+ String probableTag = ResultListFragment.getFragmentTag(p);
+ attachFragmentToContainer(fm,arrivalsFragment,new AttachParameters(probableTag, true, addToBackStack));
} else {
Log.d("BusTO", "Same bus stop, accessing existing fragment");
arrivalsFragment = (ArrivalsFragment) fm.findFragmentById(primaryFrameLayout);
}
// DO NOT CALL `setListAdapter` ever on arrivals fragment
arrivalsFragment.updateFragmentData(p);
+ // enable fragment auto refresh
+ arrivalsFragment.setReloadOnResume(true);
listenerMain.hideKeyboard();
toggleSpinner(false);
@@ -137,7 +141,7 @@
* @param resultList the List of stops found
* @param query String queried
*/
- public void createFragmentFor(List resultList,String query){
+ public void createStopListFragment(List resultList, String query, boolean addToBackStack){
listenerMain.hideKeyboard();
StopListFragment listfragment = StopListFragment.newInstance(query);
if(managerWeakRef.get()==null || shouldHaltAllActivities) {
@@ -145,7 +149,8 @@
Log.e(DEBUG_TAG, "We are asked for a new stop but we can't show anything");
return;
}
- attachFragmentToContainer(managerWeakRef.get(),listfragment,false,"search_"+query);
+ attachFragmentToContainer(managerWeakRef.get(),listfragment,
+ new AttachParameters("search_"+query, false,addToBackStack));
listfragment.setStopList(resultList);
toggleSpinner(false);
@@ -163,15 +168,22 @@
* Attach a new fragment to a cointainer
* @param fm the FragmentManager
* @param fragment the Fragment
- * @param sendToSecondaryFrame needs to be displayed in secondary frame or not
- * @param tag tag for the fragment
+ * @param parameters attach parameters
*/
- public void attachFragmentToContainer(FragmentManager fm,Fragment fragment, boolean sendToSecondaryFrame, String tag){
+ protected void attachFragmentToContainer(FragmentManager fm,Fragment fragment, AttachParameters parameters){
FragmentTransaction ft = fm.beginTransaction();
- if(sendToSecondaryFrame && secondaryFrameLayout!=NO_FRAME)
- ft.replace(secondaryFrameLayout,fragment,tag);
- else ft.replace(primaryFrameLayout,fragment,tag);
- ft.addToBackStack("state_"+tag);
+ int frameID;
+ if(parameters.attachToSecondaryFrame && secondaryFrameLayout!=NO_FRAME)
+ // ft.replace(secondaryFrameLayout,fragment,tag);
+ frameID = secondaryFrameLayout;
+ else frameID = primaryFrameLayout;
+ switch (parameters.transaction){
+ case REPLACE:
+ ft.replace(frameID,fragment,parameters.tag);
+
+ }
+ if (parameters.addToBackStack)
+ ft.addToBackStack("state_"+parameters.tag);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE);
ft.commit();
//fm.executePendingTransactions();
@@ -229,4 +241,27 @@
showToastMessage(messageID, true);
}
+ enum Transaction{
+ REPLACE,
+ }
+ static final class AttachParameters {
+ String tag;
+ boolean attachToSecondaryFrame;
+ Transaction transaction;
+ boolean addToBackStack;
+
+ public AttachParameters(String tag, boolean attachToSecondaryFrame, Transaction transaction, boolean addToBackStack) {
+ this.tag = tag;
+ this.attachToSecondaryFrame = attachToSecondaryFrame;
+ this.transaction = transaction;
+ this.addToBackStack = addToBackStack;
+ }
+
+ public AttachParameters(String tag, boolean attachToSecondaryFrame, boolean addToBackStack) {
+ this.tag = tag;
+ this.attachToSecondaryFrame = attachToSecondaryFrame;
+ this.addToBackStack = addToBackStack;
+ this.transaction = Transaction.REPLACE;
+ }
+ }
}
diff --git a/src/it/reyboz/bustorino/fragments/MainScreenFragment.java b/src/it/reyboz/bustorino/fragments/MainScreenFragment.java
--- a/src/it/reyboz/bustorino/fragments/MainScreenFragment.java
+++ b/src/it/reyboz/bustorino/fragments/MainScreenFragment.java
@@ -12,6 +12,7 @@
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;
@@ -37,7 +38,6 @@
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.zxing.integration.android.IntentIntegrator;
-import it.reyboz.bustorino.ActivityMain;
import it.reyboz.bustorino.R;
import it.reyboz.bustorino.backend.ArrivalsFetcher;
import it.reyboz.bustorino.backend.FiveTAPIFetcher;
@@ -45,9 +45,7 @@
import it.reyboz.bustorino.backend.FiveTStopsFetcher;
import it.reyboz.bustorino.backend.GTTJSONFetcher;
import it.reyboz.bustorino.backend.GTTStopsFetcher;
-import it.reyboz.bustorino.backend.Stop;
import it.reyboz.bustorino.backend.StopsFinderByName;
-import it.reyboz.bustorino.data.UserDB;
import it.reyboz.bustorino.middleware.AsyncDataDownload;
import it.reyboz.bustorino.util.Permissions;
@@ -63,7 +61,8 @@
public class MainScreenFragment extends BaseFragment implements FragmentListenerMain{
- private final String OPTION_SHOW_LEGEND = "show_legend";
+ 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";
@@ -82,6 +81,7 @@
private FloatingActionButton floatingActionButton;
private boolean setupOnAttached = true;
+ private boolean suppressArrivalsReload = false;
//private Snackbar snackbar;
/*
* Search mode
@@ -124,6 +124,8 @@
//// ACTIVITY ATTACHED (LISTENER ///
private CommonFragmentListener mListener;
+ private String pendingStopID = null;
+
public MainScreenFragment() {
// Required empty public constructor
}
@@ -207,11 +209,59 @@
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;
@@ -219,12 +269,17 @@
throw new RuntimeException(context.toString()
+ " must implement CommonFragmentListener");
}
- if (setupOnAttached){
+ if (setupOnAttached) {
+ if (pendingStopID==null)
//We want the nearby bus stops!
- mainHandler.post(new NearbyStopsRequester(getContext(),cr, locListener));
+ 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 {
}
}
@@ -232,6 +287,7 @@
public void onDetach() {
super.onDetach();
mListener = null;
+ // setupOnAttached = true;
}
@@ -245,6 +301,23 @@
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;
+ }
}
@Override
@@ -311,6 +384,8 @@
////////////////////////////////////// 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);
@@ -423,9 +498,24 @@
}
+ /**
+ * 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);
@@ -442,7 +532,7 @@
}
else {
new AsyncDataDownload(fragmentHelper,arrivalsFetchers, getContext()).execute(ID);
- Log.d("MainActiv", "Started search for arrivals of stop " + ID);
+ Log.d(DEBUG_TAG, "Started search for arrivals of stop " + ID);
}
}
/////////// LOCATION METHODS //////////
diff --git a/src/it/reyboz/bustorino/fragments/NearbyStopsFragment.java b/src/it/reyboz/bustorino/fragments/NearbyStopsFragment.java
--- a/src/it/reyboz/bustorino/fragments/NearbyStopsFragment.java
+++ b/src/it/reyboz/bustorino/fragments/NearbyStopsFragment.java
@@ -165,6 +165,7 @@
switchButton.setOnClickListener(v -> {
switchFragmentType();
});
+ Log.d(DEBUG_TAG, "onCreateView");
return root;
}
@@ -224,6 +225,7 @@
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
+ Log.d(DEBUG_TAG, "OnAttach called");
}
@@ -271,12 +273,12 @@
MIN_NUM_STOPS = Integer.parseInt(shpr.getString(getString(R.string.pref_key_num_recents),"12"));
}
+
@Override
- public void onActivityCreated(@Nullable Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
gridRecyclerView.setVisibility(View.INVISIBLE);
gridRecyclerView.addOnScrollListener(scrollListener);
-
}
@Override
diff --git a/src/it/reyboz/bustorino/fragments/ResultListFragment.java b/src/it/reyboz/bustorino/fragments/ResultListFragment.java
--- a/src/it/reyboz/bustorino/fragments/ResultListFragment.java
+++ b/src/it/reyboz/bustorino/fragments/ResultListFragment.java
@@ -190,7 +190,7 @@
}
public static String getFragmentTag(Palina p) {
- return p.ID;
+ return "palina_"+p.ID;
}
diff --git a/src/it/reyboz/bustorino/middleware/AsyncDataDownload.java b/src/it/reyboz/bustorino/middleware/AsyncDataDownload.java
--- a/src/it/reyboz/bustorino/middleware/AsyncDataDownload.java
+++ b/src/it/reyboz/bustorino/middleware/AsyncDataDownload.java
@@ -54,6 +54,7 @@
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) {
@@ -62,6 +63,7 @@
fh.setLastTaskRef(new WeakReference<>(this));
res = new AtomicReference<>();
this.context = context.getApplicationContext();
+ this.replaceFragment = true;
theFetchers = fetchers;
if (theFetchers.length < 1){
@@ -206,13 +208,13 @@
switch (t){
case ARRIVALS:
Palina palina = (Palina) o;
- fh.createOrUpdateStopFragment(palina);
+ fh.createOrUpdateStopFragment(palina, replaceFragment);
break;
case STOPS:
//this should never be a problem
List stopList = (List) o;
if(query!=null && !isCancelled()) {
- fh.createFragmentFor(stopList,query);
+ fh.createStopListFragment(stopList,query, replaceFragment);
} else Log.e(TAG,"QUERY NULL, COULD NOT CREATE FRAGMENT");
break;
case DBUPDATE:
diff --git a/src/it/reyboz/bustorino/middleware/AsyncStopFavoriteAction.java b/src/it/reyboz/bustorino/middleware/AsyncStopFavoriteAction.java
--- a/src/it/reyboz/bustorino/middleware/AsyncStopFavoriteAction.java
+++ b/src/it/reyboz/bustorino/middleware/AsyncStopFavoriteAction.java
@@ -20,10 +20,12 @@
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
import android.os.AsyncTask;
import android.widget.Toast;
import it.reyboz.bustorino.R;
import it.reyboz.bustorino.backend.Stop;
+import it.reyboz.bustorino.data.AppDataProvider;
import it.reyboz.bustorino.data.UserDB;
/**
@@ -31,11 +33,13 @@
*/
public class AsyncStopFavoriteAction extends AsyncTask {
private final Context context;
+ private final Uri FAVORITES_URI = AppDataProvider.getUriBuilderToComplete().appendPath(
+ AppDataProvider.FAVORITES).build();
/**
* Kind of actions available
*/
- public enum Action { ADD, REMOVE, TOGGLE };
+ public enum Action { ADD, REMOVE, TOGGLE , UPDATE};
/**
* Action chosen
@@ -86,6 +90,9 @@
if(Action.ADD.equals(action)) {
// add
result = UserDB.addOrUpdateStop(stop, db);
+ } else if (Action.UPDATE.equals(action)){
+
+ result = UserDB.updateStop(stop, db);
} else {
// remove
result = UserDB.deleteStop(stop, db);
@@ -108,11 +115,12 @@
super.onPostExecute(result);
if(result) {
+ UserDB.notifyContentProvider(context);
// at this point the action should be just ADD or REMOVE
if(Action.ADD.equals(action)) {
// now added
Toast.makeText(this.context, R.string.added_in_favorites, Toast.LENGTH_SHORT).show();
- } else {
+ } else if (Action.REMOVE.equals(action)) {
// now removed
Toast.makeText(this.context, R.string.removed_from_favorites, Toast.LENGTH_SHORT).show();
}