GreenStash Documentation

repository·main·Indexed 21 days ago

https://github.com/pool-of-tears/greenstash

A FOSS Android application for managing savings goals, built with Kotlin, Jetpack Compose, Room, and Hilt. It features offline-first operation, biometric app lock, and support for 100+ local currency symbols. The documentation covers goal management, transaction tracking, and technical implementation details for database backups (JSON/CSV), reminder scheduling via AlarmManager, and dynamic Android shortcuts.

Tokens
5.9K
Snippets
25
Records
29
Agent score
73%

What's inside GreenStash

  1. Overview of GreenStash

    main

    GreenStash is a Free and Open-Source Software (FOSS) Android application designed to help users plan and manage savings goals. It provides tools to establish saving habits, track progress, and manage financial data locally.

    Key Features:

    • Goal Management: Add images to goals for motivation and view required daily, weekly, or monthly savings amounts to meet deadlines.
    • Transaction Tracking: Detailed history for deposits and withdrawals.
    • Reminders: Customizable daily, semi-weekly, or weekly reminders based on goal priority.
    • Security: Inbuilt biometric app lock for financial data protection.
    • Privacy & Offline First: Fully offline operation; the app does not require internet permissions.
    • Modern UI: Built with Material Design 3 and supports Material You theming (on Android 12+).
    • Localization: Supports 100+ local currency symbols.

    Compatibility:

    • Android 7.0 (API 24) and above.
  2. Restore savings goals from a CSV string

    main

    Use GoalToCSVConverter.convertFromCSV() to parse a CSV string back into a BackupCSVModel. This model contains the schema version, the backup timestamp, and the reconstructed list of GoalWithTransactions.

    The converter includes a compatibility layer for version == 1 backups, which handles legacy deadline formats (string-based dd/MM/yyyy or yyyy/MM/dd) by converting them to epoch milliseconds using parseOldDeadlineToMillis.

    val converter = GoalToCSVConverter()
    val backupModel = converter.convertFromCSV(csvString)
    
    // Access the data
    val goals = backupModel.data
    val version = backupModel.version
  3. Configure widget settings with WidgetConfigViewModel

    main

    The WidgetConfigViewModel is used to manage the configuration of home screen widgets, specifically mapping a widget to a specific saving goal.

    It provides access to all available goals via allGoals and allows you to persist the relationship between a widget and a goal using setWidgetData.

    // Accessing all available goals for selection
    val goals = viewModel.allGoals.value
    
    // Mapping a specific widget to a selected goal
    viewModel.setWidgetData(
        widgetId = currentWidgetId,
        goalId = selectedGoalId
    ) { goalItem -> 
        // Callback triggered on the Main thread after successful database insertion
        // goalItem is the GoalWithTransactions associated with the selected goalId
        println("Widget configured for goal: ${goalItem.goal.name}")
    }
  4. Update notification after a successful deposit

    main

    If a user performs a deposit via a notification action, you can update the existing notification to confirm the transaction using updateWithDepositNotification. This replaces the current reminder with a success message showing the amount deposited, formatted with the user's default currency.

    Requires the goalId and the amount deposited.

    // Updates the notification for a specific goal with the deposited amount
    reminderSender.updateWithDepositNotification(goalId = 123L, amount = 50.0)
  5. Restore a database backup with restoreDatabaseBackup()

    main

    Restores goals and transactions from a backup string (JSON or CSV) back into the database. This method handles the parsing and database insertion logic.

    Parameters:

    • backupString: The raw string content of the backup file.
    • backupType: The format of the string (BackupType.JSON or BackupType.CSV). Defaults to JSON.
    • onFailure: A callback executed on the Main thread if parsing fails or the data is invalid.
    • onSuccess: A callback executed on the Main thread if the restoration completes successfully.

    Note: This is a suspend function and should be called from a coroutine scope.

    backupManager.restoreDatabaseBackup(
        backupString = myBackupString,
        backupType = BackupType.JSON,
        onFailure = { 
            // Handle error on Main thread
        },
        onSuccess = { 
            // Handle success on Main thread
        }
    )
  6. Schedule a savings reminder with ReminderManager

    main

    Use scheduleReminder(goalId: Long) to schedule a daily reminder for a specific goal. The reminder is automatically set for 09:30 AM. If the scheduled time for the current day has already passed, the reminder is set for the same time on the following day.

    This method uses Android's AlarmManager with RTC_WAKEUP to ensure the reminder triggers even if the device is in sleep mode.

    val reminderManager = ReminderManager(context)
    reminderManager.scheduleReminder(goalId = 123L)
  7. Use PreferenceUtil to manage application settings

    main

    The PreferenceUtil class provides a simplified interface for interacting with Android's SharedPreferences to persist application settings. It is initialized with an Android Context and manages a private preferences file named greenstash_settings.

    To use it, instantiate the class with a context and use the provided put and get methods with the predefined constant keys found in the PreferenceUtil.Companion object.

    // Initialize the utility
    val preferenceUtil = PreferenceUtil(context)
    
    // Storing a value
    preferenceUtil.putString(PreferenceUtil.DEFAULT_CURRENCY_STR, "EUR")
    preferenceUtil.putBoolean(PreferenceUtil.APP_LOCK_BOOL, true)
    
    // Retrieving a value
    val currency = preferenceUtil.getString(PreferenceUtil.DEFAULT_CURRENCY_STR, "USD")
    val isLocked = preferenceUtil.getBoolean(PreferenceUtil.APP_LOCK_BOOL, false)
  8. Build dynamic Android shortcuts with MainViewModel

    main

    The buildDynamicShortcuts method allows you to generate a list of ShortcutInfo objects for Android, typically used for home screen shortcuts. It creates one shortcut for creating a 'New Goal' and several shortcuts for existing top-priority goals.

    Shortcut Details:

    • New Goal Shortcut: Uses the action Intent.ACTION_VIEW with the data URI scheme greenstash_lc_shortcut://newGoal and the extra LC_SHORTCUT_NEW_GOAL set to true.
    • Goal Shortcuts: Uses the action Intent.ACTION_VIEW with the data URI scheme greenstash_lc_shortcut://goalId and the extra LC_SHORTCUT_GOAL_ID containing the specific goalId.

    Requirements:

    • Requires API level Build.VERSION_CODES.N_MR1 or higher.
    • Requires a Context and a limit (the total number of shortcuts to return, including the 'New Goal' shortcut).
    mainViewModel.buildDynamicShortcuts(context, limit = 5) { shortcuts ->
        // Use the list of ShortcutInfo to update the Android shortcut manager
    }
  9. Create a database backup with createDatabaseBackup()

    main

    Generates a database backup by converting goals and transaction data into either JSON or CSV format. The method saves the file to the application's cache directory and returns a chooser Intent that allows the user to share the backup file (e.g., via email, cloud storage, or file manager).

    // backupType can be BackupType.JSON or BackupType.CSV
    val chooserIntent = backupManager.createDatabaseBackup(BackupType.JSON)
    // Use the returned intent to start an activity (e.g., via startActivity(chooserIntent))
  10. Bulk schedule reminders for multiple goals

    main

    Use checkAndScheduleReminders(allGoals: List<GoalWithTransactions>) to iterate through a list of goals and schedule reminders for any goal that has its reminder property enabled but does not currently have an active scheduled reminder.

    // Assuming allGoals is a List<GoalWithTransactions> retrieved from the database
    reminderManager.checkAndScheduleReminders(allGoals)
  11. Convert savings goals to and from JSON using GoalToJSONConverter

    main

    The GoalToJSONConverter class provides utilities to serialize and deserialize savings goal data for backup and export purposes. It handles the conversion between the application's internal GoalWithTransactions data models and a JSON string format.

    It includes a built-in compatibility layer that automatically detects and migrates legacy backup files (Version 1) where goal deadlines were stored as strings (e.g., "dd/MM/yyyy") to the current format where deadlines are stored as Long (epoch milliseconds).

    val converter = GoalToJSONConverter()
    
    // Exporting data to JSON
    val goals: List<GoalWithTransactions> = // ... get data from database
    val jsonString = converter.convertToJson(goals)
    
    // Importing data from JSON
    val restoredModel = converter.convertFromJson(jsonString)
    val restoredGoals = restoredModel.data