diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 942ebee8..c323c528 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -83,6 +83,10 @@ android:name=".presentation.main.notification.NotificationActivity" android:exported="false" android:screenOrientation="portrait" /> + (R.layout.activity_main private val isUploadSuccess by lazy { intent.getBooleanExtra(KEY_FEED_UPLOAD, false) } private val isDeleteSuccess by lazy { intent.getBooleanExtra(KEY_FEED_DELETE, false) } - private val prevScreenName by lazy { intent.getStringExtra(KEY_PREV_SCREEN) } private val notiType by lazy { intent.getStringExtra(KEY_NOTI_TYPE) } private val feedId by lazy { intent.getStringExtra(KEY_FEED_ID) } @@ -125,11 +122,7 @@ class MainActivity : BindingActivity(R.layout.activity_main return } - if (prevScreenName == MY_FEED_SCREEN) { - navigateToMyPageFragment(KEY_TO_MYFEED, true) - } else { - navigateTo() - } + navigateTo() } private fun showWineyFeedResultSnackBar() { @@ -204,12 +197,6 @@ class MainActivity : BindingActivity(R.layout.activity_main } } - private inline fun navigateTo() { - supportFragmentManager.commit { - replace(R.id.fcv_main, T::class.simpleName) - } - } - private fun navigateToMyPageFragment(key: String, value: Boolean) { supportFragmentManager.commit { val bundle = Bundle() @@ -232,18 +219,20 @@ class MainActivity : BindingActivity(R.layout.activity_main startActivity(intent) } - companion object { - const val KEY_FEED_ID = "feedId" - const val KEY_TO_MY_PAGE = "navigateMyPage" - const val KEY_FROM_GOAL_PATH = "fromGoalPath" + private inline fun navigateTo() { + supportFragmentManager.commit { + replace(R.id.fcv_main, T::class.simpleName) + } + } + companion object { private const val KEY_FEED_UPLOAD = "upload" private const val KEY_FEED_DELETE = "delete" private const val KEY_NOTI_TYPE = "notiType" private const val KEY_FROM_NOTI = "fromNoti" - private const val KEY_TO_MYFEED = "toMyFeed" - private const val KEY_PREV_SCREEN = "PREV_SCREEN_NAME" - private const val MY_FEED_SCREEN = "MyFeedFragment" + const val KEY_FEED_ID = "feedId" + const val KEY_TO_MY_PAGE = "navigateMyPage" + const val KEY_FROM_GOAL_PATH = "fromGoalPath" } } diff --git a/app/src/main/java/org/go/sopt/winey/presentation/main/MainViewModel.kt b/app/src/main/java/org/go/sopt/winey/presentation/main/MainViewModel.kt index ec55fb3e..0877f4e2 100644 --- a/app/src/main/java/org/go/sopt/winey/presentation/main/MainViewModel.kt +++ b/app/src/main/java/org/go/sopt/winey/presentation/main/MainViewModel.kt @@ -26,7 +26,7 @@ class MainViewModel @Inject constructor( private val dataStoreRepository: DataStoreRepository, private val notificationRepository: NotificationRepository ) : ViewModel() { - private val _getUserState = MutableStateFlow>(UiState.Loading) + private val _getUserState = MutableStateFlow>(UiState.Empty) val getUserState: StateFlow> = _getUserState.asStateFlow() private val _logoutState = MutableStateFlow>(UiState.Empty) @@ -44,7 +44,7 @@ class MainViewModel @Inject constructor( authRepository.getUser() .onSuccess { response -> - Timber.e("SUCCESS GET USER IN MAIN") + Timber.d("SUCCESS GET USER IN MAIN") dataStoreRepository.saveUserInfo(response) _getUserState.value = UiState.Success(response) } @@ -61,7 +61,7 @@ class MainViewModel @Inject constructor( } } - private fun postLogout() { + fun postLogout() { viewModelScope.launch { _logoutState.value = UiState.Loading diff --git a/app/src/main/java/org/go/sopt/winey/presentation/main/feed/WineyFeedFragment.kt b/app/src/main/java/org/go/sopt/winey/presentation/main/feed/WineyFeedFragment.kt index e84e4e49..dd04f690 100644 --- a/app/src/main/java/org/go/sopt/winey/presentation/main/feed/WineyFeedFragment.kt +++ b/app/src/main/java/org/go/sopt/winey/presentation/main/feed/WineyFeedFragment.kt @@ -6,7 +6,6 @@ import android.os.Bundle import android.view.View import androidx.core.view.isVisible import androidx.fragment.app.activityViewModels -import androidx.fragment.app.commit import androidx.fragment.app.viewModels import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope @@ -14,21 +13,19 @@ import androidx.paging.LoadState import androidx.paging.PagingData import androidx.recyclerview.widget.ConcatAdapter import androidx.recyclerview.widget.SimpleItemAnimator -import com.google.android.material.bottomnavigation.BottomNavigationView import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.go.sopt.winey.R import org.go.sopt.winey.databinding.FragmentWineyFeedBinding +import org.go.sopt.winey.domain.entity.UserV2 import org.go.sopt.winey.domain.entity.WineyFeed import org.go.sopt.winey.domain.repository.DataStoreRepository import org.go.sopt.winey.presentation.main.MainViewModel import org.go.sopt.winey.presentation.main.feed.detail.DetailActivity import org.go.sopt.winey.presentation.main.feed.upload.UploadActivity -import org.go.sopt.winey.presentation.main.mypage.MyPageFragment import org.go.sopt.winey.presentation.main.mypage.goal.GoalPathActivity import org.go.sopt.winey.presentation.main.notification.NotificationActivity import org.go.sopt.winey.presentation.model.WineyDialogLabel @@ -60,9 +57,9 @@ class WineyFeedFragment : private val viewModel by viewModels() private val mainViewModel by activityViewModels() private lateinit var wineyFeedAdapter: WineyFeedAdapter - private lateinit var wineyFeedHeaderAdapter: WineyFeedHeaderAdapter - private lateinit var wineyFeedGoalAdapter: WineyFeedGoalAdapter private lateinit var wineyFeedLoadAdapter: WineyFeedLoadAdapter + private lateinit var wineyFeedHeaderAdapter: WineyFeedHeaderAdapter + private var wineyFeedGoalAdapter: WineyFeedGoalAdapter? = null private var clickedFeedId = -1 @Inject @@ -109,7 +106,6 @@ class WineyFeedFragment : /** Adapter */ private fun initAdapter() { - initGoalAdapter() wineyFeedHeaderAdapter = WineyFeedHeaderAdapter( onBannerClicked = { navigateToWineyInstagram() @@ -129,21 +125,16 @@ class WineyFeedFragment : } ) wineyFeedLoadAdapter = WineyFeedLoadAdapter() + initConcatAdapter() + } + private fun initConcatAdapter() { binding.rvWineyfeedPost.adapter = ConcatAdapter( wineyFeedHeaderAdapter, - wineyFeedGoalAdapter, wineyFeedAdapter.withLoadStateFooter(wineyFeedLoadAdapter) ) } - private fun initGoalAdapter() { - viewLifeCycleScope.launch { - val user = dataStoreRepository.getUserInfo().firstOrNull() ?: return@launch - wineyFeedGoalAdapter = WineyFeedGoalAdapter(requireContext(), user) - } - } - private fun navigateToWineyInstagram() { val url = INSTAGRAM_URL val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) @@ -278,13 +269,25 @@ class WineyFeedFragment : mainViewModel.getUserState.flowWithLifecycle(viewLifeCycle) .onEach { state -> when (state) { + is UiState.Loading -> { + showLoadingProgressBar() + } + is UiState.Success -> { - // 피드 생성, 삭제에 따라 유저 데이터와 프로그레스바 갱신 val userInfo = state.data ?: return@onEach - wineyFeedGoalAdapter.updateProgressBar(userInfo) + + if (wineyFeedGoalAdapter == null) { + updateConcatAdapter(userInfo) + } else { + // 피드 생성, 삭제에 따라 유저 데이터와 프로그레스바 갱신 + wineyFeedGoalAdapter?.updateGoalProgressBar(userInfo) + } + + dismissLoadingProgressBar() } is UiState.Failure -> { + dismissLoadingProgressBar() snackBar(binding.root) { state.msg } } @@ -293,6 +296,25 @@ class WineyFeedFragment : }.launchIn(viewLifeCycleScope) } + private fun updateConcatAdapter(user: UserV2) { + wineyFeedGoalAdapter = WineyFeedGoalAdapter(requireContext(), user) + binding.rvWineyfeedPost.adapter = ConcatAdapter( + wineyFeedHeaderAdapter, + wineyFeedGoalAdapter, + wineyFeedAdapter.withLoadStateFooter(wineyFeedLoadAdapter) + ) + } + + private fun showLoadingProgressBar() { + binding.pbWineyfeedLoading.isVisible = true + binding.rvWineyfeedPost.isVisible = false + } + + private fun dismissLoadingProgressBar() { + binding.pbWineyfeedLoading.isVisible = false + binding.rvWineyfeedPost.isVisible = true + } + private fun initGetWineyFeedListStateObserver() { viewModel.getWineyFeedListState.flowWithLifecycle(viewLifeCycle) .onEach { state -> @@ -450,23 +472,6 @@ class WineyFeedFragment : } /** Amplitude Event Tagging */ - private fun sendDialogClickEvent(isNavigate: Boolean) { - val eventProperties = JSONObject() - - try { - if (isNavigate) { - eventProperties.put("method", "yes") - } else { - eventProperties.put("method", "no") - } - } catch (e: JSONException) { - System.err.println("Invalid JSON") - e.printStackTrace() - } - - amplitudeUtils.logEvent("click_goalsetting", eventProperties) - } - private fun sendWineyFeedEvent(type: EventType, feed: WineyFeed) { val eventProperties = JSONObject() @@ -496,51 +501,11 @@ class WineyFeedFragment : } } - private fun showDefaultGoalSettingDialog() { - amplitudeUtils.logEvent("view_goalsetting_popup") - - val dialog = WineyDialogFragment.newInstance( - WineyDialogLabel( - stringOf(R.string.wineyfeed_goal_dialog_title), - stringOf(R.string.wineyfeed_goal_dialog_subtitle), - stringOf(R.string.wineyfeed_goal_dialog_negative_button), - stringOf(R.string.wineyfeed_goal_dialog_positive_button) - ), - handleNegativeButton = { - sendDialogClickEvent(false) - }, - handlePositiveButton = { - sendDialogClickEvent(true) - navigateToMyPageWithBundle() - } - ) - - activity?.supportFragmentManager?.let { dialog.show(it, TAG_DEFAULT_GOAL_SETTING_DIALOG) } - } - - private fun navigateToMyPageWithBundle() { - val myPageFragment = MyPageFragment().apply { - arguments = Bundle().apply { - putBoolean(KEY_FROM_WINEY_FEED, true) - } - } - activity?.supportFragmentManager?.commit { - replace(R.id.fcv_main, myPageFragment) - } - syncBnvSelectedItem() - } - - private fun syncBnvSelectedItem() { - val bottomNav: BottomNavigationView = requireActivity().findViewById(R.id.bnv_main) - bottomNav.selectedItemId = R.id.menu_mypage - } - companion object { private const val INSTAGRAM_URL = "https://instagram.com/winey__official?igshid=MzRlODBiNWFlZA==" - private const val MSG_WINEYFEED_ERROR = "ERROR" - private const val TAG_DEFAULT_GOAL_SETTING_DIALOG = "DEFAULT_GOAL_SETTING_DIALOG" + private const val TAG_FEED_DELETE_DIALOG = "FEED_DELETE_DIALOG" private const val TAG_FEED_REPORT_DIALOG = "FEED_REPORT_DIALOG" private const val TAG_UPLOAD_DIALOG = "UPLOAD_DIALOG" @@ -548,7 +513,6 @@ class WineyFeedFragment : private const val KEY_PREV_SCREEN_NAME = "PREV_SCREEN_NAME" private const val WINEY_FEED_SCREEN = "WineyFeedFragment" - private const val KEY_FROM_WINEY_FEED = "fromWineyFeed" private const val KEY_FEED_ID = "feedId" private const val KEY_FEED_WRITER_ID = "feedWriterId" diff --git a/app/src/main/java/org/go/sopt/winey/presentation/main/feed/WineyFeedGoalAdapter.kt b/app/src/main/java/org/go/sopt/winey/presentation/main/feed/WineyFeedGoalAdapter.kt index 8c7eb5f3..06369ebe 100644 --- a/app/src/main/java/org/go/sopt/winey/presentation/main/feed/WineyFeedGoalAdapter.kt +++ b/app/src/main/java/org/go/sopt/winey/presentation/main/feed/WineyFeedGoalAdapter.kt @@ -15,7 +15,6 @@ class WineyFeedGoalAdapter( private val initialUser: UserV2 ) : RecyclerView.Adapter() { private lateinit var binding: ItemWineyfeedGoalBinding - private var isInitialized = false override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WineyFeedGoalViewHolder { binding = ItemWineyfeedGoalBinding.inflate( @@ -33,18 +32,21 @@ class WineyFeedGoalAdapter( override fun getItemCount(): Int = ITEM_COUNT inner class WineyFeedGoalViewHolder : RecyclerView.ViewHolder(binding.root) { + private var isInitialized = false + fun bind() { if (!isInitialized) { isInitialized = true - // 최초 1회만 실행 (아이템 뷰의 재활용에 따라 예전 초기 데이터가 반영되는 문제 해결) - updateProgressBar(initialUser) + // 초기 유저 데이터로 최초 1회만 바인딩 + updateGoalProgressBar(initialUser) } } } - fun updateProgressBar(user: UserV2) { + fun updateGoalProgressBar(user: UserV2) { if (::binding.isInitialized) { + // 피드 생성, 삭제에 따라 바뀌는 유저 데이터 반영 binding.user = user updateProgressBarRate(user) } diff --git a/app/src/main/java/org/go/sopt/winey/presentation/main/mypage/MyPageFragment.kt b/app/src/main/java/org/go/sopt/winey/presentation/main/mypage/MyPageFragment.kt index 8961dc8f..db0ee8aa 100644 --- a/app/src/main/java/org/go/sopt/winey/presentation/main/mypage/MyPageFragment.kt +++ b/app/src/main/java/org/go/sopt/winey/presentation/main/mypage/MyPageFragment.kt @@ -1,25 +1,15 @@ package org.go.sopt.winey.presentation.main.mypage -import android.Manifest -import android.content.ActivityNotFoundException -import android.content.Context import android.content.Intent -import android.content.pm.PackageManager -import android.os.Build import android.os.Bundle -import android.provider.Settings import android.view.View import androidx.activity.OnBackPressedCallback -import androidx.core.content.ContextCompat -import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.fragment.app.commit -import androidx.fragment.app.replace -import androidx.fragment.app.viewModels import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @@ -30,13 +20,12 @@ import org.go.sopt.winey.domain.repository.DataStoreRepository import org.go.sopt.winey.presentation.main.MainViewModel import org.go.sopt.winey.presentation.main.mypage.goal.GoalPathActivity import org.go.sopt.winey.presentation.main.mypage.myfeed.MyFeedActivity +import org.go.sopt.winey.presentation.main.mypage.setting.SettingActivity import org.go.sopt.winey.presentation.main.notification.NotificationActivity import org.go.sopt.winey.presentation.nickname.NicknameActivity -import org.go.sopt.winey.presentation.onboarding.guide.GuideActivity import org.go.sopt.winey.util.amplitude.AmplitudeUtils import org.go.sopt.winey.util.binding.BindingFragment import org.go.sopt.winey.util.fragment.snackBar -import org.go.sopt.winey.util.fragment.viewLifeCycle import org.go.sopt.winey.util.fragment.viewLifeCycleScope import org.go.sopt.winey.util.view.UiState import org.go.sopt.winey.util.view.setOnSingleClickListener @@ -45,7 +34,6 @@ import javax.inject.Inject @AndroidEntryPoint class MyPageFragment : BindingFragment(R.layout.fragment_my_page) { private val mainViewModel by activityViewModels() - private val myPageViewModel by viewModels() @Inject lateinit var dataStoreRepository: DataStoreRepository @@ -57,19 +45,32 @@ class MyPageFragment : BindingFragment(R.layout.fragment_ override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) amplitudeUtils.logEvent("view_mypage") - initCheckNotificationPermission() initUserData() - initNavigation() - addListener() addObserver() + } + + // 닉네임 액티비티 갔다가 다시 돌아왔을 때 유저 데이터 갱신하도록 + override fun onStart() { + super.onStart() + mainViewModel.getUser() + } + + private fun initUserData() { + viewLifeCycleScope.launch { + val userInfo = dataStoreRepository.getUserInfo().firstOrNull() ?: return@launch + updateUserInfo(userInfo) + } + } - checkFromWineyFeed() + private fun updateUserInfo(data: UserV2) { + binding.data = data } private fun addListener() { initEditNicknameButtonClickListener() + initSettingButtonClickListener() initMyFeedButtonClickListener() initGoalPathButtonClickListener() registerBackPressedCallback() @@ -77,71 +78,6 @@ class MyPageFragment : BindingFragment(R.layout.fragment_ private fun addObserver() { setupGetUserState() - setupDeleteUserState() - } - - private fun initCheckNotificationPermission() { - isNotificationPermissionAllowed = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - ContextCompat.checkSelfPermission( - requireContext(), - Manifest.permission.POST_NOTIFICATIONS - ) == PackageManager.PERMISSION_GRANTED - } else { - true - } - } - - private fun navigateToNotificationSetting(context: Context) { - val intent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - setNotificationIntentActionOreo(context) - } else { - setNorificationIntentActionOreoLess(context) - } - try { - context.startActivity(intent) - } catch (e: ActivityNotFoundException) { - e.printStackTrace() - } - } - - private fun setNotificationIntentActionOreo(context: Context): Intent { - return Intent().also { intent -> - intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS - intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName) - intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK - } - } - - private fun setNorificationIntentActionOreoLess(context: Context): Intent { - return Intent().also { intent -> - intent.action = "android.settings.APP_NOTIFICATION_SETTINGS" - intent.putExtra("app_package", context.packageName) - intent.putExtra("app_uid", context.applicationInfo?.uid) - intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK - } - } - - private fun patchUserInfo() { - lifecycleScope.launch { - val data = dataStoreRepository.getUserInfo().first() - val newData = data?.copy(fcmIsAllowed = false) - dataStoreRepository.saveUserInfo(newData) - } - } - - // 닉네임 액티비티 갔다가 다시 돌아왔을 때 유저 데이터 갱신하도록 - override fun onStart() { - super.onStart() - mainViewModel.getUser() - initCheckNotificationPermission() - } - - private fun checkFromWineyFeed() { - val isFromWineyFeed = arguments?.getBoolean(KEY_FROM_WINEY_FEED) - if (isFromWineyFeed == true) { - showTargetSettingBottomSheet() - } } private fun initEditNicknameButtonClickListener() { @@ -150,6 +86,12 @@ class MyPageFragment : BindingFragment(R.layout.fragment_ } } + private fun initSettingButtonClickListener() { + binding.ivMypageSetting.setOnClickListener { + navigateToSettingScreen() + } + } + private fun initMyFeedButtonClickListener() { binding.btnMypageMyfeed.setOnSingleClickListener { navigateToMyFeedScreen() @@ -158,13 +100,7 @@ class MyPageFragment : BindingFragment(R.layout.fragment_ private fun initGoalPathButtonClickListener() { binding.btnMypageTrip.setOnClickListener { - navigateToGoalPath() - } - } - - private fun navigateToGoalPath() { - Intent(requireContext(), GoalPathActivity::class.java).apply { - startActivity(this) + navigateToGoalPathScreen() } } @@ -187,61 +123,27 @@ class MyPageFragment : BindingFragment(R.layout.fragment_ requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callback) } - private fun initUserData() { - viewLifeCycleScope.launch { - val data = dataStoreRepository.getUserInfo().first() - if (data != null) { - updateUserInfo(data) - } - } - } - - private fun initNavigation() { - val receivedBundle = arguments - if (receivedBundle != null) { - val value = receivedBundle.getBoolean(KEY_TO_MYFEED) - if (value) { - navigateToMyFeedScreen() - arguments?.clear() - } + private fun navigateToNicknameScreen() { + Intent(requireContext(), NicknameActivity::class.java).apply { + putExtra(KEY_PREV_SCREEN_NAME, MY_PAGE_SCREEN) + startActivity(this) } } - private fun setupDeleteUserState() { - myPageViewModel.deleteUserState.flowWithLifecycle(viewLifeCycle) - .onEach { state -> - when (state) { - is UiState.Success -> { - myPageViewModel.clearDataStore() - navigateToGuideScreen() - } - - is UiState.Failure -> { - snackBar(binding.root) { state.msg } - } - - else -> { - } - } - }.launchIn(viewLifeCycleScope) - } - - private fun navigateToGuideScreen() { - Intent(requireContext(), GuideActivity::class.java).apply { - addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) + private fun navigateToSettingScreen() { + Intent(requireContext(), SettingActivity::class.java).apply { startActivity(this) } } - private fun navigateToNicknameScreen() { - Intent(requireContext(), NicknameActivity::class.java).apply { - putExtra(KEY_PREV_SCREEN_NAME, MY_PAGE_SCREEN) + private fun navigateToMyFeedScreen() { + Intent(requireContext(), MyFeedActivity::class.java).apply { startActivity(this) } } - private fun navigateToMyFeedScreen() { - Intent(requireContext(), MyFeedActivity::class.java).apply { + private fun navigateToGoalPathScreen() { + Intent(requireContext(), GoalPathActivity::class.java).apply { startActivity(this) } } @@ -263,33 +165,9 @@ class MyPageFragment : BindingFragment(R.layout.fragment_ }.launchIn(lifecycleScope) } - private fun updateUserInfo(data: UserV2) { - binding.data = data - } - - private fun showTargetSettingBottomSheet() { - val bottomSheet = TargetAmountBottomSheetFragment() - bottomSheet.show(parentFragmentManager, bottomSheet.tag) - amplitudeUtils.logEvent("view_goalsetting") - } - - private fun showTargetNotOverDialog() { - val dialog = MyPageNotOverDialogFragment() - dialog.show(parentFragmentManager, dialog.tag) - } - - private inline fun navigateAndBackStack() { - parentFragmentManager.commit { - replace(R.id.fcv_main, T::class.simpleName) - addToBackStack(null) - } - } - companion object { private const val KEY_PREV_SCREEN_NAME = "PREV_SCREEN_NAME" private const val MY_PAGE_SCREEN = "MyPageFragment" private const val KEY_FROM_NOTI = "fromNoti" - private const val KEY_FROM_WINEY_FEED = "fromWineyFeed" - private const val KEY_TO_MYFEED = "toMyFeed" } } diff --git a/app/src/main/java/org/go/sopt/winey/presentation/main/mypage/goal/GoalPathActivity.kt b/app/src/main/java/org/go/sopt/winey/presentation/main/mypage/goal/GoalPathActivity.kt index 26122b00..ba8545b2 100644 --- a/app/src/main/java/org/go/sopt/winey/presentation/main/mypage/goal/GoalPathActivity.kt +++ b/app/src/main/java/org/go/sopt/winey/presentation/main/mypage/goal/GoalPathActivity.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch import org.go.sopt.winey.R import org.go.sopt.winey.databinding.ActivityGoalPathBinding +import org.go.sopt.winey.domain.entity.UserV2 import org.go.sopt.winey.domain.repository.DataStoreRepository import org.go.sopt.winey.presentation.main.MainActivity import org.go.sopt.winey.presentation.main.feed.WineyFeedFragment @@ -23,6 +24,10 @@ import javax.inject.Inject @AndroidEntryPoint class GoalPathActivity : BindingActivity(R.layout.activity_goal_path) { + @Inject + lateinit var dataStoreRepository: DataStoreRepository + + private var userInfo: UserV2? = null private val levelUpFromWineyFeed by lazy { intent.getBooleanExtra( WineyFeedFragment.KEY_LEVEL_UP, @@ -30,47 +35,37 @@ class GoalPathActivity : BindingActivity(R.layout.activ ) } - @Inject - lateinit var dataStoreRepository: DataStoreRepository - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setupFragmentByLevel() + initUserData() initRemainingGoal() - + setupFragmentByLevel() initBackButtonClickListener() registerBackPressedCallback() } - private fun registerBackPressedCallback() { - val callback = object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - if (levelUpFromWineyFeed) { - navigateToMainScreen() - } else { - finish() - } - } + private fun initUserData() { + lifecycleScope.launch { + userInfo = dataStoreRepository.getUserInfo().firstOrNull() ?: return@launch } - onBackPressedDispatcher.addCallback(this, callback) } - private fun initBackButtonClickListener() { - binding.ivGoalPathBack.setOnClickListener { - if (levelUpFromWineyFeed) { - navigateToMainScreen() - } else { - finish() - } + private fun initRemainingGoal() { + lifecycleScope.launch { + binding.tvGoalPathRemainingMoney.text = + getString( + R.string.goal_path_remaining_money, + userInfo?.remainingAmount?.formatAmountNumber() + ) + binding.tvGoalPathRemainingFeed.text = + getString(R.string.goal_path_remaining_feed, userInfo?.remainingCount) } } private fun setupFragmentByLevel() { lifecycleScope.launch { - val userInfo = dataStoreRepository.getUserInfo().firstOrNull() ?: return@launch - - when (userInfo.userLevel) { + when (userInfo?.userLevel) { UserLevel.FIRST.rankName -> { navigateTo() } @@ -105,19 +100,29 @@ class GoalPathActivity : BindingActivity(R.layout.activ } } - private fun initRemainingGoal() { - lifecycleScope.launch { - val userInfo = dataStoreRepository.getUserInfo().firstOrNull() ?: return@launch - binding.tvGoalPathRemainingMoney.text = - getString( - R.string.goal_path_remaining_money, - userInfo.remainingAmount.formatAmountNumber() - ) - binding.tvGoalPathRemainingFeed.text = - getString(R.string.goal_path_remaining_feed, userInfo.remainingCount) + private fun initBackButtonClickListener() { + binding.ivGoalPathBack.setOnClickListener { + if (levelUpFromWineyFeed) { + navigateToMainScreen() + } else { + finish() + } } } + private fun registerBackPressedCallback() { + val callback = object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + if (levelUpFromWineyFeed) { + navigateToMainScreen() + } else { + finish() + } + } + } + onBackPressedDispatcher.addCallback(this, callback) + } + private fun navigateToMainScreen() { Intent(this@GoalPathActivity, MainActivity::class.java).apply { putExtra(MainActivity.KEY_FROM_GOAL_PATH, true) diff --git a/app/src/main/java/org/go/sopt/winey/presentation/main/mypage/setting/SettingActivity.kt b/app/src/main/java/org/go/sopt/winey/presentation/main/mypage/setting/SettingActivity.kt new file mode 100644 index 00000000..f6c237ef --- /dev/null +++ b/app/src/main/java/org/go/sopt/winey/presentation/main/mypage/setting/SettingActivity.kt @@ -0,0 +1,349 @@ +package org.go.sopt.winey.presentation.main.mypage.setting + +import android.Manifest +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.provider.Settings +import androidx.activity.viewModels +import androidx.core.content.ContextCompat +import androidx.core.view.isGone +import androidx.core.view.isVisible +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import org.go.sopt.winey.R +import org.go.sopt.winey.databinding.ActivitySettingBinding +import org.go.sopt.winey.domain.entity.UserV2 +import org.go.sopt.winey.domain.repository.DataStoreRepository +import org.go.sopt.winey.presentation.main.MainViewModel +import org.go.sopt.winey.presentation.model.WineyDialogLabel +import org.go.sopt.winey.presentation.onboarding.guide.GuideActivity +import org.go.sopt.winey.presentation.onboarding.login.LoginActivity +import org.go.sopt.winey.util.amplitude.AmplitudeUtils +import org.go.sopt.winey.util.binding.BindingActivity +import org.go.sopt.winey.util.context.snackBar +import org.go.sopt.winey.util.context.stringOf +import org.go.sopt.winey.util.fragment.WineyDialogFragment +import org.go.sopt.winey.util.view.UiState +import org.go.sopt.winey.util.view.setOnSingleClickListener +import javax.inject.Inject + +@AndroidEntryPoint +class SettingActivity : BindingActivity(R.layout.activity_setting) { + private val settingViewModel by viewModels() + private val mainViewModel by viewModels() + + @Inject + lateinit var dataStoreRepository: DataStoreRepository + + @Inject + lateinit var amplitudeUtils: AmplitudeUtils + private var isNotificationPermissionAllowed = true + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + addListener() + addObserver() + initNotificationPermissionState() + initUserData() + } + + override fun onStart() { + super.onStart() + initNotificationPermissionState() + updateNotificationButtonByPermission() + } + + private fun addListener() { + init1On1ButtonClickListener() + initLogoutButtonClickListener() + initTermsButtonClickListener() + initWithdrawButtonClickListener() + initNotificationPermissionButtonClickListener() + initNotiToggleButtonClickListener() + initBackButtonClickListener() + } + + private fun addObserver() { + setupDeleteUserState() + setupPatchAllowedNotificationState() + setupLogoutState() + } + + private fun switchOnNotification() { + binding.ivSettingAgree.transitionToState(R.id.end, -1) + patchUserInfo() + settingViewModel.patchAllowedNotification(isAllowed = false) + } + + private fun switchOffNotification() { + binding.ivSettingAgree.transitionToState(R.id.start, -1) + patchUserInfo() + settingViewModel.patchAllowedNotification(isAllowed = true) + } + + private fun initNotificationPermissionButtonClickListener() { + binding.llSettingAgreePermissionChange.setOnClickListener { + showSystemNotificationSetting() + } + } + + private fun showSystemNotificationSetting() { + Intent().apply { + action = Settings.ACTION_APP_NOTIFICATION_SETTINGS + putExtra(Settings.EXTRA_APP_PACKAGE, packageName) + flags = Intent.FLAG_ACTIVITY_NEW_TASK + startActivity(this) + } + } + + private fun showNotificationOffConfirmDialog() { + val dialog = WineyDialogFragment.newInstance( + WineyDialogLabel( + stringOf(R.string.notification_off_dialog_title), + stringOf(R.string.notification_off_dialog_subtitle), + stringOf(R.string.notification_off_dialog_negative_button), + stringOf(R.string.notification_off_dialog_positive_button) + ), + handleNegativeButton = {}, + handlePositiveButton = { switchOffNotification() } + ) + dialog.show( + supportFragmentManager, + TAG_NOTIFICATION_OFF_DIALOG + ) + } + + private fun initUserData() { + lifecycleScope.launch { + val data = dataStoreRepository.getUserInfo().first() + if (data != null) { + updateNotificationAllowSwitchState(data) + } + } + } + + private fun updateNotificationAllowSwitchState(data: UserV2) { + if (isNotificationPermissionAllowed) { + binding.ivSettingAgree.isVisible = true + binding.llSettingAgreePermissionChange.isGone = true + binding.tvSettingAgreePermission.isGone = true + when (data.fcmIsAllowed) { + true -> { + binding.ivSettingAgree.transitionToState(R.id.end, 1) + } + + false -> { + binding.ivSettingAgree.transitionToState(R.id.start, 1) + } + } + } else { + binding.ivSettingAgree.isGone = true + binding.llSettingAgreePermissionChange.isVisible = true + binding.tvSettingAgreePermission.isVisible = true + } + } + + private fun updateNotificationButtonByPermission() { + if (isNotificationPermissionAllowed) { + binding.ivSettingAgree.isVisible = true + + binding.llSettingAgreePermissionChange.isGone = true + binding.tvSettingAgreePermission.isGone = true + } else { + binding.ivSettingAgree.isGone = true + + binding.llSettingAgreePermissionChange.isVisible = true + binding.tvSettingAgreePermission.isVisible = true + } + } + + private fun patchUserInfo() { + lifecycleScope.launch { + val data = dataStoreRepository.getUserInfo().first() + val newData = data?.copy(fcmIsAllowed = false) + dataStoreRepository.saveUserInfo(newData) + } + } + + private fun initNotiToggleButtonClickListener() { + binding.ivSettingSwitch.setOnClickListener { + val isAllowed = when (binding.ivSettingAgree.currentState) { + R.id.start -> false + R.id.end -> true + else -> false + } + + if (!isAllowed) { + switchOnNotification() + } else { + showNotificationOffConfirmDialog() + } + } + } + + private fun initNotificationPermissionState() { + isNotificationPermissionAllowed = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + ContextCompat.checkSelfPermission( + this, + Manifest.permission.POST_NOTIFICATIONS + ) == PackageManager.PERMISSION_GRANTED + } else { + true + } + } + + private fun init1On1ButtonClickListener() { + binding.clSettingTo1on1.setOnClickListener { + val url = ONE_ON_ONE_URL + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) + startActivity(intent) + } + } + + private fun initTermsButtonClickListener() { + binding.clSettingToTerms.setOnClickListener { + val url = TERMS_URL + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) + startActivity(intent) + } + } + + private fun initBackButtonClickListener() { + binding.ivSettingBack.setOnSingleClickListener { + finish() + } + } + + private fun initLogoutButtonClickListener() { + binding.clSettingLogout.setOnClickListener { + amplitudeUtils.logEvent("click_logout") + val dialog = WineyDialogFragment.newInstance( + WineyDialogLabel( + stringOf(R.string.mypage_logout_dialog_title), + stringOf(R.string.mypage_logout_dialog_subtitle), + stringOf(R.string.mypage_logout_dialog_negative_button), + stringOf(R.string.mypage_logout_dialog_positive_button) + ), + handleNegativeButton = {}, + handlePositiveButton = { mainViewModel.postLogout() } + ) + dialog.show(supportFragmentManager, TAG_LOGOUT_DIALOG) + } + } + + private fun initWithdrawButtonClickListener() { + binding.clSettingWithdraw.setOnClickListener { + val dialog = WineyDialogFragment.newInstance( + WineyDialogLabel( + stringOf(R.string.mypage_withdraw_dialog_title), + stringOf(R.string.mypage_withdraw_dialog_subtitle), + stringOf(R.string.mypage_withdraw_dialog_negative_button), + stringOf(R.string.mypage_withdraw_dialog_positive_button) + ), + handleNegativeButton = { settingViewModel.deleteUser() }, + handlePositiveButton = {} + ) + dialog.show(supportFragmentManager, TAG_WITHDRAW_DIALOG) + } + } + + private fun setupDeleteUserState() { + settingViewModel.deleteUserState.flowWithLifecycle(lifecycle) + .onEach { state -> + when (state) { + is UiState.Success -> { + settingViewModel.clearDataStore() + navigateToGuideScreen() + } + + is UiState.Failure -> { + snackBar(binding.root) { state.msg } + } + + else -> { + } + } + }.launchIn(lifecycleScope) + } + + private fun setupPatchAllowedNotificationState() { + settingViewModel.patchAllowedNotificationState.flowWithLifecycle(lifecycle) + .onEach { state -> + when (state) { + is UiState.Success -> { + when (state.data) { + true -> { + binding.ivSettingAgree.transitionToState(R.id.end, -1) + } + + false -> { + binding.ivSettingAgree.transitionToState(R.id.start, -1) + } + + null -> { + binding.ivSettingAgree.transitionToState(R.id.start, -1) + } + } + } + + else -> {} + } + } + } + + private fun setupLogoutState() { + mainViewModel.logoutState.flowWithLifecycle(lifecycle).onEach { state -> + when (state) { + is UiState.Loading -> { + } + + is UiState.Success -> { + navigateToLoginScreen() + } + + is UiState.Failure -> { + snackBar(binding.root) { state.msg } + } + + is UiState.Empty -> { + } + } + }.launchIn(lifecycleScope) + } + + private fun navigateToLoginScreen() { + Intent(this, LoginActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + startActivity(this) + finish() + } + } + + private fun navigateToGuideScreen() { + Intent(this, GuideActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) + startActivity(this) + } + } + + companion object { + + private const val ONE_ON_ONE_URL = "https://open.kakao.com/o/s751Susf" + private const val TERMS_URL = + "https://empty-weaver-a9f.notion.site/iney-9dbfe130c7df4fb9a0903481c3e377e6?pvs=4" + + private const val TAG_LOGOUT_DIALOG = "LOGOUT_DIALOG" + private const val TAG_WITHDRAW_DIALOG = "WITHDRAW_DIALOG" + + private const val TAG_NOTIFICATION_OFF_DIALOG = "offNotification" + } +} diff --git a/app/src/main/java/org/go/sopt/winey/presentation/main/mypage/MyPageViewModel.kt b/app/src/main/java/org/go/sopt/winey/presentation/main/mypage/setting/SettingViewModel.kt similarity index 94% rename from app/src/main/java/org/go/sopt/winey/presentation/main/mypage/MyPageViewModel.kt rename to app/src/main/java/org/go/sopt/winey/presentation/main/mypage/setting/SettingViewModel.kt index 6346e006..f6ae5b5e 100644 --- a/app/src/main/java/org/go/sopt/winey/presentation/main/mypage/MyPageViewModel.kt +++ b/app/src/main/java/org/go/sopt/winey/presentation/main/mypage/setting/SettingViewModel.kt @@ -1,4 +1,4 @@ -package org.go.sopt.winey.presentation.main.mypage +package org.go.sopt.winey.presentation.main.mypage.setting import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -15,15 +15,17 @@ import timber.log.Timber import javax.inject.Inject @HiltViewModel -class MyPageViewModel @Inject constructor( +class SettingViewModel @Inject constructor( private val authRepository: AuthRepository, private val dataStoreRepository: DataStoreRepository ) : ViewModel() { + private val _deleteUserState = MutableStateFlow>(UiState.Empty) val deleteUserState: StateFlow> = _deleteUserState.asStateFlow() private val _patchAllowedNotificationState = MutableStateFlow>(UiState.Empty) - val patchAllowedNotificationState: StateFlow> = _patchAllowedNotificationState.asStateFlow() + val patchAllowedNotificationState: StateFlow> = + _patchAllowedNotificationState.asStateFlow() fun deleteUser() { viewModelScope.launch { diff --git a/app/src/main/java/org/go/sopt/winey/presentation/onboarding/login/LoginActivity.kt b/app/src/main/java/org/go/sopt/winey/presentation/onboarding/login/LoginActivity.kt index 9ddc7c4a..19b6a21d 100644 --- a/app/src/main/java/org/go/sopt/winey/presentation/onboarding/login/LoginActivity.kt +++ b/app/src/main/java/org/go/sopt/winey/presentation/onboarding/login/LoginActivity.kt @@ -20,7 +20,6 @@ import org.go.sopt.winey.util.context.snackBar import org.go.sopt.winey.util.view.UiState import org.json.JSONException import org.json.JSONObject -import timber.log.Timber import javax.inject.Inject @AndroidEntryPoint @@ -46,30 +45,6 @@ class LoginActivity : } } - private fun sendEventToAmplitude(type: EventType) { - val eventProperties = JSONObject() - - try { - when (type) { - EventType.TYPE_VIEW_SCREEN -> eventProperties.put("screen_name", "sign_up") - EventType.TYPE_CLICK_BUTTON -> { - eventProperties.put("button_name", "kakao_signup_button") - .put("paging_number", 1) - } - else -> {} - } - } catch (e: JSONException) { - System.err.println("Invalid JSON") - e.printStackTrace() - } - - when (type) { - EventType.TYPE_VIEW_SCREEN -> amplitudeUtils.logEvent("view_signup", eventProperties) - EventType.TYPE_CLICK_BUTTON -> amplitudeUtils.logEvent("click_button", eventProperties) - else -> {} - } - } - private fun initLoginObserver() { viewModel.loginState.flowWithLifecycle(lifecycle).onEach { state -> when (state) { @@ -78,11 +53,6 @@ class LoginActivity : } is UiState.Success -> { - Timber.e("${state.data?.userId}") - Timber.e("${state.data?.accessToken}") - Timber.e("${state.data?.refreshToken}") - Timber.e("${state.data?.isRegistered}") - if (state.data?.isRegistered == true) { navigateTo() } else { @@ -106,4 +76,28 @@ class LoginActivity : startActivity(this) } } + + private fun sendEventToAmplitude(type: EventType) { + val eventProperties = JSONObject() + + try { + when (type) { + EventType.TYPE_VIEW_SCREEN -> eventProperties.put("screen_name", "sign_up") + EventType.TYPE_CLICK_BUTTON -> { + eventProperties.put("button_name", "kakao_signup_button") + .put("paging_number", 1) + } + else -> {} + } + } catch (e: JSONException) { + System.err.println("Invalid JSON") + e.printStackTrace() + } + + when (type) { + EventType.TYPE_VIEW_SCREEN -> amplitudeUtils.logEvent("view_signup", eventProperties) + EventType.TYPE_CLICK_BUTTON -> amplitudeUtils.logEvent("click_button", eventProperties) + else -> {} + } + } } diff --git a/app/src/main/java/org/go/sopt/winey/presentation/onboarding/login/LoginViewModel.kt b/app/src/main/java/org/go/sopt/winey/presentation/onboarding/login/LoginViewModel.kt index ad367e93..21f30781 100644 --- a/app/src/main/java/org/go/sopt/winey/presentation/onboarding/login/LoginViewModel.kt +++ b/app/src/main/java/org/go/sopt/winey/presentation/onboarding/login/LoginViewModel.kt @@ -9,9 +9,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import org.go.sopt.winey.data.model.remote.request.RequestLoginDto import org.go.sopt.winey.data.model.remote.response.ResponseLoginDto import org.go.sopt.winey.domain.repository.AuthRepository @@ -28,15 +26,11 @@ class LoginViewModel @Inject constructor( private val authRepository: AuthRepository, private val dataStoreRepository: DataStoreRepository ) : ViewModel() { - private val _isKakaoLogin = MutableStateFlow(false) - val isKakaoLogin = _isKakaoLogin.asStateFlow() - private val _loginState = MutableStateFlow>(UiState.Empty) val loginState: StateFlow> = _loginState.asStateFlow() - val kakaoLoginCallback: (OAuthToken?, Throwable?) -> Unit = { token, error -> + private val kakaoLoginCallback: (OAuthToken?, Throwable?) -> Unit = { token, error -> KakaoLoginCallback { - _isKakaoLogin.value = true Timber.d("액세스토큰 ${token?.accessToken}") Timber.d("리프레시토큰 ${token?.refreshToken}") if (token != null) { @@ -52,17 +46,19 @@ class LoginViewModel @Inject constructor( kakaoLoginRepository.loginKakao(kakaoLoginCallback, context) } - fun postLogin(socialToken: String, socialType: String) { + private fun postLogin(socialToken: String, socialType: String) { viewModelScope.launch { _loginState.value = UiState.Loading authRepository.postLogin(socialToken, RequestLoginDto(socialType)) .onSuccess { response -> - Timber.e("로그인 성공") if (response != null) { + Timber.d("로그인 성공") + Timber.d("액세스 : ${response.accessToken} \n 리프레시 : ${response.refreshToken}") + saveAccessToken(response.accessToken, response.refreshToken) saveUserId(response.userId) - Timber.e("액세스 : ${response.accessToken} , 리프레시 : ${response.refreshToken}") + _loginState.value = UiState.Success(response) } else { Timber.e("response is null") @@ -78,33 +74,21 @@ class LoginViewModel @Inject constructor( } } - fun saveSocialToken(socialAccessToken: String, socialRefreshToken: String) = + private fun saveSocialToken(socialAccessToken: String, socialRefreshToken: String) = viewModelScope.launch(Dispatchers.IO) { dataStoreRepository.saveSocialToken(socialAccessToken, socialRefreshToken) } - suspend fun getSocialToken() = withContext(Dispatchers.IO) { - dataStoreRepository.getSocialAccessToken().first() - } - - fun saveAccessToken(accessToken: String, refreshToken: String) = + private fun saveAccessToken(accessToken: String, refreshToken: String) = viewModelScope.launch(Dispatchers.IO) { dataStoreRepository.saveAccessToken(accessToken, refreshToken) } - fun saveUserId(userId: Int) = + private fun saveUserId(userId: Int) = viewModelScope.launch(Dispatchers.IO) { dataStoreRepository.saveUserId(userId) } - suspend fun getAccessToken() = withContext(Dispatchers.IO) { - dataStoreRepository.getAccessToken().first() - } - - suspend fun getRefreshToken() = withContext(Dispatchers.IO) { - dataStoreRepository.getRefreshToken().first() - } - companion object { private const val KAKAO = "KAKAO" } diff --git a/app/src/main/java/org/go/sopt/winey/presentation/splash/SplashActivity.kt b/app/src/main/java/org/go/sopt/winey/presentation/splash/SplashActivity.kt index 0d0fae9e..96a2bd62 100644 --- a/app/src/main/java/org/go/sopt/winey/presentation/splash/SplashActivity.kt +++ b/app/src/main/java/org/go/sopt/winey/presentation/splash/SplashActivity.kt @@ -111,6 +111,13 @@ class SplashActivity : BindingActivity(R.layout.activity_ } } + private inline fun navigateTo() { + Intent(this, T::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) + startActivity(this) + } + } + private fun checkAppUpdateInfo() { val appUpdateInfoTask = appUpdateManager.appUpdateInfo diff --git a/app/src/main/res/drawable/ic_wineyfeed_notification_new.xml b/app/src/main/res/drawable/ic_wineyfeed_notification_new.xml index db5b80da..05a124d0 100644 --- a/app/src/main/res/drawable/ic_wineyfeed_notification_new.xml +++ b/app/src/main/res/drawable/ic_wineyfeed_notification_new.xml @@ -4,7 +4,7 @@ android:viewportWidth="48" android:viewportHeight="48"> + android:pathData="M29,19m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0" + android:fillColor="#FF5150"/> diff --git a/app/src/main/res/layout/activity_setting.xml b/app/src/main/res/layout/activity_setting.xml new file mode 100644 index 00000000..7a62a270 --- /dev/null +++ b/app/src/main/res/layout/activity_setting.xml @@ -0,0 +1,287 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_my_page.xml b/app/src/main/res/layout/fragment_my_page.xml index 8979f5a4..eadbd331 100644 --- a/app/src/main/res/layout/fragment_my_page.xml +++ b/app/src/main/res/layout/fragment_my_page.xml @@ -26,6 +26,7 @@ app:layout_constraintTop_toTopOf="parent" /> diff --git a/app/src/main/res/layout/fragment_winey_feed.xml b/app/src/main/res/layout/fragment_winey_feed.xml index 4293241b..829fd56d 100644 --- a/app/src/main/res/layout/fragment_winey_feed.xml +++ b/app/src/main/res/layout/fragment_winey_feed.xml @@ -4,6 +4,7 @@ xmlns:tools="http://schemas.android.com/tools"> + @@ -14,17 +15,19 @@ android:layout_height="match_parent"> + android:layout_height="wrap_content" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> - + + + + + + + android:clipToPadding="false" + android:nestedScrollingEnabled="false" + android:orientation="vertical" + android:paddingBottom="50dp" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + tools:listitem="@layout/item_wineyfeed_post" /> - - - - + - diff --git a/app/src/main/res/xml/mypage_noti_agree_motion_scene.xml b/app/src/main/res/xml/mypage_noti_agree_motion_scene.xml index 3a56fd20..835624b8 100644 --- a/app/src/main/res/xml/mypage_noti_agree_motion_scene.xml +++ b/app/src/main/res/xml/mypage_noti_agree_motion_scene.xml @@ -24,12 +24,12 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="4dp" - motion:layout_constraintBottom_toBottomOf="@+id/iv_mypage_switch" - motion:layout_constraintStart_toStartOf="@+id/iv_mypage_switch" - motion:layout_constraintTop_toTopOf="@+id/iv_mypage_switch"/> + motion:layout_constraintBottom_toBottomOf="@+id/iv_setting_switch" + motion:layout_constraintStart_toStartOf="@+id/iv_setting_switch" + motion:layout_constraintTop_toTopOf="@+id/iv_setting_switch"/> + motion:layout_constraintBottom_toBottomOf="@+id/iv_setting_switch" + motion:layout_constraintEnd_toEndOf="@+id/iv_setting_switch" + motion:layout_constraintTop_toTopOf="@+id/iv_setting_switch" />