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
47 changes: 44 additions & 3 deletions Documentation/guides/OneDotNet.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,12 +195,47 @@ It is recommended to migrate to the new linker settings, as
There are currently a few "verbs" we are aiming to get working in
Xamarin.Android:

dotnet new
dotnet build
dotnet publish
dotnet run

Currently in .NET 5 console apps, `dotnet publish` is where all the
work to produce a self-contained "app" happens:
### dotnet new

To support `dotnet new`, we created a few basic project and item
templates for Android that are named following the patterns and naming
of existing .NET templates:

Templates Short Name Language Tags
-------------------------------------------- ------------------- ---------- ----------------------
Android Activity template android-activity [C#] Android
Android Java Library Binding android-bindinglib [C#] Android
Android Layout template android-layout [C#] Android
Android Class library androidlib [C#] Android
Android Application android [C#] Android
Console Application console [C#],F#,VB Common/Console
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May want to omit the entries that don't appear to follow convention, so skip console and classlib & the rest, but include wpf and wpflib.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I included wpf, wpflib, nunit, and nunit-test. I don't think we should drop console or classlib from this example, because those are the most basic templates.

Class library classlib [C#],F#,VB Common/Library
WPF Application wpf [C#],VB Common/WPF
WPF Class library wpflib [C#],VB Common/WPF
NUnit 3 Test Project nunit [C#],F#,VB Test/NUnit
NUnit 3 Test Item nunit-test [C#],F#,VB Test/NUnit

To create different types of Android projects:

dotnet new android --output MyAndroidApp --packageName com.mycompany.myandroidapp
dotnet new androidlib --output MyAndroidLibrary
dotnet new android-bindinglib --output MyJavaBinding

Once the projects are created, some basic item templates can also be
used such as:

dotnet new android-activity --name LoginActivity --namespace MyAndroidApp
dotnet new android-layout --name MyLayout --output Resources/layout

### dotnet build & publish

Currently in .NET console apps, `dotnet publish` is where all the work
to produce a self-contained "app" happens:

* The linker via the `<IlLink/>` MSBuild task
* .NET Core's version of AOT, named "ReadyToRun"
Expand Down Expand Up @@ -229,12 +264,18 @@ Play, ad-hoc distribution, etc. It could be able to sign the `.apk` or
`.aab` with different keys. As a starting point, this will currently
copy the output to a `publish` directory on disk.

[illink]: https://github.com/mono/linker/blob/master/src/linker/README.md

### dotnet run

`dotnet run` can be used to launch applications on a
device or emulator via the `--project` switch:

dotnet run --project HelloAndroid.csproj

[illink]: https://github.com/mono/linker/blob/master/src/linker/README.md
Alternatively, you could use the `Run` MSBuild target such as:

dotnet build HelloAndroid.csproj -t:Run

### Preview testing

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,22 @@ public override bool Execute ()
packWriter.WriteStartElement ("Directory");
packWriter.WriteAttributeString ("Id", "packs");
packWriter.WriteAttributeString ("Name", "packs");
packWriter.WriteAttributeString ("FileSource", packs_dir);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to include a "snippet" of what the new XML output looks like with these changes?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line adds the full path such as:

<Directory Id="packs" Name="packs" FileSource="C:\Users\myuser\android-toolchain\dotnet\packs">

It was not explicitly needed before because RecurseDirectory() adds sub-directories with FileSource, but I added it because the rest of the elements specify FileSource.

The second block looks like:

<Directory Id="templatepacks" Name="template-packs" FileSource="C:\Users\myuser\android-toolchain\dotnet\template-packs">
  <Component Id="SC983E605827BDDA589CA4DFF082E29CDFEC2CD059B4AF3C01421CC95568A8D74">
    <File Id="SC983E605827BDDA589CA4DFF082E29CDFEC2CD059B4AF3C01421CC95568A8D74" Name="Microsoft.Android.Templates.11.0.100-ci.dotnet-new.259.nupkg" KeyPath="yes" />
  </Component>
</Directory>

MSI has weird rules around the Id, which is why I used hashes for files.

foreach (var directory in Directory.EnumerateDirectories (packs_dir, "Microsoft.Android.*")) {
RecurseDirectory (packs_dir, packWriter, componentWriter, directory);
}
packWriter.WriteEndElement (); // </Directory> packs

// template-packs
var templates_dir = Path.Combine (DotNetPath, "template-packs");
packWriter.WriteStartElement ("Directory");
packWriter.WriteAttributeString ("Id", "templatepacks");
packWriter.WriteAttributeString ("Name", "template-packs");
packWriter.WriteAttributeString ("FileSource", templates_dir);
foreach (var file in Directory.EnumerateFiles (templates_dir, "Microsoft.Android.Templates.*.nupkg")) {
AddFile (templates_dir, packWriter, componentWriter, file);
}
packWriter.WriteEndElement (); // </Directory> template-packs

packWriter.WriteEndDocument (); // </Directory>
componentWriter.WriteEndDocument (); // </ComponentGroup>
Expand Down Expand Up @@ -145,22 +158,27 @@ static void RecurseDirectory (string top_dir, XmlWriter packWriter, XmlWriter co
var fileName = Path.GetFileName (file);
if (fileName.StartsWith (".") || fileName.StartsWith ("_"))
continue;
var componentId = GetId (top_dir, file);
packWriter.WriteStartElement ("Component");
packWriter.WriteAttributeString ("Id", componentId);
packWriter.WriteStartElement ("File");
packWriter.WriteAttributeString ("Id", componentId);
packWriter.WriteAttributeString ("Name", Path.GetFileName (file));
packWriter.WriteAttributeString ("KeyPath", "yes");
packWriter.WriteEndElement (); // </File>
packWriter.WriteEndElement (); // </Component>
componentWriter.WriteStartElement ("ComponentRef");
componentWriter.WriteAttributeString ("Id", componentId);
componentWriter.WriteEndElement (); // </ComponentRef>
AddFile (top_dir, packWriter, componentWriter, file);
}
packWriter.WriteEndElement (); // </Directory>
}

static void AddFile (string top_dir, XmlWriter packWriter, XmlWriter componentWriter, string file)
{
string componentId = GetId (top_dir, file);
packWriter.WriteStartElement ("Component");
packWriter.WriteAttributeString ("Id", componentId);
packWriter.WriteStartElement ("File");
packWriter.WriteAttributeString ("Id", componentId);
packWriter.WriteAttributeString ("Name", Path.GetFileName (file));
packWriter.WriteAttributeString ("KeyPath", "yes");
packWriter.WriteEndElement (); // </File>
packWriter.WriteEndElement (); // </Component>
componentWriter.WriteStartElement ("ComponentRef");
componentWriter.WriteAttributeString ("Id", componentId);
componentWriter.WriteEndElement (); // </ComponentRef>
}

static string GetId (string top_dir, string path)
{
if (string.IsNullOrEmpty (path))
Expand Down
1 change: 1 addition & 0 deletions build-tools/create-dotnet-pkg/create-dotnet-pkg.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<_FilesToCopy Include="$(DotNetPreviewPath)\sdk-manifests\$(DotNetPreviewVersionBand)\Microsoft.NET.Workload.Android\**\*" />
<_FilesToCopy Include="$(DotNetPreviewPath)\packs\Microsoft.Android.Ref\**\*" />
<_FilesToCopy Include="$(DotNetPreviewPath)\packs\Microsoft.Android.Sdk.osx-x64\**\*" />
<_FilesToCopy Include="$(DotNetPreviewPath)\template-packs\Microsoft.Android.Templates.*.nupkg" />
</ItemGroup>
<MakeDir Directories="$(PkgOutputPath);$(PayloadDir);$(PkgResourcesPath);$(LicenseDestination)"/>
<ReplaceFileContents
Expand Down
5 changes: 5 additions & 0 deletions build-tools/create-packs/Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
<Exec Command="$(DotNetPreviewTool) pack @(_GlobalProperties, ' ') -p:AndroidHostRID=osx-x64 &quot;$(MSBuildThisFileDirectory)Microsoft.Android.Sdk.proj&quot;" Condition=" '$(HostOS)' == 'Darwin' " />
<Exec Command="$(DotNetPreviewTool) pack @(_GlobalProperties, ' ') -p:AndroidHostRID=win-x64 &quot;$(MSBuildThisFileDirectory)Microsoft.Android.Sdk.proj&quot;" Condition=" '$(HostOS)' != 'Linux' " /> <!-- Windows pack should be built both Windows and macOS -->
<Exec Command="$(DotNetPreviewTool) pack @(_GlobalProperties, ' ') &quot;$(MSBuildThisFileDirectory)Microsoft.NET.Workload.Android.proj&quot;" />
<Exec Command="$(DotNetPreviewTool) pack @(_GlobalProperties, ' ') &quot;$(XamarinAndroidSourcePath)src\Microsoft.Android.Templates\Microsoft.Android.Templates.csproj&quot;" />
</Target>

<Target Name="ExtractWorkloadPacks"
Expand All @@ -92,6 +93,7 @@
<_WLPacks Include="$(XamarinAndroidSourcePath)bin\Build$(Configuration)\nupkgs\Microsoft.Android.Sdk.osx-x64.*.nupkg" Condition=" '$(HostOS)' == 'Darwin' " />
<_WLPacks Include="$(XamarinAndroidSourcePath)bin\Build$(Configuration)\nupkgs\Microsoft.Android.Sdk.win-x64.*.nupkg" Condition=" '$(HostOS)' == 'Windows' " />
<_WLPacks Include="$(XamarinAndroidSourcePath)bin\Build$(Configuration)\nupkgs\Microsoft.Android.Ref.*.nupkg" />
<_WLTemplates Include="$(XamarinAndroidSourcePath)bin\Build$(Configuration)\nupkgs\Microsoft.Android.Templates.*.nupkg" />
<!-- Runtime packs are not yet supported by workloads -->
<!-- <_WLPacks Include="$(XamarinAndroidSourcePath)bin\Build$(Configuration)\nupkgs\Microsoft.Android.Runtime.*.nupkg" /> -->
</ItemGroup>
Expand All @@ -106,6 +108,8 @@
SourceFiles="@(_WLPacks)"
DestinationFolder="$(DotNetPreviewPath)packs\$([System.String]::Copy('%(_WLPacks.Filename)').Replace('.$(_WLPackVersion)', ''))\$(_WLPackVersion)"
/>
<MakeDir Directories="$(DotNetPreviewPath)template-packs" />
<Copy SourceFiles="@(_WLTemplates)" DestinationFolder="$(DotNetPreviewPath)template-packs" />
<ItemGroup>
<_UnixExecutables Include="$(DotNetPreviewPath)packs\Microsoft.Android.Sdk.*\*\tools\$(HostOS)\**\*.*" />
<_FilesToTouch Include="$(DotNetPreviewPath)sdk-manifests\$(DotNetPreviewVersionBand)\Microsoft.NET.Workload.Android\**" />
Expand All @@ -129,6 +133,7 @@
<_PackFilesToDelete Include="$(DotNetPreviewPath)sdk-manifests\$(DotNetPreviewVersionBand)\Microsoft.Android.Workload\**\*.*" />
<_PackFilesToDelete Include="$(DotNetPreviewPath)sdk-manifests\$(DotNetPreviewVersionBand)\Microsoft.NET.Workload.Android\**\*.*" />
<_PackFilesToDelete Include="$(DotNetPreviewPath)packs\Microsoft.Android*\**\*.*" />
<_PackFilesToDelete Include="$(DotNetPreviewPath)template-packs\Microsoft.Android.Templates.*.nupkg" />
</ItemGroup>
<RemoveDir Directories="%(_PackFilesToDelete.RootDir)%(_PackFilesToDelete.Directory)" />
<Delete Files="$(_WorkloadResolverFlagFile)" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ workload manifest pack containing information about the various Microsoft.Androi
<ReplaceFileContents
SourceFile="$(XamarinAndroidSourcePath)src\Xamarin.Android.Build.Tasks\Microsoft.NET.Workload.Android\WorkloadManifest.in.json"
DestinationFile="$(WorkloadManifestJsonPath)"
Replacements="@SDK_PACK_VERSION@=$(AndroidPackVersionLong);@REF_PACK_VERSION@=$(AndroidPackVersionLong)">
Replacements="@SDK_PACK_VERSION@=$(AndroidPackVersionLong);@REF_PACK_VERSION@=$(AndroidPackVersionLong);@TEMPLATE_PACK_VERSION@=$(AndroidPackVersionLong);">
</ReplaceFileContents>

<ItemGroup>
Expand Down
15 changes: 15 additions & 0 deletions src/Microsoft.Android.Templates/Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project>
<Import Project="..\..\build-tools\scripts\XAVersionInfo.targets" />
<PropertyGroup>
<BeforePack>
_GetDefaultPackageVersion;
$(BeforePack);
</BeforePack>
</PropertyGroup>
<Target Name="_GetDefaultPackageVersion"
DependsOnTargets="GetXAVersionInfo" >
<PropertyGroup>
<PackageVersion>$(AndroidPackVersionLong)+sha.$(XAVersionHash)</PackageVersion>
</PropertyGroup>
</Target>
</Project>
23 changes: 23 additions & 0 deletions src/Microsoft.Android.Templates/Microsoft.Android.Templates.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageType>Template</PackageType>
<PackageId>Microsoft.Android.Templates</PackageId>
<Title>.NET Android Templates</Title>
<Authors>Microsoft</Authors>
<Description>Templates for Android platforms.</Description>
<IncludeContentInPack>true</IncludeContentInPack>
<IncludeBuildOutput>false</IncludeBuildOutput>
<ContentTargetFolders>content</ContentTargetFolders>
<OutputPath>..\..\bin\Build$(Configuration)\nupkgs\</OutputPath>
</PropertyGroup>

<Import Project="..\..\Configuration.props" />

<ItemGroup>
<Content Include="**\*" Exclude="**\bin\**;**\obj\**" />
<Compile Remove="**\*" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "http://json.schemastore.org/template",
"author": "Microsoft",
"classifications": [ "Android" ],
"name": "Android Activity template",
"description": "An Android Activity class",
"tags": {
"language": "C#",
"type": "item"
},
"identity": "Microsoft.Android.AndroidActivity",
"shortName": "android-activity",
"sourceName": "Activity1",
"primaryOutputs": [
{ "path": "Activity1.cs" }
],
"defaultName": "Activity1",
"symbols": {
"namespace": {
"description": "namespace for the generated code",
"replaces": "AndroidApp1",
"type": "parameter"
}
}
}
18 changes: 18 additions & 0 deletions src/Microsoft.Android.Templates/android-activity/Activity1.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Android.App;
using Android.OS;
using Android.Runtime;
using Android.Widget;

namespace AndroidApp1
{
[Activity(Label = "@string/app_name", MainLauncher = true)]
public class Activity1 : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);

// Create your application here
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"$schema": "http://json.schemastore.org/template",
"author": "Microsoft",
"classifications": [ "Android" ],
"identity": "Microsoft.Android.AndroidBinding",
"name": "Android Java Library Binding",
"description": "A project for creating an Android class library that binds to a native Java library",
"shortName": "android-bindinglib",
"tags": {
"language": "C#",
"type": "project"
},
"sourceName": "AndroidBinding1",
"preferNameDirectory": true,
"primaryOutputs": [
{ "path": "AndroidBinding1.csproj" }
],
"defaultName": "AndroidBinding1"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
Additions allow you to add arbitrary C# to the generated classes
before they are compiled. This can be helpful for providing convenience
methods or adding pure C# classes.

== Adding Methods to Generated Classes ==

Let's say the library being bound has a Rectangle class with a constructor
that takes an x and y position, and a width and length size. It will look like
this:

public partial class Rectangle
{
public Rectangle (int x, int y, int width, int height)
{
// JNI bindings
}
}

Imagine we want to add a constructor to this class that takes a Point and
Size structure instead of 4 ints. We can add a new file called Rectangle.cs
with a partial class containing our new method:

public partial class Rectangle
{
public Rectangle (Point location, Size size) :
this (location.X, location.Y, size.Width, size.Height)
{
}
}

At compile time, the additions class will be added to the generated class
and the final assembly will a Rectangle class with both constructors.


== Adding C# Classes ==

Another thing that can be done is adding fully C# managed classes to the
generated library. In the above example, let's assume that there isn't a
Point class available in Java or our library. The one we create doesn't need
to interact with Java, so we'll create it like a normal class in C#.

By adding a Point.cs file with this class, it will end up in the binding library:

public class Point
{
public int X { get; set; }
public int Y { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-android</TargetFramework>
<RootNamespace Condition="'$(name)' != '$(name{-VALUE-FORMS-}safe_namespace)'">AndroidBinding1</RootNamespace>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<enum-field-mappings>
<!--
This example converts the constants Fragment_id, Fragment_name,
and Fragment_tag from android.support.v4.app.FragmentActivity.FragmentTag
to an enum called Android.Support.V4.App.FragmentTagType with values
Id, Name, and Tag.
<mapping jni-class="android/support/v4/app/FragmentActivity$FragmentTag" clr-enum-type="Android.Support.V4.App.FragmentTagType">
<field jni-name="Fragment_name" clr-name="Name" value="0" />
<field jni-name="Fragment_id" clr-name="Id" value="1" />
<field jni-name="Fragment_tag" clr-name="Tag" value="2" />
</mapping>
-->
</enum-field-mappings>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<enum-method-mappings>
<!--
This example changes the Java method:
android.support.v4.app.Fragment.SavedState.writeToParcel (int flags)
to be:
android.support.v4.app.Fragment.SavedState.writeToParcel (Android.OS.ParcelableWriteFlags flags)
when bound in C#.
<mapping jni-class="android/support/v4/app/Fragment.SavedState">
<method jni-name="writeToParcel" parameter="flags" clr-enum-type="Android.OS.ParcelableWriteFlags" />
</mapping>
-->
</enum-method-mappings>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<metadata>
<!--
This sample removes the class: android.support.v4.content.AsyncTaskLoader.LoadTask:
<remove-node path="/api/package[@name='android.support.v4.content']/class[@name='AsyncTaskLoader.LoadTask']" />

This sample removes the method: android.support.v4.content.CursorLoader.loadInBackground:
<remove-node path="/api/package[@name='android.support.v4.content']/class[@name='CursorLoader']/method[@name='loadInBackground']" />
-->
</metadata>
Loading