From 75a0fa69ac1d4744b2931fec68dbf34484171295 Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Wed, 10 Jan 2024 15:45:23 +0900 Subject: [PATCH 01/28] =?UTF-8?q?[feat]:=20OnBoardingRequestPermissionFrag?= =?UTF-8?q?ment=EC=97=90=20=EC=9D=B4=EC=A0=84=20=EA=B6=8C=ED=95=9C=20?= =?UTF-8?q?=ED=97=88=EC=9A=A9=20=ED=95=A8=EC=88=98=EB=93=A4=20=EC=98=AE?= =?UTF-8?q?=EA=B8=B0=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OnBoardingAccessibilityService.kt | 1 - .../OnBoardingRequestPermissionFragment.kt | 108 ++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingAccessibilityService.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingAccessibilityService.kt index b084a8aa8..7feb9442c 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingAccessibilityService.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingAccessibilityService.kt @@ -3,7 +3,6 @@ package com.hmh.hamyeonham.feature.onboarding import android.accessibilityservice.AccessibilityService import android.util.Log import android.view.accessibility.AccessibilityEvent -import com.hmh.hamyeonham.common.context.toast class OnBoardingAccessibilityService : AccessibilityService() { override fun onAccessibilityEvent(event: AccessibilityEvent) { diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt index 96152843e..f395d52eb 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt @@ -1,18 +1,41 @@ package com.hmh.hamyeonham.feature.onboarding.fragment +import android.app.usage.UsageStatsManager +import android.content.Context +import android.content.Intent +import android.net.Uri import android.os.Bundle +import android.provider.Settings import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels +import com.hmh.hamyeonham.common.fragment.toast import com.hmh.hamyeonham.common.view.viewBinding +import com.hmh.hamyeonham.feature.onboarding.OnBoardingAccessibilityService import com.hmh.hamyeonham.feature.onboarding.databinding.FragmentOnBoardingRequestPermissionBinding import com.hmh.hamyeonham.feature.onboarding.viewModel.OnBoardingViewModel +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class OnBoardingRequestPermissionFragment : Fragment() { private val binding by viewBinding(FragmentOnBoardingRequestPermissionBinding::bind) private val activityViewModel by activityViewModels() + + private val accessibilitySettingsLauncher: ActivityResultLauncher = + registerForActivityResult( + ActivityResultContracts.StartActivityForResult(), + ) { + if (isAccessibilityServiceEnabled()) { + toast("접근성 서비스가 활성화되었습니다.") + } else { + toast("접근성 서비스가 활성화되지 않았습니다.") + } + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -24,5 +47,90 @@ class OnBoardingRequestPermissionFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) activityViewModel.activeActivityNextButton() + clickRequireAccessibilityButton() + } + + // 각 버튼 클릭 시 권한 요청 + private fun clickRequireAccessibilityButton() { + binding.clOnboardingPermission1.setOnClickListener { // 접근 권한 허용 + requestAccessibilitySettings() + } + binding.clOnboardingPermission2.setOnClickListener { // 사용 정보 접근 권한 허용 + requestUsageAccessPermission() + } + binding.clOnboardingPermission3.setOnClickListener { // 다른 앱 위에 그리기 권한 허용 + if (!hasOverlayPermission()) { + requestOverlayPermission() + } else { + toast("다른 앱 위에 그리기 권한이 이미 허용되어 있습니다.") + } + } + if (isAccessibilityServiceEnabled() && hasUsageStatsPermission() && hasOverlayPermission()) { + // 다음 버튼 활성화 + } else { + // 다음 버튼 비활성화 + } + } + + // 접근 권한 허용 되었는지 확인 + private fun isAccessibilityServiceEnabled(): Boolean { + val service = + requireContext().packageName + "/" + OnBoardingAccessibilityService::class.java.canonicalName + val enabledServicesSetting = Settings.Secure.getString( + requireContext().contentResolver, + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, + ) + return enabledServicesSetting?.contains(service) == true + } + + // 접근 권한 요청 함수 + private fun requestAccessibilitySettings() { + if (!isAccessibilityServiceEnabled()) { + val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) + accessibilitySettingsLauncher.launch(intent) + } else { + // toast("접근성 권한이 이미 허용되어 있습니다.") + } + } + + // 다른 앱 위에 그리기 허용 되었는지 확인 + private fun hasOverlayPermission(): Boolean { + return Settings.canDrawOverlays(requireContext()) + } + + // 다른 앱 위에 그리기 권한 요청 함수 + private fun requestOverlayPermission() { + val packageUri = Uri.parse("package:" + requireContext().packageName) + val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, packageUri) + startActivity(intent) + } + + // 사용 정보 접근 권한 허용 되었는지 확인 + private fun hasUsageStatsPermission(): Boolean { + val usageStatsManager = + requireContext().getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager + val time = System.currentTimeMillis() + val stats = usageStatsManager.queryUsageStats( + UsageStatsManager.INTERVAL_DAILY, + time - 1000 * 60, + time, + ) + return stats != null && stats.isNotEmpty() + } + + // 사용 정보 접근 권한 요청 + private fun requestUsageAccessPermission() { + if (!hasUsageStatsPermission()) { + try { + val packageUri = Uri.parse("package:" + requireContext().packageName) + val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS, packageUri) + startActivity(intent) + } catch (e: Exception) { + val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS) + startActivity(intent) + } + } else { + toast("사용 정보 접근 권한이 이미 허용되어 있습니다.") + } } } From 51a6ea2245da97251533cce5ac93f2e411a30e8b Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Wed, 10 Jan 2024 16:52:30 +0900 Subject: [PATCH 02/28] =?UTF-8?q?[feat]:=20=EC=98=A8=EB=B3=B4=EB=94=A9=20?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=EA=B6=8C=ED=95=9C=20=ED=97=88?= =?UTF-8?q?=EC=9A=A9=20ActivityResultLauncher=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OnBoardingRequestPermissionFragment.kt | 42 ++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt index f395d52eb..3cb5c5703 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt @@ -25,14 +25,31 @@ class OnBoardingRequestPermissionFragment : Fragment() { private val binding by viewBinding(FragmentOnBoardingRequestPermissionBinding::bind) private val activityViewModel by activityViewModels() + // 권한 허용에 대한 결과를 받기 위한 ActivityResultLauncher private val accessibilitySettingsLauncher: ActivityResultLauncher = registerForActivityResult( ActivityResultContracts.StartActivityForResult(), ) { if (isAccessibilityServiceEnabled()) { - toast("접근성 서비스가 활성화되었습니다.") - } else { - toast("접근성 서비스가 활성화되지 않았습니다.") + toast("접근성 허용이 승인되었습니다") + } + } + + private val overlayPermissionLauncher: ActivityResultLauncher = + registerForActivityResult( + ActivityResultContracts.StartActivityForResult(), + ) { + if (hasOverlayPermission()) { + toast("다른 앱 위에 그리기 허용이 승인되었습니다") + } + } + + private val usageStatsPermissionLauncher: ActivityResultLauncher = + registerForActivityResult( + ActivityResultContracts.StartActivityForResult(), + ) { + if (hasUsageStatsPermission()) { + toast("사용 정보 접근 허용이 승인되었습니다") } } @@ -46,16 +63,22 @@ class OnBoardingRequestPermissionFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - activityViewModel.activeActivityNextButton() clickRequireAccessibilityButton() } // 각 버튼 클릭 시 권한 요청 private fun clickRequireAccessibilityButton() { binding.clOnboardingPermission1.setOnClickListener { // 접근 권한 허용 - requestAccessibilitySettings() + if (isAccessibilityServiceEnabled()) { + toast("접근성 허용이 이미 승인되어 있습니다") + } else { + requestAccessibilitySettings() + } } binding.clOnboardingPermission2.setOnClickListener { // 사용 정보 접근 권한 허용 + if (hasUsageStatsPermission()) { + toast("사용 정보 접근 권한이 이미 허용되어 있습니다.") + } requestUsageAccessPermission() } binding.clOnboardingPermission3.setOnClickListener { // 다른 앱 위에 그리기 권한 허용 @@ -66,7 +89,7 @@ class OnBoardingRequestPermissionFragment : Fragment() { } } if (isAccessibilityServiceEnabled() && hasUsageStatsPermission() && hasOverlayPermission()) { - // 다음 버튼 활성화 + activityViewModel.activeActivityNextButton() } else { // 다음 버튼 비활성화 } @@ -88,8 +111,6 @@ class OnBoardingRequestPermissionFragment : Fragment() { if (!isAccessibilityServiceEnabled()) { val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) accessibilitySettingsLauncher.launch(intent) - } else { - // toast("접근성 권한이 이미 허용되어 있습니다.") } } @@ -103,6 +124,7 @@ class OnBoardingRequestPermissionFragment : Fragment() { val packageUri = Uri.parse("package:" + requireContext().packageName) val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, packageUri) startActivity(intent) + overlayPermissionLauncher.launch(intent) } // 사용 정보 접근 권한 허용 되었는지 확인 @@ -125,12 +147,12 @@ class OnBoardingRequestPermissionFragment : Fragment() { val packageUri = Uri.parse("package:" + requireContext().packageName) val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS, packageUri) startActivity(intent) + usageStatsPermissionLauncher.launch(intent) } catch (e: Exception) { val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS) startActivity(intent) + usageStatsPermissionLauncher.launch(intent) } - } else { - toast("사용 정보 접근 권한이 이미 허용되어 있습니다.") } } } From 9a503e6e81b2886592d0d386e5b598cbb03686d9 Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Wed, 10 Jan 2024 16:55:43 +0900 Subject: [PATCH 03/28] =?UTF-8?q?[refact]:=20=EB=B6=84=EA=B8=B0=EB=AC=B8?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fragment/OnBoardingRequestPermissionFragment.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt index 3cb5c5703..200ae3156 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt @@ -82,16 +82,14 @@ class OnBoardingRequestPermissionFragment : Fragment() { requestUsageAccessPermission() } binding.clOnboardingPermission3.setOnClickListener { // 다른 앱 위에 그리기 권한 허용 - if (!hasOverlayPermission()) { - requestOverlayPermission() + if (hasOverlayPermission()) { + toast("사용 정보 접근 권한이 이미 허용되어 있습니다.") } else { - toast("다른 앱 위에 그리기 권한이 이미 허용되어 있습니다.") + requestUsageAccessPermission() } } if (isAccessibilityServiceEnabled() && hasUsageStatsPermission() && hasOverlayPermission()) { activityViewModel.activeActivityNextButton() - } else { - // 다음 버튼 비활성화 } } From 9b923367fc9b1f12318b21c8e04967564e612a52 Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Wed, 10 Jan 2024 17:22:38 +0900 Subject: [PATCH 04/28] =?UTF-8?q?[refact]:=20string=20=EC=A0=84=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OnBoardingRequestPermissionFragment.kt | 35 +++++++++---------- .../src/main/res/values/strings.xml | 6 ++++ 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt index 200ae3156..593195db6 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt @@ -16,6 +16,7 @@ import androidx.fragment.app.activityViewModels import com.hmh.hamyeonham.common.fragment.toast import com.hmh.hamyeonham.common.view.viewBinding import com.hmh.hamyeonham.feature.onboarding.OnBoardingAccessibilityService +import com.hmh.hamyeonham.feature.onboarding.R import com.hmh.hamyeonham.feature.onboarding.databinding.FragmentOnBoardingRequestPermissionBinding import com.hmh.hamyeonham.feature.onboarding.viewModel.OnBoardingViewModel import dagger.hilt.android.AndroidEntryPoint @@ -31,7 +32,7 @@ class OnBoardingRequestPermissionFragment : Fragment() { ActivityResultContracts.StartActivityForResult(), ) { if (isAccessibilityServiceEnabled()) { - toast("접근성 허용이 승인되었습니다") + toast(getString(R.string.success_accessibility_settings)) } } @@ -40,7 +41,7 @@ class OnBoardingRequestPermissionFragment : Fragment() { ActivityResultContracts.StartActivityForResult(), ) { if (hasOverlayPermission()) { - toast("다른 앱 위에 그리기 허용이 승인되었습니다") + toast(getString(R.string.success_overlay_permission)) } } @@ -49,7 +50,7 @@ class OnBoardingRequestPermissionFragment : Fragment() { ActivityResultContracts.StartActivityForResult(), ) { if (hasUsageStatsPermission()) { - toast("사용 정보 접근 허용이 승인되었습니다") + toast(getString(R.string.success_usage_stats_permission)) } } @@ -70,22 +71,22 @@ class OnBoardingRequestPermissionFragment : Fragment() { private fun clickRequireAccessibilityButton() { binding.clOnboardingPermission1.setOnClickListener { // 접근 권한 허용 if (isAccessibilityServiceEnabled()) { - toast("접근성 허용이 이미 승인되어 있습니다") + toast(getString(R.string.already_accessibility_settings)) } else { requestAccessibilitySettings() } } binding.clOnboardingPermission2.setOnClickListener { // 사용 정보 접근 권한 허용 if (hasUsageStatsPermission()) { - toast("사용 정보 접근 권한이 이미 허용되어 있습니다.") + toast(getString(R.string.already_usage_stats_permission)) } requestUsageAccessPermission() } binding.clOnboardingPermission3.setOnClickListener { // 다른 앱 위에 그리기 권한 허용 if (hasOverlayPermission()) { - toast("사용 정보 접근 권한이 이미 허용되어 있습니다.") + toast(getString(R.string.already_overlay_permission)) } else { - requestUsageAccessPermission() + requestOverlayPermission() } } if (isAccessibilityServiceEnabled() && hasUsageStatsPermission() && hasOverlayPermission()) { @@ -140,17 +141,15 @@ class OnBoardingRequestPermissionFragment : Fragment() { // 사용 정보 접근 권한 요청 private fun requestUsageAccessPermission() { - if (!hasUsageStatsPermission()) { - try { - val packageUri = Uri.parse("package:" + requireContext().packageName) - val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS, packageUri) - startActivity(intent) - usageStatsPermissionLauncher.launch(intent) - } catch (e: Exception) { - val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS) - startActivity(intent) - usageStatsPermissionLauncher.launch(intent) - } + try { + val packageUri = Uri.parse("package:" + requireContext().packageName) + val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS, packageUri) + startActivity(intent) + usageStatsPermissionLauncher.launch(intent) + } catch (e: Exception) { + val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS) + startActivity(intent) + usageStatsPermissionLauncher.launch(intent) } } } diff --git a/feature/onboarding/src/main/res/values/strings.xml b/feature/onboarding/src/main/res/values/strings.xml index 1d7bc7e46..788cdf17c 100644 --- a/feature/onboarding/src/main/res/values/strings.xml +++ b/feature/onboarding/src/main/res/values/strings.xml @@ -28,4 +28,10 @@ 확인 하루 평균 휴대폰을 \n얼마나 사용하시나요? 해당 문항은 최대 2개까지 선택할 수 있어요 + 접근성 허용이 승인되었습니다 + 다른 앱 위에 그리기 허용이 승인되었습니다 + 사용 정보 접근 허용이 승인되었습니다 + 이미 권한이 허용되어 있습니다 + 이미 권한이 허용되어 있습니다 + 이미 권한이 허용되어 있습니다 From 7704c72989cc5e74ed36bcccf65aaee8c320220d Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Wed, 10 Jan 2024 17:23:33 +0900 Subject: [PATCH 05/28] =?UTF-8?q?[delete]:=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fragment/OnBoardingRequestPermissionFragment.kt | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt index 593195db6..39ed2be5e 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt @@ -26,7 +26,6 @@ class OnBoardingRequestPermissionFragment : Fragment() { private val binding by viewBinding(FragmentOnBoardingRequestPermissionBinding::bind) private val activityViewModel by activityViewModels() - // 권한 허용에 대한 결과를 받기 위한 ActivityResultLauncher private val accessibilitySettingsLauncher: ActivityResultLauncher = registerForActivityResult( ActivityResultContracts.StartActivityForResult(), @@ -69,20 +68,20 @@ class OnBoardingRequestPermissionFragment : Fragment() { // 각 버튼 클릭 시 권한 요청 private fun clickRequireAccessibilityButton() { - binding.clOnboardingPermission1.setOnClickListener { // 접근 권한 허용 + binding.clOnboardingPermission1.setOnClickListener { if (isAccessibilityServiceEnabled()) { toast(getString(R.string.already_accessibility_settings)) } else { requestAccessibilitySettings() } } - binding.clOnboardingPermission2.setOnClickListener { // 사용 정보 접근 권한 허용 + binding.clOnboardingPermission2.setOnClickListener { if (hasUsageStatsPermission()) { toast(getString(R.string.already_usage_stats_permission)) } requestUsageAccessPermission() } - binding.clOnboardingPermission3.setOnClickListener { // 다른 앱 위에 그리기 권한 허용 + binding.clOnboardingPermission3.setOnClickListener { if (hasOverlayPermission()) { toast(getString(R.string.already_overlay_permission)) } else { @@ -94,7 +93,6 @@ class OnBoardingRequestPermissionFragment : Fragment() { } } - // 접근 권한 허용 되었는지 확인 private fun isAccessibilityServiceEnabled(): Boolean { val service = requireContext().packageName + "/" + OnBoardingAccessibilityService::class.java.canonicalName @@ -105,7 +103,6 @@ class OnBoardingRequestPermissionFragment : Fragment() { return enabledServicesSetting?.contains(service) == true } - // 접근 권한 요청 함수 private fun requestAccessibilitySettings() { if (!isAccessibilityServiceEnabled()) { val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) @@ -113,12 +110,10 @@ class OnBoardingRequestPermissionFragment : Fragment() { } } - // 다른 앱 위에 그리기 허용 되었는지 확인 private fun hasOverlayPermission(): Boolean { return Settings.canDrawOverlays(requireContext()) } - // 다른 앱 위에 그리기 권한 요청 함수 private fun requestOverlayPermission() { val packageUri = Uri.parse("package:" + requireContext().packageName) val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, packageUri) @@ -126,7 +121,6 @@ class OnBoardingRequestPermissionFragment : Fragment() { overlayPermissionLauncher.launch(intent) } - // 사용 정보 접근 권한 허용 되었는지 확인 private fun hasUsageStatsPermission(): Boolean { val usageStatsManager = requireContext().getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager @@ -139,7 +133,6 @@ class OnBoardingRequestPermissionFragment : Fragment() { return stats != null && stats.isNotEmpty() } - // 사용 정보 접근 권한 요청 private fun requestUsageAccessPermission() { try { val packageUri = Uri.parse("package:" + requireContext().packageName) From 8ae117e56c50af07b60be1bdff67c5ea78b7f593 Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Wed, 10 Jan 2024 17:23:47 +0900 Subject: [PATCH 06/28] =?UTF-8?q?[delete]:=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../onboarding/fragment/OnBoardingRequestPermissionFragment.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt index 39ed2be5e..1d1da034c 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt @@ -66,7 +66,6 @@ class OnBoardingRequestPermissionFragment : Fragment() { clickRequireAccessibilityButton() } - // 각 버튼 클릭 시 권한 요청 private fun clickRequireAccessibilityButton() { binding.clOnboardingPermission1.setOnClickListener { if (isAccessibilityServiceEnabled()) { From 373628013dafdf38be2eeb6ae23a1893d1a2f28e Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Wed, 10 Jan 2024 18:53:54 +0900 Subject: [PATCH 07/28] [refact]: refact code --- .../OnBoardingRequestPermissionFragment.kt | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt index 1d1da034c..6c9773f08 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt @@ -63,30 +63,37 @@ class OnBoardingRequestPermissionFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + updateNextButtonState() clickRequireAccessibilityButton() } private fun clickRequireAccessibilityButton() { - binding.clOnboardingPermission1.setOnClickListener { - if (isAccessibilityServiceEnabled()) { - toast(getString(R.string.already_accessibility_settings)) - } else { - requestAccessibilitySettings() + binding.run { + clOnboardingPermission1.setOnClickListener { + if (isAccessibilityServiceEnabled()) { + toast(getString(R.string.already_accessibility_settings)) + } else { + requestAccessibilitySettings() + } } - } - binding.clOnboardingPermission2.setOnClickListener { - if (hasUsageStatsPermission()) { - toast(getString(R.string.already_usage_stats_permission)) + clOnboardingPermission2.setOnClickListener { + if (hasUsageStatsPermission()) { + toast(getString(R.string.already_usage_stats_permission)) + } else { + requestUsageAccessPermission() + } } - requestUsageAccessPermission() - } - binding.clOnboardingPermission3.setOnClickListener { - if (hasOverlayPermission()) { - toast(getString(R.string.already_overlay_permission)) - } else { - requestOverlayPermission() + clOnboardingPermission3.setOnClickListener { + if (hasOverlayPermission()) { + toast(getString(R.string.already_overlay_permission)) + } else { + requestOverlayPermission() + } } } + } + + private fun updateNextButtonState() { if (isAccessibilityServiceEnabled() && hasUsageStatsPermission() && hasOverlayPermission()) { activityViewModel.activeActivityNextButton() } From e5a2a4e531bc5a7ac5fcd6e89acdcc6cac7a55c7 Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Wed, 10 Jan 2024 19:08:15 +0900 Subject: [PATCH 08/28] =?UTF-8?q?[feat]:=20RequestPermissionFragment=20vie?= =?UTF-8?q?wModel=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OnBoardingRequestPermissionFragment.kt | 13 ++++++++----- .../OnBoardingRequestPermissionViewModel.kt | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingRequestPermissionViewModel.kt diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt index 6c9773f08..69cb6011d 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt @@ -13,24 +13,27 @@ import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels +import androidx.fragment.app.viewModels import com.hmh.hamyeonham.common.fragment.toast import com.hmh.hamyeonham.common.view.viewBinding import com.hmh.hamyeonham.feature.onboarding.OnBoardingAccessibilityService import com.hmh.hamyeonham.feature.onboarding.R import com.hmh.hamyeonham.feature.onboarding.databinding.FragmentOnBoardingRequestPermissionBinding +import com.hmh.hamyeonham.feature.onboarding.viewModel.OnBoardingRequestPermissionViewModel import com.hmh.hamyeonham.feature.onboarding.viewModel.OnBoardingViewModel import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class OnBoardingRequestPermissionFragment : Fragment() { private val binding by viewBinding(FragmentOnBoardingRequestPermissionBinding::bind) + private val viewModel by viewModels() private val activityViewModel by activityViewModels() private val accessibilitySettingsLauncher: ActivityResultLauncher = registerForActivityResult( ActivityResultContracts.StartActivityForResult(), ) { - if (isAccessibilityServiceEnabled()) { + if (checkAccessibilityServiceEnabled()) { toast(getString(R.string.success_accessibility_settings)) } } @@ -70,7 +73,7 @@ class OnBoardingRequestPermissionFragment : Fragment() { private fun clickRequireAccessibilityButton() { binding.run { clOnboardingPermission1.setOnClickListener { - if (isAccessibilityServiceEnabled()) { + if (checkAccessibilityServiceEnabled()) { toast(getString(R.string.already_accessibility_settings)) } else { requestAccessibilitySettings() @@ -94,12 +97,12 @@ class OnBoardingRequestPermissionFragment : Fragment() { } private fun updateNextButtonState() { - if (isAccessibilityServiceEnabled() && hasUsageStatsPermission() && hasOverlayPermission()) { + if (checkAccessibilityServiceEnabled() && hasUsageStatsPermission() && hasOverlayPermission()) { activityViewModel.activeActivityNextButton() } } - private fun isAccessibilityServiceEnabled(): Boolean { + private fun checkAccessibilityServiceEnabled(): Boolean { val service = requireContext().packageName + "/" + OnBoardingAccessibilityService::class.java.canonicalName val enabledServicesSetting = Settings.Secure.getString( @@ -110,7 +113,7 @@ class OnBoardingRequestPermissionFragment : Fragment() { } private fun requestAccessibilitySettings() { - if (!isAccessibilityServiceEnabled()) { + if (!checkAccessibilityServiceEnabled()) { val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) accessibilitySettingsLauncher.launch(intent) } diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingRequestPermissionViewModel.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingRequestPermissionViewModel.kt new file mode 100644 index 000000000..b0aac0dca --- /dev/null +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingRequestPermissionViewModel.kt @@ -0,0 +1,19 @@ +package com.hmh.hamyeonham.feature.onboarding.viewModel + +import android.content.Context +import android.provider.Settings +import androidx.lifecycle.ViewModel +import com.hmh.hamyeonham.feature.onboarding.OnBoardingAccessibilityService +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import javax.inject.Inject + +@HiltViewModel +class OnBoardingRequestPermissionViewModel @Inject constructor() : ViewModel() { + + + private val _isAccessibilityServiceEnabled = MutableStateFlow(false) + val isAccessibilityServiceEnabled = _isAccessibilityServiceEnabled.asStateFlow() + +} From 0fd46ba259d498dbf3da293ec4745c8675d8a2e6 Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Wed, 10 Jan 2024 20:35:35 +0900 Subject: [PATCH 09/28] =?UTF-8?q?[feat]:=20viewModel=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OnBoardingRequestPermissionFragment.kt | 57 ++++++------------- .../OnBoardingRequestPermissionViewModel.kt | 50 +++++++++++++++- 2 files changed, 66 insertions(+), 41 deletions(-) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt index 69cb6011d..d92dca054 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt @@ -1,7 +1,5 @@ package com.hmh.hamyeonham.feature.onboarding.fragment -import android.app.usage.UsageStatsManager -import android.content.Context import android.content.Intent import android.net.Uri import android.os.Bundle @@ -15,13 +13,15 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import com.hmh.hamyeonham.common.fragment.toast +import com.hmh.hamyeonham.common.fragment.viewLifeCycleScope import com.hmh.hamyeonham.common.view.viewBinding -import com.hmh.hamyeonham.feature.onboarding.OnBoardingAccessibilityService import com.hmh.hamyeonham.feature.onboarding.R import com.hmh.hamyeonham.feature.onboarding.databinding.FragmentOnBoardingRequestPermissionBinding import com.hmh.hamyeonham.feature.onboarding.viewModel.OnBoardingRequestPermissionViewModel import com.hmh.hamyeonham.feature.onboarding.viewModel.OnBoardingViewModel import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach @AndroidEntryPoint class OnBoardingRequestPermissionFragment : Fragment() { @@ -33,7 +33,7 @@ class OnBoardingRequestPermissionFragment : Fragment() { registerForActivityResult( ActivityResultContracts.StartActivityForResult(), ) { - if (checkAccessibilityServiceEnabled()) { + if (viewModel.permissionsState.value.isAccessibilityEnabled) { toast(getString(R.string.success_accessibility_settings)) } } @@ -42,7 +42,7 @@ class OnBoardingRequestPermissionFragment : Fragment() { registerForActivityResult( ActivityResultContracts.StartActivityForResult(), ) { - if (hasOverlayPermission()) { + if (viewModel.permissionsState.value.isOverlayEnabled) { toast(getString(R.string.success_overlay_permission)) } } @@ -51,7 +51,7 @@ class OnBoardingRequestPermissionFragment : Fragment() { registerForActivityResult( ActivityResultContracts.StartActivityForResult(), ) { - if (hasUsageStatsPermission()) { + if (viewModel.permissionsState.value.isUsageStatsEnabled) { toast(getString(R.string.success_usage_stats_permission)) } } @@ -66,28 +66,33 @@ class OnBoardingRequestPermissionFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - updateNextButtonState() + + viewModel.permissionsState.onEach { permissionsState -> + updateNextButtonState(permissionsState) + }.launchIn(viewLifeCycleScope) + clickRequireAccessibilityButton() + viewModel.checkPermissions(requireContext()) } private fun clickRequireAccessibilityButton() { binding.run { clOnboardingPermission1.setOnClickListener { - if (checkAccessibilityServiceEnabled()) { + if (viewModel.permissionsState.value.isAccessibilityEnabled) { toast(getString(R.string.already_accessibility_settings)) } else { requestAccessibilitySettings() } } clOnboardingPermission2.setOnClickListener { - if (hasUsageStatsPermission()) { + if (viewModel.permissionsState.value.isUsageStatsEnabled) { toast(getString(R.string.already_usage_stats_permission)) } else { requestUsageAccessPermission() } } clOnboardingPermission3.setOnClickListener { - if (hasOverlayPermission()) { + if (viewModel.permissionsState.value.isOverlayEnabled) { toast(getString(R.string.already_overlay_permission)) } else { requestOverlayPermission() @@ -96,33 +101,19 @@ class OnBoardingRequestPermissionFragment : Fragment() { } } - private fun updateNextButtonState() { - if (checkAccessibilityServiceEnabled() && hasUsageStatsPermission() && hasOverlayPermission()) { + private fun updateNextButtonState(permissionsState: OnBoardingRequestPermissionViewModel.PermissionsState) { + if (permissionsState.isAccessibilityEnabled && permissionsState.isUsageStatsEnabled && permissionsState.isOverlayEnabled) { activityViewModel.activeActivityNextButton() } } - private fun checkAccessibilityServiceEnabled(): Boolean { - val service = - requireContext().packageName + "/" + OnBoardingAccessibilityService::class.java.canonicalName - val enabledServicesSetting = Settings.Secure.getString( - requireContext().contentResolver, - Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, - ) - return enabledServicesSetting?.contains(service) == true - } - private fun requestAccessibilitySettings() { - if (!checkAccessibilityServiceEnabled()) { + if (!viewModel.permissionsState.value.isAccessibilityEnabled) { val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) accessibilitySettingsLauncher.launch(intent) } } - private fun hasOverlayPermission(): Boolean { - return Settings.canDrawOverlays(requireContext()) - } - private fun requestOverlayPermission() { val packageUri = Uri.parse("package:" + requireContext().packageName) val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, packageUri) @@ -130,18 +121,6 @@ class OnBoardingRequestPermissionFragment : Fragment() { overlayPermissionLauncher.launch(intent) } - private fun hasUsageStatsPermission(): Boolean { - val usageStatsManager = - requireContext().getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager - val time = System.currentTimeMillis() - val stats = usageStatsManager.queryUsageStats( - UsageStatsManager.INTERVAL_DAILY, - time - 1000 * 60, - time, - ) - return stats != null && stats.isNotEmpty() - } - private fun requestUsageAccessPermission() { try { val packageUri = Uri.parse("package:" + requireContext().packageName) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingRequestPermissionViewModel.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingRequestPermissionViewModel.kt index b0aac0dca..6fe895d1c 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingRequestPermissionViewModel.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingRequestPermissionViewModel.kt @@ -1,19 +1,65 @@ package com.hmh.hamyeonham.feature.onboarding.viewModel +import android.app.usage.UsageStatsManager import android.content.Context import android.provider.Settings import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import com.hmh.hamyeonham.feature.onboarding.OnBoardingAccessibilityService import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class OnBoardingRequestPermissionViewModel @Inject constructor() : ViewModel() { + data class PermissionsState( + val isAccessibilityEnabled: Boolean = false, + val isUsageStatsEnabled: Boolean = false, + val isOverlayEnabled: Boolean = false, + ) + private val _permissionsState = MutableStateFlow(PermissionsState()) + val permissionsState = _permissionsState.asStateFlow() - private val _isAccessibilityServiceEnabled = MutableStateFlow(false) - val isAccessibilityServiceEnabled = _isAccessibilityServiceEnabled.asStateFlow() + fun checkPermissions(context: Context) { + viewModelScope.launch { + val accessibilityEnabled = checkAccessibilityServiceEnabled(context) + val usageStatsEnabled = hasUsageStatsPermission(context) + val overlayEnabled = hasOverlayPermission(context) + _permissionsState.value = PermissionsState( + accessibilityEnabled, + usageStatsEnabled, + overlayEnabled, + ) + } + } + + private fun checkAccessibilityServiceEnabled(context: Context): Boolean { + val service = + context.packageName + "/" + OnBoardingAccessibilityService::class.java.canonicalName + val enabledServicesSetting = Settings.Secure.getString( + context.contentResolver, + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, + ) + return enabledServicesSetting?.contains(service) == true + } + + private fun hasUsageStatsPermission(context: Context): Boolean { + val usageStatsManager = + context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager + val time = System.currentTimeMillis() + val stats = usageStatsManager.queryUsageStats( + UsageStatsManager.INTERVAL_DAILY, + time - 1000 * 60, + time, + ) + return stats != null && stats.isNotEmpty() + } + + private fun hasOverlayPermission(context: Context): Boolean { + return Settings.canDrawOverlays(context) + } } From ec2ec58293a5603f2454771e6846635494697d78 Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Wed, 10 Jan 2024 20:37:58 +0900 Subject: [PATCH 10/28] =?UTF-8?q?[feat]:=20PermissionsState=20data=20class?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fragment/OnBoardingRequestPermissionFragment.kt | 3 ++- .../onboarding/model/OnBoardingPermissionsState.kt | 7 +++++++ .../viewModel/OnBoardingRequestPermissionViewModel.kt | 10 +++------- 3 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingPermissionsState.kt diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt index d92dca054..46cdf50b2 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt @@ -17,6 +17,7 @@ import com.hmh.hamyeonham.common.fragment.viewLifeCycleScope import com.hmh.hamyeonham.common.view.viewBinding import com.hmh.hamyeonham.feature.onboarding.R import com.hmh.hamyeonham.feature.onboarding.databinding.FragmentOnBoardingRequestPermissionBinding +import com.hmh.hamyeonham.feature.onboarding.model.OnBoardingPermissionsState import com.hmh.hamyeonham.feature.onboarding.viewModel.OnBoardingRequestPermissionViewModel import com.hmh.hamyeonham.feature.onboarding.viewModel.OnBoardingViewModel import dagger.hilt.android.AndroidEntryPoint @@ -101,7 +102,7 @@ class OnBoardingRequestPermissionFragment : Fragment() { } } - private fun updateNextButtonState(permissionsState: OnBoardingRequestPermissionViewModel.PermissionsState) { + private fun updateNextButtonState(permissionsState: OnBoardingPermissionsState) { if (permissionsState.isAccessibilityEnabled && permissionsState.isUsageStatsEnabled && permissionsState.isOverlayEnabled) { activityViewModel.activeActivityNextButton() } diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingPermissionsState.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingPermissionsState.kt new file mode 100644 index 000000000..e9aee6fb6 --- /dev/null +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingPermissionsState.kt @@ -0,0 +1,7 @@ +package com.hmh.hamyeonham.feature.onboarding.model + +data class OnBoardingPermissionsState( + val isAccessibilityEnabled: Boolean = false, + val isUsageStatsEnabled: Boolean = false, + val isOverlayEnabled: Boolean = false, +) \ No newline at end of file diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingRequestPermissionViewModel.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingRequestPermissionViewModel.kt index 6fe895d1c..6559f309c 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingRequestPermissionViewModel.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingRequestPermissionViewModel.kt @@ -6,6 +6,7 @@ import android.provider.Settings import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.hmh.hamyeonham.feature.onboarding.OnBoardingAccessibilityService +import com.hmh.hamyeonham.feature.onboarding.model.OnBoardingPermissionsState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -14,13 +15,8 @@ import javax.inject.Inject @HiltViewModel class OnBoardingRequestPermissionViewModel @Inject constructor() : ViewModel() { - data class PermissionsState( - val isAccessibilityEnabled: Boolean = false, - val isUsageStatsEnabled: Boolean = false, - val isOverlayEnabled: Boolean = false, - ) - private val _permissionsState = MutableStateFlow(PermissionsState()) + private val _permissionsState = MutableStateFlow(OnBoardingPermissionsState()) val permissionsState = _permissionsState.asStateFlow() fun checkPermissions(context: Context) { @@ -29,7 +25,7 @@ class OnBoardingRequestPermissionViewModel @Inject constructor() : ViewModel() { val usageStatsEnabled = hasUsageStatsPermission(context) val overlayEnabled = hasOverlayPermission(context) - _permissionsState.value = PermissionsState( + _permissionsState.value = OnBoardingPermissionsState( accessibilityEnabled, usageStatsEnabled, overlayEnabled, From a1228023458597e6de538abc0a9532034f425d59 Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Wed, 10 Jan 2024 20:41:09 +0900 Subject: [PATCH 11/28] =?UTF-8?q?[fix]:=20=ED=8C=8C=EC=9D=BC=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OnBoardingRequestPermissionFragment.kt | 2 +- .../{OnboardingBtnInfo.kt => OnBoardingBtnInfo.kt} | 2 +- ...dingInformation.kt => OnBoardingInformation.kt} | 2 +- .../onboarding/model/OnBoardingPermissionsState.kt | 2 +- .../onboarding/viewModel/OnBoardingViewModel.kt | 14 +++++++------- 5 files changed, 11 insertions(+), 11 deletions(-) rename feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/{OnboardingBtnInfo.kt => OnBoardingBtnInfo.kt} (80%) rename feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/{OnboardingInformation.kt => OnBoardingInformation.kt} (93%) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt index 46cdf50b2..683503ed7 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt @@ -61,7 +61,7 @@ class OnBoardingRequestPermissionFragment : Fragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, - ): View? { + ): View { return FragmentOnBoardingRequestPermissionBinding.inflate(inflater, container, false).root } diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingBtnInfo.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingBtnInfo.kt similarity index 80% rename from feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingBtnInfo.kt rename to feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingBtnInfo.kt index 728999f62..c6c39f303 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingBtnInfo.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingBtnInfo.kt @@ -1,6 +1,6 @@ package com.hmh.hamyeonham.feature.onboarding.model -data class OnboardingBtnInfo( +data class OnBoardingBtnInfo( val index: Int, val isClicked: Boolean, val text: String diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingInformation.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingInformation.kt similarity index 93% rename from feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingInformation.kt rename to feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingInformation.kt index 96f3bf75e..434d3e5c8 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingInformation.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingInformation.kt @@ -1,6 +1,6 @@ package com.hmh.hamyeonham.feature.onboarding.model -data class OnboardingInformation( +data class OnBoardingInformation( val usuallyUseTime: String = "", val problems: List = emptyList(), val challenge: List = emptyList(), diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingPermissionsState.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingPermissionsState.kt index e9aee6fb6..1f3832c2e 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingPermissionsState.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingPermissionsState.kt @@ -4,4 +4,4 @@ data class OnBoardingPermissionsState( val isAccessibilityEnabled: Boolean = false, val isUsageStatsEnabled: Boolean = false, val isOverlayEnabled: Boolean = false, -) \ No newline at end of file +) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingViewModel.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingViewModel.kt index 922b6ece1..3bb2091f0 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingViewModel.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingViewModel.kt @@ -1,8 +1,8 @@ package com.hmh.hamyeonham.feature.onboarding.viewModel import androidx.lifecycle.ViewModel -import com.hmh.hamyeonham.feature.onboarding.model.OnboardingBtnInfo -import com.hmh.hamyeonham.feature.onboarding.model.OnboardingInformation +import com.hmh.hamyeonham.feature.onboarding.model.OnBoardingBtnInfo +import com.hmh.hamyeonham.feature.onboarding.model.OnBoardingInformation import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -12,12 +12,12 @@ import javax.inject.Inject class OnBoardingViewModel @Inject constructor() : ViewModel() { private val _buttonInfoList = - MutableStateFlow>(initializeButtonInfoList()) + MutableStateFlow>(initializeButtonInfoList()) - private fun initializeButtonInfoList(): List { - val buttonInfoList = mutableListOf() + private fun initializeButtonInfoList(): List { + val buttonInfoList = mutableListOf() for (index in 0..3) { - buttonInfoList.add(OnboardingBtnInfo(index, false, "")) + buttonInfoList.add(OnBoardingBtnInfo(index, false, "")) } return buttonInfoList } @@ -58,7 +58,7 @@ class OnBoardingViewModel @Inject constructor() : ViewModel() { private fun updateOnboardingInformation() { val selectedButton = _buttonInfoList.value.find { it.isClicked } if (selectedButton != null) { - val onboardingInformation = OnboardingInformation( + val onboardingInformation = OnBoardingInformation( usuallyUseTime = selectedButton.text, problems = listOf(), challenge = listOf(), From e54fff63b8c5e2ebffbba6e38b66d6dcaf4accce Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Wed, 10 Jan 2024 20:53:25 +0900 Subject: [PATCH 12/28] =?UTF-8?q?[fix]:=20data=20class=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fragment/OnBoardingRequestPermissionFragment.kt | 5 +++-- .../onboarding/model/OnBoardingPermissionsState.kt | 7 +++++++ .../viewModel/OnBoardingRequestPermissionViewModel.kt | 10 +++------- 3 files changed, 13 insertions(+), 9 deletions(-) create mode 100644 feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingPermissionsState.kt diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt index d92dca054..683503ed7 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt @@ -17,6 +17,7 @@ import com.hmh.hamyeonham.common.fragment.viewLifeCycleScope import com.hmh.hamyeonham.common.view.viewBinding import com.hmh.hamyeonham.feature.onboarding.R import com.hmh.hamyeonham.feature.onboarding.databinding.FragmentOnBoardingRequestPermissionBinding +import com.hmh.hamyeonham.feature.onboarding.model.OnBoardingPermissionsState import com.hmh.hamyeonham.feature.onboarding.viewModel.OnBoardingRequestPermissionViewModel import com.hmh.hamyeonham.feature.onboarding.viewModel.OnBoardingViewModel import dagger.hilt.android.AndroidEntryPoint @@ -60,7 +61,7 @@ class OnBoardingRequestPermissionFragment : Fragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, - ): View? { + ): View { return FragmentOnBoardingRequestPermissionBinding.inflate(inflater, container, false).root } @@ -101,7 +102,7 @@ class OnBoardingRequestPermissionFragment : Fragment() { } } - private fun updateNextButtonState(permissionsState: OnBoardingRequestPermissionViewModel.PermissionsState) { + private fun updateNextButtonState(permissionsState: OnBoardingPermissionsState) { if (permissionsState.isAccessibilityEnabled && permissionsState.isUsageStatsEnabled && permissionsState.isOverlayEnabled) { activityViewModel.activeActivityNextButton() } diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingPermissionsState.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingPermissionsState.kt new file mode 100644 index 000000000..1f3832c2e --- /dev/null +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingPermissionsState.kt @@ -0,0 +1,7 @@ +package com.hmh.hamyeonham.feature.onboarding.model + +data class OnBoardingPermissionsState( + val isAccessibilityEnabled: Boolean = false, + val isUsageStatsEnabled: Boolean = false, + val isOverlayEnabled: Boolean = false, +) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingRequestPermissionViewModel.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingRequestPermissionViewModel.kt index 6fe895d1c..6559f309c 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingRequestPermissionViewModel.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingRequestPermissionViewModel.kt @@ -6,6 +6,7 @@ import android.provider.Settings import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.hmh.hamyeonham.feature.onboarding.OnBoardingAccessibilityService +import com.hmh.hamyeonham.feature.onboarding.model.OnBoardingPermissionsState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -14,13 +15,8 @@ import javax.inject.Inject @HiltViewModel class OnBoardingRequestPermissionViewModel @Inject constructor() : ViewModel() { - data class PermissionsState( - val isAccessibilityEnabled: Boolean = false, - val isUsageStatsEnabled: Boolean = false, - val isOverlayEnabled: Boolean = false, - ) - private val _permissionsState = MutableStateFlow(PermissionsState()) + private val _permissionsState = MutableStateFlow(OnBoardingPermissionsState()) val permissionsState = _permissionsState.asStateFlow() fun checkPermissions(context: Context) { @@ -29,7 +25,7 @@ class OnBoardingRequestPermissionViewModel @Inject constructor() : ViewModel() { val usageStatsEnabled = hasUsageStatsPermission(context) val overlayEnabled = hasOverlayPermission(context) - _permissionsState.value = PermissionsState( + _permissionsState.value = OnBoardingPermissionsState( accessibilityEnabled, usageStatsEnabled, overlayEnabled, From 40a0908e8271034ac8086aa5b3574ca75c0626f3 Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Wed, 10 Jan 2024 20:57:43 +0900 Subject: [PATCH 13/28] =?UTF-8?q?[fix]:=20naming=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{OnBoardingBtnInfo.kt => OnboardingBtnInfo.kt} | 6 +++--- ...dingInformation.kt => OnboardingInformation.kt} | 2 +- .../onboarding/viewModel/OnBoardingViewModel.kt | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) rename feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/{OnBoardingBtnInfo.kt => OnboardingBtnInfo.kt} (65%) rename feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/{OnBoardingInformation.kt => OnboardingInformation.kt} (93%) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingBtnInfo.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingBtnInfo.kt similarity index 65% rename from feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingBtnInfo.kt rename to feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingBtnInfo.kt index c6c39f303..aca274953 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingBtnInfo.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingBtnInfo.kt @@ -1,7 +1,7 @@ package com.hmh.hamyeonham.feature.onboarding.model -data class OnBoardingBtnInfo( +data class OnboardingBtnInfo( val index: Int, val isClicked: Boolean, - val text: String -) \ No newline at end of file + val text: String, +) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingInformation.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingInformation.kt similarity index 93% rename from feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingInformation.kt rename to feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingInformation.kt index 434d3e5c8..96f3bf75e 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnBoardingInformation.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingInformation.kt @@ -1,6 +1,6 @@ package com.hmh.hamyeonham.feature.onboarding.model -data class OnBoardingInformation( +data class OnboardingInformation( val usuallyUseTime: String = "", val problems: List = emptyList(), val challenge: List = emptyList(), diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingViewModel.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingViewModel.kt index 3bb2091f0..922b6ece1 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingViewModel.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingViewModel.kt @@ -1,8 +1,8 @@ package com.hmh.hamyeonham.feature.onboarding.viewModel import androidx.lifecycle.ViewModel -import com.hmh.hamyeonham.feature.onboarding.model.OnBoardingBtnInfo -import com.hmh.hamyeonham.feature.onboarding.model.OnBoardingInformation +import com.hmh.hamyeonham.feature.onboarding.model.OnboardingBtnInfo +import com.hmh.hamyeonham.feature.onboarding.model.OnboardingInformation import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -12,12 +12,12 @@ import javax.inject.Inject class OnBoardingViewModel @Inject constructor() : ViewModel() { private val _buttonInfoList = - MutableStateFlow>(initializeButtonInfoList()) + MutableStateFlow>(initializeButtonInfoList()) - private fun initializeButtonInfoList(): List { - val buttonInfoList = mutableListOf() + private fun initializeButtonInfoList(): List { + val buttonInfoList = mutableListOf() for (index in 0..3) { - buttonInfoList.add(OnBoardingBtnInfo(index, false, "")) + buttonInfoList.add(OnboardingBtnInfo(index, false, "")) } return buttonInfoList } @@ -58,7 +58,7 @@ class OnBoardingViewModel @Inject constructor() : ViewModel() { private fun updateOnboardingInformation() { val selectedButton = _buttonInfoList.value.find { it.isClicked } if (selectedButton != null) { - val onboardingInformation = OnBoardingInformation( + val onboardingInformation = OnboardingInformation( usuallyUseTime = selectedButton.text, problems = listOf(), challenge = listOf(), From df45bbd2fc4b25f4d0f5d6844fb198e372acff5e Mon Sep 17 00:00:00 2001 From: KwakEuiJin Date: Thu, 11 Jan 2024 21:00:21 +0900 Subject: [PATCH 14/28] =?UTF-8?q?[fix]:=20OnBoarding=20=EC=A0=84=EC=B2=B4?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/hmh/hamyeonham/SampleActivity.kt | 3 +- .../hamyeonham/common/primitive/StringExt.kt | 9 ++ .../feature/onboarding/OnBoardingActivity.kt | 36 +++++--- .../OnBoardingRequestPermissionFragment.kt | 6 +- .../fragment/OnBoardingSelectAppFragment.kt | 4 +- .../fragment/OnBoardingSelectDataFragment.kt | 86 ++++++++++++++----- .../OnBoardingSelectScreenTimeFragment.kt | 4 +- .../OnBoardingSelectUseTimeFragment.kt | 6 +- .../onboarding/model/OnboardingAnswer.kt | 14 +++ .../onboarding/model/OnboardingBtnInfo.kt | 7 -- .../onboarding/model/OnboardingInformation.kt | 23 ----- .../onboarding/model/OnboardingPageInfo.kt | 7 ++ .../viewModel/OnBoardingViewModel.kt | 69 --------------- .../OnBoardingRequestPermissionViewModel.kt | 2 +- .../OnBoardingSelectDataViewModel.kt | 3 +- .../viewmodel/OnBoardingViewModel.kt | 63 ++++++++++++++ 16 files changed, 193 insertions(+), 149 deletions(-) create mode 100644 feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingAnswer.kt delete mode 100644 feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingBtnInfo.kt delete mode 100644 feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingInformation.kt create mode 100644 feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingPageInfo.kt delete mode 100644 feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingViewModel.kt rename feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/{viewModel => viewmodel}/OnBoardingRequestPermissionViewModel.kt (97%) rename feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/{viewModel => viewmodel}/OnBoardingSelectDataViewModel.kt (97%) create mode 100644 feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt diff --git a/app/src/main/java/com/hmh/hamyeonham/SampleActivity.kt b/app/src/main/java/com/hmh/hamyeonham/SampleActivity.kt index 2d0ee5b96..0d88b8a0a 100644 --- a/app/src/main/java/com/hmh/hamyeonham/SampleActivity.kt +++ b/app/src/main/java/com/hmh/hamyeonham/SampleActivity.kt @@ -10,6 +10,7 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import com.hmh.hamyeonham.common.view.viewBinding import com.hmh.hamyeonham.databinding.ActivitySampleBinding import com.hmh.hamyeonham.feature.main.MainActivity +import com.hmh.hamyeonham.feature.onboarding.OnBoardingActivity import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint @@ -21,7 +22,7 @@ class SampleActivity : AppCompatActivity() { val splashScreen = installSplashScreen() initSplashAnimation(splashScreen) setContentView(binding.root) - startActivity(Intent(this, MainActivity::class.java)) + startActivity(Intent(this, OnBoardingActivity::class.java)) finish() } diff --git a/core/common/src/main/java/com/hmh/hamyeonham/common/primitive/StringExt.kt b/core/common/src/main/java/com/hmh/hamyeonham/common/primitive/StringExt.kt index eeaaf8a6c..9eff91618 100644 --- a/core/common/src/main/java/com/hmh/hamyeonham/common/primitive/StringExt.kt +++ b/core/common/src/main/java/com/hmh/hamyeonham/common/primitive/StringExt.kt @@ -18,3 +18,12 @@ inline fun SpannableStringBuilder.textAppearance( @StyleRes style: Int, builderAction: SpannableStringBuilder.() -> Unit ) = inSpans(TextAppearanceSpan(context, style), builderAction = builderAction) + +fun String.extractDigits(): Int { + return try { + this.filter { it.isDigit() }.toIntOrNull() ?: 0 + } catch (e: NumberFormatException) { + 0 + } +} + diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingActivity.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingActivity.kt index 84e0877ba..e21fc0ccc 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingActivity.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingActivity.kt @@ -9,7 +9,8 @@ import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import com.hmh.hamyeonham.common.view.viewBinding import com.hmh.hamyeonham.feature.onboarding.databinding.ActivityOnBoardingBinding -import com.hmh.hamyeonham.feature.onboarding.viewModel.OnBoardingViewModel +import com.hmh.hamyeonham.feature.onboarding.viewmodel.OnBoardingEffect +import com.hmh.hamyeonham.feature.onboarding.viewmodel.OnBoardingViewModel import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -24,28 +25,34 @@ class OnBoardingActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(binding.root) - initViewPager() + initViews() checkNextButtonEnable() setBackPressedCallback() } + private fun initViews() { + initBackButton() + initViewPager() + } + + private fun initBackButton() { + binding.ivOnboardingBack.setOnClickListener { + navigateToPreviousOnboardingStep() + } + } + private fun initViewPager() { val pagerAdapter = setOnboardingPageAdapter() binding.btnOnboardingNext.setOnClickListener { - viewModel.initializeButtonStates() navigateToNextOnboardingStep(pagerAdapter) } - binding.ivOnboardingBack.setOnClickListener { - viewModel.initializeButtonStates() - navigateToPreviousOnboardingStep() - } } private fun navigateToPreviousOnboardingStep() { - binding.vpOnboardingContainer.let { viewPager -> - val currentItem = viewPager.currentItem + binding.vpOnboardingContainer.run { + val currentItem = this.currentItem if (currentItem > 0) { - viewPager.currentItem = currentItem - 1 + this.currentItem = currentItem - 1 } else { onBackPressedCallback.isEnabled = false onBackPressedDispatcher.onBackPressed() @@ -65,9 +72,12 @@ class OnBoardingActivity : AppCompatActivity() { } private fun checkNextButtonEnable() { - viewModel.clickNextButtonEnable.flowWithLifecycle(lifecycle).onEach { clickNextButtonEnable -> - binding.btnOnboardingNext.isEnabled = clickNextButtonEnable - binding.btnOnboardingNext.isSelected = clickNextButtonEnable + viewModel.onboardEffect.flowWithLifecycle(lifecycle).onEach { + when (it) { + is OnBoardingEffect.ActiveNextButton -> { + binding.btnOnboardingNext.isEnabled = it.isActive + } + } }.launchIn(lifecycleScope) } diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt index 683503ed7..8bbc3f5a9 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt @@ -18,8 +18,8 @@ import com.hmh.hamyeonham.common.view.viewBinding import com.hmh.hamyeonham.feature.onboarding.R import com.hmh.hamyeonham.feature.onboarding.databinding.FragmentOnBoardingRequestPermissionBinding import com.hmh.hamyeonham.feature.onboarding.model.OnBoardingPermissionsState -import com.hmh.hamyeonham.feature.onboarding.viewModel.OnBoardingRequestPermissionViewModel -import com.hmh.hamyeonham.feature.onboarding.viewModel.OnBoardingViewModel +import com.hmh.hamyeonham.feature.onboarding.viewmodel.OnBoardingRequestPermissionViewModel +import com.hmh.hamyeonham.feature.onboarding.viewmodel.OnBoardingViewModel import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -104,7 +104,7 @@ class OnBoardingRequestPermissionFragment : Fragment() { private fun updateNextButtonState(permissionsState: OnBoardingPermissionsState) { if (permissionsState.isAccessibilityEnabled && permissionsState.isUsageStatsEnabled && permissionsState.isOverlayEnabled) { - activityViewModel.activeActivityNextButton() + activityViewModel.changeStateNextButton(true) } } diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectAppFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectAppFragment.kt index ea5bf1a61..9ec75b0f3 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectAppFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectAppFragment.kt @@ -8,7 +8,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import com.hmh.hamyeonham.common.view.viewBinding import com.hmh.hamyeonham.feature.onboarding.databinding.FragmentOnBoardingSelectAppBinding -import com.hmh.hamyeonham.feature.onboarding.viewModel.OnBoardingViewModel +import com.hmh.hamyeonham.feature.onboarding.viewmodel.OnBoardingViewModel class OnBoardingSelectAppFragment : Fragment() { private val binding by viewBinding(FragmentOnBoardingSelectAppBinding::bind) @@ -24,6 +24,6 @@ class OnBoardingSelectAppFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - activityViewModel.activeActivityNextButton() + activityViewModel.changeStateNextButton(true) } } diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectDataFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectDataFragment.kt index d18a293b9..7ceda0f37 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectDataFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectDataFragment.kt @@ -4,16 +4,17 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.appcompat.widget.AppCompatButton import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope -import com.hmh.hamyeonham.common.fragment.viewLifeCycleScope +import com.hmh.hamyeonham.common.primitive.extractDigits import com.hmh.hamyeonham.common.view.viewBinding import com.hmh.hamyeonham.feature.onboarding.OnBoardingFragmentType import com.hmh.hamyeonham.feature.onboarding.databinding.FragmentOnBoardingSelectDataBinding -import com.hmh.hamyeonham.feature.onboarding.viewModel.OnBoardingSelectDataViewModel -import com.hmh.hamyeonham.feature.onboarding.viewModel.OnBoardingViewModel +import com.hmh.hamyeonham.feature.onboarding.viewmodel.OnBoardingSelectDataViewModel +import com.hmh.hamyeonham.feature.onboarding.viewmodel.OnBoardingViewModel import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -24,6 +25,10 @@ class OnBoardingSelectDataFragment : Fragment() { private val viewModel by viewModels() private val activityViewModel by activityViewModels() + private val selectedButtons = mutableSetOf() + private val fragmentType: OnBoardingFragmentType? + get() = arguments?.getString(ARG_FRAGMENT_TYPE)?.toOnboardingFragmentType() + companion object { private const val ARG_FRAGMENT_TYPE = "ARG_FRAGMENT_TYPE" fun newInstance(fragmentType: OnBoardingFragmentType): OnBoardingSelectDataFragment { @@ -46,18 +51,8 @@ class OnBoardingSelectDataFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - initializeFragmentType() initViews() - } - - private fun initializeFragmentType() { - val fragmentType = requireArguments().getString(ARG_FRAGMENT_TYPE)?.toOnboardingFragmentType() - if (fragmentType != null) { - viewModel.initQuestionData(fragmentType) - } - } - override fun onResume() { - super.onResume() + fragmentType?.let { viewModel.initQuestionData(it) } val onboardingFragmentButtonList = listOf( binding.btnOnboardingSelectData1, @@ -66,18 +61,13 @@ class OnBoardingSelectDataFragment : Fragment() { binding.btnOnboardingSelectData4, ) - onboardingFragmentButtonList.forEachIndexed { index, button -> + onboardingFragmentButtonList.forEachIndexed { _, button -> button.setOnClickListener { - activityViewModel.onClickFragmentBtn(index) + toggleButtonSelection(button) } } - - activityViewModel.buttonInfoList.onEach { buttonInfoList -> - onboardingFragmentButtonList.forEachIndexed { i, button -> - button.isSelected = buttonInfoList[i].isClicked - } - }.launchIn(viewLifeCycleScope) } + private fun initViews() { viewModel.onBoardingSelectDataState.onEach { binding.apply { @@ -99,4 +89,56 @@ class OnBoardingSelectDataFragment : Fragment() { OnBoardingFragmentType.SELECT_DATA_TIME } } + + private fun toggleButtonSelection(button: AppCompatButton) { + button.isSelected = !button.isSelected + updateSelectedButtons(button) + updateUserResponse() + } + + private fun updateSelectedButtons(selectedButton: AppCompatButton) { + if (selectedButton.isSelected) { + if (fragmentType == OnBoardingFragmentType.SELECT_DATA_PROBLEM && selectedButtons.size >= 2) { + val firstSelected = selectedButtons.elementAt(0) + firstSelected.isSelected = false + selectedButtons.remove(firstSelected) + } + selectedButtons.add(selectedButton) + } else { + selectedButtons.remove(selectedButton) + } + + if (fragmentType != OnBoardingFragmentType.SELECT_DATA_PROBLEM) { + selectedButtons.filter { it != selectedButton }.forEach { it.isSelected = false } + selectedButtons.clear() + if (selectedButton.isSelected) selectedButtons.add(selectedButton) + } + } + + private fun updateUserResponse() { + val selectedQuestion = selectedButtons.map { it.text.toString() } + val firstSelected = selectedQuestion.firstOrNull() + + when (fragmentType) { + OnBoardingFragmentType.SELECT_DATA_TIME -> { + activityViewModel.updateUserResponses { + copy(usuallyUseTime = firstSelected.orEmpty()) + } + } + + OnBoardingFragmentType.SELECT_DATA_PROBLEM -> { + activityViewModel.updateUserResponses { + copy(problems = selectedQuestion) + } + } + + OnBoardingFragmentType.SELECT_DATA_PERIOD -> { + activityViewModel.updateUserResponses { + copy(period = firstSelected?.extractDigits() ?: 0) + } + } + + else -> {} + } + } } diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectScreenTimeFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectScreenTimeFragment.kt index 9f3c0b10f..4e5466e03 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectScreenTimeFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectScreenTimeFragment.kt @@ -9,7 +9,7 @@ import androidx.fragment.app.activityViewModels import com.hmh.hamyeonham.common.view.setupScreentimeGoalRange import com.hmh.hamyeonham.common.view.viewBinding import com.hmh.hamyeonham.feature.onboarding.databinding.FragmentOnBoardingSelectScreentimeBinding -import com.hmh.hamyeonham.feature.onboarding.viewModel.OnBoardingViewModel +import com.hmh.hamyeonham.feature.onboarding.viewmodel.OnBoardingViewModel class OnBoardingSelectScreenTimeFragment : Fragment() { private val binding by viewBinding(FragmentOnBoardingSelectScreentimeBinding::bind) @@ -30,6 +30,6 @@ class OnBoardingSelectScreenTimeFragment : Fragment() { super.onViewCreated(view, savedInstanceState) binding.npOnboardingScreentimeGoal.setupScreentimeGoalRange(1, 6) - activityViewModel.activeActivityNextButton() + activityViewModel.changeStateNextButton(true) } } diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt index bcb18cb3b..b365dabbc 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt @@ -4,14 +4,12 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.NumberPicker import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.fragment.app.viewModels import com.hmh.hamyeonham.common.view.setupScreentimeGoalRange import com.hmh.hamyeonham.common.view.viewBinding import com.hmh.hamyeonham.feature.onboarding.databinding.FragmentOnBoardingSelectUseTimeBinding -import com.hmh.hamyeonham.feature.onboarding.viewModel.OnBoardingViewModel +import com.hmh.hamyeonham.feature.onboarding.viewmodel.OnBoardingViewModel class OnBoardingSelectUseTimeFragment : Fragment() { private val binding by viewBinding(FragmentOnBoardingSelectUseTimeBinding::bind) @@ -29,6 +27,6 @@ class OnBoardingSelectUseTimeFragment : Fragment() { binding.npOnboardingUseTimeGoalHour.setupScreentimeGoalRange(1, 6) binding.npOnboardingUseTimeGoalMinute.setupScreentimeGoalRange(0, 59) - activityViewModel.activeActivityNextButton() + activityViewModel.changeStateNextButton(true) } } diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingAnswer.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingAnswer.kt new file mode 100644 index 000000000..562b3e09e --- /dev/null +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingAnswer.kt @@ -0,0 +1,14 @@ +package com.hmh.hamyeonham.feature.onboarding.model + +data class OnboardingAnswer( + val usuallyUseTime: String = "", + val problems: List = emptyList(), + val period: Int = -1, + val goalTime: Int = -1, + val apps: List = emptyList(), +) { + data class App( + val appCode: String = "", + val goalTime: Int = -1, + ) +} diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingBtnInfo.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingBtnInfo.kt deleted file mode 100644 index aca274953..000000000 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingBtnInfo.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.hmh.hamyeonham.feature.onboarding.model - -data class OnboardingBtnInfo( - val index: Int, - val isClicked: Boolean, - val text: String, -) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingInformation.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingInformation.kt deleted file mode 100644 index 96f3bf75e..000000000 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingInformation.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.hmh.hamyeonham.feature.onboarding.model - -data class OnboardingInformation( - val usuallyUseTime: String = "", - val problems: List = emptyList(), - val challenge: List = emptyList(), - val apps: List = emptyList(), -) { - data class Problem( - val description1: String = "", - val description2: String = "", - ) - - data class Challenge( - val period: Int = -1, - val goalTime: Int = -1, - ) - - data class App( - val appCode: String = "", - val goalTime: Int = -1, - ) -} diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingPageInfo.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingPageInfo.kt new file mode 100644 index 000000000..86d77204d --- /dev/null +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingPageInfo.kt @@ -0,0 +1,7 @@ +package com.hmh.hamyeonham.feature.onboarding.model + +data class OnboardingPageInfo( + val index: Int, + val isClicked: Boolean = false, + val text: List = emptyList(), +) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingViewModel.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingViewModel.kt deleted file mode 100644 index 922b6ece1..000000000 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingViewModel.kt +++ /dev/null @@ -1,69 +0,0 @@ -package com.hmh.hamyeonham.feature.onboarding.viewModel - -import androidx.lifecycle.ViewModel -import com.hmh.hamyeonham.feature.onboarding.model.OnboardingBtnInfo -import com.hmh.hamyeonham.feature.onboarding.model.OnboardingInformation -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow -import javax.inject.Inject - -@HiltViewModel -class OnBoardingViewModel @Inject constructor() : ViewModel() { - - private val _buttonInfoList = - MutableStateFlow>(initializeButtonInfoList()) - - private fun initializeButtonInfoList(): List { - val buttonInfoList = mutableListOf() - for (index in 0..3) { - buttonInfoList.add(OnboardingBtnInfo(index, false, "")) - } - return buttonInfoList - } - - val buttonInfoList = _buttonInfoList.asStateFlow() - - private val _canClickActivityNextButton = MutableStateFlow(false) - val clickNextButtonEnable = _canClickActivityNextButton.asStateFlow() - - fun onClickFragmentBtn(index: Int) { - _buttonInfoList.value = _buttonInfoList.value.map { buttonInfo -> - if (buttonInfo.index == index) { - buttonInfo.copy(isClicked = !buttonInfo.isClicked) - } else { - buttonInfo.copy(isClicked = false) - } - } - - _canClickActivityNextButton.value = _buttonInfoList.value.any { it.isClicked } - } - - fun initializeButtonStates() { - val clickedButton = _buttonInfoList.value.find { it.isClicked } - _buttonInfoList.value = _buttonInfoList.value.map { buttonInfo -> - if (clickedButton != null && buttonInfo.index == clickedButton.index) { - buttonInfo.copy(isClicked = false) - } else { - buttonInfo - } - } - _canClickActivityNextButton.value = false - } - - fun activeActivityNextButton() { - _canClickActivityNextButton.value = true - } - - private fun updateOnboardingInformation() { - val selectedButton = _buttonInfoList.value.find { it.isClicked } - if (selectedButton != null) { - val onboardingInformation = OnboardingInformation( - usuallyUseTime = selectedButton.text, - problems = listOf(), - challenge = listOf(), - apps = listOf(), - ) - } - } -} diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingRequestPermissionViewModel.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingRequestPermissionViewModel.kt similarity index 97% rename from feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingRequestPermissionViewModel.kt rename to feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingRequestPermissionViewModel.kt index 6559f309c..35bc89971 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingRequestPermissionViewModel.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingRequestPermissionViewModel.kt @@ -1,4 +1,4 @@ -package com.hmh.hamyeonham.feature.onboarding.viewModel +package com.hmh.hamyeonham.feature.onboarding.viewmodel import android.app.usage.UsageStatsManager import android.content.Context diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingSelectDataViewModel.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingSelectDataViewModel.kt similarity index 97% rename from feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingSelectDataViewModel.kt rename to feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingSelectDataViewModel.kt index 18a6744e5..b85c14e5e 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingSelectDataViewModel.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingSelectDataViewModel.kt @@ -1,4 +1,4 @@ -package com.hmh.hamyeonham.feature.onboarding.viewModel +package com.hmh.hamyeonham.feature.onboarding.viewmodel import androidx.lifecycle.ViewModel import com.hmh.hamyeonham.feature.onboarding.OnBoardingFragmentType @@ -19,7 +19,6 @@ class OnBoardingSelectDataViewModel @Inject constructor() : ViewModel() { fun initQuestionData(fragmentType: OnBoardingFragmentType) { val onBoardingQuestionTime = OnBoardingQuestion( - "하루 평균 휴대폰을\n얼마나 사용하나요?", "", listOf("1-4시간", "4-8시간", "8-12시간", "12시간 이상"), diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt new file mode 100644 index 000000000..f06385cb8 --- /dev/null +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt @@ -0,0 +1,63 @@ +package com.hmh.hamyeonham.feature.onboarding.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.hmh.hamyeonham.feature.onboarding.model.OnboardingAnswer +import com.hmh.hamyeonham.feature.onboarding.model.OnboardingPageInfo +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +sealed interface OnBoardingEffect { + data class ActiveNextButton(val isActive: Boolean) : OnBoardingEffect +} + +data class OnBoardingState( + val onBoardingAnswer: OnboardingAnswer = OnboardingAnswer(), + val pageInfo: List = emptyList(), +) + +@HiltViewModel +class OnBoardingViewModel @Inject constructor() : ViewModel() { + + private val _userResponses = MutableStateFlow(OnboardingAnswer()) + val userResponses = _userResponses.asStateFlow() + + private val _onBoardingState = MutableStateFlow(OnBoardingState()) + val onBoardingState = _onBoardingState.asStateFlow() + + private val _onboardEffect = MutableSharedFlow() + val onboardEffect = _onboardEffect.asSharedFlow() + + init { + _onBoardingState.value = onBoardingState.value.copy( + pageInfo = initializeButtonInfoList() + ) + } + + fun changeStateNextButton(isActive: Boolean) { + viewModelScope.launch { + _onboardEffect.emit(OnBoardingEffect.ActiveNextButton(isActive)) + } + } + + fun updateUserResponses(transform: OnboardingAnswer.() -> OnboardingAnswer) { + val currentState = userResponses.value + val newState = currentState.transform() + _userResponses.value = newState + + + } + + private fun initializeButtonInfoList(): List { + val buttonInfoList = mutableListOf() + for (index in 0..3) { + buttonInfoList.add(OnboardingPageInfo(index)) + } + return buttonInfoList + } +} From 21997587e15519edad9e6cd2e55b7775645a23e9 Mon Sep 17 00:00:00 2001 From: KwakEuiJin Date: Thu, 11 Jan 2024 21:07:43 +0900 Subject: [PATCH 15/28] =?UTF-8?q?[fix]:=20Fragment=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fragment/OnBoardingSelectDataFragment.kt | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectDataFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectDataFragment.kt index 7ceda0f37..8c6f9ddc7 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectDataFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectDataFragment.kt @@ -52,23 +52,11 @@ class OnBoardingSelectDataFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initViews() - fragmentType?.let { viewModel.initQuestionData(it) } - - val onboardingFragmentButtonList = listOf( - binding.btnOnboardingSelectData1, - binding.btnOnboardingSelectData2, - binding.btnOnboardingSelectData3, - binding.btnOnboardingSelectData4, - ) - - onboardingFragmentButtonList.forEachIndexed { _, button -> - button.setOnClickListener { - toggleButtonSelection(button) - } - } + initFragmentType() } private fun initViews() { + initQuestionButton() viewModel.onBoardingSelectDataState.onEach { binding.apply { val onBoardingQuestion = it.onBoardingQuestion @@ -82,11 +70,28 @@ class OnBoardingSelectDataFragment : Fragment() { }.launchIn(lifecycleScope) } - private fun String.toOnboardingFragmentType(): OnBoardingFragmentType { - return try { - OnBoardingFragmentType.valueOf(this) - } catch (e: Exception) { - OnBoardingFragmentType.SELECT_DATA_TIME + private fun initQuestionButton() { + val onboardingFragmentButtonList = listOf( + binding.btnOnboardingSelectData1, + binding.btnOnboardingSelectData2, + binding.btnOnboardingSelectData3, + binding.btnOnboardingSelectData4, + ) + + onboardingFragmentButtonList.forEachIndexed { _, button -> + button.setOnClickListener { + toggleButtonSelection(button) + } + } + } + + private fun initFragmentType() { + fragmentType?.let { + viewModel.initQuestionData(it) + if (it == OnBoardingFragmentType.SELECT_DATA_PERIOD) { + binding.btnOnboardingSelectData3.isEnabled = false + binding.btnOnboardingSelectData4.isEnabled = false + } } } @@ -141,4 +146,12 @@ class OnBoardingSelectDataFragment : Fragment() { else -> {} } } + + private fun String.toOnboardingFragmentType(): OnBoardingFragmentType { + return try { + OnBoardingFragmentType.valueOf(this) + } catch (e: Exception) { + OnBoardingFragmentType.SELECT_DATA_TIME + } + } } From 400050a14e131c6cbc31a46f8a2e25c206bb4ae6 Mon Sep 17 00:00:00 2001 From: KwakEuiJin Date: Thu, 11 Jan 2024 21:14:40 +0900 Subject: [PATCH 16/28] =?UTF-8?q?[fix]:=20Permission=20Fragment=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fragment/OnBoardingRequestPermissionFragment.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt index 8bbc3f5a9..7cd84ef45 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt @@ -69,7 +69,11 @@ class OnBoardingRequestPermissionFragment : Fragment() { super.onViewCreated(view, savedInstanceState) viewModel.permissionsState.onEach { permissionsState -> - updateNextButtonState(permissionsState) + permissionsState.run { + val isPermissionAllGranted = + isAccessibilityEnabled && isUsageStatsEnabled && isOverlayEnabled + activityViewModel.changeStateNextButton(isPermissionAllGranted) + } }.launchIn(viewLifeCycleScope) clickRequireAccessibilityButton() From a565ebe3da719903a925f51a46c28ced285e38a5 Mon Sep 17 00:00:00 2001 From: KwakEuiJin Date: Thu, 11 Jan 2024 22:20:01 +0900 Subject: [PATCH 17/28] [fix]: flow collect --- .../feature/onboarding/OnBoardingActivity.kt | 1 + .../OnBoardingRequestPermissionFragment.kt | 24 ++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingActivity.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingActivity.kt index e21fc0ccc..db69f4933 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingActivity.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingActivity.kt @@ -76,6 +76,7 @@ class OnBoardingActivity : AppCompatActivity() { when (it) { is OnBoardingEffect.ActiveNextButton -> { binding.btnOnboardingNext.isEnabled = it.isActive + binding.btnOnboardingNext.isSelected = it.isActive } } }.launchIn(lifecycleScope) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt index 7cd84ef45..c49556b1e 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingRequestPermissionFragment.kt @@ -12,7 +12,9 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels +import androidx.lifecycle.flowWithLifecycle import com.hmh.hamyeonham.common.fragment.toast +import com.hmh.hamyeonham.common.fragment.viewLifeCycle import com.hmh.hamyeonham.common.fragment.viewLifeCycleScope import com.hmh.hamyeonham.common.view.viewBinding import com.hmh.hamyeonham.feature.onboarding.R @@ -67,19 +69,17 @@ class OnBoardingRequestPermissionFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - - viewModel.permissionsState.onEach { permissionsState -> - permissionsState.run { - val isPermissionAllGranted = - isAccessibilityEnabled && isUsageStatsEnabled && isOverlayEnabled - activityViewModel.changeStateNextButton(isPermissionAllGranted) - } - }.launchIn(viewLifeCycleScope) - + collectPermissionState() clickRequireAccessibilityButton() viewModel.checkPermissions(requireContext()) } + private fun collectPermissionState() { + viewModel.permissionsState.flowWithLifecycle(viewLifeCycle).onEach { permissionsState -> + updateNextButtonState(permissionsState) + }.launchIn(viewLifeCycleScope) + } + private fun clickRequireAccessibilityButton() { binding.run { clOnboardingPermission1.setOnClickListener { @@ -107,8 +107,10 @@ class OnBoardingRequestPermissionFragment : Fragment() { } private fun updateNextButtonState(permissionsState: OnBoardingPermissionsState) { - if (permissionsState.isAccessibilityEnabled && permissionsState.isUsageStatsEnabled && permissionsState.isOverlayEnabled) { - activityViewModel.changeStateNextButton(true) + permissionsState.run { + if (isAccessibilityEnabled && isUsageStatsEnabled && isOverlayEnabled) { + activityViewModel.changeStateNextButton(true) + } } } From 1c2a6e43e8db9abd93826ef462de316c404cb8fa Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Fri, 12 Jan 2024 16:36:23 +0900 Subject: [PATCH 18/28] =?UTF-8?q?[feat]:=20=EB=84=98=EB=B2=84=20=ED=94=BC?= =?UTF-8?q?=EC=BB=A4=20=EA=B0=92=20=EB=B0=9B=EC=95=84=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/hmh/hamyeonham/common/time/TimeExt.kt | 5 ++++ .../OnBoardingSelectScreenTimeFragment.kt | 9 +++++++ .../OnBoardingSelectUseTimeFragment.kt | 25 +++++++++++++++++++ .../onboarding/model/OnboardingAnswer.kt | 2 +- 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/core/common/src/main/java/com/hmh/hamyeonham/common/time/TimeExt.kt b/core/common/src/main/java/com/hmh/hamyeonham/common/time/TimeExt.kt index da2a509bb..2b0ccde60 100644 --- a/core/common/src/main/java/com/hmh/hamyeonham/common/time/TimeExt.kt +++ b/core/common/src/main/java/com/hmh/hamyeonham/common/time/TimeExt.kt @@ -59,3 +59,8 @@ fun convertTimeToString(time: Long): String { if (minutes > 0 || hours == 0L) append(" $minutes 분") }.trim() } + +// 분을 ms로 바꾸는 함수 +fun Int.timeToMs(): Long { + return this * 60 * 1000L +} \ No newline at end of file diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectScreenTimeFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectScreenTimeFragment.kt index 4e5466e03..7b616bce2 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectScreenTimeFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectScreenTimeFragment.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.NumberPicker import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import com.hmh.hamyeonham.common.view.setupScreentimeGoalRange @@ -31,5 +32,13 @@ class OnBoardingSelectScreenTimeFragment : Fragment() { binding.npOnboardingScreentimeGoal.setupScreentimeGoalRange(1, 6) activityViewModel.changeStateNextButton(true) + + binding.npOnboardingScreentimeGoal.descendantFocusability = + NumberPicker.FOCUS_BLOCK_DESCENDANTS + + val screenTimeGoalTime = binding.npOnboardingScreentimeGoal.value + activityViewModel.updateUserResponses { + copy(goalTime = screenTimeGoalTime) + } } } diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt index b365dabbc..f89ec9dae 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt @@ -1,14 +1,18 @@ package com.hmh.hamyeonham.feature.onboarding.fragment import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.NumberPicker import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels +import com.hmh.hamyeonham.common.time.timeToMs import com.hmh.hamyeonham.common.view.setupScreentimeGoalRange import com.hmh.hamyeonham.common.view.viewBinding import com.hmh.hamyeonham.feature.onboarding.databinding.FragmentOnBoardingSelectUseTimeBinding +import com.hmh.hamyeonham.feature.onboarding.model.OnboardingAnswer import com.hmh.hamyeonham.feature.onboarding.viewmodel.OnBoardingViewModel class OnBoardingSelectUseTimeFragment : Fragment() { @@ -27,6 +31,27 @@ class OnBoardingSelectUseTimeFragment : Fragment() { binding.npOnboardingUseTimeGoalHour.setupScreentimeGoalRange(1, 6) binding.npOnboardingUseTimeGoalMinute.setupScreentimeGoalRange(0, 59) + + binding.npOnboardingUseTimeGoalHour.descendantFocusability = + NumberPicker.FOCUS_BLOCK_DESCENDANTS + + val useGoalHour = binding.npOnboardingUseTimeGoalHour.value + val useGoalMinute = binding.npOnboardingUseTimeGoalMinute.value + val useTotalTime = (useGoalHour * 60 + useGoalMinute).timeToMs() + + Log.d("OnBoardingSelectUseTimeFragment", "useTotalTime: $useTotalTime.toString()") + activityViewModel.changeStateNextButton(true) + + activityViewModel.updateUserResponses { + copy( + apps = listOf( + OnboardingAnswer.App( + appCode = "", + goalTime = useTotalTime, + ), + ), + ) + } } } diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingAnswer.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingAnswer.kt index 562b3e09e..f31a8e214 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingAnswer.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingAnswer.kt @@ -9,6 +9,6 @@ data class OnboardingAnswer( ) { data class App( val appCode: String = "", - val goalTime: Int = -1, + val goalTime: Long = -1, ) } From a53eb83843f9f02395cad5635bc0e48605822c61 Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Fri, 12 Jan 2024 17:14:56 +0900 Subject: [PATCH 19/28] =?UTF-8?q?[feat]:=20=EB=84=98=EB=B2=84=20=ED=94=BC?= =?UTF-8?q?=EC=BB=A4=20=EA=B0=92=20=EB=B0=9B=EC=95=84=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/onboarding/OnBoardingActivity.kt | 2 + .../OnBoardingSelectScreenTimeFragment.kt | 9 +++- .../OnBoardingSelectUseTimeFragment.kt | 41 ++++++++++++------- .../onboarding/model/OnboardingAnswer.kt | 2 +- .../viewmodel/OnBoardingViewModel.kt | 3 +- 5 files changed, 38 insertions(+), 19 deletions(-) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingActivity.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingActivity.kt index db69f4933..49adf999d 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingActivity.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingActivity.kt @@ -2,6 +2,7 @@ package com.hmh.hamyeonham.feature.onboarding import android.content.Intent import android.os.Bundle +import android.util.Log import androidx.activity.OnBackPressedCallback import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity @@ -9,6 +10,7 @@ import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import com.hmh.hamyeonham.common.view.viewBinding import com.hmh.hamyeonham.feature.onboarding.databinding.ActivityOnBoardingBinding +import com.hmh.hamyeonham.feature.onboarding.model.OnboardingAnswer import com.hmh.hamyeonham.feature.onboarding.viewmodel.OnBoardingEffect import com.hmh.hamyeonham.feature.onboarding.viewmodel.OnBoardingViewModel import dagger.hilt.android.AndroidEntryPoint diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectScreenTimeFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectScreenTimeFragment.kt index 7b616bce2..33b6cd53a 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectScreenTimeFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectScreenTimeFragment.kt @@ -1,6 +1,7 @@ package com.hmh.hamyeonham.feature.onboarding.fragment import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -37,8 +38,12 @@ class OnBoardingSelectScreenTimeFragment : Fragment() { NumberPicker.FOCUS_BLOCK_DESCENDANTS val screenTimeGoalTime = binding.npOnboardingScreentimeGoal.value - activityViewModel.updateUserResponses { - copy(goalTime = screenTimeGoalTime) + + binding.npOnboardingScreentimeGoal.setOnValueChangedListener { _, _, _ -> + Log.d("NP", "useTotalTime: $screenTimeGoalTime") + activityViewModel.updateUserResponses { + copy(goalTime = screenTimeGoalTime) + } } } } diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt index f89ec9dae..b629004a3 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt @@ -29,29 +29,40 @@ class OnBoardingSelectUseTimeFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.npOnboardingUseTimeGoalHour.setupScreentimeGoalRange(1, 6) + binding.npOnboardingUseTimeGoalHour.setupScreentimeGoalRange(0, 1) binding.npOnboardingUseTimeGoalMinute.setupScreentimeGoalRange(0, 59) - binding.npOnboardingUseTimeGoalHour.descendantFocusability = NumberPicker.FOCUS_BLOCK_DESCENDANTS + activityViewModel.changeStateNextButton(true) + val useGoalHour = binding.npOnboardingUseTimeGoalHour.value val useGoalMinute = binding.npOnboardingUseTimeGoalMinute.value val useTotalTime = (useGoalHour * 60 + useGoalMinute).timeToMs() - - Log.d("OnBoardingSelectUseTimeFragment", "useTotalTime: $useTotalTime.toString()") - - activityViewModel.changeStateNextButton(true) - - activityViewModel.updateUserResponses { - copy( - apps = listOf( - OnboardingAnswer.App( - appCode = "", - goalTime = useTotalTime, + Log.d("NP", "useTotalTime: $useTotalTime") + binding.npOnboardingUseTimeGoalHour.setOnValueChangedListener { _, _, _ -> + activityViewModel.updateUserResponses { + copy( + apps = listOf( + OnboardingAnswer.App( + appCode = "", + goalTime = useTotalTime, + ), + ), + ) + } + } + binding.npOnboardingUseTimeGoalMinute.setOnValueChangedListener { _, _, _ -> + activityViewModel.updateUserResponses { + copy( + apps = listOf( + OnboardingAnswer.App( + appCode = "", + goalTime = useTotalTime, + ), ), - ), - ) + ) + } } } } diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingAnswer.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingAnswer.kt index f31a8e214..29b043f53 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingAnswer.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/model/OnboardingAnswer.kt @@ -4,7 +4,7 @@ data class OnboardingAnswer( val usuallyUseTime: String = "", val problems: List = emptyList(), val period: Int = -1, - val goalTime: Int = -1, + val goalTime: Int = 1, val apps: List = emptyList(), ) { data class App( diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt index f06385cb8..120e66e7a 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt @@ -1,5 +1,6 @@ package com.hmh.hamyeonham.feature.onboarding.viewmodel +import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.hmh.hamyeonham.feature.onboarding.model.OnboardingAnswer @@ -49,7 +50,7 @@ class OnBoardingViewModel @Inject constructor() : ViewModel() { val currentState = userResponses.value val newState = currentState.transform() _userResponses.value = newState - + Log.d("TAG", "${_userResponses.value}") } From 2b726e7df41099af53b3a03f540cc1990ef6aeec Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Fri, 12 Jan 2024 17:50:43 +0900 Subject: [PATCH 20/28] =?UTF-8?q?[feat]:=20=EB=84=98=EB=B2=84=20=ED=94=BC?= =?UTF-8?q?=EC=BB=A4=20=EA=B0=92=20=ED=8C=8C=EC=8B=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OnBoardingSelectScreenTimeFragment.kt | 7 +- .../OnBoardingSelectUseTimeFragment.kt | 65 ++++++++++--------- 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectScreenTimeFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectScreenTimeFragment.kt index 33b6cd53a..7dbd3fa56 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectScreenTimeFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectScreenTimeFragment.kt @@ -37,12 +37,11 @@ class OnBoardingSelectScreenTimeFragment : Fragment() { binding.npOnboardingScreentimeGoal.descendantFocusability = NumberPicker.FOCUS_BLOCK_DESCENDANTS - val screenTimeGoalTime = binding.npOnboardingScreentimeGoal.value - binding.npOnboardingScreentimeGoal.setOnValueChangedListener { _, _, _ -> - Log.d("NP", "useTotalTime: $screenTimeGoalTime") + binding.npOnboardingScreentimeGoal.setOnValueChangedListener { _, _, newTime -> + Log.d("NP", "useTotalTime: $newTime") activityViewModel.updateUserResponses { - copy(goalTime = screenTimeGoalTime) + copy(goalTime = newTime) } } } diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt index b629004a3..0a2ad5add 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt @@ -1,7 +1,6 @@ package com.hmh.hamyeonham.feature.onboarding.fragment import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -18,6 +17,8 @@ import com.hmh.hamyeonham.feature.onboarding.viewmodel.OnBoardingViewModel class OnBoardingSelectUseTimeFragment : Fragment() { private val binding by viewBinding(FragmentOnBoardingSelectUseTimeBinding::bind) private val activityViewModel by activityViewModels() + private var useTotalTime: Long = 0 + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -29,40 +30,42 @@ class OnBoardingSelectUseTimeFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.npOnboardingUseTimeGoalHour.setupScreentimeGoalRange(0, 1) - binding.npOnboardingUseTimeGoalMinute.setupScreentimeGoalRange(0, 59) - binding.npOnboardingUseTimeGoalHour.descendantFocusability = - NumberPicker.FOCUS_BLOCK_DESCENDANTS + setNumberPicker() + handleNumberPickerValue() + } - activityViewModel.changeStateNextButton(true) + private fun handleNumberPickerValue() { + binding.npOnboardingUseTimeGoalHour.setOnValueChangedListener { _, _, newTime -> + useTotalTime = (newTime * 60 + binding.npOnboardingUseTimeGoalMinute.value).timeToMs() + updateViewModel() + } + binding.npOnboardingUseTimeGoalMinute.setOnValueChangedListener { _, _, newTime -> + useTotalTime = (binding.npOnboardingUseTimeGoalHour.value * 60 + newTime).timeToMs() + updateViewModel() + } + } - val useGoalHour = binding.npOnboardingUseTimeGoalHour.value - val useGoalMinute = binding.npOnboardingUseTimeGoalMinute.value - val useTotalTime = (useGoalHour * 60 + useGoalMinute).timeToMs() - Log.d("NP", "useTotalTime: $useTotalTime") - binding.npOnboardingUseTimeGoalHour.setOnValueChangedListener { _, _, _ -> - activityViewModel.updateUserResponses { - copy( - apps = listOf( - OnboardingAnswer.App( - appCode = "", - goalTime = useTotalTime, - ), - ), - ) - } + private fun setNumberPicker() { + binding.run { + npOnboardingUseTimeGoalHour.setupScreentimeGoalRange(0, 1) + npOnboardingUseTimeGoalMinute.setupScreentimeGoalRange(0, 59) + npOnboardingUseTimeGoalHour.descendantFocusability = + NumberPicker.FOCUS_BLOCK_DESCENDANTS } - binding.npOnboardingUseTimeGoalMinute.setOnValueChangedListener { _, _, _ -> - activityViewModel.updateUserResponses { - copy( - apps = listOf( - OnboardingAnswer.App( - appCode = "", - goalTime = useTotalTime, - ), + activityViewModel.changeStateNextButton(true) + } + + + private fun updateViewModel() { + activityViewModel.updateUserResponses { + copy( + apps = listOf( + OnboardingAnswer.App( + appCode = "", + goalTime = useTotalTime, ), - ) - } + ), + ) } } } From f15476b510215b904cb4f470809bae5b6d20fdf0 Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Fri, 12 Jan 2024 17:56:51 +0900 Subject: [PATCH 21/28] =?UTF-8?q?[feat]:=20=EC=84=A0=ED=83=9D=ED=95=9C=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=200=EC=9D=B8=20=EA=B2=BD=EC=9A=B0=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EB=B9=84=ED=99=9C=EC=84=B1=ED=99=94=20?= =?UTF-8?q?=EB=B6=84=EA=B8=B0=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fragment/OnBoardingSelectUseTimeFragment.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt index 0a2ad5add..3e2e02844 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt @@ -17,7 +17,7 @@ import com.hmh.hamyeonham.feature.onboarding.viewmodel.OnBoardingViewModel class OnBoardingSelectUseTimeFragment : Fragment() { private val binding by viewBinding(FragmentOnBoardingSelectUseTimeBinding::bind) private val activityViewModel by activityViewModels() - private var useTotalTime: Long = 0 + private var useTotalTime: Long = 0L override fun onCreateView( inflater: LayoutInflater, @@ -32,6 +32,11 @@ class OnBoardingSelectUseTimeFragment : Fragment() { setNumberPicker() handleNumberPickerValue() + if (useTotalTime != 0L) { + activityViewModel.changeStateNextButton(true) + } else { + activityViewModel.changeStateNextButton(false) + } } private fun handleNumberPickerValue() { @@ -52,10 +57,8 @@ class OnBoardingSelectUseTimeFragment : Fragment() { npOnboardingUseTimeGoalHour.descendantFocusability = NumberPicker.FOCUS_BLOCK_DESCENDANTS } - activityViewModel.changeStateNextButton(true) } - private fun updateViewModel() { activityViewModel.updateUserResponses { copy( From e5d87a9085a49e622bf81943c06be0177f5ff9d7 Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Fri, 12 Jan 2024 18:19:14 +0900 Subject: [PATCH 22/28] =?UTF-8?q?[feat]:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=84=B1=EA=B3=B5=20=EC=8B=9C=20main=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99=20=EB=B7=B0=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/onboarding/build.gradle.kts | 1 + .../feature/onboarding/OnBoardingDoneSingUpActivity.kt | 8 ++++++++ .../fragment/OnBoardingSelectUseTimeFragment.kt | 8 +++----- .../feature/onboarding/viewmodel/OnBoardingViewModel.kt | 3 +-- .../main/res/layout/activity_on_boarding_done_sing_up.xml | 5 +++-- 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/feature/onboarding/build.gradle.kts b/feature/onboarding/build.gradle.kts index 81e4178b2..eb064cd87 100644 --- a/feature/onboarding/build.gradle.kts +++ b/feature/onboarding/build.gradle.kts @@ -10,4 +10,5 @@ android { dependencies { implementation(projects.core.common) implementation(projects.core.designsystem) + implementation(project(":feature:main")) } diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingDoneSingUpActivity.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingDoneSingUpActivity.kt index ebad218e3..11eccd337 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingDoneSingUpActivity.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingDoneSingUpActivity.kt @@ -1,8 +1,10 @@ package com.hmh.hamyeonham.feature.onboarding +import android.content.Intent import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import com.hmh.hamyeonham.common.view.viewBinding +import com.hmh.hamyeonham.feature.main.MainActivity import com.hmh.hamyeonham.feature.onboarding.databinding.ActivityOnBoardingDoneSingUpBinding class OnBoardingDoneSingUpActivity : AppCompatActivity() { @@ -10,5 +12,11 @@ class OnBoardingDoneSingUpActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(binding.root) + + binding.btnOnboardingDoneNext.setOnClickListener { + val intent = Intent(this, MainActivity::class.java) + startActivity(intent) + finish() + } } } diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt index 3e2e02844..038cbebff 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectUseTimeFragment.kt @@ -32,19 +32,17 @@ class OnBoardingSelectUseTimeFragment : Fragment() { setNumberPicker() handleNumberPickerValue() - if (useTotalTime != 0L) { - activityViewModel.changeStateNextButton(true) - } else { - activityViewModel.changeStateNextButton(false) - } + // activityViewModel.changeStateNextButton(false) } private fun handleNumberPickerValue() { binding.npOnboardingUseTimeGoalHour.setOnValueChangedListener { _, _, newTime -> + activityViewModel.changeStateNextButton(true) useTotalTime = (newTime * 60 + binding.npOnboardingUseTimeGoalMinute.value).timeToMs() updateViewModel() } binding.npOnboardingUseTimeGoalMinute.setOnValueChangedListener { _, _, newTime -> + activityViewModel.changeStateNextButton(true) useTotalTime = (binding.npOnboardingUseTimeGoalHour.value * 60 + newTime).timeToMs() updateViewModel() } diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt index 120e66e7a..2f06f7918 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt @@ -36,7 +36,7 @@ class OnBoardingViewModel @Inject constructor() : ViewModel() { init { _onBoardingState.value = onBoardingState.value.copy( - pageInfo = initializeButtonInfoList() + pageInfo = initializeButtonInfoList(), ) } @@ -51,7 +51,6 @@ class OnBoardingViewModel @Inject constructor() : ViewModel() { val newState = currentState.transform() _userResponses.value = newState Log.d("TAG", "${_userResponses.value}") - } private fun initializeButtonInfoList(): List { diff --git a/feature/onboarding/src/main/res/layout/activity_on_boarding_done_sing_up.xml b/feature/onboarding/src/main/res/layout/activity_on_boarding_done_sing_up.xml index 19937d0a5..5ee711e91 100644 --- a/feature/onboarding/src/main/res/layout/activity_on_boarding_done_sing_up.xml +++ b/feature/onboarding/src/main/res/layout/activity_on_boarding_done_sing_up.xml @@ -65,10 +65,11 @@ android:layout_height="0dp" android:layout_marginHorizontal="20dp" android:layout_marginBottom="21dp" - android:background="@drawable/onboarding_selector_btn_next" + android:background="@drawable/all_next_btn_able" android:gravity="center" android:text="@string/all_okay" - android:textColor="#DBDAE7" + android:textColor="?white_text" + android:textAppearance="@style/TextAppearance.Hmh.TitleMedium" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> From 0834613dd25e01ed44c28b6cdff364a494231e37 Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Fri, 12 Jan 2024 18:26:12 +0900 Subject: [PATCH 23/28] =?UTF-8?q?[fix]:=20=ED=99=94=EB=A9=B4=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=20Navigation=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../navigation/DefaultNavigationProvider.kt | 7 ++++++- .../common/navigation/NavigationProvider.kt | 2 ++ .../OnBoardingDoneSingUpActivity.kt | 19 ++++++++++++++----- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/hmh/hamyeonham/navigation/DefaultNavigationProvider.kt b/app/src/main/java/com/hmh/hamyeonham/navigation/DefaultNavigationProvider.kt index 7f067392d..b7f8fe553 100644 --- a/app/src/main/java/com/hmh/hamyeonham/navigation/DefaultNavigationProvider.kt +++ b/app/src/main/java/com/hmh/hamyeonham/navigation/DefaultNavigationProvider.kt @@ -5,13 +5,14 @@ import android.content.Intent import com.hmh.hamyeonham.common.navigation.NavigationProvider import com.hmh.hamyeonham.feature.login.LoginActivity import com.hmh.hamyeonham.feature.login.UserInfoActivity +import com.hmh.hamyeonham.feature.main.MainActivity import com.hmh.hamyeonham.feature.onboarding.OnBoardingActivity import com.hmh.hamyeonham.statistics.StaticsActivity import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject class DefaultNavigationProvider @Inject constructor( - @ApplicationContext private val context: Context + @ApplicationContext private val context: Context, ) : NavigationProvider { override fun toOnboarding(): Intent { return Intent(context, OnBoardingActivity::class.java) @@ -28,4 +29,8 @@ class DefaultNavigationProvider @Inject constructor( override fun toStatics(): Intent { return Intent(context, StaticsActivity::class.java) } + + override fun toMain(): Intent { + return Intent(context, MainActivity::class.java) + } } diff --git a/core/common/src/main/java/com/hmh/hamyeonham/common/navigation/NavigationProvider.kt b/core/common/src/main/java/com/hmh/hamyeonham/common/navigation/NavigationProvider.kt index 606c00c43..0d23df478 100644 --- a/core/common/src/main/java/com/hmh/hamyeonham/common/navigation/NavigationProvider.kt +++ b/core/common/src/main/java/com/hmh/hamyeonham/common/navigation/NavigationProvider.kt @@ -7,4 +7,6 @@ interface NavigationProvider { fun toLogin(): Intent fun toUserInfo(): Intent fun toStatics(): Intent + + fun toMain(): Intent } diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingDoneSingUpActivity.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingDoneSingUpActivity.kt index 11eccd337..41a944acf 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingDoneSingUpActivity.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/OnBoardingDoneSingUpActivity.kt @@ -1,22 +1,31 @@ package com.hmh.hamyeonham.feature.onboarding -import android.content.Intent import android.os.Bundle import androidx.appcompat.app.AppCompatActivity +import com.hmh.hamyeonham.common.navigation.NavigationProvider import com.hmh.hamyeonham.common.view.viewBinding -import com.hmh.hamyeonham.feature.main.MainActivity import com.hmh.hamyeonham.feature.onboarding.databinding.ActivityOnBoardingDoneSingUpBinding +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject +@AndroidEntryPoint class OnBoardingDoneSingUpActivity : AppCompatActivity() { private val binding by viewBinding(ActivityOnBoardingDoneSingUpBinding::inflate) + + @Inject + lateinit var navigationProvider: NavigationProvider + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(binding.root) binding.btnOnboardingDoneNext.setOnClickListener { - val intent = Intent(this, MainActivity::class.java) - startActivity(intent) - finish() + moveToMainActivity() } } + + private fun moveToMainActivity() { + startActivity(navigationProvider.toMain()) + finish() + } } From 9a05e055c544b4940a4cb6654d3bb70f64f3d120 Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Fri, 12 Jan 2024 18:55:44 +0900 Subject: [PATCH 24/28] =?UTF-8?q?[fix]:=20selector=20state=5Fenabled?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/onboarding/viewmodel/OnBoardingViewModel.kt | 2 -- .../src/main/res/drawable/onboarding_selector_btn_next.xml | 5 ++--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt index 2f06f7918..cd4c175ee 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewmodel/OnBoardingViewModel.kt @@ -1,6 +1,5 @@ package com.hmh.hamyeonham.feature.onboarding.viewmodel -import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.hmh.hamyeonham.feature.onboarding.model.OnboardingAnswer @@ -50,7 +49,6 @@ class OnBoardingViewModel @Inject constructor() : ViewModel() { val currentState = userResponses.value val newState = currentState.transform() _userResponses.value = newState - Log.d("TAG", "${_userResponses.value}") } private fun initializeButtonInfoList(): List { diff --git a/feature/onboarding/src/main/res/drawable/onboarding_selector_btn_next.xml b/feature/onboarding/src/main/res/drawable/onboarding_selector_btn_next.xml index 23fc3ada5..82b2e2b93 100644 --- a/feature/onboarding/src/main/res/drawable/onboarding_selector_btn_next.xml +++ b/feature/onboarding/src/main/res/drawable/onboarding_selector_btn_next.xml @@ -1,6 +1,5 @@ - - - + + \ No newline at end of file From 4fa41ed497224d7e3793ae3d7c19a804e641fbc6 Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Fri, 12 Jan 2024 23:10:33 +0900 Subject: [PATCH 25/28] =?UTF-8?q?[feat]:=20=EC=B1=8C=EB=A6=B0=EC=A7=80=20?= =?UTF-8?q?=EA=B8=B0=EA=B0=84=20=EB=B9=84=ED=99=9C=EC=84=B1=ED=99=94=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fragment/OnBoardingSelectDataFragment.kt | 15 +++++++++++++-- .../onboarding_select_data_disable.xml | 19 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 feature/onboarding/src/main/res/drawable/onboarding_select_data_disable.xml diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectDataFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectDataFragment.kt index 8c6f9ddc7..2a300a082 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectDataFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectDataFragment.kt @@ -9,9 +9,12 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope +import com.hmh.hamyeonham.common.fragment.colorOf +import com.hmh.hamyeonham.common.fragment.drawableOf import com.hmh.hamyeonham.common.primitive.extractDigits import com.hmh.hamyeonham.common.view.viewBinding import com.hmh.hamyeonham.feature.onboarding.OnBoardingFragmentType +import com.hmh.hamyeonham.feature.onboarding.R import com.hmh.hamyeonham.feature.onboarding.databinding.FragmentOnBoardingSelectDataBinding import com.hmh.hamyeonham.feature.onboarding.viewmodel.OnBoardingSelectDataViewModel import com.hmh.hamyeonham.feature.onboarding.viewmodel.OnBoardingViewModel @@ -89,8 +92,16 @@ class OnBoardingSelectDataFragment : Fragment() { fragmentType?.let { viewModel.initQuestionData(it) if (it == OnBoardingFragmentType.SELECT_DATA_PERIOD) { - binding.btnOnboardingSelectData3.isEnabled = false - binding.btnOnboardingSelectData4.isEnabled = false + binding.run { + btnOnboardingSelectData3.isEnabled = false + btnOnboardingSelectData4.isEnabled = false + btnOnboardingSelectData1.background = + drawableOf(R.drawable.onboarding_select_data_disable) + btnOnboardingSelectData2.background = + drawableOf(R.drawable.onboarding_select_data_disable) + btnOnboardingSelectData3.setTextColor(colorOf(com.hmh.hamyeonham.core.designsystem.R.color.gray5)) + btnOnboardingSelectData4.setTextColor(colorOf(com.hmh.hamyeonham.core.designsystem.R.color.gray5)) + } } } } diff --git a/feature/onboarding/src/main/res/drawable/onboarding_select_data_disable.xml b/feature/onboarding/src/main/res/drawable/onboarding_select_data_disable.xml new file mode 100644 index 000000000..92972da50 --- /dev/null +++ b/feature/onboarding/src/main/res/drawable/onboarding_select_data_disable.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file From 2c21f6e9ee2b9067affbb36a33cd0ed6805a5e0e Mon Sep 17 00:00:00 2001 From: kangyuri1114 Date: Fri, 12 Jan 2024 23:33:00 +0900 Subject: [PATCH 26/28] =?UTF-8?q?[fix]:=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../onboarding/fragment/OnBoardingSelectDataFragment.kt | 4 ++-- .../res/layout/fragment_on_boarding_request_permission.xml | 2 +- .../src/main/res/layout/fragment_on_boarding_select_app.xml | 4 +++- .../src/main/res/layout/fragment_on_boarding_select_data.xml | 4 +++- .../res/layout/fragment_on_boarding_select_screentime.xml | 5 +++-- .../main/res/layout/fragment_on_boarding_select_use_time.xml | 5 +++-- 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectDataFragment.kt b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectDataFragment.kt index 2a300a082..96e4ea604 100644 --- a/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectDataFragment.kt +++ b/feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/fragment/OnBoardingSelectDataFragment.kt @@ -95,9 +95,9 @@ class OnBoardingSelectDataFragment : Fragment() { binding.run { btnOnboardingSelectData3.isEnabled = false btnOnboardingSelectData4.isEnabled = false - btnOnboardingSelectData1.background = + btnOnboardingSelectData3.background = drawableOf(R.drawable.onboarding_select_data_disable) - btnOnboardingSelectData2.background = + btnOnboardingSelectData4.background = drawableOf(R.drawable.onboarding_select_data_disable) btnOnboardingSelectData3.setTextColor(colorOf(com.hmh.hamyeonham.core.designsystem.R.color.gray5)) btnOnboardingSelectData4.setTextColor(colorOf(com.hmh.hamyeonham.core.designsystem.R.color.gray5)) diff --git a/feature/onboarding/src/main/res/layout/fragment_on_boarding_request_permission.xml b/feature/onboarding/src/main/res/layout/fragment_on_boarding_request_permission.xml index 1294d72fe..b223fc711 100644 --- a/feature/onboarding/src/main/res/layout/fragment_on_boarding_request_permission.xml +++ b/feature/onboarding/src/main/res/layout/fragment_on_boarding_request_permission.xml @@ -9,8 +9,8 @@ diff --git a/feature/onboarding/src/main/res/layout/fragment_on_boarding_select_data.xml b/feature/onboarding/src/main/res/layout/fragment_on_boarding_select_data.xml index 8ba0329ed..0c71ccade 100644 --- a/feature/onboarding/src/main/res/layout/fragment_on_boarding_select_data.xml +++ b/feature/onboarding/src/main/res/layout/fragment_on_boarding_select_data.xml @@ -9,7 +9,9 @@ Date: Sat, 13 Jan 2024 00:33:11 +0900 Subject: [PATCH 27/28] [fix]: fix merge error --- .github/workflows/develop_PR_builder.yml | 2 + app/build.gradle.kts | 11 +- app/proguard-rules.pro | 10 +- app/src/main/AndroidManifest.xml | 1 + .../navigation/DefaultNavigationProvider.kt | 12 +- .../hmh/hamyeonham/plugin/CommonConfigs.kt | 3 + .../hamyeonham/common/context/ContextExt.kt | 61 +++- .../hmh/hamyeonham/common/intent/ExtraExt.kt | 14 +- .../common/navigation/NavigationProvider.kt | 5 +- .../com/hmh/hamyeonham/common/time/TimeExt.kt | 6 +- .../hmh/hamyeonham/common/view/Animator.kt | 23 ++ .../com/hmh/hamyeonham/common/view/Binding.kt | 8 +- .../com/hmh/hamyeonham/common/view/ViewExt.kt | 24 +- .../src/main/res/values/themes.xml | 21 ++ .../statistics => core/network}/.gitignore | 0 core/network/build.gradle.kts | 18 + .../network}/consumer-rules.pro | 0 .../network}/proguard-rules.pro | 0 core/network/src/main/AndroidManifest.xml | 4 + .../core/network/auth/api/RefreshService.kt | 12 + .../network/auth/api/model/TokenResponse.kt | 12 + .../auth/authenticator/HMHAuthenticator.kt | 57 +++ .../datastore/DefaultHMHNetworkPreference.kt | 53 +++ .../auth/datastore/HMHNetworkPreference.kt | 10 + .../auth/interceptor/AuthInterceptor.kt | 31 ++ .../core/network/di/DataStoreModule.kt | 76 ++++ .../hamyeonham/core/network/di/NetModule.kt | 121 +++++++ .../core/network/di/RefreshModule.kt | 19 + .../core/network/model/BaseResponse.kt | 7 + core/viewmodel/main/build.gradle.kts | 5 +- .../com/hmh/hamyeonham/core/MainViewModel.kt | 27 +- .../res/drawable/ic_launcher_foreground.xml | 30 ++ .../datasource/UsageGoalsDataSource.kt | 23 +- .../datasource/UsageStatsDataSourceImpl.kt | 4 +- .../usagestats/di/UsageGoalsModule.kt | 29 ++ .../usagestats/di/UsageStatsModule.kt | 4 - .../usagestats/model/UsageGoalModel.kt | 2 +- .../usagestats/model/UsageStatModel.kt | 2 +- .../repository/DefaultUsageStatsRepository.kt | 40 ++- data/userinfo/.gitignore | 1 + data/userinfo/build.gradle.kts | 12 + data/userinfo/consumer-rules.pro | 0 data/userinfo/proguard-rules.pro | 21 ++ data/userinfo/src/main/AndroidManifest.xml | 4 + .../userinfo/datasource/UserInfoDataSource.kt | 12 + .../hamyeonham/userinfo/di/UserInfoModule.kt | 22 ++ .../userinfo/model/UserInfoModel.kt | 6 + .../repository/DefaultUserInfoRepository.kt | 14 + .../challenge/model/ChallengeStatus.kt | 3 +- .../hamyeonham/usagestats/model/UsageStat.kt | 2 +- .../repository/UsageStatsRepository.kt | 17 +- .../usecase/GetUsageGoalsUseCase.kt | 2 +- .../usecase/GetUsageStatsListUseCase.kt | 28 +- domain/userinfo/.gitignore | 1 + domain/userinfo/build.gradle.kts | 15 + domain/userinfo/consumer-rules.pro | 0 domain/userinfo/proguard-rules.pro | 21 ++ domain/userinfo/src/main/AndroidManifest.xml | 4 + .../hmh/hamyeonham/userinfo/model/UserInfo.kt | 6 + .../userinfo/repository/UserInfoRepository.kt | 7 + .../userinfo/usecase/GetUserInfoUseCase.kt | 15 + feature/challenge/build.gradle.kts | 3 +- .../hamyeonham/challenge/ChallengeFragment.kt | 3 +- .../challenge/ChallengeUsageGoalsAdapter.kt | 13 +- feature/login/build.gradle.kts | 2 + feature/login/src/main/AndroidManifest.xml | 3 - .../hamyeonham/feature/login/LoginActivity.kt | 93 ++--- .../feature/login/LoginViewModel.kt | 88 ++++- .../feature/login/LoginViewPagerAdapter.kt | 7 +- .../feature/login/UserInfoActivity.kt | 53 --- .../feature/login/UserInfoViewModel.kt | 14 - .../feature/login/data/DummyImage.kt | 5 - .../feature/login/model/LoginViewImageList.kt | 5 + .../main/res/drawable/login_kakao_btn_52.xml | 18 + .../main/res/drawable/login_kakao_icon_24.xml | 10 + .../res/drawable/login_kakao_large_wide.png | Bin 7213 -> 0 bytes .../src/main/res/layout/activity_login.xml | 64 +++- .../main/res/layout/activity_user_info.xml | 29 -- feature/login/src/main/res/values/strings.xml | 1 + feature/main/build.gradle.kts | 8 +- .../feature/main/home/HomeFragment.kt | 32 +- .../feature/main/home}/UsageStaticsAdapter.kt | 20 +- .../main/home/UsageStaticsTotalViewHolder.kt | 36 ++ .../main/home/UsageStaticsViewHolder.kt | 38 ++ .../feature/main/mypage/MyPageFragment.kt | 24 -- .../res/drawable/img_statics_blackhole.png | Bin 0 -> 307328 bytes .../src/main/res/layout/fragment_home.xml | 17 +- .../src/main/res/layout/fragment_my_page.xml | 16 - .../src/main/res/layout/item_usagestatic.xml | 67 ++++ .../res/layout/item_usagestatic_total.xml | 70 ++++ .../src/main/res/navigation/nav_graph.xml | 2 +- feature/main/src/main/res/values/strings.xml | 7 + feature/main/src/main/res/values/themes.xml | 3 + feature/mypage/.gitignore | 1 + feature/mypage/build.gradle.kts | 22 ++ feature/mypage/consumer-rules.pro | 0 feature/mypage/proguard-rules.pro | 21 ++ feature/mypage/src/main/AndroidManifest.xml | 4 + .../hmh/hamyeonham/mypage/MyPageFragment.kt | 70 ++++ .../src/main/res/drawable/ic_arrow_24.xml | 13 + .../main/res/drawable/ic_badge_white_24.xml | 9 + .../src/main/res/drawable/ic_journey_24.xml | 7 + .../src/main/res/drawable/ic_point_24.xml | 7 + .../src/main/res/drawable/ic_profile_50.xml | 4 + .../src/main/res/drawable/ic_store_24.xml | 5 + .../shape_background_radius7_gray6.xml | 13 + .../src/main/res/layout/fragment_my_page.xml | 331 ++++++++++++++++++ feature/mypage/src/main/res/values/string.xml | 11 + .../feature/onboarding/OnBoardingActivity.kt | 14 +- .../OnBoardingSelectUseTimeFragment.kt | 2 - .../viewModel/OnBoardingViewModel.kt~develop | 69 ++++ .../main/res/drawable/all_back_btn_medium.xml | 4 +- .../main/res/layout/activity_on_boarding.xml | 41 ++- .../activity_on_boarding_done_sing_up.xml | 24 +- .../fragment_on_boarding_select_data.xml | 112 +++--- .../src/main/res/values/strings.xml | 4 +- .../onboarding/src/main/res/values/themes.xml | 1 + feature/statistics/build.gradle.kts | 22 -- .../statistics/src/main/AndroidManifest.xml | 10 - .../hamyeonham/statistics/StaticsActivity.kt | 48 --- .../hamyeonham/statistics/StaticsViewModel.kt | 34 -- .../statistics/UsageStaticsTotalViewHolder.kt | 20 -- .../statistics/UsageStaticsViewHolder.kt | 28 -- .../src/main/res/layout/activity_statics.xml | 40 --- .../src/main/res/layout/item_usagestatic.xml | 65 ---- .../res/layout/item_usagestatic_total.xml | 71 ---- .../src/main/res/values/strings.xml | 6 - .../statistics/src/main/res/values/themes.xml | 26 -- settings.gradle.kts | 7 +- 129 files changed, 2071 insertions(+), 846 deletions(-) create mode 100644 core/common/src/main/java/com/hmh/hamyeonham/common/view/Animator.kt rename {feature/statistics => core/network}/.gitignore (100%) create mode 100644 core/network/build.gradle.kts rename {feature/statistics => core/network}/consumer-rules.pro (100%) rename {feature/statistics => core/network}/proguard-rules.pro (100%) create mode 100644 core/network/src/main/AndroidManifest.xml create mode 100644 core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/api/RefreshService.kt create mode 100644 core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/api/model/TokenResponse.kt create mode 100644 core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/authenticator/HMHAuthenticator.kt create mode 100644 core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/datastore/DefaultHMHNetworkPreference.kt create mode 100644 core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/datastore/HMHNetworkPreference.kt create mode 100644 core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/interceptor/AuthInterceptor.kt create mode 100644 core/network/src/main/java/com/hmh/hamyeonham/core/network/di/DataStoreModule.kt create mode 100644 core/network/src/main/java/com/hmh/hamyeonham/core/network/di/NetModule.kt create mode 100644 core/network/src/main/java/com/hmh/hamyeonham/core/network/di/RefreshModule.kt create mode 100644 core/network/src/main/java/com/hmh/hamyeonham/core/network/model/BaseResponse.kt create mode 100644 core/viewmodel/main/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/di/UsageGoalsModule.kt create mode 100644 data/userinfo/.gitignore create mode 100644 data/userinfo/build.gradle.kts create mode 100644 data/userinfo/consumer-rules.pro create mode 100644 data/userinfo/proguard-rules.pro create mode 100644 data/userinfo/src/main/AndroidManifest.xml create mode 100644 data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/datasource/UserInfoDataSource.kt create mode 100644 data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/di/UserInfoModule.kt create mode 100644 data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/model/UserInfoModel.kt create mode 100644 data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/repository/DefaultUserInfoRepository.kt create mode 100644 domain/userinfo/.gitignore create mode 100644 domain/userinfo/build.gradle.kts create mode 100644 domain/userinfo/consumer-rules.pro create mode 100644 domain/userinfo/proguard-rules.pro create mode 100644 domain/userinfo/src/main/AndroidManifest.xml create mode 100644 domain/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/model/UserInfo.kt create mode 100644 domain/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/repository/UserInfoRepository.kt create mode 100644 domain/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/usecase/GetUserInfoUseCase.kt delete mode 100644 feature/login/src/main/java/com/hmh/hamyeonham/feature/login/UserInfoActivity.kt delete mode 100644 feature/login/src/main/java/com/hmh/hamyeonham/feature/login/UserInfoViewModel.kt delete mode 100644 feature/login/src/main/java/com/hmh/hamyeonham/feature/login/data/DummyImage.kt create mode 100644 feature/login/src/main/java/com/hmh/hamyeonham/feature/login/model/LoginViewImageList.kt create mode 100644 feature/login/src/main/res/drawable/login_kakao_btn_52.xml create mode 100644 feature/login/src/main/res/drawable/login_kakao_icon_24.xml delete mode 100644 feature/login/src/main/res/drawable/login_kakao_large_wide.png delete mode 100644 feature/login/src/main/res/layout/activity_user_info.xml rename feature/{statistics/src/main/java/com/hmh/hamyeonham/statistics => main/src/main/java/com/hmh/hamyeonham/feature/main/home}/UsageStaticsAdapter.kt (70%) create mode 100644 feature/main/src/main/java/com/hmh/hamyeonham/feature/main/home/UsageStaticsTotalViewHolder.kt create mode 100644 feature/main/src/main/java/com/hmh/hamyeonham/feature/main/home/UsageStaticsViewHolder.kt delete mode 100644 feature/main/src/main/java/com/hmh/hamyeonham/feature/main/mypage/MyPageFragment.kt create mode 100644 feature/main/src/main/res/drawable/img_statics_blackhole.png delete mode 100644 feature/main/src/main/res/layout/fragment_my_page.xml create mode 100644 feature/main/src/main/res/layout/item_usagestatic.xml create mode 100644 feature/main/src/main/res/layout/item_usagestatic_total.xml create mode 100644 feature/main/src/main/res/values/themes.xml create mode 100644 feature/mypage/.gitignore create mode 100644 feature/mypage/build.gradle.kts create mode 100644 feature/mypage/consumer-rules.pro create mode 100644 feature/mypage/proguard-rules.pro create mode 100644 feature/mypage/src/main/AndroidManifest.xml create mode 100644 feature/mypage/src/main/java/com/hmh/hamyeonham/mypage/MyPageFragment.kt create mode 100644 feature/mypage/src/main/res/drawable/ic_arrow_24.xml create mode 100644 feature/mypage/src/main/res/drawable/ic_badge_white_24.xml create mode 100644 feature/mypage/src/main/res/drawable/ic_journey_24.xml create mode 100644 feature/mypage/src/main/res/drawable/ic_point_24.xml create mode 100644 feature/mypage/src/main/res/drawable/ic_profile_50.xml create mode 100644 feature/mypage/src/main/res/drawable/ic_store_24.xml create mode 100644 feature/mypage/src/main/res/drawable/shape_background_radius7_gray6.xml create mode 100644 feature/mypage/src/main/res/layout/fragment_my_page.xml create mode 100644 feature/mypage/src/main/res/values/string.xml create mode 100644 feature/onboarding/src/main/java/com/hmh/hamyeonham/feature/onboarding/viewModel/OnBoardingViewModel.kt~develop delete mode 100644 feature/statistics/build.gradle.kts delete mode 100644 feature/statistics/src/main/AndroidManifest.xml delete mode 100644 feature/statistics/src/main/java/com/hmh/hamyeonham/statistics/StaticsActivity.kt delete mode 100644 feature/statistics/src/main/java/com/hmh/hamyeonham/statistics/StaticsViewModel.kt delete mode 100644 feature/statistics/src/main/java/com/hmh/hamyeonham/statistics/UsageStaticsTotalViewHolder.kt delete mode 100644 feature/statistics/src/main/java/com/hmh/hamyeonham/statistics/UsageStaticsViewHolder.kt delete mode 100644 feature/statistics/src/main/res/layout/activity_statics.xml delete mode 100644 feature/statistics/src/main/res/layout/item_usagestatic.xml delete mode 100644 feature/statistics/src/main/res/layout/item_usagestatic_total.xml delete mode 100644 feature/statistics/src/main/res/values/strings.xml delete mode 100644 feature/statistics/src/main/res/values/themes.xml diff --git a/.github/workflows/develop_PR_builder.yml b/.github/workflows/develop_PR_builder.yml index 2f72d32c8..7a0f33e71 100644 --- a/.github/workflows/develop_PR_builder.yml +++ b/.github/workflows/develop_PR_builder.yml @@ -39,8 +39,10 @@ jobs: - name: Add Local Properties env: KAKAO_API_KEY: ${{ secrets.KAKAO_API_KEY }} + HMH_BASE_URL: ${{ secrets.HMH_BASE_URL }} run: | echo kakaoApiKey=$KAKAO_API_KEY >> ./local.properties + echo hmhBaseUrl=$HMH_BASE_URL >> ./local.properties - name: Access Firebase Service run: echo '${{ secrets.GOOGLE_SERVICES_JSON }}' > ./app/google-services.json diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 85039e336..4b292d98c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -9,9 +9,10 @@ plugins { alias(libs.plugins.crashlytics) } -val properties = Properties().apply { - load(rootProject.file("local.properties").inputStream()) -} +val properties = + Properties().apply { + load(rootProject.file("local.properties").inputStream()) + } android { namespace = "com.hmh.hamyeonham" @@ -57,16 +58,18 @@ android { dependencies { // Feature - implementation(projects.feature.statistics) implementation(projects.feature.login) implementation(projects.feature.onboarding) implementation(projects.feature.main) + implementation(projects.feature.mypage) // Domain implementation(projects.domain.usagestats) + implementation(projects.domain.userinfo) // Data implementation(projects.data.usagestats) + implementation(projects.data.userinfo) // Core implementation(projects.core.common) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index e661d21d9..eae1af9cd 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -101,7 +101,7 @@ # Application rules # Change here com.yourcompany.yourpackage --keepclassmembers @kotlinx.serialization.Serializable class com.teampophory.** { +-keepclassmembers @kotlinx.serialization.Serializable class com.hmh.** { # lookup for plugin generated serializable classes *** Companion; # lookup for serializable objects @@ -109,18 +109,18 @@ kotlinx.serialization.KSerializer serializer(...); } # lookup for plugin generated serializable classes --if @kotlinx.serialization.Serializable class com.teampophory.** --keepclassmembers class com.teampophory.<1>$Companion { +-if @kotlinx.serialization.Serializable class com.hmh.** +-keepclassmembers class com.hmh.<1>$Companion { kotlinx.serialization.KSerializer serializer(...); } # Serialization supports named companions but for such classes it is necessary to add an additional rule. # This rule keeps serializer and serializable class from obfuscation. Therefore, it is recommended not to use wildcards in it, but to write rules for each such class. --keep class com.teampophory.SerializableClassWithNamedCompanion$$serializer { +-keep class com.hmh.SerializableClassWithNamedCompanion$$serializer { *** INSTANCE; } --keep class com.teampophory.** { +-keep class com.hmh.** { @kotlinx.serialization.SerialName ; } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0870ef569..1d9c9af49 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,7 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Base.Theme.HMH" + android:usesCleartextTraffic="true" tools:targetApi="31"> ().apply { defaultConfig { val kakaoApiKey = properties["kakaoApiKey"] as? String ?: "" + val hmhBaseUrl = properties["hmhBaseUrl"] as? String ?: "" manifestPlaceholders["kakaoApiKey"] = properties["kakaoApiKey"] as String + manifestPlaceholders["hmhBaseUrl"] = properties["hmhBaseUrl"] as String buildConfigField("String", "KAKAO_API_KEY", "\"${kakaoApiKey}\"") + buildConfigField("String", "HMH_BASE_URL", "\"${hmhBaseUrl}\"") } buildFeatures.apply { viewBinding = true diff --git a/core/common/src/main/java/com/hmh/hamyeonham/common/context/ContextExt.kt b/core/common/src/main/java/com/hmh/hamyeonham/common/context/ContextExt.kt index bef8d0cac..3c4bdc7a8 100644 --- a/core/common/src/main/java/com/hmh/hamyeonham/common/context/ContextExt.kt +++ b/core/common/src/main/java/com/hmh/hamyeonham/common/context/ContextExt.kt @@ -6,9 +6,13 @@ import android.content.pm.PackageManager import android.graphics.Point import android.graphics.drawable.Drawable import android.os.Build +import android.text.SpannableStringBuilder +import android.text.Spanned +import android.text.style.ForegroundColorSpan import android.view.View import android.view.WindowInsets import android.view.WindowManager +import android.widget.TextView import android.widget.Toast import androidx.annotation.ColorRes import androidx.annotation.DrawableRes @@ -27,26 +31,26 @@ fun Context.longToast(message: String) { fun Context.snackBar( anchorView: View, - message: () -> String, + message: () -> String ) { Snackbar.make(anchorView, message(), Snackbar.LENGTH_SHORT).show() } fun Context.stringOf( - @StringRes resId: Int, + @StringRes resId: Int ) = getString(resId) fun Context.colorOf( - @ColorRes resId: Int, + @ColorRes resId: Int ) = ContextCompat.getColor(this, resId) fun Context.drawableOf( - @DrawableRes resId: Int, + @DrawableRes resId: Int ) = ContextCompat.getDrawable(this, resId) fun Context.dialogWidthPercent( dialog: Dialog?, - percent: Double = 0.8, + percent: Double = 0.8 ) { val deviceSize = getDeviceSize() dialog?.window?.run { @@ -63,10 +67,9 @@ fun Context.getDeviceSize(): IntArray { val windowMetrics = windowManager.currentWindowMetrics val windowInsets = windowMetrics.windowInsets - val insets = - windowInsets.getInsetsIgnoringVisibility( - WindowInsets.Type.navigationBars() or WindowInsets.Type.displayCutout(), - ) + val insets = windowInsets.getInsetsIgnoringVisibility( + WindowInsets.Type.navigationBars() or WindowInsets.Type.displayCutout() + ) val insetsWidth = insets.right + insets.left val insetsHeight = insets.top + insets.bottom @@ -83,20 +86,42 @@ fun Context.getDeviceSize(): IntArray { } } -fun Context.getAppNameFromPackageName(packageName: String): String = - try { - val appInfo = packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA) - packageManager.getApplicationLabel(appInfo).toString() - } catch (e: PackageManager.NameNotFoundException) { - "Unknown" - } +fun Context.getAppNameFromPackageName(packageName: String): String = try { + val appInfo = packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA) + packageManager.getApplicationLabel(appInfo).toString() +} catch (e: PackageManager.NameNotFoundException) { + "Unknown" +} fun Context.getAppIconFromPackageName(packageName: String): Drawable? { try { val appInfo = packageManager.getApplicationInfo(packageName, 0) return appInfo.loadIcon(packageManager) } catch (e: PackageManager.NameNotFoundException) { - e.printStackTrace() + return ContextCompat.getDrawable(this, R.drawable.ic_launcher_foreground) } - return ContextCompat.getDrawable(this, R.drawable.ic_launcher_foreground) +} + +fun Context.colorSecondStrAndBindText( + firstStr: String, + secondStr: String, + tv: TextView, + color: Int +) { + val mergedStr = firstStr + " " + secondStr + val builder = SpannableStringBuilder( + mergedStr + ) + builder.setSpan( + ForegroundColorSpan( + ContextCompat.getColor( + this, + color + ) + ), + mergedStr.length - secondStr.length, + mergedStr.length, + Spanned.SPAN_INCLUSIVE_EXCLUSIVE + ) + tv.text = builder } diff --git a/core/common/src/main/java/com/hmh/hamyeonham/common/intent/ExtraExt.kt b/core/common/src/main/java/com/hmh/hamyeonham/common/intent/ExtraExt.kt index 702fd2728..568133c6f 100644 --- a/core/common/src/main/java/com/hmh/hamyeonham/common/intent/ExtraExt.kt +++ b/core/common/src/main/java/com/hmh/hamyeonham/common/intent/ExtraExt.kt @@ -11,14 +11,14 @@ import kotlin.properties.ReadOnlyProperty fun intExtra(defaultValue: Int = -1) = ReadOnlyProperty { thisRef, property -> thisRef.intent.extras?.getInt( property.name, - defaultValue + defaultValue, ) ?: defaultValue } fun longExtra(defaultValue: Long = -1) = ReadOnlyProperty { thisRef, property -> thisRef.intent.extras?.getLong( property.name, - defaultValue + defaultValue, ) ?: defaultValue } @@ -26,14 +26,17 @@ fun boolExtra(defaultValue: Boolean = false) = ReadOnlyProperty { thisRef, property -> thisRef.intent.extras?.getBoolean( property.name, - defaultValue + defaultValue, ) ?: defaultValue } fun stringExtra(defaultValue: String? = null) = ReadOnlyProperty { thisRef, property -> - if (defaultValue == null) thisRef.intent.extras?.getString(property.name) - else thisRef.intent.extras?.getString(property.name, defaultValue) + if (defaultValue == null) { + thisRef.intent.extras?.getString(property.name) + } else { + thisRef.intent.extras?.getString(property.name, defaultValue) + } } fun

parcelableExtra(defaultValue: P? = null) = @@ -41,7 +44,6 @@ fun

parcelableExtra(defaultValue: P? = null) = thisRef.intent.extras?.getParcelable(property.name) ?: defaultValue } - inline fun serializableExtra(defaultValue: S? = null) = ReadOnlyProperty { thisRef, property -> thisRef.intent.serializableExtra(property.name) ?: defaultValue diff --git a/core/common/src/main/java/com/hmh/hamyeonham/common/navigation/NavigationProvider.kt b/core/common/src/main/java/com/hmh/hamyeonham/common/navigation/NavigationProvider.kt index 0d23df478..eadae17fe 100644 --- a/core/common/src/main/java/com/hmh/hamyeonham/common/navigation/NavigationProvider.kt +++ b/core/common/src/main/java/com/hmh/hamyeonham/common/navigation/NavigationProvider.kt @@ -3,10 +3,7 @@ package com.hmh.hamyeonham.common.navigation import android.content.Intent interface NavigationProvider { - fun toOnboarding(): Intent + fun toOnBoarding(): Intent fun toLogin(): Intent - fun toUserInfo(): Intent - fun toStatics(): Intent - fun toMain(): Intent } diff --git a/core/common/src/main/java/com/hmh/hamyeonham/common/time/TimeExt.kt b/core/common/src/main/java/com/hmh/hamyeonham/common/time/TimeExt.kt index 2b0ccde60..80ee84102 100644 --- a/core/common/src/main/java/com/hmh/hamyeonham/common/time/TimeExt.kt +++ b/core/common/src/main/java/com/hmh/hamyeonham/common/time/TimeExt.kt @@ -31,7 +31,8 @@ fun Long.formatNumericDate(context: Context): String = DateUtils.FORMAT_SHOW_YEAR or DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NUMERIC_DATE, ) -fun Instant.formatNumericDate(context: Context): String = toEpochMilliseconds().formatNumericDate(context) +fun Instant.formatNumericDate(context: Context): String = + toEpochMilliseconds().formatNumericDate(context) // LocalDate의 확장 함수로 해당 날짜의 시작 시간과 종료 시간을 LocalDateTime으로 반환 fun LocalDate.toStartOfDay(): LocalDateTime = LocalDateTime(year, monthNumber, dayOfMonth, 0, 0) @@ -39,7 +40,8 @@ fun LocalDate.toStartOfDay(): LocalDateTime = LocalDateTime(year, monthNumber, d fun LocalDate.toEndOfDay(): LocalDateTime = LocalDateTime(year, monthNumber, dayOfMonth, 23, 59, 59) // LocalDateTime의 확장 함수로 해당 시간을 Epoch 밀리초로 변환 -fun LocalDateTime.toEpochMilliseconds(timeZone: TimeZone): Long = toInstant(timeZone).toEpochMilliseconds() +fun LocalDateTime.toEpochMilliseconds(timeZone: TimeZone): Long = + toInstant(timeZone).toEpochMilliseconds() // 현재 날짜의 시작 시간과 종료 시간을 Epoch 밀리초로 반환하는 함수 fun getCurrentDayStartEndEpochMillis(): Pair { diff --git a/core/common/src/main/java/com/hmh/hamyeonham/common/view/Animator.kt b/core/common/src/main/java/com/hmh/hamyeonham/common/view/Animator.kt new file mode 100644 index 000000000..351a94051 --- /dev/null +++ b/core/common/src/main/java/com/hmh/hamyeonham/common/view/Animator.kt @@ -0,0 +1,23 @@ +package com.hmh.hamyeonham.common.view + +import android.animation.ObjectAnimator +import android.view.View +import android.view.animation.AccelerateInterpolator +import android.view.animation.BaseInterpolator +import android.widget.ProgressBar + +fun initAndStartProgressBarAnimation(pb: ProgressBar, progressTo: Int) { + initAndStartAnimation(pb, "progress", 0, progressTo, AccelerateInterpolator()) +} + +inline fun initAndStartAnimation( + view: View, + propertyName: String, + from: Int, + to: Int, + newInterpolator: T, +) { + ObjectAnimator.ofInt(view, propertyName, from, to).apply { + interpolator = newInterpolator + }.start() +} diff --git a/core/common/src/main/java/com/hmh/hamyeonham/common/view/Binding.kt b/core/common/src/main/java/com/hmh/hamyeonham/common/view/Binding.kt index cb7d85cf2..b28f9968e 100644 --- a/core/common/src/main/java/com/hmh/hamyeonham/common/view/Binding.kt +++ b/core/common/src/main/java/com/hmh/hamyeonham/common/view/Binding.kt @@ -12,14 +12,14 @@ import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty inline fun AppCompatActivity.viewBinding( - crossinline inflater: (LayoutInflater) -> T + crossinline inflater: (LayoutInflater) -> T, ) = lazy(LazyThreadSafetyMode.NONE) { inflater.invoke(layoutInflater) } class FragmentViewBindingDelegate( val fragment: F, - val viewBindingFactory: (View) -> T + val viewBindingFactory: (View) -> T, ) : ReadOnlyProperty { private var binding: T? = null @@ -33,11 +33,11 @@ class FragmentViewBindingDelegate( override fun onDestroy(owner: LifecycleOwner) { binding = null } - } + }, ) } } - } + }, ) } diff --git a/core/common/src/main/java/com/hmh/hamyeonham/common/view/ViewExt.kt b/core/common/src/main/java/com/hmh/hamyeonham/common/view/ViewExt.kt index 6a862f759..6f0a1dd86 100644 --- a/core/common/src/main/java/com/hmh/hamyeonham/common/view/ViewExt.kt +++ b/core/common/src/main/java/com/hmh/hamyeonham/common/view/ViewExt.kt @@ -7,7 +7,7 @@ import androidx.recyclerview.widget.RecyclerView inline fun View.setOnSingleClickListener( delay: Long = 500L, - crossinline block: (View) -> Unit + crossinline block: (View) -> Unit, ) { var previousClickedTime = 0L setOnClickListener { view -> @@ -21,28 +21,30 @@ inline fun View.setOnSingleClickListener( class ItemDiffCallback( val onItemsTheSame: (T, T) -> Boolean, - val onContentsTheSame: (T, T) -> Boolean + val onContentsTheSame: (T, T) -> Boolean, ) : DiffUtil.ItemCallback() { override fun areItemsTheSame( - oldItem: T, newItem: T + oldItem: T, + newItem: T, ): Boolean = onItemsTheSame(oldItem, newItem) override fun areContentsTheSame( - oldItem: T, newItem: T + oldItem: T, + newItem: T, ): Boolean = onContentsTheSame(oldItem, newItem) } class GridSpacingItemDecoration( private val spanCount: Int, private val spacing: Int, - private val includeEdge: Boolean + private val includeEdge: Boolean, ) : RecyclerView.ItemDecoration() { override fun getItemOffsets( outRect: Rect, view: View, parent: RecyclerView, - state: RecyclerView.State + state: RecyclerView.State, ) { val position = parent.getChildAdapterPosition(view) // item position val column = position % spanCount // item column @@ -68,8 +70,14 @@ class GridSpacingItemDecoration( } } -class VerticalSpaceItemDecoration(private val verticalSpaceHeight: Int) : RecyclerView.ItemDecoration() { - override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { +class VerticalSpaceItemDecoration(private val verticalSpaceHeight: Int) : + RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State, + ) { val position = parent.getChildAdapterPosition(view) // 첫 번째와 마지막 아이템을 제외한 경우에만 마진 적용 diff --git a/core/designsystem/src/main/res/values/themes.xml b/core/designsystem/src/main/res/values/themes.xml index 0fc281d0b..6aedc83f3 100644 --- a/core/designsystem/src/main/res/values/themes.xml +++ b/core/designsystem/src/main/res/values/themes.xml @@ -86,4 +86,25 @@ @color/blue_purple_button + + + + + + diff --git a/feature/statistics/.gitignore b/core/network/.gitignore similarity index 100% rename from feature/statistics/.gitignore rename to core/network/.gitignore diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts new file mode 100644 index 000000000..3f6f167e0 --- /dev/null +++ b/core/network/build.gradle.kts @@ -0,0 +1,18 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + hmh("feature") + hmh("serialization") +} + +android { + namespace = "com.hmh.hamyeonham.core.network" +} + +dependencies { + implementation(projects.core.common) + implementation(libs.security) + implementation(libs.bundles.retrofit) + implementation(platform(libs.okhttp.bom)) + implementation(libs.okhttp.logging.interceptor) + implementation(libs.process.phoenix) +} diff --git a/feature/statistics/consumer-rules.pro b/core/network/consumer-rules.pro similarity index 100% rename from feature/statistics/consumer-rules.pro rename to core/network/consumer-rules.pro diff --git a/feature/statistics/proguard-rules.pro b/core/network/proguard-rules.pro similarity index 100% rename from feature/statistics/proguard-rules.pro rename to core/network/proguard-rules.pro diff --git a/core/network/src/main/AndroidManifest.xml b/core/network/src/main/AndroidManifest.xml new file mode 100644 index 000000000..8bdb7e14b --- /dev/null +++ b/core/network/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/api/RefreshService.kt b/core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/api/RefreshService.kt new file mode 100644 index 000000000..a28707093 --- /dev/null +++ b/core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/api/RefreshService.kt @@ -0,0 +1,12 @@ +package com.hmh.hamyeonham.core.network.auth.api + +import com.hmh.hamyeonham.core.network.auth.api.model.TokenResponse +import com.hmh.hamyeonham.core.network.model.BaseResponse +import retrofit2.http.GET +import retrofit2.http.Header +import retrofit2.http.POST + +interface RefreshService { + @GET("api/v1/user/reissue") + suspend fun refreshToken(@Header("Authorization") token: String): BaseResponse +} diff --git a/core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/api/model/TokenResponse.kt b/core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/api/model/TokenResponse.kt new file mode 100644 index 000000000..f1b5abccd --- /dev/null +++ b/core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/api/model/TokenResponse.kt @@ -0,0 +1,12 @@ +package com.hmh.hamyeonham.core.network.auth.api.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class TokenResponse( + @SerialName("accessToken") + val accessToken: String? = null, + @SerialName("refreshToken") + val refreshToken: String? = null, +) diff --git a/core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/authenticator/HMHAuthenticator.kt b/core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/authenticator/HMHAuthenticator.kt new file mode 100644 index 000000000..661fc768f --- /dev/null +++ b/core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/authenticator/HMHAuthenticator.kt @@ -0,0 +1,57 @@ +package com.hmh.hamyeonham.core.network.auth.authenticator + +import android.content.Context +import android.util.Log +import com.hmh.hamyeonham.common.navigation.NavigationProvider +import com.hmh.hamyeonham.core.network.auth.api.RefreshService +import com.hmh.hamyeonham.core.network.auth.datastore.HMHNetworkPreference +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.runBlocking +import okhttp3.Authenticator +import okhttp3.Request +import okhttp3.Response +import okhttp3.Route +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class HMHAuthenticator @Inject constructor( + private val dataStore: HMHNetworkPreference, + private val api: RefreshService, + @ApplicationContext private val context: Context, + private val navigationProvider: NavigationProvider +) : Authenticator { + override fun authenticate(route: Route?, response: Response): Request? { + if (response.request.header("Authorization") == null) { + return null + } + + if (response.code == 401) { + val refreshToken = dataStore.refreshToken + val newTokens = runCatching { + runBlocking { + api.refreshToken("Bearer $refreshToken") + } + }.onSuccess { + val data = it.data + dataStore.refreshToken = data.refreshToken.orEmpty() + dataStore.accessToken = data.accessToken.orEmpty() + }.onFailure { + Log.e("Authenticator", it.toString()) + runBlocking { + //TODO 어떻게 처리할지 고민해보기 +// UserApiClient.instance.logout { error -> +// Log.e("Authenticator", error.toString()) +// dataStore.clear() +// ProcessPhoenix.triggerRebirth(context, navigationProvider.toOnboarding()) +// } + } + }.getOrThrow() + + return response.request.newBuilder() + .header("Authorization", "Bearer ${newTokens.data.accessToken}") + .build() + } + return null + } +} diff --git a/core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/datastore/DefaultHMHNetworkPreference.kt b/core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/datastore/DefaultHMHNetworkPreference.kt new file mode 100644 index 000000000..27ac623f7 --- /dev/null +++ b/core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/datastore/DefaultHMHNetworkPreference.kt @@ -0,0 +1,53 @@ +package com.hmh.hamyeonham.core.network.auth.datastore + +import android.content.SharedPreferences +import androidx.core.content.edit +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class DefaultHMHNetworkPreference @Inject constructor( + private val preferences: SharedPreferences +) : HMHNetworkPreference { + override var accessToken: String + get() = preferences.getString("access_token", "").orEmpty() + set(value) { + preferences.edit(commit = true) { + putString("access_token", value) + } + } + override var refreshToken: String + get() = preferences.getString("refresh_token", "").orEmpty() + set(value) { + preferences.edit(commit = true) { + putString("refresh_token", value) + } + } + override var userName: String + get() = preferences.getString("user_name", "").orEmpty() + set(value) { + preferences.edit(commit = true) { + putString("user_name", value) + } + } + override var userId: String + get() = preferences.getString("user_id", "").orEmpty() + set(value) { + preferences.edit(commit = true) { + putString("user_id", value) + } + } + override var autoLoginConfigured: Boolean + get() = preferences.getBoolean("auto_login", false) + set(value) { + preferences.edit(commit = true) { + putBoolean("auto_login", value) + } + } + + override fun clear() { + preferences.edit(commit = true) { + clear() + } + } +} diff --git a/core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/datastore/HMHNetworkPreference.kt b/core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/datastore/HMHNetworkPreference.kt new file mode 100644 index 000000000..505437a9b --- /dev/null +++ b/core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/datastore/HMHNetworkPreference.kt @@ -0,0 +1,10 @@ +package com.hmh.hamyeonham.core.network.auth.datastore + +interface HMHNetworkPreference { + var accessToken: String + var refreshToken: String + var userName: String + var userId: String + var autoLoginConfigured: Boolean + fun clear() +} diff --git a/core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/interceptor/AuthInterceptor.kt b/core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/interceptor/AuthInterceptor.kt new file mode 100644 index 000000000..6ce1461ed --- /dev/null +++ b/core/network/src/main/java/com/hmh/hamyeonham/core/network/auth/interceptor/AuthInterceptor.kt @@ -0,0 +1,31 @@ +package com.hmh.hamyeonham.core.network.auth.interceptor + +import com.hmh.hamyeonham.core.network.auth.datastore.HMHNetworkPreference +import okhttp3.Headers +import okhttp3.Interceptor +import okhttp3.Response +import javax.inject.Inject + +class AuthInterceptor @Inject constructor( + private val dataStore: HMHNetworkPreference +) : Interceptor { + private val encodedToken: String + get() = "Bearer ${dataStore.accessToken}" + + override fun intercept(chain: Interceptor.Chain): Response { + val originalRequest = chain.request() + if (!shouldRequestAuthenticatedHeaders(originalRequest.url.encodedPath)) { + return chain.proceed(originalRequest) + } + val headerRequest = originalRequest.newBuilder() + .addHeader("Authorization", encodedToken) + .addHeader("OS", "Android") + .build() + return chain.proceed(headerRequest) + } + + private fun shouldRequestAuthenticatedHeaders(encodedPath: String) = when (encodedPath) { + "/api/v1/user/reissue" -> false + else -> true + } +} diff --git a/core/network/src/main/java/com/hmh/hamyeonham/core/network/di/DataStoreModule.kt b/core/network/src/main/java/com/hmh/hamyeonham/core/network/di/DataStoreModule.kt new file mode 100644 index 000000000..8c93df175 --- /dev/null +++ b/core/network/src/main/java/com/hmh/hamyeonham/core/network/di/DataStoreModule.kt @@ -0,0 +1,76 @@ +package com.hmh.hamyeonham.core.network.di + +import android.content.Context +import android.content.SharedPreferences +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKey +import com.hmh.hamyeonham.core.network.BuildConfig +import com.hmh.hamyeonham.core.network.auth.datastore.DefaultHMHNetworkPreference +import com.hmh.hamyeonham.core.network.auth.datastore.HMHNetworkPreference +import dagger.Binds +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import java.security.GeneralSecurityException +import java.security.KeyStore +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object DataStoreModule { + private const val DEBUG_APP_PREFERNCES_NAME = "hmh_debug" + private const val APP_PREFERENCES_NAME = "hmh" + + @Provides + @Singleton + fun provideAppPreferences( + @ApplicationContext context: Context + ): SharedPreferences = if (BuildConfig.DEBUG) { + context.getSharedPreferences(DEBUG_APP_PREFERNCES_NAME, Context.MODE_PRIVATE) + } else { + try { + createEncryptedSharedPreferences(APP_PREFERENCES_NAME, context) + } catch (e: GeneralSecurityException) { + deleteMasterKeyEntry() + deleteExistingPref(APP_PREFERENCES_NAME, context) + createEncryptedSharedPreferences(APP_PREFERENCES_NAME, context) + } + } + + private fun deleteExistingPref(fileName: String, context: Context) { + context.deleteSharedPreferences(fileName) + } + + private fun deleteMasterKeyEntry() { + KeyStore.getInstance("AndroidKeyStore").apply { + load(null) + deleteEntry(MasterKey.DEFAULT_MASTER_KEY_ALIAS) + } + } + + private fun createEncryptedSharedPreferences( + fileName: String, + context: Context + ): SharedPreferences { + return EncryptedSharedPreferences.create( + context, + fileName, + MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS) + .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) + .build(), + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + + ) + } + + @Module + @InstallIn(SingletonComponent::class) + interface Binder { + @Singleton + @Binds + fun bindAppPreferences(dataStore: DefaultHMHNetworkPreference): HMHNetworkPreference + } +} diff --git a/core/network/src/main/java/com/hmh/hamyeonham/core/network/di/NetModule.kt b/core/network/src/main/java/com/hmh/hamyeonham/core/network/di/NetModule.kt new file mode 100644 index 000000000..b4f95482f --- /dev/null +++ b/core/network/src/main/java/com/hmh/hamyeonham/core/network/di/NetModule.kt @@ -0,0 +1,121 @@ +package com.hmh.hamyeonham.core.network.di + +import com.hmh.hamyeonham.common.BuildConfig +import com.hmh.hamyeonham.common.qualifier.Auth +import com.hmh.hamyeonham.common.qualifier.Log +import com.hmh.hamyeonham.common.qualifier.Secured +import com.hmh.hamyeonham.common.qualifier.Unsecured +import com.hmh.hamyeonham.core.network.auth.authenticator.HMHAuthenticator +import com.hmh.hamyeonham.core.network.auth.interceptor.AuthInterceptor +import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory +import dagger.Binds +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.serialization.json.Json +import okhttp3.Authenticator +import okhttp3.Interceptor +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Converter +import retrofit2.Retrofit +import java.util.concurrent.TimeUnit +import javax.inject.Singleton + +private const val HMHBaseUrl = BuildConfig.HMH_BASE_URL + +@Module +@InstallIn(SingletonComponent::class) +object NetModule { + @Provides + @Singleton + fun provideJson(): Json = Json { + ignoreUnknownKeys = true + coerceInputValues = true + } + + @Singleton + @Provides + fun provideJsonConverterFactory(json: Json): Converter.Factory { + return json.asConverterFactory("application/json".toMediaType()) + } + + @Singleton + @Provides + @Log + fun provideLoggingInterceptor(): Interceptor = + HttpLoggingInterceptor().setLevel( + if (BuildConfig.DEBUG) { + HttpLoggingInterceptor.Level.BODY + } else { + HttpLoggingInterceptor.Level.NONE + } + ) + + @Singleton + @Provides + @Auth + fun provideAuthInterceptor(interceptor: AuthInterceptor): Interceptor = interceptor + + @Singleton + @Provides + @Secured + fun provideOkHttpClient( + @Log logInterceptor: Interceptor, + @Auth authInterceptor: Interceptor, + authenticator: Authenticator + ): OkHttpClient = OkHttpClient.Builder() + .addInterceptor(logInterceptor) + .addInterceptor(authInterceptor) + .authenticator(authenticator) + .connectTimeout(60, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + .build() + + @Singleton + @Provides + @Unsecured + fun provideOkHttpClientNotNeededAuth( + @Log logInterceptor: Interceptor + ): OkHttpClient = OkHttpClient.Builder() + .addInterceptor(logInterceptor) + .connectTimeout(60, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + .build() + + @Singleton + @Provides + @Secured + fun provideRetrofit( + @Secured client: OkHttpClient, + converterFactory: Converter.Factory + ): Retrofit = Retrofit.Builder() + .baseUrl(HMHBaseUrl) + .client(client) + .addConverterFactory(converterFactory) + .build() + + @Singleton + @Provides + @Unsecured + fun provideRetrofitNotNeededAuth( + @Unsecured client: OkHttpClient, + converterFactory: Converter.Factory + ): Retrofit = Retrofit.Builder() + .baseUrl(HMHBaseUrl) + .client(client) + .addConverterFactory(converterFactory) + .build() + + @Module + @InstallIn(SingletonComponent::class) + interface Binder { + @Binds + @Singleton + fun provideAuthenticator(authenticator: HMHAuthenticator): Authenticator + } +} diff --git a/core/network/src/main/java/com/hmh/hamyeonham/core/network/di/RefreshModule.kt b/core/network/src/main/java/com/hmh/hamyeonham/core/network/di/RefreshModule.kt new file mode 100644 index 000000000..f29a31d63 --- /dev/null +++ b/core/network/src/main/java/com/hmh/hamyeonham/core/network/di/RefreshModule.kt @@ -0,0 +1,19 @@ +package com.hmh.hamyeonham.core.network.di + +import com.hmh.hamyeonham.common.qualifier.Unsecured +import com.hmh.hamyeonham.core.network.auth.api.RefreshService +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import retrofit2.Retrofit +import retrofit2.create +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object RefreshModule { + @Provides + @Singleton + fun provideRefreshApi(@Unsecured retrofit: Retrofit): RefreshService = retrofit.create() +} diff --git a/core/network/src/main/java/com/hmh/hamyeonham/core/network/model/BaseResponse.kt b/core/network/src/main/java/com/hmh/hamyeonham/core/network/model/BaseResponse.kt new file mode 100644 index 000000000..ee5c1a941 --- /dev/null +++ b/core/network/src/main/java/com/hmh/hamyeonham/core/network/model/BaseResponse.kt @@ -0,0 +1,7 @@ +package com.hmh.hamyeonham.core.network.model + +data class BaseResponse( + val status: Int, + val message: String, + val data: T +) diff --git a/core/viewmodel/main/build.gradle.kts b/core/viewmodel/main/build.gradle.kts index 709ce405b..5c2161e8a 100644 --- a/core/viewmodel/main/build.gradle.kts +++ b/core/viewmodel/main/build.gradle.kts @@ -3,7 +3,6 @@ plugins { hmh("feature") } - android { namespace = "com.hmh.hamyeonham.core.main" } @@ -11,12 +10,12 @@ android { dependencies { implementation(projects.domain.usagestats) implementation(projects.domain.challenge) + implementation(projects.domain.userinfo) implementation(projects.core.common) // TEST testImplementation("androidx.arch.core:core-testing:2.2.0") testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4") - testImplementation ("io.mockk:mockk:1.12.1") - + testImplementation("io.mockk:mockk:1.12.1") } diff --git a/core/viewmodel/main/src/main/java/com/hmh/hamyeonham/core/MainViewModel.kt b/core/viewmodel/main/src/main/java/com/hmh/hamyeonham/core/MainViewModel.kt index 219455a0a..cfa81eca2 100644 --- a/core/viewmodel/main/src/main/java/com/hmh/hamyeonham/core/MainViewModel.kt +++ b/core/viewmodel/main/src/main/java/com/hmh/hamyeonham/core/MainViewModel.kt @@ -2,27 +2,38 @@ package com.hmh.hamyeonham.core import androidx.lifecycle.ViewModel import com.hmh.hamyeonham.challenge.model.ChallengeStatus +import com.hmh.hamyeonham.common.time.getCurrentDayStartEndEpochMillis import com.hmh.hamyeonham.usagestats.model.UsageGoal +import com.hmh.hamyeonham.usagestats.model.UsageStatAndGoal import com.hmh.hamyeonham.usagestats.usecase.GetUsageGoalsUseCase +import com.hmh.hamyeonham.usagestats.usecase.GetUsageStatsListUseCase +import com.hmh.hamyeonham.userinfo.model.UserInfo +import com.hmh.hamyeonham.userinfo.usecase.GetUserInfoUseCase import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow -import javax.inject.Inject data class MainState( val challengeStatus: ChallengeStatus = ChallengeStatus(), val usageGoals: List = emptyList(), + val usgeStatsList: List = emptyList(), + val userInfo: UserInfo = UserInfo(), ) @HiltViewModel class MainViewModel @Inject constructor( private val getUsageGoalsUseCase: GetUsageGoalsUseCase, + private val usageStatsListUsecase: GetUsageStatsListUseCase, + private val getUserInfoUseCase: GetUserInfoUseCase, ) : ViewModel() { private val _mainState = MutableStateFlow(MainState()) val mainState = _mainState.asStateFlow() init { setGoalTimeList(getUsageGoalsUseCase()) + setUsageStatsList() + setUserInfo(getUserInfoUseCase()) } fun setChallengeStatus(challengeStatus: ChallengeStatus) { @@ -37,9 +48,23 @@ class MainViewModel @Inject constructor( } } + fun setUserInfo(userInfo: UserInfo) { + updateState { + copy(userInfo = userInfo) + } + } + fun updateState(transform: MainState.() -> MainState) { val currentState = mainState.value val newState = currentState.transform() _mainState.value = newState } + + private fun setUsageStatsList() { + val (startTime, endTime) = getCurrentDayStartEndEpochMillis() + val usageStatsList = usageStatsListUsecase(startTime, endTime) + updateState { + copy(usgeStatsList = usageStatsListUsecase(startTime, endTime)) + } + } } diff --git a/core/viewmodel/main/src/main/res/drawable/ic_launcher_foreground.xml b/core/viewmodel/main/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 000000000..7706ab9e6 --- /dev/null +++ b/core/viewmodel/main/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + diff --git a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageGoalsDataSource.kt b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageGoalsDataSource.kt index 546e835b5..fcb3e0e21 100644 --- a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageGoalsDataSource.kt +++ b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageGoalsDataSource.kt @@ -4,13 +4,22 @@ import com.hmh.hamyeonham.usagestats.model.UsageGoalModel import javax.inject.Inject class UsageGoalsDataSource @Inject constructor() { + var usageGoalList = listOf( + UsageGoalModel("total", 20000000), + UsageGoalModel("com.kakao.talk", 3000000), + UsageGoalModel("com.hmh.hamyeonham", 3000000), + UsageGoalModel("com.netflix.mediaclient", 3000000), + UsageGoalModel("com.google.android.gms", 500000), + UsageGoalModel("com.google.android.youtube", 3000000), + UsageGoalModel("com.android.chrome", 1000000), + UsageGoalModel("com.apple.android.music", 1000000), + UsageGoalModel("com.duolingo", 1000000), + UsageGoalModel("com.android.providers.calendar", 1000000), + UsageGoalModel("com.lge.sizechangable.weather.platform", 1000000), + UsageGoalModel("com.discord", 1000000), + ) + fun getUsageGoals(): List { - return listOf( - UsageGoalModel("total", 9459489), - UsageGoalModel("com.kakao.talk", 10737116), - UsageGoalModel("com.google.android.gms", 10607821), - UsageGoalModel("com.google.android.youtube", 7409658), - UsageGoalModel("com.android.chrome", 9346527), - ) + return usageGoalList } } diff --git a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSourceImpl.kt b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSourceImpl.kt index aa4da96dd..b517998fa 100644 --- a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSourceImpl.kt +++ b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSourceImpl.kt @@ -5,14 +5,14 @@ import com.hmh.hamyeonham.usagestats.model.UsageStatModel import javax.inject.Inject class UsageStatsDataSourceImpl @Inject constructor( - private val usageStatsManager: UsageStatsManager?, + private val usageStatsManager: UsageStatsManager? ) : UsageStatsDataSource { override fun getUsageStats(startTime: Long, endTime: Long): List { return usageStatsManager?.queryUsageStats( UsageStatsManager.INTERVAL_DAILY, startTime, - endTime, + endTime )?.map { UsageStatModel(it.packageName, it.totalTimeInForeground) } ?: emptyList() } } diff --git a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/di/UsageGoalsModule.kt b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/di/UsageGoalsModule.kt new file mode 100644 index 000000000..a81ee8750 --- /dev/null +++ b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/di/UsageGoalsModule.kt @@ -0,0 +1,29 @@ +package com.hmh.hamyeonham.usagestats.di + +import com.hmh.hamyeonham.usagestats.datasource.UsageGoalsDataSource +import com.hmh.hamyeonham.usagestats.repository.DefaultUsageGoalsRepository +import com.hmh.hamyeonham.usagestats.repository.UsageGoalsRepository +import dagger.Binds +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object UsageGoalsModule { + @Provides + @Singleton + fun provideUsageGoalsDataSource(): UsageGoalsDataSource { + return UsageGoalsDataSource() + } + + @Module + @InstallIn(SingletonComponent::class) + interface Binder { + @Binds + @Singleton + fun provideUsageGoalsRepository(usageGoalsRepository: DefaultUsageGoalsRepository): UsageGoalsRepository + } +} diff --git a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/di/UsageStatsModule.kt b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/di/UsageStatsModule.kt index 1e4356912..1e9434f6e 100644 --- a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/di/UsageStatsModule.kt +++ b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/di/UsageStatsModule.kt @@ -36,9 +36,5 @@ object UsageStatsModule { @Binds @Singleton fun provideUsageStatusRepository(usageStatsRepository: DefaultUsageStatsRepository): UsageStatsRepository - - @Binds - @Singleton - fun provideUsageGoalsRepository(usageGoalsRepository: DefaultUsageGoalsRepository): UsageGoalsRepository } } diff --git a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/model/UsageGoalModel.kt b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/model/UsageGoalModel.kt index d33a6fc69..d92cc73c6 100644 --- a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/model/UsageGoalModel.kt +++ b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/model/UsageGoalModel.kt @@ -2,5 +2,5 @@ package com.hmh.hamyeonham.usagestats.model data class UsageGoalModel( val packageName: String, - val goalTime: Long, + val goalTime: Long ) diff --git a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/model/UsageStatModel.kt b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/model/UsageStatModel.kt index 3e81288eb..99efbfb89 100644 --- a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/model/UsageStatModel.kt +++ b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/model/UsageStatModel.kt @@ -2,5 +2,5 @@ package com.hmh.hamyeonham.usagestats.model data class UsageStatModel( val packageName: String, - val totalTimeInForeground: Long, + val totalTimeInForeground: Long ) diff --git a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/DefaultUsageStatsRepository.kt b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/DefaultUsageStatsRepository.kt index a1fdf8338..3167c2c40 100644 --- a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/DefaultUsageStatsRepository.kt +++ b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/DefaultUsageStatsRepository.kt @@ -5,11 +5,11 @@ import com.hmh.hamyeonham.usagestats.model.UsageStat import javax.inject.Inject class DefaultUsageStatsRepository @Inject constructor( - private val usageStatsDataSource: UsageStatsDataSource, + private val usageStatsDataSource: UsageStatsDataSource ) : UsageStatsRepository { override fun getUsageStats( startTime: Long, - endTime: Long, + endTime: Long ): List { val usageStatsList = usageStatsDataSource.getUsageStats(startTime, endTime) return usageStatsList.map { usageStatModel -> @@ -20,34 +20,42 @@ class DefaultUsageStatsRepository @Inject constructor( override fun getUsageTimeForPackage( startTime: Long, endTime: Long, - packageName: String, + packageName: String ): Long { val usageStatsList = getUsageStats(startTime, endTime) return usageStatsList.firstOrNull { it.packageName == packageName }?.totalTimeInForeground ?: 0 } - override fun getUsageTimeForPackages( + override fun getUsageStatForPackages( startTime: Long, endTime: Long, - vararg packageNames: String, + vararg packageNames: String ): List { - val usageStatsList = getUsageStats(startTime, endTime) - return usageStatsList.filter { - packageNames.contains(it.packageName) + val usageStatList = getUsageStats(startTime, endTime) + return packageNames.map { packageName -> + UsageStat( + packageName, + usageStatList.find { + packageName == it.packageName + }?.totalTimeInForeground ?: 0 + ) } } - override fun getUsageTimeForPackages( + override fun getUsageStatForPackages( startTime: Long, endTime: Long, - packageNames: List, + packageNames: List ): List { - val usageStatsList = getUsageStats(startTime, endTime) - val newUsageStatsList = - usageStatsList.filter { - packageNames.contains(it.packageName) - } - return newUsageStatsList + val usageStatList = getUsageStats(startTime, endTime) + return packageNames.map { packageName -> + UsageStat( + packageName, + usageStatList.find { + packageName == it.packageName + }?.totalTimeInForeground ?: 0 + ) + } } } diff --git a/data/userinfo/.gitignore b/data/userinfo/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/data/userinfo/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/data/userinfo/build.gradle.kts b/data/userinfo/build.gradle.kts new file mode 100644 index 000000000..396a12cd5 --- /dev/null +++ b/data/userinfo/build.gradle.kts @@ -0,0 +1,12 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + hmh("feature") +} + +android { + namespace = "com.hmh.hamyeonham.data.userinfo" +} + +dependencies { + implementation(projects.domain.userinfo) +} diff --git a/data/userinfo/consumer-rules.pro b/data/userinfo/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/data/userinfo/proguard-rules.pro b/data/userinfo/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/data/userinfo/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/data/userinfo/src/main/AndroidManifest.xml b/data/userinfo/src/main/AndroidManifest.xml new file mode 100644 index 000000000..a5918e68a --- /dev/null +++ b/data/userinfo/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/datasource/UserInfoDataSource.kt b/data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/datasource/UserInfoDataSource.kt new file mode 100644 index 000000000..4f807b265 --- /dev/null +++ b/data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/datasource/UserInfoDataSource.kt @@ -0,0 +1,12 @@ +package com.hmh.hamyeonham.userinfo.datasource + +import com.hmh.hamyeonham.userinfo.model.UserInfoModel +import javax.inject.Inject + +class UserInfoDataSource @Inject constructor() { + private val userInfoModel = UserInfoModel("여민서", 0) + + fun getUserInfoModel(): UserInfoModel { + return userInfoModel + } +} diff --git a/data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/di/UserInfoModule.kt b/data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/di/UserInfoModule.kt new file mode 100644 index 000000000..3fa309b34 --- /dev/null +++ b/data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/di/UserInfoModule.kt @@ -0,0 +1,22 @@ +package com.hmh.hamyeonham.userinfo.di + +import com.hmh.hamyeonham.userinfo.repository.DefaultUserInfoRepository +import com.hmh.hamyeonham.userinfo.repository.UserInfoRepository +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object UserInfoModule { + @Module + @InstallIn(SingletonComponent::class) + interface UserInfoBinder { + @Binds @Singleton + fun provideUserInfoRepository( + userInfoRepository: DefaultUserInfoRepository + ): UserInfoRepository + } +} diff --git a/data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/model/UserInfoModel.kt b/data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/model/UserInfoModel.kt new file mode 100644 index 000000000..3f46b70f6 --- /dev/null +++ b/data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/model/UserInfoModel.kt @@ -0,0 +1,6 @@ +package com.hmh.hamyeonham.userinfo.model + +data class UserInfoModel( + val name: String, + val point: Int +) diff --git a/data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/repository/DefaultUserInfoRepository.kt b/data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/repository/DefaultUserInfoRepository.kt new file mode 100644 index 000000000..0bbd9a8d6 --- /dev/null +++ b/data/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/repository/DefaultUserInfoRepository.kt @@ -0,0 +1,14 @@ +package com.hmh.hamyeonham.userinfo.repository + +import com.hmh.hamyeonham.userinfo.datasource.UserInfoDataSource +import com.hmh.hamyeonham.userinfo.model.UserInfo +import javax.inject.Inject + +class DefaultUserInfoRepository @Inject constructor( + private val userInfoDataSource: UserInfoDataSource +) : UserInfoRepository { + override fun getUserInfo(): UserInfo { + val userInfoModel = userInfoDataSource.getUserInfoModel() + return UserInfo(userInfoModel.name, userInfoModel.point) + } +} diff --git a/domain/challenge/src/main/java/com/hmh/hamyeonham/challenge/model/ChallengeStatus.kt b/domain/challenge/src/main/java/com/hmh/hamyeonham/challenge/model/ChallengeStatus.kt index 23815c86e..ea64bb164 100644 --- a/domain/challenge/src/main/java/com/hmh/hamyeonham/challenge/model/ChallengeStatus.kt +++ b/domain/challenge/src/main/java/com/hmh/hamyeonham/challenge/model/ChallengeStatus.kt @@ -1,11 +1,10 @@ package com.hmh.hamyeonham.challenge.model - data class ChallengeStatus( val apps: List = emptyList(), val isSuccessList: List = emptyList(), val goalTime: Int = 0, - val period: Int = 0, + val period: Int = 0 ) { data class App( val appCode: String, diff --git a/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/model/UsageStat.kt b/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/model/UsageStat.kt index 73dbbcbdf..2ba8f7446 100644 --- a/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/model/UsageStat.kt +++ b/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/model/UsageStat.kt @@ -2,5 +2,5 @@ package com.hmh.hamyeonham.usagestats.model data class UsageStat( val packageName: String, - val totalTimeInForeground: Long + val totalTimeInForeground: Long, ) diff --git a/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/UsageStatsRepository.kt b/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/UsageStatsRepository.kt index dd1605f09..8defccb07 100644 --- a/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/UsageStatsRepository.kt +++ b/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/UsageStatsRepository.kt @@ -3,15 +3,24 @@ package com.hmh.hamyeonham.usagestats.repository import com.hmh.hamyeonham.usagestats.model.UsageStat interface UsageStatsRepository { - fun getUsageStats(startTime: Long, endTime: Long): List - fun getUsageTimeForPackage(startTime: Long, endTime: Long, packageName: String): Long - fun getUsageTimeForPackages( + fun getUsageStats( + startTime: Long, + endTime: Long, + ): List + + fun getUsageTimeForPackage( + startTime: Long, + endTime: Long, + packageName: String, + ): Long + + fun getUsageStatForPackages( startTime: Long, endTime: Long, vararg packageNames: String, ): List - fun getUsageTimeForPackages( + fun getUsageStatForPackages( startTime: Long, endTime: Long, packageNames: List, diff --git a/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/usecase/GetUsageGoalsUseCase.kt b/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/usecase/GetUsageGoalsUseCase.kt index e9e58cce4..1e37df2fd 100644 --- a/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/usecase/GetUsageGoalsUseCase.kt +++ b/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/usecase/GetUsageGoalsUseCase.kt @@ -5,7 +5,7 @@ import com.hmh.hamyeonham.usagestats.repository.UsageGoalsRepository import javax.inject.Inject class GetUsageGoalsUseCase @Inject constructor( - private val getUsageGoalsUseCase: UsageGoalsRepository + private val getUsageGoalsUseCase: UsageGoalsRepository, ) { operator fun invoke(): List { return getUsageGoalsUseCase.getUsageGoals() diff --git a/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/usecase/GetUsageStatsListUseCase.kt b/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/usecase/GetUsageStatsListUseCase.kt index 4bc351fc7..d018adac1 100644 --- a/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/usecase/GetUsageStatsListUseCase.kt +++ b/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/usecase/GetUsageStatsListUseCase.kt @@ -7,19 +7,19 @@ import javax.inject.Inject class GetUsageStatsListUseCase @Inject constructor( private val usageStatsRepository: UsageStatsRepository, - private val usageGoalsRepository: UsageGoalsRepository, + private val usageGoalsRepository: UsageGoalsRepository ) { companion object { private const val TOTAL = "total" } - fun getUsageStatsAndGoals( + operator fun invoke( startTime: Long, - endTime: Long, + endTime: Long ): List { - val totalUsage = getTotalUsage(startTime, endTime) val usageForSelectedApps = getUsageStatsAndGoalsForSelectedApps(startTime, endTime) + val totalUsage = getTotalUsage(usageForSelectedApps) val totalUsageStatAndGoal = UsageStatAndGoal(TOTAL, totalUsage, usageGoalsRepository.getUsageGoalTime(TOTAL)) return listOf(totalUsageStatAndGoal) + usageForSelectedApps @@ -27,33 +27,35 @@ class GetUsageStatsListUseCase @Inject constructor( private fun getUsageStatsAndGoalsForSelectedApps( startTime: Long, - endTime: Long, + endTime: Long ): List { val appList = getSelectedPackageList() - return usageStatsRepository.getUsageTimeForPackages(startTime, endTime, appList) + return usageStatsRepository.getUsageStatForPackages(startTime, endTime, appList) .map { createUsageStatAndGoal( it.packageName, it.totalTimeInForeground, - it.packageName, + it.packageName ) } } private fun getTotalUsage( - startTime: Long, - endTime: Long, - ): Long = usageStatsRepository.getUsageStats(startTime, endTime).sumOf { - it.totalTimeInForeground + usageStatAndGoalList: List + ): Long { + return usageStatAndGoalList.sumOf { + it.totalTimeInForeground + } } private fun getSelectedPackageList(): List = - usageGoalsRepository.getUsageGoals().map { it.packageName }.distinct() + usageGoalsRepository.getUsageGoals().filter { it.packageName != TOTAL } + .map { it.packageName }.distinct() private fun createUsageStatAndGoal( packageName: String, totalTimeInForeground: Long, - goalKey: String, + goalKey: String ): UsageStatAndGoal { val goalTime = usageGoalsRepository.getUsageGoalTime(goalKey) return UsageStatAndGoal(packageName, totalTimeInForeground, goalTime) diff --git a/domain/userinfo/.gitignore b/domain/userinfo/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/domain/userinfo/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/domain/userinfo/build.gradle.kts b/domain/userinfo/build.gradle.kts new file mode 100644 index 000000000..825eda066 --- /dev/null +++ b/domain/userinfo/build.gradle.kts @@ -0,0 +1,15 @@ +import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.implementation +import org.gradle.kotlin.dsl.`java-library` +import org.gradle.kotlin.dsl.kotlin +import org.gradle.kotlin.dsl.libs + +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + `java-library` + kotlin("jvm") +} + +dependencies { + implementation(libs.javax.inject) +} diff --git a/domain/userinfo/consumer-rules.pro b/domain/userinfo/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/domain/userinfo/proguard-rules.pro b/domain/userinfo/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/domain/userinfo/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/domain/userinfo/src/main/AndroidManifest.xml b/domain/userinfo/src/main/AndroidManifest.xml new file mode 100644 index 000000000..a5918e68a --- /dev/null +++ b/domain/userinfo/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/domain/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/model/UserInfo.kt b/domain/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/model/UserInfo.kt new file mode 100644 index 000000000..9337ee4ff --- /dev/null +++ b/domain/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/model/UserInfo.kt @@ -0,0 +1,6 @@ +package com.hmh.hamyeonham.userinfo.model + +data class UserInfo( + val name: String = "", + val point: Int = 0 +) diff --git a/domain/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/repository/UserInfoRepository.kt b/domain/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/repository/UserInfoRepository.kt new file mode 100644 index 000000000..6b04b5dd2 --- /dev/null +++ b/domain/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/repository/UserInfoRepository.kt @@ -0,0 +1,7 @@ +package com.hmh.hamyeonham.userinfo.repository + +import com.hmh.hamyeonham.userinfo.model.UserInfo + +interface UserInfoRepository { + fun getUserInfo(): UserInfo +} diff --git a/domain/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/usecase/GetUserInfoUseCase.kt b/domain/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/usecase/GetUserInfoUseCase.kt new file mode 100644 index 000000000..73dcdaf26 --- /dev/null +++ b/domain/userinfo/src/main/java/com/hmh/hamyeonham/userinfo/usecase/GetUserInfoUseCase.kt @@ -0,0 +1,15 @@ +package com.hmh.hamyeonham.userinfo.usecase + +import com.hmh.hamyeonham.userinfo.model.UserInfo +import com.hmh.hamyeonham.userinfo.repository.UserInfoRepository +import javax.inject.Inject + +class GetUserInfoUseCase +@Inject +constructor( + private val getUserInfo: UserInfoRepository +) { + operator fun invoke(): UserInfo { + return getUserInfo.getUserInfo() + } +} diff --git a/feature/challenge/build.gradle.kts b/feature/challenge/build.gradle.kts index 14a130614..96fe3350d 100644 --- a/feature/challenge/build.gradle.kts +++ b/feature/challenge/build.gradle.kts @@ -1,6 +1,6 @@ @Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed plugins { - hmh("feature") + hmh("feature") } android { @@ -8,7 +8,6 @@ android { } dependencies { - implementation(projects.domain.usagestats) implementation(projects.domain.challenge) diff --git a/feature/challenge/src/main/java/com/hmh/hamyeonham/challenge/ChallengeFragment.kt b/feature/challenge/src/main/java/com/hmh/hamyeonham/challenge/ChallengeFragment.kt index dff242374..4bf87a0fa 100644 --- a/feature/challenge/src/main/java/com/hmh/hamyeonham/challenge/ChallengeFragment.kt +++ b/feature/challenge/src/main/java/com/hmh/hamyeonham/challenge/ChallengeFragment.kt @@ -30,7 +30,7 @@ class ChallengeFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?, + savedInstanceState: Bundle? ): View { return FragmentChallengeBinding.inflate(inflater, container, false).root } @@ -49,7 +49,6 @@ class ChallengeFragment : Fragment() { challengeAdapter?.submitList(it.challengeStatus.isSuccessList) challengeGoalsAdapter?.submitList(it.usageGoals) }.launchIn(viewLifeCycleScope) - } private fun initChallengeRecyclerView() { diff --git a/feature/challenge/src/main/java/com/hmh/hamyeonham/challenge/ChallengeUsageGoalsAdapter.kt b/feature/challenge/src/main/java/com/hmh/hamyeonham/challenge/ChallengeUsageGoalsAdapter.kt index c913360e5..d1c6e531c 100644 --- a/feature/challenge/src/main/java/com/hmh/hamyeonham/challenge/ChallengeUsageGoalsAdapter.kt +++ b/feature/challenge/src/main/java/com/hmh/hamyeonham/challenge/ChallengeUsageGoalsAdapter.kt @@ -12,14 +12,11 @@ import com.hmh.hamyeonham.usagestats.model.UsageGoal class ChallengeUsageGoalsAdapter : ListAdapter( - ItemDiffCallback( - onItemsTheSame = { oldItem, newItem -> - oldItem.packageName == newItem.packageName - }, - onContentsTheSame = { oldItem, newItem -> - oldItem == newItem - } - ) + ItemDiffCallback(onItemsTheSame = { oldItem, newItem -> + oldItem.packageName == newItem.packageName + }, onContentsTheSame = { oldItem, newItem -> + oldItem == newItem + }) ) { class ChallengeUsageGoalsViewHolder(private val binding: ItemChallengeUsageGoalBinding) : RecyclerView.ViewHolder(binding.root) { diff --git a/feature/login/build.gradle.kts b/feature/login/build.gradle.kts index c30b53317..003257920 100644 --- a/feature/login/build.gradle.kts +++ b/feature/login/build.gradle.kts @@ -20,4 +20,6 @@ dependencies { // dot indicator implementation(libs.dot.indicator) + implementation(projects.core.designsystem) + } diff --git a/feature/login/src/main/AndroidManifest.xml b/feature/login/src/main/AndroidManifest.xml index ec0370708..035e0ed76 100644 --- a/feature/login/src/main/AndroidManifest.xml +++ b/feature/login/src/main/AndroidManifest.xml @@ -2,9 +2,6 @@ - diff --git a/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginActivity.kt b/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginActivity.kt index 4f0595d0e..da3e1a476 100644 --- a/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginActivity.kt +++ b/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginActivity.kt @@ -4,96 +4,63 @@ import android.os.Bundle import android.util.Log import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity -import com.hmh.hamyeonham.common.context.toast +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope import com.hmh.hamyeonham.common.navigation.NavigationProvider -import com.hmh.hamyeonham.feature.login.data.DummyImage +import com.hmh.hamyeonham.common.view.viewBinding import com.hmh.hamyeonham.feature.login.databinding.ActivityLoginBinding -import com.kakao.sdk.auth.model.OAuthToken -import com.kakao.sdk.common.model.ClientError -import com.kakao.sdk.common.model.ClientErrorCause -import com.kakao.sdk.user.UserApiClient import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import javax.inject.Inject @AndroidEntryPoint class LoginActivity : AppCompatActivity() { - private lateinit var binding: ActivityLoginBinding - - private val loginViewModel: LoginViewModel by viewModels() + private val binding by viewBinding(ActivityLoginBinding::inflate) + private val viewModel by viewModels() private lateinit var loginViewPagerAdapter: LoginViewPagerAdapter @Inject lateinit var navigationProvider: NavigationProvider - // 삭제 예정 - private val dummyImageList = listOf( - DummyImage( - Image = R.drawable.login_sample_rectagle_viewpager, - ), - DummyImage( - Image = R.drawable.login_sample_rectagle_viewpager, - ), - DummyImage( - Image = R.drawable.login_sample_rectagle_viewpager, - ), - ) - - private val callback: (OAuthToken?, Throwable?) -> Unit = { token, error -> - when { - error != null -> { - } - - token != null -> { - moveToUserInfoActivity() - } - } - } - override fun onCreate(savedInstanceState: Bundle?) { - binding = ActivityLoginBinding.inflate(layoutInflater) super.onCreate(savedInstanceState) setContentView(binding.root) - binding.btnLogin.setOnClickListener { - loginWithKakaoApp() + binding.ivKakaoLogin.setOnClickListener { + viewModel.loginWithKakaoApp(this) } setLoginViewPager() + handleKakaoLoginSuccess() + } + + private fun handleKakaoLoginSuccess() { + viewModel.kakaoLoginState.flowWithLifecycle(lifecycle).onEach { state -> + if (state.isSuccessResult) { + viewModel.getKakaoUserNickname() + // 닉네임 출력 + Log.d("LoginActivity", "닉네임: ${state.kakaoNickname}") + moveToOnBoardingActivity() + } + }.launchIn(lifecycleScope) } private fun setLoginViewPager() { - loginViewPagerAdapter = LoginViewPagerAdapter(dummyImageList) + val loginViewImageList = listOf( + R.drawable.login_sample_rectagle_viewpager, + R.drawable.login_sample_rectagle_viewpager, + R.drawable.login_sample_rectagle_viewpager, + ) + + loginViewPagerAdapter = LoginViewPagerAdapter(loginViewImageList) binding.run { vpLogin.adapter = loginViewPagerAdapter indicatorLoginDots.attachTo(binding.vpLogin) } } - private fun loginWithKakaoApp() { - if (UserApiClient.instance.isKakaoTalkLoginAvailable(this)) { - UserApiClient.instance.loginWithKakaoTalk(this) { token, error -> - if (error != null) { - toast("카카오 로그인 실패") - if (error is ClientError && error.reason == ClientErrorCause.Cancelled) { - toast("다시 로그인 해주세요.") - return@loginWithKakaoTalk - } - loginWithKakaoAccount() - } else if (token != null) { - toast("카카오 로그인 성공") - moveToUserInfoActivity() - } - } - } else { - loginWithKakaoAccount() - } - } - - private fun loginWithKakaoAccount() { - UserApiClient.instance.loginWithKakaoAccount(this, callback = callback) - } - - private fun moveToUserInfoActivity() { - startActivity(navigationProvider.toStatics()) + private fun moveToOnBoardingActivity() { + startActivity(navigationProvider.toOnBoarding()) finish() } } diff --git a/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginViewModel.kt b/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginViewModel.kt index 0d5cdf4cf..cc94536c9 100644 --- a/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginViewModel.kt +++ b/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginViewModel.kt @@ -1,12 +1,90 @@ package com.hmh.hamyeonham.feature.login -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData +import android.content.Context import androidx.lifecycle.ViewModel +import com.kakao.sdk.common.model.ClientError +import com.kakao.sdk.common.model.ClientErrorCause +import com.kakao.sdk.user.UserApiClient +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import javax.inject.Inject -class LoginViewModel : ViewModel(){ +@HiltViewModel +class LoginViewModel @Inject constructor() : ViewModel() { + data class KakaoLoginState( + val isSuccessResult: Boolean = false, + val accessToken: String? = null, + val refreshToken: String? = null, + val kakaoNickname: String? = null, + ) - private val _kakaoLoginResult = MutableLiveData() - val kakaoLoginResult: LiveData get() = _kakaoLoginResult + private val _kakaoLoginState = MutableStateFlow(KakaoLoginState()) + val kakaoLoginState = _kakaoLoginState.asStateFlow() + private fun updateState(transform: KakaoLoginState.() -> KakaoLoginState) { + val currentState = kakaoLoginState.value + val newState = currentState.transform() + _kakaoLoginState.value = newState + } + + fun loginWithKakaoApp(context: Context) { + if (UserApiClient.instance.isKakaoTalkLoginAvailable(context)) { + UserApiClient.instance.loginWithKakaoTalk(context) { token, error -> + if (error != null) { + if (error is ClientError && error.reason == ClientErrorCause.Cancelled) { + return@loginWithKakaoTalk + } + loginWithKakaoAccount(context) + } else if (token != null) { + updateState { + copy( + isSuccessResult = true, + accessToken = token.accessToken, + refreshToken = token.refreshToken, + ) + } + } + } + } else { + loginWithKakaoAccount(context) + } + } + + private fun loginWithKakaoAccount(context: Context) { + UserApiClient.instance.loginWithKakaoAccount(context) { token, error -> + if (error != null) { + updateState { + copy( + isSuccessResult = false, + accessToken = "", + refreshToken = "", + ) + } + } else if (token != null) { + updateState { + copy( + isSuccessResult = true, + accessToken = token.accessToken, + refreshToken = token.refreshToken, + ) + } + } + } + } + + fun getKakaoUserNickname() { + UserApiClient.instance.me { user, error -> + if (error != null) { + // 닉네임 정보 얻기 실패 시 + } else if (user != null) { + val kakaoNickname = user.kakaoAccount?.profile?.nickname + updateState { + copy( + kakaoNickname = kakaoNickname, + ) + } + } + } + } } diff --git a/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginViewPagerAdapter.kt b/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginViewPagerAdapter.kt index 328a8d7b3..446cb7109 100644 --- a/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginViewPagerAdapter.kt +++ b/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/LoginViewPagerAdapter.kt @@ -4,10 +4,9 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import coil.load -import com.hmh.hamyeonham.feature.login.data.DummyImage import com.hmh.hamyeonham.feature.login.databinding.ItemLoginViewPagerBinding -class LoginViewPagerAdapter(private val imageList: List) : +class LoginViewPagerAdapter(private val imageList: List) : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PagerViewHolder { @@ -19,9 +18,9 @@ class LoginViewPagerAdapter(private val imageList: List) : class PagerViewHolder(private val binding: ItemLoginViewPagerBinding) : RecyclerView.ViewHolder(binding.root) { - fun onBindView(imageInfo: DummyImage) { + fun onBindView(imageInfo: Int) { binding.run { - ivLoginViewPagerItem.load(imageInfo.Image) { + ivLoginViewPagerItem.load(imageInfo) { placeholder(R.drawable.login_sample_rectagle_viewpager) error(R.drawable.login_sample_rectagle_viewpager) } diff --git a/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/UserInfoActivity.kt b/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/UserInfoActivity.kt deleted file mode 100644 index fc020b486..000000000 --- a/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/UserInfoActivity.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.hmh.hamyeonham.feature.login - -import android.content.Intent -import android.os.Bundle -import androidx.activity.viewModels -import androidx.appcompat.app.AppCompatActivity -import com.hmh.hamyeonham.common.context.toast -import com.hmh.hamyeonham.feature.login.databinding.ActivityUserInfoBinding -import com.kakao.sdk.user.UserApiClient - -class UserInfoActivity : AppCompatActivity() { - private lateinit var binding: ActivityUserInfoBinding - - private val userInfoViewModel: UserInfoViewModel by viewModels() - - override fun onCreate(savedInstanceState: Bundle?) { - binding = ActivityUserInfoBinding.inflate(layoutInflater) - super.onCreate(savedInstanceState) - setContentView(binding.root) - - getKakaoUserNickname() - binding.btnLoginLogout.setOnClickListener { - logoutFromKakao() - } - } - - private fun getKakaoUserNickname() { - UserApiClient.instance.me { user, error -> - if (error != null) { - moveToLogin() - } else if (user != null) { - val kakaoNickname = user.kakaoAccount?.profile?.nickname - binding.tvLoginNickname.text = kakaoNickname - } - } - } - - private fun logoutFromKakao() { - UserApiClient.instance.logout { error -> - if (error != null) { - toast("다시 로그아웃 해주세요.") - } else { - toast("로그아웃 되었습니다.") - moveToLogin() - } - } - } - - private fun moveToLogin() { - startActivity(Intent(this, LoginActivity::class.java)) - finish() - } -} diff --git a/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/UserInfoViewModel.kt b/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/UserInfoViewModel.kt deleted file mode 100644 index 69e9365a0..000000000 --- a/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/UserInfoViewModel.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.hmh.hamyeonham.feature.login - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel - -class UserInfoViewModel : ViewModel() { - - private val _kakaoLogoutResult = MutableLiveData() - val kakaoLogoutResult: LiveData get() = _kakaoLogoutResult - - private val _kakaoUserNickname = MutableLiveData() - val kakaoUserNickname: LiveData get() = _kakaoUserNickname -} diff --git a/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/data/DummyImage.kt b/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/data/DummyImage.kt deleted file mode 100644 index 807c56a38..000000000 --- a/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/data/DummyImage.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.hmh.hamyeonham.feature.login.data - -data class DummyImage( - val Image: Int, -) diff --git a/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/model/LoginViewImageList.kt b/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/model/LoginViewImageList.kt new file mode 100644 index 000000000..718bdca21 --- /dev/null +++ b/feature/login/src/main/java/com/hmh/hamyeonham/feature/login/model/LoginViewImageList.kt @@ -0,0 +1,5 @@ +package com.hmh.hamyeonham.feature.login.model + +data class LoginViewImageList( + val viewPagerImage: Int, +) diff --git a/feature/login/src/main/res/drawable/login_kakao_btn_52.xml b/feature/login/src/main/res/drawable/login_kakao_btn_52.xml new file mode 100644 index 000000000..c22889918 --- /dev/null +++ b/feature/login/src/main/res/drawable/login_kakao_btn_52.xml @@ -0,0 +1,18 @@ + + + + + + + \ No newline at end of file diff --git a/feature/login/src/main/res/drawable/login_kakao_icon_24.xml b/feature/login/src/main/res/drawable/login_kakao_icon_24.xml new file mode 100644 index 000000000..21da8c6ad --- /dev/null +++ b/feature/login/src/main/res/drawable/login_kakao_icon_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/feature/login/src/main/res/drawable/login_kakao_large_wide.png b/feature/login/src/main/res/drawable/login_kakao_large_wide.png deleted file mode 100644 index c0c1856129a03c5e5812854c7b7a1a6134f0f5c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7213 zcmY+J1z3~c+s7FU>5!6UNJ)36bO=ay4WtDLNd-oUG>mQp$pJ$_8U~1Tw{(L@cfa%h zdw+l5_qxutoo9Q__tbNL@6XOYzSK~{!=}VWK|#S&QI^+6K|wV{){QYAp`ZYWg~;uZ z7XS}!C0UfpA?h6z6sj#1`RBSmfW2%iAKl5>?gxIOUhGgAWze%=Y*|(^X@%!PuO6d! z?K#EN%T#dcc748{d|#^Xr25M3`E&H6QDggj%t&nGmog>TNtn9v?=>;$ZRpbnyoa*q z)5ZBOhURBkdLJZ)2U{*+2UiRHu8aQPoFH$`L(*(q5xBnU#hQr3$RV=eu@41XP7X5! zP39F zx6sun3x8F+MLjRW^eum6n>UM*$u;8MZ0W#Hu2{5|UK z_Y+8K3xjqJ|9JnSDUqr>vw4vsRbkbdfB?r80UBK*<{{o2ii~*9{ZEAYC~=`x;>=b? zILq7R>rpi-;Aq>(AbUN$TRVGfQU-L@pa&c@rXQC@m}xGCdf;1^@52xrML>C*P_i&X zttD!}1F0YWhW{7G$PI*C+0OK2;+K@LUPuX~ zEG5xc@$DBtQL*5&x>WP+9=ijG{KTXv7`IvINjaIwwSSXWY4_emtsb;7dy6DhkR5MB zU)0UI(MqsOaoxyhQ7JB{G@Mk@%~0Eep72Y|q^S&?c7+LH6?u4U;}Qc*3TLX?yQx-( z&5LH*D1!`9!O!f{)5v_oUbt4%b&xmVMFkH=?LRK0K|0m1$gI;Id!0g9q*Hc*ZGoU9B&5(wfpN|Mc)6$uzRF_ z+97zPT#VXPwc{h?#x1&}D%P-na>TlyoM7d?1nMRtHZV+$I?nQ_6Z3*w%?&XcSgs|T zvX?b}ac6gFovJ3$9_kuvJfotS8L2w8z? z|7^in#f=cflk>Oaza7n0W%w`GbgWCybYf1=)F7zdP5LooHp8toFALe3`lrNN9#!GF zs!j(`O*tHe{QSasY+@n6WS=xC4QRvb4jw5ZYv=ZzE*v7vm>?z!bp7hGr)mO~!m|C5r7DQ$GD2)>T=*gwE%nA9)I)&E zAX+Tta@BjZq%W-4QS6A-BVH|LA`pNR;81N+4>ykroo19lJ$ezuFmiP3`HTuHK8Ol{ zzg54ojE5C3BSD(^3oX31=)$s4fhYfi=gzc#nD9rO)(h3*+5N@*TxNdT! z6yvfAXkEj*!9c(Es>-q{`Qd8md+U;2gYOYDDsb_-+-aKX^?BXm@(f(wwc3TpVWOEu zYJ{v`zp+(32}A~|@_8U{%pSeWrx*M*)UY)oL1U3>P#i1t$`yCA_%@UjBhIIa&!BTd zlzTc{sDfrmQu_`g+KKyovm3f^kT0KOZV;lux;I|d}m@OEJLeUITsY`bJnmnv?sEjLesBb1U{4Pp1z;()c$7X zgip_^5=cibNIP83M3~rt{b8FxLOqNWKUUE zL)lS3#vCw|x&g4pp{dUhiw;dNZ0WsfJ56f0xVzHhW zysi@axv&zf!qAoE&z)TY|J1KQlYhrmjoV}g{+ybabnmyn(GW}Ub~j9=O=-W32Yc-7 zAfTu`A(mV>gsx32g3gO+<*uuOWcT+ssfaH(5fE%&H4a5ihWv-qHX#}lGbpqiXL8MNR;z*aYA%x2_9J~j*+cM++p zW~4BPo;HBnp4@c*)?O$HsdV}fR+TE|tu+xNKU~j1=w94pTo4A{>ggpY_rBipXacBh zqB>NZcJ5G7q1s-jNHCqe;NP)Ynp!Bx z3p8|YvREo$fA`kfLDrQ)tyq|AeYUmhmbtYkWNy|0UKa1Bqs9_Yumt!~ zWu+H5rQ{Q~g?5L`BQ;98IJBlAUgGbn;_Pi@3~7*EUebd|#=Q)A2ge@s*J_KBrre>h zg(mh*JUY(!(-T-7kr-5+BEga3{tR2~8GGhSeQMt5GpN-SGC~`Hx}RT>_WgRx#%`u~ z$_OBeAV&3~B2&?-7hut##q0tBhz`{e;4T|Bj-_aFRpg{tfHq1Jr21JBxRKA10iKH@ z{_~C>jEfwH2)#@gDGNa`I35M-l0_zLBlod2vicnhxeOw{gd|7L-PI@#XZ(bt1G+jf2wY>puz*9Uz1j~m-;L0u2B@`;*c-yd2@SSQ4DbXLp&7u1@v}>ws?QP|YbmxA@^`!g8!cySIuruCamUJ3dH5*hN>cNSc$j zk?sQ8FP^tp^xMaV{HskDeI5MZA#>fU%6q)PKRffH+oEvw{D#`1OoOB?=u9mZcuQ|o zhwDo$h)T~2YoA8vwtI{B@|Nw*Qg-`Rd(u6Y?sP;|v~B}CWXskDyvGWNjoObK{7an$G6v*=)pM!O58c|5eSI436wKZ=kXjU3K7@x1 zJ>zap{D_1zxZMIhBjZfS#y3p$X>Frib5MOv!V8+}zr%CB3#+{stb zze?B5GqP8ht&3yN43F-4|4>)cR6R-N@CyoZe$?me9HK*gE4wx9F&q}c`e$#O&C52jJoIUPMJpH-98qHcgY^yPX$&J#zj(i!HGZI$co38csRrmzDWvqIvrLk?CspAP{ZuVIT`kZS1z}{KPOr*bw zde@M+_tEG29vhc#oh!FWptS>^ddgskX%0Ga;+z`pXKPG0wEf#J>@1n36Rd{NB<3S? zSq?)Kmq&L}5YYi^?u50UmLhB=Jm%HwGmLS*MbK6M3RW3!&BoJixC6W6FJ^0%*?!Kd zyN?g$t&6(n9Ie|Jeq}qO@L=H6DpF@%=Y6txxbpavYJs*Oe8O@j$auh4nnqj8Z)!F- z3pWlAaKPfI9b2l2(()la+L@5EBKbK=3xjOl4MHNiZKz3;y)6V!b0JW+Za`-2UE48X z&ZWn<%rIvD*U8wo@N2lpje&fi$xrMh7dKW-&HR9$U-1ph_4jrv{M)J-vFyCx9+dAs z`^A<#j-fUN-^{d;xZH)-J6rm2h?&uEQ|Kf22A1_rCYGFn>+ORbP5hgs_e3u}74abm z-_GMVqgTa8^K#dbD%`u4V-ju$SUuL(DtwjXxy11s$F~RU^Vf;{@7bt^Z!1w9N-Gl- z9D38==6-xETQEAZTLlZ2*v=q>U&Dwlo3yi0v0*E=f(O3D{2aJ&rbZS9Jqf>GoWUJ}dQUjd@4*%}F#j z3y;>(3tzN&$>L`fY$cfu6D1Ic&UQ zUwYwuU9L1CqC{8Ax6KM;)vTRT)T+EWEA6!IT4abb!`bCMJ959ZefQabk|C}}f0Oi^ z7VT-ABn1A9W7sikfz+NO?#6o$2G9*m_HC3c9gav;z6okW1toFGwL__EV8<7e&fOD8w}mHWn; z-wEt^KX%*fmqjWqYJO90&GKc-y* zANUG>flL?Ve|Yi;&GHB-xrrIE;C^)^=utu*g%rZagngMI$TNQ@r(rjpCqfwa6q!fj zWf%ZzY+@z}p|V8u0HlBqGsql6)7Tw8?AY4{aP#n%z)|}Hr^Z}>{9Hv!h)BjaN3L@9a%08P!(s+yQr0VzQ?NH<2t1t6>gQ8XbB&~mnA22Du zlO)TE^v5IzrGa0>w$k74Adg=ajUg_^E)l_?@(~DMi6}RohW~{_Md_oi-HGRuIDBFD@jh22YD3{4)}j! zl!onY)@*;<@i5Au^%LTu{qLInff*500eJA*58c1*ER{i{Jd8h(Ck>A_n=#%sVwY4A zyhxMRD(`=UTKB-svmA)3)<5(2enK{YT4%tF+#nBF?LEWcirq@jpm^M9XOOf6 zu*P+ZMv;!lz8A=*Y2>zAh~BFr;}XjWe)dUE<2&0(Fn?wF*Jb5WYeWI4>Un%bR3H`S zr z(evrLET_^vbjcqC@W*Ea58nfkNmDD$RW4OSXx$r5&ztaYIw;Y&wxRDlmmbRJw(q4V z*+@2Z^Xg)7)5i3zNo?ZX)Gz@9zZ|8JCvtkNnBScv;Fb9G?hAoRlP;2Vt-=?x~=&t@oE@3b@#X#*ASETpyK(Kv*1K@VY*4ERjoDI#pOJ1I1aI#nJng z5Oy|Id_9fnP25u<56}@;4srd#zfo^%Q|qGT)r3BMSH{Uazw`)=TO#JdHs#lk0+#=n z6R(lN<(v}O$+Xm`5W?~*p8>L|VY%`}UzJn|uT(+NZ#~?*Lo1d#Ht|QYDeO2`lOSEF z(gywJ0Y2|<4z8vsVz#>ZGK3*%_PKhys~CW-3=Dlkku#$8d^CVQEhLvl>=<+=n*w4E~9>Al20D~nE`-2 zF_3wI3llivGo#JQA*lE!R(f*cDY2t~sve5a^vfcHwm{?Oz5(`kx7AH#%$M?b^b_XH1%~7e|P9u zx^tyF*3;|*~}^BDvk*tm_qZ; z`QGRX{eD}nJBt5vE!i%PJ&Iz&^O)lU#?n0qkDzUtd@M`n9d*Umvy<_nH9_Q#x}x&( z!uOw{<~%#+G^~+zZJ{@2s-`Bwl!mn5InfhH)*YAYk}Pbbd7oVftBw{l9Z<}*>OWR2 zr>><{+oJ8k0zgBvlbAL{$UrvK#!QEB;1$b-a$E--UbD+bJt{&J( zy{6ceOHUE^o0S+Vjh>JnZMR#J&{f@5KH8MJ;jzPLZ9;#pC{%_6^Y?sqL^ZK{(yL2r zr3uTTw0VxLnb0|fMrwzN*H^#epMIM+wk$|{!suU17BGQrQRzMKIMJhTJ+enUO5@{V zx;r^ua+vmoIIS~hVn2h3_gb3c?N#+*)%GmBw|&`;8CPW%ht)v8F9ez5L1I8K*s`}w z^1^>PygX=kKWKX>PUF?2_WImm@N~nIl)jLyBF((k#ZSLqogJH2k7mAka$eEdG#&>O z{LuR(#y)wCRgS9qGh+E(Fh&WL1y~9LE9HAJxJWNk1?! zhnUZnEhP?rCsJFfBnN9irlyQ}wy#Z@2Au*q2<(r75+eeUNd)yNa&^>HXX}LF)_8}* z8;B$p5OdNaMt?-YhQ?%73uw~^7hi;xI9`UD2yQvgJr^k|6q^%mzBPqW^BHo5jSP|# zB3@?rnTIvn%Q(%D?e9sQrDK!ZacsLxI=4dyW*-)uXg1vB4CG$mf&jv(yn=9Dl}bwd zo`UDi5gOU(ByC0Ep#ejEp)&*+@h7cBrXI3#!)c=NG6Ps5o6|j$?)ffdJ56k(tXr$) zbJiEFh=4-L4_=3ye%2-Ph8|b%9&W-E)WR+Mc=fa%_d1&mZf-@`8GIYWlhG!G1Y%@oan2tcWt+e;(gzfGt6t6al~N`|utUtdd& zgfOwYNQd&;BbLJQyL^o}OPS!6{{^^?*iEG14HTc uSe^6HI>>&~#}Xt&E2L2W<}bT=Kx?NRKO!XuXd?grLs3!Ckgt@r4E`T&MN-ZH diff --git a/feature/login/src/main/res/layout/activity_login.xml b/feature/login/src/main/res/layout/activity_login.xml index b0f8532b1..dbafd538a 100644 --- a/feature/login/src/main/res/layout/activity_login.xml +++ b/feature/login/src/main/res/layout/activity_login.xml @@ -11,42 +11,72 @@ android:id="@+id/vp_login" android:layout_width="match_parent" android:layout_height="0dp" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintBottom_toTopOf="@id/indicator_login_dots" - app:layout_constraintTop_toTopOf="parent" android:layout_marginTop="70dp" android:layout_marginBottom="30dp" - /> + app:layout_constraintBottom_toTopOf="@id/indicator_login_dots" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> - + app:selectedDotColor="?gray2" /> - + app:layout_constraintStart_toStartOf="parent"> + + + + + + \ No newline at end of file diff --git a/feature/login/src/main/res/layout/activity_user_info.xml b/feature/login/src/main/res/layout/activity_user_info.xml deleted file mode 100644 index 8f23b60a4..000000000 --- a/feature/login/src/main/res/layout/activity_user_info.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - -