3

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.

Andy
  • 10,553
  • 21
  • 75
  • 125

3 Answers3

8

Internal storage is private for each app -- the third-party camera app has no rights to write to your internal storage.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • See, thats exactly what I've been arguing to my boss. But unfortunately because the above code worked at some point they want to keep it in this way. I do have one question that I'd like clarification in: `Context.MODE_WORLD_WRITEABLE`. I made an assumption that it allowed any app to access files and stuff from there because it isn't `MODE_PRIVATE`. Does it not work this way? Basically because it worked at some point I'm going to guess thats what it did. – Andy Aug 02 '13 at 20:53
  • 1
    @Andy: "But unfortunately because the above code worked at some point they want to keep it in this way" -- it certainly would not work reliably, if at all. "Does it not work this way?" -- yes and no. Yes, the file is writeable. You assume that the camera app is writing to the existing file. It might try to delete the existing file and create a new file. It the *directory* is world-writeable, they might succeed on the delete, but then you won't be able to read the resulting file, as it would be owned by the other app and probably private. – CommonsWare Aug 02 '13 at 21:08
  • 2
    @Andy: And bear in mind that there are thousands of camera apps, any of which could respond to your `Intent`, and therefore the behavior will vary. Just because you can get one app to work does not mean that all will. – CommonsWare Aug 02 '13 at 21:09
5

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.

lnstadrum
  • 550
  • 3
  • 15
  • Hi, I completely get that you didn't handle permissions in this post. However, could you allude to something regarding that? For instance - I'm getting "Permission Denial: getCurrentUser() ... requires android.permission.INTERACT_ACROSS_USERS" - which cannot be added in the manifest. So I'm left to believe that it has to do something with asking the camera app to write to my app's internal storage... Any ideas? – marienke Jun 28 '17 at 10:07
  • This is a better solution. – YellowJ Aug 31 '17 at 04:12
1

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.

Girish K Gupta
  • 190
  • 1
  • 7
  • for cases where the SD card is not available, see http://stackoverflow.com/questions/7720383/camera-intent-not-saving-photo – Anti Earth May 05 '14 at 10:13