# Technical Documentation: plainhub/plain-app

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/plainhub/plain-app)

### Overview (`README.md`)

# PlainApp

**PlainApp** is a free, open-source Android app that turns your phone into a self-hosted management hub you can access from any web browser on the same network — no accounts, no cloud, no subscriptions. Whether you need to transfer files, read SMS, browse photos, or mirror your screen, PlainApp puts full control back in your hands.

[<img src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" alt='Get it on Google Play' height="80">](https://play.google.com/store/apps/details?id=com.ismartcoding.plain)
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt='Get it on F-Droid' height="80">](https://f-droid.org/packages/com.ismartcoding.plain/)
[<img src="https://raw.githubusercontent.com/plainhub/plain-app/main/assets/get-it-on-github.png" alt='Get it on GitHub' height="80">](https://github.com/plainhub/plain-app/releases/latest)

## Why PlainApp?

Most phone-management tools either route your data through their own servers, lock features behind subscriptions, or plague you with ads. PlainApp is different:

- **100% local** — your data never leaves your network
- **End-to-end encrypted** — every connection is secured with TLS + XChaCha20-Poly1305
- **Completely free and ad-free** — no purchases, no upsells, ever
- **Fully open-source** — audit the code, build your own binary, or contribute

## Features 

### 🌐 Web-Based Phone Management
Open a browser on any desktop or laptop connected to the same Wi-Fi network and get a full dashboard to manage your Android device — no app installation required on the computer side. Supports PWA so you can pin it to your desktop like a native app.

| Category | What you can do |
|---|---|
| **Files** | Browse internal storage, SD card, and USB drives; upload, download, rename, delete |
| **Media** | View and manage photos, videos, and audio; stream them directly in the browser |
| **Contacts** | View, search, and manage your contacts |
| **SMS & Calls** | Read messages and call logs; send SMS from your desktop |
| **Apps** | List installed apps and export APKs |
| **Notifications** | Mirror phone notifications to the browser in real time |
| **Screen Mirror** | Live-stream the phone screen to the browser with audio and remote control support |
| **Device Info** | Check battery, storage, network, and system details |

### 📱 Built-in Android Tools
PlainApp is also a capable standalone app — not just a server:

- **Markdown Notes** — write and organize notes with a clean editor
- **RSS Reader** — follow your favorite websites with a distraction-free reader
- **Video & Audio Player** — full-featured media player with playlist support
- **TV Cast** — cast media to any DLNA/Chromecast-compatible TV
- **Peer-to-Peer Chat & File Sharing** — share files and messages directly between devices without a server
- **Pomodoro Timer** — stay focused with a built-in work timer
- **Sound Meter** — measure ambient noise levels in real time

### 🔒 Privacy & Security
- Zero data sent to any third-party server
- All traffic between phone and browser is encrypted (TLS + XChaCha20-Poly1305)
- No account registration required

Video: https://www.youtube.com/watch?v=TjRhC8pSQ6Q

Reddit: https://www.reddit.com/r/plainapp

Discord: https://discord.gg/RQWcS6DEEe

QQ Group: 812409393

## How PlainApp Compares

| Feature | **PlainApp** | AirDroid | KDE Connect | AnExplorer | Pushbullet |
|---|:---:|:---:|:---:|:---:|:---:|
| Open source | ✅ | ❌ | ✅ | ✅ | ❌ |
| 100% local / no cloud required | ✅ | ⚠️ partial | ✅ | ✅ | ❌ |
| End-to-end encrypted (local) | ✅ | ⚠️ paid | ❌ | ❌ | ❌ |
| Ad-free | ✅ | ⚠️ paid | ✅ | ⚠️ paid | ⚠️ paid |
| Free all features | ✅ | ⚠️ freemium | ✅ | ⚠️ freemium | ⚠️ freemium |
| Web browser access (no desktop app needed) | ✅ | ✅ | ❌ | ❌ | ✅ |
| File manager (browser UI) | ✅ | ✅ | ⚠️ limited | ✅ | ❌ |
| SMS from desktop | ✅ | ✅ | ✅ | ❌ | ✅ |
| Screen mirroring | ✅ | ✅ | ✅ | ❌ | ❌ |
| Media streaming in browser | ✅ | ⚠️ limited | ❌ | ❌ | ❌ |
| Notification mirroring | ✅ | ✅ | ✅ | ❌ | ✅ |
| Built-in RSS reader | ✅ | ❌ | ❌ | ❌ | ❌ |
| Markdown notes | ✅ | ❌ | ❌ | ❌ | ❌ |
| P2P chat & file sharing | ✅ | ⚠️ paid | ❌ | ❌ | ⚠️ limited |
| TV casting (DLNA/Chromecast) | ✅ | ✅ | ❌ | ❌ | ❌ |
| PWA support | ✅ | ❌ | ❌ | ❌ | ❌ |
| No account required | ✅ | ❌ | ✅ | ✅ | ❌ |

> ✅ Fully supported &nbsp; ⚠️ Partially supported or requires paid plan &nbsp; ❌ Not supported

## Donations :heart:

**This project needs you!** If you would like to support this project's further development, the creator of this project or the continuous maintenance of this project, **feel free to donate**.

Your donation is highly appreciated. Thank you!

- [Buy me a cup of Ko-Fi!](https://ko-fi.com/ismartcoding)

- Use WeChat to scan the code

<img src="assets/donate-wechat.jpeg" width="200"/>

## Screenshots

### Android App

<table>
  <tr>
    <td><img src="screenshots/app-files.png" alt="files"/></td>
    <td><img src="screenshots/app-home.webp" alt="home"/></td>
    <td><img src="screenshots/app-music-player.jpg" alt="music player"/></td>
    <td><img src="screenshots/app-chat.png" alt="chat"/></td>
  </tr>
  <tr>
    <td><img src="screenshots/app-audio.png" alt="audio"/></td>
    <td><img src="screenshots/app-images.webp" alt="images"/></td>
    <td><img src="screenshots/app-videos.jpg" alt="videos"/></td>
    <td><img src="screenshots/app-rss.jpg" alt="rss"/></td>
  </tr>
</table>

### Web Dashboard

<table>
  <tr>
    <td colspan="2"><img src="screenshots/web-home.webp" alt="home" width="100%"/></td>
    <td colspan="2"><img src="screenshots/web-images.png" alt="images" width="100%"/></td>
  </tr>
  <tr>
    <td colspan="2"><img src="screenshots/web-videos.png" alt="videos" width="100%"/></td>
    <td colspan="2"><img src="screenshots/web-notes.png" alt="notes" width="100%"/></td>
  </tr>
  <tr>
    <td colspan="2"><img src="screenshots/web-files.png" alt="files" width="100%"/></td>
    <td colspan="2"><img src="screenshots/web-calls.png" alt="calls" width="100%"/></td>
  </tr>
  <tr>
    <td colspan="2"><img src="screenshots/web-messages.png" alt="messages" width="100%"/></td>
    <td colspan="2"><img src="screenshots/web-contacts.png" alt="contacts" width="100%"/></td>
  </tr>
  <tr>
    <td colspan="2"><img src="screenshots/web-audio.png" alt="audio" width="100%"/></td>
    <td colspan="2"><img src="screenshots/web-rss.png" alt="rss" width="100%"/></td>
  </tr>
  <tr>
    <td colspan="2"><img src="screenshots/web-encryption.png" alt="encryption" width="100%"/></td>
    <td colspan="2"><img src="screenshots/web-screen-mirror.png" alt="screen mirror" width="100%"/></td>
  </tr>
  <tr>
    <td colspan="2"><img src="screenshots/web-image.png" alt="image viewer" width="100%"/></td>
    <td colspan="2"><img src="screenshots/web-video.png" alt="video player" width="100%"/></td>
  </tr>
  <tr>
    <td colspan="2"><img src="screenshots/web-chat.png" alt="chat" width="100%"/></td>
    <td colspan="2"></td>
  </tr>
</table>

## Compatibility

PlainApp requires Android 9.0 or higher.

## FAQ

[https://plainapp.app/docs](https://plainapp.app/docs)

## Support

PlainApp is fully open-source.  
If you have any doubts, you're free to review the code, build your own version, or simply choose not to use it.

If you run into issues, feel free to open a GitHub issue — I'm always happy to help.

Please don’t leave a one-star review without giving it a fair try.

## Work on plain-core

Keep [plain-core](https://github.com/plainhub/plain-core) next to this repository. Run from this repository root.

```bash
./scripts/core local        # Use local plain-core
./scripts/core local --off  # Stop using local paths
./scripts/core update       # Lock this project to the latest remote commit
```

## Build

1. Generate `release.jks` file under `$rootProject/app` folder.

```bash
keytool -genkeypair -v -keystore ./app/release.jks -alias release -keyalg RSA  -keysize 4096 -validity 36500
```

2. Create `keystore.properties` file with following content under `$rootProject` folder.

```
storePassword=
keyPassword=
keyAlias=release
storeFile=release.jks
```

---

### Overview (`plain-common/README.md`)

# plain-common

Reusable Kotlin Multiplatform code extracted from PlainApp. Source packages are
kept unchanged under `com.ismartcoding.plain`.

The module includes common helpers, crypto, extensions, logcat,
and XML/RSS support. Supporting sources are included here so this module
has no dependency on the host application.

BLE protocol and native codec adapters belong to [plain-rs features](../../plain-desktop/plain-rs/README.md). Platform GATT adapters stay in the host shared module. This Kotlin library contains no BLE source.

## Maven coordinates

```kotlin
implementation("com.ismartcoding:plain-common:0.1.7")
```

Add the public GitHub Pages Maven repository to the consuming project's
repositories:

```kotlin
maven {
    url = uri("https://plainhub.github.io/plain-app/maven")
}
```

Then add the dependency shown above. Releases use tags named
`plain-common-v<version>`; the default local version can be changed with
`-PplainCommonVersion=…`.

## Publishing

The `Publish plain-common` GitHub Actions workflow builds the KMP publications
and adds the Maven files to the dedicated `maven` branch under `maven/` when a
`plain-common-v<version>` tag is pushed. The workflow also syncs `policy.html` and `terms.html` from main so their existing public URLs stay available. It can also be run manually with a
version number. The workflow uses its temporary `GITHUB_TOKEN`; no publishing
credentials are stored in the repository.

For a local Maven cache, run `./gradlew :plain-common:publishToMavenLocal`.

---

### Overview (`plain-ui/README.md`)

# plain-ui

Shared Compose Multiplatform components in `com.ismartcoding.plain.ui`.
This is the component selection entry point for PlainApp and PlainNet (plain-router-app).
Before editing UI, choose the scene below, open its compiled example, and read the actual component signature. Pages compose existing components and connect state and events.

| Scene | Use | Compiled example |
|---|---|---|
| Page and top bar | `PScaffold` + `PTopAppBar` | [ComponentShowcasePage](../shared/src/commonMain/kotlin/com/ismartcoding/plain/ui/settings/ComponentShowcasePage.kt) |
| Primary, outlined, text action | `PFilledButton`, `POutlinedButton`, `PTextButton`; size via `ButtonSize`, role via `ButtonType` | [Button states](../shared/src/commonMain/kotlin/com/ismartcoding/plain/ui/settings/ShowcaseComponentStates.kt) |
| Text input, password, validation | `PTextField`; choose `enabled` and `readOnly` independently | [Form states](../shared/src/commonMain/kotlin/com/ismartcoding/plain/ui/settings/ShowcaseComponentStates.kt) |
| Single choice between a few options | `PSegmentedButtons` | [Segmented buttons](../shared/src/commonMain/kotlin/com/ismartcoding/plain/ui/settings/ShowcaseSegmentedButtons.kt) |
| Cards, list rows, banners, filters, removable chips | `PCard`, `PListItem`, `PBanner`, `PFilterChip`, `PInputChip` | [Component showcase](../shared/src/commonMain/kotlin/com/ismartcoding/plain/ui/settings/ComponentShowcasePage.kt) |
| Bottom sheet | `PModalBottomSheet` + `PBottomSheetTopAppBar`; primary actions use `PSheetPrimaryActionsCard`, overflow uses `PSheetActionCard` | [FileInfoBottomSheet](../shared/src/commonMain/kotlin/com/ismartcoding/plain/ui/files/FileInfoBottomSheet.kt) |
| Confirmation and input dialog | Host `confirmActionAsync` / `DialogHelper`, `TextFieldDialog`, `RadioDialog`; selection rows use `PDialogRadioRow` | [DialogHelper](../shared/src/commonMain/kotlin/com/ismartcoding/plain/ui/components/DialogHelper.kt) |
| Icon with label below | `PIconTextSmallButton` for compact toolbar; `PIconTextActionButton` for shortcuts; `PSheetPrimaryAction` for sheet actions | [State showcase](../shared/src/commonMain/kotlin/com/ismartcoding/plain/ui/settings/ShowcaseComponentStates.kt) |
| Boolean / radio / simple icon control | Official Material3 `Switch`, `RadioButton`, `IconButton`; use host icon shortcuts when applicable | [Dark theme choices](../shared/src/commonMain/kotlin/com/ismartcoding/plain/ui/settings/DarkThemePage.kt) |
| Media seek, animated waveform, countdown | `PlayerSlider`, `WaveSlider`, `CircularTimer` | [PomodoroPage](../shared/src/commonMain/kotlin/com/ismartcoding/plain/ui/pomodoro/PomodoroPage.kt) |
| Large document, scan, drag selection, fast scroll, pull refresh | `CodeEditor`, `QrCodeScanner`, `gridDragSelect`, fastscroll and pullrefresh packages | [TextFilePage](../shared/src/commonMain/kotlin/com/ismartcoding/plain/ui/media/TextFilePage.kt) |

The first eight scenes require the existing project components. Basic layout (`Box`, `Row`, `Column`), text, images and controls in the official-control row can use Compose directly. Specialized editor, scanner and gesture engines keep their own behavior; inspect their contracts before extending them. Host dialog helpers use each application's resources and state, and stay outside this library.

Use `AppTheme` as the complete theme entry. Keep the brand palette, soft dark text and AMOLED behavior; dynamic color is excluded. Shapes come from the theme: `medium` for ordinary cards/inputs, `large` for prominent preview/banner containers, smaller roles for nested surfaces, and `CircleShape` for capsules/discs. Exact dynamic geometry belongs to its specialized implementation. Brand sizes are defined in [Shapes](src/commonMain/kotlin/com/ismartcoding/plain/ui/theme/Shapes.kt), not Material default sizes.

Public layout components accept a root `modifier`. Actions use `onClick`, `enabled` and `onDismissRequest`. Required actions have no default empty callback; optional actions are nullable and create no click target when absent. Each distinct public component has its own file; overloads of the same component may share it. Code comments use English and explain only non-obvious decisions.

Read parameter behavior in the source: `PScaffold` consumes horizontal insets and passes only top/bottom padding to content; `PTextField` renders its own error message; `PInputChip.onClose` determines whether the close target exists (`onClick = null` makes the whole chip a remove action); `PListItem.dimmed` is visual dimming and its two title extension slots are mutually exclusive. Sheet primary rows accept at most four actions, including in RTL. Button loading prevents interaction while preserving label width. Do not recreate these policies in business pages.

Run `bash scripts/check-ui.sh` from plain-app or plain-router-app after UI changes, followed by the host application's build. The shared Kotlin PSI checker resolves explicit imports, aliases, wildcard imports and fully qualified calls; it ignores comments and strings, rejects new raw Material page/sheet/button/form calls and literal shapes, and checks public library layout contracts. It generates the component symbol index under the application’s `build/reports/ui-check/` from source. Legacy business occurrences are frozen by exact file, function, symbol and count in each application's `scripts/ui-component-baseline.tsv`; entries name remaining migration debt or specialized geometry. Do not add entries to get a new violation through. Delete an entry when migrating its owner. No package-wide exclusion is supported.

This syntax check does not perform whole-program type inference or detect an arbitrary layout recreating a component. Compiler checks, the scene index, examples and review remain necessary. The showcase includes disabled/loading/error/read-only states and RTL/large-text controls; device checks must also cover light, dark, AMOLED, TalkBack, keyboard and neighboring touch targets. Performance changes need release traces before claiming a measured speedup.

Maven coordinate: `com.ismartcoding:plain-ui`, repository `https://plainhub.github.io/plain-app/maven`.
The `Publish plain-ui` workflow publishes tagged `plain-ui-v<version>` releases on the dedicated `maven` branch. Consumers must upgrade after publication; local cross-project validation can selectively substitute `plain-ui`, preserving their own `plain-common` version.

---

--- METRICS ---
- Files Extracted: 3
- Estimated Token Budget: ~4055 tokens
- Canonical Reference: https://codewiki.google/github.com/plainhub/plain-app
