Как получить опцию «Никогда не спрашивать снова» в разрешении во время выполнения

В моем приложении я создал имя класса Untility, где я написал код для разрешения READ_EXTERNAL_STORAGE. Но проблема в том, что когда я нажимаю «Запретить» в приложении, я не нахожу уведомление о предупреждении, чтобы разрешить его снова. Первоначально у меня есть опция «Никогда не спрашивать снова». Как только я нажал на нее, диалоговое окно исчезло. и теперь, после запуска приложения, если я нажму «Отклонить», я больше не могу найти диалоговое сообщение, чтобы разрешить его снова. Как я могу изменить свой код, чтобы каждый раз отображать это сообщение.

Мой класс полезности

public class Utility {

public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 123;

@TargetApi(Build.VERSION_CODES.M)
public static boolean checkPermission(final Context context) {
    int currentAPIVersion = Build.VERSION.SDK_INT;
    if (currentAPIVersion >= android.os.Build.VERSION_CODES.M) {
        if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            if (ActivityCompat.shouldShowRequestPermissionRationale((Activity) context, Manifest.permission.READ_EXTERNAL_STORAGE)) {
                Log.v("TAG", "Permission is granted");
            }
            else {
                ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
            }
            return false;
        } else {
            return true;
        }
    } else {
        return true;
    }
 }

Мой другой класс здесь, который я называю классом Utility,

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    switch (requestCode) {
        case Utility.MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE:
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                if(userChoosenTask.equals("Take Photo"))
                    cameraIntent();
                else if(userChoosenTask.equals("Choose from Library"))
                    galleryIntent();

                    //do something here
            } else {
                //code for deny
                Toast.makeText(DetailMyColleague.this, "Permission Denied", Toast.LENGTH_SHORT).show();
            }
            break;
    }
}
private void selectImage() {
    final CharSequence[] items = { "Take Photo", "Choose from Library",
            "Cancel" };

    AlertDialog.Builder builder = new AlertDialog.Builder(DetailMyColleague.this);
    builder.setTitle("Add Photo!");
    builder.setItems(items, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int item) {
            boolean result=Utility.checkPermission(DetailMyColleague.this);

            if (items[item].equals("Take Photo")) {
                userChoosenTask ="Take Photo";
                if(result)
                    cameraIntent();

            } else if (items[item].equals("Choose from Library")) {
                userChoosenTask ="Choose from Library";
                if(result)
                    galleryIntent();

            } else if (items[item].equals("Cancel")) {
                dialog.dismiss();
            }
        }
    });
    builder.show();
}
private void galleryIntent()
{
    Intent intent = new Intent();
    intent.setType("image/*");
    intent.setAction(Intent.ACTION_GET_CONTENT);//
    startActivityForResult(Intent.createChooser(intent, "Select File"),SELECT_FILE);
}
private void cameraIntent()
{
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    startActivityForResult(intent, REQUEST_CAMERA);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (resultCode == Activity.RESULT_OK) {
        if (requestCode == SELECT_FILE)
            onSelectFromGalleryResult(data);
        else if (requestCode == REQUEST_CAMERA)
            onCaptureImageResult(data);
    }
}

private void onCaptureImageResult(Intent data) {
    Bitmap thumbnail = (Bitmap) data.getExtras().get("data");
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    thumbnail.compress(Bitmap.CompressFormat.JPEG, 90, bytes);

    File destination = new File(Environment.getExternalStorageDirectory(),
            System.currentTimeMillis() + ".jpg");

    FileOutputStream fo;
    try {
        destination.createNewFile();
        fo = new FileOutputStream(destination);
        fo.write(bytes.toByteArray());
        fo.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

    profilePic.setImageBitmap(thumbnail);
}
@SuppressWarnings("deprecation")
private void onSelectFromGalleryResult(Intent data) {

    Bitmap bm=null;
    if (data != null) {
        try {
            bm = MediaStore.Images.Media.getBitmap(getApplicationContext().getContentResolver(), data.getData());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    profilePic.setImageBitmap(bm);
}



Ответы (4)


Вы не можете показать то же сообщение запроса, как только они говорят «больше не спрашивать», но вы можете проверить разрешение и, если отказано, показать пользовательский диалог, который направляет их на страницу настроек с намерением:

startActivityForResult(new Intent(android.provider.Settings.ACTION_SETTINGS), 0);

Если проверка разрешений не удалась:

if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
    // show a dialog
    new AlertDialog.Builder(this).setMessage("You need to enable permissions to use this feature").setPositiveButton("Go to settings", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            // navigate to settings
            startActivityForResult(new Intent(android.provider.Settings.ACTION_SETTINGS), 0);
        }
    }).setNegativeButton("Go back", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            // leave?
            MyActivity.this.onBackPressed();
        }
    }).show();
}
person anomeric    schedule 27.07.2017
comment
где я могу написать эту строку. Извините, я новичок в разработке Android - person ; 27.07.2017
comment
Я получаю много ошибок, например MyActivity.this.onBackPressed(); startActivityForResult - person ; 27.07.2017
comment
Вы должны изменить некоторые из этих значений. Я использовал MyActivity в качестве заполнителя для названия вашей активности, если вы оттуда звоните. В противном случае вам потребуется доступ к контексту для выполнения вызовов onBackPressed или startActivityForResult. - person anomeric; 27.07.2017
comment
Было бы очень полезно, если бы вы написали этот код на основе моего кода. - person ; 27.07.2017
comment
Если вы поместите этот фрагмент в метод onCreate() вашей активности, просто замените MyActivity на имя вашей активности. - person anomeric; 27.07.2017
comment
на самом деле я не создаю никаких действий с этим классом разрешений. Это простой класс Java с логикой, который я использую для отображения разрешений. - person ; 27.07.2017
comment
У вас должна быть активность для отображения диалога. - person anomeric; 27.07.2017
comment
Да, у меня есть действие, в котором я вызываю эту логику, но я хочу создать класс Util, в котором я буду использовать всю логику разрешений. - person ; 27.07.2017
comment
Я думаю, что перемещение контекста активности в этом случае более сложно, чем необходимо, но правки должны делать то, что вам нравится. Имейте в виду, что onBackPressed был просто заполнителем для любого действия, которое вы хотите выполнить, когда пользователь не хочет переходить к настройкам, чтобы разрешить ваше разрешение. - person anomeric; 27.07.2017
comment
Большое спасибо за Вашу помощь. Это именно то, что я хочу в своем приложении. И извините за беспокойство. На самом деле я очень новичок в разработке Android. Мне еще нужно многому научиться. Спасибо еще раз. - person ; 27.07.2017

Состояние «Больше не показывать» можно выяснить, когда разрешение уже «Отказано», а метод shouldShowRequestPermissionRationale() возвращает false

Этот метод можно использовать как возможность показать дополнительную информацию пользователю, когда системные диалоги не могут быть показаны (пользователь выбирает «Принять» или «Не показывать снова») системой.

Deny             >> shouldShowRequestPermissionRationale(permission) -> true
Don't ask again  >> shouldShowRequestPermissionRationale(permission) -> false
Accept           >> shouldShowRequestPermissionRationale(permission) -> false

В случае, если вы не можете выполнить какое-либо действие, в то время как все запрошенные вами разрешения запрещены, и хотя бы одно из них отклонено флажком «Больше не спрашивать», лучший подход — перейти к настройкам приложения в системе.

Вот решение для рабочего процесса «Принять/Отклонить/Больше не спрашивать». Также здесь приведен пример для Jetpack Activity Result API. (запрос нескольких разрешений и переход к другому действию с возвратом)

Комментарии к коду:

  • Запрос разрешения (с помощью Activity Result API) также содержит проверку статуса разрешения «Предоставлено» внутри системы, вы получаете только истинный/ложный результат для каждого запрошенного вами разрешения.
  • actionPermXXX — это место, где вы можете указать свои действия с системой сразу после того, как разрешение было предоставлено (например, начать наблюдение за местоположением и/или получать изображения из внешнего хранилища и т. д.)
  • Состояние разрешения «Больше не показывать» можно сбросить, вручную изменив это разрешение в «Настройках приложений» («Настройки» > «Приложения» > «Ваше_приложение» > «Разрешения»).

PS. Код не идеален, но хорош для понимания

Фрагмент

class Fragment_ActiityResultAPI_RequestMultiplePermissions : Fragment(){

    val actionPermLocation = {
        tvPerm1.text = "GRANTED";
        tvPerm1.setTextColor(Color.GREEN)
    }
    val actionPermReadExt = {
        tvPerm2.text = "GRANTED";
        tvPerm2.setTextColor(Color.GREEN)
    }
    val permissionsAll = mutableMapOf(
        Manifest.permission.ACCESS_FINE_LOCATION  to actionPermLocation,
        Manifest.permission.READ_EXTERNAL_STORAGE to actionPermReadExt,
    )
    private val arcRequestPermissions = registerForActivityResult(RequestMultiplePermissions()){ perms ->
        perms.entries.forEach {
            if(it.value){
                permissionsAll[it.key]?.invoke()
            }
        }
    }
    private val arcNavigateToSettings = registerForActivityResult(StartSettingsActivityContract()) {
        arcRequestPermissions.launch(permissionsAll.keys.toTypedArray())
    }
    override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
        return inflater.inflate(R.layout.fragment_layout, container, false)
    }
    override fun onViewCreated(layoutView: View, savedInstanceState: Bundle?) {
        super.onViewCreated(layoutView, savedInstanceState)
        // CHECK PERMISSIONS AT FRAGMENT START
        arcRequestPermissions.launch(permissionsAll.keys.toTypedArray())
        btn.setOnClickListener {
            // CHECK PERMISSION AT BUTTON CLICK
            processPermission()
        }
    }
    private fun processPermission() {
        var atLeastOnePermDenied         = false
        var atLeastOnePermAsDontAskAgain = false
        permissionsAll.keys.toTypedArray().forEach {
            atLeastOnePermDenied         = atLeastOnePermDenied         || checkPermDenied(it)
            atLeastOnePermAsDontAskAgain = atLeastOnePermAsDontAskAgain || checkPermDontAskAgain(it)
        }
        if(atLeastOnePermAsDontAskAgain){
            showAlertNavigateToAppSettings()
            return
        }
        if(atLeastOnePermDenied){
            arcRequestPermissions.launch(permissionsAll.keys.toTypedArray())
            return
        }
        Utils.toast(requireContext(), ">>>  Execute your target action!!  <<<")
    }
    private fun checkPermDenied(perm: String): Boolean {
        return (ActivityCompat.checkSelfPermission(requireContext(), perm)
                ==
                PackageManager.PERMISSION_DENIED)
    }
    private fun checkPermDontAskAgain(perm: String): Boolean {
        return checkPermDenied(perm) && !shouldShowRequestPermissionRationale(perm)
    }
    private fun showAlertNavigateToAppSettings() {
        val builder = AlertDialog.Builder(requireContext())
        builder.setMessage("You have to grant permissions for action")
        builder.setPositiveButton("Go to Settings") { dialog, which -> // Do nothing but close the dialog
            arcNavigateToSettings.launch(1)
        }
        builder.setNegativeButton("Cancel") { dialog, which -> // Do nothing
            dialog.dismiss()
        }
        val alert = builder.create()
        alert.show()
    }
    class StartSettingsActivityContract : ActivityResultContract<Int, String?>() {
        override fun createIntent(context: Context, input: Int): Intent {
            return Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
                val uri = Uri.fromParts("package", context.packageName, null)
                this.data = uri
            }
        }
        override fun parseResult(resultCode: Int, intent: Intent?): String? {
            return ""
        }
    }
}

Макет

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@android:color/white">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:layout_width="150dp"
                android:layout_height="wrap_content"
                android:text="LOCATION: "
                android:textColor="@android:color/darker_gray"/>

            <TextView
                android:id="@+id/tvPerm1"
                android:layout_width="100dp"
                android:layout_height="wrap_content"
                android:text="Denied"
                android:textColor="@android:color/darker_gray"/>

        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:layout_width="150dp"
                android:layout_height="wrap_content"
                android:text="READ_STORAGE: "
                android:textColor="@android:color/darker_gray"/>

            <TextView
                android:id="@+id/tvPerm2"
                android:layout_width="100dp"
                android:layout_height="wrap_content"
                android:text="Denied"
                android:textColor="@android:color/darker_gray"/>

        </TableRow>

    </LinearLayout>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <Button
            android:id="@+id/btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="50dp"
            android:layout_gravity="center_horizontal"
            android:text="Launch a Camera"/>
    </FrameLayout>

</LinearLayout>

build.gradle

implementation 'androidx.activity:activity-ktx:1.2.1'
implementation 'androidx.fragment:fragment:1.3.0-alpha05'
person chatlanin    schedule 21.03.2021

Как я могу изменить свой код, чтобы каждый раз отображать это сообщение.

Вы не можете. Если пользователь отметил «Больше не спрашивать», то единственный способ, которым пользователь может предоставить разрешение, — через приложение «Настройки». Как только пользователь отметит «больше не спрашивать» и откажет в разрешении, вы не сможете отобразить диалоговое окно разрешения для любого разрешения в этой группе разрешений.

person CommonsWare    schedule 27.07.2017
comment
ок Могу ли я сделать это вручную в эмуляторе или телефоне, чтобы показать его снова - person ; 27.07.2017
comment
@ktina: Как пользователь, вы можете зайти в «Настройки» > «Приложения», найти свое приложение, нажать «Разрешения» и предоставить разрешение. Я не знаю никаких способов вернуть статус «Больше не спрашивать», кроме как полностью удалить приложение. - person CommonsWare; 27.07.2017

Флажок «Больше не спрашивать» отображается только после более чем одного отказа на код разрешения. Если вы отметите «Больше не спрашивать» и нажмете «Запретить», разрешение будет автоматически отклонено. Если вы хотите сделать выбор еще раз, перейдите на страницу «Информация о приложении» вашего приложения и очистите хранилище данных вашего приложения. Теперь, если вы вернетесь в свое приложение и снова запросите разрешение. Затем отображается диалоговое окно разрешений по умолчанию. Документацию можно посмотреть здесь.

person François Legrand    schedule 27.07.2017