ca.ualberta.cs.unter.view.RiderMainActivity.java Source code

Java tutorial

Introduction

Here is the source code for ca.ualberta.cs.unter.view.RiderMainActivity.java

Source

/*
 * Copyright (C) 2016 CMPUT301F16T18 - Alan(Xutong) Zhao, Michael(Zichun) Lin, Stephen Larsen, Yu Zhu, Zhenzhe Xu
 *
 * 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 ca.ualberta.cs.unter.view;

import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.NavigationView;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.loopj.android.http.JsonHttpResponseHandler;
import com.novoda.merlin.Merlin;
import com.novoda.merlin.NetworkStatus;
import com.novoda.merlin.registerable.bind.Bindable;
import com.novoda.merlin.registerable.connection.Connectable;
import com.novoda.merlin.registerable.disconnection.Disconnectable;

import org.json.JSONObject;
import org.osmdroid.api.IMapController;
import org.osmdroid.bonuspack.routing.Road;
import org.osmdroid.bonuspack.routing.RoadManager;
import org.osmdroid.tileprovider.tilesource.TileSourceFactory;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.MapView;
import org.osmdroid.views.overlay.Marker;
import org.osmdroid.views.overlay.Overlay;
import org.osmdroid.views.overlay.Polyline;
import org.osmdroid.views.overlay.infowindow.BasicInfoWindow;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import ca.ualberta.cs.unter.R;
import ca.ualberta.cs.unter.UnterConstant;
import ca.ualberta.cs.unter.controller.RequestController;
import ca.ualberta.cs.unter.model.OnAsyncTaskCompleted;
import ca.ualberta.cs.unter.model.OnAsyncTaskFailure;
import ca.ualberta.cs.unter.model.Route;
import ca.ualberta.cs.unter.model.User;
import ca.ualberta.cs.unter.model.request.PendingRequest;
import ca.ualberta.cs.unter.model.request.Request;
import ca.ualberta.cs.unter.util.FileIOUtil;
import ca.ualberta.cs.unter.util.OSMapUtil;
import ca.ualberta.cs.unter.util.RequestUtil;
import cz.msebera.android.httpclient.Header;

/**
 * Main activity of rider that could browse locatoin on map, search location, and send request
 *
 * Issue: it may possible break when the route is way too long for OSM to render.
 */
public class RiderMainActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener, Connectable, Disconnectable, Bindable {

    private EditText searchDepartureLocationEditText;
    private EditText searchDestinationLocationEditText;
    private Button searchDepartureButton;
    private Button searchDestinationButton;

    protected GeoPoint departureLocation;
    protected GeoPoint destinationLocation;

    private MapView map;
    private User rider;
    private Marker startMarker;
    private Marker endMarker;
    private Road[] mRoads;
    private double distance;

    protected Merlin merlin;

    private ArrayList<Request> offlineRequestList = new ArrayList<>();

    private RequestController requestController = new RequestController(new OnAsyncTaskCompleted() {
        @Override
        public void onTaskCompleted(Object o) {
            FileIOUtil.saveRiderRequestInFile((Request) o, getApplicationContext());
        }
    }, new OnAsyncTaskFailure() {
        @Override
        public void onTaskFailed(Object o) {
            Toast.makeText(getApplication(), "Device offline", Toast.LENGTH_SHORT).show();
            offlineRequestList.add((Request) o);
            FileIOUtil.saveOfflineRequestInFile((Request) o, getApplicationContext());
        }
    });

    private RequestController acceptedRequestController = new RequestController(new OnAsyncTaskCompleted() {
        @Override
        public void onTaskCompleted(Object o) {
            ArrayList<Request> acceptedRequestList = (ArrayList<Request>) o;
            checkAccepted(acceptedRequestList);
        }
    }, null);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_rider_main);

        // merline stuff, monitor network connectivity
        merlin = new Merlin.Builder().withConnectableCallbacks().withDisconnectableCallbacks()
                .withBindableCallbacks().build(this);
        merlin.registerConnectable(this);
        merlin.registerDisconnectable(this);
        merlin.registerBindable(this);

        // map stuff
        map = (MapView) findViewById(R.id.map);
        map.setTileSource(TileSourceFactory.MAPNIK);
        map.setBuiltInZoomControls(true);
        map.setMaxZoomLevel(16);
        map.setMultiTouchControls(true);

        final IMapController mapController = map.getController();
        mapController.setZoom(15);
        mapController.setCenter(UnterConstant.UALBERTA_COORDS);

        searchDepartureLocationEditText = (EditText) findViewById(R.id.editDeparture);
        assert searchDepartureLocationEditText != null;

        searchDestinationLocationEditText = (EditText) findViewById(R.id.editDestination);
        assert searchDestinationLocationEditText != null;

        // Implement geocoding with the google map api
        searchDepartureButton = (Button) findViewById(R.id.buttonSearchDep);
        searchDepartureButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String departureStr = searchDepartureLocationEditText.getText().toString();
                if (TextUtils.isEmpty(departureStr)) {
                    searchDepartureLocationEditText.setError("Please enter an address");
                    return;
                }
                OSMapUtil.GeoCoding(departureStr, new JsonHttpResponseHandler() {
                    @Override
                    public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
                        // phrase http reponse into geopoint
                        departureLocation = OSMapUtil.pharseGeoJson(response);
                        if (map.getOverlays().contains(startMarker)) {
                            map.getOverlays().remove(startMarker);
                            startMarker = createMarker(departureLocation, "Pick-Up");
                            map.getOverlays().add(startMarker);
                            mapController.setCenter(startMarker.getPosition());
                        } else {
                            startMarker = createMarker(departureLocation, "Pick-Up");
                            map.getOverlays().add(startMarker);
                            mapController.setCenter(startMarker.getPosition());
                        }
                        // set startMarker to draggable + init listener
                        startMarker.setDraggable(true);
                        startMarker.setOnMarkerDragListener(new Marker.OnMarkerDragListener() {
                            @Override
                            public void onMarkerDrag(Marker marker) {

                            }

                            @Override
                            public void onMarkerDragEnd(Marker marker) {
                                startMarker.setPosition(marker.getPosition());
                                mapController.setCenter(startMarker.getPosition());
                                map.invalidate();
                                //getRoadAsync(startPoint, testMarker.getPosition());
                            }

                            @Override
                            public void onMarkerDragStart(Marker marker) {

                            }
                        });

                    }
                });
            }
        });

        // The search button for destination location
        searchDestinationButton = (Button) findViewById(R.id.buttonSearchDest);
        searchDestinationButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String destinationStr = searchDestinationLocationEditText.getText().toString();
                if (TextUtils.isEmpty(destinationStr)) {
                    searchDestinationLocationEditText.setError("Please enter an address");
                    return;
                }
                OSMapUtil.GeoCoding(destinationStr, new JsonHttpResponseHandler() {
                    public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
                        // phares https reponse into geopoint
                        destinationLocation = OSMapUtil.pharseGeoJson(response);
                        endMarker = createMarker(destinationLocation, "Drop-Off");
                        map.getOverlays().add(endMarker);
                        mapController.setCenter(destinationLocation);
                        mapController.setZoom(15);
                        // open a dialog to confirm the location before getting the route
                        openRiderConfirmPathDialog();
                    }
                });
            }
        });

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        assert fab != null;
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                openRiderSendRequestDialog();
            }
        });

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar,
                R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        assert drawer != null;
        drawer.setDrawerListener(toggle);
        toggle.syncState();

        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        assert navigationView != null;
        navigationView.setNavigationItemSelectedListener(this);
        // Set text on drawer
        View navHeader = navigationView.getHeaderView(0);
        TextView username = (TextView) navHeader.findViewById(R.id.nav_drawer_rider_username);
        TextView email = (TextView) navHeader.findViewById(R.id.nav_drawer_rider_email);
        // Get user profile
        rider = FileIOUtil.loadUserFromFile(getApplicationContext());
        // Set text
        username.setText(rider.getUserName());
        email.setText(rider.getEmailAddress());

        acceptedRequestController.getRiderInProgressRequest(rider.getUserName());
    }

    // http://stackoverflow.com/questions/14292398/how-to-pass-data-from-2nd-activity-to-1st-activity-when-pressed-back-android
    @Override
    public void onBackPressed() {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        assert drawer != null;
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @Override
    protected void onPostResume() {
        super.onPostResume();
        merlin.bind();
    }

    @Override
    protected void onPause() {
        super.onPause();
        merlin.unbind();
    }

    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        // Handle navigation view item clicks here.
        int id = item.getItemId();

        if (id == R.id.nav_user_profile) {
            Intent intentUserProfile = new Intent(this, EditUserProfileActivity.class);
            startActivity(intentUserProfile);
        } else if (id == R.id.nav_request) {
            Intent intentRiderBrowseRequest = new Intent(this, RiderBrowseRequestActivity.class);
            startActivity(intentRiderBrowseRequest);
        } else if (id == R.id.nav_logout) {
            startActivity(new Intent(this, LoginActivity.class));
            finish();
        }

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        assert drawer != null;
        drawer.closeDrawer(GravityCompat.START);
        return true;
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 1) {
            if (resultCode == RESULT_OK) {
                String strEditText = data.getStringExtra("edittextvalue");
                searchDepartureLocationEditText.setText(strEditText);
            }
        } else if (requestCode == 2) {
            if (resultCode == RESULT_OK) {
                String strEditText = data.getStringExtra("edittextvalue");
                searchDestinationLocationEditText.setText(strEditText);
            }
        }
    }

    // A dialog allow user to confirm the location he enters
    private void openRiderConfirmPathDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(RiderMainActivity.this);

        builder.setTitle("Confirm location")
                .setPositiveButton(R.string.dialog_ok_button, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // TODO - this breaks the code if you zoom in too far
                        OSMapUtil.getRoad(startMarker.getPosition(), destinationLocation, updateMap);
                    }
                }).setNegativeButton(R.string.dialog_cancel_button, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        map.getOverlays().clear();
                    }
                });
        // Create & Show the AlertDialog
        AlertDialog dialog = builder.create();
        dialog.show();
    }

    private void openRiderSendRequestDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(RiderMainActivity.this);
        LayoutInflater inflater = this.getLayoutInflater();
        View promptView = inflater.inflate(R.layout.rider_send_request_dialog, null);

        // Set the default fare
        final Request request = new PendingRequest(rider.getUserName(),
                new Route(departureLocation, destinationLocation));
        requestController.setDistance(request, distance); // set the distance before estimating fare
        requestController.calculateEstimatedFare(request);

        final EditText fareEditText = (EditText) promptView.findViewById(R.id.edittext_fare_ridermainactivity);
        final EditText descriptionEditText = (EditText) promptView
                .findViewById(R.id.edittext_description_ridermainactivity);
        fareEditText.setText(request.getRoundedFare()); // shows fare rounded to 2 dec places

        builder.setTitle("Send Request").setView(promptView)
                .setNegativeButton(R.string.dialog_cancel_button, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int id) {
                    }
                }).setPositiveButton(R.string.dialog_send_request_button, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int id) {
                        // TODO send request to drivers
                        String description = descriptionEditText.getText().toString();
                        double fare = Double.parseDouble(fareEditText.getText().toString());
                        if (fare == 0) {
                            fareEditText.setError("Fare cannot be empty");
                        } else if (description.isEmpty()) {
                            descriptionEditText.setError("Description cannot be empty");
                        } else {
                            Route route = new Route(departureLocation, destinationLocation);
                            route.setDistance(distance);
                            Request req = new PendingRequest(rider.getUserName(), route);
                            req.setEstimatedFare(fare);
                            req.setRequestDescription(description);
                            req.setID(UUID.randomUUID().toString());
                            requestController.createRequest(req);
                            // clean up filed after it's done
                            searchDepartureLocationEditText.setText("");
                            searchDestinationLocationEditText.setText("");
                        }
                    }
                });
        // Create & Show the AlertDialog
        AlertDialog dialog = builder.create();
        dialog.show();
    }

    // TODO call openRiderNotifiedRequestDialog() when a request is accepted by a driver

    // pops up on RiderMainActivity when a request is accepted by a driver
    private void openRiderNotifiedRequestDialog(final Request request) {
        AlertDialog.Builder builder = new AlertDialog.Builder(RiderMainActivity.this);
        builder.setTitle("Request Status Message")
                .setMessage("Request has been accepted by a Driver!\n"
                        + "Click on View Request button to view request details.") // TODO replace XX with actual request ID
                .setNegativeButton(R.string.dialog_cancel_button, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int id) {
                    }
                }).setNeutralButton(R.string.dialog_view_request_button, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int id) {
                        // intent RiderRequestDetailActivity
                        Intent intent = new Intent(RiderMainActivity.this, RiderRequestDetailActivity.class);
                        intent.putExtra("request", RequestUtil.serializer(request));
                        startActivity(intent);
                    }
                });
        // Create & Show the AlertDialog
        AlertDialog dialog = builder.create();
        dialog.show();
    }

    /**
     * Create a point marker on the map
     *
     * @param geoPoint the geolocatoin of the point
     * @param title the title to display
     * @return the marker
     */
    public Marker createMarker(GeoPoint geoPoint, String title) {
        Marker marker = new Marker(map);
        marker.setPosition(geoPoint);
        marker.setTitle(title);
        marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM);
        return marker;
    }

    /**
     * An async task to draw road on map
     */
    private OnAsyncTaskCompleted updateMap = new OnAsyncTaskCompleted() {
        @Override
        public void onTaskCompleted(Object o) {
            mRoads = (Road[]) o; // not sure if this needs to be here, but it works
            if (mRoads == null)
                return;
            if (mRoads[0].mStatus == Road.STATUS_TECHNICAL_ISSUE)
                Toast.makeText(map.getContext(), "Technical issue when getting the route", Toast.LENGTH_SHORT)
                        .show();
            else if (mRoads[0].mStatus > Road.STATUS_TECHNICAL_ISSUE) //functional issues
                Toast.makeText(map.getContext(), "No possible route here", Toast.LENGTH_SHORT).show();
            Polyline[] mRoadOverlays = new Polyline[mRoads.length];
            List<Overlay> mapOverlays = map.getOverlays();
            for (int i = 0; i < mRoads.length; i++) {
                Polyline roadPolyline = RoadManager.buildRoadOverlay(mRoads[i]);
                mRoadOverlays[i] = roadPolyline;
                String routeDesc = mRoads[i].getLengthDurationText(getApplication(), -1);
                roadPolyline.setTitle(getString(R.string.app_name) + " - " + routeDesc);
                roadPolyline
                        .setInfoWindow(new BasicInfoWindow(org.osmdroid.bonuspack.R.layout.bonuspack_bubble, map));
                roadPolyline.setRelatedObject(i);
                mapOverlays.add(1, roadPolyline);

                // displays route distance on map overlay
                Toast.makeText(map.getContext(), "Distance =" + mRoads[i].mLength, Toast.LENGTH_LONG).show();
                distance = mRoads[i].mLength;
                map.invalidate();
            }
        }
    };

    protected void updateOfflineRequest() {
        ArrayList<String> offlineList = RequestUtil.getOfflineRequestList(getApplicationContext());
        if (offlineList == null)
            return;
        offlineRequestList = FileIOUtil.loadRequestFromFile(getApplicationContext(), offlineList);
        for (Request r : offlineRequestList) {
            if (r.getRiderUserName().equals(rider.getUserName())) {
                requestController.createRequest(r);
                deleteFile(RequestUtil.generateOfflineRequestFileName(r));
            }
        }
    }

    protected void checkAccepted(ArrayList<Request> requestsList) {
        ArrayList<String> fileList = RequestUtil.getRiderRequestList(this);
        if (fileList == null)
            return;
        for (Request r : requestsList) {
            // if request has been confirmed by a driver
            if (r.getDriverList() != null && r.getDriverUserName() == null) {
                Request req = FileIOUtil.loadSingleRequestFromFile(RequestUtil.generateRiderRequestFileName(r),
                        this);
                if (req.getDriverUserName() == null) {
                    openRiderNotifiedRequestDialog(r);
                }
            }
        }
    }

    @Override
    public void onBind(NetworkStatus networkStatus) {
        if (!networkStatus.isAvailable()) {
            onDisconnect();
        } else if (networkStatus.isAvailable()) {
            onConnect();
        }
    }

    @Override
    public void onConnect() {
        Log.i("Debug", "Connected");
        updateOfflineRequest();
    }

    @Override
    public void onDisconnect() {
        Log.i("Debug", "Disconnect");
    }
}