From f7c31a8e67445770ee4dcd96eaea3cf0862c74eb Mon Sep 17 00:00:00 2001 From: uiel Date: Wed, 29 May 2024 22:36:40 +0900 Subject: [PATCH 001/111] =?UTF-8?q?=F0=9F=93=91=20::=20=ED=8C=8C=EC=9D=B4?= =?UTF-8?q?=EC=96=B4=EB=B2=A0=EC=9D=B4=EC=8A=A4=20=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 4 ++++ build.gradle.kts | 11 +++++++++++ gradle/libs.versions.toml | 8 ++++++++ 3 files changed, 23 insertions(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 068abf285..c256741fc 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -121,4 +121,8 @@ dependencies { testImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.junit) + + implementation(platform(libs.firebase.bom)) + implementation(libs.firebase.messaging) + implementation(libs.firebase.analytics) } diff --git a/build.gradle.kts b/build.gradle.kts index 8d0403270..b33aa9475 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3863c9049..15ff16a6a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,6 +7,10 @@ appcompat = "1.6.1" core = "1.12.0" datastorePreferences = "1.0.0" espresso = "3.5.1" +firebaseAnalytics = "22.0.0" +firebaseBom = "33.0.0" +firebaseMessaging = "24.0.0" +googleServices = "4.4.1" javaxInject = "1" junit = "4.13.2" junitAndroid = "1.1.5" @@ -60,6 +64,10 @@ androidx-navigation-compose = { module = "androidx.navigation:navigation-compose androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigation" } composeDestinations = { group = "io.github.raamcosta.compose-destinations", name = "animations-core", version.ref = "composeDestination" } composeDestinations-ksp = { group = "io.github.raamcosta.compose-destinations", name = "ksp", version.ref = "composeDestination" } +firebase-analytics = { module = "com.google.firebase:firebase-analytics", version.ref = "firebaseAnalytics" } +firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" } +firebase-messaging = { module = "com.google.firebase:firebase-messaging", version.ref = "firebaseMessaging" } +google-services = { module = "com.google.gms:google-services", version.ref = "googleServices" } javax-inject = { module = "javax.inject:javax.inject", version.ref = "javaxInject" } # Deprecated From 8a1e71eca0d8223c6ba8b2dfb59784c9679eb704 Mon Sep 17 00:00:00 2001 From: uiel Date: Thu, 30 May 2024 21:43:33 +0900 Subject: [PATCH 002/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20notification?= =?UTF-8?q?=20=EB=AA=A8=EB=93=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/notification/.gitignore | 1 + core/notification/build.gradle.kts | 43 +++++++++++++++++++ core/notification/consumer-rules.pro | 0 core/notification/proguard-rules.pro | 21 +++++++++ .../notification/ExampleInstrumentedTest.kt | 24 +++++++++++ .../notification/src/main/AndroidManifest.xml | 4 ++ .../CheckNotificationPermission.kt | 4 ++ .../core/notification/DeviceTokenManager.kt | 4 ++ .../core/notification/NotificationManager.kt | 4 ++ .../core/notification/ExampleUnitTest.kt | 17 ++++++++ 10 files changed, 122 insertions(+) create mode 100644 core/notification/.gitignore create mode 100644 core/notification/build.gradle.kts create mode 100644 core/notification/consumer-rules.pro create mode 100644 core/notification/proguard-rules.pro create mode 100644 core/notification/src/androidTest/java/team/aliens/dms/android/core/notification/ExampleInstrumentedTest.kt create mode 100644 core/notification/src/main/AndroidManifest.xml create mode 100644 core/notification/src/main/java/team/aliens/dms/android/core/notification/CheckNotificationPermission.kt create mode 100644 core/notification/src/main/java/team/aliens/dms/android/core/notification/DeviceTokenManager.kt create mode 100644 core/notification/src/main/java/team/aliens/dms/android/core/notification/NotificationManager.kt create mode 100644 core/notification/src/test/java/team/aliens/dms/android/core/notification/ExampleUnitTest.kt diff --git a/core/notification/.gitignore b/core/notification/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/core/notification/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/notification/build.gradle.kts b/core/notification/build.gradle.kts new file mode 100644 index 000000000..161fdd5f2 --- /dev/null +++ b/core/notification/build.gradle.kts @@ -0,0 +1,43 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) +} + +android { + namespace = "team.aliens.dms.android.core.notification" + compileSdk = 34 + + defaultConfig { + minSdk = 26 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + implementation(libs.material) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso) +} \ No newline at end of file diff --git a/core/notification/consumer-rules.pro b/core/notification/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/core/notification/proguard-rules.pro b/core/notification/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/core/notification/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/core/notification/src/androidTest/java/team/aliens/dms/android/core/notification/ExampleInstrumentedTest.kt b/core/notification/src/androidTest/java/team/aliens/dms/android/core/notification/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..43060cfcc --- /dev/null +++ b/core/notification/src/androidTest/java/team/aliens/dms/android/core/notification/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package team.aliens.dms.android.core.notification + +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.notification.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/core/notification/src/main/AndroidManifest.xml b/core/notification/src/main/AndroidManifest.xml new file mode 100644 index 000000000..a5918e68a --- /dev/null +++ b/core/notification/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/notification/src/main/java/team/aliens/dms/android/core/notification/CheckNotificationPermission.kt b/core/notification/src/main/java/team/aliens/dms/android/core/notification/CheckNotificationPermission.kt new file mode 100644 index 000000000..64db383b4 --- /dev/null +++ b/core/notification/src/main/java/team/aliens/dms/android/core/notification/CheckNotificationPermission.kt @@ -0,0 +1,4 @@ +package team.aliens.dms.android.core.notification + +class CheckNotificationPermission { +} \ No newline at end of file diff --git a/core/notification/src/main/java/team/aliens/dms/android/core/notification/DeviceTokenManager.kt b/core/notification/src/main/java/team/aliens/dms/android/core/notification/DeviceTokenManager.kt new file mode 100644 index 000000000..4ef3091ad --- /dev/null +++ b/core/notification/src/main/java/team/aliens/dms/android/core/notification/DeviceTokenManager.kt @@ -0,0 +1,4 @@ +package team.aliens.dms.android.core.notification + +class DeviceTokenManager { +} \ No newline at end of file diff --git a/core/notification/src/main/java/team/aliens/dms/android/core/notification/NotificationManager.kt b/core/notification/src/main/java/team/aliens/dms/android/core/notification/NotificationManager.kt new file mode 100644 index 000000000..7cbf520e1 --- /dev/null +++ b/core/notification/src/main/java/team/aliens/dms/android/core/notification/NotificationManager.kt @@ -0,0 +1,4 @@ +package team.aliens.dms.android.core.notification + +class NotificationManager { +} \ No newline at end of file diff --git a/core/notification/src/test/java/team/aliens/dms/android/core/notification/ExampleUnitTest.kt b/core/notification/src/test/java/team/aliens/dms/android/core/notification/ExampleUnitTest.kt new file mode 100644 index 000000000..826dfaeae --- /dev/null +++ b/core/notification/src/test/java/team/aliens/dms/android/core/notification/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package team.aliens.dms.android.core.notification + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file From 387465906e23ff84bd59700e1cc5b6aa1d6c4df0 Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 31 May 2024 23:57:31 +0900 Subject: [PATCH 003/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c256741fc..98b8e7121 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -66,6 +66,7 @@ dependencies { implementation(project(ProjectPaths.Core.PROJECT)) implementation(project(ProjectPaths.Core.SCHOOL)) implementation(project(ProjectPaths.Core.UI)) + implementation(project(ProjectPaths.Core.NOTIFICATION)) implementation(project(ProjectPaths.DATA)) implementation(project(ProjectPaths.DATABASE)) From 4f480d53c88426556bd10b56d2d15a1d8d945460 Mon Sep 17 00:00:00 2001 From: uiel Date: Sat, 1 Jun 2024 23:13:14 +0900 Subject: [PATCH 004/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20notification?= =?UTF-8?q?=20=EB=AA=A8=EB=93=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- buildSrc/src/main/kotlin/ProjectPaths.kt | 1 + core/notification/build.gradle.kts | 25 ++++++++++++++++++------ settings.gradle.kts | 4 ++++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/buildSrc/src/main/kotlin/ProjectPaths.kt b/buildSrc/src/main/kotlin/ProjectPaths.kt index bbf50a9c2..8d93662af 100644 --- a/buildSrc/src/main/kotlin/ProjectPaths.kt +++ b/buildSrc/src/main/kotlin/ProjectPaths.kt @@ -15,6 +15,7 @@ object ProjectPaths { const val SCHOOL = ":core:school" const val UI = ":core:ui" const val FILE = ":core:file" + const val NOTIFICATION = ":core:notification" } object Shared { diff --git a/core/notification/build.gradle.kts b/core/notification/build.gradle.kts index 161fdd5f2..0bd967a0e 100644 --- a/core/notification/build.gradle.kts +++ b/core/notification/build.gradle.kts @@ -1,14 +1,16 @@ 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.notification" - compileSdk = 34 + compileSdk = libs.versions.compileSdk.get().toInt() defaultConfig { - minSdk = 26 + minSdk = libs.versions.minSdk.get().toInt() testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") @@ -23,16 +25,24 @@ android { ) } } + + buildFeatures { + buildConfig = true + } + compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = Versions.java + targetCompatibility = Versions.java } + kotlinOptions { - jvmTarget = "1.8" + jvmTarget = Versions.java.toString() } } dependencies { + implementation(project(ProjectPaths.DATA)) + implementation(project(ProjectPaths.Core.DESIGN_SYSTEM)) implementation(libs.androidx.core) implementation(libs.androidx.appcompat) @@ -40,4 +50,7 @@ dependencies { testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso) -} \ No newline at end of file + + implementation(libs.hilt) + ksp(libs.hilt.compiler) +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 26fa02469..69cd818af 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1,9 @@ @file:Suppress("UnstableApiUsage") +include(":core:notification") + + + pluginManagement { repositories { gradlePluginPortal() From 77cd7338b612cb12feb8cebafc4f9381e8f01a8a Mon Sep 17 00:00:00 2001 From: uiel Date: Sun, 2 Jun 2024 23:36:52 +0900 Subject: [PATCH 005/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index de07b68d2..fe262484b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,5 +38,11 @@ + + + + + From 4c8ed94b54dfa9e7bccc3b269539c3305aeb4512 Mon Sep 17 00:00:00 2001 From: uiel Date: Mon, 3 Jun 2024 23:01:19 +0900 Subject: [PATCH 006/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EA=B6=8C=ED=95=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/notification/src/main/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/core/notification/src/main/AndroidManifest.xml b/core/notification/src/main/AndroidManifest.xml index a5918e68a..1818d8cdc 100644 --- a/core/notification/src/main/AndroidManifest.xml +++ b/core/notification/src/main/AndroidManifest.xml @@ -1,4 +1,5 @@ + \ No newline at end of file From 5f7da27d4c7825520f04c2a826de4dd307a05996 Mon Sep 17 00:00:00 2001 From: uiel Date: Sun, 9 Jun 2024 23:30:52 +0900 Subject: [PATCH 007/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EA=B6=8C=ED=95=9C=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/CheckNotificationPermission.kt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/core/notification/src/main/java/team/aliens/dms/android/core/notification/CheckNotificationPermission.kt b/core/notification/src/main/java/team/aliens/dms/android/core/notification/CheckNotificationPermission.kt index 64db383b4..8d80eefa0 100644 --- a/core/notification/src/main/java/team/aliens/dms/android/core/notification/CheckNotificationPermission.kt +++ b/core/notification/src/main/java/team/aliens/dms/android/core/notification/CheckNotificationPermission.kt @@ -1,4 +1,15 @@ package team.aliens.dms.android.core.notification -class CheckNotificationPermission { -} \ No newline at end of file +import android.Manifest +import android.content.Context +import android.content.pm.PackageManager +import android.os.Build +import androidx.core.app.ActivityCompat + +fun notificationPermissionGranted(context: Context): Boolean { + return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && + ActivityCompat.checkSelfPermission( + context, + Manifest.permission.POST_NOTIFICATIONS, + ) == PackageManager.PERMISSION_GRANTED) || Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU +} From 43e2966d0334b9bd6ea623a112bf816a321bb87d Mon Sep 17 00:00:00 2001 From: uielPark Date: Tue, 11 Jun 2024 23:28:12 +0900 Subject: [PATCH 008/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EB=B2=84?= =?UTF-8?q?=EC=A0=84=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 15ff16a6a..5714d28ab 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,7 +22,7 @@ agp = "8.2.2" kotlinAndroid = "1.9.20" kotlinJvm = "1.9.20" hilt = "2.48" -google = "4.4.1" +google = "4.4.2" ksp = "1.9.10-1.0.13" moshiKotlin = "1.14.0" okhttp = "4.12.0" From d77d5008940292fdf31bdd6b71840e3e86a1ffe1 Mon Sep 17 00:00:00 2001 From: uiel Date: Sat, 15 Jun 2024 23:42:53 +0900 Subject: [PATCH 009/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20widget=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/widget/.gitignore | 1 + core/widget/build.gradle.kts | 43 +++++++++++++++++++ core/widget/consumer-rules.pro | 0 core/widget/proguard-rules.pro | 21 +++++++++ .../core/widget/ExampleInstrumentedTest.kt | 24 +++++++++++ core/widget/src/main/AndroidManifest.xml | 4 ++ .../dms/android/core/widget/MealAppWidget.kt | 4 ++ .../android/core/widget/MealWidgetReceiver.kt | 9 ++++ .../main/res/drawable/shape_widget_small.xml | 0 .../src/main/res/drawable/widget_big_meal.xml | 0 .../main/res/drawable/widget_small_meal.xml | 0 .../src/main/res/layout/big_widget_meal.xml | 0 .../main/res/layout/initial_widget_layout.xml | 0 .../src/main/res/layout/small_widget_meal.xml | 0 .../main/res/xml/big_meal_widget_provider.xml | 0 .../res/xml/small_meal_widget_provider.xml | 0 .../android/core/widget/ExampleUnitTest.kt | 17 ++++++++ 17 files changed, 123 insertions(+) create mode 100644 core/widget/.gitignore create mode 100644 core/widget/build.gradle.kts create mode 100644 core/widget/consumer-rules.pro create mode 100644 core/widget/proguard-rules.pro create mode 100644 core/widget/src/androidTest/java/team/aliens/dms/android/core/widget/ExampleInstrumentedTest.kt create mode 100644 core/widget/src/main/AndroidManifest.xml create mode 100644 core/widget/src/main/java/team/aliens/dms/android/core/widget/MealAppWidget.kt create mode 100644 core/widget/src/main/java/team/aliens/dms/android/core/widget/MealWidgetReceiver.kt rename {feature => core/widget}/src/main/res/drawable/shape_widget_small.xml (100%) rename {feature => core/widget}/src/main/res/drawable/widget_big_meal.xml (100%) rename {feature => core/widget}/src/main/res/drawable/widget_small_meal.xml (100%) rename {feature => core/widget}/src/main/res/layout/big_widget_meal.xml (100%) rename {feature => core/widget}/src/main/res/layout/initial_widget_layout.xml (100%) rename {feature => core/widget}/src/main/res/layout/small_widget_meal.xml (100%) rename {feature => core/widget}/src/main/res/xml/big_meal_widget_provider.xml (100%) rename {feature => core/widget}/src/main/res/xml/small_meal_widget_provider.xml (100%) create mode 100644 core/widget/src/test/java/team/aliens/dms/android/core/widget/ExampleUnitTest.kt diff --git a/core/widget/.gitignore b/core/widget/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/core/widget/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/widget/build.gradle.kts b/core/widget/build.gradle.kts new file mode 100644 index 000000000..29cc7c79e --- /dev/null +++ b/core/widget/build.gradle.kts @@ -0,0 +1,43 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) +} + +android { + namespace = "team.aliens.dms.android.core.widget" + compileSdk = 34 + + defaultConfig { + minSdk = 26 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + implementation(libs.material) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso) +} \ No newline at end of file diff --git a/core/widget/consumer-rules.pro b/core/widget/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/core/widget/proguard-rules.pro b/core/widget/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/core/widget/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/core/widget/src/androidTest/java/team/aliens/dms/android/core/widget/ExampleInstrumentedTest.kt b/core/widget/src/androidTest/java/team/aliens/dms/android/core/widget/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..eadac4029 --- /dev/null +++ b/core/widget/src/androidTest/java/team/aliens/dms/android/core/widget/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package team.aliens.dms.android.core.widget + +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.widget.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/core/widget/src/main/AndroidManifest.xml b/core/widget/src/main/AndroidManifest.xml new file mode 100644 index 000000000..a5918e68a --- /dev/null +++ b/core/widget/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/MealAppWidget.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/MealAppWidget.kt new file mode 100644 index 000000000..9973e9b8e --- /dev/null +++ b/core/widget/src/main/java/team/aliens/dms/android/core/widget/MealAppWidget.kt @@ -0,0 +1,4 @@ +package team.aliens.dms.android.core.widget + +class MealAppWidget { +} \ No newline at end of file diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/MealWidgetReceiver.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/MealWidgetReceiver.kt new file mode 100644 index 000000000..cd667f093 --- /dev/null +++ b/core/widget/src/main/java/team/aliens/dms/android/core/widget/MealWidgetReceiver.kt @@ -0,0 +1,9 @@ +package team.aliens.dms.android.core.widget + +import androidx.glance.appwidget.GlanceAppWidget +import androidx.glance.appwidget.GlanceAppWidgetReceiver + +class MealWidgetService : GlanceAppWidgetReceiver() { + override val glanceAppWidget: GlanceAppWidget + get() = TODO("Not yet implemented") +} \ No newline at end of file diff --git a/feature/src/main/res/drawable/shape_widget_small.xml b/core/widget/src/main/res/drawable/shape_widget_small.xml similarity index 100% rename from feature/src/main/res/drawable/shape_widget_small.xml rename to core/widget/src/main/res/drawable/shape_widget_small.xml diff --git a/feature/src/main/res/drawable/widget_big_meal.xml b/core/widget/src/main/res/drawable/widget_big_meal.xml similarity index 100% rename from feature/src/main/res/drawable/widget_big_meal.xml rename to core/widget/src/main/res/drawable/widget_big_meal.xml diff --git a/feature/src/main/res/drawable/widget_small_meal.xml b/core/widget/src/main/res/drawable/widget_small_meal.xml similarity index 100% rename from feature/src/main/res/drawable/widget_small_meal.xml rename to core/widget/src/main/res/drawable/widget_small_meal.xml diff --git a/feature/src/main/res/layout/big_widget_meal.xml b/core/widget/src/main/res/layout/big_widget_meal.xml similarity index 100% rename from feature/src/main/res/layout/big_widget_meal.xml rename to core/widget/src/main/res/layout/big_widget_meal.xml diff --git a/feature/src/main/res/layout/initial_widget_layout.xml b/core/widget/src/main/res/layout/initial_widget_layout.xml similarity index 100% rename from feature/src/main/res/layout/initial_widget_layout.xml rename to core/widget/src/main/res/layout/initial_widget_layout.xml diff --git a/feature/src/main/res/layout/small_widget_meal.xml b/core/widget/src/main/res/layout/small_widget_meal.xml similarity index 100% rename from feature/src/main/res/layout/small_widget_meal.xml rename to core/widget/src/main/res/layout/small_widget_meal.xml diff --git a/feature/src/main/res/xml/big_meal_widget_provider.xml b/core/widget/src/main/res/xml/big_meal_widget_provider.xml similarity index 100% rename from feature/src/main/res/xml/big_meal_widget_provider.xml rename to core/widget/src/main/res/xml/big_meal_widget_provider.xml diff --git a/feature/src/main/res/xml/small_meal_widget_provider.xml b/core/widget/src/main/res/xml/small_meal_widget_provider.xml similarity index 100% rename from feature/src/main/res/xml/small_meal_widget_provider.xml rename to core/widget/src/main/res/xml/small_meal_widget_provider.xml diff --git a/core/widget/src/test/java/team/aliens/dms/android/core/widget/ExampleUnitTest.kt b/core/widget/src/test/java/team/aliens/dms/android/core/widget/ExampleUnitTest.kt new file mode 100644 index 000000000..17dd4c952 --- /dev/null +++ b/core/widget/src/test/java/team/aliens/dms/android/core/widget/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package team.aliens.dms.android.core.widget + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file From 1f0571273629c16b6703f3d4009c7366194519cc Mon Sep 17 00:00:00 2001 From: uiel Date: Sun, 16 Jun 2024 23:28:55 +0900 Subject: [PATCH 010/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20widget=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- settings.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/settings.gradle.kts b/settings.gradle.kts index 26fa02469..c48b113be 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -34,6 +34,7 @@ include(":core:project") include(":core:school") include(":core:file") include(":core:ui") +include(":core:widget") include(":data") include(":network") From 08c609179a80c4738fceb099d56efc02f134f4c7 Mon Sep 17 00:00:00 2001 From: uiel Date: Sun, 16 Jun 2024 23:31:13 +0900 Subject: [PATCH 011/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=9C=84?= =?UTF-8?q?=EC=A0=AF=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EB=B0=8F=20=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 1 + buildSrc/src/main/kotlin/ProjectPaths.kt | 1 + core/widget/build.gradle.kts | 32 +++++++++++++++++++----- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 14d6b8248..80f0335f3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -66,6 +66,7 @@ dependencies { implementation(project(ProjectPaths.Core.PROJECT)) implementation(project(ProjectPaths.Core.SCHOOL)) implementation(project(ProjectPaths.Core.UI)) + implementation(project(ProjectPaths.Core.WIDGET)) implementation(project(ProjectPaths.DATA)) implementation(project(ProjectPaths.DATABASE)) diff --git a/buildSrc/src/main/kotlin/ProjectPaths.kt b/buildSrc/src/main/kotlin/ProjectPaths.kt index bbf50a9c2..b850378b4 100644 --- a/buildSrc/src/main/kotlin/ProjectPaths.kt +++ b/buildSrc/src/main/kotlin/ProjectPaths.kt @@ -15,6 +15,7 @@ object ProjectPaths { const val SCHOOL = ":core:school" const val UI = ":core:ui" const val FILE = ":core:file" + const val WIDGET = ":core:widget" } object Shared { diff --git a/core/widget/build.gradle.kts b/core/widget/build.gradle.kts index 29cc7c79e..e595b9e50 100644 --- a/core/widget/build.gradle.kts +++ b/core/widget/build.gradle.kts @@ -5,10 +5,10 @@ plugins { android { namespace = "team.aliens.dms.android.core.widget" - compileSdk = 34 + compileSdk = libs.versions.compileSdk.get().toInt() defaultConfig { - minSdk = 26 + minSdk = libs.versions.minSdk.get().toInt() testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") @@ -23,21 +23,41 @@ android { ) } } + + buildFeatures { + compose = true + } + + composeOptions { + kotlinCompilerExtensionVersion = libs.versions.compose.get() + } + compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = Versions.java + targetCompatibility = Versions.java } + kotlinOptions { - jvmTarget = "1.8" + jvmTarget = Versions.java.toString() } } dependencies { + implementation(project(ProjectPaths.Core.DESIGN_SYSTEM)) + implementation(libs.androidx.core) implementation(libs.androidx.appcompat) implementation(libs.material) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso) -} \ No newline at end of file + + implementation(libs.androidx.compose) + implementation(libs.androidx.compose.material) + + implementation("androidx.glance:glance-appwidget:1.1.0") + implementation("androidx.glance:glance-material:1.1.0") + implementation("androidx.glance:glance-material3:1.1.0") + +} From 2cbce4c5c71f13c9ae65ad39fe1d3ebea2af3057 Mon Sep 17 00:00:00 2001 From: uiel Date: Sun, 16 Jun 2024 23:31:32 +0900 Subject: [PATCH 012/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=9C=84?= =?UTF-8?q?=EC=A0=AF=20receiver=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index de07b68d2..8252be9b6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,5 +38,16 @@ + + + + + + + From 434a1faac71dfb380b11363aaa19a0d6d1d80d85 Mon Sep 17 00:00:00 2001 From: uiel Date: Sun, 16 Jun 2024 23:32:01 +0900 Subject: [PATCH 013/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20=EC=9C=84?= =?UTF-8?q?=EC=A0=AF=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EC=8B=9C=EC=8A=A4?= =?UTF-8?q?=ED=85=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/widget/designsystem/Colors.kt | 64 +++++++++++++++++++ .../core/widget/designsystem/WidgetTheme.kt | 25 ++++++++ 2 files changed, 89 insertions(+) create mode 100644 core/widget/src/main/java/team/aliens/dms/android/core/widget/designsystem/Colors.kt create mode 100644 core/widget/src/main/java/team/aliens/dms/android/core/widget/designsystem/WidgetTheme.kt diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/designsystem/Colors.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/designsystem/Colors.kt new file mode 100644 index 000000000..79dd26b77 --- /dev/null +++ b/core/widget/src/main/java/team/aliens/dms/android/core/widget/designsystem/Colors.kt @@ -0,0 +1,64 @@ +package team.aliens.dms.android.core.widget.designsystem + +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.glance.unit.ColorProvider +import team.aliens.dms.android.core.designsystem.ErrorDarken2 +import team.aliens.dms.android.core.designsystem.ErrorDefault +import team.aliens.dms.android.core.designsystem.ErrorLighten2 +import team.aliens.dms.android.core.designsystem.Gray1 +import team.aliens.dms.android.core.designsystem.Gray10 +import team.aliens.dms.android.core.designsystem.Gray2 +import team.aliens.dms.android.core.designsystem.Gray3 +import team.aliens.dms.android.core.designsystem.Gray5 +import team.aliens.dms.android.core.designsystem.Gray6 +import team.aliens.dms.android.core.designsystem.Gray7 +import team.aliens.dms.android.core.designsystem.Gray9 +import team.aliens.dms.android.core.designsystem.PrimaryDarken2 +import team.aliens.dms.android.core.designsystem.PrimaryDefault +import team.aliens.dms.android.core.designsystem.PrimaryLighten2 + +data class ColorProviders( + val primary: ColorProvider, + val onPrimary: ColorProvider, + val primaryContainer: ColorProvider, + val onPrimaryContainer: ColorProvider, + val error: ColorProvider, + val onError: ColorProvider, + val errorContainer: ColorProvider, + val onErrorContainer: ColorProvider, + val background: ColorProvider, + val onBackground: ColorProvider, + val backgroundVariant: ColorProvider, + val onBackgroundVariant: ColorProvider, + val surface: ColorProvider, + val onSurface: ColorProvider, + val surfaceVariant: ColorProvider, + val onSurfaceVariant: ColorProvider, + val icon: ColorProvider, + val line: ColorProvider, +) + +fun dynamicThemeColorProviders(): ColorProviders { + return ColorProviders( + primary = ColorProvider(PrimaryDefault), + onPrimary = ColorProvider(Gray1), + primaryContainer = ColorProvider(PrimaryLighten2), + onPrimaryContainer = ColorProvider(PrimaryDarken2), + error = ColorProvider(ErrorDefault), + onError = ColorProvider(Gray1), + errorContainer = ColorProvider(ErrorLighten2), + onErrorContainer = ColorProvider(ErrorDarken2), + background = ColorProvider(Gray2), + onBackground = ColorProvider(Gray10), + backgroundVariant = ColorProvider(Gray7), + onBackgroundVariant = ColorProvider(Gray3), + surface = ColorProvider(Gray1), + onSurface = ColorProvider(Gray9), + surfaceVariant = ColorProvider(Gray6), + onSurfaceVariant = ColorProvider(Gray5), + icon = ColorProvider(Gray5), + line = ColorProvider(Gray3), + ) +} + +internal val LocalColorProviders = staticCompositionLocalOf { dynamicThemeColorProviders() } diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/designsystem/WidgetTheme.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/designsystem/WidgetTheme.kt new file mode 100644 index 000000000..88c70d9ab --- /dev/null +++ b/core/widget/src/main/java/team/aliens/dms/android/core/widget/designsystem/WidgetTheme.kt @@ -0,0 +1,25 @@ +package team.aliens.dms.android.core.widget.designsystem + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.ReadOnlyComposable + +object WidgetTheme { + val colors: ColorProviders + @Composable + @ReadOnlyComposable + get() = LocalColorProviders.current + +} + + +@Composable +fun WidgetTheme( + colors: ColorProviders = WidgetTheme.colors, + content: @Composable () -> Unit, +) { + CompositionLocalProvider(LocalColorProviders provides colors) { + content() + } +} + From 5a2ab9aabdc58eb4485f4a10aacc8466af5ae7fd Mon Sep 17 00:00:00 2001 From: uiel Date: Sun, 16 Jun 2024 23:32:31 +0900 Subject: [PATCH 014/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20=EC=9C=84?= =?UTF-8?q?=EC=A0=AF=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dms/android/core/widget/MealAppWidget.kt | 4 - .../android/core/widget/MealWidgetReceiver.kt | 9 --- .../core/widget/meal/MealGlanceWidget.kt | 26 +++++++ .../dms/android/core/widget/meal/MealInfo.kt | 5 ++ .../android/core/widget/meal/MealWidget.kt | 78 +++++++++++++++++++ .../core/widget/meal/MealWidgetReceiver.kt | 18 +++++ 6 files changed, 127 insertions(+), 13 deletions(-) delete mode 100644 core/widget/src/main/java/team/aliens/dms/android/core/widget/MealAppWidget.kt delete mode 100644 core/widget/src/main/java/team/aliens/dms/android/core/widget/MealWidgetReceiver.kt create mode 100644 core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealGlanceWidget.kt create mode 100644 core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealInfo.kt create mode 100644 core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWidget.kt create mode 100644 core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWidgetReceiver.kt diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/MealAppWidget.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/MealAppWidget.kt deleted file mode 100644 index 9973e9b8e..000000000 --- a/core/widget/src/main/java/team/aliens/dms/android/core/widget/MealAppWidget.kt +++ /dev/null @@ -1,4 +0,0 @@ -package team.aliens.dms.android.core.widget - -class MealAppWidget { -} \ No newline at end of file diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/MealWidgetReceiver.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/MealWidgetReceiver.kt deleted file mode 100644 index cd667f093..000000000 --- a/core/widget/src/main/java/team/aliens/dms/android/core/widget/MealWidgetReceiver.kt +++ /dev/null @@ -1,9 +0,0 @@ -package team.aliens.dms.android.core.widget - -import androidx.glance.appwidget.GlanceAppWidget -import androidx.glance.appwidget.GlanceAppWidgetReceiver - -class MealWidgetService : GlanceAppWidgetReceiver() { - override val glanceAppWidget: GlanceAppWidget - get() = TODO("Not yet implemented") -} \ No newline at end of file diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealGlanceWidget.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealGlanceWidget.kt new file mode 100644 index 000000000..4c2bb9169 --- /dev/null +++ b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealGlanceWidget.kt @@ -0,0 +1,26 @@ +package team.aliens.dms.android.core.widget.meal + +import android.content.Context +import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.unit.dp +import androidx.glance.GlanceId +import androidx.glance.appwidget.GlanceAppWidget +import androidx.glance.appwidget.SizeMode +import androidx.glance.appwidget.provideContent + +class MealGlanceWidget : GlanceAppWidget() { + + companion object { + private val smallMode = DpSize(120.dp, 120.dp) + private val bigMode = DpSize(180.dp, 180.dp) + } + + override suspend fun provideGlance(context: Context, id: GlanceId) { + provideContent { + MealWidget() + } + } + + override val sizeMode: SizeMode + get() = SizeMode.Responsive(setOf(smallMode, bigMode)) +} \ No newline at end of file diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealInfo.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealInfo.kt new file mode 100644 index 000000000..8a7273661 --- /dev/null +++ b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealInfo.kt @@ -0,0 +1,5 @@ +package team.aliens.dms.android.core.widget.meal + + +sealed interface MealInfo { +} \ No newline at end of file diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWidget.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWidget.kt new file mode 100644 index 000000000..59f497779 --- /dev/null +++ b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWidget.kt @@ -0,0 +1,78 @@ +package team.aliens.dms.android.core.widget.meal + +import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.glance.GlanceModifier +import androidx.glance.Image +import androidx.glance.ImageProvider +import androidx.glance.LocalSize +import androidx.glance.background +import androidx.glance.layout.Alignment +import androidx.glance.layout.Column +import androidx.glance.layout.Row +import androidx.glance.layout.Spacer +import androidx.glance.layout.fillMaxHeight +import androidx.glance.layout.fillMaxSize +import androidx.glance.layout.padding +import androidx.glance.layout.width +import androidx.glance.text.FontWeight +import androidx.glance.text.Text +import androidx.glance.text.TextStyle +import team.aliens.dms.android.core.designsystem.DmsIcon +import team.aliens.dms.android.core.designsystem.DmsTheme +import team.aliens.dms.android.core.widget.designsystem.WidgetTheme + +@Composable +internal fun MealWidget() { + val size = LocalSize.current + Row( + modifier = GlanceModifier + .fillMaxSize() + .padding( + horizontal = 18.dp, + vertical = 16.dp, + ) + .background(DmsTheme.colorScheme.background), + ) { + Column( + modifier = GlanceModifier.fillMaxHeight() + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + Image( + provider = ImageProvider(DmsIcon.BlueBreakfast), + contentDescription = "d", + ) + Spacer(GlanceModifier.width(4.dp)) + Text( + text = "아침", + style = TextStyle( + color = WidgetTheme.colors.primary, + fontSize = 14.sp, + fontWeight = FontWeight.Bold, + ) + ) + } + Spacer(GlanceModifier.defaultWeight()) + Text( + text = "kal", + style = TextStyle( + color = WidgetTheme.colors.onSurfaceVariant, + fontSize = 12.sp, + fontWeight = FontWeight.Normal, + ) + ) + } + Spacer(GlanceModifier.defaultWeight()) + Text( + text = "ddsfdsfsdfdss", + style = TextStyle( + color = WidgetTheme.colors.surfaceVariant, + fontSize = 12.sp, + fontWeight = FontWeight.Normal, + ) + ) + } +} \ No newline at end of file diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWidgetReceiver.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWidgetReceiver.kt new file mode 100644 index 000000000..333fd60de --- /dev/null +++ b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWidgetReceiver.kt @@ -0,0 +1,18 @@ +package team.aliens.dms.android.core.widget.meal + +import android.content.Context +import androidx.glance.appwidget.GlanceAppWidget +import androidx.glance.appwidget.GlanceAppWidgetReceiver + +class MealWidgetReceiver : GlanceAppWidgetReceiver() { + override val glanceAppWidget: GlanceAppWidget + get() = MealGlanceWidget() + + override fun onEnabled(context: Context?) { + super.onEnabled(context) + } + + override fun onDisabled(context: Context?) { + super.onDisabled(context) + } +} From 2cea60442b4e85ac726f4bc672c7ef5f39042f6f Mon Sep 17 00:00:00 2001 From: uiel Date: Mon, 17 Jun 2024 23:46:57 +0900 Subject: [PATCH 015/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20meta=20data?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fe262484b..b1728056f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -44,5 +44,11 @@ + + From 09808e5b1b9665fa7ed7c75276061c56f42c5b95 Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 18 Jun 2024 23:37:57 +0900 Subject: [PATCH 016/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/widget/build.gradle.kts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/widget/build.gradle.kts b/core/widget/build.gradle.kts index e595b9e50..ce7b9960e 100644 --- a/core/widget/build.gradle.kts +++ b/core/widget/build.gradle.kts @@ -46,6 +46,10 @@ dependencies { implementation(project(ProjectPaths.Core.DESIGN_SYSTEM)) + implementation(project(ProjectPaths.DATA)) + + implementation(project(ProjectPaths.Shared.DATE)) + implementation(libs.androidx.core) implementation(libs.androidx.appcompat) implementation(libs.material) @@ -60,4 +64,5 @@ dependencies { implementation("androidx.glance:glance-material:1.1.0") implementation("androidx.glance:glance-material3:1.1.0") + implementation("androidx.work:work-runtime-ktx:2.9.0") } From baf18b1706e9ab7b81c0871579c52c182af44fae Mon Sep 17 00:00:00 2001 From: uiel Date: Wed, 19 Jun 2024 23:47:43 +0900 Subject: [PATCH 017/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20work=20di=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/aliens/dms/android/app/DmsApplication.kt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/team/aliens/dms/android/app/DmsApplication.kt b/app/src/main/java/team/aliens/dms/android/app/DmsApplication.kt index 31b2ca8d8..5ff142893 100644 --- a/app/src/main/java/team/aliens/dms/android/app/DmsApplication.kt +++ b/app/src/main/java/team/aliens/dms/android/app/DmsApplication.kt @@ -1,7 +1,19 @@ package team.aliens.dms.android.app import android.app.Application +import androidx.hilt.work.HiltWorkerFactory +import androidx.work.Configuration import dagger.hilt.android.HiltAndroidApp +import javax.inject.Inject @HiltAndroidApp -class DmsApplication : Application() +class DmsApplication : Application(), Configuration.Provider { + + @Inject + lateinit var workFactory: HiltWorkerFactory + + override val workManagerConfiguration: Configuration + get() = Configuration.Builder() + .setWorkerFactory(workFactory) + .build() +} From eb179d76922bd9dc7425e56dddb535cec30a95b7 Mon Sep 17 00:00:00 2001 From: uiel Date: Thu, 20 Jun 2024 23:36:09 +0900 Subject: [PATCH 018/111] =?UTF-8?q?=E2=9A=B0=EF=B8=8F=20::=20mealWidget=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/core/widget/meal/MealWidget.kt | 78 ------------------- 1 file changed, 78 deletions(-) delete mode 100644 core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWidget.kt diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWidget.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWidget.kt deleted file mode 100644 index 59f497779..000000000 --- a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWidget.kt +++ /dev/null @@ -1,78 +0,0 @@ -package team.aliens.dms.android.core.widget.meal - -import androidx.compose.runtime.Composable -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.glance.GlanceModifier -import androidx.glance.Image -import androidx.glance.ImageProvider -import androidx.glance.LocalSize -import androidx.glance.background -import androidx.glance.layout.Alignment -import androidx.glance.layout.Column -import androidx.glance.layout.Row -import androidx.glance.layout.Spacer -import androidx.glance.layout.fillMaxHeight -import androidx.glance.layout.fillMaxSize -import androidx.glance.layout.padding -import androidx.glance.layout.width -import androidx.glance.text.FontWeight -import androidx.glance.text.Text -import androidx.glance.text.TextStyle -import team.aliens.dms.android.core.designsystem.DmsIcon -import team.aliens.dms.android.core.designsystem.DmsTheme -import team.aliens.dms.android.core.widget.designsystem.WidgetTheme - -@Composable -internal fun MealWidget() { - val size = LocalSize.current - Row( - modifier = GlanceModifier - .fillMaxSize() - .padding( - horizontal = 18.dp, - vertical = 16.dp, - ) - .background(DmsTheme.colorScheme.background), - ) { - Column( - modifier = GlanceModifier.fillMaxHeight() - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - ) { - Image( - provider = ImageProvider(DmsIcon.BlueBreakfast), - contentDescription = "d", - ) - Spacer(GlanceModifier.width(4.dp)) - Text( - text = "아침", - style = TextStyle( - color = WidgetTheme.colors.primary, - fontSize = 14.sp, - fontWeight = FontWeight.Bold, - ) - ) - } - Spacer(GlanceModifier.defaultWeight()) - Text( - text = "kal", - style = TextStyle( - color = WidgetTheme.colors.onSurfaceVariant, - fontSize = 12.sp, - fontWeight = FontWeight.Normal, - ) - ) - } - Spacer(GlanceModifier.defaultWeight()) - Text( - text = "ddsfdsfsdfdss", - style = TextStyle( - color = WidgetTheme.colors.surfaceVariant, - fontSize = 12.sp, - fontWeight = FontWeight.Normal, - ) - ) - } -} \ No newline at end of file From 0e89f42113fb82d1593e9e1b4e4560a73f7fb980 Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 21 Jun 2024 23:55:37 +0900 Subject: [PATCH 019/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20provider=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8252be9b6..a7a643153 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,17 @@ android:name="android.appwidget.provider" android:resource="@xml/big_meal_widget_provider"/> + + + + + From 5610a2152fe647d104bd4715f1a4a01e36207fbd Mon Sep 17 00:00:00 2001 From: uiel Date: Sat, 22 Jun 2024 18:33:03 +0900 Subject: [PATCH 020/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=9C=84?= =?UTF-8?q?=EC=A0=AF=20=EC=9D=B4=EB=A6=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a7a643153..0b7db6c7f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -41,6 +41,7 @@ From 524cb65b3cf61a65e98bd4bef7d30ec6ec022413 Mon Sep 17 00:00:00 2001 From: uiel Date: Sat, 22 Jun 2024 22:43:13 +0900 Subject: [PATCH 021/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20=EA=B8=89?= =?UTF-8?q?=EC=8B=9D=20=EC=9C=84=EC=A0=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/widget/meal/MealGlanceWidget.kt | 186 +++++++++++++++++- .../dms/android/core/widget/meal/MealInfo.kt | 21 +- .../widget/meal/MealInfoStateDefinition.kt | 51 +++++ .../dms/android/core/widget/meal/MealState.kt | 7 + .../dms/android/core/widget/meal/MealType.kt | 35 ++++ .../core/widget/meal/MealWidgetReceiver.kt | 6 +- .../android/core/widget/meal/MealWorker.kt | 95 +++++++++ .../core/widget/meal/mapper/MealMapper.kt | 14 ++ .../src/main/res/layout/big_widget_meal.xml | 3 - core/widget/src/main/res/values/string.xml | 5 + .../main/res/xml/big_meal_widget_provider.xml | 5 +- 11 files changed, 411 insertions(+), 17 deletions(-) create mode 100644 core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealInfoStateDefinition.kt create mode 100644 core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealState.kt create mode 100644 core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealType.kt create mode 100644 core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWorker.kt create mode 100644 core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/mapper/MealMapper.kt create mode 100644 core/widget/src/main/res/values/string.xml diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealGlanceWidget.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealGlanceWidget.kt index 4c2bb9169..ba30cbfb8 100644 --- a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealGlanceWidget.kt +++ b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealGlanceWidget.kt @@ -1,19 +1,41 @@ package team.aliens.dms.android.core.widget.meal import android.content.Context -import androidx.compose.ui.unit.DpSize +import androidx.compose.runtime.Composable import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.glance.GlanceId +import androidx.glance.GlanceModifier +import androidx.glance.GlanceTheme +import androidx.glance.Image +import androidx.glance.ImageProvider import androidx.glance.appwidget.GlanceAppWidget -import androidx.glance.appwidget.SizeMode +import androidx.glance.appwidget.lazy.LazyColumn +import androidx.glance.appwidget.lazy.items import androidx.glance.appwidget.provideContent +import androidx.glance.background +import androidx.glance.currentState +import androidx.glance.layout.Alignment +import androidx.glance.layout.Box +import androidx.glance.layout.Column +import androidx.glance.layout.Row +import androidx.glance.layout.Spacer +import androidx.glance.layout.fillMaxHeight +import androidx.glance.layout.fillMaxSize +import androidx.glance.layout.padding +import androidx.glance.layout.width +import androidx.glance.layout.wrapContentWidth +import androidx.glance.state.GlanceStateDefinition +import androidx.glance.text.FontWeight +import androidx.glance.text.Text +import androidx.glance.text.TextStyle +import team.aliens.dms.android.core.designsystem.DmsTheme +import team.aliens.dms.android.core.widget.designsystem.WidgetTheme class MealGlanceWidget : GlanceAppWidget() { - companion object { - private val smallMode = DpSize(120.dp, 120.dp) - private val bigMode = DpSize(180.dp, 180.dp) - } + override val stateDefinition: GlanceStateDefinition<*> + get() = MealInfoStateDefinition override suspend fun provideGlance(context: Context, id: GlanceId) { provideContent { @@ -21,6 +43,152 @@ class MealGlanceWidget : GlanceAppWidget() { } } - override val sizeMode: SizeMode - get() = SizeMode.Responsive(setOf(smallMode, bigMode)) -} \ No newline at end of file + @Composable + internal fun MealWidget() { + val mealInfo = currentState() + + GlanceTheme { + when (mealInfo) { + MealInfo.Loading -> Loading() + + is MealInfo.Unavailable -> Unavailable() + + is MealInfo.Available -> { + when (MealType.getCurrentMealType()) { + MealType.Breakfast -> { + MealBig( + mealState = MealState( + mealType = MealType.Breakfast, + meal = mealInfo.breakfast, + calories = mealInfo.kcalOfBreakfast, + ) + ) + } + + MealType.Launch -> { + MealBig( + mealState = MealState( + mealType = MealType.Launch, + meal = mealInfo.lunch, + calories = mealInfo.kcalOfLunch, + ) + ) + } + + MealType.Dinner -> { + MealBig( + mealState = MealState( + mealType = MealType.Dinner, + meal = mealInfo.dinner, + calories = mealInfo.kcalOfDinner, + ) + ) + } + } + } + } + } + } + + @Composable + private fun MealBig( + mealState: MealState + ) { + Row( + modifier = GlanceModifier + .fillMaxSize() + .padding( + horizontal = 18.dp, + vertical = 16.dp, + ) + .background(DmsTheme.colorScheme.background), + ) { + Column( + modifier = GlanceModifier.fillMaxHeight() + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + Image( + provider = ImageProvider(mealState.mealType.icon), + contentDescription = null, + ) + Spacer(GlanceModifier.width(4.dp)) + Text( + text = mealState.mealType.title, + style = TextStyle( + color = WidgetTheme.colors.primary, + fontSize = 14.sp, + fontWeight = FontWeight.Bold, + ) + ) + } + Spacer(GlanceModifier.defaultWeight()) + Text( + text = mealState.calories, + style = TextStyle( + color = WidgetTheme.colors.onSurfaceVariant, + fontSize = 12.sp, + fontWeight = FontWeight.Normal, + ) + ) + } + LazyColumn( + modifier = GlanceModifier + .fillMaxHeight() + .wrapContentWidth(), + horizontalAlignment = Alignment.End, + ) { + items(mealState.meal) { + Text( + text = it, + style = TextStyle( + color = WidgetTheme.colors.surfaceVariant, + fontSize = 12.sp, + fontWeight = FontWeight.Normal, + ) + ) + } + } + } + } +} + +@Composable +private fun Loading() { + Box( + modifier = GlanceModifier + .fillMaxSize() + .background(DmsTheme.colorScheme.background), + contentAlignment = Alignment.Center, + ) { + Text( + text = "로딩중...", + style = TextStyle( + color = WidgetTheme.colors.primary, + fontSize = 14.sp, + fontWeight = FontWeight.Bold, + ) + ) + } + +} + +@Composable +private fun Unavailable() { + Box( + modifier = GlanceModifier + .fillMaxSize() + .background(DmsTheme.colorScheme.background), + contentAlignment = Alignment.Center, + ) { + Text( + text = "급식을 불러오지 못했어요", + style = TextStyle( + color = WidgetTheme.colors.primary, + fontSize = 14.sp, + fontWeight = FontWeight.Bold, + ) + ) + } +} diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealInfo.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealInfo.kt index 8a7273661..48041a40c 100644 --- a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealInfo.kt +++ b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealInfo.kt @@ -1,5 +1,24 @@ package team.aliens.dms.android.core.widget.meal +import kotlinx.serialization.Serializable +@Serializable sealed interface MealInfo { -} \ No newline at end of file + + @Serializable + data object Loading : MealInfo + + @Serializable + data class Available( + val date: String, + val breakfast: List, + val kcalOfBreakfast: String, + val lunch: List, + val kcalOfLunch: String, + val dinner: List, + val kcalOfDinner: String, + ) : MealInfo + + @Serializable + data object Unavailable : MealInfo +} diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealInfoStateDefinition.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealInfoStateDefinition.kt new file mode 100644 index 000000000..c68684f38 --- /dev/null +++ b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealInfoStateDefinition.kt @@ -0,0 +1,51 @@ +package team.aliens.dms.android.core.widget.meal + +import android.content.Context +import androidx.datastore.core.CorruptionException +import androidx.datastore.core.DataStore +import androidx.datastore.core.Serializer +import androidx.datastore.dataStore +import androidx.datastore.dataStoreFile +import androidx.glance.state.GlanceStateDefinition +import kotlinx.serialization.SerializationException +import kotlinx.serialization.json.Json +import java.io.File +import java.io.InputStream +import java.io.OutputStream + +object MealInfoStateDefinition : GlanceStateDefinition { + + private const val DATA_STORE_FILENAME = "mealInfo" + + private val Context.datastore by dataStore(DATA_STORE_FILENAME, MealInfoSerializer) + override suspend fun getDataStore(context: Context, fileKey: String): DataStore { + return context.datastore + } + + override fun getLocation(context: Context, fileKey: String): File { + return context.dataStoreFile(DATA_STORE_FILENAME) + } + + object MealInfoSerializer : Serializer { + override val defaultValue: MealInfo + get() = MealInfo.Loading + + override suspend fun readFrom(input: InputStream): MealInfo = try { + Json.decodeFromString( + MealInfo.serializer(), + input.readBytes().decodeToString() + ) + } catch (exception: SerializationException) { + throw CorruptionException("Could not read meal data: ${exception.message}") + } + + override suspend fun writeTo(t: MealInfo, output: OutputStream) { + output.use { + it.write( + Json.encodeToString(MealInfo.serializer(), t).encodeToByteArray() + ) + } + } + + } +} diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealState.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealState.kt new file mode 100644 index 000000000..2755db5f9 --- /dev/null +++ b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealState.kt @@ -0,0 +1,7 @@ +package team.aliens.dms.android.core.widget.meal + +data class MealState( + val mealType: MealType = MealType.Breakfast, + val meal: List = emptyList(), + val calories: String = "", +) diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealType.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealType.kt new file mode 100644 index 000000000..62554e6bd --- /dev/null +++ b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealType.kt @@ -0,0 +1,35 @@ +package team.aliens.dms.android.core.widget.meal + +import team.aliens.dms.android.core.designsystem.DmsIcon +import team.aliens.dms.android.shared.date.util.now + +private const val BreakfastStartTime: Int = 9 +private const val LunchStartTime: Int = 13 +private const val DinnerStartTime: Int = 19 + +enum class MealType( + val icon: Int, + val title: String, +) { + Breakfast( + icon = DmsIcon.BlueBreakfast, + title = "아침", + ), + Launch( + icon = DmsIcon.BlueLaunch, + title = "점심", + ), + Dinner( + icon = DmsIcon.BlueDinner, + title = "저녁", + ), + ; + + companion object { + internal fun getCurrentMealType(): MealType = when (now.hour) { + in BreakfastStartTime until LunchStartTime -> Launch + in LunchStartTime until DinnerStartTime -> Dinner + else -> Breakfast + } + } +} diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWidgetReceiver.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWidgetReceiver.kt index 333fd60de..bbf99b5b7 100644 --- a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWidgetReceiver.kt +++ b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWidgetReceiver.kt @@ -8,11 +8,13 @@ class MealWidgetReceiver : GlanceAppWidgetReceiver() { override val glanceAppWidget: GlanceAppWidget get() = MealGlanceWidget() - override fun onEnabled(context: Context?) { + override fun onEnabled(context: Context) { super.onEnabled(context) + MealWorker.enqueue(context) } - override fun onDisabled(context: Context?) { + override fun onDisabled(context: Context) { super.onDisabled(context) + MealWorker.cancel(context) } } diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWorker.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWorker.kt new file mode 100644 index 000000000..71a160c1c --- /dev/null +++ b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWorker.kt @@ -0,0 +1,95 @@ +package team.aliens.dms.android.core.widget.meal + +import android.content.Context +import android.os.Build +import androidx.glance.GlanceId +import androidx.glance.appwidget.GlanceAppWidgetManager +import androidx.glance.appwidget.state.updateAppWidgetState +import androidx.glance.appwidget.updateAll +import androidx.hilt.work.hiltWorker +import androidx.work.Constraints +import androidx.work.CoroutineWorker +import androidx.work.ExistingPeriodicWorkPolicy +import androidx.work.PeriodicWorkRequestBuilder +import androidx.work.WorkManager +import androidx.work.WorkerParameters +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import team.aliens.dms.android.core.widget.meal.mapper.toEntity +import team.aliens.dms.android.data.meal.repository.MealRepository +import team.aliens.dms.android.shared.date.util.now +import team.aliens.dms.android.shared.date.util.today +import java.time.Duration + +@dagger.hiltWorker +class MealWorker @AssistedInject constructor( + @Assisted val context: Context, + @Assisted workerParameters: WorkerParameters, + private val mealRepository: MealRepository, +) : CoroutineWorker(context, workerParameters) { + + companion object { + private val uniqueWorkName = MealWorker::class.java.simpleName + + internal fun enqueue(context: Context) { + val manager = WorkManager.getInstance(context) + val requestBuilder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + PeriodicWorkRequestBuilder(Duration.ofHours(8)) + } else { + TODO("VERSION.SDK_INT < O") + } + val constrains = Constraints.Builder() + .setRequiresBatteryNotLow(true) + .build() + + manager.enqueueUniquePeriodicWork( + uniqueWorkName, + ExistingPeriodicWorkPolicy.KEEP, + requestBuilder + .setConstraints(constrains) + .build(), + ) + } + + internal fun cancel(context: Context) { + WorkManager.getInstance(context).cancelUniqueWork(uniqueWorkName) + } + } + + override suspend fun doWork(): Result { + val manager = GlanceAppWidgetManager(context) + val glanceIds = manager.getGlanceIds(MealGlanceWidget::class.java) + + return try { + setWidgetState(glanceIds, MealInfo.Loading) + + try { + val mealDate = if (now.hour > 19) today.plusDays(1) else today + val response = mealRepository.fetchMeal(mealDate) + setWidgetState(glanceIds, response.toEntity()) + } catch (e: Exception) { + Result.failure() + } + Result.success() + } catch (e: Exception) { + setWidgetState(glanceIds, MealInfo.Unavailable) + e.printStackTrace() + Result.failure() + } + } + + private suspend fun setWidgetState( + glanceIds: List, + newState: MealInfo, + ) { + glanceIds.forEach { glanceId -> + updateAppWidgetState( + context = context, + glanceId = glanceId, + definition = MealInfoStateDefinition, + updateState = { newState }, + ) + } + MealGlanceWidget().updateAll(context) + } +} diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/mapper/MealMapper.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/mapper/MealMapper.kt new file mode 100644 index 000000000..7e5dfc030 --- /dev/null +++ b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/mapper/MealMapper.kt @@ -0,0 +1,14 @@ +package team.aliens.dms.android.core.widget.meal.mapper + +import team.aliens.dms.android.core.widget.meal.MealInfo +import team.aliens.dms.android.data.meal.model.Meal + +internal fun Meal.toEntity() = MealInfo.Available( + date = this.date.toString(), + breakfast = this.breakfast, + kcalOfBreakfast = this.kcalOfBreakfast!!, + lunch = this.lunch, + kcalOfLunch = this.kcalOfLunch!!, + dinner = this.dinner, + kcalOfDinner = this.kcalOfDinner!!, +) diff --git a/core/widget/src/main/res/layout/big_widget_meal.xml b/core/widget/src/main/res/layout/big_widget_meal.xml index 643cc7a2a..efba4fb68 100644 --- a/core/widget/src/main/res/layout/big_widget_meal.xml +++ b/core/widget/src/main/res/layout/big_widget_meal.xml @@ -22,7 +22,6 @@ + + 급식 메뉴 + 빠르게 오늘 급식을 확인해보세요 + \ No newline at end of file diff --git a/core/widget/src/main/res/xml/big_meal_widget_provider.xml b/core/widget/src/main/res/xml/big_meal_widget_provider.xml index 1d8bcc168..2ceb405bb 100644 --- a/core/widget/src/main/res/xml/big_meal_widget_provider.xml +++ b/core/widget/src/main/res/xml/big_meal_widget_provider.xml @@ -4,7 +4,8 @@ android:minWidth="240dp" android:minHeight="50dp" android:previewImage="@drawable/widget_big_meal" + android:description="@string/meal_widget_description" android:resizeMode="horizontal|vertical" - android:updatePeriodMillis="1800000" + android:updatePeriodMillis="3600000" android:widgetCategory="home_screen"> - \ No newline at end of file + From 15cc6fa5cbca4e6497239ae867d9c0f027dfe790 Mon Sep 17 00:00:00 2001 From: uiel Date: Sat, 22 Jun 2024 23:15:56 +0900 Subject: [PATCH 022/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 5 +++++ core/widget/build.gradle.kts | 22 ++++++++++++++++++---- gradle/libs.versions.toml | 13 +++++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 80f0335f3..00089bed5 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -124,4 +124,9 @@ dependencies { androidTestImplementation(libs.androidx.junit) implementation(libs.app.update) + + implementation(libs.androidx.work.runtime.ktx) + + implementation(libs.androidx.hilt.work) + ksp(libs.androidx.hilt.compiler) } diff --git a/core/widget/build.gradle.kts b/core/widget/build.gradle.kts index ce7b9960e..d00ab220b 100644 --- a/core/widget/build.gradle.kts +++ b/core/widget/build.gradle.kts @@ -1,6 +1,9 @@ plugins { alias(libs.plugins.android.library) alias(libs.plugins.kotlin.android) + alias(libs.plugins.ksp) + alias(libs.plugins.hilt) + alias(libs.plugins.serialization) } android { @@ -60,9 +63,20 @@ dependencies { implementation(libs.androidx.compose) implementation(libs.androidx.compose.material) - implementation("androidx.glance:glance-appwidget:1.1.0") - implementation("androidx.glance:glance-material:1.1.0") - implementation("androidx.glance:glance-material3:1.1.0") + implementation(libs.threetenbp) - implementation("androidx.work:work-runtime-ktx:2.9.0") + implementation(libs.hilt) + testImplementation(libs.hilt.testing) + ksp(libs.hilt.compiler) + kspTest(libs.hilt.compiler) + + implementation(libs.androidx.glance.appwidget) + + implementation(libs.androidx.work.runtime.ktx) + + implementation(libs.androidx.hilt.work) + ksp(libs.androidx.hilt.compiler) + + implementation (libs.kotlin.stdlib) + implementation(libs.kotlinx.serialization.json) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1d7124e6f..0b28cfa47 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,10 +8,14 @@ appUpdate = "2.1.0" core = "1.12.0" datastorePreferences = "1.0.0" espresso = "3.5.1" +glanceAppwidget = "1.1.0" +hiltWork = "1.2.0" javaxInject = "1" junit = "4.13.2" junitAndroid = "1.1.5" coroutines = "1.7.3" +kotlinStdlib = "2.0.0" +kotlinxSerializationJson = "1.6.3" lifecycle = "2.7.0" material = "1.11.0" materialCompose = "1.2.0" @@ -36,9 +40,14 @@ minSdk = "23" targetSdk = "33" java = "17" junitKtx = "1.1.5" +workRuntime = "2.9.0" +serialization = "2.0.0" [libraries] accompanist-adaptive = { module = "com.google.accompanist:accompanist-adaptive", version.ref = "accompanist" } +androidx-glance-appwidget = { module = "androidx.glance:glance-appwidget", version.ref = "glanceAppwidget" } +androidx-hilt-compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "hiltWork" } +androidx-hilt-work = { module = "androidx.hilt:hilt-work", version.ref = "hiltWork" } androidx-window = { module = "androidx.window:window", version.ref = "androidx-window" } androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activity" } androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" } @@ -59,6 +68,7 @@ androidx-compose-material = { group = "androidx.compose.material3", name = "mate androidx-compose-material-window = { group = "androidx.compose.material3", name = "material3-window-size-class-android", version.ref = "materialCompose" } androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigation" } androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigation" } +androidx-work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntime" } app-update = { module = "com.google.android.play:app-update-ktx", version.ref = "appUpdate" } composeDestinations = { group = "io.github.raamcosta.compose-destinations", name = "animations-core", version.ref = "composeDestination" } composeDestinations-ksp = { group = "io.github.raamcosta.compose-destinations", name = "ksp", version.ref = "composeDestination" } @@ -69,6 +79,8 @@ junit = { module = "junit:junit", version.ref = "junit" } coroutines = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" } coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" } +kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlinStdlib" } +kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } material = { module = "com.google.android.material:material", version.ref = "material" } moshi = { group = "com.squareup.moshi", name = "moshi-kotlin", version.ref = "moshiKotlin" } moshi-codegen = { group = "com.squareup.moshi", name = "moshi-kotlin-codegen", version.ref = "moshiKotlin" } @@ -92,3 +104,4 @@ hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } googleServices = { id = "com.google.gms.google-services", version.ref = "google" } jetbrainsKotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlinJvm" } +serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "serialization" } From 2f38f71736e6108f7c3933ca4416ba60ccdfbdbc Mon Sep 17 00:00:00 2001 From: uiel Date: Sat, 22 Jun 2024 23:16:32 +0900 Subject: [PATCH 023/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=96=B4?= =?UTF-8?q?=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/aliens/dms/android/core/widget/meal/MealWorker.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWorker.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWorker.kt index 71a160c1c..c501f2450 100644 --- a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWorker.kt +++ b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWorker.kt @@ -6,7 +6,7 @@ import androidx.glance.GlanceId import androidx.glance.appwidget.GlanceAppWidgetManager import androidx.glance.appwidget.state.updateAppWidgetState import androidx.glance.appwidget.updateAll -import androidx.hilt.work.hiltWorker +import androidx.hilt.work.HiltWorker import androidx.work.Constraints import androidx.work.CoroutineWorker import androidx.work.ExistingPeriodicWorkPolicy @@ -21,7 +21,7 @@ import team.aliens.dms.android.shared.date.util.now import team.aliens.dms.android.shared.date.util.today import java.time.Duration -@dagger.hiltWorker +@HiltWorker class MealWorker @AssistedInject constructor( @Assisted val context: Context, @Assisted workerParameters: WorkerParameters, From 303485517ed16437312cc399cd9c23b62e701a89 Mon Sep 17 00:00:00 2001 From: uiel Date: Sun, 23 Jun 2024 13:56:39 +0900 Subject: [PATCH 024/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20import=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/aliens/dms/android/network/file/di/ApiServiceModule.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/network/src/main/java/team/aliens/dms/android/network/file/di/ApiServiceModule.kt b/network/src/main/java/team/aliens/dms/android/network/file/di/ApiServiceModule.kt index 95ffccd68..8acc82c11 100644 --- a/network/src/main/java/team/aliens/dms/android/network/file/di/ApiServiceModule.kt +++ b/network/src/main/java/team/aliens/dms/android/network/file/di/ApiServiceModule.kt @@ -5,7 +5,6 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import retrofit2.Retrofit -import retrofit2.create import team.aliens.dms.android.core.network.di.GlobalRetrofitClient import team.aliens.dms.android.network.file.apiservice.FileApiService import javax.inject.Singleton From 0b7a8946ac3e769eb43f8893262852ac1b80854e Mon Sep 17 00:00:00 2001 From: uiel Date: Sun, 23 Jun 2024 19:37:00 +0900 Subject: [PATCH 025/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20style=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/widget/src/main/res/layout/small_widget_meal.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/core/widget/src/main/res/layout/small_widget_meal.xml b/core/widget/src/main/res/layout/small_widget_meal.xml index a6a0aa9bd..da6eab78a 100644 --- a/core/widget/src/main/res/layout/small_widget_meal.xml +++ b/core/widget/src/main/res/layout/small_widget_meal.xml @@ -18,7 +18,6 @@ Date: Sun, 23 Jun 2024 19:38:19 +0900 Subject: [PATCH 026/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EC=8B=9C=EA=B0=84=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/aliens/dms/android/core/widget/meal/MealWorker.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWorker.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWorker.kt index c501f2450..b208a6256 100644 --- a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWorker.kt +++ b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealWorker.kt @@ -34,7 +34,7 @@ class MealWorker @AssistedInject constructor( internal fun enqueue(context: Context) { val manager = WorkManager.getInstance(context) val requestBuilder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - PeriodicWorkRequestBuilder(Duration.ofHours(8)) + PeriodicWorkRequestBuilder(Duration.ofHours(1)) } else { TODO("VERSION.SDK_INT < O") } @@ -64,7 +64,7 @@ class MealWorker @AssistedInject constructor( setWidgetState(glanceIds, MealInfo.Loading) try { - val mealDate = if (now.hour > 19) today.plusDays(1) else today + val mealDate = if (now.hour >= 19) today.plusDays(1) else today val response = mealRepository.fetchMeal(mealDate) setWidgetState(glanceIds, response.toEntity()) } catch (e: Exception) { From f7a9f71c432feeb3abc8b9d1eab1a363dc7b7706 Mon Sep 17 00:00:00 2001 From: uiel Date: Mon, 24 Jun 2024 09:00:08 +0900 Subject: [PATCH 027/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EB=AC=B8?= =?UTF-8?q?=EC=9E=90=EC=97=B4=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/widget/src/main/res/values/string.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/widget/src/main/res/values/string.xml b/core/widget/src/main/res/values/string.xml index 4a4f0cfbf..d7c4818cc 100644 --- a/core/widget/src/main/res/values/string.xml +++ b/core/widget/src/main/res/values/string.xml @@ -1,5 +1,5 @@ 급식 메뉴 - 빠르게 오늘 급식을 확인해보세요 + 오늘 급식을 빠르게 확인해보세요 \ No newline at end of file From 07b31fd594ca3be9a92e925fb53b781db565378f Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 25 Jun 2024 18:48:51 +0900 Subject: [PATCH 028/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EA=B3=B5?= =?UTF-8?q?=EB=B0=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dms/android/core/widget/meal/MealInfoStateDefinition.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealInfoStateDefinition.kt b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealInfoStateDefinition.kt index c68684f38..a086aada2 100644 --- a/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealInfoStateDefinition.kt +++ b/core/widget/src/main/java/team/aliens/dms/android/core/widget/meal/MealInfoStateDefinition.kt @@ -18,6 +18,7 @@ object MealInfoStateDefinition : GlanceStateDefinition { private const val DATA_STORE_FILENAME = "mealInfo" private val Context.datastore by dataStore(DATA_STORE_FILENAME, MealInfoSerializer) + override suspend fun getDataStore(context: Context, fileKey: String): DataStore { return context.datastore } @@ -46,6 +47,5 @@ object MealInfoStateDefinition : GlanceStateDefinition { ) } } - } } From a3f4b66e0c668ab32b14c17794d8cd905790ce01 Mon Sep 17 00:00:00 2001 From: uiel Date: Wed, 26 Jun 2024 09:15:02 +0900 Subject: [PATCH 029/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EC=98=AC=EB=9D=BC=EC=98=A4=EA=B8=B0=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/feature/outing/OutingApplicationScreen.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/feature/src/main/java/team/aliens/dms/android/feature/outing/OutingApplicationScreen.kt b/feature/src/main/java/team/aliens/dms/android/feature/outing/OutingApplicationScreen.kt index ef3ae8acb..4f974d20e 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/outing/OutingApplicationScreen.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/outing/OutingApplicationScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.items @@ -53,10 +54,10 @@ import coil.compose.AsyncImage import com.ramcosta.composedestinations.annotation.Destination import kotlinx.coroutines.launch import org.threeten.bp.DayOfWeek -import org.threeten.bp.LocalDateTime import org.threeten.bp.LocalTime import team.aliens.dms.android.core.designsystem.AlertDialog import team.aliens.dms.android.core.designsystem.Button +import team.aliens.dms.android.core.designsystem.ContainedButton import team.aliens.dms.android.core.designsystem.DmsTheme import team.aliens.dms.android.core.designsystem.DmsTopAppBar import team.aliens.dms.android.core.designsystem.LocalToast @@ -512,12 +513,14 @@ fun OutingApplicationScreen( ) } - Button( + ContainedButton( modifier = Modifier .fillMaxWidth() .horizontalPadding() - .bottomPadding(), + .bottomPadding() + .imePadding(), onClick = { viewModel.postIntent(OutingIntent.ApplyOuting) }, + enabled = uiState.selectedOutingType != null, ) { Text(text = stringResource(id = R.string.outing_do_application)) } From 4553d5469d9f085136c52adf6f1cc2e642f9f55a Mon Sep 17 00:00:00 2001 From: uiel Date: Wed, 26 Jun 2024 09:15:12 +0900 Subject: [PATCH 030/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/team/aliens/dms/android/core/designsystem/Buttons.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/core/design-system/src/main/java/team/aliens/dms/android/core/designsystem/Buttons.kt b/core/design-system/src/main/java/team/aliens/dms/android/core/designsystem/Buttons.kt index 08ab60a4c..955b02d9b 100644 --- a/core/design-system/src/main/java/team/aliens/dms/android/core/designsystem/Buttons.kt +++ b/core/design-system/src/main/java/team/aliens/dms/android/core/designsystem/Buttons.kt @@ -38,6 +38,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +// TODO: Button 접근제한자 internal로 변경 @Composable fun Button( onClick: () -> Unit, From 9c0ea3b496f3cbf15e1eca422fdf8c0d95499946 Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 5 Jul 2024 07:38:31 +0900 Subject: [PATCH 031/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20=EB=94=94?= =?UTF-8?q?=EB=B0=94=EC=9D=B4=EC=8A=A4=20=ED=86=A0=ED=81=B0=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/core/notification/DeviceTokenManager.kt | 12 ++++++++++-- .../repository/NotificationRepositoryImpl.kt | 10 +++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/core/notification/src/main/java/team/aliens/dms/android/core/notification/DeviceTokenManager.kt b/core/notification/src/main/java/team/aliens/dms/android/core/notification/DeviceTokenManager.kt index 4ef3091ad..c4e98e4f8 100644 --- a/core/notification/src/main/java/team/aliens/dms/android/core/notification/DeviceTokenManager.kt +++ b/core/notification/src/main/java/team/aliens/dms/android/core/notification/DeviceTokenManager.kt @@ -1,4 +1,12 @@ package team.aliens.dms.android.core.notification -class DeviceTokenManager { -} \ No newline at end of file +import team.aliens.dms.android.data.notification.repository.NotificationRepository +import javax.inject.Inject + +class DeviceTokenManager @Inject constructor( + private val notificationRepository: NotificationRepository, +) { + suspend operator fun invoke(deviceToken: String) = runCatching { + notificationRepository.registerDeviceNotificationToken(deviceToken = deviceToken) + } +} diff --git a/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepositoryImpl.kt b/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepositoryImpl.kt index e919f759a..8e6727b62 100644 --- a/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepositoryImpl.kt +++ b/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepositoryImpl.kt @@ -4,15 +4,19 @@ import team.aliens.dms.android.network.notification.datasource.NetworkNotificati import team.aliens.dms.android.data.notification.model.Notification import team.aliens.dms.android.data.notification.model.NotificationTopic import team.aliens.dms.android.data.notification.model.NotificationTopicGroup +import team.aliens.dms.android.network.notification.model.RegisterFcmDeviceTokenRequest import javax.inject.Inject internal class NotificationRepositoryImpl @Inject constructor( private val networkNotificationDataSource: NetworkNotificationDataSource, ) : NotificationRepository() { - override suspend fun registerDeviceNotificationToken(deviceToken: String) { - TODO("Not yet implemented") - } + override suspend fun registerDeviceNotificationToken(deviceToken: String) = + networkNotificationDataSource.registerFcmDeviceToken( + request = RegisterFcmDeviceTokenRequest( + deviceToken = deviceToken, + ) + ) override suspend fun cancelDeviceTokenRegistration(deviceToken: String) { TODO("Not yet implemented") From 7a3f17f6473d6191833f0c982f05bb2639afbd6a Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 5 Jul 2024 07:39:01 +0900 Subject: [PATCH 032/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A0=95=EB=A0=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apiservice/NotificationApiService.kt | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/network/src/main/java/team/aliens/dms/android/network/notification/apiservice/NotificationApiService.kt b/network/src/main/java/team/aliens/dms/android/network/notification/apiservice/NotificationApiService.kt index 7865f79c9..56230635b 100644 --- a/network/src/main/java/team/aliens/dms/android/network/notification/apiservice/NotificationApiService.kt +++ b/network/src/main/java/team/aliens/dms/android/network/notification/apiservice/NotificationApiService.kt @@ -17,22 +17,34 @@ import team.aliens.dms.android.network.notification.model.UnsubscribeNotificatio internal interface NotificationApiService { @POST("/notifications/tokens") - suspend fun registerFcmDeviceToken(@Body request: RegisterFcmDeviceTokenRequest) + suspend fun registerFcmDeviceToken( + @Body request: RegisterFcmDeviceTokenRequest + ) @DELETE("/notifications/token") - suspend fun cancelFcmDeviceTokenRegistration(@Body request: CancelFcmDeviceTokenRegistrationRequest) + suspend fun cancelFcmDeviceTokenRegistration( + @Body request: CancelFcmDeviceTokenRegistrationRequest + ) @POST("/notifications/topic") - suspend fun subscribeNotificationTopic(@Body request: SubscribeNotificationTopicRequest) + suspend fun subscribeNotificationTopic( + @Body request: SubscribeNotificationTopicRequest + ) @DELETE("/notifications/topic") - suspend fun unsubscribeNotificationTopic(@Body request: UnsubscribeNotificationTopicRequest) + suspend fun unsubscribeNotificationTopic( + @Body request: UnsubscribeNotificationTopicRequest + ) @PATCH("/notifications/topic") - suspend fun batchUpdateNotificationTopic(@Body request: BatchUpdateNotificationTopicRequest) + suspend fun batchUpdateNotificationTopic( + @Body request: BatchUpdateNotificationTopicRequest + ) @GET("/notifications/topic") - suspend fun fetchNotificationTopicStatus(@Body request: FetchNotificationTopicStatusRequest): FetchNotificationTopicStatusResponse + suspend fun fetchNotificationTopicStatus( + @Body request: FetchNotificationTopicStatusRequest + ): FetchNotificationTopicStatusResponse @GET("/notifications") suspend fun fetchNotifications(): FetchNotificationsResponse From 0b16178df40b78c3680515894d8da0498f35b646 Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 5 Jul 2024 12:23:34 +0900 Subject: [PATCH 033/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=B4=EC=96=B4=EB=B2=A0=EC=9D=B4=EC=8A=A4=20=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/notification/build.gradle.kts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/notification/build.gradle.kts b/core/notification/build.gradle.kts index 0bd967a0e..ce20a1f6b 100644 --- a/core/notification/build.gradle.kts +++ b/core/notification/build.gradle.kts @@ -53,4 +53,8 @@ dependencies { implementation(libs.hilt) ksp(libs.hilt.compiler) + + implementation(platform(libs.firebase.bom)) + implementation(libs.firebase.messaging) + implementation(libs.firebase.analytics) } From a1b124c5eaa74aaeaa8c0d6158bbb9e82b91f2cf Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 5 Jul 2024 12:26:32 +0900 Subject: [PATCH 034/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20NotificationM?= =?UTF-8?q?anager=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/notification/NotificationManager.kt | 78 ++++++++++++++++++- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/core/notification/src/main/java/team/aliens/dms/android/core/notification/NotificationManager.kt b/core/notification/src/main/java/team/aliens/dms/android/core/notification/NotificationManager.kt index 7cbf520e1..91b360433 100644 --- a/core/notification/src/main/java/team/aliens/dms/android/core/notification/NotificationManager.kt +++ b/core/notification/src/main/java/team/aliens/dms/android/core/notification/NotificationManager.kt @@ -1,4 +1,78 @@ package team.aliens.dms.android.core.notification -class NotificationManager { -} \ No newline at end of file +import android.annotation.SuppressLint +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import android.os.Build +import androidx.annotation.RequiresApi +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import team.aliens.dms.android.core.designsystem.DmsIcon + +private object Notifications { + const val NOTIFICATION_ID = 0 + const val NOTIFICATION_CHANNEL_ID = "dms" + const val CHANNEL_NAME = "dms" + const val CHANNEL_DESCRIPTION = "dms notification channel" +} +@RequiresApi(Build.VERSION_CODES.N) +class NotificationManager( + private val context: Context, +) { + + init { + // TODO: 버전 대응하기 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createNotificationChannel() + } + } + + private val notificationManagerCompat: NotificationManagerCompat by lazy { + NotificationManagerCompat.from(context) + } + + private val notificationBuilder: NotificationCompat.Builder by lazy { + NotificationCompat.Builder(context, Notifications.NOTIFICATION_CHANNEL_ID) + .setSmallIcon(DmsIcon.LogoLight) + .setPriority(NotificationCompat.PRIORITY_HIGH) + } + + fun setNotificationContent( + title: String?, + body: String?, + ) { + notificationBuilder.run { + title?.run { setContentTitle(this) } + body?.run { setContentText(this) } + } + } + + @SuppressLint("MissingPermission") + fun sendNotification() { + if(notificationPermissionGranted(context = context)) { + notificationManagerCompat.notify( + Notifications.NOTIFICATION_ID, + notificationBuilder.build(), + ) + } + } + + // TODO: 버전 대응하기 + @RequiresApi(Build.VERSION_CODES.O) + private fun createNotificationChannel() { + val importance = NotificationManager.IMPORTANCE_DEFAULT + val channel = + NotificationChannel( + Notifications.NOTIFICATION_CHANNEL_ID, + Notifications.CHANNEL_NAME, + importance + ).apply { + this.description = Notifications.CHANNEL_DESCRIPTION + } + + val notificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(channel) + } +} From 91665174c7de1f26fde09558bb77f3686f43d059 Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 5 Jul 2024 12:27:00 +0900 Subject: [PATCH 035/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20DeviceTokenMa?= =?UTF-8?q?nger=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/notification/DeviceTokenManager.kt | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/core/notification/src/main/java/team/aliens/dms/android/core/notification/DeviceTokenManager.kt b/core/notification/src/main/java/team/aliens/dms/android/core/notification/DeviceTokenManager.kt index c4e98e4f8..a572da535 100644 --- a/core/notification/src/main/java/team/aliens/dms/android/core/notification/DeviceTokenManager.kt +++ b/core/notification/src/main/java/team/aliens/dms/android/core/notification/DeviceTokenManager.kt @@ -1,12 +1,23 @@ package team.aliens.dms.android.core.notification +import com.google.firebase.messaging.FirebaseMessaging import team.aliens.dms.android.data.notification.repository.NotificationRepository import javax.inject.Inject class DeviceTokenManager @Inject constructor( - private val notificationRepository: NotificationRepository, + //디바이스 토큰 로컬 저장 provider ) { - suspend operator fun invoke(deviceToken: String) = runCatching { - notificationRepository.registerDeviceNotificationToken(deviceToken = deviceToken) + fun getDeviceToken(): String? { + //디바이스 토큰 리턴 + return null + } + + fun fetchDeviceToken(deviceToken: String) { + FirebaseMessaging.getInstance().token.addOnCompleteListener { task -> + if (!task.isSuccessful) { + //Handle error + } + // 디바이스 토큰 등록 + } } } From 2f799089b364eb7cec42f8cce27fc13b69b97aaf Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 5 Jul 2024 12:28:49 +0900 Subject: [PATCH 036/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20GlobalRetrofi?= =?UTF-8?q?tClient=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dms/android/network/notification/di/ApiServiceModule.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/network/src/main/java/team/aliens/dms/android/network/notification/di/ApiServiceModule.kt b/network/src/main/java/team/aliens/dms/android/network/notification/di/ApiServiceModule.kt index 6486a119b..8eebaeccc 100644 --- a/network/src/main/java/team/aliens/dms/android/network/notification/di/ApiServiceModule.kt +++ b/network/src/main/java/team/aliens/dms/android/network/notification/di/ApiServiceModule.kt @@ -5,6 +5,7 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import retrofit2.Retrofit +import team.aliens.dms.android.core.network.di.GlobalRetrofitClient import team.aliens.dms.android.network.notification.apiservice.NotificationApiService import javax.inject.Singleton @@ -14,6 +15,7 @@ internal object ApiServiceModule { @Provides @Singleton - fun provideNotificationApiService(retrofit: Retrofit): NotificationApiService = - retrofit.create(NotificationApiService::class.java) + fun provideNotificationApiService( + @GlobalRetrofitClient retrofit: Retrofit + ): NotificationApiService = retrofit.create(NotificationApiService::class.java) } From 4f80a736314b78cde2cbcbba299fa30f4260c0d7 Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 5 Jul 2024 12:29:50 +0900 Subject: [PATCH 037/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EB=94=94?= =?UTF-8?q?=EB=B0=94=EC=9D=B4=EC=8A=A4=20=ED=86=A0=ED=81=B0=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aliens/dms/android/data/auth/repository/AuthRepository.kt | 1 + .../dms/android/data/auth/repository/AuthRepositoryImpl.kt | 2 ++ .../team/aliens/dms/android/feature/signin/SignInViewModel.kt | 1 + .../team/aliens/dms/android/network/auth/model/SignInRequest.kt | 1 + 4 files changed, 5 insertions(+) diff --git a/data/src/main/java/team/aliens/dms/android/data/auth/repository/AuthRepository.kt b/data/src/main/java/team/aliens/dms/android/data/auth/repository/AuthRepository.kt index c016d320b..d1ab33a8d 100644 --- a/data/src/main/java/team/aliens/dms/android/data/auth/repository/AuthRepository.kt +++ b/data/src/main/java/team/aliens/dms/android/data/auth/repository/AuthRepository.kt @@ -8,6 +8,7 @@ abstract class AuthRepository { abstract suspend fun signIn( accountId: String, password: String, + deviceToken: String, autoSignIn: Boolean = true, ) diff --git a/data/src/main/java/team/aliens/dms/android/data/auth/repository/AuthRepositoryImpl.kt b/data/src/main/java/team/aliens/dms/android/data/auth/repository/AuthRepositoryImpl.kt index f040e1e3e..9be53958e 100644 --- a/data/src/main/java/team/aliens/dms/android/data/auth/repository/AuthRepositoryImpl.kt +++ b/data/src/main/java/team/aliens/dms/android/data/auth/repository/AuthRepositoryImpl.kt @@ -24,6 +24,7 @@ internal class AuthRepositoryImpl @Inject constructor( override suspend fun signIn( accountId: String, password: String, + deviceToken: String, autoSignIn: Boolean, ) { val signInResponse = statusMapping( @@ -35,6 +36,7 @@ internal class AuthRepositoryImpl @Inject constructor( request = SignInRequest( accountId = accountId, password = password, + deviceToken = deviceToken, ), ) } diff --git a/feature/src/main/java/team/aliens/dms/android/feature/signin/SignInViewModel.kt b/feature/src/main/java/team/aliens/dms/android/feature/signin/SignInViewModel.kt index 1a70fb941..6b4659700 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/signin/SignInViewModel.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/signin/SignInViewModel.kt @@ -65,6 +65,7 @@ internal class SignInViewModel @Inject constructor( authRepository.signIn( accountId = uiState.accountId.trim(), password = uiState.password.trim(), + deviceToken = "", ) }.onSuccess { postSideEffect(SignInSideEffect.Success) diff --git a/network/src/main/java/team/aliens/dms/android/network/auth/model/SignInRequest.kt b/network/src/main/java/team/aliens/dms/android/network/auth/model/SignInRequest.kt index 55274cfc6..f81d368b1 100644 --- a/network/src/main/java/team/aliens/dms/android/network/auth/model/SignInRequest.kt +++ b/network/src/main/java/team/aliens/dms/android/network/auth/model/SignInRequest.kt @@ -5,4 +5,5 @@ import com.google.gson.annotations.SerializedName data class SignInRequest( @SerializedName("account_id") val accountId: String, @SerializedName("password") val password: String, + @SerializedName("device_token") val deviceToken: String, ) From 4547a8d82846c1df4a9a41b89f0f87eb43236706 Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 5 Jul 2024 12:31:04 +0900 Subject: [PATCH 038/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20default=20cha?= =?UTF-8?q?nnel=20id=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/values/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c37fe347f..d7a14af03 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,4 +1,5 @@ DMS + team.aliens.dms.android \ No newline at end of file From 95bd4381cbc499a2577c06294f3ddf929e28d273 Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 5 Jul 2024 12:34:42 +0900 Subject: [PATCH 039/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20DmsMessagingS?= =?UTF-8?q?ervice=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/service/DmsMessagingService.kt | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 app/src/main/java/team/aliens/dms/android/app/service/DmsMessagingService.kt diff --git a/app/src/main/java/team/aliens/dms/android/app/service/DmsMessagingService.kt b/app/src/main/java/team/aliens/dms/android/app/service/DmsMessagingService.kt new file mode 100644 index 000000000..fad4180c7 --- /dev/null +++ b/app/src/main/java/team/aliens/dms/android/app/service/DmsMessagingService.kt @@ -0,0 +1,45 @@ +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 + +@RequiresApi(Build.VERSION_CODES.N) +@AndroidEntryPoint +class DmsMessagingService : FirebaseMessagingService() { + + @Inject + lateinit var deviceTokenManager: DeviceTokenManager + + private val notificationManager: NotificationManager by lazy { + NotificationManager(context = this) + } + + override fun onNewToken(token: String) { + super.onNewToken(token) + CoroutineScope(Dispatchers.IO).launch { + // TODO: 디바이스 토큰 로컬에 저장 + } + } + + // TODO: 버전 대응 + @RequiresApi(Build.VERSION_CODES.N) + override fun onMessageReceived(message: RemoteMessage) { + super.onMessageReceived(message) + message.notification?.run { + notificationManager.setNotificationContent( + title = title, + body = body, + ) + } + notificationManager.sendNotification() + } +} From 8e129097be1f27d9b5455a500bc80146d9957147 Mon Sep 17 00:00:00 2001 From: uiel Date: Sat, 6 Jul 2024 13:26:37 +0900 Subject: [PATCH 040/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20device=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/device/.gitignore | 1 + core/device/build.gradle.kts | 43 +++++++++++++++++++ core/device/consumer-rules.pro | 0 core/device/proguard-rules.pro | 21 +++++++++ .../core/device/ExampleInstrumentedTest.kt | 24 +++++++++++ core/device/src/main/AndroidManifest.xml | 4 ++ .../android/core/device/ExampleUnitTest.kt | 17 ++++++++ 7 files changed, 110 insertions(+) create mode 100644 core/device/.gitignore create mode 100644 core/device/build.gradle.kts create mode 100644 core/device/consumer-rules.pro create mode 100644 core/device/proguard-rules.pro create mode 100644 core/device/src/androidTest/java/team/aliens/dms/android/core/device/ExampleInstrumentedTest.kt create mode 100644 core/device/src/main/AndroidManifest.xml create mode 100644 core/device/src/test/java/team/aliens/dms/android/core/device/ExampleUnitTest.kt diff --git a/core/device/.gitignore b/core/device/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/core/device/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/device/build.gradle.kts b/core/device/build.gradle.kts new file mode 100644 index 000000000..dfbbc80d9 --- /dev/null +++ b/core/device/build.gradle.kts @@ -0,0 +1,43 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) +} + +android { + namespace = "team.aliens.dms.android.core.device" + compileSdk = 34 + + defaultConfig { + minSdk = 24 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + implementation(libs.material) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso) +} \ No newline at end of file diff --git a/core/device/consumer-rules.pro b/core/device/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/core/device/proguard-rules.pro b/core/device/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/core/device/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/core/device/src/androidTest/java/team/aliens/dms/android/core/device/ExampleInstrumentedTest.kt b/core/device/src/androidTest/java/team/aliens/dms/android/core/device/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..b5aab263c --- /dev/null +++ b/core/device/src/androidTest/java/team/aliens/dms/android/core/device/ExampleInstrumentedTest.kt @@ -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) + } +} \ No newline at end of file diff --git a/core/device/src/main/AndroidManifest.xml b/core/device/src/main/AndroidManifest.xml new file mode 100644 index 000000000..a5918e68a --- /dev/null +++ b/core/device/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/device/src/test/java/team/aliens/dms/android/core/device/ExampleUnitTest.kt b/core/device/src/test/java/team/aliens/dms/android/core/device/ExampleUnitTest.kt new file mode 100644 index 000000000..2e898951e --- /dev/null +++ b/core/device/src/test/java/team/aliens/dms/android/core/device/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package team.aliens.dms.android.core.device + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file From e006573b29f746fdb550d72329e437509b205fbf Mon Sep 17 00:00:00 2001 From: uiel Date: Sat, 6 Jul 2024 13:27:57 +0900 Subject: [PATCH 041/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EB=B2=84?= =?UTF-8?q?=EC=A0=84=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/device/build.gradle.kts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/device/build.gradle.kts b/core/device/build.gradle.kts index dfbbc80d9..5f4b8551f 100644 --- a/core/device/build.gradle.kts +++ b/core/device/build.gradle.kts @@ -5,10 +5,10 @@ plugins { android { namespace = "team.aliens.dms.android.core.device" - compileSdk = 34 + compileSdk = libs.versions.compileSdk.get().toInt() defaultConfig { - minSdk = 24 + minSdk = libs.versions.minSdk.get().toInt() testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") @@ -24,11 +24,11 @@ android { } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = Versions.java + targetCompatibility = Versions.java } kotlinOptions { - jvmTarget = "1.8" + jvmTarget = Versions.java.toString() } } From de55a0af8e5d9c628a5166b82cab0c95239bcfcb Mon Sep 17 00:00:00 2001 From: uiel Date: Sat, 6 Jul 2024 13:28:07 +0900 Subject: [PATCH 042/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- settings.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/settings.gradle.kts b/settings.gradle.kts index 69cd818af..386b763f9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -38,6 +38,7 @@ include(":core:project") include(":core:school") include(":core:file") include(":core:ui") +include(":core:device") include(":data") include(":network") From 01eba12dbf481fee8096be570249857d957c8a7a Mon Sep 17 00:00:00 2001 From: uiel Date: Sun, 7 Jul 2024 23:32:49 +0900 Subject: [PATCH 043/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20DeviceDataSto?= =?UTF-8?q?reDataSource=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datastore/DeviceDataStoreDataSource.kt | 11 +++++++++++ .../datastore/DeviceDataStoreDataSourceImpl.kt | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 core/device/src/main/java/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSource.kt create mode 100644 core/device/src/main/java/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSourceImpl.kt diff --git a/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSource.kt b/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSource.kt new file mode 100644 index 000000000..6db1676d7 --- /dev/null +++ b/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSource.kt @@ -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() + +} diff --git a/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSourceImpl.kt b/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSourceImpl.kt new file mode 100644 index 000000000..9d403883a --- /dev/null +++ b/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSourceImpl.kt @@ -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() + } +} From 10ccb93b1f64178e9142e83398e7ec14990dbd31 Mon Sep 17 00:00:00 2001 From: uiel Date: Sun, 7 Jul 2024 23:34:14 +0900 Subject: [PATCH 044/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A0=95=EB=A0=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- settings.gradle.kts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 386b763f9..64decd26d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,9 +1,5 @@ @file:Suppress("UnstableApiUsage") -include(":core:notification") - - - pluginManagement { repositories { gradlePluginPortal() @@ -38,6 +34,7 @@ include(":core:project") include(":core:school") include(":core:file") include(":core:ui") +include(":core:notification") include(":core:device") include(":data") From b5f775e5a73512f577e90c6b9c5f6b8275c4d79b Mon Sep 17 00:00:00 2001 From: uiel Date: Sun, 7 Jul 2024 23:35:01 +0900 Subject: [PATCH 045/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/build.gradle.kts | 1 + feature/build.gradle.kts | 1 + 2 files changed, 2 insertions(+) diff --git a/data/build.gradle.kts b/data/build.gradle.kts index 956310c30..c8595d9e3 100644 --- a/data/build.gradle.kts +++ b/data/build.gradle.kts @@ -48,6 +48,7 @@ dependencies { implementation(project(ProjectPaths.Core.JWT)) implementation(project(ProjectPaths.Core.NETWORK)) implementation(project(ProjectPaths.Core.SCHOOL)) + implementation(project(ProjectPaths.Core.DEVICE)) implementation(project(ProjectPaths.DATABASE)) implementation(project(ProjectPaths.NETWORK)) diff --git a/feature/build.gradle.kts b/feature/build.gradle.kts index 933149dc5..85a230cbe 100644 --- a/feature/build.gradle.kts +++ b/feature/build.gradle.kts @@ -57,6 +57,7 @@ dependencies { implementation(project(ProjectPaths.Core.UI)) implementation(project(ProjectPaths.Core.DESIGN_SYSTEM)) implementation(project(ProjectPaths.Core.FILE)) + implementation(project(ProjectPaths.Core.NOTIFICATION)) implementation(project(ProjectPaths.DATA)) From 5052eb1cc709030b741594e72d4158d4e6f9345c Mon Sep 17 00:00:00 2001 From: uiel Date: Thu, 11 Jul 2024 23:30:25 +0900 Subject: [PATCH 046/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/device/build.gradle.kts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/core/device/build.gradle.kts b/core/device/build.gradle.kts index 5f4b8551f..e687bb982 100644 --- a/core/device/build.gradle.kts +++ b/core/device/build.gradle.kts @@ -1,6 +1,8 @@ plugins { alias(libs.plugins.android.library) alias(libs.plugins.kotlin.android) + alias(libs.plugins.hilt) + alias(libs.plugins.ksp) } android { @@ -23,16 +25,19 @@ android { ) } } + 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) @@ -40,4 +45,9 @@ dependencies { testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso) + + implementation(libs.androidx.datastore.preferences) + + implementation(libs.hilt) + ksp(libs.hilt.compiler) } \ No newline at end of file From a42b34b36ae74936427d725e3138333e1e70db5d Mon Sep 17 00:00:00 2001 From: uiel Date: Thu, 11 Jul 2024 23:32:32 +0900 Subject: [PATCH 047/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- buildSrc/src/main/kotlin/ProjectPaths.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/buildSrc/src/main/kotlin/ProjectPaths.kt b/buildSrc/src/main/kotlin/ProjectPaths.kt index 8d93662af..e008680d6 100644 --- a/buildSrc/src/main/kotlin/ProjectPaths.kt +++ b/buildSrc/src/main/kotlin/ProjectPaths.kt @@ -16,6 +16,7 @@ object ProjectPaths { const val UI = ":core:ui" const val FILE = ":core:file" const val NOTIFICATION = ":core:notification" + const val DEVICE = ":core:device" } object Shared { From d67c4a8f16c1e39f02134f7d890ea83f8aecdd98 Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 12 Jul 2024 21:35:57 +0900 Subject: [PATCH 048/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EB=94=94?= =?UTF-8?q?=EB=B0=94=EC=9D=B4=EC=8A=A4=20=ED=86=A0=ED=81=B0=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aliens/dms/android/app/MainActivity.kt | 12 ++++++++++++ .../android/app/service/DmsMessagingService.kt | 6 +++--- .../core/notification/DeviceTokenManager.kt | 18 +++++++++++------- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/team/aliens/dms/android/app/MainActivity.kt b/app/src/main/java/team/aliens/dms/android/app/MainActivity.kt index 06dd942d5..caa86de3d 100644 --- a/app/src/main/java/team/aliens/dms/android/app/MainActivity.kt +++ b/app/src/main/java/team/aliens/dms/android/app/MainActivity.kt @@ -7,11 +7,16 @@ 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 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 @@ -21,6 +26,9 @@ class MainActivity : ComponentActivity() { @Inject lateinit var isJwtAvailable: StateFlow + @Inject + lateinit var deviceTokenManager: DeviceTokenManager + @OptIn(ExperimentalMaterial3WindowSizeClassApi::class) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -42,5 +50,9 @@ class MainActivity : ComponentActivity() { ) } } + + lifecycleScope.launch { + deviceTokenManager.fetchDeviceToken() + } } } diff --git a/app/src/main/java/team/aliens/dms/android/app/service/DmsMessagingService.kt b/app/src/main/java/team/aliens/dms/android/app/service/DmsMessagingService.kt index fad4180c7..6ead0b4c9 100644 --- a/app/src/main/java/team/aliens/dms/android/app/service/DmsMessagingService.kt +++ b/app/src/main/java/team/aliens/dms/android/app/service/DmsMessagingService.kt @@ -23,10 +23,10 @@ class DmsMessagingService : FirebaseMessagingService() { NotificationManager(context = this) } - override fun onNewToken(token: String) { - super.onNewToken(token) + override fun onNewToken(deviceToken: String) { + super.onNewToken(deviceToken) CoroutineScope(Dispatchers.IO).launch { - // TODO: 디바이스 토큰 로컬에 저장 + deviceTokenManager.saveDeviceToken(deviceToken = deviceToken) } } diff --git a/core/notification/src/main/java/team/aliens/dms/android/core/notification/DeviceTokenManager.kt b/core/notification/src/main/java/team/aliens/dms/android/core/notification/DeviceTokenManager.kt index a572da535..c5b160301 100644 --- a/core/notification/src/main/java/team/aliens/dms/android/core/notification/DeviceTokenManager.kt +++ b/core/notification/src/main/java/team/aliens/dms/android/core/notification/DeviceTokenManager.kt @@ -1,23 +1,27 @@ package team.aliens.dms.android.core.notification import com.google.firebase.messaging.FirebaseMessaging +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import team.aliens.dms.android.data.notification.repository.NotificationRepository import javax.inject.Inject class DeviceTokenManager @Inject constructor( - //디바이스 토큰 로컬 저장 provider + private val notificationRepository: NotificationRepository, ) { - fun getDeviceToken(): String? { - //디바이스 토큰 리턴 - return null + suspend fun saveDeviceToken(deviceToken: String) { + notificationRepository.saveDeviceToken(deviceToken = deviceToken) } - fun fetchDeviceToken(deviceToken: String) { + suspend fun fetchDeviceToken() { FirebaseMessaging.getInstance().token.addOnCompleteListener { task -> if (!task.isSuccessful) { - //Handle error + // TODO: Handle error + } + CoroutineScope(Dispatchers.IO).launch { + notificationRepository.saveDeviceToken(deviceToken = task.result) } - // 디바이스 토큰 등록 } } } From d7d15cffafb62520acaddd1487f352a53b8532c6 Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 12 Jul 2024 21:36:20 +0900 Subject: [PATCH 049/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20DeviceStore?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../device/datastore/store/DeviceStore.kt | 10 +++++ .../device/datastore/store/DeviceStoreImpl.kt | 45 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/DeviceStore.kt create mode 100644 core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/DeviceStoreImpl.kt diff --git a/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/DeviceStore.kt b/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/DeviceStore.kt new file mode 100644 index 000000000..adeca6b36 --- /dev/null +++ b/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/DeviceStore.kt @@ -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() +} diff --git a/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/DeviceStoreImpl.kt b/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/DeviceStoreImpl.kt new file mode 100644 index 000000000..24af790e5 --- /dev/null +++ b/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/DeviceStoreImpl.kt @@ -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") + } +} From a368e8589ad42a035e8f4f82c65b227225292fe9 Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 12 Jul 2024 22:07:00 +0900 Subject: [PATCH 050/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20TokenNotFount?= =?UTF-8?q?Exception=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../store/exception/CannotStoreDeviceTokenException.kt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/exception/CannotStoreDeviceTokenException.kt diff --git a/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/exception/CannotStoreDeviceTokenException.kt b/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/exception/CannotStoreDeviceTokenException.kt new file mode 100644 index 000000000..0a51c63e9 --- /dev/null +++ b/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/exception/CannotStoreDeviceTokenException.kt @@ -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") From 401519499467427c153de7efb180105f9af5cdf9 Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 12 Jul 2024 22:07:33 +0900 Subject: [PATCH 051/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20TokenNotFound?= =?UTF-8?q?Exception=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datastore/store/exception/TokenNotFoundException.kt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/exception/TokenNotFoundException.kt diff --git a/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/exception/TokenNotFoundException.kt b/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/exception/TokenNotFoundException.kt new file mode 100644 index 000000000..10583752b --- /dev/null +++ b/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/exception/TokenNotFoundException.kt @@ -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") From 074f089d7f578d0c7bf915185aa88a082a92f3b8 Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 12 Jul 2024 22:08:51 +0900 Subject: [PATCH 052/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EB=94=94?= =?UTF-8?q?=EB=B0=94=EC=9D=B4=EC=8A=A4=20=ED=86=A0=ED=81=B0=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/NotificationRepository.kt | 4 ++++ .../repository/NotificationRepositoryImpl.kt | 18 ++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepository.kt b/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepository.kt index 2ee108b76..45374a6e7 100644 --- a/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepository.kt +++ b/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepository.kt @@ -30,4 +30,8 @@ abstract class NotificationRepository { abstract suspend fun fetchNotificationStatus(deviceToken: String): NotificationTopicGroup.Status abstract suspend fun fetchNotifications(): List + + abstract suspend fun saveDeviceToken(deviceToken: String) + + abstract suspend fun getDeviceToken(): String } diff --git a/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepositoryImpl.kt b/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepositoryImpl.kt index 8e6727b62..2cac648a5 100644 --- a/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepositoryImpl.kt +++ b/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepositoryImpl.kt @@ -1,5 +1,6 @@ package team.aliens.dms.android.data.notification.repository +import team.aliens.dms.android.core.device.datastore.DeviceDataStoreDataSource import team.aliens.dms.android.network.notification.datasource.NetworkNotificationDataSource import team.aliens.dms.android.data.notification.model.Notification import team.aliens.dms.android.data.notification.model.NotificationTopic @@ -9,14 +10,12 @@ import javax.inject.Inject internal class NotificationRepositoryImpl @Inject constructor( private val networkNotificationDataSource: NetworkNotificationDataSource, + private val deviceDataStoreDataSource: DeviceDataStoreDataSource, ) : NotificationRepository() { - override suspend fun registerDeviceNotificationToken(deviceToken: String) = - networkNotificationDataSource.registerFcmDeviceToken( - request = RegisterFcmDeviceTokenRequest( - deviceToken = deviceToken, - ) - ) + override suspend fun registerDeviceNotificationToken(deviceToken: String) { + TODO("Not yet implemented") + } override suspend fun cancelDeviceTokenRegistration(deviceToken: String) { TODO("Not yet implemented") @@ -49,4 +48,11 @@ internal class NotificationRepositoryImpl @Inject constructor( override suspend fun fetchNotifications(): List { TODO("Not yet implemented") } + + override suspend fun saveDeviceToken(deviceToken: String) { + deviceDataStoreDataSource.storeDeviceToken(deviceToken) + } + + override suspend fun getDeviceToken(): String = + deviceDataStoreDataSource.loadDeviceToken() } From 34d8ea15cf841aea3d6426e5771b1e9126ebaec9 Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 12 Jul 2024 22:09:08 +0900 Subject: [PATCH 053/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20StoreModule?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dms/android/core/device/di/StoreModule.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 core/device/src/main/java/team/aliens/dms/android/core/device/di/StoreModule.kt diff --git a/core/device/src/main/java/team/aliens/dms/android/core/device/di/StoreModule.kt b/core/device/src/main/java/team/aliens/dms/android/core/device/di/StoreModule.kt new file mode 100644 index 000000000..f5311733c --- /dev/null +++ b/core/device/src/main/java/team/aliens/dms/android/core/device/di/StoreModule.kt @@ -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.store.DeviceStore +import team.aliens.dms.android.core.device.datastore.store.DeviceStoreImpl +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal abstract class StoreModule { + + @Binds + @Singleton + abstract fun bindDeviceStore(impl: DeviceStoreImpl): DeviceStore +} From bfcd5e7713071e0c6ce8cffe2b70215fd3df9845 Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 12 Jul 2024 22:12:26 +0900 Subject: [PATCH 054/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/aliens/dms/android/app/service/DmsMessagingService.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/src/main/java/team/aliens/dms/android/app/service/DmsMessagingService.kt b/app/src/main/java/team/aliens/dms/android/app/service/DmsMessagingService.kt index 6ead0b4c9..14b0c91fb 100644 --- a/app/src/main/java/team/aliens/dms/android/app/service/DmsMessagingService.kt +++ b/app/src/main/java/team/aliens/dms/android/app/service/DmsMessagingService.kt @@ -12,7 +12,6 @@ import team.aliens.dms.android.core.notification.DeviceTokenManager import team.aliens.dms.android.core.notification.NotificationManager import javax.inject.Inject -@RequiresApi(Build.VERSION_CODES.N) @AndroidEntryPoint class DmsMessagingService : FirebaseMessagingService() { @@ -30,8 +29,6 @@ class DmsMessagingService : FirebaseMessagingService() { } } - // TODO: 버전 대응 - @RequiresApi(Build.VERSION_CODES.N) override fun onMessageReceived(message: RemoteMessage) { super.onMessageReceived(message) message.notification?.run { From d40b74da11daa022b8180e4b677374786f80d87d Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 12 Jul 2024 22:12:42 +0900 Subject: [PATCH 055/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20DataSourceMod?= =?UTF-8?q?ule?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/core/device/di/DataSourceModule.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 core/device/src/main/java/team/aliens/dms/android/core/device/di/DataSourceModule.kt diff --git a/core/device/src/main/java/team/aliens/dms/android/core/device/di/DataSourceModule.kt b/core/device/src/main/java/team/aliens/dms/android/core/device/di/DataSourceModule.kt new file mode 100644 index 000000000..3ecf4ae4d --- /dev/null +++ b/core/device/src/main/java/team/aliens/dms/android/core/device/di/DataSourceModule.kt @@ -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 +} From d73bb998030aa42191acfdc1fd4e35ba3855d169 Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 12 Jul 2024 22:36:36 +0900 Subject: [PATCH 056/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/notification/NotificationManager.kt | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/core/notification/src/main/java/team/aliens/dms/android/core/notification/NotificationManager.kt b/core/notification/src/main/java/team/aliens/dms/android/core/notification/NotificationManager.kt index 91b360433..2305bc115 100644 --- a/core/notification/src/main/java/team/aliens/dms/android/core/notification/NotificationManager.kt +++ b/core/notification/src/main/java/team/aliens/dms/android/core/notification/NotificationManager.kt @@ -16,16 +16,13 @@ private object Notifications { const val CHANNEL_NAME = "dms" const val CHANNEL_DESCRIPTION = "dms notification channel" } -@RequiresApi(Build.VERSION_CODES.N) + class NotificationManager( private val context: Context, ) { init { - // TODO: 버전 대응하기 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - createNotificationChannel() - } + createNotificationChannel() } private val notificationManagerCompat: NotificationManagerCompat by lazy { @@ -50,7 +47,7 @@ class NotificationManager( @SuppressLint("MissingPermission") fun sendNotification() { - if(notificationPermissionGranted(context = context)) { + if (notificationPermissionGranted(context = context)) { notificationManagerCompat.notify( Notifications.NOTIFICATION_ID, notificationBuilder.build(), @@ -58,21 +55,19 @@ class NotificationManager( } } - // TODO: 버전 대응하기 - @RequiresApi(Build.VERSION_CODES.O) private fun createNotificationChannel() { - val importance = NotificationManager.IMPORTANCE_DEFAULT - val channel = - NotificationChannel( + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val importance = NotificationManager.IMPORTANCE_DEFAULT + val channel = NotificationChannel( Notifications.NOTIFICATION_CHANNEL_ID, Notifications.CHANNEL_NAME, importance ).apply { this.description = Notifications.CHANNEL_DESCRIPTION } - - val notificationManager = - context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - notificationManager.createNotificationChannel(channel) + val notificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(channel) + } } } From dd8b0a4343a829bc5f62297f7b192b2ee3574fa1 Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 12 Jul 2024 22:41:09 +0900 Subject: [PATCH 057/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EA=B6=8C=ED=95=9C=20=EC=9A=94=EC=B2=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/feature/signin/SignInScreen.kt | 44 ++++++++++++++++++- feature/src/main/res/values/strings.xml | 1 + 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/feature/src/main/java/team/aliens/dms/android/feature/signin/SignInScreen.kt b/feature/src/main/java/team/aliens/dms/android/feature/signin/SignInScreen.kt index 29ee1147f..5aec20088 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/signin/SignInScreen.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/signin/SignInScreen.kt @@ -1,5 +1,13 @@ package team.aliens.dms.android.feature.signin +import android.Manifest +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.provider.Settings +import androidx.activity.compose.ManagedActivityResultLauncher +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -19,6 +27,7 @@ import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.VerticalDivider import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -37,15 +46,18 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.ramcosta.composedestinations.annotation.Destination +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import team.aliens.dms.android.core.designsystem.ContainedButton -import team.aliens.dms.android.core.designsystem.Scaffold import team.aliens.dms.android.core.designsystem.DmsTheme import team.aliens.dms.android.core.designsystem.DmsTopAppBar import team.aliens.dms.android.core.designsystem.LocalToast +import team.aliens.dms.android.core.designsystem.Scaffold import team.aliens.dms.android.core.designsystem.TextField import team.aliens.dms.android.core.designsystem.clickable +import team.aliens.dms.android.core.notification.notificationPermissionGranted import team.aliens.dms.android.core.ui.Banner import team.aliens.dms.android.core.ui.BannerDefaults import team.aliens.dms.android.core.ui.DefaultHorizontalSpace @@ -76,6 +88,23 @@ internal fun SignInScreen( val toast = LocalToast.current val context = LocalContext.current + val settingLauncher = + rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) {} + val requestPermissionLauncher = + rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { + if (!it) { + CoroutineScope(Dispatchers.IO).launch { + toast.showErrorToast(message = context.getString(R.string.sign_in_notification_revoked)) + } + + settingLauncher.launch( + Intent( + Settings.ACTION_APPLICATION_DETAILS_SETTINGS, + Uri.fromParts("package", context.packageName, null), + ), + ) + } + } viewModel.sideEffectFlow.collectInLaunchedEffectWithLifecycle { sideEffect -> when (sideEffect) { @@ -85,11 +114,16 @@ internal fun SignInScreen( } } + LaunchedEffect(Unit) { + if (!notificationPermissionGranted(context)) { + requestPermissionLauncher.requestNotificationPermission() + } + } + Scaffold( modifier = modifier.fillMaxSize(), topBar = { DmsTopAppBar( - title = { /* explicit blank */ }, colors = TopAppBarDefaults.topAppBarColors( containerColor = Color.Transparent, ), @@ -336,3 +370,9 @@ private fun SignInPreview() { } } } + +private fun ManagedActivityResultLauncher.requestNotificationPermission() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + launch(Manifest.permission.POST_NOTIFICATIONS) + } +} diff --git a/feature/src/main/res/values/strings.xml b/feature/src/main/res/values/strings.xml index 52108e59c..0e218649a 100644 --- a/feature/src/main/res/values/strings.xml +++ b/feature/src/main/res/values/strings.xml @@ -70,6 +70,7 @@ 잘못된 형식입니다 알림 기기 등록에 실패하였습니다 + 알림을 받기 위해서 권한을 허용해주세요 회원가입 회원가입 종료 From d31866379a922f60e1901d0ce65005018777baa9 Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 12 Jul 2024 22:41:32 +0900 Subject: [PATCH 058/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EB=94=94?= =?UTF-8?q?=EB=B0=94=EC=9D=B4=EC=8A=A4=20=ED=86=A0=ED=81=B0=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/feature/signin/SignInViewModel.kt | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/feature/src/main/java/team/aliens/dms/android/feature/signin/SignInViewModel.kt b/feature/src/main/java/team/aliens/dms/android/feature/signin/SignInViewModel.kt index 6b4659700..1ad900b31 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/signin/SignInViewModel.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/signin/SignInViewModel.kt @@ -13,15 +13,23 @@ import team.aliens.dms.android.data.auth.exception.BadRequestException import team.aliens.dms.android.data.auth.exception.PasswordMismatchException import team.aliens.dms.android.data.auth.exception.UserNotFoundException import team.aliens.dms.android.data.auth.repository.AuthRepository +import team.aliens.dms.android.data.notification.repository.NotificationRepository import javax.inject.Inject @HiltViewModel internal class SignInViewModel @Inject constructor( private val authRepository: AuthRepository, + private val notificationRepository: NotificationRepository, ) : BaseMviViewModel( initialState = SignInUiState.initial(), ) { + private lateinit var deviceToken: String + + init { + getDeviceToken() + } + override fun processIntent(intent: SignInIntent) { when (intent) { is SignInIntent.UpdateId -> updateId(intent.id) @@ -30,6 +38,16 @@ internal class SignInViewModel @Inject constructor( } } + private fun getDeviceToken() { + viewModelScope.launch(Dispatchers.IO) { + runCatching { + notificationRepository.getDeviceToken() + }.onSuccess { + deviceToken = it + } + } + } + private fun updateId(accountId: String): Boolean = reduce( newState = stateFlow.value.copy( accountId = accountId, @@ -65,7 +83,7 @@ internal class SignInViewModel @Inject constructor( authRepository.signIn( accountId = uiState.accountId.trim(), password = uiState.password.trim(), - deviceToken = "", + deviceToken = deviceToken, ) }.onSuccess { postSideEffect(SignInSideEffect.Success) From 4749e1ec22ba35a943386219a5811cb8581408f6 Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 12 Jul 2024 23:15:40 +0900 Subject: [PATCH 059/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 98b8e7121..e9638dd8e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -67,6 +67,7 @@ dependencies { 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)) From 513048efa617c415d1e4dedb9dec36b73726a44d Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 12 Jul 2024 23:21:46 +0900 Subject: [PATCH 060/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EB=B2=84?= =?UTF-8?q?=EC=A0=84=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e9638dd8e..c62aa5c43 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -17,8 +17,8 @@ android { minSdk = libs.versions.minSdk.get().toInt() targetSdk = libs.versions.targetSdk.get().toInt() - versionCode = 11 - versionName = "v1.3.2" + versionCode = 15 + versionName = "1.3.6" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } From 3b109d7711e0c08d8bf759c16c9359172fba7b76 Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 12 Jul 2024 23:30:03 +0900 Subject: [PATCH 061/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EB=B2=84?= =?UTF-8?q?=EC=A0=84=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c62aa5c43..a2ee33ed5 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -17,8 +17,8 @@ android { minSdk = libs.versions.minSdk.get().toInt() targetSdk = libs.versions.targetSdk.get().toInt() - versionCode = 15 - versionName = "1.3.6" + versionCode = 16 + versionName = "1.3.7" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } From 8dedb8a5dcf3822920d7fe561147bf9d3094f29c Mon Sep 17 00:00:00 2001 From: uiel Date: Sun, 21 Jul 2024 18:52:24 +0900 Subject: [PATCH 062/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=A0=91?= =?UTF-8?q?=EA=B7=BC=20=EC=A0=9C=ED=95=9C=EC=9E=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/aliens/dms/android/core/designsystem/Buttons.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/design-system/src/main/java/team/aliens/dms/android/core/designsystem/Buttons.kt b/core/design-system/src/main/java/team/aliens/dms/android/core/designsystem/Buttons.kt index 955b02d9b..20f5b6ec2 100644 --- a/core/design-system/src/main/java/team/aliens/dms/android/core/designsystem/Buttons.kt +++ b/core/design-system/src/main/java/team/aliens/dms/android/core/designsystem/Buttons.kt @@ -38,9 +38,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -// TODO: Button 접근제한자 internal로 변경 @Composable -fun Button( +private fun Button( onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, @@ -64,7 +63,7 @@ fun Button( contentColor = contentColor, shadowElevation = shadowElevation, border = border, - interactionSource = interactionSource + interactionSource = interactionSource, ) { CompositionLocalProvider(LocalContentColor provides contentColor) { ProvideTextStyle(value = DmsTheme.typography.button) { From 131247d02fe59f0919b312828f20368fe4ed716b Mon Sep 17 00:00:00 2001 From: uiel Date: Sun, 21 Jul 2024 18:52:50 +0900 Subject: [PATCH 063/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EB=94=94?= =?UTF-8?q?=EC=9E=90=EC=9D=B8=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/feature/editprofile/EditProfileImageScreen.kt | 4 ++-- .../aliens/dms/android/feature/findid/FindIdScreen.kt | 3 +-- .../dms/android/feature/outing/OutingApplicationScreen.kt | 5 ++--- .../dms/android/feature/outing/OutingStatusScreen.kt | 4 ++-- .../dms/android/feature/signup/7_SetProfileImageScreen.kt | 4 ++-- .../feature/studyroom/details/StudyRoomDetailsScreen.kt | 8 ++++---- .../android/feature/studyroom/list/StudyRoomListScreen.kt | 8 ++++---- 7 files changed, 17 insertions(+), 19 deletions(-) diff --git a/feature/src/main/java/team/aliens/dms/android/feature/editprofile/EditProfileImageScreen.kt b/feature/src/main/java/team/aliens/dms/android/feature/editprofile/EditProfileImageScreen.kt index 9a5849b0d..cbc3fc323 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/editprofile/EditProfileImageScreen.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/editprofile/EditProfileImageScreen.kt @@ -32,7 +32,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import coil.compose.AsyncImage import com.ramcosta.composedestinations.annotation.Destination -import team.aliens.dms.android.core.designsystem.Button +import team.aliens.dms.android.core.designsystem.ContainedButton import team.aliens.dms.android.core.designsystem.DmsIcon import team.aliens.dms.android.core.designsystem.DmsTheme import team.aliens.dms.android.core.designsystem.DmsTopAppBar @@ -117,7 +117,7 @@ internal fun EditProfileImageScreen( } ) Spacer(modifier = Modifier.weight(1f)) - Button( + ContainedButton( modifier = Modifier .fillMaxWidth() .horizontalPadding() diff --git a/feature/src/main/java/team/aliens/dms/android/feature/findid/FindIdScreen.kt b/feature/src/main/java/team/aliens/dms/android/feature/findid/FindIdScreen.kt index 3ae500ae4..c34ec514d 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/findid/FindIdScreen.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/findid/FindIdScreen.kt @@ -36,7 +36,6 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.ramcosta.composedestinations.annotation.Destination import team.aliens.dms.android.core.designsystem.AlertDialog -import team.aliens.dms.android.core.designsystem.Button import team.aliens.dms.android.core.designsystem.ContainedButton import team.aliens.dms.android.core.designsystem.DmsIcon import team.aliens.dms.android.core.designsystem.DmsTopAppBar @@ -80,7 +79,7 @@ internal fun FindIdScreen( }, onDismissRequest = { /* explicit blank */ }, confirmButton = { - Button( + ContainedButton( modifier = Modifier.fillMaxWidth(), onClick = navigator::navigateUp, ) { diff --git a/feature/src/main/java/team/aliens/dms/android/feature/outing/OutingApplicationScreen.kt b/feature/src/main/java/team/aliens/dms/android/feature/outing/OutingApplicationScreen.kt index f0feaa1a6..d963e62c0 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/outing/OutingApplicationScreen.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/outing/OutingApplicationScreen.kt @@ -54,7 +54,6 @@ import com.ramcosta.composedestinations.annotation.Destination import kotlinx.coroutines.launch import org.threeten.bp.DayOfWeek import org.threeten.bp.LocalTime -import team.aliens.dms.android.core.designsystem.Button import team.aliens.dms.android.core.designsystem.ContainedButton import team.aliens.dms.android.core.designsystem.DmsTheme import team.aliens.dms.android.core.designsystem.DmsTopAppBar @@ -128,7 +127,7 @@ fun OutingApplicationScreen( endTime = uiState.selectedOutingEndTime, ) Spacer(modifier = Modifier.height(20.dp)) - Button( + ContainedButton( modifier = Modifier .fillMaxWidth() .horizontalPadding() @@ -238,7 +237,7 @@ fun OutingApplicationScreen( }, selectedOnly = true, ) - Button( + ContainedButton( modifier = Modifier .fillMaxWidth() .horizontalPadding() diff --git a/feature/src/main/java/team/aliens/dms/android/feature/outing/OutingStatusScreen.kt b/feature/src/main/java/team/aliens/dms/android/feature/outing/OutingStatusScreen.kt index 50e4c78c1..9885827db 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/outing/OutingStatusScreen.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/outing/OutingStatusScreen.kt @@ -32,8 +32,8 @@ import androidx.compose.ui.text.style.TextAlign import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.ramcosta.composedestinations.annotation.Destination import team.aliens.dms.android.core.designsystem.AlertDialog -import team.aliens.dms.android.core.designsystem.Button import team.aliens.dms.android.core.designsystem.ButtonDefaults +import team.aliens.dms.android.core.designsystem.ContainedButton import team.aliens.dms.android.core.designsystem.DmsTheme import team.aliens.dms.android.core.designsystem.DmsTopAppBar import team.aliens.dms.android.core.designsystem.LocalToast @@ -148,7 +148,7 @@ fun OutingStatusScreen( ) } if (uiState.currentAppliedOutingApplication == null) { - Button( + ContainedButton( modifier = Modifier .fillMaxWidth() .bottomPadding() diff --git a/feature/src/main/java/team/aliens/dms/android/feature/signup/7_SetProfileImageScreen.kt b/feature/src/main/java/team/aliens/dms/android/feature/signup/7_SetProfileImageScreen.kt index 616030234..139d31752 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/signup/7_SetProfileImageScreen.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/signup/7_SetProfileImageScreen.kt @@ -33,7 +33,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import coil.compose.AsyncImage import com.ramcosta.composedestinations.annotation.Destination -import team.aliens.dms.android.core.designsystem.Button +import team.aliens.dms.android.core.designsystem.ContainedButton import team.aliens.dms.android.core.designsystem.DmsIcon import team.aliens.dms.android.core.designsystem.DmsTheme import team.aliens.dms.android.core.designsystem.DmsTopAppBar @@ -141,7 +141,7 @@ internal fun SetProfileImageScreen( style = DmsTheme.typography.button, ) Spacer(modifier = Modifier.height(20.dp)) - Button( + ContainedButton( modifier = Modifier .fillMaxWidth() .horizontalPadding() diff --git a/feature/src/main/java/team/aliens/dms/android/feature/studyroom/details/StudyRoomDetailsScreen.kt b/feature/src/main/java/team/aliens/dms/android/feature/studyroom/details/StudyRoomDetailsScreen.kt index 22406605f..abe55f89e 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/studyroom/details/StudyRoomDetailsScreen.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/studyroom/details/StudyRoomDetailsScreen.kt @@ -46,11 +46,11 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.ramcosta.composedestinations.annotation.Destination -import team.aliens.dms.android.core.designsystem.Button import team.aliens.dms.android.core.designsystem.ButtonDefaults -import team.aliens.dms.android.core.designsystem.Scaffold +import team.aliens.dms.android.core.designsystem.ContainedButton import team.aliens.dms.android.core.designsystem.DmsTheme import team.aliens.dms.android.core.designsystem.DmsTopAppBar +import team.aliens.dms.android.core.designsystem.Scaffold import team.aliens.dms.android.core.designsystem.ShadowDefaults import team.aliens.dms.android.core.ui.DefaultHorizontalSpace import team.aliens.dms.android.core.ui.DefaultVerticalSpace @@ -170,7 +170,7 @@ internal fun StudyRoomDetailsScreen( ) }, ) - Button( + ContainedButton( modifier = Modifier .fillMaxWidth() .horizontalPadding() @@ -408,7 +408,7 @@ private fun Seat( DmsTheme.colorScheme.onPrimary } - Button( + ContainedButton( modifier = Modifier.size(DefaultSeatButtonSize), shape = CircleShape, colors = ButtonDefaults.buttonColors( diff --git a/feature/src/main/java/team/aliens/dms/android/feature/studyroom/list/StudyRoomListScreen.kt b/feature/src/main/java/team/aliens/dms/android/feature/studyroom/list/StudyRoomListScreen.kt index 1ee5aa81c..4c7fbf934 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/studyroom/list/StudyRoomListScreen.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/studyroom/list/StudyRoomListScreen.kt @@ -28,13 +28,13 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.ramcosta.composedestinations.annotation.Destination import kotlinx.coroutines.launch -import team.aliens.dms.android.core.designsystem.Button import team.aliens.dms.android.core.designsystem.ButtonDefaults -import team.aliens.dms.android.core.designsystem.Scaffold +import team.aliens.dms.android.core.designsystem.ContainedButton import team.aliens.dms.android.core.designsystem.DmsTheme import team.aliens.dms.android.core.designsystem.DmsTopAppBar import team.aliens.dms.android.core.designsystem.ModalBottomSheet import team.aliens.dms.android.core.designsystem.OutlinedButton +import team.aliens.dms.android.core.designsystem.Scaffold import team.aliens.dms.android.core.designsystem.VerticallyFadedLazyColumn import team.aliens.dms.android.core.ui.DefaultHorizontalSpace import team.aliens.dms.android.core.ui.DefaultVerticalSpace @@ -86,7 +86,7 @@ internal fun StudyRoomListScreen( val selected = availableStudyRoomTime == uiState.selectedAvailableStudyRoomTime if (selected) { - Button( + ContainedButton( onClick = { /* explicit blank */ }, ) { Text( @@ -120,7 +120,7 @@ internal fun StudyRoomListScreen( } } } - Button( + ContainedButton( modifier = Modifier .fillMaxWidth() .horizontalPadding(), From bf84767dd36602356914b2c9933508bf9a17dba1 Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 23 Jul 2024 23:17:14 +0900 Subject: [PATCH 064/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20navigation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/aliens/dms/android/app/navigation/DmsNavigator.kt | 7 ++++--- .../app/navigation/authorized/AuthorizedNavGraph.kt | 4 ++-- .../app/navigation/authorized/AuthorizedNavigator.kt | 4 ++-- .../dms/android/feature/main/navigation/MainNavigator.kt | 2 +- ...ationNavigation.kt => NotificationSettingsNavigator.kt} | 2 +- 5 files changed, 10 insertions(+), 9 deletions(-) rename feature/src/main/java/team/aliens/dms/android/feature/notification/navigation/{NotificationNavigation.kt => NotificationSettingsNavigator.kt} (67%) diff --git a/app/src/main/java/team/aliens/dms/android/app/navigation/DmsNavigator.kt b/app/src/main/java/team/aliens/dms/android/app/navigation/DmsNavigator.kt index 17d91d62d..1bad995af 100644 --- a/app/src/main/java/team/aliens/dms/android/app/navigation/DmsNavigator.kt +++ b/app/src/main/java/team/aliens/dms/android/app/navigation/DmsNavigator.kt @@ -21,7 +21,7 @@ import team.aliens.dms.android.feature.destinations.EnterSchoolVerificationQuest import team.aliens.dms.android.feature.destinations.FindIdScreenDestination import team.aliens.dms.android.feature.destinations.MainDestination import team.aliens.dms.android.feature.destinations.NoticeDetailsScreenDestination -import team.aliens.dms.android.feature.destinations.NotificationBoxScreenDestination +import team.aliens.dms.android.feature.destinations.NotificationSettingsScreenDestination import team.aliens.dms.android.feature.destinations.OutingApplicationScreenDestination import team.aliens.dms.android.feature.destinations.PointHistoryScreenDestination import team.aliens.dms.android.feature.destinations.RemainsApplicationScreenDestination @@ -36,6 +36,7 @@ import team.aliens.dms.android.feature.destinations.StudyRoomDetailsScreenDestin import team.aliens.dms.android.feature.destinations.StudyRoomListScreenDestination import team.aliens.dms.android.feature.destinations.TermsScreenDestination import team.aliens.dms.android.feature.editpassword.navigation.EditPasswordNavGraph +import team.aliens.dms.android.feature.notification.navigation.NotificationSettingsNavigator import team.aliens.dms.android.feature.outing.navigation.OutingNavGraph import team.aliens.dms.android.feature.resetpassword.navigation.ResetPasswordNavGraph import team.aliens.dms.android.feature.signup.navigation.SignUpNavGraph @@ -46,8 +47,8 @@ class DmsNavigator( private val navController: NavController, ) : AuthorizedNavigator, UnauthorizedNavigator { - override fun openNotificationBox() { - navController.navigateSingleTop(NotificationBoxScreenDestination within navGraph) + override fun openSettingsNotification() { + navController.navigateSingleTop(NotificationSettingsScreenDestination within navGraph) } override fun openUnauthorizedNav() { diff --git a/app/src/main/java/team/aliens/dms/android/app/navigation/authorized/AuthorizedNavGraph.kt b/app/src/main/java/team/aliens/dms/android/app/navigation/authorized/AuthorizedNavGraph.kt index 0f3444d66..595f76759 100644 --- a/app/src/main/java/team/aliens/dms/android/app/navigation/authorized/AuthorizedNavGraph.kt +++ b/app/src/main/java/team/aliens/dms/android/app/navigation/authorized/AuthorizedNavGraph.kt @@ -8,7 +8,7 @@ import team.aliens.dms.android.feature.destinations.AnnouncementListScreenDestin import team.aliens.dms.android.feature.destinations.EditProfileImageScreenDestination import team.aliens.dms.android.feature.destinations.MainDestination import team.aliens.dms.android.feature.destinations.NoticeDetailsScreenDestination -import team.aliens.dms.android.feature.destinations.NotificationBoxScreenDestination +import team.aliens.dms.android.feature.destinations.NotificationSettingsScreenDestination import team.aliens.dms.android.feature.destinations.PointHistoryScreenDestination import team.aliens.dms.android.feature.destinations.RemainsApplicationScreenDestination import team.aliens.dms.android.feature.destinations.StudyRoomDetailsScreenDestination @@ -36,7 +36,7 @@ object AuthorizedNavGraph : NavGraphSpec { StudyRoomListScreenDestination, StudyRoomDetailsScreenDestination, NoticeDetailsScreenDestination, - NotificationBoxScreenDestination, + NotificationSettingsScreenDestination, PointHistoryScreenDestination, ) .routedIn(navGraphSpec = this) diff --git a/app/src/main/java/team/aliens/dms/android/app/navigation/authorized/AuthorizedNavigator.kt b/app/src/main/java/team/aliens/dms/android/app/navigation/authorized/AuthorizedNavigator.kt index 7e113dc17..69da5aa5d 100644 --- a/app/src/main/java/team/aliens/dms/android/app/navigation/authorized/AuthorizedNavigator.kt +++ b/app/src/main/java/team/aliens/dms/android/app/navigation/authorized/AuthorizedNavigator.kt @@ -4,7 +4,7 @@ import team.aliens.dms.android.feature.editpassword.navigation.EditPasswordNavig import team.aliens.dms.android.feature.editprofile.navigation.EditProfileNavigator import team.aliens.dms.android.feature.main.navigation.MainNavigator import team.aliens.dms.android.feature.notice.navigation.NoticeNavigator -import team.aliens.dms.android.feature.notification.navigation.NotificationNavigation +import team.aliens.dms.android.feature.notification.navigation.NotificationSettingsNavigator import team.aliens.dms.android.feature.outing.navigation.OutingNavigator import team.aliens.dms.android.feature.point.navigation.PointHistoryNavigator import team.aliens.dms.android.feature.remains.navigator.RemainsNavigator @@ -15,7 +15,7 @@ interface AuthorizedNavigator : EditPasswordNavigator, EditProfileNavigator, NoticeNavigator, - NotificationNavigation, + NotificationSettingsNavigator, PointHistoryNavigator, RemainsNavigator, StudyRoomNavigator, diff --git a/feature/src/main/java/team/aliens/dms/android/feature/main/navigation/MainNavigator.kt b/feature/src/main/java/team/aliens/dms/android/feature/main/navigation/MainNavigator.kt index 7107ff35e..0c61ac970 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/main/navigation/MainNavigator.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/main/navigation/MainNavigator.kt @@ -5,7 +5,7 @@ import java.util.UUID interface MainNavigator { - fun openNotificationBox() + fun openSettingsNotification() fun openUnauthorizedNav() diff --git a/feature/src/main/java/team/aliens/dms/android/feature/notification/navigation/NotificationNavigation.kt b/feature/src/main/java/team/aliens/dms/android/feature/notification/navigation/NotificationSettingsNavigator.kt similarity index 67% rename from feature/src/main/java/team/aliens/dms/android/feature/notification/navigation/NotificationNavigation.kt rename to feature/src/main/java/team/aliens/dms/android/feature/notification/navigation/NotificationSettingsNavigator.kt index d8e83b59e..caeaaba85 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/notification/navigation/NotificationNavigation.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/notification/navigation/NotificationSettingsNavigator.kt @@ -1,5 +1,5 @@ package team.aliens.dms.android.feature.notification.navigation -interface NotificationNavigation { +interface NotificationSettingsNavigator { fun navigateUp() } From a4caaf745a2f02877a1dfd1cbe1d2450aa5d2e95 Mon Sep 17 00:00:00 2001 From: uiel Date: Wed, 24 Jul 2024 23:16:40 +0900 Subject: [PATCH 065/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20NotificationB?= =?UTF-8?q?ox=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/NotificationBoxScreen.kt | 155 ------------------ .../notification/NotificationBoxViewModel.kt | 73 --------- 2 files changed, 228 deletions(-) delete mode 100644 feature/src/main/java/team/aliens/dms/android/feature/notification/NotificationBoxScreen.kt delete mode 100644 feature/src/main/java/team/aliens/dms/android/feature/notification/NotificationBoxViewModel.kt diff --git a/feature/src/main/java/team/aliens/dms/android/feature/notification/NotificationBoxScreen.kt b/feature/src/main/java/team/aliens/dms/android/feature/notification/NotificationBoxScreen.kt deleted file mode 100644 index aaa691fe6..000000000 --- a/feature/src/main/java/team/aliens/dms/android/feature/notification/NotificationBoxScreen.kt +++ /dev/null @@ -1,155 +0,0 @@ -package team.aliens.dms.android.feature.notification - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Icon -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.dp -import com.ramcosta.composedestinations.annotation.Destination -import team.aliens.dms.android.core.designsystem.DmsTheme -import team.aliens.dms.android.core.designsystem.VerticallyFadedLazyColumn -import team.aliens.dms.android.data.notification.model.Notification -import team.aliens.dms.android.data.notification.model.NotificationTopic -import team.aliens.dms.android.feature.R -import team.aliens.dms.android.feature.notification.navigation.NotificationNavigation - -@Destination -@Composable -internal fun NotificationBoxScreen( - modifier: Modifier = Modifier, - navigator: NotificationNavigation, - // notificationBoxViewModel: NotificationBoxViewModel = hiltViewModel(), -) {/* - val uiState by notificationBoxViewModel.stateFlow.collectAsStateWithLifecycle() - - Column( - modifier = modifier - .background(DormTheme.colors.background) - .fillMaxSize(), - ) { - TopBar( - title = stringResource(R.string.my_page_check_point_history), - onPrevious = navigator::popBackStack, - ) - Notifications( - modifier = Modifier.fillMaxWidth(), - newNotifications = uiState.newNotifications, - priorNotifications = uiState.priorNotifications, - ) - }*/ -} -/* - -// todo move to design system -@Composable -private fun Notifications( - modifier: Modifier = Modifier, - newNotifications: List, - priorNotifications: List, -) { - println(newNotifications) - println(priorNotifications) - VerticallyFadedLazyColumn( - modifier = modifier.fillMaxSize(), - verticalArrangement = Arrangement.spacedBy(12.dp), - ) { - if (newNotifications.isNotEmpty()) { - items(newNotifications.also { println("NEWNEW $it") }) { newNotification -> - Notification( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - notification = newNotification, - ) - } - } - if (priorNotifications.isNotEmpty()) { - items(priorNotifications.also { println("PRIPRI $it") }) { priorNotification -> - Notification( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - notification = priorNotification, - ) - } - } - } -} - -@Composable -private fun Notification( - modifier: Modifier = Modifier, - notification: Notification, -) { - Row( - modifier = modifier - .dormShadow(DmsTheme.colorScheme.line) - .fillMaxWidth() - .background( - color = if (!notification.read) { - DmsTheme.colorScheme.surface - } else { - DmsTheme.colorScheme.surface - }, - shape = RoundedCornerShape(10.dp), - ) - .clip(RoundedCornerShape(10.dp)) - .dormClickable { - // todo - } - .padding( - horizontal = 12.dp, - vertical = 8.dp, - ), - horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalAlignment = Alignment.Top, - ) { - Icon( - painter = painterResource( - when (notification.topic) { - NotificationTopic.NOTICE -> R.drawable.ic_notice - }, - ), - contentDescription = null, - tint = if (!notification.read) { - DmsTheme.colorScheme.primary - } else { - DmsTheme.colorScheme.onBackground - }, - ) - Column( - modifier = Modifier - .weight(1f) - .padding(vertical = 4.dp), - verticalArrangement = Arrangement.spacedBy(4.dp), - ) { - Body3( - text = notification.title, - ) - Caption( - text = notification.content, - ) - } - Caption( - text = createdAtDateToFormattedString(notification.createdAt), - ) - } -} - -@Composable -private fun createdAtDateToFormattedString(createdAt: String): String { - // TODO - return "8일 전" -} -*/ diff --git a/feature/src/main/java/team/aliens/dms/android/feature/notification/NotificationBoxViewModel.kt b/feature/src/main/java/team/aliens/dms/android/feature/notification/NotificationBoxViewModel.kt deleted file mode 100644 index d433cd4ff..000000000 --- a/feature/src/main/java/team/aliens/dms/android/feature/notification/NotificationBoxViewModel.kt +++ /dev/null @@ -1,73 +0,0 @@ -package team.aliens.dms.android.feature.notification -/* - -import androidx.lifecycle.viewModelScope -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import team.aliens.dms.android.feature._legacy.base.BaseMviViewModel -import team.aliens.dms.android.feature._legacy.base.MviIntent -import team.aliens.dms.android.feature._legacy.base.MviSideEffect -import team.aliens.dms.android.feature._legacy.base.MviState -import team.aliens.dms.android.domain.model.notification.Notification -import team.aliens.dms.android.domain.usecase.notification.FetchNotificationsUseCase -import javax.inject.Inject - -@HiltViewModel -internal class NotificationBoxViewModel @Inject constructor( - private val fetchNotificationsUseCase: FetchNotificationsUseCase, -) : BaseMviViewModel( - initialState = NotificationBoxState.initial(), -) { - init { - fetchNotifications() - } - - private fun fetchNotifications() { - viewModelScope.launch(Dispatchers.IO) { - fetchNotificationsUseCase().onSuccess { fetchedNotifications -> - val newNotificationsAndPriorNotifications = - fetchedNotifications.extractNewNotificationsAndPriorNotifications() - reduce( - newState = stateFlow.value.copy( - newNotifications = newNotificationsAndPriorNotifications.first, - priorNotifications = newNotificationsAndPriorNotifications.second, - ), - ) - }.onFailure { - postSideEffect(NotificationBoxSideEffect.FetchingNotificationsFailed) - } - } - } - - */ -/** - * @return [Pair.first] new notifications - * @return [Pair.second] prior notifications - *//* - - private fun List.extractNewNotificationsAndPriorNotifications(): Pair, List> { - val newNotifications = this.filter { it.read } - val priorNotifications = this.filter { !it.read } - return Pair(newNotifications, priorNotifications) - } -} - -internal sealed class NotificationBoxIntent : MviIntent - -internal data class NotificationBoxState( - val newNotifications: List, - val priorNotifications: List, -) : MviState { - companion object { - fun initial() = NotificationBoxState( - newNotifications = emptyList(), - priorNotifications = emptyList(), - ) - } -} - -internal sealed class NotificationBoxSideEffect : MviSideEffect { - object FetchingNotificationsFailed : NotificationBoxSideEffect() -} -*/ From 01d7f15396433024e61699d82cd3e972f3f1ba14 Mon Sep 17 00:00:00 2001 From: uiel Date: Thu, 25 Jul 2024 23:07:01 +0900 Subject: [PATCH 066/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EC=84=A4=EC=A0=95=20=EB=B2=84=ED=8A=BC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dms/android/feature/main/mypage/MyPageScreen.kt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/feature/src/main/java/team/aliens/dms/android/feature/main/mypage/MyPageScreen.kt b/feature/src/main/java/team/aliens/dms/android/feature/main/mypage/MyPageScreen.kt index 8b4475344..1f0fa6704 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/main/mypage/MyPageScreen.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/main/mypage/MyPageScreen.kt @@ -77,6 +77,7 @@ internal fun MyPageScreen( onNavigateToEditProfileImage: () -> Unit, onNavigateToPointHistory: (PointType) -> Unit, onNavigateToEditPassword: () -> Unit, + onNavigateToNotificationSettings: () -> Unit, onNavigateToUnauthorizedNav: () -> Unit, ) { val viewModel: MyPageViewModel = hiltViewModel() @@ -180,6 +181,7 @@ internal fun MyPageScreen( modifier = Modifier.fillMaxWidth(), onNavigateToPointHistory = onNavigateToPointHistory, onNavigateToEditPassword = onNavigateToEditPassword, + onNavigateToNotificationSettings = onNavigateToNotificationSettings, onSignOutClick = { onShouldShowSignOutDialogChange(true) }, onWithdrawalClick = { onShouldShowWithdrawDialogChange(true) }, onThemeSettingsClick = { @@ -259,9 +261,9 @@ private fun UserInformation( ) { AsyncImage( modifier = Modifier - .size(64.dp) - .clip(CircleShape) - .clickable(onClick = onNavigateToEditProfileImage), + .size(64.dp) + .clip(CircleShape) + .clickable(onClick = onNavigateToEditProfileImage), contentScale = ContentScale.Crop, model = profileImageUrl ?: DmsIcon.ProfileDefault, contentDescription = stringResource(id = R.string.profile_image), @@ -430,6 +432,7 @@ private fun Options( modifier: Modifier = Modifier, onNavigateToPointHistory: (PointType) -> Unit, onNavigateToEditPassword: () -> Unit, + onNavigateToNotificationSettings: () -> Unit, onSignOutClick: () -> Unit, onWithdrawalClick: () -> Unit, onThemeSettingsClick: () -> Unit, @@ -444,6 +447,10 @@ private fun Options( titleRes = R.string.my_page_edit_password, onClick = onNavigateToEditPassword, ), + Option( + titleRes = R.string.my_page_notification_settings, + onClick = onNavigateToNotificationSettings, + ), ) } val signOutOption = remember { From 51551676aae8b41a5163b3eaeaadc46d9f7a375b Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 26 Jul 2024 23:22:20 +0900 Subject: [PATCH 067/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20deviceToken?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dms/android/network/auth/apiservice/AuthApiServiceTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/network/src/androidTest/java/team/aliens/dms/android/network/auth/apiservice/AuthApiServiceTest.kt b/network/src/androidTest/java/team/aliens/dms/android/network/auth/apiservice/AuthApiServiceTest.kt index 17acc687c..5d414c7cc 100644 --- a/network/src/androidTest/java/team/aliens/dms/android/network/auth/apiservice/AuthApiServiceTest.kt +++ b/network/src/androidTest/java/team/aliens/dms/android/network/auth/apiservice/AuthApiServiceTest.kt @@ -56,6 +56,7 @@ class AuthApiServiceTest { val request = SignInRequest( accountId = "student", password = "rhqmffls13!", + deviceToken = "", ) val response = Gson().fromJson( context.readJsonFromRawResources(R.raw.sign_in_response), From 918ed223d955b6b13f3cb46afd4073e8ba95dba2 Mon Sep 17 00:00:00 2001 From: uiel Date: Sat, 27 Jul 2024 23:26:08 +0900 Subject: [PATCH 068/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20navigation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/team/aliens/dms/android/feature/main/Main.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/feature/src/main/java/team/aliens/dms/android/feature/main/Main.kt b/feature/src/main/java/team/aliens/dms/android/feature/main/Main.kt index 95f0cc0a8..558ea6199 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/main/Main.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/main/Main.kt @@ -168,6 +168,7 @@ internal fun Main( onNavigateToPointHistory = mainNavigator::openPointHistory, onNavigateToEditPassword = mainNavigator::openEditPasswordNav, onNavigateToUnauthorizedNav = mainNavigator::openUnauthorizedNav, + onNavigateToNotificationSettings = mainNavigator::openSettingsNotification, ) } } From 7bc00a4a0ac240dbfeeb8fb315a2d6f5ad0ec165 Mon Sep 17 00:00:00 2001 From: uiel Date: Sun, 28 Jul 2024 22:28:37 +0900 Subject: [PATCH 069/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dms/android/core/device/datastore/store/DeviceStoreImpl.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/DeviceStoreImpl.kt b/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/DeviceStoreImpl.kt index 24af790e5..3ab21c74c 100644 --- a/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/DeviceStoreImpl.kt +++ b/core/device/src/main/java/team/aliens/dms/android/core/device/datastore/store/DeviceStoreImpl.kt @@ -1,6 +1,5 @@ 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 @@ -17,7 +16,6 @@ internal class DeviceStoreImpl @Inject constructor( ) : DeviceStore() { override fun loadDeviceToken(): String = runBlocking { preferencesDataStore.data.map { preferences -> - Log.d("TEST2",preferences[DEVICE_TOKEN].toString()) preferences[DEVICE_TOKEN] ?: throw DeviceTokenNotFoundException() }.first() } @@ -26,7 +24,6 @@ internal class DeviceStoreImpl @Inject constructor( transform( onFailure = { throw CannotStoreDeviceTokenException() }, ) { - Log.d("TEST1",deviceToken) preferencesDataStore.edit { preferences -> preferences[DEVICE_TOKEN] = deviceToken } From b8d5d52d0803ab0c185bb8f6ab0559d9c3f0b2c5 Mon Sep 17 00:00:00 2001 From: uiel Date: Mon, 29 Jul 2024 22:05:14 +0900 Subject: [PATCH 070/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20enum=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dms/android/data/notification/model/NotificationTopic.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/src/main/java/team/aliens/dms/android/data/notification/model/NotificationTopic.kt b/data/src/main/java/team/aliens/dms/android/data/notification/model/NotificationTopic.kt index 47b15383c..120078363 100644 --- a/data/src/main/java/team/aliens/dms/android/data/notification/model/NotificationTopic.kt +++ b/data/src/main/java/team/aliens/dms/android/data/notification/model/NotificationTopic.kt @@ -1,7 +1,7 @@ package team.aliens.dms.android.data.notification.model enum class NotificationTopic { - NOTICE, + NOTICE, STUDY_ROOM_TIME_SLOT, STUDY_ROOM_APPLY, POINT, OUTING, ; data class Subscription( From c88f982c07d675a828b51d083fa799414bf4e791 Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 30 Jul 2024 14:20:31 +0900 Subject: [PATCH 071/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EB=A7=A4?= =?UTF-8?q?=ED=95=91=20=ED=95=A8=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/notification/model/NotificationTopic.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/data/src/main/java/team/aliens/dms/android/data/notification/model/NotificationTopic.kt b/data/src/main/java/team/aliens/dms/android/data/notification/model/NotificationTopic.kt index 120078363..d7fe9a9ec 100644 --- a/data/src/main/java/team/aliens/dms/android/data/notification/model/NotificationTopic.kt +++ b/data/src/main/java/team/aliens/dms/android/data/notification/model/NotificationTopic.kt @@ -1,5 +1,7 @@ package team.aliens.dms.android.data.notification.model +import team.aliens.dms.android.network.notification.model.BatchUpdateNotificationTopicRequest + enum class NotificationTopic { NOTICE, STUDY_ROOM_TIME_SLOT, STUDY_ROOM_APPLY, POINT, OUTING, ; @@ -9,3 +11,12 @@ enum class NotificationTopic { val subscribe: Boolean, ) } + +fun List.toModel(): List = + this.map(NotificationTopic.Subscription::toModel) + +private fun NotificationTopic.Subscription.toModel(): BatchUpdateNotificationTopicRequest.NotificationTopicRequest = + BatchUpdateNotificationTopicRequest.NotificationTopicRequest( + topic = this.topic.name, + subscribed = this.subscribe, + ) From 3cf16b903b0b19fff8beb44c13d1641fdf584164 Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 30 Jul 2024 14:21:19 +0900 Subject: [PATCH 072/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20enum=20?= =?UTF-8?q?=EB=B0=8F=20=EB=A7=A4=ED=95=91=20=ED=95=A8=EC=88=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/NotificationTopicGroup.kt | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/data/src/main/java/team/aliens/dms/android/data/notification/model/NotificationTopicGroup.kt b/data/src/main/java/team/aliens/dms/android/data/notification/model/NotificationTopicGroup.kt index 604d0416b..02486a0cf 100644 --- a/data/src/main/java/team/aliens/dms/android/data/notification/model/NotificationTopicGroup.kt +++ b/data/src/main/java/team/aliens/dms/android/data/notification/model/NotificationTopicGroup.kt @@ -1,19 +1,42 @@ package team.aliens.dms.android.data.notification.model +import team.aliens.dms.android.network.notification.model.FetchNotificationTopicStatusResponse + enum class NotificationTopicGroup { - NOTICE, STUDY_ROOM, STUDY_ROOM_APPLY, + NOTICE, STUDY_ROOM, POINT, OUTING, ; data class Status( val topicGroup: NotificationTopicGroup, - val groupTitle: String, + val groupName: String, val topicSubscriptions: List, ) { data class TopicSubscription( val topic: NotificationTopic, - val title: String, - val description: String, val subscribed: Boolean, ) } } + +fun FetchNotificationTopicStatusResponse.toModel(): List = + this.topicGroups.toModel() + +@JvmName("ListTopicGroupResponse") +private fun List.toModel(): List = + this.map(FetchNotificationTopicStatusResponse.TopicGroupResponse::toModel) + +private fun FetchNotificationTopicStatusResponse.TopicGroupResponse.toModel(): NotificationTopicGroup.Status = + NotificationTopicGroup.Status( + topicGroup = NotificationTopicGroup.valueOf(this.topicGroup), + groupName = this.groupName, + topicSubscriptions = this.topicSubscriptions.toModel(), + ) + +private fun List.toModel(): List = + this.map(FetchNotificationTopicStatusResponse.TopicGroupResponse.TopicSubscriptionResponse::toModel) + +private fun FetchNotificationTopicStatusResponse.TopicGroupResponse.TopicSubscriptionResponse.toModel(): NotificationTopicGroup.Status.TopicSubscription = + NotificationTopicGroup.Status.TopicSubscription( + topic = NotificationTopic.valueOf(this.topic), + subscribed = this.subscribed, + ) From e3998941aa08c8df9acf8286a1a7d0f54d1c7ef4 Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 30 Jul 2024 14:55:00 +0900 Subject: [PATCH 073/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=20=EA=B0=92=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/notification/repository/NotificationRepository.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepository.kt b/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepository.kt index 45374a6e7..57e578eac 100644 --- a/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepository.kt +++ b/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepository.kt @@ -27,7 +27,7 @@ abstract class NotificationRepository { abstract suspend fun batchUpdateNotificationTopic(subscriptions: List) // TODO device token 파라미터 고민 - abstract suspend fun fetchNotificationStatus(deviceToken: String): NotificationTopicGroup.Status + abstract suspend fun fetchNotificationStatus(deviceToken: String): List abstract suspend fun fetchNotifications(): List From 4d9799694d5b8e3524e94ae08c908ad5560e2b82 Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 30 Jul 2024 14:56:03 +0900 Subject: [PATCH 074/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20datasource=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/NotificationRepositoryImpl.kt | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepositoryImpl.kt b/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepositoryImpl.kt index 2cac648a5..57314d9ed 100644 --- a/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepositoryImpl.kt +++ b/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepositoryImpl.kt @@ -5,7 +5,12 @@ import team.aliens.dms.android.network.notification.datasource.NetworkNotificati import team.aliens.dms.android.data.notification.model.Notification import team.aliens.dms.android.data.notification.model.NotificationTopic import team.aliens.dms.android.data.notification.model.NotificationTopicGroup +import team.aliens.dms.android.data.notification.model.toModel +import team.aliens.dms.android.network.notification.model.BatchUpdateNotificationTopicRequest +import team.aliens.dms.android.network.notification.model.FetchNotificationTopicStatusRequest import team.aliens.dms.android.network.notification.model.RegisterFcmDeviceTokenRequest +import team.aliens.dms.android.network.notification.model.SubscribeNotificationTopicRequest +import team.aliens.dms.android.network.notification.model.UnsubscribeNotificationTopicRequest import javax.inject.Inject internal class NotificationRepositoryImpl @Inject constructor( @@ -24,26 +29,35 @@ internal class NotificationRepositoryImpl @Inject constructor( override suspend fun subscribeNotificationTopic( deviceToken: String, topic: NotificationTopic, - ) { - TODO("Not yet implemented") - } + ) = networkNotificationDataSource.subscribeNotificationTopic( + request = SubscribeNotificationTopicRequest( + deviceToken = deviceToken, + topic = topic.name, + ) + ) override suspend fun unsubscribeNotificationTopic( deviceToken: String, topic: NotificationTopic, - ) { - TODO("Not yet implemented") - } + ) = networkNotificationDataSource.unsubscribeNotificationTopic( + request = UnsubscribeNotificationTopicRequest( + deviceToken = deviceToken, + topic = topic.name, + ) + ) override suspend fun batchUpdateNotificationTopic( subscriptions: List, - ) { - TODO("Not yet implemented") - } + ) = networkNotificationDataSource.batchUpdateNotificationTopic( + request = BatchUpdateNotificationTopicRequest( + topics = subscriptions.toModel() + ) + ) + + override suspend fun fetchNotificationStatus(deviceToken: String): List = + networkNotificationDataSource.fetchNotificationTopicStatus(deviceToken = deviceToken) + .toModel() - override suspend fun fetchNotificationStatus(deviceToken: String): NotificationTopicGroup.Status { - TODO("Not yet implemented") - } override suspend fun fetchNotifications(): List { TODO("Not yet implemented") From 193d19e43d147f52b1d10069f3d59751ca0fff82 Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 30 Jul 2024 14:56:20 +0900 Subject: [PATCH 075/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20response=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/FetchNotificationTopicStatusResponse.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/network/src/main/java/team/aliens/dms/android/network/notification/model/FetchNotificationTopicStatusResponse.kt b/network/src/main/java/team/aliens/dms/android/network/notification/model/FetchNotificationTopicStatusResponse.kt index 9e88df9fc..057b4820e 100644 --- a/network/src/main/java/team/aliens/dms/android/network/notification/model/FetchNotificationTopicStatusResponse.kt +++ b/network/src/main/java/team/aliens/dms/android/network/notification/model/FetchNotificationTopicStatusResponse.kt @@ -6,14 +6,12 @@ data class FetchNotificationTopicStatusResponse( @SerializedName("topic_groups") val topicGroups: List, ) { data class TopicGroupResponse( - @SerializedName("topic_group") val topicGroup: NotificationTopicGroup, - @SerializedName("group_title") val groupTitle: String, + @SerializedName("topic_group") val topicGroup: String, + @SerializedName("group_name") val groupName: String, @SerializedName("topic_subscriptions") val topicSubscriptions: List, ) { data class TopicSubscriptionResponse( @SerializedName("topic") val topic: String, - @SerializedName("title") val title: String, - @SerializedName("description") val description: String, @SerializedName("is_subscribed") val subscribed: Boolean, ) } From 734c8e55f31c8a7af23456502ef850554a200cc1 Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 30 Jul 2024 15:05:29 +0900 Subject: [PATCH 076/111] =?UTF-8?q?=E2=9A=B0=EF=B8=8F=20::=20FetchNotifica?= =?UTF-8?q?tionTopicStatusRequest=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/FetchNotificationTopicStatusRequest.kt | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 network/src/main/java/team/aliens/dms/android/network/notification/model/FetchNotificationTopicStatusRequest.kt diff --git a/network/src/main/java/team/aliens/dms/android/network/notification/model/FetchNotificationTopicStatusRequest.kt b/network/src/main/java/team/aliens/dms/android/network/notification/model/FetchNotificationTopicStatusRequest.kt deleted file mode 100644 index 1f73dabc1..000000000 --- a/network/src/main/java/team/aliens/dms/android/network/notification/model/FetchNotificationTopicStatusRequest.kt +++ /dev/null @@ -1,7 +0,0 @@ -package team.aliens.dms.android.network.notification.model - -import com.google.gson.annotations.SerializedName - -data class FetchNotificationTopicStatusRequest( - @SerializedName("device_token") val deviceToken: String, -) From c8f7f2b557c7abbdf8e68cf105f4b3bd24f5b0be Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 30 Jul 2024 15:06:31 +0900 Subject: [PATCH 077/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20query=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/apiservice/NotificationApiService.kt | 4 ++-- .../notification/datasource/NetworkNotificationDataSource.kt | 3 +-- .../datasource/NetworkNotificationDataSourceImpl.kt | 5 ++--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/network/src/main/java/team/aliens/dms/android/network/notification/apiservice/NotificationApiService.kt b/network/src/main/java/team/aliens/dms/android/network/notification/apiservice/NotificationApiService.kt index 56230635b..ba0ee4356 100644 --- a/network/src/main/java/team/aliens/dms/android/network/notification/apiservice/NotificationApiService.kt +++ b/network/src/main/java/team/aliens/dms/android/network/notification/apiservice/NotificationApiService.kt @@ -5,9 +5,9 @@ import retrofit2.http.DELETE import retrofit2.http.GET import retrofit2.http.PATCH import retrofit2.http.POST +import retrofit2.http.Query import team.aliens.dms.android.network.notification.model.BatchUpdateNotificationTopicRequest import team.aliens.dms.android.network.notification.model.CancelFcmDeviceTokenRegistrationRequest -import team.aliens.dms.android.network.notification.model.FetchNotificationTopicStatusRequest import team.aliens.dms.android.network.notification.model.FetchNotificationTopicStatusResponse import team.aliens.dms.android.network.notification.model.FetchNotificationsResponse import team.aliens.dms.android.network.notification.model.RegisterFcmDeviceTokenRequest @@ -43,7 +43,7 @@ internal interface NotificationApiService { @GET("/notifications/topic") suspend fun fetchNotificationTopicStatus( - @Body request: FetchNotificationTopicStatusRequest + @Query("device_token") deviceToken: String, ): FetchNotificationTopicStatusResponse @GET("/notifications") diff --git a/network/src/main/java/team/aliens/dms/android/network/notification/datasource/NetworkNotificationDataSource.kt b/network/src/main/java/team/aliens/dms/android/network/notification/datasource/NetworkNotificationDataSource.kt index 9b1b56ae6..78f12d7c5 100644 --- a/network/src/main/java/team/aliens/dms/android/network/notification/datasource/NetworkNotificationDataSource.kt +++ b/network/src/main/java/team/aliens/dms/android/network/notification/datasource/NetworkNotificationDataSource.kt @@ -2,7 +2,6 @@ package team.aliens.dms.android.network.notification.datasource import team.aliens.dms.android.network.notification.model.BatchUpdateNotificationTopicRequest import team.aliens.dms.android.network.notification.model.CancelFcmDeviceTokenRegistrationRequest -import team.aliens.dms.android.network.notification.model.FetchNotificationTopicStatusRequest import team.aliens.dms.android.network.notification.model.FetchNotificationTopicStatusResponse import team.aliens.dms.android.network.notification.model.FetchNotificationsResponse import team.aliens.dms.android.network.notification.model.RegisterFcmDeviceTokenRequest @@ -21,7 +20,7 @@ abstract class NetworkNotificationDataSource { abstract suspend fun batchUpdateNotificationTopic(request: BatchUpdateNotificationTopicRequest) - abstract suspend fun fetchNotificationTopicStatus(request: FetchNotificationTopicStatusRequest): FetchNotificationTopicStatusResponse + abstract suspend fun fetchNotificationTopicStatus(deviceToken: String): FetchNotificationTopicStatusResponse abstract suspend fun fetchNotifications(): FetchNotificationsResponse } diff --git a/network/src/main/java/team/aliens/dms/android/network/notification/datasource/NetworkNotificationDataSourceImpl.kt b/network/src/main/java/team/aliens/dms/android/network/notification/datasource/NetworkNotificationDataSourceImpl.kt index f7b1d3c3d..9613061a4 100644 --- a/network/src/main/java/team/aliens/dms/android/network/notification/datasource/NetworkNotificationDataSourceImpl.kt +++ b/network/src/main/java/team/aliens/dms/android/network/notification/datasource/NetworkNotificationDataSourceImpl.kt @@ -4,7 +4,6 @@ import team.aliens.dms.android.core.network.util.handleNetworkRequest import team.aliens.dms.android.network.notification.apiservice.NotificationApiService import team.aliens.dms.android.network.notification.model.BatchUpdateNotificationTopicRequest import team.aliens.dms.android.network.notification.model.CancelFcmDeviceTokenRegistrationRequest -import team.aliens.dms.android.network.notification.model.FetchNotificationTopicStatusRequest import team.aliens.dms.android.network.notification.model.FetchNotificationTopicStatusResponse import team.aliens.dms.android.network.notification.model.FetchNotificationsResponse import team.aliens.dms.android.network.notification.model.RegisterFcmDeviceTokenRequest @@ -30,8 +29,8 @@ internal class NetworkNotificationDataSourceImpl @Inject constructor( override suspend fun batchUpdateNotificationTopic(request: BatchUpdateNotificationTopicRequest) = handleNetworkRequest { notificationApiService.batchUpdateNotificationTopic(request) } - override suspend fun fetchNotificationTopicStatus(request: FetchNotificationTopicStatusRequest): FetchNotificationTopicStatusResponse = - handleNetworkRequest { notificationApiService.fetchNotificationTopicStatus(request) } + override suspend fun fetchNotificationTopicStatus(deviceToken: String): FetchNotificationTopicStatusResponse = + handleNetworkRequest { notificationApiService.fetchNotificationTopicStatus(deviceToken) } override suspend fun fetchNotifications(): FetchNotificationsResponse = handleNetworkRequest { notificationApiService.fetchNotifications() } From 0665c44284c7ec4448deb287def65878c63f4e80 Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 30 Jul 2024 19:50:44 +0900 Subject: [PATCH 078/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20request=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/model/SubscribeNotificationTopicRequest.kt | 2 +- .../notification/model/UnsubscribeNotificationTopicRequest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/network/src/main/java/team/aliens/dms/android/network/notification/model/SubscribeNotificationTopicRequest.kt b/network/src/main/java/team/aliens/dms/android/network/notification/model/SubscribeNotificationTopicRequest.kt index 1f73551ab..534d159bf 100644 --- a/network/src/main/java/team/aliens/dms/android/network/notification/model/SubscribeNotificationTopicRequest.kt +++ b/network/src/main/java/team/aliens/dms/android/network/notification/model/SubscribeNotificationTopicRequest.kt @@ -3,6 +3,6 @@ package team.aliens.dms.android.network.notification.model import com.google.gson.annotations.SerializedName data class SubscribeNotificationTopicRequest( - @SerializedName("device_token") val deviceToken: String, + @SerializedName("token") val deviceToken: String, @SerializedName("topic") val topic: String, ) diff --git a/network/src/main/java/team/aliens/dms/android/network/notification/model/UnsubscribeNotificationTopicRequest.kt b/network/src/main/java/team/aliens/dms/android/network/notification/model/UnsubscribeNotificationTopicRequest.kt index 5f30bc353..676c7a267 100644 --- a/network/src/main/java/team/aliens/dms/android/network/notification/model/UnsubscribeNotificationTopicRequest.kt +++ b/network/src/main/java/team/aliens/dms/android/network/notification/model/UnsubscribeNotificationTopicRequest.kt @@ -3,6 +3,6 @@ package team.aliens.dms.android.network.notification.model import com.google.gson.annotations.SerializedName data class UnsubscribeNotificationTopicRequest( - @SerializedName("device_token") val deviceToken: String, + @SerializedName("token") val deviceToken: String, @SerializedName("topic") val topic: String, ) From 7a2a0937b6d64874b3cb86196a4c001967848343 Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 30 Jul 2024 23:12:47 +0900 Subject: [PATCH 079/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20import=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/repository/NotificationRepositoryImpl.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepositoryImpl.kt b/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepositoryImpl.kt index 57314d9ed..742c477bb 100644 --- a/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepositoryImpl.kt +++ b/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepositoryImpl.kt @@ -1,14 +1,12 @@ package team.aliens.dms.android.data.notification.repository import team.aliens.dms.android.core.device.datastore.DeviceDataStoreDataSource -import team.aliens.dms.android.network.notification.datasource.NetworkNotificationDataSource import team.aliens.dms.android.data.notification.model.Notification import team.aliens.dms.android.data.notification.model.NotificationTopic import team.aliens.dms.android.data.notification.model.NotificationTopicGroup import team.aliens.dms.android.data.notification.model.toModel +import team.aliens.dms.android.network.notification.datasource.NetworkNotificationDataSource import team.aliens.dms.android.network.notification.model.BatchUpdateNotificationTopicRequest -import team.aliens.dms.android.network.notification.model.FetchNotificationTopicStatusRequest -import team.aliens.dms.android.network.notification.model.RegisterFcmDeviceTokenRequest import team.aliens.dms.android.network.notification.model.SubscribeNotificationTopicRequest import team.aliens.dms.android.network.notification.model.UnsubscribeNotificationTopicRequest import javax.inject.Inject @@ -58,7 +56,6 @@ internal class NotificationRepositoryImpl @Inject constructor( networkNotificationDataSource.fetchNotificationTopicStatus(deviceToken = deviceToken) .toModel() - override suspend fun fetchNotifications(): List { TODO("Not yet implemented") } From 98d88020bac14f5cbdcae67def919feaee87548b Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 30 Jul 2024 23:13:33 +0900 Subject: [PATCH 080/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20NotificationS?= =?UTF-8?q?ettingsScreen=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../settings/NotificationSettingsScreen.kt | 201 ++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsScreen.kt diff --git a/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsScreen.kt b/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsScreen.kt new file mode 100644 index 000000000..7358876a4 --- /dev/null +++ b/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsScreen.kt @@ -0,0 +1,201 @@ +package team.aliens.dms.android.feature.notification.settings + +import androidx.annotation.StringRes +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.ramcosta.composedestinations.annotation.Destination +import team.aliens.dms.android.core.designsystem.DmsIcon +import team.aliens.dms.android.core.designsystem.DmsTheme +import team.aliens.dms.android.core.designsystem.DmsTopAppBar +import team.aliens.dms.android.core.designsystem.Scaffold +import team.aliens.dms.android.core.designsystem.Switch +import team.aliens.dms.android.core.ui.PaddingDefaults +import team.aliens.dms.android.core.ui.horizontalPadding +import team.aliens.dms.android.core.ui.topPadding +import team.aliens.dms.android.data.notification.model.NotificationTopicGroup +import team.aliens.dms.android.feature.R +import team.aliens.dms.android.feature.notification.navigation.NotificationSettingsNavigator + + +@OptIn(ExperimentalMaterial3Api::class) +@Destination +@Composable +internal fun NotificationSettingsScreen( + modifier: Modifier = Modifier, + navigator: NotificationSettingsNavigator, +) { + val viewModel: NotificationSettingsViewModel = hiltViewModel() + val uiState by viewModel.stateFlow.collectAsStateWithLifecycle() + + Scaffold( + modifier = modifier, + topBar = { + DmsTopAppBar( + title = { Text(text = stringResource(id = R.string.notification_settings)) }, + navigationIcon = { + IconButton(navigator::navigateUp) { + Icon( + painter = painterResource(id = DmsIcon.Back), + contentDescription = stringResource(id = R.string.top_bar_back_button), + ) + } + }, + ) + }, + ) { paddingValues -> + Column( + modifier = Modifier + .fillMaxSize() + .background(DmsTheme.colorScheme.surface) + .padding(paddingValues) + .topPadding(PaddingDefaults.ExtraLarge), + verticalArrangement = Arrangement.spacedBy(34.dp), + ) { + Notifications( + status = uiState.status, + viewModel = viewModel, + ) + } + } +} + +@Composable +private fun Notifications( + status: List, + viewModel: NotificationSettingsViewModel, +) { + val noticeNotifications = remember { + listOf( + Notification( + titleRes = R.string.notification_channel_notice_title, + descriptionRes = R.string.notification_channel_notice_description, + ), + ) + } + val studyRoomNotifications = remember { + listOf( + Notification( + titleRes = R.string.notification_channel_use_study_room_title, + descriptionRes = R.string.notification_channel_use_study_room_description, + ), + Notification( + titleRes = R.string.notification_channel_application_time_study_room_title, + descriptionRes = R.string.notification_channel_application_time_study_room_description, + ), + ) + } + val pointNotifications = remember { + listOf( + Notification( + titleRes = R.string.notification_channel_point_title, + descriptionRes = R.string.notification_channel_point_description, + ), + ) + } + val outingNotifications = remember { + listOf( + Notification( + titleRes = R.string.notification_channel_application_outing_title, + descriptionRes = R.string.notification_channel_application_outing_description, + ), + ) + } + status.forEach { + val notifications = when (it.topicGroup) { + NotificationTopicGroup.NOTICE -> noticeNotifications + NotificationTopicGroup.STUDY_ROOM -> studyRoomNotifications + NotificationTopicGroup.POINT -> pointNotifications + NotificationTopicGroup.OUTING -> outingNotifications + } + NotificationLayout( + viewModel = viewModel, + title = it.groupName, + notifications = notifications, + topicSubscription = it.topicSubscriptions, + ) + } +} + +@Composable +private fun NotificationLayout( + modifier: Modifier = Modifier, + viewModel: NotificationSettingsViewModel, + title: String, + notifications: List, + topicSubscription: List, +) { + Column( + modifier = modifier + .horizontalPadding(), + verticalArrangement = Arrangement.spacedBy(PaddingDefaults.Large) + ) { + Text( + text = title, + style = DmsTheme.typography.title3, + color = DmsTheme.colorScheme.surfaceVariant, + ) + notifications.forEachIndexed { index, notification -> + val subscribed = topicSubscription[index].subscribed + val topic = topicSubscription[index].topic + var isSwitchEnabled by remember { mutableStateOf(subscribed) } + + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Column { + Text( + text = stringResource(id = notification.titleRes), + style = DmsTheme.typography.body2, + color = DmsTheme.colorScheme.onSurface, + ) + Text( + text = stringResource(id = notification.descriptionRes), + style = DmsTheme.typography.caption, + color = DmsTheme.colorScheme.surfaceVariant, + ) + } + Spacer(modifier = Modifier.weight(1f)) + Switch( + checked = isSwitchEnabled, + onCheckedChange = { isChecked -> + isSwitchEnabled = isChecked + viewModel.postIntent( + NotificationSettingsIntent.UpdateNotificationTopic( + isSubscribed = isChecked, + topic = topic, + ) + ) + }, + ) + } + } + } + +} + +private class Notification( + @StringRes val titleRes: Int, + @StringRes val descriptionRes: Int, +) From 855003a6098a714943edaa1dfd0e8c0e116db286 Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 30 Jul 2024 23:14:02 +0900 Subject: [PATCH 081/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20NotificationS?= =?UTF-8?q?ettingsViewModel=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../settings/NotificationSettingsViewModel.kt | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsViewModel.kt diff --git a/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsViewModel.kt b/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsViewModel.kt new file mode 100644 index 000000000..08af121a9 --- /dev/null +++ b/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsViewModel.kt @@ -0,0 +1,145 @@ +package team.aliens.dms.android.feature.notification.settings + +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import team.aliens.dms.android.core.ui.mvi.BaseMviViewModel +import team.aliens.dms.android.core.ui.mvi.Intent +import team.aliens.dms.android.core.ui.mvi.SideEffect +import team.aliens.dms.android.core.ui.mvi.UiState +import team.aliens.dms.android.data.notification.model.NotificationTopic +import team.aliens.dms.android.data.notification.model.NotificationTopicGroup +import team.aliens.dms.android.data.notification.repository.NotificationRepository +import javax.inject.Inject + +@HiltViewModel +internal class NotificationSettingsViewModel @Inject constructor( + private val notificationRepository: NotificationRepository, +) : BaseMviViewModel( + initialState = NotificationSettingsUiState.initial() +) { + + override fun processIntent(intent: NotificationSettingsIntent) { + when (intent) { + is NotificationSettingsIntent.UpdateNotificationTopic -> this.updateNotificationTopic( + isSubscribed = intent.isSubscribed, + topic = intent.topic, + ) + } + } + + init { + fetchDeviceToken() + fetchNotificationsStatus() + } + + private fun fetchNotificationsStatus() { + viewModelScope.launch(Dispatchers.IO) { + val deviceToken = notificationRepository.getDeviceToken() + runCatching { + notificationRepository.fetchNotificationStatus(deviceToken) + }.onSuccess { + reduce(newState = stateFlow.value.copy(status = it)) + }.onFailure { + postSideEffect(NotificationSettingsSideEffect.CurrentNotificationsStatusNotFound) + } + } + } + + private fun fetchDeviceToken() { + viewModelScope.launch(Dispatchers.IO) { + runCatching { + notificationRepository.getDeviceToken() + }.onSuccess { deviceToken -> + reduce(newState = stateFlow.value.copy(deviceToken = deviceToken)) + } + } + } + + private fun updateNotificationTopic( + isSubscribed: Boolean, + topic: NotificationTopic, + ) { + viewModelScope.launch(Dispatchers.IO) { + val deviceToken = notificationRepository.getDeviceToken() + + if (isSubscribed) { + subscribeNotificationTopic( + deviceToken = deviceToken, + topic = topic, + ) + } else { + unsubscribeNotificationTopic( + deviceToken = deviceToken, + topic = topic, + ) + } + } + } + + private fun subscribeNotificationTopic( + deviceToken: String, + topic: NotificationTopic, + ) { + viewModelScope.launch(Dispatchers.IO) { + runCatching { + notificationRepository.subscribeNotificationTopic( + deviceToken = deviceToken, + topic = topic, + ) + }.onSuccess { + postSideEffect(NotificationSettingsSideEffect.SubscribeNotificationSuccess) + }.onFailure { + postSideEffect(NotificationSettingsSideEffect.SubscribeNotificationFailure) + } + } + } + + private fun unsubscribeNotificationTopic( + deviceToken: String, + topic: NotificationTopic, + ) { + viewModelScope.launch(Dispatchers.IO) { + runCatching { + notificationRepository.unsubscribeNotificationTopic( + deviceToken = deviceToken, + topic = topic, + ) + }.onSuccess { + postSideEffect(NotificationSettingsSideEffect.UnSubscribeNotificationSuccess) + }.onFailure { + postSideEffect(NotificationSettingsSideEffect.UnSubscribeNotificationFailure) + } + } + } +} + +internal data class NotificationSettingsUiState( + val deviceToken: String, + val status: List, +) : UiState() { + companion object { + fun initial(): NotificationSettingsUiState { + return NotificationSettingsUiState( + deviceToken = "", + status = listOf(), + ) + } + } +} + +internal sealed class NotificationSettingsIntent : Intent() { + class UpdateNotificationTopic( + val isSubscribed: Boolean, + val topic: NotificationTopic, + ) : NotificationSettingsIntent() +} + +internal sealed class NotificationSettingsSideEffect : SideEffect() { + data object CurrentNotificationsStatusNotFound : NotificationSettingsSideEffect() + data object SubscribeNotificationSuccess : NotificationSettingsSideEffect() + data object SubscribeNotificationFailure : NotificationSettingsSideEffect() + data object UnSubscribeNotificationSuccess : NotificationSettingsSideEffect() + data object UnSubscribeNotificationFailure : NotificationSettingsSideEffect() +} From 13025891d49df15c2c0bb4cd50efa04aa8e0fef7 Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 30 Jul 2024 23:14:18 +0900 Subject: [PATCH 082/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EB=AC=B8?= =?UTF-8?q?=EC=9E=90=EC=97=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/src/main/res/values/strings.xml | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/feature/src/main/res/values/strings.xml b/feature/src/main/res/values/strings.xml index 769000943..d35a9988e 100644 --- a/feature/src/main/res/values/strings.xml +++ b/feature/src/main/res/values/strings.xml @@ -214,6 +214,7 @@ 프로필 수정 상벌점 내역 확인 비밀번호 변경 + 알림 설정 상벌점 확인 %d 점 %d월 %d일 @@ -297,9 +298,22 @@ ch-dms-notification gr-dms-notification-notice - 공지사항 - 공지사항 - 공지사항에 관한 알림을 설정합니다. + 알림 설정 + 공지 + 자습실 + 상벌점 + 외출 + 공지 알림 + 기숙사 공지에 대한 알림입니다. + 이용 시간 알림 + 자습실 이용 시작 10분 전에 알림을 받아요. + 신청 시간 알림 + 자습실 신청 시간을 알리는 알림입니다. + 상벌점 알림 + 상벌점이 부여되었을 때 알림을 받아요. + 외출 신청 알림 + 동행자로 외출 신청되었을 때 알림을 받아요. + 학년 번호 From ab15cd32e97307c076d13cdd984ddaea085fc262 Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 30 Jul 2024 23:14:38 +0900 Subject: [PATCH 083/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20api=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../network/notification/apiservice/NotificationApiService.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/network/src/main/java/team/aliens/dms/android/network/notification/apiservice/NotificationApiService.kt b/network/src/main/java/team/aliens/dms/android/network/notification/apiservice/NotificationApiService.kt index ba0ee4356..78aace191 100644 --- a/network/src/main/java/team/aliens/dms/android/network/notification/apiservice/NotificationApiService.kt +++ b/network/src/main/java/team/aliens/dms/android/network/notification/apiservice/NotificationApiService.kt @@ -3,6 +3,7 @@ package team.aliens.dms.android.network.notification.apiservice import retrofit2.http.Body import retrofit2.http.DELETE import retrofit2.http.GET +import retrofit2.http.HTTP import retrofit2.http.PATCH import retrofit2.http.POST import retrofit2.http.Query @@ -31,7 +32,7 @@ internal interface NotificationApiService { @Body request: SubscribeNotificationTopicRequest ) - @DELETE("/notifications/topic") + @HTTP(method = "DELETE", path = "/notifications/topic", hasBody = true) suspend fun unsubscribeNotificationTopic( @Body request: UnsubscribeNotificationTopicRequest ) From e7dac26e3d02012e8b40d23a6ca845cf0c8df1f4 Mon Sep 17 00:00:00 2001 From: uiel Date: Wed, 31 Jul 2024 21:43:49 +0900 Subject: [PATCH 084/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../settings/NotificationSettingsScreen.kt | 25 ++++++++++++++++++- feature/src/main/res/values/strings.xml | 6 +++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsScreen.kt b/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsScreen.kt index 7358876a4..aa934d18f 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsScreen.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsScreen.kt @@ -19,6 +19,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -28,9 +29,11 @@ import com.ramcosta.composedestinations.annotation.Destination import team.aliens.dms.android.core.designsystem.DmsIcon import team.aliens.dms.android.core.designsystem.DmsTheme import team.aliens.dms.android.core.designsystem.DmsTopAppBar +import team.aliens.dms.android.core.designsystem.LocalToast import team.aliens.dms.android.core.designsystem.Scaffold import team.aliens.dms.android.core.designsystem.Switch import team.aliens.dms.android.core.ui.PaddingDefaults +import team.aliens.dms.android.core.ui.collectInLaunchedEffectWithLifecycle import team.aliens.dms.android.core.ui.horizontalPadding import team.aliens.dms.android.core.ui.topPadding import team.aliens.dms.android.data.notification.model.NotificationTopicGroup @@ -47,7 +50,28 @@ internal fun NotificationSettingsScreen( ) { val viewModel: NotificationSettingsViewModel = hiltViewModel() val uiState by viewModel.stateFlow.collectAsStateWithLifecycle() + val context = LocalContext.current + val toast = LocalToast.current + viewModel.sideEffectFlow.collectInLaunchedEffectWithLifecycle { sideEffect -> + when(sideEffect) { + NotificationSettingsSideEffect.CurrentNotificationsStatusNotFound -> toast.showErrorToast( + message = context.getString(R.string.notification_not_current) + ) + NotificationSettingsSideEffect.SubscribeNotificationSuccess -> toast.showSuccessToast( + message = context.getString(R.string.notification_subscribe_success) + ) + NotificationSettingsSideEffect.SubscribeNotificationFailure -> toast.showErrorToast( + message = context.getString(R.string.notification_subscribe_fail) + ) + NotificationSettingsSideEffect.UnSubscribeNotificationSuccess -> toast.showSuccessToast( + message = context.getString(R.string.notification_unsubscribe_success) + ) + NotificationSettingsSideEffect.UnSubscribeNotificationFailure -> toast.showErrorToast( + message = context.getString(R.string.notification_unsubscribe_fail) + ) + } + } Scaffold( modifier = modifier, topBar = { @@ -192,7 +216,6 @@ private fun NotificationLayout( } } } - } private class Notification( diff --git a/feature/src/main/res/values/strings.xml b/feature/src/main/res/values/strings.xml index d35a9988e..bbb083dec 100644 --- a/feature/src/main/res/values/strings.xml +++ b/feature/src/main/res/values/strings.xml @@ -314,6 +314,12 @@ 외출 신청 알림 동행자로 외출 신청되었을 때 알림을 받아요. + 알림 설정을 가져오지 못했어요 + 알림이 구독되었습니다 + 알림 구독을 실패했어요 + 알림이 구독 취소 되었어요 + 알림 구독 취소를 실패했어요 + 학년 번호 From 27753c7033b1191c8eb31cecb92152543d3e51e5 Mon Sep 17 00:00:00 2001 From: uiel Date: Wed, 31 Jul 2024 21:44:22 +0900 Subject: [PATCH 085/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20request=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/model/SubscribeNotificationTopicRequest.kt | 2 +- .../notification/model/UnsubscribeNotificationTopicRequest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/network/src/main/java/team/aliens/dms/android/network/notification/model/SubscribeNotificationTopicRequest.kt b/network/src/main/java/team/aliens/dms/android/network/notification/model/SubscribeNotificationTopicRequest.kt index 534d159bf..1f73551ab 100644 --- a/network/src/main/java/team/aliens/dms/android/network/notification/model/SubscribeNotificationTopicRequest.kt +++ b/network/src/main/java/team/aliens/dms/android/network/notification/model/SubscribeNotificationTopicRequest.kt @@ -3,6 +3,6 @@ package team.aliens.dms.android.network.notification.model import com.google.gson.annotations.SerializedName data class SubscribeNotificationTopicRequest( - @SerializedName("token") val deviceToken: String, + @SerializedName("device_token") val deviceToken: String, @SerializedName("topic") val topic: String, ) diff --git a/network/src/main/java/team/aliens/dms/android/network/notification/model/UnsubscribeNotificationTopicRequest.kt b/network/src/main/java/team/aliens/dms/android/network/notification/model/UnsubscribeNotificationTopicRequest.kt index 676c7a267..5f30bc353 100644 --- a/network/src/main/java/team/aliens/dms/android/network/notification/model/UnsubscribeNotificationTopicRequest.kt +++ b/network/src/main/java/team/aliens/dms/android/network/notification/model/UnsubscribeNotificationTopicRequest.kt @@ -3,6 +3,6 @@ package team.aliens.dms.android.network.notification.model import com.google.gson.annotations.SerializedName data class UnsubscribeNotificationTopicRequest( - @SerializedName("token") val deviceToken: String, + @SerializedName("device_token") val deviceToken: String, @SerializedName("topic") val topic: String, ) From f1a48aa98a65c7acf2aba6d0e8094a89bdbf94d8 Mon Sep 17 00:00:00 2001 From: uiel Date: Wed, 31 Jul 2024 21:46:33 +0900 Subject: [PATCH 086/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../settings/NotificationSettingsViewModel.kt | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsViewModel.kt b/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsViewModel.kt index 08af121a9..316a4086f 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsViewModel.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsViewModel.kt @@ -30,7 +30,6 @@ internal class NotificationSettingsViewModel @Inject constructor( } init { - fetchDeviceToken() fetchNotificationsStatus() } @@ -47,16 +46,6 @@ internal class NotificationSettingsViewModel @Inject constructor( } } - private fun fetchDeviceToken() { - viewModelScope.launch(Dispatchers.IO) { - runCatching { - notificationRepository.getDeviceToken() - }.onSuccess { deviceToken -> - reduce(newState = stateFlow.value.copy(deviceToken = deviceToken)) - } - } - } - private fun updateNotificationTopic( isSubscribed: Boolean, topic: NotificationTopic, @@ -116,13 +105,11 @@ internal class NotificationSettingsViewModel @Inject constructor( } internal data class NotificationSettingsUiState( - val deviceToken: String, val status: List, ) : UiState() { companion object { fun initial(): NotificationSettingsUiState { return NotificationSettingsUiState( - deviceToken = "", status = listOf(), ) } From b23f51979389577fa8bb5db6a0e073f7a522b880 Mon Sep 17 00:00:00 2001 From: uiel Date: Sat, 3 Aug 2024 16:24:53 +0900 Subject: [PATCH 087/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/aliens/dms/android/core/designsystem/DmsIcon.kt | 1 + .../design-system/src/main/res/drawable/ic_blue_bell.xml | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 core/design-system/src/main/res/drawable/ic_blue_bell.xml diff --git a/core/design-system/src/main/java/team/aliens/dms/android/core/designsystem/DmsIcon.kt b/core/design-system/src/main/java/team/aliens/dms/android/core/designsystem/DmsIcon.kt index a4deca8a5..9e37ddee8 100644 --- a/core/design-system/src/main/java/team/aliens/dms/android/core/designsystem/DmsIcon.kt +++ b/core/design-system/src/main/java/team/aliens/dms/android/core/designsystem/DmsIcon.kt @@ -5,6 +5,7 @@ object DmsIcon { val Back = R.drawable.ic_back val Backward = R.drawable.ic_backward val Bell = R.drawable.ic_bell + val BlueBell = R.drawable.ic_blue_bell val BlueBreakfast = R.drawable.ic_blue_breakfast val BlueDinner = R.drawable.ic_blue_dinner val BlueLaunch = R.drawable.ic_blue_lunch diff --git a/core/design-system/src/main/res/drawable/ic_blue_bell.xml b/core/design-system/src/main/res/drawable/ic_blue_bell.xml new file mode 100644 index 000000000..13d87c4e3 --- /dev/null +++ b/core/design-system/src/main/res/drawable/ic_blue_bell.xml @@ -0,0 +1,9 @@ + + + From 8b8bf42901e03f1df004f1b60a12fe5262f2785f Mon Sep 17 00:00:00 2001 From: uiel Date: Sat, 3 Aug 2024 16:28:14 +0900 Subject: [PATCH 088/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EA=BA=BC=EC=A7=90=20=ED=91=9C=EC=8B=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../settings/NotificationSettingsScreen.kt | 44 +++++++++++++++++-- feature/src/main/res/values/strings.xml | 3 ++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsScreen.kt b/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsScreen.kt index aa934d18f..a059205f1 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsScreen.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsScreen.kt @@ -2,23 +2,30 @@ package team.aliens.dms.android.feature.notification.settings import androidx.annotation.StringRes import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.scrollable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -32,7 +39,9 @@ import team.aliens.dms.android.core.designsystem.DmsTopAppBar import team.aliens.dms.android.core.designsystem.LocalToast import team.aliens.dms.android.core.designsystem.Scaffold import team.aliens.dms.android.core.designsystem.Switch +import team.aliens.dms.android.core.notification.notificationPermissionGranted import team.aliens.dms.android.core.ui.PaddingDefaults +import team.aliens.dms.android.core.ui.bottomPadding import team.aliens.dms.android.core.ui.collectInLaunchedEffectWithLifecycle import team.aliens.dms.android.core.ui.horizontalPadding import team.aliens.dms.android.core.ui.topPadding @@ -72,6 +81,7 @@ internal fun NotificationSettingsScreen( ) } } + Scaffold( modifier = modifier, topBar = { @@ -88,14 +98,20 @@ internal fun NotificationSettingsScreen( ) }, ) { paddingValues -> + val scrollState = rememberScrollState() + Column( modifier = Modifier .fillMaxSize() + .verticalScroll(scrollState) .background(DmsTheme.colorScheme.surface) .padding(paddingValues) - .topPadding(PaddingDefaults.ExtraLarge), - verticalArrangement = Arrangement.spacedBy(34.dp), + .topPadding(PaddingDefaults.Large), + verticalArrangement = Arrangement.spacedBy(24.dp), ) { + if(!notificationPermissionGranted(context)) { + Notice() + } Notifications( status = uiState.status, viewModel = viewModel, @@ -104,6 +120,28 @@ internal fun NotificationSettingsScreen( } } +@Composable +private fun Notice() { + Column( + modifier = Modifier + .fillMaxWidth() + .horizontalPadding() + .clip(DmsTheme.shapes.medium) + .background(DmsTheme.colorScheme.primaryContainer) + .padding(PaddingDefaults.Large), + verticalArrangement = Arrangement.spacedBy(PaddingDefaults.Medium), + ) { + Icon( + painter = painterResource(id = DmsIcon.BlueBell), + contentDescription = stringResource(id = R.string.notification_notification_icon), + tint = DmsTheme.colorScheme.primary, + ) + Text( + text = stringResource(id = R.string.notification_notice_off_notification), + style = DmsTheme.typography.body3, + ) + } +} @Composable private fun Notifications( status: List, @@ -172,7 +210,7 @@ private fun NotificationLayout( Column( modifier = modifier .horizontalPadding(), - verticalArrangement = Arrangement.spacedBy(PaddingDefaults.Large) + verticalArrangement = Arrangement.spacedBy(PaddingDefaults.Large), ) { Text( text = title, diff --git a/feature/src/main/res/values/strings.xml b/feature/src/main/res/values/strings.xml index bbb083dec..0bc93375b 100644 --- a/feature/src/main/res/values/strings.xml +++ b/feature/src/main/res/values/strings.xml @@ -320,6 +320,9 @@ 알림이 구독 취소 되었어요 알림 구독 취소를 실패했어요 + 알림 아이콘 + 현재 기기 설정에서 DMS 알림이 꺼져 있습니다.\n알림을 받으려면 기기 설정에서\nDMS 알림을 활성화해주세요. + 학년 번호 From 5d8066d0f01c5333d33d38d0424ce07a5c4353b6 Mon Sep 17 00:00:00 2001 From: uiel Date: Mon, 5 Aug 2024 22:35:59 +0900 Subject: [PATCH 089/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20response=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../network/notification/model/FetchNotificationsResponse.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/network/src/main/java/team/aliens/dms/android/network/notification/model/FetchNotificationsResponse.kt b/network/src/main/java/team/aliens/dms/android/network/notification/model/FetchNotificationsResponse.kt index 7881cdb9e..414f1a8fd 100644 --- a/network/src/main/java/team/aliens/dms/android/network/notification/model/FetchNotificationsResponse.kt +++ b/network/src/main/java/team/aliens/dms/android/network/notification/model/FetchNotificationsResponse.kt @@ -1,6 +1,7 @@ package team.aliens.dms.android.network.notification.model import com.google.gson.annotations.SerializedName +import org.threeten.bp.LocalDateTime import java.util.UUID data class FetchNotificationsResponse( @@ -12,7 +13,7 @@ data class FetchNotificationsResponse( @SerializedName("link_identifier") val linkId: UUID, @SerializedName("title") val title: String, @SerializedName("content") val content: String, - @SerializedName("created_at") val createdAt: String, - @SerializedName("is_read") val read: Boolean, + @SerializedName("created_at") val createdAt: LocalDateTime, + @SerializedName("is_read") val isRead: Boolean, ) } From ed5512d3390428b413ef4de0d2b2b9908b621152 Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 6 Aug 2024 11:19:43 +0900 Subject: [PATCH 090/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20navigator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dms/android/app/navigation/DmsNavigator.kt | 13 +++++++++---- .../app/navigation/authorized/AuthorizedNavGraph.kt | 2 ++ .../navigation/authorized/AuthorizedNavigator.kt | 2 ++ .../feature/main/navigation/MainNavigator.kt | 4 +++- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/team/aliens/dms/android/app/navigation/DmsNavigator.kt b/app/src/main/java/team/aliens/dms/android/app/navigation/DmsNavigator.kt index 1bad995af..d15902574 100644 --- a/app/src/main/java/team/aliens/dms/android/app/navigation/DmsNavigator.kt +++ b/app/src/main/java/team/aliens/dms/android/app/navigation/DmsNavigator.kt @@ -21,6 +21,7 @@ import team.aliens.dms.android.feature.destinations.EnterSchoolVerificationQuest import team.aliens.dms.android.feature.destinations.FindIdScreenDestination import team.aliens.dms.android.feature.destinations.MainDestination import team.aliens.dms.android.feature.destinations.NoticeDetailsScreenDestination +import team.aliens.dms.android.feature.destinations.NotificationBoxScreenDestination import team.aliens.dms.android.feature.destinations.NotificationSettingsScreenDestination import team.aliens.dms.android.feature.destinations.OutingApplicationScreenDestination import team.aliens.dms.android.feature.destinations.PointHistoryScreenDestination @@ -47,10 +48,6 @@ class DmsNavigator( private val navController: NavController, ) : AuthorizedNavigator, UnauthorizedNavigator { - override fun openSettingsNotification() { - navController.navigateSingleTop(NotificationSettingsScreenDestination within navGraph) - } - override fun openUnauthorizedNav() { navController.navigateSingleTop(UnauthorizedNavGraph) { popUpTo(AuthorizedNavGraph) { @@ -59,6 +56,14 @@ class DmsNavigator( } } + override fun openSettingsNotification() { + navController.navigateSingleTop(NotificationSettingsScreenDestination within navGraph) + } + + override fun openNotificationBox() { + navController.navigateSingleTop(NotificationBoxScreenDestination within navGraph) + } + override fun openStudyRoomList() { navController.navigateSingleTop(StudyRoomListScreenDestination within navGraph) } diff --git a/app/src/main/java/team/aliens/dms/android/app/navigation/authorized/AuthorizedNavGraph.kt b/app/src/main/java/team/aliens/dms/android/app/navigation/authorized/AuthorizedNavGraph.kt index 595f76759..d8195c5d8 100644 --- a/app/src/main/java/team/aliens/dms/android/app/navigation/authorized/AuthorizedNavGraph.kt +++ b/app/src/main/java/team/aliens/dms/android/app/navigation/authorized/AuthorizedNavGraph.kt @@ -8,6 +8,7 @@ import team.aliens.dms.android.feature.destinations.AnnouncementListScreenDestin import team.aliens.dms.android.feature.destinations.EditProfileImageScreenDestination import team.aliens.dms.android.feature.destinations.MainDestination import team.aliens.dms.android.feature.destinations.NoticeDetailsScreenDestination +import team.aliens.dms.android.feature.destinations.NotificationBoxScreenDestination import team.aliens.dms.android.feature.destinations.NotificationSettingsScreenDestination import team.aliens.dms.android.feature.destinations.PointHistoryScreenDestination import team.aliens.dms.android.feature.destinations.RemainsApplicationScreenDestination @@ -36,6 +37,7 @@ object AuthorizedNavGraph : NavGraphSpec { StudyRoomListScreenDestination, StudyRoomDetailsScreenDestination, NoticeDetailsScreenDestination, + NotificationBoxScreenDestination, NotificationSettingsScreenDestination, PointHistoryScreenDestination, ) diff --git a/app/src/main/java/team/aliens/dms/android/app/navigation/authorized/AuthorizedNavigator.kt b/app/src/main/java/team/aliens/dms/android/app/navigation/authorized/AuthorizedNavigator.kt index 69da5aa5d..ce57953a8 100644 --- a/app/src/main/java/team/aliens/dms/android/app/navigation/authorized/AuthorizedNavigator.kt +++ b/app/src/main/java/team/aliens/dms/android/app/navigation/authorized/AuthorizedNavigator.kt @@ -4,6 +4,7 @@ import team.aliens.dms.android.feature.editpassword.navigation.EditPasswordNavig import team.aliens.dms.android.feature.editprofile.navigation.EditProfileNavigator import team.aliens.dms.android.feature.main.navigation.MainNavigator import team.aliens.dms.android.feature.notice.navigation.NoticeNavigator +import team.aliens.dms.android.feature.notification.navigation.NotificationBoxNavigator import team.aliens.dms.android.feature.notification.navigation.NotificationSettingsNavigator import team.aliens.dms.android.feature.outing.navigation.OutingNavigator import team.aliens.dms.android.feature.point.navigation.PointHistoryNavigator @@ -15,6 +16,7 @@ interface AuthorizedNavigator : EditPasswordNavigator, EditProfileNavigator, NoticeNavigator, + NotificationBoxNavigator, NotificationSettingsNavigator, PointHistoryNavigator, RemainsNavigator, diff --git a/feature/src/main/java/team/aliens/dms/android/feature/main/navigation/MainNavigator.kt b/feature/src/main/java/team/aliens/dms/android/feature/main/navigation/MainNavigator.kt index 0c61ac970..ec0b673a3 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/main/navigation/MainNavigator.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/main/navigation/MainNavigator.kt @@ -5,9 +5,11 @@ import java.util.UUID interface MainNavigator { + fun openUnauthorizedNav() + fun openSettingsNotification() - fun openUnauthorizedNav() + fun openNotificationBox() fun openStudyRoomList() From 5dc6024c67dab968f84598e719f6594e6daedd27 Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 6 Aug 2024 11:20:23 +0900 Subject: [PATCH 091/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=ED=95=A8=20=EC=9D=B4=EB=8F=99=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dms/android/feature/main/home/HomeScreen.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/feature/src/main/java/team/aliens/dms/android/feature/main/home/HomeScreen.kt b/feature/src/main/java/team/aliens/dms/android/feature/main/home/HomeScreen.kt index f6ec86009..ad284e665 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/main/home/HomeScreen.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/main/home/HomeScreen.kt @@ -32,6 +32,7 @@ import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material3.CardDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Text import androidx.compose.material3.pulltorefresh.PullToRefreshContainer @@ -67,6 +68,7 @@ import org.threeten.bp.DayOfWeek import org.threeten.bp.LocalDate import team.aliens.dms.android.core.designsystem.ButtonDefaults import team.aliens.dms.android.core.designsystem.DmsCalendar +import team.aliens.dms.android.core.designsystem.DmsIcon import team.aliens.dms.android.core.designsystem.DmsTheme import team.aliens.dms.android.core.designsystem.DmsTopAppBar import team.aliens.dms.android.core.designsystem.LocalToast @@ -94,6 +96,7 @@ import team.aliens.dms.android.feature.main.home.MealCardType.BREAKFAST import team.aliens.dms.android.feature.main.home.MealCardType.DINNER import team.aliens.dms.android.feature.main.home.MealCardType.LUNCH import team.aliens.dms.android.shared.date.util.now +import java.util.UUID import kotlin.math.absoluteValue @OptIn(ExperimentalMaterial3Api::class) @@ -104,6 +107,7 @@ internal fun HomeScreen( viewModel: HomeViewModel = hiltViewModel(), onChangeBottomAppBarVisibility: (visible: Boolean) -> Unit, onNavigateToAnnouncementList: () -> Unit, + onNavigateToNotificationBox: () -> Unit, ) { val toast = LocalToast.current val context = LocalContext.current @@ -173,6 +177,15 @@ internal fun HomeScreen( ), ) }, + actions = { + IconButton(onClick = onNavigateToNotificationBox) { + Icon( + painter = painterResource(id = DmsIcon.Bell), + contentDescription = stringResource(id = R.string.notification_box), + tint = DmsTheme.colorScheme.icon, + ) + } + }, ) }, ) { padValues -> From c53beb5732ddb738c082de4970bc6132e36ffe82 Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 6 Aug 2024 11:20:51 +0900 Subject: [PATCH 092/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20navigation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/team/aliens/dms/android/feature/main/Main.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/feature/src/main/java/team/aliens/dms/android/feature/main/Main.kt b/feature/src/main/java/team/aliens/dms/android/feature/main/Main.kt index 558ea6199..fa20b2a52 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/main/Main.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/main/Main.kt @@ -99,6 +99,7 @@ internal fun Main( HomeScreen( onChangeBottomAppBarVisibility = onChangeBottomAppBarVisibility, onNavigateToAnnouncementList = { navController.navigateTo(MainSections.ANNOUNCEMENT_LIST.route) }, + onNavigateToNotificationBox = mainNavigator::openNotificationBox, ) } From dd034be422f06630cb5653622a6611294c3cc559 Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 6 Aug 2024 11:22:09 +0900 Subject: [PATCH 093/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20model=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/notification/model/Notification.kt | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/data/src/main/java/team/aliens/dms/android/data/notification/model/Notification.kt b/data/src/main/java/team/aliens/dms/android/data/notification/model/Notification.kt index ed23b55b0..9d55a5f22 100644 --- a/data/src/main/java/team/aliens/dms/android/data/notification/model/Notification.kt +++ b/data/src/main/java/team/aliens/dms/android/data/notification/model/Notification.kt @@ -1,5 +1,8 @@ package team.aliens.dms.android.data.notification.model +import org.threeten.bp.LocalDateTime +import team.aliens.dms.android.network.notification.model.FetchNotificationsResponse +import team.aliens.dms.android.shared.date.toLocalDateTime import java.util.UUID data class Notification( @@ -8,6 +11,19 @@ data class Notification( val linkId: UUID, val title: String, val content: String, - val createdAt: String, - val read: Boolean, -) \ No newline at end of file + val createdAt: LocalDateTime, + val isRead: Boolean, +) + +fun FetchNotificationsResponse.toModel(): List = + this.notifications.map(FetchNotificationsResponse.NotificationResponse::toModel) + +private fun FetchNotificationsResponse.NotificationResponse.toModel(): Notification = Notification( + id = this.id, + topic = NotificationTopic.valueOf(this.topic), + linkId = this.linkId, + title = this.title, + content = this.content, + createdAt = this.createdAt.toLocalDateTime(), + isRead = this.isRead, +) From df073170483b12edcd1767ac083cc56cdd56cd8a Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 6 Aug 2024 11:22:33 +0900 Subject: [PATCH 094/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20datasource=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/repository/NotificationRepositoryImpl.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepositoryImpl.kt b/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepositoryImpl.kt index 742c477bb..ada3b0bed 100644 --- a/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepositoryImpl.kt +++ b/data/src/main/java/team/aliens/dms/android/data/notification/repository/NotificationRepositoryImpl.kt @@ -56,9 +56,8 @@ internal class NotificationRepositoryImpl @Inject constructor( networkNotificationDataSource.fetchNotificationTopicStatus(deviceToken = deviceToken) .toModel() - override suspend fun fetchNotifications(): List { - TODO("Not yet implemented") - } + override suspend fun fetchNotifications(): List = + networkNotificationDataSource.fetchNotifications().toModel() override suspend fun saveDeviceToken(deviceToken: String) { deviceDataStoreDataSource.storeDeviceToken(deviceToken) From c8b98fb6922f5ea9aa1e743b053ef4b4d943746d Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 6 Aug 2024 11:23:03 +0900 Subject: [PATCH 095/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20NotificationB?= =?UTF-8?q?oxScreen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/box/NotificationBoxScreen.kt | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 feature/src/main/java/team/aliens/dms/android/feature/notification/box/NotificationBoxScreen.kt diff --git a/feature/src/main/java/team/aliens/dms/android/feature/notification/box/NotificationBoxScreen.kt b/feature/src/main/java/team/aliens/dms/android/feature/notification/box/NotificationBoxScreen.kt new file mode 100644 index 000000000..2da30357c --- /dev/null +++ b/feature/src/main/java/team/aliens/dms/android/feature/notification/box/NotificationBoxScreen.kt @@ -0,0 +1,172 @@ +package team.aliens.dms.android.feature.notification.box + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.ramcosta.composedestinations.annotation.Destination +import org.threeten.bp.LocalDateTime +import team.aliens.dms.android.core.designsystem.DmsIcon +import team.aliens.dms.android.core.designsystem.DmsTheme +import team.aliens.dms.android.core.designsystem.DmsTopAppBar +import team.aliens.dms.android.core.designsystem.Scaffold +import team.aliens.dms.android.core.designsystem.shadow +import team.aliens.dms.android.core.ui.PaddingDefaults +import team.aliens.dms.android.core.ui.horizontalPadding +import team.aliens.dms.android.core.ui.topPadding +import team.aliens.dms.android.data.notification.model.Notification +import team.aliens.dms.android.feature.R +import team.aliens.dms.android.feature.notification.navigation.NotificationBoxNavigator +import java.util.UUID + +@OptIn(ExperimentalMaterial3Api::class) +@Destination +@Composable +internal fun NotificationBoxScreen( + modifier: Modifier = Modifier, + navigator: NotificationBoxNavigator, +) { + val viewModel: NotificationBoxViewModel = hiltViewModel() + val uiState by viewModel.stateFlow.collectAsStateWithLifecycle() + + Scaffold( + modifier = modifier, + topBar = { + DmsTopAppBar( + title = { Text(text = stringResource(id = R.string.notification_box)) }, + navigationIcon = { + IconButton(navigator::navigateUp) { + Icon( + painter = painterResource(id = DmsIcon.Back), + contentDescription = stringResource(id = R.string.top_bar_back_button), + ) + } + }, + ) + }, + ) { paddingValues -> + val scrollState = rememberScrollState() + + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(scrollState) + .background(DmsTheme.colorScheme.surface) + .padding(paddingValues) + .topPadding(), + verticalArrangement = Arrangement.spacedBy(PaddingDefaults.Medium), + ) { + NotificationListLayout( + isRead = false, + notifications = uiState.notifications.filter { !it.isRead }, + onNavigateToNoticeDetails = navigator::openNoticeDetails + ) + NotificationListLayout( + isRead = true, + notifications = uiState.notifications.filter { it.isRead }, + onNavigateToNoticeDetails = navigator::openNoticeDetails, + ) + } + } +} + +@Composable +private fun NotificationListLayout( + modifier: Modifier = Modifier, + isRead: Boolean, + notifications: List, + onNavigateToNoticeDetails: (noticeId: UUID) -> Unit, +) { + + Column( + modifier = modifier + .fillMaxWidth() + .horizontalPadding(), + verticalArrangement = Arrangement.spacedBy(PaddingDefaults.Medium), + ) { + Text( + text = stringResource(id = if (isRead) R.string.notification_box_read else R.string.notification_box_not_read), + style = DmsTheme.typography.caption, + color = DmsTheme.colorScheme.onSurfaceVariant, + ) + notifications.forEach { notification -> + NotificationCard( + modifier = Modifier.shadow(), + isRead = isRead, + notification = notification, + onNavigateToNoticeDetails = onNavigateToNoticeDetails, + ) + } + } +} + +@Composable +private fun NotificationCard( + modifier: Modifier = Modifier, + isRead: Boolean, + notification: Notification, + onNavigateToNoticeDetails: (noticeId: UUID) -> Unit, +) { + Row( + modifier = modifier + .fillMaxWidth() + .background(DmsTheme.colorScheme.surface) + .clickable { onNavigateToNoticeDetails(notification.linkId) } + .padding( + horizontal = PaddingDefaults.Medium, + vertical = PaddingDefaults.Small, + ), + ) { + Icon( + painter = painterResource(id = DmsIcon.BlueNotice), + contentDescription = stringResource(id = R.string.notice), + tint = if (isRead) { + DmsTheme.colorScheme.onBackground + } else { + DmsTheme.colorScheme.primary + } + ) + Spacer(modifier = Modifier.width(PaddingDefaults.Small)) + Column { + Text( + text = notification.title, + style = DmsTheme.typography.body3, + ) + Text( + text = notification.content, + style = DmsTheme.typography.caption, + ) + } + Spacer(modifier = Modifier.weight(1f)) + Text( + text = notification.createdAt.text, + style = DmsTheme.typography.caption, + ) + } +} + +private val LocalDateTime.text: String + @Composable inline get() = stringResource( + id = R.string.notification_box_format_time, + this.monthValue, + this.dayOfMonth, + ) From 0f481f8a0b00015875b48c5823f35c5f9e8ac93a Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 6 Aug 2024 20:25:05 +0900 Subject: [PATCH 096/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20response=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../network/notification/model/FetchNotificationsResponse.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/src/main/java/team/aliens/dms/android/network/notification/model/FetchNotificationsResponse.kt b/network/src/main/java/team/aliens/dms/android/network/notification/model/FetchNotificationsResponse.kt index 414f1a8fd..0e3aed216 100644 --- a/network/src/main/java/team/aliens/dms/android/network/notification/model/FetchNotificationsResponse.kt +++ b/network/src/main/java/team/aliens/dms/android/network/notification/model/FetchNotificationsResponse.kt @@ -13,7 +13,7 @@ data class FetchNotificationsResponse( @SerializedName("link_identifier") val linkId: UUID, @SerializedName("title") val title: String, @SerializedName("content") val content: String, - @SerializedName("created_at") val createdAt: LocalDateTime, + @SerializedName("created_at") val createdAt: String, @SerializedName("is_read") val isRead: Boolean, ) } From 529fb7408053f17e39655b80ceb4bffdab58bb6e Mon Sep 17 00:00:00 2001 From: uiel Date: Wed, 7 Aug 2024 22:44:39 +0900 Subject: [PATCH 097/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EB=AC=B8?= =?UTF-8?q?=EC=9E=90=EC=97=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/src/main/res/values/strings.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/feature/src/main/res/values/strings.xml b/feature/src/main/res/values/strings.xml index 0bc93375b..03a24eb6f 100644 --- a/feature/src/main/res/values/strings.xml +++ b/feature/src/main/res/values/strings.xml @@ -323,6 +323,15 @@ 알림 아이콘 현재 기기 설정에서 DMS 알림이 꺼져 있습니다.\n알림을 받으려면 기기 설정에서\nDMS 알림을 활성화해주세요. + 알림함 + + 읽음 + 읽지 않음 + + %d월 %d일 + + 알림을 가져오지 못했어요 + 학년 번호 From bd315571ad1ed7c02a0af575e837b3682efe042a Mon Sep 17 00:00:00 2001 From: uiel Date: Thu, 8 Aug 2024 23:47:02 +0900 Subject: [PATCH 098/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20import=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aliens/dms/android/app/service/DmsMessagingService.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/java/team/aliens/dms/android/app/service/DmsMessagingService.kt b/app/src/main/java/team/aliens/dms/android/app/service/DmsMessagingService.kt index 14b0c91fb..c725efbdd 100644 --- a/app/src/main/java/team/aliens/dms/android/app/service/DmsMessagingService.kt +++ b/app/src/main/java/team/aliens/dms/android/app/service/DmsMessagingService.kt @@ -1,7 +1,5 @@ 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 @@ -9,7 +7,7 @@ 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 team.aliens.dms.android.app.NotificationManager import javax.inject.Inject @AndroidEntryPoint From f4c9b6a90eeba8c56aebffe8211e6b6c49eb5486 Mon Sep 17 00:00:00 2001 From: uiel Date: Fri, 9 Aug 2024 22:43:44 +0900 Subject: [PATCH 099/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/res/drawable-hdpi/ic_notification.png | Bin 0 -> 537 bytes .../main/res/drawable-mdpi/ic_notification.png | Bin 0 -> 355 bytes .../main/res/drawable-xhdpi/ic_notification.png | Bin 0 -> 828 bytes .../res/drawable-xxhdpi/ic_notification.png | Bin 0 -> 1367 bytes .../res/drawable-xxxhdpi/ic_notification.png | Bin 0 -> 1866 bytes .../src/main/res/drawable/ic_logo.png | Bin 0 -> 49127 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 core/design-system/src/main/res/drawable-hdpi/ic_notification.png create mode 100644 core/design-system/src/main/res/drawable-mdpi/ic_notification.png create mode 100644 core/design-system/src/main/res/drawable-xhdpi/ic_notification.png create mode 100644 core/design-system/src/main/res/drawable-xxhdpi/ic_notification.png create mode 100644 core/design-system/src/main/res/drawable-xxxhdpi/ic_notification.png create mode 100644 core/design-system/src/main/res/drawable/ic_logo.png diff --git a/core/design-system/src/main/res/drawable-hdpi/ic_notification.png b/core/design-system/src/main/res/drawable-hdpi/ic_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..4c0063094685a9a8bb7aeddfe58d6919764731f4 GIT binary patch literal 537 zcmV+!0_OdRP)L9UORDGt5xbX0q8TXdz|!h$48L`&@Ubo4~d68W@Y~XbH-IPAg+pyf1$%v9tc`4u8I>o za-gCCaWdanfkPDqC|&9eJ940+0WrXx9XU`HkWUqmPrVKF>mJBa`G+e9ngBkaZ}_3Q z3iJg(&;z;%^a0IMkuJrv&^v$FjuEW`J;M*QFP?@z8#@ZbQthG`=v}-l9*1uBIttVf zuc0{*cbUu%%N0IA|fs_U*+i*F03PPJp1*EFGtZXSZnge|02Jhqv`L3_4XWvTVdw7DmhqU}kUR1i;DMZIto4Y>o2%nX@{VSm zlaBR?3a)dRr%J2rTA!fcy4Qr1&MTG32vGNv`us+f$OzEh-`3|hszgSBj@^BIexpic z1n6x42gId;_{D&YEqG=9XuASFYymDb{i;Ab;ElZ6BDExEq;0)|LOftb*0xAplOZ{< zUO^!o@I)@i%GBp%M0!Yl58(df1^I$AoWkv~?)(?>NuJ4-H$OsZ_mT#aV{!_&w_3hB zRB+$d#Q<)#>E@Muh%P38VGwyf72oZ5xu(DxeVJCh9I~zoZ%MY+;BmM;uLadPpEAt;F60wsYE}!?A zs_O1jbLKeRr>Zk2={I>7Q(gUj-}`k{ovEsci4GkWFU)Mou#Der9Z^D)o=a;mywk23 z0(ypomy0XJmEx)zLO)=(U zghWoqzo18HjUf1RHG?s!)Q=2!(0Yi580u6J?c0I<*AEpD)s=R$~BGXw9%*Ky+jJQR1BJJ@3Ll;&j4 znj5$-zKRfV^#MXw-@pg)u=p~5umB#3yDZ14970}j*x!LK<_^B5 z#TUJg`r@N_(0Z$%X7~WBXJA%5FYXa{iqCr=&ja)1gt%Q?Z*KCYt3E)?i(=qrh#Y}6 z{Hb`-T;f!7i}J(uc}&u;NZwLbKfnt!Hkrt*(swG%fk^daHUpEmjzcVaM(n-o4s zH8Cm&tCw?gRrKt4=@cQlbkX7fkf*70000(m literal 0 HcmV?d00001 diff --git a/core/design-system/src/main/res/drawable-xxhdpi/ic_notification.png b/core/design-system/src/main/res/drawable-xxhdpi/ic_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..8d3e1011d596abc457876d2d98edc5767a2ca1aa GIT binary patch literal 1367 zcmb7^`B%~j6vaWUSTr=rrCcyhqob7znN0(V+azi(jm~5lL6W9Tic+T0D0mE9GEH$y z8#ks+3(FEri3C?LHOH)uCb`1csPL^xNUfniVeUEabAPz^{r1kw4GTGF1ltA!fj~xt z0KdaJ5BN5ajXFjywj2b3HrNpSyd%o$4%9f!QUAx(7G@vsR& zpQCIbgj!Mn_ek_dsxL#t|M(%16M|%C+kJ@ z0qZJS+~?F+Uu#5-cULEm31bfWev>5D_!^u=dGn>!H>wB_Guzh1(nE2VqbVJ)1s}Xd z#_OK{+Z^aT#UM1Wr^(Uv9T9D^w5U#59O& zYryqCKsebRUb@EZB;;6bVGSIyF^sDSON{j7twprm#o9}zlIE<7&q1Jz`@!TcUl#v1Gs z9x&6h(|_WX4rfxqhN7&1PXWu!#U-H`HS9`hifB0CttQ!8v46}g*DhC`+H z9KMs>ak6StO~3*!z=q_|%XSim5~=YSF`pMJ)k1(spoLD-dxT$;QK!Wg z%2jfy`>1mujD;DRf$>5YUAGT9)^N^Y%Ri+oK~07`PFzr3o8|z`TyY|2?_Q$p!ga9R zu`n<};zie%o>^<_W!}zRfhr#XmINWot}{wBrb%d($7+JKOzy3VSgXvmZuXdlRw&7a z_LYlVzVO8tq~;ucKi6R%K6pTq_3GELm8Fdm06y2^X6@2jHZ}S;4x~F)r-8JiMJ)>y zU_D&>CzoC|Bdt2+R;}EP?50SI7)Z^-V7??QIy~e4C4yGKB?&n6XW25ft2mkTY(P_j zLk4*$u58isH-_-4(3rgCyT)qE||%D zmpt+nT2-itf!L|8S!l?rul}O4UT(4Nr8br9d-D^+#&DHpWiy?v?{c!Mg6%iu8SS74 zc#LYF)gV+4ZG@-?0g@+7}iPU)$6s? Wz8ia}Ax)@zm>`0Gh~Ir5Qr3U7?U_9Q literal 0 HcmV?d00001 diff --git a/core/design-system/src/main/res/drawable-xxxhdpi/ic_notification.png b/core/design-system/src/main/res/drawable-xxxhdpi/ic_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..d421428fbf9b3901b083d67c46ce4493ee4b16bb GIT binary patch literal 1866 zcmV-Q2etT#P)UgSjai2p4>KO#F=>8`z_-Ope%Da_sD}R&z@sIK{DQ5zD(1yl(&jJ^1%%k3oKUKM1`HjpM1aP^O zl0?a)9Qj#LUjQ!H=;r&Vr*qWwZOU(D7$ATvmB&k&6DWnt&Vt;}!2+=+8pe`?`4=eP zQSO!DfB>#io+P8QfU~r|01#@-=2LQRP<|)F0|8v4JVnY`;H#psGL-u|I3UuP%MRw> zDf5|NrvU+6CuL4LK}v~4b3Fkd(72NMdt|uq05&U6l!^n0s~xZTH!6RZVS@m+NO=uH z08GU71b{fB*L{Rz{`oSTmCkCI6o9XUstF*L9##-^;(e(D^#*`2V=O(KzO3_W0Rdbu zB>lkd+`AF2)@mYpmcss%6)AjU*3QUE>&4FA*7h%r%! zL*ybXL-QFx09Bj;i*hk82T)B3TGRmA8PFp{p9ukFv|}xQL_$UkMa&FH+?!!$~@2| zSb9?X8U1nfGwkvM5M|;#fLUA(eMyQz=P1usK0LmApYVf}OAjnPCyoALu6`UQY?cAM zC1vCpB2l5WOPh&bo|F45B=+JcQduhpr1OXK$5}`P6-u zos&w>a7RBdaWMe@p744D_)+ zIBj8&6T|#}$q*9HVMNo8x9RQk_t?(!v2RsB!^$8TCoj$R8 zjnBr@4uR4p{Vebhe6+^;9qFdH>2btIbDTW( zvy9_-&Pwg`c%}Be?fT7kgihoTe8l>wjr8q#v`6D(yQ{UI+m43kP#4Geh|r-pV%=1) z;_DtA2VhQoU-lJ+K8X6iJ8X6iJ8a6oo1*x*GJzMfyc>n+a07*qoM6N<$ Eg5Q{FFaQ7m literal 0 HcmV?d00001 diff --git a/core/design-system/src/main/res/drawable/ic_logo.png b/core/design-system/src/main/res/drawable/ic_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..290022b564670885b14c413070ab17e5f5548d0d GIT binary patch literal 49127 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 gGu>ueQg1yo#c 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 Date: Sat, 10 Aug 2024 23:11:12 +0900 Subject: [PATCH 100/111] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20::=20navigator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/navigation/NotificationBoxNavigator.kt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 feature/src/main/java/team/aliens/dms/android/feature/notification/navigation/NotificationBoxNavigator.kt diff --git a/feature/src/main/java/team/aliens/dms/android/feature/notification/navigation/NotificationBoxNavigator.kt b/feature/src/main/java/team/aliens/dms/android/feature/notification/navigation/NotificationBoxNavigator.kt new file mode 100644 index 000000000..657b600e3 --- /dev/null +++ b/feature/src/main/java/team/aliens/dms/android/feature/notification/navigation/NotificationBoxNavigator.kt @@ -0,0 +1,9 @@ +package team.aliens.dms.android.feature.notification.navigation + +import java.util.UUID + +interface NotificationBoxNavigator { + fun navigateUp() + + fun openNoticeDetails(noticeId: UUID) +} From e661f3818fd3fe5b45d14ba3ae39cb6fdd460a63 Mon Sep 17 00:00:00 2001 From: uiel Date: Sun, 11 Aug 2024 15:38:46 +0900 Subject: [PATCH 101/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=ED=81=B4=EB=A6=AD=EC=8B=9C=20=EC=95=B1=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dms/android/app}/NotificationManager.kt | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) rename {core/notification/src/main/java/team/aliens/dms/android/core/notification => app/src/main/java/team/aliens/dms/android/app}/NotificationManager.kt (78%) diff --git a/core/notification/src/main/java/team/aliens/dms/android/core/notification/NotificationManager.kt b/app/src/main/java/team/aliens/dms/android/app/NotificationManager.kt similarity index 78% rename from core/notification/src/main/java/team/aliens/dms/android/core/notification/NotificationManager.kt rename to app/src/main/java/team/aliens/dms/android/app/NotificationManager.kt index 2305bc115..b4ac4e02d 100644 --- a/core/notification/src/main/java/team/aliens/dms/android/core/notification/NotificationManager.kt +++ b/app/src/main/java/team/aliens/dms/android/app/NotificationManager.kt @@ -1,14 +1,16 @@ -package team.aliens.dms.android.core.notification +package team.aliens.dms.android.app import android.annotation.SuppressLint import android.app.NotificationChannel import android.app.NotificationManager +import android.app.PendingIntent import android.content.Context +import android.content.Intent import android.os.Build -import androidx.annotation.RequiresApi import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import team.aliens.dms.android.core.designsystem.DmsIcon +import team.aliens.dms.android.core.notification.notificationPermissionGranted private object Notifications { const val NOTIFICATION_ID = 0 @@ -29,10 +31,18 @@ class NotificationManager( NotificationManagerCompat.from(context) } + private val messageId = System.currentTimeMillis().toInt() + private val intent = Intent(context, MainActivity::class.java) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + private val pendingIntent = PendingIntent.getActivity( + context, messageId, intent, PendingIntent.FLAG_IMMUTABLE + ) + private val notificationBuilder: NotificationCompat.Builder by lazy { NotificationCompat.Builder(context, Notifications.NOTIFICATION_CHANNEL_ID) - .setSmallIcon(DmsIcon.LogoLight) + .setSmallIcon(DmsIcon.Notification) .setPriority(NotificationCompat.PRIORITY_HIGH) + .setContentIntent(pendingIntent) } fun setNotificationContent( From 57d59a0514b389a6b715cb9cef4720df9dd78ee9 Mon Sep 17 00:00:00 2001 From: uiel Date: Sun, 11 Aug 2024 15:38:58 +0900 Subject: [PATCH 102/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/team/aliens/dms/android/core/designsystem/DmsIcon.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/design-system/src/main/java/team/aliens/dms/android/core/designsystem/DmsIcon.kt b/core/design-system/src/main/java/team/aliens/dms/android/core/designsystem/DmsIcon.kt index 9e37ddee8..9bbbb1690 100644 --- a/core/design-system/src/main/java/team/aliens/dms/android/core/designsystem/DmsIcon.kt +++ b/core/design-system/src/main/java/team/aliens/dms/android/core/designsystem/DmsIcon.kt @@ -22,6 +22,8 @@ object DmsIcon { val Information = R.drawable.ic_information val LogoDark = R.drawable.ic_logo_dark val LogoLight = R.drawable.ic_logo_light + val Logo = R.drawable.ic_logo + val Notification = R.drawable.ic_notification val Lunch = R.drawable.ic_lunch val MyPage = R.drawable.ic_person val Notice = R.drawable.ic_notice From 9bd2d99453d5a8642590e0847a37fe022cd98af7 Mon Sep 17 00:00:00 2001 From: uiel Date: Sun, 11 Aug 2024 15:46:15 +0900 Subject: [PATCH 103/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=ED=81=B4=EB=A6=AD=20=EC=8B=9C=20=EC=9D=B4=EB=8F=99?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/box/NotificationBoxScreen.kt | 124 +++++++++++++----- 1 file changed, 92 insertions(+), 32 deletions(-) diff --git a/feature/src/main/java/team/aliens/dms/android/feature/notification/box/NotificationBoxScreen.kt b/feature/src/main/java/team/aliens/dms/android/feature/notification/box/NotificationBoxScreen.kt index 2da30357c..82c7a6a71 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/notification/box/NotificationBoxScreen.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/notification/box/NotificationBoxScreen.kt @@ -3,6 +3,7 @@ package team.aliens.dms.android.feature.notification.box import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -18,7 +19,9 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel @@ -28,15 +31,17 @@ import org.threeten.bp.LocalDateTime import team.aliens.dms.android.core.designsystem.DmsIcon import team.aliens.dms.android.core.designsystem.DmsTheme import team.aliens.dms.android.core.designsystem.DmsTopAppBar +import team.aliens.dms.android.core.designsystem.LocalToast import team.aliens.dms.android.core.designsystem.Scaffold import team.aliens.dms.android.core.designsystem.shadow import team.aliens.dms.android.core.ui.PaddingDefaults +import team.aliens.dms.android.core.ui.collectInLaunchedEffectWithLifecycle import team.aliens.dms.android.core.ui.horizontalPadding import team.aliens.dms.android.core.ui.topPadding import team.aliens.dms.android.data.notification.model.Notification +import team.aliens.dms.android.data.notification.model.NotificationTopic import team.aliens.dms.android.feature.R import team.aliens.dms.android.feature.notification.navigation.NotificationBoxNavigator -import java.util.UUID @OptIn(ExperimentalMaterial3Api::class) @Destination @@ -47,6 +52,20 @@ internal fun NotificationBoxScreen( ) { val viewModel: NotificationBoxViewModel = hiltViewModel() val uiState by viewModel.stateFlow.collectAsStateWithLifecycle() + val toast = LocalToast.current + val context = LocalContext.current + + viewModel.sideEffectFlow.collectInLaunchedEffectWithLifecycle { sideEffect -> + when (sideEffect) { + NotificationBoxSideEffect.CurrentNotificationsNotFound -> toast.showErrorToast( + message = context.getString(R.string.notification_box_notifications_not_current) + ) + + is NotificationBoxSideEffect.MoveToDetail -> { + navigator.openNoticeDetails(noticeId = sideEffect.detailId) + } + } + } Scaffold( modifier = modifier, @@ -64,57 +83,95 @@ internal fun NotificationBoxScreen( ) }, ) { paddingValues -> - val scrollState = rememberScrollState() - Column( modifier = Modifier .fillMaxSize() - .verticalScroll(scrollState) .background(DmsTheme.colorScheme.surface) - .padding(paddingValues) - .topPadding(), - verticalArrangement = Arrangement.spacedBy(PaddingDefaults.Medium), + .padding(paddingValues), ) { - NotificationListLayout( - isRead = false, - notifications = uiState.notifications.filter { !it.isRead }, - onNavigateToNoticeDetails = navigator::openNoticeDetails - ) - NotificationListLayout( - isRead = true, - notifications = uiState.notifications.filter { it.isRead }, - onNavigateToNoticeDetails = navigator::openNoticeDetails, - ) + if (uiState.notifications.isEmpty()) { + Box( + modifier = Modifier + .fillMaxSize(), + contentAlignment = Alignment.Center, + ) { + Text( + text = stringResource(id = R.string.notification_box_not_exist), + style = DmsTheme.typography.body2, + color = DmsTheme.colorScheme.onSurfaceVariant, + ) + } + } else { + NotificationList( + viewModel = viewModel, + notifications = uiState.notifications, + ) + } } } } +@Composable +private fun NotificationList( + modifier: Modifier = Modifier, + viewModel: NotificationBoxViewModel, + notifications: List, +) { + val scrollState = rememberScrollState() + + Column( + modifier = modifier + .fillMaxSize() + .verticalScroll(scrollState) + .topPadding(), + verticalArrangement = Arrangement.spacedBy(PaddingDefaults.Medium), + ) { + NotificationListLayout( + viewModel = viewModel, + isRead = false, + notifications = notifications.filter { !it.isRead }, + ) + NotificationListLayout( + viewModel = viewModel, + isRead = true, + notifications = notifications.filter { it.isRead }, + ) + } +} + @Composable private fun NotificationListLayout( modifier: Modifier = Modifier, + viewModel: NotificationBoxViewModel, isRead: Boolean, notifications: List, - onNavigateToNoticeDetails: (noticeId: UUID) -> Unit, ) { - Column( modifier = modifier .fillMaxWidth() .horizontalPadding(), verticalArrangement = Arrangement.spacedBy(PaddingDefaults.Medium), ) { - Text( - text = stringResource(id = if (isRead) R.string.notification_box_read else R.string.notification_box_not_read), - style = DmsTheme.typography.caption, - color = DmsTheme.colorScheme.onSurfaceVariant, - ) - notifications.forEach { notification -> - NotificationCard( - modifier = Modifier.shadow(), - isRead = isRead, - notification = notification, - onNavigateToNoticeDetails = onNavigateToNoticeDetails, + if (notifications.isNotEmpty()) { + Text( + text = stringResource(id = if (isRead) R.string.notification_box_read else R.string.notification_box_not_read), + style = DmsTheme.typography.caption, + color = DmsTheme.colorScheme.onSurfaceVariant, ) + notifications.forEach { notification -> + NotificationCard( + modifier = Modifier.shadow(), + isRead = isRead, + notification = notification, + onNavigateToNoticeDetails = { + viewModel.postIntent( + NotificationBoxIntent.DetailNotification( + notification + ) + ) + }, + ) + } } } } @@ -124,13 +181,16 @@ private fun NotificationCard( modifier: Modifier = Modifier, isRead: Boolean, notification: Notification, - onNavigateToNoticeDetails: (noticeId: UUID) -> Unit, + onNavigateToNoticeDetails: () -> Unit, ) { Row( modifier = modifier .fillMaxWidth() .background(DmsTheme.colorScheme.surface) - .clickable { onNavigateToNoticeDetails(notification.linkId) } + .clickable( + enabled = notification.topic == NotificationTopic.NOTICE, + onClick = onNavigateToNoticeDetails, + ) .padding( horizontal = PaddingDefaults.Medium, vertical = PaddingDefaults.Small, From 83231bb8fcf02523e5ad1e4a22868dfbc88e832c Mon Sep 17 00:00:00 2001 From: uiel Date: Sun, 11 Aug 2024 15:46:25 +0900 Subject: [PATCH 104/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EB=AC=B8?= =?UTF-8?q?=EC=9E=90=EC=97=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/src/main/res/values/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/feature/src/main/res/values/strings.xml b/feature/src/main/res/values/strings.xml index 03a24eb6f..9922573e1 100644 --- a/feature/src/main/res/values/strings.xml +++ b/feature/src/main/res/values/strings.xml @@ -327,6 +327,8 @@ 읽음 읽지 않음 + + 알림이 존재하지 않습니다. %d월 %d일 From 517c5efe2f35cb97d9dec522e342bfec7cbfac6a Mon Sep 17 00:00:00 2001 From: uiel Date: Sun, 11 Aug 2024 15:48:10 +0900 Subject: [PATCH 105/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20NotificationB?= =?UTF-8?q?oxViewModel=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../box/NotificationBoxViewModel.kt | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 feature/src/main/java/team/aliens/dms/android/feature/notification/box/NotificationBoxViewModel.kt diff --git a/feature/src/main/java/team/aliens/dms/android/feature/notification/box/NotificationBoxViewModel.kt b/feature/src/main/java/team/aliens/dms/android/feature/notification/box/NotificationBoxViewModel.kt new file mode 100644 index 000000000..8b27db822 --- /dev/null +++ b/feature/src/main/java/team/aliens/dms/android/feature/notification/box/NotificationBoxViewModel.kt @@ -0,0 +1,79 @@ +package team.aliens.dms.android.feature.notification.box + +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import team.aliens.dms.android.core.ui.mvi.BaseMviViewModel +import team.aliens.dms.android.core.ui.mvi.Intent +import team.aliens.dms.android.core.ui.mvi.SideEffect +import team.aliens.dms.android.core.ui.mvi.UiState +import team.aliens.dms.android.data.notification.model.Notification +import team.aliens.dms.android.data.notification.model.NotificationTopic +import team.aliens.dms.android.data.notification.repository.NotificationRepository +import java.util.UUID +import javax.inject.Inject + +@HiltViewModel +internal class NotificationBoxViewModel @Inject constructor( + private val notificationRepository: NotificationRepository, +) : BaseMviViewModel( + initialState = NotificationBoxUiState.initial() +) { + + init { + fetchNotifications() + } + + override fun processIntent(intent: NotificationBoxIntent) { + when(intent) { + is NotificationBoxIntent.DetailNotification -> detailNotification(intent.notification) + } + } + + private fun fetchNotifications() { + viewModelScope.launch(Dispatchers.IO) { + runCatching { + notificationRepository.fetchNotifications() + }.onSuccess { notifications -> + reduce(newState = stateFlow.value.copy(notifications = notifications)) + }.onFailure { + postSideEffect(NotificationBoxSideEffect.CurrentNotificationsNotFound) + } + } + } + + private fun detailNotification(notification: Notification) { + when(notification.topic) { + NotificationTopic.NOTICE -> { + postSideEffect(NotificationBoxSideEffect.MoveToDetail(notification.linkId)) + } + else -> { + // 처리 해야 할 작업 없음 + } + } + } +} + +internal data class NotificationBoxUiState( + val notifications: List, + val topic: NotificationTopic, +) : UiState() { + companion object { + fun initial(): NotificationBoxUiState { + return NotificationBoxUiState( + notifications = listOf(), + topic = NotificationTopic.POINT, + ) + } + } +} + +internal sealed class NotificationBoxIntent : Intent() { + class DetailNotification(val notification: Notification): NotificationBoxIntent() +} + +internal sealed class NotificationBoxSideEffect : SideEffect() { + data object CurrentNotificationsNotFound: NotificationBoxSideEffect() + data class MoveToDetail(val detailId: UUID): NotificationBoxSideEffect() +} From 53c45d1725bce881d6cac41c0c0c447fdd23e2f6 Mon Sep 17 00:00:00 2001 From: uiel Date: Sun, 11 Aug 2024 22:36:50 +0900 Subject: [PATCH 106/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20add=20firebas?= =?UTF-8?q?e=20crashlytics?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 2 ++ build.gradle.kts | 1 + gradle/libs.versions.toml | 3 +++ 3 files changed, 6 insertions(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d61698479..32547d268 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -7,6 +7,7 @@ plugins { alias(libs.plugins.googleServices) alias(libs.plugins.hilt) alias(libs.plugins.ksp) + alias(libs.plugins.firebase.crashlytics) } android { @@ -130,6 +131,7 @@ dependencies { implementation(platform(libs.firebase.bom)) implementation(libs.firebase.messaging) implementation(libs.firebase.analytics) + implementation(libs.firebase.crashlytics) implementation(libs.androidx.work.runtime.ktx) diff --git a/build.gradle.kts b/build.gradle.kts index b33aa9475..68866b9bf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -20,4 +20,5 @@ plugins { alias(libs.plugins.googleServices) apply false alias(libs.plugins.ksp) apply false alias(libs.plugins.jetbrainsKotlinJvm) apply false + alias(libs.plugins.firebase.crashlytics) apply false } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7741de082..c32c8abd3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -46,6 +46,7 @@ java = "17" junitKtx = "1.1.5" workRuntime = "2.9.0" serialization = "2.0.0" +firebase-crashlytics = "2.9.9" [libraries] accompanist-adaptive = { module = "com.google.accompanist:accompanist-adaptive", version.ref = "accompanist" } @@ -78,6 +79,7 @@ composeDestinations = { group = "io.github.raamcosta.compose-destinations", name composeDestinations-ksp = { group = "io.github.raamcosta.compose-destinations", name = "ksp", version.ref = "composeDestination" } firebase-analytics = { module = "com.google.firebase:firebase-analytics", version.ref = "firebaseAnalytics" } firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" } +firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics" } firebase-messaging = { module = "com.google.firebase:firebase-messaging", version.ref = "firebaseMessaging" } google-services = { module = "com.google.gms:google-services", version.ref = "googleServices" } javax-inject = { module = "javax.inject:javax.inject", version.ref = "javaxInject" } @@ -113,3 +115,4 @@ ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } googleServices = { id = "com.google.gms.google-services", version.ref = "google" } jetbrainsKotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlinJvm" } serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "serialization" } +firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebase-crashlytics" } From 66025471baaa02ccd732a8f988d140c79ee6201a Mon Sep 17 00:00:00 2001 From: uiel Date: Mon, 12 Aug 2024 16:25:01 +0900 Subject: [PATCH 107/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aliens/dms/android/core/designsystem/DmsIcon.kt | 2 +- .../src/main/res/drawable/ic_person.xml | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/core/design-system/src/main/java/team/aliens/dms/android/core/designsystem/DmsIcon.kt b/core/design-system/src/main/java/team/aliens/dms/android/core/designsystem/DmsIcon.kt index 9bbbb1690..0d2cacdf3 100644 --- a/core/design-system/src/main/java/team/aliens/dms/android/core/designsystem/DmsIcon.kt +++ b/core/design-system/src/main/java/team/aliens/dms/android/core/designsystem/DmsIcon.kt @@ -25,7 +25,7 @@ object DmsIcon { val Logo = R.drawable.ic_logo val Notification = R.drawable.ic_notification val Lunch = R.drawable.ic_lunch - val MyPage = R.drawable.ic_person + val Person = R.drawable.ic_person val Notice = R.drawable.ic_notice val Palette = R.drawable.ic_palette val PasswordInvisible = R.drawable.ic_password_invisible diff --git a/core/design-system/src/main/res/drawable/ic_person.xml b/core/design-system/src/main/res/drawable/ic_person.xml index edca5fdb9..32b3100a3 100644 --- a/core/design-system/src/main/res/drawable/ic_person.xml +++ b/core/design-system/src/main/res/drawable/ic_person.xml @@ -1,5 +1,9 @@ - - - - + + From 33c71f6277a465db698daaad6e0bbcb53c361227 Mon Sep 17 00:00:00 2001 From: uiel Date: Mon, 12 Aug 2024 16:25:28 +0900 Subject: [PATCH 108/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/aliens/dms/android/feature/main/Main.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/feature/src/main/java/team/aliens/dms/android/feature/main/Main.kt b/feature/src/main/java/team/aliens/dms/android/feature/main/Main.kt index fa20b2a52..16ead0a7a 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/main/Main.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/main/Main.kt @@ -37,9 +37,9 @@ import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.RootNavGraph -import team.aliens.dms.android.core.designsystem.Scaffold +import team.aliens.dms.android.core.designsystem.DmsIcon import team.aliens.dms.android.core.designsystem.DmsTheme -import team.aliens.dms.android.core.designsystem.R +import team.aliens.dms.android.core.designsystem.Scaffold import team.aliens.dms.android.core.designsystem.slideInFromEnd import team.aliens.dms.android.core.designsystem.slideInFromStart import team.aliens.dms.android.core.designsystem.slideOutFromEnd @@ -243,22 +243,22 @@ private enum class MainSections( ) { HOME( route = "home", - iconRes = R.drawable.ic_home, + iconRes = DmsIcon.Home, labelRes = team.aliens.dms.android.feature.R.string.bottom_nav_home, ), APPLICATION( route = "application", - iconRes = R.drawable.ic_applicate, + iconRes = DmsIcon.Applicate, labelRes = team.aliens.dms.android.feature.R.string.bottom_nav_application, ), ANNOUNCEMENT_LIST( route = "announcement_list", - iconRes = R.drawable.ic_notice, + iconRes = DmsIcon.Notice, labelRes = team.aliens.dms.android.feature.R.string.bottom_nav_announcement_list, ), MY_PAGE( route = "my_page", - iconRes = R.drawable.ic_person, + iconRes = DmsIcon.Person, labelRes = team.aliens.dms.android.feature.R.string.bottom_nav_my_page, ), ; From b60008888a7e69954511e5de6b72329a222e57c6 Mon Sep 17 00:00:00 2001 From: uiel Date: Mon, 12 Aug 2024 23:18:01 +0900 Subject: [PATCH 109/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EA=B5=AC?= =?UTF-8?q?=EB=8F=85=20=EC=84=B1=EA=B3=B5=20=EC=B2=98=EB=A6=AC=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../settings/NotificationSettingsScreen.kt | 17 +++++------------ .../settings/NotificationSettingsViewModel.kt | 6 ------ 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsScreen.kt b/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsScreen.kt index a059205f1..2ba8da8ff 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsScreen.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsScreen.kt @@ -2,7 +2,6 @@ package team.aliens.dms.android.feature.notification.settings import androidx.annotation.StringRes import androidx.compose.foundation.background -import androidx.compose.foundation.gestures.scrollable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -17,7 +16,6 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -25,7 +23,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -41,7 +38,6 @@ import team.aliens.dms.android.core.designsystem.Scaffold import team.aliens.dms.android.core.designsystem.Switch import team.aliens.dms.android.core.notification.notificationPermissionGranted import team.aliens.dms.android.core.ui.PaddingDefaults -import team.aliens.dms.android.core.ui.bottomPadding import team.aliens.dms.android.core.ui.collectInLaunchedEffectWithLifecycle import team.aliens.dms.android.core.ui.horizontalPadding import team.aliens.dms.android.core.ui.topPadding @@ -63,19 +59,15 @@ internal fun NotificationSettingsScreen( val toast = LocalToast.current viewModel.sideEffectFlow.collectInLaunchedEffectWithLifecycle { sideEffect -> - when(sideEffect) { + when (sideEffect) { NotificationSettingsSideEffect.CurrentNotificationsStatusNotFound -> toast.showErrorToast( message = context.getString(R.string.notification_not_current) ) - NotificationSettingsSideEffect.SubscribeNotificationSuccess -> toast.showSuccessToast( - message = context.getString(R.string.notification_subscribe_success) - ) + NotificationSettingsSideEffect.SubscribeNotificationFailure -> toast.showErrorToast( message = context.getString(R.string.notification_subscribe_fail) ) - NotificationSettingsSideEffect.UnSubscribeNotificationSuccess -> toast.showSuccessToast( - message = context.getString(R.string.notification_unsubscribe_success) - ) + NotificationSettingsSideEffect.UnSubscribeNotificationFailure -> toast.showErrorToast( message = context.getString(R.string.notification_unsubscribe_fail) ) @@ -109,7 +101,7 @@ internal fun NotificationSettingsScreen( .topPadding(PaddingDefaults.Large), verticalArrangement = Arrangement.spacedBy(24.dp), ) { - if(!notificationPermissionGranted(context)) { + if (!notificationPermissionGranted(context)) { Notice() } Notifications( @@ -142,6 +134,7 @@ private fun Notice() { ) } } + @Composable private fun Notifications( status: List, diff --git a/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsViewModel.kt b/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsViewModel.kt index 316a4086f..86a71a91a 100644 --- a/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsViewModel.kt +++ b/feature/src/main/java/team/aliens/dms/android/feature/notification/settings/NotificationSettingsViewModel.kt @@ -77,8 +77,6 @@ internal class NotificationSettingsViewModel @Inject constructor( deviceToken = deviceToken, topic = topic, ) - }.onSuccess { - postSideEffect(NotificationSettingsSideEffect.SubscribeNotificationSuccess) }.onFailure { postSideEffect(NotificationSettingsSideEffect.SubscribeNotificationFailure) } @@ -95,8 +93,6 @@ internal class NotificationSettingsViewModel @Inject constructor( deviceToken = deviceToken, topic = topic, ) - }.onSuccess { - postSideEffect(NotificationSettingsSideEffect.UnSubscribeNotificationSuccess) }.onFailure { postSideEffect(NotificationSettingsSideEffect.UnSubscribeNotificationFailure) } @@ -125,8 +121,6 @@ internal sealed class NotificationSettingsIntent : Intent() { internal sealed class NotificationSettingsSideEffect : SideEffect() { data object CurrentNotificationsStatusNotFound : NotificationSettingsSideEffect() - data object SubscribeNotificationSuccess : NotificationSettingsSideEffect() data object SubscribeNotificationFailure : NotificationSettingsSideEffect() - data object UnSubscribeNotificationSuccess : NotificationSettingsSideEffect() data object UnSubscribeNotificationFailure : NotificationSettingsSideEffect() } From f5c62dcb85c969dfd5233e2667f1d174dd2b9781 Mon Sep 17 00:00:00 2001 From: uiel Date: Mon, 12 Aug 2024 23:36:21 +0900 Subject: [PATCH 110/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=ED=83=80?= =?UTF-8?q?=EC=BC=93=20API=20=EC=88=98=EC=A4=80=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c32c8abd3..03de15b83 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -41,7 +41,7 @@ hiltNavigation = "1.1.0" coil = "2.4.0" compileSdk = "34" minSdk = "23" -targetSdk = "33" +targetSdk = "34" java = "17" junitKtx = "1.1.5" workRuntime = "2.9.0" From 721d32d273c7ac252b9199433b00f8080e8aba4d Mon Sep 17 00:00:00 2001 From: uiel Date: Tue, 13 Aug 2024 10:59:38 +0900 Subject: [PATCH 111/111] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20::=20=EB=B2=84?= =?UTF-8?q?=EC=A0=84=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 32547d268..a5812d11b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -18,8 +18,8 @@ android { minSdk = libs.versions.minSdk.get().toInt() targetSdk = libs.versions.targetSdk.get().toInt() - versionCode = 16 - versionName = "1.3.7" + versionCode = 17 + versionName = "1.3.8" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" }