Repository: g0dkar/qrcode-kotlin Branch: main Commit: 215e706abb6e Files: 149 Total size: 992.3 KB Directory structure: gitextract_a8h010na/ ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── feature_request.md │ ├── renovate.json │ └── workflows/ │ ├── dokka-update.yml │ └── run-tests.yml ├── .gitignore ├── .run/ │ ├── Build.run.xml │ ├── Publish Distribution - Sonar.run.xml │ ├── Publish to Local.run.xml │ └── Run Tests.run.xml ├── CHANGELOG.md ├── CNAME ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── README.pt-br.md ├── _config.yml ├── build.gradle.kts ├── examples/ │ ├── android/ │ │ ├── build.gradle.kts │ │ ├── proguard-rules.pro │ │ └── src/ │ │ └── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── io/ │ │ │ └── github/ │ │ │ └── g0dkar/ │ │ │ └── qrcode/ │ │ │ ├── AboutActivity.kt │ │ │ ├── NewQRCodeActivity.kt │ │ │ ├── QRCodeData.kt │ │ │ ├── QRCodeDetailActivity.kt │ │ │ ├── QRCodeListActivity.kt │ │ │ └── extra/ │ │ │ ├── QRCodeListAdapter.kt │ │ │ └── QRCodeListDatasource.kt │ │ └── res/ │ │ ├── drawable/ │ │ │ └── ic_launcher_background.xml │ │ ├── drawable-v24/ │ │ │ └── ic_launcher_foreground.xml │ │ ├── layout/ │ │ │ ├── activity_about.xml │ │ │ ├── activity_main.xml │ │ │ ├── activity_new_qrcode.xml │ │ │ ├── activity_qrcode_detail.xml │ │ │ └── qrcode_list_item.xml │ │ ├── menu/ │ │ │ └── menu_main.xml │ │ ├── mipmap-anydpi-v26/ │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── values/ │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── themes.xml │ │ ├── values-land/ │ │ │ └── dimens.xml │ │ ├── values-night/ │ │ │ └── themes.xml │ │ ├── values-pt-rBR/ │ │ │ └── strings.xml │ │ ├── values-w1240dp/ │ │ │ └── dimens.xml │ │ ├── values-w600dp/ │ │ │ └── dimens.xml │ │ └── xml/ │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ ├── iosApp/ │ │ ├── .gitignore │ │ ├── iosApp/ │ │ │ ├── Assets.xcassets/ │ │ │ │ ├── AccentColor.colorset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ ├── Preview Content/ │ │ │ │ └── Preview Assets.xcassets/ │ │ │ │ └── Contents.json │ │ │ ├── presentation/ │ │ │ │ ├── ContentView.swift │ │ │ │ └── iosApp.swift │ │ │ └── utils/ │ │ │ └── NativeParser.swift │ │ ├── iosApp.xcodeproj/ │ │ │ └── project.pbxproj │ │ ├── iosAppTests/ │ │ │ └── iosAppTests.swift │ │ └── iosAppUITests/ │ │ ├── iosAppUITests.swift │ │ └── iosAppUITestsLaunchTests.swift │ ├── java/ │ │ ├── build.gradle.kts │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── examples/ │ │ ├── Example01_Shapes.java │ │ ├── Example02_Colors.java │ │ ├── Example03_SVG.java │ │ ├── Util.java │ │ ├── customClasses/ │ │ │ ├── JVMTriangleShapeFunction.java │ │ │ └── TriangleShapeFunction.java │ │ └── svg/ │ │ ├── SVGGraphicsFactory.java │ │ └── SVGQRCodeGraphics.java │ ├── js/ │ │ ├── qrcode-example.html │ │ └── qrcode-kotlin.js │ ├── kotlin/ │ │ ├── build.gradle.kts │ │ └── src/ │ │ └── main/ │ │ └── kotlin/ │ │ ├── Example00-Simple.kt │ │ ├── Example01-Shapes.kt │ │ ├── Example02-Colors.kt │ │ ├── Example03-Logo.kt │ │ ├── Example04-SVG.kt │ │ ├── Example05-BackwardsCompat.kt │ │ ├── Example06-ECL.kt │ │ ├── Example07-MaskPattern.kt │ │ ├── ProjectLogo.kt │ │ └── svg/ │ │ ├── SVGGraphicsFactory.kt │ │ └── SVGQRCodeGraphics.kt │ └── spring-web/ │ ├── .gitignore │ ├── build.gradle.kts │ └── src/ │ └── main/ │ ├── kotlin/ │ │ └── io/ │ │ └── github/ │ │ └── g0dkar/ │ │ └── qrcode/ │ │ └── springWebExample/ │ │ ├── Launcher.kt │ │ ├── QRCodeController.kt │ │ └── QRCodeService.kt │ └── resources/ │ └── application.yml ├── gradle/ │ ├── libs.versions.toml │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── package.json ├── release/ │ ├── qrcode-kotlin-jvm-4.4.1.jar │ ├── qrcode-kotlin.d.ts │ └── qrcode-kotlin.js ├── settings.gradle.kts └── src/ ├── androidMain/ │ └── kotlin/ │ └── qrcode/ │ └── render/ │ ├── QRCodeGraphics.android.kt │ ├── extensions/ │ │ └── AndroidComposeExtensions.kt │ └── graphics/ │ ├── AndroidDrawingInterface.kt │ ├── BitmapGraphics.kt │ └── DrawScopeGraphics.kt ├── commonMain/ │ └── kotlin/ │ └── qrcode/ │ ├── QRCode.kt │ ├── QRCodeBuilder.kt │ ├── QRCodeShapesEnum.kt │ ├── color/ │ │ ├── ColorType.kt │ │ ├── Colors.kt │ │ ├── DefaultColorFunction.kt │ │ ├── LinearGradientColorFunction.kt │ │ └── QRCodeColorFunction.kt │ ├── exception/ │ │ └── InsufficientInformationDensityException.kt │ ├── internals/ │ │ ├── BitBuffer.kt │ │ ├── ErrorMessage.kt │ │ ├── Polynomial.kt │ │ ├── QRCodeSetup.kt │ │ ├── QRCodeSquare.kt │ │ ├── QRData.kt │ │ ├── QRMath.kt │ │ ├── QRUtil.kt │ │ └── RSBlock.kt │ ├── raw/ │ │ ├── QRCodeEnums.kt │ │ ├── QRCodeProcessor.kt │ │ └── QRCodeRawData.kt │ ├── render/ │ │ ├── QRCodeGraphics.kt │ │ └── QRCodeGraphicsFactory.kt │ └── shape/ │ ├── CircleShapeFunction.kt │ ├── DefaultShapeFunction.kt │ ├── QRCodeShapeFunction.kt │ └── RoundSquaresShapeFunction.kt ├── commonTest/ │ └── kotlin/ │ └── qrcode/ │ └── internals/ │ ├── PolynomialTest.kt │ └── QRNumberTest.kt ├── iosMain/ │ └── kotlin/ │ ├── qrcode/ │ │ └── render/ │ │ └── QRCodeGraphics.ios.kt │ └── utils/ │ └── IOSNativeParser.kt ├── jsMain/ │ └── kotlin/ │ └── qrcode/ │ └── render/ │ └── QRCodeGraphics.js.kt ├── jvmMain/ │ └── kotlin/ │ └── qrcode/ │ └── render/ │ ├── JvmQRCodeGraphicsFactory.kt │ └── QRCodeGraphics.jvm.kt ├── jvmTest/ │ └── kotlin/ │ └── qrcode/ │ ├── QRCodeTest.kt │ ├── TestUtils.kt │ └── render/ │ └── ColorsTest.kt ├── tvosMain/ │ └── kotlin/ │ ├── qrcode/ │ │ └── render/ │ │ └── QRCodeGraphics.tvos.kt │ └── utils/ │ └── TvOSNativeParser.kt └── wasmJsMain/ └── kotlin/ └── qrcode/ └── render/ └── QRCodeGraphics.wasmjs.kt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ root = true [*] charset = utf-8 trim_trailing_whitespace = true end_of_line = lf [*.{kt,kts,md}] ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL ij_kotlin_allow_trailing_comma = true ij_kotlin_allow_trailing_comma_on_call_site = true ij_kotlin_imports_layout = *,java.**,javax.**,kotlin.**,^ ij_kotlin_packages_to_use_import_on_demand = kotlinx.android.synthetic.** ij_kotlin_name_count_to_use_star_import = 100 ij_kotlin_name_count_to_use_star_import_for_members = 100 indent_size = 4 indent_style = space insert_final_newline = true ktlint_code_style = official ktlint_function_signature_body_expression_wrapping = default ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = -1 ktlint_ignore_back_ticked_identifier = false ktlint_no-wildcard-imports = disabled ktlint_standard_no-wildcard-imports = disabled max_line_length = 120 [**/test/**.kt] ktlint_ignore_back_ticked_identifier = true ================================================ FILE: .gitattributes ================================================ # # https://help.github.com/articles/dealing-with-line-endings/ # # These are explicitly windows files and should use crlf /gradlew text eol=lf *.bat text eol=crlf *.jar binary ================================================ FILE: .github/FUNDING.yml ================================================ github: g0dkar ko_fi: 'g0dkar' #custom: 'https://min.immo' ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: '' labels: bug assignees: g0dkar --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior. For example: 1. Create a QRCode instance with `data = "answer to life, universe and everything"` 2. Invoke `render(cellSize = 42)` 3. ??? 4. See error **Expected behavior** **Screenshots or other QRCodes rendered with other tools** **Additional context** ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. For example: I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/renovate.json ================================================ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "config:best-practices" ], "automerge": true, "automergeType": "pr", "automergeStrategy": "squash", "schedule": ["on the first day of the month"] } ================================================ FILE: .github/workflows/dokka-update.yml ================================================ name: Dokka Update on: push: branches: [ "gh-page" ] jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 with: repository: ${{ github.event.pull_request.head.repo.full_name }} ref: ${{ github.event.pull_request.head.ref }} - name: Set up JDK 17 uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4 with: java-version: "17" distribution: "temurin" cache: gradle - name: Give execute permission to ./gradlew run: chmod +x ./gradlew - name: Run Dokka Update run: "./gradlew dokkaGenerate" - name: Commit Changes uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9 with: message: "[CI] Dokka Update" committer_name: "GitHub Actions" committer_email: "actions@github.com" ================================================ FILE: .github/workflows/run-tests.yml ================================================ name: Run Tests on: push: branches: [ "main" ] pull_request: branches: [ "main" ] jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 with: repository: ${{ github.event.pull_request.head.repo.full_name }} ref: ${{ github.event.pull_request.head.ref }} - name: Set up JDK 17 uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4 with: java-version: "17" distribution: "temurin" cache: gradle - name: Give execute permission to ./gradlew run: chmod +x ./gradlew - name: Build and Publish Locally run: "./gradlew clean :build :publishToMavenLocal -x :test -x :jvmTest -Pkotlin.incremental.useClasspathSnapshot=false" - name: Run Tests run: "./gradlew test -Pkotlin.incremental.useClasspathSnapshot=false" ================================================ FILE: .gitignore ================================================ HELP.md target/ !.mvn/wrapper/maven-wrapper.jar !**/src/main/** !**/src/test/** .gradle !**/src/main/**/build/ !**/src/test/**/build/ ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr local.properties ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ build/ ### VS Code ### .vscode/ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /**/node_modules /**/.jekyll-cache /**/_site /.pnp .pnp.js # testing /coverage # production /build # misc .DS_Store .env.local .env.development.local .env.test.local .env.production.local /examples/gradle* npm-debug.log* yarn-debug.log* yarn-error.log* /kotlin-js-store/ .kotlin/ #/package.json #/package-lock.json ================================================ FILE: .run/Build.run.xml ================================================ true true false false ================================================ FILE: .run/Publish Distribution - Sonar.run.xml ================================================ true true false false ================================================ FILE: .run/Publish to Local.run.xml ================================================ true true false false ================================================ FILE: .run/Run Tests.run.xml ================================================ true true false false ================================================ FILE: CHANGELOG.md ================================================ [![License](https://img.shields.io/github/license/g0dkar/qrcode-kotlin)](LICENSE) [![Maven Central](https://img.shields.io/maven-central/v/io.github.g0dkar/qrcode-kotlin.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22io.github.g0dkar%22%20AND%20a:%22qrcode-kotlin%22) # Change Log > Mostly notable changes from version to version. Some stuff might go undocumented. If you find something that you think > should be documented, please open an [issue](https://github.com/g0dkar/qrcode-kotlin/issues) :) # 4.5.0 - Latest ## ✨ New - Added (experimental) support for WASM targets (requested via Issues #140 and #167) - Please do let us know if you run into any issues with it <3 # 4.4.1 > I'm trying to keep a better CHANGELOG from now on ^^ ## 🔧 Fixed - **Fixed an issue with rendering the Timing Pattern.** I have known it for a while, but now I finally figured what was the issue and fixed it. ## ♻️ Changed - Changed default ECL from `VERY_HIGH` to `LOW` as to stay closer to what other tools seems to use as a default - Computing the `informationDensity` value now always goes for the **least possible value** _(down from a minimum of 6 set by `QRCodeBuilder`)_ - Better documentation of methods - this is an ongoing initiative! ## ✨ New - New `InsufficientInformationDensityException`: instead of an `IllegalArgumentException`, this new exception is thrown with a more helpful message - Added `drawQRCode()` extension function to a Android Compose `DrawScope` to draw QRCodes into modern Android. - Idea/request from Issue #141 by @dgmltn (Thanks!) - Added examples demonstrating what the ECL does (same data, different ECLs) - Added examples demonstrating what a Mask Pattern does (same data, different masks) - Added example with Spring Boot - Moved example QRCode files to a folder within each language examples, just to reduce clutter :) ## 🚫 Removed - `forceInformationDensity` was removed. Now the **QRCodeBuilder** class uses `infoDensity = 0` (default value) as a trigger to compute it automatically since it needs to be `>= 1` - Default value calling `QRCode()` directly is still 6 as to keep a bit of backwards compatibility 😅 ## 👀 Internal - Renamed "typeNum" to "informationDensity" - Updated dokka and KMP - Fixed dokka always triggering building the whole `docs/dokka/` folder (that is only for GH Pages) -------------------- ## 4.3.0 - Fixed an issue with `4.2.1` and iOS/macOS targets (thanks [Manuel149Br](https://github.com/Manuel149Br)!) - Added options to fine tune the Information Density parameter: - `withMinimumInformationDensity()` is now `withInformationDensity()`: Manually setting the Information Density parameter will **force the use of the Information Density specified**. - **NEW** `forceInformationDensity()`: Force the use of the current Information Density value. Default is `false`. If this is `false`, an adequate information density will be computed from the amount of data to be encoded and the Error Correction Level to apply.\ :warning: **Calling `withInformationDensity()` automatically sets this to `true`!** To replicate the old behaviour of using the specified Information Density as the minimum density allowed, first call `withInformationDensity(...)` followed by `forceInformationDensity(false)`. - :sirene: **If the data is too large for the information density value you chose, an `IllegalArgumentException` will be thrown.** - Moved `QRCodeShapesEnum` to be its own `enum` instead of being an inner element of `QRCodeBuilder`. -------------------- ## 4.2.1 - Fixed issue with the Error Correction being ignored (thanks [slaha](https://github.com/slaha)!) - Updated libs versions - **Only for project devs:** fixed the pipeline issues with running tests for PRs. - **Investigating:** Issues with very large payloads being encoded -------------------- ## 4.1.0 - Another round of improvements and fixes (special thanks to [ruicanas](https://github.com/ruicanas) and [chphmh](https://github.com/chphmh)!) - Changed the minimal requirements for the library: - Reduced the minSdk API Version of the Android implementation to `7` (down from `23`) - In theory, it can go down to `1` but all API Versions below 7 are considered deprecated. - Reduced the Java compilation target to `11` (down from `17`) -------------------- ## 4.0.7 - A bunch of improvements and optimizations (minor changes from the rework) - MAJOR thanks to [ruicanas](https://github.com/ruicanas) for fixing and improving the iOS implementation! -------------------- ## 4.0.0 - A major rework of the `QRCode` class to allow for easy creation of nice looking QRCodes - Added the first version of the iOS Support - Changed the base package of the classes from `io.github.g0dkar.qrcode` to `qrcode` - This was done to better support JavaScript ß- And because I don't really see much use in those Java-ish package names 😅 -------------------- ## 3.3.0 - Started doing the Changelog 🥲 - Added plain release files to the [release](release) directory (only the latest version will be there) - Added JavaScript (Browser) into Multiplatform support - Added Browser JS QRCode Generation example ([link](examples/js/qrcode-example.html)) - **Still needs some improvements** on how people use it, but it is a step in the right direction - **Help in this regard is much appreciated!** If interested, this is in the domain of Developer Experience, aka DevX/DX - Changed uses of `Collection`s to `Array`s instead to try and be more performant and have a smaller library as a result - **WIP:** Started work on Native targets (Windows, Linux, macOS, iOS) ================================================ FILE: CNAME ================================================ qrcodekotlin.com ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct > **TL;DR Don't be an asshole, don't call people names and don't fight. Be an adult.** ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at rafael-at-lins-dot-net. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2021 Rafael Madureira Lins de Araújo 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. ================================================ FILE: README.md ================================================ # QRCode-Kotlin [![License](https://img.shields.io/github/license/g0dkar/qrcode-kotlin)](LICENSE) [![Maven Central](https://img.shields.io/maven-central/v/io.github.g0dkar/qrcode-kotlin.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22io.github.g0dkar%22%20AND%20a:%22qrcode-kotlin%22) 💚 [_**Disponível em Português (Brasil)**_](README.pt-br.md) 💛 ![QRCode Kotlin Logo](examples/kotlin/project-banner.png) Creating QRCodes in Kotlin (and Java) is harder than it should be. **QRCode-Kotlin aims to bring a simple, straightforward and customizable way to create QRCodes**, especially in the backend. It is with this mission in mind that we keep doing our best to learn how developers use this library and their goals so that we can provide a better library/API for them. Please, feel free to share if and how you're using this project ^^ * **Pure Kotlin:** Rewritten on pure Kotlin from a reference implementation of the QRCode spec by [Kazuhiko Arase](https://github.com/kazuhikoarase/qrcode-generator) * **Lightweight:** No dependencies, `~115KB` and it does what it says on the tin. * **Easy to use:** Quickly and easily implement QRCodes with few lines of code. * **Good-looking:** Many developers don't have time and sometimes knowledge to implement the perfect QRCode, so this library tries to generate good-looking QRCodes by default. * **Server friendly:** The JVM version is mainly focused on a personal use-case where I needed to generate QRCodes on the backend, but all libraries I found were either complex or huge, usually both. * **Multiplatform:** This is a KMP library with support to Java, JavaScript, Android, iOS and tvOS. ## Table of Contents * [Installation](#installation) * [Usage](#usage) * [Spring Framework and/or Spring Boot](#spring-framework-andor-spring-boot) * [License](#license) * [Thanks and Acknowledgements](#thanks-and-acknowledgements) * [Support and Links](#support-and-links) ## 1. Installation The library is available from [Maven Central](https://search.maven.org/artifact/io.github.g0dkar/qrcode-kotlin/4.5.0/qrcode-kotlin) and [NPM JS](https://www.npmjs.com/package/qrcode-kotlin), so you can add it to your project as a dependency like any other: **Gradle:** ```groovy // Use this for both Android and JVM implementation("io.github.g0dkar:qrcode-kotlin:4.5.0") ``` **Maven - JVM:** ```xml io.github.g0dkar qrcode-kotlin-jvm 4.5.0 ``` **Maven - Android:** ```xml io.github.g0dkar qrcode-kotlin-android 4.5.0 ``` **NodeJS:** ```shell npm install qrcode-kotlin@4.5.0 ``` **Browser:** ```html ``` ## Usage To create QRCodes, the main class that should be used is the `qrcode.render.QRCode` class. It has static methods to help you create a QRCode the way you want: ```kotlin // Use one of these: val squares = QRCode.ofSquares() val circles = QRCode.ofCircles() val roundedSquares = QRCode.ofRoundedSquares() ``` With that, you'll have a [QRCodeBuilder](src/commonMain/kotlin/qrcode/QRCodeBuilder.kt) instance. It has methods to adjust colors, size, spacing, add a logo and more! Also, make sure to check the [Colors](src/commonMain/kotlin/qrcode/color/Colors.kt) class as well. Here's a code to get you started: ```kotlin val helloWorld = QRCode.ofSquares() .withColor(Colors.DEEP_SKY_BLUE) // Default is Colors.BLACK .withSize(10) // Default is 25 .build("Hello world!") // By default, QRCodes are rendered as PNGs. val pngBytes = helloWorld.render() FileOutputStream("hello-world.png").use { it.write(pngBytes) } ``` We highly recommend that you check out some examples: * [All sorts of shapes](examples/kotlin/src/main/kotlin/Example01-Shapes.kt): Squares, Circles, Rounded Squares and Custom shapes * [All about colors](examples/kotlin/src/main/kotlin/Example02-Colors.kt): Foreground, Background, Transparent backgrounds, Linear Gradient colors * [Adding a Logo](examples/kotlin/src/main/kotlin/Example03-Logo.kt): Add a logo and remove the cells behind it, or don't :) * [SVG QRCodes](examples/kotlin/src/main/kotlin/Example04-SVG.kt): How to extend the renderer to render SVG (uses [JFree SVG](https://github.com/jfree/jfreesvg)) * [The banner on the top of this README](examples/kotlin/src/main/kotlin/ProjectLogo.kt): Yup, all done with the library ^^ The examples show pretty much all that can be done with the library! Even how to extend it so that it can create SVG QRCodes ;) You can mix and match all those together. Try generating the library logo and banner with gradients and all in SVG ;) ### Spring Framework and/or Spring Boot As said earlier, one of the main reasons I developed this library was to use it on a backend application. So it is only natural to show how to do that :) This Spring Framework/Boot controller method can generate QRCodes of a given content: ```kotlin import org.springframework.core.io.ByteArrayResource import org.springframework.http.HttpHeaders.CONTENT_DISPOSITION import org.springframework.http.MediaType.IMAGE_PNG_VALUE @GetMapping("/qrcode") fun generateQrCode(content: String): ResponseEntity { val pngData = QRCode.ofSquares() .build(content) .render() val resource = ByteArrayResource(pngData, IMAGE_PNG_VALUE) return ResponseEntity.ok() .header(CONTENT_DISPOSITION, "attachment; filename=\"qrcode.png\"") .body(resource) } ``` ## Changes from v3 The main changes coming from `v3.3.0` are: 1. The main package of the classes was changed from `io.github.g0dkar.qrcode` to simply `qrcode` * The old name doesn't help languages that don't have the "package" concept, and other Kotlin libraries already name their main package this way. 2. The old `QRCode` class was rewritten to be easier to create better looking QRCodes . The previous `QRCode` class was renamed to [QRCodeProcessor](src/commonMain/kotlin/qrcode/raw/QRCodeProcessor.kt), with very minor API changes. * **For most of the simple cases, the new `QRCode` class is compatible with the old one!** 3. A bunch of optimizations on how the QRCode is drawn. Previously, we'd had a canvas for each square, which would then be copied into the QRCode. This was changed to have just one large canvas where each square will be individually drawn directly. 4. iOS and tvOS Support: Starting from `v4.0.7` an initial implementation of the `QRCodeGraphics` so that iOS and tvOS are now supported. **Any and all [feedback](https://github.com/g0dkar/qrcode-kotlin/issues/85) are very welcome!** - Thanks a lot to [ruicanas](https://github.com/ruicanas) for all his contributions to this feature :D ## License Copyright since 2021 Rafael M. Lins, Licensed under the [MIT License](https://rafaellins.mit-license.org/2021/). QR Code is trademarked by Denso Wave, Inc. ## Thanks and Acknowledgements * [Kazuhiko Arase](https://github.com/kazuhikoarase): For his reference implementation! * [Paul Varry](https://github.com/pvarry): for opening the first few issues on the repo and helping to make the library even better for everyone! :grin: * [Renan Lukas](https://github.com/RenanLukas): For his friendship, patience and help with Android, Gradle and a bunch of other stuff during the development of v2.0.0 and v3.0.0! * [Doomsdayrs](https://github.com/Doomsdayrs): For pointing out how the library could be improved using Kotlin Multiplatform, and helping out implementing it into the project. * An awesome, furry friend for all the support through all these years :) * [ruicanas](https://github.com/ruicanas): For not only pointing out some issues with the iOS implementation, but also fixing them! Thank you so much ^^ ## Support and Links * If you found any bugs, please [open an Issue](https://github.com/g0dkar/qrcode-kotlin/issues/new?assignees=g0dkar&labels=bug&template=bug_report.md&title=) 😁 * Have any suggestions? You can [make them](https://github.com/g0dkar/qrcode-kotlin/issues/new?assignees=&labels=&template=feature_request.md&title=) as well! If you enjoyed the library and want to get me some coffee, use the buttons below :love_you_gesture: [Buy me a coffee over at Ko-fi!](https://ko-fi.com/g0dkar) [Buy me a coffee over at PayPal!](https://www.paypal.com/donate/?business=EFVC68BFJQWSC&no_recurring=0&item_name=Rafael+is+working+on+Open+Source+software+in+his+free+time.+This+helps+him+keep+this+up+for+longer%2C+and+with+higher+quality%21¤cy_code=BRL) ================================================ FILE: README.pt-br.md ================================================ # QRCode-Kotlin [![License](https://img.shields.io/github/license/g0dkar/qrcode-kotlin)](LICENSE) [![Maven Central](https://img.shields.io/maven-central/v/io.github.g0dkar/qrcode-kotlin.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22io.github.g0dkar%22%20AND%20a:%22qrcode-kotlin%22) :uk: [_**Also available in English**_](README.pt-br.md) :uk: ![QRCode Kotlin Logo](examples/kotlin/project-banner.png) Criar QRCodes em Kotlin (e Java) é mais difícil do que deveria ser. **A QRCode-Kotlin tenta trazer uma forma personalizável, simples e direta de se criar QRCodes**, especialmente no backend. É com esta missão em mente que continuamos a fazer o nosso melhor para aprender como pessoas desenvolvedoras utilizam essa biblioteca nos seus projetos e quais os seus objetivos, para podermos prover uma ferramenta/API melhor para todos. Por favor, sinta-se livre para compartilhar se e como você utiliza este projeto ^^ * **Puro Kotlin:** Reescrita em puro Kotlin de uma implementação de referência da spec QRCode por [Kazuhiko Arase](https://github.com/kazuhikoarase/qrcode-generator) * **Leve:** Sem dependencias, `~115KB` e faz o que promete no rótulo. * **Fácil de usar:** Rápida e facilmente tenha QRCodes com pouquíssimas linhas de código. * **Bonito:** Muitas pessoas desenvolvedoras não têm tempo e às vezes conhecimento para implementar o QRCode perfeito, por isso esta biblioteca tenta gerar códigos bonitos por padrão. * **Amigável aos servidores:** A versão da JVM é fortemente focada em um caso de uso pessoal onde eu precisei criar QRCodes no backend, mas todas as bibliotecas que encontrei eram complexas ou enormes, normalmente os dois. * **Multiplatforma:** Esta é uma bilioteca KMP com suporte a Java, JavaScript, Android, iOS e tvOS. ## Sumário * [Instalação](#instalação) * [Uso](#uso) * [Spring Framework e/ou Spring Boot](#spring-framework-eou-spring-boot) * [Mudanças da v3](#mudanças-da-v3) * [Licença](#licença) * [Agradecimentos e Reconhecimentos](#agradecimentos-e-reconhecimentos) * [Suporte e Links](#suporte-e-links) ## Instalação A biblioteca está disponível através da [Maven Central](https://search.maven.org/artifact/io.github.g0dkar/qrcode-kotlin/4.5.0/qrcode-kotlin) e do [NPM JS](https://www.npmjs.com/package/qrcode-kotlin), portanto basta adicioná-la a seu projeto como qualquer outra: **Gradle:** ```groovy // Use esse tanto para Android quanto para a JVM implementation("io.github.g0dkar:qrcode-kotlin:4.5.0") ``` **Maven - JVM:** ```xml io.github.g0dkar qrcode-kotlin-jvm 4.5.0 ``` **Maven - Android:** ```xml io.github.g0dkar qrcode-kotlin-android 4.5.0 ``` **NodeJS:** ```shell npm install qrcode-kotlin@4.5.0 ``` **Browser:** ```html ``` ## Uso Para criar QRCodes, a principal classe que deve ser usada é a `qrcode.render.QRCode`. Ela tem métodos estáticos para ajudar na criação de um QRCode da forma que você quiser: ```kotlin // Use qualquer um desses: val quadrados = QRCode.ofSquares() val circulos = QRCode.ofCircles() val quadradosArredondados = QRCode.ofRoundedSquares() ``` Com isso, você terá uma instância de [QRCodeBuilder](src/commonMain/kotlin/qrcode/QRCodeBuilder.kt). Esta classe tem métodos para ajustar cores, tamanho, espaçamento, adicionar um logo e mais! Certifique-se de ver também a classe [Colors](src/commonMain/kotlin/qrcode/color/Colors.kt). Aqui tem um código para ajudar você a começar: ```kotlin val helloWorld = QRCode.ofSquares() .withColor(Colors.DEEP_SKY_BLUE) // Padrão é Colors.BLACK .withSize(10) // Padrão é 25px .build("Hello world!") // Por padrão, os QRCodes serão gerados como PNG val pngBytes = helloWorld.render() FileOutputStream("hello-world.png").use { it.write(pngBytes) } ``` Recomendamos fortemente que você veja os exemplos disponíveis: * [Todas as formas](examples/kotlin/src/main/kotlin/Example01-Shapes.kt): Quadrados, Círculos, Quadrados Arredondados e formas personalizadas * [Tudo sobre cores](examples/kotlin/src/main/kotlin/Example02-Colors.kt): Frente, Fundo, Fundos transparentes, cores em Gradiente Linear * [Adicionando um Logo](examples/kotlin/src/main/kotlin/Example03-Logo.kt): Adicione um logo e remova as células atrás dele, ou não :) * [QRCodes em SVG](examples/kotlin/src/main/kotlin/Example04-SVG.kt): Como estender o renderizador para criar SVG (utilizando [JFree SVG](https://github.com/jfree/jfreesvg)) * [O banner no topo deste README](examples/kotlin/src/main/kotlin/ProjectLogo.kt): Sim, feito com essa biblioteca ^^ Os exemplos mostram praticamente tudo que pode ser feito com a biblioteca! Até mesmo como estender a mesma para criar QRCodes em SVG ;) Você pode utilizar todas essas funcionalidades juntas e misturadas. Tente gerar o logo e banner com gradientes e tudo mais em SVG ;) ### Spring Framework e/ou Spring Boot Como dito anteriormente, uma das razões principais para o desenvolvimento dessa biblioteca foi para ser usada em aplicações backend. Portanto, é natural mostrar como fazer exatamente isso :) Este método de um controller do Spring Framework/Boot mostra como gerar QRCodes dado o conteúdo do mesmo: ```kotlin import org.springframework.core.io.ByteArrayResource import org.springframework.http.HttpHeaders.CONTENT_DISPOSITION import org.springframework.http.MediaType.IMAGE_PNG_VALUE @GetMapping("/qrcode") fun generateQrCode(content: String): ResponseEntity { val pngData = QRCode().ofSquares() .build(content) .render() val resource = ByteArrayResource(pngData, IMAGE_PNG_VALUE) return ResponseEntity.ok() .header(CONTENT_DISPOSITION, "attachment; filename=\"qrcode.png\"") .body(resource) } ``` ## Mudanças da v3 As principais mudanças vindo da versão `v3.3.0` são: 1. O pacote principal das classes foi mudado de `io.github.g0dkar.qrcode` para simplesmente `qrcode` * O nome anterior não ajuda linguagens que não têm esse conceito de "pacote", e outras bibliotecas Kotlin já nomeiam os seus pacotes principais dessa forma. 2. A antiga classe `QRCode` foi reescrita para ser mais fácil de se criar QRCodes mais bonitos. A antiga classe `QRCode` foi renomeada para [QRCodeProcessor](src/commonMain/kotlin/qrcode/raw/QRCodeProcessor.kt), com pouquíssimas mudanças na API. * **Para a maioria dos casos de uso simples, a nova `QRCode` é compatível com a antiga!** 3. Uma grande quantidade de otimizações em como o QRCode é desenhado. Anteriormente, tínhamos um canvas (ecrã) para cada quadrado, o qual era copiado no canvas do QRCode principal. Isto foi mudado para termos apenas um grande canvas onde cada quadrado individual será desenhado diretamente. 4. Suporte a iOS e tvOS: A partir da versão `v4.0.7`, uma implementação experimental inicial da classe `QRCodeGraphics` foi criada para que o iOS e tvOS sejam suportados. **Todo e qualquer [feedback](https://github.com/g0dkar/qrcode-kotlin/issues/85) é muito bem-vindo!** (pode comentar em português mesmo) - Um imenso agradecimento a [ruicanas](https://github.com/ruicanas) por suas contribuições a essa feature :D ## Licença Copyright desde 2021 Rafael M. Lins, Licenciado sob a [Licença MIT](https://rafaellins.mit-license.org/2021/). QR Code é marca registrada de Denso Wave, Inc. ## Agradecimentos e Reconhecimentos * [Kazuhiko Arase](https://github.com/kazuhikoarase): Por sua implementação de referência! * [Paul Varry](https://github.com/pvarry): Por abrir as primeiras _issues_ do repositório e ajudar a fazer a biblioteca melhor para todo mundo! :grin: * [Renan Lukas](https://github.com/RenanLukas): Por sua amizade, paciência e ajuda com Android, Gradle e várias outras coisas mais durante o desenvolvimento das versões v2.0.0 e v3.0.0! * [Doomsdayrs](https://github.com/Doomsdayrs): Por apontar como a biblioteca podería melhorar se tornando um projeto KMP, entre outras contribuições. * Um incrível e peludo amigo por todo o suporte através dos anos :) * [ruicanas](https://github.com/ruicanas): Por não apenas apontar problemas com a implementação iOS, mas resolvê-los também! Muito obrigado ^^ ## Suporte e Links * Se você encontrar algum bug, sinta-se livre para [abrir uma Issue](https://github.com/g0dkar/qrcode-kotlin/issues/new?assignees=g0dkar&labels=bug&template=bug_report.md&title=) * Tem sugestões? Você pode [fazê-las](https://github.com/g0dkar/qrcode-kotlin/issues/new?assignees=&labels=&template=feature_request.md&title=) também! Se você gostou da biblioteca e quiser ajudar pagando um cafézinho, use os botões abaixo :love_you_gesture: [Me pague um cafézinho via Ko-fi!](https://ko-fi.com/g0dkar) [Me pague um cafézinho via PayPal!](https://www.paypal.com/donate/?business=EFVC68BFJQWSC&no_recurring=0&item_name=Rafael+is+working+on+Open+Source+software+in+his+free+time.+This+helps+him+keep+this+up+for+longer%2C+and+with+higher+quality%21¤cy_code=BRL) ================================================ FILE: _config.yml ================================================ theme: jekyll-theme-tactile ================================================ FILE: build.gradle.kts ================================================ @file:OptIn(ExperimentalWasmDsl::class) import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED import org.gradle.api.tasks.testing.logging.TestLogEvent.PASSED import org.jetbrains.dokka.gradle.engine.parameters.VisibilityModifier import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig.Mode.PRODUCTION import java.time.LocalDateTime buildscript { dependencies { classpath(libs.kotlin.gradle.plugin) } } plugins { // Dev Plugins id("idea") // Base Plugins alias(libs.plugins.kotlin.multiplatform) alias(libs.plugins.android.library) // Kotest alias(libs.plugins.kotest.multiplatform) // Publishing Plugins signing `maven-publish` alias(libs.plugins.nexus) alias(libs.plugins.npmPublish) // Docs Plugins alias(libs.plugins.dokka) // Here because of the Android Examples alias(libs.plugins.android.application) apply false alias(libs.plugins.kotlin.android) apply false } repositories { mavenCentral() google() } group = "io.github.g0dkar" val javaVersion = JavaVersion.VERSION_1_8 val javaVersionNumber = javaVersion.majorVersion.toInt() kotlin { applyDefaultHierarchyTemplate() jvmToolchain(javaVersionNumber) compilerOptions { freeCompilerArgs.add("-Xexpect-actual-classes") } jvm() androidTarget { publishLibraryVariants("release") } js { browser { commonWebpackConfig { mode = PRODUCTION sourceMaps = true } testTask { enabled = false } binaries.library() generateTypeScriptDefinitions() } } wasmJs { browser { commonWebpackConfig { mode = PRODUCTION sourceMaps = true } testTask { enabled = false } binaries.library() generateTypeScriptDefinitions() } } // This is in place just because my main development machine is NOT a macOS :) // iOS Family of targets... since you can't just "ios()" anymore. val currentPlatform = System.getProperty("os.name") if (currentPlatform.lowercase() == "mac os x") { listOf( iosX64(), iosArm64(), iosSimulatorArm64(), // watchosX64() <- Still have to figure out how to do it for watchOS x_x // watchosArm64() tvosX64(), tvosArm64(), tvosSimulatorArm64(), ).forEach { it.binaries.framework { baseName = "qrcode_kotlin" isStatic = true } } } sourceSets { commonTest { dependencies { implementation(kotlin("test")) implementation(libs.kotest.assertions.core) implementation(libs.kotest.framework.engine) } } jvmTest { dependencies { implementation(libs.kotest.runner.junit5) } } androidTarget { dependencies { compileOnly(libs.androidx.compose.ui) } } wasmJsMain { dependencies { implementation(libs.kotlinx.browser) } } } } android { namespace = "io.github.g0dkar.qrcode" compileSdk = 7 sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") defaultConfig { minSdk = 7 } compileOptions { sourceCompatibility = javaVersion targetCompatibility = javaVersion } } /** * Kotest Configuration */ tasks { named("jvmTest") { useJUnitPlatform() filter { isFailOnNoMatchingTests = false } testLogging { showExceptions = true showStandardStreams = true events = setOf(FAILED, PASSED) exceptionFormat = FULL } } } /* *********************** */ /* After Build Publishing */ /* *********************** */ tasks { /** Copies release files into /release dir */ register("copyToReleaseDir") { dependsOn(build) doFirst { layout.projectDirectory.dir("release").asFile.deleteRecursively() } from(layout.buildDirectory.file("libs/qrcode-kotlin-jvm-$version.jar")) from(layout.buildDirectory.file("dist/js/productionLibrary/qrcode-kotlin.js")) from(layout.buildDirectory.file("dist/js/productionLibrary/qrcode-kotlin.js.map")) from(layout.buildDirectory.file("dist/js/productionLibrary/qrcode-kotlin.d.ts")) into(layout.projectDirectory.dir("release")) } } /* **************** */ /* Dev Environment */ /* **************** */ idea { module { isDownloadJavadoc = false isDownloadSources = true } } /* **************** */ /* Docs */ /* **************** */ dokka { moduleName.set("QRCode-Kotlin") basePublicationsDirectory = layout.buildDirectory.dir("javadoc") dokkaSourceSets { configureEach { skipDeprecated = true reportUndocumented = true skipEmptyPackages = true suppressGeneratedFiles = true documentedVisibilities = setOf(VisibilityModifier.Public) sourceLink { remoteUrl("https://github.com/g0dkar/qrcode-kotlin/tree/main") } } } pluginsConfiguration.html { footerMessage.set("© 2021-${LocalDateTime.now().year} Rafael M. Lins - MIT License") } } val dokkaJar by tasks.registering(Jar::class) { group = JavaBasePlugin.DOCUMENTATION_GROUP description = "Assembles Kotlin docs with Dokka" archiveClassifier.set("javadoc") from(tasks.dokkaGenerate) } val dokkaCopyToFolder by tasks.registering(Copy::class) { dependsOn(tasks.dokkaGenerate) doFirst { layout.projectDirectory.dir("docs/dokka").asFile.deleteRecursively() } from(layout.buildDirectory.dir("javadoc/html")) into(layout.projectDirectory.dir("docs/dokka")) } /* **************** */ /* Publishing */ /* **************** */ val ossrhUsername = properties.getOrDefault("ossrhUsername", System.getenv("OSSRH_USER"))?.toString() val ossrhPassword = properties.getOrDefault("ossrhPassword", System.getenv("OSSRH_PASSWORD"))?.toString() val npmAccessKey = properties.getOrDefault("npmAccessKey", System.getenv("NPM_ACCESSKEY"))?.toString() nexusPublishing { repositories { sonatype { nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/")) snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")) username.set(ossrhUsername ?: return@sonatype) password.set(ossrhPassword ?: return@sonatype) } } } publishing { publications { withType { artifact(dokkaJar) pom { val projectGitUrl = "github.com/g0dkar/qrcode-kotlin" name.set(rootProject.name) description.set("A Kotlin Library to generate QR Codes without any other dependencies.") url.set("https://$projectGitUrl") inceptionYear.set("2021") licenses { license { name.set("MIT") url.set("https://rafaellins.mit-license.org/2021/") } } developers { developer { id.set("g0dkar") name.set("Rafael Lins") email.set("rafael@lins.net.br") url.set("https://github.com/g0dkar") } } issueManagement { system.set("GitHub") url.set("https://$projectGitUrl/issues") } scm { connection.set("scm:git://$projectGitUrl.git") developerConnection.set("scm:git://$projectGitUrl.git") url.set("https://$projectGitUrl") } } } } repositories { maven { name = "sonatype" url = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/") credentials { username = ossrhUsername password = ossrhPassword } } } } signing { val key = properties.getOrDefault("signing.key", System.getenv("SIGNING_KEY"))?.toString() ?: return@signing val password = properties.getOrDefault("signing.password", System.getenv("SIGNING_PASSWORD"))?.toString() ?: return@signing useInMemoryPgpKeys(key, password) sign(publishing.publications) } // https://github.com/gradle/gradle/issues/26091 tasks.withType().configureEach { val signingTasks = tasks.withType() mustRunAfter(signingTasks) } npmPublish { readme.set(rootDir.resolve("README.md")) registries { register("npmjs") { uri.set(uri("https://registry.npmjs.org")) authToken.set(npmAccessKey) } } } ================================================ FILE: examples/android/build.gradle.kts ================================================ plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) alias(libs.plugins.compose.compiler) } android { namespace = "io.github.g0dkar.qrcode" compileSdk = 35 defaultConfig { applicationId = "io.github.g0dkar.qrcodeKotlin" minSdk = 26 targetSdk = 34 versionCode = 403 versionName = "4.2.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { isMinifyEnabled = true proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") } } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { jvmTarget = "17" } buildFeatures { viewBinding = true compose = true } } dependencies { val composeBom = platform("androidx.compose:compose-bom:2025.02.00") implementation(composeBom) implementation("io.github.g0dkar:qrcode-kotlin:4.5.0") implementation(libs.core.ktx) implementation(libs.appcompat) implementation(libs.material) implementation(libs.constraintlayout) implementation(libs.navigation.fragment.ktx) implementation(libs.navigation.ui.ktx) implementation(libs.androidx.annotation) implementation(libs.androidx.lifecycle.livedata.ktx) implementation(libs.androidx.lifecycle.viewmodel.ktx) implementation(libs.androidx.foundation) } ================================================ FILE: examples/android/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile ================================================ FILE: examples/android/src/main/AndroidManifest.xml ================================================ ================================================ FILE: examples/android/src/main/java/io/github/g0dkar/qrcode/AboutActivity.kt ================================================ package io.github.g0dkar.qrcode import android.os.Bundle import androidx.appcompat.app.AppCompatActivity class AboutActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_about) } } ================================================ FILE: examples/android/src/main/java/io/github/g0dkar/qrcode/NewQRCodeActivity.kt ================================================ package io.github.g0dkar.qrcode import android.content.Intent import android.graphics.Bitmap import android.os.Bundle import android.os.Handler import android.os.Looper import android.view.View import android.widget.AdapterView import android.widget.AdapterView.OnItemSelectedListener import android.widget.Button import android.widget.EditText import android.widget.ImageView import android.widget.Spinner import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.core.os.postDelayed import androidx.core.widget.doOnTextChanged import io.github.g0dkar.qrcode.QRCodeData.Companion.STYLE_DEFAULT import io.github.g0dkar.qrcode.QRCodeData.Companion.qrCodeForStyle class NewQRCodeActivity : AppCompatActivity() { private lateinit var qrCodeData: EditText private lateinit var qrCodeImageView: ImageView private lateinit var saveButton: Button private lateinit var styleSelect: Spinner private var qrCodeIsValid = false private val handler = Handler(Looper.getMainLooper()) companion object { const val QRCODE_DATA = "qrCodeData" const val QRCODE_STYLE = "qrCodeStyle" } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_new_qrcode) qrCodeData = findViewById(R.id.newQRCodeData) qrCodeImageView = findViewById(R.id.newQRCodePreviewImage) saveButton = findViewById(R.id.newQRCodeBtn) styleSelect = findViewById(R.id.newQRCodeStyle) qrCodeData.doOnTextChanged { text, _, _, _ -> val string = text?.toString()?.trim() val qrCodeStyle = styleSelect.selectedItemPosition updateAction(string, qrCodeStyle) } styleSelect.onItemSelectedListener = ItemSelectAction(this) saveButton.setOnClickListener { save() } } private fun updateAction(qrCodeData: String?, qrCodeStyle: Int) { qrCodeIsValid = if (qrCodeData.isNullOrBlank()) { qrCodeImageView.setImageBitmap(null) false } else { try { handler.removeCallbacksAndMessages(null) handler.postDelayed(250) { val qrCodeBitmap = qrCodeForStyle(qrCodeStyle) .build(qrCodeData) .render() .nativeImage() as Bitmap qrCodeImageView.setImageBitmap(qrCodeBitmap) } true } catch (t: Throwable) { Toast.makeText( this@NewQRCodeActivity, R.string.new_qrcode_error_preview, Toast.LENGTH_LONG, ).show() qrCodeImageView.setImageBitmap(null) false } } } private fun save() { val resultIntent = Intent() val qrCodeDataText = qrCodeData.text?.toString()?.trim() val qrCodeDataStyle = styleSelect.selectedItemPosition if (qrCodeDataText.isNullOrBlank() || !qrCodeIsValid) { setResult(RESULT_CANCELED, resultIntent) } else { resultIntent.putExtra(QRCODE_DATA, qrCodeDataText) resultIntent.putExtra(QRCODE_STYLE, qrCodeDataStyle) setResult(RESULT_OK, resultIntent) } finish() } class ItemSelectAction(val codeActivity: NewQRCodeActivity) : OnItemSelectedListener { override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { val string = codeActivity.qrCodeData.text.toString().trim() codeActivity.updateAction(string, position) } override fun onNothingSelected(parent: AdapterView<*>?) { val string = codeActivity.qrCodeData.text.toString().trim() codeActivity.updateAction(string, STYLE_DEFAULT) } } } ================================================ FILE: examples/android/src/main/java/io/github/g0dkar/qrcode/QRCodeData.kt ================================================ package io.github.g0dkar.qrcode import android.content.SharedPreferences import android.graphics.Bitmap import qrcode.QRCode import java.time.OffsetDateTime import java.time.ZoneOffset data class QRCodeData( val data: String, val style: Int = STYLE_DEFAULT, val timestamp: OffsetDateTime = OffsetDateTime.now(ZoneOffset.UTC), val bitmap: Bitmap = qrCodeForStyle(style).build(data).render().nativeImage() as Bitmap, ) : Comparable { companion object { const val STYLE_DEFAULT = 0 const val STYLE_SQUARE = 1 const val STYLE_CIRCLE = 2 const val STYLE_ROUNDED_SQUARE = 3 const val SEPARATOR = "___STYLE___" fun qrCodeForStyle(style: Int = STYLE_DEFAULT) = when (style) { STYLE_SQUARE -> QRCode.ofSquares().withInnerSpacing(0) STYLE_CIRCLE -> QRCode.ofCircles() STYLE_ROUNDED_SQUARE -> QRCode.ofRoundedSquares() else -> QRCode.ofSquares() } } fun persist(sharedPreferencesEditor: SharedPreferences.Editor) { val key = timestamp.toEpochSecond().toString() sharedPreferencesEditor.putString(key, "$data$SEPARATOR$style") } override fun compareTo(other: QRCodeData): Int = timestamp.compareTo(other.timestamp) } ================================================ FILE: examples/android/src/main/java/io/github/g0dkar/qrcode/QRCodeDetailActivity.kt ================================================ package io.github.g0dkar.qrcode import android.os.Bundle import android.view.View import android.widget.ImageView import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import java.time.Instant import java.time.OffsetDateTime import java.time.ZoneOffset class QRCodeDetailActivity : AppCompatActivity() { companion object { const val QRCODE_DATA = "qrCodeData" const val QRCODE_STYLE = "qrCodeStyle" const val QRCODE_TIMESTAMP = "qrCodeTimestamp" } private lateinit var qrCodeDetailText: TextView private lateinit var qrCodeDetailImage: ImageView private lateinit var qrCodeDetailFab: View override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_qrcode_detail) qrCodeDetailText = findViewById(R.id.qrCodeDetailText) qrCodeDetailImage = findViewById(R.id.qrCodeDetailImage) qrCodeDetailFab = findViewById(R.id.qrCodeDetailFab) setBrightness() // Needed for readability if using this app on an actual phone val qrCodeData = intent.getStringExtra(QRCODE_DATA) ?: "ERROR" val qrCodeStyle = intent.getIntExtra(QRCODE_STYLE, 0) val qrCodeTimestamp = intent.getLongExtra(QRCODE_TIMESTAMP, Instant.now().epochSecond) val qrCode = QRCodeData(qrCodeData, qrCodeStyle, OffsetDateTime.ofInstant(Instant.ofEpochSecond(qrCodeTimestamp), ZoneOffset.UTC)) qrCodeDetailText.text = qrCode.data qrCodeDetailImage.setImageBitmap(qrCode.bitmap) } private fun setBrightness() { try { val layout = window.attributes layout.screenBrightness = 1.0f window.attributes = layout } catch (_: Exception) { // NOOP } } } ================================================ FILE: examples/android/src/main/java/io/github/g0dkar/qrcode/QRCodeListActivity.kt ================================================ package io.github.g0dkar.qrcode import android.content.Intent import android.os.Bundle import android.view.View import android.widget.Toast import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.RecyclerView import io.github.g0dkar.qrcode.NewQRCodeActivity.Companion.QRCODE_DATA import io.github.g0dkar.qrcode.NewQRCodeActivity.Companion.QRCODE_STYLE import io.github.g0dkar.qrcode.extra.QRCodeListAdapter import io.github.g0dkar.qrcode.extra.QRCodeListDatasource class QRCodeListActivity : AppCompatActivity() { private val listAdapter = QRCodeListAdapter { val intent = Intent(this@QRCodeListActivity, QRCodeDetailActivity::class.java) intent.putExtra(QRCodeDetailActivity.QRCODE_DATA, it.data) intent.putExtra(QRCodeDetailActivity.QRCODE_STYLE, it.style) intent.putExtra(QRCodeDetailActivity.QRCODE_TIMESTAMP, it.timestamp.toEpochSecond()) startActivity(intent) } private lateinit var recyclerView: RecyclerView private lateinit var fab: View private lateinit var newQRCodeActivity: ActivityResultLauncher override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) fab = findViewById(R.id.fab) recyclerView = findViewById(R.id.recycler_view) QRCodeListDatasource.fromContext(this) recyclerView.adapter = listAdapter fab.setOnClickListener { startNewQRCodeActivity() } newQRCodeActivity = registerForActivityResult(StartActivityForResult()) { if (it.data != null) { val result = it.resultCode if (result == RESULT_OK) { val qrCodeData = it.data?.getStringExtra(QRCODE_DATA) val qrCodeStyle = it.data?.getIntExtra(QRCODE_STYLE, 0) if (qrCodeData != null && qrCodeStyle != null) { QRCodeListDatasource.add(qrCodeData, qrCodeStyle) } else { Toast.makeText( this@QRCodeListActivity, R.string.new_qrcode_error, Toast.LENGTH_LONG ).show() } } else { Toast.makeText( this@QRCodeListActivity, R.string.new_qrcode_error, Toast.LENGTH_LONG ).show() } } } } override fun onResume() { super.onResume() QRCodeListDatasource.liveData.observe(this) { if (it != null) { listAdapter.submitList(it) } } } override fun onStop() { super.onStop() QRCodeListDatasource.liveData.removeObservers(this) } private fun startNewQRCodeActivity() { val intent = Intent(this, NewQRCodeActivity::class.java) newQRCodeActivity.launch(intent) } } ================================================ FILE: examples/android/src/main/java/io/github/g0dkar/qrcode/extra/QRCodeListAdapter.kt ================================================ package io.github.g0dkar.qrcode.extra import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import io.github.g0dkar.qrcode.QRCodeData import io.github.g0dkar.qrcode.R class QRCodeListAdapter( private val onClick: (QRCodeData) -> Unit ) : ListAdapter(QRCodeListDiffCallback) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QRCodeListViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.qrcode_list_item, parent, false) return QRCodeListViewHolder(view, onClick) } override fun onBindViewHolder(holder: QRCodeListViewHolder, position: Int) { val qrCodeData = getItem(position) holder.bind(qrCodeData) } } class QRCodeListViewHolder( itemView: View, val onClick: (QRCodeData) -> Unit ) : RecyclerView.ViewHolder(itemView) { private val qrCodeListItemTextView: TextView = itemView.findViewById(R.id.qrcode_data) private val qrCodeListItemImageView: ImageView = itemView.findViewById(R.id.qrcode_image) private var currentQrCodeData: QRCodeData? = null init { itemView.setOnClickListener { currentQrCodeData?.let { onClick(it) } } } fun bind(qrCodeData: QRCodeData) { currentQrCodeData = qrCodeData qrCodeListItemTextView.text = qrCodeData.data qrCodeListItemImageView.setImageBitmap(qrCodeData.bitmap) } } object QRCodeListDiffCallback : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: QRCodeData, newItem: QRCodeData): Boolean { return oldItem == newItem } override fun areContentsTheSame(oldItem: QRCodeData, newItem: QRCodeData): Boolean { return oldItem.data == newItem.data } } ================================================ FILE: examples/android/src/main/java/io/github/g0dkar/qrcode/extra/QRCodeListDatasource.kt ================================================ package io.github.g0dkar.qrcode.extra import android.content.Context import android.content.SharedPreferences import androidx.appcompat.app.AppCompatActivity.MODE_PRIVATE import androidx.lifecycle.MutableLiveData import io.github.g0dkar.qrcode.QRCodeData import io.github.g0dkar.qrcode.QRCodeData.Companion.SEPARATOR import io.github.g0dkar.qrcode.QRCodeData.Companion.STYLE_DEFAULT import java.time.Instant import java.time.ZoneOffset object QRCodeListDatasource { private const val ERROR = "ERROR" private const val PREFERENCE_FILE = "qrCodeKotlinAndroidExample" private lateinit var sharedPreferences: SharedPreferences val liveData: MutableLiveData> = MutableLiveData(mutableListOf()) fun fromContext(context: Context) { val currentList = liveData.value ?: listOf() sharedPreferences = context.getSharedPreferences(PREFERENCE_FILE, MODE_PRIVATE) val newList = currentList.toMutableList().apply { sharedPreferences.all .filterValues { it != null && it.toString().isNotBlank() } .mapTo(this) { (key, value) -> val dataParts = value?.toString()?.split(SEPARATOR) QRCodeData( dataParts?.getOrNull(0)?.toString() ?: ERROR, dataParts?.getOrNull(1)?.toIntOrNull() ?: 0, Instant.ofEpochSecond(key.toLong()).atOffset(ZoneOffset.UTC), ) } if (isEmpty()) { add(QRCodeData("QRCode Kotlin", STYLE_DEFAULT)) } sortDescending() } liveData.postValue(newList) } fun add(data: String, style: Int) { val currentList = liveData.value ?: listOf() val qrCodeData = QRCodeData(data, style) val editor = sharedPreferences.edit() qrCodeData.persist(editor) editor.apply() val newList = currentList.toMutableList() .apply { add(0, qrCodeData) } liveData.postValue(newList) } fun remove(timestamp: Long) { val currentList = liveData.value ?: listOf() val newList = currentList.toMutableList() sharedPreferences.edit() .remove(timestamp.toString()) .apply() liveData.postValue(newList) } } ================================================ FILE: examples/android/src/main/res/drawable/ic_launcher_background.xml ================================================ ================================================ FILE: examples/android/src/main/res/drawable-v24/ic_launcher_foreground.xml ================================================ ================================================ FILE: examples/android/src/main/res/layout/activity_about.xml ================================================ ================================================ FILE: examples/android/src/main/res/layout/activity_main.xml ================================================ ================================================ FILE: examples/android/src/main/res/layout/activity_new_qrcode.xml ================================================