package org.mtransit.android.ui.fragment; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import org.mtransit.android.R; import org.mtransit.android.commons.BundleUtils; import org.mtransit.android.commons.CollectionUtils; import org.mtransit.android.commons.LocationUtils; import org.mtransit.android.commons.MTLog; import org.mtransit.android.commons.data.RouteTripStop; import org.mtransit.android.data.POIArrayAdapter; import org.mtransit.android.data.POIManager; import org.mtransit.android.task.RTSTripStopsLoader; import org.mtransit.android.ui.MTActivityWithLocation; import org.mtransit.android.ui.view.MapViewController; import org.mtransit.android.util.LoaderUtils; import android.app.Activity; import android.content.res.Configuration; import android.location.Location; import android.os.Bundle; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; import android.support.v4.util.Pair; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewStub; import android.widget.AbsListView; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.LatLngBounds; public class RTSTripStopsFragment extends MTFragmentV4 implements VisibilityAwareFragment, LoaderManager.LoaderCallbacks<ArrayList<POIManager>>, MTActivityWithLocation.UserLocationListener, MapViewController.MapMarkerProvider, MapViewController.MapListener { private static final String TAG = RTSTripStopsFragment.class.getSimpleName(); @Override public String getLogTag() { return TAG + "-" + this.tripId; } private static final String EXTRA_AGENCY_AUTHORITY = "extra_agency_authority"; private static final String EXTRA_ROUTE_ID = "extra_route_id"; private static final String EXTRA_TRIP_ID = "extra_trip_id"; private static final String EXTRA_TRIP_STOP_ID = "extra_trip_stop_id"; private static final String EXTRA_FRAGMENT_POSITION = "extra_fragment_position"; private static final String EXTRA_LAST_VISIBLE_FRAGMENT_POSITION = "extra_last_visible_fragment_position"; private static final String EXTRA_SHOWING_LIST_INSTEAD_OF_MAP = "extra_showing_list_instead_of_map"; private static final String EXTRA_CLOSEST_POI_SHOWN = "extra_closest_poi_shown"; public static RTSTripStopsFragment newInstance(int fragmentPosition, int lastVisibleFragmentPosition, String authority, long routeId, long tripId, Integer optStopId, boolean showingListInsteadOfMap) { RTSTripStopsFragment f = new RTSTripStopsFragment(); Bundle args = new Bundle(); args.putString(EXTRA_AGENCY_AUTHORITY, authority); f.authority = authority; args.putLong(EXTRA_ROUTE_ID, routeId); f.routeId = routeId; args.putLong(EXTRA_TRIP_ID, tripId); f.tripId = tripId; if (fragmentPosition >= 0) { args.putInt(EXTRA_FRAGMENT_POSITION, fragmentPosition); f.fragmentPosition = fragmentPosition; } if (lastVisibleFragmentPosition >= 0) { args.putInt(EXTRA_LAST_VISIBLE_FRAGMENT_POSITION, lastVisibleFragmentPosition); f.lastVisibleFragmentPosition = lastVisibleFragmentPosition; } if (optStopId != null) { args.putInt(EXTRA_TRIP_STOP_ID, optStopId); f.stopId = optStopId; } args.putBoolean(EXTRA_SHOWING_LIST_INSTEAD_OF_MAP, showingListInsteadOfMap); f.showingListInsteadOfMap = showingListInsteadOfMap; f.setArguments(args); return f; } private Long routeId; private Long tripId; private int stopId = -1; private boolean closestPOIShow = false; private String authority; private POIArrayAdapter adapter; private Location userLocation; private int fragmentPosition = -1; private int lastVisibleFragmentPosition = -1; private boolean fragmentVisible = false; private MapViewController mapViewController = new MapViewController(getLogTag(), this, this, true, true, true, false, false, false, 0, false, true, false, true, false); @Override public void onAttach(Activity activity) { super.onAttach(activity); initAdapters(activity); this.mapViewController.onAttach(activity); } @Override public void onDetach() { super.onDetach(); this.mapViewController.onDetach(); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); restoreInstanceState(savedInstanceState, getArguments()); this.mapViewController.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); View view = inflater.inflate(R.layout.fragment_rts_trip_stops, container, false); this.mapViewController.onCreateView(view, savedInstanceState); return view; } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); setupView(view); this.mapViewController.onViewCreated(view, savedInstanceState); } @Override public void onSaveInstanceState(Bundle outState) { if (!TextUtils.isEmpty(this.authority)) { outState.putString(EXTRA_AGENCY_AUTHORITY, this.authority); } if (this.routeId != null) { outState.putLong(EXTRA_ROUTE_ID, this.routeId); } if (this.tripId != null) { outState.putLong(EXTRA_TRIP_ID, this.tripId); } outState.putInt(EXTRA_TRIP_STOP_ID, this.stopId); outState.putBoolean(EXTRA_CLOSEST_POI_SHOWN, this.closestPOIShow); if (this.fragmentPosition >= 0) { outState.putInt(EXTRA_FRAGMENT_POSITION, this.fragmentPosition); } if (this.lastVisibleFragmentPosition >= 0) { outState.putInt(EXTRA_LAST_VISIBLE_FRAGMENT_POSITION, this.lastVisibleFragmentPosition); } if (this.showingListInsteadOfMap != null) { outState.putBoolean(EXTRA_SHOWING_LIST_INSTEAD_OF_MAP, this.showingListInsteadOfMap); } this.mapViewController.onSaveInstanceState(outState); super.onSaveInstanceState(outState); } private void restoreInstanceState(Bundle... bundles) { String newAuthority = BundleUtils.getString(EXTRA_AGENCY_AUTHORITY, bundles); if (!TextUtils.isEmpty(newAuthority) && !newAuthority.equals(this.authority)) { this.authority = newAuthority; } Long newTripId = BundleUtils.getLong(EXTRA_TRIP_ID, bundles); if (newTripId != null && !newTripId.equals(this.tripId)) { this.tripId = newTripId; } Long newRouteId = BundleUtils.getLong(EXTRA_ROUTE_ID, bundles); if (newRouteId != null && !newRouteId.equals(this.routeId)) { this.routeId = newRouteId; } Integer newStopId = BundleUtils.getInt(EXTRA_TRIP_STOP_ID, bundles); if (newStopId != null && !newStopId.equals(this.stopId)) { this.stopId = newStopId; } Boolean newClosestPOIShown = BundleUtils.getBoolean(EXTRA_CLOSEST_POI_SHOWN, bundles); if (newClosestPOIShown != null) { this.closestPOIShow = newClosestPOIShown; } Boolean newShowingListInsteadOfMap = BundleUtils.getBoolean(EXTRA_SHOWING_LIST_INSTEAD_OF_MAP, bundles); if (newShowingListInsteadOfMap != null) { this.showingListInsteadOfMap = newShowingListInsteadOfMap; } Integer fragmentPosition = BundleUtils.getInt(EXTRA_FRAGMENT_POSITION, bundles); if (fragmentPosition != null) { if (fragmentPosition >= 0) { this.fragmentPosition = fragmentPosition; } else { this.fragmentPosition = -1; } } Integer lastVisibleFragmentPosition = BundleUtils.getInt(EXTRA_LAST_VISIBLE_FRAGMENT_POSITION, bundles); if (lastVisibleFragmentPosition != null) { if (lastVisibleFragmentPosition >= 0) { this.lastVisibleFragmentPosition = lastVisibleFragmentPosition; } else { this.lastVisibleFragmentPosition = -1; } } this.adapter.setTag(this.authority + "-" + this.tripId); this.mapViewController.setTag(getLogTag()); } private void initAdapters(Activity activity) { this.adapter = new POIArrayAdapter(activity); this.adapter.setShowExtra(false); } @Override public POIManager getClosestPOI() { return this.adapter == null ? null : this.adapter.getClosestPOI(); } @Override public POIManager getPOI(String uuid) { return this.adapter == null ? null : this.adapter.getItem(uuid); } @Override public Collection<POIManager> getPOIs() { if (this.adapter == null || !this.adapter.isInitialized()) { return null; } HashSet<POIManager> pois = new HashSet<POIManager>(); if (this.adapter != null && this.adapter.hasPois()) { for (int i = 0; i < this.adapter.getPoisCount(); i++) { pois.add(this.adapter.getItem(i)); } } return pois; } @Override public Collection<MapViewController.POIMarker> getPOMarkers() { return null; } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); this.mapViewController.onConfigurationChanged(newConfig); } @Override public void onCameraChange(LatLngBounds latLngBounds) { } @Override public void onMapClick(LatLng position) { } private void linkAdapterWithListView(View view) { if (view == null || this.adapter == null) { return; } View listView = view.findViewById(R.id.list); if (listView != null) { this.adapter.setListView((AbsListView) listView); } } private void setupView(View view) { if (view == null) { return; } if (this.showingListInsteadOfMap) { // list inflateList(view); // inflate ASAP for view state restore switchView(view); } } @Override public void setFragmentPosition(int fragmentPosition) { this.fragmentPosition = fragmentPosition; setFragmentVisibleAtPosition(this.lastVisibleFragmentPosition); // force reset visibility } @Override public void setFragmentVisibleAtPosition(int visibleFragmentPosition) { if (this.lastVisibleFragmentPosition == visibleFragmentPosition // && (// (this.fragmentPosition == visibleFragmentPosition && this.fragmentVisible) // || // (this.fragmentPosition != visibleFragmentPosition && !this.fragmentVisible) // ) // ) { return; } this.lastVisibleFragmentPosition = visibleFragmentPosition; if (this.fragmentPosition < 0) { return; } if (this.fragmentPosition == visibleFragmentPosition) { onFragmentVisible(); } else { onFragmentInvisible(); } } private void onFragmentInvisible() { if (!this.fragmentVisible) { return; // already invisible } this.fragmentVisible = false; if (this.adapter != null) { this.adapter.onPause(); } if (!this.showingListInsteadOfMap) { // map this.mapViewController.onPause(); } } @Override public boolean isFragmentVisible() { return this.fragmentVisible; } private void onFragmentVisible() { if (this.fragmentVisible) { return; // already visible } if (!isResumed()) { return; } this.fragmentVisible = true; if (!this.showingListInsteadOfMap) { // map this.mapViewController.onResume(); } View view = getView(); switchView(view); if (this.adapter == null || !this.adapter.isInitialized()) { LoaderUtils.restartLoader(this, POIS_LOADER, null, this); } else { this.adapter.onResume(getActivity(), this.userLocation); } onUserLocationChanged(((MTActivityWithLocation) getActivity()).getUserLocation()); } private static final int POIS_LOADER = 0; @Override public Loader<ArrayList<POIManager>> onCreateLoader(int id, Bundle args) { switch (id) { case POIS_LOADER: if (this.tripId == null || TextUtils.isEmpty(this.authority)) { return null; } return new RTSTripStopsLoader(getActivity(), this.tripId, this.authority); default: MTLog.w(this, "Loader id '%s' unknown!", id); return null; } } @Override public void onLoaderReset(Loader<ArrayList<POIManager>> loader) { if (this.adapter != null) { this.adapter.clear(); } this.mapViewController.notifyMarkerChanged(this); } @Override public void onLoadFinished(Loader<ArrayList<POIManager>> loader, ArrayList<POIManager> data) { Pair<Integer, String> currentSelectedItemIndexUuid = null; if (this.stopId > 0 || !this.closestPOIShow) { if (this.stopId > 0) { currentSelectedItemIndexUuid = findStopIndexUuid(this.stopId, data); } if (currentSelectedItemIndexUuid == null) { if (!this.closestPOIShow) { currentSelectedItemIndexUuid = findClosestPOIIndexUuid(data); } } this.stopId = -1; // can only be used once this.closestPOIShow = true; // only the 1rst time } this.adapter.setPois(data); this.mapViewController.notifyMarkerChanged(this); this.adapter.updateDistanceNowAsync(this.userLocation); View view = getView(); if (this.showingListInsteadOfMap) { // list Integer selectedPosition = currentSelectedItemIndexUuid == null ? null : currentSelectedItemIndexUuid.first; if (selectedPosition != null && selectedPosition > 0) { if (view != null) { inflateList(view); ((AbsListView) view.findViewById(R.id.list)).setSelection(selectedPosition - 1); // show 1 more stop on top of the list } } } switchView(view); } private Pair<Integer, String> findStopIndexUuid(int stopId, ArrayList<POIManager> pois) { for (int i = 0; i < pois.size(); i++) { POIManager poim = pois.get(i); if (poim != null && poim.poi instanceof RouteTripStop) { RouteTripStop rts = (RouteTripStop) poim.poi; if (rts.getStop().getId() == stopId) { return new Pair<Integer, String>(i, poim.poi.getUUID()); } } } return null; } private Pair<Integer, String> findClosestPOIIndexUuid(ArrayList<POIManager> pois) { if (this.userLocation != null && CollectionUtils.getSize(pois) > 0) { LocationUtils.updateDistance(pois, this.userLocation.getLatitude(), this.userLocation.getLongitude()); ArrayList<POIManager> sortedPOIs = new ArrayList<POIManager>(pois); CollectionUtils.sort(sortedPOIs, LocationUtils.POI_DISTANCE_COMPARATOR); String closestPoiUuid = sortedPOIs.get(0).poi.getUUID(); for (int i = 0; i < pois.size(); i++) { POIManager poim = pois.get(i); if (poim.poi.getUUID().equals(closestPoiUuid)) { return new Pair<Integer, String>(i, poim.poi.getUUID()); } } } return null; } @Override public void onUserLocationChanged(Location newLocation) { if (newLocation == null) { return; } if (this.userLocation == null || LocationUtils.isMoreRelevant(getLogTag(), this.userLocation, newLocation)) { this.userLocation = newLocation; if (this.adapter != null) { this.adapter.setLocation(newLocation); } } this.mapViewController.onUserLocationChanged(newLocation); } @Override public void onPause() { super.onPause(); this.stopId = -1; onFragmentInvisible(); } @Override public void onResume() { super.onResume(); if (this.fragmentPosition >= 0 && this.fragmentPosition == this.lastVisibleFragmentPosition) { onFragmentVisible(); } // ELSE would be call later if (this.adapter != null) { this.adapter.setActivity(getActivity()); } } @Override public void onLowMemory() { super.onLowMemory(); this.mapViewController.onLowMemory(); } @Override public void onDestroyView() { super.onDestroyView(); this.mapViewController.onDestroyView(); } @Override public void onDestroy() { super.onDestroy(); if (this.adapter != null) { this.adapter.onDestroy(); } this.mapViewController.onDestroy(); } private void switchView(View view) { if (view == null) { return; } if (this.adapter == null || !this.adapter.isInitialized()) { showLoading(view); } else if (this.adapter.getPoisCount() == 0) { showEmpty(view); } else { showListOrMap(view); } } private void showListOrMap(View view) { if (this.showingListInsteadOfMap) { // list this.mapViewController.hideMap(); inflateList(view); view.findViewById(R.id.list).setVisibility(View.VISIBLE); // show } else { // map this.mapViewController.showMap(view); if (view.findViewById(R.id.list) != null) { // IF inflated/present DO view.findViewById(R.id.list).setVisibility(View.GONE); // hide } } if (view.findViewById(R.id.loading) != null) { // IF inflated/present DO view.findViewById(R.id.loading).setVisibility(View.GONE); // hide } if (view.findViewById(R.id.empty) != null) { // IF inflated/present DO view.findViewById(R.id.empty).setVisibility(View.GONE); // hide } } private void inflateList(View view) { if (view.findViewById(R.id.list) == null) { // IF NOT present/inflated DO ((ViewStub) view.findViewById(R.id.list_stub)).inflate(); // inflate linkAdapterWithListView(view); } } private void showLoading(View view) { if (view.findViewById(R.id.list) != null) { // IF inflated/present DO view.findViewById(R.id.list).setVisibility(View.GONE); // hide } this.mapViewController.hideMap(); if (view.findViewById(R.id.empty) != null) { // IF inflated/present DO view.findViewById(R.id.empty).setVisibility(View.GONE); // hide } view.findViewById(R.id.loading).setVisibility(View.VISIBLE); // show } private void showEmpty(View view) { if (view.findViewById(R.id.list) != null) { // IF inflated/present DO view.findViewById(R.id.list).setVisibility(View.GONE); // hide } this.mapViewController.hideMap(); if (view.findViewById(R.id.loading) != null) { // IF inflated/present DO view.findViewById(R.id.loading).setVisibility(View.GONE); // hide } if (view.findViewById(R.id.empty) == null) { // IF NOT present/inflated DO ((ViewStub) view.findViewById(R.id.empty_stub)).inflate(); // inflate } view.findViewById(R.id.empty).setVisibility(View.VISIBLE); // show } private Boolean showingListInsteadOfMap = null; public void setShowingListInsteadOfMap(boolean newShowingListInsteadOfMap) { if (this.showingListInsteadOfMap != null && this.showingListInsteadOfMap == newShowingListInsteadOfMap) { return; // nothing changed } this.showingListInsteadOfMap = newShowingListInsteadOfMap; // switching if (this.adapter != null) { View view = getView(); setupView(view); if (!this.showingListInsteadOfMap) { // map this.mapViewController.onResume(); } else { // list this.mapViewController.onPause(); } switchView(view); } } }