Skip to content

Latest commit

 

History

History
117 lines (103 loc) · 6.34 KB

ARCHITECTURE.md

File metadata and controls

117 lines (103 loc) · 6.34 KB

Architecture

This project follows the MVVM (Model-View-ViewModel) architecture pattern. The diagram below shows how different components interact with each other as a unit.

Project architecture diagram

Project architecture diagram

Table of Contents

Click to expand

View Layer

The View Layer (managed by MainActivity or Fragment) is responsible for displaying UI components, handling fragment navigation, and interacting with the WebView to render HTML. User interactions are propagated to the ViewModel, which processes and stores the results as a State in LiveData. The Fragment observes the LiveData to update the UI based on changes in the State.

Data Exchange Between Fragments

Each Fragment uses FragmentResultKey, an interface primarily implemented by enums, to ensure unique keys for communication. It provides a key() method, which is used in various ways to manage data exchange between fragments:

  • In Bundle.putString(), Bundle.putInt(), and similar methods to pass fragment results or arguments between fragments.
  • In Fragment.parentFragmentManager.setFragmentResult() to return a fragment result along with a Bundle.
  • In Fragment.parentFragmentManager.setFragmentResultListener() to listen for results from other fragments.
  • In SavedStateHandle.get() to retrieve an argument from a previous fragment.

That way, whenever we want to exchange data between fragments, we can simply check their FragmentResultKey implementations, without worrying about key clashes.

Displaying Chart

D3.js is used as the primary library for rendering charts. The JavaScript files, located in assets/chart/, handle the chart logic and visualization.

To enable interaction between Kotlin and JavaScript, we implement a binding layer in assetbinding/chart/. Each binding maps a JavaScript function to its Kotlin counterpart. JavaScript interacts with Kotlin via the JsInterface class, registered using WebView.addJavascriptInterface() for secure communication.

ViewModel Layer

The ViewModel Layer acts as a bridge between the View Layer and the Model Layer. It handles presentation logic and prepares data to be displayed in the UI. ViewModel are lifecycle-aware, which means they can persist through configuration changes, such as when the device is rotated, and continue to manage UI-related data without being recreated.

Managing UI State

To effectively manage data observed by the UI, this project uses:

  • SafeLiveData: A null-safe wrapper around LiveData for handling persistent and reactive UI data.
  • UiEvent: A generic state wrapper for one-time operation like displaying a snackbar, toast, etc.

Both SafeLiveData and UiEvent are used to represent the UI's State, reflecting its current status — such as text, colors, or other UI properties.

Data Synchronization

The ModelChangedListener is registered with the Repository to listen for changes in the LocalDatabase. Whenever data changes, the Repository notifies all registered listeners.

The ModelSyncListener is usually used alongside ModelSynchronizer or InfoSynchronizer to handle data updates automatically. This eliminates the need to manually override ModelChangedListener methods for each implementation.

Model Layer

The Model Layer serves as the foundation for managing business logic, data processing, and data persistence. It handles interactions between the application and the underlying data sources, such as local databases, remote APIs, or in-memory data structures.

Entities

  • Model: The base interface for all data stored in the LocalDatabase, excluding Full-Text Search (FTS) related models. It represents the complete structure of an entity and is used for operations requiring full data access.
  • Info: A lightweight version of a Model that includes only the necessary fields. It's designed to optimize SQL SELECT queries by reducing overhead when full model details aren't needed.
  • GithubReleaseModel: A class that contains information about app updates retrieved from GitHub.
  • Data Stored in SharedPreferences: User configurations, such as the user's language preference, are stored in SharedPreferences. The data is accessed using specific keys, which are typically defined as constants, like those in the SettingsPreferences.