Giter VIP home page Giter VIP logo

custom-qr-generator's Introduction

Сustom QR generator for Android

Android library for creating QR-codes with logo, custom pixel/eyes shapes, background image. Powered by ZXing.

Playground

Try library features on live app example

I'll be glad, if you leave a positive review for it in Google Play 😊

Library is also available for:

Table of contents

Installation

Android CI
To get a Git project into your build:

Step 1. Add the JitPack repository to your build file

allprojects {
    repositories {
      ...
        maven { url 'https://jitpack.io' }
    }
}

Or for gradle 7+ to settings.gradle file:

dependencyResolutionManagement {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

Step 2. Add the dependency.

dependencies {
    implementation 'com.github.alexzhirkevich:custom-qr-generator:1.6.2'

    // or new v2.0 alpha
    implementation 'com.github.alexzhirkevich:custom-qr-generator:2.0.0-alpha01'
}

Usage

There are 2 types of QR code image - raster (deprecated) image and vector image.

Raster (deprecated).
Will be removed in v2.0 since all the available features were migrated to vector
Vector
Output image type android.graphics.Bitmap android.graphics.drawable.Drawable
Size ❌ Fixed ✅ Dynamic. Based on View size
Speed ❌ Slow (> 500 ms in average), so must be created in advance and only in background thread. Coroutines support included ✅ Instant. All calculations performed during Drawable.setBounds, almost instantly

You should use deprecated Raster QR codes only if you need extra customizability or special features like in this example.


Vector code (Drawable)

Step 1. Create QR code data. There are multiple QR types: Plain Text, Url, Wi-Fi, Email, GeoPos, Profile Cards, Phone, etc.

val data = QrData.Url("https://example.com")

Step 2. Define styling options.

1. Using DSL:

val options = createQrVectorOptions {
    
    padding = .125f

    background {
        drawable = ContextCompat
            .getDrawable(context, R.drawable.frame)
    }
    
    logo {
        drawable = ContextCompat
            .getDrawable(context, R.drawable.logo)
        size = .25f
        padding = QrVectorLogoPadding.Natural(.2f)
        shape = QrVectorLogoShape
            .Circle
    }
    colors {
        dark = QrVectorColor
            .Solid(Color(0xff345288))
        ball = QrVectorColor.Solid(
            ContextCompat.getColor(context, R.color.your_color)
        )
        frame = QrVectorColor.LinearGradient(
            colors = listOf(
                0f to android.graphics.Color.RED,
                1f to android.graphics.Color.BLUE,
            ),
            orientation = QrVectorColor.LinearGradient
                .Orientation.LeftDiagonal
        )
    }
    shapes {
        darkPixel = QrVectorPixelShape
            .RoundCorners(.5f)
        ball = QrVectorBallShape
            .RoundCorners(.25f)
        frame = QrVectorFrameShape
            .RoundCorners(.25f)
    }
}

2. Using builder:

val options = QrVectorOptions.Builder()
    .setPadding(.3f)
    .setLogo(
        QrVectorLogo(
            drawable = ContextCompat
                .getDrawable(context, R.drawable.logo),
            size = .25f,
            padding = QrVectorLogoPadding.Natural(.2f),
            shape = QrVectorLogoShape
                .Circle
        )
    )
    .setBackground(
        QrVectorBackground(
            drawable = ContextCompat
                .getDrawable(context, R.drawable.frame),
        )
    )
    .setColors(
        QrVectorColors(
            dark = QrVectorColor
                .Solid(Color(0xff345288)),
            ball = QrVectorColor.Solid(
                ContextCompat.getColor(context, R.color.your_color)
            ),
            frame = QrVectorColor.LinearGradient(
                colors = listOf(
                    0f to android.graphics.Color.RED,
                    1f to android.graphics.Color.BLUE,
                ),
                orientation = QrVectorColor.LinearGradient
                    .Orientation.LeftDiagonal
            )
        )
    )
    .setShapes(
        QrVectorShapes(
            darkPixel = QrVectorPixelShape
                .RoundCorners(.5f),
            ball = QrVectorBallShape
                .RoundCorners(.25f),
            frame = QrVectorFrameShape
                .RoundCorners(.25f),
        )
    )
    .build()

Step 3. Create QR code drawable:

val drawable : Drawable = QrCodeDrawable(data, options)

To interop with Jetpack Compose, you can use this library (recommended) or convert Drawable to Bitmap (not recommended).

Step 4. Press ⭐ if you liked this lib


Raster code (Bitmap)

Deprecated (click to show). Will be removed in v2.0

Step 1. Create QR code data. There are multiple QR types: Plain Text, Url, Wi-Fi, Email, GeoPos, Profile Cards, Phone, etc.

val data = QrData.Url("https://example.com")

Step 2. Define styling options using builder:

// Color(v : Long) and Long.toColor() functions take 
// 0xAARRGGBB long and convert it to color int.
// Colors from android resources also can be used.
val options = QrOptions.Builder(1024)
    .padding(.3f)
    .background(
        QrBackground(
            drawable = ContextCompat
                .getDrawable(context, R.drawable.frame),
        )
    )
    .logo(
        QrLogo(
            drawable = ContextCompat
                .getDrawable(context, R.drawable.logo),
            size = .25f,
            padding = QrLogoPadding.Accurate(.2f),
            shape = QrLogoShape
                .Circle
        )
    )
    .colors(
        QrColors(
            dark = QrColor
                .Solid(Color(0xff345288)),
            highlighting = QrColor
                .Solid(0xddffffff.toColor()),
        )
    )
    .shapes(
        QrElementsShapes(
            darkPixel = QrPixelShape
                .RoundCorners(),
            ball = QrBallShape
                .RoundCorners(.25f),
            frame = QrFrameShape
                .RoundCorners(.25f),
            highlighting = QrBackgroundShape
                .RoundCorners(.05f)
        )
    )
    .build()

Or using DSL:

val options = createQrOptions(1024, 1024, .3f) {
    background {
        drawable = ContextCompat
            .getDrawable(context, R.drawable.frame)
    }
    logo {
        drawable = ContextCompat
            .getDrawable(context, R.drawable.logo)
        size = .25f
        padding = QrLogoPadding.Accurate(.2f)
        shape = QrLogoShape
            .Circle
    }
    colors {
        dark = QrColor
            .Solid(0xff345288.toColor())
        highlighting = QrColor
            .Solid(Color(0xddffffff))
    }
    shapes {
        darkPixel = QrPixelShape
            .RoundCorners()
        ball = QrBallShape
            .RoundCorners(.25f)
        frame = QrFrameShape
            .RoundCorners(.25f)
        highlighting = QrBackgroundShape
            .RoundCorners(.05f)
    }
}

Step 3. Create a QR code generator and pass your data and options into it:

val generator = QrCodeGenerator()
  
val bitmap = generator.generateQrCode(data, options)

QrCodeGenerator is an interface, but also is a function, that returns generator instance.

‼️ Raster QR codes must be generated in BACKGROUND THREAD. Generator supports cancellation with coroutines. generateQrCodeSuspend is always performed with Dispatchers.Default

//todo: don't use GlobalScope
GlobalScope.launch {
    val bitmap = generator.generateQrCodeSuspend(data, options)
}

Generator can work in parallel threads (different Default coroutine dispatchers). By default generator works in SingleThread. To change it pass another ThreadPolicy to QrCodeGenerator function.

For example:

val threadPolicy = when(Runtime.getRuntime().availableProcessors()){
    in 1..3 -> ThreadPolicy.SingleThread
    in 4..6 -> ThreadPolicy.DoubleThread
    else -> ThreadPolicy.QuadThread
}

val generator = QrCodeGenerator(threadPolicy)

‼️ NOTE: Use wisely! More threads doesn't mean more performance! It depends on device and size of the QR code.

Customization

Vector code (Drawable)

Shapes of QR code elements can be customized using android.graphics.Path.

For example, this is an implementation of circle pixels:

object Circle : QrVectorPixelShape {

    override fun createPath(size: Float, neighbors: Neighbors): Path = Path().apply {
        addCircle(size/2f, size/2f, size/2, Path.Direction.CW)
    }
}

Colors of QR code elements can be customized using android.graphics.Paint.

For example, this is an implementation of sweep gradient:

 class SweepGradient(
        val colors: List<Pair<Float, Int>>
    ) : QrVectorColor {

        override fun createPaint(width: Float, height: Float): Paint =
            Paint().apply {
              shader = android.graphics.SweepGradient(
                  width / 2, height / 2,
                  colors.map { it.second }.toIntArray(),
                  colors.map { it.first }.toFloatArray()
              )
        }
    }
    

Raster code (Bitmap)

Deprecated (click to show). Will be removed in v2.0

You can easily implement your own shapes and coloring for QR Code in 2 ways: using math formulas or by drawing on canvas. Second way is usually slower and uses a lot of memory but provides more freedom.

For example:

  1. Using math formulas:
object Circle : QrPixelShape {
    override fun invoke(
        i: Int, j: Int, elementSize: Int, neighbors: Neighbors
    ): Boolean {
        val center = elementSize/2.0
        return sqrt((center-i).pow(2) + (center-j).pow(2)) < center
    }
}

val options = createQrOptions(1024, .3f) {
    shapes {
        darkPixel = Circle
    }
}
//It is not scannable. Don't create such colorful QR codes
object Pride : QrColor {
    override fun invoke(
        i: Int, j: Int, width : Int, height : Int
    ): Int {
        return when(6f * j/height){
            in 0f..1f -> Color.RED
            in 1f..2f-> Color(0xffffa500)
            in 2f..3f-> Color.YELLOW
            in 3f..4f-> Color(0xff00A300)
            in 4f..5f-> Color.BLUE
            else -> Color(0xff800080)
        }
    }
}

val options = createQrOptions(1024) {
    colors {
        ball = Pride
    }
}
  1. By drawing on canvas:
val options : QrOptions = createQrOptions(1024) {
    shapes {
        darkPixel = drawShape { canvas, drawPaint, erasePaint ->
          val cx = canvas.width/2f
          val cy = canvas.height/2f
          val radius = minOf(cx,cy)
          canvas.drawCircle(cx, cy, radius, drawPaint)
          canvas.drawCircle(cx, cy, radius*2/2.5f, erasePaint)
          canvas.drawCircle(cx, cy, radius/1.75f, drawPaint)
        }
    }
}

drawShape is a generic function that can be used only inside a shapes or logo scope and only to create properties of QrElementsShapes or QrLogoShape. Usage with other type-parameters will cause an exception.

‼️ NOTE: Created shape should not be used with other QrOptions with larger size! This can cause shape quality issues.

You can also implement QrCanvasShape and cast it so necessary shape:

object Ring : QrCanvasShape {
   override fun draw(
       canvas: Canvas, drawPaint: Paint, erasePaint: Paint
   ) {
       // ...
   }
}

val ring : QrPixelShape = Ring
    .toShapeModifier(elementSize = 48)
    .asPixelShape()
// or automatically determine size with DSL
val ringPixelOptions : QrOptions = createQrOptions(1024){
    shapes {
        darkPixel = drawShape(Ring::draw)
    }
}
object CanvasColor : QrCanvasColor {
    override fun draw(canvas: Canvas) = with(canvas) {
        withRotation(135f, width/2f, height/2f) {
            drawRect(-width / 2f, -height / 2f,
                1.5f * width, height / 2f,
                Paint().apply { color = Color.BLACK }
            )
            drawRect(-width / 2f, height / 2f, 
                1.5f * width.toFloat(), 1.5f * height.toFloat(),
                Paint().apply { color = Color.DKGRAY }
            )
        }
    }
}
   

Using draw function inside colors scope you can colorize your code elements as you want. It will be converted to a QrColor.

This is QrOptions of the code above:

val options =  createQrOptions(1024, .2f) {
    colors {
        dark = QrColor.RadialGradient(
            startColor = Color.GRAY,
            endColor = Color.BLACK
        )
        ball = draw(CanvasColor::draw)
        frame = draw {
            withRotation(
                180f, width / 2f,
                height / 2f, CanvasColor::draw
            )
        }
        symmetry = true
    }
    shapes {
        darkPixel = QrPixelShape.RoundCorners()
        frame = QrFrameShape.RoundCorners(
            .25f, outer = false, inner = false
        )
    }
}

‼️ NOTE: Created color should not be used with other QrOptions with larger size!

FAQ

I can't scan my code

  • Some combinations of shapes are not compatible.
  • If you create custom shapes, always test if they corrupt your qr code.
  • Choose contrast colors for your code and dont't use too many of them at once.
  • If you are using logo, make it smaller or apply next advice.
  • Set errorCorrectionLevel explicitly to QrErrorCorrectionLevel.High

I'm trying to encode non-latin symbols and getting a corrupted QR code

See Issue #6


I want a logo/background from a device (file system, gallery)

You just need to resolve Drawable form file system. See this


I want to create shapes for frame or ball with central symmetry

See Issue #13


I want a QR code with 4 eyes

See Issue #19


custom-qr-generator's People

Contributors

alexzhirkevich avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

custom-qr-generator's Issues

Generated QR not scan-able

I asked for Java sample and I got some code that works and generates the QR I want but the generated Qr is not scannable. I am not sure what I missed. I tried it on multiple devices.

here is my code

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;

import androidx.core.content.ContextCompat;

import com.github.alexzhirkevich.customqrgenerator.HighlightingType;
import com.github.alexzhirkevich.customqrgenerator.QrData;
import com.github.alexzhirkevich.customqrgenerator.QrErrorCorrectionLevel;
import com.github.alexzhirkevich.customqrgenerator.QrHighlighting;
import com.github.alexzhirkevich.customqrgenerator.QrHighlightingKt;
import com.github.alexzhirkevich.customqrgenerator.vector.QrCodeDrawableKt;
import com.github.alexzhirkevich.customqrgenerator.vector.QrVectorOptions;
import com.github.alexzhirkevich.customqrgenerator.vector.style.QVectorShapeUtilsKt;
import com.github.alexzhirkevich.customqrgenerator.vector.style.QrVectorBackground;
import com.github.alexzhirkevich.customqrgenerator.vector.style.QrVectorBallShape;
import com.github.alexzhirkevich.customqrgenerator.vector.style.QrVectorColor;
import com.github.alexzhirkevich.customqrgenerator.vector.style.QrVectorColors;
import com.github.alexzhirkevich.customqrgenerator.vector.style.QrVectorFrameShape;
import com.github.alexzhirkevich.customqrgenerator.vector.style.QrVectorLogo;
import com.github.alexzhirkevich.customqrgenerator.vector.style.QrVectorLogoPadding;
import com.github.alexzhirkevich.customqrgenerator.vector.style.QrVectorLogoShape;
import com.github.alexzhirkevich.customqrgenerator.vector.style.QrVectorPixelShape;
import com.github.alexzhirkevich.customqrgenerator.vector.style.QrVectorShapes;
import com.github.yohannestz.cberemix.R;
import com.google.android.material.elevation.SurfaceColors;

import java.nio.charset.StandardCharsets;

public class QRCodeGenerator {
    private final Context context;

    public QRCodeGenerator(Context context) {
        this.context = context;
    }

    public Drawable drawableFromText(String data) {
        if (Utils.isHex(data)) {
            data = data.toUpperCase();
        }
        QrData qrData = new QrData.Text(data);
        return QrCodeDrawableKt.QrCodeDrawable(qrData, getQrVectorOptions(data), StandardCharsets.UTF_8);
    }

    private QrVectorOptions getQrVectorOptions(String data) {
        if (data.length() < 140)
            return getDesignQrVectorOptions(data);
        else
            return getCompatibilityQrVectorOptions();
    }

    private QrVectorOptions getDesignQrVectorOptions(String data) {
        QrVectorLogo.Builder qrVectorLogoBuilder = new QrVectorLogo.Builder();
        qrVectorLogoBuilder.setDrawable(ContextCompat.getDrawable(context, R.drawable.cbe_vector));
        qrVectorLogoBuilder.setShape(new QrVectorLogoShape.RoundCorners(0.5f));
        qrVectorLogoBuilder.setPadding(new QrVectorLogoPadding.Natural(.5f));
        qrVectorLogoBuilder.setSize(0.1f);
        QrVectorLogo qrVectorLogo = qrVectorLogoBuilder.build();

        QrVectorOptions.Builder optionsBuilder = getDefaultOptionsBuilder()
                .setShapes(
                        new QrVectorShapes(
                                new QrVectorPixelShape.Rect(.75f),
                                new QrVectorPixelShape.Rect(.75f),
                                new QrVectorBallShape.RoundCorners(.15f, true, true, true, true),
                                new QrVectorFrameShape.RoundCorners(.15f, 1f, true, true, true, true, 7),
                                true
                        )
                )
                .setColors(
                        new QrVectorColors(
                                new QrVectorColor.Solid(SurfaceColors.SURFACE_1.getColor(context)),
                                new QrVectorColor.Solid(ContextCompat.getColor(context, R.color.md_theme_light_primary)),
                                new QrVectorColor.Solid(ContextCompat.getColor(context, R.color.md_theme_light_primary)),
                                new QrVectorColor.Solid(ContextCompat.getColor(context, R.color.md_theme_light_primary))
                        )
                )
                .setBackground(
                        new QrVectorBackground(
                                ContextCompat.getDrawable(context, R.drawable.bg_box),
                                (drawable, i, i1) -> Bitmap.createBitmap(i, i1, Bitmap.Config.ARGB_8888),
                                new QrVectorColor.Solid(SurfaceColors.SURFACE_1.getColor(context))
                        )
                )
                .setLogo(qrVectorLogo)
                .setAnchorsHighlighting(new QrHighlighting(
                        new HighlightingType.Styled(
                                QrHighlightingKt.QrVersionEyeShape(
                                        new QrVectorFrameShape.AsPixelShape(new QrVectorPixelShape.Rect(.75f), 5),
                                        QVectorShapeUtilsKt.asBallShape(new QrVectorPixelShape.Rect(.75f))),
                                new QrVectorColor.Solid(SurfaceColors.SURFACE_1.getColor(context)),
                                new QrVectorPixelShape.Rect(.0f),
                                new QrVectorColor.Solid(SurfaceColors.SURFACE_1.getColor(context))
                        ),
                        new HighlightingType.Styled(
                                QrHighlightingKt.QrVersionEyeShape(
                                        new QrVectorFrameShape.AsPixelShape(new QrVectorPixelShape.Rect(.75f), 5),
                                        QVectorShapeUtilsKt.asBallShape(new QrVectorPixelShape.Rect(.75f))),
                                new QrVectorColor.Solid(ContextCompat.getColor(context, R.color.md_theme_light_primary)),
                                new QrVectorPixelShape.Rect(.0f),
                                new QrVectorColor.Solid(SurfaceColors.SURFACE_1.getColor(context))
                        ),
                        new HighlightingType.Styled(
                                new QrVectorPixelShape.Rect(.75f), new QrVectorColor.Solid(SurfaceColors.SURFACE_1.getColor(context)),
                                new QrVectorPixelShape.Rect(.75f), new QrVectorColor.Solid(ContextCompat.getColor(context, R.color.md_theme_light_primary))),
                        1.0f
                ));


        if (data.length() > 60) {
            optionsBuilder.setErrorCorrectionLevel(QrErrorCorrectionLevel.Low);
        } else {
            optionsBuilder.setErrorCorrectionLevel(QrErrorCorrectionLevel.Medium);
        }

        new HighlightingType.Styled(new QrVectorFrameShape.RoundCorners(.15f, 1f, true, true, true, true, 7), new QrVectorColor.Solid(SurfaceColors.SURFACE_1.getColor(context)), new QrVectorFrameShape.RoundCorners(.15f, 1f, true, true, true, true, 7), new QrVectorColor.Solid(ContextCompat.getColor(context, R.color.md_theme_light_primary)));
        QrHighlightingKt.QrVersionEyeShape(
                new QrVectorFrameShape.AsPixelShape(
                        new QrVectorPixelShape.Rect(.75f), 5
                ),
                QVectorShapeUtilsKt.asBallShape(new QrVectorPixelShape.Rect(.75f))
        );

        return optionsBuilder.build();
    }

    private QrVectorOptions getCompatibilityQrVectorOptions() {
        return getDefaultOptionsBuilder()
                .setShapes(
                        new QrVectorShapes(
                                new QrVectorPixelShape.RoundCorners(.0f),
                                new QrVectorPixelShape.RoundCorners(.35f),
                                new QrVectorBallShape.RoundCorners(.15f, true, true, true, true),
                                new QrVectorFrameShape.RoundCorners(.15f, 1f, true, true, true, true, 7),
                                true
                        )
                )
                .build();
    }

    private QrVectorOptions.Builder getDefaultOptionsBuilder() {
        return new QrVectorOptions.Builder()
                .setPadding(.15f)
                .setBackground(
                        new QrVectorBackground(
                                ContextCompat.getDrawable(context, R.drawable.bg_box),
                                (drawable, i, i1) -> Bitmap.createBitmap(i, i1, Bitmap.Config.ARGB_8888),
                                new QrVectorColor.Solid(SurfaceColors.SURFACE_1.getColor(context))
                        )
                )
                .setErrorCorrectionLevel(QrErrorCorrectionLevel.Low);
    }

    //for future
    private static Bitmap drawableToBitmap(Drawable drawable, int size) {
        if (drawable instanceof BitmapDrawable) {
            BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
            if (bitmapDrawable.getBitmap() != null) {
                return bitmapDrawable.getBitmap();
            }
        }
        Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);
        return bitmap;
    }
}

and in my fragment I have:

   //onStart
   binding.qrImageView.setImageDrawable(generateQr(qrDataText));
   private Drawable generateQr(String payload) {
        QRCodeGenerator qrCodeGenerator = new QRCodeGenerator(getContext());
        qrDataText = "https://cbe.com/receive?account=100003450&amount=" + payload;
        return qrCodeGenerator.drawableFromText(qrDataText);
    }

Result:
image preview

Highlight anchor elements for better recognition

When set background image to the qr code for some images, scanner not able to scan the qr code.
i did research and found if background image has black part at any of 4 corners that eye can't be differentiated by the scanner and scanner can't read it.

Versions
Library : '8b2a97c5e6' and '1.6.1'
Android: '13'

  1. Here is the QR Code which generated using this Library with background image (NOT SCANNABLE)
    Screenshot_20230501_105542

  2. Here is the QR Code which generated online using same background image. (SCANNABLE)
    Screenshot_20230501_105602

Screenshot_20230501_1056021

When adding background image to the qr code they are adding some opacity around the eyes so scanner can differentiate eyes and background. also they highlight the fourth small eye (may be this can help to read qr code easily but i am not sure) check i marked with red arrow. Can you add this option or flag to add opacity around the eyes?

Publish apks on GitHub

Is your feature request related to a problem? Please describe.

App can not be installed w/o gplay. There is no place where an app like Obtainium can grab the apk from.

Describe the solution you'd like

Signed (no debug build) apk(s) attached to GitHub releases.

Describe alternatives you've considered

N/A

Additional context

QrVectorLogo is not correctly scaled with non whole size

Library version: 1.6.0
Android version: 13

I'm using this simple vector image, that displays a circle

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="40dp"
    android:height="40dp"
    android:viewportWidth="40"
    android:viewportHeight="40">
    <path
        android:pathData="m0,20a20,20 0 0,1 40,0a20,20 0 0,1 -40,0z"
        android:fillColor="#000000" />
</vector>

When the size of the qr code view is not an integer - for example, I want it to take up part of the screen width, say 0.6, the logo is drawn rather pixelated. If I round up the size, it's drawn as it should be. Here is a sample code in Compose:

val textContent = LoremIpsum().values.first().take(300)
val context = LocalContext.current
val drawable = remember(textContent) {
    QrCodeDrawable(
        data = QrData.Text(textContent),
        options = QrVectorOptions.Builder()
            .setErrorCorrectionLevel(QrErrorCorrectionLevel.High)
            .setLogo(
                QrVectorLogo(
                    drawable = AppCompatResources.getDrawable(context, R.drawable.ic_logo),
                    padding = QrVectorLogoPadding.Natural(.28f),
                )
            )
            .setShapes(
                QrVectorShapes(
                    darkPixel = QrVectorPixelShape.RoundCorners(0.5f),
                    ball = QrVectorBallShape.Circle(1f),
                    frame = QrVectorFrameShape.Circle()
                )
            )
            .build(),
    )
}
Image(
    rememberDrawablePainter(drawable),
    contentDescription = null,
    contentScale = ContentScale.None,
    modifier = Modifier
        .size(300.5.dp)
)

Result with size 300.5.dp:

Result with size 300.dp:

Charset in Vcard

I've generated an Vcard QRcode:

val drawableBitmap = QrCodeDrawable(
qrCodeVCard, qrOptions, Charsets.UTF_8
)
but its not show correct Name in Vcard

image

Expose reserved areas

Is your feature request related to a problem? Please describe.
I created a QR code design which features randomly selected pixel highlights in my apps accent color.
Screenshot_20230709_124750_BitBanana Debug~2
It works fine most of the time as the error correction easily compensates those pixels.
But in some cases, when the higlight happens to be at an important area, like the center of an alignment pattern, it produces hard to read codes.

Describe the solution you'd like
The paint method of QrVectorColor and the shape method of QrVectorPixelShape should have an additional input parameter that gives some extra information about the current pixel.
This could either be a boolean that states if it belongs to a reserved area or an int that works like an enum stating exactly to which area it belongs.

The different reserved areas are:

  • Alignment pattern
  • timing pattern
  • dark module
  • version information
  • format information

Please refer to this page:
https://www.thonky.com/qr-code-tutorial/module-placement-matrix

Describe alternatives you've considered
The alternative would be to not use any highlights or to use a darker color. But I want to use my apps accent color and I like the way it looks now.

com.google.zxing.WriterException

Describe the bug
Caused by com.google.zxing.WriterException:
at com.google.zxing.qrcode.encoder.Encoder.chooseVersion (Encoder.java:313)
at com.google.zxing.qrcode.encoder.Encoder.recommendVersion (Encoder.java:213)
at com.google.zxing.qrcode.encoder.Encoder.encode (Encoder.java:147)
at com.github.alexzhirkevich.customqrgenerator.vector.QrCodeDrawableImpl. (QrCodeDrawable.kt:44)
at com.github.alexzhirkevich.customqrgenerator.vector.QrCodeDrawableKt.QrCodeDrawable (QrCodeDrawable.kt:29)

Versions
Library : '1.6.2'
Android: '12'

Ambiguous method call

It is not possible to use QrVectorOptions.Builder from java side.
Decompiler shows identical setters for all fields inside builder.
qrcg_issue

version used: 1.5.3

When logo set sometime getting error immutable bitmap passed to Canvas constructor

java.lang.IllegalStateException: Immutable bitmap passed to Canvas constructor
at android.graphics.Canvas.(Canvas.java:114)
at com.github.alexzhirkevich.customqrgenerator.vector.QrCodeDrawableImpl.createLogo(QrCodeDrawable.kt:547)
at com.github.alexzhirkevich.customqrgenerator.vector.QrCodeDrawableImpl.resize(QrCodeDrawable.kt:490)
at com.github.alexzhirkevich.customqrgenerator.vector.QrCodeDrawableImpl.setBounds(QrCodeDrawable.kt:131)
at android.widget.ImageView.configureBounds(ImageView.java:1299)
at android.widget.ImageView.updateDrawable(ImageView.java:1084)
at android.widget.ImageView.setImageDrawable(ImageView.java:599)

Add QrVectorPixelShape.Square(size) shape modifier

Is your feature request related to a problem? Please describe.
It seems to be more complicated to create a QR Code with square pixel shapes than it should be.

Describe the solution you'd like
There should be a built in QrVectorPixelShape.Square(size) shape modifier

Describe alternatives you've considered
Using QrVectorPixelShape.RoundCorners(0f) is different, as the shape cannot be scaled

QrVectorLogo is not correctly showed

Library version: 1.6.2
Android version: 13 (Samsung S21)

I'm using this vector image, that displays telegram logo

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="100dp"
    android:height="100dp"
    android:viewportWidth="100"
    android:viewportHeight="100">
    <group>
        <clip-path android:pathData="M50,0L50,0A50,50 0,0 1,100 50L100,50A50,50 0,0 1,50 100L50,100A50,50 0,0 1,0 50L0,50A50,50 0,0 1,50 0z" />
        <path
            android:fillColor="#ffffff"
            android:pathData="M50,0L50,0A50,50 0,0 1,100 50L100,50A50,50 0,0 1,50 100L50,100A50,50 0,0 1,0 50L0,50A50,50 0,0 1,50 0z" />
        <path
            android:fillColor="#34AADF"
            android:fillType="evenOdd"
            android:pathData="M100,0H0V100H100V0ZM50.32,77.64C65.41,77.64 77.64,65.41 77.64,50.32C77.64,35.23 65.41,23 50.32,23C35.23,23 23,35.23 23,50.32C23,65.41 35.23,77.64 50.32,77.64ZM33.38,52.24C33.38,52.24 31.5,51.02 34.71,49.72C34.71,49.72 48.24,43.96 52.94,41.94C54.74,41.13 60.84,38.53 60.84,38.53C60.84,38.53 63.66,37.4 63.42,40.15C63.36,40.97 63.02,43.27 62.6,46.11C62.44,47.2 62.26,48.37 62.09,49.56C61.15,55.64 60.14,62.29 60.14,62.29C60.14,62.29 59.98,64.16 58.65,64.48C57.32,64.81 55.13,63.35 54.74,63.02C54.67,62.97 54.36,62.76 53.88,62.44C52.18,61.28 48.43,58.74 46.84,57.34C46.29,56.86 45.66,55.88 46.92,54.75C49.73,52.07 53.09,48.75 55.13,46.64C56.07,45.67 57.01,43.4 53.09,46.15C47.54,50.13 42.06,53.86 42.06,53.86C42.06,53.86 40.81,54.67 38.47,53.94C36.12,53.21 33.38,52.24 33.38,52.24Z" />
    </group>
</vector>
logo {
    drawable = AppCompatResources.getDrawable(
        context, R.drawable.ic_logo_telegram
    )
    size = .25f
    padding = QrVectorLogoPadding.Natural(.2f)
    shape = QrVectorLogoShape.Default
}

Result. If you set the playback speed to 0.5, the first telegram logo is opened, then the qr code.

2023-06-18.23.54.54.mp4

Is the size of the positioning markings adjustable?

Describe the option you are trying to implement
I want to create QR code with smaller positioning markings (the 3 shapes on the top left and right and bottom left)

Is there any way to adjust the size of them and let the content grow a bit more?

Thanks for the library! Awesome job!

Error if set logo

If i set logo from DrawableSource.Resource it crash my app, if i set DrawableSource.Empty all ok
I convert drawable to bitmap and it throw me error if i set logo
Logs:
java.lang.IllegalArgumentException: width and height must be > 0
at android.graphics.Bitmap.createBitmap(Bitmap.java:1111)
at android.graphics.Bitmap.createBitmap(Bitmap.java:1078)
at android.graphics.Bitmap.createBitmap(Bitmap.java:1028)
at android.graphics.Bitmap.createBitmap(Bitmap.java:989)
at androidx.core.graphics.drawable.DrawableKt.toBitmap(Drawable.kt:68)
at com.github.alexzhirkevich.customqrgenerator.style.BitmapScale$FitXY.scale(BitmapScale.kt:33)
at com.github.alexzhirkevich.customqrgenerator.vector.QrCodeDrawableImpl.resize(QrCodeDrawableImpl.kt:369)
at com.github.alexzhirkevich.customqrgenerator.vector.QrCodeDrawableImpl.setBounds(QrCodeDrawableImpl.kt:128)
at androidx.core.graphics.drawable.DrawableKt.toBitmap(Drawable.kt:72)
at androidx.core.graphics.drawable.DrawableKt.toBitmap$default(Drawable.kt:46)

Code(Jetpack Compose used):
val data = QrData.Text(curSchedule?.JoinCode.toString())
val options = QrVectorOptions.Builder()
.setPadding(.3f)
.setErrorCorrectionLevel(QrErrorCorrectionLevel.High)
.setLogo(
QrVectorLogo(
drawable =drawable,
)
)
.setShapes(
QrVectorShapes(
darkPixel = QrVectorPixelShape
.RoundCorners(.5f),
ball = QrVectorBallShape
.RoundCorners(.25f),
frame = QrVectorFrameShape
.RoundCorners(.25f),
)
)
.build()
val drawable = QrCodeDrawable(context, data, options)
Image(
bitmap = drawable.toBitmap(1024, 1024).asImageBitmap(),
contentDescription = "qrCode",
modifier = Modifier
.fillMaxSize()
)

Logo from url

Can you add the option to load a logo from the url and not just from resources? please

Thank you for your work, is very nice this library.

Logo seems not cropped properly

Describe the bug
When set logo with Accurate or Natural padding (0.25) and its not cropping correctly and outline visible, also same issue for all shape circle or round rectangle etc. i tired with JPG and PNG logo, issue faced for both.

Versions
Library : '1.6.1'
Android: 13
Screenshot_20230419_113628
Screenshot_20230419_113643

Screenshot_20230419_113721
Screenshot_20230419_113704

Documentation unclear about features

Describe the option you are trying to implement
Hi, I try to create a QR code with random circle sizes and colors with the vector implementation.
Is this possible? The instagram example is only provided with the deprecated method.

It feels like this should be simple, but I can't find a way how to do this.
Thanks for your help.

P.S: If it is not possible yet I would love to see this feature implemented.

Help Implementing

Sorry for the noob question, new dev here!
I am using this version:
implementation ("com.github.alexzhirkevich:custom-qr-generator:2.0.0-alpha01")

jitpack is in my settings.gradle
maven("https://jitpack.io")

Yet theres a problem importing some functions:
import com.github.alexzhirkevich.customqrgenerator.QrData
import com.github.alexzhirkevich.customqrgenerator.createQrVectorOptions
import com.github.alexzhirkevich.customqrgenerator.dsl.QrVectorLogoShape

Unresolved reference: createQrVectorOptions
Unresolved reference: dsl

Am I missing something?

Flutter support

Hi Alex,

Thanks for this amazing example project, can you add support for Flutter for Custom Qr Generator?
I think will be amazing for pub.dev community!

Thanks in advance!

crash

2022-07-29 10:18:21.322 16256-16256/com.github.alexzhirkevich.customqrgenerator.example E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.github.alexzhirkevich.customqrgenerator.example, PID: 16256
com.github.alexzhirkevich.customqrgenerator.QrCodeCreationException
at com.github.alexzhirkevich.customqrgenerator.QrGenerator$generateQrCodeSuspend$2.invokeSuspend(QRGenerator.kt:38)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:749)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@7ecff62, Dispatchers.Main.immediate]
Caused by: java.lang.NoSuchMethodError: No static method pack(I)J in class Landroid/graphics/Color; or its super classes (declaration of 'android.graphics.Color' appears in /system/framework/framework.jar)
at com.github.alexzhirkevich.customqrgenerator.QrGenerator.drawBackground(QRGenerator.kt:231)
at com.github.alexzhirkevich.customqrgenerator.QrGenerator.createQrCodeInternal(QRGenerator.kt:53)
at com.github.alexzhirkevich.customqrgenerator.QrGenerator.access$createQrCodeInternal(QRGenerator.kt:16)
at com.github.alexzhirkevich.customqrgenerator.QrGenerator$generateQrCodeSuspend$2$1$1.invokeSuspend(QRGenerator.kt:33)
at com.github.alexzhirkevich.customqrgenerator.QrGenerator$generateQrCodeSuspend$2$1$1.invoke(QRGenerator.kt)
at com.github.alexzhirkevich.customqrgenerator.QrGenerator$generateQrCodeSuspend$2$1$1.invoke(QRGenerator.kt)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:89)
at kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScope.kt:264)
at com.github.alexzhirkevich.customqrgenerator.QrGenerator$generateQrCodeSuspend$2.invokeSuspend(QRGenerator.kt:32)

Gradient background

Is there a way that we can make a gradient background that fills the whole qr

Customizing QR code after generation

I am generating QR code using zxing library...after generating, i am getting a bitmap image...can i edit QR code in the bitmap image in future instead of generating it again?

[FIXED] Long time Generation

Taking too much time to generate Bitmap. On average devices with 1024 size takes about 8 - 10 seconds. How can we reduce generation time?

add jetpack compose support to the library

Basic usage in Jetpack compose

var text by remember {
        mutableStateOf("")
    }

QRCode(text = text, modifier = Modifier.size(200.dp))

Please add jetpack compose support and below compose function so, it can be easy to use in compose

@Composable
fun QRCode(
    modifier: Modifier = Modifier,
    text: String = "This is QR text", enableLastEye: Boolean = true,
    color: Color = Color(0xFFBD22E7), enableGradient: Boolean = true,
    gradientColors: List<Color> = listOf(Color.Red, Color.Blue),
    gradientColorDirection: QrVectorColor.LinearGradient.Orientation = QrVectorColor.LinearGradient
        .Orientation.LeftDiagonal,
    enableLogo: Boolean = false,
    logo: Int? = null, logoShape: QrVectorLogoShape = QrVectorLogoShape.Circle,
) {

    val context = LocalContext.current
    AndroidView(factory = { ctx ->

        ImageView(ctx).apply {
            val options = createQrVectorOptions {

                padding = .01f

                fourthEyeEnabled = enableLastEye


                if (enableLogo) {
                    logo {
                        drawable = logo?.let { ContextCompat.getDrawable(ctx.findActivity(), it) }
                        size = .25f
                        padding = QrVectorLogoPadding.Natural(.2f)
                        shape = logoShape
                    }
                }

                colors {
                    dark = if (enableGradient) {
                        QrVectorColor.LinearGradient(
                            colors = listOf(
                                0f to gradientColors.first().toArgb(),
                                1f to gradientColors.last().toArgb(),
                            ),
                            orientation = gradientColorDirection
                        )
                    } else {
                        QrVectorColor
                            .Solid(color.toArgb())
                    }

                }
                shapes {
                    darkPixel = QrVectorPixelShape
                        .RoundCorners(.5f)
                    ball = QrVectorBallShape
                        .RoundCorners(.25f)
                    frame = QrVectorFrameShape
                        .RoundCorners(.25f)
                }
            }

            this.setImageBitmap(
                QrCodeDrawable({ text }, options = options)
                    .toBitmap(1024, 1024)
            )
        }
    }, update = {
        it.apply {
            val options = createQrVectorOptions {

                padding = .01f

                fourthEyeEnabled = enableLastEye


                if (enableLogo) {
                    logo {
                        drawable =
                            logo?.let { ContextCompat.getDrawable(context.findActivity(), it) }
                        size = .25f
                        padding = QrVectorLogoPadding.Natural(.2f)
                        shape = logoShape
                    }
                }

                colors {
                    dark = if (enableGradient) {
                        QrVectorColor.LinearGradient(
                            colors = listOf(
                                0f to gradientColors.first().toArgb(),
                                1f to gradientColors.last().toArgb(),
                            ),
                            orientation = gradientColorDirection
                        )
                    } else {
                        QrVectorColor
                            .Solid(color.toArgb())
                    }

                }
                shapes {
                    darkPixel = QrVectorPixelShape
                        .RoundCorners(.5f)
                    ball = QrVectorBallShape
                        .RoundCorners(.25f)
                    frame = QrVectorFrameShape
                        .RoundCorners(.25f)
                }
            }

            this.setImageBitmap(
                QrCodeDrawable({ text }, options = options)
                    .toBitmap(1024, 1024)
            )
        }
    }, modifier = modifier)

}

when centralSymmetry true, frame color and ball color not applying properly and shape getting rotate and destroy

Without frame color and ball color symmetry shape is good
new QrVectorColors(getQRVectorColor(Color.BLACK),
QrVectorColor.Unspecified.INSTANCE, QrVectorColor.Unspecified.INSTANCE,
QrVectorColor.Unspecified.INSTANCE);

qr_generate_1673250708748_

when frame color and ball color set getting this
new QrVectorColors(getQRVectorColor(Color.BLACK),
QrVectorColor.Unspecified.INSTANCE,getQRVectorColor(Color.BLACK),
getQRVectorColor(Color.BLACK));

qr_generate_1673250781516_

GIF Support

is it possible to add GIF image as foreground of QR Code?

Incorrect coding Cyrillic letters

When I generate a VCard QR code with a name in Cyrillic letters, when I scan the code, the name "Роман" became "?????", but if I set the name as "Roman", the code works correctly

Any Java samples?

I am trying to implement a custom QR like the ones provided in the examples (Instagram to be specific) code in Java. I have attempted to use the builder but was not successful.

can any one provide me with examples?

thanks.

Support the different standard encodings

Is your feature request related to a problem? Please describe.
I want my QR codes to be as small as possible.
I have to encode 64 character hex strings.

Describe the solution you'd like
The QR code specification defines 4 standard ways of encoding the data.
Numeric, Alphanumeric, binary & kanji.
I could encode hex strings way more efficiently if I just upper case them and then use Alphanumeric as encoding.
So in short:
It would be awesome if we could set the encoding option somehow.

Describe alternatives you've considered
The alterative are ugly big codes 😀

Additional context
Please see this page for more information:
https://www.thonky.com/qr-code-tutorial/data-encoding

Highlight anchor elements for better recognition seems break in Library version 2.0.0-alpha01

Library version
'com.github.alexzhirkevich:custom-qr-generator:2.0.0-alpha01'

Code Snippet
QrVectorOptions.Builder options =new QrVectorOptions.Builder()...
options.setAnchorsHighlighting(new QrHighlighting(HighlightingType.Default.INSTANCE,
HighlightingType.Default.INSTANCE, HighlightingType.None.INSTANCE, 0.6f));

Library version 2.0.0 -alpha01 ouput (Alpha not applying)
Screenshot_153452

Library version 1.6.2 ouput (Correct)
Screenshot_154359

Quoted issue
#27 (comment)

java.lang.IllegalArgumentException: width and height must be > 0

Describe the bug
at android.graphics.Bitmap.createBitmap (Bitmap.java:1111)
at android.graphics.Bitmap.createBitmap (Bitmap.java:1078)
at android.graphics.Bitmap.createBitmap (Bitmap.java:1028)
at android.graphics.Bitmap.createBitmap (Bitmap.java:989)
at androidx.core.graphics.drawable.DrawableKt.toBitmap (Drawable.kt:68)
at com.github.alexzhirkevich.customqrgenerator.style.BitmapScale$FitXY.scale (BitmapScale.kt:24)
at com.github.alexzhirkevich.customqrgenerator.vector.QrCodeDrawableImpl.createLogo (QrCodeDrawable.kt:522)
at com.github.alexzhirkevich.customqrgenerator.vector.QrCodeDrawableImpl.resize (QrCodeDrawable.kt:734)
at com.github.alexzhirkevich.customqrgenerator.vector.QrCodeDrawableImpl.setBounds (QrCodeDrawable.kt:159)

Versions
Library : 1.6.2'
Android: 12,13

[BUG] Documentation and Source Code is not published

Describe the bug
The quick documentation and viewing of source code does not work with intellij, this implies that it is not published

Versions
Library : '2.0.0-alpha01'
Android: 'N/A'

To Reproduce
Try quick documentation or view the source code in Android Studio

Expected behavior
Should be able to view the source code and documentation

Screenshots
If applicable, add screenshots to help explain your problem.

Additional context
You may try this plugin instead, as this auto configures gradle artifact publish

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.