Category Archives: Android

Android notifications using MAUI (Part 1 of 10)

In my previous post Android Foreground Service using MAUI we looked at implementing a foreground service for Android using MAUI. We touched on notification channels and notifications in general but I decided to go a bit deeper into learning about what we can do with notifications…

I found a good way to learn what Android had to offer was by going through the excellent set of tutorials on the “Coding in Flow” channel on youtube (starting with Notifications Tutorial Part 1 – NOTIFICATION CHANNELS – Android Studio Tutorial).

We’re going to follow through those tutorials in the this and the next nine posts. The intention is to recreate the tutorials but using MAUI and C#, however I know (as I’ve already tried the code out) that there are several areas I was not able to get things to work 100% the same way as the tutorials. Now, this may be due to my code or the current lack of support in MAUI or simply that things have changed in the four years since those tutorials were posted – anyway so upfront, I had problems with some of the notification styles and also with the large icon – if I do figure a way to implement the same functionality I will update this and the subsequent post.

Getting Started

  • Create a MAUI application
  • We’ll add the package CommunityToolkit.Mvvm just so we can use the WeakReferenceMessenger to send messages to our platform specific code. We could ofcourse do this using an interface and register platform specific implementations of the interface, amongst other ways. But for simplicity here, we’ll pass messages around
  • Add the MessageData record
    public record MessageData(int Channel, string Title, string Message);
    

    This will just be used in the IMessenger to pass commands to our platform specific code.

  • In MauiProgram.cs before builder.Build() add the following code
    builder.Services.AddSingleton<MainPage>();
    builder.Services.AddSingleton<IMessenger, WeakReferenceMessenger>();
    
  • We’re going to use code-behind (again to keep things simple), so in the MainPage.xaml, within the ContentPage content, add the following
    <VerticalStackLayout>
       <Entry Text="Title" x:Name="Title" Margin="10" />
       <Entry Text="Message" x:Name="Message" Margin="10" />
    
       <Button Text="Send on Channel 1" Clicked="Channel1_OnClicked" Margin="10" />
       <Button Text="Send on Channel 2" Clicked="Channel2_OnClicked" Margin="10" />
    </VerticalStackLayout>
  • Now in the code behind MainPage.xaml.cs add the following code
    private readonly IMessenger _messenger;
    
    public MainPage(IMessenger messenger)
    {
       InitializeComponent();
    
       _messenger = messenger;
    }
    
    private void Channel1_OnClicked(object sender, EventArgs e)
    {
       _messenger.Send(new MessageData(1, Title.Text, Message.Text));
    }
    
    private void Channel2_OnClicked(object sender, EventArgs e)
    {
       _messenger.Send(new MessageData(2, Title.Text, Message.Text));
    }
    

At this point we have a basic test application with ways to enter a title, a message and send to notification channel 1 and/or channel 2 (we’ll discuss these in the next section).

Android platform specific code

So the MAUI shared code is complete, but now we need to start writing code specific to the Android platform. Note that there are alternatives to writing the code (as already suggests) such as creating platform specific services, but for this post we’re going to do things as simple as possible and reduce things (hopefully) to the bare minimum.

Go to the Platforms/Android folder and in MainApplication.cs add the OnCreate method and again for simplicity, we’ll just have this method set up our channels. So our code looks like this

public override void OnCreate()
{
  base.OnCreate();

  if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
  {
#pragma warning disable CA1416
    var channel1 = new NotificationChannel(Channel1Id, "Channel 1", NotificationImportance.High)
    {
      Description = "This is Channel 1"
    };

    var channel2 = new NotificationChannel(Channel2Id, "Channel 2", NotificationImportance.Low)
    {
      Description = "This is Channel 2"
    };

    if (GetSystemService(NotificationService) is NotificationManager manager)
    {
      manager.CreateNotificationChannel(channel1);
      manager.CreateNotificationChannel(channel2);
    }
#pragma warning restore CA1416
  }
}

We’ll also need to add the const values for the channel id’s, so add the following to the MainApplication class

public static readonly string Channel1Id = "channel1";
public static readonly string Channel2Id = "channel2";

These are public to make them accessible from our MainActivity, in a real world application you might prefer to have these in a separate class, but we’re not going to worry too much about such things here.

With the OnCreate method we need to check that we’re using API >= 21 (or Oreo) to use notification channels and I’m just disabling warnings from the compiler with the pragma’s.

We create two channels, the first we give a high importance, which means that by default when it receives a message it will make a sound and popup the message, unless the user changes their settings. If the user does change the settings, either they need to be reinstated or you need to uninstall and reinstall the application to get them back to default – you may find yourself doing this through the post to reset the application in the emulator.

The code to create the notification channels is fairly simple and hopefully self-explanatory.

Now, you don’t need to create the channels at start-up. If your application (as the one I’m working on that prompted the necessity to learn this stuff) only needs the channels when it goes into the background or when a button is clicked, then you can create the channels as and when required.

Finally for this section, we need to amend the MainActivity.cs to both receive messages via our MAUI UI and to then send the message through to the appropriate notification channel. We will add code to the constructor which simply routes messages from IMessenger like this

public MainActivity()
{
  var messenger = MauiApplication.Current.Services.GetService<IMessenger>();

  messenger.Register<MessageData>(this, (recipient, message) =>
  {
    if (message.Channel == 1)
    {
      SendOnChannel1(message.Title, message.Message);
    }
    else
    {
      SendOnChannel2(message.Title, message.Message);
    }
  });
}

We’re also going to need access to the NotificationManagerCompat so declare the following in the MainActivity class and override the OnCreate to set it, like this

private NotificationManagerCompat _notificationManager;

protected override void OnCreate(Bundle savedInstanceState)
{
  base.OnCreate(savedInstanceState);

  _notificationManager = NotificationManagerCompat.From(this);
}

Finally we’ll need the code for the SendOnChannel1 and SendOnChannel2 methods, which look like this

private void SendOnChannel1(string title, string message)
{
  var notification = new NotificationCompat.Builder(this, MainApplication.Channel1Id)
    .SetSmallIcon(Resource.Drawable.abc_ab_share_pack_mtrl_alpha)
    .SetContentTitle(title)
    .SetContentText(message)
    .SetPriority(NotificationCompat.PriorityHigh)
    .SetCategory(NotificationCompat.CategoryMessage)
    .Build();

    _notificationManager.Notify(1, notification);
}

private void SendOnChannel2(string title, string message)
{
  var notification = new NotificationCompat.Builder(this, MainApplication.Channel2Id)
    .SetSmallIcon(Resource.Drawable.abc_btn_check_material)
    .SetContentTitle(title)
    .SetContentText(message)
    .SetPriority(NotificationCompat.PriorityLow)
    .Build();

    _notificationManager.Notify(2, notification);
}

The two methods are currently pretty much the same, each Builder is supplied with a unique channel id (we’re reusing ours from the MainApplication). A notification requires a small icon, so we’re just reusing built in icons here (which may not show up very well) but feel free to add your own icon (which we will do in a subsequent post if you’re not sure how to). We’re setting priorities differently. If I recall these are duplicated from the code where we create the channels due to different versions of Android API support. So, we’re setting a category on channel 1 which just creates a category association with the channel – we’ll see more on this in a later post. Lastly we essentially add the notification to the NotificationManager with a unique id and we’re done.

Running our application

Now if you run your application, I’m using the Pixel 3 emulator and click the “Send on Channel 1” button, you should see a popup with the title you supplied (or defaults to Title in the text entry) and message you supplied (again defaulted to Message in the text entry) and an audible (annoying) sound, if you click it again you’ll get the same again. If, on the other hand you now click “Send on Channel 2” you’ll see another icon on the status bar appear for the channel 2 messages, but no popup or sound, this is because anything above low importance makes the sound (again unless disabled by the user). Low priority just displays the icon and you can view the message(s) by dragging down the status bar to view notifications.

So, that’s it for part 1. We now have the core MAUI code, we can pass messages around which are received by the platform specific code and which turns those messages from the UI into notifications for Android to display.

Code

Code for this an subsequent posts is found on my blog project.

Android Foreground Service using MAUI

In some situations you’ll want to keep some part of your application running even if the main application goes to sleep. For example the Android Clock applet when started will keep running even if you essentially close the Clock applet. It will display an icon in the status bar at the top right of the Android device and also include notifications.

Note: Code for this solution is available via my blog projects repository.

Getting Started

  • Create a MAUI application
  • We’ll add the package CommunityToolkit.Mvvm just so we can use the WeakReferenceMessenger to send messages to our platform specific code. We could ofcourse do this using an interface and register platform specific implementations of the interface, amongst other ways. But for simplicity here, we’ll pass messages around
  • Add the MessageData record
    public record MessageData(string Message, bool Start);
    

    This will just be used in the IMessenger to pass commands to our platform specific code.

  • In MauiProgram.cs before builder.Build() add the following code
    builder.Services.AddSingleton<MainPage>();
    builder.Services.AddSingleton<IMessenger, WeakReferenceMessenger>();
    
  • We’re going to simply use code-behind (again to keep things simple), so in the MainPage.xaml, within the ContentPage content, add the following
    <VerticalStackLayout>
      <Entry x:Name="Input" Margin="10" />
      <Button Text="Start Service" Clicked="Start_OnClicked" Margin="10" />
      <Button Text="Stop Service" Clicked="Stop_OnClicked" Margin="10" />
    </VerticalStackLayout>
    
  • Now in the code behind MainPage.xaml.cs add the following code
    private readonly IMessenger _messenger;
    
    public MainPage(IMessenger messenger)
    {
       InitializeComponent();
    
       _messenger = messenger;
    }
    
    private void Start_OnClicked(object sender, EventArgs e)
    {
       _messenger.Send(new MessageData(Input.Text, true));
    }
    
    private void Stop_OnClicked(object sender, EventArgs e)
    {
       _messenger.Send(new MessageData(Input.Text, false));
    }
    

At this point we have a basic test application and a way to start/stop the foreground service.

Android platform specific code

The first thing we’ll need to do is edit the Platforms/Android/MainApplication.cs file. We need to start by adding a notification channel which will be displayed in the statusbar and act as the interaction point with our foreground service.

The first thing we need is a channel id, this is a unique identifier within our application to denote the notification channel we’ll want to communicate with. It’s possible for us to have multiple notification channels, but in this instance we’ll just use the one. So in MainApplication add

public static readonly string ChannelId = "exampleServiceChannel";

Next, override OnCreate so it looks like this

public override void OnCreate()
{
  base.OnCreate();

  if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
  {
#pragma warning disable CA1416
    var serviceChannel =
       new NotificationChannel(ChannelId, "Example Service Channel", NotificationImportance.Default);

       if (GetSystemService(NotificationService) is NotificationManager manager)
       {
          manager.CreateNotificationChannel(serviceChannel);
       }
#pragma warning restore CA1416
   }
}

As you can see, this code is targeting API >= 21 (Oreo). We create a NotificationChannel with the ChannelId we declare earlier. The notification has a name “Example Service Channel” and an importance level. This needs to be set to Low or higher. If we set it anything above Low we’ll hear a sound when the notification is sent/updated. Whilst the user can disable this, you may or may not prefer a low importance so your service is less intrusive. Ofcourse it’s all dependent upon your requirements.

Before we get to the foreground service itself, let’s update the MainActivity.cs to handler our IMessenger messages from the UI.

In the MainActivity constructor add the following

var messenger = MauiApplication.Current.Services.GetService<IMessenger>();

messenger.Register<MessageData>(this, (recipient, message) =>
{
  if (message.Start)
  {
    StartService(message.Message);
  }
  else
  {
    StopService();
  }
});

This is just some plumbing code, basically when we press the start service button we’ll receive a message with Start as true and a message from our entry control. Obviously if we get start true we’ll call StartService and if not, we’ll call StopService.

Here’s the start and stop service code

private void StartService(string input)
{
  var serviceIntent = new Intent(this, typeof(ExampleService));
  serviceIntent.PutExtra("inputExtra", input);

  StartService(serviceIntent);
}

private void StopService()
{
  var serviceIntent = new Intent(this, typeof(ExampleService));
  StopService(serviceIntent);
}

Start service is passed the entry text and creates an intent associated with our service code (ExampleService). We add a key/value in the PutExtra method. The key is whatever we want and obviously the value is whatever we want to associated with the key. This will be handled via our service, so the key would best be a const in a real world application.

The StartService(serviceIntent) call is used on an activity that is already in the foreground – so in our case to be able to press the button the application must be in the foreground. If your application might be in the background you can call StartForegroundService(serviceIntent) to both force the application into the foreground and start the service.

Two more things before we get to the service code. The service will require a small icon, so create the folder Platforms/Android/Resources/drawable and add a .PNG into this. Mine’s named AppIcon.png as it’s a duplicate of the actual application icon.

Now we need to declare the requirement of the user permission FOREGROUND_SERVICE, so edit the AndroindManifest.xml file and add the following within the manifest section

<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

The Service code

To recap, at this point we have a basic user interface, which allows us to start and stop a service. We have a notification channel created along with the code for starting and stopping a service. We now need the service. Mine’s named ExampleService.cs and is within the Platforms/Android folder alongside the MainActivity.cs etc.

Create a basic service with the following code

[Service]
internal class ExampleService : Service
{
    public override IBinder OnBind(Intent intent)
    {
        return null;
    }
}

and here’s the usings, just in case you need to check

using Android.App;
using Android.Content;
using Android.OS;
using AndroidX.Core.App;

We’re not using the OnBind method which is used for “Bound Services” so simply return null here. We’ll also need the Service attribute on the class to register our service.

We now need to override the OnStartCommand which will get information for the key/value we added in the MainActivity (via PutExtra). We’ll also want to create an Intent back to our MainActivity so when the user clicks on the notification it can bring the application back into the foreground. Hence we create the notificationIntent.

The NotificationCompat.Builder needs to use the same channel id that we want to send messages to and it requires a PendingIntent within the builder. In the builder we must supply the icon image via SetSmallIcon and we’ll need the content title set. You can see we’re sending the message to the SetContentText.

Finally, unless we’re aiming to reuse this notification we call Build on it and pass this along with an id to the StartForeground.

public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
  var input = intent.GetStringExtra("inputExtra");

  var notificationIntent = new Intent(this, typeof(MainActivity));
  var pendingIntent = PendingIntent.GetActivity(this, 0, notificationIntent, 0);

  var notification = new NotificationCompat.Builder(this, MainApplication.ChannelId)
     .SetContentTitle("Example Service")
     .SetContentText(input)
     .SetSmallIcon(Resource.Drawable.AppIcon)
     .SetContentIntent(pendingIntent)
     .Build();

  StartForeground(1, notification);

  return StartCommandResult.NotSticky;
}

If we are intending to send multiple messages you would want to create the notification (without the Build call) and reuse this to update a notification channel, hence you’d call Build within StartForeground code, like this

var notification = new NotificationCompat.Builder(this, MainApplication.ChannelId)
     .SetContentTitle("Example Service")
     .SetContentText(input)
     .SetSmallIcon(Resource.Drawable.AppIcon)
     .SetContentIntent(pendingIntent)

StartForeground(1, notification.Build());

If we imagine that after StartForeground we start some task, for example downloading a file, then remember this should be run on a background thread and it could then update the notification before again calling the StartForeground(1, notification.Build()) code, ofcourse assuming you’re running the process from the service.

Finally this method returns StartCommandResult.NotSticky. This tells the OS to not bother recreating the application if (for example) the device runs out of memory. StartCommandResult.Sticky is used to tell the OS to recreate the service when it has enough memory. There’s also the option StartCommandResult.RedeliverIntent which is like the NotSticky but if the service is killed before calling stopSelf() for a given intent then the intent will be re-delivered until it completes.

References

This code presented here is based on How to Start a Foreground Service in Android (With Notification Channels) but translated to C# and MAUI code.

Appium, Android and MAUI

Prerequisites

You’ll need Appium server (I’m using Appium Server UI), I installed into my Windows c:\tools\Appium folder and next (whilst not a requirement but is very useful) grab the latest version of Appium Inspector this will allows us to inspect elements within our running Android application, I unzipped this to c:\tools\AppiumInspector.

Appium Server GUI has a button to run the inspector but, unless I’m missing something, this simply sends you to the GitHub repo for the inspector, so run the inspector yourself separately.

Creating a simple app.

Create yourself a MAUI application (mine’s call MauiUIAutomationTest, so you’re see this listed in various places below, if you create your own name, remember to update in the code as well).

Like my previous Appium test samples the code is simple (I’ll recreate here). The MainPage.xaml within the ContentPage looks like this

<Grid>
   <Grid.RowDefinitions>
      <RowDefinition />
      <RowDefinition />
      <RowDefinition />
   </Grid.RowDefinitions>

   <Entry Grid.Row="0" AutomationId="input" Margin="10" Text="{Binding Input}" />
   <Button Grid.Row="1" AutomationId="copy" Margin="10" Command="{Binding ProcessCommand}" Text="Copy" />
   <Label Grid.Row="2" AutomationId="output" Margin="10" Text="{Binding Output}"/>
</Grid>

I’m using MVVM Community Toolkit and so MainPageViewModel.cs is simply this

public partial class MainPageViewModel : ObservableObject
{
    [ObservableProperty] private string input;
    [ObservableProperty] private string output;

    [RelayCommand]
    private void Process()
    {
       Output = Input;
    }
}

Creating our test code

I’ve created an NUnit test class library and added the NuGet package Appium.WebDriver (yes I know it’s an app. but that’s the NuGet package we need). If you’re creating your unit test class library from scratch you’ll need NUnit and NUnit3TestAdapter installed as dependencies as well).

Now my first unit test is, as follows

public class Tests
{
    private AndroidDriver<AndroidElement> _driver;

    [SetUp]
    public void Setup()
    {
        var driverOptions = new AppiumOptions();
        driverOptions.AddAdditionalCapability(MobileCapabilityType.PlatformName, "Android");
        driverOptions.AddAdditionalCapability(MobileCapabilityType.AutomationName, "UiAutomator2"); 
        driverOptions.AddAdditionalCapability(MobileCapabilityType.DeviceName, "Pixel_3_XL_API_29");

        _driver = new AndroidDriver<AndroidElement>(new Uri("http://localhost:4723/wd/hub"), driverOptions); 
        _driver.ActivateApp("com.companyname.mauiuiautomationtest");
    }

    [TearDown]
    public void TearDown()
    {
        _driver.Quit();
    }

    [Test]
    public void ChangeInput_ThenCopy_ExpectMatchingOutput()
    {
        var entry = _driver.FindElementById("com.companyname.mauiuiautomationtest:id/input666");
        entry.Clear();
        entry.SendKeys("Hello World");
        var button = _driver.FindElementById("com.companyname.mauiuiautomationtest:id/copy666");
        button.Click();
        var label = _driver.FindElementById("com.companyname.mauiuiautomationtest:id/output666");
        Assert.That(label.Text, Is.EqualTo("Hello World"));
    }
}

Obviously this is not perfect as we’re running the setup and tear down code after every test, but as I only have one test, I don’t care (it’s for the reader to do this correctly). Now we setup the driver options, the platformName is ofcourse Android, the deviceName relates to the device you’re using, I’m using the Android emulator for the Pixel 3 API 29, finally automationName specifies the automation engine to use, to be honest you could remove this and it’ll default the standard Appium engine but UiAutomator2 is listed as Appiums flagship engine, so let’s use that.

I had all sorts of issues trying to get Appium to start my MAUI app (as it will try to install it), however I think this is likely down to the way Visual Studio builds the apk, i.e. I probably need to publish a signed apk or the likes, but for now I’ll let Visual Studio deploy the app and then access it via Appium. Hence we have the following code to active an already installed app.

 
_driver.ActivateApp("com.companyname.mauiuiautomationtest");

Eager to see something working?

If you’re eager to see something working, then

  • Deploy your application to your Android emulator (I just get Visual Studio to deploy it)
  • Run the Appium Server GUI and start a new session
  • Run your unit test

If all went well the test was green and if not, check your application name etc.

Appium Inspector

The Appium server does not come with an inspector now, you need to download it separately (as stated earlier). Ofcourse we can probably get a lot done without an inspector, but it helps and gives confidence that if it see’s our id’s etc. then Appium will. You’ll need to tell the inspector to connect to the Appium server – make sure that Remote Path has the URL /wd/hub and you have valid the following Desired Capabilities

{
  "appium:automationName": "UIAutomator2",
  "appium:deviceName": "Pixel_3_XL_API_29",
  "platformName": "Android"
}

Ensure your have Appium server is running. Before you run the inspector just make sure your app. is running (just makes things simpler) and now start a session from your Appium Inspector. It should show you the screen of the current running application.

As you can see from the code below, we expect a “copy” automation id

<Button Grid.Row="1" AutomationId="copy" Margin="10" Command="{Binding ProcessCommand}" Text="Copy" />

MAUI AutomationId’s do not appear as AutomationId or Accessibility Id for that matter, but instead they become id’s but prefixed with you app name, i.e. that’s why our unit tests has com.companyname.mauiuiautomationtest:id/output. All that really matters is that we have a unique value on an Appium accessible property to do some UI Automation testing. We do have the option of XPath locators, but these should probably be used as a the last resort, if we do not have anything else to locate against.

Change the colour of the status bar on Android (in Xamarin Forms)

In your Android project, values folder styles.xml file you’ll find something like

<style name="MainTheme" parent="MainTheme.Base">
   <!-- -->
</style>

and/or

<style name="MainTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">
   <!-- -->
</style>

Use the following element and attribute in the MainTheme.Base if that exists or the MainTheme (it will depend on the template you used at to whether one or both of these exist)

<!-- Top status bar area on Android -->
<item name="android:statusBarColor">#FF0C1436</item> 

You may wish to also change the colour of the bar background at the top of the NavigationPage (if you’re using the NavigationPage such as with Prism) by adding the following to the App.xaml

<Application.Resources>
  <ResourceDictionary>
    <Style TargetType="NavigationPage">
      <Setter Property="BarBackgroundColor" Value="Color.Red"/>
      <Setter Property="BarTextColor" Value="Color.White"/>
    </Style>
  </ResourceDictionary>
</Application.Resources>

in non-Prism you can change the Primary colour, for example again in App.xaml

<Application.Resources>
  <ResourceDictionary>
    <Color x:Key="Primary">#FF3399FF</Color>
  </ResourceDictionary>
</Application.Resources>

Accessing the host machine via the Android emulator

If you want to access a service/server running on the host machine you need to use specially designated IP addresses, such as 10.0.2.2.

Note: The following table is taken from Network address space

Network AddressDescription
10.0.2.1Router/gateway address
10.0.2.2Alias to your host loopback (i.e. 127.0.0.1 on the host machine)
10.0.2.3First DNS server
10.0.2.4/10.0.2.5/10.0.2.6Optional, second, third and fourth DNS server (if any)
10.0.2.15The emulated device network/ethernet interface
127.0.0.1The emulated device loopback interface

Creating an Android Virtual Device

Whilst running Android applications on a real device is pretty quick nowadays, it’s still useful to run our apps against an emulator.

In this post we’re going to look at creating an emulator which works/looks like a Samsung s10e.

Device information and skins

In the case of Samsung devices, simply visit Samsung, Emulator Skin Overview.

Here you can find skin downloads (to give your emulator more of a look of the device) along with device information, such as screen size, resolution etc. Download the skin.

Note: You’ll need to be registered on the Samsung developer site to download the skins, but it’s a painless process.

In the case of the s10e, the device has a 5.8 inch display and a resolution of 1080×2280. So we’re put that information into our virtual device in a minute, before we do that, extract the downloaded skin into the following folder

%USERPROFILE%\AppData\Local\Android\Sdk\skins\samsung_s10e

Now we’re got all the relevant skin related files let’s move onto creating our virtual device.

Android device manager

From Visual Studio or Android Studio, locate the button to run the Android Device Manager. In either case either press New… or Create Virtual Device…

To save trying to describe both UI’s I’m going to stick with the Android Studio UI. So after click on Create Virtual Device, do the following

  • Click New Hardware Profile
  • Set Device Name to Samsung S10e
  • Ensure Device Type is Phone/Tablet
  • Set screen size to 5.8
  • Set Resolution to 1080 x 2280

Leave all other options as they are but now scroll down to the Default Skin option. Clicking on the … button does nothing until you switch from No Skin to select a skin, any skin will work. Don’t worry if you get a warning about the skin not being the right size as we’re going to change it now. Click on the … button and locate the samsaung_s10e folder we put the skin files into.

Finally, click Finish.

  • With the new device selected, press the Next button
  • Currently the s10e is running Android Pie API Level 28, so select this or your preferred version of Android
  • Press Next and if all looks good, then press Finish

If you need to edit the configuration you can do so via the Android Virtual Device Manager, you’ll need to select the device and then click the edit button. You’ll need to click the Show Advanced Settings button to change memory, or if you wish to change the skin.

Sideloading an Android application from a website

You might want to supply your Android application outside of the Play store. This might be for testing or simply because you don’t want or need your application to go through the Play store (i.e. a bespoke application for a company).

Assuming you’ve got your APK file you can simply drop in onto a web site and link to it (or supply the location). For example

<html>
  <head>
    <title>My Android Application</title>
  </head>
  <body>
      <p>
      <h2>Install My Android application by clicking on 
this <a href="com.puridparrot.myapp.apk">link</a>
      </h2>
    </p>
  </body>
</html>

Simple enough – we might need to set the mime type, for example here’s the web.config for IIS

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <staticContent>
      <mimeMap 
         fileExtension=".apk" 
         mimeType="application/vnd.android.package-archive" />
    </staticContent>
  </system.webServer>
</configuration>

Now when you click this link from your device it’ll download the file and install it, you will need to set “Unknown sources” on to allow this to happen.

The downsides of this approach are that you obviously don’t get any notifications for updates to an application, also you’ll get a couple of security prompts to remind the user that this app. may not be from a trusted source.

React Native “Hello World”

This posts assumes you’ve installed the Android studio. I won’t go through the steps for installing as, to be honest, I’ve had an installation too long to recall any steps.

Getting started

To begin with we need to use npm or yarn to add the react native scaffolding application.

yarn add global create-react-native-app

Assuming everything’s installed we can now run

yarn create-react-native-app helloworld

Replace helloworld with your application name.

You’ll then be prompted for the template to use, managed workflow, blank, blank (TypeScript), tabs or bare workflow, minimal or minimal (TypeScript).

Let’s choose minimal (TypeScript).

Next you’ll enter the name and displayName or your application, finally you’ll be prompted to install dependencies, select Yes.

From our application folder we can run

yarn android
yarn ios

In this case I’m working on Android, so before we go any further you need to run up one of the installed Android emulators before running the next step.

Now run

yarn start

which will run react-native start followed by

yarn android

in another terminal run. This will run react-native run-android.

If all goes well then the emulator should display some text (depending on the template you used).

Using the emulator

In emulator type r r in quick succession in the emulator to reload your application. Or you can enable automatic/hot loading by pressing CTRL+M in the emulator and selecting Enable Live Reload.

Problems

Whilst setting up Android studio on Linux or Windows you need to make sure ANDROID_HOME, ANDROID_PLATFORM_TOOLS and ANDROID_SDK_ROOT environment variables are set up. For example on Windows these are

  • ANDROID_HOME – C:\Users\<Username>\AppData\Local\Android\Sdk
  • ANDROID_SDK_ROOT – C:\Users\<Username>\AppData\Local\Android\Sdk
  • ANDROID_PLATFORM_TOOLS – C:\Users\<Username>\AppData\Local\Android\Sdk\platform-tools

Hello World

At this point, hopefully we’ve got everything running so let’s open App.tsx and replace

<Text>Open up App.tsx to start working on your app!</Text>

with

<Text>Hello World</Text>

Double clicking r in the emulator will reload the application displaying Hello World. So we’re on our way to the first React Native application.

Preparing a Xamarin Forms app. for deployment to Android

So I’ve created a cross platform application using Xamarin Forms and Visual Studio 2017 and now I want to get it deployed on some devices – in this post I’m concentrating on Android devices. So let’s look at what we need to do to get this application deployed…

I’m going to assume that the application version, icon, permission requirements and all those “standard” things have been implemented/setup and concentrate on getting the application ready for deployment to our device(s).

Release build

Obviously, like any other application, we’ll want to minimize the size of our application and remove any debug centric code that might be wrapped in #if DEBUG etc.

Select the Android project (ensure the build is set to Release), right mouse click and select properties and in Android Options ensure Use Shared Runtime and Use Fast Deployment (debug model only) are unchecked.

Archive and prepare for distribution

The process of bundling up an Android application is known as generating an archive. Right mouse click on the Android project in your solution and select Archive. Visual Studio will now go about archiving your application bundle.

Now click the Distribute button where you’ll be able to target the application’s distribution channel. I have two options Ad Hoc and Google Play. I’m going to stick to Ad Hoc which allows me to use email, a web site etc. to distribute my application via side loading.

We need to sign our application, so if you’ve not yet got a keystore/identity set-up you can just click the + button.

Note: Even if you’re just creating a new keystore for each application you’ll need to make sure you remember your password as this will be required to complete the signing process.

Fill in the options and then press Save As and you’ll be prompted for the keystore password.

We have now created an APK which can be installed onto you Android device. In my case I’m using Microsoft’s AppCenter to allow specific users to access the application for testing.

References

Preparing an Application for Release
Signing the Android Application Package

Android application’s default theme colours

Note: I’m developing an application with Xamarin Forms, so, although I assume this is the same across Android development, I have not verified this.

We define a style for an Android application (see Resources/values/styles.xml). This consists of the theme being used as well as some values for customisation. These include the status bar (the bar at the top of the screen which contains notification icons such as wi-fi, Bluetooth icons etc.) as well as the caption/title colour, navigation bar colour etc.

The default colours for this and/or your applications toolbar etc. are customisable via this styles.xml file. Here’s some of the values available

  • colorPrimaryDark – this is the status bar colour, by default it’s set to #1976D2
  • colorPrimary – this is the action bar/toolbar colour, by default it’s set to #2196F3
  • colorAccent – this is the tint colour (used on controls/widgets as a highlight), by default it’s set to #FF4081

References

Material Design for Android
Styles and Themes