Android cordova plugin onActivityResult is never executed, solution

If you're working on a plugin that uses a third party API and is code that you can't simply modify as you wish, you may found code which uses the onActivityResult function to retrieve a result from the api.

Well, this is not problematic at all if you're working on any Android project but if you've tried to achieve this task working on a Cordova Plugin, probably you want to kill yourself right now because it simply doesn't works. So, please don't do it, at less not today.

Maybe you already noticed (captain obvious) that this problem is due to the Main Cordova Activity which for some reason, doesn't get triggered and you are not able to override this method. So the solution, theoretically is really simple, just create an intent and delegate the event to that intent and retrieve it in cordova using cordova.startActivityForResult.

In the following example, we are going to use the OneDrive filepicker as example.

Solution

As said before, you can't simply override the onActivityResult within the same Cordova Activity, therefore we are going to create an Empty View (a new intent) which is going to start the Filepicker. To create an empty activity, add the following markup in your plugin.xml file to create an usable view (which we are going to manipulate with a Java class later). In this case the name will be (the Intent needs to have the same name of the Java class) DialogShowPicker, note that it needs to be inside the android platform tag :

<platform name="android">
   <!--This will be the activity, if you want another name change in both of the tags (action and activity) -->
   <config-file target="AndroidManifest.xml" parent="/manifest/application">
      <activity android:name="com.ourcodeworld.plugins.onedrivefilepicker.DialogShowPicker"
        android:label="OneDrive filepicker">
        <intent-filter>
          <!-- We are going to use this name to start the activity later in Java -->
          <action android:name="com.ourcodeworld.plugins.onedrivefilepicker.DialogShowPicker" />
          <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
      </activity>
  </config-file>
</platform>

That should create the activity in the project and we can call it in our main class. You just need to create a new Intent, give the name (that we gave previously in the plugin.xml file) of the activity, and send it as parameter to the cordova.startActivityForResult which will do the trick for you.

Go to the execute function of your main class and handle the mentioned algorithm. Follow this example :

package com.ourcodeworld.plugins.onedrivefilepicker;// Change the package name according to your plugin.

import org.apache.cordova.*;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Intent;
import android.content.Context;// To toast
import android.widget.Toast;// ToToast
import android.os.Bundle;

public class myPluginMainClass extends CordovaPlugin {
    private static final String ACTION_SHOWPICKER = "showpicker";
    private CallbackContext PUBLIC_CALLBACKS = null;

    @Override
    public boolean execute(String action, JSONArray data, CallbackContext callbackContext) throws JSONException {
        final JSONObject arg_object = data.getJSONObject(0);
        String ONEDRIVE_APP_ID = "thisisTheAppIdfortheExample";
        String LINK_MODE = "download";
        PUBLIC_CALLBACKS = callbackContext;
        // If the showpicker action is executed from javascript, start the activity that starts the picker
        if (ACTION_SHOWPICKER.equals(action)) {
            // The intent expects as first parameter the given name for the activity in your plugin.xml
            Intent intent = new Intent("com.ourcodeworld.plugins.onedrivefilepicker.DialogShowPicker");
            // Send some info to the activity to retrieve it later
            intent.putExtra("app_id", ONEDRIVE_APP_ID);
            intent.putExtra("link_mode", LINK_MODE);
            // Now, cordova will expect for a result using startActivityForResult and will be handle by the onActivityResult.
            cordova.startActivityForResult((CordovaPlugin) this, intent, 0);
        }

        // Send no result, to execute the callbacks later
        PluginResult pluginResult = new  PluginResult(PluginResult.Status.NO_RESULT);
        pluginResult.setKeepCallback(true); // Keep callback

        return true;
    }

    @Override
    public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
        if(resultCode == cordova.getActivity().RESULT_OK){
            Bundle extras = data.getExtras();// Get data sent by the Intent
            String information = extras.getString("data"); // data parameter will be send from the other activity.
            tolog(information); // Shows a toast with the sent information in the other class
            PluginResult resultado = new PluginResult(PluginResult.Status.OK, "this value will be sent to cordova");
            resultado.setKeepCallback(true);
            PUBLIC_CALLBACKS.sendPluginResult(resultado);
            return;
        }else if(resultCode == cordova.getActivity().RESULT_CANCELED){
            PluginResult resultado = new PluginResult(PluginResult.Status.OK, "canceled action, process this in javascript");
            resultado.setKeepCallback(true);
            PUBLIC_CALLBACKS.sendPluginResult(resultado);
            return;
        }
        // Handle other results if exists.
        super.onActivityResult(requestCode, resultCode, data);
    }
    
    // A function to show a toast with some data, just demo
    public void tolog(String toLog){
        Context context = cordova.getActivity();
        int duration = Toast.LENGTH_SHORT;

        Toast toast = Toast.makeText(context, toLog, duration);
        toast.show();
    }
}

Now we need to handle what will happen when the activity is started. Create the Java class (DialogShowPicker.java) inside your android code and don't forget to include it in your plugin. The java class should look like :

package com.ourcodeworld.plugins.onedrivefilepicker;// Change the package name acording to your plugin name

import org.apache.cordova.*;
import android.app.Activity;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Intent;
import android.os.Bundle;
import com.microsoft.onedrivesdk.picker.*;

public class DialogShowPicker extends Activity{
    private boolean firstTime = true;

    @Override
    public void onStart() {
        super.onStart();
        // Write your code inside this condition
        // Here should start the process that expects the onActivityResult
        if(firstTime == true){
            // Do something at first initialization
            // And retrieve the parameters that we sent before in the Main file of the plugin
            Bundle extras = getIntent().getExtras();
            if (extras != null) {
               String appId = extras.getString("app_id");
               linkMode = extras.getString("link_mode");
               mPicker = Picker.createPicker(appId);

               // startPicking method will execute onActivityResult whe na file is selected.
               if(linkMode.equals("view")){
                  mPicker.startPicking(this, LinkType.WebViewLink);
               }else if(linkMode.equals("download")){
                  mPicker.startPicking(this, LinkType.DownloadLink);
               }
            }
        }
    }

    @Override
    public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
        firstTime = false;// There's a result, allow to exit the activity !
        
        // Do something with the result of the Intent data
 
        // Send parameters to retrieve in cordova.
        Intent intent = new Intent();
        intent.putExtra("data", "This is the sent information from the 2 activity :) ");
        setResult(Activity.RESULT_OK, intent);
        finish();// Exit of this activity !
    }
}

And add it to your plugin using :

<!-- Change target-dir according to your plugin -->
<source-file src="src/android/DialogShowPicker.java" target-dir="src/com/ourcodeworld/plugin/"/>

Then just execute the function with javascript (which you should define by yourself as you want in the plugin). You can see the following diagram to see what we've just done:

Notes

  • You're only able to send primitive values between Intents (strings and numbers)

Have fun

Become a more social person