I implemented an app that uses GPS location and so I am dealing with the permissions.
The app runs correctly, all locations are correctly performed.
When I install the application, the first fragment opens correctly and shows to user the request for GPS permission.
Behind that, I added also a dialog that requests to set ON the GPS in order to start the application.
If before first start (after installation) the GPS is set to OFF, when the app starts, it is shown the first fragment on background, the request for GPS permission and then the request to set ON the GPS inside settings.
If before first start (after installation) the GPS is set to ON, when the app starts, it is shown the first fragment on background, the request for GPS permission and app crashes maintaining the request for GPS permission active. If I press OK, I restart the application and all works properly.
Why this issue when GPS on settings is set to ON?
Here below a part of code:
On first fragment, onCreateView I wrote the following code for GPS permission:
mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
.addApi(LocationServices.API)
.addOnConnectionFailedListener(this)
.addConnectionCallbacks(this)
.build();
if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION)) {
showAlert(getString(R.string.PermessoDisp), getString(R.string.PermessoDispTitle));
} else {
ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
MY_PERMISSION_REQUEST_LOCATION);
}
} else {
}
In the onStart() I have:
#Override
public void onStart() {
super.onStart();
if (isLocationEnabled()) {
mGoogleApiClient.connect();
} else {
showAlertGPSSettings();
}
}
where showAlertGPSSettings() is the method that ask user to set the GPS ON in order to use app.
And I have also:
#Override
public void onConnected(#Nullable Bundle bundle) {
locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(1000);
locationRequest.setFastestInterval(500);
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, locationRequest, this);
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
switch (requestCode) {
case MY_PERMISSION_REQUEST_LOCATION:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
} else {
}
}
}
When the app crashes (at first start when GPS is set to ON), I have the following error:
java.lang.SecurityException: Client must have ACCESS_FINE_LOCATION permission to request PRIORITY_HIGH_ACCURACY locations.
The row where the error is located, is inside the onConnected method:
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, locationRequest, this);
Any suggestion?
Did you add permission in your manifest?
android.permission.ACCESS_COARSE_LOCATION or android.permission.ACCESS_FINE_LOCATION.
This permissions are needed.
Related
Creating my first app,
Looking for locations using the GoogleApi and the callback is working as intended. I use the location and it gets put into a database, onDestroy() I am closing the DB connection. But now I occasionally get a stack trace saying
java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase
I added a removeLocationUpdates() right before the closeDB call, and that MOSTLY solved the issue, but it still happens from time to time. It appears that I may still be getting a rogue callback after removeLocationUpdates and closeDB have been called.
Is this a typical delay? could a 500ms delay between the removeLocUpdates and the closeDB fix the issue? I'd imagine there'd be a better way, or that maybe I am simply doing something wrong with the removeLocUpdates. But the stack trace seems pretty clear that I am getting a callback as the trace shows
onLocationChanged
part way down the stack
EDIT to add Code:
onCreate():
openDB();
if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
//End Google Location API implementation
locationListener = new com.google.android.gms.location.LocationListener() {
#Override
public void onLocationChanged(Location location) {
//newestLocation = location;
//bestLocation = bestLocation();
//todo maybe come back and reimplement bestLocation() if needed
bestLocation = location;
getCode(null);
saveCode(null);
}};
onDestroy()
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
if (mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, locationListener);
}
}
closeDB();
onConnected()
public void onConnected(#Nullable Bundle bundle) {
CharSequence text = "Google API Connected";
Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show();
LocationRequest locationRequest = new LocationRequest()
.setFastestInterval(500)
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setMaxWaitTime(3000)
.setInterval(1000);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.ACCESS_FINE_LOCATION},10);
return;
}else {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, locationRequest, locationListener);
}
return;
}
I appreciate your help on the matter
I am trying to get the current location coordinates in longitude and latitude. Here is my code so far:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
MyLocationListener myLocationListener = new MyLocationListener();
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, myLocationListener);
}
}
and this class too:
public class MyLocationListener implements LocationListener {
private static final String TAG = "COORDINATES: ";
#Override
public void onLocationChanged(Location location) {
if(location != null){
Log.e(TAG, "Latitude: " + location.getLatitude());
Log.e(TAG, "Longitude: " + location.getLongitude());
}
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
}
When I run the app in emulator, I don't get any log message with coordinates. Any suggestions?
The best approach is to use the latest FusedLocationApi provided by Google Play Services library.
If you want to use the old approach, that is fine but you might not get very accurate results.
Either way, make sure you have enabled internet permission, either COARSE_LOCATION or FINE_LOCATION or both in your android manifest.
Also, if you have android 6.0, remember you must request runtime permissions or it won't work for you!
I answered a similar question yesterday and you can find here -which works;
There is also a link to a sample code for FusedLocationApi here.
I hope this helps you and good luck!
UPDATE
You can add Google Play Services to your build.gradle like this:
compile 'com.google.android.gms:play-services:9.2.'
But if you are only interested in one service like location, you can be specific:
compile 'com.google.android.gms:play-services-location:9.2.1'
NOTE
I would highly discourage you from getting user location on your UI thread because it will destroy user experience in the long run! Use a separate thread!!
You have to mimic the location in Emulator. You can do that by accessing the Android Device Manager and Select Emulator Control tab and send the locations to Emulator.
I'm trying to use the DriveApi in order to create some folders and upload a text file with some data for a user.
I've tried implementing the quick-start guide from (link), but it has a few fundamental issues:
The api gets connected at onResume so the user will get prompted to give access to the app immediately after he opens the app which is confusing and scary.
If you deny or press the back button at the consent screen, the onResume method will get called again and the consent screen will be shown one more time, leading to an infinite loop.
I would rather like to connect the api when the user actually needs to store data so that will make more sense to the user. I tried doing it like this:
ResultCallback<DriveFolder.DriveFolderResult> folderCreatedCallback = new
ResultCallback<DriveFolder.DriveFolderResult>() {
#Override
public void onResult(#NonNull DriveFolder.DriveFolderResult result) {
clearCurrentAction();
if (!result.getStatus().isSuccess()) {
Log.e(TAG, "Error while trying to create the folder");
return;
}
Log.d(TAG, "Created a folder: " + result.getDriveFolder().getDriveId());
}
};
public DriveApiHelper(GoogleApiClient mGoogleApiClient) {
this.mGoogleApiClient = mGoogleApiClient;
}
public void createBackupsFolder() {
currentAction = DriveActions.CREATING_FOLDER;
if (mGoogleApiClient.isConnected()) {
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("test").build();
Drive.DriveApi.getRootFolder(mGoogleApiClient).createFolder(
mGoogleApiClient, changeSet).setResultCallback(folderCreatedCallback);
} else {
mGoogleApiClient.connect();
}
}
and this is how my onResume and onConnected methods look like:
#Override
protected void onResume() {
super.onResume();
if (mGoogleApiClient == null) {
// Create the API client and bind it to an instance variable.
// We use this instance as the callback for connection and connection
// failures.
// Since no account name is passed, the user is prompted to choose.
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Drive.API)
.addScope(Drive.SCOPE_FILE)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
mDriveHelper = new DriveApiHelper(mGoogleApiClient);
}
//Log.d(TAG, mDriveHelper.getCurrentAction() + "");
Log.d("test", "Connected " + mGoogleApiClient.isConnected());
}
#Override
public void onConnected(Bundle connectionHint) {
Log.i(TAG, "API client connected.");
switch (mDriveHelper.getCurrentAction()) { //resume the last action
case CREATING_FOLDER:
mDriveHelper.createBackupsFolder();
break;
}
}
I was hoping that keeping a reference of what the user tried to do when the api was asked to connect, I can resume that action after the api successfully connected. This is the closest implementation I've got to fit my needs, but after actually clicking the 'Allow' button from the consent screen none of the api callbacks gets called (onConnected, onConnectionFailed).
I actually need to call the connect method one more time in order to get connected and also fire the onConnected successfully resuming the users' action.
Turns out that I forgot about overriding onActivityResult (it wasn't mentioned in the documentation at that time and I don't know if they included it now)
Just add:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
if (requestCode == REQUEST_CODE_RESOLUTION) {
mGoogleApiClient.connect();
}
} else {
if (requestCode == REQUEST_CODE_RESOLUTION) {
mDriveHelper.dismissStatusDialog();
}
}
}
I am trying to implement SplashScreenActivity, which will request all necessary permissions and then redirect to the MainActivity:
public class SplashScreenActivity extends Activity {
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.splash_screen);
try {
PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), 0);
((TextView) findViewById(R.id.versionView)).setText(info.versionName);
} catch (Exception e) {
throw new IllegalStateException(e);
}
if (ActivityCompat.checkSelfPermission(this, CAMERA) != PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{READ_PHONE_STATE, CAMERA}, 200);
} else {
onPermissionsReady();
}
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 200) {
onPermissionsReady();
}
}
private void onPermissionsReady() {
new Fork() {
#Override public void run() {
ApplicationContext.getInstance(SplashScreenActivity.this);
startActivity(new Intent(SplashScreenActivity.this, MainActivity.class));
}
};
}
}
I have two issues with it:
The splash screen design does not show before the Permission request dialog and the screen stays from the android background with application icons.
When you agree with the permissions, the onRequestPermissionsResult is NEVER called and the application ends.
EDIT: I created a sample application here: https://github.com/knyttl/TestApp – it demonstrates both two issues.
EDIT2: This is what happens when i agree/disagree with the permissions requests - the application just ends: https://www.youtube.com/watch?v=lhvhXcEJxLw&feature=youtu.be
You should extend from AppCompatActivity.
Try to change the line:
if (ActivityCompat.checkSelfPermission(this, CAMERA) != PERMISSION_GRANTED
to
`if (ActivityCompat.checkSelfPermission(this, Manifest.Permission.CAMERA) != PERMISSION_GRANTED`
Move the code for checking permission to onResume. Or leave it in onCreate but delay it, for example with Handler
I found out the problem: the activity has noHistory=true, which leads to killing the application as described here:
Requesting Android M permissions from Activity with noHistory="true" and/or showOnLockScreen="true"
I'm attempting to request ACCESS_FINE_LOCATION permissions in order to get the user's current location.
My logging indicates that my app does not currently have this permission when querying ContextCompat.checkSelfPermission(), but when calling ActivityCompat.requestPermissions() nothing is displayed.
My Google map code (implementing OnMapReadyCallback and ActivityCompat.OnRequestPermissionsResultCallback()) is in a FragmentActivity.
I have managed to get the requestPermissions() function working successfully in other Activities in the app, it's just the one with the Google map. It doesn't work when placed in the onCreate() method of the Activity, or in onMapReady() (where it needs to go).
if(ContextCompat.checkSelfPermission(LocationActivity.this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "not granted");
final String[] permissions = new String[] {android.Manifest.permission.ACCESS_FINE_LOCATION};
if(ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.ACCESS_FINE_LOCATION)) {
Log.d(TAG, "rationale");
// Explain to the user why permission is required, then request again
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("We need permissions")
.setCancelable(false)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
ActivityCompat.requestPermissions(LocationActivity.this, permissions, 1);
}
});
AlertDialog alert = builder.create();
alert.show();
} else {
Log.d(TAG, "request" + android.Manifest.permission.ACCESS_FINE_LOCATION);
// If permission has not been denied before, request the permission
ActivityCompat.requestPermissions(LocationActivity.this, permissions, 1);
}
} else {
Log.d(TAG, "granted");
}
Any ideas? Is it something to do with my Activity's class (FragmentActivity), or possible the Google map calling the permissions request asynchronously?
After stripping out my class completely, and it still not working, I realised that this Activity is being instantiated using a TabHost.
When I stop using the TabHost, the prompt is displayed successfully. I guess TabHosts are not supported by the new permissions prompts - is this a bug?
Same problem as App requests aren't showing up
I ended up creating a PermissionsRequestActivity which handles the permission request and response on behalf of my TabHost, then exits (pass the requested permission information in through the Intent extras Bundle).
It passes back the response to the request as a Broadcast, which is picked up by my TabHost.
Bit of a hack but works OK!
Check that you have already added the requested permission in Android's manifest file like before Android M, only then you will get expected behaviour.
Add the permission to your manifest so you can request it via
ActivityCompat.requestPermissions:
<uses-permission android:name="android.permission. ACCESS_FINE_LOCATION" />
I've faced the same problem on a project which use TabHost.
Basis on #Robin solution, I use the EventBus library for send a message from child activity to the TabActity.
EventBus : https://github.com/greenrobot/EventBus
Create an event object :
public class MessageEvent {
private String message;
public MessageEvent(String message){
this.message = message;
}
public String getMessage(){
return this.message;
}
}
In your main Activity :
private EventBus eventBus = EventBus.getDefault();
#Override
protected void onCreate(Bundle savedInstanceState) {
eventBus.register(this);
}
#Override
protected void onDestroy() {
eventBus.unregister(this);
super.onDestroy();
}
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
if (event.getMessage().equals("contacts")){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(android.Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainPage.this,new String[]{android.Manifest.permission.WRITE_CONTACTS}, 100 );
}
}
};
Set a different message for the permission your want to request.
In your child activity you can than post the adequate message :
EventBus.getDefault().post(new MessageEvent("contacts"));
Be aware of onRequestPermissionsResult callback and the request code ;)! It will only work in the main activity.