disabled screen timeout when displaying QR code (#617)
* disabled screen timeout when displaying QR code * added tests * fixed ktlint errors * Remove unnecessary test scope Co-authored-by: Carter Jernigan <git@carterjernigan.com>
This commit is contained in:
parent
ed408a6be3
commit
83b79afe4c
|
@ -0,0 +1,64 @@
|
|||
package co.electriccoin.zcash.ui.common
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.test.filters.MediumTest
|
||||
import co.electriccoin.zcash.test.UiTestPrerequisites
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class ScreenTimeoutTest : UiTestPrerequisites() {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun acquireAndReleaseScreenTimeout() = runTest {
|
||||
val testSetup = TestSetup(composeTestRule)
|
||||
|
||||
assertEquals(1, testSetup.getScreenTimeoutCount())
|
||||
|
||||
testSetup.mutableScreenTimeoutFlag.update { false }
|
||||
composeTestRule.awaitIdle()
|
||||
assertEquals(0, testSetup.getScreenTimeoutCount())
|
||||
}
|
||||
|
||||
private class TestSetup(composeTestRule: ComposeContentTestRule) {
|
||||
|
||||
val mutableScreenTimeoutFlag = MutableStateFlow(true)
|
||||
|
||||
private val screenTimeout = ScreenTimeout()
|
||||
|
||||
fun getScreenTimeoutCount() = screenTimeout.referenceCount.value
|
||||
|
||||
init {
|
||||
composeTestRule.setContent {
|
||||
CompositionLocalProvider(LocalScreenTimeout provides screenTimeout) {
|
||||
ZcashTheme {
|
||||
val disableScreenTimeout by mutableScreenTimeoutFlag.collectAsState()
|
||||
|
||||
TestView(disableScreenTimeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TestView(disableScreenTimeout: Boolean) {
|
||||
if (disableScreenTimeout) {
|
||||
DisableScreenTimeout()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package co.electriccoin.zcash.ui.screen.profile.view
|
||||
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.test.filters.MediumTest
|
||||
import cash.z.ecc.sdk.fixture.WalletAddressFixture
|
||||
import cash.z.ecc.sdk.model.WalletAddress
|
||||
import co.electriccoin.zcash.test.UiTestPrerequisites
|
||||
import co.electriccoin.zcash.ui.common.LocalScreenTimeout
|
||||
import co.electriccoin.zcash.ui.common.ScreenTimeout
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class ProfileViewScreenTimeoutTest : UiTestPrerequisites() {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun testFullBrightness() = runTest {
|
||||
val testSetup = newTestSetup(WalletAddressFixture.unified())
|
||||
|
||||
assertEquals(1, testSetup.getScreenTimeoutCount())
|
||||
}
|
||||
|
||||
private fun newTestSetup(walletAddress: WalletAddress) = TestSetup(composeTestRule, walletAddress)
|
||||
|
||||
private class TestSetup(private val composeTestRule: ComposeContentTestRule, walletAddress: WalletAddress) {
|
||||
|
||||
private val screenTimeout = ScreenTimeout()
|
||||
|
||||
fun getScreenTimeoutCount() = screenTimeout.referenceCount.value
|
||||
|
||||
init {
|
||||
composeTestRule.setContent {
|
||||
CompositionLocalProvider(LocalScreenTimeout provides screenTimeout) {
|
||||
ZcashTheme {
|
||||
ZcashTheme {
|
||||
Profile(
|
||||
walletAddress,
|
||||
onBack = { },
|
||||
onAddressDetails = { },
|
||||
onAddressBook = { },
|
||||
onSettings = { },
|
||||
onCoinholderVote = {},
|
||||
onSupport = { },
|
||||
onAbout = { }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@ package co.electriccoin.zcash.ui
|
|||
|
||||
import android.os.Bundle
|
||||
import android.os.SystemClock
|
||||
import android.view.WindowManager
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
|
@ -10,7 +9,6 @@ import androidx.annotation.VisibleForTesting
|
|||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
|
@ -18,12 +16,7 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
|||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.NavHostController
|
||||
import cash.z.ecc.android.sdk.ext.collectWith
|
||||
import co.electriccoin.zcash.spackle.EmulatorWtfUtil
|
||||
import co.electriccoin.zcash.spackle.FirebaseTestLabUtil
|
||||
import co.electriccoin.zcash.ui.common.LocalScreenBrightness
|
||||
import co.electriccoin.zcash.ui.common.LocalScreenSecurity
|
||||
import co.electriccoin.zcash.ui.common.ScreenBrightness
|
||||
import co.electriccoin.zcash.ui.common.ScreenSecurity
|
||||
import co.electriccoin.zcash.ui.common.BindCompLocalProvider
|
||||
import co.electriccoin.zcash.ui.design.compat.FontCompat
|
||||
import co.electriccoin.zcash.ui.design.component.ConfigurationOverride
|
||||
import co.electriccoin.zcash.ui.design.component.GradientSurface
|
||||
|
@ -36,7 +29,6 @@ import co.electriccoin.zcash.ui.screen.onboarding.WrapOnboarding
|
|||
import co.electriccoin.zcash.ui.screen.warning.WrapNotEnoughSpace
|
||||
import co.electriccoin.zcash.ui.screen.warning.viewmodel.StorageCheckViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
@ -90,11 +82,6 @@ class MainActivity : ComponentActivity() {
|
|||
}
|
||||
|
||||
private fun setupUiContent() {
|
||||
val screenSecurity = ScreenSecurity()
|
||||
val screenBrightness = ScreenBrightness()
|
||||
observeScreenSecurityFlag(screenSecurity)
|
||||
observeScreenBrightnessFlag(screenBrightness)
|
||||
|
||||
setContent {
|
||||
Override(configurationOverrideFlow) {
|
||||
ZcashTheme {
|
||||
|
@ -103,10 +90,7 @@ class MainActivity : ComponentActivity() {
|
|||
.fillMaxWidth()
|
||||
.fillMaxHeight()
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalScreenSecurity provides screenSecurity,
|
||||
LocalScreenBrightness provides screenBrightness
|
||||
) {
|
||||
BindCompLocalProvider {
|
||||
val isEnoughSpace by storageCheckViewModel.isEnoughSpace.collectAsState()
|
||||
if (isEnoughSpace == false) {
|
||||
WrapNotEnoughSpace()
|
||||
|
@ -148,36 +132,6 @@ class MainActivity : ComponentActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun observeScreenSecurityFlag(screenSecurity: ScreenSecurity) {
|
||||
screenSecurity.referenceCount.map { it > 0 }.collectWith(lifecycleScope) { isSecure ->
|
||||
val isTest = FirebaseTestLabUtil.isFirebaseTestLab(applicationContext) ||
|
||||
EmulatorWtfUtil.isEmulatorWtf(applicationContext)
|
||||
|
||||
if (isSecure && !isTest) {
|
||||
window.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_SECURE,
|
||||
WindowManager.LayoutParams.FLAG_SECURE
|
||||
)
|
||||
} else {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeScreenBrightnessFlag(screenBrightness: ScreenBrightness) {
|
||||
screenBrightness.referenceCount.map { it > 0 }.collectWith(lifecycleScope) { maxBrightness ->
|
||||
if (maxBrightness) {
|
||||
window.attributes = window.attributes.apply {
|
||||
this.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_FULL
|
||||
}
|
||||
} else {
|
||||
window.attributes = window.attributes.apply {
|
||||
this.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@VisibleForTesting
|
||||
internal val SPLASH_SCREEN_DELAY = 0.seconds
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
package co.electriccoin.zcash.ui.common
|
||||
|
||||
import android.view.WindowManager
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import cash.z.ecc.android.sdk.ext.collectWith
|
||||
import co.electriccoin.zcash.spackle.EmulatorWtfUtil
|
||||
import co.electriccoin.zcash.spackle.FirebaseTestLabUtil
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
@Composable
|
||||
fun ComponentActivity.BindCompLocalProvider(content: @Composable () -> Unit) {
|
||||
val screenSecurity = ScreenSecurity()
|
||||
observeScreenSecurityFlag(screenSecurity)
|
||||
|
||||
val screenBrightness = ScreenBrightness()
|
||||
observeScreenBrightnessFlag(screenBrightness)
|
||||
|
||||
val screenTimeout = ScreenTimeout()
|
||||
observeScreenTimeoutFlag(screenTimeout)
|
||||
CompositionLocalProvider(
|
||||
LocalScreenSecurity provides screenSecurity,
|
||||
LocalScreenBrightness provides screenBrightness,
|
||||
LocalScreenTimeout provides screenTimeout,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
||||
private fun ComponentActivity.observeScreenSecurityFlag(screenSecurity: ScreenSecurity) {
|
||||
screenSecurity.referenceCount.map { it > 0 }.collectWith(lifecycleScope) { isSecure ->
|
||||
val isTest = FirebaseTestLabUtil.isFirebaseTestLab(applicationContext) ||
|
||||
EmulatorWtfUtil.isEmulatorWtf(applicationContext)
|
||||
|
||||
if (isSecure && !isTest) {
|
||||
window.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_SECURE,
|
||||
WindowManager.LayoutParams.FLAG_SECURE
|
||||
)
|
||||
} else {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ComponentActivity.observeScreenBrightnessFlag(screenBrightness: ScreenBrightness) {
|
||||
screenBrightness.referenceCount.map { it > 0 }.collectWith(lifecycleScope) { maxBrightness ->
|
||||
if (maxBrightness) {
|
||||
window.attributes = window.attributes.apply {
|
||||
this.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_FULL
|
||||
}
|
||||
} else {
|
||||
window.attributes = window.attributes.apply {
|
||||
this.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ComponentActivity.observeScreenTimeoutFlag(screenTimeout: ScreenTimeout) {
|
||||
screenTimeout.referenceCount.map { it > 0 }.collectWith(lifecycleScope) { disableTimeout ->
|
||||
if (disableTimeout) {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
} else {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package co.electriccoin.zcash.ui.common
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.flow.updateAndGet
|
||||
|
||||
class ScreenTimeout {
|
||||
private val mutableReferenceCount: MutableStateFlow<Int> = MutableStateFlow(0)
|
||||
|
||||
val referenceCount = mutableReferenceCount.asStateFlow()
|
||||
|
||||
fun disableScreenTimeout() {
|
||||
mutableReferenceCount.update { it + 1 }
|
||||
}
|
||||
|
||||
fun restoreTimeout() {
|
||||
val after = mutableReferenceCount.updateAndGet { it - 1 }
|
||||
|
||||
if (after < 0) {
|
||||
error("Restored timeout reference count too many times")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val LocalScreenTimeout = compositionLocalOf { ScreenTimeout() }
|
||||
|
||||
@Composable
|
||||
fun DisableScreenTimeout() {
|
||||
val screenTimeout = LocalScreenTimeout.current
|
||||
DisposableEffect(screenTimeout) {
|
||||
screenTimeout.disableScreenTimeout()
|
||||
onDispose { screenTimeout.restoreTimeout() }
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@ import cash.z.ecc.sdk.fixture.WalletAddressFixture
|
|||
import cash.z.ecc.sdk.model.WalletAddress
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.BrightenScreen
|
||||
import co.electriccoin.zcash.ui.common.DisableScreenTimeout
|
||||
import co.electriccoin.zcash.ui.design.component.Body
|
||||
import co.electriccoin.zcash.ui.design.component.GradientSurface
|
||||
import co.electriccoin.zcash.ui.design.component.PrimaryButton
|
||||
|
@ -145,6 +146,7 @@ private fun ProfileContents(
|
|||
@Composable
|
||||
private fun QrCode(data: String, size: Dp, modifier: Modifier) {
|
||||
BrightenScreen()
|
||||
DisableScreenTimeout()
|
||||
val sizePixels = with(LocalDensity.current) { size.toPx() }.roundToInt()
|
||||
|
||||
// In the future, use actual/expect to switch QR code generator implementations for multiplatform
|
||||
|
|
Loading…
Reference in New Issue