I've been trying to use the fusedLocationApi to get my current location. I'm using android studio with an emulated Nexus 6. According to the online documentation https://developer.android.com/training/location/receive-location-updates.html?hl=es
we can request location updates using:
FusedLocationApi.requestLocationUpdates
and the callback goes to:
onLocationChanged(Location location)
However, onLocationChanged is not getting called at all. If it did, it would print something in the log cause I put a Log.d line there. Note that startLocationUpdates() does get called. Below is my code. It would be great if someone could shed some light on this =) I'm really new to Android so I'm a bit clueless rn. Could it be something about the emulator (GPS,.. idk) that is causing this issue?
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener, LocationListener {
private GoogleMap mMap; // google map works well just as default project
private GoogleApiClient mGoogleApiClient;
public static final String TAG = MapsActivity.class.getSimpleName();
private LocationRequest mLocationRequest;
private static final int REQUEST_LOCATION = 2;
private Location location;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
mLocationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(1000)
.setFastestInterval(100);
}
#Override
public void onConnected(#Nullable Bundle bundle) {
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_LOCATION);
} else {
location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (location == null) {
startLocationUpdates();
}
else {
handleNewLocation(location);
};
}
}
protected void startLocationUpdates() {
Log.d(TAG, "Requesting location updates");
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}
#Override
public void onLocationChanged(Location location) {
String msg = "Updated Location: " +
Double.toString(location.getLatitude()) + "," +
Double.toString(location.getLongitude());
Log.d(TAG, msg);
}
manifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Gradle build:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.0.0'
compile 'com.google.android.gms:play-services:9.0.0'
}
Are you sure your GPS is turned on? You can try going through this sample project by Google Google on GitHub. In this project they have also provided code for checking if Location services are enabled. If they are not, user will be given a message so they can activate their location services.
/*
* Copyright (C) 2012 The Android Open Source Project
*
* 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 com.example.mapdemo;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMyLocationButtonClickListener;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
/**
* This demo shows how GMS Location can be used to check for changes to the users location. The
* "My Location" button uses GMS Location to set the blue dot representing the users location.
* Permission for {#link android.Manifest.permission#ACCESS_FINE_LOCATION} is requested at run
* time. If the permission has not been granted, the Activity is finished with an error message.
*/
public class MyLocationDemoActivity extends AppCompatActivity
implements
OnMyLocationButtonClickListener,
OnMapReadyCallback,
ActivityCompat.OnRequestPermissionsResultCallback {
/**
* Request code for location permission request.
*
* #see #onRequestPermissionsResult(int, String[], int[])
*/
private static final int LOCATION_PERMISSION_REQUEST_CODE = 1;
/**
* Flag indicating whether a requested permission has been denied after returning in
* {#link #onRequestPermissionsResult(int, String[], int[])}.
*/
private boolean mPermissionDenied = false;
private GoogleMap mMap;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_location_demo);
SupportMapFragment mapFragment =
(SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
#Override
public void onMapReady(GoogleMap map) {
mMap = map;
mMap.setOnMyLocationButtonClickListener(this);
enableMyLocation();
}
/**
* Enables the My Location layer if the fine location permission has been granted.
*/
private void enableMyLocation() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
// Permission to access the location is missing.
PermissionUtils.requestPermission(this, LOCATION_PERMISSION_REQUEST_CODE,
Manifest.permission.ACCESS_FINE_LOCATION, true);
} else if (mMap != null) {
// Access to the location has been granted to the app.
mMap.setMyLocationEnabled(true);
}
}
#Override
public boolean onMyLocationButtonClick() {
Toast.makeText(this, "MyLocation button clicked", Toast.LENGTH_SHORT).show();
// Return false so that we don't consume the event and the default behavior still occurs
// (the camera animates to the user's current position).
return false;
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions,
#NonNull int[] grantResults) {
if (requestCode != LOCATION_PERMISSION_REQUEST_CODE) {
return;
}
if (PermissionUtils.isPermissionGranted(permissions, grantResults,
Manifest.permission.ACCESS_FINE_LOCATION)) {
// Enable the my location layer if the permission has been granted.
enableMyLocation();
} else {
// Display the missing permission error dialog when the fragments resume.
mPermissionDenied = true;
}
}
#Override
protected void onResumeFragments() {
super.onResumeFragments();
if (mPermissionDenied) {
// Permission was not granted, display error dialog.
showMissingPermissionError();
mPermissionDenied = false;
}
}
/**
* Displays a dialog with error message explaining that the location permission is missing.
*/
private void showMissingPermissionError() {
PermissionUtils.PermissionDeniedDialog
.newInstance(true).show(getSupportFragmentManager(), "dialog");
}
}
Old question but unless something has changed since I tried it last, the emulator doesn't know its location. You have to send it coordinates.
Related
This question already has answers here:
Android LocationListener, AbstractMethodError on onStatusChanged and onProviderDisabled
(6 answers)
Closed 1 year ago.
I've tried this code from an UDEMY Course. Written same like him, he can access to location & also got a Toast while I cant opening app with allowed Location.
When allowing location, my app crashing.
But when deny I can see the map and not crashing anymore.
***My google map API also working good. also taken user Internet & Location Service.
MapsActivity.java:
package com.project.demo2map;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentActivity;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
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.MarkerOptions;
import com.project.demo2map.databinding.ActivityMapsBinding;
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {
private GoogleMap mMap;
private ActivityMapsBinding binding;
LocationManager locationManager;
LocationListener locationListener;
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,0,0, locationListener);
}
}
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMapsBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
locationListener = new LocationListener() {
#Override
public void onLocationChanged(#NonNull Location location) {
Toast.makeText(MapsActivity.this, location.toString(), Toast.LENGTH_SHORT).show();
}
};
if (Build.VERSION.SDK_INT < 23) {
locationManager.requestLocationUpdates(LocationManager.
GPS_PROVIDER, 0, 0, locationListener);
}else {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
} else {
locationManager.requestLocationUpdates(LocationManager.
GPS_PROVIDER, 0, 0, locationListener);
}
}
}
/**
* Manipulates the map once available.
* This callback is triggered when the map is ready to be used.
* This is where we can add markers or lines, add listeners or move the camera. In this case,
* we just add a marker near Sydney, Australia.
* If Google Play services is not installed on the device, the user will be prompted to install
* it inside the SupportMapFragment. This method will only be triggered once the user has
* installed Google Play services and returned to the app.
*/
#Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
// Add a marker in Sydney and move the camera
LatLng sydney = new LatLng(-34, 151);
mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
}
}
CrashLog:
2021-10-25 14:59:29.003 25984-25984/com.project.demo2map D/AndroidRuntime: Shutting down VM
2021-10-25 14:59:29.006 25984-25984/com.project.demo2map E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.project.demo2map, PID: 25984
java.lang.AbstractMethodError: abstract method "void android.location.LocationListener.onProviderDisabled(java.lang.String)"
at android.location.LocationManager$ListenerTransport._handleMessage(LocationManager.java:365)
at android.location.LocationManager$ListenerTransport.access$000(LocationManager.java:275)
at android.location.LocationManager$ListenerTransport$1.handleMessage(LocationManager.java:291)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:491)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:925)
Overriding the onProviderChanged() method will fix this problem.
check if any providers active.
if( locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) ||
locationManager .isProviderEnabled(LocationManager.GPS_PROVIDER)){
//start listener to requestUpdates ...
}else{
//show message, no providers enabled.
}
I'm on a project to create an app on Android studio, to fetch data from a Google spreadsheet to work with it.
So I tried to work on it by trying the spreadsheet-quickstart for Android (Apiv4), on the Google documentation.
So I launch the quickstart app on my device.
But I've every time is displayed an error, when I launch my app on my device, especially when the program call the google sheet, and I can't connect to the sheet:
The following error occurred: null
I take a look on the program, retrieved where is that error to debug which is on the method onCancelled(). But I don't really understand some parts of the program is used, for example this method with "..." which seems have an important role to debug my error:
protected List<String> doInBackground(Void... params)
I followed perfectly the tutorial, working on Android Studio, started over 3 times, but always the same issue. I'm not confident at all with Java, and Android (first real app). That's why I need your help, to learn what I'm doing wrong.
I resume below the steps I followed, and the program I've done:
Retrieve my Sha1 FingerPrint on my computer (cmd.exe command)
Create my google spreadsheet
Activation of my Api for Android, and put my SHA1 fingerprint and the package I'll use for my app
Create my app as the tutorial said
Change the build.gradle (the good one) and Sync it
Change the AndroidManifest.xml:
Create my MainActivity.java, and replace with this code.
Build an apk, and transfer this one on my OnePlus3 (my Android phone) via USB.
Install this apk, and launch it.
Click on this button, log with my google account, display Calling Google Sheet Api, then stop it and display this error:
The following error occurred: null
Build.gradle:
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25.0.3"
defaultConfig {
applicationId "com.example.quickstart"
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
configurations.all {
resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
} }
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.google.android.gms:play-services:8.1.0'
testCompile 'junit:junit:4.12'
compile 'pub.devrel:easypermissions:0.2.1'
compile('com.google.api-client:google-api-client-android:1.22.0') {
exclude group: 'org.apache.httpcomponents'
}
compile('com.google.apis:google-api-services-sheets:v4-rev476-1.22.0') {
exclude group: 'org.apache.httpcomponents'
}
}
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.quickstart">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="Google Sheets API Android Quickstart"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="Google Sheets API Android Quickstart" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
MainActivity.java:
package com.example.quickstart;
import android.Manifest;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.text.method.ScrollingMovementMethod;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.googleapis.extensions.android.gms.auth.GooglePlayServicesAvailabilityIOException;
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.ExponentialBackOff;
import com.google.api.services.sheets.v4.SheetsScopes;
import com.google.api.services.sheets.v4.model.ValueRange;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import pub.devrel.easypermissions.AfterPermissionGranted;
import pub.devrel.easypermissions.EasyPermissions;
public class MainActivity extends Activity
implements EasyPermissions.PermissionCallbacks {
GoogleAccountCredential mCredential;
private TextView mOutputText;
private Button mCallApiButton;
ProgressDialog mProgress;
static final int REQUEST_ACCOUNT_PICKER = 1000;
static final int REQUEST_AUTHORIZATION = 1001;
static final int REQUEST_GOOGLE_PLAY_SERVICES = 1002;
static final int REQUEST_PERMISSION_GET_ACCOUNTS = 1003;
private static final String BUTTON_TEXT = "Call Google Sheets API";
private static final String PREF_ACCOUNT_NAME = "accountName";
private static final String[] SCOPES = { SheetsScopes.SPREADSHEETS_READONLY };
/**
* Create the main activity.
* #param savedInstanceState previously saved instance data.
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout activityLayout = new LinearLayout(this);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT);
activityLayout.setLayoutParams(lp);
activityLayout.setOrientation(LinearLayout.VERTICAL);
activityLayout.setPadding(16, 16, 16, 16);
ViewGroup.LayoutParams tlp = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
mCallApiButton = new Button(this);
mCallApiButton.setText(BUTTON_TEXT);
mCallApiButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mCallApiButton.setEnabled(false);
mOutputText.setText("");
getResultsFromApi();
mCallApiButton.setEnabled(true);
}
});
activityLayout.addView(mCallApiButton);
mOutputText = new TextView(this);
mOutputText.setLayoutParams(tlp);
mOutputText.setPadding(16, 16, 16, 16);
mOutputText.setVerticalScrollBarEnabled(true);
mOutputText.setMovementMethod(new ScrollingMovementMethod());
mOutputText.setText(
"Click the \'" + BUTTON_TEXT +"\' button to test the API.");
activityLayout.addView(mOutputText);
mProgress = new ProgressDialog(this);
mProgress.setMessage("Calling Google Sheets API ...");
setContentView(activityLayout);
// Initialize credentials and service object.
mCredential = GoogleAccountCredential.usingOAuth2(
getApplicationContext(), Arrays.asList(SCOPES))
.setBackOff(new ExponentialBackOff());
}
/**
* Attempt to call the API, after verifying that all the preconditions are
* satisfied. The preconditions are: Google Play Services installed, an
* account was selected and the device currently has online access. If any
* of the preconditions are not satisfied, the app will prompt the user as
* appropriate.
*/
private void getResultsFromApi() {
if (! isGooglePlayServicesAvailable()) {
acquireGooglePlayServices();
} else if (mCredential.getSelectedAccountName() == null) {
chooseAccount();
} else if (! isDeviceOnline()) {
mOutputText.setText("No network connection available.");
} else {
new MakeRequestTask(mCredential).execute();
}
}
/**
* Attempts to set the account used with the API credentials. If an account
* name was previously saved it will use that one; otherwise an account
* picker dialog will be shown to the user. Note that the setting the
* account to use with the credentials object requires the app to have the
* GET_ACCOUNTS permission, which is requested here if it is not already
* present. The AfterPermissionGranted annotation indicates that this
* function will be rerun automatically whenever the GET_ACCOUNTS permission
* is granted.
*/
#AfterPermissionGranted(REQUEST_PERMISSION_GET_ACCOUNTS)
private void chooseAccount() {
if (EasyPermissions.hasPermissions(
this, Manifest.permission.GET_ACCOUNTS)) {
String accountName = getPreferences(Context.MODE_PRIVATE)
.getString(PREF_ACCOUNT_NAME, null);
if (accountName != null) {
mCredential.setSelectedAccountName(accountName);
getResultsFromApi();
} else {
// Start a dialog from which the user can choose an account
startActivityForResult(
mCredential.newChooseAccountIntent(),
REQUEST_ACCOUNT_PICKER);
}
} else {
// Request the GET_ACCOUNTS permission via a user dialog
EasyPermissions.requestPermissions(
this,
"This app needs to access your Google account (via Contacts).",
REQUEST_PERMISSION_GET_ACCOUNTS,
Manifest.permission.GET_ACCOUNTS);
}
}
/**
* Called when an activity launched here (specifically, AccountPicker
* and authorization) exits, giving you the requestCode you started it with,
* the resultCode it returned, and any additional data from it.
* #param requestCode code indicating which activity result is incoming.
* #param resultCode code indicating the result of the incoming
* activity result.
* #param data Intent (containing result data) returned by incoming
* activity result.
*/
#Override
protected void onActivityResult(
int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode) {
case REQUEST_GOOGLE_PLAY_SERVICES:
if (resultCode != RESULT_OK) {
mOutputText.setText(
"This app requires Google Play Services. Please install " +
"Google Play Services on your device and relaunch this app.");
} else {
getResultsFromApi();
}
break;
case REQUEST_ACCOUNT_PICKER:
if (resultCode == RESULT_OK && data != null &&
data.getExtras() != null) {
String accountName =
data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
if (accountName != null) {
SharedPreferences settings =
getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putString(PREF_ACCOUNT_NAME, accountName);
editor.apply();
mCredential.setSelectedAccountName(accountName);
getResultsFromApi();
}
}
break;
case REQUEST_AUTHORIZATION:
if (resultCode == RESULT_OK) {
getResultsFromApi();
}
break;
}
}
/**
* Respond to requests for permissions at runtime for API 23 and above.
* #param requestCode The request code passed in
* requestPermissions(android.app.Activity, String, int, String[])
* #param permissions The requested permissions. Never null.
* #param grantResults The grant results for the corresponding permissions
* which is either PERMISSION_GRANTED or PERMISSION_DENIED. Never null.
*/
#Override
public void onRequestPermissionsResult(int requestCode,
#NonNull String[] permissions,
#NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(
requestCode, permissions, grantResults, this);
}
/**
* Callback for when a permission is granted using the EasyPermissions
* library.
* #param requestCode The request code associated with the requested
* permission
* #param list The requested permission list. Never null.
*/
#Override
public void onPermissionsGranted(int requestCode, List<String> list) {
// Do nothing.
}
/**
* Callback for when a permission is denied using the EasyPermissions
* library.
* #param requestCode The request code associated with the requested
* permission
* #param list The requested permission list. Never null.
*/
#Override
public void onPermissionsDenied(int requestCode, List<String> list) {
// Do nothing.
}
/**
* Checks whether the device currently has a network connection.
* #return true if the device has a network connection, false otherwise.
*/
private boolean isDeviceOnline() {
ConnectivityManager connMgr =
(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
return (networkInfo != null && networkInfo.isConnected());
}
/**
* Check that Google Play services APK is installed and up to date.
* #return true if Google Play Services is available and up to
* date on this device; false otherwise.
*/
private boolean isGooglePlayServicesAvailable() {
GoogleApiAvailability apiAvailability =
GoogleApiAvailability.getInstance();
final int connectionStatusCode =
apiAvailability.isGooglePlayServicesAvailable(this);
return connectionStatusCode == ConnectionResult.SUCCESS;
}
/**
* Attempt to resolve a missing, out-of-date, invalid or disabled Google
* Play Services installation via a user dialog, if possible.
*/
private void acquireGooglePlayServices() {
GoogleApiAvailability apiAvailability =
GoogleApiAvailability.getInstance();
final int connectionStatusCode =
apiAvailability.isGooglePlayServicesAvailable(this);
if (apiAvailability.isUserResolvableError(connectionStatusCode)) {
showGooglePlayServicesAvailabilityErrorDialog(connectionStatusCode);
}
}
/**
* Display an error dialog showing that Google Play Services is missing
* or out of date.
* #param connectionStatusCode code describing the presence (or lack of)
* Google Play Services on this device.
*/
void showGooglePlayServicesAvailabilityErrorDialog(
final int connectionStatusCode) {
GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
Dialog dialog = apiAvailability.getErrorDialog(
MainActivity.this,
connectionStatusCode,
REQUEST_GOOGLE_PLAY_SERVICES);
dialog.show();
}
/**
* An asynchronous task that handles the Google Sheets API call.
* Placing the API calls in their own task ensures the UI stays responsive.
*/
private class MakeRequestTask extends AsyncTask<Void, Void, List<String>> {
private com.google.api.services.sheets.v4.Sheets mService = null;
private Exception mLastError = null;
MakeRequestTask(GoogleAccountCredential credential) {
HttpTransport transport = AndroidHttp.newCompatibleTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
mService = new com.google.api.services.sheets.v4.Sheets.Builder(
transport, jsonFactory, credential)
.setApplicationName("Google Sheets API Android Quickstart")
.build();
}
/**
* Background task to call Google Sheets API.
* #param params no parameters needed for this task.
*/
#Override
protected List<String> doInBackground(Void... params) {
try {
return getDataFromApi();
} catch (Exception e) {
mLastError = e;
cancel(true);
return null;
}
}
/**
* Fetch a list of names and majors of students in a sample spreadsheet:
* https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
* #return List of names and majors
* #throws IOException
*/
private List<String> getDataFromApi() throws IOException {
String spreadsheetId = "629235369054-3cc387mt7mlnacv1kiq8d30auhqrh278.apps.googleusercontent.com";
String range = "GroupMar!A2:C";
List<String> results = new ArrayList<String>();
ValueRange response = this.mService.spreadsheets().values()
.get(spreadsheetId, range)
.execute();
List<List<Object>> values = response.getValues();
if (values != null) {
results.add("Name, Major");
for (List row : values) {
results.add(row.get(0) + ", " + row.get(1) + ", " + row.get(2));
}
}
return results;
}
#Override
protected void onPreExecute() {
mOutputText.setText("");
mProgress.show();
}
#Override
protected void onPostExecute(List<String> output) {
mProgress.hide();
if (output == null || output.size() == 0) {
mOutputText.setText("No results returned.");
} else {
output.add(0, "Data retrieved using the Google Sheets API:");
mOutputText.setText(TextUtils.join("\n", output));
}
}
#Override
protected void onCancelled() {
mProgress.hide();
if (mLastError != null) {
if (mLastError instanceof GooglePlayServicesAvailabilityIOException) {
showGooglePlayServicesAvailabilityErrorDialog(
((GooglePlayServicesAvailabilityIOException) mLastError)
.getConnectionStatusCode());
} else if (mLastError instanceof UserRecoverableAuthIOException) {
startActivityForResult(
((UserRecoverableAuthIOException) mLastError).getIntent(),
MainActivity.REQUEST_AUTHORIZATION);
} else {
mOutputText.setText("The following error occurred:\n"
+ mLastError.getMessage());
}
} else {
mOutputText.setText("Request cancelled.");
}
}
}
}
Can anyone help me to understand?
UPDATE: Easier way to get the SHA1 key (compared to the link i've posted in my answer) - https://stackoverflow.com/a/34223470/7368208 - If it doesn't work, the answer linked below will definitely work
I had the same problem. This error occurs when the app is unable to sign-in i.e. invalid credentials. So the problem could be
Credentials not created (but you mentioned you did) so..
Credentials mismatch between app and developers console e.g. wrong SHA1 key
The issue I faced was that the SHA1 key obtained via keytool didnt match with the actual key used in the app. So first get the SHA1 key thats actually used in the app. Check this post to see how to get the SHA1 key used in the app. If its different from the keytool key then updating it in the developers console will solve your problem.
Looking for some help with a problem in my app concerning getting the current device location. Below is the GPSLocationListener class I am using.
import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
/**
* This class takes care of capturing the location of the device.
*/
public class GPSLocationListener implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener, LocationListener,
ActivityCompat.OnRequestPermissionsResultCallback {
protected static final String TAG = "location-updates-sample";
public static final int LOCATION_RESQUEST = 1;
/**
* The desired interval for location updates. Inexact. Updates may be more or less frequent.
*/
public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 10000;
/**
* The fastest rate for active location updates. Exact. Updates will never be more frequent
* than this value.
*/
public static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS =
UPDATE_INTERVAL_IN_MILLISECONDS / 2;
/**
* Provides the entry point to Google Play services.
*/
protected GoogleApiClient mGoogleApiClient;
/**
* Stores parameters for requests to the FusedLocationProviderApi.
*/
protected LocationRequest mLocationRequest;
/**
* Represents a geographical location.
*/
protected Location mCurrentLocation;
private Activity mActivity;
public double lat;
public double lon;
public GPSLocationListener(Activity activity) {
this.mActivity = activity;
// Kick off the process of building a GoogleApiClient and requesting the LocationServices API.
buildGoogleApiClient();
}
/**
* Builds a GoogleApiClient. Uses the {#code #addApi} method to request the
* LocationServices API.
*/
protected synchronized void buildGoogleApiClient() {
Log.i(TAG, "Building GoogleApiClient");
mGoogleApiClient = new GoogleApiClient.Builder(mActivity)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
createLocationRequest();
}
/**
* Sets up the location request. Android has two location request settings:
* {#code ACCESS_COARSE_LOCATION} and {#code ACCESS_FINE_LOCATION}. These settings control
* the accuracy of the current location. This sample uses ACCESS_FINE_LOCATION, as defined in
* the AndroidManifest.xml.
* <p/>
* When the ACCESS_FINE_LOCATION setting is specified, combined with a fast update
* interval (5 seconds), the Fused Navigation Provider API returns location updates that are
* accurate to within a few feet.
* <p/>
* These settings are appropriate for mapping applications that show real-time location
* updates.
*/
protected void createLocationRequest() {
mLocationRequest = new LocationRequest();
// Sets the desired interval for active location updates. This interval is
// inexact. You may not receive updates at all if no location sources are available, or
// you may receive them slower than requested. You may also receive updates faster than
// requested if other applications are requesting location at a faster interval.
mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
// Sets the fastest rate for active location updates. This interval is exact, and your
// application will never receive updates faster than this value.
mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
/**
* Requests location updates from the FusedLocationApi.
*/
public void startLocationUpdates(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this.mActivity, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this.mActivity, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this.mActivity, Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED) {
//return;
}else {
try {
ActivityCompat.requestPermissions(this.mActivity, new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.INTERNET},
LOCATION_RESQUEST);
}catch (Exception e){
e.printStackTrace();
}
}
// If the initial location was never previously requested, we use
// FusedLocationApi.getLastLocation() to get it. If it was previously requested, we store
// its value in the Bundle and check for it in onCreate().
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this);
}
}
/**
* Removes location updates from the FusedLocationApi.
*/
protected void stopLocationUpdates() {
// It is a good practice to remove location requests when the activity is in a paused or
// stopped state. Doing so helps battery performance and is especially
// recommended in applications that request frequent location updates.
// The final argument to {#code requestLocationUpdates()} is a LocationListener
// (http://developer.android.com/reference/com/google/android/gms/location/LocationListener.html).
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
}
public void onStart() {
mGoogleApiClient.connect();
//createLocationRequest();
// Within {#code onPause()}, we pause location updates, but leave the
// connection to GoogleApiClient intact. Here, we resume receiving
// location updates if the user has requested them.
}
public void onStop() {
// Stop location updates to save battery, but don't disconnect the GoogleApiClient object.
if (mGoogleApiClient.isConnected()) {
stopLocationUpdates();
mGoogleApiClient.disconnect();
}
}
/**
* Runs when a GoogleApiClient object successfully connects.
*/
#Override
public void onConnected(Bundle connectionHint) {
Log.i(TAG, "Connected to GoogleApiClient");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this.mActivity, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this.mActivity, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this.mActivity, Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "onConnected: Just empty if statement");
}else {
try {
ActivityCompat.requestPermissions(this.mActivity, new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.INTERNET},
LOCATION_RESQUEST);
}catch (Exception e){
e.printStackTrace();
}
}
mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (mCurrentLocation == null) {
mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
Log.i(TAG, "onConnected " + String.valueOf(mCurrentLocation));
}
}
}
/**
* Callback that fires when the location changes.
*/
#Override
public void onLocationChanged(Location location) {
mCurrentLocation = location;
Log.i(TAG, "onLocationChanged: " + String.valueOf(mCurrentLocation.getLatitude()));
Log.i(TAG, "onLocationChanged: " + String.valueOf(mCurrentLocation.getLongitude()));
}
#Override
public void onConnectionSuspended(int cause) {
// The connection to Google Play services was lost for some reason. We call connect() to
// attempt to re-establish the connection.
Log.i(TAG, "Connection suspended");
mGoogleApiClient.connect();
}
#Override
public void onConnectionFailed(ConnectionResult result) {
// Refer to the javadoc for ConnectionResult to see what error codes might be returned in
// onConnectionFailed.
Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = " + result.getErrorCode());
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mActivity.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
switch (requestCode){
case LOCATION_RESQUEST:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
startLocationUpdates(); // Calling this here is the only place that does not make the app crash
getCurrentLocation();
}else {
Log.i(TAG, "onRequestPermissionsResult: Need Permissions");
return;
}
break;
default:
break;
}
return;
}
public void getCurrentLocation(){
if (mCurrentLocation != null){
lat = mCurrentLocation.getLatitude();
lon = mCurrentLocation.getLongitude();
Log.i(TAG, "getCurrentLocation(): " + String.valueOf(mCurrentLocation.getLatitude()));
Log.i(TAG, "getCurrentLocation(): " + String.valueOf(mCurrentLocation.getLongitude()));
}
}
public GoogleApiClient getGoogleApiClient(){
return mGoogleApiClient;
}
}
Trying this out on my actual device with the GPS already enabled in the settings, I get some odd behavior. If I do not at all, call the startLocationUpdates() method or I call it in the onRequestPermissionsResult() method, the app launches fine and when I grant Device Location Permissions, the mCurrentLocation object is null, but does not crash. I stop the app and then launch again, and the mCurrentLocation has latitude and longitude coordinates that I can see in the logcat, which is what I am wanting in the first place, but they only hold any values, after the second launch. Now if I uninstall the app and try to call the startLocationUpdates() method anywhere, the app crashes upon launch with the error:
Client must have ACCESS_FINE_LOCATION permission to request PRIORITY_HIGH_ACCURACY locations.
But I am checking for permissions in the onConnected() method and also in the startLocationUpdates() method as well, although I do not think this is correct but this is the only way that I can get Android Studio to not underline the
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this);
statement in red with a Permissions warning.
Here is the Activity where I am trying to get the location updates with the mapItBtnRespond() method.
package com.example.bigdaddy.as_built_weldmapper;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;
import com.example.bigdaddy.as_built_weldmapper.utilities.BendHelper;
import com.example.bigdaddy.as_built_weldmapper.utilities.GPSLocationListener;
public class SagActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener,
MajorButtonFragment.OnFragmentInteractionListener, Communicator{
/* Using this to insert into the Bend Direction field. */
public static String SAG_DIRECTION = "SAG";
/* This spinner holds the bend types */
Spinner mSagBendTypesSpinner;
/* Using this string to collect what was selected for the spinner type */
private String mBendTypeSpinnerVal;
/* All the EditText for the Activity */
private EditText mSagGpsShotEt;
private EditText mSagExistingGpsEt;
private EditText mSagCoverEt;
private EditText mSagDegreeEt;
private EditText mSagDistanceFromEt;
private EditText mSagNotesEt;
private EditText mSagOccupyIdEt;
private EditText mSagStationNumEt;
private GPSLocationListener mGPSLocationListener;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sag);
mGPSLocationListener = new GPSLocationListener(SagActivity.this);
/* checking if the MajorButtonFragment is null */
if (findViewById(R.id.majorButtonFragment) != null) {
if (savedInstanceState != null) {
return;
}
}
/* Referencing the spinner and setting the itemsSelectedListener */
mSagBendTypesSpinner = (Spinner) findViewById(R.id.bend_types_spinner);
mSagBendTypesSpinner.setOnItemSelectedListener((AdapterView.OnItemSelectedListener) this);
/* Create an ArrayAdapter using the string array and a default spinner layout */
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
R.array.bend_types_array, android.R.layout.simple_spinner_item);
/* Specify the layout to use when the list of choices appears */
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
/* Apply the adapter to the spinner */
mSagBendTypesSpinner.setAdapter(adapter);
/* Referencing and calling all the EditText for the Activity */
mSagGpsShotEt = (EditText) findViewById(R.id.eTextGpsShotForSag);
mSagExistingGpsEt = (EditText) findViewById(R.id.eTextExistGradeForSag);
mSagCoverEt = (EditText) findViewById(R.id.eTextCoverForSag);
mSagDegreeEt = (EditText) findViewById(R.id.eTextDegreeForSag);
mSagDistanceFromEt = (EditText) findViewById(R.id.eTextDistanceFromForSag);
mSagNotesEt = (EditText) findViewById(R.id.eTextNotesForSagActivity);
mSagOccupyIdEt = (EditText) findViewById(R.id.eTextJointIdSagActivity);
mSagStationNumEt = (EditText) findViewById(R.id.eTextStationNumSagActivity);
} /*onCreate() ends here.*/
#Override
protected void onStart() {
super.onStart();
Log.i("SagActivity", "onStart: ");
/* Starting the location listener here (GoogleApiClient) */
if (mGPSLocationListener != null){
mGPSLocationListener.onStart();
}
}
#Override
protected void onStop() {
super.onStop();
Log.i("SagActivity", "onStop: ");
/* Stopping the location listener here (GoogleApiClient) */
if (mGPSLocationListener != null){
mGPSLocationListener.onStop();
}
}
#Override
public void onLocalVoiceInteractionStopped() {
super.onLocalVoiceInteractionStopped();
}
#Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
mBendTypeSpinnerVal = mSagBendTypesSpinner.getSelectedItem().toString();
}
#Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
#Override
public void exitBtnRespond() {
}
/**
* This overridden method comes from the Communicator Interface and is used globally in all
* Activities that implement it to Store (write) a transaction to the database.
* The utility class saveAndInsertBend() method is invoked here.
*/
#Override
public void storeBtnRespond() {
BendHelper.saveAndInsertBend(SagActivity.this, SAG_DIRECTION, mBendTypeSpinnerVal, mSagStationNumEt,
mSagOccupyIdEt, mSagDegreeEt, mSagDistanceFromEt, mSagGpsShotEt, mSagExistingGpsEt,
mSagCoverEt, mSagNotesEt);
}
#Override
public void mapItBtnRespond() {
Toast.makeText(this, "MapItBtn clicked in SagActivity",Toast.LENGTH_LONG).show();
mGPSLocationListener.getCurrentLocation();
Log.i("SagActivity", "mapItBtnRespond: " + String.valueOf(mGPSLocationListener.lat));
Log.i("SagActivity", "mapItBtnRespond: " + String.valueOf(mGPSLocationListener.lon));
}
#Override
public void onFragmentInteraction() {
}
}
What am I doing wrong here with all of this? Any help would be greatly appreciated as I am confused with the Permissions apparently. Thanks so much for any guidance.
You are asking for FINE_LOCATION permission in onConnected(), but onConnected is called when GPS is connected. That's the reason you are getting permission error.
You should remove this:
mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
From onConnected(). Add this to getCurrentLocation() with permission request:
public void getCurrentLocation(){
//Request permission here
mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (mCurrentLocation != null){
lat = mCurrentLocation.getLatitude();
lon = mCurrentLocation.getLongitude();
Log.i(TAG, "getCurrentLocation(): " + String.valueOf(mCurrentLocation.getLatitude()));
Log.i(TAG, "getCurrentLocation(): " + String.valueOf(mCurrentLocation.getLongitude()));
}
}
Then also call this method in onConnected():
#Override
public void onConnected(Bundle connectionHint) {
Log.i(TAG, "Connected to GoogleApiClient");
getCurrentLocation();
}
I'm developing a Android app and I'm still green in this area.
In my app I'm using the FusedLocationApi to get both the last know location and make Location update requests and I got a problem where, even with both Location and WiFi activated on Android Studio emulator and Samsung Galaxy S5, I never got the LocationListener's onLocationChanged() called.
Here is my code:
package com.trackit.app.locationupdatetest;
import android.content.pm.PackageManager;
import android.location.Location;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
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.MarkerOptions;
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
private static final String TAG = MapsActivity.class.getSimpleName();
public static final String STARTING_POSITION = "Rio de Janeiro"; //Application's default position
public static final int MY_PERMISSIONS_REQUEST_FINE_LOCATION = 1; //Fine location permission
private GoogleMap mMap;
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
private MapsActivity mActivity;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
(findViewById(R.id.Location)).setEnabled(false);
(findViewById(R.id.StreetView)).setEnabled(false);
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
if (mGoogleApiClient == null)
buildGoogleAPIClient();
//Create and configure a Location Request object to used while looking for location updates
if(mLocationRequest == null)
buildLocationRequest();
mActivity = this;
}
private synchronized void buildGoogleAPIClient() {
//Configuring the Google API client before connect
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
private synchronized void buildLocationRequest() {
mLocationRequest = LocationRequest.create()
.setInterval(10000)
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setFastestInterval(1000)
.setSmallestDisplacement(1);
}
#Override
protected void onStart() {
super.onStart();
mGoogleApiClient.connect();
}
/**
* This method is called when the Activity is no longer visible to the user.
*/
#Override
protected void onStop() {
if(mGoogleApiClient.isConnected())
mGoogleApiClient.disconnect();
super.onStop();
}
/**
* This callback is triggered when the map is ready to be used.
*/
#Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
// Add a marker in Sydney and move the camera
LatLng sydney = new LatLng(-34, 151);
mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
}
public void buttonPressed(View buttonPressed){
displayUserLocation();
}
#Override
public void onConnected(#Nullable Bundle bundle) {
(findViewById(R.id.Location)).setEnabled(true);
(findViewById(R.id.StreetView)).setEnabled(true);
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
Log.e(TAG,"onConnectionFailed:"+connectionResult.getErrorCode()+","+connectionResult.getErrorMessage());
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String permissions[], #NonNull
int[] grantResults) {
//Process the user permission's response
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_FINE_LOCATION:
processLocationPermissionResult(grantResults);
break;
default:
}
}
public void processLocationPermissionResult(#NonNull int grantResults[]) {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED)
displayLocation();
else {} // permission denied, boo! Disable the functionality that depends on this permission.
}
public void displayUserLocation(){
//Verify if the app have permission to access user's location
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.
ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.
permission.ACCESS_FINE_LOCATION)) {
//Asks the permission to access the user's location
Toast.makeText(this, "This app needs to access your location. " +
"Allow the app to access it?", Toast.LENGTH_LONG).show();
} else {
ActivityCompat.requestPermissions(this,
new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
MapsActivity.MY_PERMISSIONS_REQUEST_FINE_LOCATION);
}
} else displayLocation(); //If the permission was granted
}
public void displayLocation() throws SecurityException{
Location lastLocation = LocationServices.FusedLocationApi.
getLastLocation(mGoogleApiClient);
if(lastLocation != null) {
Log.e(TAG, "!!!!!!");
LatLng position = new LatLng(lastLocation.getLatitude(), lastLocation.
getLongitude());
mMap.addMarker(new MarkerOptions().position(position).title("Marker in " +
MapsActivity.STARTING_POSITION));
mMap.moveCamera(CameraUpdateFactory.newLatLng(position));
} else{
Log.e(TAG, "??????");
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
mLocationRequest, this);
}
}
#Override
public void onLocationChanged(Location location) {
if(location.getAccuracy() < 10 && location.getSpeed() < 55.55555555555556){
Log.e(TAG, "onLocationChanged() started");
LatLng position = new LatLng(location.getLatitude(), location.getLongitude());
mMap.addMarker(new MarkerOptions().position(position).title("Marker in " +
MapsActivity.STARTING_POSITION));
mMap.moveCamera(CameraUpdateFactory.newLatLng(position));
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
Log.e(TAG, "onLocationChanged() terminated");
}
/*else{
//Continue listening for a more accurate location
}*/
}
}
Below are both my AndroidManifest.xml and my gradle:
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.trackit.app.locationupdatetest">
<!--
The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
Google Maps Android API v2, but you must specify either coarse or fine
location permissions for the 'MyLocation' functionality.
-->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature
android:glEsVersion="0x00020000"
android:required="true"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<!--
The API key for Google Maps-based APIs is defined as a string resource.
(See the file "res/values/google_maps_api.xml").
Note that the API key is linked to the encryption key used to sign the APK.
You need a different API key for each encryption key, including the release key that is used to
sign the APK for publishing.
You can define the keys for the debug and release targets in src/debug/ and src/release/.
-->
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="#string/google_maps_key" />
<activity
android:name=".MapsActivity"
android:label="#string/title_activity_maps">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "24.0.1"
defaultConfig {
applicationId "com.trackit.app.locationupdatetest"
minSdkVersion 21
targetSdkVersion 24
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.2.0'
compile 'com.google.android.gms:play-services:9.4.0'
}
I'v searched a lot this week but I've got no helpful answer yet =s
These are some of the threads I've searched (I don't want to look like a lazy guy, because I'm not, I'm just stuck)
onLocationChanged does not get called using fusedLocationAPI.requestLocationUpdates
Unable to get location updates
onLocationChanged not called on some devices
on locaton changed never gets called in android google client api
onLocationChanged isn't being called
Can someone point my errors? =S
I'll venture this answer... if it's helpful, great... if not, sorry...
I have a similar Activity that uses the FusedLocationprovider.
I'm no expert, and can't say WHY yours is not working, but mine is working, and I've noticed the following differences:
YOUR CODE:
private synchronized void buildGoogleAPIClient() {
//Configuring the Google API client before connect
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
private synchronized void buildLocationRequest() {
mLocationRequest = LocationRequest.create()
.setInterval(10000)
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setFastestInterval(1000)
.setSmallestDisplacement(1);
}
MY CODE:
protected synchronized void buildGoogleApiClient() {
this.mGoogleApiClient = new GoogleApiClient.Builder(this).
addConnectionCallbacks(this).
addOnConnectionFailedListener(this).
addApi(LocationServices.API).build();
createLocationRequest();
}
protected void createLocationRequest() {
this.mLocationRequest = new LocationRequest();
this.mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
this.mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
this.mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
Differences:
1) You put:
if(mLocationRequest == null)
buildLocationRequest();
in your onCreate method. Is this necessary? Wouldn't mLocationRequest always be null in onCreate()?
2) Your methods are private, mine protected
3) your createLocationrequest() is labeled as 'synchronized' ... could is be not working because of this?
4) you use the setSmallestDisplacement() method. I have seen posts that say this method doesn't work properly and results in onLocationChanged() not being called.
5) You instantiate your LocationRequest with: 'LocationRequest.create()' and not "new LocationRequest()' could this be the problem?
Does this help?
Here is an alternate answer.
There is one line of code that is vital in order for onlocationChanged to ever be called:
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
mLocationRequest, this);
And in your case, this code resides very deeply in your code with many conditions needing to be met for this to be reached:
Conditions you require:
security exception not thrown
last location == null
(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
!(ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)
button pushed
I would consider putting MORE LOGS in your code in order to determine that these conditions are in fact being met... trace backwards from:
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
mLocationRequest, this);
PS - I see you're using the Android N permission model. I have yet to put my toes in that water due to the confusion it seems to create... I just set my target SDK = 22. :)
Each time I run the application, my SecurityException gets thrown and the error from the debugger reads as so:
java.lang.SecurityException: "gps" location provider requires ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission.
This seems like a simple mistake, however, my manifest file is completely correct. Here it is, and here is my MapActivity code as well:
<?xml version="1.0" encoding="utf-8"?>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<uses-permission android:name="com.dev.cromer.jason.coverme.permission.MAPS_RECEIVE" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value= "#string/google_maps_key" />
<activity
android:name=".MapActivity"
android:label="#string/title_activity_map" >
</activity>
</application>
My Activity:
package com.dev.cromer.jason.coverme;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
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.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
public class MapActivity extends FragmentActivity implements LocationListener {
private GoogleMap mMap; // Might be null if Google Play services APK is not available.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
setUpMapIfNeeded();
}
#Override
protected void onResume() {
super.onResume();
setUpMapIfNeeded();
}
private void setUpMapIfNeeded() {
// 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) {
//mMap.setMyLocationEnabled(true);
//mMap.setOnMyLocationChangeListener(this);
setUpMap();
}
}
}
private void setUpMap() {
mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));
mMap.setMyLocationEnabled(true);
LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
try {
Location myLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (myLocation != null) {
Log.d("TAG", "Not null");
}
else {
Log.d("TAG", "NULL");
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
}
}
catch (SecurityException se) {
Log.d("TAG", "SE CAUGHT");
se.printStackTrace();
}
}
#Override
public void onLocationChanged(Location location) {
Log.d("CHANGED", "LOCATION UPDATED");
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
}
ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION, and WRITE_EXTERNAL_STORAGE are all part of the Android 6.0 runtime permission system. In addition to having them in the manifest as you do, you also have to request them from the user at runtime (using requestPermissions()) and see if you have them (using checkSelfPermission()).
One workaround in the short term is to drop your targetSdkVersion below 23.
But, eventually, you will want to update your app to use the runtime permission system.
For example, this activity works with five permissions. Four are runtime permissions, though it is presently only handling three (I wrote it before WRITE_EXTERNAL_STORAGE was added to the runtime permission roster).
/***
Copyright (c) 2015 CommonsWare, LLC
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.
From _The Busy Coder's Guide to Android Development_
https://commonsware.com/Android
*/
package com.commonsware.android.permmonger;
import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
private static final String[] INITIAL_PERMS={
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.READ_CONTACTS
};
private static final String[] CAMERA_PERMS={
Manifest.permission.CAMERA
};
private static final String[] CONTACTS_PERMS={
Manifest.permission.READ_CONTACTS
};
private static final String[] LOCATION_PERMS={
Manifest.permission.ACCESS_FINE_LOCATION
};
private static final int INITIAL_REQUEST=1337;
private static final int CAMERA_REQUEST=INITIAL_REQUEST+1;
private static final int CONTACTS_REQUEST=INITIAL_REQUEST+2;
private static final int LOCATION_REQUEST=INITIAL_REQUEST+3;
private TextView location;
private TextView camera;
private TextView internet;
private TextView contacts;
private TextView storage;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
location=(TextView)findViewById(R.id.location_value);
camera=(TextView)findViewById(R.id.camera_value);
internet=(TextView)findViewById(R.id.internet_value);
contacts=(TextView)findViewById(R.id.contacts_value);
storage=(TextView)findViewById(R.id.storage_value);
if (!canAccessLocation() || !canAccessContacts()) {
requestPermissions(INITIAL_PERMS, INITIAL_REQUEST);
}
}
#Override
protected void onResume() {
super.onResume();
updateTable();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.actions, menu);
return(super.onCreateOptionsMenu(menu));
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case R.id.camera:
if (canAccessCamera()) {
doCameraThing();
}
else {
requestPermissions(CAMERA_PERMS, CAMERA_REQUEST);
}
return(true);
case R.id.contacts:
if (canAccessContacts()) {
doContactsThing();
}
else {
requestPermissions(CONTACTS_PERMS, CONTACTS_REQUEST);
}
return(true);
case R.id.location:
if (canAccessLocation()) {
doLocationThing();
}
else {
requestPermissions(LOCATION_PERMS, LOCATION_REQUEST);
}
return(true);
}
return(super.onOptionsItemSelected(item));
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
updateTable();
switch(requestCode) {
case CAMERA_REQUEST:
if (canAccessCamera()) {
doCameraThing();
}
else {
bzzzt();
}
break;
case CONTACTS_REQUEST:
if (canAccessContacts()) {
doContactsThing();
}
else {
bzzzt();
}
break;
case LOCATION_REQUEST:
if (canAccessLocation()) {
doLocationThing();
}
else {
bzzzt();
}
break;
}
}
private void updateTable() {
location.setText(String.valueOf(canAccessLocation()));
camera.setText(String.valueOf(canAccessCamera()));
internet.setText(String.valueOf(hasPermission(Manifest.permission.INTERNET)));
contacts.setText(String.valueOf(canAccessContacts()));
storage.setText(String.valueOf(hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)));
}
private boolean canAccessLocation() {
return(hasPermission(Manifest.permission.ACCESS_FINE_LOCATION));
}
private boolean canAccessCamera() {
return(hasPermission(Manifest.permission.CAMERA));
}
private boolean canAccessContacts() {
return(hasPermission(Manifest.permission.READ_CONTACTS));
}
private boolean hasPermission(String perm) {
return(PackageManager.PERMISSION_GRANTED==checkSelfPermission(perm));
}
private void bzzzt() {
Toast.makeText(this, R.string.toast_bzzzt, Toast.LENGTH_LONG).show();
}
private void doCameraThing() {
Toast.makeText(this, R.string.toast_camera, Toast.LENGTH_SHORT).show();
}
private void doContactsThing() {
Toast.makeText(this, R.string.toast_contacts, Toast.LENGTH_SHORT).show();
}
private void doLocationThing() {
Toast.makeText(this, R.string.toast_location, Toast.LENGTH_SHORT).show();
}
}
(from this sample project)
For the requestPermissions() function, should the parameters just be "ACCESS_COARSE_LOCATION"? Or should I include the full name "android.permission.ACCESS_COARSE_LOCATION"?
I would use the constants defined on Manifest.permission, as shown above.
Also, what is the request code?
That will be passed back to you as the first parameter to onRequestPermissionsResult(), so you can tell one requestPermissions() call from another.
My simple solution is this
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) ==
PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) ==
PackageManager.PERMISSION_GRANTED) {
googleMap.setMyLocationEnabled(true);
googleMap.getUiSettings().setMyLocationButtonEnabled(true);
} else {
Toast.makeText(this, R.string.error_permission_map, Toast.LENGTH_LONG).show();
}
or you can open permission dialog in else like this
} else {
ActivityCompat.requestPermissions(this, new String[] {
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION },
TAG_CODE_PERMISSION_LOCATION);
}
CAUSE: "Beginning in Android 6.0 (API level 23), users grant permissions to apps while the app is running, not when they install the app." In this case, "ACCESS_FINE_LOCATION" is a "dangerous permission and for that reason, you get this 'java.lang.SecurityException: "gps" location provider requires ACCESS_FINE_LOCATION permission.' error (https://developer.android.com/training/permissions/requesting.html).
SOLUTION: Implementing the code provided at https://developer.android.com/training/permissions/requesting.html under the "Request the permissions you need" and "Handle the permissions request response" headings.