Android M — проверьте разрешение времени выполнения — как определить, поставил ли пользователь отметку «Больше не спрашивать»?

Согласно этому: http://developer.android.com/preview/features/runtime-permissions.html#coding приложение может проверять разрешения времени выполнения и запрашивать разрешения, если они еще не были предоставлены. После этого отобразится следующий диалог:

В случае, если Пользователь отклоняет важное разрешение, приложение должно отображать объяснение, почему необходимо разрешение и какое влияние имеет отказ. В этом диалоговом окне есть два варианта:

  1. повторить попытку (снова запрашивается разрешение);
  2. deny (приложение будет работать без этого разрешения).

Однако, если пользователь проверяет Больше никогда не спрашивать , второй диалог с объяснением не должен отображаться, особенно если пользователь уже отказался однажды . Теперь вопрос: как мое приложение узнает, проверил ли пользователь параметр Больше не спрашивать ? IMO onRequestPermissionsResult (int requestCode, String [] permissions, int [] grantResults) не дает мне этой информации.

Второй вопрос: делает ли У Google есть планы включить настраиваемое сообщение в диалоговое окно разрешений, которое объяснит, почему приложению требуется разрешение? Таким образом, никогда не будет второго диалогового окна, которое, безусловно, улучшит ux.


Developer Preview 2 вносит некоторые изменения в то, как разрешения запрашиваются приложением (см. Также http ://developer.android.com/preview/support.html#preview2-notes).

Первый диалог теперь выглядит так:

Нет флажка «Больше никогда не показывать» (в отличие от предварительного просмотра для разработчиков 1). Если пользователь отказывает в разрешении и если разрешение необходимо для приложения, он может представить другой диалог, чтобы объяснить причину, по которой приложение запрашивает это разрешение, например вот так:

Если пользователь снова откажется, приложение должно либо выключите, если ему абсолютно необходимо это разрешение, либо продолжайте работу с ограниченной функциональностью. Если пользователь пересматривает (и выбирает повторную попытку), разрешение запрашивается снова. На этот раз приглашение выглядит так:

Второй раз отображается флажок «Больше не спрашивать». Если пользователь снова отказывает и флажок установлен, ничего больше не должно происходить. Отмечен ли флажок, можно определить с помощью Activity.shouldShowRequestPermissionRationale (String), например вот так:

  if (shouldShowRequestPermissionRationale (Manifest.permission.WRITE_CONTACTS)) {...  

Это то, что документация Android говорит (https://developer.android.com/training/permissions/requesting.html):

Чтобы помочь найти ситуации, в которых вам нужно предоставить дополнительные объяснения, система предоставляет метод Activity.shouldShowRequestPermissionRationale (String). Этот метод возвращает значение true, если приложение ранее запрашивало это разрешение, а пользователь отклонил запрос. Это означает, что вам, вероятно, следует объяснить пользователю, зачем вам это разрешение.

Если пользователь ранее отклонил запрос разрешения и выбрал параметр Больше не спрашивать в системе запросов на разрешение диалог, этот метод возвращает false. Метод также возвращает false, если политика устройства запрещает приложению иметь это разрешение.

Чтобы узнать, отказано ли пользователю с помощью «никогда не спрашивать снова», вы можете еще раз проверить метод shouldShowRequestPermissionRationale в своем onRequestPermissionsResult, когда пользователь не предоставил разрешение.

  @Overridepublic void onRequestPermissionsResult (int requestCode, String [] permissions, int [] grantResults) {if (requestCode == REQUEST_PERMISSION) {//для каждой проверки разрешений  если пользователь предоставил/отклонил их//вы можете сгруппировать обоснование в одном диалоге,//это просто пример для (int i = 0, len = permissions.length; i  

Вы можете открыть настройки своего приложения с помощью этого кода:

  Intent intent = new Intent (Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts ("package", getPackageName (), null); intent.setData (uri); startActivityForResult (intent, REQUEST_PERMISSION);  /pre> 

Невозможно отправить пользователя прямо на страницу авторизации.


Вы можете проверить shouldShowRequestPermissionRationale () в своем onRequestPermissionsResult () .

https://youtu.be/C8lUdPVSzDk?t=2m23s

Проверьте, было ли предоставлено разрешение в onRequestPermissionsResult () код>. Если не , отметьте shouldShowRequestPermissionRationale () .

  1. Если этот метод возвращает true , то покажите объяснение, почему требуется это конкретное разрешение. Затем в зависимости от выбора пользователя снова requestPermissions () .
  2. Если он возвращает false , то выдает сообщение об ошибке, что разрешение не было предоставлено и приложение не может продолжить работу или определенная функция отключена.

Ниже приведен пример кода.

  @Overridepublic void onRequestPermissionsResult (int requestCode, @NonNull String [] permissions, @NonNull int [] grantResults) {super  .onRequestPermissionsResult (requestCode, разрешения, grantResults);  switch (requestCode) {case STORAGE_PERMISSION_REQUEST: if (grantResults.length> 0 && grantResults [0] == PackageManager.PERMISSION_GRANTED) {//разрешение было предоставлено :) downloadFile ();  } else {//разрешение не было предоставлено if (getActivity () == null) {return;  } если (ActivityCompat.shouldShowRequestPermissionRationale (getActivity (), Manifest.permission.WRITE_EXTERNAL_STORAGE)) {showStoragePermissionRationale ();  } else {Snackbar snackbar = Snackbar.make (getView (), getResources (). getString (R.string.message_no_storage_permission_snackbar), Snackbar.LENGTH_LONG);  Snackbar.setAction (getResources (). getString (R.string.settings), new View.OnClickListener () {@Override public void onClick (View v) {if (getActivity () == null) {return;} Намерение намерения =  new Intent (); intent.setAction (Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts («пакет», getActivity (). getPackageName (), null); intent.setData (uri); OrderDetailFragment.this.startActivity (intent.startActivity  );}});  Snackbar.show ();  } } перемена;  }}  

Очевидно, карты Google делают именно это для разрешения местоположения.

2


Вот хороший и простой способ проверить текущий статус разрешений:

  @Retention (RetentionPolicy.SOURCE) @  IntDef ({GRANTED, DENIED, BLOCKED_OR_NEVER_ASKED}) public @interface PermissionStatus {} public static final int GRANTED = 0;  общедоступный статический конечный int DENIED = 1;  общедоступный статический финальный int BLOCKED_OR_NEVER_ASKED = 2;  @PermissionStatus public static int getPermissionStatus (Activity activity, String androidPermissionName) {if (ContextCompat.checkSelfPermission (activity, androidPermissionName)! = PackageManager.PERMISSION_GRANTED) {if (! ActivityCompat.shouldShowRequestPermissionRationED_Name;  } return DENIED;  } return GRANTED;  }  

Предостережение: возвращает BLOCKED_OR_NEVER_ASKED при первом запуске приложения до того, как пользователь принял/отклонил разрешение через приглашение пользователя (на устройствах SDK 23+)

Обновление:

Библиотека поддержки Android теперь, похоже, имеет очень похожий класс android. support.v4.content. PermissionChecker , который содержит checkSelfPermission () , который возвращает:

  public static final int PERMISSION_GRANTED = 0; public static final int PERMISSION_DENIED  = -1; общедоступный статический финал int PERMISSION_DENIED_APP_OP = -2;  

5


После того, как пользователь поставил отметку «Больше не спрашивать», вопрос не может быть отображен снова. Но пользователю можно объяснить, что он ранее отказал в разрешении и необходимо предоставить разрешение в настройках. И дайте ему ссылку на настройки с помощью следующего кода:

  @Overridepublic void onRequestPermissionsResult (int permsRequestCode, String [] permissions, int [] grantResults) {if (grantResults.length  > 0 && grantResults [0] == PackageManager.PERMISSION_GRANTED) {//теперь у вас есть разрешение, продолжайте//TODO: something} else {if (ActivityCompat.shouldShowRequestPermissionRationale (MainActivity.this, Manifest.permission.READ_CALL_LOG)) {//сейчас пользователь отказал в разрешении (но не навсегда!)} else {//теперь пользователь отказал в разрешении навсегда!  Snackbar snackbar = Snackbar.make (findViewById (android.R.id.content), "Вы ранее отклонили это разрешение.  N" + "Вы должны утвердить это разрешение в " Разрешения  "в настройках приложения на вашем устройстве.  ", Snackbar.LENGTH_LONG) .setAction (" Настройки ", новый View.OnClickListener () {@Override public void onClick (Просмотр представления) {startActivity (новое намерение (android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse ("  "+ BuildConfig.APPLICATION_ID)));}});  Просмотр снэкбарвиев = снэкбар.getView ();  TextView textView = (TextView) snackbarView.findViewById (android.support.design.R.id.snackbar_text);  textView.setMaxLines (5); //Или столько, сколько вам нужно Snackbar.show ();  }} return;}  

1


Вы можете определить это, проверив, должно ли обоснование отображаться внутри onRequestPermissionsResult () метод обратного вызова. И если вы обнаружите, что для какого-либо разрешения установлено значение никогда больше не спрашивать , вы можете попросить пользователей предоставить разрешения в настройках.

Моя полная реализация будет такой, как показано ниже. Он работает как для одиночных , так и для множественных запросов разрешений. Используйте следующую команду или используйте мою библиотеку напрямую.

  @Overridepublic void onRequestPermissionsResult (int requestCode, @NonNull String [] permissions, @NonNull int [] grantResults) {if (permissions.  длина == 0) {возврат;  } логическое allPermissionsGranted = true;  if (grantResults.length> 0) {for (int grantResult: grantResults) {if (grantResult! = PackageManager). PERMISSION_GRANTED) {allPermissionsGranted = false;  перемена;  }}} если (! allPermissionsGranted) {логическое значение somePermissionsForeverDenied = false;  for (String permission: permissions) {if (ActivityCompat.shouldShowRequestPermissionRationale (this, разрешение)) {//отказано Log.e ("отказано", разрешение);  } else {if (ActivityCompat.checkSelfPermission (this, permission) == PackageManager.PERMISSION_GRANTED) {//разрешено Log.e ("разрешено", разрешение);  } else {//установить, чтобы никогда больше не спрашивать Log.e ("никогда не спрашивать снова", разрешение);  somePermissionsForeverDenied = true;  }}} if (somePermissionsForeverDenied) {последний AlertDialog.Builder alertDialogBuilder = новый AlertDialog.Builder (это);  alertDialogBuilder.setTitle («Требуются разрешения») .setMessage («Вы принудительно отказали в некоторых требуемых разрешениях« + »для этого действия. Откройте настройки, перейдите к разрешениям и разрешите их.») .setPositiveButton («Настройки», новое  DialogInterface.OnClickListener () {@Override public void onClick (DialogInterface dialog, int which) {Intent intent = new Intent (Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts ("package", getPackageName (), null)); intent.ag  .FLAG_ACTIVITY_NEW_TASK); startActivity (intent);}}) .setNegativeButton ("Отмена", новый DialogInterface.OnClickListener () {@Override public void onClick (DialogInterface dialog, int which) {}}) .setCancelable (false) .create (  ) .Показать();  }} else {switch (requestCode) {//действуем в соответствии с кодом запроса, который использовался при запросе разрешений.  }}}  

2


Может быть полезно для кого-то: -

Я заметил, что если мы проверим флаг shouldShowRequestPermissionRationale () в методе обратного вызова onRequestPermissionsResult (), он покажет только два состояния .

Состояние 1: -Возврат истина: - Каждый раз, когда пользователь нажимает "Запретить разрешения" (включая самый первый раз).

Состояние 2: -Возвращает false: - если пользователь выбирает «больше никогда не спрашивает».

Ссылка на подробный рабочий пример

2


Если вы хотите обнаружить все «состояния» (первый раз отклонено, только что было отклонено, просто было отклонено с помощью "Never Ask Again" или навсегда отклонено), вы можете сделать следующее:

Создать 2 логических значения:

  private boolean beforeClickPermission  Rat; частное логическое значение afterClickPermissionRat;  

Установите первое перед запросом разрешения:

  beforeClickPermissionRat = shouldShowRequestPermissionRationale (Manifest.permission. READ_EXTERNAL_STORAGE);  

Установите второй внутри вашего метода onRequestPermissionsResult :

  afterClickPermissionRat =  shouldShowRequestPermissionRationale (Manifest.permission.READ_EXTERNAL_STORAGE);  

Используйте следующую «таблицу истинности», чтобы делать все, что вам нужно в onRequestPermissionsResult () (после проверки что у вас все еще нет разрешения):

 //до после//FALSE FALSE = было отказано навсегда, все еще отказано навсегда -> Настройки приложения//FALSE TRUE  = Отказ в первый раз, еще не отказано окончательно -> Ничего//ИСТИНА ЛОЖЬ = Только что было отказано навсегда -> Изменение моей подписи на «Перейти к настройкам приложения для редактирования разрешений»//ИСТИНА ИСТИНА = Не было отказано навсегда, все же  не отклонено навсегда -> Ничего  

4


У меня была такая же проблема, и я понял это. Чтобы упростить жизнь, я написал класс util для обработки разрешений во время выполнения.

  public class PermissionUtil {/* * Проверьте, является ли версия Marshmallow и выше.  * Используется при решении запросить разрешение во время выполнения * */public static boolean shouldAskPermission () {return (Build.VERSION.SDK_INT> = Build.VERSION_CODES.M);  } частное статическое логическое значение shouldAskPermission (контекст контекста, разрешение String) {if (shouldAskPermission ()) {int permissionResult = ActivityCompat.checkSelfPermission (контекст, разрешение);  если (разрешениеResult! = PackageManager.PERMISSION_GRANTED) {вернуть истину;  }} return false;  } public static void checkPermission (контекст контекста, разрешение String, прослушиватель PermissionAskListener) {/* * Если разрешение не предоставлено * */if (shouldAskPermission (context, permission)) {/* * Если разрешение было отклонено ранее * */if ((  (Активность) контекст) .shouldShowRequestPermissionRationale (разрешение)) {listener.onPermissionPreviouslyDenied ();  } else {/* * В разрешении отказано или запрошено в первый раз * */if (PreferencesUtil.isFirstTimeAskingPermission (контекст, разрешение)) {PreferencesUtil.firstTimeAskingPermission (context, permission, false);  listener.onPermissionAsk ();  } else {/* * Обрабатывать функцию без разрешения или запрашивать разрешение у пользователя вручную * */listener.onPermissionDisabled ();  }}} еще {listener.onPermissionGranted ();  }}/* * Обратный вызов в различных случаях при проверке разрешения * * 1. Ниже M разрешение времени выполнения не требуется.  В этом случае будет вызываться onPermissionGranted ().  * Если разрешение уже предоставлено, будет вызываться onPermissionGranted ().  * * 2. Выше M, если разрешение запрашивается в первый раз, будет вызываться onPermissionAsk ().  * * 3. Выше M, если разрешение было запрошено, но не предоставлено, будет вызываться onPermissionPreviouslyDenied () *.  * * 4. Выше M, если разрешение отключено политикой устройства или пользователь установил флажок «Никогда не спрашивать снова» * в предыдущем запросе разрешения, будет вызываться onPermissionDisabled ().  * */public interface PermissionAskListener {/* * Обратный вызов для запроса разрешения * */void onPermissionAsk ();/* * Обратный вызов при отказе в разрешении * */void onPermissionPreviouslyDenied ();/* * Обратный вызов при разрешении «Больше не показывать» установлен и  denied * */void onPermissionDisabled ();/* * Обратный вызов при предоставленном разрешении * */void onPermissionGranted ();  }}  

И методы PreferenceUtil следующие.

  public static void firstTimeAskingPermission  (Контекст контекста, разрешение String, логическое isFirstTime) {SharedPreferences sharedPreference = context.getSharedPreferences (PREFS_FILE_NAME, MODE_PRIVATE; sharedPreference.edit (). PutBoolean (разрешение, isFirstTime) .apply ();} public static логическое разрешение isFirstmissionerTime  ) {return context.getSharedPreferences (PREFS_FILE_NAME, MODE_PRIVATE) .getBoolean (permission, true);}  

Теперь все, что вам нужно, это использовать метод * checkPermission * с правильными аргументами.

Вот пример,

  PermissionUtil.checkPermission (context, Manifest.permission.WRITE_EXTERNAL_STORAGE, new PermissionUtil.PermissionAskListener () {@Override public  void onPermissionAsk () {ActivityCompat.requestPermissions (thisActivity, new String [] {Manifest.permission.READ_CONTACTS}, REQUEST_EXTERNAL_STORAGE);} @Overrid  e public void onPermissionPreviouslyDenied () {//показать диалог, объясняющий разрешение, а затем запросить разрешение} @Override public void onPermissionDisabled () {Toast.makeText (context, "Permission Disabled.", Toast.LENGTH_SHORT) .show ();  } @Override public void onPermissionGranted () {readContacts ();  }});  

как мое приложение узнает, поставил ли пользователь отметку «Больше не спрашивать»?

Если пользователь выбрал Больше никогда не спрашивать , вы получите обратный вызов на onPermissionDisabled .

Удачного кодирования 🙂

8


Метод shouldShowRequestPermissionRationale () можно использовать, чтобы проверить, выбрал ли пользователь параметр `` никогда больше не спрашивал '' и отказал ли ему в разрешении. Существует множество примеров кода, поэтому я бы предпочел объяснить, как использовать его для такой цели, потому что я думаю, что его имя и его реализация делают это более сложно, чем есть на самом деле.

Как объясняется в разделе «Запрос разрешений во время выполнения», этот метод возвращает true, если опция «никогда не спрашивать снова» видна, иначе false; поэтому он возвращает false в самый первый раз, когда отображается диалог, затем со второго раза он возвращает true, и только если пользователь отказывает в разрешении, выбирая параметр, в этот момент он снова возвращает false.

Чтобы обнаружить такой случай, вы можете либо определить последовательность ложно-истина-ложь, либо (что проще) иметь флаг, который отслеживает начальное время отображения диалогового окна. После этого этот метод возвращает либо true, либо false, где false позволит вам определить, когда этот параметр выбран.

0


Полное объяснение для каждого случая разрешения

 /** * Случай 1: у пользователя нет разрешения * Случай 2: у пользователя есть разрешение * * Случай 3: Пользователь никогда не видел диалоговое окно разрешений * Случай 4: Пользователь отказал в разрешении один раз, но не нажал на него  Флажок «Никогда не показывать снова» * Случай 5: Пользователь отказал в разрешении и также установил флажок «Больше никогда не показывать».  * Случай 6: Пользователь предоставил разрешение * */public void handlePermission () {if (ContextCompat.checkSelfPermission (MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)! = PackageManager.PERMISSION_GRANTED) {//Это случай 1. Теперь мы  необходимо дополнительно проверить, было ли показано разрешение раньше или нет if (ActivityCompat.shouldShowRequestPermissionRationale (MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {//Это случай 4.} else {//Это случай 3. Запрос разрешения здесь  }} else {//Это случай 2. У вас есть разрешение, теперь вы можете делать все, что с этим связано}} public void onRequestPermissionsResult (int requestCode, String [] permissions, int [] grantResults) {if (grantResults [0] ==  PackageManager.PERMISSION_GRANTED) {//Это случай 2 (разрешение теперь предоставлено)} else {//Это снова случай 1, поскольку разрешение не предоставлено пользователем//Теперь далее мы проверяем, было ли использование запрещено постоянно или нет if (ActivityCompat.  shouldShowRequestPermissionRationale (MainActivity.this,  Manifest.permission.WRITE_EXTERNAL_STORAGE)) {//case 4 Пользователь отказал в разрешении, но не навсегда} else {//case 5. В разрешении отказано навсегда. //Теперь вы можете открыть страницу настроек разрешений.  }}}  


Полезная функция для определения произвольное разрешение заблокировано от запроса (в Kotlin):

  приватное развлечение isPermissionBlockedFromAsking (activity: Activity, разрешение: String): Boolean {if (Build.VERSION.SDK_INT>  = Build.VERSION_CODES.M) {return ContextCompat.checkSelfPermission (активность, разрешение)! = PackageManager.PERMISSION_GRANTED &&! Activity.shouldShowRequestPermissionRationale (разрешение) && PreferenceManager.getDefaultSharedPreferences (разрешение, false) (false) (действие, false) (return activity) .getBoo  code> 

Использование этого требует установки логического значения общего предпочтения с именем вашего желаемого разрешения (например, android.Manifest.permission. READ_PHONE_STATE ) на true при первом запросе разрешения.


Пояснение:

Build.VERSION.SDK_INT> = Build.VERSION_CODES.M как некоторые из код можно запускать только на уровне API 23+.

ContextCompat.checkSelfPermission (activity, permission)! = PackageManager.PERMISSION_GRANTED , чтобы проверить, у нас еще нет разрешение.

! activity.shouldShowRequestPermissionRationale (permission) , чтобы проверить, отклонил ли пользователь повторный запрос приложения. Из-за особенностей этой функции также требуется следующая строка.

PreferenceManager.getDefaultSharedPreferences (activity) .getBoolean (permission, false) это используется (вместе с с установкой значения true при первом запросе разрешения), чтобы различать состояния «Никогда не спрашивал» и «Больше не спрашивать», поскольку предыдущая строка не возвращает эту информацию.


Пожалуйста, не бросайте в меня камни за это решение.

Это работает, но немного "взломано".

Когда вы вызываете requestPermissions , зарегистрируйте текущее время.

  mAskedPermissionTime = System.currentTimeMillis ();  

Затем в onRequestPermissionsResult

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

  if (System.currentTimeMillis () - mAskedPermissionTime  

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

Используйте на свой страх и риск.

3


Я написал сокращение для запроса разрешения в Android M. Этот код также обеспечивает обратную совместимость со старыми версиями Android.

Весь уродливый код извлекается во фрагмент, который присоединяется к Activity, запрашивая разрешения, и отсоединяется от него. вы можете использовать PermissionRequestManager следующим образом:

  new PermissionRequestManager ()//Здесь нам нужна AppCompatActivity, если вы не используете библиотеки поддержки,  нужно будет немного изменить//класс PermissionReuqestManager .withActivity (this)//Список всех необходимых разрешений .withPermissions (android.Manifest.permission.CALL_PHONE, android.Manifest.permission.READ_CALENDAR)//Этот Runnable вызывается всякий раз, когда запрос  был успешным. withSuccessHandler (new Runnable () {@Override public void run () {//Сделайте что-нибудь с вашими разрешениями!//Это вызывается после того, как пользователь предоставил все разрешения,//мы одна из старых платформ, где//пользователь делает  не нужно предоставлять разрешения//вручную, или все разрешения уже предоставлены}})//Необязательно, вызывается, когда пользователь не предоставил все разрешения .withFailureHandler (new Runnable () {@Override public void run () {//Это  вызывается, если пользователь отклонил одно или все запрошенные разрешения Le (this.getClass (). getSimpleName (), «Невозможно запросить разрешение»);}})//После этого вызова пользователю предлагается предоставить  права .request ();  

Взгляните: https://gist.github.com/crysxd/385b57d74045a8bd67c4110c34ab74aa

0


Вместо этого вы получите обратный вызов на onRequestPermissionsResult () как PERMISSION_DENIED, когда вы снова запрашиваете разрешение, попадая в ложное состояние shouldShowRequestPermissionRationale()

Из документа Android:

Когда система просит пользователя предоставить разрешение, пользователь может указать системе не запрашивать это разрешение снова. В этом случае каждый раз, когда приложение использует requestPermissions () для повторного запроса этого разрешения, система немедленно отклоняет запрос. Система вызывает ваш метод обратного вызова onRequestPermissionsResult () и передает PERMISSION_DENIED так же, как если бы пользователь снова явно отклонил ваш запрос. Это означает, что когда вы вызываете requestPermissions () , вы не можете предполагать, что произошло какое-либо прямое взаимодействие с пользователем.


  public void onRequestPermissionsResult (int requestCode, @NonNull String permissions [], @NonNull int [] grantResults) {switch (requestCode  ) {case PERMISSIONS_REQUEST_EXTERNAL_STORAGE: {if (grantResults.length> 0) {if (ActivityCompat.shouldShowRequestPermissionRationale (this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {//Запрещено} else {if (ActivityCompatALNAL_STORAGE_EXTERMIN_EXTORMISS_IN_EXT_E_MAN_EXTORMISS_RITE_EXTOR_WRITE_EXT_RITE_EXTOR_STORmission  ) == PackageManager.PERMISSION_GRANTED) {//К чему вы хотите} else {//Боб никогда не проверял click}}}}}}  


Попробуйте это простая библиотека разрешений. Он выполнит все операции, связанные с разрешением, за 3 простых шага. Это сэкономило мне время. Вы можете завершить всю работу, связанную с разрешениями, за 15 минут .

Он может обрабатывать Deny, Он может обрабатывать «Никогда больше не спрашивать», Он может вызывать настройки приложения для получения разрешения, Он может выдавать Rational сообщение, Он может выдавать сообщение Denial, Он может выдавать список принятых разрешения, он может предоставить список запрещенных разрешений и т. д.

https://github.com/ParkSangGwon/TedPermission

Шаг 1: добавьте свою зависимость

  dependencies {compile 'gun0912.ted: tedpermission: 2.1.1'//проверьте ссылку выше на наличие последних библиотек}  

Шаг 2: запросить разрешения

  TedPermission.with (this) .setPermissionListener (прослушиватель разрешений) .setDeniedMessage ("  Если вы отклоняете разрешение, вы не можете использовать эту службу  n  nВключите разрешения в [Настройка]> [Разрешение] ") .setPermissions (Manifest.permission.READ_CONTACTS, Manifest.permission.ACCESS_FINE_LOCATION) .check ();  

Шаг 3. Обработка ответа о разрешении

  PermissionListener permissionlistener = new  PermissionListener () {@Override public void onPermissionGranted () {Toast.makeText (MainActivity.this, «Разрешение предоставлено», Toast.LENGTH_SHORT) .show ();  } @Override public void onPermissionDenied (ArrayList  deniedPermissions) {Toast.makeText (MainActivity.this, «Permission Denied  n» + deniedPermissions.toString (), Toast.LENGTH_SHORT) .show ();  }};  

2


Вы можете использовать

shouldShowRequestPermissionRationale() 

внутри

  onRequestPermissionsResult ()  

См. пример ниже:

Проверьте, есть ли у него разрешение, когда пользователь нажимает кнопку:

  @Overridepublic void onClick (View v) {if (v.getId () == R.id.appCompatBtn_changeProfileCoverPhoto) {if (Build.VERSION.SDK_INT  

Когда пользователь ответит в диалоговом окне разрешения, мы перейдем к onRequestPermissionResult:

  @Overridepublic void onRequestPermissionsResult (int requestCode, @NonNull String [] permissions, @NonNull int [] grantResults) {super.onRequestPermissionsResult (requestCode, permissions, grantResults);  if (requestCode == WRITE_EXTERNAL_PERMISSION_REQUEST_CODE) {//Случай 1. Разрешение предоставлено.  if (grantResults.length> 0 && grantResults [0] == PackageManager.PERMISSION_GRANTED) {if (ContextCompat. checkSelfPermission (SettingsActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {//Перед переходом я все же еще раз проверяю разрешение для хорошей практики.  navigateTo (MainActivity.class);  }} else {//Случай 2. В разрешении было отказано if (ActivityCompat.shouldShowRequestPermissionRationale (this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {//Случай 2.1.  shouldShowRequest ... возвращает true, потому что//разрешение было отклонено ранее.  Если приложение запускается впервые, мы//окажемся в этой части кода.  Потому что ему нужно отказать хотя бы один раз, чтобы//получить onRequestPermissionsResult.  Snackbar snackbar = Snackbar.make (findViewById (R.id.relLayout_container), R.string.you_must_verify_permissions_to_send_media, Snackbar.LENGTH_LONG);  snackbar.setAction ("VERIFY", new View.OnClickListener () {@Override public void onClick (View v) {ActivityCompat.requestPermissions (SettingsActivity.this, new String [] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, WRITE_EXTERNODIS_REQUEST})  );  Snackbar.show ();  } else {//Случай 2.2.  В разрешении уже было отказано, и пользователь установил флажок «Больше не спрашивать». //Перемещаем пользователя к настройкам, если он разрешит это время.  AlertDialog.Builder builder = новый AlertDialog.Builder (это);  builder.setMessage (R.string.instructions_to_turn_on_storage_permission) .setPositiveButton (getString (R.string.settings), new DialogInterface.OnClickListener () {@Override public void onClick (DialogInterface dialog, int which) {Intent settings (Intent Settings.  ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts ("пакет", getPackageName (), null); settingsIntent.setData (uri); startActivityForResult (settingsIntent, 7);}}) .setNegativeButton (getString_not (R.string)).  ноль);  Dialog dialog = builder.create ();  dialog.show ();  }}}}  


вы можете слушать красиво.

  interface PermissionListener {fun onNeedPermission () fun onPermissionPreviouslyDenied (numberDenyPermission: Int) fun onPermissionDisabledPermanently (  numberDenyPermission: Int) fun onPermissionGranted ()}  

MainClass для разрешения

  class PermissionUtil  {private val PREFS_FILENAME = "permission" private val TAG = "PermissionUtil" частное развлечение shouldAskPermission (контекст: контекст, разрешение: строка): логическое {if (Build.VERSION.SDK_INT> = Build.VERSION_CODES.M) {val permissionResult = ActivityCompat  .checkSelfPermission (контекст, разрешение) if (permissionResult! = PackageManager.PERMISSION_GRANTED) {return true}} return false} fun checkPermission (контекст: контекст, разрешение: String, слушатель: PermissionLi  stener) {Log. i (TAG, "CheckPermission for $ permission") if (shouldAskPermission (context, permission)) {//Разрешение загрузки истории val sharedPreference = context.getSharedPreferences (PREFS_FILENAME, 0) val numberShowPermissionDialog = sharedPreference.getInt (разрешение, 0) if (  numberShowPermissionDialog == 0) {(context as? Activity) ?. let {if (ActivityCompat.shouldShowRequestPermissionRationale (it, разрешение)) {Log.e (TAG, "Пользователь отказал в разрешении, но не навсегда") listener.onPermissionPreviouslyDenied (numberShowPermissionDialog)  } else {Log.e (TAG, "Permission denied навсегда.") listener.onPermissionDisabledPermanently (numberShowPermissionDialog)}}?: kotlin.run {listener.onNeedPermission ()}} else {//Это FirstTime listener.onNeedPermission ()}//Сохранить разрешение истории sharedPreference.edit (). PutInt (permission, numberShowPermissionDialog + 1) .apply ()} else {listener.onPermissionGranted ()}}}  

Используется таким образом

  PermissionUti  l (). checkPermission (this, Manifest.permission.ACCESS_FINE_LOCATION, object: PermissionListener {переопределить удовольствие onNeedPermission () {log ("---------------------->  onNeedPermission ")//ActivityCompat.requestPermissions (this @ SplashActivity,//Array (1) {Manifest.permission.ACCESS_FINE_LOCATION},//118)} переопределить удовольствие onPermissionPreviouslyDenied (numberDenyPermission: Int) {log (" ------  ----------------> onPermissionPreviouslyDenied ")} переопределить удовольствие onPermissionDisabledPermanently (numberDenyPermission: Int) {log (" -----------------  -----> onPermissionDisabled ")} переопределить удовольствие onPermissionGranted () {log (" ----------------------> onPermissionGranted ")}})  

переопределить onRequestPermissionsResult в activity или fragmnet

  переопределить удовольствие onRequestPermissionsResult (requestCode: Int, permissions:  Array , grantResults: IntArray) {if (requestCode == 118) {if (permissions [0] == Manifest.permission.ACCESS_FINE_LOCATION && grantResults [0] == Packag  eManager.PERMISSION_GRANTED) {getLastLocationInMap ()}}}  


Я нашел для многих длинный и запутанный ответ, и после прочтения нескольких ответов мой вывод:

  if (! ActivityCompat.shouldShowRequestPermissionRationale (this  , Manifest.permission.READ_EXTERNAL_STORAGE)) Toast.makeText (this, «навсегда отклонено», Toast.LENGTH_SHORT) .show ();  


Я также хотел бы получить информацию о том, выбрал ли пользователь вариант «никогда больше не спрашивать». Я достиг «почти решения» с уродливым флагом, но прежде чем я расскажу вам, как это сделать, я расскажу вам о своей мотивации:

Сначала я хотел бы предложить функцию ссылки на разрешения. Если пользователь использует его и не имеет прав, он/она получает либо 1-й диалог сверху, либо 2-й и 3-й диалог. Когда пользователь выбрал «Никогда больше не спрашивать», я хотел бы отключить эту функцию и отобразить ее по-другому. - Мое действие запускается текстовой записью счетчика, я также хотел бы добавить «(Разрешение отозвано)» к отображаемому тексту метки. Это показывает пользователю: «Функциональность есть, но я не могу ее использовать из-за настроек моих разрешений». Однако это не представляется возможным, поскольку я не могу проверить, был ли выбран вариант «Никогда не спрашивать снова».

Я пришел к решению, с которым могу жить, если мои функции всегда включены с активной проверкой разрешений. Я показываю тост-сообщение в onRequestPermissionsResult () в случае отрицательного ответа, но только в том случае, если я не показываю всплывающее окно с настраиваемым обоснованием. Поэтому, если пользователь выбрал «Никогда больше не спрашивать», он получит только всплывающее сообщение. Если пользователь не хочет выбирать вариант «никогда больше не спрашивать», он получает только настраиваемое обоснование и всплывающее окно с запросом разрешения от операционной системы, но не всплывает, поскольку три уведомления подряд будут слишком болезненными.


Мне нужно реализовать динамическое разрешение для камеры. Возможны 3 случая: 1. Разрешить, 2. Запрещено, 3. Больше не спрашивать.

  @Override public void onRequestPermissionsResult (int requestCode, @NonNull String []  permissions, @NonNull int [] grantResults) {for (String permission: permissions) {if (ActivityCompat.shouldShowRequestPermissionRationale (getActivity (), permission)) {//отказано Log.e ("отказано", разрешение);  } else {if (ActivityCompat.checkSelfPermission (getActivity (), permission) == PackageManager.PERMISSION_GRANTED) {//разрешено Log.e ("разрешено", разрешение);  } else {//установить, чтобы никогда больше не спрашивать Log.e ("никогда не спрашивать снова", разрешение); //здесь что-то делаем.  }}} если (requestCode! = MaterialBarcodeScanner.RC_HANDLE_CAMERA_PERM) {super.onRequestPermissionsResult (requestCode, разрешения, grantResults);  возвращаться;  } если (grantResults.length! = 0 && grantResults [0] == PackageManager.PERMISSION_GRANTED) {mScannerView.setResultHandler (это);  mScannerView.startCamera (mCameraId);  mScannerView.setFlash (mFlash);  mScannerView.setAutoFocus (mAutoFocus);  возвращаться;  } else {//установить, чтобы никогда больше не спрашивать Log.e ("установить, чтобы никогда больше не спрашивать", permissions [0]);  } DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener () {public void onClick (DialogInterface dialog, int id) {dialog.cancel ();  }};  AlertDialog.Builder builder = новый AlertDialog.Builder (getActivity ());  строитель. setTitle ("Ошибка") .setMessage (R.string.no_camera_permission) .setPositiveButton (android.R.string.ok, listener) .show ();} private void insertDummyContactWrapper () {int hasWriteContactsPermission = checkSelfPermission (ManifAMERApermission.  );  если (hasWriteContactsPermission! = PackageManager.PERMISSION_GRANTED) {requestPermissions (новая строка [] {Manifest.permission.CAMERA}, REQUEST_CODE_ASK_PERMISSIONS);  возвращаться;  } mScannerView.setResultHandler (это);  mScannerView.startCamera (mCameraId);  mScannerView.setFlash (mFlash);  mScannerView.setAutoFocus (mAutoFocus);  } private int checkSelfPermission (String camera) {if (ContextCompat.checkSelfPermission (getActivity (), Manifest.permission.CAMERA)! = PackageManager.PERMISSION_GRANTED) {return REQUEST_CODE_ASK_PERMISSIONS;  } else {return REQUEST_NOT_CODE_ASK_PERMISSIONS;  }}  


Вы можете использовать метод if (ActivityCompat.shouldShowRequestPermissionRationale (this, Manifest.permission.CAMERA) , чтобы определить, проверяется ли никогда не спрашивать или нет.

Подробнее ссылка: Отметьте это

Чтобы проверить использование нескольких разрешений:

  if (ActivityCompat.shouldShowRequestPermissionRationale (this, Manifest.permission.CAMERA) ||  ActivityCompat.shouldShowRequestPermissionRationale (this, Manifest.permission.WRITE_EXTERNAL_STORAGE) || ActivityCompat.shouldShowRequestPermissionRationale (this, Manifest.permission.ACCESS_FINE_LOCATION) || ActivityCompat.permission.ACCESS_FINE_LOCATION) || ActivityCompat.permission.ACCESS_FINE_LOCATION)  это приложение ", новый DialogInterface.OnClickListener () {@Override public void onClick (DialogInterface dialog, int whic  h) {переключатель (который) {case DialogInterface.BUTTON_POSITIVE: checkAndRequestPermissions ();  перемена;  case DialogInterface.BUTTON_NEGATIVE://продолжаем логику, отключив связанные функции или выйдя из приложения.  Конец();  перемена;  }}});  }//в разрешении отказано (проверяется и никогда больше не спрашивать)//shouldShowRequestPermissionRationale вернет false else {объяснение ("Вам нужно предоставить некоторые обязательные разрешения для продолжения. Вы хотите перейти к настройкам приложения?"); ////продолжаем логику, отключив связанные функции, или выходим из приложения.  }  

метод объяснения ()

  private void объяснение (String msg) {final android.support.v7.app.AlertDialog  .Builder dialog = новый android.support.v7.app.AlertDialog.Builder (это);  dialog.setMessage (msg) .setPositiveButton ("Да", новый DialogInterface.OnClickListener () {@Override public void onClick (DialogInterface paramDialogInterface, int paramInt) {//permissionsclass.requestPermission (type, code); android  .provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse ("package: com.exampledemo.parsaniahardik.marshmallowpermission")));}}). setNegativeButton ("Отмена", новый DialogInterface.OnClickListener () {@Override public void onClick (DialogInterface paramDialogInterface, int paramInt) {finish ();}});  dialog.show ();  }  

Приведенный выше код также покажет диалоговое окно, которое перенаправит пользователя на экран настроек приложения, откуда он может дать разрешение, если установлен флажок, кнопка никогда не спрашивать снова.


Расширение на mVck в ответе выше, следующая логика определяет, проверено ли «Больше не спрашивать» для данного запроса разрешения:

  bool bStorage = grantResults [0] == Permission.  Разрешено; bool bNeverAskForStorage =! BStorage && (_bStorageRationaleBefore == true && _bStorageRationaleAfter == false || _bStorageRationaleBefore == false && _bStorageRationaleAfter == false);  

из полный пример см. в этом ответе)

  private bool _bStorageRationaleBefore; private bool _bStorageRationaleAfter;  private const int ANDROID_PERMISSION_REQUEST_CODE__SDCARD = 2;//private const int ANDROID_PERMISSION_REQUEST_CODE__CAMERA = 1; private const int ANDROID_PERMISSION_REQUEST_CODE__NONE = 0; общедоступное переопределение void OnRequestPermissionsCum_Resmission_request_request_CODE__NONE = 0; общедоступное переопределение void OnRequestPermissionsCum_Resmission, запрос на получение разрешений] [запрос прав доступа]  , разрешения, grantResults);  переключатель (requestCode) {case ANDROID_PERMISSION_REQUEST_CODE__SDCARD: _bStorageRationaleAfter = ShouldShowRequestPermissionRationale (Android.Manifest.Permission.WriteExternalStorage);  bool bStorage = grantResults [0] == Permission.Granted;  bool bNeverAskForStorage =! bStorage && (_bStorageRationaleBefore == true && _bStorageRationaleAfter == false || _bStorageRationaleBefore == false && _bStorageRationaleAfter == false);  перемена;  }} private List  GetRequiredPermissions (out int requestCode) {//Android v6 требует явного предоставления разрешений от пользователя во время выполнения по соображениям безопасности requestCode = ANDROID_PERMISSION_REQUEST_CODE__NONE; //0 List  requiredPermissions = new List  ();  _bStorageRationaleBefore = ShouldShowRequestPermissionRationale (Android.Manifest.Permission.WriteExternalStorage);  Разрешение writeExternalStoragePerm = ApplicationContext.CheckSelfPermission (Android.Manifest.Permission.WriteExternalStorage); //if (extStoragePerm == Permission.Denied) if (writeExternalStoragePerm! = Permission.Granted) {requestCode | = ANDROID_PERMISSION_REQUEST_CODE__SDCARD;  requiredPermissions.Add (Android.Manifest.Permission.WriteExternalStorage);  } return requiredPermissions;} защищенное переопределение void OnCreate (Bundle savedInstanceState) {base. OnCreate (savedInstanceState); //Android v6 требует явного предоставления разрешений от пользователя во время выполнения из соображений безопасности int requestCode;  Список  requiredPermissions = GetRequiredPermissions (out requestCode);  if (requiredPermissions! = null && requiredPermissions.Count> 0) {if (requestCode> = ANDROID_PERMISSION_REQUEST_CODE__SDCARD) {_savedInstanceState = savedInstanceState;  RequestPermissions (requiredPermissions.ToArray (), requestCode);  возвращаться;  }}} OnCreate2 (savedInstanceState);}  


Чтобы точно ответить на вопрос, Что происходит, когда пользователь нажимает «Больше не спрашивать»?

Переопределенный метод/функция

  onRequestPermissionsResult (requestCode: Int, permissions: Array , grantResults: IntArray)  

Массив grantResult выходит в быть Пустым, так что, может, ты сможешь там что-нибудь сделать? Но это не лучшая практика.

Как справиться с «Никогда больше не спрашивать»?

Я работаю с фрагментом, который требует READ_EXTERNAL_STORAGE разрешение.

  переопределить удовольствие onViewCreated (view: View, savedInstanceState: Bundle?) {Super.onViewCreated (view, savedInstanceState), когда {isReadPermissionsGranted () -> {/**  * Разрешения предоставлены */getDirectories ()} isPermissionDeniedBefore () -> {/** * Пользователь ранее отказал, объясните, почему нам нужно разрешение, и спросите еще раз */updateUIForDeniedPermissions () checkIfPermissionIsGrantedNow ()} else -> {/*  * * Необходимо запросить разрешения, в первый раз */checkIfPermissionIsGrantedNow ()/** * Если пользователь выберет «Не спрашивать снова», он никогда больше не спросит!  поэтому просто обновите пользовательский интерфейс для запрещенных разрешений */updateUIForDeniedPermissions ()}}}  

Остальные функции тривиальны.

 //Предоставлены разрешения на чтение и запись.  ) == PackageManager.PERMISSION_GRANTED)} fun isReadPermissionDenied (context: Context): Boolean {return ActivityCompat.shouldShowRequestPermissionRationale (context as Activity, PermissionsUtils.READ_EXTERNAL_STORAGE_PERMISSIONS)}  

# div>


Метод без OnRequestPermissionResult и без shouldShowRequestPermissionRationale:

  public static void requestDanger  ousPermission (действие AppCompatActivity, разрешение String) {if (hasPermission (действие, разрешение)) return;  просить разрешения();  new Handler (). postDelayed (() -> {if (activity. getLifecycle (). getCurrentState () == Lifecycle.State.RESUMED) {намерение намерения = новое намерение (Settings.ACTION_APPLICATION_DETAILS_SETTINGS);  intent.setData (Uri.parse ("пакет:" + context.getPackageName ()));  context.startActivity (намерение);  }}, 250);  }  

Открывает настройки устройства через 250 мс, если не появилось всплывающее окно разрешения (что имеет место, если было выбрано «Больше не спрашивать».

2



Разрешения приложений

Некоторые приложения или игры в Microsoft Store разработаны чтобы воспользоваться преимуществами определенных аппаратных или программных возможностей вашего устройства с Windows. Приложению для работы с фотографиями может потребоваться использовать камеру вашего телефона, или ресторанный гид может использовать ваше местоположение, чтобы рекомендовать близлежащие места.

В Windows 10 , используйте страницу конфиденциальности, чтобы выбрать, какие приложения могут использовать ту или иную функцию. Выберите Пуск > Настройки > Конфиденциальность . Выберите приложение (например, Календарь) и выберите, какие разрешения для приложений включены или отключены.

На странице конфиденциальности не будут перечислены приложения, которым разрешено использовать все системные ресурсы. Вы не можете использовать конфиденциальность настройки для управления возможностями, которые могут использовать эти приложения. Windows Настольные приложения подпадают под эту категорию.

Чтобы просмотреть разрешения для приложения, перейдите на страницу продукта приложения в Microsoft Store или в Интернете. Если вы не хотите, чтобы приложение использовало какие-либо из перечисленных функций, вы можете не устанавливать его.

Подробнее о том, какие разрешения позволяют приложению делать:

Доступ ко всем вашим файлам, периферийным устройствам, приложениям, программам и реестру: Приложение имеет возможность читать или записывать все ваши файлы (включая документы, изображения и музыку) и настройки реестра, что позволяет приложению вносить изменения в ваш компьютер и настройки. Он может использовать любые периферийные устройства, которые подключены к вашему устройству или являются его частью (например, камеры, микрофоны или принтеры), без уведомления вас. Он также имеет доступ к вашему местоположению и может использовать функции платформы, такие как история местоположений, диагностика приложений и многое другое, которые недоступны для большинства приложений Магазина. Вы не можете контролировать большинство разрешений для этого приложения в Настройках> Конфиденциальность. Обратите внимание: хотя приложение имеет возможность доступа к этим ресурсам, на самом деле оно может этого не делать. Для получения дополнительной информации о том, что приложение собирает или использует, ознакомьтесь с политикой конфиденциальности разработчика.

Информация об учетной записи: доступ к любой информации вашей учетной записи.

Разрешить повышение: разрешает запуск приложения с правами администратора без предварительного запроса пользователя.

Диагностика приложения: Получить диагностическую информацию о других запущенных приложениях.

Bluetooth: активировать и использовать любые соединения Bluetooth между вашим устройством и другими устройствами.

Календарь: доступ к своим календарям.

История звонков: доступ к истории телефонных звонков, совершенных вами на устройстве, в Skype или других телефонных приложениях.

Контакты: доступ к своим контактам, людям или приложениям адресной книги.

Пользовательские действия по установке: установка дополнительного программного обеспечения.

Электронная почта: доступ к электронной почте и информации учетной записи для учетных записей электронной почты.

Распознавание лиц: активируйте и используйте любое оборудование для распознавания лиц.

Файловая система: доступ к файлам и папкам, к которым у вас есть доступ, и чтение или запись всех ваших файлов (включая документы, изображения , и музыку).

Считыватель отпечатков пальцев: Активируйте и используйте любое оборудование для считывания отпечатков пальцев.

Локальная система службы: установите службу на машине, которая работает с максимальными привилегиями.

Местоположение: активировать и использовать GPS или другие функции определения местоположения на вашем устройстве. Доступ к данным о местоположении в Картах и ​​других приложениях для определения местоположения.

Обмен сообщениями: доступ к своим мгновенным сообщениям и информации об учетной записи.

Микрофон: активируйте микрофон на своем устройстве и используйте его.

Изменяемое приложение: Разрешите пользователю изменять приложение.

Движение: Активировать и использовать акселерометр или другую функцию определения движения на вашем устройстве.

Музыкальная библиотека: доступ к любым музыкальным файлам из Музыкальной библиотеки на вашем устройстве.

Связь ближнего радиуса действия : Активируйте и используйте любые соединения ближнего радиуса действия (NFC) между вашим устройством и другими устройствами.

Уведомления: Получите доступ к своим уведомлениям, найденным в действии center.

Пакетные службы: Установите службу на машину.

Прокладка совместимости с перенаправлением записи пакета: Позволяет приложению создавать, изменять или удалять файлы в папке установки приложения.

Библиотека изображений: доступ к любым файлам изображений из библиотеки изображений на ваше устройство.

Задачи: доступ к списку задач в Outlook и других приложениях для отслеживания задач.

Невиртуализированные ресурсы: записывайте записи реестра и файлы, которые не очищаются при удалении.

Видеотека: Доступ к любым видеофайлам из видеотеки на ваше устройство.

Распознавание голоса: активируйте и u установите любое оборудование для распознавания голоса.

Веб-камера: активируйте и используйте камеру на вашем устройстве.

WiFi: активируйте и используйте соединения Wi-Fi между вашим устройством, Интернетом и другими устройствами.

Проводные соединения: активируйте и используйте любые проводные соединения, включая Ethernet, USB и последовательные соединения между вашим устройством, Интернетом и другими устройствами.

Оцените статью
clickpad.ru
Добавить комментарий