/*
* 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.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Color;
import android.location.Location;
import android.nfc.NfcAdapter;
import android.nfc.NfcManager;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewManager;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;
import com.pnikosis.materialishprogress.ProgressWheel;
import org.croudtrip.Constants;
import org.croudtrip.R;
import org.croudtrip.api.TripsResource;
import org.croudtrip.api.directions.NavigationResult;
import org.croudtrip.api.directions.RouteLocation;
import org.croudtrip.api.trips.JoinTripRequest;
import org.croudtrip.api.trips.JoinTripRequestUpdate;
import org.croudtrip.api.trips.JoinTripRequestUpdateType;
import org.croudtrip.api.trips.JoinTripStatus;
import org.croudtrip.api.trips.SuperTrip;
import org.croudtrip.api.trips.UserWayPoint;
import org.croudtrip.fragments.SubscriptionFragment;
import org.croudtrip.location.LocationUpdater;
import org.croudtrip.trip.MyTripPassengerDriversAdapter;
import org.croudtrip.utils.CrashCallback;
import org.croudtrip.utils.CrashPopup;
import org.croudtrip.utils.DefaultTransformer;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.TimeZone;
import javax.inject.Inject;
import it.neokree.materialnavigationdrawer.MaterialNavigationDrawer;
import retrofit.Callback;
import retrofit.RetrofitError;
import retrofit.client.Response;
import roboguice.inject.InjectView;
import rx.Subscriber;
import rx.Subscription;
import rx.functions.Action1;
import timber.log.Timber;
/**
* This fragment shows the passenger his current trip and several information like the related
* drivers, the prices, a map etc.
*
* @author Alexander Popp, Vanessa Lange
*/
public class JoinDrivingFragment extends SubscriptionFragment {
@InjectView(R.id.btn_joint_trip_reached)
private Button btnReachedDestination; //also handles the "My driver is here" stuff
@InjectView(R.id.btn_joint_trip_cancel)
private Button btnCancelTrip;
@InjectView(R.id.btn_joint_trip_report)
private Button btnReportDriver;
@InjectView(R.id.join_trip_sending)
private LinearLayout llSending;
@InjectView(R.id.join_trip_waiting)
private LinearLayout llWaiting;
@InjectView(R.id.join_trip_driving)
private LinearLayout llDriving;
@InjectView(R.id.fl_join_trip_driving_map)
private FrameLayout flMap;
@InjectView(R.id.pickup_time)
private TextView tvPickupTime;
@InjectView(R.id.tv_my_trip_driver_passengers_title)
private TextView tvMyDrivers;
@InjectView(R.id.nfc_explanation)
private TextView tvNfcExplanation;
@InjectView(R.id.nfc_icon)
private ImageView ivNfcIcon;
@InjectView(R.id.pb_join_trip_driving_reached_destination)
private ProgressWheel progressBarDest;
@InjectView(R.id.pb_join_trip_driving_report)
private ProgressWheel progressBarReport;
@InjectView(R.id.pb_join_trip_driving_cancel)
private ProgressWheel progressBarCancel;
@InjectView(R.id.pb_join_trip_map_progressBar)
private ProgressWheel progressBarMap;
@InjectView(R.id.pb_my_trip_drivers_progressBar)
private ProgressWheel progressBarDrivers;
@InjectView(R.id.iv_transparent_image)
private ImageView transparentImageView;
@Inject
TripsResource tripsResource;
@Inject
private LocationUpdater locationUpdater;
private NfcAdapter nfcAdapter;
private PendingIntent nfcPendingIntent;
private GoogleMap googleMap;
// Passengers list
private MyTripPassengerDriversAdapter adapter;
@InjectView(R.id.rv_join_trip_driving_drivers)
private RecyclerView recyclerView;
private ArrayList<Integer> colors;
private ArrayList<Integer> shadesOfGray;
private int colorPosition = 0;
private int shadesOfGrayPosition = 0;
private final int DEFAULT_ZOOM_LEVEL = 2;
//***************************** Methods *****************************//
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Register local broadcasts
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(joinRequestExpiredReceiver,
new IntentFilter(Constants.EVENT_JOIN_REQUEST_EXPIRED));
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(nfcScannedReceiver,
new IntentFilter(Constants.EVENT_NFC_TAG_SCANNED));
IntentFilter filter = new IntentFilter();
filter.addAction(Constants.EVENT_SECONDARY_DRIVER_ACCEPTED);
filter.addAction(Constants.EVENT_SECONDARY_DRIVER_DECLINED);
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(secondaryDriverAcceptedDeclinedReceiver, filter);
//Register nfc adapter
nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity());
if (nfcAdapter != null) {
nfcPendingIntent = PendingIntent.getActivity(getActivity(), 0, new Intent(getActivity(), getActivity().getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
}
//Initialize colors for different routes on the map
colors = new ArrayList<>();
colors.add(Color.BLUE);
colors.add(Color.GREEN);
colors.add(Color.RED);
colors.add(Color.YELLOW);
shadesOfGray = new ArrayList<>();
shadesOfGray.add(Color.GRAY);
shadesOfGray.add(Color.DKGRAY);
shadesOfGray.add(Color.LTGRAY);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
setHasOptionsMenu(true);
((MaterialNavigationDrawer) getActivity()).getCurrentSection().setNotificationsText("");
((MaterialNavigationDrawer) getActivity()).getCurrentSection().setTitle(getString(R.string.menu_my_trip));
((MaterialNavigationDrawer) getActivity()).setTitle(R.string.menu_my_trip);
View view = inflater.inflate(R.layout.fragment_join_driving, container, false);
return view;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
llSending.setVisibility(View.GONE);
llWaiting.setVisibility(View.GONE);
llDriving.setVisibility(View.GONE);
// Fill the drivers list
View header = view.findViewById(R.id.ll_join_trip_driving_info);
adapter = new MyTripPassengerDriversAdapter(header);
//mapProgressBar = (ProgressWheel) adapter.getHeader().findViewById(R.id.pb_my_trip_map_progressBar);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
// Get the route to display it on the map
SupportMapFragment mapFragment = (SupportMapFragment) getChildFragmentManager()
.findFragmentById(R.id.f_join_trip_driving_map);
googleMap = mapFragment.getMap();
// Remove the header from the layout. Otherwise it exists twice
((ViewManager) view).removeView(header);
// Hack to prevent the recyclerview from scrolling when touching the map
transparentImageView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
recyclerView.requestDisallowInterceptTouchEvent(true);
return false;
case MotionEvent.ACTION_UP:
recyclerView.requestDisallowInterceptTouchEvent(false);
return true;
case MotionEvent.ACTION_MOVE:
recyclerView.requestDisallowInterceptTouchEvent(true);
return false;
default:
return true;
}
}
});
final SharedPreferences prefs = getActivity().getSharedPreferences(Constants.SHARED_PREF_FILE_PREFERENCES, Context.MODE_PRIVATE);
if (prefs.getBoolean(Constants.SHARED_PREF_KEY_WAITING, false)) {
// WAITING for ACCEPT: passenger is currently waiting for the drivers approval
setButtonInactive(btnReportDriver);
setButtonInactive(btnReachedDestination);
setButtonActive(btnCancelTrip);
llSending.setVisibility(View.VISIBLE);
flMap.setVisibility(View.GONE);
btnReachedDestination.setText(getResources().getString(R.string.join_trip_results_driverArrival));
} else if (prefs.getBoolean(Constants.SHARED_PREF_KEY_DRIVING, false)) {
// IN THE CAR: passenger is currently on a trip already in the car
setButtonInactive(btnCancelTrip);
setButtonActive(btnReachedDestination);
setButtonActive(btnReportDriver);
llDriving.setVisibility(View.VISIBLE);
flMap.setVisibility(View.VISIBLE);
btnReachedDestination.setText(getResources().getString(R.string.join_trip_results_left_Car));
btnReachedDestination.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Handle here all the stuff that happens when the trip is successfully completed (user hits "I have reached my destination")
if (prefs.getBoolean(Constants.SHARED_PREF_KEY_DRIVING, false)) {
updateTrip(JoinTripRequestUpdateType.LEAVE_CAR, progressBarDest);
} else {
// If the user enters the car and leaves the car without this method called twice we need this distinction
updateTrip(JoinTripRequestUpdateType.ENTER_CAR, progressBarDest);
}
}
});
} else {
// WAITING for DRIVER: passenger is currently waiting for his driver but already accepted
setButtonActive(btnCancelTrip);
setButtonActive(btnReachedDestination);
setButtonActive(btnReportDriver);
llWaiting.setVisibility(View.VISIBLE);
flMap.setVisibility(View.VISIBLE);
switchToNfcIfAvailable();
btnReachedDestination.setText(getResources().getString(R.string.join_trip_results_driverArrival));
btnReachedDestination.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Handle here all the stuff that happens when the user enters the car (user hits "My driver is here")
if (prefs.getBoolean(Constants.SHARED_PREF_KEY_DRIVING, false)) {
updateTrip(JoinTripRequestUpdateType.LEAVE_CAR, progressBarDest);
} else {
// If the user enters the car and leaves the car without this method called twice we need this distinction
updateTrip(JoinTripRequestUpdateType.ENTER_CAR, progressBarDest);
}
}
});
}
/*
Cancel trip
*/
btnCancelTrip.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Handle here all the stuff that happens when the user cancels the trip
Toast.makeText(getActivity(), getResources().getString(R.string.my_trip_driver_cancel_trip), Toast.LENGTH_SHORT).show();
//updateTrip(JoinTripRequestUpdateType.CANCEL, progressBarCancel);
// just a quick cancel of all active super trips
// TODO: Adjust the stuff that is written to the shared preferences, since it is not that simple anymore for super trips
progressBarCancel.setVisibility(View.VISIBLE);
tripsResource.cancelActiveSuperTrips().compose( new DefaultTransformer<Object>() )
.subscribe( new Action1<Object>() {
@Override
public void call(Object o) {
progressBarCancel.setVisibility(View.GONE);
sendUserBackToSearch();
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
Toast.makeText(getActivity(), R.string.join_trip_results_error, Toast.LENGTH_SHORT).show();
progressBarCancel.setVisibility(View.GONE);
}
});
}
});
loadRequest();
}
/**
* Downloads the current(super-)trip from the server
*/
private void loadRequest(){
subscriptions.add(tripsResource.getAllActiveTrips()
.compose(new DefaultTransformer<List<SuperTrip>>())
.subscribe(new LoadRequestSubscriber()));
}
/*
Parse and show information about the current trip, like price, driver and cost
*/
private void showJoinedTrip(List<JoinTripRequest> requests) {
if (requests == null || requests.isEmpty()) {
Timber.e("List<JoinTripRequest> is empty or doesn't exist");
return;
}
if(flMap.getVisibility() == View.VISIBLE) {
drawRoutesOnMap(requests);
}
// Show drivers
for(JoinTripRequest r : requests) {
adapter.updateRequest(r);
}
progressBarDrivers.setVisibility(View.GONE);
// Show correct plural of drivers
int numDrivers = adapter.getNumDrivers();
Resources res = getResources();
tvMyDrivers.setText(res.getQuantityString(R.plurals.join_trip_results_my_drivers, numDrivers, numDrivers));
// TODO: for first/next driver
// Show arrival time
String dateAsString = "";
Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(TimeZone.getDefault());
calendar.setTimeInMillis(1000 * (requests.get(0).getEstimatedArrivalTimestamp()));
//Display remaining time in the format hh:mm
if ((calendar.get(Calendar.HOUR_OF_DAY) < 10) && (calendar.get((Calendar.MINUTE)) < 10))
dateAsString = "0" + calendar.get(Calendar.HOUR_OF_DAY) + ":0" + calendar.get(Calendar.MINUTE);
else if (calendar.get(Calendar.HOUR_OF_DAY) < 10)
dateAsString = "0" + calendar.get(Calendar.HOUR_OF_DAY) + ":" + calendar.get(Calendar.MINUTE);
else if (calendar.get((Calendar.MINUTE)) < 10)
dateAsString = calendar.get(Calendar.HOUR_OF_DAY) + ":0" + calendar.get(Calendar.MINUTE);
else
dateAsString = calendar.get(Calendar.HOUR_OF_DAY) + ":" + calendar.get(Calendar.MINUTE);
tvPickupTime.setText(dateAsString);
}
private void drawRoutesOnMap(List<JoinTripRequest> requests) {
colorPosition = 0;
googleMap.clear();
for (final JoinTripRequest joinTripRequest : requests) {
//get route for a driver who has not accepted yet -> draw it in gray
if (joinTripRequest.getStatus().equals(JoinTripStatus.PASSENGER_ACCEPTED)) {
subscriptions.add(tripsResource
.computePendingNavigationResultForOffer(joinTripRequest.getOffer().getId(), joinTripRequest.getId())
.subscribe(new Action1<NavigationResult>() {
@Override
public void call(final NavigationResult navigationResult) {
//Update the view with the received data
if (isAdded()) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
try {
List<UserWayPoint> wayPoints = navigationResult.getUserWayPoints();
List<RouteLocation> polyline = navigationResult.getRoute()
.getPolylineWaypointsForUser(
joinTripRequest.getSuperTrip().getQuery().getPassenger(),
wayPoints);
List<LatLng> polylinePoints = getPolylinePoints(polyline, false);
// Correct line color (alternating)
googleMap.addPolyline(new PolylineOptions().addAll(polylinePoints).color(shadesOfGray.get(shadesOfGrayPosition % shadesOfGray.size())));
shadesOfGrayPosition++;
// Show dots at different driver pick-up/drop-offs
addMarker(polylinePoints.get(0));
addMarker(polylinePoints.get(polylinePoints.size() - 1));
} catch (IllegalArgumentException ex) {
CrashPopup.show(getActivity(), ex);
}
}
});
}
}
}, new CrashCallback(getActivity(), "failed to get navigation")));
}
//get route for a driver who has already accepted -> draw it in a "normal" color
else {
subscriptions.add(tripsResource
.computeNavigationResultForOffer(joinTripRequest.getOffer().getId())
.subscribe(new Action1<NavigationResult>() {
@Override
public void call(final NavigationResult navigationResult) {
//Update the view with the received data
if (isAdded()) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
try {
List<UserWayPoint> wayPoints = navigationResult.getUserWayPoints();
List<RouteLocation> polyline = navigationResult.getRoute()
.getPolylineWaypointsForUser(
joinTripRequest.getSuperTrip().getQuery().getPassenger(),
wayPoints);
List<LatLng> polylinePoints = getPolylinePoints(polyline, false);
// Correct line color (alternating)
googleMap.addPolyline(new PolylineOptions()
.addAll(polylinePoints).color(colors.get(colorPosition % colors.size())));
colorPosition++;
// Show dots at different driver pick-up/drop-offs
addMarker(polylinePoints.get(0));
addMarker(polylinePoints.get(polylinePoints.size() - 1));
} catch (IllegalArgumentException ex) {
CrashPopup.show(getActivity(), ex);
}
}
});
}
}
}, new CrashCallback(getActivity(), "failed to get navigation")));
}
}
googleMap.setMyLocationEnabled(true);
progressBarMap.setVisibility(View.INVISIBLE);
Location location = locationUpdater.getLastLocation();
if (location == null)
return;
LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(latLng, DEFAULT_ZOOM_LEVEL);
googleMap.animateCamera(cameraUpdate);
}
/*
Changes the UI to indicate the passenger that he should use NFC to enter the car.
In detail: Show icon and explanation for NFC and hide the corresponding button
*/
private void switchToNfcIfAvailable() {
NfcManager manager = (NfcManager) getActivity().getSystemService(Context.NFC_SERVICE);
NfcAdapter adapter = manager.getDefaultAdapter();
if (adapter != null && adapter.isEnabled()) {
// nfc exists and is enabled.
ivNfcIcon.setVisibility(View.VISIBLE);
tvNfcExplanation.setVisibility(View.VISIBLE);
btnReachedDestination.setVisibility(View.GONE);
}
}
/*
Redirect user to the very first screen of the "join" flow. Here he can start a new join-request
*/
private void sendUserBackToSearch() {
SharedPreferences prefs = getActivity().getSharedPreferences(Constants.SHARED_PREF_FILE_PREFERENCES, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(Constants.SHARED_PREF_KEY_ACCEPTED, false);
editor.putBoolean(Constants.SHARED_PREF_KEY_DRIVING, false);
editor.putBoolean(Constants.SHARED_PREF_KEY_WAITING, false);
editor.apply();
Intent startingIntent = new Intent(Constants.EVENT_CHANGE_JOIN_UI);
LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(startingIntent);
}
/*
Send the new status of the trip to the server. The status may be canceled, entered the car and left the car
*/
private void updateTrip(final JoinTripRequestUpdateType updateType, final ProgressWheel progressBar) {
//show loading indicator
if (progressBar != null) {
progressBar.setVisibility(View.VISIBLE);
}
final SharedPreferences prefs = getActivity().getSharedPreferences(Constants.SHARED_PREF_FILE_PREFERENCES, Context.MODE_PRIVATE);
//send update trip request to server
JoinTripRequestUpdate requestUpdate = new JoinTripRequestUpdate(updateType);
Subscription subscription = tripsResource.updateJoinRequest(prefs.getLong(Constants.SHARED_PREF_KEY_TRIP_ID, -1), requestUpdate)
.compose(new DefaultTransformer<JoinTripRequest>())
.subscribe(new Action1<JoinTripRequest>() {
@Override
public void call(JoinTripRequest joinTripRequest) {
Timber.d("update trip successfully called");
adapter.updateRequest(joinTripRequest);
//hide loading indicator
if (progressBar != null) {
progressBar.setVisibility(View.GONE);
}
if (updateType.equals(JoinTripRequestUpdateType.CANCEL)) {
//if a user cancelled everything related to the status can be erased locally
// + redirection to the search screen
sendUserBackToSearch();
return;
} else if (updateType.equals(JoinTripRequestUpdateType.LEAVE_CAR)) {
//Check if this was the last part of a supertrip
tripsResource.getAllActiveTrips(new Callback<List<SuperTrip>>() {
@Override
public void success(List<SuperTrip> superTrips, Response response) {
if (superTrips.size() == 0) {
//no trips active anymore means that the passenger has reached the destination...
sendUserBackToSearch();
} else {
//..otherwise he must be able to enter the next car
//get JoinTripRequest for the remaining SuperTrip
tripsResource.getJoinTripRequestsForSuperTrip(superTrips.get(0).getId(), new Callback<List<JoinTripRequest>>() {
@Override
public void success(List<JoinTripRequest> joinTripRequests, Response response) {
//get the next JoinTripRequest
JoinTripRequest nextRequest = null;
//the next request which was accepted by passenger+driver but the passenger did not enter the
//car yet is the next part of the SuperTrip
for (JoinTripRequest request : joinTripRequests) {
if (request.getStatus().equals(JoinTripStatus.DRIVER_ACCEPTED)) {
nextRequest = request;
break;
}
}
//No correct JoinTripRequest is found
if (nextRequest == null) {
showErrorMessage(progressBar);
return;
}
//remove status "driving" locally
final SharedPreferences prefs = getActivity().getSharedPreferences(Constants.SHARED_PREF_FILE_PREFERENCES, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(Constants.SHARED_PREF_KEY_DRIVING, false);
editor.putLong(Constants.SHARED_PREF_KEY_TRIP_ID, nextRequest.getId()); //will break
editor.apply();
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(nfcScannedReceiver,
new IntentFilter(Constants.EVENT_NFC_TAG_SCANNED));
//show correct ui elements
llWaiting.setVisibility(View.VISIBLE);
llDriving.setVisibility(View.GONE);
switchToNfcIfAvailable();
//change description and functionality of button back to "driver is here"
btnReachedDestination.setText(getResources().getString(R.string.join_trip_results_driverArrival));
btnReachedDestination.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
updateTrip(JoinTripRequestUpdateType.ENTER_CAR, progressBarDest);
}
});
}
@Override
public void failure(RetrofitError error) {
showErrorMessage(progressBar);
}
});
}
}
@Override
public void failure(RetrofitError error) {
showErrorMessage(progressBar);
}
});
} else if (updateType.equals(JoinTripRequestUpdateType.ENTER_CAR)) {
//the user is now in the car -> switch to driving status
//save status "driving" locally
SharedPreferences prefs = getActivity().getSharedPreferences(Constants.SHARED_PREF_FILE_PREFERENCES, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(Constants.SHARED_PREF_KEY_DRIVING, true);
editor.apply();
//hide nfc related ui elements
ivNfcIcon.setVisibility(View.GONE);
tvNfcExplanation.setVisibility(View.GONE);
//change description of button from "enter car" to "leave car"
btnReachedDestination.setText(getResources().getString(R.string.join_trip_results_left_Car));
btnReachedDestination.setVisibility(View.VISIBLE);
btnReachedDestination.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
updateTrip(JoinTripRequestUpdateType.LEAVE_CAR, progressBarDest);
}
});
//passenger can not cancel while he is in the car -> disable button
setButtonInactive(btnCancelTrip);
//display or hide the correct view groups
llWaiting.setVisibility(View.GONE);
llDriving.setVisibility(View.VISIBLE);
flMap.setVisibility(View.VISIBLE);
}
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
CrashPopup.show(getActivity(), throwable);
showErrorMessage(progressBar);
}
});
subscriptions.add(subscription);
}
private void showErrorMessage(ProgressWheel progressBar) {
Toast.makeText(getActivity(), R.string.join_trip_results_error, Toast.LENGTH_SHORT).show();
if (progressBar != null) {
progressBar.setVisibility(View.GONE);
}
}
/*
Activate the given button and make it clickable
*/
private void setButtonActive(Button button) {
button.setEnabled(true);
}
/*
Deactivate the given button and make it unclickable
*/
private void setButtonInactive(Button button) {
button.setEnabled(false);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
menu.clear();
}
@Override
public void onResume() {
super.onResume();
SharedPreferences prefs = getActivity().getSharedPreferences(Constants.SHARED_PREF_FILE_PREFERENCES, Context.MODE_PRIVATE);
if (!prefs.getBoolean(Constants.SHARED_PREF_KEY_WAITING, false) && !prefs.getBoolean(Constants.SHARED_PREF_KEY_DRIVING, false)) {
//listen for NFC events
if (nfcAdapter != null) {
nfcAdapter.enableForegroundDispatch(getActivity(), nfcPendingIntent, null, null);
}
}
}
@Override
public void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(getActivity().getApplicationContext())
.unregisterReceiver(joinRequestExpiredReceiver);
LocalBroadcastManager.getInstance(getActivity().getApplicationContext())
.unregisterReceiver(secondaryDriverAcceptedDeclinedReceiver);
}
@Override
public void onDestroy() {
super.onDestroy();
//Since the NFC scan pauses the fragment we must unregister this receiver in onDestroy,
//otherwise we could not catch this Intent
LocalBroadcastManager.getInstance(getActivity().getApplicationContext()).unregisterReceiver(nfcScannedReceiver);
}
//The onReceive method is fired when the join trip request expires on the server
//The passenger is redirected to the join trip UI accordingly
private BroadcastReceiver joinRequestExpiredReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Timber.d("Request expired broadcast receiver: onReceive");
sendUserBackToSearch();
}
};
//The onReceive method is fired when an nfc tag is scanned
private BroadcastReceiver nfcScannedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//Check again if the passenger has the correct status for an nfc scan
SharedPreferences prefs = context.getSharedPreferences(Constants.SHARED_PREF_FILE_PREFERENCES, Context.MODE_PRIVATE);
if (!prefs.getBoolean(Constants.SHARED_PREF_KEY_WAITING, false) && !prefs.getBoolean(Constants.SHARED_PREF_KEY_DRIVING, false)) {
updateTrip(JoinTripRequestUpdateType.ENTER_CAR, progressBarDest);
//disable listening to the "scanned NFC" event
LocalBroadcastManager.getInstance(getActivity().getApplicationContext()).unregisterReceiver(nfcScannedReceiver);
}
}
};
private BroadcastReceiver secondaryDriverAcceptedDeclinedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Timber.d("A secondary driver has accepted or declined");
loadRequest();
}
};
//*************************** Map Utils ********************************//
private List<LatLng> getPolylinePoints(List<RouteLocation> route, boolean dashed) {
List<LatLng> polylinePoints = new ArrayList<LatLng>();
LatLng first = null;
LatLng last = null;
for (RouteLocation loc : route) {
last = new LatLng(loc.getLat(), loc.getLng());
if (first == null) {
first = last;
} else if (dashed) {
createDashedLine(first, last, Color.GRAY);
}
polylinePoints.add(last);
}
return polylinePoints;
}
/**
* Add a marker on the map
* @param position
*/
private void addMarker(LatLng position) {
googleMap.addMarker(
new MarkerOptions()
.position(position)
.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_marker))
.anchor(0.5f, 0.5f)
.flat(true)
);
}
/**
* Create a dashed polyline depending on the map zoom
* @param latLngOrig
* @param latLngDest
* @param color
*/
private void createDashedLine(LatLng latLngOrig, LatLng latLngDest, int color){
double difLat = latLngDest.latitude - latLngOrig.latitude;
double difLng = latLngDest.longitude - latLngOrig.longitude;
double divLat = difLat / (DEFAULT_ZOOM_LEVEL * 2);
double divLng = difLng / (DEFAULT_ZOOM_LEVEL * 2);
LatLng tmpLatOri = latLngOrig;
for(int i = 0; i < (DEFAULT_ZOOM_LEVEL * 2); i++){
LatLng loopLatLng = tmpLatOri;
if(i > 0){
loopLatLng = new LatLng(tmpLatOri.latitude + (divLat * 0.25f), tmpLatOri.longitude + (divLng * 0.25f));
}
googleMap.addPolyline(new PolylineOptions()
.add(loopLatLng)
.add(new LatLng(tmpLatOri.latitude + divLat, tmpLatOri.longitude + divLng))
.color(color)
.width(5f));
tmpLatOri = new LatLng(tmpLatOri.latitude + divLat, tmpLatOri.longitude + divLng);
}
}
//*************************** Inner classes ********************************//
/**
* This Subscriber loads the current JoinTripRequest (SuperTripRequest)
*/
private class LoadRequestSubscriber extends Subscriber<List<SuperTrip>> {
@Override
public void onNext(final List<SuperTrip> trips) {
if (trips == null || trips.isEmpty()) {
Timber.d("Currently there are no trips running.");
return;
}
subscriptions.add(tripsResource
.getJoinTripRequestsForSuperTrip(trips.get(0).getId())
.subscribe(new Action1<List<JoinTripRequest>>() {
@Override
public void call(final List<JoinTripRequest> joinTripRequests) {
Timber.d("Got List of JoinTripRequests");
//Update the view with the received data
//if (isAdded()) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
showJoinedTrip(joinTripRequests);
}
});
//}
}
}, new CrashCallback(getActivity(), "failed to get join requests")));
}
@Override
public void onCompleted() {}
@Override
public void onError(Throwable throwable) {
progressBarDrivers.setVisibility(View.GONE);
Timber.e(throwable.getMessage());
Toast.makeText(getActivity(), getString(R.string.join_trip_results_error), Toast.LENGTH_LONG).show();
}
}
}