Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AN] feat: 놀이터 온보딩 UI 구현 #775

Merged
merged 7 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@
android:exported="false"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize" />
<activity android:name=".presentation.ui.playground.onboarding.OnboardingActivity"
android:exported="false"/>

<activity
android:name=".presentation.ui.recentpet.RecentPetActivity"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.happy.friendogly.data.source.KakaoLoginDataSource
import com.happy.friendogly.data.source.MemberDataSource
import com.happy.friendogly.data.source.MessagingDataSource
import com.happy.friendogly.data.source.MyClubDataSource
import com.happy.friendogly.data.source.OnboardingDataSource
import com.happy.friendogly.data.source.PetDataSource
import com.happy.friendogly.data.source.PlaygroundDataSource
import com.happy.friendogly.data.source.RecentPetsDataSource
Expand All @@ -20,6 +21,7 @@ import com.happy.friendogly.firebase.source.MessagingDataSourceImpl
import com.happy.friendogly.kakao.source.KakaoLoginDataSourceImpl
import com.happy.friendogly.local.source.AddressDataSourceImpl
import com.happy.friendogly.local.source.AlarmSettingDataSourceImpl
import com.happy.friendogly.local.source.OnboardingDataSourceImpl
import com.happy.friendogly.local.source.RecentPetsDataSourceImpl
import com.happy.friendogly.local.source.TokenDataSourceImpl
import com.happy.friendogly.remote.source.AlamTokenDataSourceImpl
Expand Down Expand Up @@ -104,4 +106,8 @@ abstract class DataSourceModule {
@Binds
@Singleton
abstract fun bindsRecentPetsDataSource(dataSourceImpl: RecentPetsDataSourceImpl): RecentPetsDataSource

@Binds
@Singleton
abstract fun bindOnboardingDataSource(dataSourceImpl: OnboardingDataSourceImpl): OnboardingDataSource
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.Context
import com.happy.friendogly.local.di.AddressModule
import com.happy.friendogly.local.di.AlarmTokenModule
import com.happy.friendogly.local.di.ChatAlarmModule
import com.happy.friendogly.local.di.OnboardingModule
import com.happy.friendogly.local.di.PlaygroundAlarmModule
import com.happy.friendogly.local.di.TokenManager
import dagger.Module
Expand Down Expand Up @@ -45,4 +46,10 @@ object DataStoreModule {
fun providesPlaygroundAlarmModule(
@ApplicationContext appContext: Context,
): PlaygroundAlarmModule = PlaygroundAlarmModule(appContext)

@Provides
@Singleton
fun providesOnboardingModule(
@ApplicationContext appContext: Context,
): OnboardingModule = OnboardingModule(appContext)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.happy.friendogly.data.repository.KakaoLoginRepositoryImpl
import com.happy.friendogly.data.repository.MemberRepositoryImpl
import com.happy.friendogly.data.repository.MessagingRepositoryImpl
import com.happy.friendogly.data.repository.MyClubRepositoryImpl
import com.happy.friendogly.data.repository.OnboardingRepositoryImpl
import com.happy.friendogly.data.repository.PetRepositoryImpl
import com.happy.friendogly.data.repository.PlaygroundRepositoryImpl
import com.happy.friendogly.data.repository.RecentPetsRepositoryImpl
Expand All @@ -28,6 +29,7 @@ import com.happy.friendogly.domain.repository.KakaoLoginRepository
import com.happy.friendogly.domain.repository.MemberRepository
import com.happy.friendogly.domain.repository.MessagingRepository
import com.happy.friendogly.domain.repository.MyClubRepository
import com.happy.friendogly.domain.repository.OnboardingRepository
import com.happy.friendogly.domain.repository.PetRepository
import com.happy.friendogly.domain.repository.PlaygroundRepository
import com.happy.friendogly.domain.repository.RecentPetsRepository
Expand Down Expand Up @@ -110,4 +112,8 @@ abstract class RepositoryModule {
@Binds
@Singleton
abstract fun bindsRecentPetsRepository(repositoryImpl: RecentPetsRepositoryImpl): RecentPetsRepository

@Binds
@Singleton
abstract fun bindOnboardingRepository(repositoryImpl: OnboardingRepositoryImpl): OnboardingRepository
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.happy.friendogly.data.repository

import com.happy.friendogly.data.source.OnboardingDataSource
import com.happy.friendogly.domain.repository.OnboardingRepository
import javax.inject.Inject

class OnboardingRepositoryImpl
@Inject
constructor(
private val source: OnboardingDataSource,
) : OnboardingRepository {
override suspend fun isShown(): Result<Boolean> = source.isShown()

override suspend fun setShown(isShown: Boolean): Result<Unit> = source.setShown(isShown)

override suspend fun deleteIsShown(): Result<Unit> = source.deleteShown()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.happy.friendogly.data.source

interface OnboardingDataSource {
suspend fun isShown(): Result<Boolean>

suspend fun setShown(isShown: Boolean): Result<Unit>

suspend fun deleteShown(): Result<Unit>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.happy.friendogly.domain.repository

interface OnboardingRepository {
suspend fun isShown(): Result<Boolean>

suspend fun setShown(isShown: Boolean): Result<Unit>

suspend fun deleteIsShown(): Result<Unit>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.happy.friendogly.domain.usecase

import com.happy.friendogly.domain.repository.OnboardingRepository
import javax.inject.Inject

class DeleteOnboardingShownUseCase
@Inject
constructor(
private val repository: OnboardingRepository,
) {
suspend operator fun invoke(): Result<Unit> = repository.deleteIsShown()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.happy.friendogly.domain.usecase

import com.happy.friendogly.domain.repository.OnboardingRepository
import javax.inject.Inject

class GetOnboardingShownUseCase
@Inject
constructor(
private val repository: OnboardingRepository,
) {
suspend operator fun invoke(): Result<Boolean> = repository.isShown()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.happy.friendogly.domain.usecase

import com.happy.friendogly.domain.repository.OnboardingRepository
import javax.inject.Inject

class SetOnboardingShownUseCase
@Inject
constructor(
private val repository: OnboardingRepository,
) {
suspend operator fun invoke(isShown: Boolean): Result<Unit> = repository.setShown(isShown)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.happy.friendogly.local.di

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.emptyPreferences
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.map
import java.io.IOException

class OnboardingModule(val context: Context) {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = DATA_STORE_NAME)

private val key = booleanPreferencesKey(IS_ONBOARDING)

var isShown: Flow<Boolean> =
context.dataStore.data.catch { exception ->
if (exception is IOException) {
emit(emptyPreferences())
} else {
throw exception
}
}.map { preferences ->
preferences[key] ?: false
}

suspend fun setShown(value: Boolean) {
context.dataStore.edit { preferences ->
preferences[key] = value
}
}

suspend fun deleteIsShown() {
context.dataStore.edit { prefs ->
prefs.remove(key)
}
}

companion object {
private const val IS_ONBOARDING = "IS_ONBOARDING_SHOWN"
private const val DATA_STORE_NAME = "onboardingDataStore"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.happy.friendogly.local.source

import com.happy.friendogly.data.source.OnboardingDataSource
import com.happy.friendogly.local.di.OnboardingModule
import kotlinx.coroutines.flow.first
import javax.inject.Inject

class OnboardingDataSourceImpl
@Inject
constructor(
private val onboardingModule: OnboardingModule,
) : OnboardingDataSource {
override suspend fun isShown(): Result<Boolean> =
runCatching {
onboardingModule.isShown.first()
}

override suspend fun setShown(isShown: Boolean): Result<Unit> =
runCatching {
onboardingModule.setShown(isShown)
}

override suspend fun deleteShown(): Result<Unit> =
runCatching {
onboardingModule.deleteIsShown()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import android.content.Context
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.happy.friendogly.R
import com.happy.friendogly.databinding.ActivityMainBinding
import com.happy.friendogly.domain.usecase.GetOnboardingShownUseCase
import com.happy.friendogly.domain.usecase.SetOnboardingShownUseCase
import com.happy.friendogly.firebase.analytics.AnalyticsHelper
import com.happy.friendogly.presentation.base.BaseActivity
import com.happy.friendogly.presentation.ui.chatlist.ChatListFragment
Expand All @@ -19,6 +22,7 @@ import com.happy.friendogly.presentation.ui.permission.MultiPermission
import com.happy.friendogly.presentation.ui.petdetail.PetDetailActivity
import com.happy.friendogly.presentation.ui.petdetail.PetsDetail
import com.happy.friendogly.presentation.ui.playground.PlaygroundFragment
import com.happy.friendogly.presentation.ui.playground.onboarding.OnboardingActivity
import com.happy.friendogly.presentation.ui.profilesetting.ProfileSettingActivity
import com.happy.friendogly.presentation.ui.profilesetting.model.Profile
import com.happy.friendogly.presentation.ui.recentpet.RecentPetActivity
Expand All @@ -31,6 +35,7 @@ import com.happy.friendogly.presentation.utils.logMyClubClick
import com.happy.friendogly.presentation.utils.logMyPageFragmentSwitched
import com.happy.friendogly.presentation.utils.logPlaygroundFragmentSwitched
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import javax.inject.Inject

@AndroidEntryPoint
Expand All @@ -40,6 +45,12 @@ class MainActivity :
@Inject
lateinit var analyticsHelper: AnalyticsHelper

@Inject
lateinit var setOnboardingShownUseCase: SetOnboardingShownUseCase

@Inject
lateinit var getOnboardingShownUseCase: GetOnboardingShownUseCase

private lateinit var permission: MultiPermission

override fun initCreateView() {
Expand Down Expand Up @@ -83,17 +94,20 @@ class MainActivity :
ClubListFragment.TAG,
)

R.id.playgroundFragment ->
R.id.playgroundFragment -> {
showOnboarding()
switchFragment(
PlaygroundFragment::class.java,
PlaygroundFragment.TAG,
)
}

R.id.chatListFragment ->
R.id.chatListFragment -> {
switchFragment(
ChatListFragment::class.java,
ChatListFragment.TAG,
)
}

R.id.myPageFragment ->
switchFragment(
Expand All @@ -106,6 +120,15 @@ class MainActivity :
}
}

private fun showOnboarding() {
lifecycleScope.launch {
if (!getOnboardingShownUseCase().getOrDefault(false)) {
startActivity(Intent(this@MainActivity, OnboardingActivity::class.java))
setOnboardingShownUseCase(true)
}
}
}

private fun switchFragment(
fragmentClass: Class<out Fragment>,
tag: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.happy.friendogly.presentation.ui.playground.onboarding

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.tabs.TabLayoutMediator
import com.happy.friendogly.databinding.ActivityOnboardingBinding

class OnboardingActivity : AppCompatActivity() {
lateinit var binding: ActivityOnboardingBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityOnboardingBinding.inflate(layoutInflater)
setContentView(binding.root)
initOnboardingPager()
}

private fun initOnboardingPager() {
binding.vpDescExplain.adapter = OnboardingAdapter(this)
TabLayoutMediator(binding.tlDescIndicator, binding.vpDescExplain) { tab, position ->
}.attach()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.happy.friendogly.presentation.ui.playground.onboarding

import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter

class OnboardingAdapter(
fragmentActivity: FragmentActivity,
) : FragmentStateAdapter(fragmentActivity) {
override fun getItemCount(): Int = 3

override fun createFragment(position: Int): Fragment {
return OnboardingFragment().apply {
arguments = OnboardingFragment.getBundle(position)
}
}
}
Loading
Loading