Não é possível obter permissão WRITE_SETTINGS

85

Quando tenho uma API alvo de 23 no Android M Preview 3, não consigo adquirir a permissão Manifest.permission.WRITE_SETTTINGS.

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

Solicitar permissão não abre a caixa de diálogo que eu esperava, mas se eu fizer a seguinte chamada sem essa permissão,

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

A ligação vai, exceto porque eu não tenho permissão.

Não tenho certeza para onde ir a partir daqui. Existe uma nova API de ringtone para 23? Ou essa mudança de permissão apenas tornou impossível para qualquer aplicativo que não seja do sistema alterar o toque?

Justin
fonte

Respostas:

134

Para usar WRITE_SETTINGS, com base nos documentos:

  1. Tenha o <uses-permission>elemento no manifesto normalmente.

  2. LigueSettings.System.canWrite() para ver se você está qualificado para redigir as configurações.

  3. Se canWrite()retornar false, inicie a ACTION_MANAGE_WRITE_SETTINGSatividade para que o usuário possa concordar em permitir que seu aplicativo realmente grave nas configurações.

Em outras palavras, escrever nas configurações agora é uma opção dupla (concordar em instalar, concordar separadamente em Configurações para permitir), semelhante a APIs de administrador de dispositivo, serviços de acessibilidade etc.

Observe também que ainda não tentei usá-los - isso se baseia em uma pesquisa que fiz ontem sobre as alterações do Android 6.0 .

CommonsWare
fonte
Obrigado Mark! Funcionou como um encanto. developer.android.com/preview/features/runtime-permissions.html precisa de alguma atualização se vamos ter várias novas maneiras de solicitar permissões. (Eu já tinha lido o seu blog antes de postar, mas obviamente não guardei essa informação quando precisei)
Justin
Isso funcionou, de fato. Mas para o usuário final, essa é uma abordagem ruim. Algum sinal do Google mudando esse comportamento?
Fhl
2
@Fhl: Não sei por que eles seguiram esse caminho em vez da dangerousabordagem de permissão de tempo de execução regular que usaram com outras coisas no Android 6.0. Ficarei surpreso se isso mudar em breve.
CommonsWare
2
você pode especificar seu aplicativo no intent como este:intent.setData(Uri.parse("package:" + Context.getPackageName()));
Olegas Gončarovas
8
Outra coisa a observar é que parece haver um bug no Android que causa um aplicativo que foi instalado anteriormente, onde o usuário concedeu permissões de gravação na caixa de diálogo descrita acima, onde a chave de alternância será colocada na posição ativada, mas canWrite retornará falso. Para fazer com que o método canWrite () retorne true, o usuário deve desligar e ligar novamente ... Vejo isso em desenvolvimento, mas espero que não seja algo que os clientes vejam.
Matt Wolfe
45

Além da resposta do CommonsWare e do comentário do Ogix, aqui está um código fictício:

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

O Fragment PopupwritePermission então fornece uma janela onde a situação é explicada. Um clique no botão OK abrirá o menu do sistema Android, onde a permissão pode ser concedida:

private void openAndroidPermissionsMenu() {
    Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
    intent.setData(Uri.parse("package:" + getActivity().getPackageName()));
    startActivity(intent);
}
KP.dev
fonte
40

As respostas anteriores são ótimas, tenho pouco acréscimo para obter também o resultado para o pedido de permissão.

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

E então no 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
        }
    }
Yshahak
fonte
Eu coloquei seu código e ele funciona bem, até mesmo a permissão concedida, mas ainda assim o toque personalizado não está sendo atribuído e ainda tenho o problema de permissão de Write_Setting negada.
Zia Ur Rahman
4
ActivityCompat.requestPermissions (context, new String [] {Manifest.permission.WRITE_SETTINGS}, ....); não pode ser usado. É uma permissão especial. Só podemos solicitar essa permissão com a intenção conforme declarado na documentação. Também antes de Marshmello, a permissão é concedida permanentemente no momento da instalação
Anônimo
2
@yshahak qual é a sua variável MainActivity.CODE_WRITE_SETTINGS_PERMISSION?
Bruno Bieri
@BrunoBieri sim você certo, omiti isso. Vou editar minha resposta para que seja detalhada.
yshahak
Então o que é MainActivity.CODE_WRITE_SETTINGS_PERMISSION?
Parênteses
13

Este é um exemplo completo:

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);
    }
}
Daniel Raouf
fonte
intent.setData (Uri.parse ("pacote:" + getActivity (). getPackageName ()));
Ole K
8

A partir do Android Marshmellow, você precisa usar permissões de tempo de execução que visam a mais segurança, ou usar permissão quando necessário aqui está a documentação

e para a documentação das configurações de gravação está aqui

No manifesto adicionar

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

Em sua classe

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

E usar assim

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();
        }
Abhishek Garg
fonte
5

A permissão android.permission.WRITE_SETTINGSestá agora no grupo signature|appop|pre23|preinstalledcomo android.permission.CHANGE_NETWORK_STATEeandroid.permission.SYSTEM_ALERT_WINDOW

Isso significa que você o obtém no SDK 22 e abaixo. Na versão mais recente, você deve ser um operador de aplicativo.

passsy
fonte
4

Eu usei abaixo como ..

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

Permissão de manifesto

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

Mencione a permissão abaixo em AndroidManifest.xml

Em Atividade, use abaixo se mais para alterar a configuração.

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);
}
Sourabh Tejraj
fonte
Por favor, use esta permissão. <uses-permission android: name = "android.permission.WRITE_SETTINGS" />
Sourabh Tejraj
1

Kotlin Version in Simple Steps

Siga esses passos:

1. Adicione o elemento de uso da permissão manifest.xmlnormalmente:

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

2. Onde você deseja alterar as configurações, verifique o acesso de gravação:

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

3. Adicione também estas linhas de código no caso de solicitar a permissão:

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
}
aminografia
fonte