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
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ android {
applicationId = "co.kr.tnt"
minSdk = 27
targetSdk = 35
versionCode = 3
versionCode = 5
versionName = "0.0.1"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import androidx.compose.runtime.Composable
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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
Expand Down Expand Up @@ -58,7 +59,7 @@ fun TnTSnackbar(
Icon(
painter = painterResource(icon),
contentDescription = null,
tint = TnTTheme.colors.neutralColors.Neutral50,
tint = Color.Unspecified,
modifier = Modifier.height(24.dp),
)
}
Expand Down
16 changes: 16 additions & 0 deletions core/designsystem/src/main/res/drawable/ic_check_success.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28">
<path
android:pathData="M2,14C2,7.373 7.373,2 14,2C20.627,2 26,7.373 26,14C26,20.627 20.627,26 14,26C7.373,26 2,20.627 2,14Z"
android:fillColor="#1ED45A"/>
<group>
<clip-path
android:pathData="M4,4h20v20h-20z"/>
<path
android:pathData="M11.958,16.93L19.157,9.731C19.281,9.607 19.426,9.544 19.592,9.541C19.758,9.538 19.906,9.602 20.035,9.731C20.165,9.86 20.229,10.009 20.229,10.177C20.229,10.344 20.165,10.493 20.035,10.622L12.486,18.185C12.335,18.335 12.159,18.41 11.958,18.41C11.757,18.41 11.582,18.335 11.431,18.185L7.952,14.705C7.828,14.581 7.767,14.434 7.769,14.264C7.77,14.094 7.836,13.944 7.965,13.814C8.094,13.685 8.243,13.62 8.41,13.62C8.578,13.62 8.727,13.685 8.856,13.814L11.958,16.93Z"
android:fillColor="#ffffff"/>
</group>
</vector>
29 changes: 5 additions & 24 deletions core/ui/src/main/java/co/kr/tnt/ui/coil/ResizeTransformation.kt
Original file line number Diff line number Diff line change
@@ -1,37 +1,18 @@
package co.kr.tnt.ui.coil

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import co.kr.tnt.ui.extensions.toResizedByteArray
import coil.size.Size
import coil.transform.Transformation
import java.io.ByteArrayOutputStream

class ResizeTransformation(
private val maxSizeInBytes: Int,
) : Transformation {
override val cacheKey: String = "resize_to_max_size_$maxSizeInBytes"

override suspend fun transform(input: Bitmap, size: Size): Bitmap {
var bitmap = input
var quality = 100

while (true) {
val byteArrayOutputStream = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, byteArrayOutputStream)
val compressedSize = byteArrayOutputStream.size()

if (compressedSize <= maxSizeInBytes) {
break
}

bitmap = Bitmap.createScaledBitmap(
bitmap,
(bitmap.width * 0.9).toInt(),
(bitmap.height * 0.9).toInt(),
true,
)
quality -= 5
override suspend fun transform(input: Bitmap, size: Size): Bitmap =
input.toResizedByteArray(maxSizeInBytes).let { resizedByteArray ->
BitmapFactory.decodeByteArray(resizedByteArray, 0, resizedByteArray.size)
}

return bitmap
}
}
1 change: 1 addition & 0 deletions core/ui/src/main/java/co/kr/tnt/ui/component/TnTLoading.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ fun TnTLoading(modifier: Modifier) {
) {
CircularProgressIndicator(
color = TnTTheme.colors.redColors.Red500,
trackColor = TnTTheme.colors.neutralColors.Neutral300,
)
}
}
32 changes: 32 additions & 0 deletions core/ui/src/main/java/co/kr/tnt/ui/extensions/Bitmap.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package co.kr.tnt.ui.extensions

import android.graphics.Bitmap
import java.io.ByteArrayOutputStream

fun Bitmap.toResizedByteArray(maxSizeInBytes: Int): ByteArray {
var resizedBitmap = this
var quality = 100

val byteArrayOutputStream = ByteArrayOutputStream()
resizedBitmap.compress(Bitmap.CompressFormat.JPEG, quality, byteArrayOutputStream)
var compressedSize = byteArrayOutputStream.size()

while (compressedSize > maxSizeInBytes) {
resizedBitmap = Bitmap.createScaledBitmap(
resizedBitmap,
(resizedBitmap.width * 0.9).toInt(),
(resizedBitmap.height * 0.9).toInt(),
true,
)

if (quality > 50) {
quality -= 5
}

byteArrayOutputStream.reset()
resizedBitmap.compress(Bitmap.CompressFormat.JPEG, quality, byteArrayOutputStream)
compressedSize = byteArrayOutputStream.size()
}

return byteArrayOutputStream.toByteArray()
}
11 changes: 11 additions & 0 deletions core/ui/src/main/java/co/kr/tnt/ui/model/SnackbarType.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package co.kr.tnt.ui.model

import androidx.annotation.DrawableRes
import co.kr.tnt.core.designsystem.R

enum class SnackbarType(
@DrawableRes val iconRes: Int,
) {
WARNING(R.drawable.ic_warning),
SUCCESS(R.drawable.ic_check_success),
}
17 changes: 11 additions & 6 deletions core/ui/src/main/java/co/kr/tnt/ui/utils/FileUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ package co.kr.tnt.ui.utils

import android.content.Context
import android.database.Cursor
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.provider.MediaStore
import android.util.Log
import co.kr.tnt.domain.IMAGE_MAX_SIZE
import co.kr.tnt.ui.extensions.toResizedByteArray
import java.io.File
import java.io.FileOutputStream

fun Uri.toFile(context: Context): File? {
return getRealPathFromUri(this, context)?.let { filePath ->
Expand All @@ -31,14 +31,19 @@ fun getRealPathFromUri(uri: Uri, context: Context): String? {
return null
}

fun Uri.convertToAllowedImageFormat(context: Context): File {
fun Uri.convertToAllowedImageFormat(
context: Context,
maxSizeInBytes: Int = IMAGE_MAX_SIZE,
): File {
val inputStream = context.contentResolver.openInputStream(this)
val bitmap = BitmapFactory.decodeStream(inputStream)
inputStream?.close()

val convertedFile = File(context.cacheDir, "image.png")
val outputStream = FileOutputStream(convertedFile)
val compressedBytes = bitmap.toResizedByteArray(maxSizeInBytes)
val convertedFile = File(context.cacheDir, "image.jpg")
val outputStream = convertedFile.outputStream()

bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
outputStream.use { it.write(compressedBytes) }
outputStream.flush()
outputStream.close()

Expand Down
9 changes: 8 additions & 1 deletion feature/main/src/main/java/co/kr/tnt/main/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,14 @@ class MainActivity : ComponentActivity() {
@OptIn(ExperimentalPermissionsApi::class)
@Composable
private fun CheckPermissionEffect() {
val notificationPermission = rememberMultiplePermissionsState(TnTPermission.NOTIFICATION.values)
val notificationPermission =
rememberMultiplePermissionsState(TnTPermission.NOTIFICATION.values) { permissionResult ->
val isGranted = permissionResult.values.all { it }

if (isGranted) {
viewModel.setEvent(MainUiEvent.OnNotificationPermissionGrantChecked)
}
}

LaunchedEffect(Unit) {
if (notificationPermission.shouldShowRationale.not()) {
Expand Down
1 change: 1 addition & 0 deletions feature/main/src/main/java/co/kr/tnt/main/MainContract.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ internal class MainContract {
) : UiState

sealed class MainUiEvent : UiEvent {
data object OnNotificationPermissionGrantChecked : MainUiEvent()
data object OnNotificationPermissionRevoked : MainUiEvent()
data class OnGetMessagingTokenSucceeded(val token: String) : MainUiEvent()
data object OnGetMessagingTokenFailed : MainUiEvent()
Expand Down
1 change: 1 addition & 0 deletions feature/main/src/main/java/co/kr/tnt/main/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ internal class MainViewModel @Inject constructor(
BaseViewModel<MainUiState, MainUiEvent, MainSideEffect>(MainUiState()) {
override suspend fun handleEvent(event: MainUiEvent) {
when (event) {
MainUiEvent.OnNotificationPermissionGrantChecked -> settingRepository.setEnablePushNotification(true)
MainUiEvent.OnNotificationPermissionRevoked -> settingRepository.setEnablePushNotification(false)
is MainUiEvent.OnGetMessagingTokenSucceeded -> {
viewModelScope.launch {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package co.kr.tnt.trainee.mealrecord.record

import android.content.Context
import androidx.core.net.toUri
import androidx.lifecycle.viewModelScope
import co.kr.tnt.domain.repository.TraineeRepository
import co.kr.tnt.domain.utils.DateFormatter
Expand All @@ -11,8 +10,6 @@ import co.kr.tnt.trainee.mealrecord.record.TraineeMealRecordContract.TraineeMeal
import co.kr.tnt.trainee.mealrecord.record.TraineeMealRecordContract.TraineeMealRecordUiState.DialogState
import co.kr.tnt.ui.base.BaseViewModel
import co.kr.tnt.ui.utils.convertToAllowedImageFormat
import co.kr.tnt.ui.utils.isAllowedImageFormat
import co.kr.tnt.ui.utils.toFile
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import java.io.File
Expand Down Expand Up @@ -93,6 +90,7 @@ internal class TraineeMealRecordViewModel @Inject constructor(
}
}

// TODO Context 제거
private fun postMealRecord(context: Context) {
updateState { copy(isLoading = true) }

Expand All @@ -103,13 +101,7 @@ internal class TraineeMealRecordViewModel @Inject constructor(

viewModelScope.launch {
val state = currentState
val imageFile: File? = state.image?.toFile(context)?.let { file ->
if (!isAllowedImageFormat(file)) {
file.toUri().convertToAllowedImageFormat(context)
} else {
file
}
}
val imageFile: File? = state.image?.convertToAllowedImageFormat(context)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

감사합니다.. 🥺

runCatching {
traineeRepository.postMealRecord(
mealImage = imageFile,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package co.kr.tnt.trainee.signup

import android.content.Context
import android.net.Uri
import androidx.core.net.toUri
import androidx.lifecycle.viewModelScope
import co.kr.tnt.domain.model.User
import co.kr.tnt.domain.repository.SignUpRepository
Expand All @@ -12,8 +11,6 @@ import co.kr.tnt.trainee.signup.TraineeSignUpContract.TraineeSignUpUiEvent
import co.kr.tnt.trainee.signup.TraineeSignUpContract.TraineeSignUpUiState
import co.kr.tnt.ui.base.BaseViewModel
import co.kr.tnt.ui.utils.convertToAllowedImageFormat
import co.kr.tnt.ui.utils.isAllowedImageFormat
import co.kr.tnt.ui.utils.toFile
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import java.io.File
Expand Down Expand Up @@ -60,13 +57,7 @@ internal class TraineeSignUpViewModel @Inject constructor(
updateState { copy(isLoading = true) }

val state = currentState
val profileImageFile: File? = imageUri?.toFile(context)?.let { file ->
if (!isAllowedImageFormat(file)) {
file.toUri().convertToAllowedImageFormat(context)
} else {
file
}
}
val profileImageFile: File? = imageUri?.convertToAllowedImageFormat(context)

runCatching {
signUpRepository.signUp(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ internal class AddPtSessionContract {

val isEnableComplete: Boolean
get() = selectedMember != null && selectedDate != null && selectedStartTime != null &&
selectedEndTime != null && isErrorTime.not()
selectedEndTime != null && isErrorTime.not() && isErrorMemo.not()

enum class BottomSheetType {
NONE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import co.kr.tnt.domain.model.PtSession
import co.kr.tnt.ui.base.UiEvent
import co.kr.tnt.ui.base.UiSideEffect
import co.kr.tnt.ui.base.UiState
import co.kr.tnt.ui.model.SnackbarType
import java.time.LocalDate
import java.time.YearMonth

Expand Down Expand Up @@ -38,6 +39,9 @@ internal class TrainerHomeContract {
data object NavigateToNotification : TrainerHomeSideEffect
data object NavigateToAddPtSession : TrainerHomeSideEffect
data object NavigateToInvite : TrainerHomeSideEffect
data class ShowToast(val message: String) : TrainerHomeSideEffect
data class ShowToast(
val message: String,
val type: SnackbarType = SnackbarType.WARNING,
) : TrainerHomeSideEffect
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,10 @@ internal fun TrainerHomeRoute(
TrainerHomeSideEffect.NavigateToNotification -> navigateToNotification()
TrainerHomeSideEffect.NavigateToAddPtSession -> navigateToAddPtSession(state.selectedDay.toString())
TrainerHomeSideEffect.NavigateToInvite -> navigateToInvite(ScreenMode.CLOSE)
is TrainerHomeSideEffect.ShowToast -> toast.show(effect.message)
is TrainerHomeSideEffect.ShowToast -> toast.show(
message = effect.message,
icon = effect.type.iconRes,
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import co.kr.tnt.trainer.home.TrainerHomeContract.TrainerHomeUiEvent
import co.kr.tnt.trainer.home.TrainerHomeContract.TrainerHomeUiState
import co.kr.tnt.trainer.home.TrainerHomeContract.TrainerHomeUiState.DialogState
import co.kr.tnt.ui.base.BaseViewModel
import co.kr.tnt.ui.model.SnackbarType
import com.kizitonwose.calendar.core.yearMonth
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.async
Expand Down Expand Up @@ -117,8 +118,14 @@ internal class TrainerHomeViewModel @Inject constructor(
trainerRepository.postCompleteSession(ptSession.id)
}.onSuccess {
getDailyPtSessions(currentState.selectedDay)
}.onFailure {
sendEffect(TrainerHomeSideEffect.ShowToast("서버 요청에 실패했어요"))
sendEffect(
TrainerHomeSideEffect.ShowToast(
message = "PT 수업을 완료했어요",
type = SnackbarType.SUCCESS,
),
)
}.onFailure { throwable ->
sendEffect(TrainerHomeSideEffect.ShowToast(throwable.message ?: "서버 요청에 실패했어요"))
}
}
}
Expand Down
Loading