diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a334df10..b0ca2d8f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -99,15 +99,15 @@ - + (R.layout.activity_main if (isFromAlarm) { - val action = HomeFragmentDirections.actionHomeFragmentToFragmentFamiliar() + val message = intent.getSerializableExtra(MESSAGE) as Message + var action = HomeFragmentDirections.actionHomeFragmentToFragmentFamiliar() + when (message.type) { + BEFORE_MEETING -> { + } + END_MEETING -> { +// val meetingId = message.meetingId +// val participants = message.participants +// action = HomeFragmentDirections.{홈 -> 유저리뷰로 이동하는 navi} + } + RECOMMEND_USER -> { + action = HomeFragmentDirections.actionHomeFragmentToFragmentMyPage() + } + } val navHostFragment = supportFragmentManager.findFragmentById(R.id.fl_main) as NavHostFragment navHostFragment.navController.navigate(action) } @@ -138,7 +152,14 @@ class MainActivity : BindingActivity(R.layout.activity_main } fun getIntent(context: Context, id: Int, isFromAlarm: Boolean = false) = Intent(context, MainActivity::class.java).apply { putExtra("id", id) - putExtra("isFromAlarm", isFromAlarm) + putExtra(IS_FROM_ALARM, isFromAlarm) } + + private const val IS_FROM_ALARM = "isFromAlarm" + private const val MESSAGE = "message" + + private const val BEFORE_MEETING = "BEFORE_MEETING" + private const val END_MEETING = "END_MEETING" + private const val RECOMMEND_USER = "RECOMMEND_USER" } } diff --git a/app/src/main/java/com/teumteum/teumteum/presentation/home/HomeFragment.kt b/app/src/main/java/com/teumteum/teumteum/presentation/home/HomeFragment.kt index 5f32d5e4..394657d1 100644 --- a/app/src/main/java/com/teumteum/teumteum/presentation/home/HomeFragment.kt +++ b/app/src/main/java/com/teumteum/teumteum/presentation/home/HomeFragment.kt @@ -25,6 +25,7 @@ import com.teumteum.teumteum.presentation.group.GroupListUiState import com.teumteum.teumteum.presentation.group.GroupListViewModel import com.teumteum.teumteum.presentation.group.join.GroupDetailActivity import com.teumteum.teumteum.presentation.group.search.SearchActivity +import com.teumteum.teumteum.presentation.notification.AlertsListActivity import com.teumteum.teumteum.util.callback.CustomBackPressedCallback import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.launchIn @@ -80,7 +81,12 @@ class HomeFragment : AppBarMenu.IconStyle( resourceId = R.drawable.ic_bell, useRippleEffect = false, - clickEvent = null + clickEvent = { + Intent(requireActivity(), AlertsListActivity::class.java).apply { + startActivity(this) + (activity as? BindingActivity<*>)?.openActivitySlideAnimation() + } + } ) ) } diff --git a/app/src/main/java/com/teumteum/teumteum/presentation/mypage/setting/viewModel/SettingViewModel.kt b/app/src/main/java/com/teumteum/teumteum/presentation/mypage/setting/viewModel/SettingViewModel.kt index 19881687..51278d8a 100644 --- a/app/src/main/java/com/teumteum/teumteum/presentation/mypage/setting/viewModel/SettingViewModel.kt +++ b/app/src/main/java/com/teumteum/teumteum/presentation/mypage/setting/viewModel/SettingViewModel.kt @@ -142,11 +142,12 @@ class SettingViewModel @Inject constructor( return false } - private val _alarmState = MutableStateFlow(false) + private val _alarmState = MutableStateFlow(settingRepository.getNotification()) val alarmState: StateFlow = _alarmState.asStateFlow() fun onToggleChange(newToggleState: Boolean) { - _alarmState.value = newToggleState + settingRepository.setNotification(newToggleState) + _alarmState.value = settingRepository.getNotification() } private val _settingStatus = MutableStateFlow(SettingStatus.DEFAULT) diff --git a/app/src/main/java/com/teumteum/teumteum/presentation/notification/AlertsListActivity.kt b/app/src/main/java/com/teumteum/teumteum/presentation/notification/AlertsListActivity.kt new file mode 100644 index 00000000..54177ac5 --- /dev/null +++ b/app/src/main/java/com/teumteum/teumteum/presentation/notification/AlertsListActivity.kt @@ -0,0 +1,108 @@ +package com.teumteum.teumteum.presentation.notification + +import android.os.Bundle +import androidx.activity.viewModels +import androidx.core.view.isVisible +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import com.teumteum.base.BindingActivity +import com.teumteum.base.component.appbar.AppBarLayout +import com.teumteum.base.component.appbar.AppBarMenu +import com.teumteum.base.databinding.LayoutCommonAppbarBinding +import com.teumteum.base.util.extension.defaultToast +import com.teumteum.domain.entity.TeumAlert +import com.teumteum.teumteum.R +import com.teumteum.teumteum.databinding.ActivityAlertsListBinding +import com.teumteum.teumteum.presentation.group.GroupListUiState +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach + +@AndroidEntryPoint +class AlertsListActivity + : BindingActivity(R.layout.activity_alerts_list), AppBarLayout { + + private lateinit var adapter: AlertsListAdapter + private val viewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + initAppBarLayout() + initList() + viewModel.getAlerts() + observe() + } + + override val appBarBinding: LayoutCommonAppbarBinding + get() = binding.appBar + + override fun initAppBarLayout() { + setAppBarHeight(48) + + addMenuToLeft( + AppBarMenu.IconStyle( + resourceId = R.drawable.ic_arrow_left_l, + useRippleEffect = false, + clickEvent = ::finish + ) + ) + setAppBarTitleText(R.string.alerts_list_title) + } + + private fun initItems() { + adapter.setItems(listOf( + TeumAlert("틈 채우기", "한별님이 당신을 추천했어요!", RECOMMEND_USER, "2024-01-30T16:33:00", false), + TeumAlert("틈 채우기", "한별님이 당신을 추천했어요!", RECOMMEND_USER, "2024-01-31T16:33:00", false), + TeumAlert("틈 채우기", "한별님이 당신을 추천했어요!", RECOMMEND_USER, "2024-02-10T16:33:00", false), + TeumAlert("틈 채우기", "한별님이 당신을 추천했어요!", RECOMMEND_USER, "2024-02-11T16:33:00", false), + TeumAlert("틈 채우기", "한별님이 당신을 추천했어요!", RECOMMEND_USER, "2024-02-12T09:33:00", false), + TeumAlert("틈 채우기", "한별님이 당신을 추천했어요!", RECOMMEND_USER, "2024-02-12T10:33:00", false) + )) + } + + private fun observe() { + viewModel.alertsData.flowWithLifecycle(lifecycle) + .onEach { + binding.clEmpty.isVisible = it is AlertsListUiState.Empty + binding.tvNoticeEmpty.isVisible = it !is AlertsListUiState.Empty + binding.rvAlertsList.isVisible = it !is AlertsListUiState.Empty + when (it) { + is AlertsListUiState.SetAlerts -> { + adapter.setItems(it.data) + } + is AlertsListUiState.Failure -> { + defaultToast(it.msg) + } + else -> {} + } + }.launchIn(lifecycleScope) + } + + private fun initList() { + adapter = AlertsListAdapter { + when (it.type) { + // 알림 리스트에서 클릭 시 이동하는 정책이 있다면 + BEFORE_MEETING -> { + // 틈틈으로 이동 + // openActivitySlideAnimation() + } + END_MEETING -> { + // 해당 모임 종료 화면으로 이동 + // openActivitySlideAnimation() + } + RECOMMEND_USER -> { + // 마이페이지로 이동 + // openActivitySlideAnimation() + } + } + } + binding.rvAlertsList.adapter = adapter + } + + companion object { + private const val BEFORE_MEETING = "BEFORE_MEETING" + private const val END_MEETING = "END_MEETING" + private const val RECOMMEND_USER = "RECOMMEND_USER" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/teumteum/teumteum/presentation/notification/AlertsListAdapter.kt b/app/src/main/java/com/teumteum/teumteum/presentation/notification/AlertsListAdapter.kt new file mode 100644 index 00000000..018876cb --- /dev/null +++ b/app/src/main/java/com/teumteum/teumteum/presentation/notification/AlertsListAdapter.kt @@ -0,0 +1,80 @@ +package com.teumteum.teumteum.presentation.notification + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.teumteum.base.R.* +import com.teumteum.base.util.extension.setOnSingleClickListener +import com.teumteum.domain.entity.TeumAlert +import com.teumteum.teumteum.databinding.ItemAlertsListBinding +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter + +class AlertsListAdapter(private val itemClick: (TeumAlert) -> (Unit)) : + RecyclerView.Adapter() { + private val alertList = mutableListOf() + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): AlertsListViewHolder { + val binding = ItemAlertsListBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + return AlertsListViewHolder(binding, parent.context, itemClick) + } + + override fun onBindViewHolder(holder: AlertsListViewHolder, position: Int) { + holder.onBind(alertList[position]) + } + + override fun getItemCount(): Int = alertList.size + + fun setItems(newItems: List) { + alertList.clear() + alertList.addAll(newItems.sortedByDescending { + LocalDateTime.parse(it.createdAt, DateTimeFormatter.ISO_LOCAL_DATE_TIME) + }) + notifyDataSetChanged() + } + + class AlertsListViewHolder( + private val binding: ItemAlertsListBinding, + private val context: Context, + private val itemClick: (TeumAlert) -> (Unit) + ) : RecyclerView.ViewHolder(binding.root) { + + fun onBind(item: TeumAlert) { + binding.tvTitle.text = item.title + binding.tvContent.text = item.body + binding.tvTime.text = getTimeDifference(item.createdAt) + + if (!item.isRead) { + binding.root.setBackgroundColor(context.getColor(color.elevation_level01)) + } else { + binding.root.setBackgroundColor(context.getColor(color.transparent)) + } + binding.root.setOnSingleClickListener { + itemClick(item) + } + } + + private fun getTimeDifference(inputDateTime: String): String { + val currentTime = LocalDateTime.now() + val formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME + val inputTime = LocalDateTime.parse(inputDateTime, formatter) + + val diffInMinutes = java.time.Duration.between(inputTime, currentTime).toMinutes() + val diffInHours = java.time.Duration.between(inputTime, currentTime).toHours() + + return when { + diffInMinutes < 60 -> "${diffInMinutes}분 전" + diffInHours < 24 -> "${diffInHours}시간 전" + else -> "${inputTime.monthValue}월 ${inputTime.dayOfMonth}일" + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/teumteum/teumteum/presentation/notification/AlertsViewModel.kt b/app/src/main/java/com/teumteum/teumteum/presentation/notification/AlertsViewModel.kt new file mode 100644 index 00000000..d5df66e9 --- /dev/null +++ b/app/src/main/java/com/teumteum/teumteum/presentation/notification/AlertsViewModel.kt @@ -0,0 +1,40 @@ +package com.teumteum.teumteum.presentation.notification + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.teumteum.domain.entity.TeumAlert +import com.teumteum.domain.repository.UserRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class AlertsViewModel @Inject constructor( + private val repository: UserRepository +) : ViewModel() { + + private val _alertsData = MutableStateFlow(AlertsListUiState.Init) + val alertsData: StateFlow = _alertsData + + fun getAlerts() { + viewModelScope.launch { + repository.getAlerts() + .onSuccess { + if (it.alerts.isEmpty()) _alertsData.value = AlertsListUiState.Empty + else _alertsData.value = AlertsListUiState.SetAlerts(it.alerts) + } + .onFailure { + _alertsData.value = AlertsListUiState.Failure("알림 가져오기 실패") + } + } + } +} + +sealed interface AlertsListUiState { + object Init : AlertsListUiState + object Empty : AlertsListUiState + data class SetAlerts(val data: List) : AlertsListUiState + data class Failure(val msg: String) : AlertsListUiState +} \ No newline at end of file diff --git a/app/src/main/java/com/teumteum/teumteum/presentation/onboarding/AccessLocationActivity.kt b/app/src/main/java/com/teumteum/teumteum/presentation/onboarding/AccessLocationActivity.kt deleted file mode 100644 index 55fb5cf2..00000000 --- a/app/src/main/java/com/teumteum/teumteum/presentation/onboarding/AccessLocationActivity.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.teumteum.teumteum.presentation.onboarding - -import android.content.Intent -import android.os.Bundle -import androidx.activity.result.contract.ActivityResultContracts -import com.teumteum.base.BindingActivity -import com.teumteum.base.util.extension.setOnSingleClickListener -import com.teumteum.teumteum.R -import com.teumteum.teumteum.databinding.ActivityAccessLocationBinding -import com.teumteum.teumteum.presentation.signin.SignInActivity -import com.teumteum.teumteum.util.PermissionUtils - -class AccessLocationActivity - : BindingActivity(R.layout.activity_access_location) { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - initView() - } - - private val locationPermissionRequest = registerForActivityResult( - ActivityResultContracts.RequestMultiplePermissions() - ) { - startActivity(Intent(this@AccessLocationActivity, SignInActivity::class.java)) - openActivitySlideAnimation() - finish() - } - - private fun checkLocationPermission() { - locationPermissionRequest.launch( - arrayOf( - PermissionUtils.ACCESS_FINE_LOCATION, PermissionUtils.ACCESS_COARSE_LOCATION - ) - ) - } - - private fun initView() { - with(binding) { - btnDecline.setOnSingleClickListener { - startActivity(Intent(this@AccessLocationActivity, SignInActivity::class.java)) - } - btnAllow.setOnSingleClickListener { - checkLocationPermission() - } - } - } - - override fun finish() { - super.finish() - closeActivitySlideAnimation() - } - - companion object { - } -} \ No newline at end of file diff --git a/app/src/main/java/com/teumteum/teumteum/presentation/onboarding/OnBoardingActivity.kt b/app/src/main/java/com/teumteum/teumteum/presentation/onboarding/OnBoardingActivity.kt index 52dc6a36..0de16db1 100644 --- a/app/src/main/java/com/teumteum/teumteum/presentation/onboarding/OnBoardingActivity.kt +++ b/app/src/main/java/com/teumteum/teumteum/presentation/onboarding/OnBoardingActivity.kt @@ -2,6 +2,7 @@ package com.teumteum.teumteum.presentation.onboarding import android.content.Intent import android.os.Bundle +import androidx.activity.result.contract.ActivityResultContracts import androidx.recyclerview.widget.RecyclerView import com.google.android.material.tabs.TabLayoutMediator import com.teumteum.base.BindingActivity @@ -12,6 +13,10 @@ import com.teumteum.domain.entity.CommonViewPagerEntity import com.teumteum.teumteum.R import com.teumteum.teumteum.databinding.ActivityOnboardingBinding import com.teumteum.teumteum.presentation.onboarding.adapter.OnBoardingViewPagerAdapter +import com.teumteum.teumteum.presentation.signin.SignInActivity +import com.teumteum.teumteum.util.PermissionUtils +import com.teumteum.teumteum.util.custom.dialog.CommonDialogConfig +import com.teumteum.teumteum.util.custom.dialog.CommonDialogFragment import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint @@ -38,13 +43,42 @@ class OnBoardingActivity setAppBarHeight(48) } + private val locationPermissionRequest = registerForActivityResult( + ActivityResultContracts.RequestMultiplePermissions() + ) { + startActivity(Intent(this@OnBoardingActivity, SignInActivity::class.java)) + openActivitySlideAnimation() + finish() + } + + private fun checkLocationPermission() { + locationPermissionRequest.launch( + arrayOf( + PermissionUtils.ACCESS_FINE_LOCATION, PermissionUtils.ACCESS_COARSE_LOCATION + ) + ) + } + private fun setUpListener() { binding.btnStart.setOnSingleClickListener { - startActivity(Intent(this, AccessLocationActivity::class.java)) - openActivitySlideAnimation() + initDialog() } } + private fun initDialog() { + CommonDialogFragment.newInstance( + commonDialogConfig = CommonDialogConfig( + title = getString(R.string.onboarding_tv_access_location_title), + description = getString(R.string.onboarding_tv_access_location_subtitle), + positiveButtonText = getString(R.string.onboarding_tv_access_location_allow), + negativeButtonText = getString(R.string.onboarding_tv_access_location_decline) + ), + onPositiveButtonClicked = { checkLocationPermission() }, + onNegativeButtonClicked = { startActivity(Intent(this@OnBoardingActivity, SignInActivity::class.java)) + } + ).show(supportFragmentManager, "CommonDialogFragmentTag") + } + private fun initViewPagerItem() { with(viewpagerList) { add( diff --git a/app/src/main/java/com/teumteum/teumteum/presentation/signin/SignInViewModel.kt b/app/src/main/java/com/teumteum/teumteum/presentation/signin/SignInViewModel.kt index 3abd9c3d..ca029067 100644 --- a/app/src/main/java/com/teumteum/teumteum/presentation/signin/SignInViewModel.kt +++ b/app/src/main/java/com/teumteum/teumteum/presentation/signin/SignInViewModel.kt @@ -6,6 +6,7 @@ import com.teumteum.domain.repository.AuthRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import timber.log.Timber import javax.inject.Inject @HiltViewModel @@ -19,23 +20,27 @@ class SignInViewModel @Inject constructor( var oauthId = "" fun updateMemberState(socialLoginResult: SocialLoginResult) { - if (socialLoginResult.messages == null) { - if (socialLoginResult.oauthId.isNullOrEmpty()) { + Timber.tag("teum-login").d("${socialLoginResult}") + if (socialLoginResult.message == null) { + if (!socialLoginResult.accessToken.isNullOrEmpty() && !socialLoginResult.refreshToken.isNullOrEmpty()) { // 기존 회원일 때 repository.setAutoLogin( socialLoginResult.accessToken!!, socialLoginResult.refreshToken!!) _memberState.value = SignInUiState.Success } - else { + else if (!socialLoginResult.oauthId.isNullOrEmpty()){ // 새로 가입해야 할 때 oauthId = socialLoginResult.oauthId!! _memberState.value = SignInUiState.UserNotRegistered } + else { + _memberState.value = SignInUiState.Failure("소셜로그인 실패") + } } else { // 통신 실패 - _memberState.value = SignInUiState.Failure(socialLoginResult.messages!!) + _memberState.value = SignInUiState.Failure(socialLoginResult.message!!) } } diff --git a/app/src/main/java/com/teumteum/teumteum/presentation/signin/SocialWebViewActivity.kt b/app/src/main/java/com/teumteum/teumteum/presentation/signin/SocialWebViewActivity.kt index ef2b230f..b30578f5 100644 --- a/app/src/main/java/com/teumteum/teumteum/presentation/signin/SocialWebViewActivity.kt +++ b/app/src/main/java/com/teumteum/teumteum/presentation/signin/SocialWebViewActivity.kt @@ -18,6 +18,7 @@ import com.teumteum.base.component.appbar.AppBarLayout import com.teumteum.base.component.appbar.AppBarMenu import com.teumteum.base.databinding.LayoutCommonAppbarBinding import com.teumteum.base.util.extension.defaultSnackBar +import com.teumteum.base.util.extension.defaultToast import com.teumteum.data.BuildConfig import com.teumteum.domain.entity.SocialLoginResult import com.teumteum.teumteum.R @@ -50,7 +51,7 @@ class SocialWebViewActivity provider = intent.getStringExtra(EXTRA_KEY_PROVIDER).toString() initProvider(provider) - initCookieManager() +// initCookieManager() initAppBarLayout() initWebView() observer() @@ -175,7 +176,7 @@ class SocialWebViewActivity goToTermsActivity() } is SignInUiState.Failure -> { - defaultSnackBar(binding.root, it.msg) + defaultToast(it.msg) finish() } @@ -192,7 +193,7 @@ class SocialWebViewActivity goToHomeScreen() } is MyInfoUiState.Failure -> { - defaultSnackBar(binding.root, state.msg) + defaultToast(state.msg) goToTermsActivity() } else -> {} diff --git a/app/src/main/java/com/teumteum/teumteum/presentation/signup/SignUpViewModel.kt b/app/src/main/java/com/teumteum/teumteum/presentation/signup/SignUpViewModel.kt index 4c7d352b..43625b0c 100644 --- a/app/src/main/java/com/teumteum/teumteum/presentation/signup/SignUpViewModel.kt +++ b/app/src/main/java/com/teumteum/teumteum/presentation/signup/SignUpViewModel.kt @@ -204,6 +204,15 @@ class SignUpViewModel @Inject constructor( _interestSelf.value = ArrayList(interests) } + fun setAllInterests(interests: List, selfResource: Array, fieldResource: Array) { + interestSelf.value.clear() + interestField.value.clear() + for (i in interests) { + if (i in selfResource) addInterestSelf(i) + else if (i in fieldResource) addInterestField(i) + } + } + private var _goalText = MutableStateFlow("") val goalText: StateFlow = _goalText.asStateFlow() diff --git a/app/src/main/java/com/teumteum/teumteum/presentation/signup/complete/CardCompleteFragment.kt b/app/src/main/java/com/teumteum/teumteum/presentation/signup/complete/CardCompleteFragment.kt index 91f0f75e..9d853b57 100644 --- a/app/src/main/java/com/teumteum/teumteum/presentation/signup/complete/CardCompleteFragment.kt +++ b/app/src/main/java/com/teumteum/teumteum/presentation/signup/complete/CardCompleteFragment.kt @@ -55,16 +55,17 @@ class CardCompleteFragment val interests = mutableListOf() for (i in interestField.value) { - interests.add(Interest("#$i")) + interests.add(Interest(i)) } for (i in interestSelf.value) { - interests.add(Interest("#$i")) + interests.add(Interest(i)) } binding.cardviewBack.apply { tvGoalContent.text = goalText.value CHARACTER_CARD_LIST_BACK[characterId.value]?.let { ivCharacter.setImageResource(it) } submitInterestList(interests) isModify = false + setIsModifyDetail(false) // isModifyDetail = false } } diff --git a/app/src/main/java/com/teumteum/teumteum/presentation/signup/fix/CardFixFragment.kt b/app/src/main/java/com/teumteum/teumteum/presentation/signup/fix/CardFixFragment.kt index 4bbbae7c..aded80ff 100644 --- a/app/src/main/java/com/teumteum/teumteum/presentation/signup/fix/CardFixFragment.kt +++ b/app/src/main/java/com/teumteum/teumteum/presentation/signup/fix/CardFixFragment.kt @@ -65,12 +65,13 @@ class CardFixFragment val interests = mutableListOf() for (i in interestField.value) { - interests.add(Interest("#$i")) + interests.add(Interest(i)) } for (i in interestSelf.value) { - interests.add(Interest("#$i")) + interests.add(Interest(i)) } binding.cardviewBack.apply { + setIsModifyDetail(true) tvGoalContent.text = goalText.value SignupUtils.CHARACTER_CARD_LIST_BACK[characterId.value]?.let { ivCharacter.setImageResource(it) } submitInterestList(interests) @@ -182,12 +183,17 @@ class CardFixFragment navigateTo() } } - rvInterests.setOnSingleClickListener { + interestAdapter.onAddItemClick = { (activity as SignUpActivity).apply { showNextButtonOnFixingField() navigateTo() } } + currentList.observe(viewLifecycleOwner) { interests -> + val selfArray = resources.getStringArray(R.array.interest_1) + val fieldArray = resources.getStringArray(R.array.interest_2) + viewModel.setAllInterests(interests.map { it.toString() }, selfArray, fieldArray) + } } } diff --git a/app/src/main/java/com/teumteum/teumteum/presentation/signup/intro/CardIntroActivity.kt b/app/src/main/java/com/teumteum/teumteum/presentation/signup/intro/CardIntroActivity.kt index 234bcbfe..a3e9d15c 100644 --- a/app/src/main/java/com/teumteum/teumteum/presentation/signup/intro/CardIntroActivity.kt +++ b/app/src/main/java/com/teumteum/teumteum/presentation/signup/intro/CardIntroActivity.kt @@ -76,6 +76,7 @@ class CardIntroActivity ) binding.cardviewBack.submitInterestList(interests) binding.cardviewBack.isModify = false + binding.cardviewBack.setIsModifyDetail(false) // binding.cardviewBack.isModifyDetail = false } } @@ -119,8 +120,8 @@ class CardIntroActivity } companion object { - const val INTEREST_EXAMPLE_1 = "#사이드 프로젝트" - const val INTEREST_EXAMPLE_2 = "#네트워킹" - const val INTEREST_EXAMPLE_3 = "#모여서 각자 일하기" + const val INTEREST_EXAMPLE_1 = "사이드 프로젝트" + const val INTEREST_EXAMPLE_2 = "네트워킹" + const val INTEREST_EXAMPLE_3 = "모여서 각자 일하기" } } \ No newline at end of file diff --git a/app/src/main/java/com/teumteum/teumteum/presentation/splash/SplashActivity.kt b/app/src/main/java/com/teumteum/teumteum/presentation/splash/SplashActivity.kt index 3368a7d6..7685b0c2 100644 --- a/app/src/main/java/com/teumteum/teumteum/presentation/splash/SplashActivity.kt +++ b/app/src/main/java/com/teumteum/teumteum/presentation/splash/SplashActivity.kt @@ -1,6 +1,7 @@ package com.teumteum.teumteum.presentation.splash import android.content.Intent +import android.os.Build import android.os.Bundle import android.os.Handler import android.os.Looper @@ -9,6 +10,7 @@ import androidx.appcompat.app.AlertDialog import androidx.lifecycle.lifecycleScope import com.teumteum.base.BindingActivity import com.teumteum.base.util.extension.defaultSnackBar +import com.teumteum.domain.entity.Message import com.teumteum.teumteum.R import com.teumteum.teumteum.databinding.ActivitySplashBinding import com.teumteum.teumteum.presentation.MainActivity @@ -23,14 +25,22 @@ class SplashActivity private val viewModel by viewModels() private var isFromAlarm = false + private var message = Message("", "", "") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) isFromAlarm = intent.getBooleanExtra(IS_FROM_ALARM, false) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) getMessage() checkNetwork() } + private fun getMessage() { + if (isFromAlarm) { + message = intent.getSerializableExtra(MESSAGE) as Message + } + } + private fun checkNetwork() { if (NetworkManager.checkNetworkState(this)) checkAutoLogin() else { @@ -98,7 +108,9 @@ class SplashActivity } private fun startHomeScreen() { - startActivity(MainActivity.getIntent(this, -1, isFromAlarm = isFromAlarm)) + val intent = MainActivity.getIntent(this, -1, isFromAlarm = isFromAlarm) + if (isFromAlarm) intent.putExtra(MESSAGE, message) + startActivity(intent) finish() } @@ -108,5 +120,6 @@ class SplashActivity const val HAVE_TO_SIGN_IN = 2 const val IS_FROM_ALARM = "isFromAlarm" + const val MESSAGE = "message" } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_alerts_list.xml b/app/src/main/res/layout/activity_alerts_list.xml new file mode 100644 index 00000000..759e9a94 --- /dev/null +++ b/app/src/main/res/layout/activity_alerts_list.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_common_dialog.xml b/app/src/main/res/layout/fragment_common_dialog.xml index f2932000..0fa9adf7 100644 --- a/app/src/main/res/layout/fragment_common_dialog.xml +++ b/app/src/main/res/layout/fragment_common_dialog.xml @@ -72,7 +72,7 @@ android:padding="12dp" android:stateListAnimator="@null" android:text="@{dialogConfig.negativeButtonText}" - android:textColor="@color/text_button_primary_default" + android:textColor="@color/text_button_alternative" android:textSize="15sp" app:layout_constraintBottom_toBottomOf="@id/btn_common_dialog_positive" app:layout_constraintEnd_toStartOf="@id/btn_common_dialog_positive" diff --git a/app/src/main/res/layout/item_alerts_list.xml b/app/src/main/res/layout/item_alerts_list.xml new file mode 100644 index 00000000..5f1ffb40 --- /dev/null +++ b/app/src/main/res/layout/item_alerts_list.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4f2fb711..9d4c1658 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -340,5 +340,8 @@ teum_notification_channel #%1$s + 알림 + 알림이 아직 없어요 + 최대 한 달 전까지의 알림을 확인할 수 있어요 \ No newline at end of file diff --git a/core/data/src/main/java/com/teumteum/data/datasource/remote/RemoteUserDataSource.kt b/core/data/src/main/java/com/teumteum/data/datasource/remote/RemoteUserDataSource.kt index 484ac2d6..da4901a2 100644 --- a/core/data/src/main/java/com/teumteum/data/datasource/remote/RemoteUserDataSource.kt +++ b/core/data/src/main/java/com/teumteum/data/datasource/remote/RemoteUserDataSource.kt @@ -3,6 +3,7 @@ package com.teumteum.data.datasource.remote import com.teumteum.data.model.request.RequestDeviceToken import com.teumteum.data.model.request.RequestUserInfoWithOAuthId import com.teumteum.data.service.UserService +import com.teumteum.domain.entity.Alerts import com.teumteum.domain.entity.Friend import com.teumteum.domain.entity.FriendRecommend import com.teumteum.domain.entity.SignUpResult @@ -59,4 +60,8 @@ class RemoteUserDataSource @Inject constructor( suspend fun patchDeviceToken(token: RequestDeviceToken): Boolean { return service.patchDeviceToken(token).isSuccessful } + + suspend fun getAlerts(): Alerts { + return service.getAlerts() + } } \ No newline at end of file diff --git a/core/data/src/main/java/com/teumteum/data/local/TeumTeumDataStoreImpl.kt b/core/data/src/main/java/com/teumteum/data/local/TeumTeumDataStoreImpl.kt index 3d9f5e1d..98d79a96 100644 --- a/core/data/src/main/java/com/teumteum/data/local/TeumTeumDataStoreImpl.kt +++ b/core/data/src/main/java/com/teumteum/data/local/TeumTeumDataStoreImpl.kt @@ -37,6 +37,10 @@ class TeumTeumDataStoreImpl @Inject constructor( get() = userPref.getBoolean(PREF_ASKED_NOTIFICATION, false) set(value) = userPref.edit { putBoolean(PREF_ASKED_NOTIFICATION, value)} + override var onNotification: Boolean + get() = userPref.getBoolean(PREF_ON_NOTIFICATION, true) + set(value) = userPref.edit { putBoolean(PREF_ON_NOTIFICATION, value)} + override fun clearLocalPref() = userPref.edit { clear() } companion object { @@ -47,5 +51,6 @@ class TeumTeumDataStoreImpl @Inject constructor( private const val PREF_IS_FIRST_AFTER_INSTALL = "IS_FIRST_AFTER_INSTALL" private const val PREF_DEVICE_TOKEN = "DEVICE_TOKEN" private const val PREF_ASKED_NOTIFICATION = "ASKED_NOTIFICATION" + private const val PREF_ON_NOTIFICATION = "ON_NOTIFICATION" } } diff --git a/core/data/src/main/java/com/teumteum/data/repository/SettingRepositoryImpl.kt b/core/data/src/main/java/com/teumteum/data/repository/SettingRepositoryImpl.kt index a4cc5d8c..e1395815 100644 --- a/core/data/src/main/java/com/teumteum/data/repository/SettingRepositoryImpl.kt +++ b/core/data/src/main/java/com/teumteum/data/repository/SettingRepositoryImpl.kt @@ -9,7 +9,8 @@ import com.teumteum.domain.repository.SettingRepository import javax.inject.Inject class SettingRepositoryImpl @Inject constructor( - private val dataSource: RemoteSettingDataSource + private val dataSource: RemoteSettingDataSource, + private val dataStore: TeumTeumDataStore ): SettingRepository { override suspend fun logOut(): Result { return runCatching { @@ -47,5 +48,10 @@ class SettingRepositoryImpl @Inject constructor( } } + override fun setNotification(isActivated: Boolean) { + dataStore.onNotification = isActivated + } + + override fun getNotification(): Boolean = dataStore.onNotification } \ No newline at end of file diff --git a/core/data/src/main/java/com/teumteum/data/repository/UserRepositoryImpl.kt b/core/data/src/main/java/com/teumteum/data/repository/UserRepositoryImpl.kt index 11ef331c..ac565ba2 100644 --- a/core/data/src/main/java/com/teumteum/data/repository/UserRepositoryImpl.kt +++ b/core/data/src/main/java/com/teumteum/data/repository/UserRepositoryImpl.kt @@ -5,6 +5,7 @@ import com.google.gson.GsonBuilder import com.teumteum.domain.TeumTeumDataStore import com.teumteum.data.datasource.remote.RemoteUserDataSource import com.teumteum.data.model.request.RequestUserInfo +import com.teumteum.domain.entity.Alerts import com.teumteum.domain.entity.Friend import com.teumteum.domain.entity.FriendMyPage import com.teumteum.domain.entity.FriendRecommend @@ -101,4 +102,9 @@ class UserRepositoryImpl @Inject constructor( } } + override suspend fun getAlerts(): Result { + return runCatching { + dataSource.getAlerts() + } + } } \ No newline at end of file diff --git a/core/data/src/main/java/com/teumteum/data/service/UserService.kt b/core/data/src/main/java/com/teumteum/data/service/UserService.kt index 7264e55d..490dc399 100644 --- a/core/data/src/main/java/com/teumteum/data/service/UserService.kt +++ b/core/data/src/main/java/com/teumteum/data/service/UserService.kt @@ -2,6 +2,7 @@ package com.teumteum.data.service import com.teumteum.data.model.request.RequestDeviceToken import com.teumteum.data.model.request.RequestUserInfoWithOAuthId +import com.teumteum.domain.entity.Alerts import com.teumteum.domain.entity.Friend import com.teumteum.domain.entity.FriendRecommend import com.teumteum.domain.entity.SignUpResult @@ -65,5 +66,8 @@ interface UserService { suspend fun patchDeviceToken( @Body token: RequestDeviceToken ): Response + + @GET("alerts") + suspend fun getAlerts(): Alerts } diff --git a/core/domain/src/main/java/com/teumteum/domain/TeumTeumDataStore.kt b/core/domain/src/main/java/com/teumteum/domain/TeumTeumDataStore.kt index 7953fc9a..7aba189b 100644 --- a/core/domain/src/main/java/com/teumteum/domain/TeumTeumDataStore.kt +++ b/core/domain/src/main/java/com/teumteum/domain/TeumTeumDataStore.kt @@ -9,6 +9,7 @@ interface TeumTeumDataStore { var userInfo: String var deviceToken: String var askedNotification: Boolean + var onNotification: Boolean fun clearLocalPref() } diff --git a/core/domain/src/main/java/com/teumteum/domain/entity/Alerts.kt b/core/domain/src/main/java/com/teumteum/domain/entity/Alerts.kt new file mode 100644 index 00000000..9e5c33d7 --- /dev/null +++ b/core/domain/src/main/java/com/teumteum/domain/entity/Alerts.kt @@ -0,0 +1,8 @@ +package com.teumteum.domain.entity + +import kotlinx.serialization.Serializable + +@Serializable +data class Alerts( + val alerts: List +) diff --git a/core/domain/src/main/java/com/teumteum/domain/entity/Message.kt b/core/domain/src/main/java/com/teumteum/domain/entity/Message.kt new file mode 100644 index 00000000..a11f403d --- /dev/null +++ b/core/domain/src/main/java/com/teumteum/domain/entity/Message.kt @@ -0,0 +1,9 @@ +package com.teumteum.domain.entity + +data class Message( + var title: String, + var body: String, + var type: String, + var meetingId: Int? = -1, + var participants: List? = listOf() +): java.io.Serializable \ No newline at end of file diff --git a/core/domain/src/main/java/com/teumteum/domain/entity/SocialLoginResult.kt b/core/domain/src/main/java/com/teumteum/domain/entity/SocialLoginResult.kt index f2eaa1a5..72278c14 100644 --- a/core/domain/src/main/java/com/teumteum/domain/entity/SocialLoginResult.kt +++ b/core/domain/src/main/java/com/teumteum/domain/entity/SocialLoginResult.kt @@ -7,5 +7,5 @@ data class SocialLoginResult ( val accessToken: String?, val refreshToken: String?, val oauthId: String?, - val messages: String? + val message: String? ) \ No newline at end of file diff --git a/core/domain/src/main/java/com/teumteum/domain/entity/TeumAlert.kt b/core/domain/src/main/java/com/teumteum/domain/entity/TeumAlert.kt new file mode 100644 index 00000000..18ad702c --- /dev/null +++ b/core/domain/src/main/java/com/teumteum/domain/entity/TeumAlert.kt @@ -0,0 +1,12 @@ +package com.teumteum.domain.entity + +import kotlinx.serialization.Serializable + +@Serializable +data class TeumAlert ( + val title: String, + val body: String, + val type: String, + val createdAt: String, + val isRead: Boolean +) \ No newline at end of file diff --git a/core/domain/src/main/java/com/teumteum/domain/repository/SettingRepository.kt b/core/domain/src/main/java/com/teumteum/domain/repository/SettingRepository.kt index 0c979cf2..1bdd98cb 100644 --- a/core/domain/src/main/java/com/teumteum/domain/repository/SettingRepository.kt +++ b/core/domain/src/main/java/com/teumteum/domain/repository/SettingRepository.kt @@ -8,4 +8,6 @@ interface SettingRepository { suspend fun signOut(withDrawReasons: WithDrawReasons): Result suspend fun getMyPageOpenMeeting(participantUserId: Long): Result> suspend fun getMyPageClosedMeeting(participantUserId: Long): Result> + fun setNotification(isActivated: Boolean) + fun getNotification(): Boolean } \ No newline at end of file diff --git a/core/domain/src/main/java/com/teumteum/domain/repository/UserRepository.kt b/core/domain/src/main/java/com/teumteum/domain/repository/UserRepository.kt index 5590b65d..0872547e 100644 --- a/core/domain/src/main/java/com/teumteum/domain/repository/UserRepository.kt +++ b/core/domain/src/main/java/com/teumteum/domain/repository/UserRepository.kt @@ -1,5 +1,6 @@ package com.teumteum.domain.repository +import com.teumteum.domain.entity.Alerts import com.teumteum.domain.entity.Friend import com.teumteum.domain.entity.FriendRecommend import com.teumteum.domain.entity.Friends @@ -25,6 +26,5 @@ interface UserRepository { suspend fun updateUserInfo(user: UserInfo): Result suspend fun getFriendInfo(userId: Long): UserInfo? suspend fun postFriend(userId: Long): Result - - + suspend fun getAlerts(): Result } \ No newline at end of file