Skip to content

Conversation

@comst19
Copy link
Collaborator

@comst19 comst19 commented Jan 8, 2026

1. ⭐️ 변경된 내용

  • Android 13(Tiramisu) 미만에서는 NotificationManagerCompat.areNotificationsEnabled() 를 사용
  • lifecycleState를 관찰하여, 사용자가 설정 화면에 다녀왔을 때 UI가 업데이트되도록 remember 추가
  • Android 13(Tiramisu) 미만 버전에서 클릭 시, 권한 팝업 대신 시스템의 앱 알림 설정 화면으로 바로 이동하도록 변경

2. 🖼️ 스크린샷(선택)

3. 💡 알게된 부분

4. 📌 이 부분은 꼭 봐주세요!

문제:
기존 코드에서는 Android 13(Tiramisu) 미만일 경우 permissionList.permissions.find { ... } 내부 조건이 항상 true를 반환하도록 되어 있어, 권한 리스트의 첫 번째 상태를 참조하는 문제가 생기고 그로 인해 UI에 알람 권한 허용 여부가 부정확한 경우가 생김

// 기존 코드
 val permissionList = rememberMultiplePermissionsState(
   listOfNotNull(
        when {
            SDK_INT < 33 -> READ_EXTERNAL_STORAGE
            else -> READ_MEDIA_IMAGES
        },
        if (SDK_INT >= UPSIDE_DOWN_CAKE) READ_MEDIA_VISUAL_USER_SELECTED else null,
        if (SDK_INT >= TIRAMISU) POST_NOTIFICATIONS else null,
        READ_CONTACTS
   )
)

val notificationPermission = permissionList.permissions
    .find { if (SDK_INT >= TIRAMISU) it.permission == POST_NOTIFICATIONS else true }

checked = notificationPermission?.status == PermissionStatus.Granted

@tgyuuAn @kkh725

Summary by CodeRabbit

  • Bug Fixes

    • Notification toggles now reflect the actual notification state by combining system-level settings and runtime permission status.
    • On older Android, enabling notifications opens the app's notification settings when needed.
  • New Features

    • Lifecycle-aware permission state collection for more consistent, timely permission prompts across Android versions.
    • Permission request flow adjusted to avoid unnecessary prompts.

✏️ Tip: You can customize this high-level summary in your review settings.

@comst19 comst19 requested review from kkh725 and tgyuuAn January 8, 2026 14:05
@comst19 comst19 self-assigned this Jan 8, 2026
@comst19 comst19 added 리뷰 원해요🔥 피어의 리뷰를 기다리는 ing.. 🔥 ㅁㅅ민수 labels Jan 8, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 8, 2026

📝 Walkthrough

Walkthrough

Notification permission handling was refactored in sign-up and settings: lifecycle-aware state collection added, notification enablement now considers NotificationManagerCompat for pre-TIRAMISU and POST_NOTIFICATIONS for TIRAMISU+, and toggle actions either request permission or open system app notification settings.

Changes

Cohort / File(s) Summary
Sign-up access rights page
feature/auth/src/main/java/com/puzzle/auth/graph/signup/page/AccessRightsPage.kt
Added LocalLifecycleOwner and collectAsStateWithLifecycle() for permission state. Compute notification enabled using NotificationManagerCompat.areNotificationsEnabled() for pre-TIRAMISU and POST_NOTIFICATIONS state for TIRAMISU+. Replaced LaunchedEffect(permissionList) with LaunchedEffect(Unit) and request permissions only when any are denied. Notification row now uses combined isNotificationGranted and routes toggles to handlePermission (TIRAMISU+) or opens app notification settings (pre-TIRAMISU).
Settings notification body
feature/setting/src/main/java/com/puzzle/setting/graph/main/SettingScreen.kt
Added NotificationManagerCompat and LocalContext usage. Determine notification enabled state via rememberPermissionState(POST_NOTIFICATIONS) (TIRAMISU+) or NotificationManagerCompat.from(context).areNotificationsEnabled() (pre-TIRAMISU). On toggle when not granted, request permission on TIRAMISU+ or open app notification settings (Settings.ACTION_APP_NOTIFICATION_SETTINGS) for older APIs.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant UI as App UI (Toggle)
  participant PermAPI as Permission API (POST_NOTIFICATIONS)
  participant SysSettings as System Settings / NotificationManager

  User->>UI: Toggle Push Notifications
  UI->>SysSettings: check areNotificationsEnabled()
  alt Android >= TIRAMISU
    UI->>PermAPI: check POST_NOTIFICATIONS state
    PermAPI-->>UI: Granted / Denied
    alt Granted
      UI-->>User: Enable toggle (persist)
    else Denied
      UI->>PermAPI: requestPermission()
      PermAPI-->>User: system permission dialog
      PermAPI-->>UI: result (Granted/Denied)
      UI-->>User: Update toggle / show settings prompt
    end
  else Android < TIRAMISU
    SysSettings-->>UI: areNotificationsEnabled() result
    alt Enabled
      UI-->>User: Enable toggle (persist)
    else Disabled
      UI->>SysSettings: open ACTION_APP_NOTIFICATION_SETTINGS
      SysSettings-->>User: show app notification settings
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

잡다한 🗂️

Poem

🐰 I hop through lifecycle and permission gates,
TIRAMISU asks, old APIs open settings gates,
If granted I dance, if denied I steer,
To request or to settings — the path is clear,
A rabbit cheers as notifications appear 🥕🔔

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: fixing notification permission logic for Android versions below Tiramisu (SDK < 33), which matches the core objective of the PR.
Description check ✅ Passed The description follows the required template structure with all four sections completed. It explains what changed, includes the key problem that was fixed, identifies the specific bug in the old code, and highlights important aspects for reviewers.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
feature/auth/src/main/java/com/puzzle/auth/graph/signup/page/AccessRightsPage.kt (2)

101-107: Consider optimizing lifecycle state dependency.

The current implementation recomputes isNotificationGranted on every lifecycle state change. While areNotificationsEnabled() is lightweight, you could optimize by filtering for specific states or using derivedStateOf to reduce unnecessary recompositions.

♻️ Optional optimization
-    val isNotificationGranted = remember(lifecycleState, notificationPermission?.status) {
+    val isNotificationGranted = remember(
+        key1 = if (lifecycleState.isAtLeast(Lifecycle.State.RESUMED)) lifecycleState else null,
+        key2 = notificationPermission?.status
+    ) {
         if (SDK_INT >= TIRAMISU) {
             notificationPermission?.status == PermissionStatus.Granted
         } else {
             NotificationManagerCompat.from(context).areNotificationsEnabled()
         }
     }

This limits recomputation to when the screen becomes visible (RESUMED state), which is when the user might return from settings.


255-266: Extract duplicated handlePermission to shared utility.

The handlePermission function is duplicated in both AccessRightsPage.kt and SettingScreen.kt with identical logic. Consider extracting it to a common utility module to improve maintainability.

♻️ Suggested refactor

Create a shared utility file (e.g., core/common/src/main/java/com/puzzle/common/permission/PermissionUtils.kt):

package com.puzzle.common.permission

import android.content.Context
import android.content.Intent
import android.net.Uri
import android.provider.Settings
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionState
import com.google.accompanist.permissions.PermissionStatus
import com.google.accompanist.permissions.shouldShowRationale

@OptIn(ExperimentalPermissionsApi::class)
fun handlePermission(context: Context, permission: PermissionState?) {
    permission?.let {
        if (it.status == PermissionStatus.Granted || !it.status.shouldShowRationale) {
            val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
                data = Uri.fromParts("package", context.packageName, null)
            }
            context.startActivity(intent)
        } else {
            it.launchPermissionRequest()
        }
    }
}

Then import and use it in both files.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bf43d6e and 540056e.

📒 Files selected for processing (1)
  • feature/auth/src/main/java/com/puzzle/auth/graph/signup/page/AccessRightsPage.kt
🧰 Additional context used
🧬 Code graph analysis (1)
feature/auth/src/main/java/com/puzzle/auth/graph/signup/page/AccessRightsPage.kt (2)
feature/auth/src/main/java/com/puzzle/auth/graph/signup/SignUpViewModel.kt (1)
  • onBackClick (112-121)
feature/setting/src/main/java/com/puzzle/setting/graph/main/SettingScreen.kt (1)
  • handlePermission (401-413)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (5)
feature/auth/src/main/java/com/puzzle/auth/graph/signup/page/AccessRightsPage.kt (5)

32-33: LGTM! Imports support lifecycle-aware permission handling.

The added imports are necessary for the new implementation: lifecycle state tracking, notification status checking on older APIs, and state management.

Also applies to: 44-46


67-70: LGTM! Lifecycle tracking enables state refresh on resume.

Collecting lifecycle state allows the notification permission status to be rechecked when the user returns from system settings, which is essential for pre-Tiramisu devices.


92-94: LGTM! Correct API-level handling for notification permission.

The conditional retrieval correctly reflects that POST_NOTIFICATIONS runtime permission only exists on API 33+.


109-114: LGTM! Conditional permission request prevents unnecessary prompts.

The change to LaunchedEffect(Unit) with a hasDenied check correctly implements the requirement to show already-granted permissions without immediately launching a permission request. This improves UX by respecting previously granted permissions.


155-165: LGTM! Correct API-level branching for notification settings.

The implementation correctly handles both API levels:

  • API 33+: Uses runtime permission flow via handlePermission
  • Below API 33: Opens notification settings directly, since no runtime permission exists

@comst19 comst19 changed the title [PC-1627] 권한 허용 화면 로직 수정 [PC-1627] 알람 권한 티라미수 이하 버전 로직 수정 Jan 8, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
@feature/setting/src/main/java/com/puzzle/setting/graph/main/SettingScreen.kt:
- Around line 230-237: The pre-TIRAMISU notification permission check in
NotificationBody is computed on every recomposition and isn’t lifecycle-aware,
so the UI won’t refresh when the user toggles notifications in system settings;
change this by hoisting isPermissionGranted into a remembered MutableState
(e.g., remember { mutableStateOf(...) }) and update that state from a
lifecycle-aware coroutine (use lifecycleOwner.repeatOnStarted or a
LaunchedEffect tied to lifecycleOwner) to call
NotificationManagerCompat.from(context).areNotificationsEnabled() on resume;
reference NotificationBody for where to create and read the state, and mirror
the lifecycle pattern used in SettingRoute and AccessRightsPage to trigger a
state refresh when the screen resumes.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 540056e and a299df3.

📒 Files selected for processing (1)
  • feature/setting/src/main/java/com/puzzle/setting/graph/main/SettingScreen.kt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (1)
feature/setting/src/main/java/com/puzzle/setting/graph/main/SettingScreen.kt (1)

43-43: LGTM! Necessary additions for pre-TIRAMISU support.

The NotificationManagerCompat import and LocalContext retrieval are correctly added to support the new notification permission logic for Android versions below TIRAMISU.

Also applies to: 224-224

Comment on lines +265 to +272
if (SDK_INT >= TIRAMISU) {
handlePermission(context, notificationPermission)
} else {
val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
}
context.startActivity(intent)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Settings.ACTION_APP_NOTIFICATION_SETTINGS EXTRA_APP_PACKAGE Android API 21 32 documentation

💡 Result:

Summary — official docs and practical compatibility:

  • Official (Android SDK): Settings.ACTION_APP_NOTIFICATION_SETTINGS and Settings.EXTRA_APP_PACKAGE were added in API level 26 (Android O). Use Intent action Settings.ACTION_APP_NOTIFICATION_SETTINGS with extra Settings.EXTRA_APP_PACKAGE (android.provider.extra.APP_PACKAGE). [1]

  • Pre-O compatibility (API 21–25): many apps use the settings action string "android.settings.APP_NOTIFICATION_SETTINGS" and the undocumented extras "app_package" (String) and "app_uid" (int) to open an app's notification settings on Lollipop—Marshmallow—Nougat devices. This approach is not part of the public SDK and may not work on all devices. Example usages and community guidance are shown on Stack Overflow. [2]

References:
[1] Android Developers — Settings (API reference) (shows ACTION_APP_NOTIFICATION_SETTINGS and EXTRA_APP_PACKAGE added in API 26).
[2] Stack Overflow — “Any way to link to the Android notification settings for my app?” (compatibility examples for API 21–25 using "app_package" / "app_uid").


Fix Intent approach for API 21-25 compatibility.

The code uses Settings.ACTION_APP_NOTIFICATION_SETTINGS and Settings.EXTRA_APP_PACKAGE, which were added in API level 26. These constants are unavailable on API 21-25, causing the pre-TIRAMISU branch (which includes API 21-25) to crash when executed on those devices.

For API 21-25 compatibility, use the undocumented Intent action "android.settings.APP_NOTIFICATION_SETTINGS" with extras "app_package" (String) and "app_uid" (int) instead. Add a version check:

  • API 26+: Use Settings.ACTION_APP_NOTIFICATION_SETTINGS with Settings.EXTRA_APP_PACKAGE
  • API 21-25: Use the undocumented action string with "app_package" and "app_uid" extras

Copy link
Member

@tgyuuAn tgyuuAn left a comment

Choose a reason for hiding this comment

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

해당 PR은 어떤 맥락에서 등장한 PR일까요?
PR에 Context가 빈약한 것 같아요.
코드가 작성된 이유도 작성되었으면 좋겠어요!

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
@feature/setting/src/main/java/com/puzzle/setting/graph/main/SettingScreen.kt:
- Around line 235-246: The permission check in SettingScreen.kt for
isPermissionGranted is inconsistent with AccessRightsPage.kt: update the
remember block used to compute isPermissionGranted so that on SDK_INT >=
TIRAMISU you return true only if notifications are enabled OR the runtime
notificationPermission?.status == PermissionStatus.Granted (i.e., use the same
areNotificationsEnabled() || notificationPermission?.status ==
PermissionStatus.Granted logic used in AccessRightsPage.kt), referencing
NotificationManagerCompat.from(context).areNotificationsEnabled() and the
notificationPermission variable to ensure both system setting and runtime
permission are considered.
🧹 Nitpick comments (1)
feature/setting/src/main/java/com/puzzle/setting/graph/main/SettingScreen.kt (1)

433-444: Extract duplicated function to shared utility.

This handlePermission function is duplicated in AccessRightsPage.kt (lines 257-268). Consider extracting it to a common utility module or package to maintain DRY principles and ensure consistent behavior across the app.

♻️ Suggestion: Extract to common module

Create a shared utility file like feature-common/src/main/java/com/puzzle/common/permission/PermissionUtils.kt:

@OptIn(ExperimentalPermissionsApi::class)
fun handlePermission(context: Context, permission: PermissionState?) {
    permission?.let {
        if (it.status == PermissionStatus.Granted || !it.status.shouldShowRationale) {
            val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
                data = Uri.fromParts("package", context.packageName, null)
            }
            context.startActivity(intent)
        } else {
            it.launchPermissionRequest()
        }
    }
}

Then import and use it in both files.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a299df3 and 686d2cc.

📒 Files selected for processing (2)
  • feature/auth/src/main/java/com/puzzle/auth/graph/signup/page/AccessRightsPage.kt
  • feature/setting/src/main/java/com/puzzle/setting/graph/main/SettingScreen.kt
🧰 Additional context used
🧬 Code graph analysis (2)
feature/setting/src/main/java/com/puzzle/setting/graph/main/SettingScreen.kt (1)
feature/auth/src/main/java/com/puzzle/auth/graph/signup/page/AccessRightsPage.kt (1)
  • handlePermission (256-268)
feature/auth/src/main/java/com/puzzle/auth/graph/signup/page/AccessRightsPage.kt (1)
feature/setting/src/main/java/com/puzzle/setting/graph/main/SettingScreen.kt (1)
  • handlePermission (432-444)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (6)
feature/setting/src/main/java/com/puzzle/setting/graph/main/SettingScreen.kt (2)

248-252: Review auto-enable behavior for UX implications.

This effect automatically enables push notifications when permission is granted, even if the user hasn't explicitly toggled it on. While this may be intentional, consider whether users who grant permission from system settings might want to manually control the toggle.

For example: A user grants notification permission but may still want to decide later whether to enable push notifications in the app.

Is this auto-enable behavior the intended UX? If so, consider adding a comment explaining the rationale.


280-287: LGTM! Correct pre-TIRAMISU behavior.

The conditional logic correctly handles both API levels:

  • TIRAMISU+: Uses runtime permission flow
  • Pre-TIRAMISU: Opens system notification settings directly

This aligns with the PR objectives to avoid showing permission popup on older Android versions.

feature/auth/src/main/java/com/puzzle/auth/graph/signup/page/AccessRightsPage.kt (4)

68-69: LGTM! Lifecycle-aware state handling.

The lifecycle state collection enables the UI to update when the user returns from the system settings screen, which is essential for reflecting permission changes.


101-109: LGTM! Correct combined permission check.

The logic correctly combines both checks for TIRAMISU+:

  • Runtime permission status (POST_NOTIFICATIONS)
  • System notification settings (areNotificationsEnabled())

This is the proper approach since on Android 13+, both can be controlled independently.

Note: Ensure SettingScreen.kt uses this same logic (see comment on that file).


111-116: LGTM! Improved permission request logic.

Changing to LaunchedEffect(Unit) ensures permissions are only requested once during initial composition, and the hasDenied check prevents unnecessary requests when all permissions are already granted.


157-167: LGTM! Consistent pre-TIRAMISU handling.

The notification row correctly:

  • Uses the combined isNotificationGranted state for the toggle
  • Routes to appropriate handlers based on API level
  • Opens system notification settings directly on pre-TIRAMISU devices

This aligns with the PR objectives and matches the approach in SettingScreen.kt.

Comment on lines +235 to +246
val isPermissionGranted = remember(
lifecycleState,
notificationPermission?.status
) {
if (SDK_INT >= TIRAMISU) {
notificationPermission?.status == PermissionStatus.Granted
} else {
NotificationManagerCompat
.from(context)
.areNotificationsEnabled()
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Inconsistent permission check logic for TIRAMISU+ between files.

For Android 13+ (TIRAMISU), this code only checks the runtime permission status, but in AccessRightsPage.kt (lines 104-108), the logic uses:

areNotificationsEnabled || notificationPermission?.status == PermissionStatus.Granted

This inconsistency could cause issues because:

  • On TIRAMISU+, users can grant the runtime permission but still disable notifications in system settings
  • The two screens would show different permission states for the same notification status
🔧 Proposed fix to align with AccessRightsPage.kt logic
 val isPermissionGranted = remember(
     lifecycleState,
     notificationPermission?.status
 ) {
+    val areNotificationsEnabled = NotificationManagerCompat
+        .from(context)
+        .areNotificationsEnabled()
+
     if (SDK_INT >= TIRAMISU) {
-        notificationPermission?.status == PermissionStatus.Granted
+        areNotificationsEnabled || notificationPermission?.status == PermissionStatus.Granted
     } else {
-        NotificationManagerCompat
-            .from(context)
-            .areNotificationsEnabled()
+        areNotificationsEnabled
     }
 }
🤖 Prompt for AI Agents
In @feature/setting/src/main/java/com/puzzle/setting/graph/main/SettingScreen.kt
around lines 235 - 246, The permission check in SettingScreen.kt for
isPermissionGranted is inconsistent with AccessRightsPage.kt: update the
remember block used to compute isPermissionGranted so that on SDK_INT >=
TIRAMISU you return true only if notifications are enabled OR the runtime
notificationPermission?.status == PermissionStatus.Granted (i.e., use the same
areNotificationsEnabled() || notificationPermission?.status ==
PermissionStatus.Granted logic used in AccessRightsPage.kt), referencing
NotificationManagerCompat.from(context).areNotificationsEnabled() and the
notificationPermission variable to ensure both system setting and runtime
permission are considered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ㅁㅅ민수 리뷰 원해요🔥 피어의 리뷰를 기다리는 ing.. 🔥

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants