Не могу получить разрешение WRITE_SETTINGS

85

Когда у меня есть целевой API 23 в Android M Preview 3, я не могу получить разрешение Manifest.permission.WRITE_SETTTINGS.

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

Запрос разрешения не вызывает диалоговое окно, которое я ожидал бы, но если я сделаю следующий вызов без этого разрешения,

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

Звонок будет за исключением случаев, когда у меня нет разрешения.

Я не уверен, что делать дальше. Есть ли новый API рингтонов для 23? Или это изменение разрешения просто сделало невозможным изменение мелодии звонка для каких-либо несистемных приложений?

Джастин
источник

Ответы:

134

Чтобы использовать WRITE_SETTINGS, на основе документации:

  1. Сохраните <uses-permission>элемент в манифесте как обычно.

  2. Позвоните,Settings.System.canWrite() чтобы узнать, имеете ли вы право выписывать настройки.

  3. Если canWrite()возвращается false, запуск на ACTION_MANAGE_WRITE_SETTINGSактивность , так что пользователь может договориться там , чтобы ваше приложение на самом деле писать настройки.

Другими словами, запись в настройки теперь является двойной подпиской (согласитесь на установку, согласитесь отдельно в Настройках, чтобы разрешить), что похоже на API администратора устройства, службы доступности и т. Д.

Также обратите внимание, что я еще не пробовал их использовать - это основано на исследовании, которое я провел вчера по изменениям Android 6.0 .

CommonsWare
источник
Спасибо, Марк! Работал как шарм. developer.android.com/preview/features/runtime-permissions.html нуждается в некотором обновлении, если у нас будет несколько новых способов запроса разрешений. (Я уже читал ваш блог перед публикацией, но, очевидно, не сохранил эту информацию, когда она мне была нужна)
Джастин
Это действительно сработало. Но для конечного пользователя это плохой подход. Есть ли признаки того, что Google меняет это поведение?
Fhl
2
@Fhl: Я не знаю, почему они пошли по этому пути вместо обычного dangerousподхода к разрешению времени выполнения, который они использовали с другими вещами в Android 6.0. Я буду удивлен, если это изменится в ближайшее время.
CommonsWare
2
вы можете указать свое приложение в намерении следующим образом:intent.setData(Uri.parse("package:" + Context.getPackageName()));
Олегас Гончаровас
8
Еще одна вещь, на которую следует обратить внимание, похоже, есть ошибка в Android, которая вызывает приложение, которое было ранее установлено, где пользователь дал разрешения на запись в диалоговом окне, описанном выше, где тумблер будет помещен в положение включения, но canWrite возвращает false .. Чтобы метод canWrite () возвращал true, пользователь должен выключить и снова включить этот переключатель ... Я вижу это в разработке, но, надеюсь, это не будет тем, что увидят клиенты.
Мэтт Вулф
45

В дополнение к ответу CommonsWare и комментарию Ogix, вот еще какой-то фиктивный код:

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;
}

Затем Fragment PopupwritePermission дает окно, в котором объясняется ситуация. Щелчок по кнопке OK откроет системное меню Android, в котором можно предоставить разрешение:

private void openAndroidPermissionsMenu() {
    Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
    intent.setData(Uri.parse("package:" + getActivity().getPackageName()));
    startActivity(intent);
}
KP.dev
источник
40

Предыдущие ответы великолепны, у меня есть небольшое дополнение, чтобы также получить результат для запроса разрешения.

 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);
            }
        }
    }

А потом в 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
        }
    }
Ишахак
источник
Я поместил ваш код, и он работает нормально, даже если разрешение предоставлено, но пользовательская мелодия звонка не назначается и по-прежнему имеет проблему с отказом в разрешении Write_Setting.
Зия Ур Рахман
4
ActivityCompat.requestPermissions (контекст, новая строка [] {Manifest.permission.WRITE_SETTINGS}, ....); нельзя использовать. Это специальное разрешение. Мы можем запросить это разрешение только с намерением, указанным в документации. Также до Marshmello разрешение предоставляется постоянно во время установки
Аноним
2
@yshahak какая у вас переменная MainActivity.CODE_WRITE_SETTINGS_PERMISSION?
Bruno Bieri
@BrunoBieri, да, ты прав, я это пропустил. Я отредактирую свой ответ, чтобы он был подробным.
yshahak 04
Так что MainActivity.CODE_WRITE_SETTINGS_PERMISSION?
Brackets
13

Это полный пример:

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);
    }
}
Даниэль Рауф
источник
intent.setData (Uri.parse ("пакет:" + getActivity (). getPackageName ()));
Ole K
8

Что касается Android Marshmellow, вам необходимо использовать разрешения времени выполнения, которые нацелены на большую безопасность, или использовать разрешение, когда это необходимо здесь документация

а для записи настроек документация здесь

В манифесте добавить

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

В вашем классе

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);
    }
}

И используйте это так

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();
        }
Абхишек Гарг
источник
5

Разрешение android.permission.WRITE_SETTINGSсейчас в группе signature|appop|pre23|preinstalledвродеandroid.permission.CHANGE_NETWORK_STATE иandroid.permission.SYSTEM_ALERT_WINDOW

Это означает, что вы получаете его на SDK 22 и ниже. В более новой версии вы должны быть оператором приложения.

прохожий
источник
4

Я использовал ниже как ..

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();
        }
    }

Разрешение манифеста

<uses-permission  android:name="android.permission.WRITE_SETTINGS" tools:ignore="ProtectedPermissions" />
Энамул Хак
источник
2

Упомяните ниже разрешение в AndroidManifest.xml

В действии используйте ниже, если еще для изменения настройки.

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);
}
Сураб Теджрадж
источник
Пожалуйста, используйте это разрешение. <uses-permission android: name = "android.permission.WRITE_SETTINGS" />
Сураб Теджрадж,
1

Kotlin Version in Simple Steps

Следуй этим шагам:

1. Добавьте элемент использования разрешения в manifest.xmlобычно:

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

2. Там, где вы хотите изменить настройки, проверьте доступ на запись:

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

3. Также добавьте эти строки кода в случае запроса разрешения:

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
}
аминография
источник