diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml new file mode 100644 index 0000000..72b3cee --- /dev/null +++ b/.github/workflows/verify.yml @@ -0,0 +1,50 @@ +name: Verify + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +jobs: + structure-and-secrets: + name: Structure & Secrets + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Required files + run: | + for f in README.md LICENSE app/build.gradle build.gradle settings.gradle gradle.properties; do + if [ ! -f "$f" ]; then + echo "::error::Missing required file: $f" + exit 1 + fi + done + + - name: No committed API keys (Food Safety OpenAPI uses base64-like service keys) + run: | + # Sanity check: no hardcoded service keys in source + if grep -rEn 'FOOD_API_KEY[[:space:]]*=[[:space:]]*"[A-Za-z0-9+/=]{20,}"' app/src/ 2>/dev/null; then + echo "::error::Hardcoded Food Safety API key in source" + exit 1 + fi + + - name: .gitignore covers known secret paths + run: | + if ! grep -Fq "local.properties" .gitignore; then + echo "::warning::local.properties should be in .gitignore" + fi + + - name: README links resolve + run: | + missing=0 + for link in $(grep -oE '\]\(\./[^)]+\)' README.md | sed 's|](\./||; s|)$||' | sort -u); do + if [ ! -e "$link" ]; then + echo "::warning::README links to missing path: $link" + missing=$((missing+1)) + fi + done + if [ "$missing" -gt 0 ]; then + echo "::warning::$missing README link(s) point to non-existent paths" + fi diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e5d8a55 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 jumincho + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 55235bf..0b8737d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,23 @@ -# Beating Yesterday (어제의 나 이기기) +
+
+## 라이선스
+
+[MIT License](./LICENSE)
+
+---
+
+
+
+## English
+
+> Android app that gamifies daily self-improvement against yesterday's record.
+
+Logs daily calorie intake and study time, then judges today's record against yesterday's.
+Calorie scoring is BMI-aware, so this is less a diet tracker than a personalized
+self-management tool that frames each day as a contest with yesterday's you.
+
+### Features
+
+- **User profile** — name, sex, age, height, weight → auto-computed BMI.
+- **Diet** — log breakfast / lunch / dinner; the Korean Food Safety OpenAPI returns calories, and a BMI-bracket-aware (under / normal / over) calorie score is computed.
+- **Study timer** — circular timer with drag-to-set hours / minutes / seconds, plus a stopwatch.
+- **TODO** — SQLite-backed list with swipe-to-refresh.
+- **Daily contest** — yesterday's calorie score + study time vs today's, reported as "did you beat yesterday?"
+
+### Screens
+
+Bottom navigation with three tabs:
+
+| Tab | Description |
+| --- | --- |
+| Diet | Food entry · weight management |
+| Home | Profile · daily yesterday-vs-today contest |
+| Productivity | TODO · study timer |
+
+### Tech stack
+
+- **Language**: Java
+- **Platform**: Android (minSdk 21 / targetSdk 32)
+- **Architecture**: Fragment + ViewModel
+- **Storage**: SharedPreferences (profile, daily records) + SQLite (TODO)
+- **Network**: HttpURLConnection + Korean Food Safety OpenAPI
+- **UI**: Material Components, ConstraintLayout, RecyclerView, Navigation Component
+
+### Layout
+
+```
+app/src/main/
+├── java/com/jumincho/beatingyesterday/
+│ ├── MainActivity.java
+│ ├── data/ # data layer
+│ │ ├── FoodCalorieApi.java # Food Safety API client
+│ │ ├── Note.java # TODO model
+│ │ ├── NoteAdapter.java
+│ │ └── NoteDatabase.java # SQLite helper
+│ └── ui/
+│ ├── ProfileSetupActivity.java # first-launch profile entry
+│ ├── home/ # home (contest result)
+│ ├── diet/ # diet / weight management
+│ └── productivity/ # TODO + timer
+│ ├── CircularTimerView.java # custom circular timer view
+│ └── TimerMode.java
+└── res/
+ ├── layout/ # screen layouts
+ ├── navigation/ # bottom-tab navigation graph
+ └── ...
+```
+
+### Secrets handling
+
+The build system reads the Food Safety OpenAPI key from `local.properties` and
+injects it via Gradle `buildConfigField` as `BuildConfig.FOOD_API_KEY`. No
+secrets are present in source. `local.properties` is gitignored.
+
+| `local.properties` key | `BuildConfig` field | Use site |
+| --- | --- | --- |
+| `FOOD_API_KEY` | `BuildConfig.FOOD_API_KEY` | Food Safety calorie lookup |
+
+### Build
+
+1. Get an API key from the [Korean Food Safety OpenAPI](https://various.foodsafetykorea.go.kr/nutrient/).
+2. Add a `local.properties` entry at the repo root:
+
+ ```properties
+ FOOD_API_KEY=your_key_here
+ ```
+
+3. Open the project in Android Studio → `Run`.
+
+```bash
+./gradlew assembleDebug
+```
+
+Requirements:
+- Android Studio Bumblebee+
+- JDK 8+
+- Android SDK 32
+
+### Materials
+
+- Demo video: