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: FCM 구현 #312

Merged
merged 16 commits into from
Aug 15, 2024
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
10 changes: 9 additions & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@

<application
android:name=".application.FriendoglyApplication"
android:allowBackup="true"
android:allowBackup="false"
tools:replace="android:allowBackup"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
Expand Down Expand Up @@ -100,6 +101,13 @@
android:scheme="@string/KAKAO_OAUTH_HOST" />
</intent-filter>
</activity>
<service android:name=".presentation.alarm.AlarmReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />

</intent-filter>
</service>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.happy.friendogly.application

import android.app.Application
import com.google.firebase.FirebaseApp
import com.happy.friendogly.BuildConfig
import com.happy.friendogly.application.di.AppModule
import com.kakao.sdk.common.KakaoSdk
Expand All @@ -13,5 +14,6 @@ class FriendoglyApplication : Application() {
NaverMapSdk.NaverCloudPlatformClient(BuildConfig.NAVER_CLIEND_ID)
KakaoSdk.init(this, BuildConfig.KAKAO_NATIVE_APP_KEY)
AppModule.setInstance(applicationContext)
FirebaseApp.initializeApp(this)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import com.happy.friendogly.BuildConfig
import com.happy.friendogly.analytics.AnalyticsHelper
import com.happy.friendogly.crashlytics.CrashlyticsHelper
import com.happy.friendogly.data.repository.AddressRepositoryImpl
import com.happy.friendogly.data.repository.AlarmSettingRepositoryImpl
import com.happy.friendogly.data.repository.AlarmTokenRepositoryImpl
import com.happy.friendogly.data.repository.AuthRepositoryImpl
import com.happy.friendogly.data.repository.ChatRepositoryImpl
import com.happy.friendogly.data.repository.ClubRepositoryImpl
Expand All @@ -16,6 +18,8 @@ import com.happy.friendogly.data.repository.TokenRepositoryImpl
import com.happy.friendogly.data.repository.WebSocketRepositoryImpl
import com.happy.friendogly.data.repository.WoofRepositoryImpl
import com.happy.friendogly.data.source.AddressDataSource
import com.happy.friendogly.data.source.AlarmSettingDataSource
import com.happy.friendogly.data.source.AlarmTokenDataSource
import com.happy.friendogly.data.source.AuthDataSource
import com.happy.friendogly.data.source.ChatDataSource
import com.happy.friendogly.data.source.ClubDataSource
Expand All @@ -26,6 +30,8 @@ import com.happy.friendogly.data.source.TokenDataSource
import com.happy.friendogly.data.source.WebSocketDataSource
import com.happy.friendogly.data.source.WoofDataSource
import com.happy.friendogly.domain.repository.AddressRepository
import com.happy.friendogly.domain.repository.AlarmSettingRepository
import com.happy.friendogly.domain.repository.AlarmTokenRepository
import com.happy.friendogly.domain.repository.AuthRepository
import com.happy.friendogly.domain.repository.ChatRepository
import com.happy.friendogly.domain.repository.ClubRepository
Expand All @@ -37,9 +43,11 @@ import com.happy.friendogly.domain.repository.TokenRepository
import com.happy.friendogly.domain.repository.WebSocketRepository
import com.happy.friendogly.domain.repository.WoofRepository
import com.happy.friendogly.domain.usecase.DeleteAddressUseCase
import com.happy.friendogly.domain.usecase.DeleteAlarmSettingUseCase
import com.happy.friendogly.domain.usecase.DeleteClubMemberUseCase
import com.happy.friendogly.domain.usecase.DeleteTokenUseCase
import com.happy.friendogly.domain.usecase.GetAddressUseCase
import com.happy.friendogly.domain.usecase.GetAlarmSettingUseCase
import com.happy.friendogly.domain.usecase.GetChatListUseCase
import com.happy.friendogly.domain.usecase.GetChatMemberUseCase
import com.happy.friendogly.domain.usecase.GetClubUseCase
Expand All @@ -66,17 +74,23 @@ import com.happy.friendogly.domain.usecase.PublishEnterUseCase
import com.happy.friendogly.domain.usecase.PublishLeaveUseCase
import com.happy.friendogly.domain.usecase.PublishSendMessageUseCase
import com.happy.friendogly.domain.usecase.SaveAddressUseCase
import com.happy.friendogly.domain.usecase.SaveAlamTokenUseCase
import com.happy.friendogly.domain.usecase.SaveAlarmSettingUseCase
import com.happy.friendogly.domain.usecase.SaveJwtTokenUseCase
import com.happy.friendogly.domain.usecase.SubScribeMessageUseCase
import com.happy.friendogly.kakao.source.KakaoLoginDataSourceImpl
import com.happy.friendogly.local.di.AddressModule
import com.happy.friendogly.local.di.AlarmModule
import com.happy.friendogly.local.di.AlarmTokenModule
import com.happy.friendogly.local.di.TokenManager
import com.happy.friendogly.local.source.AddressDataSourceImpl
import com.happy.friendogly.local.source.AlarmSettingDataSourceImpl
import com.happy.friendogly.local.source.TokenDataSourceImpl
import com.happy.friendogly.remote.api.AuthenticationListener
import com.happy.friendogly.remote.api.BaseUrl
import com.happy.friendogly.remote.api.WebSocketService
import com.happy.friendogly.remote.di.RemoteModule
import com.happy.friendogly.remote.source.AlamTokenDataSourceImpl
import com.happy.friendogly.remote.source.AuthDataSourceImpl
import com.happy.friendogly.remote.source.ChatDataSourceImpl
import com.happy.friendogly.remote.source.ClubDataSourceImpl
Expand All @@ -97,6 +111,8 @@ class AppModule(context: Context) {
private val authenticationListener: AuthenticationListener =
AuthenticationListenerImpl(context, tokenManager)
private val addressModule = AddressModule(context)
private val alarmModule = AlarmModule(context)
private val alarmTokenModule = AlarmTokenModule(context)

// service
private val authService =
Expand Down Expand Up @@ -152,6 +168,13 @@ class AppModule(context: Context) {
authenticationListener = authenticationListener,
)

private val alarmTokenService =
RemoteModule.createAlarmTokenService(
baseUrl = baseUrl,
tokenManager = tokenManager,
authenticationListener = authenticationListener,
)

// data source
private val authDataSource: AuthDataSource = AuthDataSourceImpl(service = authService)
private val clubDataSource: ClubDataSource = ClubDataSourceImpl(service = clubService)
Expand All @@ -165,6 +188,10 @@ class AppModule(context: Context) {
private val webSocketDataSource: WebSocketDataSource =
WebSocketDataSourceImpl(service = webSocketService)
private val chatDataSource: ChatDataSource = ChatDataSourceImpl(service = chatService)
private val alarmSettingDataSource: AlarmSettingDataSource =
AlarmSettingDataSourceImpl(alarmModule = alarmModule)
private val alarmTokenDataSource: AlarmTokenDataSource =
AlamTokenDataSourceImpl(service = alarmTokenService)

// repository
private val authRepository: AuthRepository = AuthRepositoryImpl(source = authDataSource)
Expand All @@ -181,6 +208,10 @@ class AppModule(context: Context) {
val webSocketRepository: WebSocketRepository =
WebSocketRepositoryImpl(source = webSocketDataSource)
private val chatRepository: ChatRepository = ChatRepositoryImpl(source = chatDataSource)
private val alarmSettingRepository: AlarmSettingRepository =
AlarmSettingRepositoryImpl(source = alarmSettingDataSource)
private val alarmTokenRepository: AlarmTokenRepository =
AlarmTokenRepositoryImpl(source = alarmTokenDataSource)

// use case
val postKakaoLoginUseCase: PostKakaoLoginUseCase =
Expand Down Expand Up @@ -234,6 +265,14 @@ class AppModule(context: Context) {
PublishLeaveUseCase(repository = webSocketRepository)
val subScribeMessageUseCase: SubScribeMessageUseCase =
SubScribeMessageUseCase(repository = webSocketRepository)
val deleteAlarmSettingUseCase: DeleteAlarmSettingUseCase =
DeleteAlarmSettingUseCase(repository = alarmSettingRepository)
val saveAlarmSettingUseCase: SaveAlarmSettingUseCase =
SaveAlarmSettingUseCase(repository = alarmSettingRepository)
val getAlarmSettingUseCase: GetAlarmSettingUseCase =
GetAlarmSettingUseCase(repository = alarmSettingRepository)
val saveAlarmTokenUseCase: SaveAlamTokenUseCase =
SaveAlamTokenUseCase(repository = alarmTokenRepository)

companion object {
private var instance: AppModule? = null
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.happy.friendogly.data.mapper

import com.happy.friendogly.data.model.AlarmTokenDto
import com.happy.friendogly.remote.model.request.DeviceTokenRequest

fun AlarmTokenDto.toRemote(): DeviceTokenRequest =
DeviceTokenRequest(
deviceToken = token,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.happy.friendogly.data.model

data class AlarmTokenDto(
val token: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.happy.friendogly.data.repository

import com.happy.friendogly.data.source.AlarmSettingDataSource
import com.happy.friendogly.domain.repository.AlarmSettingRepository

class AlarmSettingRepositoryImpl(private val source: AlarmSettingDataSource) : AlarmSettingRepository {
override suspend fun saveAlarm(isSet: Boolean): Result<Unit> = source.saveAlarm(isSet)

override suspend fun getAlarm(): Result<Boolean> = source.getAlarm()

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

import com.happy.friendogly.data.source.AlarmTokenDataSource
import com.happy.friendogly.domain.repository.AlarmTokenRepository

class AlarmTokenRepositoryImpl(private val source: AlarmTokenDataSource) : AlarmTokenRepository {
override suspend fun saveToken(token: String): Result<Unit> = source.saveToken(token)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.happy.friendogly.data.source

interface AlarmSettingDataSource {
suspend fun saveAlarm(isSet: Boolean): Result<Unit>

suspend fun getAlarm(): Result<Boolean>

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

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

interface AlarmSettingRepository {
suspend fun saveAlarm(isSet: Boolean): Result<Unit>

suspend fun getAlarm(): Result<Boolean>

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

interface AlarmTokenRepository {
suspend fun saveToken(token: String): Result<Unit>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.happy.friendogly.domain.usecase

import com.happy.friendogly.domain.repository.AlarmSettingRepository

class DeleteAlarmSettingUseCase(
private val repository: AlarmSettingRepository,
) {
suspend operator fun invoke(): Result<Unit> = repository.deleteAlarm()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.happy.friendogly.domain.usecase

import com.happy.friendogly.domain.repository.AlarmSettingRepository

class GetAlarmSettingUseCase(
private val repository: AlarmSettingRepository,
) {
suspend operator fun invoke(): Result<Boolean> = repository.getAlarm()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.happy.friendogly.domain.usecase

import com.happy.friendogly.domain.repository.AlarmTokenRepository

class SaveAlamTokenUseCase(
private val repository: AlarmTokenRepository,
) {
suspend operator fun invoke(token: String): Result<Unit> = repository.saveToken(token)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.happy.friendogly.domain.usecase

import com.happy.friendogly.domain.repository.AlarmSettingRepository

class SaveAlarmSettingUseCase(
private val repository: AlarmSettingRepository,
) {
suspend operator fun invoke(isSet: Boolean): Result<Unit> = repository.saveAlarm(isSet)
}
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 AlarmModule(val context: Context) {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = DATA_STORE_NAME)

private val key = booleanPreferencesKey(ALARM_SETTING)

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

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

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

companion object {
private const val ALARM_SETTING = "ALARM_SETTING"
private const val DATA_STORE_NAME = "alarmDataStore"
}
}
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.edit
import androidx.datastore.preferences.core.emptyPreferences
import androidx.datastore.preferences.core.stringPreferencesKey
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 AlarmTokenModule(val context: Context) {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = DATA_STORE_NAME)

private val key = stringPreferencesKey(FCM_TOKEN)

var token: Flow<String> =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아래 코멘트와 유사합니다!

context.dataStore.data.catch { exception ->
if (exception is IOException) {
emit(emptyPreferences())
} else {
throw exception
}
}.map { preferences ->
preferences[key] ?: ""
}

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

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

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

import com.happy.friendogly.data.source.AlarmSettingDataSource
import com.happy.friendogly.local.di.AlarmModule
import kotlinx.coroutines.flow.first

class AlarmSettingDataSourceImpl(
private val alarmModule: AlarmModule,
) : AlarmSettingDataSource {
override suspend fun saveAlarm(isSet: Boolean): Result<Unit> =
runCatching {
alarmModule.saveSetting(isSet)
}

override suspend fun getAlarm(): Result<Boolean> =
runCatching {
alarmModule.isSet.first()
}

override suspend fun deleteAlarm(): Result<Unit> =
runCatching {
alarmModule.deleteSetting()
}
}
Loading
Loading