I play around with the Preview M and test my app on it, especially the "saving a file to external storage" part.
Before the download/save process starts, i request for
Manifest.permission.WRITE_EXTERNAL_STORAGE
permission as described on the developer page: https://developer.android.com/preview/features/runtime-permissions.html
The dialog occurs as expected:
"Allow <AppName> to access photos, media, and files on your device?" Deny / Allow
If i hit the "deny"-button, the onRequestPermissionsResult-method of the activity is called.
If i press the "allow"-button, the activity is first recreated and afterwards the onRequestPermissionsResult-method is called. I think it's a result of the granted permission.
But the second situation is tricky because i want to trigger a callback that starts the download, but this object is null at this point:
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
getLogger().error("onRequestPermissionsResult ( " + requestCode + ", " + permissions + ", " + grantResults + " )");
switch (requestCode) {
//permission for saving files?
case PermissionCode.WRITE_EXTERNAL_STORAGE: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//HERE IS THE NULL-OBJECT
if (controller != null) {
controller.triggerCallback();
}
}
break;
}
default: {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
}
So my questions are:
Can I avoid this recreation?
If no, how can i redesign my code to solve the problem - i'm totally idealess at the moment
EDIT 1:
I tried to solve the problem with a handler and postDelayed - but i skipped it.
EDIT 2:
I preliminary solved it and show a Toast to user with the request to push the download button again (in case of granted permission). But Google: ARE YOU SERIOUS?
EDIT 3:
No recreation happens with the latest SDK 6.0 (Version 23) - maybe somebody heard my weeping in Mountain View :-)
You can always check the download condition immediately after the activity recreates itself in onCreate():
static final int MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 0;
boolean initiateDownload = false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState != null) {
initiateDownload = savedInstanceState.getBoolean("toDownload");
}
}
#Override
public void onResume() {
super.onResume();
final boolean hasPermission = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED;
if(initiateDownload && hasPermission) {
// start download here...
} else {
requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE},
MainActivity.MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE) {
if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {
initiateDownload = true;
} else {
// denied permission...
}
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("toDownload", initiateDownload);
}
Android may destroy and recreate your activity for a number of reasons. The use case of requesting permissions is no exception. You can use your preferred way of saving and restoring UI state. If your state is parcelable you can do it like the example below. Why this is not mentioned in Request App Permissions or Android RuntimePermissions Sample I don't know.
See more in
Saving and restoring transient UI state
and
Saving UI States
public class ExampleActivity extends AppCompatActivity {
private static final String CURRENT_DOWNLOAD_ITEM = "CURRENT_DOWNLOAD_ITEM";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null)
currentDownloadItem = savedInstanceState.getParcelable(CURRENT_DOWNLOAD_ITEM);
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putParcelable(CURRENT_DOWNLOAD_ITEM, currentDownloadItem);
super.onSaveInstanceState(outState);
}
public void downloadItem(Parcelable item) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
currentDownloadItem = item;
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
} else
perfornDownload(item);
}
private Parcelable currentDownloadItem;
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (currentDownloadItem != null) {
perfornDownload(currentDownloadItem);
currentDownloadItem = null;
} else
Toast.makeText(this, "Please select download again now that we have permission to save it", Toast.LENGTH_LONG).show();
} else
Toast.makeText(this, "Download cancelled, as we don\'t have permission to save the downloaded files", Toast.LENGTH_LONG).show();
break;
}
}
private void perfornDownload(Parcelable item) {
// Here we are sure we have the permission
}
}
Related
In our android webview app, We ask for read_contacts, location, camera, audio permissions at a time on initialization of app. What happens is that simultaneously the webview url is also loaded. This causes some crucial data like location not to be passed to webview in the first load.
What we expect from the app is to load the webview url immediately after user allows, grants permissions for the above only and not before that. We tried using onRequestPermissionsResult for achieving this, but unable to do so. The code we have tried is as given hereunder
if (!check_permission(4) || !check_permission(3)) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.POST_NOTIFICATIONS, Manifest.permission.WRITE_CONTACTS, Manifest.permission.READ_PHONE_NUMBERS,Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.RECORD_AUDIO,Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE},
contact_perm);
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults){
if (requestCode == 1) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
get_location();
asw_view.loadUrl(url);
}
}
}
Any help would be appreciated.
You're checking only for 0th element, i.e., grantResults[0] == PackageManager.PERMISSION_GRANTED in onRequestPermissionsResult(...).
You need to check like this:
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
if (requestCode == 1) {
// Use this method to check if all the permissions are GRANTED.
if (areAllPermissionsGranted(grantResults)) {
get_location();
asw_view.loadUrl(url);
} else {
/*
* NOTE:
* -----
* Add a Log here to check if all the permissions are granted.
* If this block doesn't executes, it means all the permissions are granted,
* something else is wrong inside your 'if' block and you need to debug that block.
* */
}
}
}
// Method to check if all the results are GRANTED.
private Boolean areAllPermissionsGranted(int[] grantResults) {
boolean isGranted = false;
if (grantResults.length > 0) {
for (int grantResult : grantResults) {
if (grantResult == PackageManager.PERMISSION_GRANTED) {
isGranted = true;
} else {
// if a single permission is NOT_GRANTED, return from the method.
return false;
}
}
}
return isGranted;
}
Use this library
gradle:
implementation 'com.nabinbhandari.android:permissions:3.8'
then:
String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION}; //add your requested permissions to this array
String rationale = "Please provide location permission so that you can ...";
Permissions.Options options = new Permissions.Options()
.setRationaleDialogTitle("Info")
.setSettingsDialogTitle("Warning");
Permissions.check(this/*context*/, permissions, rationale, options, new
PermissionHandler() {
#Override
public void onGranted() {
// do your task here
}
#Override
public void onDenied(Context context, ArrayList<String> deniedPermissions) {
// permission denied, block the feature.
}
});
I have problem for run time permission.my code is:
public class MainActivity extends AppCompatActivity {
int i = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
ActivityCompat.requestPermissions(MainActivity.this, permissions, 0x3);
while (i == 0) {
//
}
//
}
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case 0x3:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(G.context, "allowed",Toast.LENGTH_LONG).show();
} else {
Toast.makeText(G.context, "deney", Toast.LENGTH_LONG).show();
}
break; } }}
I want whlie block run after user response permission. but while block dont wait for permission result. How can delay in program, until user response to run-time permission, and after run whlie and another line?
You just have to call the rest of your code if the "allowed" case of your onRequestPermissionResult method.
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(G.context, "allowed",Toast.LENGTH_LONG).show();
//The rest of your code
} else {
Toast.makeText(G.context, "deney", Toast.LENGTH_LONG).show();
//Ask again
}
I have a navigation drawer. In navigation drawer I am doing inflate the fragment. At the first fragment I ask to user for the runtime permission. but in the second fragment I want to do that it ask user for the same permission again. but in my case first fragment ask the permission. but in the second fragment it does not ask for the permission. so can anyone tell me what is the issue? because I am using the same code in the both situation.
This is my first fragment code :-
if (checkPermission(Manifest.permission.ACCESS_FINE_LOCATION, getActivity().getApplicationContext(), getActivity())) {
IsConsumed = 1;
fetchLocationData();
} else {
requestPermission(Manifest.permission.ACCESS_FINE_LOCATION, PERMISSION_REQUEST_CODE_LOCATION, getActivity().getApplicationContext(), getActivity());
}
public void requestPermission(String strPermission, int perCode, Context _c, Activity _a) {
if (ActivityCompat.shouldShowRequestPermissionRationale(_a, strPermission)) {
Toast.makeText(getActivity(), "GPS permission allows us to access location data. Please allow in App Settings for additional functionality.", Toast.LENGTH_LONG).show();
} else {
ActivityCompat.requestPermissions(_a, new String[]{strPermission}, perCode);
}
}
public static boolean checkPermission(String strPermission, Context _c, Activity _a) {
int result = ContextCompat.checkSelfPermission(_c, strPermission);
if (result == PackageManager.PERMISSION_GRANTED) {
return true;
} else {
return false;
}
}
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_CODE_LOCATION:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
fetchLocationData();
} else {
Toast.makeText(getActivity(), "Permission Denied, You cannot access location data.", Toast.LENGTH_LONG).show();
}
break;
}
}
I am using the same code in the second fragment. but it does not ask for the permission.I tried to print the log it goes into the if() condition.and print the log. but it does not ask for the permission.
Marshmallow has redesigned getting permissions. So Handled permissions before calling the method which needs permissions and it works fine, but It crashes in the following scenario:
Step 1: Opened app and gave all the necessary permissions
Step 2: Clicked Home button(So the app is in background)
Step 3: Manually changed the permissions in the Settings
Step 4: Launched the app from multitasks, now it crashes because of app context becomes invalid
Observed that app gets created again, don't understand why this happens. Any suggestions to rectify this issue would be welcome!
It's because of additional features added from Marshmallow. You need to request from user at runtime. For this use this class which I have made. Then use it whereever required
public class AppPermission {
public static boolean isMarshmallowPlusDevice() {
return Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1;
}
#TargetApi(Build.VERSION_CODES.M)
public static boolean isPermissionRequestRequired(Activity activity, #NonNull String[] permissions, int requestCode) {
if (isMarshmallowPlusDevice() && permissions.length > 0) {
List<String> newPermissionList = new ArrayList<>();
for (String permission : permissions) {
if (PackageManager.PERMISSION_GRANTED != activity.checkSelfPermission(permission)) {
newPermissionList.add(permission);
}
}
if (newPermissionList.size() > 0) {
activity.requestPermissions(newPermissionList.toArray(new String[newPermissionList.size()]), requestCode);
return true;
}
}
return false;
}
}
Then put this code where you require permission from user.
if (!AppPermission.isPermissionRequestRequired(SignInActivity.this, new String[]{"android.permission.GET_ACCOUNTS"},
REQUEST_APP_PERMISSION)) {
// Your code if permission available
}
After this, in your Fragment or Activity put this code -
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQUEST_APP_PERMISSION:
for (int i = 0; i < permissions.length; i++) {
String permission = permissions[i];
int grantResult = grantResults[i];
switch (permission) {
case "android.permission.GET_ACCOUNTS":
if (PackageManager.PERMISSION_GRANTED == grantResult) {
// Your code
}
break;
}
}
break;
}
}
The above code is for request permission for GET_ACCOUNTS you can change it to whatever required.
Observed that app gets created again, don't understand why this
happens. Any suggestions to rectify this issue would be welcome!
Because when permissions change, application "state" should be invalidated. The proper way to do that is destroy the root context, which is the application itself.
You have to check the permissions granted status each time you query the API methods that require these permissions. You can't rely on some SharedPreferences flag indicating that "user granted the permissions in onboarding, ok, lets have fun". Make your app stateless in this regards.
For example, you can create some BaseActivity/BaseFragment or Utility and move all the checking logic in there.
Define a boolean value at first
private boolean isPermissionGranted = false;
And then check if permission granted:
if (!isPermissionGranted) {
checkPermission();
}
Actual code for run time permission check is as follow:
private void checkPermission() {
int hasPermission = ContextCompat.checkSelfPermission(UserProfile.this, Manifest.permission.CAMERA);
int hasWritePermission = ContextCompat.checkSelfPermission(UserProfile.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (hasPermission != PackageManager.PERMISSION_GRANTED && hasWritePermission != PackageManager.PERMISSION_GRANTED) {
if (!ActivityCompat.shouldShowRequestPermissionRationale(UserProfile.this, Manifest.permission.CAMERA) && !ActivityCompat.shouldShowRequestPermissionRationale(UserProfile.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
showMessage(getString(R.string.allow_access_to_camera_external_storage),
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(UserProfile.this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_CODE_ASK_PERMISSIONS);
}
});
return;
}
ActivityCompat.requestPermissions(UserProfile.this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_CODE_ASK_PERMISSIONS);
return;
} else {
isPermissionGranted = true;
}
}
private void showMessage(String message, DialogInterface.OnClickListener listener) {
new AlertDialog.Builder(UserProfile.this)
.setMessage(message)
.setPositiveButton(R.string.ok, listener)
.setNegativeButton(R.string.cancel, null)
.create()
.show();
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE_ASK_PERMISSIONS:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
isPermissionGranted = true;
} else {
isPermissionGranted = false;
Toast.makeText(UserProfile.this, R.string.permission_denied, Toast.LENGTH_SHORT)
.show();
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
You can take reference from above code and implement it in your application.
I need to request a permission manually for API level 23 and where the dialog window appears and the application goes into background.
Here is my code:
#Override
public void onDialogDone(int dialog, boolean result) {
DialogFragment newFragment;
switch (dialog) {
case EULA_DIALOG:
if (result) {
boolean numberExists = _appRegistration.checkPhoneNumber();
if (!numberExists) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.READ_PHONE_STATE},
MY_PERMISSIONS_PHONE_STATE_READ);
return;
} else {
//do some work
}
} else {
Intent intent = new Intent(...);
startActivity(intent);
finish();
}
break;....
}
}
Also, in the MainActivity I have overridden the following method:
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case MY_PERMISSIONS_PHONE_STATE_READ: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// do some work
} else {
// do some work
}
return;
}
}
But if I click "allow" or "deny", this method is not called, because application is in background mode... I logged onPause and onStop methods and both of them are called after the permission window is shown.
There was MainActivity problem, in manifest file it was configured as:
android:noHistory="true"
I just removed this property and application started working as it was expected.
Try with this
if (!numberExists) {
ActivityCompat.requestPermissions(getActivity(),
new String[]{Manifest.permission.READ_PHONE_STATE},
MY_PERMISSIONS_PHONE_STATE_READ);
return;
This way you are starting the request from your activity and the callbacks will be received.