2

I am building app my first Android app using WebView and Camera features. I have been following the official documentation and I have encounter many problems.

For example Saving Media Files according to the documentation, sample code provided under and I use it as it is provided:

/** Create a file Uri for saving an image or video */
private static Uri getOutputMediaFileUri(int type){
      return Uri.fromFile(getOutputMediaFile(type));
}

/** Create a File for saving an image or video */
private static File getOutputMediaFile(int type){
    // To be safe, you should check that the SDCard is mounted
    // using Environment.getExternalStorageState() before doing this.

    File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
              Environment.DIRECTORY_PICTURES), "MyCameraApp");
    // This location works best if you want the created images to be shared
    // between applications and persist after your app has been uninstalled.

    // Create the storage directory if it does not exist
    if (! mediaStorageDir.exists()){
        if (! mediaStorageDir.mkdirs()){
            Log.d("MyCameraApp", "failed to create directory");
            return null;
        }
    }

    // Create a media file name
    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;
}

It works perfectly on a device that I am running Android 5.0.2 but it can not create the directory on Android Android 6.0.1. I was debugging the process and I noticed that it returns null on this part of the code:

if (! mediaStorageDir.mkdirs()){
            Log.d("MyCameraApp", "failed to create directory");
            return null;
        }

I was searching online and I have found similar threads regarding this problem, such as (Why can i not make a directory inside Environment.DIRECTORY_PICTURES?, android environment.directory_pictures not accessible and Android getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) returns path to /storage/emulated/0 instead of pictures path) etc...

On the last thread if you read a comment says:

If you are looking for memory card path you wont get it on all devices. As per official documentation public static String DIRECTORY_PICTURES Standard directory in which to place pictures that are available to the user. you cannot control what path is returned

Which makes sense, this is why I want to use the default, but since it is not working on Android 6.0.1 I was thinking to apply a workaround by specifying which location to save the files. Taken from the official documentation Image capture intent:

MediaStore.EXTRA_OUTPUT - This setting requires a Uri object specifying a path and file name where you'd like to save the picture. This setting is optional but strongly recommended. If you do not specify this value, the camera application saves the requested picture in the default location with a default name, specified in the returned intent's Intent.getData() field.

If I have understand correctly I could specify an alternative file path location, such as mediaFile = File.createTempFile(timeStamp, extension, mWebViewActivity.getExternalCacheDir());.

This small workaround helped me overcome the problem in Android 6.0.1 and on Android 5.0.2, but the down side is on both cases when I retrieve the data are empty.

Sample of code retrieving data from documentation Receiving camera intent result:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
        if (resultCode == RESULT_OK) {
            // Image captured and saved to fileUri specified in the Intent
            Toast.makeText(this, "Image saved to:\n" +
                     data.getData(), Toast.LENGTH_LONG).show();
        } else if (resultCode == RESULT_CANCELED) {
            // User cancelled the image capture
        } else {
            // Image capture failed, advise user
        }
    }

    if (requestCode == CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE) {
        if (resultCode == RESULT_OK) {
            // Video captured and saved to fileUri specified in the Intent
            Toast.makeText(this, "Video saved to:\n" +
                     data.getData(), Toast.LENGTH_LONG).show();
        } else if (resultCode == RESULT_CANCELED) {
            // User cancelled the video capture
        } else {
            // Video capture failed, advise user
        }
    }
}

In case that I have changed the location to save the data from File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyCameraApp"); to mediaFile = File.createTempFile(timeStamp, extension, mWebViewActivity.getExternalCacheDir());, both Environments (5.0.2 and 6.0.1) can overcome the problem and "pretend that the work fine". By saying pretend I mean I do not get any errors or the APP is not crashing, but there is a problem.

As the documentation states:

If you do not specify this value, the camera application saves the requested picture in the default location with a default name, specified in the returned intent's Intent.getData() field.

Which is what exactly happens in may case. Both versions can not understand the Uri that I am using. On 5.0.2 it is able to take the default location and "somehow" completes the process but on 6.0.1 since it can not find the default location, it just takes the picture/video but it does not store it anywhere so it can retrieved and the data are empty.

I have included in my Manifest file:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

**Update: ** Adding chooser,

public Intent makeChooserIntent(String title, WebChromeClient.FileChooserParams fileChooserParams) {

        Intent galleryIntent = new Intent(Intent.ACTION_CHOOSER, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);

        Intent chooser = Intent.createChooser(galleryIntent, title);

        List<Intent> intentActivitiesList = new ArrayList<>();
        if (checkCameraHardware(mWebViewActivity.getApplicationContext())) {
            Log.d(TAG, "Device has camera.");

            //create new Intent
            Intent videoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
            Intent imageIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);

            intentActivitiesList.add(imageIntent);
            intentActivitiesList.add(videoIntent);
            intentActivitiesList.add(fileChooserParams.createIntent());

            try {
                imageFileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE); // create a file to save the image
                videoFileUri = getOutputMediaFileUri(MEDIA_TYPE_VIDEO);  // create a file to save the video
            } catch (IOException e) {
                Log.e(TAG, "Error creating tmp file: " + e);
            }

            chooser.putExtra(MediaStore.EXTRA_OUTPUT, videoFileUri); // set the image file name
            chooser.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); // set the video image quality to high
            chooser.putExtra(MediaStore.EXTRA_OUTPUT, imageFileUri);
            chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentActivitiesList.toArray(new Parcelable[]{}));
        }
        return chooser;
    }

I think the error comes from the chooser, because is not looking for the correct Uri.

So my question, is this a bug, or I am setting something completely wrong?

Sorry for the long question/post but I tried to explain all problems/solutions that I have applied.

If I debug the retrieved data on Android 5.0.2 if I have changed

Thanos
  • 1,618
  • 4
  • 28
  • 49
  • 2
    If you're targetting SDK 23 and using a Marshmallow (Lollipop will work without problem as long as correct permissions are in the manifest) device you need to obtain dangerous permissions at runtime : http://developer.android.com/training/permissions/requesting.html record audio/reading/writing external storage are considered (rightly so) as 'Dangerous Permissions'. – Mark Feb 17 '16 at 11:04
  • Hello @MarkKeen yeah I noticed that, Initially my APP was crashing, but then I found the workaround as you pointed from the link and it actually really simple. I modified my build.gradle to use `targetSdkVersion 22`and it worked just fine on both Android versions (5.0.2 and 6.0.1). But it is true what you said. – Thanos Feb 17 '16 at 11:08
  • What is the specific problem you're facing? If you've already found solutions to some of your problems then its kind of irrelevant information as it doesn't pertain to the current problem. – Mark Feb 17 '16 at 11:15
  • The problem that I am facing, is that I do not know why the APP can not load the specified URI in both versions (5.0.2 and 6.0.1). On 5.0.2 it uses the default location `File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyCameraApp");` but on 6.0.1 it crashes. So I changed this to `mediaFile = File.createTempFile(timeStamp, extension, mWebViewActivity.getExternalCacheDir());`. The problem is temporary solved as it does not work on 6.0.1, it can not find the Uri, on 5.0.2 it uses the default Uri and stores the data there. – Thanos Feb 17 '16 at 11:23
  • try : `File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath() + "/" + "your_filename_with_extension");` – Mark Feb 17 '16 at 14:15
  • I had the same idea, I tried that 5 minutes ago. It did not work, I think the problem comes from the chooser that I have implemented. I will update my question with the part that I am using from the chooser. – Thanos Feb 17 '16 at 14:41

0 Answers0