Stylianos Gakis
03/11/2022, 11:32 AM@Composable
fun AwakeScreen(content: @Composable () -> Unit) {
val window: Window? = null// can I get access to the current window here? Or some way to get the hosting activity which holds the window?
DisposableEffect(Unit) {
window!!.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
onDispose {
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
}
content()
}
But I am not sure we have access to this inside our composables. Would I have to somehow hoist this all the way up to the hosting activity? Anything smarter I should be able to do perhaps?@Composable
fun AwakeScreen(content: @Composable () -> Unit) {
val context = LocalContext.current
DisposableEffect(Unit) {
val powerManager = context.getSystemService(PowerManager::class.java)
val wakeLock = powerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "myapp:wakelock")
wakeLock.acquire(10.minutes.inWholeMilliseconds)
onDispose {
wakeLock.release()
}
}
content()
}
However PowerManager.SCREEN_DIM_WAKE_LOCK
is deprecated suggesting to use the FLAG_KEEP_SCREEN_ON
on the window as I said before. Can’t find a way to do that so will use the deprecated option for now 😅Albert Chang
03/11/2022, 12:16 PMStylianos Gakis
03/11/2022, 12:22 PM@Composable
fun AwakeScreen(content: @Composable () -> Unit) {
val context = LocalContext.current
DisposableEffect(Unit) {
val window = context.findWindow()
window!!.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
onDispose {
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
}
content()
}
private fun Context.findWindow(): Window? {
var context = this
while (context is ContextWrapper) {
if (context is Activity) return context.window
context = context.baseContext
}
return null
}
Now I gotta do a !!
or at least a ?
on the window though, which either way doesn’t make me feel that great. I guess accessing it like this from a composable has no way of failing since it will always find the activity though right?
I’d like to at least document that in that function if so, so my colleagues don’t need to worry about this not working.LocalContext.current.findWindow()
and LocalView.current.context.findWindow()
should probably be equivalent since this is how they’re provided so that’s not important I assumeAlbert Chang
03/11/2022, 12:44 PMLocalView
(in a library) is safer as some people are overriding the context with their own ones (e.g. to use a locale different from the default one).Stylianos Gakis
03/11/2022, 12:46 PMAdam Powell
03/11/2022, 3:41 PMprivate val View.keepScreenOnState: KeepScreenOnState
get() = getTag(R.id.keep_screen_on_state) as? KeepScreenOnState
?: KeepScreenOnState(this).also { setTag(R.id.keep_screen_on_state, it) }
private class KeepScreenOnState(private val view: View) {
private var refCount = 0
set(value) {
val newValue = value.coerceAtLeast(0)
field = newValue
view.keepScreenOn = newValue > 0
}
fun request() {
refCount++
}
fun release() {
refCount--
}
}
@Composable
fun KeepScreenOnRequest() {
val view = LocalView.current
DisposableEffect(view) {
val ksoState = view.keepScreenOnState
ksoState.request()
onDispose {
ksoState.release()
}
}
}
and then declare the resource id keep_screen_on_state
in an ids.xml
resource file so that the R
constant used above is thereandroid:keepScreenOn="true"
approach noted in the docs you linked above; that's the view property mentionedStylianos Gakis
03/11/2022, 4:44 PMAdam Powell
03/11/2022, 4:46 PMStylianos Gakis
03/11/2022, 4:53 PMDejan Predovic
03/11/2022, 6:42 PM@Composable
fun KeepScreenOn() {
val activity = LocalContext.current as Activity
DisposableEffect(Unit) {
activity.window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
onDispose { activity.window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) }
}
}
Works perfectly for meAdam Powell
03/12/2022, 3:17 AMDejan Predovic
03/12/2022, 1:16 PMval keepScreenOnCounter = AtomicInteger(0)
@Composable
fun KeepScreenOn() {
val activity = LocalContext.current as Activity
DisposableEffect(Unit) {
val counter = keepScreenOnCounter.incrementAndGet()
if (counter == 1) activity.window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
onDispose {
val counter = keepScreenOnCounter.decrementAndGet()
if (counter == 0) activity.window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
}
}
Adam Powell
03/12/2022, 2:16 PMStylianos Gakis
03/14/2022, 9:42 AM.keepScreenOn
in the modifier chain of a composable I think it’d feel more natural.fun Modifier.keepScreenOn() = composed {
KeepScreenOnRequest()
this
}
Can anyone confirm that this is/isn’t fine?Not sure if I am breaking some Modifier rule though 😅 Can we just have whatever composables as part of a composed
modifier? Even ones that emit UI? Is there some part of the documentation that talks more about all this?
This and this both don’t mention this, making me think this isn’t what I want to do for some reason?Albert Chang
03/14/2022, 11:47 AMStylianos Gakis
03/14/2022, 12:04 PMonGloballyPositioned
or onKeyEvent
which alters the keyboard behavior.Albert Chang
03/14/2022, 12:06 PMStylianos Gakis
03/14/2022, 12:13 PMSomeComposable(modifier = Modifier.keepScreenOn())
as I feel like this is modifying my SomeComposable
by making it keep the screen on, just like I’d expect all other modifiers to work. Especially compared to having to put a KeepScreenOnRequest()
at the top of a composable as a side effect at the spot I’d also put a SideEffect
or smth like that. Maybe if I found a better name for it it’d feel less weird.@Composable
fun ScreenOn(content: @Composable () -> Unit) {
... same code as Adam posted
content()
}
where I can add a `ScreenOn() { myComposables() } and just take the extra indentationAlbert Chang
03/14/2022, 12:19 PMBackHandler()
. I think you just have to get used to it.Stylianos Gakis
03/14/2022, 12:22 PMBackHandler
exists. And you’re right, it’s the exact same approach. Should probably just keep it like that then! Thanks for helping me out on this one everyone!