#android #android-camera #android-permissions #android-camera-intent #android-11
Вопрос:
Я открываю программу выбора камеры и файлов в своем приложении. Он отлично работает во всех версиях, включая Android 11. Но в Google pixel 5 с Android 11 он ломается при попытке открыть камеру. Приложение не ломается, но камера продолжает закрываться, отображается предупреждение, и ниже происходит исключение.
java.lang.Исключение IllegalStateException: GCA не предоставил разрешение на запись file:///storage/emulated/0/Pictures/JPEG_20210727_095347_634440296695227002.jpg
Java-код :
/***Camera or file chooser ***/
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
if ( Build.VERSION.SDK_INT <= 29) {
file_path = filePathCallback;
Intent takePictureIntent = null;
boolean includePhoto = true;
if (includePhoto)
{
takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null)
{
File photoFile = null;
try {
photoFile = create_image();
takePictureIntent.putExtra("PhotoPath", cam_file_data);
} catch (IOException ex) {
Log.e(TAG, "Image file creation failed", ex);
}
if (photoFile != null) {
cam_file_data = "file:" photoFile.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
} else {
cam_file_data = null;
takePictureIntent = null;
}
}
}
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType(file_type);
if (multiple_files) {
contentSelectionIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
}
Intent[] intentArray;
if (takePictureIntent != null) {
intentArray = new Intent[]{
takePictureIntent
};
} else {
intentArray = new Intent[0];
}
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "File chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
startActivityForResult(chooserIntent, file_req_code);
return true;
} else if ( Build.VERSION.SDK_INT >= 30) {
if (mFilePathCallback != null) {
mFilePathCallback.onReceiveValue(null);
}
mFilePathCallback = filePathCallback;
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
File photoFile = null;
try {
photoFile = createImageFile();
takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath);
} catch (IOException ex) {
}
if (photoFile != null) {
mCameraPhotoPath = "file:" photoFile.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(photoFile));
} else {
takePictureIntent = null;
}
}
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("image/*");
Intent[] intentArray;
if (takePictureIntent != null) {
intentArray = new Intent[]{takePictureIntent};
} else {
intentArray = new Intent[0];
}
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE);
return true;
} else {
return false;
}
}
private File create_image() throws IOException {
@SuppressLint("SimpleDateFormat")
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "img_" timeStamp "_";
File storageDir = getApplicationContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES); //getCacheDir();
return File.createTempFile(imageFileName, ".jpg", storageDir);
}
Перезвонить
/*******Camera Request Callback******/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (requestCode != INPUT_FILE_REQUEST_CODE || mFilePathCallback == null)
{
super.onActivityResult(requestCode, resultCode, intent);
return;
}
Uri[] results = null;
if (resultCode == Activity.RESULT_OK) {
if (intent == null) {
if (mCameraPhotoPath != null) {
results = new Uri[]{Uri.parse(mCameraPhotoPath)};
}
} else {
String dataString = intent.getDataString();
if (dataString != null)
{
results = new Uri[]{Uri.parse(dataString)};
}
}
}
mFilePathCallback.onReceiveValue(results);
mFilePathCallback = null;
} else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
Uri[] results = null;
if (resultCode == Activity.RESULT_CANCELED) {
if (requestCode == file_req_code) {
file_path.onReceiveValue(null);
return;
}
}
if (resultCode == Activity.RESULT_OK) {
if (requestCode == file_req_code) {
if (null == file_path) {
return;
}
ClipData clipData;
String stringData;
try {
clipData = intent.getClipData();
stringData = intent.getDataString();
} catch (Exception e) {
clipData = null;
stringData = null;
}
if (clipData == null amp;amp; stringData == null amp;amp; cam_file_data != null) {
results = new Uri[]{
Uri.parse(cam_file_data)
};
} else {
if (clipData != null) {
final int numSelectedFiles = clipData.getItemCount();
results = new Uri[numSelectedFiles];
for (int i = 0; i < clipData.getItemCount(); i ) {
results[i] = clipData.getItemAt(i).getUri();
}
} else {
results = new Uri[]{
Uri.parse(stringData)
};
}
}
}
}
file_path.onReceiveValue(results);
file_path = null;
} else {
if (requestCode == file_req_code) {
if (null == file_data)
return;
Uri result = intent == null || resultCode != Activity.RESULT_OK ? null : intent.getData();
file_data.onReceiveValue(result);
file_data = null;
}
}
}
Файл манифеста Android :
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.CAMERA2" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29"
/>
Комментарии:
1. Я думаю, что эта проблема связана с новым механизмом хранения данных в Android 11. Поскольку вы создаете временный файл, можете ли вы попробовать сохранить его в каталоге кэша приложения вместо
getApplicationContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
. В этом случае вам даже не понадобитсяWRITE_EXTERNAL_STORAGE
разрешение2. Дайте мне знать, исправил ли он это для вас или нет, и проголосуйте, если вы найдете это полезным.
3. Вы должны разбиться с a
FileUriExposedException
, какUri.fromFile()
это было запрещено, по сути, в течение пяти лет. ИспользуйтеFileProvider
, пожалуйста. В качестве побочного преимущества это прояснит эту проблему.4. @gtxtreme Можете ли вы поделиться некоторыми примерами кода, чтобы использовать каталог кэша для хранения? Перепробовал много кодов с помощью FileProvider. Но некоторые ошибки проявляются.
5. @CommonsWare Происходит только исключение IllegalStateException.