1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settingslib.qrcode 18 19 import android.annotation.ColorInt 20 import android.graphics.Bitmap 21 import android.graphics.Color 22 import com.google.zxing.BarcodeFormat 23 import com.google.zxing.EncodeHintType 24 import com.google.zxing.MultiFormatWriter 25 import com.google.zxing.WriterException 26 import java.nio.charset.StandardCharsets 27 import java.util.EnumMap 28 29 object QrCodeGenerator { 30 /** 31 * Generates a barcode image with [contents]. 32 * 33 * @param contents The contents to encode in the barcode 34 * @param size The preferred image size in pixels 35 * @param invert Whether to invert the black/white pixels (e.g. for dark mode) 36 * @return Barcode bitmap 37 */ 38 @JvmStatic 39 @Throws(WriterException::class, java.lang.IllegalArgumentException::class) encodeQrCodenull40 fun encodeQrCode(contents: String, size: Int, invert: Boolean): Bitmap = 41 encodeQrCode(contents, size, DEFAULT_MARGIN, invert) 42 43 private const val DEFAULT_MARGIN = -1 44 45 /** 46 * Generates a barcode image with [contents]. 47 * 48 * @param contents The contents to encode in the barcode 49 * @param size The preferred image size in pixels 50 * @param margin The margin around the actual barcode 51 * @param invert Whether to invert the black/white pixels (e.g. for dark mode) 52 * @return Barcode bitmap 53 */ 54 @JvmOverloads 55 @JvmStatic 56 @Throws(WriterException::class, IllegalArgumentException::class) 57 fun encodeQrCode( 58 contents: String, 59 size: Int, 60 margin: Int = DEFAULT_MARGIN, 61 invert: Boolean = false, 62 ): Bitmap { 63 val hints = EnumMap<EncodeHintType, Any>(EncodeHintType::class.java) 64 if (!isIso88591(contents)) { 65 hints[EncodeHintType.CHARACTER_SET] = StandardCharsets.UTF_8.name() 66 } 67 if (margin != DEFAULT_MARGIN) { 68 hints[EncodeHintType.MARGIN] = margin 69 } 70 val qrBits = MultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE, size, size, hints) 71 @ColorInt val setColor = if (invert) Color.WHITE else Color.BLACK 72 @ColorInt val unsetColor = if (invert) Color.BLACK else Color.WHITE 73 @ColorInt val pixels = IntArray(size * size) 74 for (x in 0 until size) { 75 for (y in 0 until size) { 76 pixels[x * size + y] = if (qrBits[x, y]) setColor else unsetColor 77 } 78 } 79 return Bitmap.createBitmap(size, size, Bitmap.Config.RGB_565).apply { 80 setPixels(pixels, 0, size, 0, 0, size, size) 81 } 82 } 83 isIso88591null84 private fun isIso88591(contents: String): Boolean = 85 StandardCharsets.ISO_8859_1.newEncoder().canEncode(contents) 86 } 87