Created
June 28, 2022 21:34
-
-
Save KONFeature/2f84436e1c0a1926505cac934d470f90 to your computer and use it in GitHub Desktop.
Revisions
-
KONFeature created this gist
Jun 28, 2022 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,105 @@ import android.content.Intent import android.graphics.PixelFormat import android.os.IBinder import android.view.Gravity import android.view.WindowManager import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.pointer.consumeAllChanges import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.ComposeView import androidx.lifecycle.ViewTreeLifecycleOwner import androidx.lifecycle.ViewTreeViewModelStoreOwner import androidx.savedstate.setViewTreeSavedStateRegistryOwner import kotlin.math.roundToInt /** * Service that is ready to display compose overlay view * @author Quentin Nivelais */ abstract class ComposeOverlayViewService : ViewReadyService() { // Build the layout param for our popup private val layoutParams by lazy { WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT ).apply { gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL } } // The current offset of our overlay composable private var overlayOffset by mutableStateOf(Offset.Zero) // Access our window manager private val windowManager by lazy { overlayContext.getSystemService(WindowManager::class.java) } // Build our compose view private val composeView by lazy { ComposeView(overlayContext) } override fun onBind(intent: Intent): IBinder? = null override fun onCreate() { super.onCreate() // Bound the compose lifecycle, view model and view tree saved state, into our view service ViewTreeLifecycleOwner.set(composeView, this) ViewTreeViewModelStoreOwner.set(composeView) { viewModelStore } composeView.setViewTreeSavedStateRegistryOwner(this) // Set the content of our compose view composeView.setContent { Content() } // Push the compose view into our window manager windowManager.addView(composeView, layoutParams) } override fun onDestroy() { super.onDestroy() // Remove our compose view from the window manager windowManager.removeView(composeView) } @Composable abstract fun Content() /** * Draggable box container (not used by default, since not every overlay should be draggable) */ @Composable internal fun OverlayDraggableContainer(modifier: Modifier = Modifier, content: @Composable BoxScope.() -> Unit) = Box( modifier = modifier.pointerInput(Unit) { detectDragGestures { change, dragAmount -> change.consumeAllChanges() // Update our current offset val newOffset = overlayOffset + dragAmount overlayOffset = newOffset // Update the layout params, and then the view layoutParams.apply { x = overlayOffset.x.roundToInt() y = overlayOffset.y.roundToInt() } windowManager.updateViewLayout(composeView, layoutParams) } }, content = content ) } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,9 @@ class MyComposeOverlayService : ComposeOverlayViewService() { override fun onBind(intent: Intent): IBinder? = null @Composable override fun Content() = OverlayDraggableContainer { Text("My super component") } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,57 @@ import android.content.Context import android.hardware.display.DisplayManager import android.view.Display import android.view.WindowManager import androidx.lifecycle.LifecycleService import androidx.lifecycle.ViewModelStore import androidx.lifecycle.ViewModelStoreOwner import androidx.savedstate.SavedStateRegistry import androidx.savedstate.SavedStateRegistryController import androidx.savedstate.SavedStateRegistryOwner /** * Service that is ready to display view, provide a ui context on the primary screen, and all the tools needed to built a view with state managment, view model etc * @author Quentin Nivelais */ abstract class ViewReadyService : LifecycleService(), SavedStateRegistryOwner, ViewModelStoreOwner { /** * Build our saved state registry controller */ private val savedStateRegistryController: SavedStateRegistryController by lazy(LazyThreadSafetyMode.NONE) { SavedStateRegistryController.create(this) } /** * Build our view model store */ private val internalViewModelStore: ViewModelStore by lazy { ViewModelStore() } /** * Context dedicated to the view */ internal val overlayContext: Context by lazy { // Get the default display val defaultDisplay: Display = getSystemService(DisplayManager::class.java).getDisplay(Display.DEFAULT_DISPLAY) // Create a display context, and then the window context createDisplayContext(defaultDisplay) .createWindowContext(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, null) } override fun onCreate() { super.onCreate() // Restore the last saved state registry savedStateRegistryController.performRestore(null) } override fun onDestroy() { super.onDestroy() } override val savedStateRegistry: SavedStateRegistry get() = savedStateRegistryController.savedStateRegistry override fun getViewModelStore(): ViewModelStore = internalViewModelStore }