Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
45 changes: 27 additions & 18 deletions .claude/hooks/issue-type-guard.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# PostToolUse Bash hook: `gh issue create` 직후 새 이슈의 "type 라벨" 검증.
# PostToolUse Bash hook: `gh issue create` 직후 새 이슈의 "type 라벨" + "본문 양식" 검증.
#
# GitHub 네이티브 Issue Type 은 organization 전용이라 개인 레포(1hyok/SlowClock)에는 없음 →
# 종류 분류를 Task / Bug / Feature "라벨" 로 운영한다. 이 hook 은 새 이슈에 type 라벨이
Expand All @@ -9,6 +9,7 @@
# - fix(...) → Bug
# - feat(...) → Feature
# - chore/refactor/test/ci/build/docs(...) → Task
# - 본문이 .github/ISSUE_TEMPLATE/custom.md 양식(## 📜 Overview …)을 안 따르면 → 재작성 안내
#
# Bypass: 의도적으로 prefix 와 다른 라벨을 쓰려면 issue 본문에
# `<!-- type-override: <Task|Bug|Feature> -->` marker 를 박는다.
Expand Down Expand Up @@ -39,27 +40,35 @@ type_label="$(echo "$data" | jq -r '[.labels[].name] | map(select(. == "Task" or
# override marker (Task|Bug|Feature)
override="$(echo "$body" | grep -oE '<!-- *type-override: *(Task|Bug|Feature) *-->' | grep -oE '(Task|Bug|Feature)' | head -1)"

# type 라벨 누락
# ── 보정 필요사항 누적 (type 라벨 + 본문 양식) ──────────────
problems=""

# 1) type 라벨 누락
if [ "$type_label" = "null" ]; then
jq -nc --arg url "$url" --arg n "$issue_num" \
'{hookSpecificOutput: {hookEventName: "PostToolUse", additionalContext: ("Issue \($url) 에 type 라벨(Task/Bug/Feature) 누락. title prefix 에 맞는 라벨을 즉시 부여: gh issue edit \($n) --add-label <Task|Bug|Feature>. (fix→Bug, feat→Feature, chore/refactor/test/ci/build/docs→Task)")}}'
exit 0
problems="${problems}- type 라벨(Task/Bug/Feature) 누락 → gh issue edit ${issue_num} --add-label <Type> (fix→Bug, feat→Feature, chore/refactor/test/ci/build/docs→Task)"$'\n'
# 2) title prefix ↔ 라벨 mismatch (override 없을 때만)
elif [ -z "$override" ]; then
expected=""
case "$title" in
fix\(*|fix:*) expected="Bug" ;;
feat\(*|feat:*) expected="Feature" ;;
chore\(*|chore:*|refactor\(*|refactor:*|test\(*|test:*|ci\(*|ci:*|build\(*|build:*|docs\(*|docs:*) expected="Task" ;;
esac
if [ -n "$expected" ] && [ "$type_label" != "$expected" ]; then
problems="${problems}- type 라벨=${type_label} ↔ title prefix mismatch (expected=${expected}) → gh issue edit ${issue_num} --remove-label ${type_label} --add-label ${expected} (의도면 본문에 <!-- type-override: ${type_label} -->)"$'\n'
fi
fi

# override 있으면 통과
[ -n "$override" ] && exit 0

# title prefix → expected Type 추론
expected=""
case "$title" in
fix\(*|fix:*) expected="Bug" ;;
feat\(*|feat:*) expected="Feature" ;;
chore\(*|chore:*|refactor\(*|refactor:*|test\(*|test:*|ci\(*|ci:*|build\(*|build:*|docs\(*|docs:*) expected="Task" ;;
esac
# 3) 본문 양식 검사 (.github/ISSUE_TEMPLATE/custom.md 준수 — '📜 Overview' 섹션 필수)
if ! printf '%s' "$body" | grep -q "📜 Overview"; then
problems="${problems}- 본문이 이슈 템플릿 양식 위반 (## 📜 Overview / ## 📌 Child Issue / ## 📍 Note 필요) → gh issue edit ${issue_num} --body 로 .github/ISSUE_TEMPLATE/custom.md 양식에 맞춰 재작성"$'\n'
fi

if [ -n "$expected" ] && [ "$type_label" != "$expected" ]; then
jq -nc --arg url "$url" --arg actual "$type_label" --arg expected "$expected" --arg title "$title" --arg n "$issue_num" \
'{hookSpecificOutput: {hookEventName: "PostToolUse", additionalContext: ("Issue \($url) 의 type 라벨=\($actual) 가 title prefix 와 mismatch. Title: \"\($title)\" → expected=\($expected). 보정: gh issue edit \($n) --remove-label \($actual) --add-label \($expected). 의도적이면 issue 본문에 `<!-- type-override: \($actual) -->` 추가.")}}'
# ── 문제 있으면 한 번에 안내 ──────────────────────────────
if [ -n "$problems" ]; then
msg="Issue ${url} 보정 필요:"$'\n'"${problems}"
jq -nc --arg msg "$msg" \
'{hookSpecificOutput: {hookEventName: "PostToolUse", additionalContext: $msg}}'
fi

exit 0
1 change: 1 addition & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
root = true

[*.{kt,kts}]
ktlint_code_style = ktlint_official
ktlint_function_naming_ignore_when_annotated_with = Composable
ktlint_standard_package-name = disabled
6 changes: 6 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ jobs:
run: |
echo "$GOOGLE_SERVICES_JSON_B64" | base64 -d > app/google-services.json

# core/data 의 R.raw.service_account 컴파일 위해 stub raw 생성 (CI 런타임 미실행).
- name: Create stub service_account raw (CI only)
run: |
mkdir -p core/data/src/main/res/raw
printf '%s' '{"type":"service_account","project_id":"ci-stub"}' > core/data/src/main/res/raw/service_account

- name: Run Android Lint
run: ./gradlew lintDebug --continue --parallel

Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/screenshot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ jobs:
}
EOF

# VertexAIRepository 가 core/data 의 R.raw.service_account 를 참조 — 멀티모듈에서 raw 리소스가
# 없으면 R.raw 미생성으로 컴파일 실패. CI 는 실 키 불필요(런타임 미실행)라 stub raw 생성.
- name: Create stub service_account raw (CI only)
run: |
mkdir -p core/data/src/main/res/raw
printf '%s' '{"type":"service_account","project_id":"ci-stub"}' > core/data/src/main/res/raw/service_account

- name: Run validateScreenshotTest
run: ./gradlew :app:validateScreenshotTest --parallel

Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ jobs:
run: |
echo "$GOOGLE_SERVICES_JSON_B64" | base64 -d > app/google-services.json

# core/data 의 R.raw.service_account 컴파일 위해 stub raw 생성 (CI 런타임 미실행).
- name: Create stub service_account raw (CI only)
run: |
mkdir -p core/data/src/main/res/raw
printf '%s' '{"type":"service_account","project_id":"ci-stub"}' > core/data/src/main/res/raw/service_account

- name: Run unit tests
run: ./gradlew testDebugUnitTest --continue --parallel

Expand Down
14 changes: 13 additions & 1 deletion .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 16 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ plugins {
alias(libs.plugins.ktlint.gradle)
alias(libs.plugins.compose.screenshot)
alias(libs.plugins.firebase.app.distribution)
alias(libs.plugins.hilt)
alias(libs.plugins.ksp)
id("com.google.gms.google-services")
}

Expand Down Expand Up @@ -88,6 +90,17 @@ android {
}

dependencies {
implementation(project(":core:model"))
implementation(project(":core:common"))
implementation(project(":core:ui"))
implementation(project(":core:alarm"))
implementation(project(":core:data"))
implementation(project(":feature:main"))
implementation(project(":feature:addschedule"))
implementation(project(":feature:recommendation"))
implementation(project(":feature:information"))
implementation(project(":feature:profile"))
implementation(project(":feature:familygroup"))
implementation(platform("com.google.firebase:firebase-bom:33.14.0"))
implementation("com.google.firebase:firebase-analytics")
implementation(libs.androidx.core.ktx)
Expand Down Expand Up @@ -116,6 +129,9 @@ dependencies {
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.material.icons.extended)
implementation(libs.androidx.navigation.compose)
implementation(libs.hilt.android)
ksp(libs.hilt.compiler)
implementation(libs.androidx.hilt.navigation.compose)
implementation(libs.firebase.ui.auth)
implementation(libs.volley)
implementation(libs.androidx.material)
Expand All @@ -138,6 +154,5 @@ dependencies {
// ktlint (org.jlleitschuh.gradle.ktlint) — 버전을 lint.yml 의 ktlint_version 과 통일
ktlint {
version.set("1.8.0")
android.set(true)
ignoreFailures.set(false)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ class ExampleInstrumentedTest {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.slowclock", appContext.packageName)
}
}
}
47 changes: 27 additions & 20 deletions app/src/main/java/com/example/slowclock/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ import com.example.slowclock.ui.theme.SlowClockTheme
import com.firebase.ui.auth.AuthUI
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.messaging.FirebaseMessaging
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch


@AndroidEntryPoint
class MainActivity : ComponentActivity() {
private lateinit var authManager: AuthManager
private val dummyDataManager = DummyDataManager()
Expand Down Expand Up @@ -65,7 +66,7 @@ class MainActivity : ComponentActivity() {
},
onError = { error ->
Log.e("AUTH", "로그인 실패: $error")
}
},
)

// 로그인 상태 확인
Expand All @@ -87,7 +88,7 @@ class MainActivity : ComponentActivity() {
authManager.ensureShareCodeForUser(
currentUser.uid,
currentUser.displayName ?: "",
currentUser.email ?: ""
currentUser.email ?: "",
)
}
addDummyData()
Expand Down Expand Up @@ -128,17 +129,20 @@ class MainActivity : ComponentActivity() {
val descriptionText = "일정 시간에 울리는 알림"
val importance = NotificationManager.IMPORTANCE_HIGH
val soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM)
val audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build()

val channel = NotificationChannel("schedule_channel", name, importance).apply {
description = descriptionText
enableLights(true)
enableVibration(true)
setSound(soundUri, audioAttributes) // 🔊 사운드 설정 추가
}
val audioAttributes =
AudioAttributes
.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build()

val channel =
NotificationChannel("schedule_channel", name, importance).apply {
description = descriptionText
enableLights(true)
enableVibration(true)
setSound(soundUri, audioAttributes) // 🔊 사운드 설정 추가
}

val notificationManager: NotificationManager =
getSystemService(NOTIFICATION_SERVICE) as NotificationManager
Expand All @@ -148,12 +152,12 @@ class MainActivity : ComponentActivity() {
private fun requestNotificationPermission() {
if (ContextCompat.checkSelfPermission(
this,
android.Manifest.permission.POST_NOTIFICATIONS
android.Manifest.permission.POST_NOTIFICATIONS,
) != PackageManager.PERMISSION_GRANTED
) {
requestPermissions(
arrayOf(android.Manifest.permission.POST_NOTIFICATIONS),
1001
1001,
)
}
}
Expand All @@ -162,13 +166,16 @@ class MainActivity : ComponentActivity() {
val user = FirebaseAuth.getInstance().currentUser
if (user != null) {
FirebaseMessaging.getInstance().token.addOnSuccessListener { token ->
val db = com.google.firebase.firestore.FirebaseFirestore.getInstance()
db.collection("users").document(user.uid)
val db =
com.google.firebase.firestore.FirebaseFirestore
.getInstance()
db
.collection("users")
.document(user.uid)
.update("fcmToken", token)
.addOnSuccessListener { Log.d("FCM", "로그인 후 토큰 Firestore 저장 성공") }
.addOnFailureListener { e -> Log.e("FCM", "로그인 후 토큰 Firestore 저장 실패", e) }
}
}
}

}
}
12 changes: 8 additions & 4 deletions app/src/main/java/com/example/slowclock/SlowClockApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import android.app.Application
import com.google.firebase.FirebaseApp
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.firestore.FirebaseFirestoreSettings
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class SlowClockApplication : Application() {
override fun onCreate() {
super.onCreate()
Expand All @@ -14,9 +16,11 @@ class SlowClockApplication : Application() {

// Firestore 설정
val firestore = FirebaseFirestore.getInstance()
val settings = FirebaseFirestoreSettings.Builder()
.setPersistenceEnabled(true) // 오프라인 지원 활성화
.build()
val settings =
FirebaseFirestoreSettings
.Builder()
.setPersistenceEnabled(true) // 오프라인 지원 활성화
.build()
firestore.firestoreSettings = settings
}
}
}
Loading
Loading