From 109a7506215bed68161875496b780deee9bd67c4 Mon Sep 17 00:00:00 2001 From: starry-shivam Date: Thu, 26 Sep 2024 19:12:52 +0530 Subject: [PATCH] feat: Remove Gson & switch to Kotlinx.serialization for serializing backup files Signed-off-by: starry-shivam --- .github/FUNDING.yml | 2 +- .github/ISSUE_TEMPLATE/--bug-report.yml | 2 +- .github/ISSUE_TEMPLATE/--feature-request.yml | 4 +- .../PULL_REQUEST_TEMPLATE/general_template.md | 14 ++- .../translations_template.md | 1 + .github/pull_request_template.md | 7 +- .github/workflows/android.yml | 22 ++--- .github/workflows/release.yml | 6 +- .idea/kotlinc.xml | 2 +- CONTRIBUTING.md | 54 +++++++--- README.md | 76 +++++++++----- app/build.gradle | 5 +- .../starry/greenstash/backup/BackupManager.kt | 14 +-- .../greenstash/backup/BitmapSerializer.kt | 64 ++++++++++++ .../greenstash/backup/BitmapTypeAdapter.kt | 98 ------------------- .../greenstash/backup/GoalToJSONConverter.kt | 30 +++--- .../database/core/GoalWithTransactions.kt | 2 + .../starry/greenstash/database/goal/Goal.kt | 4 + .../database/transaction/Transaction.kt | 2 + app/src/main/res/values/strings.xml | 2 +- build.gradle | 5 +- legal/PRIVACY-POLICY.md | 20 +++- 22 files changed, 242 insertions(+), 194 deletions(-) create mode 100644 app/src/main/java/com/starry/greenstash/backup/BitmapSerializer.kt delete mode 100644 app/src/main/java/com/starry/greenstash/backup/BitmapTypeAdapter.kt diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 6c264452..f6ce0d45 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,3 @@ # These are supported funding model platforms -github: [starry-shivam] +github: [ starry-shivam ] diff --git a/.github/ISSUE_TEMPLATE/--bug-report.yml b/.github/ISSUE_TEMPLATE/--bug-report.yml index 65138646..7abedd6b 100644 --- a/.github/ISSUE_TEMPLATE/--bug-report.yml +++ b/.github/ISSUE_TEMPLATE/--bug-report.yml @@ -1,7 +1,7 @@ name: "\U0001F41E Bug report" description: Create a report to help us improve. title: "[Bug]: " -labels: ["\U0001F41E bug"] +labels: [ "\U0001F41E bug" ] body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/--feature-request.yml b/.github/ISSUE_TEMPLATE/--feature-request.yml index 13c768ea..c2b2ef8f 100644 --- a/.github/ISSUE_TEMPLATE/--feature-request.yml +++ b/.github/ISSUE_TEMPLATE/--feature-request.yml @@ -1,7 +1,7 @@ name: Feature Request description: Suggest a new feature or an UI/UX enhancements. title: "[Feature Request]" -labels: [enhancement] +labels: [ enhancement ] body: - type: checkboxes id: checklist @@ -14,7 +14,7 @@ body: required: true - label: I checked, but didn't find any duplicates (open OR closed) of this issue in the repo. required: true - - label: This feature is merely an UI/UX update. + - label: This feature is merely an UI/UX update. required: false - label: This feature is not going to conflict with many of the existing features/options. required: true diff --git a/.github/PULL_REQUEST_TEMPLATE/general_template.md b/.github/PULL_REQUEST_TEMPLATE/general_template.md index 4c8087a6..3709ef71 100644 --- a/.github/PULL_REQUEST_TEMPLATE/general_template.md +++ b/.github/PULL_REQUEST_TEMPLATE/general_template.md @@ -1,20 +1,28 @@ ### Description + ### Related Issue + -## Type of change +## Type of change + ### Pull Request checklist + + - [ ] The commit message uses the [conventional commiting method][conv-commits]. - [ ] Made sure that your PR is not duplicate -- [ ] **Tests**: This PR includes thorough tests or an explanation of why it does not (for bug fixes/features). Your PR should pass all CI checks in our Gtihub Actions [Workflow](https://github.com/Pool-Of-Tears/GreenStash/actions) -- [ ] **Screenshots**: This PR includes screenshots or GIFs of the changes made or an explanation of why it does not (*optional*) +- [ ] **Tests**: This PR includes thorough tests or an explanation of why it does not (for bug + fixes/features). Your PR should pass all CI checks in our Gtihub + Actions [Workflow](https://github.com/Pool-Of-Tears/GreenStash/actions) +- [ ] **Screenshots**: This PR includes screenshots or GIFs of the changes made or an explanation of + why it does not (*optional*) [conv-commits]:https://kapeli.com/cheat_sheets/Conventional_Commits.docset/Contents/Resources/Documents/index \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE/translations_template.md b/.github/PULL_REQUEST_TEMPLATE/translations_template.md index 32b6ee13..49a8aa3b 100644 --- a/.github/PULL_REQUEST_TEMPLATE/translations_template.md +++ b/.github/PULL_REQUEST_TEMPLATE/translations_template.md @@ -1,4 +1,5 @@ ### Tanslation language + ### Translation checklist diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 7b9679b9..273e1bdc 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,3 +1,6 @@ Please go the the `Preview` tab and select the appropriate sub-template: -- [General PR](?expand=1&template=general_template.md): for all other PRs, including bug fixes, adding features etc. -- [Translation PR](?expand=1&template=translations_template.md): specifically to be used for translation PRs \ No newline at end of file + +- [General PR](?expand=1&template=general_template.md): for all other PRs, including bug fixes, + adding features etc. +- [Translation PR](?expand=1&template=translations_template.md): specifically to be used for + translation PRs \ No newline at end of file diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index d536a1e2..195d3a9e 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -12,15 +12,15 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: set up JDK 17 - uses: actions/setup-java@v3 - with: - java-version: '17' - distribution: 'temurin' - cache: gradle + - uses: actions/checkout@v3 + - name: set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + cache: gradle - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - name: Build with Gradle - run: ./gradlew build + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build with Gradle + run: ./gradlew build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 692b8302..68a6435f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,7 @@ on: push: tags: - 'v*' - + jobs: Build: name: Build/Sign APK @@ -17,7 +17,7 @@ jobs: BUILD_TOOL_VERSION=$(ls /usr/local/lib/android/sdk/build-tools/ | tail -n 1) echo "BUILD_TOOL_VERSION=$BUILD_TOOL_VERSION" >> $GITHUB_ENV echo Last build tool version is: $BUILD_TOOL_VERSION - + - name: set up JDK 17 uses: actions/setup-java@v3 with: @@ -65,7 +65,7 @@ jobs: keyPassword: ${{ secrets.KEY_PASSWORD }} env: BUILD_TOOLS_VERSION: ${{ env.BUILD_TOOL_VERSION }} - + - name: Make artifact uses: actions/upload-artifact@v2 with: diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 6d0ee1c2..d4b7accb 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 38d2c3fb..bddc9c1e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,41 +1,67 @@ # Contributing -For bug reports and feature requests, please search in issues first (including the closed ones). If there are no duplicates, feel free to [submit an issue][issues] with an issue template. +For bug reports and feature requests, please search in issues first (including the closed ones). If +there are no duplicates, feel free to [submit an issue][issues] with an issue template. -**We'll probably ignore and close your issue if it's not using the existing templates or doesn't contain sufficient description.** +**We'll probably ignore and close your issue if it's not using the existing templates or doesn't +contain sufficient description.** ## Bug Report -When submitting a bug report, please make sure your issue contains **enough** information for reproducing the problem, including the options or the custom command being used, the link to the video, and other fields in the issue template. +When submitting a bug report, please make sure your issue contains **enough** information for +reproducing the problem, including the options or the custom command being used, the link to the +video, and other fields in the issue template. ## Feature Request -**GreenStash** is designed to provide users with a straightforward and effective tool for managing their finances on Android. Our focus is to offer essential budgeting functionalities in an intuitive interface, helping users track their savings effortlessly. As such, we prioritize feature requests that align with our core mission and the fundamental principles of budget management. +**GreenStash** is designed to provide users with a straightforward and effective tool for managing +their finances on Android. Our focus is to offer essential budgeting functionalities in an intuitive +interface, helping users track their savings effortlessly. As such, we prioritize feature requests +that align with our core mission and the fundamental principles of budget management. -Our app already includes a range of features designed to assist users in budgeting effectively, such as goal setting and customizable categories. While we are open to suggestions for improvements and enhancements, we may not be able to accommodate requests for features that deviate significantly from our app's primary purpose or that are beyond the scope of traditional budget management. Therefore, we kindly ask our users to consider the app's intended functionality and limitations when submitting feature requests. +Our app already includes a range of features designed to assist users in budgeting effectively, such +as goal setting and customizable categories. While we are open to suggestions for improvements and +enhancements, we may not be able to accommodate requests for features that deviate significantly +from our app's primary purpose or that are beyond the scope of traditional budget management. +Therefore, we kindly ask our users to consider the app's intended functionality and limitations when +submitting feature requests. -We appreciate the feedback and input from our users, and we are committed to continually improving **GreenStash** to meet the evolving needs of our user base. However, please understand that not all feature requests may be feasible or aligned with our app's mission and vision. +We appreciate the feedback and input from our users, and we are committed to continually improving * +*GreenStash** to meet the evolving needs of our user base. However, please understand that not all +feature requests may be feasible or aligned with our app's mission and vision. -If you'd like to request a feature you deem necessary and useful, open a new Github issue with the Feature-Request template [here][feature-req]. +If you'd like to request a feature you deem necessary and useful, open a new Github issue with the +Feature-Request template [here][feature-req]. ## Pull Request -If you wish to contribute to the project by submitting code directly, please first leave a comment under the relevant issue or file a new issue, describe the changes you are about to make. +If you wish to contribute to the project by submitting code directly, please first leave a comment +under the relevant issue or file a new issue, describe the changes you are about to make. -As per our project's guidelines, we adhere to [conventional commits][conv-commits]. Therefore, it's expected that all PRs align with this convention. Should a PR not meet these standards, we'll kindly request a review and revision. +As per our project's guidelines, we adhere to [conventional commits][conv-commits]. Therefore, it's +expected that all PRs align with this convention. Should a PR not meet these standards, we'll kindly +request a review and revision. -Please use the provided Pull Request templates according to your needs. Currently, there are two to choose from: -- [General PR](.github/PULL_REQUEST_TEMPLATE/general_template.md): for all other PRs, including bug fixes, adding features etc. -- [Translation PR](.github/PULL_REQUEST_TEMPLATE/translations_template.md): specifically to be used for translation PRs +Please use the provided Pull Request templates according to your needs. Currently, there are two to +choose from: + +- [General PR](.github/PULL_REQUEST_TEMPLATE/general_template.md): for all other PRs, including bug + fixes, adding features etc. +- [Translation PR](.github/PULL_REQUEST_TEMPLATE/translations_template.md): specifically to be used + for translation PRs > [!TIP] > -> To avoid multiple pull requests resolving the same issue, let others know you are working on it by saying so in a comment, or ask the issue to be assigned to yourself. +> To avoid multiple pull requests resolving the same issue, let others know you are working on it by +> saying so in a comment, or ask the issue to be assigned to yourself. ## Building From Source -Fork this project, import and compile it with the latest version of [Android Studio](https://developer.android.com/studio/). +Fork this project, import and compile it with the latest version +of [Android Studio](https://developer.android.com/studio/). [issues]: https://github.com/Pool-Of-Tears/GreenStash/issues/new/choose + [feature-req]: https://github.com/Pool-Of-Tears/GreenStash/issues/new?assignees=&labels=enhancement&projects=&template=--feature-request.yml&title=%5BFeature+Request%5D + [conv-commits]:https://kapeli.com/cheat_sheets/Conventional_Commits.docset/Contents/Resources/Documents/index \ No newline at end of file diff --git a/README.md b/README.md index 58a873d3..9ca01764 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,9 @@ ------ -**GreenStash** is a simple [FOSS](https://en.m.wikipedia.org/wiki/Free_and_open-source_software) android app to help you plan and manage your savings goals easily and establish the habit of saving money. +**GreenStash** is a simple [FOSS](https://en.m.wikipedia.org/wiki/Free_and_open-source_software) +android app to help you plan and manage your savings goals easily and establish the habit of saving +money. ------ @@ -32,17 +34,20 @@

Highlights

-- Clean & beautiful UI based on Google's [material design three](https://m3.material.io/) guidelines. +- Clean & beautiful UI based on Google's [material design three](https://m3.material.io/) + guidelines. - Add images to your saving goals to keep you motivated! - View how much you need to save daily/weekly/monthly to achieve your goal before deadline. - View detailed transaction (withdraw/deposit) history. -- Get daily, semi-weekly or weekly reminders for your saving goals based on goal priority. +- Get daily, semi-weekly or weekly reminders for your saving goals based on goal priority. - Supports around 100+ local currency symbols. - Inbuilt biometric app lock to keep your financial data safe and secure. - Fully offline, greenstash doens't require internet permission to work. - Compatible with Android 7.0 and above (API 24+) -- Supports [Material You](https://www.androidpolice.com/everything-we-love-about-material-you/amp/) theming in devices running on Android 12+ -- MAD: UI and logic written with pure Kotlin. Single activity, no fragments, only composable destinations. +- Supports [Material You](https://www.androidpolice.com/everything-we-love-about-material-you/amp/) + theming in devices running on Android 12+ +- MAD: UI and logic written with pure Kotlin. Single activity, no fragments, only composable + destinations. ------ @@ -60,46 +65,68 @@

Donations

GreenStash doesn't contain any ads and doesn't sell your data. -The development of the app is financed by individual user contributions, such as you purchasing the app via Google Play or becoming a sponsor on Github ❤️ +The development of the app is financed by individual user contributions, such as you purchasing the +app via Google Play or becoming a sponsor on Github ❤️ -Become a [Sponsor](https://github.com/sponsors/starry-shivam) on Github | Purchase it on [Google Play](https://play.google.com/store/apps/details?id=com.starry.greenstash) +Become a [Sponsor](https://github.com/sponsors/starry-shivam) on Github | Purchase it +on [Google Play](https://play.google.com/store/apps/details?id=com.starry.greenstash) ------

Contributions

-Contributions are welcome! +Contributions are welcome! ->[!Note] +> [!Note] > ->For submitting bug reports, feature requests, questions, or any other ideas to improve, please read [CONTRIBUTING.md](/CONTRIBUTING.md) for instructions and guidelines first. +>For submitting bug reports, feature requests, questions, or any other ideas to improve, please +> read [CONTRIBUTING.md](/CONTRIBUTING.md) for instructions and guidelines first. ------

Translations

-If you want to make the app available in your language, you're welcome to create a pull request with your translation file. The base string resources can be found under: +If you want to make the app available in your language, you're welcome to create a pull request with +your translation file. The base string resources can be found under: + ``` /app/src/main/res/values/strings.xml ``` -It is easiest to make a translation using the Android Studio XML editor, but you can always use your favorite XML text editor instead. Check out this guide to learn more about translation strings from [Helpshift](https://developers.helpshift.com/android/i18n/) for Android. + +It is easiest to make a translation using the Android Studio XML editor, but you can always use your +favorite XML text editor instead. Check out this guide to learn more about translation strings +from [Helpshift](https://developers.helpshift.com/android/i18n/) for Android. ------

Tech Stack

-- [Kotlin](https://kotlinlang.org/) - First class and official programming language for Android development. -- [Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html) - To improve performance by doing I/O tasks out of main thread asynchronously. -- [Flow](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/) - A cold asynchronous data stream that sequentially emits values and completes normally or with an exception. -- [Android Architecture Components](https://developer.android.com/topic/libraries/architecture) - Collection of libraries that help you design robust, testable, and maintainable apps. - - [Jetpack Compose](https://developer.android.com/jetpack/compose?gclsrc=ds&gclsrc=ds) - Jetpack Compose is Android’s recommended modern toolkit for building native UI - - [LiveData](https://developer.android.com/topic/libraries/architecture/livedata) - Data objects that notify views when the underlying database changes. - - [ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel) - Stores UI-related data that isn't destroyed on UI changes. -- [Lottie](https://airbnb.design/lottie) - Lottie is an Android, iOS and React Native library that renders After Effects animations in real time. -- [Coil](https://coil-kt.github.io/coil/compose) - An image loading library for Android backed by Kotlin Coroutines. -- [Gson](https://github.com/google/gson) - A Java serialization/deserialization library to convert Java Objects into JSON and back. -- [Dagger-Hilt](https://dagger.dev/hilt) For [Dependency injection (DI)](https://developer.android.com/training/dependency-injection) -- [Room database](https://developer.android.com/jetpack/androidx/releases/room) - Persistence library provides an abstraction layer over SQLite to allow for more robust database access while harnessing the full power of SQLite. +- [Kotlin](https://kotlinlang.org/) - First class and official programming language for Android + development. +- [Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html) - To improve + performance by doing I/O tasks out of main thread asynchronously. +- [Flow](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/) - + A cold asynchronous data stream that sequentially emits values and completes normally or with an + exception. +- [Android Architecture Components](https://developer.android.com/topic/libraries/architecture) - + Collection of libraries that help you design robust, testable, and maintainable apps. + - [Jetpack Compose](https://developer.android.com/jetpack/compose?gclsrc=ds&gclsrc=ds) - Jetpack + Compose is Android’s recommended modern toolkit for building native UI + - [LiveData](https://developer.android.com/topic/libraries/architecture/livedata) - Data objects + that notify views when the underlying database changes. + - [ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel) - Stores + UI-related data that isn't destroyed on UI changes. +- [Lottie](https://airbnb.design/lottie) - Lottie is an Android, iOS and React Native library that + renders After Effects animations in real time. +- [Coil](https://coil-kt.github.io/coil/compose) - An image loading library for Android backed by + Kotlin Coroutines. +- [Kotlinx.serialization](https://kotlinlang.org/docs/serialization.html) - Provides sets of + libraries for various serialization formats – JSON, CBOR, protocol buffers, and others. +- [Dagger-Hilt](https://dagger.dev/hilt) + For [Dependency injection (DI)](https://developer.android.com/training/dependency-injection) +- [Room database](https://developer.android.com/jetpack/androidx/releases/room) - Persistence + library provides an abstraction layer over SQLite to allow for more robust database access while + harnessing the full power of SQLite. ------ @@ -114,6 +141,7 @@ It is easiest to make a translation using the Android Studio XML editor, but you [MIT License][license] © [Stɑrry Shivɑm][github] [license]: /LICENSE + [github]: https://github.com/starry-shivam ``` diff --git a/app/build.gradle b/app/build.gradle index aa55e1dc..008864f5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,6 +4,7 @@ plugins { id 'org.jetbrains.kotlin.plugin.compose' id 'dagger.hilt.android.plugin' id 'com.google.devtools.ksp' + id 'org.jetbrains.kotlin.plugin.serialization' id "com.mikepenz.aboutlibraries.plugin" version "11.1.3" } @@ -116,8 +117,8 @@ dependencies { ksp "androidx.hilt:hilt-compiler:1.2.0" // DataStore Preferences. implementation("androidx.datastore:datastore-preferences:1.1.1") - // Gson JSON parser. - implementation 'com.google.code.gson:gson:2.10.1' + // Kotlin Serialization. + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3" // Coil Image loading library. implementation "io.coil-kt:coil-compose:2.6.0" // Material 3 calender / Date picker. diff --git a/app/src/main/java/com/starry/greenstash/backup/BackupManager.kt b/app/src/main/java/com/starry/greenstash/backup/BackupManager.kt index 0a06244a..37638c23 100644 --- a/app/src/main/java/com/starry/greenstash/backup/BackupManager.kt +++ b/app/src/main/java/com/starry/greenstash/backup/BackupManager.kt @@ -81,8 +81,8 @@ class BackupManager(private val context: Context, private val goalDao: GoalDao) log("Fetching goals from database and serialising into ${backupType.name}...") val goalsWithTransactions = goalDao.getAllGoals() val backupString = when (backupType) { - BackupType.JSON -> goalToJsonConverter.convertToJson(goalsWithTransactions) - BackupType.CSV -> goalToCsvConverter.convertToCSV(goalsWithTransactions) + JSON -> goalToJsonConverter.convertToJson(goalsWithTransactions) + CSV -> goalToCsvConverter.convertToCSV(goalsWithTransactions) } log("Creating a ${backupType.name} file inside cache directory...") @@ -93,8 +93,8 @@ class BackupManager(private val context: Context, private val goalDao: GoalDao) log("Building and returning chooser intent for backup file.") val intentType = when (backupType) { - BackupType.JSON -> "application/json" - BackupType.CSV -> "text/csv" + JSON -> "application/json" + CSV -> "text/csv" } return@withContext Intent(Intent.ACTION_SEND).apply { type = intentType @@ -115,14 +115,14 @@ class BackupManager(private val context: Context, private val goalDao: GoalDao) */ suspend fun restoreDatabaseBackup( backupString: String, - backupType: BackupType = BackupType.JSON, + backupType: BackupType = JSON, onFailure: () -> Unit, onSuccess: () -> Unit, ) = withContext(Dispatchers.IO) { log("Parsing backup file...") when (backupType) { - BackupType.JSON -> restoreJsonBackup(backupString, onFailure, onSuccess) - BackupType.CSV -> restoreCsvBackup(backupString, onFailure, onSuccess) + JSON -> restoreJsonBackup(backupString, onFailure, onSuccess) + CSV -> restoreCsvBackup(backupString, onFailure, onSuccess) } } diff --git a/app/src/main/java/com/starry/greenstash/backup/BitmapSerializer.kt b/app/src/main/java/com/starry/greenstash/backup/BitmapSerializer.kt new file mode 100644 index 00000000..f554d1bc --- /dev/null +++ b/app/src/main/java/com/starry/greenstash/backup/BitmapSerializer.kt @@ -0,0 +1,64 @@ +/** + * MIT License + * + * Copyright (c) [2022 - Present] Stɑrry Shivɑm + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +package com.starry.greenstash.backup + +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import java.io.ByteArrayOutputStream + +/** + * KSerializer for serializing and deserializing goal images which + * are stored as [Bitmap] in the database. + */ +object BitmapSerializer : KSerializer { + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor("Bitmap", PrimitiveKind.STRING) + + // Encode the Bitmap to a Base64 string + override fun serialize(encoder: Encoder, value: Bitmap) { + val stream = ByteArrayOutputStream() + value.compress(Bitmap.CompressFormat.PNG, 100, stream) + val byteArray = stream.toByteArray() + encoder.encodeString( + android.util.Base64.encodeToString( + byteArray, android.util.Base64.NO_WRAP + ) + ) + } + + // Decode the Base64 string to a Bitmap + override fun deserialize(decoder: Decoder): Bitmap { + val byteArray = + android.util.Base64.decode(decoder.decodeString(), android.util.Base64.NO_WRAP) + return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/starry/greenstash/backup/BitmapTypeAdapter.kt b/app/src/main/java/com/starry/greenstash/backup/BitmapTypeAdapter.kt deleted file mode 100644 index 07639230..00000000 --- a/app/src/main/java/com/starry/greenstash/backup/BitmapTypeAdapter.kt +++ /dev/null @@ -1,98 +0,0 @@ -/** - * MIT License - * - * Copyright (c) [2022 - Present] Stɑrry Shivɑm - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - - -package com.starry.greenstash.backup - -import android.graphics.Bitmap -import android.graphics.BitmapFactory -import android.util.Base64 -import com.google.gson.JsonDeserializationContext -import com.google.gson.JsonDeserializer -import com.google.gson.JsonElement -import com.google.gson.JsonParseException -import com.google.gson.JsonPrimitive -import com.google.gson.JsonSerializationContext -import com.google.gson.JsonSerializer -import java.io.ByteArrayOutputStream -import java.lang.reflect.Type - - -/** - * Gson type adaptor used for serializing and deserializing goal image which is - * stored as [Bitmap] in the database. - * Currently used for backup & restore functionality. - */ -class BitmapTypeAdapter : JsonSerializer, JsonDeserializer { - - /** - * Gson invokes this call-back method during serialization when it encounters a field of the - * specified type. - * - * - * In the implementation of this call-back method, you should consider invoking - * [JsonSerializationContext.serialize] method to create JsonElements for any - * non-trivial field of the `src` object. However, you should never invoke it on the - * `src` object itself since that will cause an infinite loop (Gson will call your - * call-back method again). - * - * @param src the object that needs to be converted to Json. - * @param typeOfSrc the actual type (fully genericized version) of the source object. - * @return a JsonElement corresponding to the specified object. - */ - override fun serialize( - src: Bitmap?, typeOfSrc: Type?, context: JsonSerializationContext? - ): JsonElement { - val byteArrayOutputStream = ByteArrayOutputStream() - src?.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream) - return JsonPrimitive( - Base64.encodeToString( - byteArrayOutputStream.toByteArray(), Base64.NO_WRAP - ) - ) - } - - /** - * Gson invokes this call-back method during deserialization when it encounters a field of the - * specified type. - * - * In the implementation of this call-back method, you should consider invoking - * [JsonDeserializationContext.deserialize] method to create objects - * for any non-trivial field of the returned object. However, you should never invoke it on the - * the same type passing `json` since that will cause an infinite loop (Gson will call your - * call-back method again). - * - * @param json The Json data being deserialized - * @param typeOfT The type of the Object to deserialize to - * @return a deserialized object of the specified type typeOfT which is a subclass of `T` - * @throws JsonParseException if json is not in the expected format of `typeofT` - */ - override fun deserialize( - json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext? - ): Bitmap? { - if (json?.asString == null) return null - val byteArray: ByteArray = Base64.decode(json.asString, Base64.NO_WRAP) - return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.count()) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/starry/greenstash/backup/GoalToJSONConverter.kt b/app/src/main/java/com/starry/greenstash/backup/GoalToJSONConverter.kt index 2bbd6836..455d71fe 100644 --- a/app/src/main/java/com/starry/greenstash/backup/GoalToJSONConverter.kt +++ b/app/src/main/java/com/starry/greenstash/backup/GoalToJSONConverter.kt @@ -25,32 +25,26 @@ package com.starry.greenstash.backup -import android.graphics.Bitmap import androidx.annotation.Keep -import com.google.gson.Gson -import com.google.gson.GsonBuilder import com.starry.greenstash.database.core.GoalWithTransactions +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json /** * Converts [GoalWithTransactions] data to JSON format and vice versa. */ class GoalToJSONConverter { - /** - * Instance of [Gson] with custom type adaptor applied for serializing - * and deserializing [Bitmap] fields. - */ - private val gsonInstance = GsonBuilder() - .registerTypeAdapter(Bitmap::class.java, BitmapTypeAdapter()) - .setDateFormat(ISO8601_DATE_FORMAT) - .create() + // JSON serializer/deserializer. + private val json = Json { + ignoreUnknownKeys = true + encodeDefaults = true + explicitNulls = false + } companion object { /** Backup schema version. */ const val BACKUP_SCHEMA_VERSION = 1 - - /** An ISO-8601 date format for Gson */ - private const val ISO8601_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" } /** @@ -62,6 +56,7 @@ class GoalToJSONConverter { * @param data list of [GoalWithTransactions] to be backed up. */ @Keep + @Serializable data class BackupJsonModel( val version: Int = BACKUP_SCHEMA_VERSION, val timestamp: Long, @@ -69,10 +64,11 @@ class GoalToJSONConverter { ) fun convertToJson(goalWithTransactions: List): String = - gsonInstance.toJson( + json.encodeToString( + BackupJsonModel.serializer(), BackupJsonModel(timestamp = System.currentTimeMillis(), data = goalWithTransactions) ) - fun convertFromJson(json: String): BackupJsonModel = - gsonInstance.fromJson(json, BackupJsonModel::class.java) + fun convertFromJson(jsonString: String): BackupJsonModel = + json.decodeFromString(BackupJsonModel.serializer(), jsonString) } \ No newline at end of file diff --git a/app/src/main/java/com/starry/greenstash/database/core/GoalWithTransactions.kt b/app/src/main/java/com/starry/greenstash/database/core/GoalWithTransactions.kt index b36db1ff..ceb74bd0 100644 --- a/app/src/main/java/com/starry/greenstash/database/core/GoalWithTransactions.kt +++ b/app/src/main/java/com/starry/greenstash/database/core/GoalWithTransactions.kt @@ -31,8 +31,10 @@ import androidx.room.Relation import com.starry.greenstash.database.goal.Goal import com.starry.greenstash.database.transaction.Transaction import com.starry.greenstash.database.transaction.TransactionType +import kotlinx.serialization.Serializable @Keep +@Serializable data class GoalWithTransactions( @Embedded val goal: Goal, @Relation( diff --git a/app/src/main/java/com/starry/greenstash/database/goal/Goal.kt b/app/src/main/java/com/starry/greenstash/database/goal/Goal.kt index d2308007..919028c2 100644 --- a/app/src/main/java/com/starry/greenstash/database/goal/Goal.kt +++ b/app/src/main/java/com/starry/greenstash/database/goal/Goal.kt @@ -30,15 +30,19 @@ import androidx.annotation.Keep import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey +import com.starry.greenstash.backup.BitmapSerializer +import kotlinx.serialization.Serializable enum class GoalPriority(val value: Int) { High(3), Normal(2), Low(1) } @Keep +@Serializable @Entity(tableName = "saving_goal") data class Goal( val title: String, val targetAmount: Double, val deadline: String, + @Serializable(with = BitmapSerializer::class) val goalImage: Bitmap?, val additionalNotes: String, diff --git a/app/src/main/java/com/starry/greenstash/database/transaction/Transaction.kt b/app/src/main/java/com/starry/greenstash/database/transaction/Transaction.kt index 8482bcc3..98d063b1 100644 --- a/app/src/main/java/com/starry/greenstash/database/transaction/Transaction.kt +++ b/app/src/main/java/com/starry/greenstash/database/transaction/Transaction.kt @@ -31,6 +31,7 @@ import androidx.room.Entity import androidx.room.ForeignKey import androidx.room.PrimaryKey import com.starry.greenstash.database.goal.Goal +import kotlinx.serialization.Serializable import java.text.DateFormat import java.util.Date @@ -39,6 +40,7 @@ enum class TransactionType { } @Keep +@Serializable @Entity( tableName = "transaction", foreignKeys = [ ForeignKey( diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2b150657..24df0835 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -153,7 +153,7 @@ Please note that backups do not include app settings. Backup Restore - Select backup file type + Select backup file format Note: Due to limitations of the CSV format, goal images will not be backed up. Create Backup Restore Backup diff --git a/build.gradle b/build.gradle index c5d6a82d..a183eda2 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ buildscript { ext { - kotlin_version = '2.0.0' + kotlin_version = '2.0.20' gradle_version = '8.6.1' hilt_version = '2.51.1' room_version = '2.6.1' @@ -23,5 +23,6 @@ plugins { id 'com.android.library' version "$gradle_version" apply false id 'org.jetbrains.kotlin.android' version "$kotlin_version" apply false id 'org.jetbrains.kotlin.plugin.compose' version "$kotlin_version" apply false - id 'com.google.devtools.ksp' version '2.0.0-1.0.21' apply false + id 'com.google.devtools.ksp' version '2.0.20-1.0.25' apply false + id 'org.jetbrains.kotlin.plugin.serialization' version "$kotlin_version" apply false } \ No newline at end of file diff --git a/legal/PRIVACY-POLICY.md b/legal/PRIVACY-POLICY.md index 20236af9..1e979931 100644 --- a/legal/PRIVACY-POLICY.md +++ b/legal/PRIVACY-POLICY.md @@ -1,17 +1,27 @@ ## Privacy Policy -This Privacy Policy statement is made by Pool-Of-Tears, consisting of all the entities listed here (collectively referred to as "Pool-Of-Tears," "we," "us," or "our"). Our mission is to help people protect their privacy, data, and devices from online threats. GreenStash does not track you with ads or abuse your privacy. We do not require you to create any kind of account with your personally identifiable information in order to use the app. +This Privacy Policy statement is made by Pool-Of-Tears, consisting of all the entities listed here ( +collectively referred to as "Pool-Of-Tears," "we," "us," or "our"). Our mission is to help people +protect their privacy, data, and devices from online threats. GreenStash does not track you with ads +or abuse your privacy. We do not require you to create any kind of account with your personally +identifiable information in order to use the app. -GreenStash is a completely offline application and does not require internet permission to work. All of your data is stored locally on your device's internal storage and never leaves your device. +GreenStash is a completely offline application and does not require internet permission to work. All +of your data is stored locally on your device's internal storage and never leaves your device. #### Terms of Use -Thank you so much for using GreenStash. To protect the app and our users, we need this user agreement to set rules that are required to use our software. GreenStash is free and open-source software, which is totally free to use for non-commercial purposes as long as you agree to our privacy policies. +Thank you so much for using GreenStash. To protect the app and our users, we need this user +agreement to set rules that are required to use our software. GreenStash is free and open-source +software, which is totally free to use for non-commercial purposes as long as you agree to our +privacy policies. -If you have found a bug or vulnerability in the GreenStash app, there are several ways to report the problem so we can fix it: +If you have found a bug or vulnerability in the GreenStash app, there are several ways to report the +problem so we can fix it: - Writing us an email with a detailed description of the issue you're facing. - Contacting us in our [Telegram](https://t.me/PotApps) support group. -- Creating an issue on the [GitHub](https://github.com/Pool-Of-Tears/GreenStash) repository of the app. +- Creating an issue on the [GitHub](https://github.com/Pool-Of-Tears/GreenStash) repository of the + app. This Privacy Policy is subject to updates without any prior notice.