diff --git a/Documentation/release-notes/master.md b/Documentation/release-notes/master.md
index 5a78f7c719d..14e9bff6d28 100644
--- a/Documentation/release-notes/master.md
+++ b/Documentation/release-notes/master.md
@@ -17,8 +17,35 @@ Description of new feature
* Description of known issue in the new feature, if applicable
+### Static/Default Interface Methods (Preview)
+
+With the new support for default interface methods in C# 8.0 we can now produce bindings that better match Java libraries
+that use these features. This includes:
+
+* Default interface methods
+* Static interface methods
+* Interface constants
+
+To enable this preview in your bindings project, add the following properties to your `.csproj`:
+
+```
+preview
+<_EnableInterfaceMembers>True
+```
+
+Note that enabling this only adds new members, it does not remove the existing alternatives previously used to expose
+these methods/fields.
+
+
### Build and deployment performance
+ * Bindings projects should now build considerably faster:
+ * [GitHub PR 440](https://github.com/xamarin/java.interop/pull/440)
+ * [GitHub PR 441](https://github.com/xamarin/java.interop/pull/441)
+ * [GitHub PR 442](https://github.com/xamarin/java.interop/pull/442)
+ * [GitHub PR 448](https://github.com/xamarin/java.interop/pull/448)
+ * [GitHub PR 449](https://github.com/xamarin/java.interop/pull/449)
+ * [GitHub PR 452](https://github.com/xamarin/java.interop/pull/452)
* [GitHub PR nnnn](https://github.com/xamarin/xamarin-android/pull/nnnn):
Description of improvement
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/Generator.cs b/src/Xamarin.Android.Build.Tasks/Tasks/Generator.cs
index f0a189ecf7b..f1ecbe93522 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/Generator.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/Generator.cs
@@ -45,6 +45,10 @@ public class BindingsGenerator : AndroidToolTask
[Required]
public string MonoAndroidFrameworkDirectories { get; set; }
+ public string LangVersion { get; set; }
+
+ public bool EnableInterfaceMembersPreview { get; set; }
+
public ITaskItem[] TransformFiles { get; set; }
public ITaskItem[] ReferencedManagedLibraries { get; set; }
public ITaskItem[] AnnotationsZipFiles { get; set; }
@@ -131,6 +135,9 @@ protected override string GenerateCommandLineCommands ()
if (UseShortFileNames)
cmd.AppendSwitch ("--use-short-file-names");
+ if (EnableInterfaceMembersPreview && SupportsCSharp8)
+ cmd.AppendSwitch ("--lang-features=interface-constants,default-interface-methods");
+
return cmd.ToString ();
}
@@ -142,5 +149,27 @@ protected override string GenerateFullPathToTool ()
{
return Path.Combine (ToolPath, ToolExe);
}
+
+ bool SupportsCSharp8 {
+ get {
+ // These are the values that pre-date C# 8. We assume any
+ // new value we encounter is something that supports it.
+ switch (LangVersion) {
+ case "7.3":
+ case "7.2":
+ case "7.1":
+ case "7":
+ case "6":
+ case "5":
+ case "4":
+ case "3":
+ case "ISO-2":
+ case "ISO-1":
+ return false;
+ }
+
+ return true;
+ }
+ }
}
}
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BindingBuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BindingBuildTest.cs
index 1333a761455..8c65c8373cb 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BindingBuildTest.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BindingBuildTest.cs
@@ -510,5 +510,63 @@ public void DesignTimeBuild (string classParser)
}
}
}
+
+ [Test]
+ [TestCaseSource (nameof (ClassParseOptions))]
+ public void BindDefaultInterfaceMethods (string classParser)
+ {
+ var proj = new XamarinAndroidBindingProject {
+ IsRelease = true,
+ };
+
+ // The sources for the .jar is in the jar itself.
+ string classesJarBase64 = @"
+UEsDBBQACAgIANWk6UwAAAAAAAAAAAAAAAAJAAQATUVUQS1JTkYv/soAAAMAUEsHCAAAAAACAAAAAAA
+AAFBLAwQUAAgICADVpOlMAAAAAAAAAAAAAAAAFAAAAE1FVEEtSU5GL01BTklGRVNULk1G803My0xLLS
+7RDUstKs7Mz7NSMNQz4OVyLkpNLElN0XWqBAlY6BnEG5obKmj4FyUm56QqOOcXFeQXJZYA1WvycvFyA
+QBQSwcIFGFrLUQAAABFAAAAUEsDBAoAAAgAAK2k6UwAAAAAAAAAAAAAAAAEAAAAY29tL1BLAwQKAAAI
+AACtpOlMAAAAAAAAAAAAAAAADAAAAGNvbS94YW1hcmluL1BLAwQKAAAIAACwpOlMAAAAAAAAAAAAAAA
+AEQAAAGNvbS94YW1hcmluL3Rlc3QvUEsDBBQACAgIAJmk6UwAAAAAAAAAAAAAAAAuAAAAY29tL3hhbW
+FyaW4vdGVzdC9EZWZhdWx0SW50ZXJmYWNlTWV0aG9kcy5jbGFzc3WOvU7DMBSFjxsnKeWnXUE8QLrgh
+ScAhBSJnwHE7qQ3JVUSC8dGFc/EwsrAA/BQiOuqLKnw8Pn4+LuWv38+vwCcY5biKMVUIKqMYWbzXEBe
+mgUJTG/qju58W5B91EXDTbIkd6HtX3jj0G+DzPL5E28v3q8FJg/G25Ku6zB1ekWV9o3LO0e20iXdkns
+2i/5spV+1QFaaVq11q23dKUe9U//4ArMwoRrdLdV9saLSJQICI4QVc4ogmTGfThBugFH0zuR/MpNNE7
+x015NDL3C868VDL2XuYbL1joMTjI+BNpYC+/xcaA820uEvUEsHCIw1aijpAAAAhQEAAFBLAwQUAAgIC
+ACYpOlMAAAAAAAAAAAAAAAAHAAAAERlZmF1bHRJbnRlcmZhY2VNZXRob2RzLmphdmF1zLEOwiAQBuCd
+p7hRl0Zd2YyLgw9xwlGJFCocTWPTdxdSHarxxv///utR3bElUKFrRuwwWt8wJZZC9PnqrALrmaJBRXA
+ig9nx+RNciG9BJzEJKKeXtnowIcBmCxNE4hw97CTMP6glPmJcuf1f91y5w7cbgtWQ3rCOBnSZymJhNX
+nkPJYnUsziBVBLBwgzfz2miQAAAPUAAABQSwECFAAUAAgICADVpOlMAAAAAAIAAAAAAAAACQAEAAAAA
+AAAAAAAAAAAAAAATUVUQS1JTkYv/soAAFBLAQIUABQACAgIANWk6UwUYWstRAAAAEUAAAAUAAAAAAAA
+AAAAAAAAAD0AAABNRVRBLUlORi9NQU5JRkVTVC5NRlBLAQIKAAoAAAgAAK2k6UwAAAAAAAAAAAAAAAA
+EAAAAAAAAAAAAAAAAAMMAAABjb20vUEsBAgoACgAACAAAraTpTAAAAAAAAAAAAAAAAAwAAAAAAAAAAA
+AAAAAA5QAAAGNvbS94YW1hcmluL1BLAQIKAAoAAAgAALCk6UwAAAAAAAAAAAAAAAARAAAAAAAAAAAAA
+AAAAA8BAABjb20veGFtYXJpbi90ZXN0L1BLAQIUABQACAgIAJmk6UyMNWoo6QAAAIUBAAAuAAAAAAAA
+AAAAAAAAAD4BAABjb20veGFtYXJpbi90ZXN0L0RlZmF1bHRJbnRlcmZhY2VNZXRob2RzLmNsYXNzUEs
+BAhQAFAAICAgAmKTpTDN/PaaJAAAA9QAAABwAAAAAAAAAAAAAAAAAgwIAAERlZmF1bHRJbnRlcmZhY2
+VNZXRob2RzLmphdmFQSwUGAAAAAAcABwDOAQAAVgMAAAAA
+";
+ proj.Jars.Add (new AndroidItem.EmbeddedJar ("dim.jar") {
+ BinaryContent = () => Convert.FromBase64String (classesJarBase64)
+ });
+
+ proj.AndroidClassParser = classParser;
+
+ proj.SetProperty ("_EnableInterfaceMembers", "True");
+ proj.SetProperty ("LangVersion", "preview");
+
+ using (var b = CreateDllBuilder (Path.Combine ("temp", TestName), false, false)) {
+ proj.NuGetRestore (b.ProjectDirectory);
+ Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
+
+ string asmpath = Path.GetFullPath (Path.Combine (Path.GetDirectoryName (new Uri (GetType ().Assembly.CodeBase).LocalPath), b.ProjectDirectory, b.Output.OutputPath, (proj.AssemblyName ?? proj.ProjectName) + ".dll"));
+ Assert.IsTrue (File.Exists (asmpath), "assembly does not exist");
+
+ var cs = b.Output.GetIntermediaryAsText (Path.Combine ("generated", "src", "Com.Xamarin.Test.IDefaultInterfaceMethods.cs"));
+ Assert.IsTrue (cs.Contains ("int Quux ();"), "Quux not generated.");
+ Assert.IsTrue (cs.Contains ("virtual unsafe int Foo ()"), "Foo not generated.");
+ Assert.IsTrue (cs.Contains ("virtual unsafe int Bar {"), "Bar not generated.");
+ Assert.IsTrue (cs.Contains ("set {"), "(Baz) setter not generated.");
+ }
+ }
}
}
diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Bindings.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Bindings.targets
index 8afb3eac59e..0b5dd75d422 100755
--- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Bindings.targets
+++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Bindings.targets
@@ -494,6 +494,8 @@ Copyright (C) 2012 Xamarin Inc. All rights reserved.
ToolPath="$(MonoAndroidToolsDirectory)"
ToolExe="$(BindingsGeneratorToolExe)"
UseShortFileNames="$(UseShortGeneratorFileNames)"
+ LangVersion="$(LangVersion)"
+ EnableInterfaceMembersPreview="$(_EnableInterfaceMembers)"
/>
diff --git a/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/DimBindingTests.cs b/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/DimBindingTests.cs
new file mode 100644
index 00000000000..accd2db949a
--- /dev/null
+++ b/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/DimBindingTests.cs
@@ -0,0 +1,95 @@
+using System;
+using Com.Xamarin.Android;
+using Java.Lang;
+using NUnit.Framework;
+
+namespace Xamarin.Android.JcwGenTests
+{
+ [TestFixture]
+ public class DimTest
+ {
+ [Test]
+ public void TestDefaultInterfaceMethods ()
+ {
+ var empty = new EmptyOverrideClass ();
+ var iface = empty as IDefaultMethodsInterface;
+
+ Assert.AreEqual (0, iface.Foo ());
+ Assert.AreEqual (2, iface.Bar);
+ Assert.DoesNotThrow (() => iface.Bar = 5);
+
+ Assert.AreEqual (0, iface.InvokeFoo ());
+
+ Assert.Throws (() => iface.ToImplement ());
+ }
+
+ [Test]
+ public void TestOverriddenDefaultInterfaceMethods ()
+ {
+ var over = new ImplementedOverrideClass ();
+ var iface = over as IDefaultMethodsInterface;
+
+ Assert.AreEqual (6, over.Foo ());
+ Assert.AreEqual (100, over.Bar);
+ Assert.DoesNotThrow (() => over.Bar = 5);
+
+ Assert.AreEqual (6, iface.InvokeFoo ());
+ }
+
+ [Test]
+ public void TestManagedEmptyDefaultInterfaceMethods ()
+ {
+ // Test using empty C# implementing interface
+ var empty = new ManagedEmptyDefault ();
+ var iface = empty as IDefaultMethodsInterface;
+
+ Assert.AreEqual (0, iface.Foo ());
+
+ Assert.AreEqual (0, iface.InvokeFoo ());
+ }
+
+ [Test]
+ public void TestManagedOverriddenDefaultInterfaceMethods ()
+ {
+ // Test using method overridden in C#
+ var over = new ManagedOverrideDefault ();
+ var iface = over as IDefaultMethodsInterface;
+
+ Assert.AreEqual (15, over.Foo ());
+ Assert.AreEqual (15, iface.Foo ());
+
+ Assert.AreEqual (15, iface.InvokeFoo ());
+ }
+
+ [Test]
+ public void TestStaticMethods ()
+ {
+ Assert.AreEqual (10, IStaticMethodsInterface.Foo ());
+
+ Assert.AreEqual (3, IStaticMethodsInterface.Value);
+ Assert.DoesNotThrow (() => IStaticMethodsInterface.Value = 5);
+ }
+
+ [Test]
+ public void TestChainedDefaultInterfaceMethods ()
+ {
+ var over = new ImplementedChainOverrideClass ();
+ var iface = over as IDefaultMethodsInterface;
+
+ Assert.AreEqual (6, over.Foo ());
+ Assert.AreEqual (100, over.Bar);
+ Assert.DoesNotThrow (() => over.Bar = 5);
+
+ Assert.AreEqual (6, iface.InvokeFoo ());
+ }
+
+ class ManagedEmptyDefault : Java.Lang.Object, IDefaultMethodsInterface
+ {
+ }
+
+ class ManagedOverrideDefault : Java.Lang.Object, IDefaultMethodsInterface
+ {
+ public int Foo () => 15;
+ }
+ }
+}
diff --git a/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/Properties/AndroidManifest.xml b/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/Properties/AndroidManifest.xml
index 92da57bf571..78bc48f3b95 100644
--- a/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/Properties/AndroidManifest.xml
+++ b/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/Properties/AndroidManifest.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/Xamarin.Android.JcwGen-Tests.csproj b/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/Xamarin.Android.JcwGen-Tests.csproj
index f09792aa5da..634a17f7402 100644
--- a/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/Xamarin.Android.JcwGen-Tests.csproj
+++ b/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/Xamarin.Android.JcwGen-Tests.csproj
@@ -17,9 +17,10 @@
Xamarin.Android.JcwGen-Tests
Properties\AndroidManifest.xml
armeabi-v7a;x86
- v8.1
+ v9.0
false
true
+ preview
@@ -51,6 +52,7 @@
+
diff --git a/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/Xamarin.Android.McwGen-Tests.csproj b/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/Xamarin.Android.McwGen-Tests.csproj
index e606aa04730..cbcae2e0410 100644
--- a/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/Xamarin.Android.McwGen-Tests.csproj
+++ b/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/Xamarin.Android.McwGen-Tests.csproj
@@ -14,6 +14,11 @@
Xamarin.Android.McwGen-Tests
False
class-parse
+ <_JavacSourceVersion>1.8
+ <_JavacTargetVersion>1.8
+ v9.0
+ preview
+ <_EnableInterfaceMembers>True
@@ -113,6 +118,21 @@
Jars/xamarin-test.jar
+
+ Jars/xamarin-test.jar
+
+
+ Jars/xamarin-test.jar
+
+
+ Jars/xamarin-test.jar
+
+
+ Jars/xamarin-test.jar
+
+
+ Jars/xamarin-test.jar
+
diff --git a/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/DefaultMethodsInterface.java b/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/DefaultMethodsInterface.java
new file mode 100644
index 00000000000..2ac218c652b
--- /dev/null
+++ b/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/DefaultMethodsInterface.java
@@ -0,0 +1,10 @@
+package com.xamarin.android;
+
+public interface DefaultMethodsInterface
+{
+ default int foo () { return 0; }
+ default int getBar () { return 2; }
+ default void setBar (int value) { }
+ default int toImplement () { throw new UnsupportedOperationException (); }
+ default int invokeFoo () { return foo (); }
+}
diff --git a/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/EmptyOverrideClass.java b/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/EmptyOverrideClass.java
new file mode 100644
index 00000000000..d647bfa1dc6
--- /dev/null
+++ b/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/EmptyOverrideClass.java
@@ -0,0 +1,5 @@
+package com.xamarin.android;
+
+public class EmptyOverrideClass implements DefaultMethodsInterface
+{
+}
diff --git a/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/ImplementedChainOverrideClass.java b/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/ImplementedChainOverrideClass.java
new file mode 100644
index 00000000000..ad1c1d6385c
--- /dev/null
+++ b/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/ImplementedChainOverrideClass.java
@@ -0,0 +1,18 @@
+package com.xamarin.android;
+
+public class ImplementedChainOverrideClass extends EmptyOverrideClass
+{
+ @Override
+ public int foo () {
+ return 6;
+ }
+
+ @Override
+ public int getBar () {
+ return 100;
+ }
+
+ @Override
+ public void setBar (int value) {
+ }
+}
diff --git a/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/ImplementedOverrideClass.java b/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/ImplementedOverrideClass.java
new file mode 100644
index 00000000000..d5079f198e5
--- /dev/null
+++ b/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/ImplementedOverrideClass.java
@@ -0,0 +1,18 @@
+package com.xamarin.android;
+
+public class ImplementedOverrideClass implements DefaultMethodsInterface
+{
+ @Override
+ public int foo () {
+ return 6;
+ }
+
+ @Override
+ public int getBar () {
+ return 100;
+ }
+
+ @Override
+ public void setBar (int value) {
+ }
+}
diff --git a/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/StaticMethodsInterface.java b/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/StaticMethodsInterface.java
new file mode 100644
index 00000000000..9755d39deab
--- /dev/null
+++ b/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/StaticMethodsInterface.java
@@ -0,0 +1,8 @@
+package com.xamarin.android;
+
+public interface StaticMethodsInterface
+{
+ static int foo () { return 10; }
+ static int getValue () { return 3; }
+ static void setValue (int value) { }
+}