I have an application that is meant for "locked Android phones" meaning the application is the only application users will have access to on that phone and that is done using MySync.
Before the application had to support Android 11 (complieSdkVersion 30), the user was able to change screen brightness and other system settings from the application (Since the user did not have access to the settings app). But now the WRITE_SETTINGS and CHANGE_CONFIGURATION are deprecated and no longer have desirable effects.
I am aware that it is possible to prompt the user to accept the settings with this prompt:
boolean permission;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
permission = Settings.System.canWrite(context);
} else {
permission = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_SETTINGS) == PackageManager.PERMISSION_GRANTED;
}
if (permission) {
initApp();
} else {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
intent.setData(Uri.parse("package:" + context.getPackageName()));
startActivityForResult(intent, SettingsActivity.CODE_WRITE_SETTINGS_PERMISSION);
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_SETTINGS}, SettingsActivity.CODE_WRITE_SETTINGS_PERMISSION);
}
}
But since the user does not have access to the settings application in my app eco-system, I can not give the user a prompt that would open up the settings and allow the user to stay in the settings.
Is there no way around this permission request and is there no way to do it on the manifest level or some other way?
Related
I am using Android 13 device for testing. Notification is receiving on version 11 and below but not on 12 and above. I have implemented notification permission also explored some stack answers but still did not find and solution. From firebase if i directly send test notification then it receives but it does not receiving from backend when some action is performed in app.
In Android 12 and above, you also need to request the "group notification" permission.
here is an example how it can looks like:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.GROUP_NOTIFICATION)
!= PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.GROUP_NOTIFICATION},
REQUEST_GROUP_NOTIFICATION_PERMISSION);
}
}
And your have to add POST_NOTIFICATIONS permission
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
if (Build.VERSION.SDK_INT >= 33) {
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.POST_NOTIFICATIONS},101);
}
else {
createChannel();
}
}
I have a problem re-requesting the permissions required to scan and connect to bluetooth devices when targeting SDK 31 (Android 12).
I call this method inside my main activity's onCreate():
public void requestBluetoothPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if ((this.checkSelfPermission(Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED)
|| (this.checkSelfPermission(Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED)) {
Log.w(getClass().getName(), "requestBluetoothPermissions() BLUETOOTH_SCAN AND BLUETOOTH_CONNECT permissions needed => requesting them...");
this.requestPermissions(new String[]{
Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT
}, MyActivity.REQUEST_BLUETOOTH_PERMISSIONS);
}
}
}
It's works fine the first time it is called i.e. an Android pop-up is displayed to the user, prompting him to grant the permissions.
But if he refuses to grant the permissions, next time onCreate() is called, the pop-up will not be displayed, which means the user remains unable to grant the permissions.
Any idea why and how to fix this ?
It appears Android 12 blocks requesting the same permission after user denied it once only.
Therefore, I ended up using ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.BLUETOOTH_SCAN) to determine wether the permission can be requested or not, in which case a snackbar message is displayed explaining why is the permission needed, with a button opening the app settings where permission can be granted.
Here is a sample of the code:
public void requestBluetoothPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if ((this.checkSelfPermission(Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED)
|| (this.checkSelfPermission(Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED)) {
if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(),
Manifest.permission.BLUETOOTH_SCAN)) {
// display permission rationale in snackbar message
} else {
Log.w(getClass().getName(), "requestBluetoothPermissions() BLUETOOTH_SCAN AND BLUETOOTH_CONNECT permissions needed => requesting them...");
this.requestPermissions(new String[]{
Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT
}, MyActivity.REQUEST_BLUETOOTH_PERMISSIONS);
}
}
}
}
I am trying to use the "new" way of requesting external storage write permission.
But the request is automatically denied and no window pops up asking the user for permission.
Virtual Device: Pixel 2 API 30
SDK 30
I did add the permissions into the manifest file
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.lab6">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE "/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
WRITE_EXTERNAL_STORAGE is a dangerous permission from my knowledge, so it has to be requested at runtime.
into my gradle dependencies I added these:
implementation 'androidx.activity:activity:1.2.0'
implementation 'androidx.fragment:fragment:1.3.0'
The activity looks like this:
public class MainActivity extends AppCompatActivity {
private ActivityResultLauncher<String> requestPermissionLauncher =
registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {
if (isGranted) {
Log.d("mihai", "Permission is granted now.");
} else {
Log.d("mihai", "Permission refused ");
}
});
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button downloadBtn = findViewById(R.id.downloadButton);
Button loadBtn = findViewById(R.id.loadButton);
downloadBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.d("mihai", "requesting permission");
if (ContextCompat.checkSelfPermission(
getApplicationContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) ==
PackageManager.PERMISSION_GRANTED) {
Log.d("mihai", "permission already granted");
} else {
Log.d("mihai", "launching");
requestPermissionLauncher.launch(
Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
}
});
}
}
And these are the logs I get. As you can see, the request is denied. No question pops up on the phone.
2022-04-28 19:55:15.351 7499-7499/com.example.lab6 D/mihai: requesting permission
2022-04-28 19:55:15.353 7499-7499/com.example.lab6 D/mihai: launching
2022-04-28 19:55:15.592 7499-7499/com.example.lab6 D/mihai: Permission refused
Because you have denied the permission multiple times.
From the documentation:
Starting in Android 11 (API level 30), if the user taps Deny for a specific permission more than once during your app's lifetime of installation on a device, the user doesn't see the system permissions dialog if your app requests that permission again. The user's action implies "don't ask again." On previous versions, users would see the system permissions dialog each time your app requested a permission, unless the user had previously selected a "don't ask again" checkbox or option.
You will need to redirect the user to the app's setting detail to grant the permission manually.
To open an app specific app's detail setting screen:
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
Apparently the MIUI OS has already implemented its own Permissions system before Marshmallow does. I'm currently testing a video recording app for the Xiaomi Mi 4i, which uses a MIUI based on API 21 [Android 5.0.2], and it needs the Record Audio permission which is not granted by default by MIUI's Permissions Manager.
So far the way I've managed to alter the permissions is by accessing the Permissions Manager window for the app on clicking the OK button in the AlertDialog:
isMIUI = MIUIUtils.isMIUI();
if(isMIUI)
{
AlertDialog.Builder adb = new AlertDialog.Builder(this);
adb.setMessage("If you intend to use the video recording feature, please enable the 'Record Audio' permission in the settings menu. You will be redirected there if you press OK.")
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS");
intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity");
intent.putExtra("extra_pkgname", "com.picmix.mobile");
startActivity(intent);
}
})
.setNegativeButton("CANCEL", null)
.create();
adb.show();
}
But this isn't good enough for me. I need to check if the Record Audio permission is already checked in the MIUI Permissions Manager in order to run this only once.
How do I check for the permissions granted or to be notified in the MIUI Permissions Manager programmatically?
private boolean resourceCanBeAccessed() {
boolean response = true;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if(ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
== PackageManager.PERMISSION_DENIED ) {
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.RECORD_AUDIO}, 1);
response = false;
}
}
return response;
}
You just need to call this method before accessing the resource. This method will return true if the permission is granted. It the permission is not granted then it will grant the permission
Since I can use Settings.canDrawOverlays to check if the user granted this permission on API >= 23, how I can check if the user have it on older APIS?
Does this permission is automically granted on API < 23 and no need to check for it?
Currently, I start my service only on API 23+ after the permission are granted.
#SuppressLint("NewApi")
public void checkDrawOverlayPermission() {
if(Build.VERSION.SDK_INT >= 23) {
/** check if we already have permission to draw over other apps */
if (!Settings.canDrawOverlays(this)) {
/** if not construct intent to request permission */
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
/** request permission via start activity for result */
startActivityForResult(intent, REQUEST_CODE);
} else {
startService(new Intent(this, ChatHeadService.class));
}
}
}
But what if the user is on API <= 22? How I can make sure the application wont crash and my service will start?
Overlay permission is only required for Marshmallow (API 23) and above. In previous APIs this permission is provided by default.