No se puede obtener el permiso WRITE_SETTINGS

85

Cuando tengo una API de destino de 23 en Android M Preview 3, parece que no puedo adquirir el permiso Manifest.permission.WRITE_SETTTINGS.

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

Solicitar permiso no muestra el cuadro de diálogo que esperaba, pero si hago la siguiente llamada sin este permiso,

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

La llamada se hará excepto porque no tengo el permiso.

No estoy seguro de adónde ir desde aquí. ¿Existe una nueva API de tono de llamada para 23? ¿O este cambio de permiso simplemente hizo imposible que cualquier aplicación ajena al sistema cambiara el tono de llamada?

Justin
fuente

Respuestas:

134

Para usar WRITE_SETTINGS, según los documentos:

  1. Tener el <uses-permission>elemento en el manifiesto como de costumbre.

  2. LlameSettings.System.canWrite() para ver si es elegible para escribir configuraciones.

  3. Si canWrite()regresa false, inicie la ACTION_MANAGE_WRITE_SETTINGSactividad para que el usuario pueda aceptar que su aplicación realmente escriba en la configuración.

En otras palabras, escribir en la configuración ahora es una opción doble (aceptar instalar, aceptar por separado en Configuración para permitir), similar a las API de administración de dispositivos, servicios de accesibilidad, etc.

También tenga en cuenta que todavía no he intentado usarlos; esto se basa en una investigación que hice ayer sobre los cambios de Android 6.0 .

CommonsWare
fuente
Gracias Mark! Trabajado como un encanto. developer.android.com/preview/features/runtime-permissions.html necesita algunas actualizaciones si vamos a tener varias formas nuevas de solicitar permisos. (Ya había leído su blog antes de publicarlo, pero obviamente no conservé esa información cuando la necesitaba)
Justin
Esto funcionó, de hecho. Pero para el usuario final, este es un mal enfoque. ¿Alguna señal de que Google esté cambiando este comportamiento?
Fhl
2
@Fhl: No sé por qué tomaron esta ruta en lugar del dangerousenfoque habitual de permisos de tiempo de ejecución que usaron con otras cosas en Android 6.0. Me sorprendería que esto cambiara pronto.
CommonsWare
2
puede especificar su aplicación en la intención de esta manera:intent.setData(Uri.parse("package:" + Context.getPackageName()));
Olegas Gončarovas
8
Otra cosa a tener en cuenta es que parece haber un error en Android que causa una aplicación que se instaló previamente donde el usuario otorgó permisos de escritura en el cuadro de diálogo descrito anteriormente, donde el interruptor de palanca se colocará en la posición habilitada pero canWrite devuelve falso. Para que el método canWrite () vuelva a ser verdadero, el usuario debe apagar y encender el interruptor ... Veo esto en desarrollo, pero espero que no sea algo que los clientes vean.
Matt Wolfe
45

Además de la respuesta de CommonsWare y el comentario de Ogix, aquí hay un código ficticio:

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

El Fragment PopupwritePermission luego da una ventana donde se explica la situación. Un clic en el botón Aceptar abrirá el menú del sistema Android donde se puede otorgar el permiso:

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

Las respuestas anteriores son geniales, solo tengo una pequeña adición para obtener también el resultado de la solicitud de permiso.

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

Y luego en el 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
fuente
Puse su código, y funciona bien, incluso el permiso otorgado, pero aún así el tono de llamada personalizado no se está asignando y todavía tengo el problema de permiso denegado Write_Setting.
Zia Ur Rahman
4
ActivityCompat.requestPermissions (contexto, nueva cadena [] {Manifest.permission.WRITE_SETTINGS}, ....); No puede ser usado. Es un permiso especial. Solo podemos solicitar este permiso con la intención que se indica en la documentación. También antes de Marshmello, el permiso se otorga permanentemente en el momento de la instalación
Anónimo
2
@yshahak ¿cuál es tu variable MainActivity.CODE_WRITE_SETTINGS_PERMISSION?
Bruno Bieri
@BrunoBieri sí tienes razón, lo omití. Editaré mi respuesta para que sea detallada.
yshahak
Entonces, ¿qué MainActivity.CODE_WRITE_SETTINGS_PERMISSION?
Corchetes
12

Este es un ejemplo 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
fuente
intent.setData (Uri.parse ("paquete:" + getActivity (). getPackageName ()));
Ole K
7

A partir de Android Marshmellow, debe usar permisos de tiempo de ejecución que apuntan a una mayor seguridad, o usar permisos cuando sea necesario aquí es documentación

y para la documentación de configuración de escritura está aquí

En manifiesto agregar

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

En tu clase

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

Y úsalo así

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
fuente
5

El permiso android.permission.WRITE_SETTINGSahora está en el grupo signature|appop|pre23|preinstalledcomo android.permission.CHANGE_NETWORK_STATEyandroid.permission.SYSTEM_ALERT_WINDOW

Esto significa que lo obtienes en SDK 22 e inferior. En la versión más reciente, debe ser un operador de la aplicación.

pasivo
fuente
4

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

Permiso manifiesto

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

Mencione a continuación el permiso en AndroidManifest.xml

En Actividad, use a continuación si no es así para cambiar la configuración.

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
fuente
Utilice este permiso. <uses-allow android: name = "android.permission.WRITE_SETTINGS" />
Sourabh Tejraj
1

Kotlin Version in Simple Steps

Sigue estos pasos:

1. Agregue el elemento de uso del permiso manifest.xmlnormalmente:

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

2. Donde desee cambiar la configuración, verifique el acceso de escritura:

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

3. También agregue estas líneas de código en caso de solicitar el permiso:

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
}
aminografía
fuente