Can't get WRITE_SETTINGS permission
Asked Answered
T

10

93

When I have a target API of 23 on Android M Preview 3, I cannot seem to acquire the Manifest.permission.WRITE_SETTTINGS permission.

requestPermissions(new String[]{Manifest.permission.WRITE_SETTINGS}, 101);

Request permission doesn't bring up the dialog I would expect, but if I make the following call without this permission,

 RingtoneManager.setActualDefaultRingtoneUri(activity, RingtoneManager.TYPE_RINGTONE, ringUri);

The call will except because I don't have the permission.

I'm not sure where to go from here. Is there a new ringtone API for 23? Or did this permission change just make it impossible for any non-system apps to change the ringtone?

Tamworth answered 18/8, 2015 at 22:10 Comment(0)
R
145

To use WRITE_SETTINGS, based on the docs:

  1. Have the <uses-permission> element in the manifest as normal.

  2. Call Settings.System.canWrite() to see if you are eligible to write out settings.

  3. If canWrite() returns false, start up the ACTION_MANAGE_WRITE_SETTINGS activity so the user can agree there to allow your app to actually write to settings.

In other words, writing to settings is now a double-opt-in (agree to install, agree separately in Settings to allow), akin to device admin APIs, accessibility services, etc.

Also note that I have not tried using these yet — this is based on research that I did yesterday on Android 6.0 changes.

Retinol answered 18/8, 2015 at 22:27 Comment(10)
Thanks Mark! Worked like a charm. developer.android.com/preview/features/runtime-permissions.html needs some updating if we are going to have multiple new ways to request permissions. (I had already read your blog before posting, but obviously didn't retain that piece of info when I needed it)Tamworth
This worked, indeed. But for the end user this is a bad approach. Any signs of Google changing this behaviour?Elisabetta
@Fhl: I do not know why they went this route instead of the regular dangerous runtime permission approach they went with other things in Android 6.0. I will be surprised if this changes any time soon.Retinol
you can specify your app in the intent like this: intent.setData(Uri.parse("package:" + Context.getPackageName()));Pie
Another thing to note is there seems to be a bug in Android that causes an app that was previously installed where the user gave write permissions in the dialog described above, where the toggle switch will be put in the enabled position yet canWrite returns false.. In order to get canWrite() method to return true the user must toggle the switch off and back on... I see this in development but hopefully it won't be something customers see.Pigskin
I don't see a bug report open for the issue mentioned by Matt Wolf so I opened one here: code.google.com/p/android/issues/…Amaryl
Is there a way to do this in Espresso? GrantPermissionsRule.grant() will does not work for WRITE_SETTINGSCorduroy
@Kirk: Purely with Espresso, not that I know of. In principle, you should be able to mix in some UiAutomator stuff to navigate the Settings app. That will be fairly fragile, depending on the specific device (since Settings apps are frequently changed by manufacturers). If there's an adb shell command that can grant WRITE_SETTINGS, you should be able to use that, but I don't know if there is a way to do that.Retinol
dev2qa.com/how-to-grant-write-settings-permission-in-androidMoonstruck
My apps not showing in Modify system settingsMaravedi
P
49

In addition to the answer from CommonsWare and the comment from Ogix, here is some dummy code:

private boolean checkSystemWritePermission() {
    boolean retVal = true;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        retVal = Settings.System.canWrite(this);
        Log.d(TAG, "Can Write Settings: " + retVal);
        if(retVal){
            Toast.makeText(this, "Write allowed :-)", Toast.LENGTH_LONG).show();
        }else{
            Toast.makeText(this, "Write not allowed :-(", Toast.LENGTH_LONG).show();
            FragmentManager fm = getFragmentManager();
            PopupWritePermission dialogFragment = new PopupWritePermission();
            dialogFragment.show(fm, getString(R.string.popup_writesettings_title));
        }
    }
    return retVal;
}

The Fragment PopupwritePermission then gives a window where the situation is explained. A click on the OK Button will open the Android System Menu where the Permission can be granted:

private void openAndroidPermissionsMenu() {
    Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
    intent.setData(Uri.parse("package:" + getActivity().getPackageName()));
    startActivity(intent);
}
Peacetime answered 2/4, 2016 at 10:40 Comment(0)
H
42

The previous answers are great, I have just little addition for also getting the result for the permission asking.

 public static void youDesirePermissionCode(Activity context){
        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) {
            //do your code
        }  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()));
                context.startActivityForResult(intent, MainActivity.CODE_WRITE_SETTINGS_PERMISSION);
            } else {
                ActivityCompat.requestPermissions(context, new String[]{Manifest.permission.WRITE_SETTINGS}, MainActivity.CODE_WRITE_SETTINGS_PERMISSION);
            }
        }
    }

And then in the Activity:

@SuppressLint("NewApi")
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == MainActivity.CODE_WRITE_SETTINGS_PERMISSION && Settings.System.canWrite(this)){
            Log.d("TAG", "MainActivity.CODE_WRITE_SETTINGS_PERMISSION success");
            //do your code
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == MainActivity.CODE_WRITE_SETTINGS_PERMISSION && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            //do your code
        }
    }
Harvard answered 22/9, 2016 at 10:9 Comment(6)
I put your code, and it works fine,even the permission granted, but still the custom ringtone is not assigning and still have the Write_Setting permission denied issue.Deuced
ActivityCompat.requestPermissions(context, new String[]{Manifest.permission.WRITE_SETTINGS}, ....); cannot be used. It's a special permission. We can only request this permission with an intent as said in the documentation. Also prior to Marshmello the permission is granted permenantly at install timeAmplify
@Harvard what is your variable MainActivity.CODE_WRITE_SETTINGS_PERMISSION ?Albert
@BrunoBieri yes you right, I omitted that. I will edit my answer so it will be verbose.Harvard
So what's MainActivity.CODE_WRITE_SETTINGS_PERMISSION?Dragline
It's just constant field integer in this snippet. Something like public static int CODE_WRITE_SETTINGS_PERMISSION = 42;Harvard
C
14

This is a complete example:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    if (Settings.System.canWrite(context) {
        // Do stuff here
    }
    else {
        Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_WRITE_SETTINGS);
        intent.setData(Uri.parse("package:" + getActivity().getPackageName()));
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }
}
Calamondin answered 19/2, 2017 at 13:13 Comment(1)
intent.setData(Uri.parse("package:" + getActivity().getPackageName()));Must
H
9

As of android Marshmellow , you require to use runtime permissions which aims to more security , or use permission when need here is documenatation

and for Write Settings documentation is here

In manifest add

<uses-permission android:name="android.permission.WRITE_SETTINGS" />

In your class

private boolean checkSystemWritePermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if(Settings.System.canWrite(context))
            return true;
        else 
            openAndroidPermissionsMenu();
    }
    return false;
}

private void openAndroidPermissionsMenu() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
        intent.setData(Uri.parse("package:" + context.getPackageName()));
        context.startActivity(intent);
    }
}

And use it like this

try {
       if (checkSystemWritePermission()) {
            RingtoneManager.setActualDefaultRingtoneUri(context, RingtoneManager.TYPE_RINGTONE, newUri);
            Toast.makeText(context, "Set as ringtoon successfully ", Toast.LENGTH_SHORT).show();
            }else {
                Toast.makeText(context, "Allow modify system settings ==> ON ", Toast.LENGTH_LONG).show();
            }
        } catch (Exception e) {
            Log.i("ringtoon",e.toString());
            Toast.makeText(context, "unable to set as Ringtoon ", Toast.LENGTH_SHORT).show();
        }
Hexyl answered 16/4, 2018 at 6:18 Comment(0)
B
7

The permission android.permission.WRITE_SETTINGS is now in the group signature|appop|pre23|preinstalled like android.permission.CHANGE_NETWORK_STATE and android.permission.SYSTEM_ALERT_WINDOW

This means you get it on sdk 22 and below. On newer version you have to be an app operator.

Beleaguer answered 11/9, 2015 at 15:0 Comment(0)
B
6

I have used bellow like..

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        boolean retVal = true;
        retVal = Settings.System.canWrite(this);
        if (retVal == false) {
            if (!Settings.System.canWrite(getApplicationContext())) {

                Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS, Uri.parse("package:" + getPackageName()));
                Toast.makeText(getApplicationContext(), "Please, allow system settings for automatic logout ", Toast.LENGTH_LONG).show();
                startActivityForResult(intent, 200);
            }
        }else {
            Toast.makeText(getApplicationContext(), "You are not allowed to wright ", Toast.LENGTH_LONG).show();
        }
    }

Manifest permission

<uses-permission  android:name="android.permission.WRITE_SETTINGS" tools:ignore="ProtectedPermissions" />
Brianabriand answered 28/3, 2020 at 15:38 Comment(0)
C
6

Kotlin Version in Simple Steps

Follow these steps:

1. Add the permission's usage element in the manifest.xml normally:

<uses-permission
    android:name="android.permission.WRITE_SETTINGS"
    tools:ignore="ProtectedPermissions" />

2. Where you want to change the settings, check the write access:

if (context.canWriteSettings) {
    // change the settings here ...
} else {
    startManageWriteSettingsPermission()
}

3. Also add these lines of code in case of requesting the permission:

private fun startManageWriteSettingsPermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        Intent(
            Settings.ACTION_MANAGE_WRITE_SETTINGS,
            Uri.parse("package:${context.packageName}")
        ).let {
            startActivityForResult(it, REQUEST_CODE_WRITE_SETTINGS_PERMISSION)
        }
    }
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)

    when (requestCode) {
        REQUEST_CODE_WRITE_SETTINGS_PERMISSION -> {
            if (context.canWriteSettings) {
                // change the settings here ...
            } else {
                Toast.makeText(context, "Write settings permission is not granted!", Toast.LENGTH_SHORT).show()
            }
        }
    }
}

val Context.canWriteSettings: Boolean
    get() = Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.System.canWrite(this)

companion object {
    private const val REQUEST_CODE_WRITE_SETTINGS_PERMISSION = 5
}
Conservatory answered 10/11, 2020 at 8:42 Comment(0)
L
3

Mention below permission in AndroidManifest.xml

In Activity use below if else for changing setting.

if(Settings.System.canWrite(this)){
    // change setting here
}
else{
    //Migrate to Setting write permission screen. 
    Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
    intent.setData(Uri.parse("package:" + mContext.getPackageName()));
    startActivity(intent);
}
Libbylibeccio answered 23/1, 2018 at 10:29 Comment(1)
Please use this permission. <uses-permission android:name="android.permission.WRITE_SETTINGS" />Libbylibeccio
O
2

In my case i have solved by this way.:

public void checkSystemWriteSettings(Context context) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!Settings.System.canWrite(context)) {
                Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS, Uri.parse("package:" + context.getApplicationInfo().packageName));
                startActivity(intent);
            }else
            {
                Settings.System.putInt(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, 10);

            }
        }
    }
Oblivion answered 10/8, 2022 at 12:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.