Skip to content

Commit

Permalink
🔀 :: (#681) fcm 구축
Browse files Browse the repository at this point in the history
  • Loading branch information
parkuiery committed Jul 12, 2024
2 parents c8d0bb4 + 3b109d7 commit 800eeb1
Show file tree
Hide file tree
Showing 46 changed files with 696 additions and 11 deletions.
6 changes: 6 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ dependencies {
implementation(project(ProjectPaths.Core.PROJECT))
implementation(project(ProjectPaths.Core.SCHOOL))
implementation(project(ProjectPaths.Core.UI))
implementation(project(ProjectPaths.Core.NOTIFICATION))
implementation(project(ProjectPaths.Core.DEVICE))

implementation(project(ProjectPaths.DATA))
implementation(project(ProjectPaths.DATABASE))
Expand Down Expand Up @@ -123,4 +125,8 @@ dependencies {
androidTestImplementation(libs.androidx.junit)

implementation(libs.app.update)

implementation(platform(libs.firebase.bom))
implementation(libs.firebase.messaging)
implementation(libs.firebase.analytics)
}
12 changes: 12 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,17 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".service.DmsMessagingService"
android:exported="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<meta-data
android:name="om.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_logo_image"/>
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/default_notification_channel_id" />
</application>
</manifest>
12 changes: 12 additions & 0 deletions app/src/main/java/team/aliens/dms/android/app/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@ import androidx.activity.compose.setContent
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
import androidx.core.view.WindowCompat
import androidx.lifecycle.lifecycleScope
import com.google.accompanist.adaptive.calculateDisplayFeatures
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
import com.google.android.play.core.install.model.AppUpdateType
import com.google.android.play.core.install.model.UpdateAvailability
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import team.aliens.dms.android.core.designsystem.DmsTheme
import team.aliens.dms.android.core.jwt.di.IsJwtAvailable
import team.aliens.dms.android.core.notification.DeviceTokenManager
import javax.inject.Inject

@AndroidEntryPoint
Expand All @@ -24,6 +29,9 @@ class MainActivity : ComponentActivity() {
@Inject
lateinit var isJwtAvailable: StateFlow<Boolean>

@Inject
lateinit var deviceTokenManager: DeviceTokenManager

@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -46,6 +54,10 @@ class MainActivity : ComponentActivity() {
)
}
}

lifecycleScope.launch {
deviceTokenManager.fetchDeviceToken()
}
}

private fun checkAppUpdate() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package team.aliens.dms.android.app.service

import android.os.Build
import androidx.annotation.RequiresApi
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import team.aliens.dms.android.core.notification.DeviceTokenManager
import team.aliens.dms.android.core.notification.NotificationManager
import javax.inject.Inject

@AndroidEntryPoint
class DmsMessagingService : FirebaseMessagingService() {

@Inject
lateinit var deviceTokenManager: DeviceTokenManager

private val notificationManager: NotificationManager by lazy {
NotificationManager(context = this)
}

override fun onNewToken(deviceToken: String) {
super.onNewToken(deviceToken)
CoroutineScope(Dispatchers.IO).launch {
deviceTokenManager.saveDeviceToken(deviceToken = deviceToken)
}
}

override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
message.notification?.run {
notificationManager.setNotificationContent(
title = title,
body = body,
)
}
notificationManager.sendNotification()
}
}
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">DMS</string>
<string name="default_notification_channel_id">team.aliens.dms.android</string>
</resources>
11 changes: 11 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
// TODO: Remove once KTIJ-19369 is fixed
@file:Suppress("DSL_SCOPE_VIOLATION")

buildscript {
repositories {
google()
mavenCentral()
}

dependencies {
classpath(libs.google.services)
}
}

plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.android.library) apply false
Expand Down
2 changes: 2 additions & 0 deletions buildSrc/src/main/kotlin/ProjectPaths.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ object ProjectPaths {
const val SCHOOL = ":core:school"
const val UI = ":core:ui"
const val FILE = ":core:file"
const val NOTIFICATION = ":core:notification"
const val DEVICE = ":core:device"
}

object Shared {
Expand Down
1 change: 1 addition & 0 deletions core/device/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
53 changes: 53 additions & 0 deletions core/device/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.hilt)
alias(libs.plugins.ksp)
}

android {
namespace = "team.aliens.dms.android.core.device"
compileSdk = libs.versions.compileSdk.get().toInt()

defaultConfig {
minSdk = libs.versions.minSdk.get().toInt()

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}

buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}

compileOptions {
sourceCompatibility = Versions.java
targetCompatibility = Versions.java
}

kotlinOptions {
jvmTarget = Versions.java.toString()
}
}

dependencies {
implementation(project(ProjectPaths.Core.DATASTORE))

implementation(libs.androidx.core)
implementation(libs.androidx.appcompat)
implementation(libs.material)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso)

implementation(libs.androidx.datastore.preferences)

implementation(libs.hilt)
ksp(libs.hilt.compiler)
}
Empty file added core/device/consumer-rules.pro
Empty file.
21 changes: 21 additions & 0 deletions core/device/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package team.aliens.dms.android.core.device

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("team.aliens.dms.android.core.device.test", appContext.packageName)
}
}
4 changes: 4 additions & 0 deletions core/device/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package team.aliens.dms.android.core.device.datastore

abstract class DeviceDataStoreDataSource {

abstract fun loadDeviceToken(): String

abstract suspend fun storeDeviceToken(deviceToken: String)

abstract suspend fun clearDeviceToken()

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package team.aliens.dms.android.core.device.datastore

import team.aliens.dms.android.core.device.datastore.store.DeviceStore
import javax.inject.Inject

internal class DeviceDataStoreDataSourceImpl @Inject constructor(
private val deviceStore: DeviceStore,
) : DeviceDataStoreDataSource() {
override fun loadDeviceToken(): String = deviceStore.loadDeviceToken()

override suspend fun storeDeviceToken(deviceToken: String) {
deviceStore.storeDeviceToken(deviceToken)
}

override suspend fun clearDeviceToken() {
deviceStore.clearDeviceToken()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package team.aliens.dms.android.core.device.datastore.store

internal abstract class DeviceStore {

abstract fun loadDeviceToken(): String

abstract suspend fun storeDeviceToken(deviceToken: String)

abstract suspend fun clearDeviceToken()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package team.aliens.dms.android.core.device.datastore.store

import android.util.Log
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
import team.aliens.dms.android.core.datastore.PreferencesDataStore
import team.aliens.dms.android.core.datastore.util.transform
import team.aliens.dms.android.core.device.datastore.store.exception.CannotStoreDeviceTokenException
import team.aliens.dms.android.core.device.datastore.store.exception.DeviceTokenNotFoundException
import javax.inject.Inject

internal class DeviceStoreImpl @Inject constructor(
private val preferencesDataStore: PreferencesDataStore,
) : DeviceStore() {
override fun loadDeviceToken(): String = runBlocking {
preferencesDataStore.data.map { preferences ->
Log.d("TEST2",preferences[DEVICE_TOKEN].toString())
preferences[DEVICE_TOKEN] ?: throw DeviceTokenNotFoundException()
}.first()
}

override suspend fun storeDeviceToken(deviceToken: String) {
transform(
onFailure = { throw CannotStoreDeviceTokenException() },
) {
Log.d("TEST1",deviceToken)
preferencesDataStore.edit { preferences ->
preferences[DEVICE_TOKEN] = deviceToken
}
}
}

override suspend fun clearDeviceToken() {
transform {
preferencesDataStore.edit { preferences -> preferences.clear() }
}
}

private companion object {
val DEVICE_TOKEN = stringPreferencesKey("device-token")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package team.aliens.dms.android.core.device.datastore.store.exception

import team.aliens.dms.android.core.datastore.exception.TransformFailureException

class CannotStoreDeviceTokenException : TransformFailureException("Cannot store deviceToken")
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package team.aliens.dms.android.core.device.datastore.store.exception

import team.aliens.dms.android.core.datastore.exception.LoadFailureException

sealed class TokenNotFoundException(message: String?) : LoadFailureException(message)

class DeviceTokenNotFoundException : TokenNotFoundException("Device token not found")
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package team.aliens.dms.android.core.device.di

import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import team.aliens.dms.android.core.device.datastore.DeviceDataStoreDataSource
import team.aliens.dms.android.core.device.datastore.DeviceDataStoreDataSourceImpl
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
internal abstract class DataSourceModule {

@Binds
@Singleton
abstract fun bindDeviceDataStoreDataSource(impl: DeviceDataStoreDataSourceImpl): DeviceDataStoreDataSource
}
Loading

0 comments on commit 800eeb1

Please sign in to comment.