java.interop icon indicating copy to clipboard operation
java.interop copied to clipboard

Proposal? Re-think Java bindings with Channels

Open jonpryor opened this issue 2 years ago • 19 comments

Context: 9057654549ac533520116ae68d0ba3d602ba3b2a Context: https://github.com/xamarin/java.interop/commits/main/src/Java.Interop.Dynamic Context: https://www.youtube.com/watch?v=bgK_6anwMcw Context: https://github.com/Redth/Xamarin.Binding.Helpers Context: https://github.com/xamarin/xamarin-android/pull/5926 Context: https://github.com/xamarin/xamarin-android/issues/5352 Context: https://gist.github.com/Redth/958d1a6efd047e32c7709b3b5c3b63bb Context: https://docs.flutter.dev/development/platform-integration/platform-channels

Binding a Java library can be extremely difficult. While we have (on-again, off-again) tried to improve the reliability of class-parse and generator (f658ab26dc505b2ccc1eb548ffbe2ad1a291c926, 7f1a5ab1606e4055eb4705aeafa7a22117998a33, …), and "simplify" the process (xamarin/xamarin-android/issues#5352), at some point we have to ask: is this infrastructure actually usable by "mere mortals"?

Dynamic Bindings?

@jonpryor played around with the idea of using C#4 dynamic to interact with Java types; see the history of the src/Java.Interop.Dynamic directory.

https://github.com/xamarin/java.interop/blob/4cbc07a5747c3a8f147117aa6aa43b1178263bdc/tests/Java.Interop.Dynamic-Tests/Java.Interop/DynamicJavaInstanceTests.cs#L47-L61

A problem with this approach is that it was found to be quite slow when running on Android devices, with the first Java.Interop.Dynamic invocation taking seconds to resolve. This may now be addressable by using a custom @(AndroidAotProfile), and warrants further investigation.

Slim Bindings?

An alternate idea to the "full stack" binding approach are "slim bindings", as proposed by Redth/Xamarin.Bindings.Helpers. This, in combination with the %(AndroidJavaSource.Bind) Build action, could be extremely interesting:

  1. Create a new "simple" Java file:

    package example;
    public class SimpleJava {
    }
    
  2. Add a .java file to the .csproj with %(AndroidJavaSource.Bind)=True:

    <ItemGroup>
      <AndroidJavaSource Include="Example.java" Bind="True" />
    </ItemGroup>
    
  3. Example.java is built "before" the Compile target, is bound, and the binding is available for use by code in the Compile target.

A problem with Slim bindings is that you still need to write native Java code. We may need to "bite the bullet" and provide some form of integration with Gradle, so that gradlew is used to build the Java code.

Background: Channels?

Flutter uses an alternate idea, platform channels. This has been kinda/sorta prototyped by @Redth: https://gist.github.com/Redth/958d1a6efd047e32c7709b3b5c3b63bb

A challenge with Channels is that they contain lots of boilerplate and strings.

Proposal: Generatable Channels?

Thus, the proposal: "some of the above":

  1. Integrate Gradle into the build process, so that it's "reasonable" for customers to add Java/Kotlin/whatever code and libraries to a C# binding project. gradlew would be run as a pre-Compile step, so that outputs of the Gradle build could be used as inputs for binding.

  2. Implement/otherwise obtain "channel" infrastructure.

  3. Customers use a new @net-channel Javadoc comment (or Java annotation) in the Java code. When present, the type (and members?) with @net-channel are not bound, but instead the "two sides" of the Channel infrastructure would be generated, both Java & C# code.

For example, the customer writes:

package example;

/** @net-channel */
public class UsedByChannels {
    public int doSomething() {return 42;}
}

At pre-Compile, the Java & C# "pairs" of this channel are generated, a'la https://gist.github.com/Redth/958d1a6efd047e32c7709b3b5c3b63bb

// generated Java-side channel code
package example.channels;

public class UsedByChannelsChannel extends Channel {
    @Override
    public Object receiveFromManaged (string id, Object self, Object... parameters) {
        if (id.equals ("doSomething")) {
            return ((UsedByChannels) self).doSomething ();
        }
    }
}
// generated C#-side channel code
namespace Example.Channels;

partial class UsedByChannelsChannel {
    public const string doSomething = "doSomething";
}

A problem with this idea is that it's still relying on code generation. The only way this will be reliable is if what it does is so simple it cannot fail, which may in fact be a non-starter as the sketch above is implicitly relying on class-parse (to get the UsedByChannels class and doSomething names), so the removal of "magical Java" may be a requirement.

A challenge with (2) is to consider ways to allow the boilerplate to be generated, as this may constrain what the API can look like.

jonpryor avatar Jan 18 '22 19:01 jonpryor

Another avenue to explore for "slim" bindings is Java.Interop.Dynamic:

dynamic my_class_constructor = new Java.Interop.Dynamic.DynamicJavaClass ("example/UsedByChannels");
var my_class = my_class_constructor ();
var answer = my_class.doSomething ();   // 42

source: https://github.com/xamarin/java.interop/pull/508

jpobst avatar Jan 18 '22 20:01 jpobst

Java.Interop.Dynamic is an interesting alternate approach, but iirc when @mattleibow explored it in #508, it had huge perf implications (i.e. the first call was iirc ~10 seconds; subsequent calls were "reasonably fast", but that first invocation was death). We spitballed an idea of putting it into a NuGet package which contained AOT information (a Profiled AOT .aotprofile file which just contains everything in Java.Interop.Dynamic plus dependencies?), but never actually pursued this idea.

It might still be worth investigating, but there are a lot of "unknowns" regarding what runtime performance would look like.

(Though there are likewise a lot of unknowns regarding what runtime performance of a "channel" infrastructure would imply as well!)

The other problem is that Java.Interop.Dynamic requires JNI class names. One typo, and you have NoClassDefFoundError being thrown…. This is another reason I'm interested on a solution that has some form of codegen: fewer pieces that need to be potentially typo'd. (Though until such a thing exists…)

#508 was also a source for https://github.com/xamarin/java.interop/issues/833 …

That said, I will update the original issue description to mention Java.Interop.Dynamic, for completeness.

jonpryor avatar Jan 18 '22 21:01 jonpryor

In addition to Java.Interop.Dynamic being slow, it's also an android only thing. A similar approach is not super feasible on iOS and while we're talking about this in Java currently, we want an approach that is more consistent for customers across platforms.

In fact I played with making my own dynamic (yes I know different than Java.Interop.Dynamic) implementation to call the appropriate iOS selectors, etc, and it didn't pan out so well in the end as there's no knowledge of return type info from the runtime to inferred to the implementation, so you end up having to specify this info anyway, and then the API is just messy. Furthermore this required Interpreter on iOS which is kind of a non-starter unless we had a mixed mode interpreter mode for the platform.

Channels is kind of nice because the architecture/structure is similar across platforms even if the platform specific code is obviously different.

Redth avatar Jan 18 '22 21:01 Redth

Couple things from my work around this... It's pretty easy to make gradle spit out the dependency info so that you can find the jars/aars to include in the C# project, like: https://raw.githubusercontent.com/Redth/Xamarin.Binding.Helpers/main/Xamarin.Binding.Helpers/FetchXamarinBindingInfo.gradle

The other challenge might be finding duplicates, for instance, if the project references a nuget that has an apk bundled in it, how do you know not to include a different version of the same artifact from the gradle project? - this seems very difficult to solve properly without making our binding nuget packages describe the artifact so you can infer its identity (so, maven group id, artifact id, version).

Redth avatar Jan 18 '22 21:01 Redth

The downside to channels is you have to write native code. By the time you're doing that, what value does Xamarin offer?

Based on my experience supporting users, I suspect most of them do not know how to write native code. (Even I can only do the absolute bare minimum in Java.)

While channels may feel like a "cleaner" solution, I do not think it will be any easier for most users than our current Java binding process.

jpobst avatar Jan 18 '22 21:01 jpobst

As for the generation / boilerplate, I think the simplicity of constraining the API is helpful here and not trying to generate too much is best. What I think is a big win is generating the boilerplate registration code and using Java annotations to describe the channel ID and message ID's.

Redth avatar Jan 18 '22 21:01 Redth

making our binding nuget packages describe the artifact

There has been a little bit of work around this for the NuGets we create: https://www.nuget.org/packages/Xamarin.AndroidX.Core

It isn't currently used for anything, so I don't know how well it holds up in practice.

jpobst avatar Jan 18 '22 21:01 jpobst

The downside to channels is you have to write native code. By the time you're doing that, what value does Xamarin offer?

Based on my experience supporting users, I suspect most of them do not know how to write native code. (Even I can only do the absolute bare minimum in Java.)

While channels may feel like a "cleaner" solution, I do not think it will be any easier for most users than our current Java binding process.

Unfortunately writing code is just one aspect of the process.

As an exercise in the past I tried figuring out how to get all of the required dependencies to write a Xamarin.Android binding for MapBox SDK. Unless it's changed, those are gated by auth on their own maven feed, so first you need to figure out how to authenticate and actually construct a URL for each of the binaries you need, and then how to ascertain their dependency chains and obtain them as well. Inevitably using gradle to do this was the easiest route. Then I needed to figure out where those went, copy them over, know which artifacts you need, etc.

The actual binding process we've seen the success rate as being not very high as well - this too is highly specialized of knowledge/learning which isn't really something that your .NET developer skills help with arguably.

If you flip the perspective, yes writing native code is less than ideal, but most developers can hobble through a basic tutorial that the SDK vendors offer which are written specifically around guiding you through the native IDE/toolchain to the point where they can change the samples around enough to make a 'basic map' appear with annotations on it. At least there they have code completion and reasonable build errors. It's layer less of indirection.

No matter how you cut it, this is an advanced topic, but as a wholistic approach, I do think writing a bit of native code will be more successful in cases where you aren't trying to wrap every API in the entire SDK you are using in your app. This is also what other platforms such as Flutter and React Native are doing and it seems to resonate reasonably well with their users. We have the added advantage that we have some basic 'types' we already know about in our .NET SDK's. For example, we have Java.Lang.Object and the android View and we can comfortably pass a MapBoxView which subclasses View and not care about the strong type as we bring it back over the channel into our .NET apps and add it to our ViewGroup/Hierarchy. We don't really have to serialize things we are message passing, just find a lowest common denominator type that we can proxy it through as.

Redth avatar Jan 18 '22 22:01 Redth

Another possibility might be to use source generators and allow definitions to be provided in C#. It has the same issue as dynamic in that it requires "magic strings", but does not have the performance downside, and does not require native code.

For example, many people have requested we bind AndroidX.SplashScreen just for one method, but we are holding off because it's still an alpha:

https://developer.android.com/reference/androidx/core/splashscreen/SplashScreen.Companion#(android.app.Activity).installSplashScreen()

Perhaps this could be written as:

[SlimBinding ("androidx.core.splashscreen.SplashScreen$Companion")]
public partial class SplashScreen
{
  [SlimBinding ("installSplashScreen")]
  public partial Object InstallSplashScreen (Activity activity);
}

And a source generator could generate a binding for this.

jpobst avatar Jan 18 '22 22:01 jpobst

The downside to channels is you have to write native code. By the time you're doing that, what value does Xamarin offer? Based on my experience supporting users, I suspect most of them do not know how to write native code. (Even I can only do the absolute bare minimum in Java.) While channels may feel like a "cleaner" solution, I do not think it will be any easier for most users than our current Java binding process.

Unfortunately writing code is just one aspect of the process.

As an exercise in the past I tried figuring out how to get all of the required dependencies to write a Xamarin.Android binding for MapBox SDK. Unless it's changed, those are gated by auth on their own maven feed, so first you need to figure out how to authenticate and actually construct a URL for each of the binaries you need, and then how to ascertain their dependency chains and obtain them as well. Inevitably using gradle to do this was the easiest route. Then I needed to figure out where those went, copy them over, know which artifacts you need, etc.

The actual binding process we've seen the success rate as being not very high as well - this too is highly specialized of knowledge/learning which isn't really something that your .NET developer skills help with arguably.

If you flip the perspective, yes writing native code is less than ideal, but most developers can hobble through a basic tutorial that the SDK vendors offer which are written specifically around guiding you through the native IDE/toolchain to the point where they can change the samples around enough to make a 'basic map' appear with annotations on it. At least there they have code completion and reasonable build errors. It's layer less of indirection.

No matter how you cut it, this is an advanced topic, but as a wholistic approach, I do think writing a bit of native code will be more successful in cases where you aren't trying to wrap every API in the entire SDK you are using in your app. This is also what other platforms such as Flutter and React Native are doing and it seems to resonate reasonably well with their users. We have the added advantage that we have some basic 'types' we already know about in our .NET SDK's. For example, we have Java.Lang.Object and the android View and we can comfortably pass a MapBoxView which subclasses View and not care about the strong type as we bring it back over the channel into our .NET apps and add it to our ViewGroup/Hierarchy. We don't really have to serialize things we are message passing, just find a lowest common denominator type that we can proxy it through as.

I would have to agree with Jon here. The customers we interact with usually fail with bindings due to tooling on our side and the complexity that goes along with it. They need to understand parts of the native code and API anyway to create the binding so taking the next step and implementing that once in swift (maybe one day) and Java and then only interacting with the channels from c# isnt too much of a stretch.

The goal here for me (based on customer learnings) is to remove as much magic as possible and make something that is repeatably reliable.

alexkblount avatar Jan 18 '22 22:01 alexkblount

I would have to agree with Jon here.

Every participant in this conversation is "Jon", but it seems like you mean @Redth! 😁

jpobst avatar Jan 18 '22 22:01 jpobst

Another possibility might be to use source generators and allow definitions to be provided in C#. It has the same issue as dynamic in that it requires "magic strings", but does not have the performance downside, and does not require native code.

For example, many people have requested we bind AndroidX.SplashScreen just for one method, but we are holding off because it's still an alpha:

https://developer.android.com/reference/androidx/core/splashscreen/SplashScreen.Companion#(android.app.Activity).installSplashScreen()

Perhaps this could be written as:


[SlimBinding ("androidx.core.splashscreen.SplashScreen$Companion")]

public partial class SplashScreen

{

  [SlimBinding ("installSplashScreen")]

  public partial Object InstallSplashScreen (Activity activity);

}

And a source generator could generate a binding for this.

I like this idea, how would you generate dealing with Java initiated call backs though? Thoughts on what the Java listener pattern might look like with this?

Redth avatar Jan 18 '22 22:01 Redth

The customers we interact with usually fail with bindings due to tooling on our side and the complexity that goes along with it.

I think we need to dive deeper into this, and understand exactly where our tooling is failing. Many of the current pain points, such as finding all the Java dependencies and including them in your project, or ensuring you don't have duplicate jars, will still exist.

A slim bindings project will (hopefully!) remove the need for metadata but I don't know that it fixes anything else.

jpobst avatar Jan 18 '22 22:01 jpobst

@Redth wrote:

this seems very difficult to solve properly without making our binding nuget packages describe the artifact so you can infer its identity (so, maven group id, artifact id, version).

This already exists for "recent" packages, as of: https://github.com/xamarin/AndroidX/commit/940028ed035d9729bc8fd6edbb65e44b8ab1165b

For example, if you grab the Xamarin.AndroidX.Core 1.6.0.3 NuGet package and look at the Xamarin.AndroidX.Core.nuspec file, you'll see:

<tags>Xamarin AndroidX Xamarin.AndroidX Support Google core artifact=androidx.core:core artifact_versioned=androidx.core:core:1.6.0</tags>

This should allow us to search NuGet.org for pre-existing artifacts. @moljac should be able to provide more details, if needed.

(Edited to add: @jpobst mentioned this fact above, and I overlooked it. My bad.)

jonpryor avatar Jan 19 '22 01:01 jonpryor

@Redth asked:

how would you generate dealing with Java initiated call backs though?

The "glib" answer is "manually"!:

[SlimBinding ("android/view/View$OnClickListener")]
partial interface IMyViewOnClickListener {
    [SlimBinding ("onClick")]
    void OnClick (MyView v);
}

[SlimBinding (…)]
partial class MyView {
    [SlimBinding ("setOnClickListener")]
    public partial void SetOnClickListener (IMyViewOnClickListener? listener);
}

I can imagine this working, but I can also imagine a developer needing to "copy over" lots of types to make a class hierarchy work.

I don't see this as being "scalable"; I'm not immediately fond of it.

jonpryor avatar Jan 19 '22 01:01 jonpryor

@jpobst wrote:

For example, many people have requested we bind AndroidX.SplashScreen just for one method, but we are holding off because it's still an alpha…

This feels like another good use case for Java.Interop.Dynamic, frankly, even if that isn't a solution that works on iOS.

jonpryor avatar Jan 19 '22 01:01 jonpryor

Kinda related, I would really like if there was a tool that I could give it a Maven ID and version and it would generate the full solution with all the binding projects wired up correctly. Grabbing appropriate dependencies from NuGet would be a plus. This will free me (the developer) to just fix the binding errors which I have actually became somewhat good at it after some time.

And more on the NuGet packages point, I wouldn't even mind that tool asking me to manually input the names or versions for the NuGet packages I want to include instead of downloading the raw jar or aar. A sort of loop that asks "Do you want to include this dependency from NuGet?", "What is the ID of this package?", "What is the version of this package?". At least that way I know that I have like 10/20/30/Whatever minutes of work ahead of me to get this solution to the correct state instead (not an exaggeration) 10+ hours.

I know that tools like bindenator exists but still, it requires listing of dependencies and more importantly version resolution has to be done manually (eg. a dependency lower in the tree requiring a package of a higher version than higher parts of the tree, in which case I have to choose the higher version).

This issue applies to slim bindings too. I would argue it's more relevant to slim bindings than full bindings because you usually resort to slim bindings (which I have used once before) because you have a very complex dependency tree and you don't want to generate bindings for all of that but you still have to go and list them all and include all the jars and aars which is half of the annoyance, the other half being creating the projects. The binding process itself isn't that bad. Usually with the help of the documentation I can get it work. It's just that the grunt work I have to do to get to that point is really off-putting.

I think the priority isn't improving the binding process itself (that's important too, I wish the JI binder would figure out automatically that a method inherited from a protected method can't be public, for example) but rather what we have to got through before that. This was discussed here xamarin/xamarin-android/5352 before

AmrAlSayed0 avatar Jan 25 '22 20:01 AmrAlSayed0

Kinda related, I would really like if there was a tool that I could give it a Maven ID and version and it would generate the full solution with all the binding projects wired up correctly.

I work on tool that does exactly this, but it is not solution oriented, but central project (for given Maven Id (g:a:v)) and set of transitive projects according to pom.xml dependency tree. First version will most likely not build dependency tree, but add dependencies as InputJar or ReferenceJar.

Here I hit the problem with question whether dependency is already bound. With assumption that main project was never bound and published on nuget.org. How to detect mapping from dependency Maven Id to some nuget.

If you check

https://api.nuget.org/v3-flatcontainer/Xamarin.AndroidX.Window/1.0.0.5-beta04/Xamarin.AndroidX.Window.nuspec

you'll see I started adding metadata so I have that mapping:

<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
    <metadata>
        <id>Xamarin.AndroidX.Window</id>
        <version>1.0.0.5-beta04</version>
        <title>Xamarin AndroidX - window</title>
        <authors>Microsoft</authors>
        <requireLicenseAcceptance>true</requireLicenseAcceptance>
        <license type="expression">MIT</license>
        <licenseUrl>https://licenses.nuget.org/MIT</licenseUrl>
        <projectUrl>https://go.microsoft.com/fwlink/?linkid=2113238</projectUrl>
        <iconUrl>https://go.microsoft.com/fwlink/?linkid=2099392</iconUrl>
        <description>Xamarin.Android bindings for AndroidX - window artifact=androidx.window:window artifact_versioned=androidx.window:window:1.0.0-beta04</description>
        <copyright>© Microsoft Corporation. All rights reserved.</copyright>
        <tags>Xamarin AndroidX Xamarin.AndroidX Support Google window artifact=androidx.window:window artifact_versioned=androidx.window:window:1.0.0-beta04</tags>
        <repository type="git" url="https://github.com/xamarin/AndroidX.git" branch="refs/tags/20211202-net6-with-java-interop-version-fix" commit="6bb04152ea4b1960588675562da3ad78b755535c" />
        <dependencies>
            <group targetFramework="MonoAndroid9.0">
                <dependency id="Xamarin.AndroidX.Annotation" version="1.2.0.4" include="All" />
                <dependency id="Xamarin.AndroidX.Collection" version="1.1.0.11" include="All" />
                <dependency id="Xamarin.AndroidX.Core" version="1.7.0.1" include="All" />
                <dependency id="Xamarin.Kotlin.StdLib" version="1.5.31.3" include="All" />
                <dependency id="Xamarin.KotlinX.Coroutines.Android" version="1.5.2.3" include="All" />
            </group>
            <group targetFramework="net6.0-android31.0">
                <dependency id="Xamarin.AndroidX.Annotation" version="1.2.0.4" include="All" />
                <dependency id="Xamarin.AndroidX.Collection" version="1.1.0.11" include="All" />
                <dependency id="Xamarin.AndroidX.Core" version="1.7.0.1" include="All" />
                <dependency id="Xamarin.Kotlin.StdLib" version="1.5.31.3" include="All" />
                <dependency id="Xamarin.KotlinX.Coroutines.Android" version="1.5.2.3" include="All" />
            </group>
        </dependencies>
        <frameworkAssemblies>
            <frameworkAssembly assemblyName="Java.Interop" targetFramework="MonoAndroid9.0" />
            <frameworkAssembly assemblyName="Mono.Android" targetFramework="MonoAndroid9.0" />
            <frameworkAssembly assemblyName="mscorlib" targetFramework="MonoAndroid9.0" />
            <frameworkAssembly assemblyName="System.Core" targetFramework="MonoAndroid9.0" />
            <frameworkAssembly assemblyName="System" targetFramework="MonoAndroid9.0" />
            <frameworkAssembly assemblyName="System.Numerics" targetFramework="MonoAndroid9.0" />
            <frameworkAssembly assemblyName="System.Numerics.Vectors" targetFramework="MonoAndroid9.0" />
            <frameworkAssembly assemblyName="System.Xml" targetFramework="MonoAndroid9.0" />
        </frameworkAssemblies>
    </metadata>
</package>

Flooding nuget.org with tons of bindings of the same artifact is not desirable solution either and search for Maven Id returns too much data. But this is being worked on.

Grabbing appropriate dependencies from NuGet would be a plus. This will free me (the developer) to just fix the binding errors which I have actually became somewhat good at it after some time.

Nuget dependencies will be problem, but this will be phase 2.

And more on the NuGet packages point, I wouldn't even mind that tool asking me to manually input the names or versions for the NuGet packages I want to include instead of downloading the raw jar or aar. A sort of loop that asks "Do you want to include this dependency from NuGet?", "What is the ID of this package?", "What is the version of this package?". At least that way I know that I have like 10/20/30/Whatever minutes of work ahead of me to get this solution to the correct state instead (not an exaggeration) 10+ hours.

I planned to build key-value-pairs of Maven Ids and "suggested" Nugets which would be used in 2nd step - after building dependency tree and flattening it.

I know that tools like bindenator exists but still, it requires listing of dependencies and more importantly version resolution has to be done manually (eg. a dependency lower in the tree requiring a package of a higher version than higher parts of the tree, in which case I have to choose the higher version).

This is what I call config-tuning and bigger problem is skipped bound versions for strict dependency versions (i.e [2.3.0]). Then you have either to bind 2.3.0, although you might have 2.2.4 and 2.3.1 or forget that version and everything related.

This issue applies to slim bindings too. I would argue it's more relevant to slim bindings than full bindings because you usually resort to slim bindings (which I have used once before) because you have a very complex dependency tree and you don't want to generate bindings for all of that but you still have to go and list them all and include all the jars and aars which is half of the annoyance, the other half being creating the projects. The binding process itself isn't that bad. Usually with the help of the documentation I can get it work. It's just that the grunt work I have to do to get to that point is really off-putting.

I think process will be easier as soon as I finish pom.xml dependency tree generation and then mapping that to binding project tree is trivial. The problem will be not to clutter folder structure.

I think the priority isn't improving the binding process itself (that's important too, I wish the JI binder would figure out automatically that a method inherited from a protected method can't be public, for example) but rather what we have to got through before that. This was discussed here xamarin/xamarin-android/5352 before

OK. this is another topic.

moljac avatar Jan 25 '22 21:01 moljac

Not sure if this issue is the right place for my comment (or maybe https://github.com/xamarin/xamarin-android/issues/5352) - but I'd like to share my experience with a more "advanced" binding that I had to implement.

In a Xamarin.Android application we have created bindings for Jitsi Meet SDK which so happens to be built using React Native 😄.

The version that we have created bindings for at the time was 2.9.0.

Basically all we were interested in was to be able to start JitsiMeetActivity with some minimum configuration, so only a few types had to be surfaced to C#.

Back when I've worked on this I've also documented the steps in case the bindings needed maintenance:

High level steps for updating the bindings

  1. Checkout https://github.com/jitsi/jitsi-meet-sdk-samples and import the Android project in Android Studio
  2. In build.gradle make sure you have pinned the version you're interested in (e.g. implementation ('org.jitsi.react:jitsi-meet-sdk:2.9.0'))
  3. Open Terminal in Android Studio and run ./gradlew app:dependencies to view the dependency tree of org.jitsi.react:jitsi-meet-sdk:2.9.0
  4. Some of the dependencies have official Nuget packages (e.g. AndroidX, Kotlin Stdlib, Timber) - the other need to be manually added to the projects (both the binding projects and the sample application)
  5. The non-Nuget packages can be retrieved from $HOME/.gradle/caches/modules-2/files-2. (it's recommended to delete this directory and do a Gradle Sync to have only the Github sample app dependencies)
  6. Copy react-native-0.61.5-jitsi.1.aar to Jitsi.Android.ReactNative/BoundAar
  7. Copy jitsi-meet-sdk-2.9.0.aar to Jitsi.Android.Meet.Sdk/BoundAar
  8. Copy the other .aar/.jar files to UnboundLibs and make sure they are linked with the proper Build Action in all three projects
  9. Update Jitsi.Android.ReactNative/Properties/AssemblyInfo.cs and Jitsi.Android.Meet.Sdk/Properties/AssemblyInfo.cs with the corresponding AssemblyVersion
  10. Revisit Metadata.xml from each biding project and check the build logs (Verbosity enabled) for errors (e.g. Error while processing type '[Class] org.jitsi.meet.sdk.AmplitudeModule':) if there are missing types that you actually need in C#
  11. To integrate the SDK in your own Xamarin application you will need to reference the following:
    • Jitsi.Android.ReactNative/bin/Release/Jitsi.Android.ReactNative.dll
    • Jitsi.Android.Meet.Sdk/bin/Release/Jitsi.Android.Meet.Sdk.dll
    • All UnboundLibs with the same build actions as Jitsi.Android.SampleApp
    • All Nuget packages as Jitsi.Android.SampleApp

Without a question the most complex thing was to identify and include all .jar / .aar (direct and transitive) dependencies (that we didn't even had to expose in C#).

If you're looking for some feedback that would help with prioritization, I'd say that automatically including all .jar / .aar dependencies (without even creating bindings for them) would be a big step forward.

cosminstirbu avatar Apr 01 '22 15:04 cosminstirbu