mirror of
https://github.com/Myzel394/Alibi.git
synced 2025-06-18 14:55:26 +02:00
Merge pull request #80 from Myzel394/fix-71-improve-landscape
Improve landscape
This commit is contained in:
commit
38fc299fc6
@ -1,3 +1,9 @@
|
||||
# services
|
||||
|
||||
This folder contains all available services.
|
||||
|
||||
## VideoRecorderService
|
||||
|
||||
I found it a bit confusing on how to properly handle the services, so I made this diagram
|
||||
to help me understand it better. I hope it helps you too.
|
||||
|
||||
|
@ -194,7 +194,9 @@ class VideoRecorderService :
|
||||
selectedCamera,
|
||||
videoCapture
|
||||
)
|
||||
cameraControl = CameraControl(camera!!)
|
||||
cameraControl = CameraControl(camera!!).also {
|
||||
it.init()
|
||||
}
|
||||
onCameraControlAvailable()
|
||||
|
||||
_cameraAvailableListener.complete(Unit)
|
||||
@ -309,17 +311,25 @@ class VideoRecorderService :
|
||||
}
|
||||
|
||||
class CameraControl(
|
||||
val camera: Camera
|
||||
val camera: Camera,
|
||||
// Save state for optimistic updates
|
||||
var torchEnabled: Boolean = false,
|
||||
) {
|
||||
fun init() {
|
||||
torchEnabled = camera.cameraInfo.torchState.value == TorchState.ON
|
||||
}
|
||||
|
||||
fun enableTorch() {
|
||||
torchEnabled = true
|
||||
camera.cameraControl.enableTorch(true)
|
||||
}
|
||||
|
||||
fun disableTorch() {
|
||||
torchEnabled = false
|
||||
camera.cameraControl.enableTorch(false)
|
||||
}
|
||||
|
||||
fun isTorchEnabled(): Boolean {
|
||||
fun isHardwareTorchReallyEnabled(): Boolean {
|
||||
return camera.cameraInfo.torchState.value == TorchState.ON
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import android.os.Build
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
val BIG_PRIMARY_BUTTON_SIZE = 64.dp
|
||||
val BIG_PRIMARY_BUTTON_MAX_WIDTH = 450.dp
|
||||
|
||||
val SHEET_BOTTOM_OFFSET = 24.dp
|
||||
val MAX_AMPLITUDE = 20000
|
||||
|
14
app/src/main/java/app/myzel394/alibi/ui/README.md
Normal file
14
app/src/main/java/app/myzel394/alibi/ui/README.md
Normal file
@ -0,0 +1,14 @@
|
||||
# ui
|
||||
|
||||
This folder contains all user interfaces. The folder is structured as follows:
|
||||
|
||||
* `components`: contains all reusable components
|
||||
* `atoms`, `molecules`, `organisms`, `pages`: contains components that are generic and can be
|
||||
reused in different contexts
|
||||
* `<name>Screen/{atoms,molecules,organisms,pages}`: contains components that are specific to a
|
||||
screen
|
||||
* `screens`: contains all screens. Screens are composed of components from the `components` folder
|
||||
* `models`: contains view models used by the screens
|
||||
* `utils`: contains general utility functions
|
||||
|
||||
The root Kotlin files are used for the general setup of the UI.
|
@ -1,6 +1,6 @@
|
||||
package app.myzel394.alibi.ui.components.RecorderScreen.atoms
|
||||
|
||||
import android.Manifest
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
@ -11,10 +11,7 @@ import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Mic
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@ -25,12 +22,10 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.myzel394.alibi.R
|
||||
import app.myzel394.alibi.ui.utils.PermissionHelper
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
@ -41,8 +36,10 @@ fun BigButton(
|
||||
onClick: () -> Unit,
|
||||
onLongClick: () -> Unit = {},
|
||||
) {
|
||||
val orientation = LocalConfiguration.current.orientation
|
||||
|
||||
BoxWithConstraints {
|
||||
val isLarge = maxWidth > 500.dp
|
||||
val isLarge = maxWidth > 500.dp && orientation == Configuration.ORIENTATION_PORTRAIT
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
@ -61,11 +61,18 @@ fun RealtimeAudioVisualizer(
|
||||
}
|
||||
|
||||
val configuration = LocalConfiguration.current
|
||||
val screenWidth = with(LocalDensity.current) { configuration.screenWidthDp.dp.toPx() }
|
||||
// Use greater value of width and height to make sure the amplitudes are shown
|
||||
// when the user rotates the device
|
||||
val availableSpace = with(LocalDensity.current) {
|
||||
Math.max(
|
||||
configuration.screenWidthDp.dp.toPx(),
|
||||
configuration.screenHeightDp.dp.toPx()
|
||||
)
|
||||
}
|
||||
|
||||
LaunchedEffect(screenWidth) {
|
||||
LaunchedEffect(availableSpace) {
|
||||
// Add 1 to allow the visualizer to overflow the screen
|
||||
audioRecorder.setMaxAmplitudesAmount(ceil(screenWidth.toInt() / BOX_DIFF).toInt() + 1)
|
||||
audioRecorder.setMaxAmplitudesAmount(ceil(availableSpace.toInt() / BOX_DIFF).toInt() + 1)
|
||||
}
|
||||
|
||||
Canvas(modifier = modifier) {
|
||||
|
@ -1,45 +1,22 @@
|
||||
package app.myzel394.alibi.ui.components.RecorderScreen.molecules
|
||||
|
||||
import android.Manifest
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.InsertDriveFile
|
||||
import androidx.compose.material.icons.automirrored.filled.InsertDriveFile
|
||||
import androidx.compose.material.icons.filled.Mic
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
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.rememberCoroutineScope
|
||||
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.draw.clip
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
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 app.myzel394.alibi.R
|
||||
import app.myzel394.alibi.db.AppSettings
|
||||
import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE
|
||||
import app.myzel394.alibi.ui.components.RecorderScreen.atoms.BigButton
|
||||
import app.myzel394.alibi.ui.components.atoms.PermissionRequester
|
||||
import app.myzel394.alibi.ui.models.AudioRecorderModel
|
||||
import app.myzel394.alibi.ui.utils.PermissionHelper
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@Composable
|
||||
fun AudioRecordingStart(
|
||||
@ -62,7 +39,7 @@ fun AudioRecordingStart(
|
||||
|
||||
PermissionRequester(
|
||||
permission = Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
icon = Icons.Default.InsertDriveFile,
|
||||
icon = Icons.AutoMirrored.Filled.InsertDriveFile,
|
||||
onPermissionAvailable = {
|
||||
startRecording = true
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
package app.myzel394.alibi.ui.components.RecorderScreen.molecules
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -14,6 +17,8 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.myzel394.alibi.ui.components.RecorderScreen.atoms.DeleteButton
|
||||
import app.myzel394.alibi.ui.components.RecorderScreen.atoms.PauseResumeButton
|
||||
import app.myzel394.alibi.ui.components.RecorderScreen.atoms.SaveButton
|
||||
@ -23,6 +28,8 @@ import kotlinx.coroutines.delay
|
||||
|
||||
@Composable
|
||||
fun RecordingControl(
|
||||
modifier: Modifier = Modifier,
|
||||
orientation: Int = LocalConfiguration.current.orientation,
|
||||
initialDelay: Long = 0L,
|
||||
isPaused: Boolean,
|
||||
recordingTime: Long,
|
||||
@ -85,8 +92,55 @@ fun RecordingControl(
|
||||
}
|
||||
}
|
||||
|
||||
when (orientation) {
|
||||
Configuration.ORIENTATION_LANDSCAPE -> {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
modifier = modifier,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.alpha(saveButtonAlpha),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
SaveButton(
|
||||
onSave = onSave,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.alpha(pauseButtonAlpha),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
PauseResumeButton(
|
||||
isPaused = isPaused,
|
||||
onChange = onPauseResume,
|
||||
)
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.alpha(deleteButtonAlpha),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
DeleteButton(
|
||||
onDelete = onDelete,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = modifier,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@ -119,4 +173,6 @@ fun RecordingControl(
|
||||
SaveButton(onSave = onSave)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -40,6 +40,7 @@ fun RecordingStatus(
|
||||
progress: Float,
|
||||
recordingStart: LocalDateTime,
|
||||
maxDuration: Long,
|
||||
progressModifier: Modifier = Modifier.width(300.dp),
|
||||
) {
|
||||
val animateIn = rememberInitialRecordingAnimation(recordingTime)
|
||||
|
||||
@ -73,9 +74,8 @@ fun RecordingStatus(
|
||||
)
|
||||
) {
|
||||
LinearProgressIndicator(
|
||||
progress = progress,
|
||||
modifier = Modifier
|
||||
.width(300.dp)
|
||||
progress = { progress },
|
||||
modifier = progressModifier,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -2,50 +2,18 @@ package app.myzel394.alibi.ui.components.RecorderScreen.molecules
|
||||
|
||||
import android.Manifest
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.indication
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
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.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.InsertDriveFile
|
||||
import androidx.compose.material.icons.filled.CameraAlt
|
||||
import androidx.compose.material.icons.filled.InsertDriveFile
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
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.draw.clip
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
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.lifecycle.viewmodel.compose.viewModel
|
||||
import app.myzel394.alibi.R
|
||||
import app.myzel394.alibi.db.AppSettings
|
||||
import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE
|
||||
import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE
|
||||
import app.myzel394.alibi.ui.components.RecorderScreen.atoms.BigButton
|
||||
import app.myzel394.alibi.ui.components.atoms.PermissionRequester
|
||||
import app.myzel394.alibi.ui.models.VideoRecorderModel
|
||||
@ -83,7 +51,7 @@ fun VideoRecordingStart(
|
||||
|
||||
PermissionRequester(
|
||||
permission = Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
icon = Icons.Default.InsertDriveFile,
|
||||
icon = Icons.AutoMirrored.Filled.InsertDriveFile,
|
||||
onPermissionAvailable = {
|
||||
showSheet = true
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
package app.myzel394.alibi.ui.components.RecorderScreen.organisms
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
@ -16,6 +19,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RealtimeAudioVisualizer
|
||||
@ -33,6 +37,7 @@ fun AudioRecordingStatus(
|
||||
audioRecorder: AudioRecorderModel,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val configuration = LocalConfiguration.current.orientation
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
@ -62,6 +67,61 @@ fun AudioRecordingStatus(
|
||||
.weight(1f),
|
||||
)
|
||||
|
||||
when (configuration) {
|
||||
Configuration.ORIENTATION_LANDSCAPE -> {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement
|
||||
.spacedBy(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.weight(3f),
|
||||
) {
|
||||
RecordingStatus(
|
||||
recordingTime = audioRecorder.recordingTime,
|
||||
progress = audioRecorder.progress,
|
||||
recordingStart = audioRecorder.recordingStart,
|
||||
maxDuration = audioRecorder.settings!!.maxDuration,
|
||||
progressModifier = Modifier.fillMaxWidth(.9f),
|
||||
)
|
||||
|
||||
MicrophoneStatus(audioRecorder)
|
||||
}
|
||||
|
||||
RecordingControl(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth(),
|
||||
isPaused = audioRecorder.isPaused,
|
||||
recordingTime = audioRecorder.recordingTime,
|
||||
onDelete = {
|
||||
scope.launch {
|
||||
runCatching {
|
||||
audioRecorder.stopRecording(context)
|
||||
}
|
||||
runCatching {
|
||||
audioRecorder.destroyService(context)
|
||||
}
|
||||
audioRecorder.batchesFolder!!.deleteRecordings()
|
||||
}
|
||||
},
|
||||
onPauseResume = {
|
||||
if (audioRecorder.isPaused) {
|
||||
audioRecorder.resumeRecording()
|
||||
} else {
|
||||
audioRecorder.pauseRecording()
|
||||
}
|
||||
},
|
||||
onSave = {
|
||||
audioRecorder.onRecordingSave(false)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
RecordingStatus(
|
||||
recordingTime = audioRecorder.recordingTime,
|
||||
progress = audioRecorder.progress,
|
||||
@ -105,4 +165,6 @@ fun AudioRecordingStatus(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package app.myzel394.alibi.ui.components.RecorderScreen.organisms
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@ -9,13 +10,13 @@ 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.requiredWidthIn
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.text.ClickableText
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Save
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@ -29,6 +30,7 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@ -40,6 +42,7 @@ import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.myzel394.alibi.R
|
||||
import app.myzel394.alibi.db.AppSettings
|
||||
import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_MAX_WIDTH
|
||||
import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE
|
||||
import app.myzel394.alibi.ui.components.RecorderScreen.molecules.AudioRecordingStart
|
||||
import app.myzel394.alibi.ui.components.RecorderScreen.molecules.QuickMaxDurationSelector
|
||||
@ -64,6 +67,7 @@ fun StartRecording(
|
||||
showAudioRecorder: Boolean,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val orientation = LocalConfiguration.current.orientation
|
||||
|
||||
val label = stringResource(
|
||||
R.string.ui_recorder_action_start_description_2,
|
||||
@ -98,10 +102,33 @@ fun StartRecording(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(bottom = 32.dp),
|
||||
.padding(bottom = if (orientation == Configuration.ORIENTATION_PORTRAIT) 32.dp else 16.dp),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
when (orientation) {
|
||||
Configuration.ORIENTATION_LANDSCAPE -> {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (showAudioRecorder)
|
||||
AudioRecordingStart(
|
||||
audioRecorder = audioRecorder,
|
||||
appSettings = appSettings,
|
||||
)
|
||||
VideoRecordingStart(
|
||||
videoRecorder = videoRecorder,
|
||||
appSettings = appSettings,
|
||||
onHideAudioRecording = onHideTopBar,
|
||||
onShowAudioRecording = onShowTopBar,
|
||||
showPreview = !showAudioRecorder,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
if (showAudioRecorder)
|
||||
@ -116,6 +143,9 @@ fun StartRecording(
|
||||
onShowAudioRecording = onShowTopBar,
|
||||
showPreview = !showAudioRecorder,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val forceUpdate = rememberForceUpdateOnLifeCycleChange()
|
||||
Column(
|
||||
@ -133,6 +163,7 @@ fun StartRecording(
|
||||
TextButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.requiredWidthIn(max = BIG_PRIMARY_BUTTON_MAX_WIDTH)
|
||||
.height(BIG_PRIMARY_BUTTON_SIZE)
|
||||
.semantics {
|
||||
contentDescription = label
|
||||
|
@ -1,10 +1,13 @@
|
||||
package app.myzel394.alibi.ui.components.RecorderScreen.organisms
|
||||
|
||||
import android.content.res.Configuration
|
||||
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.Row
|
||||
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
|
||||
@ -16,9 +19,14 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
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.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@ -37,28 +45,96 @@ import kotlinx.coroutines.launch
|
||||
fun VideoRecordingStatus(
|
||||
videoRecorder: VideoRecorderModel,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val availableCameras = CameraInfo.queryAvailableCameras(context)
|
||||
val orientation = LocalConfiguration.current.orientation
|
||||
|
||||
KeepScreenOn()
|
||||
|
||||
when (orientation) {
|
||||
Configuration.ORIENTATION_LANDSCAPE -> {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement
|
||||
.spacedBy(32.dp),
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth(0.9f)
|
||||
.align(Alignment.CenterVertically),
|
||||
) {
|
||||
_VideoGeneralInfo(videoRecorder)
|
||||
_VideoRecordingStatus(videoRecorder)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth(0.9f)
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement
|
||||
.spacedBy(32.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
_VideoControls(videoRecorder)
|
||||
HorizontalDivider()
|
||||
_PrimitiveControls(videoRecorder)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
.fillMaxSize()
|
||||
.padding(bottom = 32.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Box {}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement
|
||||
.spacedBy(24.dp),
|
||||
.spacedBy(16.dp),
|
||||
) {
|
||||
_VideoGeneralInfo(videoRecorder)
|
||||
_VideoRecordingStatus(videoRecorder)
|
||||
}
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement
|
||||
.spacedBy(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
_VideoControls(videoRecorder)
|
||||
HorizontalDivider()
|
||||
_PrimitiveControls(videoRecorder)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun _VideoGeneralInfo(videoRecorder: VideoRecorderModel) {
|
||||
val context = LocalContext.current
|
||||
val availableCameras = CameraInfo.queryAvailableCameras(context)
|
||||
val orientation = LocalConfiguration.current.orientation
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement
|
||||
.spacedBy(if (orientation == Configuration.ORIENTATION_LANDSCAPE) 12.dp else 24.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.CameraAlt,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(64.dp)
|
||||
modifier = Modifier.size(if (orientation == Configuration.ORIENTATION_LANDSCAPE) 48.dp else 64.dp)
|
||||
)
|
||||
|
||||
if (videoRecorder.isStartingRecording) {
|
||||
@ -98,49 +174,25 @@ fun VideoRecordingStatus(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (videoRecorder.isStartingRecording) {
|
||||
Text(
|
||||
stringResource(R.string.ui_videoRecorder_info_starting),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
)
|
||||
} else {
|
||||
@Composable
|
||||
fun _VideoRecordingStatus(videoRecorder: VideoRecorderModel) {
|
||||
RecordingStatus(
|
||||
recordingTime = videoRecorder.recordingTime,
|
||||
progress = videoRecorder.progress,
|
||||
recordingStart = videoRecorder.recordingStart,
|
||||
maxDuration = videoRecorder.settings!!.maxDuration,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement
|
||||
.spacedBy(32.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.padding(bottom = 32.dp),
|
||||
) {
|
||||
if (!videoRecorder.isStartingRecording) {
|
||||
val cameraControl = videoRecorder.recorderService!!.cameraControl!!
|
||||
if (cameraControl.hasTorchAvailable()) {
|
||||
val isTorchEnabled = cameraControl.isTorchEnabled()
|
||||
@Composable
|
||||
fun _PrimitiveControls(videoRecorder: VideoRecorderModel) {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
TorchStatus(
|
||||
enabled = isTorchEnabled,
|
||||
onChange = {
|
||||
if (isTorchEnabled) {
|
||||
cameraControl.disableTorch()
|
||||
} else {
|
||||
cameraControl.enableTorch()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalDivider()
|
||||
|
||||
if (!videoRecorder.isStartingRecording) {
|
||||
RecordingControl(
|
||||
orientation = Configuration.ORIENTATION_PORTRAIT,
|
||||
// There may be some edge cases where the app may crash if the
|
||||
// user stops or pauses the recording too soon, so we simply add a
|
||||
// small delay to prevent that
|
||||
@ -169,9 +221,27 @@ fun VideoRecordingStatus(
|
||||
videoRecorder.onRecordingSave(false)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun _VideoControls(videoRecorder: VideoRecorderModel) {
|
||||
if (!videoRecorder.isStartingRecording) {
|
||||
val cameraControl = videoRecorder.recorderService!!.cameraControl!!
|
||||
if (cameraControl.hasTorchAvailable()) {
|
||||
var torchEnabled by rememberSaveable { mutableStateOf(cameraControl.torchEnabled) }
|
||||
|
||||
TorchStatus(
|
||||
enabled = torchEnabled,
|
||||
onChange = {
|
||||
if (torchEnabled) {
|
||||
torchEnabled = false
|
||||
cameraControl.disableTorch()
|
||||
} else {
|
||||
Box {}
|
||||
torchEnabled = true
|
||||
cameraControl.enableTorch()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -305,7 +305,9 @@ fun DCIMFolderExplanationDialog(
|
||||
},
|
||||
text = {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(32.dp),
|
||||
) {
|
||||
@ -408,7 +410,9 @@ fun InternalFolderExplanationDialog(
|
||||
},
|
||||
text = {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(32.dp),
|
||||
) {
|
||||
@ -525,7 +529,8 @@ fun SelectionSheet(
|
||||
)
|
||||
if (!SUPPORTS_SAVING_VIDEOS_IN_CUSTOM_FOLDERS) {
|
||||
Column(
|
||||
modifier = Modifier.padding(horizontal = 32.dp, vertical = 12.dp),
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 32.dp, vertical = 12.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
|
@ -1,7 +1,6 @@
|
||||
package app.myzel394.alibi.ui.components.atoms
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.pm.PackageManager
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@ -10,9 +9,9 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.OpenInNew
|
||||
import androidx.compose.material.icons.filled.Cancel
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.OpenInNew
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
@ -21,14 +20,14 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
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.ui.platform.LocalContext
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.app.ActivityCompat
|
||||
@ -173,7 +172,7 @@ fun PermissionRequester(
|
||||
contentPadding = ButtonDefaults.ButtonWithIconContentPadding,
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.OpenInNew,
|
||||
Icons.AutoMirrored.Filled.OpenInNew,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(ButtonDefaults.IconSize),
|
||||
)
|
||||
|
@ -0,0 +1,39 @@
|
||||
package app.myzel394.alibi.ui.components.atoms
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Rotate90DegreesCw
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.myzel394.alibi.R
|
||||
|
||||
@Composable
|
||||
fun RotateDeviceToPortrait(
|
||||
modifier: Modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.widthIn(max = 300.dp),
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Rotate90DegreesCw,
|
||||
contentDescription = stringResource(R.string.ui_rotateDevice_portrait_label),
|
||||
modifier = Modifier.size(48.dp),
|
||||
tint = MaterialTheme.colorScheme.tertiary,
|
||||
)
|
||||
Text(stringResource(R.string.ui_rotateDevice_portrait_label))
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package app.myzel394.alibi.ui.screens
|
||||
|
||||
import android.widget.Space
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
@ -17,8 +16,8 @@ import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.OpenInNew
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.OpenInNew
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
@ -42,11 +41,10 @@ import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
import app.myzel394.alibi.R
|
||||
import app.myzel394.alibi.BuildConfig
|
||||
import app.myzel394.alibi.ui.TRANSLATION_HELP_URL
|
||||
import app.myzel394.alibi.R
|
||||
import app.myzel394.alibi.ui.REPO_URL
|
||||
import app.myzel394.alibi.ui.TRANSLATION_HELP_URL
|
||||
import app.myzel394.alibi.ui.components.AboutScreen.atoms.DonationsTile
|
||||
import app.myzel394.alibi.ui.components.AboutScreen.atoms.GPGKeyOverview
|
||||
|
||||
@ -68,9 +66,10 @@ fun AboutScreen(
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onBackNavigate) {
|
||||
val label = stringResource(R.string.goBack)
|
||||
Icon(
|
||||
Icons.Default.ArrowBack,
|
||||
contentDescription = "Back"
|
||||
Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = label,
|
||||
)
|
||||
}
|
||||
},
|
||||
@ -159,7 +158,7 @@ fun AboutScreen(
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
Icon(
|
||||
Icons.Default.OpenInNew,
|
||||
Icons.AutoMirrored.Filled.OpenInNew,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(ButtonDefaults.IconSize)
|
||||
)
|
||||
@ -196,7 +195,7 @@ fun AboutScreen(
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
Icon(
|
||||
Icons.Default.OpenInNew,
|
||||
Icons.AutoMirrored.Filled.OpenInNew,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(ButtonDefaults.IconSize)
|
||||
)
|
||||
|
@ -1,18 +1,11 @@
|
||||
package app.myzel394.alibi.ui.screens
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.gestures.scrollable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
@ -29,22 +22,20 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
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.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
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.NavController
|
||||
import app.myzel394.alibi.R
|
||||
import app.myzel394.alibi.dataStore
|
||||
import app.myzel394.alibi.db.AppSettings
|
||||
import app.myzel394.alibi.db.NotificationSettings
|
||||
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms.LandingElement
|
||||
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms.NotificationPresetSelect
|
||||
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.molecules.EditNotificationInput
|
||||
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.organisms.NotificationEditor
|
||||
import app.myzel394.alibi.ui.components.atoms.RotateDeviceToPortrait
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@ -80,9 +71,10 @@ fun CustomRecordingNotificationsScreen(
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onBackNavigate) {
|
||||
val label = stringResource(R.string.goBack)
|
||||
Icon(
|
||||
Icons.Default.ArrowBack,
|
||||
contentDescription = "Back"
|
||||
Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = label,
|
||||
)
|
||||
}
|
||||
},
|
||||
@ -92,6 +84,10 @@ fun CustomRecordingNotificationsScreen(
|
||||
modifier = Modifier
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
) { padding ->
|
||||
val orientation = LocalConfiguration.current.orientation
|
||||
|
||||
when (orientation) {
|
||||
Configuration.ORIENTATION_PORTRAIT -> {
|
||||
if (showEditor) {
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
@ -121,4 +117,17 @@ fun CustomRecordingNotificationsScreen(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(padding),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
RotateDeviceToPortrait()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
@ -38,21 +38,21 @@ import app.myzel394.alibi.dataStore
|
||||
import app.myzel394.alibi.ui.SUPPORTS_DARK_MODE_NATIVELY
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AboutTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderEncoderTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.VideoRecorderFrameRateTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderOutputFormatTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderSamplingRateTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderShowAllMicrophonesTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.CustomNotificationTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.DeleteRecordingsImmediatelyTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.DividerTitle
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.VideoRecorderQualityTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.EnableAppLockTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.ImportExport
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.atoms.InAppLanguagePicker
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.IntervalDurationTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.MaxDurationTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderOutputFormatTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderSamplingRateTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.SaveFolderTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderShowAllMicrophonesTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.EnableAppLockTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.VideoRecorderBitrateTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.VideoRecorderFrameRateTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.VideoRecorderQualityTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.atoms.InAppLanguagePicker
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.atoms.ThemeSelector
|
||||
import app.myzel394.alibi.ui.components.atoms.GlobalSwitch
|
||||
import app.myzel394.alibi.ui.components.atoms.MessageBox
|
||||
@ -99,9 +99,10 @@ fun SettingsScreen(
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onBackNavigate) {
|
||||
val label = stringResource(R.string.goBack)
|
||||
Icon(
|
||||
Icons.Default.ArrowBack,
|
||||
contentDescription = "Back"
|
||||
Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = label,
|
||||
)
|
||||
}
|
||||
},
|
||||
|
@ -1,4 +1,4 @@
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="app_name">Alibi</string>
|
||||
|
||||
<string name="dialog_close_cancel_label">Cancel</string>
|
||||
@ -189,4 +189,6 @@
|
||||
<string name="ui_settings_option_saveFolder_explainMediaFolder_openFolderExplanation">Tap on a folder to open it in the Files app</string>
|
||||
<string name="ui_videoRecorder_action_start_settings_cameraLens_unknown_label">Unknown</string>
|
||||
<string name="ui_settings_option_saveFolder_explainInternalFolder_explanation">To protect your privacy, Alibi stores its batches into its own private, encrypted storage. This storage is only accessible by Alibi and can\'t be accessed by other apps or by a possible intruder. Once you save the recording, you will be asked where you want to save the recording to.</string>
|
||||
<string name="ui_rotateDevice_portrait_label">Please rotate your device to portait mode</string>
|
||||
<string name="goBack">Back</string>
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user