How to persist permission in android API 19 (KitKat)? - java

In my application I store the path of image in my SQlite db for further use. The path that I get is
content://com.android.providers.media.documents/document/image%3A71964
When I retrieve this path from the database and try to retrieve the image from that path android throws
java.lang.SecurityException: Permission Denial: opening provider
com.android.providers.media.MediaDocumentsProvider
from ProcessRecord{42c84ec8 23911:com.gots.gb/u0a248} (pid=23911, uid=10248)
requires android.permission.MANAGE_DOCUMENTS or android.permission.MANAGE_DOCUMENTS
According https://developer.android.com/guide/topics/providers/document-provider.html#permissions I need to persist permission by adding the following code
final int takeFlags = intent.getFlags()
& (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Check for the freshest data.
getContentResolver().takePersistableUriPermission(uri, takeFlags);
When I added this code to my ImageAdapter class which extends BaseAdapter android throws
08-21 02:14:38.530: W/System.err(24452): java.lang.SecurityException:
No permission grant found for UID 10248 and Uri
content://com.android.providers.media.documents/document/image:71964
This is the relevant part of my ImageAdapter code
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView ;
if (convertView == null){
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(185, 185));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(8, 8, 8, 8);
}
else{
imageView = (ImageView)convertView ;
}
InputStream is = null;
Bitmap bitmap = null ;
try {
Log.d(TAG,String.valueOf(list.get(position)));
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
final int takeFlags = intent.getFlags()
& (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Check for the freshest data.
if (Build.VERSION.SDK_INT >= 19){
mContext.getContentResolver().takePersistableUriPermission(list.get(position), takeFlags);
}
is = mContext.getContentResolver().openInputStream(list.get(position));
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
bitmap = BitmapFactory.decodeStream(is,null, options);
is.close();
imageView.setImageBitmap(bitmap);
return imageView;
}
catch (Exception e) {
e.printStackTrace();
return null;
}
what am I doing wrong?
Thanks

I believe I've solved it. The request intent:
Intent intent;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
}else{
intent = new Intent(Intent.ACTION_GET_CONTENT);
}
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setType("image/*");
startActivityForResult(Intent.createChooser(intent, getResources().getString(R.string.form_pick_photos)), REQUEST_PICK_PHOTO);
and onActivityResult
...
// kitkat fixed (broke) content access; to keep the URIs valid over restarts need to persist access permission
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
final int takeFlags = data.getFlags() & Intent.FLAG_GRANT_READ_URI_PERMISSION;
ContentResolver resolver = getActivity().getContentResolver();
for (Uri uri : images) {
resolver.takePersistableUriPermission(uri, takeFlags);
}
}
...
I haven't tested this pre-kitkat, my phone is running 5.1, can anyone verify this on older phones?

The code listed below,
// kitkat fixed (broke) content access; to keep the URIs valid over restarts need to persist access permission
if(Utils.isKitkat())
{
final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION);
ContentResolver resolver = getActivity().getContentResolver();
for (Uri uri : images)
{
resolver.takePersistableUriPermission(uri, takeFlags);
}
}
worked fine for me, anyway I noticed that the max number of persistable uri granted to my app is limited to 128. If I select more than 128 uri, I get the error:
java.lang.SecurityException: Permission Denial: opening provider........
just when I try to process an image for which I wasn't able to persist the permission. Can you figure out any solution?

Related

Edited bitmap only saving drawn changes without original picture in shared storage and empty bitmap in internal storage

I was adding a new feature to my drawing app project where users could open any picture from the phone gallery and draw on it. But I am having issues saving the new bitmap. Here are the details below :
My app has 3 activities :
Main Activity as a menu,
RecyclerView activity that acts as a gallery,
Drawing activity where users draw
Up until recently whenever a user saved an image, it would create two copies of it. One private that would be stored in application's personal storage that is not available to phone's gallery to use but is displayed in applications own gallery activity, and one public picture in media which will be displayed in the phones gallery. My gallery class checks the internal folder of the application and lists all files in it to display, that is why I also save in private app directory too. Save image method has two sections that handles the scoped storage issues with api 28+ and <28 respectively.
Recently I added a feature where users can pick any picture from phone's gallery and edit it (both images that app created and those that it did not).It uses intent picker function.
Expected results are :
Open a picture from the gallery in the drawing activity and draw on top of it
Save edited version as a new image both privately in app's storage and a public shared copy in phones main gallery
Current issues :
Image opens and is drawn upon without problems (both images made by the app and foreign pictures)
Saving images causes the app to save an empty bitmap in phones private gallery and changes (edits drawn) in shared storage on the phone without actual image that was meant to be edited.
Here is the code :
From MainActivty, code that opens the images from phone's gallery in order to edit them.
// checks permissions for reading external and scoped storage before opening image picker
private void pickAnImage() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "SDK >= Q , requesting permission");
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1000);
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q && checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "SDK < Q , requesting permission");
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1000);
} else {
Log.d(TAG, "Permission exists, starting pick intent");
Intent gallery = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(gallery, IMAGE);
}
}
// handles results from permissions and begins intent picker
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1000) {
Log.d(TAG, "result code good");
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Permission is granted, starting pick");
Intent gallery = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(gallery, IMAGE);
} else {
Log.d(TAG, "While result code is good, permission was denied");
Toast.makeText(MainActivity.this, "Permission Denied !", Toast.LENGTH_SHORT).show();
}
}
}
// handles results from intent picker and feeds the image path to the drawing activity for image editing
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && requestCode == IMAGE && data != null) {
Log.d(TAG, "Data from picked image is not null, creating uri and path");
Uri selectedImageUri = data.getData();
String picturePath = getPath(getApplicationContext(), selectedImageUri);
// Log.d("Picture Path", picturePath);
if (picturePath != null) {
Log.d(TAG, "Path creating success, calling art activity");
Intent intent = new Intent(getApplicationContext(), ArtActivity.class);
intent.putExtra("image", picturePath);
startActivity(intent);
} else {
Log.d(TAG, "Path was null");
finish();
}
}else{
Log.d(TAG, "Data seems to be null, aborting");
}
}
// obtains the path from the picked image
private static String getPath(Context context, Uri uri) {
String result = null;
String[] proj = {MediaStore.Images.Media.DATA};
Cursor cursor = context.getContentResolver().query(uri, proj, null, null, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
int column_index = cursor.getColumnIndexOrThrow(proj[0]);
result = cursor.getString(column_index);
Toast.makeText(context, "" + result, Toast.LENGTH_SHORT).show();
}
cursor.close();
}
else {
Toast.makeText(context.getApplicationContext(), "Failed to get image path , result is null or permission problem ?", Toast.LENGTH_SHORT).show();
result = "Not found";
Toast.makeText(context, "" + result, Toast.LENGTH_SHORT).show();
}
return result;
}
Below are methods that handle bitmap editing process :
// Inside initialization method that sets up paint and bitmap objects before drawing
if (bitmap != null) {
// if bitmap object is not null it means that we are feeding existing bitmap to be edited, therefore we just scale it to screen size before giving it to canvas
loadedBitmap = scale(bitmap, width, height);
} else {
// bitmap fed to this method is null therefore it means we are not editing existing picture so we create a new object to draw on
this.bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888, true);
}
Inside onDraw method that handles drawing as it happens :
if (loadedBitmap != null) {
// loadedBitmap not being null means we are editing existing image from above, we previously scaled it so now we feed it to the canvas for editing
canvas.drawBitmap(loadedBitmap, 0, 0, paintLine);
canvas.clipRect(0, 0, loadedBitmap.getWidth(), loadedBitmap.getHeight());
} else {
// we are not editing an image so we draw new empty bitmap to use
canvas.drawBitmap(bitmap, 0, 0, tPaintline);
}
And finally, here is the infamous save image method that is the root of current problems :
#SuppressLint("WrongThread") // not sure what to do otherwise about this lint
public void saveImage() throws IOException {
//create a filename and canvas
String filename = "appName" + System.currentTimeMillis();
Canvas canvas;
// loadedBitmap is the edited one, bitmap is a new one if we did not edit anything
if(loadedBitmap != null){
canvas = new Canvas(loadedBitmap);
} else{
canvas = new Canvas(bitmap);
}
// save image handle for newer api that has scoped storage and updated code
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// feed all the data to content values
OutputStream fos;
ContentResolver resolver = context.getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, filename + ".jpg");
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
fos = resolver.openOutputStream(Objects.requireNonNull(imageUri));
draw(canvas);
// compress correct bitmap, loaded is edited, bitmap is new art
if(loadedBitmap != null){
loadedBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
}else{
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
}
Objects.requireNonNull(fos).close();
imageSaved = true;
} else {
// for api older then 28 before scoped storage
// create app directory and a new image file
ContextWrapper cw = new ContextWrapper(getContext());
File directory = cw.getDir("files", Context.MODE_PRIVATE);
pathString = cw.getDir("files", Context.MODE_PRIVATE).toString();
File myPath = new File(directory, filename + ".jpg");
FileOutputStream fileOutputStream = new FileOutputStream(myPath);
// check permissions
try {
if(ContextCompat.checkSelfPermission(
context, Manifest.permission.WRITE_EXTERNAL_STORAGE) ==
PackageManager.PERMISSION_GRANTED){
draw(canvas);
// save edited or new bitmap to private internal storage
if(loadedBitmap != null){
MediaStore.Images.Media.insertImage(context.getContentResolver(), loadedBitmap, filename, "made with appName");
}else{
MediaStore.Images.Media.insertImage(context.getContentResolver(), bitmap, filename, "made with appName");
}
// now also add a copy to shared storage so it will show up in phone's gallery
addImageToGallery(myPath.getPath(), context);
imageSaved = true;
}else{
// request permissions if not available
requestPermissions((Activity) context,
new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },
100);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
fileOutputStream.flush();
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Save image to gallery method used above :
// for old api <28, it adds the image to shared storage and phone's gallery
public static void addImageToGallery(final String filePath, final Context context) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
values.put(MediaStore.MediaColumns.DATA, filePath);
context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
}
That is all, if anyone knows bitmap, canvas and permissions (old and new) feel free to help. I am sure tha changes I need are small. Check the current issues above in order to know what to look for.

Camera intent is not working properly in android version 11

I have try some following solution to captured image on android version 11. But this solution are not working. when I use bitmap that time I get blur image this Is not visible properly.I have added the below code in the manifest.
android:requestLegacyExternalStorage="true" ` Add this top stored image in external storage`
<queries>
<intent>`
<action android:name="android.media.action.IMAGE_CAPTURE" />
</intent>
</queries>
// add code in class call image Intent
public static Intent getPickImageIntent(Context context) {
mContext = context;
Intent chooserIntent = null;
List<Intent> intentList = new ArrayList<>();
Intent pickIntent = new Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// takePhotoIntent.putExtra("return-data", true);
// takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(getTempFile(context)));
intentList = addIntentsToList(context, intentList, pickIntent);
intentList = addIntentsToList(context, intentList, takePhotoIntent);
if (intentList.size() > 0) {
chooserIntent = Intent.createChooser(intentList.remove(intentList.size() - 1),
context.getString(R.string.pick_image_intent_text));
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentList.toArray(new Parcelable[]{}));
}
return chooserIntent;
}
when I add temp file path then this is not working in above API level 30
// pass image uri to activity set image in imageview
public static Uri getImageFromResultUri(Context context, int resultCode,
Intent imageReturnedIntent) {
File imageFile = getTempFile(context);
Uri selectedImage = null;
int sdkVersion = Build.VERSION.SDK_INT;
if (resultCode == Activity.RESULT_OK) {
boolean isCamera = (imageReturnedIntent == null ||
imageReturnedIntent.getData() == null ||
imageReturnedIntent.getData().toString().contains(imageFile.toString()));
if (isCamera) { /** CAMERA **/
// selectedImage = Uri.fromFile(imageFile);
Bitmap photo = (Bitmap) imageReturnedIntent.getExtras().get("data");
selectedImage = getImageUri(context,photo);
} else { /** ALBUM **/
selectedImage = imageReturnedIntent.getData();
}
}
return selectedImage;
}
when I convert Bitmap image to URI
public static Uri getImageUri(Context mContext, Bitmap inImage){
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
inImage.compress(Bitmap.CompressFormat.JPEG,100,bytes);
String path = MediaStore.Images.Media.insertImage(mContext.getContentResolver(),inImage,"Title",null);
return Uri.parse(path);
}
when I convert the image bitmap to URI I get a thumbnail so this is a blur so how can I get an image in android version 11 without using a bitmap. And I don't what to store this image in the gallery. Image is getting blur in every device.
When I use takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(getTempFile(context))); this code then this is working properly in below version 11. but how can I use same code for android version 11
Uri.fromFile(getTempFile(context)));
You should use FileProvider and FileProvider.getUriForFile() instead to provide a valid uri to camera intent.
Also for versions below Android 11;
which is your target API version? requestLegacyExternalStorage will work only when targetting at most Android 10, when targetting Android 11 you have to use Scoped Storage, thus implement support for this feature. without this implementation you won't be able to access files outside your private app folder on Android 11+ devices

I can't save file to gallery after taking photo by camera by using the FileProvider for Android 7.1.1

I build my app with FileProvider and I want to save the image after I take it. But I can't find the image in the gallery.
I found these source codes from the Android Studio tutorial. I don't know what is the problem. I tried use debugger and I think the createFile() is correct. I also have my bitmap works. It can display the image I take but I can't add the image to the gallery.
I have this in my Manifest.xml
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.temp.test"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths"/>
</provider>
And in the file_paths.xml I have
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external" path="Android/data/com.temp.test/files/Pictures" />
</paths>
This is how I write the activity
private String mCurrentPhotoPath;
private ImageView mImageView;
private ImageButton StartCameraBtn;
private File photoFile = null;
//requestCode
private static final int REQUEST_IMAGE_CAPTURE = 1;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_photo_note);
mImageView = (ImageView) findViewById(R.id.imageView);
StartCameraBtn = (ImageButton) findViewById(R.id.StartCamera);
StartCameraBtn.setOnClickListener(this);
}
public void onClick(View view)
{
clearAllFocus();
switch (view.getId())
{
case R.id.StartCamera:
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null)
{
try
{
photoFile = createFile();
}
catch (IOException e)
{
e.printStackTrace();
}
if(photoFile != null){
Uri photoURI = FileProvider.getUriForFile(this,
"com.temp.test",
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
break;
...
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
mImageView.setImageBitmap(null);
switch (requestCode)
{
case REQUEST_IMAGE_CAPTURE:
if (resultCode == RESULT_OK)
{
setPic();
galleryAddPic();
}
break;
}
}
private File createFile() throws IOException
{
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = image.getAbsolutePath();
return image;
}
private void galleryAddPic()
{
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File(mCurrentPhotoPath);
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
}
private void setPic()
{
// Get the dimensions of the View
int targetW = mImageView.getWidth();
int targetH = mImageView.getHeight();
// Get the dimensions of the bitmap
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
// Determine how much to scale down the image
int scaleFactor = Math.min(photoW/targetW, photoH/targetH);
// Decode the image file into a Bitmap sized to fill the View
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
mImageView.setImageBitmap(bitmap);
}
Please help! Thank you!
I use debugger to test the galleryaddpic() and the URI has been accessed sucessfully, but I don't know why I can't add it to the gallery. I can't find the image file in the android VM directory either.
This is the debug log:
https://i.stack.imgur.com/JN9uJ.png
I'm using chickendinner as my domian, and keep is the name of my app.
Thank you!
Call this function in onActivityResult. It worked for me!
It is in fragment.In activity you can use "this" instead of "getActivity()".
private void galleryAddPic() {
File f = new File(imageFilePath); //set your picture's path
try {
MediaStore.Images.Media.insertImage(getActivity().getContentResolver(),
f.getAbsolutePath(), f.getName(), null);
getActivity().sendBroadcast(new Intent(
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(f)));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
Your code handles
write new photo as a jpg-file to the filesystem
add new photo to media-db via broadcst Intent.ACTION_MEDIA_SCANNER_SCAN_FILE
create a FileProvider that allow other apps to access private //Android/data/com.temp.test/files/Pictures/... files through a content-uri content://com.temp.test/...
I assume that the media-db scanner has no read-permission to your app-s private data directory Android/data/com.temp.test/files/Pictures via a file-uri and therefore cannot add the new photo to media-db.
Whatsapp and other apps store their received/send photo in public readable internal memory (i.e. /sdcard/PICTURES/WhatsApp/ ) where the media scanner can read it via file-uri via Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.
I donot know, if the media scanner can handle content: -uris instead of file uri-s: You can try this:
private void galleryAddPic()
{
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
// assume that mCurrentPhotoPath ="Android/data/com.temp.test/files/Pictures/myTestImage.jpg"
// can be accessed from outside as "content://com.temp.test/myTestImage.jpg"
Uri contentUri = Uri.parse("content://com.temp.test/myTestImage.jpg");
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
}
Please let us know if this works.
If this does not work you can try to manually insert a "content://com.temp.test/..." entry into media database.
using public readable internal memory directories should work without a fileprovider

Issue with an intent working on Lollipop and older, but not on newer

I am currently working on an activity that will let the user select an image from the gallery, and then put that image's URI into an SQLite database.
I have another activity where I take that URI and display it on an ImageView. I have this working perfectly on Lollipop and older. But anything newer it crashes when I pull up the activity that displays the image.
Here is the LOGCAT line of the crash:
Caused by: java.lang.SecurityException: Permission Denial: opening provider com.google.android.apps.photos.contentprovider.MediaContentProvider from ProcessRecord{8193e94 13574:jeremy.com.wineofmine/u0a97} (pid=13574, uid=10097) that is not exported from uid 10044
This makes it seem like a permissions thing, but I am requesting the WRITE_EXTERNAL_STORAGE and READ_EXTERNAL STORAGE in the manifest, as well as requesting those permissions on run-time on the activity where it displays the image.
And here is the exact line where it's crashing (this is in the activity where it displays the image:)
bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageURI);
And this is imageURI:
imageURI = Uri.parse(cursor.getString(cursor.getColumnIndexOrThrow(WineContract.WineEntry.COLUMN_WINE_IMAGE)));
//This code returns this: content://com.google.android.apps.photos.contentprovider/-1/1/content%3A%2F%2Fmedia%2Fexternal%2Fimages%2Fmedia%2F62/ACTUAL/860591124
Here is the code to the relevant bits.
This is the intent to open up the gallery:
Intent intentGallery = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intentGallery, SELECT_IMAGE);
And this is the onActivityResult method. The main goal of this method is to set the imageThumbail ImageView as the thumbnail, and also to set "photoURI" as the selected image's URI.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//This is if they choose an image from the gallery
if (requestCode == SELECT_IMAGE && resultCode == RESULT_OK) {
if (requestCode == SELECT_IMAGE) {
// Get the url from data
Uri selectedImageUri = data.getData();
if (null != selectedImageUri) {
// Get the path from the Uri
String path = getPathFromURI(selectedImageUri);
Log.i("ADDACTIVITY", "Image Path : " + path);
bitmap = null;
try {
bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), selectedImageUri);
} catch (IOException e) {
e.printStackTrace();
}
bitmapThumbnail = ThumbnailUtils.extractThumbnail(bitmap,175,175);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmapThumbnail.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byteArray = stream.toByteArray();
// Set the image in ImageView
imageThumbnail.setImageBitmap(bitmapThumbnail);
//Setting photoURI (which gets put into the database) as the gallery's image URI
photoURI = data.getData();
}
}
}
}
Any pointers to what I could be doing wrong, would be great.

Unable to convert uri to Bitmap in android

I am trying to convert a google doc into a bitmap so I can perform OCR within it.
I am however getting the errors:
E/BitmapFactory: Unable to decode stream: java.io.FileNotFoundException: /document/acc=4;doc=14882: open failed: ENOENT (No such file or directory)
E/ReadFile: Bitmap must be non-null
E/CropTest: Failed to read bitmap
Code:
/**
* Fires an intent to spin up the "file chooser" UI and select an image.
*/
public void performFileSearch(View view) {
// ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's file
// browser.
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
// Filter to only show results that can be "opened", such as a
// file (as opposed to a list of contacts or timezones)
intent.addCategory(Intent.CATEGORY_OPENABLE);
// Filter to show only images, using the image MIME data type.
// If one wanted to search for ogg vorbis files, the type would be "audio/ogg".
// To search for all documents available via installed storage providers,
// it would be "*/*".
intent.setType("application/vnd.google-apps.document");
startActivityForResult(intent, READ_REQUEST_CODE);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData){
if(requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK){
Uri uri = null;
if(resultData != null){
uri = resultData.getData();
Log.i(TAG, "Uri" + uri.toString());
Toast.makeText(MainActivity.this, "Uri:" + uri.toString(), Toast.LENGTH_LONG).show();
IMGS_PATH = Environment.getExternalStorageDirectory().toString()+ "/TesseractSample/imgs";
prepareDirectory(IMGS_PATH);
prepareTesseract();
startOCR(uri);
}
}
}
//Function that begins the OCR functionality.
private void startOCR(Uri imgUri) {
try {
Log.e(TAG, "Inside the startOCR function");
BitmapFactory.Options options = new BitmapFactory.Options();
// 1 - means max size. 4 - means maxsize/4 size. Don't use value <4, because you need more memory in the heap to store your data.
options.inSampleSize = 4;
// FileOutputStream outStream = new FileOutputStream(String.valueOf(imgUri));
Bitmap bm = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imgUri);
// bm.compress(Bitmap.CompressFormat.PNG,100,outStream);
Bitmap bitmap = BitmapFactory.decodeFile(imgUri.getPath());
// bitmap = toGrayscale(bitmap);
//The result variable will hold whatever is returned from "extractText" function.
result = extractText(bm);
//Creating the intent to go to the CropTest
Intent intentToCropTest = new Intent(MainActivity.this, CropTest.class);
intentToCropTest.putExtra("result",result);
startActivity(intentToCropTest);
//Setting the string result to the content of the TextView.
// textView.setText(result);
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
You are trying to treat Android Uri as file path. Don't do that. Instead retrive a ContentResolver instance and use it to convert Uri to stream:
AssetFileDescriptor fd = context.getContentResolver()
.openAssetFileDescriptor(uri, "r");
InputStream is = fd.createInputStream();
If AssetFileDescriptor is not supported (you get null or an Exception happens), try a more direct route:
InputStream is = context.getContentResolver().openInputStream();
There is also another super-duper powerful content-type-aware approach, which existed for ages, but was re-discovered by Google's own developers around the time of Android N release. It requires a lot more infrastructure on ContentProvider side (may not be supported in older versions of Google Services):
ContentResolver r = getContentResolver();
String[] streamTypes = r.getStreamTypes(uri, "*/*");
AssetFileDescriptor descriptor = r.openTypedAssetFileDescriptor(
uri,
streamTypes[0],
null);
InputStream is = descriptor.createInputStream();
Then use obtained stream to create Bitmap:
Bitmap bitmap = BitmapFactory.decodeStream(is);

Categories

Resources