From 055ab2a3a7ab94b3b769e6156a77dc271bcd8481 Mon Sep 17 00:00:00 2001 From: algosketch Date: Sun, 29 Dec 2024 12:32:00 +0900 Subject: [PATCH 01/14] =?UTF-8?q?fix=20:=20=EC=9E=85=EC=9E=A5=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=AC=B8=EA=B5=AC=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- presentation/src/main/res/values/strings.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 626bf778b..beae2619a 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -320,10 +320,10 @@ 링크를 삭제했어요 유효한 링크가 아니에요 - - QR 스캔 - 주최한 공연이 없어요 - 공연을 주최하고 QR 스캐너로\n관객 입장을 관리해 보세요 + + 입장 확인 + 입장을 확인할 공연이 없어요 + 공연을 주최하고 QR 코드로\n관객 입장을 확인해 보세요 사용되었어요 From 4fd32ca4724f07f64d0c8fa68b9849346d8a1529 Mon Sep 17 00:00:00 2001 From: algosketch Date: Sun, 29 Dec 2024 13:02:33 +0900 Subject: [PATCH 02/14] =?UTF-8?q?fix=20:=20=EC=A7=80=EB=82=9C=20=EA=B3=B5?= =?UTF-8?q?=EC=97=B0=20=EB=B9=84=ED=99=9C=EC=84=B1=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/network/response/HostedShowDto.kt | 11 +++-- .../screen/qr/HostedShowScreen.kt | 47 +++++++++++++------ 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/data/src/main/java/com/nexters/boolti/data/network/response/HostedShowDto.kt b/data/src/main/java/com/nexters/boolti/data/network/response/HostedShowDto.kt index 29708fe86..7ed2383f3 100644 --- a/data/src/main/java/com/nexters/boolti/data/network/response/HostedShowDto.kt +++ b/data/src/main/java/com/nexters/boolti/data/network/response/HostedShowDto.kt @@ -1,5 +1,7 @@ package com.nexters.boolti.data.network.response +import com.nexters.boolti.data.util.toLocalDate +import com.nexters.boolti.data.util.toLocalDateTime import com.nexters.boolti.domain.model.Show import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -10,13 +12,16 @@ import java.time.LocalDateTime internal data class HostedShowDto( @SerialName("showId") val showId: String, @SerialName("showName") val showName: String, + @SerialName("date") val date: String, + @SerialName("salesStartTime") val salesStartDate: String, + @SerialName("salesEndTime") val salesEndDate: String, ) { fun toDomain(): Show = Show( id = showId, name = showName, - date = LocalDateTime.now(), - salesStartDate = LocalDate.now(), - salesEndDate = LocalDate.now(), + date = date.toLocalDateTime(), + salesStartDate = salesStartDate.toLocalDate(), + salesEndDate = salesEndDate.toLocalDate(), thumbnailImage = "", ) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowScreen.kt index 003e23b47..8f0c8cd40 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowScreen.kt @@ -35,7 +35,7 @@ import com.nexters.boolti.presentation.R import com.nexters.boolti.presentation.component.BtBackAppBar import com.nexters.boolti.presentation.theme.BooltiTheme import com.nexters.boolti.presentation.theme.Grey30 -import com.nexters.boolti.presentation.theme.Grey60 +import com.nexters.boolti.presentation.theme.Grey50 import com.nexters.boolti.presentation.theme.point1 import java.time.LocalDate import java.time.LocalDateTime @@ -94,7 +94,7 @@ private fun HostedShowItem( onClick: (showId: String, showName: String) -> Unit, ) { val enable = LocalDate.now().toEpochDay() <= show.date.toLocalDate().toEpochDay() - val tint = if (enable) White else Grey60 + val tint = if (enable) White else Grey50 Row( modifier = Modifier @@ -117,18 +117,6 @@ private fun HostedShowItem( } } -@Preview -@Composable -fun HostedShowItemPreview() { - BooltiTheme { - Surface { - HostedShowItem( - Show("", "hello world", LocalDateTime.now(), LocalDate.now(), LocalDate.now(), "") - ) { _, _ -> } - } - } -} - @Composable fun EmptyHostedShow( modifier: Modifier, @@ -161,3 +149,34 @@ fun EmptyHostedShow( } } } + +@Preview +@Composable +fun HostedShowItemPreview() { + BooltiTheme { + Surface { + HostedShowItem( + Show("", "hello world", LocalDateTime.now(), LocalDate.now(), LocalDate.now(), "") + ) { _, _ -> } + } + } +} + +@Preview +@Composable +fun OutDatedHostedShowItemPreview() { + BooltiTheme { + Surface { + HostedShowItem( + Show( + "", + "hello world", + LocalDateTime.now().minusDays(1), + LocalDate.now(), + LocalDate.now(), + "" + ) + ) { _, _ -> } + } + } +} \ No newline at end of file From 7ec1ca2ae7a712bde97caa5e9fa60e05d3eee6e7 Mon Sep 17 00:00:00 2001 From: algosketch Date: Sun, 29 Dec 2024 13:26:42 +0900 Subject: [PATCH 03/14] =?UTF-8?q?fix=20:=20QR=20=EC=8A=A4=EC=BA=94=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20UI=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/screen/qr/QrScanScreen.kt | 37 +++++++++++++---- .../src/main/res/drawable/ic_camera_flip.xml | 41 +++++++++++++++++++ presentation/src/main/res/values/strings.xml | 3 +- 3 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 presentation/src/main/res/drawable/ic_camera_flip.xml diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt index 56343eee3..0b191d1ec 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt @@ -3,7 +3,7 @@ package com.nexters.boolti.presentation.screen.qr import androidx.compose.foundation.background import androidx.compose.foundation.clickable 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 @@ -38,10 +38,12 @@ import com.nexters.boolti.presentation.QrScanEvent import com.nexters.boolti.presentation.QrScanViewModel import com.nexters.boolti.presentation.R import com.nexters.boolti.presentation.component.BTDialog -import com.nexters.boolti.presentation.component.BtCloseableAppBar +import com.nexters.boolti.presentation.component.BtAppBar +import com.nexters.boolti.presentation.component.BtAppBarDefaults import com.nexters.boolti.presentation.component.CircleBgIcon import com.nexters.boolti.presentation.component.ToastSnackbarHost import com.nexters.boolti.presentation.theme.Error +import com.nexters.boolti.presentation.theme.Grey30 import com.nexters.boolti.presentation.theme.Grey50 import com.nexters.boolti.presentation.theme.Success import com.nexters.boolti.presentation.theme.Warning @@ -91,9 +93,20 @@ fun QrScanScreen( Scaffold( topBar = { - BtCloseableAppBar( + BtAppBar( title = uiState.showName, - onClickClose = onClickClose, + navigateButtons = { + BtAppBarDefaults.AppBarIconButton( + onClick = onClickClose, + iconRes = R.drawable.ic_arrow_back, + ) + }, + actionButtons = { + BtAppBarDefaults.AppBarIconButton( + onClick = {}, + iconRes = R.drawable.ic_camera_flip, + ) + } ) }, bottomBar = { @@ -136,12 +149,20 @@ fun QrScanScreen( @Composable private fun QrScanBottombar(onClick: () -> Unit) { - Box( + Column( modifier = Modifier .fillMaxWidth() .background(MaterialTheme.colorScheme.background), - contentAlignment = Alignment.Center, + horizontalAlignment = Alignment.CenterHorizontally, ) { + Text( + modifier = Modifier.padding(top = 20.dp), + text = stringResource(R.string.entry_code_notice), + textAlign = TextAlign.Center, + style = MaterialTheme.typography.bodySmall.copy( + color = Grey50, + ), + ) Row( modifier = Modifier .clickable(onClick = onClick) @@ -154,13 +175,13 @@ private fun QrScanBottombar(onClick: () -> Unit) { .size(20.dp) .padding(end = 4.dp), painter = painterResource(id = R.drawable.ic_book), - tint = Grey50, + tint = Grey30, contentDescription = stringResource(R.string.show_entry_code), ) Text( text = stringResource(id = R.string.show_entry_code), style = MaterialTheme.typography.bodySmall, - color = Grey50, + color = Grey30, ) } } diff --git a/presentation/src/main/res/drawable/ic_camera_flip.xml b/presentation/src/main/res/drawable/ic_camera_flip.xml new file mode 100644 index 000000000..63fbd7efc --- /dev/null +++ b/presentation/src/main/res/drawable/ic_camera_flip.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index beae2619a..2256747a3 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -241,7 +241,8 @@ 입장 코드는 주최자 계정의 마이 > QR 스캔 > 해당 공연 스캐너에서 확인 가능해요. 입장 코드를 입력해 주세요 올바른 입장 코드를 입력해 주세요 - 입장코드 보기 + 입장 코드 보기 + QR 코드가 인식되지 않을 경우 티켓 화면 하단의\n\'입장 코드 입력하기\'에 아래 입장 코드를 입력해 주세요. 코드 복사 식별 코드를 복사했어요 From dcb34f137977054452a0fb472fb8be79e1c932ba Mon Sep 17 00:00:00 2001 From: HamBP Date: Tue, 7 Jan 2025 23:11:29 +0900 Subject: [PATCH 04/14] =?UTF-8?q?feat=20:=20QR=20=EC=9D=B8=EC=8B=9D=20?= =?UTF-8?q?=EC=8B=9C=20=EC=A7=84=EB=8F=99=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- presentation/src/main/AndroidManifest.xml | 1 + .../boolti/presentation/QrScanActivity.kt | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/presentation/src/main/AndroidManifest.xml b/presentation/src/main/AndroidManifest.xml index ad4f4ba0d..a7cb6c9ab 100644 --- a/presentation/src/main/AndroidManifest.xml +++ b/presentation/src/main/AndroidManifest.xml @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt b/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt index d892d3738..d987da434 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt @@ -1,7 +1,12 @@ package com.nexters.boolti.presentation import android.Manifest +import android.content.Context +import android.os.Build import android.os.Bundle +import android.os.VibrationEffect +import android.os.Vibrator +import android.os.VibratorManager import android.view.KeyEvent import androidx.activity.ComponentActivity import androidx.activity.compose.setContent @@ -40,6 +45,21 @@ class QrScanActivity : ComponentActivity() { private val callback = BarcodeCallback { result: BarcodeResult -> result.text ?: return@BarcodeCallback viewModel.scan(result.text) + + val vibrator = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + (getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager).defaultVibrator + } else { + getSystemService(Context.VIBRATOR_SERVICE) as Vibrator + } + + vibrator.vibrate( + VibrationEffect.createOneShot( + 100, + VibrationEffect.DEFAULT_AMPLITUDE + ) + ) + lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { barcodeView.pause() From 61eb6ecd98ff19c26ad4e1fec27e38919642f748 Mon Sep 17 00:00:00 2001 From: HamBP Date: Tue, 7 Jan 2025 23:53:35 +0900 Subject: [PATCH 05/14] =?UTF-8?q?feat=20:=20=EC=A0=84=EB=A9=B4=20=EC=B9=B4?= =?UTF-8?q?=EB=A9=94=EB=9D=BC=20=EC=8A=A4=EC=9C=84=EC=B9=AD=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/QrScanActivity.kt | 16 +++++++++++++++- .../presentation/screen/qr/QrScanScreen.kt | 3 ++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt b/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt index d987da434..c9be2ce2e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt @@ -2,6 +2,7 @@ package com.nexters.boolti.presentation import android.Manifest import android.content.Context +import android.hardware.Camera import android.os.Build import android.os.Bundle import android.os.VibrationEffect @@ -29,6 +30,7 @@ import kotlinx.coroutines.launch @AndroidEntryPoint class QrScanActivity : ComponentActivity() { + var isBackCamera = true private val barcodeView: DecoratedBarcodeView by lazy { DecoratedBarcodeView(this).apply { @@ -78,7 +80,8 @@ class QrScanActivity : ComponentActivity() { BooltiTheme { QrScanScreen( barcodeView = barcodeView, - onClickClose = { finish() } + onClickClose = { finish() }, + onClickSwitchCamera = ::switchCamera ) } } @@ -97,4 +100,15 @@ class QrScanActivity : ComponentActivity() { override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { return barcodeView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event) } + + private fun switchCamera() { + barcodeView.pause() + isBackCamera = !isBackCamera + barcodeView.cameraSettings.requestedCameraId = if (isBackCamera) { + Camera.CameraInfo.CAMERA_FACING_BACK + } else { + Camera.CameraInfo.CAMERA_FACING_FRONT + } + barcodeView.resume() + } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt index 0b191d1ec..8ccda6e12 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt @@ -54,6 +54,7 @@ fun QrScanScreen( barcodeView: DecoratedBarcodeView, viewModel: QrScanViewModel = hiltViewModel(), onClickClose: () -> Unit, + onClickSwitchCamera: () -> Unit, ) { var showEntryCodeDialog by remember { mutableStateOf(false) } val snackbarHostState = remember { SnackbarHostState() } @@ -103,7 +104,7 @@ fun QrScanScreen( }, actionButtons = { BtAppBarDefaults.AppBarIconButton( - onClick = {}, + onClick = onClickSwitchCamera, iconRes = R.drawable.ic_camera_flip, ) } From aab014ffcfbc9485bf1146fa41ba823fb8d7fbb9 Mon Sep 17 00:00:00 2001 From: HamBP Date: Thu, 9 Jan 2025 21:50:20 +0900 Subject: [PATCH 06/14] =?UTF-8?q?fix=20:=20QR=20=ED=99=95=EC=9D=B8=20?= =?UTF-8?q?=EC=8B=9C=20=EC=8A=A4=EB=82=B5=20=EB=B0=94=20=EC=83=81=EB=8B=A8?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/screen/qr/QrScanScreen.kt | 71 +++++++++++++------ presentation/src/main/res/values/strings.xml | 4 +- 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt index 8ccda6e12..64f37773e 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt @@ -3,10 +3,12 @@ package com.nexters.boolti.presentation.screen.qr import androidx.compose.foundation.background import androidx.compose.foundation.clickable 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.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape @@ -28,6 +30,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import androidx.hilt.navigation.compose.hiltViewModel @@ -67,6 +70,7 @@ fun QrScanScreen( val notMatchedErrMessage = stringResource(R.string.error_ticket_not_matched) val uiState by viewModel.uiState.collectAsStateWithLifecycle() + var bottomPadding by remember { mutableStateOf(0.dp) } LaunchedEffect(barcodeView) { barcodeView.resume() @@ -78,9 +82,20 @@ fun QrScanScreen( val (iconId, errMessage) = when (event) { is QrScanEvent.ScanError -> { when (event.errorType) { - QrErrorType.ShowNotToday -> Pair(R.drawable.ic_warning, notTodayErrMessage) - QrErrorType.UsedTicket -> Pair(R.drawable.ic_error, usedTicketErrMessage) - QrErrorType.TicketNotFound -> Pair(R.drawable.ic_error, notMatchedErrMessage) + QrErrorType.ShowNotToday -> Pair( + R.drawable.ic_warning, + notTodayErrMessage + ) + + QrErrorType.UsedTicket -> Pair( + R.drawable.ic_error, + usedTicketErrMessage + ) + + QrErrorType.TicketNotFound -> Pair( + R.drawable.ic_error, + notMatchedErrMessage + ) } } @@ -114,24 +129,37 @@ fun QrScanScreen( QrScanBottombar { showEntryCodeDialog = true } }, snackbarHost = { - ToastSnackbarHost( - hostState = snackbarHostState, - modifier = Modifier.padding(bottom = 100.dp), - leadingIcon = { - snackbarIconId?.let { - CircleBgIcon( - painter = painterResource(it), - bgColor = when (it) { - R.drawable.ic_check -> Success - R.drawable.ic_error -> Error - else -> Warning - } - ) - } - }, - ) + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.TopCenter, + ) { + ToastSnackbarHost( + modifier = Modifier + .offset { // Scaffold의 inner padding 만큼 상단을 뚫고 나가는 문제가 있음. 해당 값 보정. + IntOffset(0, bottomPadding.toPx().toInt()) + } + .padding(top = 18.dp + 44.dp), // 44.dp 는 top bar 높이 값 수동 계산 + hostState = snackbarHostState, + leadingIcon = { + snackbarIconId?.let { + CircleBgIcon( + painter = painterResource(it), + bgColor = when (it) { + R.drawable.ic_check -> Success + R.drawable.ic_error -> Error + else -> Warning + } + ) + } + }, + ) + } }, ) { innerPadding -> + LaunchedEffect(innerPadding) { + bottomPadding = innerPadding.calculateBottomPadding() + } + AndroidView( modifier = Modifier .padding(innerPadding) @@ -194,7 +222,10 @@ private fun EntryCodeDialog( onDismiss: () -> Unit, ) { BTDialog(showCloseButton = false, onDismiss = onDismiss, onClickPositiveButton = onDismiss) { - Text(text = stringResource(R.string.manager_code), style = MaterialTheme.typography.titleLarge) + Text( + text = stringResource(R.string.manager_code), + style = MaterialTheme.typography.titleLarge + ) Text( modifier = Modifier .padding(top = 24.dp) diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 2256747a3..faf3ac693 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -18,7 +18,7 @@ 모두 지우기 아직 공연일이 아니에요 - 이미 사용된 티켓이에요 + 이미 입장에 사용한 티켓이에요 이 공연의 티켓이 아니에요 존재하지 않는 티켓이에요 @@ -325,7 +325,7 @@ 입장 확인 입장을 확인할 공연이 없어요 공연을 주최하고 QR 코드로\n관객 입장을 확인해 보세요 - 사용되었어요 + 입장을 확인했어요 결제 내역이 없어요 From 8a362502825be2e3abd7fa88d916ccde3e6b4673 Mon Sep 17 00:00:00 2001 From: HamBP Date: Thu, 9 Jan 2025 22:47:12 +0900 Subject: [PATCH 07/14] =?UTF-8?q?feat=20:=20QR=20=EC=9D=B8=EC=8B=9D=20?= =?UTF-8?q?=EC=8B=9C=20=ED=85=8C=EB=91=90=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/screen/qr/QrScanScreen.kt | 72 ++++++++++++------- 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt index 64f37773e..13012e941 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt @@ -1,6 +1,7 @@ package com.nexters.boolti.presentation.screen.qr import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -15,6 +16,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -27,6 +29,7 @@ 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.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign @@ -50,6 +53,7 @@ import com.nexters.boolti.presentation.theme.Grey30 import com.nexters.boolti.presentation.theme.Grey50 import com.nexters.boolti.presentation.theme.Success import com.nexters.boolti.presentation.theme.Warning +import kotlinx.coroutines.delay import kotlinx.coroutines.launch @Composable @@ -62,7 +66,6 @@ fun QrScanScreen( var showEntryCodeDialog by remember { mutableStateOf(false) } val snackbarHostState = remember { SnackbarHostState() } var snackbarIconId by remember { mutableStateOf(null) } - val scope = rememberCoroutineScope() val successMessage = stringResource(R.string.message_ticket_validated) val notTodayErrMessage = stringResource(R.string.error_show_not_today) @@ -71,39 +74,54 @@ fun QrScanScreen( val uiState by viewModel.uiState.collectAsStateWithLifecycle() var bottomPadding by remember { mutableStateOf(0.dp) } + var borderColor: Color by remember { mutableStateOf(Color.Transparent) } LaunchedEffect(barcodeView) { barcodeView.resume() } LaunchedEffect(viewModel.event) { - scope.launch { - viewModel.event.collect { event -> - val (iconId, errMessage) = when (event) { - is QrScanEvent.ScanError -> { - when (event.errorType) { - QrErrorType.ShowNotToday -> Pair( - R.drawable.ic_warning, - notTodayErrMessage - ) + viewModel.event.collect { event -> + val (iconId, errMessage, color) = when (event) { + is QrScanEvent.ScanError -> { + when (event.errorType) { + QrErrorType.ShowNotToday -> Triple( + R.drawable.ic_warning, + notTodayErrMessage, + Warning + ) - QrErrorType.UsedTicket -> Pair( - R.drawable.ic_error, - usedTicketErrMessage - ) + QrErrorType.UsedTicket -> Triple( + R.drawable.ic_error, + usedTicketErrMessage, + Error + ) - QrErrorType.TicketNotFound -> Pair( - R.drawable.ic_error, - notMatchedErrMessage - ) - } + QrErrorType.TicketNotFound -> Triple( + R.drawable.ic_error, + notMatchedErrMessage, + Error + ) } - - is QrScanEvent.ScanSuccess -> Pair(R.drawable.ic_error, successMessage) } - snackbarIconId = iconId + + is QrScanEvent.ScanSuccess -> Triple( + R.drawable.ic_error, + successMessage, + Success + ) + } + snackbarIconId = iconId + + launch { snackbarHostState.showSnackbar(errMessage) } + + launch { + borderColor = color + delay(4000L) + borderColor = Color.Transparent + } } } @@ -136,7 +154,12 @@ fun QrScanScreen( ToastSnackbarHost( modifier = Modifier .offset { // Scaffold의 inner padding 만큼 상단을 뚫고 나가는 문제가 있음. 해당 값 보정. - IntOffset(0, bottomPadding.toPx().toInt()) + IntOffset( + 0, + bottomPadding + .toPx() + .toInt() + ) } .padding(top = 18.dp + 44.dp), // 44.dp 는 top bar 높이 값 수동 계산 hostState = snackbarHostState, @@ -163,7 +186,8 @@ fun QrScanScreen( AndroidView( modifier = Modifier .padding(innerPadding) - .fillMaxSize(), + .fillMaxSize() + .border(width = 2.dp, color = borderColor), factory = { barcodeView }, ) From 29514c9dec46b755e6f213234a54a6328fd5621d Mon Sep 17 00:00:00 2001 From: HamBP Date: Thu, 9 Jan 2025 23:01:08 +0900 Subject: [PATCH 08/14] =?UTF-8?q?fix=20:=20=EC=9E=91=EC=9D=80=20ui=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=82=AC=ED=95=AD=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nexters/boolti/presentation/component/ToastSnackbarHost.kt | 2 +- .../com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/component/ToastSnackbarHost.kt b/presentation/src/main/java/com/nexters/boolti/presentation/component/ToastSnackbarHost.kt index f5b0e0138..b6cb025a6 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/component/ToastSnackbarHost.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/component/ToastSnackbarHost.kt @@ -38,7 +38,7 @@ fun ToastSnackbarHost( ) { if (leadingIcon != null) { leadingIcon() - Spacer(modifier = Modifier.padding(end = 12.dp)) + Spacer(modifier = Modifier.padding(end = 8.dp)) } Text( modifier = Modifier.padding(vertical = 12.dp), diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt index 13012e941..57c071bb5 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt @@ -233,7 +233,7 @@ private fun QrScanBottombar(onClick: () -> Unit) { ) Text( text = stringResource(id = R.string.show_entry_code), - style = MaterialTheme.typography.bodySmall, + style = MaterialTheme.typography.titleSmall, color = Grey30, ) } From 1b8f3074ebc9cd4c91d59ed0fcdacca2066ae830 Mon Sep 17 00:00:00 2001 From: HamBP Date: Mon, 13 Jan 2025 00:13:49 +0900 Subject: [PATCH 09/14] =?UTF-8?q?fix=20:=20=EB=B0=94=ED=85=80=20=EB=82=B4?= =?UTF-8?q?=EB=B9=84=EA=B2=8C=EC=9D=B4=EC=85=98=20=EB=8B=A4=ED=81=AC=20?= =?UTF-8?q?=EB=AA=A8=EB=93=9C=EB=A1=9C=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/theme/Theme.kt | 37 +++++++------------ 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/theme/Theme.kt b/presentation/src/main/java/com/nexters/boolti/presentation/theme/Theme.kt index 66b58cffe..699b26ac3 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/theme/Theme.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/theme/Theme.kt @@ -1,9 +1,15 @@ package com.nexters.boolti.presentation.theme +import android.app.Activity import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalView +import androidx.core.view.ViewCompat +import androidx.core.view.WindowCompat private val DarkColorScheme = darkColorScheme( primary = Orange01, @@ -19,39 +25,22 @@ private val DarkColorScheme = darkColorScheme( onSurfaceVariant = Grey15, outline = Grey80, outlineVariant = Grey10, - -// secondary = PurpleGrey80, -// tertiary = Pink80 -) - -private val LightColorScheme = lightColorScheme( -// primary = Purple40, -// secondary = PurpleGrey40, -// tertiary = Pink40 - - /* Other default colors to override - background = Color(0xFFFFFBFE), - surface = Color(0xFFFFFBFE), - onPrimary = Color.White, - onSecondary = Color.White, - onTertiary = Color.White, - onBackground = Color(0xFF1C1B1F), - onSurface = Color(0xFF1C1B1F), - */ ) @Composable fun BooltiTheme( - darkTheme: Boolean = true, // FIXME light theme이 추가된다면 이 부분을 isSystemInDarkTheme()로 수정하세요. content: @Composable () -> Unit ) { - val colorScheme = when { - darkTheme -> DarkColorScheme - else -> LightColorScheme + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + val window = (view.context as Activity).window + WindowCompat.getInsetsController(window, view).isAppearanceLightNavigationBars = false + } } MaterialTheme( - colorScheme = colorScheme, + colorScheme = DarkColorScheme, typography = Typography, content = content ) From 26de053e4302a345f9c8dc0db0b81c27fbfce565 Mon Sep 17 00:00:00 2001 From: HamBP Date: Mon, 13 Jan 2025 21:46:59 +0900 Subject: [PATCH 10/14] =?UTF-8?q?refactor=20:=20vibrator=20=EA=B5=AC?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=ED=99=95=EC=9E=A5=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=8D=BC=ED=8B=B0=20=EC=B6=94=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nexters/boolti/presentation/QrScanActivity.kt | 8 +------- .../nexters/boolti/presentation/extension/Context.kt | 12 ++++++++++++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt b/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt index 3f6f2b2b4..b9aa14554 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt @@ -25,6 +25,7 @@ import com.journeyapps.barcodescanner.BarcodeResult import com.journeyapps.barcodescanner.DecoratedBarcodeView import com.journeyapps.barcodescanner.DefaultDecoderFactory import com.nexters.boolti.presentation.extension.requestPermission +import com.nexters.boolti.presentation.extension.vibrator import com.nexters.boolti.presentation.screen.qr.QrScanScreen import com.nexters.boolti.presentation.theme.BooltiTheme import dagger.hilt.android.AndroidEntryPoint @@ -51,13 +52,6 @@ class QrScanActivity : ComponentActivity() { result.text ?: return@BarcodeCallback viewModel.scan(result.text) - val vibrator = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - (getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager).defaultVibrator - } else { - getSystemService(Context.VIBRATOR_SERVICE) as Vibrator - } - vibrator.vibrate( VibrationEffect.createOneShot( 100, diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/extension/Context.kt b/presentation/src/main/java/com/nexters/boolti/presentation/extension/Context.kt index 6a71a2ead..ccab08655 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/extension/Context.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/extension/Context.kt @@ -4,7 +4,12 @@ import android.app.Activity import android.content.Context import android.content.ContextWrapper import android.content.pm.PackageManager +import android.os.Build +import android.os.VibrationEffect +import android.os.Vibrator +import android.os.VibratorManager import androidx.core.content.ContextCompat +import androidx.core.content.ContextCompat.getSystemService fun Context.requireActivity(): Activity { var ctx = this @@ -24,3 +29,10 @@ fun Context.requireActivity(): Activity { fun Context.checkGrantedPermission(permission: String): Boolean { return ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED } + +val Context.vibrator: Vibrator + get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + (getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager).defaultVibrator + } else { + getSystemService(Context.VIBRATOR_SERVICE) as Vibrator + } \ No newline at end of file From 60a20491c0e5abb8f0198f8a3e399a58e59dea41 Mon Sep 17 00:00:00 2001 From: HamBP Date: Mon, 13 Jan 2025 21:56:38 +0900 Subject: [PATCH 11/14] =?UTF-8?q?fix=20:=20=EC=A3=BC=EC=B5=9C=ED=95=9C=20?= =?UTF-8?q?=EA=B3=B5=EC=97=B0=EC=9D=B4=20=EC=97=86=EC=9D=84=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=20=ED=99=94=EB=A9=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/screen/qr/HostedShowScreen.kt | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowScreen.kt index e828e0f3b..37665aa0c 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/HostedShowScreen.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding @@ -60,7 +61,9 @@ fun HostedShowScreen( } ) { innerPadding -> if (uiState.shows.isEmpty()) { - EmptyHostedShow(modifier = modifier.padding(innerPadding)) + EmptyHostedShow(modifier = modifier + .padding(innerPadding) + .fillMaxSize()) } else { HostedShows( modifier = modifier @@ -181,4 +184,16 @@ fun OutDatedHostedShowItemPreview() { ) { _, _ -> } } } +} + +@Preview(widthDp = 360, heightDp = 760) +@Composable +fun EmptyShowItemPreview() { + BooltiTheme { + Surface { + EmptyHostedShow( + Modifier + ) + } + } } \ No newline at end of file From 89d5898dd925fdd39cc131a35ff2713032d2ea14 Mon Sep 17 00:00:00 2001 From: HamBP Date: Mon, 13 Jan 2025 23:58:15 +0900 Subject: [PATCH 12/14] =?UTF-8?q?fix=20:=204=EC=B4=88=20=EC=9D=B4=EC=A0=84?= =?UTF-8?q?=EC=97=90=20QR=EC=9D=84=20=EC=9D=B8=EC=8B=9D=ED=95=A0=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=EC=9D=B4=EC=A0=84=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=EC=99=80=20=ED=85=8C=EB=91=90=EB=A6=AC=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nexters/boolti/presentation/screen/qr/QrScanScreen.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt index b8ca28c65..8826976ce 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt @@ -54,6 +54,7 @@ import com.nexters.boolti.presentation.theme.Grey30 import com.nexters.boolti.presentation.theme.Grey50 import com.nexters.boolti.presentation.theme.Success import com.nexters.boolti.presentation.theme.Warning +import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -76,6 +77,7 @@ fun QrScanScreen( val uiState by viewModel.uiState.collectAsStateWithLifecycle() var bottomPadding by remember { mutableStateOf(0.dp) } var borderColor: Color by remember { mutableStateOf(Color.Transparent) } + var showingBorderJob: Job? = null LaunchedEffect(barcodeView) { barcodeView.resume() @@ -115,10 +117,12 @@ fun QrScanScreen( snackbarIconId = iconId launch { + snackbarHostState.currentSnackbarData?.dismiss() snackbarHostState.showSnackbar(errMessage) } - launch { + showingBorderJob?.cancel() + showingBorderJob = launch { borderColor = color delay(4000L) borderColor = Color.Transparent From 797fb33fe77c417b8224c1340a7a84c21980649a Mon Sep 17 00:00:00 2001 From: HamBP Date: Tue, 14 Jan 2025 00:10:34 +0900 Subject: [PATCH 13/14] =?UTF-8?q?fix=20:=20painter=20->=20image=20vector?= =?UTF-8?q?=20=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nexters/boolti/presentation/component/CircleBgIcon.kt | 5 +++-- .../nexters/boolti/presentation/screen/qr/QrScanScreen.kt | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/component/CircleBgIcon.kt b/presentation/src/main/java/com/nexters/boolti/presentation/component/CircleBgIcon.kt index 3c68a133f..5e5959e0f 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/component/CircleBgIcon.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/component/CircleBgIcon.kt @@ -9,11 +9,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.graphics.vector.ImageVector @Composable fun CircleBgIcon( modifier: Modifier = Modifier, - painter: Painter, + imageVector: ImageVector, bgColor: Color, ) { Box( @@ -21,6 +22,6 @@ fun CircleBgIcon( .clip(CircleShape) .background(bgColor) ) { - Icon(painter = painter, contentDescription = null) + Icon(imageVector = imageVector, contentDescription = null) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt index 8826976ce..fc23e62ad 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt @@ -31,8 +31,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp @@ -172,7 +174,7 @@ fun QrScanScreen( leadingIcon = { snackbarIconId?.let { CircleBgIcon( - painter = painterResource(it), + imageVector = ImageVector.vectorResource(it), bgColor = when (it) { R.drawable.ic_check -> Success R.drawable.ic_error -> Error From 7af0d0f6bd972c535fc1819d8b0f1c0c0eee5421 Mon Sep 17 00:00:00 2001 From: HamBP Date: Tue, 14 Jan 2025 00:45:10 +0900 Subject: [PATCH 14/14] =?UTF-8?q?fix=20:=20=EC=A0=84=ED=9B=84=EB=A9=B4=20?= =?UTF-8?q?=EC=B9=B4=EB=A9=94=EB=9D=BC=EA=B0=80=20=EB=91=98=20=EB=8B=A4=20?= =?UTF-8?q?=EC=9E=88=EC=9D=84=20=EA=B2=BD=EC=9A=B0=EC=97=90=EB=A7=8C=20?= =?UTF-8?q?=EC=B9=B4=EB=A9=94=EB=9D=BC=20=EB=B3=80=EA=B2=BD=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20=EB=85=B8=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/presentation/QrScanActivity.kt | 8 +--- .../presentation/screen/qr/QrScanScreen.kt | 45 ++++++++++++++++--- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt b/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt index b9aa14554..74e0fe15d 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/QrScanActivity.kt @@ -1,14 +1,10 @@ package com.nexters.boolti.presentation import android.Manifest -import android.content.Context -import android.hardware.Camera -import android.os.Build import android.graphics.Color +import android.hardware.Camera import android.os.Bundle import android.os.VibrationEffect -import android.os.Vibrator -import android.os.VibratorManager import android.view.KeyEvent import androidx.activity.ComponentActivity import androidx.activity.SystemBarStyle @@ -34,7 +30,7 @@ import kotlinx.coroutines.launch @AndroidEntryPoint class QrScanActivity : ComponentActivity() { - var isBackCamera = true + private var isBackCamera = true private val barcodeView: DecoratedBarcodeView by lazy { DecoratedBarcodeView(this).apply { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt index fc23e62ad..d1faec25f 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/qr/QrScanScreen.kt @@ -1,5 +1,8 @@ package com.nexters.boolti.presentation.screen.qr +import android.content.Context +import android.hardware.camera2.CameraCharacteristics +import android.hardware.camera2.CameraManager import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable @@ -9,15 +12,14 @@ 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.offset import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold -import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -25,13 +27,14 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -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.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource @@ -81,6 +84,9 @@ fun QrScanScreen( var borderColor: Color by remember { mutableStateOf(Color.Transparent) } var showingBorderJob: Job? = null + val context = LocalContext.current + val cameraSwitchable = rememberSaveable { hasBothSidesCameras(context) } + LaunchedEffect(barcodeView) { barcodeView.resume() } @@ -144,10 +150,12 @@ fun QrScanScreen( ) }, actionButtons = { - BtAppBarDefaults.AppBarIconButton( - onClick = onClickSwitchCamera, - iconRes = R.drawable.ic_camera_flip, - ) + if (cameraSwitchable) { + BtAppBarDefaults.AppBarIconButton( + onClick = onClickSwitchCamera, + iconRes = R.drawable.ic_camera_flip, + ) + } } ) }, @@ -271,3 +279,26 @@ private fun EntryCodeDialog( ) } } + +private fun hasBothSidesCameras(context: Context): Boolean { + val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager + val cameraIds = cameraManager.cameraIdList + + var hasFrontCamera = false + var hasBackCamera = false + + for (cameraId in cameraIds) { + val characteristics = cameraManager.getCameraCharacteristics(cameraId) + val lensFacing = characteristics.get(CameraCharacteristics.LENS_FACING) + + if (lensFacing == CameraCharacteristics.LENS_FACING_FRONT) { + hasFrontCamera = true + } + + if (lensFacing == CameraCharacteristics.LENS_FACING_BACK) { + hasBackCamera = true + } + } + + return hasFrontCamera && hasBackCamera +} \ No newline at end of file