/* * Copyright (C) 2014-2015 University of South Florida (sjbarbeau@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onebusaway.android.report.ui; import org.onebusaway.android.R; import org.onebusaway.android.app.Application; import org.onebusaway.android.io.ObaAnalytics; import org.onebusaway.android.io.elements.ObaArrivalInfo; import org.onebusaway.android.io.elements.ObaRoute; import org.onebusaway.android.io.elements.ObaStop; import org.onebusaway.android.io.elements.ObaStopElement; import org.onebusaway.android.map.MapParams; import org.onebusaway.android.map.googlemapsv2.BaseMapFragment; import org.onebusaway.android.report.connection.GeocoderTask; import org.onebusaway.android.report.connection.ServiceListTask; import org.onebusaway.android.report.constants.ReportConstants; import org.onebusaway.android.report.ui.adapter.EntrySpinnerAdapter; import org.onebusaway.android.report.ui.adapter.SectionItem; import org.onebusaway.android.report.ui.adapter.ServiceSpinnerItem; import org.onebusaway.android.report.ui.adapter.SpinnerItem; import org.onebusaway.android.report.ui.dialog.ReportSuccessDialog; import org.onebusaway.android.report.ui.util.IssueLocationHelper; import org.onebusaway.android.report.ui.util.ServiceUtils; import org.onebusaway.android.util.LocationUtils; import org.onebusaway.android.util.PreferenceUtils; import org.onebusaway.android.util.ShowcaseViewUtils; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.location.Address; import android.location.Geocoder; import android.location.Location; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.Window; import android.view.inputmethod.EditorInfo; import android.widget.AdapterView; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.Spinner; import android.widget.TextView; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import edu.usf.cutr.open311client.Open311; import edu.usf.cutr.open311client.Open311Manager; import edu.usf.cutr.open311client.models.Service; import edu.usf.cutr.open311client.models.ServiceListRequest; import edu.usf.cutr.open311client.models.ServiceListResponse; public class InfrastructureIssueActivity extends BaseReportActivity implements BaseMapFragment.OnFocusChangedListener, ServiceListTask.Callback, ReportProblemFragmentCallback, IssueLocationHelper.Callback, SimpleArrivalListFragment.Callback, GeocoderTask.Callback { private static final int REQUEST_CODE = 0; private static final String SHOW_STOP_MARKER = ".showMarker"; private static final String SELECTED_SERVICE = ".selectedService"; private static final String SELECTED_SERVICE_TYPE = ".selectedServiceType"; private static final String RESTORED_SERVICE = ".restoredService"; private static final String SHOW_CATEGORIES = ".showCategories"; private static final String SHOW_INFO = ".showInfo"; private static final String HEURISTIC_MATCH = ".isHeuristicMatch"; private static final String ARRIVAL_LIST = ".arrivalList"; private static final String TRIP_INFO = ".tripInfo"; private static final String AGENCY_NAME = ".agencyName"; private static final String BLOCK_ID = ".blockId"; private static final String ACTION_BAR_TITLE = ".actionBarTitle"; private static final String TUTORIAL_COUNTER = "tutorial_counter"; /** * UI Elements */ private EditText mAddressEditText; private Spinner mServicesSpinner; private RelativeLayout mBusStopHeader; //Map Fragment private BaseMapFragment mMapFragment; // Services spinner container private FrameLayout mServicesSpinnerFrameLayout; //Location helper for tracking the issue location private IssueLocationHelper mIssueLocationHelper; /** * Open311 client */ private Open311 mOpen311; /** * Store the transit service type if there is no bus stop selected * Then, when a user selects a bus stop, find the transit service by type and show */ private String mTransitServiceIssueTypeWithoutStop; /** * Selected transit service object */ private Service mSelectedTransitService; /** * Select this issue type when the activity starts * If a user selects "report stop or trip problem" from the main activity, set the default * value to this variable */ private String mDefaultIssueType; /** * True if the transit services were matched heuristically, and false if they were not */ private boolean mIsAllTransitHeuristicMatch; /** * Restore this issue on rotation */ private String mRestoredServiceName; /** * Selected arrival information for trip problem reporting */ private ObaArrivalInfo mArrivalInfo; /** * Block ID for the trip in mArrivalInfo */ private String mBlockId; /** * Agency name for trip problem reporting */ private String mAgencyName; /** * For rotation changes: * Save instance state vars * Restores old selected categories and marker position */ private boolean mShowCategories = false; private boolean mShowStopMarker = false; private boolean mShowArrivalListFragment = false; /** * Starts the InfrastructureIssueActivity. * * startActivityForResult was used to close the calling activity. This used in BaseReportActivity * to close it when a user submits an issue. * * @param activity The parent activity. * @param intent The Intent containing focusId, lat, lon of the map */ public static void start(Activity activity, Intent intent) { activity.startActivityForResult(makeIntent(activity, intent), REQUEST_CODE); } /** * Starts the InfrastructureIssueActivity with a given open311 service category selected * * startActivityForResult was used to close the calling activity. This used in BaseReportActivity * to close it when a user submits an issue. * * @param activity The parent activity. * @param intent The Intent containing focusId, lat, lon of the map */ public static void startWithService(Activity activity, Intent intent, String serviceKeyword) { intent = makeIntent(activity, intent); intent.putExtra(SELECTED_SERVICE, serviceKeyword); activity.startActivityForResult(intent, REQUEST_CODE); } /** * Starts the InfrastructureIssueActivity with a given open311 service category selected * * @param context The context of the parent activity. * @param intent The Intent containing focusId, lat, lon of the map */ public static void startWithService(Context context, Intent intent, String serviceKeyword) { intent = makeIntent(context, intent); intent.putExtra(SELECTED_SERVICE, serviceKeyword); context.startActivity(intent); } /** * Starts the InfrastructureIssueActivity with a given open311 service category selected * * startActivityForResult was used to close the calling activity. This used in BaseReportActivity * to close it when a user submits an issue. * * @param activity The parent activity. * @param intent The Intent containing focusId, lat, lon of the map * @param obaArrivalInfo Arrival info for trip problems * @param agencyName Name of the agency that operates the service * @param blockId Block ID for the trip in obaArrivalInfo */ public static void startWithService(Activity activity, Intent intent, String serviceKeyword , ObaArrivalInfo obaArrivalInfo, String agencyName, String blockId) { intent = makeIntent(activity, intent); // Put trip issue specific data intent.putExtra(SELECTED_SERVICE, serviceKeyword); intent.putExtra(TRIP_INFO, obaArrivalInfo); intent.putExtra(AGENCY_NAME, agencyName); intent.putExtra(BLOCK_ID, blockId); activity.startActivityForResult(intent, REQUEST_CODE); } /** * Returns an intent that will start the MapActivity with a particular stop * focused with the center of the map at a particular point. * * @param context The context of the activity. * @param intent The Intent containing focusId, lat, lon of the map */ public static Intent makeIntent(Context context, Intent intent) { Intent myIntent = new Intent(context, InfrastructureIssueActivity.class); myIntent.putExtra(MapParams.STOP_ID, intent.getStringExtra(MapParams.STOP_ID)); myIntent.putExtra(MapParams.STOP_NAME, intent.getStringExtra(MapParams.STOP_NAME)); myIntent.putExtra(MapParams.STOP_CODE, intent.getStringExtra(MapParams.STOP_CODE)); myIntent.putExtra(MapParams.CENTER_LAT, intent.getDoubleExtra(MapParams.CENTER_LAT, 0)); myIntent.putExtra(MapParams.CENTER_LON, intent.getDoubleExtra(MapParams.CENTER_LON, 0)); return myIntent; } @Override protected void onCreate(Bundle savedInstanceState) { supportRequestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); super.onCreate(savedInstanceState); setContentView(R.layout.infrastructure_issue); setUpOpen311(); setUpProgressBar(); setupMapFragment(savedInstanceState); setupLocationHelper(savedInstanceState); setupViews(); setupIntentData(savedInstanceState); setupIconColors(); initLocation(); setActionBarTitle(savedInstanceState); } /** * Set the intent parameters when the activity starts * @param savedInstanceState */ private void setupIntentData(Bundle savedInstanceState) { if (savedInstanceState == null) { double lat = getIntent().getDoubleExtra(MapParams.CENTER_LAT, 0); double lon = getIntent().getDoubleExtra(MapParams.CENTER_LON, 0); // If a bus stop selected in the main screen // Set the focus to the current stop String stopId = getIntent().getStringExtra(MapParams.STOP_ID); String stopName = getIntent().getStringExtra(MapParams.STOP_NAME); String stopCode = getIntent().getStringExtra(MapParams.STOP_CODE); if (stopId != null) { mIssueLocationHelper.setObaStop(new ObaStopElement(stopId, lat, lon, stopName, stopCode)); showBusStopHeader(stopName); mShowStopMarker = true; } mArrivalInfo = (ObaArrivalInfo) getIntent().getSerializableExtra(TRIP_INFO); mAgencyName = getIntent().getStringExtra(AGENCY_NAME); mBlockId = getIntent().getStringExtra(BLOCK_ID); mDefaultIssueType = getIntent().getStringExtra(SELECTED_SERVICE); } } @Override protected void onStart() { super.onStart(); ObaAnalytics.reportActivityStart(this); } @Override protected void onPause() { ShowcaseViewUtils.hideShowcaseView(); super.onPause(); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putFloat(MapParams.ZOOM, mMapFragment.getMapView().getZoomLevelAsFloat()); ObaStop obaStop = mIssueLocationHelper.getObaStop(); if (obaStop != null) { String stopId = obaStop.getId(); getIntent().putExtra(MapParams.STOP_ID, stopId); outState.putString(MapParams.STOP_ID, stopId); outState.putString(MapParams.STOP_NAME, obaStop.getName()); outState.putString(MapParams.STOP_CODE, obaStop.getStopCode()); outState.putBoolean(SHOW_STOP_MARKER, true); } outState.putDouble(MapParams.CENTER_LAT, mIssueLocationHelper.getIssueLocation().getLatitude()); outState.putDouble(MapParams.CENTER_LON, mIssueLocationHelper.getIssueLocation().getLongitude()); SpinnerItem spinnerItem = (SpinnerItem) mServicesSpinner.getSelectedItem(); if (spinnerItem != null && (!spinnerItem.isHint() && !spinnerItem.isSection() || (spinnerItem.isHint() && mServicesSpinnerFrameLayout.getVisibility() == View.VISIBLE))) { Service service = ((ServiceSpinnerItem) spinnerItem).getService(); outState.putString(RESTORED_SERVICE, service.getService_name()); outState.putBoolean(SHOW_CATEGORIES, true); } if (isInfoVisible()) { String infoText = ((TextView) mInfoHeader.findViewById(R.id.ri_info_text)).getText(). toString(); outState.putString(SHOW_INFO, infoText); } outState.putBoolean(ARRIVAL_LIST, mShowArrivalListFragment); outState.putBoolean(HEURISTIC_MATCH, mIsAllTransitHeuristicMatch); outState.putString(AGENCY_NAME, mAgencyName); outState.putString(BLOCK_ID, mBlockId); outState.putString(SELECTED_SERVICE_TYPE, mTransitServiceIssueTypeWithoutStop); outState.putString(ACTION_BAR_TITLE, getTitle().toString()); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); if (savedInstanceState != null) { mShowCategories = savedInstanceState.getBoolean(SHOW_CATEGORIES, false); mShowStopMarker = savedInstanceState.getBoolean(SHOW_STOP_MARKER, false); mShowArrivalListFragment = savedInstanceState.getBoolean(ARRIVAL_LIST, false); mRestoredServiceName = savedInstanceState.getString(RESTORED_SERVICE); mAgencyName = savedInstanceState.getString(AGENCY_NAME); mBlockId = savedInstanceState.getString(BLOCK_ID); mTransitServiceIssueTypeWithoutStop = savedInstanceState.getString(SELECTED_SERVICE_TYPE); mIsAllTransitHeuristicMatch = savedInstanceState.getBoolean(HEURISTIC_MATCH); String bundleStopId = savedInstanceState.getString(MapParams.STOP_ID); String stopName = savedInstanceState.getString(MapParams.STOP_NAME); String stopCode = savedInstanceState.getString(MapParams.STOP_CODE); if (bundleStopId != null) { double lat = savedInstanceState.getDouble(MapParams.CENTER_LAT, 0); double lon = savedInstanceState.getDouble(MapParams.CENTER_LON, 0); Location location = LocationUtils.makeLocation(lat, lon); mIssueLocationHelper.updateMarkerPosition(location, new ObaStopElement(bundleStopId, lat, lon, stopName, stopCode)); } String infoText = savedInstanceState.getString(SHOW_INFO); if (infoText != null) { addInfoText(infoText); } if (mShowArrivalListFragment) { removeTripProblemFragment(); showArrivalListFragment(mIssueLocationHelper.getObaStop()); } } } /** * Set default open311 client */ private void setUpOpen311() { mOpen311 = Open311Manager.getDefaultOpen311(); } /** * Setting up the BaseMapFragment * BaseMapFragment was used to implement a map. */ private void setupMapFragment(Bundle bundle) { FragmentManager fm = getSupportFragmentManager(); Fragment fragment = fm.findFragmentByTag(BaseMapFragment.TAG); if (fragment != null) { mMapFragment = (BaseMapFragment) fragment; mMapFragment.setOnFocusChangeListener(this); } if (mMapFragment == null) { mMapFragment = new BaseMapFragment(); mMapFragment.setArguments(bundle); // Register listener for map focus callbacks mMapFragment.setOnFocusChangeListener(this); fm.beginTransaction().add(R.id.ri_frame_map_view, mMapFragment, BaseMapFragment.TAG).commit(); } fm.beginTransaction().show(mMapFragment).commit(); } /** * Setting up the location helper * IssueLocationHelper helps tracking the issue location and issue stop */ private void setupLocationHelper(Bundle savedInstanceState) { double lat; double lon; if (savedInstanceState == null) { lat = getIntent().getDoubleExtra(MapParams.CENTER_LAT, 0); lon = getIntent().getDoubleExtra(MapParams.CENTER_LON, 0); } else { lat = savedInstanceState.getDouble(MapParams.CENTER_LAT, 0); lon = savedInstanceState.getDouble(MapParams.CENTER_LON, 0); } Location mapCenterLocation = LocationUtils.makeLocation(lat, lon); mIssueLocationHelper = new IssueLocationHelper(mapCenterLocation, this); // Set map center location mMapFragment.setMapCenter(mapCenterLocation, true, false); } /** * Initialize UI components */ private void setupViews() { if (getSupportActionBar() != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); } mServicesSpinnerFrameLayout = (FrameLayout) findViewById(R.id.ri_spinnerView); mAddressEditText = (EditText) findViewById(R.id.ri_address_editText); mAddressEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (actionId == EditorInfo.IME_ACTION_SEARCH) { searchAddress(); return true; } return false; } }); CustomScrollView mainScrollView = (CustomScrollView) findViewById(R.id.ri_scrollView); mainScrollView.addInterceptScrollView(findViewById(R.id.ri_frame_map_view)); mServicesSpinner = (Spinner) findViewById(R.id.ri_spinnerServices); mServicesSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) { if (mShowCategories) { mShowCategories = false; return; } SpinnerItem spinnerItem = (SpinnerItem) mServicesSpinner.getSelectedItem(); if (!spinnerItem.isHint() && !spinnerItem.isSection()) { Service service = ((ServiceSpinnerItem) spinnerItem).getService(); onSpinnerItemSelected(service); } else if (spinnerItem.isHint()) { clearReportingFragments(false); } } @Override public void onNothingSelected(AdapterView<?> parentView) { } }); mBusStopHeader = (RelativeLayout) findViewById(R.id.bus_stop_header); } private void setActionBarTitle(Bundle savedInstanceState) { if (savedInstanceState == null) { setActionBarTitle(mDefaultIssueType); } else { setTitle(savedInstanceState.getString(ACTION_BAR_TITLE)); } } /** * Set action bar title by issue type * * @param issueType could be stop, trip, dynamic_stop or dynamic_trip */ private void setActionBarTitle(String issueType) { if (ServiceUtils.isTransitStopServiceByType(issueType)) { setTitle(getString(R.string.rt_stop_problem_title)); } else if (ServiceUtils.isTransitTripServiceByType(issueType)) { setTitle(getString(R.string.rt_arrival_problem_title)); } else { setTitle(getString(R.string.rt_infrastructure_problem_title)); } } private void setupIconColors() { ((ImageView) findViewById(R.id.ri_ic_location)).setColorFilter( getResources().getColor(R.color.material_gray)); ((ImageView) findViewById(R.id.ri_ic_category)).setColorFilter( getResources().getColor(R.color.material_gray)); ((ImageView) findViewById(R.id.ri_header_location)).setColorFilter( getResources().getColor(android.R.color.white)); } /** * Show the current location on the map */ private void initLocation() { syncAddress(mIssueLocationHelper.getIssueLocation()); } private void onSpinnerItemSelected(Service service) { // Set static issue type to null mTransitServiceIssueTypeWithoutStop = null; mSelectedTransitService = null; if (service.getService_code() != null && !(service.getType() != null && service.getType().contains(ReportConstants.DYNAMIC_SERVICE))) { showOpen311Reporting(service); } else if (!ReportConstants.DEFAULT_SERVICE.equalsIgnoreCase(service.getType())) { mSelectedTransitService = service; //Remove the info text for select category removeInfoText(); showTransitService(service.getType()); } // Set action bar title based on the selected issue setActionBarTitle(service.getType()); } /** * Places a marker into the map from given Location * * @param location position for marker */ private void updateMarkerPosition(Location location) { int markerId = mMapFragment.addMarker(location, null); mIssueLocationHelper.handleMarkerUpdate(markerId); } /** * Called when the issue location changes * Retrieves Open311 services from current address * * @param location current issue location */ private void syncAddress(Location location) { syncAddressString(location); showProgress(Boolean.TRUE); ServiceListRequest slr = new ServiceListRequest(location.getLatitude(), location.getLongitude()); List<Open311> open311List = Open311Manager.getAllOpen311(); ServiceListTask serviceListTask = new ServiceListTask(slr, open311List, this); serviceListTask.execute(); } /** * Search address string from mAddressEditText */ private void searchAddress() { showProgress(Boolean.TRUE); String addressString = mAddressEditText.getText().toString(); Location location = getLocationByAddress(addressString); if (location != null) { mMapFragment.setMapCenter(location, true, true); syncAddress(location); } else { String message = getResources().getString(R.string.ri_address_not_found); createToastMessage(message); } showProgress(Boolean.FALSE); } /** * Calculates the address of a given location * * @param location takes location object */ private void syncAddressString(Location location) { GeocoderTask gct = new GeocoderTask(this, location, this); gct.execute(); } /** * Converts plain address string to Location object * * @param addressString takes address string * @return Location of given address String */ public Location getLocationByAddress(String addressString) { Geocoder coder = new Geocoder(this); List<Address> addressList; Location location; try { addressList = coder.getFromLocationName(addressString, 3); if (addressList == null || addressList.size() == 0) { return null; } Address address = addressList.get(0); location = new Location(""); location.setLatitude(address.getLatitude()); location.setLongitude(address.getLongitude()); return location; } catch (IOException e) { e.printStackTrace(); } return null; } @Override public void onClearMarker(int markerId) { mMapFragment.removeMarker(markerId); } /** * Called by the BaseMapFragment when a stop obtains focus, or no stops have focus * * @param stop the ObaStop that obtained focus, or null if no stop is in focus * @param routes a HashMap of all route display names that serve this stop - key is routeId */ @Override public void onFocusChanged(ObaStop stop, HashMap<String, ObaRoute> routes, Location location) { mIssueLocationHelper.updateMarkerPosition(location, stop); // Don't call syncAddress if you are restoring the instance if (mShowStopMarker) { if (stop != null) { // Show bus stop name on the header showBusStopHeader(stop.getName()); } mShowStopMarker = false; return; } // Clear all reporting fragments clearReportingFragments(); if (stop != null) { // Clear manually added markers mIssueLocationHelper.clearMarkers(); // Show bus stop name on the header showBusStopHeader(stop.getName()); showServicesSpinner(); if (mTransitServiceIssueTypeWithoutStop != null) { showTransitService(mTransitServiceIssueTypeWithoutStop); syncAddressString(location); } else { syncAddress(stop.getLocation()); } // Clear static issue type mTransitServiceIssueTypeWithoutStop = null; } else if (location != null) { hideBusStopHeader(); syncAddress(location); } // Set action bar title based on the selected issue setActionBarTitle(""); } @Override public void onSendReport() { (new ReportSuccessDialog()).show(getSupportFragmentManager(), ReportSuccessDialog.TAG); } @Override public void onBackPressed() { super.onBackPressed(); // If a fragment closes via back button, show 'Choose a Problem' category mServicesSpinner.setSelection(0); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: finishActivityWithResult(); return true; default: return super.onOptionsItemSelected(item); } } /** * Called by the ServicesTask when Open311 endpoint returns ServiceList * * @param services ServiceListResponse * @param open311 returns active open311 */ @Override public void onServicesTaskCompleted(ServiceListResponse services, Open311 open311) { // Close progress showProgress(Boolean.FALSE); // Set main open311 this.mOpen311 = open311; prepareServiceList(services); } /** * Called when geocoder converts the location to an address string * * @param address the address string from given location */ @Override public void onGeocoderTaskCompleted(String address) { mAddressEditText.setText(address); } /** * Prepares the service lists and shows as categories in the screen * Adds static service categories (stop and trip problems) if they are * not specified by open311 endpoint */ private void prepareServiceList(ServiceListResponse services) { // Create the service list List<Service> serviceList = new ArrayList<>(); // Add services to list if service response is successful if (services != null && services.isSuccess() && Open311Manager.isAreaManagedByOpen311(services.getServiceList())) { for (Service s : services.getServiceList()) { if (s.getService_name() != null && s.getService_code() != null) { serviceList.add(s); } } } if (mShowStopMarker) { mMapFragment.setFocusStop(mIssueLocationHelper.getObaStop(), new ArrayList<ObaRoute>()); showServicesSpinner(); } else if (Open311Manager.isAreaManagedByOpen311(serviceList)) { // Set marker on the map if there are open311 services updateMarkerPosition(mIssueLocationHelper.getIssueLocation()); showServicesSpinner(); } else { // If there is no open311 services return and there is no stop selected // then hide the categories if (mIssueLocationHelper.getObaStop() == null) { hideServicesSpinner(); // Show information to the user if there is no error on location addInfoText(getString(R.string.report_dialog_stop_header)); } } // Mark the services that are transit-related mIsAllTransitHeuristicMatch = ServiceUtils .markTransitServices(getApplicationContext(), serviceList); /** * Map the group names with service list */ Map<String, List<Service>> serviceListMap = new TreeMap<>(); for (Service s : serviceList) { String groupName = s.getGroup() == null ? getString(R.string.ri_others) : s.getGroup(); List<Service> mappedList = serviceListMap.get(groupName); if (mappedList != null) { mappedList.add(s); } else { mappedList = new ArrayList<>(); mappedList.add(s); serviceListMap.put(groupName, mappedList); } } /** * Create Ordered Spinner items */ ArrayList<SpinnerItem> spinnerItems = new ArrayList<>(); ServiceSpinnerItem hintServiceSpinnerItem = new ServiceSpinnerItem( new Service(getString(R.string.ri_service_default), ReportConstants.DEFAULT_SERVICE)); hintServiceSpinnerItem.setHint(true); spinnerItems.add(hintServiceSpinnerItem); // Create Transit categories first if (serviceListMap.get(ReportConstants.ISSUE_GROUP_TRANSIT) != null) { spinnerItems.add(new SectionItem(ReportConstants.ISSUE_GROUP_TRANSIT)); for (Service s : serviceListMap.get(ReportConstants.ISSUE_GROUP_TRANSIT)) { spinnerItems.add(new ServiceSpinnerItem(s)); } } // Create the rest of the categories for (String key : serviceListMap.keySet()) { // Skip if it is transit category if (ReportConstants.ISSUE_GROUP_TRANSIT.equals(key)) { continue; } spinnerItems.add(new SectionItem(key)); for (Service s : serviceListMap.get(key)) { spinnerItems.add(new ServiceSpinnerItem(s)); } } EntrySpinnerAdapter adapter = new EntrySpinnerAdapter(this, spinnerItems); mServicesSpinner.setAdapter(adapter); if (mDefaultIssueType != null) { // Select an default issue category programmatically selectDefaultTransitCategory(spinnerItems); } else if (mRestoredServiceName != null) { restoreSelection(spinnerItems); } } /** * Programmatically select a issue category on rotation * * @param spinnerItems the issue services loaded into the services spinner */ private void restoreSelection(ArrayList<SpinnerItem> spinnerItems) { for (int i = 0; i < spinnerItems.size(); i++) { SpinnerItem item = spinnerItems.get(i); if (item instanceof ServiceSpinnerItem) { Service service = ((ServiceSpinnerItem) item).getService(); if (service.getService_name().equals(mRestoredServiceName)) { mServicesSpinner.setSelection(i, true); mRestoredServiceName = null; if (ServiceUtils.isTransitServiceByType(service.getType())) { mSelectedTransitService = service; } break; } } } } /** * Programmatically select a default issue category from the spinner * * @param spinnerItems the issue services loaded into the services spinner */ private void selectDefaultTransitCategory(ArrayList<SpinnerItem> spinnerItems) { int selectPosition = -1; Service selectedService = null; for (int i = 0; i < spinnerItems.size(); i++) { SpinnerItem item = spinnerItems.get(i); if (item instanceof ServiceSpinnerItem) { Service service = ((ServiceSpinnerItem) item).getService(); boolean transitServiceFound = false; if (getString(R.string.ri_selected_service_stop).equals(mDefaultIssueType) && ServiceUtils.isTransitStopServiceByType(service.getType()) && !mIsAllTransitHeuristicMatch) { // We have an explicit (not heuristic-based) match for stop problem transitServiceFound = true; } else if (getString(R.string.ri_selected_service_trip).equals(mDefaultIssueType) && ServiceUtils.isTransitTripServiceByType(service.getType())) { transitServiceFound = true; } if (transitServiceFound) { selectPosition = i; selectedService = service; // If transit service selected and no bus stop selected remove markers if (ServiceUtils.isTransitServiceByType(service.getType()) && mIssueLocationHelper.getObaStop() == null) { mIssueLocationHelper.clearMarkers(); } break; } } } // If is a stop problem type, an all transit heuristic-based match, and no bus stop selected remove markers if (getString(R.string.ri_selected_service_stop).equals(mDefaultIssueType) && mIsAllTransitHeuristicMatch && mIssueLocationHelper.getObaStop() == null) { mIssueLocationHelper.clearMarkers(); } // Set mDefaultIssueType = null to prevent auto selections mDefaultIssueType = null; // Give the user a break from tutorials - allow at least one interaction without a tutorial int counter = Application.getPrefs().getInt(TUTORIAL_COUNTER, 0); counter++; PreferenceUtils.saveInt(TUTORIAL_COUNTER, counter); // Set selected category if it is in the spinner items list if (selectPosition != -1) { showServicesSpinner(); mServicesSpinner.setSelection(selectPosition, true); // Show tutorial if this geographic area supports Open311 services if (counter % 2 == 0 && selectedService != null && ServiceUtils.isTransitOpen311ServiceByType(selectedService.getType())) { ShowcaseViewUtils .showTutorial(ShowcaseViewUtils.TUTORIAL_SEND_FEEDBACK_OPEN311_CATEGORIES, this, null); } } } private void hideServicesSpinner() { mServicesSpinnerFrameLayout.setVisibility(View.GONE); } private void showServicesSpinner() { mServicesSpinnerFrameLayout.setVisibility(View.VISIBLE); } /** * Disables open311 reporting if there is no category for region * Show static services */ public void showTransitService(String issueType) { clearReportingFragments(); ObaStop obaStop = mIssueLocationHelper.getObaStop(); if (obaStop != null) { if (ReportConstants.STATIC_TRANSIT_SERVICE_STOP.equals(issueType)) { showStopProblemFragment(obaStop); } else if (ReportConstants.STATIC_TRANSIT_SERVICE_TRIP.equals(issueType)) { showTripProblemFragment(obaStop); } else if (ReportConstants.DYNAMIC_TRANSIT_SERVICE_STOP.equals(issueType)) { showOpen311Reporting(mSelectedTransitService); } else if (ReportConstants.DYNAMIC_TRANSIT_SERVICE_TRIP.equals(issueType)) { if (mArrivalInfo != null) { showOpen311Reporting(mSelectedTransitService, mArrivalInfo); mArrivalInfo = null; } else { showTripProblemFragment(obaStop); } } } else { addInfoText(getString(R.string.report_dialog_stop_header)); mTransitServiceIssueTypeWithoutStop = issueType; mIssueLocationHelper.clearMarkers(); } } private void showOpen311Reporting(Service service) { clearReportingFragments(); showOpen311ProblemFragment(service); updateMarkerPosition(mIssueLocationHelper.getIssueLocation()); } private void showOpen311Reporting(Service service, ObaArrivalInfo obaArrivalInfo) { clearReportingFragments(); showOpen311ProblemFragment(service, obaArrivalInfo); updateMarkerPosition(mIssueLocationHelper.getIssueLocation()); } private void clearReportingFragments() { clearReportingFragments(true); } private void clearReportingFragments(boolean removeInfo) { if (removeInfo) { removeInfoText(); } removeOpen311ProblemFragment(); removeStopProblemFragment(); removeTripProblemFragment(); } private void showStopProblemFragment(ObaStop obaStop) { ReportStopProblemFragment.show(this, obaStop, R.id.ri_report_stop_problem); } private void showTripProblemFragment(ObaStop obaStop) { if (mArrivalInfo == null) { showArrivalListFragment(obaStop); } else { // Show default trip problem issue reporting ReportTripProblemFragment.show(this, mArrivalInfo, R.id.ri_report_stop_problem); mArrivalInfo = null; } } private void showArrivalListFragment(ObaStop obaStop) { LayoutInflater layoutInflater = LayoutInflater.from(this); View v = layoutInflater.inflate(R.layout.arrivals_list_header, null); v.setVisibility(View.GONE); mShowArrivalListFragment = true; SimpleArrivalListFragment.show(this, R.id.ri_report_stop_problem, obaStop, this); } @Override public void onArrivalItemClicked(ObaArrivalInfo obaArrivalInfo, String agencyName, String blockId) { mShowArrivalListFragment = false; mAgencyName = agencyName; mBlockId = blockId; removeFragmentByTag(SimpleArrivalListFragment.TAG); if (mSelectedTransitService != null && ReportConstants.DYNAMIC_TRANSIT_SERVICE_TRIP.equals(mSelectedTransitService.getType())) { // Show open311 defined issue reporting service showOpen311ProblemFragment(mSelectedTransitService, obaArrivalInfo); } else { // Show default trip problem issue reporting ReportTripProblemFragment.show(this, obaArrivalInfo, R.id.ri_report_stop_problem); } } private void showBusStopHeader(String text) { // First remove info text removeInfoText(); ((TextView) findViewById(R.id.ri_bus_stop_text)).setText(text); if (mBusStopHeader.getVisibility() != View.VISIBLE) { mBusStopHeader.setVisibility(View.VISIBLE); } } public void hideBusStopHeader() { findViewById(R.id.bus_stop_header).setVisibility(View.GONE); } private void showOpen311ProblemFragment(Service service) { Open311ProblemFragment.show(this, R.id.ri_report_stop_problem, mOpen311, service); } private void showOpen311ProblemFragment(Service service, ObaArrivalInfo obaArrivalInfo) { Open311ProblemFragment.show(this, R.id.ri_report_stop_problem, mOpen311, service, obaArrivalInfo, mAgencyName, mBlockId); } public void removeOpen311ProblemFragment() { removeFragmentByTag(Open311ProblemFragment.TAG); } private void removeStopProblemFragment() { removeFragmentByTag(ReportStopProblemFragment.TAG); } private void removeTripProblemFragment() { mShowArrivalListFragment = false; removeFragmentByTag(ReportTripProblemFragment.TAG); removeFragmentByTag(SimpleArrivalListFragment.TAG); ((LinearLayout) findViewById(R.id.ri_report_stop_problem)).removeAllViews(); } public IssueLocationHelper getIssueLocationHelper() { return mIssueLocationHelper; } public String getCurrentAddress() { return mAddressEditText.getText().toString(); } public void finishActivityWithResult() { Intent returnIntent = new Intent(); returnIntent.putExtra(BaseReportActivity.CLOSE_REQUEST, true); setResult(RESULT_OK, returnIntent); finish(); } }