|
|
|
// Copyright 2019 The Hush developers
|
|
|
|
package org.myhush.silentdragon
|
|
|
|
|
|
|
|
import android.app.Activity
|
|
|
|
import android.content.Intent
|
|
|
|
import android.content.pm.PackageManager
|
|
|
|
import android.net.Uri
|
|
|
|
import android.os.Bundle
|
|
|
|
import android.support.v4.app.ActivityCompat
|
|
|
|
import android.support.v4.content.ContextCompat
|
|
|
|
import android.support.v7.app.AppCompatActivity
|
|
|
|
import android.util.Log
|
|
|
|
import android.view.Menu
|
|
|
|
import android.view.MenuItem
|
|
|
|
import android.view.SurfaceHolder
|
|
|
|
import android.view.SurfaceView
|
|
|
|
import com.google.android.gms.vision.CameraSource
|
|
|
|
import com.google.android.gms.vision.Detector
|
|
|
|
import com.google.android.gms.vision.barcode.Barcode
|
|
|
|
import com.google.android.gms.vision.barcode.BarcodeDetector
|
|
|
|
import kotlinx.android.synthetic.main.activity_qr_reader.*
|
|
|
|
import java.io.IOException
|
|
|
|
import android.app.AlertDialog
|
|
|
|
import android.text.InputType
|
|
|
|
import android.widget.EditText
|
|
|
|
|
|
|
|
class QrReaderActivity : AppCompatActivity() {
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
const val REQUEST_ADDRESS = 1
|
|
|
|
const val REQUEST_CONNDATA = 2
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
|
super.onCreate(savedInstanceState)
|
|
|
|
setContentView(R.layout.activity_qr_reader)
|
|
|
|
|
|
|
|
title = "Scan QR Code"
|
|
|
|
|
|
|
|
val code = intent.getIntExtra("REQUEST_CODE", 0)
|
|
|
|
if (code == REQUEST_ADDRESS)
|
|
|
|
txtQrCodeHelp.text = ""
|
|
|
|
|
|
|
|
lblErrorMsg.text = ""
|
|
|
|
|
|
|
|
setupCamera()
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
|
|
|
when (requestCode) {
|
|
|
|
50 -> {
|
|
|
|
if (grantResults.isEmpty() || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
|
|
|
|
// Do what if user refuses permission? Go back?
|
|
|
|
} else {
|
|
|
|
recreate()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
|
|
|
// Inflate the menu; this adds items to the action bar if it is present.
|
|
|
|
menuInflater.inflate(R.menu.menu_qrcodereader, menu)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
|
|
return when (item.itemId) {
|
|
|
|
R.id.action_manual_input -> {
|
|
|
|
val builder = AlertDialog.Builder(this)
|
|
|
|
builder.setTitle("Paste the code here manually")
|
|
|
|
|
|
|
|
// Set up the input
|
|
|
|
val input = EditText(this)
|
|
|
|
// Specify the type of input expected; this, for example, sets the input as a password, and will mask the text
|
|
|
|
input.inputType = InputType.TYPE_CLASS_TEXT
|
|
|
|
builder.setView(input)
|
|
|
|
|
|
|
|
// Set up the buttons
|
|
|
|
builder.setPositiveButton("OK") { dialog, which ->
|
|
|
|
run {
|
|
|
|
val txt = input.text.toString()
|
|
|
|
processText(txt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
builder.setNegativeButton("Cancel") { dialog, which -> dialog.cancel() }
|
|
|
|
|
|
|
|
builder.create().show()
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
else -> super.onOptionsItemSelected(item)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun setupCamera() {
|
|
|
|
val cameraView = findViewById<SurfaceView>(R.id.camera_view)
|
|
|
|
|
|
|
|
val barcodeDetector = BarcodeDetector.Builder(this).setBarcodeFormats(Barcode.QR_CODE).build()
|
|
|
|
val cameraSource = CameraSource.Builder(this, barcodeDetector)
|
|
|
|
.setAutoFocusEnabled(true)
|
|
|
|
.setRequestedPreviewSize(640, 480)
|
|
|
|
.build()
|
|
|
|
|
|
|
|
|
|
|
|
cameraView.holder.addCallback(object : SurfaceHolder.Callback {
|
|
|
|
override fun surfaceCreated(holder: SurfaceHolder) {
|
|
|
|
try {
|
|
|
|
if (ContextCompat.checkSelfPermission(applicationContext, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
|
|
|
|
ActivityCompat.requestPermissions(this@QrReaderActivity, arrayOf(android.Manifest.permission.CAMERA), 50)
|
|
|
|
} else {
|
|
|
|
cameraSource.start(cameraView.holder)
|
|
|
|
|
|
|
|
val w = cameraView.width
|
|
|
|
val h = cameraView.height
|
|
|
|
val scale = cameraSource.previewSize.width.toDouble() / cameraSource.previewSize.height.toDouble()
|
|
|
|
|
|
|
|
val scaleWidth = (h.toDouble() / scale).toInt()
|
|
|
|
|
|
|
|
cameraView.layout((w - scaleWidth)/2, 0, scaleWidth , h)
|
|
|
|
println("Preview size: ${cameraSource.previewSize}")
|
|
|
|
}
|
|
|
|
} catch (ie: IOException) {
|
|
|
|
Log.e("CAMERA SOURCE", ie.message)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}
|
|
|
|
|
|
|
|
override fun surfaceDestroyed(holder: SurfaceHolder) {
|
|
|
|
cameraSource.stop()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
btnQrCodeCancel.setOnClickListener {
|
|
|
|
setResult(Activity.RESULT_CANCELED)
|
|
|
|
finish()
|
|
|
|
}
|
|
|
|
|
|
|
|
barcodeDetector.setProcessor(object : Detector.Processor<Barcode> {
|
|
|
|
override fun release() {}
|
|
|
|
|
|
|
|
override fun receiveDetections(detections: Detector.Detections<Barcode>) {
|
|
|
|
val barcodes = detections.detectedItems
|
|
|
|
if (barcodes.size() != 0) {
|
|
|
|
runOnUiThread {
|
|
|
|
val barcodeInfo = barcodes.valueAt(0).displayValue
|
|
|
|
processText(barcodeInfo)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun processText(barcodeInfo: String) {
|
|
|
|
val code = intent.getIntExtra("REQUEST_CODE", 0)
|
|
|
|
|
|
|
|
// See if this the data is of the right format
|
|
|
|
if (code == REQUEST_CONNDATA && !barcodeInfo.startsWith("ws")) {
|
|
|
|
Log.i(TAG, "Not a connection")
|
|
|
|
var err = barcodeInfo
|
|
|
|
if (err.length > 48) {
|
|
|
|
err = err.substring(0, 22) + "...." + err.substring(err.length - 22, err.length)
|
|
|
|
}
|
|
|
|
lblErrorMsg.text = "\"$err\" is not a valid connection string!"
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if (code == REQUEST_ADDRESS &&
|
|
|
|
!DataModel.isValidAddress(StringBuilder(barcodeInfo).toString()) &&
|
|
|
|
!barcodeInfo.startsWith("hush:")) {
|
|
|
|
Log.i(TAG, "Not an address")
|
|
|
|
var err = barcodeInfo
|
|
|
|
if (err.length > 48) {
|
|
|
|
err = err.substring(0, 22) + "...." + err.substring(err.length - 22, err.length)
|
|
|
|
}
|
|
|
|
lblErrorMsg.text = "\"$err\" is not a valid HUSH address!"
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// The data seems valid, so return it.
|
|
|
|
val data = Intent()
|
|
|
|
|
|
|
|
// Payment URIs are often formatted as "hush:<addr>", but this casuses parsing problems.
|
|
|
|
// So change it to hush://<addr>, so that it parses properly
|
|
|
|
if (barcodeInfo.startsWith("hush:") && !barcodeInfo.startsWith("hush://")) {
|
|
|
|
data.data = Uri.parse(barcodeInfo.replaceFirst("hush:", "hush://"))
|
|
|
|
} else {
|
|
|
|
data.data = Uri.parse(barcodeInfo)
|
|
|
|
}
|
|
|
|
|
|
|
|
setResult(Activity.RESULT_OK, data)
|
|
|
|
finish()
|
|
|
|
}
|
|
|
|
|
|
|
|
private val TAG = "QrReader"
|
|
|
|
}
|