Xamarin Android for Android Developers

In a previous post I started to describe my new project PodcastUtilities for Android using Xamarin Android. This is the second part of that journey which describes the steps I took to get the basic Visual Studio Solution setup.

Create Application Project

As I said before I am using VS2019, as it was the most recent stable version available when I started. To get started I created an empty solution called PodcastUtilities and in that solution I created a new Xamarin Android App

New Project

I wanted the minimum amount of generated code so I went for a blank app.

App icon and colour scheme

I wanted to replace the app icon and colour palette with my own so I copied my png and scaleable graphics into the solution. It is a real benefit that the project structure is pretty much identical to that of a regular Java/Kotlin Android Studio project.

Resource structure

I used the wonderful materialpalette.com to generate the colour scheme. The result was placed in the colors.xml in the usual place, note that I added ic_app_background for the app icon which was generated in a separate file by VS2019.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<resources>
	<!-- Palette generated by Material Palette - materialpalette.com/blue/light-green -->
	<color name="primary">#2196F3</color>
	<color name="primary_dark">#1976D2</color>
	<color name="primary_light">#BBDEFB</color>
	<color name="accent">#8BC34A</color>
	<color name="primary_text">#212121</color>
	<color name="secondary_text">#757575</color>
	<color name="icons">#FFFFFF</color>
	<color name="divider">#BDBDBD</color>

	<color name="background">#FFFFFF</color>
	<color name="transparent">#00ffffff</color>

	<color name="ic_app_background">#FFFFFF</color>
</resources>

App name and package name

If you have setup app development in Android Studio you will know that Gradle makes it pretty easy to have different display names and package names for different flavours off the app, for instance debug and release.

android {
    compileSdkVersion 30
    buildToolsVersion '30.0.2'
    defaultConfig {
        applicationId "com.andrewandderek.trailblazer"
        minSdkVersion 19
        targetSdkVersion 30
        versionCode 17
        versionName "1.7.0"
    }

    buildTypes {
        debug {
            applicationIdSuffix '.debug'
        }
    }

This is especially useful as it enables both debug and release versions to be installed on the same device. Its done differently in the Xamarin project.

The package name is set in the AndroidManifest.xml.

To automatically edit this file as part of the build I used a Powershell script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
param ([string] $ProjectDir, [string] $ConfigurationName)
Write-Host "ProjectDir: $ProjectDir"
Write-Host "ConfigurationName: $ConfigurationName"

$ManifestPath = $ProjectDir + "Properties\AndroidManifest.xml"

Write-Host "ManifestPath: $ManifestPath"

[xml] $xdoc = Get-Content $ManifestPath

$package = $xdoc.manifest.package

If ($ConfigurationName -eq "Release" -and $package.EndsWith(".debug")) 
{ 
    $package = $package.Replace(".debug", "") 
}
If ($ConfigurationName -eq "Debug" -and -not $package.EndsWith(".debug")) 
{ 
    $package = $package + ".debug" 
}

If ($package -ne $xdoc.manifest.package) 
{
    $xdoc.manifest.package = $package
    $xdoc.Save($ManifestPath)
    Write-Host "AndroidManifest.xml package name updated to $package"
}

$appname = $xdoc.manifest.application.label

If ($ConfigurationName -eq "Release" -and $appname.EndsWith("_debug")) 
{ 
    $appname = $appname.Replace("_debug", "") 
}
If ($ConfigurationName -eq "Debug" -and -not $appname.EndsWith("_debug")) 
{ 
    $appname = $appname + "_debug" 
}

If ($appname -ne $xdoc.manifest.application.label) 
{
    $xdoc.manifest.application.label = $appname
    $xdoc.Save($ManifestPath)
    Write-Host "AndroidManifest.xml application name updated to $appname"
}

This script is called from the bottom of the csproj file like this

1
2
3
4
5
6
7
8
9
10
11
  <PropertyGroup>
    <PreBuildEvent>PowerShell -File "$(ProjectDir)..\Update-PackageName.ps1" $(ProjectDir) $(ConfigurationName)</PreBuildEvent>
  </PropertyGroup>
  <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
    Other similar extension points exist, see Microsoft.Common.targets.
    <Target Name="BeforeBuild">
    </Target>
    <Target Name="AfterBuild">
    </Target>
  -->
</Project>

The powershell script is stored in the enclosing solution folder. I have to use $(ProjectDir) and be relative as for some reason the $(SolutiontDir) does not work with the Xamarin tooling. The configuration is passed in and it sets the package attribute as well as the android:label without affecting the rest of the file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
    android:versionCode="1" 
    package="com.andrewandderek.podcastutilities.debug" 
    android:installLocation="auto" 
    android:versionName="0.1 step1">
    
    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="31" />
	<application 
        android:allowBackup="true" 
        android:icon="@mipmap/ic_app" 
        android:label="@string/app_name_debug" 
        android:roundIcon="@mipmap/ic_app_round" 
        android:supportsRtl="true" 
        android:theme="@style/AppTheme">
    </application>
	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
	<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
	<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>

In debug it is

package="com.andrewandderek.podcastutilities.debug" 
android:label="@string/app_name_debug" 

And in release it is

package="com.andrewandderek.podcastutilities" 
android:label="@string/app_name" 

The script actually just appends debug to the existing value depending on the build type. So the actual names are specified in the VS2029 project properties UI for the app package name

UI2

and in the strings.xml resources for the app display name

1
2
3
<resources>
    <string name="app_name">PodcastUtilities</string>
    <string name="app_name_debug">Dbg PodcastUtilities</string>

I actually specify the app display name twice, the android:label in the AndroidManifest.xml file, shown above, sets the app name for display in settings and the app info screen.

The display on the home screen/launcher controlled by Label= in the code file for the MainActivity like this

1
2
3
4
5
6
#if DEBUG
    [Activity(Label = "@string/app_name_debug", Theme = "@style/AppTheme", MainLauncher = true)]
#else
    [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)]
#endif
    public class MainActivity : AppCompatActivity

Update package references

The final piece of creating the new app is to upgrade the dependencies. The new app was generated from a project template by VS2019 and that template is now quite old. For example it was targetting an older version of the framework. It was using Android 11 and Mono 10.

The first thing to do is to upgrade this. A little care is needed as for some reason there are two places to set the Android version so its best to set them both, in the project properties UI here

UI1

and here

UI2

Some people have reported that even after setting it in both places they still needed to manually change the csproj file here

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
  ...
    <TargetFrameworkVersion>v12.0</TargetFrameworkVersion>
  ...
  </PropertyGroup>

The next problem is that VS2019 ships with JDK 8 Hotspot. Targetting Android 12 requires JDK 11, this is a appears to be a requirement of the platform/support libraries as its also true for Android Studio.

Upgrading to JDK 11 is pretty straightforward. There are Microsoft builds of OpenJDK that can be installed and there are instructions here.

There were still a couple of problems, setting the JDK in Tools->Options->Xamarin->Android Settings->Java Development Kit Location works but the setting seems to revert when exiting and restarting VS. This is a common problem and there may be a fix in later versions of VS2019.

Also the Xamarin Android designer in VS2019 does not work with JDK 11, again there may be a fix for later versions although I would suggest if you want to target Android 12 you should use VS2022 as the SDK selection and designer both work.

VS Designer error

Next is to check the Nuget packages for the app, I only had three references and I upgraded them all to the latest.

Nuget Packages

Putting it all together

Building and deploying the new app to a device we can see that it all appears to work.

The app name is set correctly on the launcher/home screen. Its also set in the app info page in settings.

Launcher App Info

The app launches and looks like this

Main Activity

As can be seen from the instructions, it not completely plain sailing. Its not a disaster there are just different pain points from using Android Studio.