{"id":19883298,"url":"https://github.com/rh-id/a-flash-deck","last_synced_at":"2026-01-08T15:05:53.314Z","repository":{"id":42503478,"uuid":"440377534","full_name":"rh-id/a-flash-deck","owner":"rh-id","description":"A simple Flash Card application to assist in learning and remembering something.","archived":false,"fork":false,"pushed_at":"2024-01-02T02:12:09.000Z","size":2352,"stargazers_count":24,"open_issues_count":9,"forks_count":6,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-01-02T03:25:40.306Z","etag":null,"topics":["a-navigator","a-provider","android","android-app","android-application","android-apps","flashcard","flashcard-application","flashcards"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rh-id.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2021-12-21T03:24:06.000Z","updated_at":"2024-01-02T03:25:41.544Z","dependencies_parsed_at":"2024-01-02T03:25:33.558Z","dependency_job_id":"10ca37af-7e98-40e8-9af6-12cdc2050e28","html_url":"https://github.com/rh-id/a-flash-deck","commit_stats":null,"previous_names":[],"tags_count":46,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rh-id%2Fa-flash-deck","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rh-id%2Fa-flash-deck/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rh-id%2Fa-flash-deck/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rh-id%2Fa-flash-deck/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rh-id","download_url":"https://codeload.github.com/rh-id/a-flash-deck/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224315872,"owners_count":17291133,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["a-navigator","a-provider","android","android-app","android-application","android-apps","flashcard","flashcard-application","flashcards"],"created_at":"2024-11-12T17:19:49.117Z","updated_at":"2026-01-08T15:05:53.307Z","avatar_url":"https://github.com/rh-id.png","language":"Java","readme":"# a-flash-deck\n\n![Languages](https://img.shields.io/github/languages/top/rh-id/a-flash-deck)\n![Downloads](https://img.shields.io/github/downloads/rh-id/a-flash-deck/total)\n![GitHub release (by tag)](https://img.shields.io/github/downloads/rh-id/a-flash-deck/latest/total)\n![Release](https://img.shields.io/github/v/release/rh-id/a-flash-deck)\n![Android CI](https://github.com/rh-id/a-flash-deck/actions/workflows/gradlew-build.yml/badge.svg)\n![Release Build](https://github.com/rh-id/a-flash-deck/actions/workflows/android-release.yml/badge.svg)\n![Emulator Test](https://github.com/rh-id/a-flash-deck/actions/workflows/android-emulator-test.yml/badge.svg)\n\nA simple and easy to use flash card app to help you study.\n\n## Screenshots\n\u003cimg src=\"https://github.com/rh-id/a-flash-deck/blob/master/fastlane/metadata/android/en-US/images/featureGraphic.png\" width=\"1024\"/\u003e\n\n\u003cimg src=\"https://github.com/rh-id/a-flash-deck/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png\" height=\"512\"/\u003e\n\u003cimg src=\"https://github.com/rh-id/a-flash-deck/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png\" height=\"512\"/\u003e\n\u003cimg src=\"https://github.com/rh-id/a-flash-deck/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png\" height=\"512\"/\u003e\n\u003cimg src=\"https://github.com/rh-id/a-flash-deck/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png\" height=\"512\"/\u003e\n\u003cimg src=\"https://github.com/rh-id/a-flash-deck/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png\" height=\"512\"/\u003e\n\n## Features\n* Easily add deck and cards\n* Add notification timer to periodically asking you question\n* Support dark mode and light mode\n* Easily export \u0026 share your decks to your friends\n* Record voices and attach images for the cards\n* Create shortcut to show random card from deck for casual study (Android 8 and above)\n* Flash bot to smartly suggest list of card to test you\n\n## Project Structure\n\nThis project is a multi-module Android application.\n\n```mermaid\ngraph TD\n    App[:app] --\u003e Base[:base]\n    App --\u003e Bot[:bot]\n    App --\u003e Timer[:timer-notification]\n    Bot --\u003e Base\n    Timer --\u003e Base\n```\n\n*   `:app`: The main application module that contains the UI and presentation layer.\n*   `:base`: A library module that contains base classes and utilities shared across other modules.\n*   `:bot`: A library module that contains the logic for the \"Flash bot\" feature.\n*   `:timer-notification`: A library module for handling timer-based notifications.\n\nThis project is intended for demo app for [a-navigator](https://github.com/rh-id/a-navigator) and [a-provider](https://github.com/rh-id/a-provider) library usage. The app still works as production even though it is demo app.\n\n## Architecture\n\nThe app follows a modern Android architecture, utilizing a combination of established libraries and custom frameworks to create a modular and maintainable codebase.\n\n*   **Dependency Injection**: The app uses a custom service locator pattern with the `a-provider` library. A global `Provider` is initialized in the `MainApplication` class, which is then used to provide dependencies throughout the app. This creates a centralized and easy-to-manage dependency graph.\n\n*   **Navigation**: Navigation between screens is handled by the `a-navigator` library. This library provides a flexible and powerful way to manage navigation, including support for different screen types and transitions.\n\n*   **Reactive Programming**: The app makes extensive use of `RxJava` for handling asynchronous operations and UI events. This allows for a more concise and readable code, especially when dealing with complex asynchronous workflows.\n\n*   **Modular Design**: The app is divided into several modules, each with a specific responsibility. This promotes a clean separation of concerns and makes the codebase easier to understand and maintain.\n\n*   **Error Handling**: A global exception handler is set up in `MainApplication` to log crashes and other unexpected errors. This helps to ensure that the app is as stable as possible.\n\n*   **Background Jobs**: Background tasks are handled by `WorkManager`, with a custom configuration provided by `MainApplication`. This allows for efficient and reliable execution of background tasks, such as syncing data or sending notifications.\n\n### Presentation Layer: The `StatefulView` Pattern\n\nThe presentation layer is built on a custom component-based architecture centered around the `StatefulView` class. This pattern deviates from traditional MVP or MVVM in favor of a more self-contained and reactive approach.\n\nHere’s a breakdown of the workflow:\n\n*   **View and Logic Combined**: `StatefulView` classes (e.g., `HomePage`, `SettingsPage`) are responsible for both creating the Android `View` and handling the presentation logic. This makes each `StatefulView` a self-contained UI component.\n\n*   **Lifecycle**: The `a-navigator` library manages the lifecycle of `StatefulView`s. When you navigate to a new screen, the navigator creates the corresponding `StatefulView` instance. The `dispose` method is then called when the view is no longer needed, which is crucial for unsubscribing from RxJava streams and preventing memory leaks.\n\n*   **Dependency Injection**: Dependencies are injected in two ways:\n    1.  The navigator injects navigation-related components (like `INavigator` and `AppBarSV`) using the `@NavInject` annotation.\n    2.  Other dependencies (like data sources, commands, and notifiers) are provided by the `a-provider` service locator via the `provideComponent` method.\n\n*   **UI Creation**: The `createView` method is where the UI is constructed. It inflates an XML layout, finds `View`s by their IDs, and sets up event listeners. It also subscribes to RxJava streams to react to state changes.\n\n*   **State Management and Reactivity**: The UI state is managed using RxJava’s `BehaviorSubject`. The UI elements subscribe to these subjects, so whenever the state changes (e.g., a test starts or stops), the UI updates automatically and reactively.\n\n*   **User Interaction**: User actions, handled in methods like `onClick`, trigger business logic by calling command classes (e.g., `mNewCardCmd`, `mTestStateModifier.startTest`). These commands perform operations and update the state, which in turn updates the UI through the reactive streams.\n\n```mermaid\nsequenceDiagram\n    participant Nav as Navigator\n    participant SV as StatefulView\n    participant UI as View (XML)\n    participant Rx as RxJava Subject\n\n    Nav-\u003e\u003eSV: Instantiate (via Provider)\n    Nav-\u003e\u003eSV: createView(activity, container)\n    SV-\u003e\u003eUI: Inflate layout\n    SV-\u003e\u003eUI: Find views \u0026 set listeners\n    SV-\u003e\u003eRx: Subscribe to state changes\n    Rx--\u003e\u003eSV: Emit current state\n    SV-\u003e\u003eUI: Update UI\n\n    Note over Nav, SV: Navigation or Back Press\n    Nav-\u003e\u003eSV: dispose()\n    SV-\u003e\u003eRx: Unsubscribe (prevent leaks)\n```\n\n## Workflow\n\nThe application's workflow is designed to be modular, scalable, and reactive. It follows a clear separation of concerns, with distinct layers for the UI, business logic, and data.\n\n### Data Layer\n\nThe data layer is responsible for all data-related operations. It is built on top of the following components:\n\n*   **Room Persistence Library**: The app uses Room for local data persistence. It has two databases:\n    *   `AppDatabase`: The main database for the app, managing entities like `Deck`, `Card`, `Test`, `AndroidNotification`, and `NotificationTimer`.\n    *   `BotDatabase`: A separate database for the \"Flash bot\" feature, managing entities like `CardLog` and `SuggestedCard`.\n*   **DAOs (Data Access Objects)**: Each entity has a corresponding DAO that defines the methods for accessing and manipulating the data in the database.\n\n### Business Logic: The Command Pattern\n\nThe business logic is encapsulated in command classes, which follow the command pattern. These commands are responsible for executing specific business operations, such as creating a new deck or updating a card.\n\nSome of the key command classes include:\n\n*   `NewDeckCmd`: Creates a new deck.\n*   `UpdateCardCmd`: Updates an existing card.\n*   `DeleteDeckCmd`: Deletes a deck.\n*   `ExportImportCmd`: Handles the export and import of decks.\n\nThese commands are provided by the `CommandProviderModule` and are injected into the `StatefulView`s where they are needed.\n\n### End-to-End Data Flow\n\nThe app's data flow is designed to be unidirectional and reactive, ensuring that the UI is always in sync with the underlying data. Here's a step-by-step overview of the data flow:\n\n```mermaid\nsequenceDiagram\n    participant User\n    participant SV as StatefulView\n    participant Cmd as Command\n    participant DAO as DAO/Room\n    participant Notifier\n    participant Rx as RxJava\n\n    User-\u003e\u003eSV: Interaction (Click)\n    SV-\u003e\u003eCmd: Execute Command\n    Cmd-\u003e\u003eDAO: Update Data\n    DAO--\u003e\u003eCmd: Success\n    Cmd-\u003e\u003eNotifier: Broadcast Change\n    Notifier-\u003e\u003eRx: Emit Event\n    Rx--\u003e\u003eSV: OnNext(Event)\n    SV-\u003e\u003eUI: Update View\n```\n\n1.  **User Interaction**: The user interacts with a `StatefulView` (e.g., clicks a button).\n2.  **Command Execution**: The `StatefulView` invokes the appropriate command to handle the user's action.\n3.  **Data Manipulation**: The command interacts with the data layer (via the DAOs) to create, read, update, or delete data.\n4.  **State Notification**: After the data is updated, the command uses a notifier (e.g., `DeckChangeNotifier`) to broadcast that the data has changed.\n5.  **UI Update**: The `StatefulView`s subscribe to these notifiers and update their UI in response to the change notifications. This is done reactively using `RxJava`, ensuring that the UI always reflects the current state of the data.\n\n### Threading\n\nTo ensure that the UI remains responsive, all database and business logic operations are performed on background threads. This is achieved through a combination of `RxJava` schedulers and a dedicated `ExecutorService`, which is provided by the `BaseProviderModule`.\n\n## Testing\n\nThe project includes both unit and instrumentation tests, although the coverage could be improved. Here's a summary of the testing strategy:\n\n*   **Unit Tests**: The project has a `unitTest` artifact, but it currently only contains a boilerplate example. This is an area for improvement, as the business logic in the command classes and other components could be unit-tested with mocked dependencies.\n\n*   **Instrumentation Tests**: The project has an `androidTest` artifact with at least one meaningful test, `ExportImportCmdTest.java`. This test demonstrates a good approach to testing database interactions and file operations, using a separate `Provider` and an in-memory database to ensure that tests are hermetic and isolated.\n\n*   **Areas for Improvement**: In addition to adding more unit tests, the project would benefit from UI tests using a framework like Espresso. This would allow for the verification of the application's user interface and user flows, ensuring that the app behaves as expected from the user's perspective.\n\n## CI/CD and Automation\n\nThe project uses a combination of GitHub Actions and Fastlane to automate the build, test, and release process.\n\n### GitHub Actions\n\nThe project has three GitHub Actions workflows:\n\n*   `gradlew-build.yml`: Builds the project with Gradle on every push and pull request to the `master` branch.\n*   `android-release.yml`: Creates a GitHub release and attaches the debug and release APKs when a new tag starting with \"v\" is pushed.\n*   `android-emulator-test.yml`: Runs Android instrumentation tests on an emulator on every push and pull request to the `master` branch.\n\n### Fastlane\n\nThe project uses Fastlane to manage the app's metadata for the Google Play Store. This includes the app's title, description, screenshots, and changelogs. The metadata is stored in the `fastlane/metadata` directory and is organized by language.\n\n\n## How to Build\n\n1.  Clone the repository: `git clone https://github.com/rh-id/a-flash-deck.git`\n2.  Open the project in Android Studio.\n3.  Build the project using Gradle: `./gradlew assembleDebug`\n\n## Libraries Used\n\nThe app uses [a-navigator](https://github.com/rh-id/a-navigator) framework as navigator and `StatefulView` as base structure, combined with [a-provider](https://github.com/rh-id/a-provider) library for service locator, and finally RxAndroid to handle UI use cases.\n\n## Support this project\nConsider donation to support this project\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ca href=\"https://teer.id/rh-id\"\u003ehttps://teer.id/rh-id\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frh-id%2Fa-flash-deck","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frh-id%2Fa-flash-deck","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frh-id%2Fa-flash-deck/lists"}