TASK-039: design pass — app icon, visibility badge, header truncation, PR label

- Add a custom adaptive launcher icon (background/foreground/monochrome
  layers), replacing Android's bare default. Original hand-drawn git-branch
  glyph on the app's own established primary green, not a reproduction of
  Gitea's logo.
- Add a compact VisibilityBadge for the repository list's public/private
  label, replacing an unstyled full-width text line.
- Truncate the references screen's repository-name header to 2 lines with
  an ellipsis instead of letting a long owner/name wrap to 3 lines and push
  the branch/tag list down.
- Change "Pull Request" -> "PR" in strings_workitems.xml and
  strings_notifications.xml, matching the "PR" abbreviation already used
  everywhere else in the app and fixing a visible line-wrap on the live
  notifications list.

Build/test/lint green, live-verified on device (light and dark mode).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
최민영 2026-08-29 18:42:51 +09:00
parent 9a8f855e5e
commit 9663d9ab9c
11 changed files with 304 additions and 8 deletions

View File

@ -7,6 +7,8 @@
<application
android:allowBackup="false"
android:dataExtractionRules="@xml/data_extraction_rules"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.UBGitea">

View File

@ -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)) }

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- App icon background layer. This app had no custom launcher icon at all until now (Android's
own default was showing), so this is a solid fill in the same green already used as this
app's Material 3 primary color (ui/theme/Color.kt's GreenPrimaryLight), so the home-screen
icon and the in-app theme read as the same deliberate color, not two different greens. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3B6A1F"
android:pathData="M0,0h108v108h-108z" />
</vector>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- A simple git-branch glyph (trunk node, a straight line up to a main-branch node, a curved line
out to a second branch node), reading immediately as "a git tool" without reproducing Gitea's
own trademarked logo. Every coordinate stays within the adaptive-icon safe zone (the centered
66dp-diameter circle on this 108dp canvas), since the system mask crops outside it on some
launchers. This app has no icon library (see CLAUDE.md's established convention), so this is
hand-drawn geometry, circles for nodes and stroked paths for the connecting branch lines,
matching the same plain-vector approach already used for the notification small icon. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:pathData="M40,78 L40,30"
android:strokeColor="#FFFFFFFF"
android:strokeWidth="6"
android:strokeLineCap="round"
android:fillColor="#00000000" />
<path
android:pathData="M40,78 C40,60 74,64 74,46"
android:strokeColor="#FFFFFFFF"
android:strokeWidth="6"
android:strokeLineCap="round"
android:fillColor="#00000000" />
<path
android:fillColor="#FFFFFFFF"
android:pathData="M33,78 A7,7 0 1,0 47,78 A7,7 0 1,0 33,78 Z" />
<path
android:fillColor="#FFFFFFFF"
android:pathData="M33,30 A7,7 0 1,0 47,30 A7,7 0 1,0 33,30 Z" />
<path
android:fillColor="#FFFFFFFF"
android:pathData="M67,46 A7,7 0 1,0 81,46 A7,7 0 1,0 67,46 Z" />
</vector>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Android 13+ themed-icon layer. The system re-tints this to match the device wallpaper theme,
so (like the notification small icon) only the shape/alpha matters, not this fill color.
Same glyph as ic_launcher_foreground.xml. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:pathData="M40,78 L40,30"
android:strokeColor="#FF000000"
android:strokeWidth="6"
android:strokeLineCap="round"
android:fillColor="#00000000" />
<path
android:pathData="M40,78 C40,60 74,64 74,46"
android:strokeColor="#FF000000"
android:strokeWidth="6"
android:strokeLineCap="round"
android:fillColor="#00000000" />
<path
android:fillColor="#FF000000"
android:pathData="M33,78 A7,7 0 1,0 47,78 A7,7 0 1,0 33,78 Z" />
<path
android:fillColor="#FF000000"
android:pathData="M33,30 A7,7 0 1,0 47,30 A7,7 0 1,0 33,30 Z" />
<path
android:fillColor="#FF000000"
android:pathData="M67,46 A7,7 0 1,0 81,46 A7,7 0 1,0 67,46 Z" />
</vector>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_monochrome" />
</adaptive-icon>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_monochrome" />
</adaptive-icon>

View File

@ -12,7 +12,7 @@
<string name="notifications_page_number">%1$d페이지</string>
<string name="notifications_repository">%1$s/%2$s</string>
<string name="notifications_subject_issue">이슈</string>
<string name="notifications_subject_pull_request">Pull Request</string>
<string name="notifications_subject_pull_request">PR</string>
<string name="notifications_subject_commit">커밋</string>
<string name="notifications_subject_repository">저장소</string>
<string name="notifications_subject_other">기타</string>

View File

@ -1,6 +1,6 @@
<resources>
<string name="workitems_tab_issues">이슈</string>
<string name="workitems_tab_pull_requests">Pull Request</string>
<string name="workitems_tab_pull_requests">PR</string>
<string name="workitems_empty">표시할 항목이 없습니다.</string>
<string name="workitems_retry">다시 시도</string>
<string name="workitems_sign_in_again">다시 로그인</string>

View File

@ -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.

View File

@ -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.