steroid-team/app

View on GitHub
app/src/main/java/com/github/steroidteam/todolist/view/MapFragment.java

Summary

Maintainability
A
0 mins
Test Coverage
F
26%
package com.github.steroidteam.todolist.view;

import android.content.pm.PackageManager;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SearchView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import com.github.steroidteam.todolist.R;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
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.google.android.gms.tasks.Task;
import java.io.IOException;
import java.util.List;

public class MapFragment extends Fragment implements OnMapReadyCallback {
    public static final String LOCATION_REQ = MapFragment.class.toString() + "/location";
    public static final String LOCATION_KEY = "location";
    public static final String LOCATION_NAME_KEY = "location-name";

    private static final String TAG = MapFragment.class.getSimpleName();

    private GoogleMap map;
    // The entry point to the Fused Location Provider.
    private FusedLocationProviderClient fusedLocationProviderClient;
    // A default location (Sydney, Australia) and default zoom to use when location permission is
    // not granted.
    private final LatLng defaultLocation = new LatLng(-33.8523341, 151.2106085);
    private static final int DEFAULT_ZOOM = 15;
    private static final int ZOOM_SEARCH = 10;
    private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1;
    private boolean locationPermissionGranted;

    // The geographical location where the device is currently located. That is, the last-known
    // location retrieved by the Fused Location Provider.
    private Location lastKnownLocation;

    // Keys for storing activity state.
    private static final String KEY_LOCATION = "location";

    private SearchView searchView;
    private Marker marker;

    @Override
    public View onCreateView(
            @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        View root = inflater.inflate(R.layout.fragment_map, container, false);

        root.findViewById(R.id.map_save_location).setOnClickListener(this::onSavePressed);

        // Retrieve location and camera position from saved instance state.
        if (savedInstanceState != null) {
            lastKnownLocation = savedInstanceState.getParcelable(KEY_LOCATION);
        }

        // Retrieve the content view that renders the map.
        // setContentView(R.layout.fragment_map);
        searchView = root.findViewById(R.id.sv_location);

        searchView.setOnQueryTextListener(createOnQueryTextListener());
        // Construct a FusedLocationProviderClient.
        fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(getContext());

        // Get the SupportMapFragment and request notification when the map is ready to be used.
        SupportMapFragment mapFragment =
                (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);

        return root;
    }

    private SearchView.OnQueryTextListener createOnQueryTextListener() {
        return new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String s) {
                String location = searchView.getQuery().toString();
                List<Address> addressList = null;

                if (location != null || !location.equals("")) {
                    markerSearchLocation(location, addressList);
                    searchView.setQuery("", false);
                }
                return false;
            }

            @Override
            public boolean onQueryTextChange(String s) {
                return false;
            }
        };
    }

    private void markerSearchLocation(String location, List<Address> addressList) {
        Geocoder geocoder = new Geocoder(getContext());
        try {
            addressList = geocoder.getFromLocationName(location, 1);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (!checkLocationHasBeenFound(addressList)) {
            Toast.makeText(getContext(), getString(R.string.location_not_found), Toast.LENGTH_SHORT)
                    .show();
            return;
        }
        Address address = addressList.get(0);
        locationHasBeenFound(address);
    }

    private boolean checkLocationHasBeenFound(List<Address> addressList) {
        return addressList.size() > 0;
    }

    private void locationHasBeenFound(Address address) {
        LatLng latLng = new LatLng(address.getLatitude(), address.getLongitude());
        String locationName = address.getLocality();
        if (marker != null) {
            marker.setPosition(latLng);
            marker.setTitle(locationName);
        } else {
            marker = map.addMarker(new MarkerOptions().position(latLng).title(locationName));
        }
        map.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, ZOOM_SEARCH));
    }

    /**
     * Manipulates the map when it's available. This callback is triggered when the map is ready to
     * be used.
     */
    @Override
    public void onMapReady(GoogleMap map) {
        this.map = map;
        // Prompt the user for permission.
        getLocationPermission();

        // Turn on the My Location layer and the related control on the map.
        updateLocationUI();

        // Get the current location of the device and set the position of the map.
        getDeviceLocation();
    }

    public void onSavePressed(View view) {
        if (marker != null) {
            LatLng position = marker.getPosition();

            Geocoder geocoder = new Geocoder(getContext());
            List<Address> addressList = null;
            try {
                addressList = geocoder.getFromLocation(position.latitude, position.longitude, 2);
            } catch (IOException e) {
                e.printStackTrace();
            }

            Bundle bundle = new Bundle();
            bundle.putParcelable(LOCATION_KEY, position);
            bundle.putString(LOCATION_NAME_KEY, addressList.get(0).getLocality());

            getParentFragmentManager().setFragmentResult(LOCATION_REQ, bundle);
        }
        // Go back to the original fragment.
        getParentFragmentManager().popBackStack();
    }

    /** Gets the current location of the device, and positions the map's camera. */
    private void getDeviceLocation() {
        /*
         * Get the best and most recent location of the device, which may be null in rare
         * cases when a location is not available.
         */
        try {
            if (locationPermissionGranted) {
                Task<Location> locationResult = fusedLocationProviderClient.getLastLocation();
                locationResult.addOnCompleteListener(getActivity(), this::onCompleteDeviceLocation);
            }
        } catch (SecurityException e) {
            Log.e("Exception: %s", e.getMessage(), e);
        }
    }

    private void onCompleteDeviceLocation(@NonNull Task<Location> task) {
        if (task.isSuccessful()) {
            // Set the map's camera position to the current location of the device.
            lastKnownLocation = task.getResult();
            deviceLocationMarker(lastKnownLocation);
        } else {
            Log.d(TAG, "Current location is null. Using defaults.");
            Log.e(TAG, "Exception: %s", task.getException());
            map.moveCamera(CameraUpdateFactory.newLatLngZoom(defaultLocation, DEFAULT_ZOOM));
            map.getUiSettings().setMyLocationButtonEnabled(false);
        }
    }

    public void deviceLocationMarker(Location location) {
        if (location != null) {
            deviceLocationMarkerLocNotNull(location);
        } else {
            deviceLocationMarkerLocIsNull();
        }
    }

    private void deviceLocationMarkerLocNotNull(Location location) {
        map.moveCamera(
                CameraUpdateFactory.newLatLngZoom(
                        new LatLng(location.getLatitude(), location.getLongitude()), DEFAULT_ZOOM));
        if (marker != null) {
            marker.setPosition(new LatLng(location.getLatitude(), location.getLongitude()));
            marker.setTitle(getString(R.string.current_location_message));
        } else {
            LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
            marker =
                    map.addMarker(
                            new MarkerOptions()
                                    .position(latLng)
                                    .title(getString(R.string.current_location_message)));
        }
    }

    private void deviceLocationMarkerLocIsNull() {
        map.moveCamera(CameraUpdateFactory.newLatLng(defaultLocation));
        if (marker != null) {
            marker.setPosition(defaultLocation);
            marker.setTitle(getString(R.string.default_location));
        } else {
            marker =
                    map.addMarker(
                            new MarkerOptions()
                                    .position(defaultLocation)
                                    .title(getString(R.string.default_location)));
        }
    }

    /** Prompts the user for permission to use the device location. */
    private void getLocationPermission() {
        /*
         * Request location permission, so that we can get the location of the
         * device. The result of the permission request is handled by a callback,
         * onRequestPermissionsResult.
         */
        if (ContextCompat.checkSelfPermission(
                        getContext(), android.Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            locationPermissionGranted = true;
        } else {
            ActivityCompat.requestPermissions(
                    getActivity(),
                    new String[] {android.Manifest.permission.ACCESS_FINE_LOCATION},
                    PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
        }
    }

    /** Handles the result of the request for location permissions. */
    @Override
    public void onRequestPermissionsResult(
            int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        locationPermissionGranted = false;
        switch (requestCode) {
            case PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION:
                {
                    // If request is cancelled, the result arrays are empty.
                    if (grantResults.length > 0
                            && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                        locationPermissionGranted = true;
                    }
                }
        }
        updateLocationUI();
    }

    /** Updates the map's UI settings based on whether the user has granted location permission. */
    private void updateLocationUI() {
        if (map == null) {
            return;
        }
        try {
            if (locationPermissionGranted) {
                map.setMyLocationEnabled(true);
                map.getUiSettings().setMyLocationButtonEnabled(true);
            } else {
                map.setMyLocationEnabled(false);
                map.getUiSettings().setMyLocationButtonEnabled(false);
                lastKnownLocation = null;
                getLocationPermission();
            }
        } catch (SecurityException e) {
            Log.e("Exception: %s", e.getMessage());
        }
    }
}