7

I can press a button, open up the native camera app, and successfully take a picture. But then when I check the Gallery or Photos native apps on my phone, the picture isn't saved there. I'm very new to Android so it's likely I'm missing something important in my code.

Questions:

1) Where are these pictures being saved?

2) Can I modify the below code somehow to save instead to internal storage, so all pictures taken with my app are private and only accessible through my app?

3) If I wanted to save these pictures to an object, along with some text/other input, what would be the best way? Should I just save a Uri or some identifier to reference the image later, or save the actual BitMap image?

Any help is greatly appreciated, thanks!

Here is my code to take the picture:

mImageButton.setOnClickListener(new View.OnClickListener()
{
    public void onClick(View v)
    {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        imageUri = CameraUtils.getOutputMediaFileUri(CameraUtils.MEDIA_TYPE_IMAGE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
        startActivityForResult(intent, REQUEST_IMAGE);
    }
}

CameraUtils class taken straight from Google developer guides:

public static Uri getOutputMediaFileUri(int type)
{
    return Uri.fromFile(getOutputMediaFile(type));
}

public static File getOutputMediaFile(int type)
{
    File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), "camera");

    if (!mediaStorageDir.exists())
    {
        if (!mediaStorageDir.mkdirs())
        {
            return null;
        }
    }

    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    File mediaFile;
    if (type == MEDIA_TYPE_IMAGE)
    {
        mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                "IMG_" + timeStamp + ".jpg");
    }
    else if(type == MEDIA_TYPE_VIDEO)
    {
        mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                "VID_" + timeStamp + ".mp4");
    }
    else
    {
        return null;
    }

    return mediaFile;
}
pez
  • 3,859
  • 12
  • 40
  • 72
  • Camera permission alone wasn't doing the saving feature for me.. worked when I added the `WRITE_EXTERNAL_STORAGE` beside the `CAMERA` permission. https://developer.android.com/training/data-storage#permissions – Kidus Tekeste Mar 22 '20 at 02:54

1 Answers1

5

1) By looking at the code, I'd expect the pictures to be saved in a directory called 'camera' which would be found in the Pictures folder on your device (external storage). These might not instantly appear in your gallery, however in later versions of Android (Kitkat and maybe jelly-bean though I can't verify that right now) you should be able to open the Photos app and find them somewhere in there. If that is not the case, then launch a file explorer app (example apps are ASTRO File Manager or X-Plore) and browse to the Pictures/camera directory where you should see your images. The next time your media gets re-indexed (phone reboot, or a re-index triggered from elsewhere), you should see these pictures in your gallery/photo apps. If you want to refresh your media programatically, here might help. Finally, make sure you have the READ_EXTERNAL_STORAGE permission in your Android manifest as specified this (Android doc).

2) If you want to save images to be only available to your application, you need to save them to your application's internal data directory. Take a look at this straight from the Android doc. Make sure to use the MODE_PRIVATE flag.

3) For this, you would want to store the file path somewhere accessible to your app. Either you could save your file paths to a text file with some other text data, or you could use a sqlite database. Finally, you could use an ORM like ORMLite for Android to save a java object which might hold data for your picture and have some fields you want to persist (title, description, path, etc). Here and here is an intro on how to get started with SQLite database in Android (straight from the official doc). If you want to use ORMLite, there is plenty of information on their site here. The developer has spent a lot of time answering StackOverflow questions..

All of your questions can be answered with a few simple Google searches. They are very standard and basic things to do in Android, so you should be able to find a plethora of information and tutorials online.

EDIT:

In response to your comment about the second question. This is what I would probably do (or something similar):

Note that I didn't test this. It's from the top of my head. If you have more issues comment here!

Activity code...

mImageButton.setOnClickListener(new View.OnClickListener()
{
    public void onClick(View v)
    {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        imageUri = CameraUtils.getOutputMediaFileUri(currentActivity, CameraUtils.MEDIA_TYPE_IMAGE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
        startActivityForResult(intent, REQUEST_IMAGE);
    }
}

public void onActivityResult(int requestCode, int resultCode, Intent data)
{
    if (requestCode == REQUEST_IMAGE)
    {
        if (resultCode == RESULT_OK)
        {
            String pathToInternallyStoredImage = CameraUtils.saveToInternalStorage(this, imageUri);
            // Load the bitmap from the path and display it somewhere, or whatever
        }
        else if (resultCode == RESULT_CANCELED)
        {
            //Cancel code
        }
    }
}

CameraUtils class code...

public static Uri getOutputMediaFileUri(int type)
{
    return Uri.fromFile(getOutputMediaFile(type));
}

public static File getOutputMediaFile(int type)
{
    File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), "camera");

    createMediaStorageDir(mediaStorageDir);

    return createFile(type, mediaStorageDir);
}

private static File getOutputInternalMediaFile(Context context, int type)
{
    File mediaStorageDir = new File(context.getFilesDir(), "myInternalPicturesDir");

    createMediaStorageDir(mediaStorageDir);

    return createFile(type, mediaStorageDir);
}

private static void createMediaStorageDir(File mediaStorageDir) // Used to be 'private void ...'
{
    if (!mediaStorageDir.exists())
    {
        mediaStorageDir.mkdirs(); // Used to be 'mediaStorage.mkdirs();'
    }
} // Was flipped the other way

private static File createFile(int type, File mediaStorageDir ) // Used to be 'private File ...'
{
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    File mediaFile = null;
    if (type == MEDIA_TYPE_IMAGE)
    {
        mediaFile = new File(mediaStorageDir .getPath() + File.separator +
                "IMG_" + timeStamp + ".jpg");
    }
    else if(type == MEDIA_TYPE_VIDEO)
    {
        mediaFile = new File(mediaStorageDir .getPath() + File.separator +
                "VID_" + timeStamp + ".mp4");
    }
    return mediaFile;
}

public static String saveToInternalStorage(Context context, Uri tempUri)
{
    InputStream in = null;
    OutputStream out = null;

    File sourceExternalImageFile = new File(tempUri.getPath());
    File destinationInternalImageFile = new File(getOutputInternalMediaFile(context).getPath());

    try
    {
        destinationInternalImageFile.createNewFile();

        in = new FileInputStream(sourceExternalImageFile);
        out = new FileOutputStream(destinationInternalImageFile);

        // Transfer bytes from in to out
        byte[] buf = new byte[1024];
        int len;
        while ((len = in.read(buf)) > 0)
        {
            out.write(buf, 0, len);
        }
    }
    catch (IOException e)
    {
        e.printStackTrace();
        //Handle error
    }
    finally
    {
        try {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                in.close();
            }
        } catch (IOException e) {
            // Eh
        }
    }
    return destinationInternalImageFile.getPath();
}

So now you have the path pointing to your internally stored image, which you can then manipulate/load from your onActivityResult.

Community
  • 1
  • 1
maraci
  • 1,090
  • 7
  • 11
  • Thanks for the reply! Regarding #2, I've seen that part of the docs before, but I'm not sure how to implement it into my app. Do you know of any examples that show how to do it? I can only find snippets of code. – pez Oct 06 '14 at 22:26
  • Thanks again! Running your code on my phone, it always seems to freeze in the camera app right after I take the picture. Using `Log.d`, it seems that everything is called, and logcat isn't showing any errors. But after I take the picture, it won't let me confirm it with the check-mark in the native camera app; I can only hit the "x" to re-take the picture, or press the Home or Back buttons on my phone's navigation bar. – pez Oct 06 '14 at 23:35
  • 1
    Do you have these three permissions in your Android manifest? `` `` `` You really only need the write permission to save images (as well as the camera feature I believe). But usually the read is included as well since you'll probably need it. – maraci Oct 07 '14 at 15:21
  • I know I have the permissions to use the camera and to write to external storage, but I'm not sure if I have the read permission. When I get home from work later today I'll check and retest the code and let you know. Thanks for all the effort! – pez Oct 07 '14 at 15:28
  • 1
    Ah I think I know what the problem is. Silly of me for not realizing this sooner. What's happening is that the Camera app is failing to save to your internal URI (it obviously can't..). In my apps, I pass a uri pointing to the external storage in the camera intent (like you were previously doing), and then in my onActivityResult, I open the file and save it to my app's internal folder using basic java IO. I'll edit my answer – maraci Oct 07 '14 at 16:13
  • Looks great, I'll test it as soon as I can and update! – pez Oct 07 '14 at 17:06
  • 2
    Regarding permissions, **you don't need** camera permission to use MediaStore.ACTION_IMAGE_CAPTURE intent. – Alex Cohn Oct 07 '14 at 18:48
  • 1
    @AlexCohn You are right. I was wrong with that comment, did some testing and reading of my own, and realize this now. Thanks! – maraci Oct 07 '14 at 18:58
  • @maraci Did you mean to delete `getOutputInternalMediaFileUri`? It isn't in the `CameraUtils` class anymore, but is still referenced here `imageUri = CameraUtils.getOutputInternalMediaFileUri(currentActivity, CameraUtils.MEDIA_TYPE_IMAGE);` – pez Oct 08 '14 at 00:05
  • 1
    Yes sorry I forgot to make the change in the onClick method. – maraci Oct 08 '14 at 15:46