Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,10 @@ dependencies {
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.navigation3.runtime)
implementation(libs.androidx.navigation3.ui)
implementation(libs.androidx.lifecycle.viewmodel.navigation3)
implementation(libs.androidx.material3.navigation3)
implementation(libs.timber)
implementation(libs.kim)
implementation(project.dependencies.platform(libs.koin.bom))
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@

<category android:name="android.intent.category.DEFAULT"/>

<data android:mimeType="image/jpeg"/>
<data android:mimeType="image/*"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE"/>

<category android:name="android.intent.category.DEFAULT"/>

<data android:mimeType="image/jpeg"/>
<data android:mimeType="image/*"/>
</intent-filter>
</activity>

Expand Down
14 changes: 8 additions & 6 deletions app/src/main/kotlin/com/darkrockstudios/app/securecamera/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.LifecycleResumeEffect
import androidx.navigation.NavHostController
import androidx.navigation3.runtime.NavBackStack
import com.darkrockstudios.app.securecamera.auth.AuthorizationRepository
import com.darkrockstudios.app.securecamera.navigation.AppNavHost
import com.darkrockstudios.app.securecamera.navigation.NavController
import com.darkrockstudios.app.securecamera.navigation.enforceAuth
import com.darkrockstudios.app.securecamera.preferences.AppPreferencesDataSource
import com.darkrockstudios.app.securecamera.ui.theme.SecureCameraTheme
Expand All @@ -19,8 +20,8 @@ import org.koin.compose.koinInject
@Composable
fun App(
capturePhoto: MutableState<Boolean?>,
startDestination: String,
navController: NavHostController
backStack: NavBackStack,
navController: NavController
) {
KoinContext {
SecureCameraTheme {
Expand All @@ -38,11 +39,11 @@ fun App(
modifier = Modifier.imePadding()
) { paddingValues ->
AppNavHost(
backStack = backStack,
navController = navController,
capturePhoto = capturePhoto,
modifier = Modifier,
snackbarHostState = snackbarHostState,
startDestination = startDestination,
paddingValues = paddingValues,
)
}
Expand All @@ -53,14 +54,15 @@ fun App(

@Composable
private fun VerifySessionOnResume(
navController: NavHostController,
navController: NavController,
hasCompletedIntro: Boolean?,
authorizationRepository: AuthorizationRepository
) {
var requireAuthCheck = remember { false }
LifecycleResumeEffect(hasCompletedIntro) {
if (hasCompletedIntro == true && requireAuthCheck) {
enforceAuth(authorizationRepository, navController.currentDestination, navController)
// Use the top-of-stack key in Nav3
enforceAuth(authorizationRepository, null, navController)
}
onPauseOrDispose {
requireAuthCheck = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.path
import androidx.compose.ui.unit.dp

public val Camera: ImageVector
val Camera: ImageVector
get() {
if (_Camera != null) {
return _Camera!!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import androidx.navigation3.runtime.NavKey
import androidx.navigation3.runtime.rememberNavBackStack
import com.darkrockstudios.app.securecamera.auth.AuthorizationRepository
import com.darkrockstudios.app.securecamera.navigation.AppDestinations
import com.darkrockstudios.app.securecamera.navigation.*
import com.darkrockstudios.app.securecamera.navigation.Camera
import com.darkrockstudios.app.securecamera.preferences.AppPreferencesDataSource
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.firstOrNull
Expand All @@ -31,7 +33,7 @@ class MainActivity : ComponentActivity() {
private val locationRepository: LocationRepository by inject()
private val preferences: AppPreferencesDataSource by inject()
private val authorizationRepository: AuthorizationRepository by inject()
lateinit var navController: NavHostController
lateinit var navController: NavController

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -45,33 +47,34 @@ class MainActivity : ComponentActivity() {

enableEdgeToEdge()

val startDestination = determineStartRoute()
val startKey = determineStartKey()
setContent {
navController = rememberNavController()
App(capturePhoto, startDestination, navController)
val backStack = rememberNavBackStack(startKey)
val controller = remember(backStack) { Nav3CompatController(backStack) }
navController = controller
App(capturePhoto, backStack, navController)
}

startKeepAliveWatcher()
}

private fun determineStartRoute(): String {
private fun determineStartKey(): NavKey {
val photosToImport = receiveFiles()
val hasCompletedIntro = runBlocking { preferences.hasCompletedIntro.firstOrNull() ?: false }
val startDestination = if (hasCompletedIntro) {
val targetDestination = if (photosToImport.isNotEmpty()) {
AppDestinations.createImportPhotosRoute(photosToImport)
return if (hasCompletedIntro) {
val targetKey: DestinationKey = if (photosToImport.isNotEmpty()) {
ImportPhotos(PhotoImportJob(photosToImport))
} else {
AppDestinations.CAMERA_ROUTE
Camera
}
if (authorizationRepository.checkSessionValidity()) {
targetDestination
targetKey
} else {
AppDestinations.createPinVerificationRoute(targetDestination)
PinVerification(targetKey)
}
} else {
AppDestinations.INTRODUCTION_ROUTE
Introduction
}
return startDestination
}

override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
Expand Down Expand Up @@ -117,13 +120,13 @@ class MainActivity : ComponentActivity() {
val intent = getIntent()

return if (Intent.ACTION_SEND == intent.action && intent.type != null) {
if (intent.type?.startsWith("image/jpeg") == true) {
if (intent.type?.startsWith("image/") == true) {
handleSingleImage(intent)
} else {
emptyList()
}
} else if (Intent.ACTION_SEND_MULTIPLE == intent.action && intent.type != null) {
if (intent.type?.startsWith("image/jpeg") == true) {
if (intent.type?.startsWith("image/") == true) {
handleMultipleImages(intent)
} else {
emptyList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.navigation.NavHostController
import com.darkrockstudios.app.securecamera.R
import com.darkrockstudios.app.securecamera.navigation.NavController

/**
* About screen content
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AboutContent(
navController: NavHostController,
navController: NavController,
modifier: Modifier = Modifier,
paddingValues: PaddingValues,
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,14 @@
package com.darkrockstudios.app.securecamera.auth

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Camera
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
Expand All @@ -41,8 +20,10 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavController
import androidx.navigation3.runtime.NavKey
import com.darkrockstudios.app.securecamera.R
import com.darkrockstudios.app.securecamera.navigation.NavController
import com.darkrockstudios.app.securecamera.navigation.navigateClearingBackStack
import com.darkrockstudios.app.securecamera.ui.HandleUiEvents
import org.koin.androidx.compose.koinViewModel

Expand All @@ -53,7 +34,7 @@ import org.koin.androidx.compose.koinViewModel
fun PinVerificationContent(
navController: NavController,
snackbarHostState: SnackbarHostState,
returnRoute: String,
returnKey: NavKey,
modifier: Modifier = Modifier
) {
val viewModel: PinVerificationViewModel = koinViewModel()
Expand Down Expand Up @@ -117,11 +98,9 @@ fun PinVerificationContent(
fun verifyPin() {
viewModel.verify(
pin = pin,
returnRoute = returnRoute,
onNavigate = {
navController.navigate(it) {
popUpTo(0) { inclusive = true }
}
returnKey = returnKey,
onNavigate = { destKey ->
navController.navigateClearingBackStack(destKey)
},
onFailure = { pin = "" }
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package com.darkrockstudios.app.securecamera.auth

import android.content.Context
import androidx.lifecycle.viewModelScope
import androidx.navigation3.runtime.NavKey
import com.darkrockstudios.app.securecamera.BaseViewModel
import com.darkrockstudios.app.securecamera.R
import com.darkrockstudios.app.securecamera.gallery.vibrateDevice
import com.darkrockstudios.app.securecamera.navigation.AppDestinations
import com.darkrockstudios.app.securecamera.navigation.Introduction
import com.darkrockstudios.app.securecamera.usecases.InvalidateSessionUseCase
import com.darkrockstudios.app.securecamera.usecases.PinSizeUseCase
import com.darkrockstudios.app.securecamera.usecases.SecurityResetUseCase
Expand Down Expand Up @@ -90,7 +91,8 @@ class PinVerificationViewModel(
}
}

fun verify(pin: String, returnRoute: String, onNavigate: (String) -> Unit, onFailure: () -> Unit) {

fun verify(pin: String, returnKey: NavKey, onNavigate: (NavKey) -> Unit, onFailure: () -> Unit) {
val currentState = uiState.value

if (pin.isBlank()) {
Expand All @@ -115,8 +117,7 @@ class PinVerificationViewModel(
failedAttempts = 0
)
}

onNavigate(returnRoute)
onNavigate(returnKey)
}
} else {
val newFailedAttempts = authRepository.incrementFailedAttempts()
Expand Down Expand Up @@ -144,7 +145,7 @@ class PinVerificationViewModel(
// Nuke it all
securityResetUseCase.reset()
showMessage(appContext.getString(R.string.pin_verification_all_data_deleted))
onNavigate(AppDestinations.INTRODUCTION_ROUTE)
onNavigate(Introduction)
}

onFailure()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,17 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import com.darkrockstudios.app.securecamera.R
import com.darkrockstudios.app.securecamera.navigation.AppDestinations
import com.darkrockstudios.app.securecamera.navigation.Gallery
import com.darkrockstudios.app.securecamera.navigation.NavController
import com.darkrockstudios.app.securecamera.navigation.Settings

@Composable
fun BottomCameraControls(
modifier: Modifier = Modifier,
onCapture: (() -> Unit)?,
isLoading: Boolean,
navController: NavHostController,
navController: NavController,
) {
val context = LocalContext.current

Expand All @@ -38,7 +39,7 @@ fun BottomCameraControls(
.padding(start = 16.dp, end = 16.dp, bottom = 8.dp),
) {
ElevatedButton(
onClick = { navController.navigate(AppDestinations.SETTINGS_ROUTE) },
onClick = { navController.navigate(Settings) },
enabled = isLoading.not(),
modifier = Modifier.align(Alignment.BottomStart),
) {
Expand Down Expand Up @@ -73,7 +74,7 @@ fun BottomCameraControls(
}

ElevatedButton(
onClick = { navController.navigate(AppDestinations.GALLERY_ROUTE) },
onClick = { navController.navigate(Gallery) },
enabled = isLoading.not(),
modifier = Modifier.align(Alignment.BottomEnd),
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import com.darkrockstudios.app.securecamera.KeepScreenOnEffect
import com.darkrockstudios.app.securecamera.navigation.NavController
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.rememberMultiplePermissionsState

@OptIn(ExperimentalPermissionsApi::class)
@Composable
internal fun CameraContent(
capturePhoto: MutableState<Boolean?>,
navController: NavHostController,
navController: NavController,
modifier: Modifier,
paddingValues: PaddingValues,
) {
Expand Down
Loading