-
-
Save handstandsam/6ecff2f39da72c0b38c07aa80bbb5a2f to your computer and use it in GitHub Desktop.
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) | |
} | |
} |
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 | |
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 | |
} | |
} |
val viewModelStore = ViewModelStore()
val viewModelStoreOwner = object : ViewModelStoreOwner {
override val viewModelStore: ViewModelStore
get() = viewModelStore
}
Does anyone else get the error Class 'MyLifecycleOwner' is not abstract and does not implement abstract member public abstract val lifecycle: Lifecycle defined in androidx.savedstate.SavedStateRegistryOwner
with lifecycle version 2.6.1?
Does anyone else get the error
Class 'MyLifecycleOwner' is not abstract and does not implement abstract member public abstract val lifecycle: Lifecycle defined in androidx.savedstate.SavedStateRegistryOwner
with lifecycle version 2.6.1?
I'm having the same issue
Does anyone else get the error Class 'MyLifecycleOwner' is not abstract and does not implement abstract member public abstract val lifecycle: Lifecycle defined in androidx.savedstate.SavedStateRegistryOwner with lifecycle version 2.6.1?
I am facing the same problem with version 2.5.1. However, I have managed to resolve it by overriding the lifecycle
as a variable instead of a function. Although the IDE is currently displaying an error ("lifecycle" overrides nothing), but the project can still be compiled and built without any issues.
override val lifecycle: Lifecycle get() = lifecycleRegistry
I have found a fix to the problem. Removing FLAG_NOT_TOUCHABLE
and setting alpha
of params
to 1f
did the job. Now the button or any other views/composables are receiving click events and also the transparency is removed. Also, Thanks to @tberghuis for his excellent Floating Timer open-source code.
Does anyone else get the error Class 'MyLifecycleOwner' is not abstract and does not implement abstract member public abstract val lifecycle: Lifecycle defined in androidx.savedstate.SavedStateRegistryOwner with lifecycle version 2.6.1?
I am facing the same problem with version 2.5.1. However, I have managed to resolve it by overriding the
lifecycle
as a variable instead of a function. Although the IDE is currently displaying an error ("lifecycle" overrides nothing), but the project can still be compiled and built without any issues.
override val lifecycle: Lifecycle get() = lifecycleRegistry
I also ran into this problem. Follow @AhmedMousa7 's method to solve this problem,
Android Studio prompts
Class 'LifecycleOwner' is not abstract and does not implement abstract member public abstract fun getLifecycle(): Lifecycle defined in androidx.savedstate.SavedStateRegistryOwner
'lifecycle' overrides nothing
but gradle compiles and runs without problems.
I found that the version of LifecycleOwner
inherited by SavedStateRegistryOwner
is wrong. The dependency it uses is androidx.lifecycle:lifecycle-common:2.0.0
, not the expected 2.6.1
. I suspect this is the reason.
Anyone else also using androidx.lifecycle:lifecycle-common:2.0.0
?
There is an open source library to create overlay view with jetpackcompose
https://github.com/KuhakuPixel/UberAlles/
(DISCLAIMER: I am the author and still in early stage of development, would appreciate your feedback and fix :D )
@wilinz @FreePhoenix888 I fixed the ComposeView not recomposing by, in addition to lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
, adding
lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_START)
lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
Reached the same conclusions on some of the comments, specially the updated APIs and the requirement to trigger the START and RESUME lifecycle events so recomposition worked. I found the snippet here, that's why I was unable to find the updates suggested by commenters: https://www.jetpackcompose.app/snippets/OverlayService See my code here: https://github.com/xrubioj/JetpackComposeOverlayTest
Does anyone know how to handle on back button pressed in this service to close the service/remove the overlay when the back button is pressed?
give permission in manifest file::
permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
in MainActivity code is ::
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (!Settings.canDrawOverlays(this)) {
val intent = Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:$packageName")
)
startActivityForResult(intent, 101)
}
setContent {
TikDownloaderTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
val context = LocalContext.current
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
AddFloatingButtonOverlay(windowManager)
}
}
}
}
}
@composable
fun AddFloatingButtonOverlay(windowManager: WindowManager) {
val context = LocalContext.current
val composeView = remember {
ComposeView(context).apply {
setContent {
Box {
FloatingButton(onClick = { })
}
}
}
}
// Set layout parameters for the floating button
val params = WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
android.graphics.PixelFormat.TRANSLUCENT
)
// Set the position of the floating button on the screen
params.x = 100 // Set the X position
params.y = 100 // Set the Y position
composeView.setViewTreeLifecycleOwner(LocalLifecycleOwner.current)
composeView.setViewTreeSavedStateRegistryOwner(LocalSavedStateRegistryOwner.current)
windowManager.addView(composeView, params)
}
@composable
fun FloatingButton(onClick: () -> Unit) {
FloatingActionButton(
onClick = onClick,
modifier = androidx.compose.ui.Modifier.padding(16.dp)
) {
Text(text = "Floating Overlay")
}
}
Thanks for this, I used this code to create a floating timer https://github.com/tberghuis/FloatingCountdownTimer
HI ! thanks a lot for your beautiful project that helped me to understand some things. the readme and the documentation are quite short. So as the project has a lot of dependency injections it took me a while to understand the overlay mechanism. Do you have a site or a place you posted a kind of step by step documentation build or something else ?
@GC-public-projects there is no documentation as my implementation is hacked together using this gist and some stackoverflow answers.
I think Google should provide an official sample implementation now that XML views are no longer encouraged.
give permission in manifest file::
permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
in MainActivity code is ::
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (!Settings.canDrawOverlays(this)) { val intent = Intent( Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName") ) startActivityForResult(intent, 101) } setContent { TikDownloaderTheme { // A surface container using the 'background' color from the theme Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { val context = LocalContext.current val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager AddFloatingButtonOverlay(windowManager) } } } }
}
@composable fun AddFloatingButtonOverlay(windowManager: WindowManager) { val context = LocalContext.current val composeView = remember { ComposeView(context).apply { setContent { Box { FloatingButton(onClick = { }) } } } }
// Set layout parameters for the floating button val params = WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, android.graphics.PixelFormat.TRANSLUCENT ) // Set the position of the floating button on the screen params.x = 100 // Set the X position params.y = 100 // Set the Y position composeView.setViewTreeLifecycleOwner(LocalLifecycleOwner.current) composeView.setViewTreeSavedStateRegistryOwner(LocalSavedStateRegistryOwner.current) windowManager.addView(composeView, params)
}
@composable fun FloatingButton(onClick: () -> Unit) { FloatingActionButton( onClick = onClick, modifier = androidx.compose.ui.Modifier.padding(16.dp) ) { Text(text = "Floating Overlay") } }
This solution works for me, thanks a lot. Saved me two days.
thank you but how i can get viewModelStoreOwner
val viewModelStoreOwner = ViewModelStoreOwner { viewModelStore } not working now