diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 523a70a..097c688 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,8 @@ diff --git a/app/src/main/java/com/ubisam/ubgitea/feature/repositories/RepositoryListScreen.kt b/app/src/main/java/com/ubisam/ubgitea/feature/repositories/RepositoryListScreen.kt index 19ab8ee..d8775d0 100644 --- a/app/src/main/java/com/ubisam/ubgitea/feature/repositories/RepositoryListScreen.kt +++ b/app/src/main/java/com/ubisam/ubgitea/feature/repositories/RepositoryListScreen.kt @@ -15,6 +15,7 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.selection.selectable import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api @@ -24,6 +25,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.pulltorefresh.PullToRefreshBox import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface import androidx.compose.material3.Tab import androidx.compose.material3.TabRow import androidx.compose.material3.Text @@ -35,6 +37,7 @@ import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.semantics.Role +import androidx.compose.ui.text.style.TextOverflow import androidx.activity.compose.BackHandler import com.ubisam.ubgitea.R import com.ubisam.ubgitea.domain.organizations.OrganizationSummary @@ -255,11 +258,7 @@ private fun RepositoryContent( .padding(horizontal = 16.dp, vertical = 12.dp), ) { Text("${repository.owner}/${repository.name}", style = MaterialTheme.typography.titleMedium) - Text( - stringResource( - if (repository.isPrivate) R.string.repositories_private else R.string.repositories_public, - ), - ) + VisibilityBadge(repository.isPrivate, Modifier.padding(top = 4.dp, bottom = 2.dp)) repository.description?.let { Text(it, style = MaterialTheme.typography.bodyMedium) } Text( repository.defaultBranch?.let { @@ -305,6 +304,31 @@ private fun RepositoryContent( internal fun repositoryLazyListKey(coordinates: RepositoryCoordinates): String = "${coordinates.owner}/${coordinates.name}" +// A compact colored badge instead of a full-width plain-text line (which previously had no +// explicit style at all, so it rendered at LocalTextStyle's default size -- larger and more +// visually prominent than the metadata lines around it, an unintentional inconsistency). Private +// gets a neutral surface tint (matches Gitea web's own muted treatment for private repos); public +// gets the theme's primary-container green, since a publicly-visible repository is the more +// "affirmative" state worth a touch of accent color. +@Composable +private fun VisibilityBadge(isPrivate: Boolean, modifier: Modifier = Modifier) { + val label = stringResource(if (isPrivate) R.string.repositories_private else R.string.repositories_public) + val containerColor = if (isPrivate) MaterialTheme.colorScheme.surfaceVariant else MaterialTheme.colorScheme.primaryContainer + val contentColor = if (isPrivate) MaterialTheme.colorScheme.onSurfaceVariant else MaterialTheme.colorScheme.onPrimaryContainer + Surface( + color = containerColor, + contentColor = contentColor, + shape = RoundedCornerShape(4.dp), + modifier = modifier, + ) { + Text( + label, + style = MaterialTheme.typography.labelSmall, + modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp), + ) + } +} + // detectHorizontalDragGestures only claims a gesture once its motion resolves as predominantly // horizontal, so this coexists with LazyColumn's own vertical scroll rather than fighting it. private fun Modifier.modeSwipeGesture( @@ -446,7 +470,12 @@ private fun ReferencesContent( verticalAlignment = Alignment.CenterVertically, ) { Column(Modifier.weight(1f)) { - Text("${state.repository.owner}/${state.repository.name}", style = MaterialTheme.typography.titleLarge) + Text( + "${state.repository.owner}/${state.repository.name}", + style = MaterialTheme.typography.titleLarge, + maxLines = 2, + overflow = TextOverflow.Ellipsis, + ) Text(stringResource(R.string.repositories_account, state.accountName)) } OutlinedButton(onClick = onLogout) { Text(stringResource(R.string.repositories_logout)) } diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..1200e13 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..ac4f04c --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,35 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_monochrome.xml b/app/src/main/res/drawable/ic_launcher_monochrome.xml new file mode 100644 index 0000000..ead32bb --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_monochrome.xml @@ -0,0 +1,31 @@ + + + + + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..8fde456 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..8fde456 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/values/strings_notifications.xml b/app/src/main/res/values/strings_notifications.xml index a5d67d2..9433ee0 100644 --- a/app/src/main/res/values/strings_notifications.xml +++ b/app/src/main/res/values/strings_notifications.xml @@ -12,7 +12,7 @@ %1$d페이지 %1$s/%2$s 이슈 - Pull Request + PR 커밋 저장소 기타 diff --git a/app/src/main/res/values/strings_workitems.xml b/app/src/main/res/values/strings_workitems.xml index 122060e..2856ba7 100644 --- a/app/src/main/res/values/strings_workitems.xml +++ b/app/src/main/res/values/strings_workitems.xml @@ -1,6 +1,6 @@ 이슈 - Pull Request + PR 표시할 항목이 없습니다. 다시 시도 다시 로그인 diff --git a/docs/development-log.md b/docs/development-log.md index faa286b..46919be 100644 --- a/docs/development-log.md +++ b/docs/development-log.md @@ -686,3 +686,48 @@ This is the append-only implementation and handoff record shared by Codex and Cl - Known limitations: reactive auth retry's actual live effect (recovering from a genuine 401 without forcing re-login) hasn't been observed end-to-end, since that requires a rare, hard-to-trigger real revocation event — covered instead by direct unit tests of the retry orchestration. Favorite push notifications remain one app-wide toggle, not per-repository (deliberate scope decision, not an oversight). - Next recommended task: this closes the two explicitly-requested items from this "continue developing" stretch. Remaining work across the whole session is largely live-confirmation of already-built features that haven't been directly exercised yet (pinch-zoom/CSV/PDF viewers, Issues/PR numbered pagination with real data, the actual favorite-push notification firing end-to-end). - Next owner: Claude, continuing per the standing direction. + +## 2026-08-29 (continued) — TASK-039: Design pass — app icon, visibility badge, header truncation, PR-label Korean-ization + +- Agent and role: Claude, direct continuation of the same live session. The human product owner asked + for an open-ended design review ("디자인 수정해줄래? 앱의 아이콘부터 해서 전체적으로 확인해보고 + 부족한 부분 고쳐줘") after all previously-requested features were built. Proceeded autonomously, + screen by screen, per the standing direction. +- Commit/diff: on `claude/task-015-notifications`; new launcher-icon drawables and `mipmap-anydpi-v26` + wrappers, `AndroidManifest.xml`'s `android:icon`/`android:roundIcon`, `RepositoryListScreen.kt`'s + new `VisibilityBadge` composable and references-header title truncation, `strings_workitems.xml`/ + `strings_notifications.xml`'s `Pull Request` → `PR`, `tasks/039-design-pass-app-icon-and-visual-polish.md`. +- Feature implemented: (1) a custom adaptive app icon replacing Android's bare default (an original + green git-branch glyph matching the app's own primary color, not a reproduction of Gitea's logo); + (2) a compact styled badge for the repository-list visibility label, replacing an unstyled + full-width text line; (3) 2-line ellipsis truncation on the references screen's repository-name + header, which a real long-named repository was wrapping to 3 lines; (4) "Pull Request" → "PR" in + the two string resources that were the only English text left in an otherwise fully-Korean UI — + also the cause of a visible line-wrap on the live notifications list, since it was the widest word + in a `Row` with no wrap handling. +- How it was implemented: the icon uses only vector primitives (arc-path circles, stroked lines), no + icon library, matching this app's established convention. `VisibilityBadge` reuses the same + `Surface` + `RoundedCornerShape` + `labelSmall` construction already used for `FilterChip` + elsewhere on the same screen. "PR" was already this app's own convention in five other strings + (`workitems_pr_refs`, `workitems_permission_denied`, the "PR/이슈" button label, etc.) — only the + tab label and the notifications subject label had been left as the English full phrase. +- Files changed: see `tasks/039-design-pass-app-icon-and-visual-polish.md`'s Scope/Evidence sections. +- Checks and results: `./gradlew compileDebugKotlin testDebugUnitTest assembleDebug lintDebug` BUILD + SUCCESSFUL (run twice, once per round of changes), all unit tests passing, 0 lint errors. +- Live verification: installed on the human product owner's device over wireless `adb` after each + round. Icon confirmed via the Recents/task-switcher view. Badge and header truncation confirmed via + screenshot on a real long-named repository. PR-label fix confirmed via screenshot against the + notifications "전체" tab with real PR/issue data — every row now fits "PR" on one line alongside + its state label instead of wrapping. Dark mode forced via `adb shell cmd uimode night yes` and + reviewed for the repository list and references screens (no contrast issues found), then restored. +- Review findings and resolutions: None from a separate review pass. +- Known limitations: work-items/code-browser screens still don't show their own repository/branch + context in a header (needs ViewModel-layer changes, scoped out of this visual-polish batch); + dark-mode review only covered two of the app's screens so far; the code browser, commit + history/diff, and work-item/PR detail views with real content haven't been reviewed yet — this is + the first batch from an ongoing design-review pass, not a complete one. +- Next recommended task: continue the design review into the code browser (file tree, text/image/CSV/PDF + viewers), commit history/diff view, work-item/PR detail screens with real content, and extend the + dark-mode check to the remaining screens; separately, consider a small follow-up task to surface + repository/branch context in the work-items and code-browser headers. +- Next owner: Claude, continuing per the standing direction. diff --git a/tasks/039-design-pass-app-icon-and-visual-polish.md b/tasks/039-design-pass-app-icon-and-visual-polish.md new file mode 100644 index 0000000..d722df4 --- /dev/null +++ b/tasks/039-design-pass-app-icon-and-visual-polish.md @@ -0,0 +1,128 @@ +# TASK-039: Design pass — app icon, visibility badge, header truncation, PR-label Korean-ization + +Status: implemented; all required checks pass; live-confirmed on the human product owner's device +Owner: Claude +Reviewer: Codex + +## Outcome + +The human product owner asked for an open-ended design review: "디자인 수정해줄래? 앱의 아이콘부터 +해서 전체적으로 확인해보고 부족한 부분 고쳐줘" (fix the design — starting with the app icon, review +the whole app and fix what's lacking). This task covers the first batch of concrete gaps found and +fixed while screen-by-screen reviewing the app (repository list, references/branches, work items, +notifications, settings) in both light and dark mode: + +- **App icon**: the app had no custom launcher icon at all — Android's bare default was showing on + the home screen and app switcher. Added a full adaptive icon (background + foreground + monochrome + layers) with an original hand-drawn git-branch glyph on the app's own established primary green. +- **Repository visibility label**: the 공개/비공개 (public/private) text on each repository-list row + had no explicit style (rendered oversized/inconsistent next to the other metadata lines) and took a + full-width line. Replaced with a small rounded badge, matching the compact-chip look Gitea's own web + UI uses for the same information. +- **References screen header truncation**: a repository with a long owner/name combination (a real + one already in the human product owner's own Gitea instance) wrapped the title to 3 lines, pushing + the branch/tag list far down the screen. Capped it to 2 lines with an ellipsis. +- **"Pull Request" → "PR"**: two string resources (the work-items tab label, the notifications subject + label) were the only English text left in an otherwise fully-Korean UI, and were also the widest + word in a `Row` that doesn't wrap gracefully — on the live notifications list this visibly broke + "Pull Request" across two lines next to the state label. The rest of the app had already settled on + "PR" as the Korean-context abbreviation (`workitems_permission_denied`, `workitems_pr_refs`, etc.); + matched that. + +## Scope + +- Included: `mipmap-anydpi-v26/ic_launcher.xml`/`ic_launcher_round.xml` + their three drawable layers; + `AndroidManifest.xml`'s `android:icon`/`android:roundIcon`; `RepositoryListScreen.kt`'s new + `VisibilityBadge` composable and the references-header title's `maxLines`/`overflow`; + `strings_workitems.xml`/`strings_notifications.xml`'s `Pull Request` → `PR` string values. +- Excluded: no icon library was introduced (this app has never depended on + `androidx.compose.material.icons` — see Design notes); no change to `WorkItemsScreen.kt` to surface + which repository/branch a work-items or code-browser screen belongs to (a real gap noticed while + reviewing, but it needs new state threaded through the ViewModel/use-case layer, not just a view + change — flagged for a follow-up task rather than folded into this visual-polish batch); no explicit + in-app back button anywhere (deliberate, pre-existing, app-wide pattern — this device's own 3-button + system navigation already provides a visible back affordance, so this wasn't treated as a gap). + +## Design notes + +- **Original glyph, not Gitea's logo**: the launcher icon is a hand-drawn trunk-and-branch motif (a + straight line, a curved line, three circular nodes) built from plain vector primitives — deliberately + not a reproduction of Gitea's own trademarked logo. +- **Same green as the in-app theme**: the icon background reuses `GreenPrimaryLight` (`#3B6A1F`, + `ui/theme/Color.kt`) exactly, so the home-screen icon and the in-app Material 3 theme read as one + deliberate color choice rather than two unrelated greens. +- **Adaptive icon only, no legacy PNG mipmaps**: `minSdk` is 28 (> 26), so only the + `mipmap-anydpi-v26` adaptive-icon XML wrappers are needed; there is no pre-Android-8 device this app + can run on that would need a flat PNG fallback. +- **`VisibilityBadge` reuses the existing chip pattern**: a `Surface` + `RoundedCornerShape(4.dp)` + + `labelSmall` text, the same construction already used for `FilterChip` elsewhere in this screen — + not a new UI primitive, just the same small-badge shape applied to a second piece of metadata. +- **"PR" was already this app's own convention, not a new one**: `strings_workitems.xml` already used + "PR" in half a dozen other strings (`workitems_pr_refs`, `workitems_permission_denied`, the + "PR/이슈" button label on the repository-detail header) — only the tab label and the notifications + subject label had been left as the English "Pull Request". This was a one-line fix per file, not a + design decision made fresh here. + +## Acceptance criteria + +- [x] The app has a custom launcher icon (visible on the home screen and in Recents), not Android's + bare default. +- [x] The icon renders correctly as both a round and non-round adaptive icon shape. +- [x] Repository visibility (공개/비공개) renders as a small styled badge, not an unstyled full-width + text line. +- [x] A repository with a long owner/name combination shows a 2-line-max, ellipsized title on the + references screen instead of pushing the branch list down with a 3-line wrap. +- [x] No user-facing English text remains next to otherwise-Korean labels in the work-items tabs or + the notifications list. +- [x] Dark mode reviewed for the repository list and references screens: badges, selected-branch + highlighting, and text all stay legible with no contrast regressions. +- [x] Build, unit tests, and lint pass. +- [x] Live-confirmed on the human product owner's device for every change above. + +## Required checks + +```text +./gradlew compileDebugKotlin testDebugUnitTest assembleDebug lintDebug +``` + +## Evidence + +- `app/src/main/res/drawable/ic_launcher_background.xml`, `ic_launcher_foreground.xml`, + `ic_launcher_monochrome.xml`; `app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml`, + `ic_launcher_round.xml`; `AndroidManifest.xml`'s new `android:icon`/`android:roundIcon`. +- `feature/repositories/RepositoryListScreen.kt`: new private `VisibilityBadge` composable, used in + place of the old unstyled `Text` for `repositories_private`/`repositories_public`; the references + header's repository-name `Text` gained `maxLines = 2, overflow = TextOverflow.Ellipsis`. +- `res/values/strings_workitems.xml`: `workitems_tab_pull_requests` changed from `Pull Request` to + `PR`. `res/values/strings_notifications.xml`: `notifications_subject_pull_request` changed the same + way. +- `./gradlew compileDebugKotlin testDebugUnitTest assembleDebug lintDebug`: BUILD SUCCESSFUL, all + unit tests passing, 0 lint errors (run twice — once after the icon/badge/truncation changes, once + after the PR-label change). +- Live device: installed over wireless `adb` after each round. + - App icon: screenshot-confirmed rendering cleanly in the Recents/task-switcher view. + - `VisibilityBadge`: screenshot-confirmed on the repository list — compact rounded badges (gray for + 비공개, green-tinted for 공개) replacing the old full-width text line. + - Header truncation: screenshot-confirmed on `RobotAutomationDevelopment-Dept/LGE-OC6-Inline-CylinderTypeWelder-PRI-REPO` + (a real, already-long-named repository) — title now wraps to exactly 2 lines with a trailing + ellipsis instead of 3, and the branch/tag list starts noticeably higher on screen. + - PR label: screenshot-confirmed on the notifications "전체" tab against real data (a repository + with an actual PR/issue history) — every row now reads "PR" inline with "이슈"/"열림"/"닫힘"/"병합됨" + on one line instead of wrapping "Pull" and "Request" onto two. + - Dark mode: forced via `adb shell cmd uimode night yes`, screenshot-reviewed the repository list + and references screens, restored to `night no` afterward — no contrast or legibility issues found. + +## Known limitations + +- Work-items and code-browser screens still don't show which repository/branch they belong to in + their own header — noticed during this review but scoped out (see Scope) since it needs new state + threaded through the ViewModel/use-case layer, not a view-only change. +- Dark-mode review in this pass covered the repository list and references screens only, not work + items/notifications/settings/code-browser — those still need a dark-mode pass. +- The rest of the screen-by-screen review (code browser file tree and viewers, commit history/diff, + work-item and PR detail views with real content) has not yet happened — this task covers the first + batch of concrete fixes found so far, not a complete pass. + +## Review findings + +None yet — pending Codex/human review.