2

On upgrade to Android 10 the onShowFileChooser code I have been using inside my Hybrid Android Web View App since Android 5 or 6 stopped providing access to the camera. The user could still access stored photos, but the camera option was no longer offered to the user.

The code I had been using is show below

      //For Android 5.0+
    public boolean onShowFileChooser (WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams)
        {
        if(mUMA != null)
            {
            mUMA.onReceiveValue (null);
            }
        mUMA = filePathCallback;
        Intent takePictureIntent = new Intent (MediaStore.ACTION_IMAGE_CAPTURE);
        if (takePictureIntent.resolveActivity (MainActivity.this.getPackageManager()) != null)
            {
            File photoFile = null;
            try
                {
                photoFile = createImageFile();
                takePictureIntent.putExtra("PhotoPath", mCM);
                }
            catch(IOException ex)
                {
                Log.e(TAG, "Image file creation failed", ex);
                }
            if (photoFile != null)
                {
                mCM = "file:" + photoFile.getAbsolutePath();
                takePictureIntent.putExtra (MediaStore.EXTRA_OUTPUT, Uri.fromFile (photoFile));
                }
            else
                {
                takePictureIntent = null;
                }
            }
        Intent contentSelectionIntent = new Intent (Intent.ACTION_GET_CONTENT);
        contentSelectionIntent.addCategory (Intent.CATEGORY_OPENABLE);
        contentSelectionIntent.setType ("image/*");
        Intent[] intentArray;
        if (takePictureIntent != null)
            {
            intentArray = new Intent[] {takePictureIntent};
            }
        else
            {
            intentArray = new Intent[0];
            }

        Intent chooserIntent = new Intent (Intent.ACTION_CHOOSER);
        chooserIntent.putExtra (Intent.EXTRA_INTENT, contentSelectionIntent);
        chooserIntent.putExtra (Intent.EXTRA_TITLE, "Image Chooser");
        chooserIntent.putExtra (Intent.EXTRA_INITIAL_INTENTS, intentArray);
        startActivityForResult (chooserIntent, FileChooserActivityCode);
        return true;
        }

together with the following code to process the result

protected void onActivityResult (int requestCode, int resultCode, Intent intent)
    {
    super.onActivityResult(requestCode, resultCode, intent);

    if (requestCode == FileChooserActivityCode)
        {
        Uri[] results = null;
        //Check if response is positive
        if (resultCode == Activity.RESULT_OK)
            {
            if (null == mUMA)
                {
                return;
                }
            if (intent == null || intent.getData() == null)
                {
                //Capture Photo if no image available
                if (mCM != null)
                    {
                    results = new Uri[]{Uri.parse(mCM)};
                    }
                }
            else
                {
                String dataString = intent.getDataString();
                if(dataString != null)
                    {
                    results = new Uri[]{Uri.parse(dataString)};
                    }
                }
            }
        mUMA.onReceiveValue(results);
        mUMA = null;
        }
    }

I have found out how to access the camera from this link and changed the takePictureIntent code to the following :

    if(takePictureIntent.resolveActivity(MainActivity.this.getPackageManager()) != null) {

    File photoFile = null;
    Uri photoUri = null;

    if (isAndroidQ) {
        // Android Q compatibility
        photoUri = createImageUri();
        mCameraUri = photoUri;
        if (photoUri != null) {
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
        }
    } else {
        try {
            photoFile = createImageFile();
            takePictureIntent.putExtra("PhotoPath", mCM);
        } catch (IOException ex) {
            Log.e(LOG_TAG, "Image file creation failed", ex);
        }
        if (photoFile != null) {
            mCM = "file:" + photoFile.getAbsolutePath();
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
        } else {
            takePictureIntent = null;
        }
    }
}

private Uri createImageUri()
        {
        String status = Environment.getExternalStorageState();
        if (status.equals(Environment.MEDIA_MOUNTED))
            {
            return MainActivity.this.getApplicationContext().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new ContentValues());
            }
        else
            {
            return MainActivity.this.getApplicationContext().getContentResolver().insert(MediaStore.Images.Media.INTERNAL_CONTENT_URI, new ContentValues());
            }
        }

But now after taking the photo inside the onActivityResult code the variable mCM is null and nothing is passed to the web view.

Please can someone tell me what I have missed?

Steve Brooker
  • 1,043
  • 11
  • 28

3 Answers3

2

If you are targeting Android 10 - this version introduces a new, more secure/private way for accessing storage named Scoped Storage. check out THIS article for more info how to implement

for more complex solution you should show what happens in createImageUri and createImageFile methods

HERE we have nice doc how to handle old and new way

optionally you can disable new access way, but only for Android 10, starting 11 this support will be required. Add android:requestLegacyExternalStorage="true" for <application> tag in manifest

snachmsm
  • 17,866
  • 3
  • 32
  • 74
  • optionally you can disable new access way, but only for Android 10, starting 11 this support will be required. Add android:requestLegacyExternalStorage="true" for tag in manifest – sunnyuppal33 Nov 16 '20 at 17:41
  • well, to be precise it isn't... as long as app targets 10 and have `requestLegacyExternalStorage="true"` everything will work on Android 11 and above. another problem is that Google Play forces apps to publish/update versions targeting only 2 versions below current (good move!) – snachmsm Nov 16 '20 at 20:41
1

For Android 10, SDK 30

Add android:requestLegacyExternalStorage="true" for application tag in manifest

Starting from marshmallow, android requires user permissions to access camera and external storage. Without these permissions, the Webview app can’t initiate a camera upload, so you have to make the user to grant permissions, So here is how to fix camera upload in android Webview.

Add this code below the OnCreate method.

if(!hasPermissions(this, PERMISSIONS)){
          ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSION_ALL);
      }

Then add this code in the activity

int PERMISSION_ALL = 1;
   String[] PERMISSIONS = {
           
           android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
           android.Manifest.permission.CAMERA
   };

   public static boolean hasPermissions(Context context, String... permissions) {
       if (context != null && permissions != null) {
           for (String permission : permissions) {
               if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
                   return false;
               }
           }
       }
       return true;
   }}

Remember that you will have to declare permissions in your android manifest too.

<uses-permission
        android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.CAMERA" />
Shah0651
  • 307
  • 3
  • 8
0

All in needed to add was

else if (mCameraUri != null)
                    {
                    results = new Uri[]{mCameraUri};
                    }
Steve Brooker
  • 1,043
  • 11
  • 28