Skip to content
Open
Show file tree
Hide file tree
Changes from 79 commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
1a97d79
โ™ป๏ธ chore: ํด๋” ํŒŒํŠธ ์˜ค๋ฅ˜ ์ œ๊ฑฐ
Hongji03 May 7, 2026
bb8fbf3
:sparkles: ๋งํฌ ์ƒ์„ฑ ํ™”๋ฉด ๋ฆฌํŒฉํ† ๋ง
Hongji03 May 10, 2026
cae9a16
:sparkles: ์ปค์Šคํ…€ ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€ ์ƒ์„ฑ
Hongji03 May 10, 2026
ef003df
:sparkles: ๋งํฌ ์ƒ์„ธ ์กฐํšŒ ํ™”๋ฉด Top Bar ์ƒ์„ฑ
Hongji03 May 27, 2026
cda40a1
Merge remote-tracking branch 'origin/develop' into chore/#126-link
Hongji03 May 27, 2026
1917332
:sparkles: ๋งํฌ ์ƒ์„ธ ์กฐํšŒ ๊ธฐ๋ณธ ํ™”๋ฉด ๋ฆฌํŒฉํ† ๋ง ์™„๋ฃŒ
Hongji03 May 27, 2026
b160d51
:sparkles: ๋งํฌ ์ƒ์„ธ ์กฐํšŒ ํ™”๋ฉด์— ๋“œ๋กญ๋‹ค์šด ์ถ”๊ฐ€
Hongji03 May 28, 2026
0c1d6cc
:recycle: ์ €์žฅ๋œ ๋งํฌ ์กฐํšŒ ํ™”๋ฉด ๋ฐ ์ˆ˜์ • ํ™”๋ฉด ๋ฆฌํŒฉํ† ๋ง
Hongji03 Jun 19, 2026
522b105
:sparkles: ์ €์žฅ๋œ ๋งํฌ ์‚ญ์ œ ๋ชจ๋‹ฌ ๊ตฌํ˜„ ์™„๋ฃŒ
Hongji03 Jun 19, 2026
cd65a08
:recycle: AI ์š”์•ฝ ๋ชจ๋‹ฌ ๋ฆฌํŒฉํ† ๋ง
Hongji03 Jun 20, 2026
516e0eb
:sparkles: AI ํƒœ๊ทธ ๋ฏธ ๋งํฌ ์š”์•ฝ, AI ์š”์•ฝ Modal ์—ฐ๋™ ๊ตฌํ˜„ ์™„๋ฃŒ
Hongji03 Jun 20, 2026
ed304fb
:sparkles: LinkCardItem ๋””์ž์ธ ๋ชจ๋“ˆ์— ์ƒ์„ฑ ์™„๋ฃŒ
Hongji03 Jun 20, 2026
4b6f8d4
:sparkles: ๋งํฌ ์ƒ์„ฑ ํ™”๋ฉด ๋ฆฌํŒฉํ† ๋ง
Hongji03 May 10, 2026
eac6b65
:sparkles: ์ปค์Šคํ…€ ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€ ์ƒ์„ฑ
Hongji03 May 10, 2026
9a82420
:sparkles: Rebase 'chore/#126-link' onto develop
Hongji03 May 27, 2026
c36b349
:sparkles: ๋งํฌ ์ƒ์„ธ ์กฐํšŒ ๊ธฐ๋ณธ ํ™”๋ฉด ๋ฆฌํŒฉํ† ๋ง ์™„๋ฃŒ
Hongji03 May 27, 2026
b34cf5a
:sparkles: ๋งํฌ ์ƒ์„ธ ์กฐํšŒ ํ™”๋ฉด์— ๋“œ๋กญ๋‹ค์šด ์ถ”๊ฐ€
Hongji03 May 28, 2026
03b924b
:recycle: ์ €์žฅ๋œ ๋งํฌ ์กฐํšŒ ํ™”๋ฉด ๋ฐ ์ˆ˜์ • ํ™”๋ฉด ๋ฆฌํŒฉํ† ๋ง
Hongji03 Jun 19, 2026
3a38cf7
:sparkles: ์ €์žฅ๋œ ๋งํฌ ์‚ญ์ œ ๋ชจ๋‹ฌ ๊ตฌํ˜„ ์™„๋ฃŒ
Hongji03 Jun 19, 2026
82d3384
:recycle: AI ์š”์•ฝ ๋ชจ๋‹ฌ ๋ฆฌํŒฉํ† ๋ง
Hongji03 Jun 20, 2026
d01469e
:sparkles: AI ํƒœ๊ทธ ๋ฏธ ๋งํฌ ์š”์•ฝ, AI ์š”์•ฝ Modal ์—ฐ๋™ ๊ตฌํ˜„ ์™„๋ฃŒ
Hongji03 Jun 20, 2026
d33e92f
:sparkles: LinkCardItem ๋””์ž์ธ ๋ชจ๋“ˆ์— ์ƒ์„ฑ ์™„๋ฃŒ
Hongji03 Jun 20, 2026
ddde533
Merge remote-tracking branch 'origin/chore/#126-link' into chore/#126โ€ฆ
Hongji03 Jun 20, 2026
7ccb07d
Merge pull request #142
KateteDeveloper Jun 21, 2026
6aa03ba
:zap: ์ƒˆ๋กœ์šด ๋งํฌ ์ €์žฅ ๋ฐ ์ €์žฅ๋œ ๋งํฌ ํ™”๋ฉด ์ค‘๋ณต ํ˜ธ์ถœ ์ œ๊ฑฐ
Hongji03 Jun 21, 2026
c57bb3d
:sparkles: LinkCardItem์— ์™ธ๋ถ€ ๋งํฌ ํŒŒ๋ผ๋ฏธํ„ฐ ์ถ”๊ฐ€
Hongji03 Jun 21, 2026
e9faa7c
:sparkles: core ๋ชจ๋“ˆ์— ์žˆ๋˜ design ๋ชจ๋“ˆ ์˜์กด์„ฑ ์ œ๊ฑฐ ์™„๋ฃŒ
Hongji03 Jun 21, 2026
6f5513f
:sparkles: ํ•˜๋‚˜์˜ CATEGORY_MAP์„ ๊ธฐ์ค€์œผ๋กœ id/name์„ ์–‘๋ฐฉํ–ฅ ์กฐํšŒํ•˜๋„๋ก ์ˆ˜์ •
Hongji03 Jun 21, 2026
7f70dea
:sparkles: SituationId๋กœ ์„ ํƒํ•˜๊ฒŒ ๋ณ€๊ฒฝ ๋ฐ Situation Map์œผ๋กœ ๊ตฌํ˜„ ๋ณ€๊ฒฝ ์™„๋ฃŒ
Hongji03 Jun 21, 2026
71f5c0d
:sparkles: SituationId๋กœ ์„ ํƒํ•˜๊ฒŒ ๋ณ€๊ฒฝ ์™„๋ฃŒ
Hongji03 Jun 21, 2026
f2fe87f
:sparkles: ํŒŒ๋ผ๋ฏธํ„ฐ๋ช… ์ง๊ด€์ ์ด๊ฒŒ ์ˆ˜์ •
Hongji03 Jun 21, 2026
ceaa443
:sparkles: graphicsLayer ๋Œ€์‹  shadow๋กœ ๋ณ€๊ฒฝ
Hongji03 Jun 21, 2026
92cf18d
Merge pull request #132
codebidoof Jun 21, 2026
155ec68
:lipstick: fillMaxWidth ์†์„ฑ๊ฐ’ ์‚ญ์ œ
Hongji03 Jun 23, 2026
523e337
:heavy_plus_sign: disign ๋ชจ๋“ˆ์— coil ์˜์กด์„ฑ ์ถ”๊ฐ€
Hongji03 Jun 23, 2026
b8f42c3
:zap: modifier ํŒŒ๋ผ๋ฏธํ„ฐ์— ์ถ”๊ฐ€
Hongji03 Jun 23, 2026
c7bb7b6
:lipstick: ๋กœ๊ณ  ํฌ๊ธฐ ์ถ•์†Œ ๋ฐ Preview์— ThemeProvider ์ถ”๊ฐ€, FontFamily ์‚ญ์ œ
Hongji03 Jun 23, 2026
9f7a772
:zap: DrawableRes ํŒŒ๋ผ๋ฏธํ„ฐ์— ์•ž์— param ์ถ”๊ฐ€ (ํŒ€์› ํ”ผ๋“œ๋ฐฑ ๋ฐ˜์˜)
Hongji03 Jun 23, 2026
97dc501
:zap: ๋„๋ฉ”์ธ ์ด๋ฏธ์ง€ ์ถ”๊ฐ€ ๋ฐ ํŒ€์› ํ”ผ๋“œ๋ฐฑ ๋ฐ˜์˜
Hongji03 Jun 23, 2026
ae715ef
:art: ์ค‘๋ณต ์ฝ”๋“œ ์‚ญ์ œ
Hongji03 Jun 23, 2026
58a76d0
:art: clickable ์˜์—ญ padding ์œ„๋กœ ์˜ฌ๋ฆฌ๊ธฐ
Hongji03 Jun 23, 2026
f83aa5e
:art: ์ƒํ™ฉ ๋ฐ ๊ฐ์ •์นฉ ์ˆ˜์ •
Hongji03 Jun 25, 2026
4177d52
:sparkles: ๋งํฌ ์ €์žฅ์— ์ƒํ™ฉ ์ •๋ณด ์ถ”๊ฐ€
Hongji03 Jun 25, 2026
1f14c4f
:sparkles: ๋งํฌ ์ƒ์„ฑ ํ™”๋ฉด ๋ฆฌํŒฉํ† ๋ง
Hongji03 May 10, 2026
14c80b2
:sparkles: ์ปค์Šคํ…€ ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€ ์ƒ์„ฑ
Hongji03 May 10, 2026
6956155
:sparkles: Rebase 'chore/#126-link' onto develop
Hongji03 May 27, 2026
a3e4141
:sparkles: ๋งํฌ ์ƒ์„ธ ์กฐํšŒ ๊ธฐ๋ณธ ํ™”๋ฉด ๋ฆฌํŒฉํ† ๋ง ์™„๋ฃŒ
Hongji03 May 27, 2026
72b7beb
:sparkles: ๋งํฌ ์ƒ์„ธ ์กฐํšŒ ํ™”๋ฉด์— ๋“œ๋กญ๋‹ค์šด ์ถ”๊ฐ€
Hongji03 May 28, 2026
3656030
:recycle: ์ €์žฅ๋œ ๋งํฌ ์กฐํšŒ ํ™”๋ฉด ๋ฐ ์ˆ˜์ • ํ™”๋ฉด ๋ฆฌํŒฉํ† ๋ง
Hongji03 Jun 19, 2026
488e453
:sparkles: ์ €์žฅ๋œ ๋งํฌ ์‚ญ์ œ ๋ชจ๋‹ฌ ๊ตฌํ˜„ ์™„๋ฃŒ
Hongji03 Jun 19, 2026
badd798
:recycle: AI ์š”์•ฝ ๋ชจ๋‹ฌ ๋ฆฌํŒฉํ† ๋ง
Hongji03 Jun 20, 2026
15a9dcd
:sparkles: AI ํƒœ๊ทธ ๋ฏธ ๋งํฌ ์š”์•ฝ, AI ์š”์•ฝ Modal ์—ฐ๋™ ๊ตฌํ˜„ ์™„๋ฃŒ
Hongji03 Jun 20, 2026
3422028
:sparkles: LinkCardItem ๋””์ž์ธ ๋ชจ๋“ˆ์— ์ƒ์„ฑ ์™„๋ฃŒ
Hongji03 Jun 20, 2026
45b03a5
:sparkles: ๋งํฌ ์ƒ์„ธ ์กฐํšŒ ํ™”๋ฉด Top Bar ์ƒ์„ฑ
Hongji03 May 27, 2026
71c41df
:zap: ์ƒˆ๋กœ์šด ๋งํฌ ์ €์žฅ ๋ฐ ์ €์žฅ๋œ ๋งํฌ ํ™”๋ฉด ์ค‘๋ณต ํ˜ธ์ถœ ์ œ๊ฑฐ
Hongji03 Jun 21, 2026
f3e834b
:sparkles: LinkCardItem์— ์™ธ๋ถ€ ๋งํฌ ํŒŒ๋ผ๋ฏธํ„ฐ ์ถ”๊ฐ€
Hongji03 Jun 21, 2026
6bc7736
:sparkles: core ๋ชจ๋“ˆ์— ์žˆ๋˜ design ๋ชจ๋“ˆ ์˜์กด์„ฑ ์ œ๊ฑฐ ์™„๋ฃŒ
Hongji03 Jun 21, 2026
02cc312
:sparkles: ํ•˜๋‚˜์˜ CATEGORY_MAP์„ ๊ธฐ์ค€์œผ๋กœ id/name์„ ์–‘๋ฐฉํ–ฅ ์กฐํšŒํ•˜๋„๋ก ์ˆ˜์ •
Hongji03 Jun 21, 2026
0c9138c
:sparkles: SituationId๋กœ ์„ ํƒํ•˜๊ฒŒ ๋ณ€๊ฒฝ ๋ฐ Situation Map์œผ๋กœ ๊ตฌํ˜„ ๋ณ€๊ฒฝ ์™„๋ฃŒ
Hongji03 Jun 21, 2026
33e3734
:sparkles: SituationId๋กœ ์„ ํƒํ•˜๊ฒŒ ๋ณ€๊ฒฝ ์™„๋ฃŒ
Hongji03 Jun 21, 2026
d86b3ac
:sparkles: ํŒŒ๋ผ๋ฏธํ„ฐ๋ช… ์ง๊ด€์ ์ด๊ฒŒ ์ˆ˜์ •
Hongji03 Jun 21, 2026
4cf3e1b
:sparkles: graphicsLayer ๋Œ€์‹  shadow๋กœ ๋ณ€๊ฒฝ
Hongji03 Jun 21, 2026
742bb1e
:lipstick: fillMaxWidth ์†์„ฑ๊ฐ’ ์‚ญ์ œ
Hongji03 Jun 23, 2026
26cc654
:heavy_plus_sign: disign ๋ชจ๋“ˆ์— coil ์˜์กด์„ฑ ์ถ”๊ฐ€
Hongji03 Jun 23, 2026
1422b6a
:zap: modifier ํŒŒ๋ผ๋ฏธํ„ฐ์— ์ถ”๊ฐ€
Hongji03 Jun 23, 2026
0b32e8c
:lipstick: ๋กœ๊ณ  ํฌ๊ธฐ ์ถ•์†Œ ๋ฐ Preview์— ThemeProvider ์ถ”๊ฐ€, FontFamily ์‚ญ์ œ
Hongji03 Jun 23, 2026
dd8fb76
:zap: DrawableRes ํŒŒ๋ผ๋ฏธํ„ฐ์— ์•ž์— param ์ถ”๊ฐ€ (ํŒ€์› ํ”ผ๋“œ๋ฐฑ ๋ฐ˜์˜)
Hongji03 Jun 23, 2026
f3d00c4
:zap: ๋„๋ฉ”์ธ ์ด๋ฏธ์ง€ ์ถ”๊ฐ€ ๋ฐ ํŒ€์› ํ”ผ๋“œ๋ฐฑ ๋ฐ˜์˜
Hongji03 Jun 23, 2026
ac2984f
:art: ์ค‘๋ณต ์ฝ”๋“œ ์‚ญ์ œ
Hongji03 Jun 23, 2026
eb9a021
:art: clickable ์˜์—ญ padding ์œ„๋กœ ์˜ฌ๋ฆฌ๊ธฐ
Hongji03 Jun 23, 2026
406cb78
:art: ์ƒํ™ฉ ๋ฐ ๊ฐ์ •์นฉ ์ˆ˜์ •
Hongji03 Jun 25, 2026
0b5645c
:sparkles: ๋งํฌ ์ €์žฅ์— ์ƒํ™ฉ ์ •๋ณด ์ถ”๊ฐ€
Hongji03 Jun 25, 2026
8962f7e
Merge remote-tracking branch 'origin/chore/#126-link' into chore/#126โ€ฆ
Hongji03 Jun 25, 2026
7878663
:zap: null์„ ํ—ˆ์šฉ ์•ˆํ•˜๊ณ  ๊ธฐ๋ณธ๊ฐ’์„ ๋นˆ ๋ฌธ์ž์—ด๋กœ ์‚ฌ์šฉ
Hongji03 Jun 25, 2026
63218e4
:zap: FlowRow๋กœ ๊ตฌํ˜„ ๋ณ€๊ฒฝ
Hongji03 Jun 25, 2026
e9c8c57
:zap: home ๋ชจ๋“ˆ LocalColorTheme.current์—์„œ Material.linkuColors๋กœ ๋ณ€๊ฒฝ
Hongji03 Jun 25, 2026
bf47a4d
:zap: home ๋ชจ๋“ˆ clickable์—์„œ noRippleClickable๋กœ ๋ณ€๊ฒฝ
Hongji03 Jun 25, 2026
e953b32
:zap: mypage ๋ชจ๋“ˆ LocalColorTheme.current์—์„œ Material.linkuColors๋กœ ๋ณ€๊ฒฝ
Hongji03 Jun 25, 2026
4bf61d9
:zap: mypage ๋ชจ๋“ˆ clickable์—์„œ noRippleClickable๋กœ ๋ณ€๊ฒฝ
Hongji03 Jun 25, 2026
3d1dc87
:zap: shape๋ฅผ remember๋กœ ๊ฐ์‹ธ ๋ถˆํ•„์š”ํ•œ ํ• ๋‹น ๋ฐฉ์ง€
Hongji03 Jun 25, 2026
b2f49ed
:zap: clickable์—์„œ noRippleClickable๋กœ ๋ณ€๊ฒฝ
Hongji03 Jun 25, 2026
11811bb
:zap: AsyncImage๋กœ ๋ณ€๊ฒฝ
Hongji03 Jun 25, 2026
91512d1
:zap: core์— ์žˆ๋Š” EmotionType์„ ๊ณตํ†ต์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ฒŒ ์ˆ˜์ •
Hongji03 Jun 25, 2026
5ade90b
:truck: ์•ˆ์“ฐ๋Š” ํŒŒ์ผ ์ œ๊ฑฐ
Hongji03 Jun 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 0 additions & 22 deletions app/src/androidTest/java/com/linku/ExampleInstrumentedTest.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,32 +1,17 @@
<<<<<<<< HEAD:app/src/androidTest/java/com/linku/link/ExampleInstrumentedTest.kt
package com.linku.link
========
package com.linku
>>>>>>>> fd1304faab6b86e04c17e31a0786ce151290d292:app/src/androidTest/java/com/linku/ExampleInstrumentedTest.kt

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
<<<<<<<< HEAD:app/src/androidTest/java/com/linku/link/ExampleInstrumentedTest.kt
assertEquals("com.linku.link", appContext.packageName)
========
assertEquals("com.linku", appContext.packageName)
>>>>>>>> fd1304faab6b86e04c17e31a0786ce151290d292:app/src/androidTest/java/com/linku/ExampleInstrumentedTest.kt
}
}
4 changes: 4 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@

</activity>

<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="default_channel" />

<!-- FCM Service -->
<service
android:name="com.linku.LinkUFireBaseMessageService"
Expand Down
82 changes: 79 additions & 3 deletions app/src/main/java/com/linku/LinkUFireBaseMessageService.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,94 @@
package com.linku

import android.app.NotificationManager
import android.app.PendingIntent
import android.util.Log
import androidx.core.app.NotificationCompat
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.linku.core.di.ApplicationScope
import com.linku.core.repository.AlarmRepository
import com.linku.data.preference.NotificationPreference
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import javax.inject.Inject


@AndroidEntryPoint
class LinkUFireBaseMessageService : FirebaseMessagingService() {

// FirebaseMessagingService๋Š” ์ƒ์„ฑ์ž ์ฃผ์ž…์ด ๋ถˆ๊ฐ€. ๋”ฐ๋ผ์„œ ํ•„๋“œ ์ฃผ์ž… ์‚ฌ์šฉ
@Inject
lateinit var alarmRepository: AlarmRepository
@Inject
lateinit var notificationPreference: NotificationPreference

@Inject
@ApplicationScope
lateinit var externalScope: CoroutineScope

//FireBase์—์„œ ์ƒˆ ํ† ํฐ์ด ๋ฐœ๊ธ‰๋˜์—ˆ์„ ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ์ฝœ๋ฐฑ
override fun onNewToken(token: String) {
super.onNewToken(token)
// TODO: ์„œ๋ฒ„์— FCM ํ† ํฐ ์ „์†ก
if (BuildConfig.DEBUG) {
Log.d("FCM Token", token)
}

notificationPreference.setFcmToken(token)

externalScope.launch {
alarmRepository.registerFCMToken(token)
}
}

// FireBase๋กœ๋ถ€ํ„ฐ ๋ฉ”์„ธ์ง€๋ฅผ ๋ฐ›์•˜์„ ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ์ฝœ๋ฐฑ
override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
// TODO: ํ‘ธ์‹œ ์•Œ๋ฆผ ์ฒ˜๋ฆฌ

// ํ‘ธ์‹œ์•Œ๋ฆผ ํ™œ์„ฑํ™” ์•ˆ๋˜์–ด์žˆ์œผ๋ฉด ์ข…๋ฃŒ
if (!notificationPreference.isMasterNotificationEnabled()) return

// FCM ๋ฉ”์‹œ์ง€ ํƒ€์ž…์— ๋”ฐ๋ผ title/body ์ถ”์ถœ
// Notification Message: message.notification์—์„œ ์ถ”์ถœ
// Data Message: message.data์—์„œ ์ถ”์ถœ
// ๋‘˜ ๋‹ค ์—†์œผ๋ฉด ์ฒ˜๋ฆฌ ๋ถˆํ•„์š”๋กœ ํŒ๋‹จํ•˜์—ฌ ์ข…๋ฃŒ
val title = message.notification?.title ?: message.data["title"] ?: return
val body = message.notification?.body ?: message.data["message"] ?: return
val targetId = message.data["targetId"] ?: "null"

Log.d("FCM", """
FCM ์ˆ˜์‹ 
title: $title
body: $body
targetId: $targetId
""".trimIndent()
)

// ์ผ๋‹จ์€ ์•กํ‹ฐ๋น„ํ‹ฐ๋กœ์˜ ์ด๋™์ฒ˜๋ฆฌ๋งŒ ๊ตฌํ˜„. ์ถ”ํ›„ ์ˆ˜์ • ์˜ˆ์ •
val pendingIntent = PendingIntent.getActivity(
this,
0,
packageManager.getLaunchIntentForPackage(packageName),
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
Comment on lines +69 to +74

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

๐Ÿฉบ Stability & Availability | ๐ŸŸ  Major

๐Ÿงฉ Analysis chain

๐ŸŒ Web query:

Android PendingIntent.getActivity intent parameter nullable NonNull

๐Ÿ’ก Result:

In the Android API, the intent parameter of the PendingIntent.getActivity method is marked as @NonNull [1][2]. While some older documentation or specific framework versions might occasionally display ambiguous nullability annotations in generated documentation [3][4], the official source code for PendingIntent.getActivity explicitly defines the intent parameter as @NonNull [1][2]. Attempting to pass a null Intent to this method will typically result in a NullPointerException, as the system requires a valid Intent to define the activity that the PendingIntent should launch [5].

Citations:


getLaunchIntentForPackage์˜ nullable ๋ฐ˜ํ™˜๊ฐ’์ด PendingIntent.getActivity์— ์ „๋‹ฌ๋˜์–ด NPE ์œ„ํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค.

packageManager.getLaunchIntentForPackage(packageName)์€ ๋Ÿฐ์ฒ˜ ํ™œ๋™์„ ์ฐพ์ง€ ๋ชปํ•˜๋ฉด null์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์œผ๋‚˜, PendingIntent.getActivity์˜ intent ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” @NonNull์œผ๋กœ ์„ ์–ธ๋˜์–ด ์žˆ์–ด null ์ „๋‹ฌ ์‹œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์ œ์•ˆ ์ˆ˜์ •
-        val pendingIntent = PendingIntent.getActivity(
-            this,
-            0,
-            packageManager.getLaunchIntentForPackage(packageName),
-            PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
-        )
+        val launchIntent = packageManager.getLaunchIntentForPackage(packageName)
+        val pendingIntent = launchIntent?.let {
+            PendingIntent.getActivity(
+                this,
+                0,
+                it,
+                PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+            )
+        }
๐Ÿ“ Committable suggestion

โ€ผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
val pendingIntent = PendingIntent.getActivity(
this,
0,
packageManager.getLaunchIntentForPackage(packageName),
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
val launchIntent = packageManager.getLaunchIntentForPackage(packageName)
val pendingIntent = launchIntent?.let {
PendingIntent.getActivity(
this,
0,
it,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
}
๐Ÿค– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/linku/LinkUFireBaseMessageService.kt` around lines 69 -
74, `LinkUFireBaseMessageService`์˜ `PendingIntent.getActivity` ํ˜ธ์ถœ์—
`packageManager.getLaunchIntentForPackage(packageName)`์˜ nullable ๊ฒฐ๊ณผ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋“ค์–ด๊ฐ€
NPE ์œ„ํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค. `getLaunchIntentForPackage` ๊ฒฐ๊ณผ๋ฅผ ๋จผ์ € ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ณ , null์ด๋ฉด
`pendingIntent` ์ƒ์„ฑ์„ ๊ฑด๋„ˆ๋›ฐ๊ฑฐ๋‚˜ ๋Œ€์ฒด `Intent`๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก `pendingIntent` ์ƒ์„ฑ ๋กœ์ง์„ ์ˆ˜์ •ํ•˜์„ธ์š”. ํŠนํžˆ
`LinkUFireBaseMessageService`์˜ ํ•ด๋‹น ์•Œ๋ฆผ/ํด๋ฆญ ์ฒ˜๋ฆฌ ๊ฒฝ๋กœ์—์„œ null-safe ๊ฐ€๋“œ๊ฐ€ ์žˆ๋„๋ก ์ •๋ฆฌํ•ด ์ฃผ์„ธ์š”.


// ์•Œ๋ฆผ ์ œ์ž‘
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_logo)
.setContentTitle(title)
.setContentText(body)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
.build()

// ์•Œ๋ฆผ ์ถœ๋ ฅ
getSystemService(NotificationManager::class.java)
.notify(System.currentTimeMillis().toInt(), notification)
}

}
companion object {
const val CHANNEL_ID = "default_channel"
}
}
151 changes: 119 additions & 32 deletions app/src/main/java/com/linku/MainApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ import com.linku.file.FileViewModel
import com.linku.file.viewmodel.folder.state.FolderStateViewModel
import com.linku.home.HomeApp
import com.linku.home.HomeViewModel
import com.linku.home.screen.SaveLinkResultScreen
import com.linku.home.component.LinkCategoryOption
import com.linku.home.screen.LinkDetailScreen
import com.linku.home.screen.SaveLinkScreen
import com.linku.linku_android.curation.curationGraph
import com.linku.login.navigation.LoginApp
Expand Down Expand Up @@ -159,10 +160,9 @@ fun MainApp(
// ๊ถŒํ•œ ์š”์ฒญ ๋Ÿฐ์ณ
val notificationPermissionLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted -> // ์‹œ์Šคํ…œ ๊ถŒํ•œ ์š”์ฒญ ๊ฒฐ๊ณผ๋ฅผ ๋กœ์ปฌ์— ์ €์žฅ
) { isGranted ->
viewModel.setNotificationEnabled(isGranted)
Log.d("MainApp", "์•Œ๋ฆผ ๊ถŒํ•œ ์š”์ฒญ ๊ฒฐ๊ณผ: $isGranted")
Log.d("MainApp", "์‹œ์Šคํ…œ ๊ถŒํ•œ ์ƒํƒœ: ${ContextCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED}")
}


Expand All @@ -173,6 +173,8 @@ fun MainApp(
// Android 13 ์ด์ƒ์—์„œ๋Š” POST_NOTIFICATIONS ๋Ÿฐํƒ€์ž„ ๊ถŒํ•œ์ด ํ•„์š”ํ•˜๋ฏ€๋กœ ์กฐ๊ฑด๋ถ€ ์š”์ฒญ
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
notificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
} else {
viewModel.setNotificationEnabled(true)
}
requestNotificationPermission = false // ํ•œ ๋ฒˆ๋งŒ ์š”์ฒญํ•˜๋„๋ก ์ฒ˜๋ฆฌ
}
Expand Down Expand Up @@ -359,7 +361,7 @@ fun MainApp(
HomeApp(
viewModel = homeViewModel,
nickname = nickname.orEmpty().ifBlank { "๋งํ" },
onNavigateToMyPage = { // TODO: ์ถ”ํ›„ ์•Œ๋ฆผ ์„ค์ • ํŽ˜์ด์ง€๋กœ ์ด๋™
onNavigateToMyPage = {
navigator.navigate(NavigationRoute.MyPage.route) {
popUpTo(navigator.graph.findStartDestination().id) {
saveState = true
Expand All @@ -369,6 +371,13 @@ fun MainApp(
restoreState = true
}
},
onNavigateToSaveLink = { url ->
homeViewModel.setUrl(url)
navigator.navigate("savelink")
},
onNavigateToLinkDetail = { linkuId ->
navigator.navigate("savelinkresult/$linkuId")
},
onShowNavBar = { showNavBar = it }
)
}
Expand Down Expand Up @@ -453,18 +462,29 @@ fun MainApp(
SaveLinkScreen(
image = vm.image,
url = vm.url,
title = vm.title,
memo = vm.memo,
selectedEmotionId = vm.selectedEmotionId,
selectedSituationId = vm.selectedSituationId,
jobId = vm.jobId ?: 3L,
onPickImage = { imagePicker.launch("image/*") },
onUrlChange = vm::setUrl,
onTitleChange = vm::setTitle,
onMemoChange = vm::setMemo,
onEmotionSelect = vm::selectEmotion,
onSituationSelect = vm::selectSituation,
onSaveClick = {
// ์ €์žฅ ๋ฒ„ํŠผ ๋กœ๊ทธ + API ํ˜ธ์ถœ
Log.d("SaveLink", "try save -> url=${vm.url}, memo=${vm.memo}, emotionId=${vm.selectedEmotionId}, image=${vm.image?.name}")
Log.d(
"SaveLink",
"try save -> url=${vm.url}, memo=${vm.memo}, emotionId=${vm.selectedEmotionId}, situationId=${vm.selectedSituationId}, image=${vm.image?.name}"
)

vm.saveLink(
onSucceed = { saved ->
Log.d("SaveLink", "success -> id=${saved.linkuId}, title=${saved.title}, domain=${saved.domain}")
Log.d(
"SaveLink",
"success -> id=${saved.linkuId}, title=${saved.title}, domain=${saved.domain}"
)
vm.loadLinkDetail(saved.linkuId)
vm.resetForm()
navigator.navigate("savelinkresult/${saved.linkuId}")
Expand Down Expand Up @@ -496,48 +516,115 @@ fun MainApp(
vm.loadCategoryColors()
}

fun emotionNameOf(id: Long?): String {
return when (id) {
1L -> "์ฆ๊ฑฐ์›€"
2L -> "ํ‰์˜จ"
3L -> "์„ค๋ ˜"
4L -> "์Šฌํ””"
5L -> "์งœ์ฆ"
6L -> "๋ถ„๋…ธ"
else -> "๊ฐ์ •"
}
}

// TODO: ์นดํ…Œ๊ณ ๋ฆฌ API ์—ฐ๋™ ํ›„ categoryId ๊ธฐ์ค€ ์‹ค์ œ ์นดํ…Œ๊ณ ๋ฆฌ๋ช…/์ƒ‰์ƒ ๋งคํ•‘์œผ๋กœ ๊ต์ฒด
val CATEGORY_MAP = linkedMapOf(
1L to "์–ดํ•™",
2L to "๋‰ด์Šค",
3L to "๊ณต๋ถ€๋ฒ•",
4L to "ITยท๊ฐœ๋ฐœ",
5L to "์ž๊ธฐ๊ณ„๋ฐœ",
6L to "์ทจ์—…ยท์ด์ง",
7L to "๋น„์ฆˆ๋‹ˆ์Šค ์ธ์‚ฌ์ดํŠธ",
8L to "์ƒ์‚ฐ์„ฑยทํˆด",
9L to "๋ผ์ดํ”„์Šคํƒ€์ผ",
10L to "์‹ฌ๋ฆฌยท์ž๊ธฐ์ดํ•ด",
11L to "์—์„ธ์ดยท์นผ๋Ÿผ",
12L to "ํŠธ๋ Œ๋“œ",
13L to "๋””์ž์ธยท์˜ˆ์ˆ ",
14L to "์˜์ƒยท๋ฎค์ง",
15L to "๋ง›์ง‘ยท์—ฌํ–‰",
16L to "๊ธฐํƒ€"
)
Comment on lines +519 to +549

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P1] ์ด๋ ‡๊ฒŒ ๋งคํ•‘ํ•จ์ˆ˜๊ฐ€ MainApp์— ์žˆ์œผ๋ฉด ๋ชจ๋“ˆ์„ฑ์— ์œ„๋ฐฐ๋œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค!
๋˜ ์ง€๊ธˆ ๊ฐ™์€ ๋ฉ€ํ‹ฐ ๋ชจ๋“ˆ์—์„œ๋Š” core๋ฅผ ์ฐธ์กฐํ•ด์„œ ์“ฐ๊ณ  ์žˆ๊ธฐ์— app์—์„œ ๋งคํ•‘ํ•˜๋ฉด ๋‹ค๋ฅธ ํ”ผ์ณ ๋ชจ๋“ˆ์€ ์‚ฌ์šฉํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค!

์ด๊ฑด ์•„์˜ˆ ์ •์ ์œผ๋กœ ์ •ํ•ด์ง„ ๋งคํ•‘ ๊ตฌํ˜„์ด๋‹ˆ ์—ฌ๊ธฐ์„œ ์ด ๋ถ€๋ถ„์€ ์‚ญ์ œ ํ•˜๊ณ , ์ด๋ฏธ ์ž˜ ๋งŒ๋“ค์–ด์ง„ CategoryType, EmotionType์ด ์žˆ์œผ๋‹ˆ ๋ฐ›์•„์™€์„œ ์“ธ ์ˆ˜ ์žˆ๊ฒŒ ์ˆ˜์ •ํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™์•„์š” ๐Ÿ˜Š

emotionNameOf, CATEGORY_MAP, categoryNameOf, categoryIdOf ์‚ญ์ œ ํ•˜๊ณ ,
์‚ฌ์šฉ๋ถ€๋งŒ ๊น”๋”ํ•˜๊ฒŒ ํ˜ธ์ถœํ•ด์„œ

val categoryType = CategoryType.fromId(linkDetail?.categoryId)
val emotionType = EmotionType.fromId(linkDetail?.emotionId)

ํ•œ๋‹ค๋ฉด ๋” ์ข‹์€ ๋ฐฉํ–ฅ์ผ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค:)

์ถ”๊ฐ€์ ์œผ๋กœ
์ง€๊ธˆ fromId๊ฐ€ Long ํƒ€์ž…๋งŒ ๋ฐ›๊ฒŒ ๋˜์–ด์žˆ์–ด์„œ categoryId/emotionId๊ฐ€ Long?์ด๋ผ ๊ทธ๋Œ€๋กœ ์“ฐ๋ฉด ํƒ€์ž… ์—๋Ÿฌ๊ฐ€ ๋‚  ๊ฒƒ ๊ฐ™์€๋ฐ, ๋งž๋‚˜์š”? @ugmin1030 ํ™•์ธ ํ•œ ๋ฒˆ ๋ถ€ํƒ๋“œ๋ ค์š”!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

์นดํ…Œ๊ณ ๋ฆฌ๋Š” API ์—ฐ๋™์œผ๋กœ ๊ตฌํ˜„ํ•  ์˜ˆ์ •์ด๋ผ ์ง€๊ธˆ์€ ์ผ๋‹จ ์ €๋ ‡๊ฒŒ ๊ตฌํ˜„ํ•ด๋†จ์—ˆ์–ด์š”ใ… ใ…  ์ถ”ํ›„์— ํŒ€์žฅ๋‹˜์ด design ๋ชจ๋“ˆ์— ์นดํ…Œ๊ณ ๋ฆฌ ๊ด€๋ จ๋œ ํŒŒ์ผ ์˜ฌ๋ ค์ฃผ์‹œ๋ฉด ๊ทธ ๋•Œ API ์—ฐ๋™ํ•˜๋ฉด์„œ ์ˆ˜์ •ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค


fun categoryNameOf(id: Long?): String {
return CATEGORY_MAP[id] ?: "์นดํ…Œ๊ณ ๋ฆฌ"
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

fun categoryIdOf(name: String): Long? {
return CATEGORY_MAP.entries
.firstOrNull { it.value == name }
?.key
}

fun keywordToTags(keyword: String?): List<String> {
return keyword
.orEmpty()
.split(",", " ", "#")
.map { it.trim() }
.filter { it.isNotBlank() }
.take(4)
}

// ์ง„ํ–‰๋ฅ /์ƒ‰์ƒ ๋งต ์ˆ˜์ง‘
val aiProgress = vm.aiProgress.collectAsState().value
val categoryColorMap = vm.categoryColorMap.collectAsState().value

val categoryOptions = categoryColorMap.mapNotNull { (name, style) ->
val id = categoryIdOf(name) ?: return@mapNotNull null

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P1] ์—ฌ๊ธฐ์„œ ์„œ๋ฒ„์—์„œ ๋‚ด๋ ค์ค€ string ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ LongId๋กœ ์—ญ๋งคํ•‘ํ•˜๋Š”๊ฑฐ ๋งž๋‚˜์š”? ์Œ ์•„๋‹ˆ๋ฉด categoryColorMap์„ String ํ‚ค๊ฐ€ ์•„๋‹ˆ๋ผ CategoryType ํ‚ค๋กœ ๋ฐ”๊พธ๋Š” ๋ฐฉํ–ฅ์€ ์–ด๋–ค๊ฐ€์š”?

private val _categoryColorMap = MutableStateFlow<Map<CategoryType, CategoryColorStyle>>(emptyMap())

์ด๋ ‡๊ฒŒ ํ™ˆ๋ทฐ๋ชจ๋ธ์—์„œ ์•„์˜ˆ String name์„ ํ•œ ๋ฒˆ ๋ณ€ํ™˜ํ•˜๋ฉด.... ๋ญ ์—ญ ๋งคํ•‘ํ•  ํ•„์š”๋Š” ์—†์ง€ ์•Š์„๊นŒ ์‹ถ์€๋ฐ

๊ทธ๋Ÿฌ๋ฉด ์—ฌ๊ธฐ์„œ๋Š”

val categoryOptions = categoryColorMap.map { (type, style) ->
    LinkCategoryOption(
        id = type.id,
        name = type.tagName,
        color = style.color4
    )
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™๊ธฐ๋„ ํ•˜๊ณ ..? ์Œ ์•„๋‹ˆ๋ฉด ์•„์˜ˆ LinkCategoryOption์ด Long id๋ฅผ ๋”ฐ๋กœ ๋“ค ํ•„์š” ์—†์ด CategoryType์„ ์ง์ ‘ ๋“œ๋Š” ๋ฐฉ์‹๋„ ์žˆ์„ ๊ฒƒ ๊ฐ™์€๋ฐ ์ง€ํ˜„์ด๊ฐ€ ๊ฐœ๋ฐœํ•œ ํŒŒํŠธ์ด๋‹ˆ ํ•œ ๋ฒˆ ๋ณด๊ณ  ํŒ๋‹จํ•ด์ฃผ์„ธ์š”:)


LinkCategoryOption(
id = id,
name = name,
color = style.color4
)
}
Comment on lines +519 to +582

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

์ด๋ฆ„๋งŒ์œผ๋กœ๋„ ์œ ๋‹ˆํฌํ•œ ๋ฐ์ดํ„ฐ๋“ค์— Long ํ˜•์‹ id๊ฐ€ ๋“ค์–ด๊ฐ€๋Š” ๊ฑด ๋ถˆ์•ˆ๋ถˆ์•ˆํ•˜๋„ค์š”..

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

์ด๋„˜์œผ๋กœ ๋ฐ”๊ฟ”๋ณผ๊ฒŒ์˜ˆ


// ์™ธ๋ถ€ ๋ธŒ๋ผ์šฐ์ € ์—ด๊ธฐ
fun openUrl(url: String) {
runCatching {
val fixed = if (url.startsWith("http")) url else "https://$url"
val fixed = if (
url.startsWith("http://") || url.startsWith("https://")
) {
url
} else {
"https://$url"
}

val intent = Intent(
Intent.ACTION_VIEW,
fixed.toUri()
)

context.startActivity(intent)
}.onFailure {
Toast.makeText(context, "๋งํฌ๋ฅผ ์—ด ์ˆ˜ ์—†์–ด์š”.", Toast.LENGTH_SHORT).show()
}
}

SaveLinkResultScreen(
link = vm.linkDetail,
aiArticle = vm.aiArticleDetail,
isLoading = vm.isLoadingLinkDetail || vm.isLoadingAiArticle,
isAiLoading = vm.isLoadingAiArticle,
onBack = { navigator.popBackStack() },
onOpenLink = { url -> openUrl(url) },
categoryColorMap = categoryColorMap,
onSubmitEdit = { title, memo, categoryId, emotionId ->
vm.updateLink(
title = title,
memo = memo,
categoryId = categoryId,
emotionId = emotionId,
onSucceed = { Toast.makeText(context, "์ˆ˜์ • ์™„๋ฃŒ", Toast.LENGTH_SHORT).show() },
onFailed = { e ->
Log.e("SaveLinkResult", "์ˆ˜์ • ์‹คํŒจ", e)
Toast.makeText(context, e.message ?: "์ˆ˜์ •์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.", Toast.LENGTH_SHORT).show()
}
)
},
onRequestAiSummary = { vm.loadAiArticle(linkuId) },
aiProgress = aiProgress,
onCancelAi = { vm.cancelAiArticleJob() }
val linkDetail = vm.linkDetail
val aiArticle = vm.aiArticleDetail

val displayKeyword = aiArticle?.keyword?.trim().orEmpty()
.ifEmpty { linkDetail?.keyword.orEmpty() }

val displaySummary = aiArticle?.summary?.trim().orEmpty()
.ifEmpty { linkDetail?.summary.orEmpty() }

LinkDetailScreen(
linkTitle = linkDetail?.title.orEmpty(),
category = categoryNameOf(linkDetail?.categoryId),
emotion = emotionNameOf(linkDetail?.emotionId),
situationId = null, // TODO: ์ƒ์„ธ API์— situationId ์ƒ๊ธฐ๋ฉด linkDetail?.situationId๋กœ ๋ณ€๊ฒฝ
linkUrl = linkDetail?.linku.orEmpty(),
memo = linkDetail?.memo.orEmpty(),
tags = keywordToTags(displayKeyword),
aiSummary = displaySummary,
categoryOptions = categoryOptions,
onBack = {
navigator.popBackStack()
}
)
}

Expand Down
Loading