From 48bd8a1182ef304a7bb71cdac80bb3ad086b9204 Mon Sep 17 00:00:00 2001 From: Leekangmin Date: Thu, 5 Jan 2023 18:49:51 +0900 Subject: [PATCH] =?UTF-8?q?[feature/#92]=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 8 +- .../my/update/ProfileUpdateActivity.kt | 133 ++++++++++++++++++ .../my/update/ProfileUpdateViewModel.kt | 71 ++++++++++ 3 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/teamfillin/fillin/presentation/my/update/ProfileUpdateActivity.kt create mode 100644 app/src/main/java/com/teamfillin/fillin/presentation/my/update/ProfileUpdateViewModel.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a2c7d4b..f4af81b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -95,7 +95,13 @@ android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity" android:theme="@style/LicenseTheme" /> - + + diff --git a/app/src/main/java/com/teamfillin/fillin/presentation/my/update/ProfileUpdateActivity.kt b/app/src/main/java/com/teamfillin/fillin/presentation/my/update/ProfileUpdateActivity.kt new file mode 100644 index 0000000..78cd05a --- /dev/null +++ b/app/src/main/java/com/teamfillin/fillin/presentation/my/update/ProfileUpdateActivity.kt @@ -0,0 +1,133 @@ +package com.teamfillin.fillin.presentation.my.update + +import android.content.Intent +import android.graphics.Color +import android.graphics.ImageDecoder +import android.os.Build +import android.os.Bundle +import android.provider.MediaStore +import android.util.Log +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.activity.viewModels +import androidx.core.view.isVisible +import androidx.core.widget.doAfterTextChanged +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import com.bumptech.glide.Glide +import com.teamfillin.fillin.R +import com.teamfillin.fillin.core.base.BindingActivity +import com.teamfillin.fillin.core.context.colorOf +import com.teamfillin.fillin.core.context.toast +import com.teamfillin.fillin.core.view.UiState +import com.teamfillin.fillin.databinding.ActivityProfileUpdateBinding +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import java.io.IOException + +@AndroidEntryPoint +class ProfileUpdateActivity : + BindingActivity(R.layout.activity_profile_update) { + private val viewModel by viewModels() + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding.viewModel = viewModel + initView() + initEvent() + observe() + } + + private fun initView() { + binding.etNickname.doAfterTextChanged { + editTextNameBlankCheck() + } + + binding.etCamera.doAfterTextChanged { + editTextCameraBlankCheck() + } + } + + private fun initEvent() { + binding.ivNicknameClear.setOnClickListener { + viewModel.nickname.value = "" + } + + binding.ivCameraClear.setOnClickListener { + viewModel.cameraName.value = "" + } + + binding.ivProfile.setOnClickListener { + val intent = Intent(Intent.ACTION_GET_CONTENT) + intent.type = "image/*" + filterActivityLauncher.launch(intent) + } + + binding.btnAddPhoto.setOnClickListener { + toast("asdf") + viewModel.putUser() + } + } + + private fun observe() { + viewModel.isClickable.flowWithLifecycle(lifecycle) + .onEach { + with(binding.btnAddPhoto) { + isClickable = it + setBackgroundColor( + if (it) colorOf(R.color.fillin_red) else Color.parseColor("#474645") + ) + setTextColor( + if (it) colorOf(R.color.fillin_black) else Color.parseColor("#6F6F6F") + ) + } + }.launchIn(lifecycleScope) + + viewModel.updateUser.flowWithLifecycle(lifecycle) + .onEach { + when (it) { + is UiState.Success -> { + toast("프로필 수정이 완료되었습니다.") + finish() + } + is UiState.Failure -> { + toast(it.msg) + } + else -> {} + } + } + } + + private fun editTextNameBlankCheck() { + binding.ivNicknameClear.isVisible = !binding.etNickname.text.isNullOrEmpty() + } + + private fun editTextCameraBlankCheck() { + binding.ivCameraClear.isVisible = !binding.etCamera.text.isNullOrEmpty() + } + + private val filterActivityLauncher: ActivityResultLauncher = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + if (it.resultCode == RESULT_OK && it.data != null) { + val imageUri = it.data?.data + try { + imageUri?.let { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + viewModel.setImageBitmap(ImageDecoder.decodeBitmap(ImageDecoder.createSource(contentResolver, imageUri))) + } else { + viewModel.setImageBitmap(MediaStore.Images.Media.getBitmap(contentResolver, imageUri)) + } + } + } catch (e: IOException) { + e.printStackTrace() + } + + Glide.with(this).load(imageUri).circleCrop().into(binding.ivProfile) + } else if (it.resultCode == RESULT_CANCELED) { + toast("사진 선택 취소") + } else { + Log.d("ActivityResult", "something wrong") + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/teamfillin/fillin/presentation/my/update/ProfileUpdateViewModel.kt b/app/src/main/java/com/teamfillin/fillin/presentation/my/update/ProfileUpdateViewModel.kt new file mode 100644 index 0000000..189d2af --- /dev/null +++ b/app/src/main/java/com/teamfillin/fillin/presentation/my/update/ProfileUpdateViewModel.kt @@ -0,0 +1,71 @@ +package com.teamfillin.fillin.presentation.my.update + +import android.graphics.Bitmap +import android.util.Log +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.teamfillin.fillin.core.view.UiState +import com.teamfillin.fillin.data.response.ResponseUpdateUser +import com.teamfillin.fillin.data.service.AuthService +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import okhttp3.MediaType +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody +import okio.BufferedSink +import javax.inject.Inject + +@HiltViewModel +class ProfileUpdateViewModel @Inject constructor( + private val service: AuthService +): ViewModel() { + val nickname = MutableStateFlow("") + val isClickable: Flow = nickname.map { it.isNotBlank() } + + val cameraName = MutableStateFlow("") + + private val _updateUser = MutableStateFlow>(UiState.Loading) + val updateUser: StateFlow> = _updateUser + + private var imageBitmap: Bitmap? = null + + fun setImageBitmap(bitmap: Bitmap) { + imageBitmap = bitmap + } + + fun putUser() { + val textHashMap = hashMapOf() + textHashMap["nickname"] = nickname.value.toRequestBody("text/plain".toMediaTypeOrNull()) + textHashMap["camera"] = cameraName.value.toRequestBody("text/plain".toMediaTypeOrNull()) + val bitmapRequestBody = imageBitmap?.let { BitmapRequestBody(it) } + val bitmapMultipartBody = bitmapRequestBody?.let { MultipartBody.Part.createFormData("imageUrl", "imageUrl", it) } + + viewModelScope.launch { + runCatching { + service.putUser(bitmapMultipartBody, textHashMap) + }.onSuccess { + _updateUser.value = UiState.Success(it.data) + }.onFailure { + _updateUser.value = UiState.Failure("서버 통신 오류 : $it") + } + } + } + + companion object { + class BitmapRequestBody(private val bitmap: Bitmap) : RequestBody() { + override fun contentType(): MediaType? { + return "image/png".toMediaTypeOrNull() + } + + override fun writeTo(sink: BufferedSink) { + bitmap.compress(Bitmap.CompressFormat.PNG, 99, sink.outputStream()) //99프로 압축 + } + } + } +} \ No newline at end of file