/* * The CroudTrip! application aims at revolutionizing the car-ride-sharing market with its easy, * user-friendly and highly automated way of organizing shared Trips. Copyright (C) 2015 Nazeeh Ammari, * Philipp Eichhorn, Ricarda Hohn, Vanessa Lange, Alexander Popp, Frederik Simon, Michael Weber * This program is free software: you can redistribute it and/or modify it under the terms of the GNU * Affero 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 Affero General Public License for more details. * You should have received a copy of the GNU Affero General Public License along with this program. * If not, see http://www.gnu.org/licenses/. */ package org.croudtrip.fragments.join; import android.app.Activity; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.location.Address; import android.location.Geocoder; import android.location.Location; import android.os.Bundle; import android.support.v4.content.LocalBroadcastManager; import android.support.v7.app.AlertDialog; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import com.gc.materialdesign.views.Slider; import com.getbase.floatingactionbutton.FloatingActionButton; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GooglePlayServicesNotAvailableException; import com.google.android.gms.common.GooglePlayServicesRepairableException; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.PendingResult; import com.google.android.gms.common.api.ResultCallback; import com.google.android.gms.location.places.Place; import com.google.android.gms.location.places.PlaceBuffer; import com.google.android.gms.location.places.Places; import com.google.android.gms.location.places.ui.PlacePicker; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.LatLngBounds; import com.pnikosis.materialishprogress.ProgressWheel; import org.croudtrip.Constants; import org.croudtrip.MainApplication; import org.croudtrip.R; import org.croudtrip.db.DatabaseHelper; import org.croudtrip.fragments.SubscriptionFragment; import org.croudtrip.fragments.offer.DispatchOfferTripFragment; import org.croudtrip.location.LocationUpdater; import org.croudtrip.location.MyAutoCompleteTextView; import org.croudtrip.location.PlaceAutocompleteAdapter; import org.croudtrip.utils.CrashPopup; import org.croudtrip.utils.DefaultTransformer; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; import it.neokree.materialnavigationdrawer.MaterialNavigationDrawer; import it.neokree.materialnavigationdrawer.elements.MaterialSection; import pl.charmas.android.reactivelocation.ReactiveLocationProvider; import roboguice.inject.InjectView; import rx.Observable; import rx.Subscription; import rx.functions.Action1; import rx.functions.Func1; import rx.schedulers.Schedulers; import timber.log.Timber; /** * Created by alex on 22.04.15. */ public class JoinSearchFragment extends SubscriptionFragment implements GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks { private final int REQUEST_PLACE_PICKER = 122; private DatabaseHelper dbHelper; private GoogleApiClient googleApiClient; private PlaceAutocompleteAdapter adapter; private org.croudtrip.db.Place lastSelected; private Location specifiedLocation; @InjectView(R.id.address) private TextView tv_address; @InjectView(R.id.places) private Button btn_destination; @InjectView(R.id.destination) private MyAutoCompleteTextView tv_destination; @InjectView(R.id.slider_waitingTime) private Slider slider_waitingTime; @InjectView(R.id.waitingTime) private TextView tv_waitingTime; @InjectView(R.id.pb_join_trip_destination) private ProgressWheel progressBar; @InjectView(R.id.layout_load_location) private LinearLayout loadLocationLayout; @InjectView(R.id.join) private Button btn_join; @Inject LocationUpdater locationUpdater; private Geocoder geocoder; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); geocoder = new Geocoder(getActivity()); dbHelper = ((MainApplication) getActivity().getApplication()).getHelper(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); setHasOptionsMenu(true); if (googleApiClient == null) { googleApiClient = new GoogleApiClient.Builder(getActivity().getApplicationContext()) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .addApi(Places.GEO_DATA_API) .build(); googleApiClient.connect(); } else if (!googleApiClient.isConnected()) { googleApiClient.connect(); } View view = inflater.inflate(R.layout.fragment_join_search, container, false); return view; } @Override public void onViewCreated( View view, Bundle savedInstanceState ) { super.onViewCreated(view, savedInstanceState); /* Set up the autocomplete textView */ tv_destination.setOnItemClickListener(mAutocompleteClickListener); tv_destination.setThreshold(0); LatLngBounds bounds = null; if (locationUpdater.getLastLocation() != null) { bounds = LatLngBounds.builder().include(new LatLng(locationUpdater.getLastLocation().getLatitude(), locationUpdater.getLastLocation().getLongitude())).build(); } adapter = new PlaceAutocompleteAdapter(getActivity(), android.R.layout.simple_list_item_1, bounds, null); tv_destination.setAdapter(adapter); adapter.setGoogleApiClient(googleApiClient); tv_destination.clearFocus(); tv_destination.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View view, boolean hasFocus) { if(hasFocus){ tv_destination.showDropDown(); } } }); /* Populate the autocomplete textView */ try { List<org.croudtrip.db.Place> savedPlaces = dbHelper.getPlaceDao().queryForAll(); ArrayList<PlaceAutocompleteAdapter.PlaceAutocomplete> history = new ArrayList<>(); for (int i=savedPlaces.size()-1; i>=0; i--) { if (history.size() == 5) { break; } PlaceAutocompleteAdapter.PlaceAutocomplete a = adapter.new PlaceAutocomplete(savedPlaces.get(i).getId(), savedPlaces.get(i).getDescription()); history.add(a); } adapter.setHistory(history); } catch (SQLException e) { CrashPopup.show(getActivity(), e); e.printStackTrace(); } /* Insert the last known location as soon as it is known */ ReactiveLocationProvider locationProvider = new ReactiveLocationProvider(getActivity()); Subscription subscription = locationProvider.getLastKnownLocation().observeOn(Schedulers.io()) .subscribe(new Action1<Location>() { @Override public void call(Location location) { LatLngBounds bounds = LatLngBounds.builder().include(new LatLng(location.getLatitude(), location.getLongitude())).build(); adapter.setBounds(bounds); } }); subscriptions.add(subscription); /* Open the placepicker on button click */ btn_destination.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { PlacePicker.IntentBuilder intentBuilder = new PlacePicker.IntentBuilder(); Intent intent = intentBuilder.build(getActivity().getApplicationContext()); startActivityForResult(intent, REQUEST_PLACE_PICKER); } catch (GooglePlayServicesRepairableException e) { CrashPopup.show(getActivity(), e); e.printStackTrace(); } catch (GooglePlayServicesNotAvailableException e) { CrashPopup.show(getActivity(), e); e.printStackTrace(); } } }); /* Load and set the maximum waiting time */ final SharedPreferences prefs = getActivity().getSharedPreferences(Constants.SHARED_PREF_FILE_PREFERENCES, Context.MODE_PRIVATE); int waitingTime = prefs.getInt(Constants.SHARED_PREF_KEY_WAITING_TIME, 10); tv_waitingTime.setText(getString(R.string.join_max_waiting) + " " + waitingTime); slider_waitingTime.setValue(waitingTime); slider_waitingTime.setOnValueChangedListener(new Slider.OnValueChangedListener() { @Override public void onValueChanged(int i) { tv_waitingTime.setText(getString(R.string.join_max_waiting) + " " + i); } }); /* Retrieve starting position, save destination and try to join a trip */ btn_join.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { /* Save maximum waiting time */ SharedPreferences.Editor editor = prefs.edit(); editor.putInt(Constants.SHARED_PREF_KEY_WAITING_TIME, slider_waitingTime.getValue()); editor.apply(); org.croudtrip.db.Place tempPlace = lastSelected; lastSelected = null; /* Get the starting position either by placepicker or GPS data */ Location currentLocation; if (specifiedLocation == null) { // retrieve current position currentLocation = locationUpdater.getLastLocation(); if (currentLocation == null) { Toast.makeText(getActivity().getApplicationContext(), R.string.offer_trip_no_location, Toast.LENGTH_SHORT).show(); AlertDialog.Builder adb = new AlertDialog.Builder(getActivity()); adb.setTitle(getResources().getString(R.string.enable_gps_title)); adb.setMessage(getResources().getString(R.string.gpd_not_available)); adb.setPositiveButton(getResources().getString(R.string.redirect_to_placepicker), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { try { PlacePicker.IntentBuilder intentBuilder = new PlacePicker.IntentBuilder(); Intent intent = intentBuilder.build(getActivity().getApplicationContext()); startActivityForResult(intent, REQUEST_PLACE_PICKER); } catch (GooglePlayServicesRepairableException e) { CrashPopup.show(getActivity(), e); e.printStackTrace(); } catch (GooglePlayServicesNotAvailableException e) { CrashPopup.show(getActivity(), e); e.printStackTrace(); } return; } }); adb.show(); return; } } else { currentLocation = specifiedLocation; specifiedLocation = null; } // get destination from string LatLng destination = null; try { List<Address> addresses = null; if (tv_address.getText() == null || tv_address.getText().equals("")) { addresses = geocoder.getFromLocationName(tv_destination.getText().toString(), 1); } else { addresses = geocoder.getFromLocationName(tv_address.getText().toString(), 1); } if (addresses != null && addresses.size() > 0) destination = new LatLng(addresses.get(0).getLatitude(), addresses.get(0).getLongitude()); } catch (IOException e) { Toast.makeText(getActivity().getApplicationContext(), R.string.join_trip_no_destination, Toast.LENGTH_SHORT).show(); return; } // no destination received if (destination == null) { Toast.makeText(getActivity().getApplicationContext(), R.string.offer_trip_no_destination, Toast.LENGTH_SHORT).show(); return; } if( currentLocation == null ) { Toast.makeText(getActivity().getApplicationContext(), R.string.offer_trip_no_location, Toast.LENGTH_SHORT).show(); return; } /* Save or update destination in database */ try { if (tempPlace != null) { dbHelper.getPlaceDao().delete(tempPlace); dbHelper.getPlaceDao().create(tempPlace); } else { tempPlace = new org.croudtrip.db.Place(); tempPlace.setId(tv_destination.getText().toString()); tempPlace.setDescription(tv_destination.getText().toString()); dbHelper.getPlaceDao().delete(tempPlace); dbHelper.getPlaceDao().create(tempPlace); } } catch (SQLException e) { CrashPopup.show(getActivity(), e); e.printStackTrace(); } /* Set status to "searching" */ editor.putBoolean(Constants.SHARED_PREF_KEY_SEARCHING, true); editor.apply(); /* Add trip information to extras and update UI */ Bundle extras = new Bundle(); //extras.putString(JoinDispatchFragment.KEY_ACTION_TO_RUN, JoinDispatchFragment.ACTION_START_BACKGROUND_SEARCH); extras.putInt(JoinDispatchFragment.KEY_MAX_WAITING_TIME, slider_waitingTime.getValue() * 60); // max waiting time in seconds extras.putDouble(JoinDispatchFragment.KEY_CURRENT_LOCATION_LATITUDE, currentLocation.getLatitude()); extras.putDouble(JoinDispatchFragment.KEY_CURRENT_LOCATION_LONGITUDE,currentLocation.getLongitude()); extras.putDouble(JoinDispatchFragment.KEY_DESTINATION_LATITUDE, destination.latitude); extras.putDouble(JoinDispatchFragment.KEY_DESTINATION_LONGITUDE, destination.longitude); Intent startingIntent = new Intent(Constants.EVENT_CHANGE_JOIN_UI); startingIntent.putExtras(extras); LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(startingIntent); } }); /* Open the "Offer Trip" Fragment on click at the floating action button */ FloatingActionButton btn_offer = (FloatingActionButton) view.findViewById(R.id.btn_offer_trip); btn_offer.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { MaterialNavigationDrawer drawer = (MaterialNavigationDrawer) getActivity(); drawer.setFragment(new DispatchOfferTripFragment(), getString(R.string.menu_offer_trip)); MaterialSection section = drawer.getSectionByTitle(getString(R.string.menu_offer_trip)); if (section == null) { drawer.getSectionByTitle(getString(R.string.menu_my_trip)); } drawer.setSection(section); } }); // if there is currently no position available disable the offer trip button if( locationUpdater.getLastLocation() == null && specifiedLocation == null ) { btn_join.setEnabled( false ); loadLocationLayout.setVisibility(View.VISIBLE); Subscription sub = locationProvider.getLastKnownLocation() /* JUST FOR TESTING!!! .observeOn( Schedulers.newThread() ) .subscribeOn(Schedulers.newThread()) .flatMap(new Func1<Location, Observable<Location>>() { @Override public Observable<Location> call(Location location) { try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } return Observable.just(location); } })*/ .compose(new DefaultTransformer<Location>()) .subscribe( new Action1<Location>() { @Override public void call(Location location) { if( location == null ) return; locationUpdater.setLastLocation( location ); btn_join.setEnabled(true); loadLocationLayout.setVisibility(View.GONE); } }); subscriptions.add( sub ); } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { // The user has selected a place. Extract the location if (requestCode == REQUEST_PLACE_PICKER && resultCode == Activity.RESULT_OK) { Place place = PlacePicker.getPlace(data, getActivity()); Location l = new Location("placePicker"); l.setLatitude(place.getLatLng().latitude); l.setLongitude(place.getLatLng().longitude); specifiedLocation = l; btn_join.setEnabled(true); //loadLocationLayout.setVisibility(View.VISIBLE); } else { super.onActivityResult(requestCode, resultCode, data); } } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); menu.clear(); } @Override public void onConnected(Bundle bundle) { adapter.setGoogleApiClient(googleApiClient); } @Override public void onConnectionSuspended(int i) { adapter.setGoogleApiClient(null); } @Override public void onConnectionFailed(ConnectionResult connectionResult) { Toast.makeText(getActivity(), "Could not connect to Google API Client: Error " + connectionResult.getErrorCode(), Toast.LENGTH_SHORT).show(); // Disable API access in the adapter because the client was not initialised correctly. adapter.setGoogleApiClient(null); } private AdapterView.OnItemClickListener mAutocompleteClickListener = new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { /* Retrieve the place ID of the selected item from the Adapter. The adapter stores each Place suggestion in a PlaceAutocomplete object from which we read the place ID. */ final PlaceAutocompleteAdapter.PlaceAutocomplete item = adapter.getItem(position); final String placeId = String.valueOf(item.placeId); /* Issue a request to the Places Geo Data API to retrieve a Place object with additional details about the place. */ progressBar.setVisibility(View.VISIBLE); PendingResult<PlaceBuffer> placeResult = Places.GeoDataApi.getPlaceById(googleApiClient, placeId); placeResult.setResultCallback(new ResultCallback<PlaceBuffer>() { @Override public void onResult(PlaceBuffer places) { if (!places.getStatus().isSuccess()) { // Request did not complete successfully places.release(); return; } Place place; try { place = places.get(0); } catch (IllegalStateException e) { places.release(); return; } lastSelected = new org.croudtrip.db.Place(); lastSelected.setId(place.getId()); lastSelected.setDescription(place.getAddress() + ""); tv_address.setText(place.getAddress()); progressBar.setVisibility(View.GONE); places.release(); InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); } }); Toast.makeText(getActivity().getApplicationContext(), "Clicked: " + item.description, Toast.LENGTH_SHORT).show(); } }; }