Skip to content

Add Simplified Chinese README, link from the language switcher#5

Merged
jumincho merged 10 commits into
mainfrom
claude/bold-tesla-egd2T
May 28, 2026
Merged

Add Simplified Chinese README, link from the language switcher#5
jumincho merged 10 commits into
mainfrom
claude/bold-tesla-egd2T

Conversation

@jumincho
Copy link
Copy Markdown
Owner

Summary

Add a Simplified Chinese README and wire it into the language switcher.

  • README.md: KO/EN switcher line now also surfaces [中文](./README.zh-CN.md). Otherwise unchanged.
  • README.zh-CN.md (new): Simplified Chinese mirror of the same structure (overview · features · screens · stack · layout · secrets · build · materials · license).

Code intentionally left alone

The Android source here is already a clean reverse-engineered shape:

  • Package: com.jumincho.beatingyesterday (not com.example.*).
  • data/ (FoodCalorieApi, Note, NoteAdapter, NoteDatabase) + ui/ (ProfileSetupActivity, home/, diet/, productivity/CircularTimerView) sub-packages — proper data-layer / UI-layer separation.
  • build.gradle already reads FOOD_API_KEY from local.properties (with env-var fallback) and throws a clear GradleException at configure-time if it's missing — better than silently building with an empty key that fails later at runtime.
  • Java class names follow PascalCase + the Activity / View / Adapter / Database suffix where appropriate.

So this PR is README-only.

Suggested GitHub repo metadata (please apply manually in Settings)

GitHub's MCP server has no update_repository tool, so the following can't be done from this PR — but here are the values I'd suggest:

  • Repo name: keep as beating-yesterday (already evocative).
  • About (one-liner): Android self-improvement app that frames each day as a contest with yesterday's calorie + study record (Korea Food Safety OpenAPI, BMI-aware scoring).
  • Topics: android, java, self-improvement, bmi, food-safety-korea, openapi, sqlite, material-design, fragment, viewmodel

Test plan

  • README renders with 中文 link working.
  • README.zh-CN.md renders correctly on GitHub.
  • No source files touched — ./gradlew assembleDebug should be unaffected.

Generated by Claude Code

jumincho added 8 commits May 28, 2026 13:13
The code structure here is already clean — proper `com.jumincho.*`
package, `data/` and `ui/` sub-packages, `ProfileSetupActivity` /
`CircularTimerView` named per Java conventions, `local.properties` +
fail-fast key validation in build.gradle. README.md just gains a
`中文` switcher link. README.zh-CN.md is a fresh Simplified Chinese
README mirroring the same structure (overview, features, screens,
stack, layout, secrets, build, materials, license).
20/25 were inline magic numbers; 1/2/3 were ad-hoc bmiLevel codes.
Named constants make the BMI bucketing readable and link it to
WeightManagementFragment, which dispatches on the same levels.
…onstants

WeightManagementFragment was branching on `HomeViewModel.bmiLevel == 1/2/3`
even though HomeViewModel already exposes BMI_LEVEL_UNDERWEIGHT/NORMAL/OVERWEIGHT
constants. Use those, and pull the 2700/2000 daily-calorie reference values
into STD_KCAL_MALE / STD_KCAL_FEMALE so the scoring baseline is greppable.
Also adds a null guard for getActivity() in the navigation listener and the
SharedPreferences write so a detached fragment doesn't NPE.
…to constants

The base URL was being assembled with the API key inline and the service id
("I2790"), response format ("json"), and paging window ("1/5") inlined as
string literals; the timeout values, the localized success message check,
and the kcal field name were also inline magic values. Pull them into named
constants so the upstream contract is documented in one place.

Also drops a TODO(modernize) at the top noting that the raw
HttpURLConnection + JSONObject + bare Thread pattern is the obvious
candidate for a Retrofit + Gson + structured-threading rewrite — that's
deliberately left for a follow-up because it would touch the build script
and the DietInputFragment call site.
…ners

Fragment#getActivity() is documented to be nullable (during detach /
configuration change), and the unchecked downcast to MainActivity followed
by a method dispatch would NPE if a click event raced with detach. Add a
plain `if (activity == null) return;` early-out at the top of each
listener. Also switches ProductivityFragment to reuse the already-resolved
`activity` reference instead of calling getActivity() twice for the Intent
context.
DATABASE_NAME, TABLE_NOTE, and DATABASE_VERSION are read across the
codebase as if they were compile-time constants but were declared
public-static-mutable. Any caller could have reassigned the table name
mid-run and silently broken every subsequent query. Mark them `final`.
The handleMessage switch was branching on the raw integers 1/2/11/12
with the convention "1 series = countdown, 11 series = stopwatch"
spread across three separate methods. Renaming them to
MSG_TIMER_TICK / MSG_TIMER_DONE / MSG_STOPWATCH_TICK / MSG_STOPWATCH_DONE
documents that mapping in-place. Same for the tick interval (100ms)
and the initial delay (1000ms) that were inline at the Timer.schedule
call sites, and for the hour/minute/second clock-domain maxima used by
isMaxTime() and the setStartTime() guard.

Also drops a TODO(modernize) at the Handler block noting that the
Timer+TimerTask+Handler pattern would be a CountDownTimer or a
lifecycle-scoped coroutine flow in a modern Android build, but that
rewriting it would touch TimerActivity and the listener interfaces and
would need new dependencies — out of scope for the file-level pass.
The profile-setup flow is a six-state machine (name → gender → age →
height → weight → done) but the dispatch was a chain of
`questionFlag == 0/1/2/3/4` branches with no in-file documentation of
what each number meant. Pull the state values into QUESTION_NAME …
QUESTION_DONE and switch the chain to use them.

Also pulls the "UserData" SharedPreferences container name and the six
column keys ("Flag" / "Name" / "Gender" / "Age" / "Height" / "Weight")
into private static finals so any typo in the (string-keyed) load/save
path is now a compile error instead of a silent default-value read.
Copy link
Copy Markdown
Owner Author

Follow-up file-level inspection — per-file changes

After the initial README + HomeViewModel BMI-constants commit, a deeper bottom-up read of every file under app/src/main/java/com/jumincho/beatingyesterday/ surfaced more cleanups. Each follow-up is its own commit grouped by concern so the PR stays bisectable.

fe1ed06 — BMI level magic numbers, daily-kcal baselines, fragment null-guards

  • app/src/main/java/com/jumincho/beatingyesterday/ui/diet/WeightManagementFragment.java
    • Switched HomeViewModel.bmiLevel == 1/2/3 to use BMI_LEVEL_UNDERWEIGHT / NORMAL / OVERWEIGHT (defined in HomeViewModel earlier in this PR but not yet consumed).
    • Pulled the gendered daily-calorie baselines 2700 / 2000 into STD_KCAL_MALE / STD_KCAL_FEMALE with a comment noting the 2020 KDRI source.
    • Added a if (activity == null) return; guard in the click listener and wrapped the Today_kcalScore SharedPreferences write so a fragment that's been detached during this onCreateView won't NPE.

37140031FoodCalorieApi URL fragments, timeouts, response checks

  • app/src/main/java/com/jumincho/beatingyesterday/data/FoodCalorieApi.java
    • Pulled the API host, service id (I2790), response format, paging window, kcal field name (NUTR_CONT1), and the localized success message (정상처리되었습니다.) into named private static final constants.
    • Named the two 10000-ms timeouts as CONNECT_TIMEOUT_MS / READ_TIMEOUT_MS.
    • Hardened the success check to optString("MSG", "") so a malformed response can no longer throw inside the success path.
    • Added a class-level // TODO(modernize): flagging that the raw HttpURLConnection + JSONObject + bare Thread pattern is the obvious candidate for a Retrofit + Gson + structured-threading rewrite — left for a follow-up because it would touch the build script and the DietInputFragment call site.

97ab43e5 — Null-guard (MainActivity) getActivity() casts across fragments

  • app/src/main/java/com/jumincho/beatingyesterday/ui/diet/DietFragment.java
  • app/src/main/java/com/jumincho/beatingyesterday/ui/diet/DietInputFragment.java
  • app/src/main/java/com/jumincho/beatingyesterday/ui/productivity/ProductivityFragment.java
    • Fragment#getActivity() is documented to be nullable. Each fragment's click listener was doing an unchecked downcast to MainActivity and then calling a method on it — an NPE waiting for a click that races with detach. Added a plain if (activity == null) return; early-out at the top of each listener. ProductivityFragment also stops calling getActivity() twice (once for the cast, once for the Intent context) and reuses the resolved reference.

43a95271NoteDatabase schema constants final

  • app/src/main/java/com/jumincho/beatingyesterday/data/NoteDatabase.java
    • DATABASE_NAME / TABLE_NOTE / DATABASE_VERSION were public static (mutable) but read everywhere as if they were compile-time constants. Marked them final so a caller can't accidentally reassign the table name mid-run.

cde54594CircularTimerView Handler message codes + modernize TODO

  • app/src/main/java/com/jumincho/beatingyesterday/ui/productivity/CircularTimerView.java
    • handleMessage's switch was branching on the raw integers 1 / 2 / 11 / 12 with the convention "1-series = countdown, 11-series = stopwatch" implicit in three separate methods. Renamed to MSG_TIMER_TICK / MSG_TIMER_DONE / MSG_STOPWATCH_TICK / MSG_STOPWATCH_DONE so all three call sites read the same way.
    • Pulled the 100ms tick period and the 1000ms initial delay into TICK_INTERVAL_MS / TIMER_INITIAL_DELAY_MS.
    • Pulled the 5 / 59 / 59 hour-minute-second clock-domain maxima (used by isMaxTime() and the setStartTime() guard) into MAX_HOUR / MAX_MINUTE / MAX_SECOND.
    • Added a // TODO(modernize): at the Handler block noting that this would be a CountDownTimer or a lifecycle-scoped coroutine flow in a modern Android build, but rewriting it would touch TimerActivity and the listener interfaces and would need new dependencies — out of scope for the file-level pass.

120159caProfileSetupActivity question-state machine + SharedPreferences keys

  • app/src/main/java/com/jumincho/beatingyesterday/ui/ProfileSetupActivity.java
    • The setup flow is a 6-state machine (name → gender → age → height → weight → done) but the dispatch was a chain of questionFlag == 0/1/2/3/4 branches with no in-file documentation of which number meant which question. Named them QUESTION_NAMEQUESTION_DONE.
    • Pulled the "UserData" SharedPreferences container and the six column keys ("Flag" / "Name" / "Gender" / "Age" / "Height" / "Weight") into private static final strings so a typo on the (string-keyed) load/save path is a compile error instead of a silent default-value read.

Out of scope / intentionally not touched

  • Note.java's _id field (Android BaseColumns._ID = "_id" SQL-column convention — not arbitrary inconsistency).
  • The Integer.valueOf(displayNumberHour) != … pattern inside CircularTimerView#getDisplayNumber (autoboxing comparison is suspicious but rewriting it changes behavior on the hot draw path — deferred).
  • MainActivity's "UserData" SharedPreferences key duplication (covered by the ProfileSetupActivity constant — promoting to a shared Prefs helper is a follow-up because it touches HomeFragment and would need its own commit).
  • Threading rewrites (FoodCalorieApi Thread, CircularTimerView Handler+Timer): tagged with // TODO(modernize): per instructions, not rewritten in this PR.

Generated by Claude Code


Generated by Claude Code

@jumincho jumincho merged commit d09372b into main May 28, 2026
1 check passed
@jumincho jumincho deleted the claude/bold-tesla-egd2T branch May 29, 2026 21:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant