An open API service indexing awesome lists of open source software.

https://github.com/tarek-bohdima/asteroidradar

Offline-first Android app tracking Near Earth Objects via NASA's NeoWs API, with daily WorkManager refresh and Astronomy Picture of the Day.
https://github.com/tarek-bohdima/asteroidradar

android coil coroutines-android fragment kotlin kotlin-android ksp livedata moshi mvvm-android nasa-api navigation-component near-earth-objects offline-first recyclerview retrofit2 room-database udacity-android-nanodegree workmanager-kotlin

Last synced: 5 days ago
JSON representation

Offline-first Android app tracking Near Earth Objects via NASA's NeoWs API, with daily WorkManager refresh and Astronomy Picture of the Day.

Awesome Lists containing this project

README

          

# Asteroid Radar

[![Android CI](https://github.com/Tarek-Bohdima/AsteroidRadar/actions/workflows/build_pull_request.yml/badge.svg)](https://github.com/Tarek-Bohdima/AsteroidRadar/actions/workflows/build_pull_request.yml)
[![Kotlin](https://img.shields.io/badge/Kotlin-1.6.21-blueviolet?logo=kotlin)](#)
[![minSdk](https://img.shields.io/badge/minSdk-26-brightgreen)](#)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](#license)

Asteroid Radar tracks Near Earth Objects (NEOs) using NASA's
[Asteroids NeoWs](https://api.nasa.gov/) feed and shows the
[Astronomy Picture of the Day](https://api.nasa.gov/) on launch.
Asteroids close-approaching Earth in the next seven days are pulled in the
background once a day, cached locally, and filtered by today / week / all stored.
Available on Google Play (internal track).

## Features

- **NeoWs feed** — close-approaching asteroids for the next seven days, with
potentially-hazardous flagging.
- **APOD** — NASA's Astronomy Picture of the Day on the main screen.
- **Offline-first** — Room is the source of truth; the UI reads from the DB,
the network path only writes.
- **Daily background refresh** via WorkManager (constraints: unmetered network
+ charging + battery-not-low + device-idle).
- **Filters** — sealed `AsteroidsFilter` (`TODAY` / `WEEK` / `STORED`) maps to
one DAO query each.

## Tech stack

| Layer | Choice |
|---|---|
| Language | Kotlin |
| UI | Fragments + Data Binding + Navigation Component (safe-args) |
| Async | Kotlin Coroutines |
| Networking | Retrofit + Moshi + OkHttp; Scalars and Moshi converter factories registered in order so `String` returns get the raw body and `@JsonClass`-annotated returns get the codegen-generated adapter |
| Persistence | Room (compiled with KSP) |
| Background | WorkManager (`PeriodicWorkRequest`, KEEP policy) |
| Image loading | Coil |
| Logging | Timber |

Architecture is a standard offline-first repo pattern (`domain` / `network` /
`database` / `repository` / `ui` / `work`). See [`CLAUDE.md`](CLAUDE.md) for
the load-bearing details and module-by-module orientation.

## Build and run

Requires JDK 17 and the Android SDK. Common commands:

```bash
./gradlew assembleDebug # debug APK
./gradlew assembleRelease # signed release APK (needs keystore env vars)
./gradlew bundleRelease # signed AAB for Play Store
./gradlew test # all unit tests
./gradlew connectedAndroidTest # instrumented tests (needs device/emulator)
```

Single-test run:

```bash
./gradlew :app:testDebugUnitTest --tests "com.tarek.asteroidradar.ExampleUnitTest"
```

## Configuration

`app/build.gradle` reads secrets from environment variables first, falling back
to `local.properties`. For local development add to `local.properties`:

```properties
NASA_API_KEY=your_nasa_api_key # https://api.nasa.gov/ → "Generate API Key"
# release-signing only — needed for assembleRelease / bundleRelease
KEYSTORE_PATH=/absolute/path/to/keystore.jks
KEYSTORE_PASSWORD=...
KEY_ALIAS=...
KEY_PASSWORD=...
```

The keystore must be **PKCS12** — `signingConfigs.release.storeType` is hard-coded
to `"PKCS12"` in `app/build.gradle`.

### Firebase Crashlytics (Phase 15b)

Release builds ship structured logs and crash reports to Firebase Crashlytics
via the typed-event logging pattern documented in
[`docs/patterns/structured-logging.md`](docs/patterns/structured-logging.md).
The Firebase plugins are applied **conditionally** on the presence of
`app/google-services.json`, so debug builds keep working for anyone who hasn't
set up Firebase yet — only `assembleRelease` / `bundleRelease` / `packageRelease`
require it (the build script fails fast if it's missing).

To set up locally:

1. Create a Firebase project at .
2. Add an Android app with package name `com.tarek.asteroidradar` (must match
`applicationId`).
3. Download the generated `google-services.json` and place it at
`app/google-services.json`. The file is gitignored — never commit it.

For CI, add the file's base64-encoded contents as the
`GOOGLE_SERVICES_JSON_BASE64` GitHub Secret; the release workflow decodes it
into place before building.

The debug build type never wires the Crashlytics sink (`LoggerReleaseModule`
lives in `app/src/release/`), so no Crashlytics traffic is generated during
local development.

## Release flow

Releases are tag-driven. Push a tag matching `v*` and
[`.github/workflows/release.yml`](.github/workflows/release.yml) takes over:

1. Validates that `vMAJOR.MINOR.PATCH` matches `versionMajor/Minor/Patch` in
`app/build.gradle` — mismatch fails the workflow before building.
2. Runs unit tests, then builds and signs both the APK (attached to the GitHub
Release for sideloading) and the AAB (uploaded as a workflow artifact for
manual upload to Play Console).
3. Tags whose name contains `INTERNAL`, `alpha`, `beta`, `rc`, or `RC` are
auto-flagged as pre-release.

Required GitHub Secrets: `NASA_API_KEY`, `KEYSTORE_BASE64` (the PKCS12 keystore
base64-encoded), `KEYSTORE_PASSWORD`, `KEY_ALIAS`, `KEY_PASSWORD`,
`GOOGLE_SERVICES_JSON_BASE64` (the Firebase config file, base64-encoded — see
"Firebase Crashlytics" above).

Tag/version conventions: SemVer with an optional classifier suffix that maps to
a Play Store track:

| Classifier | Play track |
|---|---|
| `-INTERNAL` | Internal testing |
| `-ALPHA` | Closed alpha |
| `-BETA` | Open beta |
| `-RC` | Production rollout candidate |
| (none) / `-RELEASE` | Production |

## Roadmap

The phased modernization plan lives in
[`docs/IMPROVEMENT_PLAN.md`](docs/IMPROVEMENT_PLAN.md) (Gradle Kotlin DSL +
version catalog → convention plugin → code-quality tooling → toolchain bump →
Hilt → R8 → tests + Kover → edge-to-edge → eventual Compose migration).

## License

Released under the MIT License — every source file ships with the full block in
its header. See any `.kt` or `.gradle` file (e.g. [`build.gradle`](build.gradle))
for the canonical text.