# PRD — ear_android_app

Evidence date: 2026-10-16.

Source: `git@bitbucket.org:overdrive/ear_android_app.git`, `master` at `5c15b03` (2023-03-10, Jaco Brits, "eAr app POC").

There is no prior PRD in the source repository or in Silicon Ark. Requirements below are recovered from the Android project. Provenance is labelled. Nothing here is a new product decision.

## Product purpose

**Documented** only by the commit message "eAr app POC" and by the user-visible name `eAr` (`strings.xml`).

**Implemented.** On launch, the app shows a full-screen `WebView` and loads `https://www.ear-academy.com/home/login`.

**Inferred.** The app is a shell around that website's login page, not a native Ear Academy client. Login, lessons, purchases, and account rules live on the site. They are **not specified by this repository**.

**Unknown / Requires Confirmation.** Whether this shell was meant to ship, and whether it is still the mobile entry point for Ear Academy.

## Users and interactions

No account, role, or permission model exists in this repository.

| Actor | What they do | Evidence | Status |
|---|---|---|---|
| Person who launches the app | Taps the launcher icon labelled `eAr`. The system starts `MainActivity` | `MAIN` / `LAUNCHER` intent filter; `app_name` string | **Implemented** |
| `MainActivity` | Loads the login URL into the WebView | `MainActivity.onCreate` | **Implemented** |
| Person using the loaded page | Interacts with whatever `www.ear-academy.com` renders | Not in this repo | **Unknown** beyond the URL |
| Ear Academy website | Serves `/home/login` and any later pages the WebView opens | URL literal only | **Unknown / Requires Confirmation** what that page requires |
| Instrumented test | Asserts the package name is `com.example.ear` | `ExampleInstrumentedTest` | **Implemented** as a template test. It does not launch the WebView |

There is no admin actor, no guest-versus-member branch, and no in-app settings screen.

## Systems the product talks to

1. `https://www.ear-academy.com/home/login` over HTTPS, loaded by `WebView.loadUrl`. No request headers, cookies, or query parameters are set in code.
2. Google Maven and Maven Central, at build time only (`settings.gradle`).

No database, push service, analytics SDK, map SDK, or other host is named in the source.

The sibling Silicon Ark set `ear_lambdas` also names `https://www.ear-academy.com`, on admin cron paths. This app does not call those paths. Provenance of a shared product: **Inferred** from the hostname. **Requires Confirmation.**

## Major workflows

### WF-1 — Cold start to the login page

Trigger: the user launches the app (or the system delivers `android.intent.action.MAIN`).

Steps in `MainActivity.onCreate`:

1. `super.onCreate(savedInstanceState)`.
2. `setContentView(R.layout.activity_main)`.
3. `findViewById(R.id.webview)`.
4. `setWebViewClient(new WebViewClient())`.
5. `loadUrl("https://www.ear-academy.com/home/login")`.
6. `WebSettings.setJavaScriptEnabled(true)`.
7. `WebSettings.setDomStorageEnabled(true)`.

`savedInstanceState` is not read. There is no `onSaveInstanceState` override and no `WebView.restoreState`.

Expected behaviour that the code attempts: the WebView navigates to that URL, and later navigations that the default `WebViewClient` handles stay inside the WebView.

Outputs: a full-screen WebView. No local success or error view.

Validation: none in the app. The URL is a compile-time literal.

Error conditions present in the source:

- The manifest contains no `<uses-permission>`. `android.permission.INTERNET` is absent. On Android, an app without that permission cannot open network sockets. Provenance: **Implemented** (the permission is not declared). The resulting WebView error page was not executed in this review. **Requires Confirmation** on a device.
- No `WebViewClient.onReceivedError`, `onReceivedHttpError`, or `onReceivedSslError` override. A failed load has no app-defined message.
- JavaScript and DOM storage are enabled only after `loadUrl`. Whether the first response runs with those flags is **Unknown / Requires Confirmation** (the source order is **Implemented**).

### WF-2 — Follow a link inside the WebView

Trigger: the loaded page navigates.

**Implemented:** a `WebViewClient` is installed, with no overrides. For `http` and `https`, the platform default is to load the URL in the same WebView.

**Unknown / Requires Confirmation:** what happens for non-http schemes (`mailto:`, `tel:`, intent URLs, file downloads). No `shouldOverrideUrlLoading`, `DownloadListener`, or `WebChromeClient` is set.

The app does not restrict the WebView to `www.ear-academy.com`. Any navigation the page starts can leave that host. Provenance: **Inferred** from the absence of a host allow-list.

### WF-3 — Leave the page

Trigger: the system back control.

**Implemented:** `MainActivity` does not override `onBackPressed` and does not register an `OnBackPressedCallback`. It does not call `WebView.goBack`.

**Inferred:** back finishes the activity instead of walking WebView history. **Requires Confirmation** on a device, because the platform back behaviour also depends on the Android version and predictive-back settings, which are not configured here (`targetSdk` 33, no `android:enableOnBackInvokedCallback` in the manifest).

### WF-4 — Configuration change

Trigger: rotation or another configuration change that destroys the activity. The manifest does not set `android:configChanges` on the activity.

**Implemented:** the new `onCreate` always calls `loadUrl` with the login URL and ignores `savedInstanceState`.

**Inferred:** in-page navigation and form input are discarded, and the login URL is loaded again.

## Functional requirements

### FR-001 — Launcher entry

The system shows a launcher icon labelled `eAr` and starts `MainActivity` when that icon is used.

- Actor: the person launching the app.
- Trigger: `MAIN` / `LAUNCHER`.
- Inputs: none from the user beyond the launch.
- Behaviour: one exported activity, `.MainActivity`.
- Output: `MainActivity` is the task root.
- Validation: none.
- Errors: none defined.
- Evidence: `AndroidManifest.xml`, `strings.xml`.
- Implementation: **Implemented**.
- Confidence: High.

### FR-002 — Full-screen web content

The activity content is a `WebView` that matches the parent width and height.

- Actor: `MainActivity`.
- Trigger: `setContentView`.
- Inputs: `R.layout.activity_main`.
- Behaviour: a `RelativeLayout` whose only child is `@+id/webview`.
- Output: the WebView fills the screen. No toolbar, nav bar, or loading indicator is in the layout.
- Validation: none.
- Errors: if `findViewById(R.id.webview)` returned null, the next call would throw. The layout does define that id.
- Evidence: `activity_main.xml`, `MainActivity.java`.
- Implementation: **Implemented**.
- Confidence: High.

### FR-003 — Open the Ear Academy login URL

On every create of `MainActivity`, the WebView loads `https://www.ear-academy.com/home/login`.

- Actor: `MainActivity`.
- Trigger: `onCreate`.
- Inputs: the string literal in source. Not a build field, not an intent extra, not a preference.
- Behaviour: `loadUrl` with that exact string. Scheme is `https`. Path is `/home/login`. No fragment and no query.
- Output: an HTTP request performed by the WebView, if the platform allows it.
- Validation: the app does not check the URL, the certificate, or the HTTP status.
- Errors: see WF-1. Network permission is not declared.
- Evidence: `MainActivity.java` line that calls `loadUrl`.
- Implementation: **Implemented**.
- Confidence: High for the literal. The page contract is **Unknown**.

### FR-004 — Keep web navigations in the app

A `WebViewClient` is attached before the load, so the WebView handles its own URL loading.

- Actor: `MainActivity`.
- Trigger: `onCreate`, before `loadUrl`.
- Inputs: `new WebViewClient()` with no subclass.
- Behaviour: no app code overrides URL handling, page-finished, or errors.
- Output: default WebViewClient behaviour.
- Validation: none.
- Errors: none defined by the app.
- Evidence: `MainActivity.java`.
- Implementation: **Implemented**.
- Confidence: High that the client is the stock class. Confidence in every default edge (downloads, custom schemes) is Low. See WF-2.

### FR-005 — JavaScript

JavaScript is enabled on the WebView settings object.

- Actor: `MainActivity`.
- Trigger: `onCreate`, after `loadUrl`.
- Inputs: `true`.
- Behaviour: `setJavaScriptEnabled(true)`. The app does not call `addJavascriptInterface`. The ProGuard file contains only the commented Android Studio sample for a JavaScript interface. That sample is not active (`minifyEnabled false`, and the keep rule is commented).
- Output: the WebView is allowed to run JavaScript, subject to the timing note in WF-1.
- Validation: none.
- Errors: none defined.
- Evidence: `MainActivity.java`, `app/proguard-rules.pro`.
- Implementation: **Implemented**.
- Confidence: High for the call. Medium for whether the first document sees the flag.

### FR-006 — DOM storage

DOM storage is enabled on the WebView settings object.

- Actor: `MainActivity`.
- Trigger: `onCreate`, after `loadUrl`, immediately after JavaScript is enabled.
- Inputs: `true`.
- Behaviour: `setDomStorageEnabled(true)`. No other `WebSettings` method is called (no cache mode, no user-agent override, no mixed-content mode, no geolocation, no file access, no safe browsing override).
- Output: DOM storage is permitted, subject to the same timing note as FR-005.
- Validation: none.
- Errors: none defined.
- Evidence: `MainActivity.java`.
- Implementation: **Implemented**.
- Confidence: High for the call.

### FR-007 — Application identity

The installable application is identified as follows.

| Field | Value | Evidence |
|---|---|---|
| Launcher label | `eAr` | `strings.xml` `app_name` |
| `applicationId` / package | `com.example.ear` | `app/build.gradle` `defaultConfig`; namespace the same |
| `versionName` | `1.0` | `defaultConfig` |
| `versionCode` | `1` | `defaultConfig` |
| Theme | `Theme.EAr` | manifest `android:theme`; styles in `themes.xml` |
| min / target / compile SDK | 21 / 33 / 33 | `app/build.gradle` |

- Implementation: **Implemented**.
- Confidence: High.
- **Requires Confirmation:** whether `com.example.ear` and version `1.0` were placeholders.

### FR-008 — Web history back

The app does not implement back-through-WebView-history.

- Actor: the person pressing the system back control.
- Trigger: back.
- Inputs: none read from the WebView.
- Behaviour: no `goBack`, no `canGoBack`.
- Output: platform handling of an activity with no back callback. See WF-3.
- Evidence: `MainActivity.java` has only `onCreate`.
- Implementation: **Not implemented** (no history back).
- Confidence: High that the code is absent. Medium for the exact system gesture result.

### FR-009 — Load and session recovery

The app does not restore WebView state and does not configure cookies.

- Actor: Android, on process or activity recreate; the WebView cookie store, on a later launch.
- Trigger: configuration change, process death, or a second cold start.
- Inputs: `savedInstanceState` is ignored. `CookieManager` is never referenced.
- Behaviour: each `onCreate` loads the login URL again. Cookie persistence is whatever the platform WebView does by default. The app neither clears nor persists cookies itself.
- Output: a new load of `/home/login`.
- Evidence: `MainActivity.java`.
- Implementation: **Implemented** that state is ignored. Cookie policy is **Inferred** as the platform default.
- **Unknown / Requires Confirmation:** whether a logged-in website session should survive, and whether it does on a real device.

### FR-010 — Native account features

The app does not implement registration, login forms, logout, profiles, roles, or purchases.

- Evidence: the only screen is the WebView. No other Java class exists under `app/src/main/java`.
- Implementation: **Not implemented** in this repository.
- The remote page may implement those. That behaviour is **Unknown** here.

### FR-011 — Network permission

The manifest does not declare `INTERNET` or `ACCESS_NETWORK_STATE`.

- Actor: the package manager at install time.
- Trigger: installation of the APK produced from this manifest.
- Behaviour: no `<uses-permission>` elements exist.
- Output: the app is not granted the normal internet permission by the manifest.
- Errors: WebView loads of a remote URL are expected to fail without it. Not executed in this review.
- Evidence: `AndroidManifest.xml`.
- Implementation: **Implemented** as an omission.
- Confidence: High that the declaration is absent. High that stock Android requires the permission for outbound traffic.
- **Requires Confirmation:** whether a local manifest outside Git added the permission before any install.

### FR-012 — Tests shipped with the POC

The repository contains the two Android Studio template tests and no test of the URL or the WebView.

- `ExampleUnitTest.addition_isCorrect` asserts `2 + 2 == 4` on the JVM.
- `ExampleInstrumentedTest.useAppContext` asserts the target context package name is `com.example.ear`.
- Implementation: **Implemented**.
- Confidence: High.
- These tests do not verify FR-003 through FR-006.

## Integrations

| System | Direction | Trigger | Data | Auth in this repo | Failure behaviour |
|---|---|---|---|---|---|
| `https://www.ear-academy.com/home/login` | Outbound from the device | `MainActivity.onCreate` | HTTPS GET of that URL via WebView. No headers set in app code | None in app code. The page's own login is outside this repo | No app handler. See FR-011 |
| Google / Maven Central | Build machine only | Gradle dependency resolution | AARs listed in `app/build.gradle` | None | Build fails if the repositories are unreachable. Not executed in this review |

## Constraints

- **Implemented:** Java source compatibility 1.8 (`compileOptions`). IDE `misc.xml` records `languageLevel` JDK_11 and an Embedded JDK. IDE `compiler.xml` sets bytecode target 11. All three are in the same commit. The app class uses no Java 11 syntax. Gradle remains the build that defines the shipped bytecode when the wrapper is used.
- **Implemented:** `minSdk` 21 (Android 5.0). `targetSdk` and `compileSdk` 33.
- **Implemented:** `android.useAndroidX=true` and `android.nonTransitiveRClass=true`.
- **Implemented:** release `minifyEnabled false`. ProGuard rules file is the untouched template.
- **Implemented:** `android:allowBackup="true"`, with `backup_rules.xml` and `data_extraction_rules.xml` still the sample files (includes and excludes commented out). `tools:targetApi="31"` is set on `<application>` while `targetSdk` is 33.
- **Implemented:** `android:supportsRtl="true"`. No RTL-specific layouts.
- **Implemented:** `android:exported="true"` on the launcher activity.
- **Not in the repo:** network security config, certificate pinning, backup encryption choice, Play App Signing, or a privacy policy URL.
- **Unknown / Requires Confirmation:** whether the remote site supports the WebView user agent on API 21 through 33.

## Edge cases

| Case | What the code does | Provenance |
|---|---|---|
| No network permission | Permission not in the manifest | **Implemented** omission |
| Page HTTP error or TLS failure | No override; WebView default | **Implemented** absence of a handler |
| Airplane mode / DNS failure | Same | **Implemented** absence of a handler |
| User rotates the device | Activity recreated; login URL loaded again | **Implemented** call; **Inferred** data loss |
| User presses back after in-site navigation | No `goBack` | **Implemented** absence; effect **Inferred** |
| Link leaves `ear-academy.com` | No allow-list | **Inferred** |
| Second launch after a website login | Cookies not managed by the app | **Inferred** platform default; product intent **Unknown** |
| JavaScript `alert` / file input / camera / geolocation | No `WebChromeClient`, no permissions | **Implemented** absence. Those prompts are **not supported** by app code |
| Process death | No state save | **Implemented** |
| Duplicate launch of the activity | Standard launch mode (not set, so `standard`). No `singleTask` | **Implemented**. A second instance is possible if something else starts the activity. The only intent filter is the launcher |
| Empty or missing WebView id | Would NPE. The id is present | **Implemented** layout matches the code |
| Release shrinker stripping WebView glue | `minifyEnabled false` | **Implemented** |

## Discrepancies

1. Task-creation listing included `sonar-project.properties` and `sonarqube_scan.bat`. Git at `5c15b03` does not.
2. `.idea/gradle.xml` sets `gradleHome` to `$PROJECT_DIR$/../../../../Gradle/gradle-6.9.3` and `distributionType` `DEFAULT_WRAPPED`. The wrapper file `gradle-wrapper.properties` requests Gradle `7.5`. A wrapped build uses 7.5. An IDE build that honoured `gradleHome` would use 6.9.3. Which one produced any historical APK is **Unknown**.
3. `.idea/misc.xml` language level is JDK_11 and `.idea/compiler.xml` bytecode target is 11. `app/build.gradle` `compileOptions` are Java 1.8.
4. `constraintlayout` and, for widgets, `material` are on the classpath. The layout does not use them. The theme parent does use Material Components.
5. Commit message says POC. `versionName` is already `1.0`. Those two statements are both in the tree. Which one describes the intended release state **Requires Confirmation**.

## Not established

- Contents of the login page, and which account actions it offers.
- Whether the site allows embedding in a WebView (cookies, `X-Frame` is irrelevant for a top-level WebView load; other bot or user-agent rules might exist). **Unknown**.
- Store listing, signing certificate, and distribution channel.
- Scan-host Sonar file contents.
- Any requirement that conflicts with this behaviour, because no earlier PRD exists.
