This is an open-source Native Android rewrite of the "My-Personal-Stuff" app (originally Flutter). It serves as a practical demonstration and production-ready implementation of the a-navigator and a-provider libraries.
The app is designed to track, manage, and remind you of your personal belongings—whether it's tracking food expiration, organizing warehouse-like logistics at home, or managing maintenance schedules.
- Item Management: Easily add, edit, and organize items.
- Smart Reminders: Set up notifications for expiration dates or custom events.
- Usage Tracking: Log when and how much of an item is used.
- Maintenance Logs: Keep track of repairs or maintenance tasks for specific items.
- Barcode Support: Scan barcodes for quick input and searching.
The application is designed around a central Dashboard (HomePage) that provides quick access to all major functions.
- Dashboard: The entry point where you can navigate to different lists or quickly add new entries.
- Adding an Item:
- Click "Add Item" on the dashboard.
- Fill in details (Name, Image, Tags).
- Save to store it in the local database.
- Tracking Usage/Maintenance/Reminders:
- These are linked to specific items.
- From the dashboard, click "Add Usage" or "Add Maintenance".
- Select the target item from the list.
- Log the details (e.g., amount used, maintenance performed).
This project follows a Modular Single-Activity Architecture, emphasizing separation of concerns and scalability.
- a-navigator: Handles navigation. The app uses a single
MainActivity, and all screens are implemented asStatefulViews (View-based architecture) rather than Fragments or Activities. - a-provider: A lightweight Dependency Injection (DI) framework used to manage services, repositories, and UI components.
- RxJava3: Used heavily for reactive programming, handling asynchronous operations, and event buses.
The codebase is split into feature-centric modules to enforce boundaries:
| Module | Purpose |
|---|---|
:app |
Main entry point containing MainActivity, DI setup, navigation routing, and app-level commands for Item CRUD operations |
:base |
Shared utilities, entities (Item, ItemImage, ItemTag), common DAOs, base provider modules, Rx utilities, logging infrastructure, and shared UI components (AppBar, ImageSV, SelectionPage) |
:barcode |
Barcode scanning functionality using Camera2 API with ScanBarcodePage and ScanBarcodePreview components |
:item-usage |
Item usage tracking feature with entities (ItemUsage, ItemUsageImage), commands, DAO, and UI pages (ItemUsagesPage, ItemUsageDetailPage) |
:item-maintenance |
Item maintenance tracking with entities (ItemMaintenance, ItemMaintenanceImage), commands, DAO, and UI pages (ItemMaintenancesPage, ItemMaintenanceDetailPage) |
:item-reminder |
Reminder and alarm scheduling using WorkManager with entities (ItemReminder), commands, DAO, and UI pages (ItemRemindersPage, ItemReminderDetailPage) |
:settings |
App configuration and preferences with SettingsPage, theme management, log viewing, and license display |
Instead of a monolithic database, the app uses Multiple Room Databases, one for each feature module:
| Database | Module | Entities |
|---|---|---|
AppDatabase |
base | AndroidNotification, Item, ItemImage, ItemTag |
ItemMaintenanceDatabase |
item-maintenance | ItemMaintenance, ItemMaintenanceImage |
ItemUsageDatabase |
item-usage | ItemUsage, ItemUsageImage |
ItemReminderDatabase |
item-reminder | ItemReminder |
This ensures that modules remain decoupled and can be maintained or extracted independently. Each database has its own DAO registered separately to decouple from the database instance.
- Single
MainActivityhosts all screens viaNavigator - All pages implement
StatefulView<Activity>instead of using Fragments - StatefulView lifecycle:
createView()→dispose() - Navigation uses route strings defined in
Routesclass - Route configuration in
NavigatorProvidermaps route names to StatefulViewFactory
- Global Provider scope (Application-level) initialized in
MainApplication - Activity Provider scope (Activity-level) created in
MainActivity - StatefulView Provider scope (View-level) created per page
- Modules register dependencies using
ProviderModuleinterface - Registration types:
register(): Eager instantiationregisterLazy(): Lazy instantiation (created on first access)registerAsync(): Asynchronous instantiation (created in background)registerPool(): Pool of instances for StatefulViews
Business logic is encapsulated using the Command Pattern:
- Commands are stateless classes (e.g.,
NewItemCmd,QueryItemCmd,PagedItemMaintenanceCmd) - Each command performs a specific business operation
- Commands execute on background threads via
ExecutorService - Commands return RxJava types (
Single<T>,Observable<T>) to the UI - Example:
NewItemCmd.execute(ItemState)returnsSingle<ItemState>
- Each module has a
ChangeNotifierclass for event-driven updates - Notifiers use RxJava
PublishSubjectto emit events - Notifiers expose
Flowablestreams for subscribers - Pattern:
itemAdded(),itemUpdated(),itemDeleted()→getAddedItemFlow(),getUpdatedItemFlow(),getDeletedItemFlow() - Event handlers (
*EventHandlerclasses) listen to base module events and trigger feature-specific actions - Example:
ItemMaintenanceEventHandlerlistens toItemChangeNotifierand handles cascade deletions
- Repositories encapsulate data access logic
- Example:
AndroidNotificationRepowrapsAndroidNotificationDao - Repositories handle complex queries and data transformations
graph TB
subgraph "Application Layer"
MA[MainActivity]
NP[NavigatorProvider]
NAV[Navigator]
end
subgraph "Base Module"
BP[BaseProviderModule]
DBP[DatabaseProviderModule]
RX[RxProviderModule]
AD[AppDatabase]
ID[ItemDao]
ICN[ItemChangeNotifier]
end
subgraph "Feature Module Example: item-maintenance"
IMP[ItemMaintenanceProviderModule]
IDP[ItemMaintenanceDatabaseProviderModule]
ICP[ItemMaintenanceCmdProviderModule]
IMDB[ItemMaintenanceDatabase]
IMD[ItemMaintenanceDao]
IMCN[ItemMaintenanceChangeNotifier]
IMEH[ItemMaintenanceEventHandler]
IMFC[ItemMaintenanceFileHelper]
NIC[NewItemMaintenanceCmd]
QIC[QueryItemMaintenanceCmd]
PIC[PagedItemMaintenanceCmd]
IMPs[ItemMaintenancesPage]
IMPDP[ItemMaintenanceDetailPage]
end
MA -->|creates| NP
NP -->|manages| NAV
NAV -->|navigates to| StatefulView[StatefulView Pages]
MA -->|initializes| Provider[Provider DI Container]
Provider -->|registers| BP
Provider -->|registers| RX
BP -->|registers| DBP
Provider -->|registers| IMP
IMP -->|registers| IDP
IMP -->|registers| ICP
ICP -->|provides| NIC
ICP -->|provides| QIC
ICP -->|provides| PIC
IMP -->|provides| IMCN
IMP -->|provides| IMEH
IMP -->|provides| IMFC
IDP -->|provides| IMDB
IMDB -->|provides| IMD
DBP -->|provides| AD
AD -->|provides| ID
NIC -->|uses| IMD
QIC -->|uses| IMD
NIC -->|emits to| IMCN
IMEH -->|listens to| ICN
IMCN -->|notifies| StatefulView
IMPs -->|invokes| QIC
PIC -->|invokes| IMD
StatefulView -->|injects dependencies via| Provider
Command[NIC/QIC/PIC] -->|injects dependencies via| Provider
- MainApplication: Initializes global
Provider(DI container) and registersAppProviderModulewith all feature modules - MainActivity:
- Creates Activity-scoped Provider with
RxProviderModule - Retrieves global Provider via
BaseApplication.of(this).getProvider() - Gets
NavigatorProviderwhich sets up navigation routes - Sets up
RxDisposerfor subscription lifecycle - Sets up theme change listener via
SettingsSharedPreferences - Registers back press handler
- Processes any pending notifications
- Creates Activity-scoped Provider with
- Navigator:
- Loads
SplashPageas initial route - After initialization (or splash timeout), routes to
HomePage
- Loads
- HomePage:
- Creates nested Provider for StatefulView scope
- Injects required dependencies (
ExecutorService,AppNotificationHandler,RxDisposer,QueryItemCmd) - Sets up drawer navigation and button click handlers
- Subscribes to notification events to navigate to appropriate pages
sequenceDiagram
participant OS as Android OS
participant App as MainApplication
participant GP as Global Provider
participant AP as Activity Provider
participant SV as StatefulView Provider
participant Activity as MainActivity
participant NP as NavigatorProvider
participant Nav as Navigator
participant Splash as SplashPage
participant Home as HomePage
OS->>App: onCreate()
App->>GP: Create Provider with AppProviderModule
GP->>GP: Register all modules (Base, ItemUsage, ItemMaintenance, etc.)
OS->>Activity: onCreate()
Activity->>AP: Create Provider with RxProviderModule
Activity->>GP: Get global provider
GP->>NP: Get NavigatorProvider
NP->>NP: Setup navigation routes (map route names to StatefulViewFactory)
NP->>Nav: Return Navigator instance
Nav->>Splash: Push SplashPage as initial route
Splash->>Home: Push HomePage after initialization
Home->>SV: Create nested provider for StatefulView
SV->>Home: Inject dependencies
Home-->>Activity: Render Dashboard
- User Interaction: User clicks a button in a
StatefulView(e.g., "Add Item") - Navigation:
Navigator.push(routeName)navigates to new StatefulView - View Creation: New StatefulView's
createView()is called - Dependency Injection: StatefulView injects required Commands via Provider
- Command Execution: User input triggers
Command.execute()method - Background Execution: Command runs on
ExecutorServicebackground thread - Database Access: Command uses DAO to perform database operations
- Event Emission: Notifier emits event via
PublishSubject - UI Update: RxJava subscription observes Flowable and updates UI on main thread
- Cleanup: When StatefulView is disposed,
RxDisposerclears all subscriptions
sequenceDiagram
participant User as User
participant SV as StatefulView (HomePage)
participant Nav as Navigator
participant Cmd as Command (NewItemCmd)
participant ES as ExecutorService
participant DAO as ItemDao
participant DB as AppDatabase
participant Notifier as ItemChangeNotifier
participant Rx as RxDisposer
participant SV2 as StatefulView (ItemsPage)
User->>SV: Click "Add Item"
SV->>Nav: push(ITEM_DETAIL_PAGE)
Nav->>SV2: createView()
SV2->>Cmd: Get from Provider
User->>SV2: Enter item details and save
SV2->>Cmd: execute(ItemState)
Cmd->>ES: Submit task
ES->>DAO: insertItem(ItemState)
DAO->>DB: Perform INSERT
DB-->>DAO: Result
Notifier->>Notifier: itemAdded(ItemState)
Cmd-->>SV2: Single<ItemState>
SV2->>Rx: Add subscription
Notifier->>SV2: Emit itemAdded event
SV2->>Nav: pop() or navigate
SV2->>Rx: Dispose all subscriptions
a-personal-stuff/
├── app/ # Main application module
│ ├── src/main/java/m/co/rh/id/a_personal_stuff/app/
│ │ ├── MainActivity.java # Single activity hosting all views
│ │ ├── MainApplication.java # Application class with global provider
│ │ ├── provider/
│ │ │ ├── AppProviderModule.java # Root DI module registration
│ │ │ ├── NavigatorProvider.java # Navigation route setup
│ │ │ ├── StatefulViewProvider.java # StatefulView-scoped provider
│ │ │ ├── CommandProviderModule.java # App-level commands
│ │ │ ├── component/
│ │ │ │ └── AppNotificationHandler.java # Notification processing
│ │ │ └── command/
│ │ │ ├── NewItemCmd.java # Create item command
│ │ │ ├── UpdateItemCmd.java # Update item command
│ │ │ ├── DeleteItemCmd.java # Delete item command
│ │ │ ├── QueryItemCmd.java # Query item command
│ │ │ ├── PagedItemCmd.java # Paged item query
│ │ │ └── *Item{Tag/Image}Cmd.java # Tag/Image commands
│ │ └── ui/
│ │ ├── page/
│ │ │ ├── SplashPage.java # Splash screen
│ │ │ ├── HomePage.java # Main dashboard
│ │ │ ├── ItemsPage.java # Item list
│ │ │ ├── ItemDetailPage.java # Item add/edit
│ │ │ ├── ItemSelectPage.java # Item selection dialog
│ │ │ └── DonationsPage.java # Donation page
│ │ └── component/
│ │ ├── item/
│ │ │ ├── ItemListSV.java # Item list StatefulView
│ │ │ ├── ItemItemSV.java # Item item StatefulView
│ │ │ └── ItemAdapter.java # RecyclerView adapter
│ │ └── adapter/
│ │ └── ItemSuggestionAdapter.java
│
├── base/ # Shared base module
│ ├── src/main/java/m/co/rh/id/a_personal_stuff/base/
│ │ ├── BaseApplication.java # Base application class
│ │ ├── constants/
│ │ │ ├── Constants.java # App constants (DB names)
│ │ │ └── Routes.java # Route name constants
│ │ ├── entity/
│ │ │ ├── Item.java # Item entity
│ │ │ ├── ItemImage.java # Item image entity
│ │ │ ├── ItemTag.java # Item tag entity
│ │ │ ├── AndroidNotification.java # Notification entity
│ │ │ └── ItemState.java # Item state model
│ │ ├── dao/
│ │ │ ├── ItemDao.java # Item data access object
│ │ │ └── AndroidNotificationDao.java # Notification DAO
│ │ ├── room/
│ │ │ ├── AppDatabase.java # Main Room database
│ │ │ └── converter/Converter.java # Type converters
│ │ ├── repository/
│ │ │ └── AndroidNotificationRepo.java # Notification repository
│ │ ├── provider/
│ │ │ ├── BaseProviderModule.java # Base DI module
│ │ │ ├── DatabaseProviderModule.java # Database DI module
│ │ │ ├── RxProviderModule.java # RxJava DI module
│ │ │ ├── notifier/
│ │ │ │ └── ItemChangeNotifier.java # Item change event bus
│ │ │ ├── component/
│ │ │ │ ├── ItemFileHelper.java # File operations
│ │ │ │ └── FileHelper.java # Base file helper
│ │ │ └── IStatefulViewProvider.java # SV provider interface
│ │ ├── rx/
│ │ │ └── RxDisposer.java # Subscription manager
│ │ ├── ui/
│ │ │ ├── component/
│ │ │ │ └── AppBarSV.java # App bar StatefulView
│ │ │ ├── page/
│ │ │ │ ├── common/
│ │ │ │ │ ├── ImageSV.java # Image viewer SV
│ │ │ │ │ ├── ImageViewPage.java # Image viewer page
│ │ │ │ │ └── SelectionPage.java # Selection dialog
│ │ │ └── recyclerview/
│ │ │ └── CustomLinearLayoutManager.java
│ │ └── util/
│ │ └── UiUtils.java # UI utilities
│
├── item-maintenance/ # Item maintenance feature module
│ ├── src/main/java/m/co/rh/id/a_personal_stuff/item_maintenance/
│ │ ├── entity/
│ │ │ ├── ItemMaintenance.java # Maintenance entity
│ │ │ ├── ItemMaintenanceImage.java # Maintenance image entity
│ │ │ └── ItemMaintenanceState.java # Maintenance state model
│ │ ├── dao/
│ │ │ └── ItemMaintenanceDao.java # Maintenance DAO
│ │ ├── room/
│ │ │ └── ItemMaintenanceDatabase.java # Maintenance Room database
│ │ ├── provider/
│ │ │ ├── ItemMaintenanceProviderModule.java
│ │ │ ├── ItemMaintenanceDatabaseProviderModule.java
│ │ │ ├── ItemMaintenanceCmdProviderModule.java
│ │ │ ├── command/
│ │ │ │ ├── NewItemMaintenanceCmd.java
│ │ │ │ ├── UpdateItemMaintenanceCmd.java
│ │ │ │ ├── DeleteItemMaintenanceCmd.java
│ │ │ │ ├── QueryItemMaintenanceCmd.java
│ │ │ │ └── PagedItemMaintenanceCmd.java
│ │ │ ├── component/
│ │ │ │ ├── ItemMaintenanceFileHelper.java
│ │ │ │ └── ItemMaintenanceEventHandler.java
│ │ │ └── notifier/
│ │ │ └── ItemMaintenanceChangeNotifier.java
│ │ └── ui/
│ │ ├── page/
│ │ │ ├── ItemMaintenancesPage.java
│ │ │ └── ItemMaintenanceDetailPage.java
│ │ └── component/
│ │ ├── ItemMaintenanceListSV.java
│ │ └── ItemMaintenanceItemSV.java
│
├── item-usage/ # Item usage tracking module
│ └── (Similar structure to item-maintenance)
│
├── item-reminder/ # Item reminder/notification module
│ ├── src/main/java/m/co/rh/id/a_personal_stuff/item_reminder/
│ │ ├── entity/
│ │ │ └── ItemReminder.java
│ │ ├── dao/
│ │ │ └── ItemReminderDao.java
│ │ ├── room/
│ │ │ └── ItemReminderDatabase.java
│ │ ├── provider/
│ │ │ ├── ItemReminderProviderModule.java
│ │ │ ├── ItemReminderDatabaseProviderModule.java
│ │ │ ├── ItemReminderCmdProviderModule.java
│ │ │ ├── command/
│ │ │ │ ├── NewItemReminderCmd.java
│ │ │ │ ├── DeleteItemReminderCmd.java
│ │ │ │ ├── QueryItemReminderCmd.java
│ │ │ │ └── PagedItemReminderCmd.java
│ │ │ ├── component/
│ │ │ │ ├── ItemReminderEventHandler.java
│ │ │ │ └── IItemReminderNotificationHandler.java
│ │ │ └── notifier/
│ │ │ └── ItemReminderChangeNotifier.java
│ │ ├── ui/
│ │ │ ├── page/
│ │ │ │ ├── ItemRemindersPage.java
│ │ │ │ └── ItemReminderDetailPage.java
│ │ │ └── component/
│ │ │ ├── ItemReminderListSV.java
│ │ │ └── ItemReminderItemSV.java
│ │ └── workmanager/
│ │ ├── WorkManagerConstants.java
│ │ └── worker/
│ │ └── ItemReminderNotificationWorker.java
│
├── barcode/ # Barcode scanning module
│ ├── src/main/java/m/co/rh/id/a_personal_stuff/barcode/
│ │ ├── provider/
│ │ │ └── BarcodeProviderModule.java
│ │ ├── ui/
│ │ │ ├── NavBarcodeConfig.java # Navigation configuration
│ │ │ ├── ScanBarcodePage.java # Barcode scanner page
│ │ │ └── component/
│ │ │ └── ScanBarcodePreview.java # Camera preview
│
├── settings/ # Settings module
│ ├── src/main/java/m/co/rh/id/a_personal_stuff/settings/
│ │ ├── provider/
│ │ │ ├── SettingsProviderModule.java
│ │ │ └── component/
│ │ │ └── SettingsSharedPreferences.java
│ │ └── ui/
│ │ ├── page/
│ │ │ ├── SettingsPage.java # Main settings page
│ │ │ ├── LogPage.java # Log viewer
│ │ │ └── LicensesPage.java # Licenses display
│ │ └── component/
│ │ ├── ThemeMenuSV.java # Theme selector
│ │ ├── VersionMenuSV.java # Version display
│ │ ├── LogMenuSV.java # Log menu
│ │ └── LicensesMenuSV.java # Licenses menu
│
├── graphics/ # App graphics (launcher icon, etc.)
├── fastlane/ # App store metadata (descriptions, screenshots)
│ ├── metadata/android/en-US/
│ │ ├── images/ # Screenshots and feature graphic
│ │ ├── full_description.txt
│ │ ├── short_description.txt
│ │ └── title.txt
│ └── README.md
│
├── .github/workflows/ # CI/CD pipelines
│ ├── gradlew-build.yml # Build verification
│ ├── android-release.yml # Release build automation
│ └── android-emulator-test.yml # Emulator testing
│
├── build.gradle # Root build configuration
├── settings.gradle # Project module configuration
├── gradle.properties # Gradle properties (room_version, nav_version)
└── README.md # This file
Application Scope (MainApplication)
└── Global Provider
├── BaseProviderModule (executors, logger, WorkManager, base DB)
├── RxProviderModule (Rx utilities)
├── FeatureProviderModules (each feature's DI setup)
├── NavigatorProvider (navigation configuration)
└── Notifiers (event publishers)
Activity Scope (MainActivity)
└── Activity Provider
├── RxDisposer (subscription lifecycle)
└── Navigator (navigation instance)
StatefulView Scope (each page)
└── StatefulView Provider (nested)
├── Commands (business logic)
├── Component handlers
└── View-specific dependencies
- ExecutorService: Weighted thread pool with max weight 5 for general async operations
- ScheduledExecutorService: Single-threaded for scheduled tasks
- WorkManager: For background notifications and reminders
- RxJava Schedulers:
Schedulers.from(executorService): Background execution for commandsAndroidSchedulers.mainThread(): UI updatesobserveOn()andsubscribeOn()for thread switching
When an item is deleted:
DeleteItemCmd.execute()called withItemState- Command deletes item from
AppDatabaseviaItemDao ItemChangeNotifier.itemDeleted(itemState)emits eventItemMaintenanceEventHandlersubscribes toItemChangeNotifier- On
itemDeletedevent, handler deletes related maintenance records - Similar flow for
ItemUsageEventHandlerandItemReminderEventHandler
- ItemState: Aggregate of Item, ItemImages, and ItemTags
- Feature-specific states:
ItemMaintenanceState,ItemUsageState - States are passed between views via navigation arguments
- States are cloned before modification to avoid reference issues
FileHelper: Base class for file operations (app directory management)ItemFileHelper: Manages item-related files (images, etc.)ItemMaintenanceFileHelper: Manages maintenance-related files- File deletion cascades through event handlers
| Layer | Technology |
|---|---|
| Language | Java 17 |
| Build System | Gradle 8.14.3 (Android Gradle Plugin 8.13.2) |
| Min SDK | 21 (Android 5.0 Lollipop) |
| Target SDK | 33 (Android 13) |
| DI Framework | a-provider v0.0.23 |
| Navigation | a-navigator v0.0.66 |
| Database | Room 2.5.1 |
| Async | RxJava 3.1.4, RxAndroid 3.0.0 |
| Logging | a-logger v0.0.3 |
| UI | AndroidX, Material Design |
| Background Tasks | WorkManager 2.8.1 |
| Image Viewer | PhotoView 2.3.0 |
| Barcode | Camera2 API |
| Concurrency | concurrent-utils v0.0.3 (WeightedThreadPool) |
| Memory Leaks | LeakCanary Plumber 2.9.1 |
| Desugaring | Android Desugar Libs 2.0.4 |
The project leverages automation to ensure quality and streamline releases.
Located in .github/workflows/, the project has three main pipelines:
-
Android CI (
gradlew-build.yml):- Triggers on push/PR to
master - Runs
./gradlew buildto ensure project builds successfully - Verifies compilation and basic checks
- Triggers on push/PR to
-
Android Release (
android-release.yml):- Triggers when a tag starting with
v*is pushed (e.g.,v1.3.0) - Builds release APK with signing
- Uses repository secrets for signing configuration:
SIGNING_KEY: Base64-encoded keystoreKEY_STORE_PASSWORD: Keystore passwordALIAS: Key aliasKEY_PASSWORD: Key password
- Creates GitHub Release with:
- Release APK artifact
- Changelog from
fastlane/metadata/android/en-US/changelogs/{versionCode}.txt
- Triggers when a tag starting with
-
Emulator Test (
android-emulator-test.yml):- Runs instrumented tests on Android emulator
- Verifies UI and integration tests pass
The fastlane/ directory contains metadata for store listings:
metadata/android/en-US/: Store metadata for English (US)images/featureGraphic.png: Feature graphic (1024x500)images/icon.png: App icon (512x512)images/phoneScreenshots/: Phone screenshots (1.png to 4.png)full_description.txt: Full app descriptionshort_description.txt: Short app description (80 characters max)title.txt: App titlechangelogs/{versionCode}.txt: Per-version changelog
This structure allows for version-controlled store presence management.
The project includes testing infrastructure:
- Unit Tests: JUnit 4.13.2
- Android Instrumented Tests: AndroidX Test, Espresso 3.5.1
- Room Database Testing: Room Testing 2.5.1 (uses exported schemas in
schemas/directories) - Test Runner: AndroidJUnitRunner
To run tests:
# Run unit tests
./gradlew test
# Run instrumented tests (requires connected device/emulator)
./gradlew connectedAndroidTest- JDK 17 or higher
- Android SDK 33
- Gradle (via Gradle Wrapper included)
# Build debug APK
./gradlew assembleDebug
# Build release APK (requires signing)
./gradlew assembleRelease
# Clean build
./gradlew clean- Debug APK:
app/build/outputs/apk/debug/app-debug.apk - Release APK:
app/build/outputs/apk/release/app-release.apk
Consider donation to support this project
| https://trakteer.id/rh-id |
This project is licensed under the GPL-3.0 License.




