Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ namespace Microsoft.Android.Sdk.TrimmableTypeMap;
/// Line 2: ManagedKey;JavaKey
/// Line 3: CompatJniName;JavaKey
///
/// Java keys use dots (not slashes): e.g., "android.app.Activity"
/// Java keys use dots for packages and preserve '$' for nested classes:
/// e.g., "android.app.Activity" and "android.view.View$OnClickListener".
/// </summary>
public static class AcwMapWriter
{
Expand All @@ -28,10 +29,10 @@ public static class AcwMapWriter
public static void Write (TextWriter writer, IEnumerable<JavaPeerInfo> peers)
{
foreach (var peer in peers.OrderBy (p => p.ManagedTypeName, StringComparer.Ordinal)) {
string javaKey = JniSignatureHelper.JniNameToJavaName (peer.JavaName);
string javaKey = JniSignatureHelper.JniNameToJavaBinaryName (peer.JavaName);
string managedKey = peer.ManagedTypeName;
string partialAsmQualifiedName = $"{managedKey}, {peer.AssemblyName}";
string compatJniName = JniSignatureHelper.JniNameToJavaName (peer.CompatJniName);
string compatJniName = JniSignatureHelper.JniNameToJavaBinaryName (peer.CompatJniName);

// Line 1: PartialAssemblyQualifiedName;JavaKey
writer.Write (partialAsmQualifiedName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ static void ApplyTypeProperty (

foreach (var peer in allPeers) {
if (peer.ManagedTypeName == managedName) {
app.SetAttributeValue (AndroidNs + xmlAttrName, peer.JavaName.Replace ('/', '.'));
app.SetAttributeValue (AndroidNs + xmlAttrName, JniSignatureHelper.JniNameToJavaBinaryName (peer.JavaName));
return;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ static readonly (string Property, string Attribute) [] LayoutMappings = [

internal static void UpdateApplicationElement (XElement app, JavaPeerInfo peer, int targetSdkVersion = 0)
{
string jniName = JniSignatureHelper.JniNameToJavaName (peer.JavaName);
string jniName = JniSignatureHelper.JniNameToJavaBinaryName (peer.JavaName);
app.SetAttributeValue (AttName, jniName);

var component = peer.ComponentAttribute;
Expand All @@ -249,7 +249,7 @@ internal static void UpdateApplicationElement (XElement app, JavaPeerInfo peer,

internal static void AddInstrumentation (XElement manifest, JavaPeerInfo peer, string packageName)
{
string jniName = JniSignatureHelper.JniNameToJavaName (peer.JavaName);
string jniName = JniSignatureHelper.JniNameToJavaBinaryName (peer.JavaName);
var element = new XElement ("instrumentation",
new XAttribute (AttName, jniName));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,17 @@ internal static string JniNameToJavaName (string jniName)
return jniName.Replace ('/', '.').Replace ('$', '.');
}

/// <summary>
/// Converts a JNI type name to a Java binary type name.
/// JNI and Java binary names use '$' for nested classes, but JNI uses '/' for packages.
/// e.g., "android/app/Activity" → "android.app.Activity"
/// e.g., "android/view/View$OnClickListener" → "android.view.View$OnClickListener"
/// </summary>
internal static string JniNameToJavaBinaryName (string jniName)
{
return jniName.Replace ('/', '.');
}

/// <summary>
/// Extracts the Java package name from a JNI type name.
/// e.g., "com/example/MainActivity" \u2192 "com.example"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ class ManifestGenerator
var managedToManifestNames = new Dictionary<string, string> (allPeers.Count, StringComparer.Ordinal);
foreach (var peer in allPeers) {
if (!string.IsNullOrEmpty (peer.ManagedTypeName)) {
managedToManifestNames [peer.ManagedTypeName] = JniSignatureHelper.JniNameToJavaName (peer.JavaName);
managedToManifestNames [peer.ManagedTypeName] = JniSignatureHelper.JniNameToJavaBinaryName (peer.JavaName);
}
}

Expand All @@ -128,7 +128,7 @@ class ManifestGenerator
continue;
}

string jniName = JniSignatureHelper.JniNameToJavaName (peer.JavaName);
string jniName = JniSignatureHelper.JniNameToJavaBinaryName (peer.JavaName);
if (existingTypes.Contains (jniName)) {
continue;
}
Expand Down Expand Up @@ -295,8 +295,8 @@ void RewriteCompatNames (XElement manifest, IReadOnlyList<JavaPeerInfo> allPeers
// Build mapping: fully-qualified compat Java name → CRC Java name
var compatToCrc = new Dictionary<string, string> (allPeers.Count, StringComparer.Ordinal);
foreach (var peer in allPeers) {
string javaName = JniSignatureHelper.JniNameToJavaName (peer.JavaName);
string compatName = JniSignatureHelper.JniNameToJavaName (peer.CompatJniName);
string javaName = JniSignatureHelper.JniNameToJavaBinaryName (peer.JavaName);
string compatName = JniSignatureHelper.JniNameToJavaBinaryName (peer.CompatJniName);
if (javaName != compatName) {
compatToCrc [compatName] = javaName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -502,9 +502,7 @@ static void AddPeerByDotName (Dictionary<string, List<JavaPeerInfo>> peersByDotN

static void AddJniLookupNames (Dictionary<string, List<JavaPeerInfo>> peersByDotName, string jniName, JavaPeerInfo peer)
{
var simpleName = JniSignatureHelper.GetJavaSimpleName (jniName);
var packageName = JniSignatureHelper.GetJavaPackageName (jniName);
var manifestName = packageName.IsNullOrEmpty () ? simpleName : packageName + "." + simpleName;
var manifestName = JniSignatureHelper.JniNameToJavaBinaryName (jniName);
AddPeerByDotName (peersByDotName, manifestName, peer);

var javaSourceName = JniSignatureHelper.JniNameToJavaName (jniName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,14 +254,16 @@ public void Execute_GenerateNativeAotProguardConfiguration_UsesDgmlTypeMetadata
<Node Id="1" Label="Type metadata: [UnnamedProject]UnnamedProject.MainActivity" />
<Node Id="2" Label="Type metadata: [Mono.Android]Android.App.Activity" />
<Node Id="3" Label="Type metadata: [My.Assembly]Duplicate.Type" />
<Node Id="4" Label="Unrelated node" />
<Node Id="4" Label="Type metadata: [Xamarin.AndroidX.Activity]AndroidX.Activity.Result.Contract.ActivityResultContracts+TakePicture" />
<Node Id="5" Label="Unrelated node" />
</Nodes>
</DirectedGraph>
""");
File.WriteAllText (acwMapFile, """
UnnamedProject.MainActivity, UnnamedProject;crc64a1.MainActivity
Android.App.Activity, Mono.Android;android.app.Activity
Duplicate.Type, My.Assembly;my.app.Duplicate
AndroidX.Activity.Result.Contract.ActivityResultContracts+TakePicture, Xamarin.AndroidX.Activity;androidx.activity.result.contract.ActivityResultContracts$TakePicture
Duplicate.Type;wrong.Duplicate
Other.Type;other.Type
""");
Expand All @@ -279,6 +281,7 @@ public void Execute_GenerateNativeAotProguardConfiguration_UsesDgmlTypeMetadata
StringAssert.Contains ("-keep class crc64a1.MainActivity { *; }", proguard);
StringAssert.Contains ("-keep class android.app.Activity { *; }", proguard);
StringAssert.Contains ("-keep class my.app.Duplicate { *; }", proguard);
StringAssert.Contains ("-keep class androidx.activity.result.contract.ActivityResultContracts$TakePicture { *; }", proguard);
StringAssert.DoesNotContain ("wrong.Duplicate", proguard);
StringAssert.DoesNotContain ("other.Type", proguard);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,22 @@ public void Write_UserType_SlashesConvertedToDots ()
Assert.Equal ("my.namespace.MyActivity;crc64abcdef.MyActivity", lines [2]);
}

[Fact]
public void Write_NestedJniClass_PreservesDollarSign ()
{
var peer = MakeMcwPeer (
"androidx/activity/result/contract/ActivityResultContracts$TakePicture",
"AndroidX.Activity.Result.Contract.ActivityResultContracts+TakePicture",
"Xamarin.AndroidX.Activity");

var lines = WriteLines (new [] { peer });

Assert.Equal (3, lines.Length);
Assert.Equal ("AndroidX.Activity.Result.Contract.ActivityResultContracts+TakePicture, Xamarin.AndroidX.Activity;androidx.activity.result.contract.ActivityResultContracts$TakePicture", lines [0]);
Assert.Equal ("AndroidX.Activity.Result.Contract.ActivityResultContracts+TakePicture;androidx.activity.result.contract.ActivityResultContracts$TakePicture", lines [1]);
Assert.Equal ("androidx.activity.result.contract.ActivityResultContracts$TakePicture;androidx.activity.result.contract.ActivityResultContracts$TakePicture", lines [2]);
}

[Fact]
public void Write_MultipleTypes_OrderedByManagedName ()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ public void JniNameToJavaName_ConvertsCorrectly (string jniName, string expected
Assert.Equal (expected, JniSignatureHelper.JniNameToJavaName (jniName));
}

[Theory]
[InlineData ("android/app/Activity", "android.app.Activity")]
[InlineData ("java/lang/Object", "java.lang.Object")]
[InlineData ("android/view/View$OnClickListener", "android.view.View$OnClickListener")]
Comment thread
jonathanpeppers marked this conversation as resolved.
public void JniNameToJavaBinaryName_ConvertsCorrectly (string jniName, string expected)
{
Assert.Equal (expected, JniSignatureHelper.JniNameToJavaBinaryName (jniName));
}

[Theory]
[InlineData ("com/example/MainActivity", "com.example")]
[InlineData ("java/lang/Object", "java.lang")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,20 @@ public void Activity_MainLauncher ()
Assert.True (filter?.Elements ("category").Any (c => (string?)c.Attribute (AttName) == "android.intent.category.LAUNCHER"));
}

[Fact]
public void Activity_NestedClass_PreservesBinaryName ()
{
var gen = CreateDefaultGenerator ();
var peer = CreatePeer ("com/example/app/Outer$NestedActivity", new ComponentInfo {
Kind = ComponentKind.Activity,
});

var doc = GenerateAndLoad (gen, [peer]);
var activity = doc.Root?.Element ("application")?.Element ("activity");

Assert.Equal ("com.example.app.Outer$NestedActivity", (string?) activity?.Attribute (AttName));
}

[Fact]
public void Activity_WithProperties ()
{
Expand Down
Loading