Tutorial hero image
Lesson icon

Migrating Cordova Plugins to Capacitor (Android)

13 min read

Originally published September 02, 2020

Cordova has a huge ecosystem of existing plugins that have been created over the past decade. Capacitor has its own method for allowing developers to create plugins, for themselves or for the community in general, but this ecosystem is still in its infancy as Capacitor is a relatively new project. It will take some time before Capacitor plugins are as prolific as their Cordova counterparts, which is why it is fortunate that we can still use Cordova plugins in Capacitor.

This means that, in general, you won't need to give up access to the plugins you want to use if you move to Capacitor. Most of the time, installing a Cordova plugin in Capacitor is as simple as running:

npm install name-of-plugin

and then running:

npx cap sync

To pull that plugin into your native Capacitor projects. However, that is not always the case. There are some Cordova plugins that just won't work in Capacitor, and some that will require additional manual configuration before they work. This tutorial will explore how Capacitor utilises Cordova plugins, and how we can attempt to migrate Cordova plugins that don't just work out of the box.

WARNING: The purpose of this tutorial is to serve as a deep dive into how a Cordova plugin is utilised by Capacitor and, through understanding this process, how we can attempt to configure plugins correctly for Capacitor. It goes into a lot of technical detail, and if you're only interested in getting the cordova-plugin-facebook4 plugin up and running you might find it frustrating. I have a much more brief tutorial that covers setting up cordova-plugin-facebook4 in Capacitor here: Using Cordova Plugins that Require Install Variables with Capacitor.

Outline

1. Cordova Plugins that are Incompatible with Capacitor

Before we get into the main portion of this tutorial, it is important to note that there is a list of known plugins that do not work with Capacitor. In some cases Capacitor provides its own way to achieve the functionality already, and sometimes there are other reasons the plugin doesn't work. You can find a list of these incompatible plugins here: known incompatible Cordova plugins. Keep in mind that this is not necessarily an exhaustive list of all incompatible plugins.

2. Cordova Plugins that Require Additional Configuration

This will be the main focus of this tutorial: Cordova plugins that don't work out of the box, but can be made to work with some additional configuration. The trick is to figure out what needs to be done to configure the plugin correctly, which can be difficult to do if you don't really know how it works behind the scenes.

The basic principles behind how Cordova plugins and Capacitor plugins work are the same. They both have code that runs in the native iOS or Android projects, and allow communication between your application running in the web view and the rest of the native code, such that your application can send requests for native functionality and receive results.

This is why Capacitor is able to utilise Cordova plugins, because the idea is fundamentally the same. Capacitor can just take the same native files that the Cordova plugin is using and add them to the Capacitor project.

One of the key differences between Cordova and Capacitor is that Cordova takes more of an abstracted approach, where things are configured through Cordova. Capacitor takes a more simplistic approach where things are generally configured directly in the native projects. This means that there are a lot of things happening in "Cordova-land" for a plugin to work outside of the native files for the plugin, and that is going to need to be translated to work in the native projects that Capacitor creates for us (either by us, or by Capacitor itself).

The plugin.xml File

The plugin.xml file that Cordova plugins use is the key to getting everything correctly configured for Capacitor. The plugin.xml file serves as a sort of road map as to how the plugin should be installed and configured in a Cordova project. Capacitor will look at this file and interpret it the best it can in order to set up the plugin in Capacitor as well. Most of the time this works just fine.

Sometimes, however, it might not work. This is especially the case when you want to use a Cordova plugin that makes use of install variables that look like this:

cordova plugin add cordova-plugin-facebook4 --variable APP_ID="123456789" --variable APP_NAME="myApplication"

Capacitor doesn't have a mechanism to supply variables like this when installing a plugin. By understanding how the plugin.xml file works, we can see what sort of configurations need to be added to our native projects, and if there is anything that Capacitor hasn't been able to add automatically we can attempt to do it ourselves.

We will use the cordova-plugin-facebook4 plugin as an example to see how Capacitor treats the plugin.xml file. This tutorial will focus specifically on Android.

NOTE: To really solidify these concepts, you might find it useful to set up your own Capacitor project and follow along in Android Studio. It is also helpful to have the source code for the cordova-plugin-facebook4 plugin to reference on GitHub, and even the source code for Capacitor as well.

Example Cordova/Capacitor Plugin Migration: Facebook (cordova-plugin-facebook4)

Let's try to get a complete picture of what is happening when we install a Cordova plugin in a Capacitor project, and we will talk about how to tackle the manual configuration we need to do along the way.

Consider the cordova-plugin-facebook4 plugin that is used to access the Facebook SDK in Cordova projects (and with the techniques we will cover in this tutorial... Capacitor projects too). Most of the functionality for this plugin is contained within the src/ios and src/android folders. It is in these folders that the native code to make the Facebook SDK work for both iOS and Android is contained, therefore you will find a bunch of Objective-C and Java code.

For the sake of this tutorial, it is not important to understand what is going on in these files (nor is it ever, generally, if you are just using the plugin). The purpose of the code in these files is to call various methods of the Facebook SDK and expose them to the application through Cordova (or in our case, Capacitor).

So, these files are probably important for Capacitor to pull into the project if we want to make use of the plugin. What happens to them?

We could go ahead and install the plugin in a Capacitor project to find out by running the following commands:

npm install cordova-plugin-facebook4
npx cap sync

NOTE: Keep an eye out for warnings when you run the npx cap sync command, as it may give hints as to additional configurations you will need to add to the native projects.

If we inspect the native Android project inside of our Capacitor project we would find that the ConnectPlugin.java file that was inside of the src/android folder of the plugin is now located at:

  • capacitor-cordova-android-plugins/src/main/java/org.apachce.cordova.facebook/ConnectPlugin.java

Great! The native file to make this functionality work has been set up by Capacitor automatically. But we're not quite done. This Cordova plugin does more than just what is contained in that file, there is still additional configurations that Cordova performs by following instructions in the plugin.xml file that we talked about. Therefore, there is more Capacitor is going to have to do as well.

Let's take a look at that now, and how the plugin.xml file plays into getting the plugin set up for usage in Capacitor. As I mentioned before, understanding the plugin.xml file is the key to understanding how a plugin needs to be set up in Capacitor.

Although there is a bit more to the plugin.xml, we will just be focusing on the Android portion of the plugin.xml file, which is contained within the <platform name="android"> tag:

plugin.xml

    <platform name="android">
        <js-module src="www/facebook-native.js" name="FacebookConnectPlugin">
            <clobbers target="facebookConnectPlugin" />
        </js-module>

        <config-file target="res/xml/config.xml" parent="/*">
            <feature name="FacebookConnectPlugin">
                <param name="android-package" value="org.apache.cordova.facebook.ConnectPlugin" />
                <param name="onload" value="true" />
            </feature>
            <access origin="https://m.facebook.com" />
            <access origin="https://graph.facebook.com" />
            <access origin="https://api.facebook.com" />
            <access origin="https://*.fbcdn.net" />
            <access origin="https://*.akamaihd.net" />
            <preference name="android-minSdkVersion" value="15" />
        </config-file>

        <source-file src="src/android/facebookconnect.xml" target-dir="res/values" />
        <!-- Used for cordova-android 6 -->
        <config-file target="res/values/facebookconnect.xml" parent="/*">
            <string name="fb_app_id">$APP_ID</string>
            <string name="fb_app_name">$APP_NAME</string>
            <bool name="fb_hybrid_app_events">$FACEBOOK_HYBRID_APP_EVENTS</bool>
        </config-file>
        <!-- Used for cordova-android 7 -->
        <config-file target="app/src/main/res/values/facebookconnect.xml" parent="/*">
            <string name="fb_app_id">$APP_ID</string>
            <string name="fb_app_name">$APP_NAME</string>
            <bool name="fb_hybrid_app_events">$FACEBOOK_HYBRID_APP_EVENTS</bool>
        </config-file>

        <config-file target="AndroidManifest.xml" parent="application">
            <meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/fb_app_id"/>
            <meta-data android:name="com.facebook.sdk.ApplicationName" android:value="@string/fb_app_name" />
            <activity android:name="com.facebook.FacebookActivity"
              android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
              android:label="@string/fb_app_name" />
        </config-file>

        <framework src="com.facebook.android:facebook-android-sdk:$FACEBOOK_ANDROID_SDK_VERSION"/>

        <!-- cordova plugin src files -->
        <source-file src="src/android/ConnectPlugin.java" target-dir="src/org/apache/cordova/facebook" />

    </platform>

There are a few different types of tags present here:

  • <js-module>
  • <config-file>
  • <source-file>
  • <framework>

Each of these contain instructions on how the native platforms should be configured. A lot of this will be performed by Capacitor for us automatically, but not everything can be handled for us. Let's see how Capacitor treats this file.

First, we have the <js-module> section:

<js-module src="www/facebook-native.js" name="FacebookConnectPlugin">
    <clobbers target="facebookConnectPlugin" />
</js-module>

The src indicates the JavaScript file in the cordova-plugin-facebook4 plugin that we are working in, in this case the one located at www/facebook-native.js, that will be loaded by the browser to export the methods that the plugin makes available to the application. This file exports a bunch of methods like: getLoginStatus, showDialog, login, logout, and so on. These are the methods that we would call from within our application to access the native functionality. A clobbers target of facebookConnectPlugin here means that these methods would be made available under the window.facebookConnectPlugin namespace. This means that within our application we would be able to make a call to window.facebookConnectPlugin.getLoginStatus() to access the functionality.

This is how <js-module> is used by Cordova, but how does Capacitor handle this instruction?

There are a few things that go on behind the scenes, but in short, Capacitor will:

  1. Create its own cordova_plugins.js file that defines a list of all of the Cordova plugins being used, and each plugin entry will contain a reference to the JavaScript file that needs to be loaded (e.g. facebook-native.js)
  2. This list is then loaded in the cordova.js file, and prepared for export by the JSExport.java file
  3. Capacitor's Bridge.java file (which is the "main engine" of Capacitor) will then use its JSInjector to inject the necessary JavaScript files for the plugin directly into the web view for use by the app.

In the end, we have more or less the same result without having to perform any configuration ourselves. Capacitor will take that facebook-native.js file that exports all of the methods we can use to integrate with the Facebook SDK, and inject it into the web view which will make it available to our application.

Let's move on to the next section:

<config-file target="res/xml/config.xml" parent="/*">
    <feature name="FacebookConnectPlugin">
        <param name="android-package" value="org.apache.cordova.facebook.ConnectPlugin" />
        <param name="onload" value="true" />
    </feature>
    <access origin="https://m.facebook.com" />
    <access origin="https://graph.facebook.com" />
    <access origin="https://api.facebook.com" />
    <access origin="https://*.fbcdn.net" />
    <access origin="https://*.akamaihd.net" />
    <preference name="android-minSdkVersion" value="15" />
</config-file>

This section wants to add some configurations to the res/xml/config.xml file. The way in which Capacitor will handle this is to look for any config-file that has a target that includes config.xml in its name and it will add the configurations to it's own config.xml file located at app/src/main/res/xml/config.xml in the native Android project. The result will look like this:

<?xml version='1.0' encoding='utf-8'?>
<widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
  <access origin="*" />

  <feature name="FacebookConnectPlugin">
    <param name="android-package" value="org.apache.cordova.facebook.ConnectPlugin"/>
    <param name="onload" value="true"/>
  </feature>

</widget>

Notice that only the <feature> entries get copied over. This is where it is useful to understand this process in detail. Now we can clearly see something that is being left out when Capacitor tries to use this plugin. If this is something required to make the plugin work, we could now investigate configuring our native project to also include these additional configurations. In this case, I don't think it will make a difference as the default Capacitor config.xml file already sets a wildcard access origin: <access origin="*"> and the minSdkVersion is already higher than 15.

If there were a <preference> there that we did need to set, we could set that manually in the capacitor.config.json file by adding a preferences object to capacitor.config.json. For example, if we did need to set android-minSdkVersion to 15 we could do it like this:

{
  "appId": "com.joshmorony.myapp",
  "appName": "myapp",
  "bundledWebRuntime": false,
  "npmClient": "npm",
  "webDir": "www",
  "plugins": {
    "SplashScreen": {
      "launchShowDuration": 0
    }
  },
  "cordova": {
    "preferences": {
      "android-minSdkVersion": "15"
    }
  }
}

The config.xml file would then end up looking like this:

<?xml version='1.0' encoding='utf-8'?>
<widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
  <access origin="*" />

  <feature name="FacebookConnectPlugin">
    <param name="android-package" value="org.apache.cordova.facebook.ConnectPlugin"/>
    <param name="onload" value="true"/>
  </feature>

  <preference name="android-minSdkVersion" value="15" />
</widget>

The end result is again similar here, but it's important to keep in mind that the Cordova plugin's plugin.xml file can't control what happens in Capacitor directly. In this sense, even though this particular result ends up at res/xml/config.xml, if the target specified a different file path it wouldn't make a difference to Capacitor (any that have config.xml in the target will get added to this same file, any that don't will be ignored).

NOTE: Capacitor will also look for <config-file> entries with a target of AndroidManifest.xml and add any configurations supplied there to the AndroidManifest.xml file that is automatically created at capacitor-cordova-android-plugins/src/main/AndroidManifest.xml. This file will automatically be merged with any other AndroidManifest.xml files from other modules in the project (including the main manifest file at app/src/main/AndroidManifest.xml).

Still with me? Let's keep diving.

The next few sections looks like this:

<source-file src="src/android/facebookconnect.xml" target-dir="res/values" />
<!-- Used for cordova-android 6 -->
<config-file target="res/values/facebookconnect.xml" parent="/*">
    <string name="fb_app_id">$APP_ID</string>
    <string name="fb_app_name">$APP_NAME</string>
    <bool name="fb_hybrid_app_events">$FACEBOOK_HYBRID_APP_EVENTS</bool>
</config-file>
<!-- Used for cordova-android 7 -->
<config-file target="app/src/main/res/values/facebookconnect.xml" parent="/*">
    <string name="fb_app_id">$APP_ID</string>
    <string name="fb_app_name">$APP_NAME</string>
    <bool name="fb_hybrid_app_events">$FACEBOOK_HYBRID_APP_EVENTS</bool>
</config-file>

This should be interesting because now we are encountering those Cordova install variables, in this case:

  • $APP_ID
  • $APP_NAME
  • $FACEBOOK_HYBRID_APP_EVENTS

This doesn't mean much to Capacitor, in fact, you will soon see that all of the above achieves absolutely nothing for us.

The source-file entry in Cordova-land points to a file in the plugin that should be executed. In this case, it wants to copy the facebookconnect.xml file to the res/values directory. It then uses the config-file entries to target the file that was just created and set up some strings/variables inside of it for the application to use. Again, that is what is happening with Cordova, but how does Capacitor handle this?

Capacitor will actually copy this file into our native Android project at capacitor-cordova-android-plugins/src/main/res/values/facebookconnect.xml but the result will look like this:

<?xml version='1.0' encoding='utf-8'?>
<resources>
</resources>

When what the plugin.xml file wanted to do was this:

<?xml version='1.0' encoding='utf-8'?>
<resources>
    <string name="fb_app_id">$APP_ID</string>
    <string name="fb_app_name">$APP_NAME</string>
    <bool name="fb_hybrid_app_events">$FACEBOOK_HYBRID_APP_EVENTS</bool>
</resources>

NOTE: Cordova would also replace $APP_ID, $APP_NAME, and $FACEBOOK_HYBRID_APP_EVENTS with the install variables supplied when running the install command.

Remember that the following two <config-file> entries don't contain config.xml in the target so they will be ignored by Capacitor. The end result is that we have a file with no resource definitions, which is absolutely useless to us.

This is fine, though, because it doesn't do any harm either. However, we do need those <string> and <bool> entries defined for this plugin. It would be wise at this point to take a peek at the native code for the plugin (i.e. ConnectPlugin.java) and see if and where it is making use of these variables. If the native code is making use of these variables then you definitely want to make sure that they are defined.

It is common to see native Android code making use of these strings and booleans by referencing getResources(). It is also common for Android native code to make use of the <preference> values supplied by referencing getSharedPreferences(). It can be a good idea to search through the native .java files the plugin uses to see if they are expecting these values to be defined anywhere.

In this case, ConnectPlugin.java isn't making use of the variables, but rather it is some additional configuration that we will be adding to AndroidManifest.xml in the next step that makes use of the variables. This means that we don't need to define these variables, as we could just supply the values manually instead of using @string/fb_app_id in the AndroidManifest.xml. However, I think it is nicer to define the variables, and it is safer to just do this in general since sometimes the native code will be making use of these values (or if not now, maybe an update of the plugin will - best to interfere with things as little as possible).

Defining these variables manually in the native Android project is simple enough, if you open app/src/main/res/values/strings.xml you will find a file like the following:

<?xml version='1.0' encoding='utf-8'?>
<resources>
    <string name="app_name">myapp</string>
    <string name="title_activity_main">myapp</string>
    <string name="package_name">com.joshmorony.myapp</string>
    <string name="custom_url_scheme">com.joshmorony.myapp</string>
</resources>

These values can be referenced throughout the native Android project. We can just add the values required for the Facebook plugin to this file. I don't think the $FACEBOOK_HYBRID_APP_EVENTS variable is actually used anywhere by the plugin, but we will add it anyway:

<?xml version='1.0' encoding='utf-8'?>
<resources>
    <string name="app_name">facebooktest</string>
    <string name="title_activity_main">facebooktest</string>
    <string name="package_name">com.joshmorony.facebooktest</string>
    <string name="custom_url_scheme">com.joshmorony.facebooktest</string>
    <string name="fb_app_id">123</string>
    <string name="fb_app_name">myapp</string>
    <bool name="fb_hybrid_app_events">true</bool>
</resources>

Let's move on to the next section:

<config-file target="AndroidManifest.xml" parent="application">
    <meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/fb_app_id"/>
    <meta-data android:name="com.facebook.sdk.ApplicationName" android:value="@string/fb_app_name" />
    <activity android:name="com.facebook.FacebookActivity"
      android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
      android:label="@string/fb_app_name" />
</config-file>

We have another <config-file> and, again, this one does not include config.xml in its target. However, since it has a target of AndroidManifest.xml it will merge all of these values:

<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/fb_app_id"/>
<meta-data android:name="com.facebook.sdk.ApplicationName" android:value="@string/fb_app_name" />
<activity android:name="com.facebook.FacebookActivity"
  android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
  android:label="@string/fb_app_name" />

Into the AndroidManifest.xml within the capacitor-cordova-android-plugins module (along with the configurations for any other plugins that target AndroidManifest.xml) and then those values will be merged with the Android projects main AndroidManifest.xml file. Notice that the <meta-data> and <activity> tags that will be added to the manifest reference those <string> variables that we just added to strings.xml.

If you didn't set up those variables, you would need to manually add these <meta-data> and <activity> tags to the AndroidManifest.xml file and supply the fb_app_id and fb_app_name strings manually rather than referencing @string/fb_app_id and @string/fb_app_name. This is why it is often beneficial to set up those variables in strings.xml rather than replacing values manually.

Just a couple more lines left to take a look at now:

<framework src="com.facebook.android:facebook-android-sdk:$FACEBOOK_ANDROID_SDK_VERSION"/>

This is a tag that we haven't dealt with yet, and it is also trying to make use of a $FACEBOOK_ANDROID_SDK_VERSION Cordova install variable. As a result of this line, Capacitor will add the following inside of the capacitor.build.gradle file under dependencies:

// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN

android {
  compileOptions {
      sourceCompatibility JavaVersion.VERSION_1_8
      targetCompatibility JavaVersion.VERSION_1_8
  }
}

apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies {

    implementation "com.facebook.android:facebook-android-sdk:5.13.0"
}


if (hasProperty('postBuildExtras')) {
  postBuildExtras()
}

You can see above that implementation "com.facebook.android:facebook-android-sdk:5.13.0" has been added automatically after installing this plugin. But, wait a minute... how did it know to replace $FACEBOOK_ANDROID_SDK_VERSION with 5.13.0?

If we look a little further up the plugin.xml file (outside of the <platform name="android"> section) we would find some additional preferences being defined:

<preference name="APP_ID" />
<preference name="APP_NAME" />
<preference name="FACEBOOK_HYBRID_APP_EVENTS" default="false" />
<preference name="FACEBOOK_ANDROID_SDK_VERSION" default="5.13.0"/>

Notice that a preference for FACEBOOK_ANDROID_SDK_VERSION is supplied, with a default value of 5.13.0. Capacitor will create a preferences array from all of the preference tags listed in plugin.xml. It will then loop through all of those preferences, and when creating the framework string that is added to capacitor.build.gradle:

implementation "com.facebook.android:facebook-android-sdk:$FACEBOOK_ANDROID_SDK_VERSION

Capacitor will use a regular expression to find the $ variable syntax being used here, and replace it with the default value of the preference for FACEBOOK_ANDROID_SDK_VERSION that was found in the plugin.xml file (if that preference exists). Since there is a preference defined for FACEBOOK_ANDROID_SDK_VERSION in the plugins plugin.xml file, the string will be modified to be:

implementation "com.facebook.android:facebook-android-sdk:5.13.0

Before replacing $FACEBOOK_ANDROID_SDK_VERSION with the default value listed in the preference, it will first check to see if there is an entry in the native Android variables.gradle file for FACEBOOK_ANDROID_SDK_VERSION. If there is, e.g:

ext {
    minSdkVersion = 21
    compileSdkVersion = 29
    targetSdkVersion = 29
    androidxAppCompatVersion = '1.1.0'
    androidxCoreVersion =  '1.2.0'
    androidxMaterialVersion =  '1.1.0-rc02'
    androidxBrowserVersion =  '1.2.0'
    androidxLocalbroadcastmanagerVersion =  '1.0.0'
    androidxExifInterfaceVersion = '1.2.0'
    firebaseMessagingVersion =  '20.1.2'
    playServicesLocationVersion =  '17.0.0'
    junitVersion =  '4.12'
    androidxJunitVersion =  '1.1.1'
    androidxEspressoCoreVersion =  '3.2.0'
    cordovaAndroidVersion =  '7.0.0'
    FACEBOOK_ANDROID_SDK_VERSION = '5.10.0'
}

Capacitor will instead leave $FACEBOOK_ANDROID_SDK_VERSION as it is in capacitor.build.gradle and whatever value is listed in variables.gradle will be used when the application is built.

There are situations where this happens (a $VARIABLE gets copied literally over into the native project) and Capacitor doesn't have special logic to substitute a value where the $ syntax exists. Again, this is where it is important to understand how this whole process works because you will be able to see where this is happening, and hopefully, find a way to override that value manually in the native project. For example, in another plugin I migrated to Capacitor, it tried to use some Cordova install variables to set some <preference> tags:

<preference name="keyHash" value="$KEY_HASH" />

However, this was just copied literally into the config.xml file as $KEY_HASH. This meant when the native plugin tried to make use of the key hash value, it literally used the string value "$KEY_HASH". In order to overwrite this dodgy preference, I was able to just supply the actual value using the capacitor.config.json file:

{
  "appId": "com.joshmorony.myapp",
  "appName": "myapp",
  "bundledWebRuntime": false,
  "npmClient": "npm",
  "webDir": "www",
  "plugins": {
    "SplashScreen": {
      "launchShowDuration": 0
    }
  },
  "cordova": {
    "preferences": {
      "keyHash": "aabbccdd"
    }
  }
}

This resulted in both being defined in config.xml, but the Capacitor one took precedence:

<preference name="keyHash" value="$KEY_HASH" />

<preference name="keyHash" value="aabbccdd" />

This brings us to the final line of the plugin.xml file for cordova-plugin-facebook4 which is:

<source-file src="src/android/ConnectPlugin.java" target-dir="src/org/apache/cordova/facebook" />

We have already had a peek of the result of this at the beginning when we searched our native Android project for where those native files were added.

Capacitor will use these source-file entries to determine what native files for the plugin need to be pulled over to the Capacitor project. In this case, it will place the ConnectPlugin.java file inside of the src/main/java folder, e.g:

  • capacitor-cordova-android-plugins/src/main/java/org.apachce.cordova.facebook/ConnectPlugin.java

If the native file has an extension of .aidl instead of .java it will be placed inside of a aidl folder instead of java.

Summary

The techniques I have covered in this tutorial will likely make it possible to migrate most Cordova plugins that require install variables to Capacitor. I haven't met one that I haven't been able to port over yet, but I also haven't tried all that many. Likely, there will be some gotchas in some plugins depending on the way that they are designed.

If all else fails, you could create a fork of the Cordova plugin, and there is also the option to build your own Capacitor plugin. I know it is appealing to be able to just install whatever functionality you need with a single command, but that comes with risk. There are lots of well maintained plugins to use, but there are many more abandoned plugins. This was a big problem with Cordova and it will be with Capacitor as well: relying on community created plugins without the ability to fix/maintain them yourself. The more niche the functionality you are implementing, the more likely you are to find a plugin that hasn't been updated in 3 years but it's your only option... unless you learn to build plugins yourself.

The difference with Capacitor, I think, is that the plugin creation process is more friendly. A lot of the time, you don't really even need to know much about the native code. You can generally find examples for iOS or Android of whatever functionality you want, copy those into your native projects, and set up a Capacitor plugin to make a call to it. You now have full control over the native code in your project. If the plugin breaks in the future, you don't need to open issues on an abandoned GitHub repository, you can Google X not working in iOS 18.1 and update the native code directly yourself with the fix. Of course, the more you do this the more you will start to feel comfortable with the native side of things as well.

If you enjoyed this article, feel free to share it with others!