FlowerApp is an Android computer-vision application that classifies 102 flower categories with a TensorFlow Lite model running entirely on the device. This repository demonstrates the mobile machine-learning integration through Kotlin, Jetpack Compose, Android system image APIs, and lifecycle-aware state management.
FlowerApp is a published Android application available on Google Play. To protect the production application's proprietary user interface and product design, the complete production UI source is not included in this repository.
This public repository provides:
- core Android application logic;
- TensorFlow Lite model integration;
- image preprocessing and on-device inference;
- camera handling;
- Photo Picker and gallery handling;
- scoped
FileProviderintegration; ViewModelandStateFlowstate management;- model-contract validation tests;
- a simplified Jetpack Compose demonstration UI.
The public demo UI is intentionally different from the production UI. The screenshots below are from the published production application, and this repository does not contain the complete source code or every feature of that application.
These images show the published production application. Its visual design, animations, media, navigation, and complete UI implementation are intentionally excluded from this public repository.
| Capability | Implementation |
|---|---|
| Flower recognition | 102-class MobileNetV2 TensorFlow Lite model |
| Inference | Fully on-device; selected images are not uploaded |
| Gallery input | Android system Photo Picker |
| Camera input | Activity Result API with a scoped, non-exported FileProvider |
| Image safety | Bounded decoding, MIME checks, and EXIF orientation handling |
| UI state | Lifecycle-aware StateFlow exposed by a ViewModel |
| Model safety | Runtime tensor shape, type, and label-count validation |
| Results | Top prediction, confidence, and inference-only latency |
| Privacy | No Internet, storage, analytics, advertising, or tracking dependency |
Photo Picker ─┐
├─> Safe bitmap decode ─> EXIF correction ─> 224 × 224 RGB
Camera ───────┘ │
v
Float32 normalization (1/255)
│
v
TensorFlow Lite inference
│
v
Top-one label, confidence, latency
- Select a flower image through the system Photo Picker or capture one with the camera.
- Decode the image at a bounded size and apply its orientation metadata.
- Resize the bitmap to 224 × 224 pixels and convert RGB channels to
FLOAT32values in the range[0, 1]. - Run the bundled TensorFlow Lite model locally.
- Select the highest output score and display its flower label, confidence, and model-inference time.
- Delete managed temporary camera files after processing or cancellation.
MainActivity
└── FlowerDemoScreen (Jetpack Compose)
├── Photo Picker / Camera launchers
└── FlowerViewModel
├── FlowerUiState (StateFlow)
├── TemporaryCameraFileManager
└── FlowerImageProcessor
├── SafeBitmapDecoder
├── ImagePreprocessor
└── TensorFlowFlowerClassifier
└── TensorFlow Lite Interpreter
The UI observes immutable state for model checking, idle, loading, success, and error conditions. Decode and inference work run away from the main thread. Interpreter access is serialized, the classifier is reused between predictions, and resources are closed with the ViewModel lifecycle.
The bundled classifier validates its model contract before accepting predictions. It requires one input tensor, one output tensor, matching FLOAT32 types, the expected shapes, and one label per output class.
| Model property | Contract |
|---|---|
| Architecture | MobileNetV2 |
| Input | 1 × 224 × 224 × 3 |
| Input type | FLOAT32 |
| Channel order | RGB |
| Normalization | channel value ÷ 255.0 |
| Output | 1 × 102 |
| Output type | FLOAT32 |
| Output activation | Softmax |
| Selection | Top-one argmax |
| Labels | 102 non-empty entries |
The related Flower Classification project documents dataset preparation, model evaluation, and TensorFlow Lite conversion. This Android repository focuses on safe mobile integration and does not redistribute the Oxford 102 Flowers dataset.
SafeBitmapDecoder limits the largest decoded dimension to 1024 pixels before the model-specific resize. Android 9 and newer use ImageDecoder; Android 8 uses sampled BitmapFactory decoding. The Android 8 path reads EXIF orientation and applies the required rotation or reflection.
ImagePreprocessor then:
- scales the bitmap to 224 × 224;
- extracts pixels in RGB order;
- writes each channel to a native-order direct
ByteBuffer; - divides channel values by
255.0; - rewinds the buffer before interpreter invocation.
- Gallery selection uses
ActivityResultContracts.PickVisualMediawith image-only input. - Camera capture uses
ActivityResultContracts.TakePicture. - Captured images are written to
cacheDir/camera_images/. - A non-exported
FileProviderexposes only that managed cache path. - Temporary images are deleted after processing, cancellation, reset, or stale-file cleanup.
- Camera hardware is optional, and camera permission is requested only when capture is selected.
Inference runs locally. The public application logic contains no network client and requests no Internet or broad storage permission. It includes no analytics, advertising, tracking, Firebase, or remote service integration.
Declared permission:
<uses-permission android:name="android.permission.CAMERA" />Gallery access is provided by the system Photo Picker. Application backup and device-transfer extraction are disabled for application data.
See the public Privacy Policy for a complete description of image handling, permissions, and data practices.
.
├── app/
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── assets/ # TFLite model and 102 labels
│ │ ├── java/.../flowerapp/
│ │ │ ├── classifier/ # Model contract and preprocessing
│ │ │ ├── data/ # Decode/inference coordination
│ │ │ ├── ui/ # Simplified Compose demo
│ │ │ ├── utils/ # Safe decoding and camera files
│ │ │ ├── viewmodel/ # Lifecycle-aware state
│ │ │ └── MainActivity.kt
│ │ └── res/ # Manifest resources and XML policies
│ └── test/ # Local unit and model-contract tests
├── docs/images/
│ ├── branding/
│ └── google-play/ # Production application screenshots
├── gradle/ # Version catalog and wrapper
├── README.md
├── LICENSE
└── THIRD_PARTY_NOTICES.md
- Android Studio with support for Android Gradle Plugin 8.11.1
- JDK 17 or newer for Gradle
- Android SDK 36
- Android 8.0 (API 26) or newer device/emulator
- Included Gradle wrapper
git clone https://github.com/fatemehsabourinia/FlowerApp.git
cd FlowerAppOpen the repository root in Android Studio, allow Gradle sync to complete, select the app run configuration, and run it on an Android 8.0 or newer device or emulator.
Build a debug APK:
./gradlew --no-daemon :app:assembleDebugRun the local validation sequence:
./gradlew --no-daemon :app:testDebugUnitTest
./gradlew --no-daemon :app:lintDebug
./gradlew --no-daemon :app:assembleDebugThe test suite covers model tensor contracts, label alignment, preprocessing bounds, prediction selection, startup privacy constraints, temporary camera-file management, and ViewModel state transitions. Device-level camera and Photo Picker flows should additionally be verified on representative Android versions.
Validation reproduced on 18 July 2026:
- 16 local unit tests passed with 0 failures and 0 skipped tests.
- Android lint completed with 0 errors and 17 warnings.
- Debug APK assembly completed successfully.
- Instrumentation tests were not run.
The lint warnings are dependency/version advisories and upstream TensorFlow Lite native-library 16 KB alignment notices; no lint error is suppressed by a baseline.
The Google Play listing identifies the application as FlowerApp and the developer as Alireza Zaeri & Fatemeh Sabourinia. Availability may vary by country, account, or Google Play access conditions.
The production package ID is com.zaeri.sabourinia.flowerapp, which is also retained as this repository's Android application ID. The app display name remains Flower Identifier, while FlowerApp is the canonical repository name.
- The public Compose interface is a simplified engineering demonstration, not the production interface.
- The repository does not include every production application feature or production UI resource.
- Classification is limited to the 102 flower categories represented by the model.
- The classifier does not implement calibrated uncertainty or non-flower rejection.
- Predictions can be incorrect and must not be treated as scientific identification.
- Inference time varies by device, Android version, thermal state, and current system load.
- Automated device/instrumentation coverage is not included.
- Add automated device tests for camera, Photo Picker, and process recreation flows.
- Add adaptive launcher icon resources for modern Android launchers.
- Expand accessibility and localization coverage in the demonstration UI.
- Evaluate quantized model variants and delegate performance across representative devices.
- Add continuous integration for unit tests, lint, and debug assembly.
This project was developed by Fatemeh Sabourinia and Alireza Zaeri.
Original source code in this repository is available under the MIT License. The existing licence retains both project developers as copyright holders.
Dependencies, pretrained components, datasets, the TensorFlow Lite model, production screenshots, and branding may remain subject to separate upstream terms. See THIRD_PARTY_NOTICES.md for details.






