From 0e32b7c78aa828a6aca8290b6d027a55666ed7d3 Mon Sep 17 00:00:00 2001 From: Taewan Park Date: Fri, 24 Nov 2023 23:08:10 +0900 Subject: [PATCH] v0.1.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 오픈소스 라이선스 추가 - 상품 추가 화면에서 목표 가격 설정을 단계별로 할 수 있게 추가 - 상품 추가 화면에서 목표 가격에 아무 값이 없으면 창이 꺼지는 문제 수정 - 상품 인기 순위 섞이는 버그 수정 Co-Authored-By: EunhoKang Co-Authored-By: ootr47 <83055885+ootr47@users.noreply.github.com> Co-Authored-By: 손문기 <39684860+Muungi@users.noreply.github.com> Co-Authored-By: ByeongIk Choi --- android/app/build.gradle.kts | 8 +++- android/app/src/main/AndroidManifest.xml | 6 +++ .../confirm/ConfirmItemLinkFragment.kt | 31 ++++++++++---- .../setprice/SetTargetPriceFragment.kt | 41 +++++++++++++++---- .../setprice/SetTargetPriceViewModel.kt | 12 +++++- .../ui/home/mypage/MyPageFragment.kt | 3 +- .../ui/home/mypage/MyPageViewModel.kt | 21 +++++----- .../res/layout/fragment_confirm_item_link.xml | 9 ++-- .../src/main/res/layout/fragment_my_page.xml | 6 +-- .../res/layout/fragment_set_target_price.xml | 5 ++- android/app/src/main/res/values/strings.xml | 3 +- android/app/src/main/res/values/styles.xml | 5 +++ android/build.gradle.kts | 1 + android/release_notes.txt | 19 ++++----- 14 files changed, 115 insertions(+), 55 deletions(-) diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index fe45e72..56669c7 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -2,6 +2,7 @@ plugins { id("com.android.application") id("org.jetbrains.kotlin.android") id("com.google.gms.google-services") + id("com.google.android.gms.oss-licenses-plugin") id("com.google.firebase.crashlytics") id("com.google.firebase.firebase-perf") id("kotlin-kapt") @@ -18,8 +19,8 @@ android { applicationId = "app.priceguard" minSdk = 29 targetSdk = 34 - versionCode = 1 - versionName = "0.1.0" + versionCode = 2 + versionName = "0.1.1" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } @@ -94,6 +95,9 @@ dependencies { implementation("androidx.navigation:navigation-fragment-ktx:$navVersion") implementation("androidx.navigation:navigation-ui-ktx:$navVersion") + // OSS licences + implementation("com.google.android.gms:play-services-oss-licenses:17.0.1") + // Glide implementation("com.github.bumptech.glide:glide:4.16.0") } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index affaed1..c0c7503 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -41,6 +41,12 @@ + + \ No newline at end of file diff --git a/android/app/src/main/java/app/priceguard/ui/additem/confirm/ConfirmItemLinkFragment.kt b/android/app/src/main/java/app/priceguard/ui/additem/confirm/ConfirmItemLinkFragment.kt index cd00188..955c6de 100644 --- a/android/app/src/main/java/app/priceguard/ui/additem/confirm/ConfirmItemLinkFragment.kt +++ b/android/app/src/main/java/app/priceguard/ui/additem/confirm/ConfirmItemLinkFragment.kt @@ -5,17 +5,19 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController +import app.priceguard.R import app.priceguard.data.dto.ProductVerifyDTO import app.priceguard.databinding.FragmentConfirmItemLinkBinding +import java.text.NumberFormat import kotlinx.serialization.json.Json class ConfirmItemLinkFragment : Fragment() { private var _binding: FragmentConfirmItemLinkBinding? = null private val binding get() = _binding!! - private val viewModel: ConfirmItemLinkViewModel by viewModels() + + private lateinit var productInfo: ProductVerifyDTO override fun onCreateView( inflater: LayoutInflater, @@ -28,12 +30,23 @@ class ConfirmItemLinkFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.lifecycleOwner = viewLifecycleOwner - binding.viewModel = viewModel + binding.initListener() + binding.initView() + } + + private fun FragmentConfirmItemLinkBinding.initView() { val productJson = requireArguments().getString("product") ?: return - val productInfo = Json.decodeFromString(productJson) - viewModel.setProductInfo(productInfo) + productInfo = Json.decodeFromString(productJson) + + tvConfirmItemPrice.text = + String.format( + resources.getString(R.string.won), + NumberFormat.getNumberInstance().format(productInfo.productPrice) + ) + tvConfirmItemBrand.text = productInfo.shop + tvConfirmItemItemTitle.text = productInfo.productName + imageUrl = productInfo.imageUrl } override fun onDestroyView() { @@ -45,9 +58,9 @@ class ConfirmItemLinkFragment : Fragment() { btnConfirmItemNext.setOnClickListener { val action = ConfirmItemLinkFragmentDirections.actionConfirmItemLinkFragmentToSetTargetPriceFragment( - viewModel?.flow?.value?.productCode ?: "", - viewModel?.flow?.value?.productName ?: "", - viewModel?.flow?.value?.productPrice ?: 0 + productInfo.productCode ?: "", + productInfo.productName ?: "", + productInfo.productPrice ?: 0 ) findNavController().navigate(action) } diff --git a/android/app/src/main/java/app/priceguard/ui/additem/setprice/SetTargetPriceFragment.kt b/android/app/src/main/java/app/priceguard/ui/additem/setprice/SetTargetPriceFragment.kt index f777a1d..dd90be8 100644 --- a/android/app/src/main/java/app/priceguard/ui/additem/setprice/SetTargetPriceFragment.kt +++ b/android/app/src/main/java/app/priceguard/ui/additem/setprice/SetTargetPriceFragment.kt @@ -11,7 +11,10 @@ import androidx.navigation.fragment.findNavController import app.priceguard.R import app.priceguard.databinding.FragmentSetTargetPriceBinding import app.priceguard.ui.util.lifecycle.repeatOnStarted +import com.google.android.material.slider.Slider +import com.google.android.material.slider.Slider.OnSliderTouchListener import dagger.hilt.android.AndroidEntryPoint +import java.text.NumberFormat @AndroidEntryPoint class SetTargetPriceFragment : Fragment() { @@ -39,8 +42,14 @@ class SetTargetPriceFragment : Fragment() { val title = requireArguments().getString("productTitle") ?: "" val price = requireArguments().getInt("productPrice") + binding.tvSetPriceCurrentPrice.text = + String.format( + resources.getString(R.string.won), + NumberFormat.getNumberInstance().format(price) + ) + viewModel.setProductInfo(productCode, title, price) - binding.etTargetPrice.setText(price.toString()) + binding.etTargetPrice.setText((price * 0.8).toInt().toString()) binding.initListener() handleEvent() @@ -50,26 +59,35 @@ class SetTargetPriceFragment : Fragment() { btnConfirmItemBack.setOnClickListener { findNavController().navigateUp() } - slTargetPrice.addOnChangeListener { slider, value, fromUser -> + slTargetPrice.addOnChangeListener { _, value, _ -> if (!etTargetPrice.isFocused) { - tvTargetPricePercent.text = - String.format(getString(R.string.current_price_percent), value.toInt()) - etTargetPrice.setText(((viewModel?.state?.value?.productPrice ?: 0) * value.toInt() / 100).toString()) + setTargetPriceAndPercent(value) } } + slTargetPrice.addOnSliderTouchListener(object : OnSliderTouchListener { + override fun onStartTrackingTouch(slider: Slider) { + etTargetPrice.clearFocus() + setTargetPriceAndPercent(slider.value) + } + + override fun onStopTrackingTouch(slider: Slider) { + } + }) etTargetPrice.addTextChangedListener { if (etTargetPrice.isFocused) { if (it.toString().matches("^\\d+\$".toRegex())) { val targetPrice = it.toString().toFloat() - var percent = ((targetPrice / (viewModel?.state?.value?.productPrice ?: 0)) * 100).toInt() + var percent = + ((targetPrice / (viewModel?.state?.value?.productPrice ?: 0)) * 100).toInt() tvTargetPricePercent.text = String.format(getString(R.string.current_price_percent), percent) + percent = 10 * ((percent + 5) / 10) if (targetPrice > (viewModel?.state?.value?.productPrice ?: 0)) { tvTargetPricePercent.text = getString(R.string.over_current_price) percent = 100 } else if (percent < 1) { - percent = 1 + percent = 0 } slTargetPrice.value = percent.toFloat() } @@ -77,6 +95,14 @@ class SetTargetPriceFragment : Fragment() { } } + private fun FragmentSetTargetPriceBinding.setTargetPriceAndPercent(value: Float) { + tvTargetPricePercent.text = + String.format(getString(R.string.current_price_percent), value.toInt()) + etTargetPrice.setText( + ((viewModel?.state?.value?.productPrice ?: 0) * value.toInt() / 100).toString() + ) + } + private fun handleEvent() { repeatOnStarted { viewModel.event.collect { event -> @@ -86,7 +112,6 @@ class SetTargetPriceFragment : Fragment() { SetTargetPriceViewModel.SetTargetPriceEvent.SuccessProductAdd -> { activity?.finish() - // TODO: AddProductItem 액티비티 종료 후 상품 세부 정보 화면으로 이동하기 } } } diff --git a/android/app/src/main/java/app/priceguard/ui/additem/setprice/SetTargetPriceViewModel.kt b/android/app/src/main/java/app/priceguard/ui/additem/setprice/SetTargetPriceViewModel.kt index d61d8c6..b7c054e 100644 --- a/android/app/src/main/java/app/priceguard/ui/additem/setprice/SetTargetPriceViewModel.kt +++ b/android/app/src/main/java/app/priceguard/ui/additem/setprice/SetTargetPriceViewModel.kt @@ -59,9 +59,17 @@ class SetTargetPriceViewModel @Inject constructor(private val productRepository: } fun updateTargetPrice(price: String) { - _state.value = state.value.copy(targetPrice = price.toInt()) + if (price.toIntOrNull() != null) { + _state.value = state.value.copy(targetPrice = price.toInt()) + } } + fun setProductInfo(productCode: String, name: String, price: Int) { - _state.value = state.value.copy(productCode = productCode, productName = name, productPrice = price) + _state.value = + state.value.copy( + productCode = productCode, + productName = name, + productPrice = price + ) } } diff --git a/android/app/src/main/java/app/priceguard/ui/home/mypage/MyPageFragment.kt b/android/app/src/main/java/app/priceguard/ui/home/mypage/MyPageFragment.kt index 7cf6152..5406092 100644 --- a/android/app/src/main/java/app/priceguard/ui/home/mypage/MyPageFragment.kt +++ b/android/app/src/main/java/app/priceguard/ui/home/mypage/MyPageFragment.kt @@ -14,6 +14,7 @@ import app.priceguard.databinding.FragmentMyPageBinding import app.priceguard.ui.home.mypage.MyPageViewModel.MyPageEvent import app.priceguard.ui.intro.IntroActivity import app.priceguard.ui.util.lifecycle.repeatOnStarted +import com.google.android.gms.oss.licenses.OssLicensesMenuActivity import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @@ -69,7 +70,7 @@ class MyPageFragment : Fragment() { } Setting.LICENSE -> { - // TODO: 오픈소스 라이선스 + startActivity(Intent(activity, OssLicensesMenuActivity::class.java)) } Setting.LOGOUT -> { diff --git a/android/app/src/main/java/app/priceguard/ui/home/mypage/MyPageViewModel.kt b/android/app/src/main/java/app/priceguard/ui/home/mypage/MyPageViewModel.kt index 63596ea..c553dd2 100644 --- a/android/app/src/main/java/app/priceguard/ui/home/mypage/MyPageViewModel.kt +++ b/android/app/src/main/java/app/priceguard/ui/home/mypage/MyPageViewModel.kt @@ -26,8 +26,8 @@ class MyPageViewModel @Inject constructor( val firstName: String ) - private val _flow = MutableStateFlow(MyPageInfo("", "", "")) - val flow = _flow.asStateFlow() + private val _state = MutableStateFlow(MyPageInfo("", "", "")) + val state = _state.asStateFlow() private val _event = MutableSharedFlow() val event = _event.asSharedFlow() @@ -39,20 +39,19 @@ class MyPageViewModel @Inject constructor( private fun setInfo() { viewModelScope.launch { val userData = tokenRepository.getUserData() - _flow.value = - MyPageInfo(userData.name, userData.email, if (userData.name.isNotEmpty()) userData.name.first().toString() else "") + _state.value = + MyPageInfo( + userData.name, + userData.email, + if (userData.name.isNotEmpty()) userData.name.first().toString() else "" + ) } } fun logout() { viewModelScope.launch { - val resetTokenJob = launch { - tokenRepository.clearTokens() - } - resetTokenJob.join() - if (resetTokenJob.isCompleted) { - _event.emit(MyPageEvent.StartIntroAndExitHome) - } + tokenRepository.clearTokens() + _event.emit(MyPageEvent.StartIntroAndExitHome) } } } diff --git a/android/app/src/main/res/layout/fragment_confirm_item_link.xml b/android/app/src/main/res/layout/fragment_confirm_item_link.xml index 60fe11c..603db39 100644 --- a/android/app/src/main/res/layout/fragment_confirm_item_link.xml +++ b/android/app/src/main/res/layout/fragment_confirm_item_link.xml @@ -5,8 +5,8 @@ + name="imageUrl" + type="String" /> + app:imageFromUrl="@{imageUrl}"/> @@ -102,7 +100,6 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="16dp" - android:text="@{viewModel.flow.productPrice.toString()}" android:textAppearance="?attr/textAppearanceTitleMedium" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@id/iv_confirm_item_shop_logo" /> diff --git a/android/app/src/main/res/layout/fragment_my_page.xml b/android/app/src/main/res/layout/fragment_my_page.xml index c5ff19d..f26b28e 100644 --- a/android/app/src/main/res/layout/fragment_my_page.xml +++ b/android/app/src/main/res/layout/fragment_my_page.xml @@ -74,7 +74,7 @@ android:layout_margin="16dp" android:background="@drawable/bg_circle" android:gravity="center" - android:text="@{viewModel.flow.firstName}" + android:text="@{viewModel.state.firstName}" android:textAppearance="?attr/textAppearanceTitleMedium" android:textColor="?attr/colorSurface" app:layout_constraintBottom_toBottomOf="parent" @@ -86,7 +86,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" - android:text="@{viewModel.flow.name}" + android:text="@{viewModel.state.name}" android:textAppearance="?attr/textAppearanceBodyLarge" app:layout_constraintBottom_toTopOf="@id/tv_my_page_email" app:layout_constraintStart_toEndOf="@id/tv_my_page_profile" @@ -96,7 +96,7 @@ android:id="@+id/tv_my_page_email" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@{viewModel.flow.email}" + android:text="@{viewModel.state.email}" android:textAppearance="?attr/textAppearanceBodyMedium" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="@id/tv_my_page_name" diff --git a/android/app/src/main/res/layout/fragment_set_target_price.xml b/android/app/src/main/res/layout/fragment_set_target_price.xml index 7f68250..141a67c 100644 --- a/android/app/src/main/res/layout/fragment_set_target_price.xml +++ b/android/app/src/main/res/layout/fragment_set_target_price.xml @@ -57,7 +57,6 @@ android:id="@+id/tv_set_price_current_price" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@{String.valueOf(viewModel.state.productPrice)}" android:textAppearance="@style/TextAppearance.Material3.TitleMedium" app:layout_constraintEnd_toStartOf="@id/gl_vertical_end" app:layout_constraintTop_toTopOf="@id/tv_set_price_name" /> @@ -73,6 +72,7 @@ android:paddingHorizontal="24dp" android:textSize="22sp" android:textStyle="bold" + android:imeOptions="actionDone" android:onTextChanged="@{(content, s, b, c) -> viewModel.updateTargetPrice(content.toString())}" app:layout_constraintEnd_toStartOf="@id/gl_vertical_end" app:layout_constraintStart_toEndOf="@id/gl_vertical_start" @@ -83,8 +83,9 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:focusable="true" + android:stepSize="10" android:value="80" - android:valueFrom="1" + android:valueFrom="0" android:valueTo="100" app:layout_constraintEnd_toStartOf="@id/gl_vertical_end" app:layout_constraintStart_toEndOf="@id/gl_vertical_start" diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 4593723..90cd942 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -15,7 +15,7 @@ 이름은 필수 입력 사항입니다. 유효하지 않은 이메일입니다. 유효한 이메일입니다. - 비밀번호는 8~16자 사이어야 합니다. + 비밀번호는 영문 대소문자와 특수문자가 포함된 8~16자 사이어야 합니다. 적합한 비밀번호입니다. 비밀번호가 일치하지 않습니다. 비밀번호가 일치합니다. @@ -78,4 +78,5 @@ 잘못된 요청입니다. 삭제되었습니다. 취소 + %s원 \ No newline at end of file diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 828f25a..4d3fd41 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -1,6 +1,11 @@ + + diff --git a/android/build.gradle.kts b/android/build.gradle.kts index b2aab4f..feaf270 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -11,6 +11,7 @@ plugins { buildscript { dependencies { + classpath("com.google.android.gms:oss-licenses-plugin:0.10.6") classpath("androidx.navigation:navigation-safe-args-gradle-plugin:2.7.5") } } diff --git a/android/release_notes.txt b/android/release_notes.txt index f3b06b0..98406d0 100644 --- a/android/release_notes.txt +++ b/android/release_notes.txt @@ -1,14 +1,13 @@ November 24, 2023 -v0.1.0 +v0.1.1 🚀 Feature Updates -- 이름, E-mail, password를 입력하여 회원가입 진행 -- E-mail, password를 입력하여 로그인 -- 로그아웃 버튼을 통해 로그아웃 -- 회원가입을 진행하면 자동 로그인 -- JWT 토큰을 통한 자동 로그인 기능 지원 -- 트래킹하고 싶은 상품을 URL을 통해 목표 가격과 함께 등록 -- 메인 화면에서 트래킹 중인 상품 목록을 확인 -- 상세 화면에서 트래킹 중지 버튼을 통해 상품을 삭제 -- 인기 상품 화면을 통해 트래킹중인 사용자가 많은 순서로 인기 상품 목록을 확인 \ No newline at end of file +- 오픈소스 라이선스 추가 +- 상품 추가 화면에서 목표 가격 설정을 단계별로 할 수 있게 추가 + + +🔧 Bug Fixes + +- 상품 추가 화면에서 목표 가격에 아무 값이 없으면 창이 꺼지는 문제 수정 +- 상품 인기 순위 섞이는 버그 수정 \ No newline at end of file