2
\$\begingroup\$

I am trying to get the users current location and then the render the route using here-maps sdk with the location of the car provided from the Intent, I have a feeling I can improve it a lot as I have to again and again check for permission to get the last known location of user as below:

if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                        && ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                    return;
                }
                location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);

Is there an efficient way to do this?

Below is the complete java file for reference

import static com.sjain.routeplanner.GpsService.current_location;

public class ViewMapActivity extends AppCompatActivity {
    private static final String LOG_TAG = ViewMapActivity.class.getSimpleName();

    // permissions request code
    private final static int REQUEST_CODE_ASK_PERMISSIONS = 1;

    /**
     * Permissions that need to be explicitly requested from end user.
     */
    private static final String[] REQUIRED_SDK_PERMISSIONS = new String[]{
            Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.WRITE_EXTERNAL_STORAGE,
            Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.ACCESS_COARSE_LOCATION};

    // map embedded in the map fragment
    private Map map = null;

    // map fragment embedded in this activity
    private MapFragment mapFragment = null;

    // TextView for displaying the current map scheme
    private TextView textViewResult;

    // MapRoute for this activity
    private static MapRoute mapRoute = null;

    boolean paused = false;
    private MapMarker marker;
    private RouteItem route = null;

    private LocationManager locationManager;
    static Location location;

    LocationListener locationListener = new LocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            Double lat = location.getLatitude();
            Double lon = location.getLongitude();
            current_location = lat.toString() + "," + lon.toString();
            marker.setCoordinate(new GeoCoordinate(lat, lon));
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
            // available
            if (status == 0x00000000) {
                if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                        && ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                    return;
                }
                location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
                marker.setCoordinate(new GeoCoordinate(location.getLatitude(), location.getLongitude()));
            }
        }

        @Override
        public void onProviderEnabled(String provider) {
        }

        @Override
        public void onProviderDisabled(String provider) {
            Toast.makeText(getApplicationContext(), "Please Enable GPS service", Toast.LENGTH_SHORT).show();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        checkPermissions();
        intentInfo();
    }

    private void intentInfo() {
        try {

            // getting route details from intent
            String id = this.getIntent().getStringExtra("route_id");
            String name = this.getIntent().getStringExtra("route_name");
            String start_name = this.getIntent().getStringExtra("start_point_name");
            String end_name = this.getIntent().getStringExtra("end_point_name");
            String start_coordinate = this.getIntent().getStringExtra("start_point_coordinate");
            String end_coordinate = this.getIntent().getStringExtra("end_point_coordinate");
            ArrayList<String> waypoints = this.getIntent().getStringArrayListExtra("route_waypoints");
            ArrayList<String> waypoint_name = this.getIntent().getStringArrayListExtra("waypoint_name");
            route = new RouteItem(name, id, start_name + ";" + start_coordinate, end_name + ";" + end_coordinate, waypoints, waypoint_name);

            // setting route name
            TextView title = (TextView) findViewById(R.id.title);
            String details = "Route: " + name;
            title.setText(details);

            //setting route info
            StringBuilder sb = new StringBuilder();
            sb.append(route.getStart_point());
            sb.append('\n');
            sb.append(route.getEnd_point());
            sb.append('\n');
            TextView info = (TextView) findViewById(R.id.info);
            info.setText(sb);

            String type = this.getIntent().getStringExtra("type");
            // if type is display just display the route
            // if track then display the eta and current status
            if (Objects.equals(type, "track")) {
                String location = this.getIntent().getStringExtra("location");
                track_my_ride(location);
            }
        } catch (Error e) {
            Log.e("ViewMapActivity.class", "intentInfo: ", e);
            Toast.makeText(this, "Oops!! Error getting route info..", Toast.LENGTH_SHORT).show();
            startActivity(new Intent(this, ViewRouteActivity.class));
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        paused = false;
    }

    @Override
    protected void onPause() {
        super.onPause();
        paused = true;
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        map = null;
        super.onDestroy();
    }

    private void initialize() {
        setContentView(R.layout.activity_map_view);

        // Search for the map fragment to finish setup by calling init().
        mapFragment = (MapFragment) getFragmentManager().findFragmentById(R.id.mapFragment);
        mapFragment.init(new OnEngineInitListener() {
            @Override
            public void onEngineInitializationCompleted(OnEngineInitListener.Error error) {
                if (error == OnEngineInitListener.Error.NONE) {
                    // retrieve a reference of the map from the map fragment
                    map = mapFragment.getMap();
                    // Set the map center coordinate to the Vancouver region (no animation)
                    map.setCenter(new GeoCoordinate(19.152568, 72.855825), Map.Animation.NONE);
                    // Set the map zoom level to the average between min and max (no animation)
                    map.setZoomLevel((map.getMaxZoomLevel() + map.getMinZoomLevel()) / 2);

                    // create marker to display users current location
                    marker = new MapMarker();
                    if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                            && ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                        return;
                    }
                    Location temp_location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
                    marker.setCoordinate(new GeoCoordinate(temp_location.getLatitude(), temp_location.getLongitude()));
                    map.addMapObject(marker);
                } else {
                    Log.e(LOG_TAG, "Cannot initialize MapFragment (" + error + ")");
                }
            }
        });
    }

    /**
     * Checks the dynamically controlled permissions and requests missing permissions from end user.
     */
    protected void checkPermissions() {
        final List<String> missingPermissions = new ArrayList<>();
        // check all required dynamic permissions
        for (final String permission : REQUIRED_SDK_PERMISSIONS) {
            final int result = ContextCompat.checkSelfPermission(this, permission);
            if (result != PackageManager.PERMISSION_GRANTED) {
                missingPermissions.add(permission);
            }
        }
        if (!missingPermissions.isEmpty()) {
            // request all missing permissions
            final String[] permissions = missingPermissions
                    .toArray(new String[missingPermissions.size()]);
            ActivityCompat.requestPermissions(this, permissions, REQUEST_CODE_ASK_PERMISSIONS);
        } else {
            final int[] grantResults = new int[REQUIRED_SDK_PERMISSIONS.length];
            Arrays.fill(grantResults, PackageManager.PERMISSION_GRANTED);
            onRequestPermissionsResult(REQUEST_CODE_ASK_PERMISSIONS, REQUIRED_SDK_PERMISSIONS,
                    grantResults);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
                                           @NonNull int[] grantResults) {
        switch (requestCode) {
            case REQUEST_CODE_ASK_PERMISSIONS:
                for (int index = permissions.length - 1; index >= 0; --index) {
                    if (grantResults[index] != PackageManager.PERMISSION_GRANTED) {
                        // exit the app if one permission is not granted
                        Toast.makeText(this, "Required permission '" + permissions[index]
                                + "' not granted, exiting", Toast.LENGTH_LONG).show();
                        finish();
                        return;
                    }
                }
                // all permissions were granted
                initialize();
                locationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
                if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
                        ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                    return;
                }
                locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 30000, 0, locationListener);
                break;
        }
    }

    public void getDirections(View v) {
        // 1. clear previous results
        textViewResult = (TextView) findViewById(R.id.result);
        textViewResult.setText("");
        textViewResult.setVisibility(View.VISIBLE);
        if (map != null && mapRoute != null) {
            map.removeMapObject(mapRoute);
            mapRoute = null;
        }
        // 2. Initialize RouteManager
        RouteManager routeManager = new RouteManager();

        // 3. Select routing options
        RoutePlan routePlan = new RoutePlan();

        RouteOptions routeOptions = new RouteOptions();
        routeOptions.setTransportMode(RouteOptions.TransportMode.CAR);
        routeOptions.setRouteType(RouteOptions.Type.FASTEST);
        routePlan.setRouteOptions(routeOptions);

        //start point
        String[] start_coordinate = route.getStart_point_coordinate().split(",");
        routePlan.addWaypoint(new GeoCoordinate(Double.parseDouble(start_coordinate[0]), Double.parseDouble(start_coordinate[1])));
        MapMarker start_marker = new MapMarker();
        start_marker.setCoordinate(new GeoCoordinate(Double.parseDouble(start_coordinate[0]), Double.parseDouble(start_coordinate[1])));

        // adding waypoints
        ArrayList<String> waypoints = route.getWaypoints();
        for (int i = 0; i < waypoints.size(); i++) {
            String[] d = waypoints.get(i).split(",");
            routePlan.addWaypoint(new GeoCoordinate(Double.parseDouble(d[0]), Double.parseDouble(d[1])));
        }

        //end point
        String[] end_coordinate = route.getEnd_point_coordinate().split(",");
        routePlan.addWaypoint(new GeoCoordinate(Double.parseDouble(end_coordinate[0]), Double.parseDouble(end_coordinate[1])));
        MapMarker end_marker = new MapMarker();
        Image destination_image = new Image();
        Image origin_image = new Image();
        try {
            destination_image.setImageResource(R.drawable.destination);
            origin_image.setImageResource(R.drawable.origin);
        } catch (IOException e) {
            e.printStackTrace();
        }
        end_marker.setCoordinate(new GeoCoordinate(Double.parseDouble(end_coordinate[0]), Double.parseDouble(end_coordinate[1])));
        end_marker.setIcon(destination_image);
        start_marker.setIcon(origin_image);
        map.addMapObject(start_marker);
        map.addMapObject(end_marker);

        // 5. Retrieve Routing information via RouteManagerEventListener
        RouteManager.Error error = routeManager.calculateRoute(routePlan, routeManagerListener);
        if (error != RouteManager.Error.NONE) {
            Toast.makeText(getApplicationContext(),
                    "Route calculation failed with: " + error.toString(), Toast.LENGTH_SHORT)
                    .show();
        }

    }

    public void track_my_ride(String location) {
        // 1. clear previous results
        textViewResult = (TextView) findViewById(R.id.result);
        textViewResult.setText("");
        if (map != null && mapRoute != null) {
            map.removeMapObject(mapRoute);
            mapRoute = null;
        }

        // 2. Initialize RouteManager
        RouteManager routeManager = new RouteManager();

        // 3. Select routing options
        RoutePlan routePlan = new RoutePlan();

        RouteOptions routeOptions = new RouteOptions();
        routeOptions.setTransportMode(RouteOptions.TransportMode.CAR);
        routeOptions.setRouteType(RouteOptions.Type.FASTEST);
        routePlan.setRouteOptions(routeOptions);

        GeoCoordinate coordinate;
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            return;
        }
        Location location1 = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
        if(location1 != null) {
            coordinate = new GeoCoordinate(location1.getLatitude(), location1.getLongitude());
        }
        else {
            // default nesco it park coordinates
            //coordinates taken from here maps
            coordinate = new GeoCoordinate(19.15254,72.85571);
        }
        // 4. Select Waypoints for your routes
        routePlan.addWaypoint(coordinate);

        // supposed to be current or last known location of bus
        String[] bus_coordinate = location.split(",");
        routePlan.addWaypoint(new GeoCoordinate(
                Double.parseDouble(bus_coordinate[0]),
                Double.parseDouble(bus_coordinate[1])));

        // 5. Retrieve Routing information via RouteManagerEventListener
        RouteManager.Error error = routeManager.calculateRoute(routePlan, routeManagerListener);
        if (error != RouteManager.Error.NONE) {
            Toast.makeText(getApplicationContext(),
                    "Route calculation failed with: " + error.toString(), Toast.LENGTH_SHORT)
                    .show();
        }
    }

    private RouteManager.Listener routeManagerListener = new RouteManager.Listener() {
        public void onCalculateRouteFinished(RouteManager.Error errorCode,
                                             List<RouteResult> result) {

            if (errorCode == RouteManager.Error.NONE && result.get(0).getRoute() != null) {
                // create a map route object and place it on the map
                mapRoute = new MapRoute(result.get(0).getRoute());
                map.addMapObject(mapRoute);

                // Get the bounding box containing the route and zoom in (no animation)
                GeoBoundingBox gbb = result.get(0).getRoute().getBoundingBox();
                map.zoomTo(gbb, Map.Animation.NONE, Map.MOVE_PRESERVE_ORIENTATION);

                textViewResult.setText(String.format(getString(R.string.route_maneuvers), result.get(0).getRoute().getManeuvers().size()));
            } else {
                textViewResult.setText(
                        String.format("Route calculation failed: %s", errorCode.toString()));
            }
        }

        public void onProgress(int percentage) {
            textViewResult.setText(String.format(getString(R.string.precent_done), percentage));
        }
    };
}
\$\endgroup\$
2
  • 2
    \$\begingroup\$ Usually, the Grace Hopper principle (do not ask permissions; ask forgiveness) is a way to go. Try what you want to do, and if an exception happens, then ask permission. Testing for (dynamically changeable) permission achieves nothing because of TOC-TOU race. \$\endgroup\$
    – vnp
    Commented May 23, 2017 at 7:20
  • \$\begingroup\$ @vnp Though I already have the permissions as you can see from my checkPermission() method in` onCreate` method bus still I have to keep a check, so there is no way around this I will have to do this everytime I try to get lastknownlocation \$\endgroup\$
    – warl0ck
    Commented May 25, 2017 at 8:14

1 Answer 1

1
\$\begingroup\$

Permissions If there is a chance that permissions are withdrawn at runtime, possibly causing Permission related exceptions then I am not sure there is a way around asking for permissions on each query.

Separation of concerns Your activity does a lot of different things. I'd try to pull out the entire Permission Handling as much as possible and put it into it's own class/component. The same I'd suggest for the content of the methods track_my_ride and getDirections - btw you're mixing naming conventions :) - see Other points#1 but at the very least, stick to one principle.

Extracting permission handling code This is a refactoring task. Basically I see two options (there may be more though):

  1. Have a base-activity that contains all the permission handling code (I usually try to avoid creating elaborate hierarchies of Activities, because: Composition over inheritance)
  2. Move the permission handling into it's own class and provide an interface for others (such as Activities) to interact with it.

Other points

  1. Variable naming: I'd recommend using camelCase for variable names as well as methods as per the Java naming conventions
  2. UI text: Ui text should be kept in the appropriate language file (strings.xml) and not be hard-coded in one language. This facilitates internationalization and re-use of UI text
  3. I'd recommend using Google's FusedLocationProvider as described here this way you'll receive updates to locations in a continuous fashion, as per your requirements (interval, displacement, accuracy etc) you can also use it to request one-time locations
\$\endgroup\$
2
  • \$\begingroup\$ Thanks for your valuable input, but can you add code snippet how am I supposed to put permission handling in its own class. \$\endgroup\$
    – warl0ck
    Commented Jun 2, 2017 at 7:56
  • \$\begingroup\$ @warl0ck exporting permission handling into it's own component is not a small code-snippet :) - but if you grasp the conceptual idea it will be straight forward to write. I updated the answer to elaborate on the concept of externalizing the code for handling permissions. \$\endgroup\$
    – AgentKnopf
    Commented Jun 2, 2017 at 9:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.