3

Я создаю BottomSheetDialogFragment, который позволяет пользователю делать снимок или выбирать из своей библиотеки. Для доступа к любой функции требуется разрешение WRITE_EXTERNAL_STORAGE.Предотвратить отображение предупреждающего сигнала из-за тусклого другого диалогового окна DialogFragment

Поэтому я хотел бы получить разрешение от BottomSheetDialogFragment, не позволяя пользователю нажимать на что-либо еще, пока не будет предоставлено разрешение. Если я прошу разрешения в onViewCreated, разрешения диалоговом окне отображаются в порядке:

Permission Request

Хотя, если разрешение будет отказано, и пользователь пытается снова я пытаюсь отобразить обоснование в качестве AlertDialog, но диалог блокируется; предположительно тусклый от BottomSheetDialogFragment:

Permission Rationale

Я думаю, что это вызвано анимацией BottomSheetDialogFragment, который не отображает фон тусклым, пока фрагмент не завершит свою анимацию. Это происходит случайно после onViewCreated. Кто-нибудь знает, есть ли способ заставить AlertDialog спереди, не закрывая или отклоняя BottomSheetDialogFragment? Или если есть способ прослушать анимацию BottomSheetDialogFragment?

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


Вот Fragment:

public class ImageChooserDialogFragment extends BottomSheetDialogFragment { 

    public interface OnImageChosenListener { 
     void onImageChosen(Uri data); 
    } 

    private static final String PREFIX_IMAGE_CAPTURE = "IMG_"; 

    private static final int REQUEST_PERMISSION_CAMERA = 0; 
    private static final int REQUEST_PERMISSION_STORAGE = 1; 

    private static final int REQUEST_IMAGE_CAPTURE  = 2; 
    private static final int REQUEST_IMAGE_SELECTION = 3; 

    private static final String[] PERMISSIONS_CAMERA = new String[] { 
      Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA 
    }; 

    private static final String[] PERMISSIONS_STORAGE = new String[] { 
      Manifest.permission.WRITE_EXTERNAL_STORAGE 
    }; 

    private boolean hasFeatureCamera; 

    private Uri mCurrentPhotoResource; 

    private View mView; 

    private OnImageChosenListener mOnImageChosenListener; 

    public static ImageChooserDialogFragment newInstance() { 
     return new ImageChooserDialogFragment(); 
    } 

    @Override 
    public void onAttach(Context context) { 
     super.onAttach(context); 
     Timber.d("onAttach"); 

     if(context instanceof OnImageChosenListener) { 
      mOnImageChosenListener = (OnImageChosenListener) context; 
     } 
    } 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     Fragment parent = getParentFragment(); 

     if(parent != null) { 
      onAttachToFragment(parent); 
     } 

     PackageManager manager = getContext().getPackageManager(); 
     if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 
      hasFeatureCamera = manager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY); 
     } else { 
      hasFeatureCamera = manager.hasSystemFeature(PackageManager.FEATURE_CAMERA) 
        || manager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT); 
     } 
    } 

    public void onAttachToFragment(Fragment fragment) { 
     if(fragment instanceof OnImageChosenListener) { 
      mOnImageChosenListener = (OnImageChosenListener) fragment; 
     } 
    } 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     mView = inflater.inflate(R.layout.dialog_image_chooser, container, false); 
     ButterKnife.bind(this, mView); 

     if(!hasFeatureCamera) { 
      mView.setVisibility(View.GONE); 
     } 

     return mView; 
    } 

    @Override 
    public void onViewCreated(View view, Bundle savedInstanceState) { 
     super.onViewCreated(view, savedInstanceState); 
     if(!PermissionUtil.checkPermissions(getContext(), PERMISSIONS_STORAGE)) { 
      requestPermissionsWithRationale(REQUEST_PERMISSION_STORAGE, PERMISSIONS_STORAGE); 
     } else if(!hasFeatureCamera) { 
      dispatchImageSelectionIntent(); 
     } else { 
      displayRequestPermissionsAlert(R.string.msg_snackbar_permissions_storage); 
     } 
    } 

    @Override 
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 
              @NonNull int[] grantResults) { 
     switch(requestCode) { 
      case REQUEST_PERMISSION_CAMERA: 
       if(PermissionUtil.verifyPermissions(grantResults)) { 
        dispatchImageCaptureIntent(); 
       } else { 
        displayRequestPermissionsAlert(R.string.msg_snackbar_permissions_camera); 
       } break; 
      case REQUEST_PERMISSION_STORAGE: 
       if(PermissionUtil.verifyPermissions(grantResults)) { 
        if(!hasFeatureCamera) { 
         dispatchImageSelectionIntent(); 
        } 
       } else { 
        displayRequestPermissionsAlert(R.string.msg_snackbar_permissions_storage); 
       } break; 
      default: 
       super.onRequestPermissionsResult(requestCode, permissions, grantResults); 
     } 
    } 

    @Override 
    public void onActivityResult(int requestCode, int resultCode, Intent data) { 
     switch (requestCode) { 
      case REQUEST_IMAGE_CAPTURE: 
       if(resultCode == Activity.RESULT_OK) { 
        handleImageCaptureResult(data); 
       } else { 
        destroyTemporaryFile(); 
       } break; 
      case REQUEST_IMAGE_SELECTION: 
       if(resultCode == Activity.RESULT_OK) { 
        handleImageSelectionResult(data); 
       } break; 
      default: 
       super.onActivityResult(requestCode, resultCode, data); 
     } 
    } 

    @OnClick(R.id.photo_take) 
    public void onClickCapture() { 
     if(!PermissionUtil.checkPermissions(getContext(), PERMISSIONS_CAMERA)) { 
      requestPermissionsWithRationale(REQUEST_PERMISSION_CAMERA, PERMISSIONS_CAMERA); 
     } else { 
      dispatchImageCaptureIntent(); 
     } 
    } 

    @OnClick(R.id.photo_choose) 
    public void onClickChoose() { 
     if(!PermissionUtil.checkPermissions(getContext(), PERMISSIONS_STORAGE)) { 
      requestPermissionsWithRationale(REQUEST_PERMISSION_STORAGE, PERMISSIONS_STORAGE); 
     } else { 
      dispatchImageSelectionIntent(); 
     } 
    } 

    private void dispatchImageCaptureIntent() { 
     Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 

     if(intent.resolveActivity(getContext().getPackageManager()) != null) { 
      try { 
       File image = createTemporaryFile(); 
       Uri data = FileProvider.getUriForFile(getContext(), "com.example.app.fileprovider", image); 
       intent.putExtra(MediaStore.EXTRA_OUTPUT, data); 
       startActivityForResult(intent, REQUEST_IMAGE_CAPTURE); 
      } catch (IOException exception) { 
       Timber.w(exception, "Error occurred while creating image file"); 
      } 
     } else { 
      // TODO: handle no application to handle intent 
     } 
    } 

    private void dispatchImageSelectionIntent() { 
     final Intent intent = new Intent(Intent.ACTION_PICK, 
       MediaStore.Images.Media.EXTERNAL_CONTENT_URI); 
     if(intent.resolveActivity(getContext().getPackageManager()) != null) { 
      startActivityForResult(intent, REQUEST_IMAGE_SELECTION); 
     } else { 
      // TODO: handle no application to handle intent 
     } dismiss(); 
    } 

    private void dispatchDetailSettingsIntent() { 
     Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, 
       Uri.fromParts("package", getContext().getPackageName(), null)); 
     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 

     if(intent.resolveActivity(getContext().getPackageManager()) != null) { 
      startActivity(intent); 
     } else { 
      // TODO: handle no application to handle intent 
     } dismiss(); 
    } 

    private void dispatchMediaScanIntent(Uri data) { 
     Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, data); 
     getContext().sendBroadcast(intent); 
    } 

    private void displayPermissionsRationale(int requestCode) { 
     switch (requestCode) { 
      case REQUEST_PERMISSION_CAMERA: 
       Timber.d("Request Image capture rationale"); 
       DialogFactory.createRationaleAlert(getContext(), 
         R.string.title_dialog_rationale_camera, 
         R.string.msg_dialog_rationale_camera).show(); 
       break; 
      case REQUEST_PERMISSION_STORAGE: 
       Timber.d("Request Image selection rationale"); 
       DialogFactory.createRationaleAlert(getContext(), 
         R.string.title_dialog_rationale_storage, 
         R.string.msg_dialog_rationale_storage).show(); 
       break; 
      default: 
       Timber.d("No rationale"); 
     } 
    } 

    private void displayRequestPermissionsAlert(@StringRes int message) { 
     Snackbar.make(mView, message, Snackbar.LENGTH_LONG) 
       .setAction(R.string.action_settings, view -> dispatchDetailSettingsIntent()).show(); 
     dismiss(); 
    } 

    private void requestPermissionsWithRationale(int requestCode, @NonNull String[] permissions) { 
     Timber.d("Request permissions with rationale"); 
     if(PermissionUtil.shouldShowRequestPermissionsRationale(this, permissions)) { 
      Timber.d("Display rationale"); 
      displayPermissionsRationale(requestCode); 
     } else { 
      Timber.d("Request Permissions"); 
      requestPermissions(permissions, requestCode); 
     } 
    } 

    private File createTemporaryFile() throws IOException { 
     String fileName = PREFIX_IMAGE_CAPTURE /*+ TimeUtil.getTimeStamp()*/; 

     File directory = getContext().getExternalFilesDir(Environment.DIRECTORY_DCIM); 
     File file = File.createTempFile(fileName, ".jpeg", directory); 

     mCurrentPhotoResource = Uri.fromFile(file); 

     return file; 
    } 

    private void destroyTemporaryFile() { 
     File file = new File(mCurrentPhotoResource.getPath()); 

     if(file.delete()) { 
      Timber.i("Temporary file deleted"); 
     } else { 
      Timber.w("Failed to delete temporary file: " + file); 
     } 
    } 

    private void handleImageCaptureResult(Intent intent) { 
     if(mCurrentPhotoResource != null) { 
      dispatchMediaScanIntent(mCurrentPhotoResource); 

      if (mOnImageChosenListener != null) { 
       mOnImageChosenListener.onImageChosen(mCurrentPhotoResource); 
      } else { 
       Timber.w("Parent Activity or Fragment does not implement OnImageChosenListener; captured result cannot be used"); 
      } 
     } 
    } 

    private void handleImageSelectionResult(Intent intent) { 
     Timber.d("Selection: " + intent.getData()); 
     if(mOnImageChosenListener != null) { 
      mOnImageChosenListener.onImageChosen(intent.getData()); 
     } else { 
      Timber.w("Parent Activity or Fragment does not implement OnImageChosenListener; selected result cannot be used"); 
     } 
    } 

} 

И DialogFactory класс:

public final class DialogFactory { 

    public static AlertDialog createRationaleAlert(
      Context context, @StringRes int title, @StringRes int message) { 
     AlertDialog.Builder builder = new AlertDialog.Builder(context) 
       .setTitle(title).setMessage(message); 
     return createRationaleAlert(context, builder); 
    } 

    public static AlertDialog createRationaleAlert(
      Context context, CharSequence title, CharSequence message) { 
     AlertDialog.Builder builder = new AlertDialog.Builder(context) 
       .setTitle(title).setMessage(message); 
     return createRationaleAlert(context, builder); 
    } 

    private static AlertDialog createRationaleAlert(
      Context context, AlertDialog.Builder builder) { 
     builder.setPositiveButton(R.string.btn_try, (dialog, which) -> { 
      Intent intent = new Intent(Intent.ACTION_VIEW); 
      context.startActivity(intent); 
     }).setNegativeButton(R.string.btn_cancel, (dialog, which) -> { 
      dialog.cancel(); 
     }); 

     return builder.create(); 
    } 

} 

Проблема возникает, когда Fragment вызовы DialogFactory.createRationaleAlert().show().

+0

Я в той же ситуации. Любые используемые решения? –

+0

@ AitorGómez Nope.В настоящее время я показываю только «BottomSheetDialogFragment» после получения разрешения. – Bryan

+0

Я попытался поместить диалог внутри поддиалогового фрагмента, вызванного из первого диалогового окна, но он тоже не работает. Спасибо. –

ответ

2

После долгих раздумий, я, наконец, нашел решение. Хитрость заключается в том, чтобы реализовать DialogInterface.OnShowListener, и создать новый Dialog в onShow() обратного вызова:

public class ImageChooserDialogFragment extends BottomSheetDialogFragment 
     implements DialogInterface.OnShowListener { 

    @Override @NonNull 
    public Dialog onCreateDialog(Bundle savedInstanceState) { 
     super.onCreateDialog(savedInstanceState); 
     getDialog().setOnShowListener(this); 
    } 

    @Override 
    public void onShow(DialogInterface dialog) { 
     if(!PermissionUtil.checkPermissions(getContext(), PERMISSIONS_STORAGE)) { 
      requestPermissionsWithRationale(REQUEST_PERMISSION_STORAGE, PERMISSIONS_STORAGE); 
     } else { 
      displayRequestPermissionsAlert(R.string.msg_snackbar_permissions_storage); 
     } 
    } 

    // ... 

} 
1

Есть ли способ прослушать анимацию BottomSheetDialogFragment?

mBottomSheetController = BottomSheetBehavior.from(mBottomSheet); 
mBottomSheetController.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { 
    @Override 
    public void onStateChanged(@NonNull View bottomSheet, int newState) { 
     switch (newState) { 
      case BottomSheetBehavior.STATE_EXPANDED: 
       //fully expanded, this is the event you want to listen for 
      break; 
     } 
    } 

    @Override 
    public void onSlide(@NonNull View bottomSheet, float slideOffset) { 
     //do nothing 
    } 
}); 
+0

Это не работает. Во-первых, очевидно, что 'onStateChanged()' не вызывается для начального состояния; поэтому, если я сам не настроил состояние, это не вызывается до тех пор, пока не будет перемещен 'BottomSheetDialog'. Во-вторых, если я устанавливаю состояние в STATE_EXPANDED, по какой-то причине я получаю обратный вызов для 'STATE_SETTLING'. Я получаю обратный вызов для 'STATE_EXPANDED', если я физически перемещаю диалог в расширенное состояние. – Bryan

+0

Хотя это и доказывает мое подозрение, что это имеет какое-то отношение к анимации диалога, учитывая, что если я вручную переместил диалог в 'STATE_EXPANDED', он отобразит диалоговое окно логического обоснования над фоном dim. – Bryan

+0

Тогда, возможно, одно (слегка взломанное) решение состоит в том, чтобы вручную вызвать setState перед запуском диалога логики. Я не могу объяснить поведение, которое вы получаете (если вы не манипулируете нижним листом из слушателя) –

0

Создать свой диалог как TYPE_SYSTEM_ALERT.

Используй это:.

builder.create().getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 
+0

Это просто порождает «BadTokenException» с сообщением «Невозможно добавить окно [email protected] - разрешено разрешение для типа окна 2003 ". – Bryan

0

отключить флаг, который отвечает за тусклый фон

dialog.getWindow() clearFlags (WindowManager.LayoutParams.FLAG_DIM_BEHIND);

+0

Это делает фон самого диалога прозрачным, а не фоном в диалоговом окне. – Bryan

+0

попробуйте это alertDialog.getWindow(). ClearFlags (WindowManager.LayoutParams.FLAG_DIM_BEHIND); –

 Смежные вопросы

  • Нет связанных вопросов^_^