32 Commits

Author SHA1 Message Date
a140b56aec TASK-075: chip border, quiet short loads, exit question, back to top
A selected filter chip is outlined; the loading cup waits 450 ms before it
shows; Back on 홈 asks before leaving; long lists offer a button back to the
top; AnimatedContent no longer animates its own size, which made sideways
moves drift diagonally.

Agent: main (opus), implementer (sonnet) for Chips.kt and ExitConfirmDialog.kt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:29:41 +09:00
bb546421fc TASK-069: bottom tabs glide past the tabs between
The four tab screens are pages of one pager that a tab press scrolls, so 홈
to 알림 passes 저장소 and 작업 instead of sliding straight there. Swiping is
off; entering and leaving other screens is unchanged.

Agent: main (opus)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:21:42 +09:00
a28feb1a47 TASK-069: the first screen fades in under the cup instead of gliding
Until the session is known the route shown is the repository list; when it
becomes Home the change played the full-width tab glide just as the launch
cover was fading. That hand-over is now a plain fade, and the cover waits for
it (at most 1.5 s longer) before it leaves.

Agent: main (opus)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:16:17 +09:00
7d41e89c99 TASK-073: the app fetches and installs its own update, without a file
"업데이트" streams the published APK into a system install session instead of
opening the browser, so nothing is saved to the phone. Progress, cancel and
retry are shown. Android 12+ is asked to skip its confirmation, which it does
once this app is the installer of the copy being replaced.

Adds REQUEST_INSTALL_PACKAGES and UPDATE_PACKAGES_WITHOUT_USER_ACTION.
Not run on a device.

Agent: main (opus)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:51:08 +09:00
7368b27c65 TASK-069: bottom tab switches glide the whole width
Agent: main (opus)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:28:44 +09:00
a9f08ebbdf TASK-070: update check and download read the team's Gitea
The old download page asked for a password and is no longer used. The
app now reads version.json and fetches UBGitea.apk from the apk branch.

Agent: main (opus)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:01:33 +09:00
11bb082f0f TASK-069: in-app motion built on the tea cup
Splash, cup loader, list skeleton, empty-screen cup, pull to refresh
indicator, star and tab icon spring, screen and tab transitions, and a
row by row diff intro. Compose animation APIs only.

Agent: main (opus)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 13:34:42 +09:00
69556b1e23 TASK-066: download page and in-app update check
- version 0.2.0 (versionCode 2)
- The app reads version.json from the download page once at launch and
  offers the page when a newer build is out; settings shows the version
  and a check button.
- tools/publish-download.py encrypts an APK under the download password
  and writes the page that decrypts it in the browser.

Agent: main (opus)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:52:45 +09:00
54530096e0 TASK-065: code tabs, comparing two files, and reading aids
- Up to five code files saved as tabs in a strip above the bottom tabs.
- Compare a file with one from another ref, commit or place; the
  current file goes left or right, then the other is chosen.
- The side pane on a wide screen is dragged to size and snaps back to
  its default; the header/source split snaps to the middle.
- A tapped name is marked everywhere in the file, with previous/next.
- The Repositories tab pressed again goes back to the list page the
  repository came from, then to the first page.

Agent: main (opus)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:38:55 +09:00
5cc11d8420 TASK-057: side pane cards, in-place folder tree, unfolded Home headline
- The viewer's side pane is two cards: folder structure, and classes and
  functions. A button swaps them and the choice is kept.
- Folders open in place; files inside them open from the tree.
- Unfolded Home: the headline is a card and the columns are equal.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 15:44:54 +09:00
9046985ba4 TASK-055: first device feedback on the redesign
Repository names get a line to themselves; the repository, work and notification tabs lose their title; a today count on Home opens today's activity of that kind; the outline is a foldable tree of classes and methods without fields; Home can show only starred repositories and remembers the choice; settings has a light/dark/system theme choice.

Whether the app is dark is now the app's decision (UBGiteaTheme.isDark), so the logo moved off a -night drawable. 420 unit tests pass, lint reports 0 errors. Installed on the product owner's Z Fold7.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 15:09:39 +09:00
d1a45abc16 TASK-050..054: tabs, Home, Work, code viewer, C++ pairs, fold layouts
Four tabs in an app shell that becomes a rail from 600dp, a brighter sky-blue palette with the real UbiSam logo, a Home tab built on Gitea's activity feeds, a Work tab that searches issues and pull requests across repositories, a full-screen code viewer with search, outline and Visual Studio colors, C/C++ header/source pairing, and two-pane layouts for wide windows.

The five tasks were built and checked together, so they are one commit: MainActivity, the routes and the shared components belong to all of them. 409 unit tests pass, lint reports 0 errors. Verified on an emulator up to the sign-in screen only; no physical device was available.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 14:35:00 +09:00
9a8f855e5e TASK-038: Settings screen with opt-in reactive auth retry and favorite-push toggle
Two features gated behind a new Settings screen so neither silently
changes existing behavior:

- Reactive auth retry (opt-in, default off): a live 401 despite
  proactive refresh already judging the token fine now optionally
  retries once with a forcibly-refreshed token before falling back to
  re-login. ReactiveTokenProvider is a separate interface from the
  existing fun interface BearerTokenProvider (not an addition to it),
  so every existing test fake keeps compiling; a new shared
  callAuthenticated helper (used by all four Gitea API classes instead
  of four duplicated retry implementations) checks for it and falls
  back to the old single-attempt behavior otherwise.
- Favorite push notifications (on/off, default on): TASK-036's
  background checker can now be turned off entirely, closing that
  task's own 'no in-app way to disable this' limitation.

Every new constructor parameter defaults to preserving old behavior
exactly, so no existing call site or test needed to change.

Live-verified: Settings screen opens from a new button, both switches
toggle and persist across navigation, no crash.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-29 18:11:12 +09:00
18e59d960c TASK-036 follow-up: deep-link the favorite-push notification
Tapping the notification now opens the app directly at that
repository's file browser on its default branch, instead of just the
app's normal entry point. The push-detection step only tracks one
scalar 'latest push time' per repository, not which branch produced
it, so the deep link targets the repository's default branch rather
than the specific pushed branch -- still a correct, useful 'take me to
this repository' action.

ListFavoritedRepositoriesUseCase now returns full RepositorySummary
(not just coordinates) so defaultBranch is available; MainActivity
bridges the notification's PendingIntent extras into its existing
Route/routeStack navigation via a mutableStateOf Activity property read
by a LaunchedEffect, mirroring the existing OAuth-callback consumption
pattern.

Live-verified via a simulated notification-tap intent (adb shell am
start with the exact extras FavoritePushNotifier sets): opened directly
at the target repository's file browser on the requested branch,
screenshot-confirmed, no crash.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-29 17:26:23 +09:00
b1a00233b0 TASK-036: push notification when a favorited repository gets a new push
Gitea's own notification feed doesn't fire for plain pushes (only
issue/PR activity), so this app now periodically checks each favorited
repository's most recent push time itself and posts a local
notification when it advances past what was last seen.

- FavoritePushDetector: pure comparison logic, never notifies on a
  repository's first sighting (only a real advance counts), avoiding a
  notification storm the first time this feature ever runs.
- GetLatestPushEpochMillisUseCase: extracts the same 'newest branch
  commit time' heuristic RepositoryListViewModel already uses for the
  repository list's own last-push row, as a shared, reusable use case.
- SharedPreferencesLastSeenPushStore: one long per favorited repo, not
  Room (docs/architecture.md reserves Room for offline caching).
- FavoritePushCheckWorker: 15-minute WorkManager periodic job,
  network-constrained, no-ops cleanly with no session or no favorites.
- POST_NOTIFICATIONS requested at runtime on API 33+; a denial
  degrades safely, the worker still runs and updates its baseline.

No deep link from the notification to the specific repository yet --
tapping it opens the app normally. End-to-end delivery (a real push
landing, 15 minutes elapsing) is unit-tested and the scheduling/
permission wiring is live-confirmed (prompt appeared, was granted, no
crash), but an actual notification firing hasn't been observed within
this same live-testing session.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-29 17:15:35 +09:00
f693dbdb5b TASK-030: Gitea-green theme, pagination footer height fix, repo language/star
Replaces Compose Material3's unthemed default (purple) with a real color
scheme built from Gitea's own brand green, verified against Gitea's source
(--color-logo: #609926). Fixes a live-reported bug where pagination footer
button heights visibly varied by pinning one explicit height on every
button across all four screens' footers, and by sharing one
SimplePaginationFooter for the plain previous/next case. Each repository
row now shows its Gitea-reported primary language and a tappable star
toggle wired to the existing toggleFavorite action.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 23:10:59 +09:00
2818dab09a TASK-027/028: image+CSV viewer with gallery swipe, numbered Issues/PR pagination
TASK-027: opening an image (PNG/JPEG/GIF/BMP/WebP, detected by magic bytes)
now decodes and shows it, with swipe navigation to the previous/next image
in the same folder; opening a .csv file shows a scrollable table instead of
raw text. Both reuse the tab-swipe/content-scroll gesture-conflict fix so
neither ever triggers the Files/History tab to change underneath it.

TASK-028: the Issues/PR list shows up to 5 numbered page buttons plus a
[ page / total ] jump field, backed by Gitea's X-Total-Count header
(verified against Gitea's own source) and a server-side type=issues filter
so the count excludes pull requests. Every other paginated list is
unchanged, per this session's earlier scoping decision.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 21:50:24 +09:00
e219016998 TASK-026: run OAuth login/consent in a Custom Tab instead of the external browser
Login previously left the app entirely for a separate browser task. Now uses
androidx.browser Custom Tabs, which overlays the app's own activity instead of
switching away, and returns automatically on redirect. Deliberately not a
WebView, per RFC 8252: an in-app WebView could read the Gitea login form's
credentials directly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 20:27:23 +09:00
8cf7acd9ac TASK-025: tabbed branch detail screen, repo commit time, swipeable tabs, and live-testing follow-up fixes
- Branch/tag tap opens one screen with swipeable Files/History tabs (Route.BranchDetail replaces Route.CodeBrowser/Route.History)
- Repository rows show last-commit time; repository All/Favorites tabs are swipeable
- Fix overlapping commit-detail layout (BoxWithConstraints sibling stacking)
- Fix BranchDetailScreen tab row jammed against status bar
- Fix diff view horizontal scroll not working
- Simplify references-loading message; remove in-app back buttons in favor of system back gesture; replace manual refresh buttons with pull-to-refresh

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 01:39:17 +09:00
4b3c11d849 Add TASK-024: history screen redesign
Live, iterative round of changes driven by the human product owner
testing on their own device over wireless adb:
- Tapping a branch/tag opens commit history instead of the file browser
- Selected reference row is color-coded instead of a text label
- Branch/tag rows and commit rows show commit date/time with time zone
- Tapping a changed file shows a parsed before/after diff, client-side
  (new UnifiedDiffParser, no extra network call)
- The two diff panels scroll vertically in sync and share one
  horizontal scroll position
- Diff lines are colored by text only (red/green)
- On a >=600dp-wide screen (a Z Fold's inner screen unfolded) the file
  list and selected diff show side by side
- Removed the branch/tag compare feature entirely, per direct request

Also fixes a real bug found via live device debugging: getCommitDetail
requested the wrong URL path (missing "git/"), causing every commit
tap to 404.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 00:32:42 +09:00
5778dc9b2a fix: recover safely from OAuth callback failures 2026-08-26 18:41:17 +09:00
f14be3ac90 Add TASK-015: read-only Gitea notifications
Signed-in users can view notifications (unread-only or all), paginated,
and jump from an issue/PR notification to that repository's work items.
No mark-read/unread/pinned write endpoint is exposed, matching the
existing single-write-scope posture from ADR-004.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 15:55:19 +09:00
4fe3bf1c9f Implement TASK-014: organizations and repository org filter
Adds read-only GET /user/orgs and GET /orgs/{org}/repos to
GiteaRepositoryBrowserApi (a third interface, OrganizationsRepository),
reusing existing repository mapping rather than duplicating it.

Repository list gains an organization filter chip row (all + one per
org), keeping the existing recent-push sort and paging. Organizations
are fetched once per session and cached; a failure to load them
degrades to an empty filter row instead of blocking the repository
list itself. Filtering only applies to the all-repositories mode,
since Gitea star is not organization-scoped.

Closes MVP outcome #2 in docs/product.md. No task file existed
beforehand; created it first per AGENTS.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 15:34:22 +09:00
7756ec4d7e Implement TASK-013: typed application navigation
Replace MainActivity's three independent booleans (browsingCode/
browsingHistory/browsingWorkItems) with one typed List<Route> back
stack, restorable through configuration change and process death.

Route holds only stable primitives (owner/name/ref/ref-kind), never
tokens or content. A genuine LIFO stack, not just a refactor: back now
correctly unwinds through arbitrarily nested screens instead of
relying on which boolean happened to still be true underneath another.

No task file existed for this task beforehand (only proposed in an
earlier handoff document); created it first per AGENTS.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 15:19:56 +09:00
c397fbeea9 Implement TASK-008: pull-request and issue read-only UI
Separate issue and PR list/detail tabs, paginated, with comments
(issues) and reviews+comments (PRs). Deleted/unavailable authors
render a fixed placeholder instead of crashing or inventing identity;
every status is a text label, never color-only.

Reuses TASK-010 directly; no shared/domain file touched. Both list
types dedupe by number per page. Entry point is a new button on the
repository references screen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 15:08:26 +09:00
429668bf26 Implement TASK-007: commit history and change viewer UI
Paginated commit history for the current ref, commit detail with
distinguished added/modified/deleted/renamed file status, bounded and
explicitly-truncated patch text, and a ref comparison flow validating
one entered ref against the currently open ref as base.

Reuses TASK-004's listCommits and this session's own TASK-009
HistoryRepository; no shared/domain file touched. Entry point is a new
history button on the existing code browser header.

Self-review caught and fixed a bug before writing tests: a failed
commit-detail fetch discarded which commit had failed, so retry
silently reloaded the whole list instead of re-fetching the commit the
user tried to open.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 14:56:43 +09:00
525a0aa2ff Add Favorites tab backed by Gitea star (first write endpoint)
TASK-018, human product owner priority-1 request. Adds write:user
OAuth scope (ADR-004) and relaxes OAuthClientConfig's read-only guard
to an explicit, hard-coded write-scope allow-list rather than a
general write-scope allowance. GiteaRepositoryBrowserApi gains star/
unstar/listStarred via a new RepositoryFavorites interface, reusing
its existing repository mapping. RepositoryListViewModel tracks an
ALL/FAVORITES mode; toggling favorite confirms against the server
before updating UI state rather than acting optimistically.

No create/edit/merge/close/comment endpoint is introduced. Favorite
state is never cached locally.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 13:51:04 +09:00
93b8d587ee Sort repositories by recent push, page the list, show last-pushed branch
TASK-017, human product owner priority-1 request. Repository list now
requests sort=updated&order=desc from Gitea, loads 5 repositories per
page with explicit prev/next navigation, and each visible row
independently fetches and shows its most recently committed branch.

Removes the legacy eager-loading RepositoryListApi in favor of the
existing paged GiteaRepositoryBrowser.listRepositories. Fixes two
issues found in self-review: missing error handling on page
navigation, and repositories with no default branch (empty repos)
previously failing the whole page instead of just that row.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 13:24:31 +09:00
16c46a5c54 feat: browse repository source by ref 2026-08-26 10:35:20 +09:00
bbbff77a5e feat: browse repository branches and tags 2026-08-26 10:24:19 +09:00
a4451e9d16 feat: add Gitea login and repository list 2026-08-25 23:21:47 +09:00
75aa5f83e5 Bootstrap Android Compose application 2026-08-25 21:42:50 +09:00