0

I'm writing an app where I have to show a Google Map standard fragment and draw on it the updated position of the mobile device (GCS in this app). The app seems to work fine only if launched trough Android Studio on the remote device. If I try to launch it straight from the device itself the map never get ready: it shows up but with very low details.

package myapp.activities;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.FragmentActivity;

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.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
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.material.snackbar.Snackbar;

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

import myapp.R;

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback, ActivityCompat.OnRequestPermissionsResultCallback, LocationListener {
    private static final int PERMISSION_REQUEST_LOCATION = 11;
    private static final int LOCATION_UPDATE_DELAY = 1000;

    private final String gps_provider = LocationManager.GPS_PROVIDER;

    private GoogleMap map;
    private Location location;
    private LocationManager location_manager;
    private View layout;
    private BitmapDescriptor base_icon;
    private MarkerOptions base_marker_options;
    private Marker base_marker;

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

        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);

        if (mapFragment != null)
            mapFragment.getMapAsync(this);

        //USER CODE

        layout = findViewById(R.id.map);
        assert layout != null;
   
        location = new Location(getString(R.string.location_provider));
        location.isFromMockProvider();

        base_icon = BitmapDescriptorFactory.fromResource(R.drawable.base);

        base_marker_options = new MarkerOptions().title(getString(R.string.destination_base_label)).icon(base_icon);

        initLocalization();
        //
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == PERMISSION_REQUEST_LOCATION) {
            if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Snackbar.make(layout, R.string.fine_location_access_granted, Snackbar.LENGTH_SHORT).show();
                startLocalization();
            } else {
                Snackbar.make(layout, R.string.denied_fine_location_access_rationale, Snackbar.LENGTH_SHORT).show();
            }
        }
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        map = googleMap;

        map.getUiSettings().setZoomControlsEnabled(true);
        map.getUiSettings().setCompassEnabled(true);
    }

    @Override
    protected void onStart() {
        super.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        stopTelemetry();
    }

    @Override
    public void onLocationChanged(Location location) {
        this.location.set(location);

        if (map != null) {
            updateGCS(location);
        }
    }

    @Override
    public void onStatusChanged(String s, int i, Bundle bundle) {
    }

    @Override
    public void onProviderEnabled(String s) {
    }

    @Override
    public void onProviderDisabled(String s) {
    }
    
private void updateGCS(final Location location) {
    if(map==null)
        return;

    final LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());

    if (base_marker == null) {
        base_marker_options.position(latLng);
        base_marker = map.addMarker(base_marker_options);
    } else
        base_marker.setPosition(latLng);

    final float _zoom = map.getCameraPosition().zoom;

    final CameraPosition _cp = new CameraPosition.Builder().target(latLng).zoom(_zoom).build();
    final CameraUpdate _cu = CameraUpdateFactory.newCameraPosition(_cp);

    map.animateCamera(_cu);
}
    
    private void requestLocationPermission() {
        if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                Manifest.permission.ACCESS_FINE_LOCATION)) {
            Snackbar.make(layout, R.string.denied_fine_location_access_rationale,
                    Snackbar.LENGTH_INDEFINITE).setAction(R.string.ok, new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    ActivityCompat.requestPermissions(MapsActivity.this,
                            new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                            PERMISSION_REQUEST_LOCATION);
                }
            }).show();

        } else {
            Snackbar.make(layout, R.string.denied_fine_location_access_warning, Snackbar.LENGTH_SHORT).show();
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSION_REQUEST_LOCATION);
        }
    }

    private void initLocalization() {
        if (ActivityCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            Snackbar.make(layout,
                    R.string.fine_location_access_granted,
                    Snackbar.LENGTH_SHORT).show();
            startLocalization();
        } else {
            requestLocationPermission();
        }
    }

    @SuppressLint("MissingPermission")
    private void startLocalization() {
        try {
            location_manager = (LocationManager) getSystemService(LOCATION_SERVICE);

            if (!location_manager.isProviderEnabled(gps_provider)) {
                Intent gpsOptionsIntent = new Intent(
                        android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                startActivity(gpsOptionsIntent);
            } else {
                location_manager.requestLocationUpdates(gps_provider, LOCATION_UPDATE_DELAY, 2, this);
            }
        } catch (Throwable e) {
            Log.e(getClass().getName(), "Exception: " + e.getMessage());
        }
    }   
}
weirdgyn
  • 718
  • 10
  • 40

2 Answers2

1

That's the problem of release key. You have setup the configuration with debug key but you need also the release key configuration. There are few steps. You need to Api key as you already get it, if not then check the link https://developers.google.com/maps/documentation/android-sdk/get-api-key If you are using it for Android only than restrict the key for Android SDK For debug and release key check the link https://developer.android.com/studio/publish/app-signing#debug-mode Note: You have to provide the debug, release and Google Play console SHA-1 Fingerprint certificate. If you need help then check this answer How to get the SHA-1 fingerprint certificate in Android Studio for debug mode? Configure it and enjoy.

Abubakar
  • 46
  • 5
  • Thanks for your answer . I already generated the Google API key following the link inside `google_maps_api.xml` file generated by *Android Studio* I supposed this's the debug api key. The next step would be to to create the a release API key starting from the SHA1 fingerprint release certificate but how can I get this? I'm a little confused – weirdgyn Jul 15 '20 at 14:20
  • @weirdgyn Sorry i have provided last link wrong. Please check this link https://stackoverflow.com/questions/15727912/sha-1-fingerprint-of-keystore-certificate for release key SHA-1 fingerprint. – Abubakar Jul 16 '20 at 08:35
0

Abubakar answer is a good starting hint but I'd like to add just a comment on the overall procedure I followed:

  • Create your own keystore trough Generate Signed Bundle or APK (Build menu), I did it just for release build,
  • Start a project from scratch (on Google Developer Console),
  • Enable Google Map Android SDK on the new project,
  • Restrict API key to Android app using app package id (from AndroidManifest.xml) and SHA1 signature,
  • Update manifest com.google.android.geo.API_KEY references with the generated key.

To create SHA1 key use keystore file (.jks) and keytool line command from Java SDK (command line provided in comments above).

NOTE: after creating the keystore a popup window will invite you to convert it in a different format. Just follow the hint and use the command provided in the dialog.

BTW both debug and release com.google.android.geo.API_KEY references (google_map_api.xml files inside debug and release folders if you used the standard Android Studio wizard) should point to the same api key value otherwise Google Map's component will throw an exception complaining about failed authentication.

weirdgyn
  • 718
  • 10
  • 40