org.croudtrip.fragments.join.JoinDrivingFragment.java Source code

Java tutorial

Introduction

Here is the source code for org.croudtrip.fragments.join.JoinDrivingFragment.java

Source

/*
 * 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();
        }
    }
}