ODK Collect: how to avoid polluting the DCIM folder with duplicated images

Hi,

With ODK Collect, every time I capture an image while filling a form,
two copies of the picture are saved: one in the system's default
location for pictures taken with the camera (/sdcard/DCIM/Camera on my
device), plus one in the instance directory.
Eventually, when I delete the saved instance, the copy inside the
instance folder will be deleted with the instance, but the "original"
one will stay there forever until you manually delete it.

I thought this was expected behavior but I don't want the duplicate file
in /sdcard/DCIM. I thought I would change the source code a little bit
so that it would move instead of copying the file, or delete it
after copying. I thought the Camera component (or whatever it is called)
was saving photos into a default location and returning the url, and
that Collect was copying the picture from that url to the instance folder.

However, I had a look at the source code and I found out that ODK
Collect never actually copies the file from a location it doesn't
determine to a location it determines...

Instead, ODK Collect starts an Intent to capture the picture and passes
to this intent the location where to save the image, which is a fixed
temporary file path /sdcard/odk/.cache/temp.jpg. Then, when the intent
returns, it moves the file from this temporary file to the instance
folder (and gives it a unique name). I know it does so as a workaround
to a particular bug in older versions of Android but that's not the
point. (see code below)

So this seems to indicate that the duplication happens outside ODK
before the intent completes and return. That is, when you ask the camera
to capture a photo and save it into a given path, it actually saves two
copies of it, one in the given path and one in its default location
/sdcard/DCIM/Camera.

Is there a way to tell the intent to save the file only into the path
you pass it, and not saving a copy in the default location? (indeed this
is what I would expect it to do when you tell it where you want the file
to be saved). Or is this another Android bug? Or is this impossible by
design (that is, a picture taken via an intent has to be saved in the
default location, even if you are also saving it elsewhere)??

Thanks
m.

From ImageWidget.java:
Intent i = new
Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);

             i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT,
                 Uri.fromFile(new File(Collect.TMPFILE_PATH)));
             try {
                 ((Activity) getContext()).startActivityForResult(i,
                     FormEntryActivity.IMAGE_CAPTURE);
                 mWaitingForData = true;
             } catch (ActivityNotFoundException e) {
                 Toast.makeText(getContext(),

getContext().getString(R.string.activity_not_found, "image capture"),
Toast.LENGTH_SHORT);
}

From FormEntryActivity.java's onActivityResult:

             File fi = new File(Collect.TMPFILE_PATH);

             String mInstanceFolder =
                 mInstancePath.substring(0, 

mInstancePath.lastIndexOf("/") + 1);
String s = mInstanceFolder + "/" +
System.currentTimeMillis() + ".jpg";

             File nf = new File(s);
             if (!fi.renameTo(nf)) {
                 Log.e(t, "Failed to rename " + fi.getAbsolutePath());
             } else {
                 Log.i(t, "renamed " + fi.getAbsolutePath() + " to " 
  • nf.getAbsolutePath());
    }

The tricky thing is that we want users to be able to replace the built-in
Camera app with their own implementations, so putting code to specifically
delete the built-in app's images within Collect is not ideal; Carl or Yaw
may know a better way to handle this. This is also a concern when
attempting to encrypt or otherwise secure the contents of a submission on
the phone.

Mitch

··· On Mon, Dec 26, 2011 at 1:30 PM, Matteo Sisti Sette < matteosistisette@gmail.com> wrote:

Hi,

With ODK Collect, every time I capture an image while filling a form, two
copies of the picture are saved: one in the system's default location for
pictures taken with the camera (/sdcard/DCIM/Camera on my device), plus one
in the instance directory.
Eventually, when I delete the saved instance, the copy inside the instance
folder will be deleted with the instance, but the "original" one will stay
there forever until you manually delete it.

I thought this was expected behavior but I don't want the duplicate file
in /sdcard/DCIM. I thought I would change the source code a little bit so
that it would move instead of copying the file, or delete it after
copying. I thought the Camera component (or whatever it is called) was
saving photos into a default location and returning the url, and that
Collect was copying the picture from that url to the instance folder.

However, I had a look at the source code and I found out that ODK Collect
never actually copies the file from a location it doesn't determine to a
location it determines...

Instead, ODK Collect starts an Intent to capture the picture and passes to
this intent the location where to save the image, which is a fixed
temporary file path /sdcard/odk/.cache/temp.jpg. Then, when the intent
returns, it moves the file from this temporary file to the instance
folder (and gives it a unique name). I know it does so as a workaround to a
particular bug in older versions of Android but that's not the point. (see
code below)

So this seems to indicate that the duplication happens outside ODK before
the intent completes and return. That is, when you ask the camera to
capture a photo and save it into a given path, it actually saves two copies
of it, one in the given path and one in its default location
/sdcard/DCIM/Camera.

Is there a way to tell the intent to save the file only into the path
you pass it, and not saving a copy in the default location? (indeed this is
what I would expect it to do when you tell it where you want the file to be
saved). Or is this another Android bug? Or is this impossible by design
(that is, a picture taken via an intent has to be saved in the default
location, even if you are also saving it elsewhere)??

Thanks
m.

From ImageWidget.java:
Intent i = new Intent(android.provider.**
MediaStore.ACTION_IMAGE_**CAPTURE);

           i.putExtra(android.provider.**MediaStore.EXTRA_OUTPUT,
               Uri.fromFile(new File(Collect.TMPFILE_PATH)));
           try {
               ((Activity) getContext()).**startActivityForResult(i,
                   FormEntryActivity.IMAGE_**CAPTURE);
               mWaitingForData = true;
           } catch (ActivityNotFoundException e) {
               Toast.makeText(getContext(),

getContext().getString(R.**string.activity_not_found, "image capture"),
Toast.LENGTH_SHORT);
}

From FormEntryActivity.java's onActivityResult:

           File fi = new File(Collect.TMPFILE_PATH);

           String mInstanceFolder =
               mInstancePath.substring(0,

mInstancePath.lastIndexOf("/") + 1);
String s = mInstanceFolder + "/" +
System.currentTimeMillis() + ".jpg";

           File nf = new File(s);
           if (!fi.renameTo(nf)) {
               Log.e(t, "Failed to rename " + fi.getAbsolutePath());
           } else {
               Log.i(t, "renamed " + fi.getAbsolutePath() + " to " +

nf.getAbsolutePath());
}

--
Mitch Sundt
Software Engineer
University of Washington
mitchellsundt@gmail.com

The Android camera (at least in early versions) has lots of weird
bugs. That plus our desire to have the image in the Android image
content provider (otherwise viewing the image is really slow) is why
things are built the way they are.

As far as I know, the image capture code we have saves the image in
the temp location first (has to be on public storage, like /sdcard
because the Camera app has no write permissions elsewhere), and then
copies it to the instance folder. If you are seeing duplicates,
Android's media scanner on your device might now be running on every
image capture and sticking the image (either the temp or the copied
image) into the DCIM folder. Or there could be another Android
feature/bug on your device.

The fastest way to sort out your use case is to build a sample
application that takes and displays a picture using intents. Start by
targeting your device and APIs, then if you need support on other
devices, try it out on those. You may have to look at the Android
camera code and media scanning code (http://source.android.com/) to
get the behavior you want.

We also have a post on StackOverflow that might help.

··· On Wed, Dec 28, 2011 at 13:47, Mitch S wrote: > The tricky thing is that we want users to be able to replace the built-in > Camera app with their own implementations, so putting code to specifically > delete the built-in app's images within Collect is not ideal; Carl or Yaw > may know a better way to handle this. This is also a concern when > attempting to encrypt or otherwise secure the contents of a submission on > the phone. > > Mitch > > > > On Mon, Dec 26, 2011 at 1:30 PM, Matteo Sisti Sette wrote: >> >> Hi, >> >> With ODK Collect, every time I capture an image while filling a form, two >> copies of the picture are saved: one in the system's default location for >> pictures taken with the camera (/sdcard/DCIM/Camera on my device), plus one >> in the instance directory. >> Eventually, when I delete the saved instance, the copy inside the instance >> folder will be deleted with the instance, but the "original" one will stay >> there forever until you manually delete it. >> >> I thought this was expected behavior but I don't want the duplicate file >> in /sdcard/DCIM. I thought I would change the source code a little bit so >> that it would _move_ instead of _copying_ the file, or delete it after >> copying. I thought the Camera component (or whatever it is called) was >> saving photos into a default location and returning the url, and that >> Collect was copying the picture from that url to the instance folder. >> >> However, I had a look at the source code and I found out that ODK Collect >> never actually copies the file from a location it doesn't determine to a >> location it determines... >> >> Instead, ODK Collect starts an Intent to capture the picture and passes to >> this intent the location where to save the image, which is a fixed temporary >> file path /sdcard/odk/.cache/temp.jpg. Then, when the intent returns, it >> _moves_ the file from this temporary file to the instance folder (and gives >> it a unique name). I know it does so as a workaround to a particular bug in >> older versions of Android but that's not the point. (see code below) >> >> >> So this seems to indicate that the duplication happens outside ODK before >> the intent completes and return. That is, when you ask the camera to capture >> a photo and save it into a given path, it actually saves two copies of it, >> one in the given path and one in its default location /sdcard/DCIM/Camera. >> >> Is there a way to tell the intent to save the file _only_ into the path >> you pass it, and not saving a copy in the default location? (indeed this is >> what I would expect it to do when you tell it where you want the file to be >> saved). Or is this another Android bug? Or is this impossible by design >> (that is, a picture taken via an intent _has_ to be saved in the default >> location, even if you are _also_ saving it elsewhere)?? >> >> >> >> Thanks >> m. >> >> >> >> >> From ImageWidget.java: >> Intent i = new >> Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); >> >> i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, >> Uri.fromFile(new File(Collect.TMPFILE_PATH))); >> try { >> ((Activity) getContext()).startActivityForResult(i, >> FormEntryActivity.IMAGE_CAPTURE); >> mWaitingForData = true; >> } catch (ActivityNotFoundException e) { >> Toast.makeText(getContext(), >> >> getContext().getString(R.string.activity_not_found, "image capture"), >> Toast.LENGTH_SHORT); >> } >> >> >> >> From FormEntryActivity.java's onActivityResult: >> >> File fi = new File(Collect.TMPFILE_PATH); >> >> String mInstanceFolder = >> mInstancePath.substring(0, >> mInstancePath.lastIndexOf("/") + 1); >> String s = mInstanceFolder + "/" + >> System.currentTimeMillis() + ".jpg"; >> >> File nf = new File(s); >> if (!fi.renameTo(nf)) { >> Log.e(t, "Failed to rename " + fi.getAbsolutePath()); >> } else { >> Log.i(t, "renamed " + fi.getAbsolutePath() + " to " + >> nf.getAbsolutePath()); >> } >> >> > > > > -- > Mitch Sundt > Software Engineer > University of Washington > mitchellsundt@gmail.com

Hello,

I am using ODK in my Android application, and I also facing same issue, in which image is getting saved to both Camera folder and odk instance folder.
The images contain sensitive information, so this is severe issue for us.
Do we have any fix or work around to deal with this issue.