cyph / cordova-plugin-chooser

Cordova file chooser plugin.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

the data field of getFile() return is always an empty Uint8Array (Android SDK 28)

zxtxin opened this issue · comments

Yes, looks like data binding is wrong,

u added base64 value to the data property in android, can you please

JSONObject result = new JSONObject();

					result.put("data", base64);
					result.put("mediaType", mediaType);
					result.put("name", name);
					result.put("uri", uri.toString());

I tried using the plugin in an Ionic 3 project, running on Android 10.
The plugin usage in the project is extremely basic:

const filters = 'image/*,document/pdf,document/doc,document/docx,document/ppt,document/pptx,document/xls,document/xlsx';

window.chooser.getFile(filters, (file: any) => {
    // success callback code goes here
}

By doing some analysis and adding some custom logs to the native Android code, it seems like is that this code below, at line 123 from Chooser.java, always returns false.

data.getBooleanExtra(Chooser.INCLUDE_DATA, false)

Even if includeData has the value true in the code below at line 72, inside the execute() method:

intent.putExtra(Chooser.INCLUDE_DATA, includeData);

According to some logs I added, I found the following:

// this is logged inside the "execute()" method, "args[1]" is supposed to be the boolean that decides if the base64 data should be returned
2020-07-08 03:33:54.545 16315-16768/<package.name> I/args[1]: true

// this is logged right before the extras are put into the intent for 
2020-07-08 03:33:54.546 16315-16768/<package.name> I/chooseFile includeData: true 

// This is logged inside the "onActivityResult()" method. 
// The log is exactly this: Log.i("data getBooleanExtra", data.getBooleanExtra(Chooser.INCLUDE_DATA, false).toString());
// Even if "includeData" is set as true, this always results to false.
2020-07-08 03:19:31.960 16315-16315/<package.name> I/data getBooleanExtra: false /

Apparently, for some reason, it seems like the getBooleanExtra() is not finding the value for the key Chooser.INCLUDE_DATA in the Intent's extras, so it always uses the default value, false.

I honestly don't know why this happens or if it's API-level specific, and I honestly can't put much more time and effort into understanding and solving this, right now. I'm also not acquainted with native Android development, so there's also that.
So, as a workaround, I'll simply change the default value for the Chooser.INCLUDE_EXTRA to true, like this:

data.getBooleanExtra(Chooser.INCLUDE_DATA, true)

I'll always need the Base64 string, anyways.

@CaioMelo8 is right,the Intent provided to the onActivityResult method seems not to be the intent that is created with the extra data, thus intent.getExtras() always returns null.

I'm not an experienced Android developer so I changed the code to this, not sure if this is a proper solution.

  private CallbackContext callback;
  private Boolean includeData;

  public void chooseFile (CallbackContext callbackContext, String accept, Boolean includeData) {
    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    intent.setType("*/*");
    if (!accept.equals("*/*")) {
      intent.putExtra(Intent.EXTRA_MIME_TYPES, accept.split(","));
    }
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
    intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
    this.includeData = includeData; // store in instance variable instead of extra

and then this:

  @Override
  public void onActivityResult (int requestCode, int resultCode, Intent data) {
    try {
      if (requestCode == Chooser.PICK_FILE_REQUEST && this.callback != null) {
        if (resultCode == Activity.RESULT_OK) {
          Uri uri = data.getData();

          if (uri != null) {
            ContentResolver contentResolver =
              this.cordova.getActivity().getContentResolver()
              ;

            String name = Chooser.getDisplayName(contentResolver, uri);

            String mediaType = contentResolver.getType(uri);
            if (mediaType == null || mediaType.isEmpty()) {
              mediaType = "application/octet-stream";
            }

            String base64 = "";

            if (this.includeData) {
              byte[] bytes = Chooser.getBytesFromInputStream(
                contentResolver.openInputStream(uri)
              );

              base64 = Base64.encodeToString(bytes, Base64.DEFAULT);
            }

Sorry about the late response on this, and thanks a lot for the PR @se-schwarz! Your PR looks good to me and works in my testing.