Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 업로드 화질 저하 이슈 해결 #498

Merged
merged 30 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
dd3dc34
chore: 전체 gitignore 지정
briandr97 Sep 14, 2023
695d4f4
Merge branch 'dev_android' of https://github.com/woowacourse-teams/20…
briandr97 Sep 15, 2023
36378be
Merge branch 'dev_android' of https://github.com/woowacourse-teams/20…
briandr97 Oct 3, 2023
3c02085
Merge branch 'dev_android' of https://github.com/woowacourse-teams/20…
briandr97 Oct 4, 2023
d7a1d86
Merge branch 'dev_android' of https://github.com/woowacourse-teams/20…
briandr97 Oct 4, 2023
45fdef8
Merge branch 'dev_android' of https://github.com/woowacourse-teams/20…
briandr97 Oct 5, 2023
58fd874
Merge branch 'dev_android' of https://github.com/woowacourse-teams/20…
briandr97 Oct 10, 2023
69b99a0
Merge branch 'dev_android' of https://github.com/woowacourse-teams/20…
briandr97 Oct 13, 2023
6b0c5d3
Merge branch 'dev_android' of https://github.com/woowacourse-teams/20…
briandr97 Oct 16, 2023
efa55dd
Merge branch 'dev_android' of https://github.com/woowacourse-teams/20…
briandr97 Oct 17, 2023
b544482
feat: TakePhoto로 api 변경 및 리사이징 기능 추가
briandr97 Oct 17, 2023
bade642
feat: 필요한 권한 추가
briandr97 Oct 18, 2023
56c6a7d
feat: 스낵바 유틸 클래스 변경
briandr97 Oct 18, 2023
1706851
feat: 이전 버전을 위한 권한 추가와 권한 요청 스낵바 관련 로직 변경
briandr97 Oct 18, 2023
e72ad60
feat: merge develop
briandr97 Oct 18, 2023
387553c
Merge branch 'dev_android' of https://github.com/woowacourse-teams/20…
briandr97 Oct 18, 2023
cc8dad6
feat: 이미지 리사이징 및 화면 방향 맞춤
briandr97 Oct 18, 2023
cbaf08e
feat: 변수명 변경
briandr97 Oct 19, 2023
7cf4cce
feat: 로케이션 로직 분리
briandr97 Oct 19, 2023
e93124a
feat: 함수 분리
briandr97 Oct 19, 2023
47999ed
feat: WRITE 권한의 maxSdkVersion 변경
briandr97 Oct 19, 2023
35dd08e
feat: 토스트 or 스낵바 메시지 리소스화
briandr97 Oct 19, 2023
423c032
feat: file의 초기값이 빈 파일에서 null로 변경되면서 검증 로직 변경
briandr97 Oct 19, 2023
ff5cd1c
feat: file의 초기값 변경에 따른 테스트 수정
briandr97 Oct 19, 2023
bed0971
feat: 분리 로직 다시 원래대로 변경
briandr97 Oct 19, 2023
086cf88
feat: 사진의 가로 세로를 맞추기 위한 로직 변경
briandr97 Nov 6, 2023
30b1f09
feat: 권한 관련 로직 수정
briandr97 Nov 6, 2023
989f4ea
feat: merge develop
briandr97 Nov 6, 2023
340d9a8
merge dev_android
briandr97 Nov 6, 2023
f16ea16
feat: 잘못 작성한 로직 수정
briandr97 Nov 7, 2023
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
6 changes: 5 additions & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />

<application
android:name=".NaagaApplication"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import com.now.naaga.databinding.ActivityAdventureDetailBinding
import com.now.naaga.presentation.adventuredetail.viewpager.ViewPagerAdapter
import com.now.naaga.presentation.uimodel.model.LetterUiModel
import com.now.naaga.util.extension.repeatOnStarted
import com.now.naaga.util.extension.showSnackbarWithEvent
import com.now.naaga.util.extension.showShortSnackbarWithEvent
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
Expand Down Expand Up @@ -66,7 +66,7 @@ class AdventureDetailActivity : AppCompatActivity(), AnalyticsDelegate by Defaul
}

private fun showReRequestSnackbar() {
binding.root.showSnackbarWithEvent(
binding.root.showShortSnackbarWithEvent(
message = getString(R.string.snackbar_action_re_request_message),
actionTitle = getString(R.string.snackbar_action__re_request_title),
) { finish() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import com.now.naaga.presentation.onadventure.OnAdventureActivity
import com.now.naaga.presentation.setting.SettingActivity
import com.now.naaga.presentation.upload.UploadActivity
import com.now.naaga.util.extension.openSetting
import com.now.naaga.util.extension.showSnackbarWithEvent
import com.now.naaga.util.extension.showShortSnackbarWithEvent
import com.now.naaga.util.extension.showToast
import dagger.hilt.android.AndroidEntryPoint

Expand Down Expand Up @@ -116,7 +116,7 @@ class BeginAdventureActivity : AppCompatActivity(), AnalyticsDelegate by Default
}

private fun showPermissionSnackbar() {
binding.root.showSnackbarWithEvent(
binding.root.showShortSnackbarWithEvent(
message = getString(R.string.snackbar_location_message),
actionTitle = getString(R.string.snackbar_action_title),
action = { openSetting() },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.location.Location
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.provider.Settings
import android.view.View
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
Expand All @@ -20,14 +20,15 @@ import com.google.android.gms.location.LocationServices
import com.google.android.gms.tasks.CancellationToken
import com.google.android.gms.tasks.CancellationTokenSource
import com.google.android.gms.tasks.OnTokenCanceledListener
import com.google.android.material.snackbar.Snackbar
import com.now.domain.model.Coordinate
import com.now.naaga.R
import com.now.naaga.data.firebase.analytics.AnalyticsDelegate
import com.now.naaga.data.firebase.analytics.DefaultAnalyticsDelegate
import com.now.naaga.data.firebase.analytics.UPLOAD_OPEN_CAMERA
import com.now.naaga.data.throwable.DataThrowable
import com.now.naaga.databinding.ActivityUploadBinding
import com.now.naaga.presentation.upload.UploadViewModel.Companion.FILE_EMPTY
import com.now.naaga.util.BitmapBuilder
import com.now.naaga.util.extension.openSetting
import com.now.naaga.util.extension.showSnackbarWithEvent
import com.now.naaga.util.extension.showToast
Expand All @@ -40,43 +41,57 @@ import java.time.LocalDateTime
class UploadActivity : AppCompatActivity(), AnalyticsDelegate by DefaultAnalyticsDelegate() {
private lateinit var binding: ActivityUploadBinding
private val viewModel: UploadViewModel by viewModels()

private val cameraLauncher = registerForActivityResult(
ActivityResultContracts.TakePicturePreview(),
) { bitmap ->
if (bitmap != null) {
setImage(bitmap)
private var imageUri: Uri? = null
private val cameraLauncher = registerForActivityResult(ActivityResultContracts.TakePicture()) { success ->
if (!success) return@registerForActivityResult
if (imageUri == null) {
showToast(getString(R.string.upload_image_orientation_error))
return@registerForActivityResult
}
setImage(requireNotNull(imageUri) { "imageUri가 null입니다" })
Copy link
Collaborator

Choose a reason for hiding this comment

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

[P1]
string을 상수화 해도 좋겠습니다!

Copy link
Collaborator Author

@briandr97 briandr97 Oct 19, 2023

Choose a reason for hiding this comment

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

에러 문구를 상수화해야 할 이유가 있을까요?

Copy link
Collaborator

Choose a reason for hiding this comment

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

에러문구를 상수화 해놓으면 동반객체를 보았을 때 어떤 오류들이 발생할 수 있겠구나 한 번에 볼 수 있을 것 같아요.
사실 현재 코드가 길기 때문에 더 보기 힘든 것도 있지만 한 번에 관리하는 것이 코드의 가독성도, 리뷰에도 편한 것 같습니다. 만약 정말 에러가 발생한다고 가정했을 때 해당 문구를 어디서 사용하는지 바로가기를 통해 확인할 수도 있구요.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

저는 사실 에러 문구를 굳이 상수화 해야하는가? 라고 생각합니다.
보통 에러를 확인할 때 이 객체에서 어떤 에러가 일어날 수 있을까?를 생각하며 한눈에 모든 에러를 살펴보기 보다는 에러가 발생했을 때 어떤 에러가 발생했는 지를 확인한다고 생각합니다.
그렇기 때문에 굳이 한 곳에 모아놓아야하나? 라는 의문점이 듭니다.
또한 에러가 정말로 발생했을 때도 해당 에러가 발생한 곳으로 바로 이동이 가능하기 때문에 거기서 변수를 한 번 더 눌러서 확인하는 것 보다는 해당 코드에서 어떤 에러인지 바로 인지할 수 있는 것이 더 좋다고 생각합니다.

}

private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions(),
) { permission: Map<String, Boolean> ->
private val storagePermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
val isGranted: Boolean = permissions.values.all { it }
if (!isGranted) {
showStoragePermissionSnackBar()
return@registerForActivityResult
}
openCamera()
}

val keys = permission.entries.map { it.key }
val isStorageRequest = storagePermissions.any { keys.contains(it) }
if (isStorageRequest) {
if (permission.entries.map { it.value }.contains(false)) {
showPermissionSnackbar(getString(R.string.snackbar_storage_message))
} else {
openCamera()
private val locationPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
val isGranted: Boolean = permissions.values.all { true }
Copy link
Collaborator

Choose a reason for hiding this comment

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

[P4]
여기서 true를 it으로 바꿔도 동작을 하는데 true로 작성한 이유가 무엇인가요?
true로 작성한다면 항상 위치 권한이 부여된 것으로 판단하게 되지 않나요?! 그래서 it으로 작성해야 하는게 아닌가라는 생각이 들었는데..
잠깐의 코드리뷰를 통해 판단한 제 생각이기도 하고 이렇게 작성한 빅스의 생각과 과정이 있었을 것 같아 정말 궁금해 물어봅니다!!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

it이 맞습니다. 잘못 작성했네요 수정했습니다!

if (!isGranted) {
showLocationPermissionSnackBar()
return@registerForActivityResult
}
return@registerForActivityResult
setCoordinate()
}
showPermissionSnackbar(getString(R.string.snackbar_location_message))

private val isStoragePermissionGranted: Boolean
get() = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
storagePermissionsBelow28.all { checkSelfPermission(it) == PackageManager.PERMISSION_GRANTED }
} else {
true
}

private val locationSettingLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
setCoordinate()
}

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

binding = ActivityUploadBinding.inflate(layoutInflater)
setContentView(binding.root)

initViewModel()
subscribe()
registerAnalytics(this.lifecycle)
setCoordinate()
setClickListeners()
locationPermissionLauncher.launch(locationPermissions)
registerAnalytics(this.lifecycle)
}

private fun initViewModel() {
Expand Down Expand Up @@ -115,29 +130,88 @@ class UploadActivity : AppCompatActivity(), AnalyticsDelegate by DefaultAnalytic
}
}

private fun setClickListeners() {
binding.ivUploadCameraIcon.setOnClickListener {
logClickEvent(getViewEntryName(it), UPLOAD_OPEN_CAMERA)
openCameraWithPermission()
}
binding.ivUploadPhoto.setOnClickListener {
logClickEvent(getViewEntryName(it), UPLOAD_OPEN_CAMERA)
openCameraWithPermission()
}
binding.ivUploadBack.setOnClickListener {
finish()
}
binding.btnUploadSubmit.setOnClickListener {
if (isFormValid().not()) {
showToast(getString(R.string.upload_error_insufficient_info_message))
} else {
viewModel.postPlace()
}
}
}

private fun changeVisibility(view: View, status: Int) {
view.visibility = status
}

private fun showPermissionSnackbar(message: String) {
private fun showStoragePermissionSnackBar() {
binding.root.showSnackbarWithEvent(
message = message,
message = getString(R.string.snackbar_storage_message),
actionTitle = getString(R.string.snackbar_action_title),
length = Snackbar.LENGTH_LONG,
Copy link
Collaborator

Choose a reason for hiding this comment

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

확실히 길어지니까 좋네요👍

Copy link
Collaborator

Choose a reason for hiding this comment

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

[P3]
저장소 권한은 LONG을 주고, 위치 권한은 LENGTH_INDEFINITE을 준 이유가 있나요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

해당 부분은 PR 첫 게시글에 적혀있습니다

action = { openSetting() },
)
}

private fun showLocationPermissionSnackBar() {
binding.root.showSnackbarWithEvent(
message = getString(R.string.snackbar_location_message),
actionTitle = getString(R.string.snackbar_action_title),
length = Snackbar.LENGTH_INDEFINITE,
action = {
val appDetailsIntent = getSettingIntent()
locationSettingLauncher.launch(appDetailsIntent)
},
Comment on lines +172 to +175
Copy link
Collaborator

Choose a reason for hiding this comment

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

[P1]
이 액션은 다른 곳에서도 사용되고 있어 제가 Context의 확장함수로 만들어두었습니다. 그것을 사용하는 것은 어떨까요?

그리고 그렇게 된다면 showStoragePermissionSnackBar()showLocationPermissionSnackBar() 메서드는 message의 차이만 있게 됩니다.
showPermissionSnackBar(message: String) 으로 변경하면 코드가 더 줄어들고 가독성도 좋아질 것 같아요!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

해당 확장함수는 확인 했지만 위에서 말씀하셨듯이 보여주는 길이가 달라져서 사용하지 못했습니다.
크롱이 만들어둔 확장함수는 전부 SHORT 이기 때문입니다.

)
}

private fun getSettingIntent() = Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.parse("package:$packageName"),
).addCategory(Intent.CATEGORY_DEFAULT)

private fun setImage(uri: Uri) {
binding.ivUploadCameraIcon.visibility = View.GONE
val bitmap = getBitmap(uri)
binding.ivUploadPhoto.setImageBitmap(bitmap)
val file = saveFile(bitmap)
viewModel.setFile(file)
}

private fun getBitmap(uri: Uri) = BitmapBuilder(uri, contentResolver)
.addScaling(RESIZE)
.setProperRotate()
.build()

private fun saveFile(bitmap: Bitmap): File {
val tempFile = File.createTempFile(System.currentTimeMillis().toString(), ".jpeg", cacheDir)
FileOutputStream(tempFile).use {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it)
}
return tempFile
}

private fun setCoordinate() {
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
val fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
fusedLocationClient.getCurrentLocation(PRIORITY_HIGH_ACCURACY, createCancellationToken())
.addOnSuccessListener { location ->
location.let { viewModel.setCoordinate(getCoordinate(location)) }
}
.addOnFailureListener { }
} else {
requestPermissionLauncher.launch(locationPermissions)
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_DENIED) {
showLocationPermissionSnackBar()
return
}
val fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
fusedLocationClient.getCurrentLocation(PRIORITY_HIGH_ACCURACY, createCancellationToken())
.addOnSuccessListener { location ->
location.let { viewModel.setCoordinate(getCoordinate(location)) }
}.addOnFailureListener { }
}

private fun createCancellationToken(): CancellationToken {
Expand All @@ -163,80 +237,39 @@ class UploadActivity : AppCompatActivity(), AnalyticsDelegate by DefaultAnalytic
return (number * 10_000).toLong().toDouble() / 10_000
}

private fun setClickListeners() {
binding.ivUploadCameraIcon.setOnClickListener {
logClickEvent(getViewEntryName(it), UPLOAD_OPEN_CAMERA)
requestStoragePermission()
}
binding.ivUploadPhoto.setOnClickListener {
logClickEvent(getViewEntryName(it), UPLOAD_OPEN_CAMERA)
requestStoragePermission()
}
binding.ivUploadBack.setOnClickListener {
finish()
}
binding.btnUploadSubmit.setOnClickListener {
if (isFormValid().not()) {
showToast(getString(R.string.upload_error_insufficient_info_message))
} else {
viewModel.postPlace()
}
private fun openCameraWithPermission() {
if (!isStoragePermissionGranted) {
storagePermissionLauncher.launch(storagePermissionsBelow28)
return
}
}

private fun requestStoragePermission() {
val permissionToRequest = storagePermissions.toMutableList()

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return openCamera()
}

requestPermissionLauncher.launch(permissionToRequest.toTypedArray())
openCamera()
}

private fun openCamera() {
cameraLauncher.launch(null)
}

private fun setImage(bitmap: Bitmap) {
binding.ivUploadCameraIcon.visibility = View.GONE
binding.ivUploadPhoto.setImageBitmap(bitmap)
val uri = getImageUri(bitmap) ?: Uri.EMPTY
val file = makeImageFile(uri)
viewModel.setFile(file)
}

private fun makeImageFile(uri: Uri): File {
val bitmap = contentResolver.openInputStream(uri).use {
BitmapFactory.decodeStream(it)
imageUri = createImageUri().getOrElse {
Snackbar.make(binding.root, getString(R.string.upload_retry), Snackbar.LENGTH_SHORT).show()
return
}
val tempFile = File.createTempFile("image", ".jpeg", cacheDir) ?: FILE_EMPTY

FileOutputStream(tempFile).use {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it)
}
return tempFile
cameraLauncher.launch(imageUri)
}

private fun getImageUri(bitmap: Bitmap): Uri? {
val resolver = applicationContext.contentResolver
resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
?.let { imageUri ->
val outputStream = resolver.openOutputStream(imageUri)
outputStream?.use { stream ->
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream)
}
return imageUri
}
return null
private fun createImageUri(): Result<Uri> {
val imageUri: Uri = contentResolver.insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues,
) ?: return Result.failure(IllegalStateException("이미지 uri를 가져오지 못했습니다."))
return Result.success(imageUri)
}

private fun isFormValid(): Boolean {
return viewModel.isFormValid()
}

companion object {
private val storagePermissions = arrayOf(
private const val PRIORITY_HIGH_ACCURACY = 100
private const val RESIZE = 500
private val storagePermissionsBelow28 = arrayOf(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
)

Expand All @@ -250,8 +283,6 @@ class UploadActivity : AppCompatActivity(), AnalyticsDelegate by DefaultAnalytic
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
}

const val PRIORITY_HIGH_ACCURACY = 100

fun getIntent(context: Context): Intent {
return Intent(context, UploadActivity::class.java)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import javax.inject.Inject
class UploadViewModel @Inject constructor(
private val placeRepository: PlaceRepository,
) : ViewModel() {
private var file = FILE_EMPTY
private var file: File? = null
Copy link
Collaborator

Choose a reason for hiding this comment

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

[P3]
빅스는 빠르게 터뜨리기 vs 안전하게 처리하기 중 어떤 것을 더 선호하시나요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

상황에 따라 다르지만 현재 상황에서는 빈 파일이 서버에 업로드 되는 것 보단 에러를 발생시키고 실패했다는 피드백을 유저에게 주는 것이 좋을 것 같습니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

해당 상황에서는 isFormValid로 체크하므로 터질일은 없다고 생각됩니다.

Copy link
Collaborator

Choose a reason for hiding this comment

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

저는 안전하게 처리하는 방법을 선택했던 것인데요. 이것을 선택한 이유는 서버에서 한 번 검수를 한 뒤에 데이터를 넣는 작업을 진행하고 있고, File이 없는 경우 사용자에게 피드백을 주어도 어떠한 대처를 할 수 없다고 판단했기 때문입니다.
사실 isFormValid메서드를 통해 Null인지 확인하고 있어 터질일은 없겠지만요.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

저는 대처를 할 수 없더라도 유저가 등록이 되었는지 되지 않았는지를 아는게 좋다고 생각합니다.
또한 마지막 줄에서 말씀하셨듯이 해당 방법이 안전하지 않다고 생각하지 않습니다!


val name = MutableLiveData<String>()

Expand All @@ -43,7 +43,7 @@ class UploadViewModel @Inject constructor(
}

fun isFormValid(): Boolean {
return (file != FILE_EMPTY) && (_coordinate.value != null) && (name.value != null)
return (file != null) && (_coordinate.value != null) && (name.value != null)
}

fun postPlace() {
Expand All @@ -55,7 +55,7 @@ class UploadViewModel @Inject constructor(
name = name.value.toString(),
description = "",
coordinate = coordinate,
file = file,
file = file!!,
)
}.onSuccess {
_successUpload.setValue(UploadStatus.SUCCESS)
Expand Down
Loading
Loading