-
Notifications
You must be signed in to change notification settings - Fork 2
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
[Feature/#92] 프로필 수정 구현 #95
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package com.teamfillin.fillin.data.response | ||
|
||
data class ResponseUpdateUser( | ||
val updateUser: UpdateUserDto | ||
) { | ||
data class UpdateUserDto( | ||
val id: Int, | ||
val social: String, | ||
val nickname: String, | ||
val imageUrl: String, | ||
val refreshToken: String, | ||
val isDeleted: Boolean, | ||
val updatedAt: String, | ||
val email: String, | ||
val createdAt: String, | ||
val idKey: String, | ||
val camera: List<String> | ||
) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<ActivityProfileUpdateBinding>(R.layout.activity_profile_update) { | ||
private val viewModel by viewModels<ProfileUpdateViewModel>() | ||
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 = "" | ||
} | ||
Comment on lines
+53
to
+59
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이거 함수로 빼보자 |
||
|
||
binding.ivProfile.setOnClickListener { | ||
val intent = Intent(Intent.ACTION_GET_CONTENT) | ||
intent.type = "image/*" | ||
Comment on lines
+62
to
+63
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이거 사용합시다 |
||
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") | ||
) | ||
Comment on lines
+78
to
+83
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Color.parseColor는 최대한 지양해보자! |
||
} | ||
}.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<Intent> = | ||
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)) | ||
} | ||
} | ||
Comment on lines
+115
to
+121
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ContentUriRequestBody 사용하는 쪽으로 가보는건 어떨깝쇼 |
||
} 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") | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Boolean> = nickname.map { it.isNotBlank() } | ||
|
||
val cameraName = MutableStateFlow("") | ||
|
||
private val _updateUser = MutableStateFlow<UiState<ResponseUpdateUser>>(UiState.Loading) | ||
val updateUser: StateFlow<UiState<ResponseUpdateUser>> = _updateUser | ||
|
||
private var imageBitmap: Bitmap? = null | ||
|
||
fun setImageBitmap(bitmap: Bitmap) { | ||
imageBitmap = bitmap | ||
} | ||
|
||
fun putUser() { | ||
val textHashMap = hashMapOf<String, RequestBody>() | ||
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프로 압축 | ||
} | ||
} | ||
} | ||
} |
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="18dp" | ||
android:height="18dp" | ||
android:viewportWidth="18" | ||
android:viewportHeight="18"> | ||
<path | ||
android:strokeWidth="1" | ||
android:pathData="M4,2.002L14,12.002" | ||
android:fillColor="#00000000" | ||
android:strokeColor="#FDFAF9"/> | ||
<path | ||
android:strokeWidth="1" | ||
android:pathData="M4,12L14,2" | ||
android:fillColor="#00000000" | ||
android:strokeColor="#FDFAF9"/> | ||
</vector> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Glide 뗄까...