Skip to content

Instantly share code, notes, and snippets.

@handstandsam
Last active June 26, 2025 11:10
Show Gist options
  • Save handstandsam/6ecff2f39da72c0b38c07aa80bbb5a2f to your computer and use it in GitHub Desktop.
Save handstandsam/6ecff2f39da72c0b38c07aa80bbb5a2f to your computer and use it in GitHub Desktop.

Revisions

  1. handstandsam revised this gist Jan 20, 2021. 1 changed file with 0 additions and 25 deletions.
    25 changes: 0 additions & 25 deletions OverlayService.kt
    Original file line number Diff line number Diff line change
    @@ -26,31 +26,6 @@ class OverlayService : Service() {

    val windowManager get() = getSystemService(WINDOW_SERVICE) as WindowManager

    fun show1() {
    val dialog: AlertDialog = AlertDialog.Builder(this)
    .setTitle("Title")
    .create()
    val dialogWindow: Window = dialog.window!!
    val dialogWindowAttributes: WindowManager.LayoutParams = dialogWindow.getAttributes()

    // Set fixed width (280dp) and WRAP_CONTENT height
    val lp = WindowManager.LayoutParams()
    lp.copyFrom(dialogWindowAttributes)
    lp.width =
    TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 280f, resources.displayMetrics).toInt()
    lp.height = WindowManager.LayoutParams.WRAP_CONTENT
    dialogWindow.attributes = lp

    // Set to TYPE_SYSTEM_ALERT so that the Service can display it
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    dialogWindow.setType(WindowManager.LayoutParams.TYPE_TOAST)
    }
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    dialogWindow.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY)
    }
    dialog.show()
    }

    override fun onCreate() {
    super.onCreate()
    setTheme(R.style.ThemeOverlay_AppCompat_Light)
  2. handstandsam created this gist Jan 20, 2021.
    41 changes: 41 additions & 0 deletions MyLifecycleOwner.kt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,41 @@
    import android.os.Bundle
    import androidx.lifecycle.Lifecycle
    import androidx.lifecycle.LifecycleRegistry
    import androidx.savedstate.SavedStateRegistry
    import androidx.savedstate.SavedStateRegistryController
    import androidx.savedstate.SavedStateRegistryOwner

    internal class MyLifecycleOwner : SavedStateRegistryOwner {
    private var mLifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)
    private var mSavedStateRegistryController: SavedStateRegistryController = SavedStateRegistryController.create(this)

    /**
    * @return True if the Lifecycle has been initialized.
    */
    val isInitialized: Boolean
    get() = true

    override fun getLifecycle(): Lifecycle {
    return mLifecycleRegistry
    }

    fun setCurrentState(state: Lifecycle.State) {
    mLifecycleRegistry.currentState = state
    }

    fun handleLifecycleEvent(event: Lifecycle.Event) {
    mLifecycleRegistry.handleLifecycleEvent(event)
    }

    override fun getSavedStateRegistry(): SavedStateRegistry {
    return mSavedStateRegistryController.savedStateRegistry
    }

    fun performRestore(savedState: Bundle?) {
    mSavedStateRegistryController.performRestore(savedState)
    }

    fun performSave(outBundle: Bundle) {
    mSavedStateRegistryController.performSave(outBundle)
    }
    }
    102 changes: 102 additions & 0 deletions OverlayService.kt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,102 @@
    import android.app.AlertDialog
    import android.app.Service
    import android.content.Intent
    import android.graphics.PixelFormat
    import android.os.Build
    import android.os.IBinder
    import android.util.TypedValue
    import android.view.Window
    import android.view.WindowManager
    import androidx.compose.foundation.background
    import androidx.compose.foundation.layout.wrapContentSize
    import androidx.compose.material.Text
    import androidx.compose.ui.Modifier
    import androidx.compose.ui.graphics.Color
    import androidx.compose.ui.platform.ComposeView
    import androidx.compose.ui.unit.sp
    import androidx.lifecycle.Lifecycle
    import androidx.lifecycle.ViewModelStore
    import androidx.lifecycle.ViewTreeLifecycleOwner
    import androidx.lifecycle.ViewTreeViewModelStoreOwner
    import androidx.savedstate.ViewTreeSavedStateRegistryOwner
    import com.viatek.fitnation.echelon_android.R


    class OverlayService : Service() {

    val windowManager get() = getSystemService(WINDOW_SERVICE) as WindowManager

    fun show1() {
    val dialog: AlertDialog = AlertDialog.Builder(this)
    .setTitle("Title")
    .create()
    val dialogWindow: Window = dialog.window!!
    val dialogWindowAttributes: WindowManager.LayoutParams = dialogWindow.getAttributes()

    // Set fixed width (280dp) and WRAP_CONTENT height
    val lp = WindowManager.LayoutParams()
    lp.copyFrom(dialogWindowAttributes)
    lp.width =
    TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 280f, resources.displayMetrics).toInt()
    lp.height = WindowManager.LayoutParams.WRAP_CONTENT
    dialogWindow.attributes = lp

    // Set to TYPE_SYSTEM_ALERT so that the Service can display it
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    dialogWindow.setType(WindowManager.LayoutParams.TYPE_TOAST)
    }
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    dialogWindow.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY)
    }
    dialog.show()
    }

    override fun onCreate() {
    super.onCreate()
    setTheme(R.style.ThemeOverlay_AppCompat_Light)
    showOverlay()
    }

    private fun showOverlay() {
    val layoutFlag: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
    } else {
    WindowManager.LayoutParams.TYPE_PHONE
    }

    val params = WindowManager.LayoutParams(
    WindowManager.LayoutParams.WRAP_CONTENT,
    WindowManager.LayoutParams.WRAP_CONTENT,
    layoutFlag,
    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
    PixelFormat.TRANSLUCENT
    )

    val composeView = ComposeView(this)
    composeView.setContent {
    Text(
    text = "Hello",
    color = Color.Black,
    fontSize = 50.sp,
    modifier = Modifier
    .wrapContentSize()
    .background(Color.Green)
    )
    }

    // Trick The ComposeView into thinking we are tracking lifecycle
    val viewModelStore = ViewModelStore()
    val lifecycleOwner = MyLifecycleOwner()
    lifecycleOwner.performRestore(null)
    lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
    ViewTreeLifecycleOwner.set(composeView, lifecycleOwner)
    ViewTreeViewModelStoreOwner.set(composeView) { viewModelStore }
    ViewTreeSavedStateRegistryOwner.set(composeView, lifecycleOwner)

    windowManager.addView(composeView, params)
    }

    override fun onBind(intent: Intent): IBinder? {
    return null
    }
    }