diff --git a/res/layout/fragment_favorites.xml b/res/layout/fragment_favorites.xml
index 07ef784..d2b132d 100644
--- a/res/layout/fragment_favorites.xml
+++ b/res/layout/fragment_favorites.xml
@@ -1,40 +1,41 @@
-
+ android:layout_alignParentTop="true"
+ />
\ No newline at end of file
diff --git a/src/it/reyboz/bustorino/adapters/AdapterListener.java b/src/it/reyboz/bustorino/adapters/AdapterListener.java
new file mode 100644
index 0000000..389d33c
--- /dev/null
+++ b/src/it/reyboz/bustorino/adapters/AdapterListener.java
@@ -0,0 +1,7 @@
+package it.reyboz.bustorino.adapters;
+
+import it.reyboz.bustorino.backend.Stop;
+
+public interface AdapterListener {
+ void onTappedStop(Stop stop);
+}
diff --git a/src/it/reyboz/bustorino/adapters/StopRecyclerAdapter.java b/src/it/reyboz/bustorino/adapters/StopRecyclerAdapter.java
new file mode 100644
index 0000000..57f35fa
--- /dev/null
+++ b/src/it/reyboz/bustorino/adapters/StopRecyclerAdapter.java
@@ -0,0 +1,157 @@
+package it.reyboz.bustorino.adapters;
+
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.MenuInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.List;
+
+import it.reyboz.bustorino.R;
+import it.reyboz.bustorino.backend.Stop;
+
+public class StopRecyclerAdapter extends RecyclerView.Adapter {
+ private List stops;
+ private static final int row_layout = R.layout.entry_bus_stop;
+ private static final int busIcon = R.drawable.bus;
+ private static final int trainIcon = R.drawable.subway;
+ private static final int tramIcon = R.drawable.tram;
+ private static final int cityIcon = R.drawable.city;
+
+ private AdapterListener listener;
+ private int position;
+
+
+
+ protected static class ViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener{
+ TextView busStopIDTextView;
+ TextView busStopNameTextView;
+ //TextView busLineVehicleIcon;
+ TextView busStopLinesTextView;
+ TextView busStopLocaLityTextView;
+ Stop mStop;
+
+ public ViewHolder(@NonNull View itemView, AdapterListener listener) {
+ super(itemView);
+ busStopIDTextView = (TextView) itemView.findViewById(R.id.busStopID);
+ busStopNameTextView = (TextView) itemView.findViewById(R.id.busStopName);
+ busStopLinesTextView = (TextView) itemView.findViewById(R.id.routesThatStopHere);
+ busStopLocaLityTextView = (TextView) itemView.findViewById(R.id.busStopLocality);
+
+ mStop = new Stop("");
+
+ itemView.setOnClickListener(view -> {
+ listener.onTappedStop(mStop);
+ });
+ }
+ //many thanks to https://stackoverflow.com/questions/26466877/how-to-create-context-menu-for-recyclerview
+ @Override
+ public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) {
+ MenuInflater inflater = new MenuInflater(view.getContext());
+ inflater.inflate(R.menu.menu_favourites_entry, contextMenu);
+ }
+ }
+
+ public StopRecyclerAdapter(List stops,AdapterListener listener) {
+ this.stops = stops;
+ this.listener = listener;
+ }
+
+ public void setStops(List stops){
+ this.stops = stops;
+ notifyDataSetChanged();
+ }
+
+ public List getStops() {
+ return stops;
+ }
+
+ public int getPosition() {
+ return position;
+ }
+
+ public void setPosition(int position) {
+ this.position = position;
+ }
+
+ @NonNull
+ @Override
+ public StopRecyclerAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(row_layout, parent, false);
+
+ return new StopRecyclerAdapter.ViewHolder(view, listener);
+ }
+
+ @Override
+ public void onViewRecycled(@NonNull StopRecyclerAdapter.ViewHolder holder) {
+ holder.itemView.setOnLongClickListener(null);
+ super.onViewRecycled(holder);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull StopRecyclerAdapter.ViewHolder vh, int position) {
+ Log.d("StopRecyclerAdapter", "Called for position "+position);
+ Stop stop = stops.get(position);
+ vh.busStopIDTextView.setText(stop.ID);
+ vh.mStop = stop;
+ Log.d("StopRecyclerAdapter", "Stop: "+stop.ID);
+
+ // NOTE: intentionally ignoring stop username in search results: if it's in the favorites, why are you searching for it?
+ vh.busStopNameTextView.setText(stop.getStopDisplayName());
+ String whatStopsHere = stop.routesThatStopHereToString();
+ if(whatStopsHere == null) {
+ vh.busStopLinesTextView.setVisibility(View.GONE);
+ } else {
+ vh.busStopLinesTextView.setText(whatStopsHere);
+ vh.busStopLinesTextView.setVisibility(View.VISIBLE); // might be GONE due to View Holder Pattern
+ }
+
+ if(stop.type == null) {
+ vh.busStopLinesTextView.setCompoundDrawablesWithIntrinsicBounds(busIcon, 0, 0, 0);
+ } else {
+ switch(stop.type) {
+ case BUS:
+ default:
+ vh.busStopLinesTextView.setCompoundDrawablesWithIntrinsicBounds(busIcon, 0, 0, 0);
+ break;
+ case METRO:
+ case RAILWAY:
+ vh.busStopLinesTextView.setCompoundDrawablesWithIntrinsicBounds(trainIcon, 0, 0, 0);
+ break;
+ case TRAM:
+ vh.busStopLinesTextView.setCompoundDrawablesWithIntrinsicBounds(tramIcon, 0, 0, 0);
+ break;
+ case LONG_DISTANCE_BUS:
+ // è l'opposto della città ma va beh, dettagli.
+ vh.busStopLinesTextView.setCompoundDrawablesWithIntrinsicBounds(cityIcon, 0, 0, 0);
+ }
+ }
+
+ if (stop.location == null) {
+ vh.busStopLocaLityTextView.setVisibility(View.GONE);
+ } else {
+ vh.busStopLocaLityTextView.setText(stop.location);
+ vh.busStopLocaLityTextView.setVisibility(View.VISIBLE); // might be GONE due to View Holder Pattern
+ }
+ //trick to set the position
+ vh.itemView.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View view) {
+ setPosition(vh.getAdapterPosition());
+ return false;
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return stops.size();
+ }
+}
diff --git a/src/it/reyboz/bustorino/fragments/CommonScrollListener.java b/src/it/reyboz/bustorino/fragments/CommonScrollListener.java
index 530d658..086287a 100644
--- a/src/it/reyboz/bustorino/fragments/CommonScrollListener.java
+++ b/src/it/reyboz/bustorino/fragments/CommonScrollListener.java
@@ -1,85 +1,84 @@
/*
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.annotation.NonNull;
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
- */
+ public void onScrollStateChanged(AbsListView absListView, int i) {
+
}
@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");
listener.showFloatingActionButton(false);
//lastScrollUp = true;
} else if (lastvisibleitem > firstVisibleItem) {
//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) {
+ public void onScrollStateChanged(@NonNull 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 d42f5c7..3c45a71 100644
--- a/src/it/reyboz/bustorino/fragments/FavoritesFragment.java
+++ b/src/it/reyboz/bustorino/fragments/FavoritesFragment.java
@@ -1,279 +1,310 @@
/*
BusTO - Fragments components
Copyright (C) 2021 Fabio Mazza
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
package it.reyboz.bustorino.fragments;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
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 androidx.recyclerview.widget.DividerItemDecoration;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
import it.reyboz.bustorino.*;
+import it.reyboz.bustorino.adapters.AdapterListener;
import it.reyboz.bustorino.adapters.StopAdapter;
+import it.reyboz.bustorino.adapters.StopRecyclerAdapter;
import it.reyboz.bustorino.backend.Stop;
import it.reyboz.bustorino.data.FavoritesViewModel;
import it.reyboz.bustorino.middleware.AsyncStopFavoriteAction;
public class FavoritesFragment extends BaseFragment {
- private ListView favoriteListView;
+ private RecyclerView favoriteRecyclerView;
private EditText busStopNameText;
private TextView favoriteTipTextView;
private ImageView angeryBusImageView;
+ private LinearLayoutManager llManager;
@Nullable
private CommonFragmentListener mListener;
public static final String FRAGMENT_TAG = "BusTOFavFragment";
-
+ private final AdapterListener adapterListener = new AdapterListener() {
+ @Override
+ public void onTappedStop(Stop stop) {
+ mListener.requestArrivalsForStopID(stop.ID);
+ }
+ };
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;
}
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) -> {
- /**
+ favoriteRecyclerView = root.findViewById(R.id.favoritesRecyclerView);
+ //favoriteListView = root.findViewById(R.id.favoriteListView);
+ /*favoriteRecyclerView.setOn((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);
}
});
+
+ */
+
+ llManager = new LinearLayoutManager(getContext());
+ llManager.setOrientation(LinearLayoutManager.VERTICAL);
+ favoriteRecyclerView.setLayoutManager(llManager);
+ DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(favoriteRecyclerView.getContext(),
+ llManager.getOrientation());
+ favoriteRecyclerView.addItemDecoration(dividerItemDecoration);
+
angeryBusImageView = root.findViewById(R.id.angeryBusImageView);
favoriteTipTextView = root.findViewById(R.id.favoriteTipTextView);
- registerForContextMenu(favoriteListView);
+ registerForContextMenu(favoriteRecyclerView);
FavoritesViewModel model = new ViewModelProvider(this).get(FavoritesViewModel.class);
model.getFavorites().observe(getViewLifecycleOwner(), this::showStops);
showStops(new ArrayList<>());
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;
}
-
+ /*
+ This method is apparently NOT CALLED ANYMORE
+ */
@Override
public void onCreateContextMenu(@NonNull ContextMenu menu, @NonNull View v,
ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
- if (v.getId() == R.id.favoriteListView) {
+ Log.d("Favorites Fragment", "Creating context menu on "+v);
+ if (v.getId() == R.id.favoritesRecyclerView) {
// 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);
+ if(!(favoriteRecyclerView.getAdapter() instanceof StopRecyclerAdapter))
+ return false;
+
+ StopRecyclerAdapter adapter = (StopRecyclerAdapter) favoriteRecyclerView.getAdapter();
+ Stop busStop = (Stop) adapter.getStops().get(adapter.getPosition());
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:
if (busStop.getLatitude() == null | busStop.getLongitude() == null |
mListener==null
) {
Toast.makeText(getContext(), R.string.cannot_show_on_map_no_position, Toast.LENGTH_SHORT).show();
return true;
}
//GeoPoint point = new GeoPoint(busStop.getLatitude(), busStop.getLongitude());
mListener.showMapCenteredOnStop(busStop);
return true;
default:
return super.onContextItemSelected(item);
}
}
void showStops(List busStops){
// If no data is found show a friendly message
if(BuildConfig.DEBUG)
Log.d("BusTO - Favorites", "We have "+busStops.size()+" favorites in the list");
if (busStops.size() == 0) {
- favoriteListView.setVisibility(View.INVISIBLE);
+ favoriteRecyclerView.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);
+ favoriteRecyclerView.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));
+ favoriteRecyclerView.setAdapter(new StopRecyclerAdapter(busStops,adapterListener));
}
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
index af7572f..33d2ac9 100644
--- a/src/it/reyboz/bustorino/fragments/FragmentHelper.java
+++ b/src/it/reyboz/bustorino/fragments/FragmentHelper.java
@@ -1,271 +1,271 @@
/*
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;
import android.content.Context;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import android.util.Log;
import android.widget.Toast;
import it.reyboz.bustorino.R;
import it.reyboz.bustorino.backend.Fetcher;
import it.reyboz.bustorino.backend.Palina;
import it.reyboz.bustorino.backend.Stop;
import it.reyboz.bustorino.backend.utils;
import it.reyboz.bustorino.middleware.*;
import java.lang.ref.WeakReference;
import java.util.List;
/**
* Helper class to manage the fragments and their needs
*/
public class FragmentHelper {
//GeneralActivity act;
private final FragmentListenerMain listenerMain;
private final WeakReference managerWeakRef;
private Stop lastSuccessfullySearchedBusStop;
//support for multiple frames
private final int secondaryFrameLayout;
private final int primaryFrameLayout;
private final Context context;
public static final int NO_FRAME = -3;
private static final String DEBUG_TAG = "BusTO FragmHelper";
private WeakReference lastTaskRef;
private boolean shouldHaltAllActivities=false;
public FragmentHelper(FragmentListenerMain listener, FragmentManager framan, Context context, int mainFrame) {
this(listener,framan, context,mainFrame,NO_FRAME);
}
public FragmentHelper(FragmentListenerMain listener, FragmentManager fraMan, Context context, int primaryFrameLayout, int secondaryFrameLayout) {
this.listenerMain = listener;
this.managerWeakRef = new WeakReference<>(fraMan);
this.primaryFrameLayout = primaryFrameLayout;
this.secondaryFrameLayout = secondaryFrameLayout;
this.context = context.getApplicationContext();
}
/**
* Get the last successfully searched bus stop or NULL
*
* @return the stop
*/
public Stop getLastSuccessfullySearchedBusStop() {
return lastSuccessfullySearchedBusStop;
}
public void setLastSuccessfullySearchedBusStop(Stop stop) {
this.lastSuccessfullySearchedBusStop = stop;
}
public void setLastTaskRef(WeakReference lastTaskRef) {
this.lastTaskRef = lastTaskRef;
}
/**
* 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, boolean addToBackStack){
boolean sameFragment;
- ArrivalsFragment arrivalsFragment;
+ ArrivalsFragment arrivalsFragment = null;
if(managerWeakRef.get()==null || shouldHaltAllActivities) {
//SOMETHING WENT VERY WRONG
Log.e(DEBUG_TAG, "We are asked for a new stop but we can't show anything");
return;
}
FragmentManager fm = managerWeakRef.get();
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);
+ if (arrivalsFragment == null) sameFragment = false;
+ else sameFragment = arrivalsFragment.isFragmentForTheSameStop(p);
} else {
sameFragment = false;
Log.d(DEBUG_TAG, "We aren't showing an ArrivalsFragment");
}
setLastSuccessfullySearchedBusStop(p);
-
+ if (sameFragment){
+ Log.d("BusTO", "Same bus stop, accessing existing fragment");
+ arrivalsFragment = (ArrivalsFragment) fm.findFragmentById(primaryFrameLayout);
+ if (arrivalsFragment == null) sameFragment = false;
+ }
if(!sameFragment) {
//set the String to be displayed on the fragment
String displayName = p.getStopDisplayName();
- String displayStuff;
if (displayName != null && displayName.length() > 0) {
arrivalsFragment = ArrivalsFragment.newInstance(p.ID,displayName);
} else {
arrivalsFragment = ArrivalsFragment.newInstance(p.ID);
}
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);
}
/**
* Called when you need to display the results of a search of stops
* @param resultList the List of stops found
* @param query String queried
*/
public void createStopListFragment(List resultList, String query, boolean addToBackStack){
listenerMain.hideKeyboard();
StopListFragment listfragment = StopListFragment.newInstance(query);
if(managerWeakRef.get()==null || shouldHaltAllActivities) {
//SOMETHING WENT VERY WRONG
Log.e(DEBUG_TAG, "We are asked for a new stop but we can't show anything");
return;
}
attachFragmentToContainer(managerWeakRef.get(),listfragment,
new AttachParameters("search_"+query, false,addToBackStack));
listfragment.setStopList(resultList);
listenerMain.readyGUIfor(FragmentKind.STOPS);
toggleSpinner(false);
}
/**
* Wrapper for toggleSpinner in Activity
* @param on new status of spinner system
*/
public void toggleSpinner(boolean on){
listenerMain.toggleSpinner(on);
}
/**
* Attach a new fragment to a cointainer
* @param fm the FragmentManager
* @param fragment the Fragment
* @param parameters attach parameters
*/
protected void attachFragmentToContainer(FragmentManager fm,Fragment fragment, AttachParameters parameters){
if(shouldHaltAllActivities) //nothing to do
return;
FragmentTransaction ft = fm.beginTransaction();
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);
if(!fm.isDestroyed() && !shouldHaltAllActivities)
ft.commit();
//fm.executePendingTransactions();
}
public synchronized void setBlockAllActivities(boolean shouldI) {
this.shouldHaltAllActivities = shouldI;
}
- public void stopLastRequestIfNeeded(){
+ public void stopLastRequestIfNeeded(boolean interruptIfRunning){
if(lastTaskRef == null) return;
AsyncDataDownload task = lastTaskRef.get();
if(task!=null){
- task.cancel(false);
+ task.cancel(interruptIfRunning);
}
}
/**
* Wrapper to show the errors/status that happened
* @param res result from Fetcher
*/
public void showErrorMessage(Fetcher.Result res){
//TODO: implement a common set of errors for all fragments
switch (res){
case OK:
break;
case CLIENT_OFFLINE:
showToastMessage(R.string.network_error, true);
break;
case SERVER_ERROR:
if (utils.isConnected(context)) {
showToastMessage(R.string.parsing_error, true);
} else {
showToastMessage(R.string.network_error, true);
}
case PARSER_ERROR:
default:
showShortToast(R.string.internal_error);
break;
case QUERY_TOO_SHORT:
showShortToast(R.string.query_too_short);
break;
case EMPTY_RESULT_SET:
showShortToast(R.string.no_bus_stop_have_this_name);
break;
}
}
public void showToastMessage(int messageID, boolean short_lenght) {
final int length = short_lenght ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG;
if (context != null)
Toast.makeText(context, messageID, length).show();
}
private void showShortToast(int messageID){
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
index 4d7a8f9..22771de 100644
--- a/src/it/reyboz/bustorino/fragments/MainScreenFragment.java
+++ b/src/it/reyboz/bustorino/fragments/MainScreenFragment.java
@@ -1,725 +1,739 @@
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.os.Build;
import android.os.Bundle;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
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 java.util.Map;
import it.reyboz.bustorino.R;
import it.reyboz.bustorino.backend.*;
import it.reyboz.bustorino.middleware.AppLocationManager;
import it.reyboz.bustorino.middleware.AsyncDataDownload;
import it.reyboz.bustorino.util.LocationCriteria;
import it.reyboz.bustorino.util.Permissions;
import static it.reyboz.bustorino.util.Permissions.LOCATION_PERMISSIONS;
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 = utils.getDefaultArrivalsFetchers();
//// HIDDEN BUT IMPORTANT ELEMENTS ////
FragmentManager fragMan;
Handler mainHandler;
private final Runnable refreshStop = new Runnable() {
public void run() {
if(getContext() == null) return;
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();
+ Log.e("BusTO-RefreshStop", "Asking for refresh when there is no fragment");
+ // 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;
boolean locationPermissionGranted, locationPermissionAsked = false;
AppLocationManager locationManager;
private final LocationCriteria cr = new LocationCriteria(2000, 10000);
//Location
private AppLocationManager.LocationRequester requester = new AppLocationManager.LocationRequester() {
@Override
public void onLocationChanged(Location loc) {
}
@Override
public void onLocationStatusChanged(int status) {
if(status == AppLocationManager.LOCATION_GPS_AVAILABLE && !isNearbyFragmentShown()){
//request Stops
pendingNearbyStopsRequest = false;
if (getContext()!= null)
mainHandler.post(new NearbyStopsRequester(getContext(), cr));
}
}
@Override
public long getLastUpdateTimeMillis() {
return 50;
}
@Override
public LocationCriteria getLocationCriteria() {
return cr;
}
@Override
public void onLocationProviderAvailable() {
//Log.w(DEBUG_TAG, "pendingNearbyStopRequest: "+pendingNearbyStopsRequest);
if(!isNearbyFragmentShown() && getContext()!=null){
pendingNearbyStopsRequest = false;
mainHandler.post(new NearbyStopsRequester(getContext(), cr));
}
}
@Override
public void onLocationDisabled() {
}
};
private final ActivityResultLauncher requestPermissionLauncher =
registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback