#6 Floating Windows on Android: Keyboard Input
Tags: android, kotlin, tutorial, learning
Published: 2020-10-14 12:29:29

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 5: Moving Window](https://localazy.com/blog/floating-windows-on-android-5-moving-window).

**In this article, I will teach you how to allow keyboard input in the floating window.**

## Almost There

In the previous articles, we created the main app, foreground service, and the floating window. We can even move the window around the screen. However, it’s still not possible to use it as the soft keyboard is not triggered, so it’s impossible to write any note.

Let's solve this last issue so we can wrap everything nicely together to get the fully functional app in the next articles. 

## What's The Problem? 

You may remember that in the fourth article about the floating window, we add a specific flag to `LayoutParams` for our window:

- `FLAG_NOT_FOCUSABLE` - The window won't ever get key input focus, so the user can not send key or other button events to it. Those will instead go to whatever focusable window is behind it.

The description is pretty clear. When this flag is set, our window is not going to receive any keyboard inputs. However, when this flag is not set, the floating window interfere with the apps behind it, and make it impossible to use keyboard for them. 

## The Solution

Obviously, the solution is not that hard to find. We just need to enable and disable this flag at the right time. 

Changing the flag is a pretty simple task. Here it goes: 

```kotlin
private fun enableKeyboard() {  
  if (windowParams.flags and WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE != 0) {  
    windowParams.flags = windowParams.flags and WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE.inv()  
    update()  
  }  
}  
  
private fun disableKeyboard() {  
  if (windowParams.flags and WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE == 0) {  
    windowParams.flags = windowParams.flags or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE  
    update()  
  }  
}
```

## When Enable/Disable?

The logic is straightforward. When the window is activated  - the user clicks on it - we should enable the keyboard input. When the user clicks outside of the window, we should disable it. And that's it. 

## Universal Solution

For a universal solution across different window layouts, we subclass `LinearLayout` to create a container for our window. 

Fortunately, we set another flag to `LayoutParams`:

- `FLAG_WATCH_OUTSIDE_TOUCH` - Receive events for touches that occur outside of your window. 

So, with a custom `ViewContainer` we can monitor clicks both inside and outside the window. The full source code for such container is here: 

```kotlin
class WindowContentLayout : LinearLayout {  
  
  constructor(context: Context) : super(context)  
  constructor(context: Context, attrs: AttributeSet) : super(context, attrs)  
  constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)  
  
  private var listener: ((activate: Boolean) -> Unit)? = null  
  
  fun setListener(listener: (activate: Boolean) -> Unit) {  
    this.listener = listener  
  }  
  
  override fun onTouchEvent(event: MotionEvent): Boolean {  
    if (event.action == MotionEvent.ACTION_DOWN) listener?.invoke(true)  
    if (event.action == MotionEvent.ACTION_OUTSIDE) listener?.invoke(false)  
    return super.onTouchEvent(event)  
  }  
  
  override fun onInterceptTouchEvent(event: MotionEvent): Boolean  {  
    if (event.action == MotionEvent.ACTION_DOWN) listener?.invoke(true)  
    if (event.action == MotionEvent.ACTION_OUTSIDE) listener?.invoke(false)  
    return super.onInterceptTouchEvent(event)  
  }  
  
}
```

Now, we have to use our subclassed `LinearLayout` in the `window.xml` - the layout for our window:

```xml
<com.localazy.quicknote.windows.WindowContentLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:weightSum="1">

    <!-- The content of the window omitted for brevity. -->

</com.localazy.quicknote.windows.WindowContentLayout>
```

And as we know that the root element is always our `WindowContentLayout`, we can change the line where `rootView` is inflated to:

```kotlin
private val rootView = layoutInflater.inflate(R.layout.window, null) as WindowContentLayout
```

With `rootView` being `WindowContentLayout`, we can react to the events and enable/disable the soft keyboard support as necessary: 

```kotlin
rootView.setListener {  
  if (it) {  
    enableKeyboard()  
  } else {  
    disableKeyboard()  
  }  
}
```

All events sent to the window are intercepted, and so it's a fully universal solution working for all windows with any content. 

## Results

Smooth! We can use the keyboard both for the normal app running behind our window as well as in our floating note. 

{% youtube iHNyBH_XhkU %}

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

## 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)