15

I'm trying to generate some QR from my app, but I've seen there's a lot of types of QR like contact, Wi-Fi, etc.. And I'm wondering if there's a free API or library to implement this, I've seen that there's websites that are already generating it so I wanted to know if for Android is there any API or library to use.

What I've checked :

http://goqr.me/api

Zxing

But I'm not sure if there's a function to say ok I want a QR for a contact so I can add all of the information of it.

StuartDTO
  • 783
  • 7
  • 26
  • 72
  • For various formats, check out this excellent answer -- https://stackoverflow.com/a/26738158/3437352 – Siddharth Kamaria Oct 20 '20 at 12:09
  • I guess that's what I was looking for! is there any page where I have this updated? – StuartDTO Oct 20 '20 at 12:17
  • Someone posted this useful link in the same thread which I linked above -- https://github.com/zxing/zxing/wiki/Barcode-Contents – Siddharth Kamaria Oct 20 '20 at 12:23
  • @SiddharthKamaria So, I don't need any API or external stuff to generate QR I can use the same library to generate and to read? If so, with that library I can I custom the color add images, etc? – StuartDTO Oct 22 '20 at 13:25
  • For read, I use Google's Mobile Vision API primarily because it detects alot of the QR formats out of the box without us mincing the strings -- https://developers.google.com/vision/android/barcodes-overview. Regarding, your other query, I am honestly not aware of how to add overlays and change QR color :( – Siddharth Kamaria Oct 22 '20 at 14:20
  • @SiddharthKamaria Is it easy to implement? I mean, to read the barcode do you have a sample example? Could you post an answer so I can mark yours as a correct one? I was not aware of google Mobile vision API and yes, my aim is to detect the more formats QR the better.... – StuartDTO Oct 23 '20 at 16:43
  • I'll add an example as an answer. – Siddharth Kamaria Oct 23 '20 at 16:45
  • And just to add more clarity to this convo, it isn't that the QR codes are different "types" per say. The QR code just contains data. That's it. When it is scanned, the data is read. It just depends on what the system or app that scans it does with the data. – TJ Olsen Feb 03 '22 at 18:45

4 Answers4

27

QR code generation using ZXing

Add the following ZXing core dependency in your app level build.gradle file.

implementation 'com.google.zxing:core:3.4.0'

Sample code to generate a 512x512 px WiFi QR code. You can set the resultant Bitmap in an ImageView.

fun getQrCodeBitmap(ssid: String, password: String): Bitmap {
    val size = 512 //pixels
    val qrCodeContent = "WIFI:S:$ssid;T:WPA;P:$password;;"
    val hints = hashMapOf<EncodeHintType, Int>().also { it[EncodeHintType.MARGIN] = 1 } // Make the QR code buffer border narrower
    val bits = QRCodeWriter().encode(qrCodeContent, BarcodeFormat.QR_CODE, size, size) 
    return Bitmap.createBitmap(size, size, Bitmap.Config.RGB_565).also {
        for (x in 0 until size) {
            for (y in 0 until size) {
                it.setPixel(x, y, if (bits[x, y]) Color.BLACK else Color.WHITE)
            }
        }
    }
}

To generate other types of QR code such as SMS, VCard etc. you can check out this helpful ZXing Wiki.

Scanning QR code using Google Mobile Vision API

Add the following GMS dependency to your app level build.gradle.

implementation 'com.google.android.gms:play-services-vision:20.1.2'

Step 1: Setup the Barcode processor callback.

private val processor = object : Detector.Processor<Barcode> {
    
    override fun receiveDetections(detections: Detector.Detections<Barcode>?) {
        detections?.apply {
            if (detectedItems.isNotEmpty()) {
                val qr = detectedItems.valueAt(0)
                // Parses the WiFi format for you and gives the field values directly
                // Similarly you can do qr.sms for SMS QR code etc.
                qr.wifi?.let { 
                    Log.d(TAG, "SSID: ${it.ssid}, Password: ${it.password}")
                }
            }
        }
    }

    override fun release() {}
} 

Step 2: Setup the BardcodeDetector with the barcode processor callback and add it to the CameraSource as follows. Don't forget to check for Manifest.permission.CAMERA at runtime and add the same to your AndroidManifest.xml.

private fun setupCameraView() {
    if (ContextCompat.checkSelfPermission(requireContext(), android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
        BarcodeDetector.Builder(requireContext()).setBarcodeFormats(QR_CODE).build().apply {
            setProcessor(processor)
            if (!isOperational) {
                Log.d(TAG, "Native QR detector dependencies not available!")
                return
            }
            cameraSource = CameraSource.Builder(requireContext(), this).setAutoFocusEnabled(true)
                .setFacing(CameraSource.CAMERA_FACING_BACK).build()
        }
    } else {
        // Request camers permission from user
        // Add <uses-permission android:name="android.permission.CAMERA" /> to AndroidManifest.xml
    }
}

Step 3: Add a SurfaceView to your layout to host your CameraSource.

<SurfaceView
    android:id="@+id/surfaceView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" /> 

Step 4: Create a callback to start and stop the CameraSource when the surface is created / destroyed.

private val callback = object : SurfaceHolder.Callback {

    override fun surfaceCreated(holder: SurfaceHolder) {
        // Ideally, you should check the condition somewhere 
        // before inflating the layout which contains the SurfaceView
        if (isPlayServicesAvailable(requireActivity()))
            cameraSource?.start(holder)
    } 

    override fun surfaceDestroyed(holder: SurfaceHolder) {
        cameraSource?.stop()
    }

    override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { }
}


// Helper method to check if Google Play Services are up to-date on the phone
fun isPlayServicesAvailable(activity: Activity): Boolean {
    val code = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(applicationContext)
    if (code != ConnectionResult.SUCCESS) {
        GoogleApiAvailability.getInstance().getErrorDialog(activity, code, code).show()
        return false
    }
    return true
}

Step 5: Link everything together with the lifecycle methods.

// Create camera source and attach surface view callback to surface holder
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    return inflater.inflate(R.layout.fragment_camera_sheet, container, false).also {
        setupCamera()
        it.surfaceView.holder.addCallback(callback)
    }
}

// Free up camera source resources
override fun onDestroy() {
    super.onDestroy()
    cameraSource?.release()
}
Siddharth Kamaria
  • 2,448
  • 2
  • 17
  • 37
  • Amazing @Siddhart Kamaria, I'll let you know once I test it. It means that I have a when with all the possible detections and do its own stuff there? I mean now you have put qr.wifi but to make sure I can accept all I have to put all of the types there, right? – StuartDTO Oct 24 '20 at 09:47
  • @StuartDTO Yes correct, you can use a when statement in detections and do the correct processing, but make sure to handle null / empty values. Also do let me know if there are any issues with the code because I took this out from one of my projects and edited a bit! – Siddharth Kamaria Oct 24 '20 at 11:19
  • I guess it doesn't work because the camera permission, is not asking it to accept or deny – StuartDTO Oct 24 '20 at 13:40
  • I've asked the permissions in the else block but then it happens nothing. If I go back and open the activity again it shows a black screen but not camera stuff – StuartDTO Oct 24 '20 at 13:50
  • 1
    I finally make it work, but is there any way to create like a custom scan view? – StuartDTO Oct 24 '20 at 14:11
  • @StuartDTO By custom scan view, I think you want an overlay to place your QR code in. For now, I have added a transparent rectangle drawable `` on top of the `SurfaceView` with some border color. But I think Mobile Vision provides some graphic overlay options too, which I haven't explored yet. If you manage to add some cool overlay please do let me know on this thread - would like to make my app cool too :) – Siddharth Kamaria Oct 24 '20 at 14:19
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/223553/discussion-between-stuartdto-and-siddharth-kamaria). – StuartDTO Oct 24 '20 at 14:43
  • please correct this line to : val bits = QRCodeWriter().encode(qrCodeContent, BarcodeFormat.QR_CODE, size, size) – hamid Mahmoodi Dec 24 '21 at 12:46
5

U can generate QR with Zxing using QRCodeWriter class and use encode() function where the first param of it is the actual data to be held by the QR. Custom example:

val qrCodeData: String = "data"
val bitMatrix = QRCodeWriter().encode(
    String(
        qrCodeData.toByteArray(charset(CHARSET)),
        Charset.forName(CHARSET)
    ),
    BarcodeFormat.QR_CODE,
    size,
    size,
    hints
)

Where hints are also part of this lib and can be found in EncodeHintType.

Then u have to generate a Bitmap that can be displayed in e.g. ImageView.

val bitmap = Bitmap.createBitmap(
    size,
    size,
    Bitmap.Config.ARGB_8888
)

for (x in 0 until size) {
    for (y in 0 until size) {
        val fillColor = if (bitMatrix[x, y]) Color.BLACK else Color.WHITE
        bitmap.setPixel(x, y, fillColor) // <-- color ur QR to default black and white
    }
}
P.Juni
  • 2,365
  • 14
  • 26
  • But how's the way that I can generate the QR as this generators where I can say it's for Twitter, it's for a SMS, it's a Wifi QR... which is the paramter? – StuartDTO Oct 20 '20 at 11:38
  • I dont get a question. What is the diff in QR for Twitter, QR for SMS etc ? – P.Juni Oct 20 '20 at 11:39
  • 1
    You should add the dependency for Zxing in your answer `implementation 'com.google.zxing:core:3.4.0'` – Siddharth Kamaria Oct 20 '20 at 11:59
  • 1
    He said he checked it, but sure i could – P.Juni Oct 20 '20 at 12:01
  • @StuartDTO You can create strings for various type yourself and pass it to ZXing. For instance, WiFi has the format `"WIFI:S:$ssid;T:WPA;P:$passkey;;"`. Similarly you can find strings for contacts etc. Btw this WiFi code is understood by Google Mobile Vision Barcode API too without splitting up the string manually. – Siddharth Kamaria Oct 20 '20 at 12:03
  • @SiddharthKamaria the thing is is there any any site where I can see the format of these types of QR? for instance how to create a QR that goes directly to a contact form or save it, or to make a call, I saw plenty of types of QR on google, but I want to know the way to create them – StuartDTO Oct 20 '20 at 12:16
  • So, I don't need any API or external stuff to generate QR I can use the same library to generate and to read? If so, with that library I can I custom the color add images, etc? – StuartDTO Oct 22 '20 at 13:25
4

For vCard I can recommend this How to create vcf file using java?

ZXing the right way [2021 Update]

Dependency Gradle Scripts/build.gradle(Module:app)

implementation 'com.journeyapps:zxing-android-embedded:4.3.0'

Code

import android.util.Log;
import android.graphics.Bitmap;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.journeyapps.barcodescanner.BarcodeEncoder;

public static Bitmap generateQR(String content, int size) {
    Bitmap bitmap = null;
    try {
        BarcodeEncoder barcodeEncoder = new BarcodeEncoder();
        bitmap = barcodeEncoder.encodeBitmap(content,
            BarcodeFormat.QR_CODE, size, size);
    } catch (WriterException e) {
        Log.e("generateQR()", e.getMessage());
    }
    return bitmap;
}

More details here: dx.dragan.ba/qr-creation-android/

dixdragan
  • 71
  • 1
2

If you are using zxing-android-embedded

dependencies {
    implementation 'com.journeyapps:zxing-android-embedded:3.6.0'
}

You can make it shorter

val barcodeEncoder = BarcodeEncoder()
val bitmap = barcodeEncoder.encodeBitmap(content, BarcodeFormat.QR_CODE, 512, 512)

your_image_view.setImageBitmap(bitmap)
Skizo-ozᴉʞS ツ
  • 19,464
  • 18
  • 81
  • 148