Ledger is an Android app that helps automate sales orders, customer deposits, and debt tracking. Originally developed to support local shops, it reduces manual work and errors while ensuring accurate bookkeeping. The app scales to thousands of entries with no performance loss and works offline by storing all data locally, requiring no internet connection or user logins.
Direct download available below:
Click to expand
- Sort, filter, and search functionality.
- Visualized charts and statistics.
- Supports English (US) and Indonesian language.
- Dark theme support.
- Full offline.
- Auto-updates with new releases from GitHub.
- Minimum SDK: 30 (Android 11)
- Target SDK: 35 (Android 15)
- Orientations supported: Portrait and landscape
The app has been tested on Android 13 (SDK 33) using a physical device and on Android 15 (SDK 35) through an emulator. If you encounter any issues, please report them by opening a GitHub issue.
Required:
MANAGE_EXTERNAL_STORAGE
: Persist data in external storage (documents directory) even after app uninstallation.
Optional:
ACCESS_NETWORK_STATE
andINTERNET
: Check and download app updates from GitHub.REQUEST_INSTALL_PACKAGES
: Install the downloaded app updates from GitHub.
-
Install Android Studio and make sure the Android SDK is downloaded during setup.
-
Install JDK 17 as this project requires it. Android Studio may bundle its own JDK, but you can configure it to use JDK 17 by following this Gradle JDK configuration guide. Additionally, set the
JAVA_HOME
environment variable to the JDK 17 installation directory. -
Clone this repository and navigate to the project directory:
git clone https://github.com/robifr/ledger.git cd ledger
-
Install Python 3 and set up the virtual environment for generating third-party licenses:
./gradlew setupPythonEnvironment
-
Create
keystore.p12
file to sign the app:keytool -genkeypair -v -keystore <keystore_path> -storetype PKCS12 -keyalg RSA -keysize 2048 -validity 10000 -alias <keystore_alias>
Replace
<keystore_path>
with a relative path like./keystore.p12
and<keystore_alias>
with your chosen alias. Use a secure password when prompted. -
Create a
keystore.properties
file in the project root directory and add the following content:key.alias=<keystore_alias> key.password=<keystore_password> key.storeFile=<keystore_path> key.storePassword=<keystore_password>
Replace the placeholders with the values you used when creating the keystore. Since PKCS12 keystore is being used,
key.password
andkey.storePassword
should be the same.
Caution
Keep the keystore.p12
, keystore.properties
file, and its credentials private. Don't commit
them to version control.
-
Sync dependencies:
./gradlew build
-
It's recommended to run the automated tests before building the app. If everything is successful, build the app:
./gradlew assembleRelease
The generated APK will be located in the
app/build/outputs/apk/release/
directory.
- debug: The default for development. It's fully debuggable and includes tools like LeakCanary for troubleshooting.
- qa: Similar to the release build, but with a different database location and prepopulated data for testing in a near-production setup.
- release: The final production version, optimized for stability and performance. This build is signed and minified.
Use ./gradlew assemble<VariantName>
to build different variants: Debug
, Qa
, or Release
.
A Python script is used to generate a raw formatted text file for third-party licenses, which is displayed on the About > Third-Party Licenses screen.
Always ensure that third-party licenses are up-to-date whenever the dependencies change:
./gradlew licensee
This command collects licenses for the dependencies. However, some third-party licenses that fall
outside the Gradle ecosystem can't be obtained by Licensee.
To address this, you need to manually add those licenses in the unlisted_libraries()
function
within format_licensee.py
. Then, re-run the above command to
reflect the updates.
This project follows the MVVM (Model-View-ViewModel) architecture pattern. Detailed information about the architecture is covered in the ARCHITECTURE.md file.
This project includes both unit tests and Android instrumentation tests. These tests focus on the critical sections of the code, such as the ViewModel and Model layers, as this project uses the MVVM architecture.
-
Unit tests are located in
app/src/test/
. Some tests use fake objects to simulate real dependencies, like when performing database transactions in the repository layer. This approach is simpler than mocking numerous components. Run them with:./gradlew testDebugUnitTest --rerun-tasks
The
--rerun-tasks
flag forces Gradle to re-run the tests and avoid using cached results. -
Android instrumentation tests are located in
app/src/androidTest/
and require a compatible physical device or emulator. Run them with:./gradlew connectedDebugAndroidTest
Code coverage reports can also be generated using JaCoCo. Always
run ./gradlew clean
before generating the reports to remove any stale data. Then run:
./gradlew testDebugUnitTest --rerun-tasks jacocoDebugUnitTestReport
./gradlew connectedDebugAndroidTest jacocoDebugAndroidTestReport
./gradlew jacocoMergeDebugReport
The merged HTML and XML reports, which combine both the unit test and Android test reports, will be
generated in app/build/reports/jacoco/jacocoMergeDebugReport/
.
For Kotlin code, we follow Kotlin coding conventions with these project-specific additions:
- Use explicit types on the left-hand side (LHS) of variable, property, or method declarations.
- Treat platform types as nullable and specify their type explicitly.
- Use an underscore (
_
) prefix for private, protected or internal properties and methods.
Spotless is used to enforce the code style. To format all files according to the project's style, run:
./gradlew spotlessApply
This will format the code and add the header license to files with the following extensions:
.java
, .kt
, .gradle.kts
, .js
, and .html
. While for .py
files, only the header license
will be added, due to Spotless requirement to install black
formatter.
For .xml
files, only the header license will be added by Spotless. We use the Android Studio
formatter for these files, which is configured in the .idea/codeStyles/
directory. To apply it:
- Go to Settings > Editor > Code Style.
- Click the Scheme dropdown and select Project.
- Alternatively, use the Import Scheme option next to the dropdown to import
.idea/codeStyles/Project.xml
if it isn't already applied.
To format the file, right-click on the file or the directory containing it and select Reformat Code.
Ledger is distributed under the terms of the Apache License, Version 2.0. For more information, see the LICENSE file.
Copyright 2024 Robi