I have AddActivity, which lets you get the URI from either a picture you can take from the camera, or an image that you can select from the gallery. Then you can go to DetailsActivity to view the image. I have it working right now until you restart the device. After you restart and try to go to DetailsActivity for that image, this is the error:
Caused by: java.lang.SecurityException: Permission Denial: opening provider com.android.providers.media.MediaDocumentsProvider from ProcessRecord{3a5e86d 2915:jeremy.com.wineofmine/u0a321} (pid=2915, uid=10321) requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs
I went to the "Open Files Using Storage Access Framework" Android Development page and read up on the Persist Permissions section. I'm having trouble applying it to my project though.
I think the main thing I don't understand is that it looks like you need to call an intent (in my case inside the DetailsActivity), but I don't even have an intent there.
Here is the intent that lets you pick the gallery image. This is in AddActivity:
Intent intentGallery = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intentGallery.addCategory(Intent.CATEGORY_OPENABLE);
intentGallery.setType("image/*");
intentGallery.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intentGallery.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivityForResult(intentGallery, SELECT_IMAGE);
In the DetailsActivity, this is where it actually crashes:
imageURI = Uri.parse(cursor.getString(cursor.getColumnIndexOrThrow(WineContract.WineEntry.COLUMN_WINE_IMAGE)));
bitmap = null;
try {
//If the cursor does not have anything in the image column, set the image to null, with a height so the textviews look decent
if (cursor.isNull(cursor.getColumnIndexOrThrow(WineContract.WineEntry.COLUMN_WINE_IMAGE))){
mFullImage.setImageBitmap(null);
mFullImage.setMaxHeight(300);
}else{
//remake the bitmap from the URI in the image column
//********This next line is where the program crashes**********
bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageURI);
mFullImage.setImageBitmap(bitmap);
}
Could I get some help with figuring out how to apply this to my project?
To handle permission results overide onRequestPermissionsResult as below
#Override
public void onRequestPermissionsResult (int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case General.REQUESTPERMISSION:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//reload my activity with permission granted or use the features that required the permission
} else {
Messenger.makeToast(getContext(), R.string.noPermissionMarshmallow);
}
break;
}
}
and to persist the permission implement as shown below in your onActivityResult method
#Override
public void onActivityResult (int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//if ok user selected a file
if (resultCode == RESULT_OK) {
Uri sourceTreeUri = data.getData();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getContext().getContentResolver().takePersistableUriPermission(sourceTreeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
}
}
In onActivityResult(), call takePersistableUriPermission() on a ContentResolver, passing in the Uri that you got along with the mode flag(s) that indicate what access you want (read, write, both).
a bit late but...
We need to provide persistent Uri permission. Instead of doing it on onActivityResult as oppose to prior answers, I prefer to add it as a flag:
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).also {
it.addCategory(Intent.CATEGORY_OPENABLE)
it.type = "image/*"
it.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
it.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
Also, a note worth mentioning is persistent permission is available only to Intent.ACTION_OPEN_DOCUMENT and NOT Intent.ACTION_GET_CONTENT whereas the latter one is like a one-time thing.
The permission denial issue needs to be dealt with the first time you receive a URI. If you use registerForActivityResult() instead of startActivityForResult()
// Kotlin
private val pickImage = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result: ActivityResult ->
if (result.resultCode == Activity.RESULT_OK) {
// you will get result here in result.data
val uri = result.data?.data!!
requireActivity().contentResolver.takePersistableUriPermission(
uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
// Do something else with the URI. E.g, save the URI as a string in the database
}
}
Related
I found a tutorial online for creating a QR Code Scanner App for Android. It works great but the output of the scan is a toast notification as you can see in the code:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK) {
val result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data)
if (result != null) {
if (result.contents == null) {
Toast.makeText(this, "Cancelled", Toast.LENGTH_LONG).show()
} else {
Toast.makeText(this, "Scanned: " + result.contents, Toast.LENGTH_LONG).show()
}
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
}
}
What i would like to do is for the scanned code to launch the url inside the app itself. As I want the pages to be hosted in the app itself, what i've done was create an actvity with the webview that loads the internal html page.
I took the scanner code and changed the positive outcome to open the webview activity :
startActivity(Intent(this, PagInfoActivity::class.java))
Which works fine. Anytime a QR Code is detected the app automatically loads the desired activity. I know this is not ideal, as the QR Code link itself is not being used to open the page, but what I was trying to do was to use that scan result on the webview load. I've created QRCodes with text strings instead of URLs so that i could inject them in the loadURL of the webview like this:
WebView.loadUrl("file:///android_asset/*QRCODE string*.html");
Is it possible to call result.contents from the MainActivity?
There are multiple ways you can achieve this. First option can be Explicit Intents
For Example,
You need to pass it as an extra:
Intent i = new Intent(this, PagInfoActivity.class);
i.putExtra("result", result.contents);
startActivity(i);
Then extract it from your PagInfoActivity like this:
Intent intent = getIntent();
String result= intent.getExtras().getString("result");
I tried to get path from
Intent intent = new Intent();
intent.setType("application/pdf");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(
Intent.createChooser(intent, "Complete action using"), 2);
But I did't get path from Marshamallow, I can get path from lollipop.
How I can get file path from internal storage?
I think you will have to use file provider for that.
Refer this.
https://developer.android.com/reference/android/support/v4/content/FileProvider
When you start an activity using startActivityForResult, the calling activity will get the results (if any) in onActivityResult. You should override that method in your calling activity...
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
}
This will method will get called as soon as the results are returned from the activity started by startActivityForResult. You can use the requestCode parameter to match (or filter out) the results you are expecting. In your case, you should check that requestCode is equal to 2 which is the value you provided in startActivityForResult.
If the requestCode is equal to 2, then you know that you need to process a PDF file. The intent parameter "could" have the path of that PDF file...
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
if(requestCode == 2 && data != null){
//This is the uri to the file (which could be null)
Uri uri = data.getData();
if(uri != null){
Log.i("LOG_TAG", String.format("uri path = %s", uri));
}
}
}
Now, to browse and select files you will do it the following way (Android Kitkat and above...I think)...
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/pdf");
startActivityForResult(intent, SELECT_FILE_REQUEST_CODE);
Then you receive the selected PDF uri as mentioned above
I am trying to send mail from my app.The problem is after successful/unsuccessful delivery of the email it doesn't return to the activity, meaning that onActivityResult() is not being called.
Here is my code:
String[] recipients = {"soham#gmail.com"};
Intent email = new Intent(Intent.ACTION_SEND, Uri.parse("mailto:"));
// prompts email clients only
email.setType("message/rfc822");
email.putExtra(Intent.EXTRA_EMAIL, recipients);
try{
// the user can choose the email client
startActivityForResult(Intent.createChooser(email, "Choose an email client from..."), 1);
}catch(android.content.ActivityNotFoundException ex){
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
if(requestCode == 1){
if(resultCode == RESULT_OK){
}else if (resultCode == RESULT_CANCELED){
}
}
}
I have checked this but not working for me. Can anyone tell me what am I doing wrong.
Edit
I got the problem.It will work fine in Activity.But it will not work on Fragment or FragmentActivity. All fragment is closing down forcefully.You can say my app is going on the background.How to solve this issue?Anybody got any idea.
If i uderstand your problem correctly;
In your activity's onActivityResult part you can find your fragment
YourFragment fragment = (YourFragment)getSupportFragmentManager().findFragmentById(R.id.your_framelayout_id);
And use your fragment's public method:
if(fragment != null)
{
fragment.yourPublicMethod();
}
In yourPublicMethod you can do whatever you want. I hope this helps you.
The program flow is that the user selects an image from the gallery and it is displayed in an image view. This works fine. At the same time, the Uri is written to a database.
Later, when the program is run again or that activity is displayed again, the Uri is retrieved from the database and the image is displayed again. This second showing of the image is what causes the exception:
requires android.permission.MANAGE_DOCUMENTS or android.permission.MANAGE_DOCUMENTS
I added the permission to the manifest and the exception still occurs.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
The same procedure attempts to display both images, and it works when it comes from the picker, but fails when it comes from the presistent Uri from the database.
Picker Intent
try {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(
Intent.createChooser(intent, "Select Picture"),
PICK_IMAGE);
} catch (Exception e) {
Toast.makeText(getApplicationContext(),
"Error",
Toast.LENGTH_LONG).show();
Log.e(e.getClass().getName(), e.getMessage(), e);
}
With the result from the picker intent I use
Uri selectedImageUri = data.getData();
With the database read I use. localImage is a String.
Uri.parse(localImage)
I read this bug report https://issues.apache.org/jira/browse/CB-5398, but I'm not sure how to use it to resolve this situation. The code below from another thread seems to attempt to resolve a similar issue with permissions and the photo picker, but in my case I'm not picking again, I just need to display the image from the Uri.
I guess it is a matter of resetting permissions. I'm not really sure. I have the permissions in the manifest. Perhaps I need different code to display an image from a Uri not obtained from the picker? Maybe I need to target a different API? My build.gradle says targetSdkVersion 21, minSdkVersion 15. These were the defaults. I'm in Android Studio 1.0.2.
I'm stumped. Any help is appreciated.
public static final String KITKAT_VALUE = 1002;
Intent intent;
if (Build.VERSION.SDK_INT < 19){
intent = new Intent();
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
startActivityForResult(intent, KITKAT_VALUE);
} else {
intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
startActivityForResult(intent, KITKAT_VALUE);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == KITKAT_VALUE ) {
if (resultCode == Activity.RESULT_OK) {
// do something here
}
}
}
At the same time, the Uri is written to a database.
That's not going to work prior to API Level 19, and then only if you are using the Storage Access Framework (ACTION_OPEN_DOCUMENT) and take the offered persistent permissions. Given your existing code, the temporary permissions that you are granted for that Uri will expire, at the latest when your process is terminated.
I added the permission to the manifest and the exception still occurs.
You cannot hold MANAGE_DOCUMENTS, unless you are signed by the signing key that signed the firmware.
I am attempting to launch the built-in camera to take a picture, a picture that will have a name specified by the activity launching the camera. (code below)
When the camera returns, onActivityResult() goes straight to resultCode == Activity.RESULT_CANCELED. Any explanation for this and solutions would be greatly appreciated.
The camera indeed does take the image, I can see it in my sdcard with a file viewer, but its name is the stock one from the camera. How can I get the name of this taken image to be the one supplied by the activity?
Camera intent code
Intent camera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File image = new File("Team image.jpg");
camera.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
camera.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(image));
camera.putExtra(MediaStore.Images.Media.TITLE, "Team image");
startActivityForResult(camera, PICTURE_RESULT);
activityresult code
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
if(requestCode == PICTURE_RESULT){
if(resultCode == Activity.RESULT_OK) {
if(data!=null){
Bitmap image = BitmapFactory.decodeFile(data.getExtras().get(MediaStore.Images.Media.TITLE).toString());
grid.add(image);
images.addItem(image);
}
if(data==null){
Toast.makeText(Team_Viewer.this, "no data.", Toast.LENGTH_SHORT).show();
}
}
else if(resultCode == Activity.RESULT_CANCELED) {
Toast.makeText(Team_Viewer.this, "Picture could not be taken.", Toast.LENGTH_SHORT).show();
}
}
}
Have you mark the launch mode of your activity as "singleInstance"?
That may cause your first problem.
My camera goes normal when I remove the "singleInstance".
The two issues are likely related, having to do with the way you are creating the file reference that passes to the camera. If you want your image file to save to the SD Card, you need to create a file reference that includes a full-path to that location, not just a filename. For example, this code would save the image file on the SD card root:
Intent camera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File image = new File(Environment.getExternalStorageDirectory(),"TeamImage.jpg");
camera.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(image));
startActivityForResult(camera, PICTURE_RESULT);
I also changed your filename to not include a space; only because I'm not certain that the Camera application won't blow up on that piece also. Since the Camera is getting confused trying to open and write to your file location, that is likely why you always return with RESULT_CANCELED. You don't need the WRITE_EXTERNAL_STORAGE permission here, since the Camera app is doing the SD Card access.
One more note: I don't believe other MediaStore extras can be passed with this Intent. Typically, if you want metadata to be attached to your image, you have to insert the Uri reference with that metadata into the MediaStore ContentProvider prior to saving the image to disk.
Hope that helps!
Not sure what's wrong with your code, here's what works for me:
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, CAMERA_PIC_REQUEST);
and
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
switch (requestCode) {
case CAMERA_PIC_REQUEST:
Bitmap b = (Bitmap) data.getExtras().get("data");
if (b != null) {
updateThumbnail(b);
if (mBitmap != b) {
b.recycle();
}
}
break;
}
}