Skip to content

Commit

Permalink
[BWA-21] Disable screen capture (#110)
Browse files Browse the repository at this point in the history
  • Loading branch information
SaintPatrck committed May 28, 2024
1 parent e9fb28d commit 9537bfa
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 5 deletions.
34 changes: 32 additions & 2 deletions app/src/main/kotlin/com/bitwarden/authenticator/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,32 @@ package com.bitwarden.authenticator

import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.WindowManager
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.getValue
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
import com.bitwarden.authenticator.ui.platform.feature.rootnav.RootNavScreen
import com.bitwarden.authenticator.ui.platform.theme.AuthenticatorTheme
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

private val mainViewModel: MainViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

var shouldShowSplashScreen = true
installSplashScreen().setKeepOnScreenCondition { shouldShowSplashScreen }
super.onCreate(savedInstanceState)

observeViewModelEvents()

if (savedInstanceState == null) {
mainViewModel.trySendAction(
Expand Down Expand Up @@ -51,4 +57,28 @@ class MainActivity : AppCompatActivity() {
MainAction.ReceiveNewIntent(intent = intent)
)
}

private fun observeViewModelEvents() {
Log.d("TAG", "observeViewModelEvents() called")
mainViewModel
.eventFlow
.onEach { event ->
Log.d("TAG", "observeViewModelEvents: onEach $event")
when(event) {
is MainEvent.ScreenCaptureSettingChange -> {
handleScreenCaptureSettingChange(event)
}
}
}
.launchIn(lifecycleScope)
}

private fun handleScreenCaptureSettingChange(event: MainEvent.ScreenCaptureSettingChange) {
Log.d("TAG", "handleScreenCaptureSettingChange() called with: event = $event")
if (event.isAllowed) {
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
} else {
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
}
}
}
10 changes: 10 additions & 0 deletions app/src/main/kotlin/com/bitwarden/authenticator/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import kotlinx.coroutines.flow.update
import kotlinx.parcelize.Parcelize
import javax.inject.Inject

/**
* A view model that helps launch actions for the [MainActivity].
*/
@HiltViewModel
class MainViewModel @Inject constructor(
settingsRepository: SettingsRepository,
Expand All @@ -27,6 +30,13 @@ class MainViewModel @Inject constructor(
.appThemeStateFlow
.onEach { trySendAction(MainAction.Internal.ThemeUpdate(it)) }
.launchIn(viewModelScope)

settingsRepository
.isScreenCaptureAllowedStateFlow
.onEach { isAllowed ->
sendEvent(MainEvent.ScreenCaptureSettingChange(isAllowed))
}
.launchIn(viewModelScope)
}

override fun handleAction(action: MainAction) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,19 @@ interface SettingsDiskSource {
systemBioIntegrityState: String,
value: Boolean?,
)

/**
* Gets whether or not the user has enabled screen capture.
*/
fun getScreenCaptureAllowed(): Boolean?

/**
* Emits updates that track [getScreenCaptureAllowed].
*/
fun getScreenCaptureAllowedFlow(): Flow<Boolean?>

/**
* Stores whether or not [isScreenCaptureAllowed].
*/
fun storeScreenCaptureAllowed(isScreenCaptureAllowed: Boolean?)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import com.bitwarden.authenticator.data.platform.repository.util.bufferedMutable
import com.bitwarden.authenticator.ui.platform.feature.settings.appearance.model.AppLanguage
import com.bitwarden.authenticator.ui.platform.feature.settings.appearance.model.AppTheme
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.onSubscription

private const val APP_THEME_KEY = "$BASE_KEY:theme"
Expand All @@ -16,6 +15,7 @@ private const val ACCOUNT_BIOMETRIC_INTEGRITY_VALID_KEY = "$BASE_KEY:accountBiom
private const val ALERT_THRESHOLD_SECONDS_KEY = "$BASE_KEY:alertThresholdSeconds"
private const val FIRST_LAUNCH_KEY = "$BASE_KEY:hasSeenWelcomeTutorial"
private const val CRASH_LOGGING_ENABLED_KEY = "$BASE_KEY:crashLoggingEnabled"
private const val SCREEN_CAPTURE_ALLOW_KEY = "screenCaptureAllowed"

/**
* Primary implementation of [SettingsDiskSource].
Expand All @@ -27,8 +27,8 @@ class SettingsDiskSourceImpl(
private val mutableAppThemeFlow =
bufferedMutableSharedFlow<AppTheme>(replay = 1)

private val mutableScreenCaptureAllowedFlowMap =
mutableMapOf<String, MutableSharedFlow<Boolean?>>()
private val mutableScreenCaptureAllowedFlow =
bufferedMutableSharedFlow<Boolean?>()

private val mutableAlertThresholdSecondsFlow =
bufferedMutableSharedFlow<Int>()
Expand Down Expand Up @@ -127,4 +127,21 @@ class SettingsDiskSourceImpl(
value = value,
)
}

override fun getScreenCaptureAllowed(): Boolean? {
return getBoolean(key = SCREEN_CAPTURE_ALLOW_KEY)
}

override fun getScreenCaptureAllowedFlow(): Flow<Boolean?> = mutableScreenCaptureAllowedFlow
.onSubscription { emit(getScreenCaptureAllowed()) }

override fun storeScreenCaptureAllowed(
isScreenCaptureAllowed: Boolean?,
) {
putBoolean(
key = SCREEN_CAPTURE_ALLOW_KEY,
value = isScreenCaptureAllowed,
)
mutableScreenCaptureAllowedFlow.tryEmit(isScreenCaptureAllowed)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ interface SettingsRepository {
*/
val hasSeenWelcomeTutorialFlow: StateFlow<Boolean>

/**
* Sets whether or not screen capture is allowed for the current user.
*/
var isScreenCaptureAllowed: Boolean

/**
* Whether or not screen capture is allowed for the current user.
*/
val isScreenCaptureAllowedStateFlow: StateFlow<Boolean>

/**
* Clears any previously stored encrypted user key used with biometrics for the current user.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.bitwarden.authenticator.data.platform.repository

import com.bitwarden.authenticator.BuildConfig
import com.bitwarden.authenticator.data.auth.datasource.disk.AuthDiskSource
import com.bitwarden.authenticator.data.authenticator.datasource.sdk.AuthenticatorSdkSource
import com.bitwarden.authenticator.data.platform.datasource.disk.SettingsDiskSource
Expand All @@ -15,6 +16,8 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn

private val DEFAULT_IS_SCREEN_CAPTURE_ALLOWED = BuildConfig.DEBUG

/**
* Primary implementation of [SettingsRepository].
*/
Expand Down Expand Up @@ -75,6 +78,25 @@ class SettingsRepositoryImpl(
initialValue = hasSeenWelcomeTutorial,
)

override var isScreenCaptureAllowed: Boolean
get() = settingsDiskSource.getScreenCaptureAllowed()
?: DEFAULT_IS_SCREEN_CAPTURE_ALLOWED
set(value) {
settingsDiskSource.storeScreenCaptureAllowed(
isScreenCaptureAllowed = value,
)
}

override val isScreenCaptureAllowedStateFlow: StateFlow<Boolean>
get() = settingsDiskSource.getScreenCaptureAllowedFlow()
.map { isAllowed -> isAllowed ?: DEFAULT_IS_SCREEN_CAPTURE_ALLOWED }
.stateIn(
scope = unconfinedScope,
started = SharingStarted.Lazily,
initialValue = settingsDiskSource.getScreenCaptureAllowed()
?: DEFAULT_IS_SCREEN_CAPTURE_ALLOWED,
)

override suspend fun setupBiometricsKey(): BiometricsKeyResult {
biometricsEncryptionManager.setupBiometrics()
return authenticatorSdkSource
Expand Down

0 comments on commit 9537bfa

Please sign in to comment.