com.ibm.pickmeup.activities.MapActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.ibm.pickmeup.activities.MapActivity.java

Source

/*******************************************************************************
 * Copyright (c) 2014 IBM Corp.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 *
 * The Eclipse Public License is available at
 *   http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 ******************************************************************************/
package com.ibm.pickmeup.activities;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Point;
import android.location.Location;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.Projection;
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.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.ibm.pickmeup.PickMeUpApplication;
import com.ibm.pickmeup.R;
import com.ibm.pickmeup.utils.Constants;
import com.ibm.pickmeup.utils.LocationUtils;

import java.util.Calendar;

public class MapActivity extends FragmentActivity {

    private final static String TAG = MapActivity.class.getName();
    private GoogleMap mMap; // Might be null if Google Play services APK is not available.
    private BroadcastReceiver coordinatesChangedBroadcastReceiver;
    private Marker driverMarker;
    private PickMeUpApplication app;
    private TextView distanceDetails;
    private TextView etaDetails;
    private Location passengerLocation;
    private Location driverLocation;
    private Calendar calendar;
    private String AM_PM;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.d(TAG, ".onCreate() entered");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_map);

        // initialise
        initMapActivity();
    }

    @Override
    protected void onResume() {
        Log.d(TAG, ".onResume() entered");
        super.onResume();

        // register coordinatesChangedBroadcastReceiver
        registerReceivers();

        // setup google map layout
        setUpMapIfNeeded();

        // register current activity as a running activity inside our application class
        app.setCurrentRunningActivity(TAG);
    }

    @Override
    protected void onPause() {
        Log.d(TAG, ".onPause() entered");
        super.onPause();

        // unregister
        unregisterReceivers();

        // unregister current activity inside our application class
        app.setCurrentRunningActivityEmpty();
    }

    /**
     * Initialising shared properties
     */
    private void initMapActivity() {
        Log.d(TAG, ".initMapActivity() entered");
        app = (PickMeUpApplication) getApplication();
        driverLocation = new Location("");
    }

    /**
     * Sets up the map if it is possible to do so (i.e., the Google Play services APK is correctly
     * installed) and the map has not already been instantiated.. This will ensure that we only ever
     * call {@link #setUpMap()} once when {@link #mMap} is not null.
     * <p/>
     * If it isn't installed {@link SupportMapFragment} (and
     * {@link com.google.android.gms.maps.MapView MapView}) will show a prompt for the user to
     * install/update the Google Play services APK on their device.
     * <p/>
     * A user can return to this FragmentActivity after following the prompt and correctly
     * installing/updating/enabling the Google Play services. Since the FragmentActivity may not
     * have been completely destroyed during this process (it is likely that it would only be
     * stopped or paused), {@link #onCreate(Bundle)} may not be called again so we should call this
     * method in {@link #onResume()} to guarantee that it will be called.
     */
    private void setUpMapIfNeeded() {
        Log.d(TAG, ".setUpMapIfNeeded() entered");
        // Do a null check to confirm that we have not already instantiated the map.
        if (mMap == null) {
            // Try to obtain the map from the SupportMapFragment.
            mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap();
            // Check if we were successful in obtaining the map.
            if (mMap != null) {
                setUpMap();
            } else {
                mapUnavailable();
            }
        }
    }

    /**
     * Show a toast message to the user about Location services and return to the previous activity
     */
    private void mapUnavailable() {
        Toast.makeText(this, R.string.location_services_map_unavailable, Toast.LENGTH_SHORT).show();
        openChat();
    }

    /**
     * Create and register coordinatesChangedBroadcastReceiver
     */
    private void registerReceivers() {
        Log.d(TAG, ".registerReceivers() entered");
        if (coordinatesChangedBroadcastReceiver == null) {
            Log.d(TAG, ".registerReceivers() - Registering coordinatesChangedBroadcastReceiver");
            coordinatesChangedBroadcastReceiver = new BroadcastReceiver() {

                @Override
                public void onReceive(Context context, Intent intent) {
                    // update map with new coordinates
                    updateMap(intent);
                }
            };
        }

        // registering coordinatesChangedBroadcastReceiver
        getApplicationContext().registerReceiver(coordinatesChangedBroadcastReceiver,
                new IntentFilter(Constants.ACTION_INTENT_COORDINATES_CHANGED));
    }

    /**
     * Setup map, markers, ETA and Distance values
     */
    private void setUpMap() {
        Log.d(TAG, ".setUpMap() entered");

        // setup ETA and Distance values
        distanceDetails = (TextView) findViewById(R.id.distanceWithValue);
        etaDetails = (TextView) findViewById(R.id.etaWithValue);
        distanceDetails.setText(R.string.distance_calculating);
        etaDetails.setText(R.string.eta_calculating);

        // setup passenger marker and move the camera accordingly
        passengerLocation = LocationUtils.getInstance(this).getLocation();

        if (passengerLocation == null) {
            mapUnavailable();
        } else {
            LatLng passengerLatLng = new LatLng(passengerLocation.getLatitude(), passengerLocation.getLongitude());
            mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(passengerLatLng, 10));
            mMap.addMarker(new MarkerOptions().position(passengerLatLng)
                    .icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_passenger)));
        }
    }

    /**
     * Update map with the new coordinates for the driver
     * @param intent containing driver coordinates
     */
    private void updateMap(final Intent intent) {
        // not logging entry as it will flood the logs

        // getting driver LatLng values from the intent
        final LatLng driverLatLng = new LatLng(intent.getFloatExtra(Constants.LATITUDE, 0),
                intent.getFloatExtra(Constants.LONGITUDE, 0));

        // create driver marker if it doesn't exist and move the camera accordingly
        if (driverMarker == null) {
            mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(driverLatLng, 10));
            driverMarker = mMap.addMarker(new MarkerOptions().position(driverLatLng)
                    .icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_driver)));
            return;
        }

        // update driver location with LatLng
        driverLocation.setLatitude(driverLatLng.latitude);
        driverLocation.setLongitude(driverLatLng.longitude);

        // calculate current distance to the passenger
        float distance = passengerLocation.distanceTo(driverLocation) / 1000;

        // set the distance text
        distanceDetails.setText(String.format(getResources().getString(R.string.distance_with_value), distance));

        // calculating ETA - we are assuming here that the car travels at 20mph to simplify the calculations
        calendar = Calendar.getInstance();
        calendar.add(Calendar.MINUTE, Math.round(distance / 20 * 60));

        // set AM/PM to a relevant value
        AM_PM = getString(R.string.am);
        if (calendar.get(Calendar.AM_PM) == 1) {
            AM_PM = getString(R.string.pm);
        }

        // format ETA string to HH:MM
        String eta = String.format(getResources().getString(R.string.eta_with_value),
                calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), AM_PM);

        // set the ETA text
        etaDetails.setText(eta);

        // as we are throttling updates to the coordinates, we might need to smooth out the moving
        // of the driver's marker. To do so we are going to draw temporary markers between the
        // previous and the current coordinates. We are going to use interpolation for this and
        // use handler/looper to set the marker's position

        // get hold of the handler
        final Handler handler = new Handler();
        final long start = SystemClock.uptimeMillis();

        // get map projection and the driver's starting point
        Projection proj = mMap.getProjection();
        Point startPoint = proj.toScreenLocation(driverMarker.getPosition());
        final LatLng startLatLng = proj.fromScreenLocation(startPoint);
        final long duration = 150;

        // create new Interpolator
        final Interpolator interpolator = new LinearInterpolator();

        // post a Runnable to the handler
        handler.post(new Runnable() {
            @Override
            public void run() {
                // calculate how soon we need to redraw the marker
                long elapsed = SystemClock.uptimeMillis() - start;
                float t = interpolator.getInterpolation((float) elapsed / duration);
                double lng = t * intent.getFloatExtra(Constants.LONGITUDE, 0) + (1 - t) * startLatLng.longitude;
                double lat = t * intent.getFloatExtra(Constants.LATITUDE, 0) + (1 - t) * startLatLng.latitude;

                // set the driver's marker position
                driverMarker.setPosition(new LatLng(lat, lng));
                if (t < 1.0) {
                    handler.postDelayed(this, 10);
                }
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.chat_map_actions, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle presses on the action bar items
        switch (item.getItemId()) {
        case R.id.action_chat:
            openChat();
            return true;
        case R.id.action_map:
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    /**
     * We want to go back to the previous activity therefore we just finish the current one to go
     * back through the stack
     */
    private void openChat() {
        Log.d(TAG, ".openChat() entered");
        finish();
    }

    /**
     * Unregister all local BroadcastReceivers
     */
    private void unregisterReceivers() {
        Log.d(TAG, ".unregisterReceivers() entered");
        if (coordinatesChangedBroadcastReceiver != null) {
            getApplicationContext().unregisterReceiver(coordinatesChangedBroadcastReceiver);
            coordinatesChangedBroadcastReceiver = null;
        }

    }

}