Unable to write file on Android 11 - FileNotFoundException - java

I've read all the relevant questions and answers, but I still can not open a file for writing with FileOutputStream. When I call
FileOutputStream fos = new FileOutputStream(newpathandfilename);
It throws a FileNotFoundException
What I did so far:
AndroidManifest.xml has:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:remove="android:maxSdkVersion"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:remove="android:maxSdkVersion"/>
Also application tag has android:requestLegacyExternalStorage set to true.
At runtime I check the permission:
private void showExplanation(String title,
String message,
final String permission,
final int permissionRequestCode) {
AlertDialog.Builder builder = new AlertDialog.Builder(this.getContext());
builder.setTitle(title)
.setMessage(message)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
requestPermission(permission, permissionRequestCode);
}
});
builder.create().show();
}
private void requestPermission(String permissionName, int permissionRequestCode) {
ActivityCompat.requestPermissions(this.getActivity(),
new String[]{permissionName}, permissionRequestCode);
}
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case 1: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
Toast.makeText(this.getActivity(), "Permission denied to read your External storage", Toast.LENGTH_SHORT).show();
}
return;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
Then:
if (ActivityCompat.checkSelfPermission(this.getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
Log.d("Granted","Granted");
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(this.getActivity(),
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
showExplanation("Permission required", "Permission required", Manifest.permission.WRITE_EXTERNAL_STORAGE, 1);
} else {
requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, 1);
}
}
It writes the access is granted already.
The path is /storage/emulated/0/myApp/NewFile20220520_222112.jpg.
The exception is: open failed: EPERM (Operation not permitted)
compileSdkVersion 30
targetSdkVersion 30
Device: API level 30
Any idea what is missing?

Related

Unable to open captured image: open failed: EACCES (Permission denied)

I am working on this project for an app that can capture an image and then perform Text Recognition on the image using Google ML Toolkit. It is for my personal use only so I don't really care about "respecting the user's choice not to allow permissions", if I could I would ask them all at startup instead of asking when I need them.
Right now I basically request Camera Access at startup (which works fine) and then I show the camera feed to the user in the center of the screen. Then, the user can click on a "capture" button in order to capture the current frame and save it to a certain path. Then, I would like to decode the file in that path into bitmap format, display it to the user and then use the ML Toolkit.
However, I get an error, which I think is a permission error from the READ_EXTERNAL_STORAGE permission. Since the error I get says "Unable to decode stream: java.io.FileNotFoundException: /mnt/sdcard/Pictures/CameraXPhotos/1642088145797.jpg: open failed: EACCES (Permission denied)"
From what I understood I have to manually request permissions. From Android Developers it is stated that: "Any app that declares the WRITE_EXTERNAL_STORAGE permission is implicitly granted this (READ_EXTERNAL_STORAGE) permission." So I theoretically just need to ask for the WRITE_EXTERNAL_STORAGE permission. So I do the following:
onCreate{
button_capture.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v)
{
checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, STORAGE_WRITE_PERMISSION_CODE);
capturePhoto();
}
});
}
public void checkPermission(String permission, int requestCode){
if (ContextCompat.checkSelfPermission(MainActivity.this, permission) == PackageManager.PERMISSION_DENIED) {
// Requesting the permission
ActivityCompat.requestPermissions(MainActivity.this, new String[] { permission }, requestCode);
}
else {
Toast.makeText(MainActivity.this, "Permission already granted", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults){
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == CAMERA_PERMISSION_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(MainActivity.this, "Camera Permission Granted", Toast.LENGTH_SHORT) .show();
} else {
Toast.makeText(MainActivity.this, "Camera Permission Denied", Toast.LENGTH_SHORT) .show();
}
}
else if (requestCode == STORAGE_WRITE_PERMISSION_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(MainActivity.this, "Storage Permission Granted", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "Storage Permission Denied", Toast.LENGTH_SHORT).show();
}
}
}
But once I click on the capture button, it doesn't ask for permissions and crashes with the error "open failed: EACCES (Permission denied)" because of the line in my code in the capturePhoto() method where I try to open a file from its absolute path. I don't really understand why, with methods checkPermission() and onRequestPermissionsResult() I am handling permissions correctly right?
Is it because the capturePhoto() method runs in parallel of the checkPermissions() method and therefore does not wait for the permissions to be given?
I hope my issue is understanble, this is my first question so if I did anything wrong in my question feel free to point it out to me!
Could it be that it is because in my checkPermission() method I check if the permission was denied but not if it was not granted in the first place?
EDIT: Thank you for your comments #blackapps. The code that lets the user capture a picture is the following:
private void capturePhoto() {
File photoDir = new File("/mnt/sdcard/Pictures/CameraXPhotos");
if(!photoDir.exists())
photoDir.mkdir();
Date date = new Date();
String timestamp = String.valueOf(date.getTime());
String photoFilePath = photoDir.getAbsolutePath() + "/" + timestamp + ".jpg";
File photoFile = new File(photoFilePath);
imageCapture.takePicture(
new ImageCapture.OutputFileOptions.Builder(photoFile).build(),
getExecutor(),
new ImageCapture.OnImageSavedCallback() {
#Override
public void onImageSaved(#NonNull ImageCapture.OutputFileResults outputFileResults) {
Toast.makeText(MainActivity.this, "Photo has been taken", Toast.LENGTH_SHORT).show();
}
#Override
public void onError(#NonNull ImageCaptureException exception) {
Toast.makeText(MainActivity.this, "Error taking photo: " + exception.getMessage(), Toast.LENGTH_SHORT).show();
}
}
);
Bitmap bitmap = BitmapFactory.decodeFile(photoFile.getAbsolutePath());
getTextFromImage(bitmap);
image_result.setImageBitmap(bitmap);
// return photoFilePath;
}

Marshmallow permissions and explanation

Could explain me plaese how i should explain to user permission
i work with Camera2API and i implement such snippet of code to ask permishion dinamicly
private void openCamera(int width, int height) {
setUpCameraOutputs(width, height);
CameraHelper.configureTransform(width, height, textureView, previewSize, getActivity());
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Time out waiting to lock camera opening.");
}
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) !=
PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(
getActivity(), new String[]{android.Manifest.permission.CAMERA},
MY_PERMISSIONS_REQUEST);
// MY_PERMISSIONS_REQUEST is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}
manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
}
}
according to google docks i should check if user deny permission before
there is method which return true of false
shouldShowRequestPermissionRationale();
and according google
This method returns true if the app has requested this permission previously and the user denied the request.
if i understand correctly according google comments inside this method implementation
// Show an explanation to the user asynchronously -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
And finally for example user deny my permission before and next time when he go to my screen with camera app should create the my castom pop-up with explanation "Please agree this permishion if you want to continue" and for example user agree this time and i should recall this method again according this
// sees the explanation, try again to request the permission.
but this method
shouldShowRequestPermissionRationale();
return to me true again, because it didn't know about the user intent to agree with permission.
Could you explain me please how to make it properly way? Maybe you have example?
Eventually i find a solution with help of #nuuneoi, thanks a lot!
And implement it like this
public void camera(View view) {
toCamera();
}
private void toCamera() {
if (!isCheckPermission()){
return;
}
if (isProcessWasFinish()) {
startActivity(new Intent(getApplicationContext(), CameraActivity.class));
overridePendingTransition(R.anim.open_next, R.anim.close_main);
} else {
startActivity(new Intent(getApplicationContext(), UserDataScreen.class));
overridePendingTransition(R.anim.open_next, R.anim.close_main);
}
}
private boolean isCheckPermission() {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) !=
PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
showMessageOKCancel("You need to allow access to Camera");
return false;
}
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.CAMERA},
MY_PERMISSIONS_REQUEST);
return false;
// MY_PERMISSIONS_REQUEST is an
// app-defined int constant. The callback method gets the
// result of the request.
}
return true;
}
private void showMessageOKCancel(String message) {
new AlertDialog.Builder(MainActivity.this)
.setMessage(message)
.setPositiveButton("OK", listener)
.setNegativeButton("Cancel", listener)
.create()
.show();
}
DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
final int BUTTON_NEGATIVE = -2;
final int BUTTON_POSITIVE = -1;
#Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case BUTTON_NEGATIVE:
// int which = -2
dialog.dismiss();
break;
case BUTTON_POSITIVE:
// int which = -1
ActivityCompat.requestPermissions(
MainActivity.this, new String[]{android.Manifest.permission.CAMERA},
MY_PERMISSIONS_REQUEST);
dialog.dismiss();
break;
}
}
};
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions,
#NonNull int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.e(MY_LOG, "Camera permission Granted");
// permission was granted, yay! Do the
// contacts-related task you need to do.
toCamera();
} else {
Log.e(MY_LOG, "Camera permission Denied");
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
}
default: {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
// other 'case' lines to check for other
// permissions this app might request
}
}
Hey you can use some of the libraries that are great for asking user permissions on app launch.
There's a great library to help you do that in Android :
Github link for the Permission dispatcher library
You can find the usage examples of the permission dispatcher library here
You can also check these libraries :
App-Runtime-Permissions-Android
Assent
MarshmallowPermissionManager

Downloading files and asking for storge permission in android

In my android app. When someone tries to download file, it should ask for storage permission.. if granted, it should start downloading the file.
Next time if permission already granted, just start new download.
here is the part of my code...
public void downloadFile(String url) {
Uri source = Uri.parse(url);
// Make a new request pointing to the download url
DownloadManager.Request request = new DownloadManager.Request(source);
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
String fileExtension = MimeTypeMap.getFileExtensionFromUrl(url);
String fileName = URLUtil.guessFileName(url, null, fileExtension);
// save the file in the "Downloads" folder of SDCARD
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
// get download service and enqueue file
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
}
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.contains("?dl=1")) {
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
downloadFile(url);
} else {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_WRITE_EXTERNAL_STORAGE);
}
return true;
}
The problem with this code is:
When user deny the permission app crashes.
When user allow the permission for the first, time the download doesn't start immediately... user need to click again on the download link.
You need to implement the callback function ActivityCompat.OnRequestPermissionsResultCallback.
So first step is to change your class signature to something like this but be careful to extend the Activity that you need the permission results in, MainActivity is just an example:
public class MainActivity extends AppCompatActivity
implements ActivityCompat.OnRequestPermissionsResultCallback
Second step is to override the following function:
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
if( requestCode == REQUEST_WRITE_EXTERNAL_STORAGE)
{
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//Do something if the permission is granted
}
else
{
//Do something if the permission denied
}
}
}
Your shouldOverrideUrlLoading method always returns true. Is that the wanted behavior?
Try to add a permission request response code as below:
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[],
int[] grantResults) {
switch (requestCode) {
case REQUEST_WRITE_EXTERNAL_STORAGE: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// do your work
} else {
// permission denied
}
return;
}
}
}
Source: http://developer.android.com/training/permissions/requesting.html#perm-request

Android M permission of the tested READ_PHONE_STATE(dangerous permissions)

If the device is running Android 6.0 or higher when im trying to get phone number using getLine1Number():
java.lang.SecurityException: Requires READ_PHONE_STATE: Neither user 10184 nor current process has android.permission.READ_PHONE_STATE.
This is coming out.
I declared permission as :
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
In Android 6.0, you need to explicitly ask the user to grant the permissions. Just declaring it in the manifest isn't enough.
This article in the docs is a great place to start learning the new model, but I'll give a brief summary.
Every time you perform an action that requires a "dangerous permission," you need to check if the permission is currently granted, because the user can revoke it at any time.
This can be done with the checkSelfPermission method.
if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
// We do not have this permission. Let's ask the user
}
You can request the permission with the requestPermissions method, as such
ActivityCompat.requestPermissions(thisActivity, new String[]{Manifest.permission.READ_PHONE_STATE}, PERMISSION_READ_STATE);
Where PERMISSION_READ_STATE is a constant integer defined by you to check in the callback method later.
You will then override onRequestPermissionsResult in your activity and see if the permission was granted. If it was, you can go ahead and preform the dangerous action.
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_READ_STATE: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission granted!
// you may now do the action that requires this permission
} else {
// permission denied
}
return;
}
}
}
public class MainActivity extends AppCompatActivity {
TextView textView;
String device_unique_id,IMEI;
private static final int MY_PERMISSIONS_REQUEST_READ_PHONE_STATE = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView)findViewById(R.id.textView);
}
public void GetImei(View view)
{
loadIMEI();
}
public void loadIMEI() {
// Check if the READ_PHONE_STATE permission is already available.
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_PHONE_STATE)) {
// get_imei_data();
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_PHONE_STATE},
MY_PERMISSIONS_REQUEST_READ_PHONE_STATE);
}
} else {
TelephonyManager mngr = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
IMEI = mngr.getDeviceId();
device_unique_id = Settings.Secure.getString(this.getContentResolver(),
Settings.Secure.ANDROID_ID);
textView.setText(device_unique_id+"----"+mngr.getDeviceId());
// READ_PHONE_STATE permission is already been granted.
Toast.makeText(this,"Alredy granted",Toast.LENGTH_SHORT).show();
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions,#NonNull int[] grantResults) {
if (requestCode == MY_PERMISSIONS_REQUEST_READ_PHONE_STATE) {
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Toast.makeText(this,"Alredy DONE",Toast.LENGTH_SHORT).show();
TelephonyManager mngr = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
IMEI = mngr.getDeviceId();
device_unique_id = Settings.Secure.getString(this.getContentResolver(),Settings.Secure.ANDROID_ID);
textView.setText(device_unique_id+"----"+mngr.getDeviceId());
} else {
Toast.makeText(this,"ehgehfg",Toast.LENGTH_SHORT).show();
}
}
}

When to request permission at runtime for Android Marshmallow 6.0?

I am testing my app on Marshmallow 6.0 and it's getting force closed for the android.permission.READ_EXTERNAL_STORAGE, even if it is defined in the Manifest already. Somewhere I have read that if I request permission at runtime then it would not force close your application. I have read this android document also, which is for requesting runtime permission.
So, I came to know that we can request a permission like below which is mentioned in the android document.
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {
// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}
The above code has a callback method onRequestPermissionsResult which gets the result.
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
}
}
My question is where to exactly request the permission to user? Should we use the requesting permission at start of the app or should we do it as when the permission is required?
This is worked for me !!!
In Your Splash Activity of your application do the following,
1) Declare an int variable for request code,
private static final int REQUEST_CODE_PERMISSION = 2;
2) Declare a string array with the number of permissions you need,
String[] mPermission = {Manifest.permission.READ_CONTACTS, Manifest.permission.READ_SMS,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.WRITE_EXTERNAL_STORAGE};
3) Next Check the condition for runtime permission on your onCreate method,
try {
if (ActivityCompat.checkSelfPermission(this, mPermission[0])
!= MockPackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(this, mPermission[1])
!= MockPackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(this, mPermission[2])
!= MockPackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(this, mPermission[3])
!= MockPackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
mPermission, REQUEST_CODE_PERMISSION);
// If any permission aboe not allowed by user, this condition will execute every tim, else your else part will work
}
} catch (Exception e) {
e.printStackTrace();
}
4) Now Declare onRequestPermissionsResult method to check the request code,
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
Log.e("Req Code", "" + requestCode);
if (requestCode == REQUEST_CODE_PERMISSION) {
if (grantResults.length == 4 &&
grantResults[0] == MockPackageManager.PERMISSION_GRANTED &&
grantResults[1] == MockPackageManager.PERMISSION_GRANTED &&
grantResults[2] == MockPackageManager.PERMISSION_GRANTED &&
grantResults[3] == MockPackageManager.PERMISSION_GRANTED) {
// Success Stuff here
}
}
}
Do like this
private static final int REQUEST_ACCESS_FINE_LOCATION = 111;
In your onCreate
boolean hasPermissionLocation = (ContextCompat.checkSelfPermission(getApplicationContext(),
Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED);
if (!hasPermissionLocation) {
ActivityCompat.requestPermissions(ThisActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_ACCESS_FINE_LOCATION);
}
then check result
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode)
{
case REQUEST_ACCESS_FINE_LOCATION: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
{
Toast.makeText(ThisActivity.this, "Permission granted.", Toast.LENGTH_SHORT).show();
//reload my activity with permission granted
finish();
startActivity(getIntent());
} else
{
Toast.makeText(ThisActivity.this, "The app was not allowed to get your location. Hence, it cannot function properly. Please consider granting it this permission", Toast.LENGTH_LONG).show();
}
}
}
}
In general, request needed permissions it as soon as you need them. This way you can inform the user why you need the permission and handle permission denies much easier.
Think of scenarios where the user revokes the permission while your app runs: If you request it at startup and never check it later this could lead to unexpected behaviour or exceptions.
In my opinion, there is no one correct answer to your question. I strongly suggest you to look at this official permissions patterns page.
Couple of things suggested by Google :
"Your permissions strategy depends on the clarity and importance of the permission type you are requesting. These patterns offer different ways of introducing permissions to the user."
"Critical permissions should be requested up-front. Secondary permissions may be requested in-context."
"Permissions that are less clear should provide education about what the permission involves, whether done up-front or in context."
This illustration might give you better understanding.
Maybe the most crucial thing here is that whether you ask the permission up-front or in the context, you should always keep in mind that these permissions can be revoked anytime by the user (e.g. your app is still running, in background).
You should make sure that your app doesn't crash just because you asked the permission on the very beginning of the app and assumed that user didn't change his/her preference about that permission.
For requesting runtime permission i use GitHub Library
Add library in Build.gradle file
dependencies {
compile 'gun0912.ted:tedpermission:1.0.3'
}
Create Activity and add PermissionListener
public class MainActivity extends AppCompatActivity{
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
PermissionListener permissionlistener = new PermissionListener() {
#Override
public void onPermissionGranted() {
Toast.makeText(RationaleDenyActivity.this, "Permission Granted", Toast.LENGTH_SHORT).show();
//Camera Intent and access Location logic here
}
#Override
public void onPermissionDenied(ArrayList<String> deniedPermissions) {
Toast.makeText(RationaleDenyActivity.this, "Permission Denied\n" + deniedPermissions.toString(), Toast.LENGTH_SHORT).show();
}
};
new TedPermission(this)
.setPermissionListener(permissionlistener)
.setRationaleTitle(R.string.rationale_title)
.setRationaleMessage(R.string.rationale_message) // "we need permission for access camera and find your location"
.setDeniedTitle("Permission denied")
.setDeniedMessage("If you reject permission,you can not use this service\n\nPlease turn on permissions at [Setting] > [Permission]")
.setGotoSettingButtonText("Settings")
.setPermissions(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)
.check();
}
}
string.xml
<resources>
<string name="rationale_title">Permission required</string>
<string name="rationale_message">we need permission for read <b>camera</b> and find your <b>location</b></string>
</resources>
Android Easy Runtime Permissions with Dexter:
1. Dexter Permissions Library
To get started with Dexter, add the dependency in your build.gradle
dependencies {
// Dexter runtime permissions
implementation 'com.karumi:dexter:4.2.0'
}
1.1 Requesting Single Permission
To request a single permission, you can use withPermission() method by passing the required permission. You also need a PermissionListener callback to receive the state of the permission.
> onPermissionGranted() will be called once the permission is granted.
> onPermissionDenied() will be called when the permission is denied. Here you can check whether the permission is permanently denied by using response.isPermanentlyDenied() condition.
The below code requests CAMERA permission.
Dexter.withActivity(this)
.withPermission(Manifest.permission.CAMERA)
.withListener(new PermissionListener() {
#Override
public void onPermissionGranted(PermissionGrantedResponse response) {
// permission is granted, open the camera
}
#Override
public void onPermissionDenied(PermissionDeniedResponse response) {
// check for permanent denial of permission
if (response.isPermanentlyDenied()) {
// navigate user to app settings
}
}
#Override
public void onPermissionRationaleShouldBeShown(PermissionRequest permission, PermissionToken token) {
token.continuePermissionRequest();
}
}).check();
1.2 Requesting Multiple Permissions
To request multiple permissions at the same time, you can use withPermissions() method. Below code requests STORAGE and LOCATION permissions.
Dexter.withActivity(this)
.withPermissions(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_FINE_LOCATION)
.withListener(new MultiplePermissionsListener() {
#Override
public void onPermissionsChecked(MultiplePermissionsReport report) {
// check if all permissions are granted
if (report.areAllPermissionsGranted()) {
// do you work now
}
// check for permanent denial of any permission
if (report.isAnyPermissionPermanentlyDenied()) {
// permission is denied permenantly, navigate user to app settings
}
}
#Override
public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions, PermissionToken token) {
token.continuePermissionRequest();
}
})
.onSameThread()
.check();
A good explanation and HowTo can be found here:
https://inthecheesefactory.com/blog/things-you-need-to-know-about-android-m-permission-developer-edition/en
I wrote this code to check and request the permissions at runtime in a BaseActivity.class which is parent of every other Activity.class I implemented:
public static final int PERMISSION_REQUEST = 42;
public static final int MULTIPLE_PERMISSION_REQUEST = 43;
//Marshmallow Permission Model
public boolean requestPermission(String permission /* Manifest.permission...*/) {
if (ContextCompat.checkSelfPermission(this,
permission) != PackageManager.PERMISSION_GRANTED) {
if (Utils.hasMarshmallow())
ActivityCompat.requestPermissions(this,
new String[]{permission}, PERMISSION_REQUEST
);
else {
requestPermissions(new String[]{permission},
PERMISSION_REQUEST);
}
return false;
} else {
return true;
}
}
public boolean requestPermission(String... permissions) {
final List<String> permissionsList = new ArrayList<String>();
for (String perm : permissions) {
addPermission(permissionsList, perm);
}
if (permissionsList.size() > 0) {
if (Utils.hasMarshmallow())
requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
MULTIPLE_PERMISSION_REQUEST);
else
ActivityCompat.requestPermissions(this, permissionsList.toArray(new String[permissionsList.size()]),
MULTIPLE_PERMISSION_REQUEST);
return false;
} else
return true;
}
private boolean addPermission(List<String> permissionsList, String permission) {
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
permissionsList.add(permission);
// Check for Rationale Option
if (Utils.hasMarshmallow())
if (!shouldShowRequestPermissionRationale(permission))
return false;
}
return true;
}
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST:
case MULTIPLE_PERMISSION_REQUEST: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
Simply example call:
activity.requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE);
Return result will let you know if the permission is already granted or not.
calling this function we can allow user to open dialog for asking permission to allow camera and record Audio.
if ( ActivityCompat.shouldShowRequestPermissionRationale (this, Manifest.permission.CAMERA) ||
ActivityCompat.shouldShowRequestPermissionRationale (this,
Manifest.permission.RECORD_AUDIO) ) {
Toast.makeText (this,
R.string.permissions_needed,
Toast.LENGTH_LONG).show ();
} else {
ActivityCompat.requestPermissions (
this,
new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO},
CAMERA_MIC_PERMISSION_REQUEST_CODE);
}
https://material.io/guidelines/patterns/permissions.html
This link will give you different type of scenario where permissions can be asked. Choose accordingly to your needs.
I like short code. I use RxPermission for permissions.
RxPermission is best library, which makes permission code unexpected just 1 line.
RxPermissions rxPermissions = new RxPermissions(this);
rxPermissions
.request(Manifest.permission.CAMERA,
Manifest.permission.READ_PHONE_STATE) // ask single or multiple permission once
.subscribe(granted -> {
if (granted) {
// All requested permissions are granted
} else {
// At least one permission is denied
}
});
add in your build.gradle
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
dependencies {
implementation 'com.github.tbruyelle:rxpermissions:0.10.1'
implementation 'com.jakewharton.rxbinding2:rxbinding:2.1.1'
}
Isn't this easy?
if ( ActivityCompat.shouldShowRequestPermissionRationale (this, Manifest.permission.CAMERA) ||
ActivityCompat.shouldShowRequestPermissionRationale (this,
Manifest.permission.RECORD_AUDIO) ) {
Toast.makeText (this,
R.string.permissions_needed,
Toast.LENGTH_LONG).show ();
} else {
ActivityCompat.requestPermissions (
this,
new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO},
CAMERA_MIC_PERMISSION_REQUEST_CODE);
}

Categories

Resources