The image source is a Uri which is stored in Firestore.
If a user saves their profile photo from either camera or album, it gets its Uri then stores as a string in the Firestore. There's no problem until this point.
I'll say the photo taken from Camera as CameraImage, and the photo chosen from the album as AlbumImage.
But when displaying the AlbumImage's Uri(as String) retrieved from Firestore, Security Exception is thrown.
Possible solutions I'm assuming are:
need to save the Uri in a different way.
need to load Uri in a different way.
Loding the image(error on this line):
iv_photo.setImageURI(Uri.parse(mUser.getPhotoString()));
Activity for result:
Intent selectIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
selectIntent.setType("image/*");
galleryResultLauncher.launch(selectIntent);
ActivityResultLauncher<Intent> galleryResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
#Override
public void onActivityResult(ActivityResult result) {
if(result.getResultCode() == Activity.RESULT_OK){
Uri selectedImg = result.getData().getData();
iv_photo.setImageURI(selectedImg);
mUri = selectedImg.toString();
}
}
});
Security Exception:
Permission Denial: opening provider com.google.android.apps.photos.contentprovider.impl.MediaContentProvider from ProcessRecord{c7fba1f 23624:com.example.seanlee_thefootballgallery_2201/u0a165} (pid=23624, uid=10165) that is not exported from UID 10118
AlbumImage URIs is similar to this.
content://com.example.seanlee_thefootballgallery_2201/app_images/10-02-2022-09-17-01.jpg
CameraImage URIs is similar to this.
content://com.google.android.apps.photos.contentprovider/-1/1/content%3A%2F%2Fmedia%2Fexternal%2Fimages%2Fmedia%2F25/ORIGINAL/NONE/image%2Fjpeg/967503137
Manifest:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
you can upload your photo to firebase storage first, you can read this upload file to firebase storage .after that you can add more code like this to get Url from storage
StorageReference storageReference = FirebaseStorage.getInstance().getReference().child()
//that child is up to you
storageReference.putFile(imageUri).addOnCompleteListener(task ->
storageReference.getDownloadUrl().addOnSuccessListener(uri -> {
String url = uri.toString();
}));
after that you can save url into firestore. if you want show it into your app you should get url from firestore first after that download it from firebase storage. for download file you can read this download file from firebase storage
hope it can solve your problem :)
Solved.
Changing intent action from ACTION_PICK to ACTION_OPEN_DOCUMENT solved the issue.
Related
I am trying to write a simple function to register a user to firebase and upload his profile picture to firebase storage.
The code I currently have:
public static void createNewUser(String email, String username, String password, #Nullable Uri profileImage) {
auth.createUserWithEmailAndPassword(email, password).addOnCompleteListener(authTask -> {
if (authTask.isSuccessful()) {
assert auth.getCurrentUser() != null;
String uid = auth.getCurrentUser().getUid();
StorageReference storageReference = storage.getReference("profileImages/" + uid);
if (profileImage != null)
storageReference.putFile(profileImage);
}
else throw new RuntimeException("Error creating user!");
});
}
The Uri is retrieved with the following code:(The Uri is a path to a image in device storage)
ActivityResultLauncher<String> contentGetter = registerForActivityResult(new ActivityResultContracts.GetContent(), uri -> {
profileImage.setImageURI(uri);
this.profileImageUri = uri;
});
contentGetter.launch("image/*");
NOTE: the error is thrown from "storageReference.putFile(profileImage);"
When I run this code I get the following error:
java.lang.SecurityException: Permission Denial: opening provider com.android.providers.media.MediaDocumentsProvider from ProcessRecord{fcb00f2 10396:com.mediamania/u0a146} (pid=10396, uid=10146) requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs
My current premissions:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Any idea what causes this?
Found the error, I was using the wrong intent in the file chooser, here's the code snippet:
ActivityResultLauncher<Intent> chooserLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() != Activity.RESULT_OK)
throw new RuntimeException("Error choosing image!");
Intent data = result.getData();
// Process result
}
);
// Configure chooser
Intent chooser = new Intent(Intent.ACTION_OPEN_DOCUMENT);
chooser.addCategory(Intent.CATEGORY_OPENABLE);
chooser.setType("image/*");
In androidx.activity version 1.2.0-alpha05 API for TakePicture contract has been changed:
The TakePicture contract now returns a boolean indicating success rather than a thumbnail Bitmap as this was very rarely supported by camera apps when writing the image to the provided Uri
While in alpha04 callback received a Bitmap object, now only a Boolean object that describes success is received by the callback.
So now the Uri Parameter of the launch method of the launcher must not be null, but must be the destination where the picture is saved. Did not manage to create an Uri object that is accepted by the launcher and that can be used for my app to read the result picture.
Does anybody have an example for me for a valid Uri object that can be provided to the launcher?
I can't find any example on the internet
Here is an example.
File file = new File(getFilesDir(), "picFromCamera");
Uri uri = FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".provider", file);
ActivityResultLauncher<Uri> mGetContent = registerForActivityResult(
new ActivityResultContracts.TakePicture(),
new ActivityResultCallback<Boolean>() {
#Override
public void onActivityResult(Boolean result) {
// do what you need with the uri here ...
}
});
mGetContent.launch(uri);
Note1: You are likely to run into FileUriExposedException , need to expose this uri for the camera app to access
Related: android.os.FileUriExposedException: file:///storage/emulated/0/test.txt exposed beyond app through Intent.getData()
Note2: If you have <uses-permission android:name="android.permission.CAMERA" /> declared in your manifest, you need to have permission before launching, otherwise java.lang.SecurityException: Permission Denial
thanks #babyishTank for good answer, after adding following changes works for me
val directory = File(context.filesDir, "camera_images")
if(!directory.exists()){
directory.mkdirs()
}
val file = File(directory,"${Calendar.getInstance().timeInMillis}.png")
Uri uri = FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".provider", file);
ActivityResultLauncher<Uri> mGetContent = registerForActivityResult(
new ActivityResultContracts.TakePicture(),
new ActivityResultCallback<Boolean>() {
#Override
public void onActivityResult(Boolean result) {
// do what you need with the uri here ...
}
});
mGetContent.launch(uri);
and in filepaths.xml add following code
<files-path
name="images"
path="camera_images/"/>
I'm trying to select pdf file from the device and upload them to the server. ACTION_GET_CONTENT is used to select pdf from the device.
sel_book.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent();
intent.setType("application/pdf");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select PDF"), 1);
}
});
On activity result I get the Uri and save it as a String.
public void onActivityResult(int requestCode, int resultCode, Intent result) {
if (resultCode == RESULT_OK) {
if (requestCode == 1) {
Uri uri = result.getData();
String uriString = uri.toString();
File myFile = new File(uriString);
path = myFile.getAbsolutePath();
}
}
}
It results the path as, /document/primary:Download/Aptitude_2016_17.pdf. I need to use this to create a new file. File selectedFile = new File(selectedFilePath);. But it doesn't create File. selectedFile.isFile() returns false. I have no idea why is it. Please help me to solve this. Thanks in advance.
ACTION_GET_CONTENT is used to select pdf from the device.
This allows the user to select a piece of content. It does not have to be a file.
On activity result I get the Uri and save it as a String.
That is not how you use a Uri.
It results the path as, /document/primary:Download/Aptitude_2016_17.pdf.
That is not a filesystem path. That is a part of a Uri that has a content scheme. You do not get a file from ACTION_GET_CONTENT. You get a Uri that points to a piece of content. That Uri could point to anything that the user and the other app choose:
A file that you can access, via a Uri with a file scheme
A file, but one that you cannot access (e.g., on internal storage of another app)
The contents of a BLOB column in the database
A piece of content that needs to be downloaded
And so on
Use ContentResovler and openInputStream() to get a stream on whatever the content is. Either use that directly (with whatever you are using to upload this content), or use that stream to make your own file with a copy of that content, so that you have a file that you can use.
I have been at this all day and can't seem to get it to work. It use to work before according to the previous person who worked on it.
cameraIntent = new Intent("android.media.action.IMAGE_CAPTURE");
String imageName = CustomApp.getTimeStamp(0) ;
String path = CustomApp.getCurrentActivity().getDir("images", Context.MODE_WORLD_WRITEABLE).getPath()+File.separator+imageName;
File file = new File(path) ;
Uri img = Uri.fromFile(file) ;
Intent passthruDataIntent = new Intent();
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, img);
CustomApp.getCurrentActivity().startActivityForResult(cameraIntent, CustomConstants.REQUESTCODE_CAMERA);
Similar code has been posted on here, but it doesn't seem to work on my nexus 4 on 4.2.2. I tried external storage and it works fine then. Any insight on why it might not be working would be very helpful. Thanks.
Internal storage is private for each app -- the third-party camera app has no rights to write to your internal storage.
I was fighting with the same problem and got a solution much later that the question was asked, but I believe this might be useful to the community.
With the new dynamic permissions introduced in Android 6.0 / API level 23 the topic question has become particularly important, since you need to request the permissions at runtime and handle the both accepting and rejecting reactions of the user. To use the camera activity you need to ask for the corresponding permission first (android.permission.CAMERA). Then, if you store the picture in an external directory, the corresponding permission android.permission.READ_EXTERNAL_STORAGE also needed to be granted to your app by the user. A runtime permission request seems natural to the user at the moment when the user is about to perform the intended action (e.g., if the camera access permission request appears just after the button "Take photo" is pressed). However, if you use the external storage to save the camera picture, you need to ask at the same time for two permissions when your app takes a photo: (1) use the camera and (2) access the external storage. The latter might be frustrating since it is not necessarily clear why your app tries to reach the user files while the user expects just a photo to be taken.
The solution allowing to avoid the external storage and to save the camera picture directly consists in using the content providers. According to the storage options documentation,
Android provides a way for you to expose even your private data to other applications — with a content provider. A content provider is an optional component that exposes read/write access to your application data, subject to whatever restrictions you want to impose.
This is exactly what you need to allow to the camera activity to save the picture directly into the local storage of your app, so that you can easily access it then without requesting additional permissions (only the camera access needed to be granted).
A good article with a code example is provided here. The following generalized code inspired by this article is used in our app to do the trick.
The content provider class:
/**
* A content provider that allows to store the camera image internally without requesting the
* permission to access the external storage to take shots.
*/
public class CameraPictureProvider extends ContentProvider {
private static final String FILENAME = "picture.jpg";
private static final Uri CONTENT_URI = Uri.parse("content://xyz.example.app/cameraPicture");
#Override
public boolean onCreate() {
try {
File picture = new File(getContext().getFilesDir(), FILENAME);
if (!picture.exists())
if (picture.createNewFile()) {
getContext().getContentResolver().notifyChange(CONTENT_URI, null);
return true;
}
} catch (IOException | NullPointerException e) {
e.printStackTrace();
}
return false;
}
#Nullable
#Override
public ParcelFileDescriptor openFile(#NonNull Uri uri, #NonNull String mode) throws FileNotFoundException {
try {
File picture = new File(getContext().getFilesDir(), FILENAME);
if (!picture.exists())
picture.createNewFile();
return ParcelFileDescriptor.open(picture, ParcelFileDescriptor.MODE_READ_WRITE);
} catch (IOException | NullPointerException e) {
e.printStackTrace();
}
return null;
}
#Nullable
#Override
public Cursor query(#NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return null;
}
#Nullable
#Override
public String getType(#NonNull Uri uri) {
String lc = uri.getPath().toLowerCase();
if (lc.endsWith(".jpg") || lc.endsWith(".jpeg"))
return "image/jpeg";
return null;
}
#Nullable
#Override
public Uri insert(#NonNull Uri uri, ContentValues values) {
return null;
}
#Override
public int delete(#NonNull Uri uri, String selection, String[] selectionArgs) {
return 0;
}
#Override
public int update(#NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
}
The content provider is needed to be declared in the app manifest:
<provider android:authorities="xyz.example.app"
android:enabled="true"
android:exported="true"
android:name="xyz.example.app.CameraPictureProvider" />
Finally, to use the content provider in order to capture the camera picture, the following code is invoked from a calling activity:
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, CameraPictureProvider.CONTENT_URI);
startActivityForResult(takePictureIntent, 0);
Please note that the camera permission request needed to be handled separately (it is not done in the presented code sample).
It is also worth noticing that the permission requests needed to be handled only if you are using build tools version 23 or higher. The same code is compatible with lower-level build tools, and is useful in case you are not bothered by the runtime permission requests but just want to avoid using the external storage.
I had the same problem. I solved it by first saving the photos on external memory and then copied to internal memory. Hope this helps.
i'm trying to create an app that allows video recording. i know that using MediaStore.ACTION_IMAGE_CAPTURE, it actually calls the camera system from my app and after taking the picture, it will return to my app with result.
while using the code, i found a MediaStore.ACTION_VIDEO_CAPTURE. which i assume it will camera but in video mode, rather then image capturing mode.
the code that i used for calling the camera in video mode:
Intent takeVideoFromCameraIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
Uri mUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory(), "/Record/vid_"+ String.valueOf(System.currentTimeMillis()) + ".mp4"));
takeVideoFromCameraIntent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, mUri);
startActivityForResult(takeVideoFromCameraIntent, RESULT_OK);
when i run the app with a real device, it does call the camera in video mode and also allows video recording. however, when i press the record button to finish recording, it returns to my app with a force close message saying that the camera is not responding.
at 1st, i thought that the video has not been captured, but when i searched for the file, it actually exist.
then, i thought its my onActivityResult method that causes the problem, but after i comment it with /* ... */ , it still have the same problem. but there isn't any details shown in LogCat.
i realize that i got the error because i'm adding extra to it. what i just needed to do is
Intent takeVideoFromCameraIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
startActivityForResult(takeVideoFromCameraIntent, 1111);
then, add an onActivityResult, with the request code == 1111 (depends on what you entered) and retrieve the last modified file that consist of the extension ".mp4" from the default folder of camera "DCIM/Camera"
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == 1111)//cam
{
File folder = new File(Environment.getExternalStorageDirectory(), "/DCIM/Camera");
long folderModi = folder.lastModified();
FilenameFilter filter = new FilenameFilter()
{
public boolean accept(File dir, String name)
{
return (name.endsWith(mp4));
}
};
File[] folderList = folder.listFiles(filter);
String recentName = "";
for(int i=0; i<folderList.length;i++)
{
long fileModi = folderList[i].lastModified();
if(folderModi == fileModi)
{
recentName = folderList[i].getName();
}
}
}
this way, i can get the name of the file and also do the modification (e.g renaming) with it.
hope this would help other people. =)
please,add your logcat.
For the video capture, i am using the MediaRecorder class, I suggest you tu use this.
If you are interested, i can give you the right code.
I think, your problem is resolved by using this code.
//create new Intent
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
fileUri = getOutputMediaFileUri(MEDIA_TYPE_VIDEO); // create a file to save the video
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // set the image file name
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); // set the video image quality to high
// start the Video Capture Intent
startActivityForResult(intent, CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE);
Use this code in an activity and also set some property in xml file.
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
If you have another problem, please reply me.