This document explains how to implement background execution support for Flic2 buttons in your MAUI Android app, ensuring that Flic2 buttons remain connected and functional even when the app is not in the foreground.
To keep Flic2 buttons working in the background on Android, you need to implement background services in your main application project. The Flic2 MAUI library provides helper templates and guidance to make this implementation easier, but the actual service components must be in your app project to avoid Java callable wrapper build issues.
The implementation consists of three main components that you'll add to your project:
- FlicBackgroundService - Foreground service that keeps Flic connections alive
- FlicBootReceiver - Automatically restarts the service after device boot
- Helper code in MainActivity - Starts the background service
Due to Android's Java callable wrapper requirements, background service components cannot be distributed in NuGet packages. They must be compiled as part of your main application. The library provides helper templates and documentation to make implementation straightforward.
Create a new file Platforms/Android/FlicBackgroundService.cs in your MAUI project:
using Android.App;
using Android.Content;
using Android.OS;
using AndroidX.Core.App;
using flic2lib.Maui;
namespace YourApp.Platforms.Android;
[Service(Enabled = true, Exported = false, ForegroundServiceType = global::Android.Content.PM.ForegroundService.TypeConnectedDevice)]
public class FlicBackgroundService : Service
{
private const int NOTIFICATION_ID = 1001;
private const string CHANNEL_ID = "flic_background_channel";
public override IBinder? OnBind(Intent? intent) => null;
public override StartCommandResult OnStartCommand(Intent? intent, StartCommandFlags flags, int startId)
{
CreateNotificationChannel();
StartForeground(NOTIFICATION_ID, CreateNotification());
// Initialize Flic manager for background operation
FlicManager.Init();
return StartCommandResult.Sticky;
}
private void CreateNotificationChannel()
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var channel = new NotificationChannel(CHANNEL_ID, "Flic Background Service", NotificationImportance.Low)
{
Description = "Keeps Flic buttons connected when app is in background"
};
var notificationManager = (NotificationManager?)GetSystemService(NotificationService);
notificationManager?.CreateNotificationChannel(channel);
}
}
private Notification CreateNotification()
{
var intent = new Intent(this, typeof(MainActivity));
intent.SetFlags(ActivityFlags.SingleTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable);
var notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.SetContentTitle("Flic Buttons Connected")
.SetContentText("Your Flic buttons are ready to use")
.SetSmallIcon(Resource.Drawable.notification_icon) // Add your notification icon
.SetOngoing(true)
.SetPriority(NotificationCompat.PriorityLow)
.SetContentIntent(pendingIntent)
.Build();
return notification;
}
}Create a new file Platforms/Android/FlicBootReceiver.cs in your MAUI project:
using Android.App;
using Android.Content;
using Android.OS;
namespace YourApp.Platforms.Android;
[BroadcastReceiver(Enabled = true, Exported = true)]
[IntentFilter(new[] { Intent.ActionBootCompleted, Intent.ActionMyPackageReplaced })]
public class FlicBootReceiver : BroadcastReceiver
{
public override void OnReceive(Context? context, Intent? intent)
{
if (context == null || intent?.Action == null) return;
if (intent.Action == Intent.ActionBootCompleted ||
intent.Action == Intent.ActionMyPackageReplaced)
{
var serviceIntent = new Intent(context, typeof(FlicBackgroundService));
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
context.StartForegroundService(serviceIntent);
else
context.StartService(serviceIntent);
}
}
}Add the following to your Platforms/Android/AndroidManifest.xml:
<application android:allowBackup="true" android:icon="@mipmap/appicon"
android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true">
<!-- Your Flic Background Service -->
<service android:name=".FlicBackgroundService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="connectedDevice" />
<!-- Your Flic Boot Receiver -->
<receiver android:name=".FlicBootReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="1000">
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
</application>
<!-- Required permissions for background execution -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />Add this to your MainActivity.cs:
using Microsoft.Extensions.Logging;
namespace YourApp
{
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation |
ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize |
ConfigChanges.Density)]
public class MainActivity : MauiAppCompatActivity
{
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Start the background service for handling Flic button events
var serviceIntent = new Intent(this, typeof(FlicBackgroundService));
StartForegroundService(serviceIntent);
}
}
}The background service maintains Bluetooth connections to your Flic buttons and handles button events even when your app is not in the foreground. The service:
- Runs as an Android foreground service with persistent notification
- Maintains Bluetooth connections to paired Flic buttons
- Processes button events (click, double-click, hold) in the background
- Can trigger actions, send notifications, or wake your app
- Automatically restarts after device boot or app updates
The library includes helper templates and documentation to guide your implementation:
Located in flic2lib.Maui/Helpers/AndroidBackgroundServiceHelper.cs, this helper provides:
- Guidance on implementing Android foreground services
- Template methods for notification handling
- Documentation on Android background execution requirements
The flic2lib.Maui.Sample project contains a complete working example:
FlicBackgroundService.cs- Full service implementationFlicBootReceiver.cs- Boot receiver implementation- Proper AndroidManifest.xml configuration
- Integration with MainActivity
- Copy template files from the sample project to your app
- Customize the service to handle your specific button events
- Add service and receiver declarations to your AndroidManifest.xml
- Start the service from your MainActivity
- Handle button events according to your app's needs
- Your foreground service keeps the app process alive when needed
- Persistent notification informs user that Flic buttons are connected
- FlicManager continues to handle button events in the background
- Your service processes events and triggers appropriate actions
- Service automatically restarts if killed by the system
- Your
FlicBootReceiverreceivesBOOT_COMPLETEDbroadcast - Receiver starts your background service automatically
- Service reinitializes FlicManager and reconnects to buttons
- Background operation resumes automatically
To handle button events in your background service, modify the OnFlicButtonEvent method in your FlicBackgroundService.cs:
private void OnFlicButtonEvent(FlicButton button, FlicButtonEvent buttonEvent)
{
// Handle different button actions
switch (buttonEvent)
{
case FlicButtonEvent.Click:
// Single click action
ShowNotification($"Button {button.Name} clicked!");
break;
case FlicButtonEvent.DoubleClick:
// Double click action - launch app to specific page
LaunchAppToPage("MainPage");
break;
case FlicButtonEvent.Hold:
// Long press action
TriggerCustomAction(button);
break;
}
}
private void ShowNotification(string message)
{
var notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.SetContentTitle("Flic Button")
.SetContentText(message)
.SetSmallIcon(Resource.Drawable.notification_icon)
.SetAutoCancel(true)
.Build();
var notificationManager = NotificationManagerCompat.From(this);
notificationManager.Notify(Random.Shared.Next(), notification);
}
private void LaunchAppToPage(string pageName)
{
var intent = new Intent(this, typeof(MainActivity));
intent.PutExtra("page", pageName);
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.ClearTop);
StartActivity(intent);
}Your implementation should handle different Android versions:
- Android 8.0+ (API 26+): Use
StartForegroundService()for proper foreground service handling - Android 7.1 and below (API 25-): Fall back to
StartService()for compatibility - All versions: Include proper API level checks to prevent crashes
Example in your MainActivity:
var serviceIntent = new Intent(this, typeof(FlicBackgroundService));
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
StartForegroundService(serviceIntent);
else
StartService(serviceIntent);-
Service not starting:
- Ensure all permissions are declared in AndroidManifest.xml
- Check that service is properly declared with correct class name
- Verify device isn't in aggressive battery optimization mode
-
Buttons not responding:
- Check that Bluetooth is enabled and buttons are paired through your main app first
- Verify FlicManager is properly initialized in the service
- Check device logs for Bluetooth-related errors
-
Service stops unexpectedly:
- Make sure notification channel is created before starting foreground service
- Use
StartCommandResult.Stickyto automatically restart if killed - Handle service lifecycle properly (OnStartCommand, OnDestroy)
-
Boot receiver not working:
- Ensure
RECEIVE_BOOT_COMPLETEDpermission is granted - Check that receiver is properly declared in manifest
- Verify device allows apps to start at boot (some manufacturers disable this)
- Ensure
Ensure these permissions are in your AndroidManifest.xml:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />- Test thoroughly: Test your background service on different Android versions and manufacturers
- Handle errors gracefully: Include proper error handling in your service implementation
- Minimize battery usage: Only keep the service running when Flic buttons are actively needed
- User communication: Use clear notifications to inform users when background services are active
- Regular updates: Keep your implementation up to date with Android best practices and library updates
- Sample implementation:
flic2lib.Maui.Sampleproject - Android foreground services: Android Developer Documentation
- Background execution limits: Android Background Execution Limits
- Cross-platform background guide: Cross Platform Background Execution
- Call KeepFlicButtonsConnected early: Call it in MainActivity.OnCreate() for immediate background support
- Trust the library: The library handles all Android version compatibility automatically
- Keep manifest updated: Always include the required permissions and service declarations
- Monitor library updates: Background execution improvements come with library updates
- User transparency: The persistent notification keeps users informed automatically
This library-level implementation ensures reliable background operation while following Android's best practices and providing the easiest possible integration for developers.