Skip to content

Commit

Permalink
[AN] feat: FCM 구현 (#312)
Browse files Browse the repository at this point in the history
  • Loading branch information
gaeun5744 authored and takoyakimchi committed Oct 23, 2024
1 parent 54082f6 commit 6678b54
Show file tree
Hide file tree
Showing 25 changed files with 432 additions and 1 deletion.
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 @@ -105,6 +106,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> =
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

0 comments on commit 6678b54

Please sign in to comment.