#2 Floating Windows on Android: Foreground Service
Tags: android, kotlin, service, floating
Published: 2020-09-28 08:58:38

Have you ever wondered how to make those floating windows used by Facebook Heads and other apps? Have you ever wanted to use the same technology in your app? It’s easy, and I will guide you through the whole process.

I'm the author of [Floating Apps](https://floatingapps.net); the first app of its kind on Google Play and the most popular one with over 8 million downloads. After 6 years of the development of the app, I know a bit about it. It’s sometimes tricky, and I spent months reading documentation and Android source code and experimenting. I received feedback from tens of thousands of users and see various issues on different phones with different Android versions.

Here's what I learned along the way. 

Before reading this article, it's recommended to go through [Floating Windows on Android 1: Jetpack Compose & Room](https://localazy.com/blog/floating-windows-on-android-1-jetpack-compose-and-room).

**In this article, I teach you how to build the long-running foreground service that is necessary for floating windows and what are the limitations.**

## The Service

For the floating technology, it's necessary to have [Service](https://developer.android.com/guide/components/services) and not [Activity](https://developer.android.com/guide/components/activities/intro-activities). Android can have only one `Activity` in the foreground and so if we use `Activity`, other apps would be paused or restarted. And that's not the desired behavior - we want not to interrupt the current task in any way. 

Standard Android services are not designed for long-running operations. They are rather designed to do a task in the background and finish. 

To avoid our `Service` from being killed by the Android system, it's better to use a foreground service. 

For our specific simple app, we use a service that is always running and renders a permanent notification. For your app, having the service running only when there are some floating windows active may be a better approach.

The magic is hidden in the overriding `onStartCommand` method and returning `START_STICKY` and `START_NOT_STICKY` correctly. The source code for this is shown below in this article. 


## The Limitations

### Show Notification

A foreground service must show permanent/foreground notification shortly after the service is launched. If we fail to do so, the app is terminated.

On some devices, this may cause occasional crashes as the process may take a bit longer than the hard-coded interval.

Be sure to show the foreground notification as the first thing. The source code for this is shown below in this article.

Also, some users simply dislike the permanent notification being shown, but there is a little we can do about it. They can, on some devices, hide the notification in the phone’s Settings.

### Killed On Some Devices
On some phones and tablets, it's impossible to avoid the services from being killed thanks to the vendors who are integrating aggressive memory and process management. 

There is an excellent website on this topic: [Don't kill my app!](https://dontkillmyapp.com/)


## The Permission 

From Android API level 28, extra permission is necessary for foreground services. Add the line below to your `AndroidManifest.xml`:

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

## The Notification

From Android O, a permanent notification is required, and with all the experience, I would recommend to use it for older versions too to prevent the service from being killed by Android. 

The full source code for the foreground notification, including the code for stopping the service:

```kotlin
/**  
 * Remove the foreground notification and stop the service. 
 */
private fun stopService() {  
  stopForeground(true)  
  stopSelf()  
}

/**
 * Create and show the foreground notification.
 */
private fun showNotification() {

  val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

  val exitIntent = Intent(this, FloatingService::class.java).apply {
    putExtra(INTENT_COMMAND, INTENT_COMMAND_EXIT)
  }

  val noteIntent = Intent(this, FloatingService::class.java).apply {
    putExtra(INTENT_COMMAND, INTENT_COMMAND_NOTE)
  }

  val exitPendingIntent = PendingIntent.getService(
    this, CODE_EXIT_INTENT, exitIntent, 0
  )

  val notePendingIntent = PendingIntent.getService(
    this, CODE_NOTE_INTENT, noteIntent, 0
  )

  // From Android O, it's necessary to create a notification channel first.
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    try {
      with(
        NotificationChannel(
          NOTIFICATION_CHANNEL_GENERAL,
          getString(R.string.notification_channel_general),
          NotificationManager.IMPORTANCE_DEFAULT
        )
      ) {
        enableLights(false)
        setShowBadge(false)
        enableVibration(false)
        setSound(null, null)
        lockscreenVisibility = Notification.VISIBILITY_PUBLIC
        manager.createNotificationChannel(this)
      }
    } catch (ignored: Exception) {
      // Ignore exception.
    }
  }

  with(
    NotificationCompat.Builder(
      this,
      NOTIFICATION_CHANNEL_GENERAL
    )
  ) {
    setTicker(null)
    setContentTitle(getString(R.string.app_name))
    setContentText(getString(R.string.notification_text))
    setAutoCancel(false)
    setOngoing(true)
    setWhen(System.currentTimeMillis())
    setSmallIcon(R.drawable.ic_launcher_foreground)
    priority = Notification.PRIORITY_DEFAULT
    setContentIntent(notePendingIntent)
    addAction(
      NotificationCompat.Action(
        0,
        getString(R.string.notification_exit),
        exitPendingIntent
      )
    )
    startForeground(CODE_FOREGROUND_SERVICE, build())
  }
    
}
```
Our notification is permanent and cannot be cancelled. It's clickable and when clicked, it invokes **INTENT_COMMAND_NOTE** command. Also, the notification has the exit action to invoke **INTENT_COMMAND_EXIT**. 

## OnStartCommand

As mentioned above, the magic behavior of the service is hidden inside `onStartCommand`. It's simple:

```kotlin
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {

  val command = intent.getStringExtra(INTENT_COMMAND) ?: ""

  // Exit the service if we receive the EXIT command.
  // START_NOT_STICKY is important here, we don't want 
  // the service to be relaunched. 
  if (command == INTENT_COMMAND_EXIT) {
    stopService()
    return START_NOT_STICKY
  }

  // Be sure to show the notification first for all commands.
  // Don't worry, repeated calls have no effects.
  showNotification()

  // Show the floating window for adding a new note.
  if (command == INTENT_COMMAND_NOTE) {
    Toast.makeText(
      this, 
      "Floating window to be added in the next lessons.", 
      Toast.LENGTH_SHORT
    ).show()
  }
    
  return START_STICKY
}
```

## Service & AndroidManifest

For our service, we need a record in `AndroidManifest.xml` file. 

```kotlin
<service  
  android:name=".FloatingService"  
  android:excludeFromRecents="true"  
  android:exported="false"  
  android:label="@string/app_name"  
  android:roundIcon="@mipmap/ic_launcher_round"
  android:stopWithTask="false" />
```
**Explanation of parameters above:**
- `android:excludeFromRecents` - Don't show the service in *Recent items* screen.
- `android:exported` - There is no reason for the service to be accessible from outside the app.
- `android:stopWithTask` - Don't stop the service when the app is terminated, e.g. swiped out of the *Recent items* screen.

## Start Service

For starting the service, let's create a small helper method. We need to handle the requirement for Android O and above - to use `startForegroundService` instead of  `startService`. 

```kotlin
fun Context.startFloatingService(command: String = "") {  

  val intent = Intent(this, FloatingService::class.java)  
  if (command.isNotBlank()) {
    intent.putExtra(INTENT_COMMAND, command)  
  }
  
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {  
    this.startForegroundService(intent)  
  } else {  
    this.startService(intent)  
  }  
  
}
```

In one of the following articles, we will learn how to start our service when the device boot, but for the time being, let's stick with starting the service when the main app is launched. For this, we just add a single line to our existing `MainActivity`'s `onCreate` method. 

```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)

  // Start the foreground service.
  startFloatingService()

  // ... the rest of the code ...
  
}
```

## Multi Process Approach

If your floating service is heavy and may cause occasional crashes, you may want to separate it from your app.

You can use `android:multiprocess` in your `AndroidManifest.xml` and separate your activities from the service by running them in a different process.

However, keep in mind that using more processes involves extra effort for synchronizing state as activities, and the service would no longer share memory.

## Localization

Above, we added new strings `notification_channel_general`, `notification_text` and `notification_exit`, so be sure to run the Gradle task `uploadStrings` to upload your translations to the [Localazy platform](https://localazy.com) for translating.

**A minute after I uploaded my 11 strings, 6 of them are ready in 80 languages!**

More information on the importance of app localization was described in [Floating Windows on Android 1: Jetpack Compose & Room](https://localazy.com/blog/floating-windows-on-android-1-jetpack-compose-and-room).

## Results

The animation below demonstrates how the permanent notification is shown when the main app is launched and remains active even if the main app is removed from the `Recent items` screen. 

{% youtube 7YpkNxBlqLI %}

## Source Code
The whole source code for this article is [available on Github](https://github.com/vaclavhodek/quicknote_2).

## Stay Tuned

Eager to learn more about Android development? Follow me (<a href="https://twitter.com/vaclavhodek">@vaclavhodek</a>) and Localazy (<a href="https://twitter.com/localazy">@localazy</a>) on Twitter, or like <a href="https://www.facebook.com/localazy">Localazy on Facebook</a>.

## The Series

This article is part of the **Floating Windows on Android** series. 

- [Floating Windows on Android 1: Jetpack Compose & Room](https://localazy.com/blog/floating-windows-on-android-1-jetpack-compose-and-room)
- [Floating Windows on Android 2: Foreground Service](https://localazy.com/blog/floating-windows-on-android-2-foreground-service)
- [Floating Windows on Android 3: Permissions](https://localazy.com/blog/floating-windows-on-android-3-permissions)
- [Floating Windows on Android 4: Floating Window](https://localazy.com/blog/floating-windows-on-android-4-floating-window)
- [Floating Windows on Android 5: Moving Window](https://localazy.com/blog/floating-windows-on-android-5-moving-window)
- [Floating Windows on Android 6: Keyboard Input](https://localazy.com/blog/floating-windows-on-android-6-keyboard-input)
- [Floating Windows on Android 7: Boot Receiver](https://localazy.com/blog/floating-windows-on-android-7-boot-receiver)
- [Floating Windows on Android 8: The Final App](https://localazy.com/blog/floating-windows-on-android-8-the-final-app)
- [Floating Windows on Android 9: Shortcomings](https://localazy.com/blog/floating-windows-on-android-9-shortcomings)
- [Floating Windows on Android 10: Tips & Tricks](https://localazy.com/blog/floating-windows-on-android-10-tips-and-tricks)