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.
Related
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 have an activity where we click image using,
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.TITLE, "NewPicture");
imageUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
takePictureIntent.putExtra(MediaStore.EXTRA_SCREEN_ORIENTATION, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(takePictureIntent, 2);
and have activity for result as,
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
if (resultCode == RESULT_OK)
{
mImageView.setImageBitmap(mImageBitmap);
}
}
after clicking and saving photo onDestroy() is called so, I have used onSaveInstanceState(), onRestoreInstanceState() to resume same activity without recreating it when resumed from background(kept in background for 1 min or 10 secs) still the activity is not restored.
Issue occurs in low memory device currently using Samsung J1 with Version: 4.4.4, RAM:512 MB
How to solve this? Please help thanks in advance.
I have encounter this once,
The problem with your device option
1.got to settings
2.find the developer options
3.In Apps section check the Don't keep activities is false..
what it means image picker is detached form activity as new activity.
once user leaves the old activity it will kill by option on settings.
Check you may found..
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
}
}
I have been using Action_View to install apk using following code
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setDataAndType(Uri.fromFile(new File(location + "myAPK.apk")),
"application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
It opens the Install Prompt window in device. Now user can install or cancel the installation process.
I am only interested when user click the install package but installation failed due to some reason may be corrupt apk or mismatched signed apk etc.
How can i capture the event when installation failed.. Can i get the result from ACTION_INSTALL_PACKAGE
I have gone through reading System Broadcast Messages but all are used for either Pacakge added or replaced.
Any Clue ?
Quoting the documentation for ACTION_INSTALL_PACKAGE:
Output: If EXTRA_RETURN_RESULT, returns whether the install succeeded.
Quoting the documentation for EXTRA_RETURN_RESULT:
Used as a boolean extra field with ACTION_INSTALL_PACKAGE or ACTION_UNINSTALL_PACKAGE. Specifies that the installer UI should return to the application the result code of the install/uninstall. The returned result code will be RESULT_OK on success or RESULT_FIRST_USER on failure.
So, add EXTRA_RETURN_RESULT to your Intent, with a value of true, and use startActivityForResult().
Launch the Intent with startActivityForResult:
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setDataAndType(Uri.fromFile(new File(location + "myAPK.apk")),
"application/vnd.android.package-archive");
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
startActivityForResult(intent, MY_CONSTANT);
Then analyse the result
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case ...
}
}
I only want to add my own considerations and experiences. I had encountered the same problem.
If you want to get the result the best way is that suggested by Nano and CommonsWare.
I want to highlight that if your apk is internally located you may face the problem "parsing the package error". I want remember you that you need the right permission in the manifest <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />.
But I noticed that is also needed add Intent.FLAG_GRANT_READ_URI_PERMISSION.
So the complete example code could be like this mine:
Uri fileUri = FileProvider.getUriForFile(getApplicationContext(), BuildConfig.APPLICATION_ID + ".provider", currentFile);
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setDataAndType(fileUri,"application/vnd.android.package-archive");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
startActivityForResult(intent, APK_INSTALL_CODE);
Then I would also remember you that this mechanism is all asynchronous, so if you need to know the result to move on, you need to wait it.
I don't know if this is the best way to do but it works:
Uri fileUri = FileProvider.getUriForFile(getApplicationContext(), BuildConfig.APPLICATION_ID + ".provider", currentFile);
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setDataAndType(fileUri,"application/vnd.android.package-archive");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
startActivityForResult(intent, APK_INSTALL_CODE);
while (WAIT_APK_INSTALL) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == APK_INSTALL_CODE) {
if (resultCode == 1) {
WAIT_APK_INSTALL = false;
//SUCCESS
}
else {
//FAILED
//maybe it's needed crash the app like me. In my case the installation was necessary
Intent intent = new Intent(getBaseContext(), ErrorActivity.class);
intent.putExtra(GlobalStrings.INTENT_EXTRA_MESSAGE, getString(R.string.apk_installation_failed));
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); // required when starting from Application
startActivity(intent);
System.exit(1); // kill off the crashed app
}
}
}
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;
}
}