Finish v1.16.34(216)#2

This commit is contained in:
Giuseppe Scorrano 2021-05-17 17:35:10 +02:00
commit 653d4def4e
19 changed files with 2 additions and 974 deletions

View File

@ -1 +0,0 @@
/build

View File

@ -1,46 +0,0 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 30
defaultConfig {
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.1.1"
}
compileOptions {
kotlinOptions.freeCompilerArgs += ['-module-name', "barcode.kaiteki"]
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_1_8
}
// For Kotlin projects
kotlinOptions {
jvmTarget = "1.8"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'com.google.android.material:material:1.3.0-alpha03'
api 'androidx.camera:camera-core:1.0.0-alpha03'
api 'androidx.camera:camera-camera2:1.0.0-alpha03'
api 'com.google.zxing:core:3.4.0'
}

View File

@ -1,21 +0,0 @@
# 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

View File

@ -1,6 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.kroegerama.kaiteki.bcode">
<uses-permission android:name="android.permission.CAMERA" />
</manifest>

View File

@ -1,72 +0,0 @@
package com.kroegerama.kaiteki.bcode
import android.graphics.ImageFormat
import android.util.Log
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
import com.google.zxing.BinaryBitmap
import com.google.zxing.MultiFormatReader
import com.google.zxing.PlanarYUVLuminanceSource
import com.google.zxing.Result
import com.google.zxing.common.HybridBinarizer
internal interface ResultListener {
fun onResult(result: Result, imageWidth: Int, imageHeight: Int, imageRotation: Int)
fun onNoResult()
}
internal class BarcodeAnalyzer(
private val listener: ResultListener,
private val reader: MultiFormatReader
) : ImageAnalysis.Analyzer {
var enabled = true
var inverted = false
override fun analyze(image: ImageProxy, rotationDegrees: Int) {
if (!enabled) return
//YUV_420 is normally the input type here, but other YUV types are also supported in theory
if (ImageFormat.YUV_420_888 != image.format && ImageFormat.YUV_422_888 != image.format && ImageFormat.YUV_444_888 != image.format) {
Log.e(TAG, "Unexpected format: ${image.format}")
listener.onNoResult()
return
}
val byteBuffer = image.image?.planes?.firstOrNull()?.buffer
if (byteBuffer == null) {
listener.onNoResult()
return
}
var data = ByteArray(byteBuffer.remaining()).also { byteBuffer.get(it) }
var width = image.width
var height = image.height
// val rotatedData = ByteArray(data.size)
// for (y in 0 until height) {
// for (x in 0 until width) rotatedData[x * height + height - y - 1] = data[x + y * width]
// }
//
// data = rotatedData
// val tmp = width
// width = height
// height = tmp
val source = PlanarYUVLuminanceSource(data, width, height, 0, 0, width, height, false).let {
if (inverted) it.invert() else it
}
val bitmap = BinaryBitmap(HybridBinarizer(source))
try {
val result = reader.decodeWithState(bitmap)
listener.onResult(result, width, height, rotationDegrees)
} catch (e: Exception) {
listener.onNoResult()
}
}
companion object {
private const val TAG = "BarcodeAnalyzer"
}
}

View File

@ -1,16 +0,0 @@
package com.kroegerama.kaiteki.bcode
import com.google.zxing.Result
interface BarcodeResultListener {
/**
* @param result zxing result
*
* @return return true to dismiss the dialog/fragment
*/
fun onBarcodeResult(result: Result): Boolean
fun onBarcodeScanCancelled()
}

View File

@ -1,32 +0,0 @@
package com.kroegerama.kaiteki.bcode
import android.graphics.*
import android.media.Image
import com.google.zxing.PlanarYUVLuminanceSource
import java.io.ByteArrayOutputStream
internal fun Image.toBitmap(): Bitmap {
val yBuffer = planes[0].buffer // Y
val uBuffer = planes[1].buffer // U
val vBuffer = planes[2].buffer // V
val ySize = yBuffer.remaining()
val uSize = uBuffer.remaining()
val vSize = vBuffer.remaining()
val nv21 = ByteArray(ySize + uSize + vSize)
//U and V are swapped
yBuffer.get(nv21, 0, ySize)
vBuffer.get(nv21, ySize, vSize)
uBuffer.get(nv21, ySize + vSize, uSize)
val yuvImage = YuvImage(nv21, ImageFormat.NV21, this.width, this.height, null)
val out = ByteArrayOutputStream()
yuvImage.compressToJpeg(Rect(0, 0, yuvImage.width, yuvImage.height), 50, out)
val imageBytes = out.toByteArray()
return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
}
internal fun PlanarYUVLuminanceSource.toBitmap() =
Bitmap.createBitmap(renderThumbnail(), thumbnailWidth, thumbnailHeight, Bitmap.Config.ARGB_8888)

View File

@ -1,53 +0,0 @@
package com.kroegerama.kaiteki.bcode
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.content.res.TypedArray
import android.os.Build
import android.util.AttributeSet
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
internal fun AttributeSet?.handleArguments(
context: Context, attrs: IntArray, defStyleAttr: Int, defStyleRes: Int,
block: TypedArray.() -> Unit
) = this?.let {
val arr = context.obtainStyledAttributes(it, attrs, defStyleAttr, defStyleRes)
block(arr)
arr.recycle()
}
internal typealias Style = R.styleable
internal val Context.hasCameraPermission
get() = isPermissionGranted(Manifest.permission.CAMERA)
internal fun Fragment.requestCameraPermission(requestCode: Int) =
requestPermission(Manifest.permission.CAMERA, requestCode)
internal val IntArray.isPermissionGranted
get() = size > 0 && get(0) == PackageManager.PERMISSION_GRANTED
private fun Context.isPermissionGranted(permission: String) =
ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
private fun Fragment.requestPermission(permission: String, requestCode: Int) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return
if (context?.isPermissionGranted(permission) == true) return
requestPermissions(arrayOf(permission), requestCode)
}
internal class Debouncer(
val debounceTime: Int
) {
private var lastShot = 0L
operator fun <T> invoke(block: () -> T) = if (System.currentTimeMillis() - lastShot > debounceTime) {
block.invoke().also {
lastShot = System.currentTimeMillis()
}
} else {
null
}
}

View File

@ -1,65 +0,0 @@
package com.kroegerama.kaiteki.bcode.ui
import android.content.Context
import android.os.Handler
import android.util.Log
import android.view.LayoutInflater
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.os.postDelayed
import androidx.lifecycle.LifecycleOwner
import com.google.zxing.BarcodeFormat
import com.google.zxing.Result
import com.kroegerama.kaiteki.bcode.BarcodeResultListener
import com.kroegerama.kaiteki.bcode.R
import com.kroegerama.kaiteki.bcode.hasCameraPermission
import com.kroegerama.kaiteki.bcode.views.BarcodeView
fun Context.showBarcodeAlertDialog(
owner: LifecycleOwner,
listener: BarcodeResultListener,
formats: List<BarcodeFormat> = listOf(BarcodeFormat.QR_CODE),
barcodeInverted: Boolean = false
) {
if (!hasCameraPermission) {
Log.w("BarcodeAlertDialog", "Camera permission required")
Toast.makeText(this, "Camera permission required", Toast.LENGTH_LONG).show()
return
}
val view = LayoutInflater.from(this).inflate(R.layout.dlg_barcode, null, false)
val bcode = view.findViewById<BarcodeView>(R.id.bcode)
val handler = Handler()
val dlg = AlertDialog.Builder(this)
.setOnDismissListener { bcode.unbind() }
.setView(view)
.setOnCancelListener {
listener.onBarcodeScanCancelled()
}
.setNegativeButton(android.R.string.cancel) { _, _ ->
listener.onBarcodeScanCancelled()
}
.show()
bcode.setFormats(formats)
bcode.setBarcodeInverted(barcodeInverted)
bcode.setBarcodeResultListener(object : BarcodeResultListener {
override fun onBarcodeResult(result: Result): Boolean {
val doDismiss = listener.onBarcodeResult(result)
if (doDismiss) {
handler.postDelayed(500) {
dlg.dismiss()
}
}
return doDismiss
}
override fun onBarcodeScanCancelled() {
//Ignore: BarcodeView will never emit this event
}
})
bcode.bindToLifecycle(owner)
}

View File

@ -1,109 +0,0 @@
package com.kroegerama.kaiteki.bcode.ui
import android.content.DialogInterface
import android.os.Bundle
import android.os.Handler
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.os.bundleOf
import androidx.core.os.postDelayed
import androidx.fragment.app.FragmentManager
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.zxing.BarcodeFormat
import com.google.zxing.Result
import com.kroegerama.kaiteki.bcode.*
import kotlinx.android.synthetic.main.dlg_barcode.*
class BarcodeBottomSheet : BottomSheetDialogFragment(), BarcodeResultListener {
private val formats: List<BarcodeFormat>? by lazy {
arguments?.getSerializable(KEY_FORMATS) as List<BarcodeFormat>
}
private val barcodeInverted by lazy {
arguments?.getBoolean(KEY_INVERTED, false) ?: false
}
private val handler = Handler()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
inflater.inflate(R.layout.dlg_barcode, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
formats?.let(bcode::setFormats)
bcode.setBarcodeInverted(barcodeInverted)
bcode.setBarcodeResultListener(this)
if (requireContext().hasCameraPermission) {
bcode.bindToLifecycle(this)
} else {
requestCameraPermission(REQUEST_CAMERA)
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
when (requestCode) {
REQUEST_CAMERA ->
if (grantResults.isPermissionGranted)
bcode.bindToLifecycle(this)
else
dismissAllowingStateLoss()
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
override fun onStop() {
super.onStop()
bcode.unbind()
}
override fun onBarcodeResult(result: Result): Boolean {
if ((parentFragment as? BarcodeResultListener)?.onBarcodeResult(result) == true) {
handler.postDelayed(500) {
dismiss()
}
return true
} else if ((activity as? BarcodeResultListener)?.onBarcodeResult(result) == true) {
handler.postDelayed(500) {
dismiss()
}
return true
}
return false
}
override fun onBarcodeScanCancelled() {
//Ignore: BarcodeView will never emit this event
}
override fun onCancel(dialog: DialogInterface) {
(parentFragment as? BarcodeResultListener)?.onBarcodeScanCancelled()
(activity as? BarcodeResultListener)?.onBarcodeScanCancelled()
super.onCancel(dialog)
}
companion object {
private const val KEY_FORMATS = "formats"
private const val KEY_INVERTED = "inverted"
private const val REQUEST_CAMERA = 0xbb_ca
fun show(
fm: FragmentManager,
formats: List<BarcodeFormat> = listOf(BarcodeFormat.QR_CODE),
barcodeInverted: Boolean = false,
tag: String? = null
) = BarcodeBottomSheet().apply {
arguments = bundleOf(
KEY_FORMATS to formats,
KEY_INVERTED to barcodeInverted
)
show(fm, tag)
}
}
}

View File

@ -1,120 +0,0 @@
package com.kroegerama.kaiteki.bcode.ui
import android.content.DialogInterface
import android.os.Bundle
import android.os.Handler
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.Window
import androidx.core.os.bundleOf
import androidx.core.os.postDelayed
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import com.google.zxing.BarcodeFormat
import com.google.zxing.Result
import com.kroegerama.kaiteki.bcode.*
import kotlinx.android.synthetic.main.dlg_barcode.*
open class BarcodeDialog : DialogFragment(), BarcodeResultListener {
private val formats: List<BarcodeFormat>? by lazy {
arguments?.getSerializable(KEY_FORMATS) as List<BarcodeFormat>
}
private val barcodeInverted by lazy {
arguments?.getBoolean(KEY_INVERTED, false) ?: false
}
private val handler = Handler()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
inflater.inflate(R.layout.dlg_barcode, container, false).also {
dialog?.window?.run {
requestFeature(Window.FEATURE_NO_TITLE)
requestFeature(Window.FEATURE_SWIPE_TO_DISMISS)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
formats?.let(bcode::setFormats)
bcode.setBarcodeInverted(barcodeInverted)
bcode.setBarcodeResultListener(this)
if (requireContext().hasCameraPermission) {
bcode.bindToLifecycle(this)
} else {
requestCameraPermission(REQUEST_CAMERA)
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
when (requestCode) {
REQUEST_CAMERA ->
if (grantResults.isPermissionGranted)
bcode.bindToLifecycle(this)
else
dismissAllowingStateLoss()
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
override fun onBarcodeScanCancelled() {
//Ignore: BarcodeView will never emit this event
}
override fun onStop() {
super.onStop()
bcode.unbind()
}
override fun onStart() {
super.onStart()
dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
}
override fun onBarcodeResult(result: Result): Boolean {
if ((parentFragment as? BarcodeResultListener)?.onBarcodeResult(result) == true) {
handler.postDelayed(500) {
dismiss()
}
return true
} else if ((activity as? BarcodeResultListener)?.onBarcodeResult(result) == true) {
handler.postDelayed(500) {
dismiss()
}
return true
}
return false
}
override fun onCancel(dialog: DialogInterface) {
(parentFragment as? BarcodeResultListener)?.onBarcodeScanCancelled()
(activity as? BarcodeResultListener)?.onBarcodeScanCancelled()
super.onCancel(dialog)
}
companion object {
private const val KEY_FORMATS = "formats"
private const val KEY_INVERTED = "inverted"
private const val REQUEST_CAMERA = 0xbd_ca
fun show(
fm: FragmentManager,
formats: List<BarcodeFormat> = listOf(BarcodeFormat.QR_CODE),
barcodeInverted: Boolean = false,
tag: String? = null
) = BarcodeDialog().apply {
arguments = bundleOf(
KEY_FORMATS to formats,
KEY_INVERTED to barcodeInverted
)
show(fm, tag)
}
}
}

View File

@ -1,84 +0,0 @@
package com.kroegerama.kaiteki.bcode.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import com.google.zxing.BarcodeFormat
import com.google.zxing.Result
import com.kroegerama.kaiteki.bcode.*
import kotlinx.android.synthetic.main.dlg_barcode.*
class BarcodeFragment : Fragment(), BarcodeResultListener {
private val formats: List<BarcodeFormat>? by lazy {
arguments?.getSerializable(KEY_FORMATS) as List<BarcodeFormat>
}
private val barcodeInverted by lazy {
arguments?.getBoolean(KEY_INVERTED, false) ?: false
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
inflater.inflate(R.layout.dlg_barcode, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
formats?.let(bcode::setFormats)
bcode.setBarcodeInverted(barcodeInverted)
bcode.setBarcodeResultListener(this)
if (requireContext().hasCameraPermission) {
bcode.bindToLifecycle(this)
} else {
requestCameraPermission(REQUEST_CAMERA)
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
when (requestCode) {
REQUEST_CAMERA ->
if (grantResults.isPermissionGranted)
bcode.bindToLifecycle(this)
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
override fun onStop() {
super.onStop()
bcode.unbind()
}
override fun onBarcodeResult(result: Result): Boolean {
if ((parentFragment as? BarcodeResultListener)?.onBarcodeResult(result) == true) {
return true
} else if ((activity as? BarcodeResultListener)?.onBarcodeResult(result) == true) {
return true
}
return false
}
override fun onBarcodeScanCancelled() {
//Ignore: BarcodeView will never emit this event
}
companion object {
private const val KEY_FORMATS = "formats"
private const val KEY_INVERTED = "inverted"
private const val REQUEST_CAMERA = 0xbf_ca
fun makeInstance(
formats: List<BarcodeFormat> = listOf(BarcodeFormat.QR_CODE),
barcodeInverted: Boolean = false
) = BarcodeFragment().apply {
arguments = bundleOf(
KEY_FORMATS to formats,
KEY_INVERTED to barcodeInverted
)
}
}
}

View File

@ -1,197 +0,0 @@
package com.kroegerama.kaiteki.bcode.views
import android.content.Context
import android.content.res.Resources
import android.graphics.Color
import android.graphics.Matrix
import android.os.Handler
import android.os.HandlerThread
import android.util.*
import android.view.LayoutInflater
import android.view.Surface
import android.view.TextureView
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.camera.core.*
import androidx.lifecycle.LifecycleOwner
import com.google.zxing.BarcodeFormat
import com.google.zxing.DecodeHintType
import com.google.zxing.MultiFormatReader
import com.google.zxing.Result
import com.kroegerama.kaiteki.bcode.*
import com.kroegerama.kaiteki.bcode.R
import kotlin.math.max
class BarcodeView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr), ResultListener {
private val textureView: TextureView
private val resultView: ResultPointView
private var bufferSize = SizeF(0f, 0f)
private var listener: BarcodeResultListener? = null
private val barcodeReader by lazy { MultiFormatReader() }
private val analyzer by lazy { BarcodeAnalyzer(this, barcodeReader) }
private val resultDebouncer = Debouncer(500)
init {
LayoutInflater.from(context).inflate(R.layout.barcode_view, this)
keepScreenOn = true
textureView = findViewById(R.id.textureView)
resultView = findViewById(R.id.resultView)
attrs.handleArguments(context, Style.BarcodeView, defStyleAttr, 0) {
resultView.showResultPoints = getBoolean(Style.BarcodeView_showResultPoints, true)
resultView.setResultPointColor(getColor(Style.BarcodeView_resultPointColor, Color.GREEN))
val defaultSize =
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f, Resources.getSystem().displayMetrics)
resultView.setPointSize(getDimension(Style.BarcodeView_resultPointSize, defaultSize))
analyzer.inverted = getBoolean(Style.BarcodeView_barcodeInverted, false)
}
textureView.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
updateTransform()
}
}
override fun onResult(result: Result, imageWidth: Int, imageHeight: Int, imageRotation: Int) {
resultView.setResult(result, imageWidth, imageHeight, imageRotation)
val d = resultDebouncer {
listener?.onBarcodeResult(result)
}
if (d == true) {
// dialog/fragment will be dismissed -> do not send any more events
listener = null
}
}
override fun onNoResult() {
resultView.clear()
}
fun setBarcodeResultListener(listener: BarcodeResultListener) {
this.listener = listener
}
/**
* enable scanning of inverted barcodes (e.g. white QR Code on black background)
*/
fun setBarcodeInverted(inverted: Boolean) {
analyzer.inverted = inverted
}
fun bindToLifecycle(owner: LifecycleOwner) {
textureView.post { startPreview(owner) }
}
fun unbind() {
resultView.clear()
listener = null
CameraX.unbindAll()
}
fun setFormats(formats: List<BarcodeFormat>) = barcodeReader.setHints(
mapOf(
DecodeHintType.POSSIBLE_FORMATS to formats
)
)
private fun startPreview(owner: LifecycleOwner) {
val metrics = DisplayMetrics().also { textureView.display.getRealMetrics(it) }
val screenSize = Size(metrics.widthPixels, metrics.heightPixels)
val screenAspectRatio = Rational(metrics.widthPixels, metrics.heightPixels)
val screenRotation = textureView.display.rotation
val previewConfig = PreviewConfig.Builder().apply {
setLensFacing(CameraX.LensFacing.BACK)
setTargetResolution(screenSize / 2)
setTargetAspectRatio(screenAspectRatio)
setTargetRotation(screenRotation)
}.build()
val preview = Preview(previewConfig).apply {
setOnPreviewOutputUpdateListener(::previewOutputUpdated)
}
val analysisConfig = ImageAnalysisConfig.Builder().apply {
setLensFacing(CameraX.LensFacing.BACK)
setTargetResolution(screenSize / 2)
setTargetAspectRatio(screenAspectRatio)
setTargetRotation(textureView.display.rotation)
setImageReaderMode(ImageAnalysis.ImageReaderMode.ACQUIRE_LATEST_IMAGE)
val analyzerThread = HandlerThread("BarcodeAnalyzer").apply { start() }
setCallbackHandler(Handler(analyzerThread.looper))
}.build()
val analysis = ImageAnalysis(analysisConfig).apply { analyzer = this@BarcodeView.analyzer }
CameraX.bindToLifecycle(owner, preview, analysis)
}
private fun previewOutputUpdated(output: Preview.PreviewOutput) {
// https://github.com/android/camera/blob/848cf1e2c8404599050d79086dee1d0c8951b66e/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/utils/AutoFitPreviewBuilder.kt#L100
(textureView.parent as? ViewGroup)?.apply {
val idx = indexOfChild(textureView)
removeView(textureView)
addView(textureView, idx)
textureView.surfaceTexture = output.surfaceTexture
}
bufferSize = SizeF(output.textureSize.height.toFloat(), output.textureSize.width.toFloat())
updateTransform()
}
private fun updateTransform() {
val viewFinderWidth = textureView.width.toFloat()
val viewFinderHeight = textureView.height.toFloat()
val viewFinderRotation = when (textureView.display.rotation) {
Surface.ROTATION_0 -> 0
Surface.ROTATION_90 -> 90
Surface.ROTATION_180 -> 180
Surface.ROTATION_270 -> 270
else -> return
}
val matrix = Matrix()
val centerX = viewFinderWidth / 2f
val centerY = viewFinderHeight / 2f
matrix.postRotate(-viewFinderRotation.toFloat(), centerX, centerY)
val bufferRatio = bufferSize.width / bufferSize.height
val viewRatio = viewFinderWidth / viewFinderHeight
if (bufferRatio > viewRatio) {
val factor = bufferRatio / viewRatio
matrix.preScale(factor, 1f, centerX, centerY)
} else {
val factor = viewRatio / bufferRatio
matrix.preScale(1f, factor, centerX, centerY)
}
if (viewFinderRotation % 180 != 0) {
if (bufferRatio > viewRatio) {
val s = 1f / bufferRatio
matrix.preScale(s, s, centerX, centerY)
} else {
val s = max(bufferRatio, 1f / viewRatio)
matrix.preScale(s, s, centerX, centerY)
}
}
// val dbgScale = .95f
// matrix.preScale(dbgScale, dbgScale, centerX, centerY)
textureView.setTransform(matrix)
}
}
private operator fun Size.div(other: Int): Size = Size(width / other, height / other)

View File

@ -1,102 +0,0 @@
package com.kroegerama.kaiteki.bcode.views
import android.content.Context
import android.content.res.Resources
import android.graphics.*
import android.util.AttributeSet
import android.util.TypedValue
import android.view.View
import androidx.annotation.ColorInt
import com.google.zxing.Result
import com.kroegerama.kaiteki.bcode.BuildConfig
import com.kroegerama.kaiteki.bcode.Style
import com.kroegerama.kaiteki.bcode.handleArguments
import kotlin.math.max
class ResultPointView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private val pPoints = Paint().apply {
style = Paint.Style.STROKE
color = Color.GREEN
strokeWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f, Resources.getSystem().displayMetrics)
strokeCap = Paint.Cap.ROUND
}
private var resultPoints = floatArrayOf()
private var rect = RectF()
var showResultPoints = true
set(value) {
field = value
invalidate()
}
init {
attrs.handleArguments(context, Style.ResultPointView, defStyleAttr, 0) {
showResultPoints = getBoolean(Style.ResultPointView_showResultPoints, showResultPoints)
pPoints.color = getColor(Style.ResultPointView_resultPointColor, pPoints.color)
pPoints.strokeWidth =
getDimension(Style.ResultPointView_resultPointSize, pPoints.strokeWidth)
}
}
fun setResultPointColor(@ColorInt color: Int) {
pPoints.color = color
invalidate()
}
fun setPointSize(size: Float) {
pPoints.strokeWidth = size
}
fun clear() {
resultPoints = floatArrayOf()
postInvalidate()
}
fun setResult(result: Result, imageWidth: Int, imageHeight: Int, imageRotation: Int) {
if (!showResultPoints) return
val localMatrix = createMatrix(imageWidth.toFloat(), imageHeight.toFloat(), imageRotation)
resultPoints = result.resultPoints.flatMap { listOf(it.x, it.y) }.toFloatArray()
localMatrix.mapPoints(resultPoints)
if (BuildConfig.DEBUG) {
rect = RectF(0f, 0f, imageWidth.toFloat(), imageHeight.toFloat())
localMatrix.mapRect(rect)
}
postInvalidate()
}
private fun createMatrix(imageWidth: Float, imageHeight: Float, imageRotation: Int) = Matrix().apply {
preTranslate((width - imageWidth) / 2f, (height - imageHeight) / 2f)
preRotate(imageRotation.toFloat(), imageWidth / 2f, imageHeight / 2f)
val wScale: Float
val hScale: Float
if (imageRotation % 180 == 0) {
wScale = width.toFloat() / imageWidth
hScale = height.toFloat() / imageHeight
} else {
wScale = height.toFloat() / imageWidth
hScale = width.toFloat() / imageHeight
}
val scale = max(wScale, hScale)
preScale(scale, scale, imageWidth / 2f, imageHeight / 2f)
}
override fun onDraw(canvas: Canvas) {
if (showResultPoints) canvas.drawPoints(resultPoints, pPoints)
if (BuildConfig.DEBUG) canvas.drawRect(rect, pPoints)
}
}

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:parentTag="android.widget.FrameLayout">
<TextureView
android:id="@+id/textureView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.kroegerama.kaiteki.bcode.views.ResultPointView
android:id="@+id/resultView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</merge>

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<com.kroegerama.kaiteki.bcode.views.BarcodeView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/bcode"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:resultPointColor="#09E85E"
app:resultPointSize="8dp"
app:showResultPoints="true" />

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="showResultPoints" format="boolean" />
<attr name="resultPointColor" format="color" />
<attr name="resultPointSize" format="dimension" />
<declare-styleable name="ResultPointView">
<attr name="showResultPoints" />
<attr name="resultPointColor" />
<attr name="resultPointSize" />
</declare-styleable>
<declare-styleable name="BarcodeView">
<attr name="showResultPoints" />
<attr name="resultPointColor" />
<attr name="resultPointSize" />
<attr name="barcodeInverted" format="boolean" />
</declare-styleable>
</resources>

View File

@ -10,7 +10,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.0'
classpath 'com.android.tools.build:gradle:4.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:4.3.5'
classpath 'com.google.firebase:perf-plugin:1.3.5'

View File

@ -1,2 +1,2 @@
include ':dynamic_gramm'
include ':app', ':pointmobilescannerlibrary', ':dynamic_vgalimenti', ':dynamic__base', ':zebrascannerlibrary', ':honeywellscannerlibrary', ':dynamic_ime', ':dynamic_frudis', ':dynamic_saporiveri_pv', ':keyobardemulatorscannerlibrary', ':barcode_base_android_library', ':dynamic_saporiveri', ':barcode_kaiteki'
include ':app', ':pointmobilescannerlibrary', ':dynamic_vgalimenti', ':dynamic__base', ':zebrascannerlibrary', ':honeywellscannerlibrary', ':dynamic_ime', ':dynamic_frudis', ':dynamic_saporiveri_pv', ':keyobardemulatorscannerlibrary', ':barcode_base_android_library', ':dynamic_saporiveri'