Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
565643f19e | ||
|
|
4c92045563 | ||
|
|
7116aa7278 | ||
|
|
860e867ad4 | ||
|
|
3b509cd09c | ||
|
|
51ec228fd8 | ||
|
|
9d1dec146d | ||
|
|
7bd9fe36fe | ||
|
|
85dba0604c | ||
|
|
56c70f5974 | ||
|
|
098b7740a0 | ||
|
|
7d92481fa6 | ||
|
|
2e285a5b1d | ||
|
|
ad25c4681f | ||
|
|
c00c87ceca | ||
|
|
e001a92870 | ||
|
|
7a32b00e1f | ||
|
|
50cedf782d | ||
|
|
fb0d22198f | ||
|
|
af528df448 | ||
|
|
a9b5771d57 | ||
|
|
a161e8fc0f | ||
|
|
ec17d07695 | ||
|
|
be2fc2f559 | ||
|
|
de3aa752ff | ||
|
|
411cbb243f | ||
|
|
f76c1521fe | ||
|
|
9b9224b100 |
@@ -1,5 +1,18 @@
|
||||
# Change Log
|
||||
|
||||
## 1.32.0
|
||||
* Add bulk mode
|
||||
* Remember region of interest permanently
|
||||
* Export generated barcode as SVG
|
||||
* Run detection on images in background
|
||||
* Remove a scan from result view
|
||||
* Update Indonesian translation
|
||||
|
||||
## 1.31.0
|
||||
* Stop detection while region of interest is modified
|
||||
* Fix resetting region of interest
|
||||
* Add Polish translation
|
||||
|
||||
## 1.30.1
|
||||
* Improve usability of cropping limiter
|
||||
* Draw round corners around region of interest
|
||||
|
||||
@@ -9,8 +9,8 @@ android {
|
||||
minSdkVersion 9
|
||||
targetSdkVersion sdk_version
|
||||
|
||||
versionCode 67
|
||||
versionName '1.30.1'
|
||||
versionCode 69
|
||||
versionName '1.32.0'
|
||||
|
||||
// it's recommended to set this value to the lowest API level
|
||||
// able to provide all the functionality
|
||||
|
||||
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 47 KiB |
@@ -1,14 +1,14 @@
|
||||
package de.markusfisch.android.binaryeye
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.support.test.InstrumentationRegistry
|
||||
import android.support.test.runner.AndroidJUnit4
|
||||
|
||||
import de.markusfisch.android.binaryeye.rs.Preprocessor
|
||||
import de.markusfisch.android.binaryeye.zxing.Zxing
|
||||
|
||||
import org.junit.Assert.fail;
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import java.util.regex.Pattern
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@@ -19,22 +19,27 @@ class PreprocessorTest {
|
||||
val assets = InstrumentationRegistry.getInstrumentation()
|
||||
.context.assets
|
||||
val pattern = Pattern.compile(
|
||||
"[0-9]+-([0-9]+)x([0-9]+)-([0-9]+)deg.yuv"
|
||||
"[0-9]+-([0-9]+)deg.jpg"
|
||||
)
|
||||
val files = assets.list("yuv") ?: return
|
||||
for (file in files) {
|
||||
val m = pattern.matcher(file)
|
||||
if (!m.find() || m.groupCount() < 3) {
|
||||
continue
|
||||
val samples = assets.list("samples")
|
||||
checkNotNull(samples) { "no samples found" }
|
||||
for (sample in samples) {
|
||||
val m = pattern.matcher(sample)
|
||||
if (!m.find() || m.groupCount() < 1) {
|
||||
fail("invalid sample: $sample")
|
||||
}
|
||||
val frameWidth = m.group(1) ?: return
|
||||
val frameHeight = m.group(2) ?: return
|
||||
val frameOrientation = m.group(3) ?: return
|
||||
val frameData = assets.open("yuv/$file").readBytes()
|
||||
val bitmap = BitmapFactory.decodeStream(
|
||||
assets.open("samples/$sample")
|
||||
)
|
||||
val frameData = getNV21(bitmap)
|
||||
val frameWidth = bitmap.width
|
||||
val frameHeight = bitmap.height
|
||||
val frameOrientation = m.group(1) ?: return
|
||||
val preprocessor = Preprocessor(
|
||||
InstrumentationRegistry.getTargetContext(),
|
||||
frameWidth.toInt(),
|
||||
frameHeight.toInt()
|
||||
frameWidth,
|
||||
frameHeight,
|
||||
null
|
||||
)
|
||||
val outWidth: Int
|
||||
val outHeight: Int
|
||||
@@ -51,7 +56,48 @@ class PreprocessorTest {
|
||||
val result = zxing.decode(frameData, outWidth, outHeight)
|
||||
preprocessor.destroy()
|
||||
|
||||
checkNotNull(result) { "no barcode found in $file" }
|
||||
checkNotNull(result) { "no barcode found in $sample" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getNV21(bitmap: Bitmap): ByteArray {
|
||||
val width = bitmap.width
|
||||
val height = bitmap.height
|
||||
val argb = IntArray(width * height)
|
||||
bitmap.getPixels(argb, 0, width, 0, 0, width, height)
|
||||
return encodeYUV420SP(argb, width, height)
|
||||
}
|
||||
|
||||
private fun encodeYUV420SP(argb: IntArray, width: Int, height: Int): ByteArray {
|
||||
val yuv = ByteArray(ceilIfUneven(width) * ceilIfUneven(height) * 3 / 2)
|
||||
var yIndex = 0
|
||||
var uvIndex = width * height
|
||||
var r: Int
|
||||
var g: Int
|
||||
var b: Int
|
||||
var Y: Int
|
||||
var u: Int
|
||||
var v: Int
|
||||
var index = 0
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) {
|
||||
val pixel = argb[index]
|
||||
r = pixel and 0xff0000 shr 16
|
||||
g = pixel and 0xff00 shr 8
|
||||
b = pixel and 0xff
|
||||
Y = (66 * r + 129 * g + 25 * b + 128 shr 8) + 16
|
||||
u = (-38 * r - 74 * g + 112 * b + 128 shr 8) + 128
|
||||
v = (112 * r - 94 * g - 18 * b + 128 shr 8) + 128
|
||||
yuv[yIndex++] = (if (Y < 0) 0 else if (Y > 255) 255 else Y).toByte()
|
||||
if (y % 2 == 0 && index % 2 == 0) {
|
||||
yuv[uvIndex++] = (if (v < 0) 0 else if (v > 255) 255 else v).toByte()
|
||||
yuv[uvIndex++] = (if (u < 0) 0 else if (u > 255) 255 else u).toByte()
|
||||
}
|
||||
++index
|
||||
}
|
||||
}
|
||||
return yuv
|
||||
}
|
||||
|
||||
private fun ceilIfUneven(n: Int) = if (n and 1 == 1) n + 1 else n
|
||||
|
||||
@@ -24,7 +24,7 @@ import com.google.zxing.ResultMetadataType
|
||||
import com.google.zxing.ResultPointCallback
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
import de.markusfisch.android.binaryeye.app.*
|
||||
import de.markusfisch.android.binaryeye.data.Scan
|
||||
import de.markusfisch.android.binaryeye.database.Scan
|
||||
import de.markusfisch.android.binaryeye.graphics.Mapping
|
||||
import de.markusfisch.android.binaryeye.graphics.frameToView
|
||||
import de.markusfisch.android.binaryeye.graphics.isPortrait
|
||||
@@ -34,8 +34,6 @@ import de.markusfisch.android.binaryeye.widget.DetectorView
|
||||
import de.markusfisch.android.binaryeye.widget.toast
|
||||
import de.markusfisch.android.binaryeye.zxing.Zxing
|
||||
import de.markusfisch.android.cameraview.widget.CameraView
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
@@ -64,8 +62,11 @@ class CameraActivity : AppCompatActivity() {
|
||||
private var rotate = false
|
||||
private var invert = false
|
||||
private var flash = false
|
||||
private var decoding = true
|
||||
private var returnResult = false
|
||||
private var frontFacing = false
|
||||
private var bulkMode = false
|
||||
private var ignoreNext: String? = null
|
||||
private var fallbackBuffer: IntArray? = null
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
@@ -74,7 +75,7 @@ class CameraActivity : AppCompatActivity() {
|
||||
grantResults: IntArray
|
||||
) {
|
||||
when (requestCode) {
|
||||
REQUEST_CAMERA -> if (grantResults.isNotEmpty() &&
|
||||
PERMISSION_CAMERA -> if (grantResults.isNotEmpty() &&
|
||||
grantResults[0] != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
toast(R.string.no_camera_no_fun)
|
||||
@@ -116,9 +117,7 @@ class CameraActivity : AppCompatActivity() {
|
||||
|
||||
initCameraView()
|
||||
initZoomBar()
|
||||
restoreZoom()
|
||||
detectorView.updateRoi = { recreatePreprocessor = true }
|
||||
detectorView.setPaddingFromWindowInsets()
|
||||
initDetectorView()
|
||||
|
||||
if (intent?.action == Intent.ACTION_SEND &&
|
||||
intent.type == "text/plain"
|
||||
@@ -132,6 +131,7 @@ class CameraActivity : AppCompatActivity() {
|
||||
resetPreProcessor()
|
||||
fallbackBuffer = null
|
||||
saveZoom()
|
||||
saveCropHandlePos()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
@@ -139,7 +139,7 @@ class CameraActivity : AppCompatActivity() {
|
||||
System.gc()
|
||||
zxing.updateHints(prefs.tryHarder)
|
||||
returnResult = "com.google.zxing.client.android.SCAN" == intent.action
|
||||
if (hasCameraPermission(this, REQUEST_CAMERA)) {
|
||||
if (hasCameraPermission(this)) {
|
||||
openCamera()
|
||||
}
|
||||
}
|
||||
@@ -178,17 +178,29 @@ class CameraActivity : AppCompatActivity() {
|
||||
zoomBar.max = savedState.getInt(ZOOM_MAX)
|
||||
zoomBar.progress = savedState.getInt(ZOOM_LEVEL)
|
||||
frontFacing = savedState.getBoolean(FRONT_FACING)
|
||||
bulkMode = savedState.getBoolean(BULK_MODE)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
outState.putInt(ZOOM_MAX, zoomBar.max)
|
||||
outState.putInt(ZOOM_LEVEL, zoomBar.progress)
|
||||
outState.putBoolean(FRONT_FACING, frontFacing)
|
||||
outState.putBoolean(BULK_MODE, bulkMode)
|
||||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
|
||||
// always give crop handle precedence over other controls
|
||||
// because it can easily overlap and would then be inaccessible
|
||||
if (detectorView.onTouchEvent(ev)) {
|
||||
return true
|
||||
}
|
||||
return super.dispatchTouchEvent(ev)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.activity_camera, menu)
|
||||
menu.findItem(R.id.bulk_mode).isChecked = bulkMode
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -218,6 +230,11 @@ class CameraActivity : AppCompatActivity() {
|
||||
switchCamera()
|
||||
true
|
||||
}
|
||||
R.id.bulk_mode -> {
|
||||
bulkMode = bulkMode xor true
|
||||
item.isChecked = bulkMode
|
||||
true
|
||||
}
|
||||
R.id.preferences -> {
|
||||
startActivity(MainActivity.getPreferencesIntent(this))
|
||||
true
|
||||
@@ -347,7 +364,8 @@ class CameraActivity : AppCompatActivity() {
|
||||
val frameWidth = cameraView.frameWidth
|
||||
val frameHeight = cameraView.frameHeight
|
||||
val frameOrientation = cameraView.frameOrientation
|
||||
var decoding = true
|
||||
ignoreNext = null
|
||||
decoding = true
|
||||
camera.setPreviewCallback { frameData, _ ->
|
||||
if (decoding) {
|
||||
decodeFrame(
|
||||
@@ -356,8 +374,10 @@ class CameraActivity : AppCompatActivity() {
|
||||
frameHeight,
|
||||
frameOrientation
|
||||
)?.let { result ->
|
||||
postResult(result)
|
||||
decoding = false
|
||||
if (result.text != ignoreNext) {
|
||||
postResult(result)
|
||||
decoding = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -387,6 +407,7 @@ class CameraActivity : AppCompatActivity() {
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {}
|
||||
})
|
||||
restoreZoom()
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@@ -418,6 +439,33 @@ class CameraActivity : AppCompatActivity() {
|
||||
)
|
||||
}
|
||||
|
||||
private fun initDetectorView() {
|
||||
detectorView.onRoiChange = {
|
||||
decoding = false
|
||||
}
|
||||
detectorView.onRoiChanged = {
|
||||
decoding = true
|
||||
recreatePreprocessor = true
|
||||
}
|
||||
detectorView.setPaddingFromWindowInsets()
|
||||
restoreCropHandlePos()
|
||||
}
|
||||
|
||||
private fun saveCropHandlePos() {
|
||||
val pos = detectorView.getCropHandlePos()
|
||||
prefs.cropHandleX = pos.x
|
||||
prefs.cropHandleY = pos.y
|
||||
prefs.cropHandleOrientation = detectorView.currentOrientation
|
||||
}
|
||||
|
||||
private fun restoreCropHandlePos() {
|
||||
detectorView.setCropHandlePos(
|
||||
prefs.cropHandleX,
|
||||
prefs.cropHandleY,
|
||||
prefs.cropHandleOrientation
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateFlashFab(unavailable: Boolean) {
|
||||
if (unavailable) {
|
||||
flashFab.setImageResource(R.drawable.ic_action_create)
|
||||
@@ -591,37 +639,46 @@ class CameraActivity : AppCompatActivity() {
|
||||
showResult(
|
||||
this@CameraActivity,
|
||||
result,
|
||||
returnResult
|
||||
returnResult,
|
||||
bulkMode
|
||||
)
|
||||
if (bulkMode) {
|
||||
ignoreNext = result.text
|
||||
toast(result.text)
|
||||
detectorView.postDelayed({
|
||||
decoding = true
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMapping() = if (rotate) rotatedMapping else nativeMapping
|
||||
|
||||
companion object {
|
||||
private const val REQUEST_CAMERA = 1
|
||||
private const val PICK_FILE_RESULT_CODE = 1
|
||||
private const val ZOOM_MAX = "zoom_max"
|
||||
private const val ZOOM_LEVEL = "zoom_level"
|
||||
private const val FRONT_FACING = "front_facing"
|
||||
private const val BULK_MODE = "bulk_mode"
|
||||
}
|
||||
}
|
||||
|
||||
fun showResult(
|
||||
activity: Activity,
|
||||
result: Result,
|
||||
isResult: Boolean = false
|
||||
isResult: Boolean = false,
|
||||
bulkMode: Boolean = false
|
||||
) {
|
||||
val scan = Scan(result)
|
||||
if (isResult) {
|
||||
activity.setResult(Activity.RESULT_OK, getReturnIntent(result))
|
||||
activity.finish()
|
||||
} else {
|
||||
if (prefs.useHistory) {
|
||||
GlobalScope.launch {
|
||||
db.insertScan(scan)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
val scan = Scan(result)
|
||||
if (prefs.useHistory) {
|
||||
scan.id = db.insertScan(scan)
|
||||
}
|
||||
if (!bulkMode) {
|
||||
activity.startActivity(
|
||||
MainActivity.getDecodeIntent(activity, scan)
|
||||
)
|
||||
|
||||
@@ -11,7 +11,7 @@ import de.markusfisch.android.binaryeye.R
|
||||
import de.markusfisch.android.binaryeye.app.colorSystemAndToolBars
|
||||
import de.markusfisch.android.binaryeye.app.initSystemBars
|
||||
import de.markusfisch.android.binaryeye.app.setFragment
|
||||
import de.markusfisch.android.binaryeye.data.Scan
|
||||
import de.markusfisch.android.binaryeye.database.Scan
|
||||
import de.markusfisch.android.binaryeye.fragment.DecodeFragment
|
||||
import de.markusfisch.android.binaryeye.fragment.EncodeFragment
|
||||
import de.markusfisch.android.binaryeye.fragment.HistoryFragment
|
||||
|
||||
@@ -25,6 +25,7 @@ import de.markusfisch.android.binaryeye.view.recordToolbarHeight
|
||||
import de.markusfisch.android.binaryeye.widget.CropImageView
|
||||
import de.markusfisch.android.binaryeye.widget.toast
|
||||
import de.markusfisch.android.binaryeye.zxing.Zxing
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
class PickActivity : AppCompatActivity() {
|
||||
private val zxing = Zxing()
|
||||
@@ -68,6 +69,8 @@ class PickActivity : AppCompatActivity() {
|
||||
return
|
||||
}
|
||||
|
||||
var result: Result? = null
|
||||
val scope = CoroutineScope(Dispatchers.IO)
|
||||
val scannedRect = Rect()
|
||||
fun scanWithinBounds() = crop(
|
||||
bitmap,
|
||||
@@ -75,31 +78,38 @@ class PickActivity : AppCompatActivity() {
|
||||
cropImageView.imageRotation
|
||||
)?.let {
|
||||
scannedRect.set(0, 0, it.width, it.height)
|
||||
zxing.decodePositiveNegative(it)
|
||||
scope.launch(Dispatchers.IO) {
|
||||
result = zxing.decodePositiveNegative(it)
|
||||
result?.let {
|
||||
withContext(Dispatchers.Main) {
|
||||
if (!isFinishing) {
|
||||
vibrator.vibrate()
|
||||
}
|
||||
cropImageView.updateResultPoints(
|
||||
mapResult(
|
||||
scannedRect.width(),
|
||||
scannedRect.height(),
|
||||
0,
|
||||
cropImageView.getBoundsRect(),
|
||||
it
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cropImageView = findViewById(R.id.image) as CropImageView
|
||||
cropImageView.setImageBitmap(bitmap)
|
||||
cropImageView.onScan = {
|
||||
scanWithinBounds()?.let {
|
||||
if (!isFinishing) {
|
||||
vibrator.vibrate()
|
||||
}
|
||||
mapResult(
|
||||
scannedRect.width(),
|
||||
scannedRect.height(),
|
||||
0,
|
||||
cropImageView.getBoundsRect(),
|
||||
it
|
||||
)
|
||||
}
|
||||
scanWithinBounds()
|
||||
}
|
||||
cropImageView.doOnApplyWindowInsets { v, insets ->
|
||||
(v as CropImageView).windowInsets.set(insets)
|
||||
}
|
||||
|
||||
findViewById(R.id.scan).setOnClickListener {
|
||||
scanImage(scanWithinBounds())
|
||||
scanImage(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import android.view.ViewGroup
|
||||
import android.widget.CursorAdapter
|
||||
import android.widget.TextView
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
import de.markusfisch.android.binaryeye.data.Database
|
||||
import de.markusfisch.android.binaryeye.database.Database
|
||||
|
||||
class ScansAdapter(context: Context, cursor: Cursor) :
|
||||
CursorAdapter(context, cursor, false) {
|
||||
|
||||
@@ -2,7 +2,7 @@ package de.markusfisch.android.binaryeye.app
|
||||
|
||||
import android.app.Application
|
||||
import android.support.v8.renderscript.RenderScript
|
||||
import de.markusfisch.android.binaryeye.data.Database
|
||||
import de.markusfisch.android.binaryeye.database.Database
|
||||
import de.markusfisch.android.binaryeye.preference.Preferences
|
||||
|
||||
val db = Database()
|
||||
@@ -11,12 +11,11 @@ val prefs = Preferences()
|
||||
class BinaryEyeApp : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
db.open(this)
|
||||
prefs.init(this)
|
||||
|
||||
if (prefs.forceCompat) {
|
||||
RenderScript.forceCompat()
|
||||
}
|
||||
|
||||
db.open(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,41 +6,48 @@ import android.content.pm.PackageManager
|
||||
import android.support.v4.app.ActivityCompat
|
||||
import android.support.v4.content.ContextCompat
|
||||
|
||||
fun hasCameraPermission(activity: Activity, requestCode: Int = 1): Boolean {
|
||||
return hasPermission(activity, Manifest.permission.CAMERA, requestCode)
|
||||
const val PERMISSION_CAMERA = 1
|
||||
fun hasCameraPermission(activity: Activity): Boolean {
|
||||
return hasPermission(
|
||||
activity,
|
||||
Manifest.permission.CAMERA,
|
||||
PERMISSION_CAMERA
|
||||
)
|
||||
}
|
||||
|
||||
fun hasWritePermission(activity: Activity, requestCode: Int = 2): Boolean {
|
||||
const val PERMISSION_WRITE = 2
|
||||
fun hasWritePermission(activity: Activity): Boolean {
|
||||
return hasPermission(
|
||||
activity,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
requestCode
|
||||
PERMISSION_WRITE
|
||||
)
|
||||
}
|
||||
|
||||
fun hasLocationPermission(activity: Activity, requestCode: Int = 3): Boolean {
|
||||
const val PERMISSION_LOCATION = 3
|
||||
fun hasLocationPermission(activity: Activity): Boolean {
|
||||
return hasPermission(
|
||||
activity,
|
||||
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||
requestCode
|
||||
PERMISSION_LOCATION
|
||||
)
|
||||
}
|
||||
|
||||
fun hasPermission(
|
||||
private fun hasPermission(
|
||||
activity: Activity,
|
||||
permission: String,
|
||||
requestCode: Int
|
||||
): Boolean {
|
||||
return if (ContextCompat.checkSelfPermission(activity, permission) !=
|
||||
PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
ActivityCompat.requestPermissions(
|
||||
activity,
|
||||
arrayOf(permission),
|
||||
requestCode
|
||||
)
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
) = if (ContextCompat.checkSelfPermission(
|
||||
activity,
|
||||
permission
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
ActivityCompat.requestPermissions(
|
||||
activity,
|
||||
arrayOf(permission),
|
||||
requestCode
|
||||
)
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package de.markusfisch.android.binaryeye.data
|
||||
package de.markusfisch.android.binaryeye.database
|
||||
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
@@ -1,4 +1,4 @@
|
||||
package de.markusfisch.android.binaryeye.data
|
||||
package de.markusfisch.android.binaryeye.database
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
@@ -1,16 +1,12 @@
|
||||
package de.markusfisch.android.binaryeye.data
|
||||
package de.markusfisch.android.binaryeye.database
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Environment
|
||||
import de.markusfisch.android.binaryeye.app.hasWritePermission
|
||||
import de.markusfisch.android.binaryeye.app.writeExternalFile
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
|
||||
fun exportDatabase(activity: Activity, fileName: String): Boolean {
|
||||
if (!hasWritePermission(activity)) {
|
||||
return false
|
||||
}
|
||||
val dbFile = File(
|
||||
Environment.getDataDirectory(),
|
||||
"//data//${activity.packageName}//databases//${Database.FILE_NAME}"
|
||||
@@ -1,4 +1,4 @@
|
||||
package de.markusfisch.android.binaryeye.data
|
||||
package de.markusfisch.android.binaryeye.database
|
||||
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
@@ -1,4 +1,4 @@
|
||||
package de.markusfisch.android.binaryeye.data
|
||||
package de.markusfisch.android.binaryeye.database
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
@@ -19,7 +19,7 @@ data class Scan(
|
||||
val suggestedPrice: String?,
|
||||
val upcEanExtension: String?,
|
||||
val timestamp: String = getDateTime(),
|
||||
val id: Long = 0L
|
||||
var id: Long = 0L
|
||||
) : Parcelable {
|
||||
constructor(result: Result) : this(
|
||||
result.text,
|
||||
@@ -25,7 +25,12 @@ import java.io.OutputStream
|
||||
import java.util.*
|
||||
|
||||
class BarcodeFragment : Fragment() {
|
||||
private var barcode: Bitmap? = null
|
||||
private enum class FileType {
|
||||
PNG, SVG
|
||||
}
|
||||
|
||||
private var barcodeBitmap: Bitmap? = null
|
||||
private var barcodeSvg: String? = null
|
||||
private var content: String = ""
|
||||
private var format: BarcodeFormat? = null
|
||||
|
||||
@@ -53,7 +58,8 @@ class BarcodeFragment : Fragment() {
|
||||
val format = args.getSerializable(FORMAT) as BarcodeFormat? ?: return view
|
||||
val size = args.getInt(SIZE)
|
||||
try {
|
||||
barcode = Zxing.encodeAsBitmap(content, format, size, size)
|
||||
barcodeBitmap = Zxing.encodeAsBitmap(content, format, size, size)
|
||||
barcodeSvg = Zxing.encodeAsSvg(content, format, size, size)
|
||||
} catch (e: Exception) {
|
||||
var message = e.message
|
||||
if (message == null || message.isEmpty()) {
|
||||
@@ -71,14 +77,14 @@ class BarcodeFragment : Fragment() {
|
||||
val imageView = view.findViewById<ConfinedScalingImageView>(
|
||||
R.id.barcode
|
||||
)
|
||||
imageView.setImageBitmap(barcode)
|
||||
imageView.setImageBitmap(barcodeBitmap)
|
||||
imageView.post {
|
||||
// make sure to invoke this after ScalingImageView.onLayout()
|
||||
imageView.minWidth /= 2f
|
||||
}
|
||||
|
||||
view.findViewById<View>(R.id.share).setOnClickListener {
|
||||
val bitmap = barcode
|
||||
val bitmap = barcodeBitmap
|
||||
bitmap?.let {
|
||||
share(bitmap)
|
||||
}
|
||||
@@ -98,8 +104,12 @@ class BarcodeFragment : Fragment() {
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.save -> {
|
||||
askForFileNameAndSave()
|
||||
R.id.export_svg -> {
|
||||
askForFileNameAndSave(FileType.SVG)
|
||||
true
|
||||
}
|
||||
R.id.export_png -> {
|
||||
askForFileNameAndSave(FileType.PNG)
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
@@ -108,23 +118,33 @@ class BarcodeFragment : Fragment() {
|
||||
|
||||
// dialogs do not have a parent view
|
||||
@SuppressLint("InflateParams")
|
||||
private fun askForFileNameAndSave() {
|
||||
private fun askForFileNameAndSave(fileType: FileType) {
|
||||
val ac = activity ?: return
|
||||
if (!hasWritePermission(ac)) {
|
||||
return
|
||||
}
|
||||
val view = ac.layoutInflater.inflate(R.layout.dialog_save_file, null)
|
||||
val editText = view.findViewById<EditText>(R.id.file_name)
|
||||
editText.setText(encodeFileName("${format.toString()}_$content"))
|
||||
AlertDialog.Builder(ac)
|
||||
.setView(view)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
val bitmap = barcode
|
||||
bitmap?.let {
|
||||
saveAsFile(
|
||||
bitmap,
|
||||
addSuffixIfNotGiven(
|
||||
editText.text.toString(),
|
||||
".png"
|
||||
)
|
||||
)
|
||||
val fileName = editText.text.toString()
|
||||
when (fileType) {
|
||||
FileType.PNG -> saveAs(
|
||||
addSuffixIfNotGiven(fileName, ".png"),
|
||||
"image/png"
|
||||
) {
|
||||
barcodeBitmap?.saveAsPng(it)
|
||||
}
|
||||
FileType.SVG -> saveAs(
|
||||
addSuffixIfNotGiven(fileName, ".svg"),
|
||||
"image/svg+xmg"
|
||||
) { outputStream ->
|
||||
barcodeSvg?.let {
|
||||
outputStream.write(it.toByteArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
@@ -132,15 +152,14 @@ class BarcodeFragment : Fragment() {
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun saveAsFile(bitmap: Bitmap, fileName: String) {
|
||||
private fun saveAs(
|
||||
fileName: String,
|
||||
mimeType: String,
|
||||
write: (outputStream: OutputStream) -> Unit
|
||||
) {
|
||||
val ac = activity ?: return
|
||||
if (!hasWritePermission(ac)) {
|
||||
return
|
||||
}
|
||||
GlobalScope.launch {
|
||||
val message = writeExternalFile(ac, fileName, "image/png") {
|
||||
bitmap.saveAsPng(it)
|
||||
}.toSaveResult()
|
||||
val message = writeExternalFile(ac, fileName, mimeType, write).toSaveResult()
|
||||
GlobalScope.launch(Main) {
|
||||
ac.toast(message)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import de.markusfisch.android.binaryeye.R
|
||||
import de.markusfisch.android.binaryeye.actions.ActionRegistry
|
||||
import de.markusfisch.android.binaryeye.actions.wifi.WifiAction
|
||||
import de.markusfisch.android.binaryeye.app.*
|
||||
import de.markusfisch.android.binaryeye.data.Scan
|
||||
import de.markusfisch.android.binaryeye.database.Scan
|
||||
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
|
||||
import de.markusfisch.android.binaryeye.widget.toast
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -31,13 +31,14 @@ class DecodeFragment : Fragment() {
|
||||
private lateinit var format: String
|
||||
private lateinit var fab: FloatingActionButton
|
||||
|
||||
private var action = ActionRegistry.DEFAULT_ACTION
|
||||
private var isBinary = false
|
||||
private val parentJob = Job()
|
||||
private val scope: CoroutineScope = CoroutineScope(Dispatchers.Main + parentJob)
|
||||
private val content: String
|
||||
get() = contentView.text.toString()
|
||||
|
||||
private val parentJob = Job()
|
||||
private val scope: CoroutineScope = CoroutineScope(Dispatchers.Main + parentJob)
|
||||
private var action = ActionRegistry.DEFAULT_ACTION
|
||||
private var isBinary = false
|
||||
private var id = 0L
|
||||
|
||||
override fun onCreate(state: Bundle?) {
|
||||
super.onCreate(state)
|
||||
@@ -59,6 +60,7 @@ class DecodeFragment : Fragment() {
|
||||
|
||||
val scan = arguments?.getParcelable(SCAN) as Scan?
|
||||
?: throw IllegalArgumentException("DecodeFragment needs a Scan")
|
||||
id = scan.id
|
||||
|
||||
val inputContent = scan.content
|
||||
isBinary = hasNonPrintableCharacters(
|
||||
@@ -196,6 +198,9 @@ class DecodeFragment : Fragment() {
|
||||
menu.findItem(R.id.copy_to_clipboard).isVisible = false
|
||||
menu.findItem(R.id.create).isVisible = false
|
||||
}
|
||||
if (id > 0L) {
|
||||
menu.findItem(R.id.remove).isVisible = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
@@ -215,6 +220,11 @@ class DecodeFragment : Fragment() {
|
||||
)
|
||||
true
|
||||
}
|
||||
R.id.remove -> {
|
||||
db.removeScan(id)
|
||||
backOrFinish()
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
@@ -226,6 +236,15 @@ class DecodeFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun backOrFinish() {
|
||||
val fm = fragmentManager
|
||||
if (fm != null && fm.backStackEntryCount > 0) {
|
||||
fm.popBackStack()
|
||||
} else {
|
||||
activity?.finish()
|
||||
}
|
||||
}
|
||||
|
||||
private fun executeAction(content: ByteArray) {
|
||||
val ac = activity ?: return
|
||||
if (content.isNotEmpty()) {
|
||||
|
||||
@@ -21,10 +21,10 @@ import android.widget.ListView
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
import de.markusfisch.android.binaryeye.adapter.ScansAdapter
|
||||
import de.markusfisch.android.binaryeye.app.*
|
||||
import de.markusfisch.android.binaryeye.data.Database
|
||||
import de.markusfisch.android.binaryeye.data.exportCsv
|
||||
import de.markusfisch.android.binaryeye.data.exportDatabase
|
||||
import de.markusfisch.android.binaryeye.data.exportJson
|
||||
import de.markusfisch.android.binaryeye.database.Database
|
||||
import de.markusfisch.android.binaryeye.database.exportCsv
|
||||
import de.markusfisch.android.binaryeye.database.exportDatabase
|
||||
import de.markusfisch.android.binaryeye.database.exportJson
|
||||
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
|
||||
import de.markusfisch.android.binaryeye.view.useVisibility
|
||||
import de.markusfisch.android.binaryeye.widget.toast
|
||||
|
||||
@@ -12,10 +12,7 @@ class Dots(context: Context) {
|
||||
private val radius = 8f * context.resources.displayMetrics.density
|
||||
|
||||
init {
|
||||
paint.color = ContextCompat.getColor(
|
||||
context,
|
||||
R.color.dot
|
||||
)
|
||||
paint.color = ContextCompat.getColor(context, R.color.dot)
|
||||
paint.style = Paint.Style.FILL
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,21 @@ import android.preference.PreferenceManager
|
||||
class Preferences {
|
||||
lateinit var preferences: SharedPreferences
|
||||
|
||||
var cropHandleX = -1
|
||||
set(value) {
|
||||
apply(CROP_HANDLE_X, value)
|
||||
field = value
|
||||
}
|
||||
var cropHandleY = -1
|
||||
set(value) {
|
||||
apply(CROP_HANDLE_Y, value)
|
||||
field = value
|
||||
}
|
||||
var cropHandleOrientation = 0
|
||||
set(value) {
|
||||
apply(CROP_HANDLE_ORIENTATION, value)
|
||||
field = value
|
||||
}
|
||||
var showCropHandle = true
|
||||
set(value) {
|
||||
apply(SHOW_CROP_HANDLE, value)
|
||||
@@ -85,6 +100,12 @@ class Preferences {
|
||||
}
|
||||
|
||||
fun update() {
|
||||
cropHandleX = preferences.getInt(CROP_HANDLE_X, cropHandleX)
|
||||
cropHandleY = preferences.getInt(CROP_HANDLE_Y, cropHandleY)
|
||||
cropHandleOrientation = preferences.getInt(
|
||||
CROP_HANDLE_ORIENTATION,
|
||||
cropHandleOrientation
|
||||
)
|
||||
showCropHandle = preferences.getBoolean(SHOW_CROP_HANDLE, showCropHandle)
|
||||
zoomBySwiping = preferences.getBoolean(ZOOM_BY_SWIPING, zoomBySwiping)
|
||||
autoRotate = preferences.getBoolean(AUTO_ROTATE, autoRotate)
|
||||
@@ -131,6 +152,9 @@ class Preferences {
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val CROP_HANDLE_X = "crop_handle_x"
|
||||
const val CROP_HANDLE_Y = "crop_handle_y"
|
||||
const val CROP_HANDLE_ORIENTATION = "crop_handle_orientation"
|
||||
const val SHOW_CROP_HANDLE = "show_crop_handle"
|
||||
const val ZOOM_BY_SWIPING = "zoom_by_swiping"
|
||||
const val AUTO_ROTATE = "auto_rotate"
|
||||
|
||||
@@ -3,7 +3,7 @@ package de.markusfisch.android.binaryeye.rs
|
||||
import android.content.Context
|
||||
import android.graphics.Rect
|
||||
import android.support.v8.renderscript.*
|
||||
import de.markusfisch.android.binaryeye.renderscript.ScriptC_rotator
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
private const val SCALE_FACTOR = .75f
|
||||
@@ -19,7 +19,7 @@ class Preprocessor(
|
||||
|
||||
private val rs = RenderScript.create(context)
|
||||
private val resizeScript = ScriptIntrinsicResize.create(rs)
|
||||
private val rotatorScript = ScriptC_rotator(rs)
|
||||
private val rotateScript = ScriptC_rotate(rs)
|
||||
|
||||
private var yuvType: Type? = null
|
||||
private var yuvAlloc: Allocation? = null
|
||||
@@ -65,6 +65,10 @@ class Preprocessor(
|
||||
// make sure the dimensions are always a multiple of 4
|
||||
outWidth -= outWidth % 4
|
||||
outHeight -= outHeight % 4
|
||||
|
||||
// make sure the dimensions are always greater than 4
|
||||
outWidth = max(4, outWidth)
|
||||
outHeight = max(4, outHeight)
|
||||
} else {
|
||||
outWidth = (width * SCALE_FACTOR).roundToInt()
|
||||
outHeight = (height * SCALE_FACTOR).roundToInt()
|
||||
@@ -113,7 +117,7 @@ class Preprocessor(
|
||||
rotatedAlloc?.destroy()
|
||||
rotatedAlloc = null
|
||||
resizeScript.destroy()
|
||||
rotatorScript.destroy()
|
||||
rotateScript.destroy()
|
||||
rs.destroy()
|
||||
}
|
||||
|
||||
@@ -125,10 +129,10 @@ class Preprocessor(
|
||||
fun resizeAndRotate(frame: ByteArray) {
|
||||
resize(frame)
|
||||
val t = resizedType ?: return
|
||||
rotatorScript._inImage = resizedAlloc
|
||||
rotatorScript._inWidth = t.x
|
||||
rotatorScript._inHeight = t.y
|
||||
rotatorScript.forEach_rotate90(
|
||||
rotateScript._inImage = resizedAlloc
|
||||
rotateScript._inWidth = t.x
|
||||
rotateScript._inHeight = t.y
|
||||
rotateScript.forEach_rotate90(
|
||||
rotatedAlloc, // ignored in kernel, just to satisfy forEach
|
||||
rotatedAlloc
|
||||
)
|
||||
|
||||
@@ -14,18 +14,13 @@ class CropImageView(context: Context, attr: AttributeSet) :
|
||||
ConfinedScalingImageView(context, attr) {
|
||||
val windowInsets = Rect()
|
||||
|
||||
var onScan: (() -> List<Point>?)? = null
|
||||
var onScan: (() -> Unit)? = null
|
||||
|
||||
private val dots = Dots(context)
|
||||
private val boundsPaint = context.getDashedBorderPaint()
|
||||
private val lastMappedRect = RectF()
|
||||
private val padding: Int = (24f * context.resources.displayMetrics.density).roundToInt()
|
||||
private val onScanRunnable = Runnable {
|
||||
onScan?.invoke()?.let {
|
||||
resultPoints = it
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
private val onScanRunnable = Runnable { onScan?.invoke() }
|
||||
|
||||
private var resultPoints: List<Point>? = null
|
||||
|
||||
@@ -33,6 +28,11 @@ class CropImageView(context: Context, attr: AttributeSet) :
|
||||
scaleType = ScaleType.CENTER_CROP
|
||||
}
|
||||
|
||||
fun updateResultPoints(points: List<Point>) {
|
||||
resultPoints = points
|
||||
invalidate()
|
||||
}
|
||||
|
||||
override fun onLayout(
|
||||
changed: Boolean,
|
||||
left: Int,
|
||||
|
||||
@@ -15,14 +15,18 @@ import de.markusfisch.android.binaryeye.graphics.Dots
|
||||
import de.markusfisch.android.binaryeye.graphics.getBitmapFromDrawable
|
||||
import de.markusfisch.android.binaryeye.graphics.getDashedBorderPaint
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class DetectorView : View {
|
||||
val currentOrientation = resources.configuration.orientation
|
||||
val roi = Rect()
|
||||
|
||||
var updateRoi: (() -> Unit)? = null
|
||||
var onRoiChange: (() -> Unit)? = null
|
||||
var onRoiChanged: (() -> Unit)? = null
|
||||
|
||||
private val orientation = resources.configuration.orientation
|
||||
private val dots = Dots(context)
|
||||
private val invalidateRunnable: Runnable = Runnable {
|
||||
marks = null
|
||||
@@ -34,8 +38,8 @@ class DetectorView : View {
|
||||
)
|
||||
private val handleXRadius = handleBitmap.width / 2
|
||||
private val handleYRadius = handleBitmap.height / 2
|
||||
private val handleHome = Point()
|
||||
private val handlePos = Point(-1, -1)
|
||||
private val handleHome = Point()
|
||||
private val center = Point()
|
||||
private val touchDown = Point()
|
||||
private val distToFull: Int
|
||||
@@ -45,9 +49,11 @@ class DetectorView : View {
|
||||
private val padding: Int
|
||||
|
||||
private var marks: List<Point>? = null
|
||||
private var orientation = resources.configuration.orientation
|
||||
private var handleGrabbed = false
|
||||
private var handleMoved = false
|
||||
private var handleActive = false
|
||||
private var minY = 0
|
||||
private var maxY = 0
|
||||
private var minDist = 0
|
||||
private var shadeColor = 0
|
||||
|
||||
init {
|
||||
@@ -67,6 +73,23 @@ class DetectorView : View {
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) :
|
||||
super(context, attrs, defStyleAttr)
|
||||
|
||||
fun setCropHandlePos(x: Int, y: Int, orientation: Int) {
|
||||
if (orientation == currentOrientation) {
|
||||
handlePos.set(x, y)
|
||||
} else {
|
||||
handlePos.set(y, x)
|
||||
}
|
||||
if (x > -1) {
|
||||
handleActive = true
|
||||
}
|
||||
}
|
||||
|
||||
fun getCropHandlePos() = if (handleActive) {
|
||||
handlePos
|
||||
} else {
|
||||
Point(-1, -1)
|
||||
}
|
||||
|
||||
fun mark(points: List<Point>) {
|
||||
marks = points
|
||||
invalidate()
|
||||
@@ -75,27 +98,23 @@ class DetectorView : View {
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(): Parcelable? {
|
||||
if (!handleMoved) {
|
||||
if (!handleActive) {
|
||||
return super.onSaveInstanceState()
|
||||
}
|
||||
return SavedState(super.onSaveInstanceState()).apply {
|
||||
savedHandlePos.set(handlePos)
|
||||
savedOrientation = orientation
|
||||
savedHandlePos.set(getCropHandlePos())
|
||||
savedOrientation = currentOrientation
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRestoreInstanceState(state: Parcelable) {
|
||||
super.onRestoreInstanceState(
|
||||
if (state is SavedState) {
|
||||
if (state.savedOrientation == orientation) {
|
||||
handlePos.set(state.savedHandlePos)
|
||||
} else {
|
||||
handlePos.set(
|
||||
state.savedHandlePos.y,
|
||||
state.savedHandlePos.x
|
||||
)
|
||||
}
|
||||
handleMoved = true
|
||||
setCropHandlePos(
|
||||
state.savedHandlePos.x,
|
||||
state.savedHandlePos.y,
|
||||
state.savedOrientation
|
||||
)
|
||||
state.superState
|
||||
} else {
|
||||
state
|
||||
@@ -114,6 +133,9 @@ class DetectorView : View {
|
||||
touchDown.set(x, y)
|
||||
handleGrabbed = abs(x - handlePos.x) < handleXRadius &&
|
||||
abs(y - handlePos.y) < handleYRadius
|
||||
if (handleGrabbed) {
|
||||
onRoiChange?.invoke()
|
||||
}
|
||||
handleGrabbed
|
||||
} else {
|
||||
false
|
||||
@@ -123,8 +145,9 @@ class DetectorView : View {
|
||||
if (handleGrabbed) {
|
||||
handlePos.set(x, y)
|
||||
if (distSq(handlePos, touchDown) > minMoveThresholdSq) {
|
||||
handleMoved = true
|
||||
handleActive = true
|
||||
}
|
||||
updateClipRect()
|
||||
invalidate()
|
||||
true
|
||||
} else {
|
||||
@@ -140,17 +163,19 @@ class DetectorView : View {
|
||||
}
|
||||
MotionEvent.ACTION_UP -> {
|
||||
if (handleGrabbed) {
|
||||
if (!handleMoved) {
|
||||
if (!handleActive) {
|
||||
val mn = min(center.x, center.y) * .8f
|
||||
handlePos.set(
|
||||
(center.x * 1.5f).roundToInt(),
|
||||
(center.y * 1.25f).roundToInt()
|
||||
(center.x + mn).roundToInt(),
|
||||
(center.y + mn).roundToInt()
|
||||
)
|
||||
handleMoved = true
|
||||
handleActive = true
|
||||
invalidate()
|
||||
} else {
|
||||
snap(x, y)
|
||||
}
|
||||
updateRoi?.invoke()
|
||||
updateClipRect()
|
||||
onRoiChanged?.invoke()
|
||||
handleGrabbed = false
|
||||
}
|
||||
false
|
||||
@@ -163,32 +188,44 @@ class DetectorView : View {
|
||||
if (abs(x - center.x) < distToFull ||
|
||||
abs(y - center.y) < distToFull
|
||||
) {
|
||||
handlePos.set(handleHome)
|
||||
handleMoved = false
|
||||
reset()
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
private fun reset() {
|
||||
handlePos.set(handleHome)
|
||||
handleActive = false
|
||||
roi.set(0, 0, 0, 0)
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||
super.onLayout(changed, left, top, right, bottom)
|
||||
if (!changed) {
|
||||
return
|
||||
}
|
||||
val width = right - left
|
||||
val height = bottom - top
|
||||
center.set(
|
||||
left + (width / 2),
|
||||
top + (height / 2)
|
||||
)
|
||||
minY = padding * 2
|
||||
maxY = height - minY
|
||||
handleHome.set(
|
||||
width - handleXRadius - paddingRight - padding,
|
||||
height - handleYRadius - paddingBottom - fabHeight
|
||||
)
|
||||
if (handlePos.x < 0) {
|
||||
handlePos.set(handleHome)
|
||||
if (handleActive) {
|
||||
updateClipRect()
|
||||
} else {
|
||||
reset()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
canvas.drawColor(0, PorterDuff.Mode.CLEAR)
|
||||
if (handleMoved) {
|
||||
if (handleActive) {
|
||||
drawClip(canvas)
|
||||
}
|
||||
marks?.let {
|
||||
@@ -205,7 +242,6 @@ class DetectorView : View {
|
||||
}
|
||||
|
||||
private fun drawClip(canvas: Canvas) {
|
||||
val minDist = updateClipRect()
|
||||
if (minDist < 1) {
|
||||
return
|
||||
}
|
||||
@@ -230,13 +266,14 @@ class DetectorView : View {
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateClipRect(): Int {
|
||||
private fun updateClipRect() {
|
||||
clampHandlePos()
|
||||
val dx = abs(handlePos.x - center.x)
|
||||
val dy = abs(handlePos.y - center.y)
|
||||
val d = min(dx, dy)
|
||||
minDist = min(dx, dy)
|
||||
shadeColor = (min(
|
||||
1f,
|
||||
d.toFloat() / distToFull.toFloat()
|
||||
minDist.toFloat() / distToFull.toFloat()
|
||||
) * 128f).toInt() shl 24
|
||||
roi.set(
|
||||
center.x - dx,
|
||||
@@ -244,7 +281,11 @@ class DetectorView : View {
|
||||
center.x + dx,
|
||||
center.y + dy
|
||||
)
|
||||
return d
|
||||
}
|
||||
|
||||
private fun clampHandlePos() {
|
||||
handlePos.x = min(center.x * 2, max(0, handlePos.x))
|
||||
handlePos.y = min(maxY, max(minY, handlePos.y))
|
||||
}
|
||||
|
||||
internal class SavedState : BaseSavedState {
|
||||
|
||||
@@ -156,5 +156,42 @@ class Zxing(possibleResultPoint: ResultPointCallback? = null) {
|
||||
bitmap.setPixels(pixels, 0, w, 0, 0, w, h)
|
||||
return bitmap
|
||||
}
|
||||
|
||||
fun encodeAsSvg(
|
||||
text: String,
|
||||
format: BarcodeFormat,
|
||||
width: Int,
|
||||
height: Int
|
||||
): String {
|
||||
val hints = EnumMap<EncodeHintType, Any>(EncodeHintType::class.java)
|
||||
hints[EncodeHintType.CHARACTER_SET] = "utf-8"
|
||||
val result = MultiFormatWriter().encode(
|
||||
text,
|
||||
format,
|
||||
0,
|
||||
0,
|
||||
hints
|
||||
)
|
||||
val sb = StringBuilder()
|
||||
sb.append("<svg width=\"$width\" height=\"$height\"")
|
||||
sb.append(" viewBox=\"0 0 $width $height\"")
|
||||
sb.append(" xmlns=\"http://www.w3.org/2000/svg\">\n")
|
||||
val w = result.width
|
||||
val h = result.height
|
||||
val xf = width.toFloat() / w
|
||||
val yf = height.toFloat() / h
|
||||
for (y in 0 until h) {
|
||||
for (x in 0 until w) {
|
||||
if (result.get(x, y)) {
|
||||
val ox = x * xf
|
||||
val oy = y * yf
|
||||
sb.append("<rect x=\"$ox\" y=\"$oy\"")
|
||||
sb.append(" width=\"$xf\" height=\"$yf\"/>\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.append("</svg>\n")
|
||||
return sb.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="56dp"
|
||||
android:height="56dp"
|
||||
android:viewportWidth="56"
|
||||
android:viewportHeight="56">
|
||||
<path
|
||||
android:pathData="M28,32m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:gradientRadius="24"
|
||||
android:centerX="28"
|
||||
android:centerY="32"
|
||||
android:type="radial">
|
||||
<item android:offset="0" android:color="#FF000000"/>
|
||||
<item android:offset="1" android:color="#00000000"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M28,28m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"
|
||||
android:fillColor="#B6D46F"/>
|
||||
<path
|
||||
android:pathData="M33,31L35,31L35,23C35,21.9 34.1,21 33,21L25,21L25,23L33,23L33,31ZM23,33L23,17L21,17L21,21L17,21L17,23L21,23L21,33C21,34.1 21.9,35 23,35L33,35L33,39L35,39L35,35L39,35L39,33L23,33Z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt" android:width="56dp" android:height="56dp" android:viewportWidth="56" android:viewportHeight="56">
|
||||
<path android:pathData="M4 32a24 24 0 1 1 48 0 24 24 0 1 1-48 0">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient android:gradientRadius="24" android:centerX="28" android:centerY="32" android:type="radial">
|
||||
<item android:offset="0" android:color="#FF000000"/>
|
||||
<item android:offset="1" android:color="#00000000"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path android:pathData="M4 28a24 24 0 1 1 48 0 24 24 0 1 1-48 0" android:fillColor="#B6D46F"/>
|
||||
<path android:pathData="M33 31h2v-8c0-1.1-0.9-2-2-2h-8v2h8v8zm-10 2V17h-2v4h-4v2h4v10c0 1.1 0.9 2 2 2h10v4h2v-4h4v-2H23z" android:fillColor="#FFFFFF"/>
|
||||
</vector>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FFFFFF" android:pathData="M14 10H2v2h12v-2zm0-4H2v2h12V6zm4 8v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zM2 16h8v-2H2v2z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:fillColor="#FFFFFFFF" android:pathData="M4.413 15.067h-0.88v1.93h-1.37v-5.69h2.27c0.433 0 0.817 0.08 1.15 0.24 0.34 0.16 0.6 0.39 0.78 0.69 0.187 0.293 0.28 0.627 0.28 1 0 0.553-0.2 0.997-0.6 1.33-0.4 0.333-0.943 0.5-1.63 0.5zm-0.88-2.7v1.64h0.9c0.267 0 0.47-0.067 0.61-0.2s0.21-0.32 0.21-0.56c0-0.267-0.073-0.48-0.22-0.64-0.14-0.16-0.337-0.24-0.59-0.24h-0.91zm8.587-1.06v5.69h-1.36l-2.02-3.54v3.54H7.37v-5.69h1.37l2.01 3.54v-3.54h1.37zm5.527 2.65v2.34c-0.213 0.233-0.523 0.42-0.93 0.56-0.407 0.147-0.853 0.22-1.34 0.22-0.74 0-1.333-0.23-1.78-0.69-0.447-0.453-0.687-1.087-0.72-1.9v-0.49c0-0.56 0.1-1.05 0.3-1.47 0.193-0.413 0.477-0.733 0.85-0.96 0.367-0.227 0.793-0.34 1.28-0.34 0.707 0 1.257 0.163 1.65 0.49 0.387 0.32 0.613 0.803 0.68 1.45h-1.32c-0.047-0.32-0.147-0.547-0.3-0.68-0.16-0.133-0.383-0.2-0.67-0.2-0.34 0-0.603 0.143-0.79 0.43-0.193 0.293-0.29 0.71-0.29 1.25v0.35c0 0.567 0.097 0.993 0.29 1.28 0.193 0.28 0.5 0.42 0.92 0.42 0.353 0 0.617-0.077 0.79-0.23v-0.89h-0.95v-0.94h2.33zm4.733-6.991L18.481 6.96l1.463 1.464-2.065 2.066 0.975 0.975L20.921 9.4l1.463 1.464-0.005-3.898z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:fillColor="#FFFFFFFF" android:pathData="M5.45 15.487c0-0.2-0.07-0.357-0.21-0.47-0.14-0.113-0.39-0.23-0.75-0.35-0.353-0.12-0.643-0.237-0.87-0.35-0.74-0.36-1.11-0.857-1.11-1.49 0-0.313 0.093-0.59 0.28-0.83 0.18-0.24 0.44-0.427 0.78-0.56 0.333-0.14 0.71-0.21 1.13-0.21 0.407 0 0.773 0.073 1.1 0.22 0.327 0.147 0.577 0.357 0.75 0.63 0.18 0.267 0.27 0.573 0.27 0.92H5.46c0-0.233-0.073-0.413-0.22-0.54-0.14-0.127-0.33-0.19-0.57-0.19-0.247 0-0.44 0.053-0.58 0.16-0.14 0.107-0.21 0.243-0.21 0.41 0 0.147 0.077 0.28 0.23 0.4 0.153 0.12 0.427 0.243 0.82 0.37 0.393 0.127 0.717 0.263 0.97 0.41 0.613 0.353 0.92 0.84 0.92 1.46 0 0.5-0.187 0.89-0.56 1.17-0.373 0.287-0.887 0.43-1.54 0.43-0.46 0-0.877-0.083-1.25-0.25-0.373-0.167-0.657-0.393-0.85-0.68-0.187-0.287-0.28-0.617-0.28-0.99h1.38c0 0.3 0.077 0.523 0.23 0.67 0.16 0.147 0.417 0.22 0.77 0.22 0.227 0 0.407-0.05 0.54-0.15 0.127-0.1 0.19-0.237 0.19-0.41zm3.158-4.18l1.13 4.14 1.13-4.14h1.53l-1.91 5.69h-1.5l-1.9-5.69h1.52zm8.814 2.65v2.34c-0.213 0.233-0.523 0.42-0.93 0.56-0.407 0.147-0.853 0.22-1.34 0.22-0.74 0-1.333-0.23-1.78-0.69-0.447-0.453-0.687-1.087-0.72-1.9v-0.49c0-0.56 0.1-1.05 0.3-1.47 0.193-0.413 0.477-0.733 0.85-0.96 0.367-0.227 0.793-0.34 1.28-0.34 0.707 0 1.257 0.163 1.65 0.49 0.387 0.32 0.613 0.803 0.68 1.45h-1.32c-0.047-0.32-0.147-0.547-0.3-0.68-0.16-0.133-0.383-0.2-0.67-0.2-0.34 0-0.603 0.143-0.79 0.43-0.193 0.293-0.29 0.71-0.29 1.25v0.35c0 0.567 0.097 0.993 0.29 1.28 0.193 0.28 0.5 0.42 0.92 0.42 0.353 0 0.617-0.077 0.79-0.23v-0.89h-0.95v-0.94h2.33zm4.164-7.051L17.69 6.9l1.463 1.464-2.065 2.066 0.975 0.975 2.065-2.066 1.464 1.464-0.005-3.898z"/>
|
||||
</vector>
|
||||
@@ -21,6 +21,12 @@
|
||||
android:title="@string/switch_camera"
|
||||
android:icon="@drawable/ic_action_switch_camera"
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/bulk_mode"
|
||||
android:title="@string/bulk_mode"
|
||||
android:icon="@drawable/ic_action_bulk_mode"
|
||||
android:checkable="true"
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/preferences"
|
||||
android:title="@string/preferences"
|
||||
|
||||
@@ -2,8 +2,13 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:material="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/save"
|
||||
android:title="@string/export_to_file"
|
||||
android:icon="@drawable/ic_action_save"
|
||||
android:id="@+id/export_svg"
|
||||
android:title="@string/export_svg"
|
||||
android:icon="@drawable/ic_action_save_svg"
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/export_png"
|
||||
android:title="@string/export_png"
|
||||
android:icon="@drawable/ic_action_save_png"
|
||||
material:showAsAction="ifRoom"/>
|
||||
</menu>
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
<menu
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:material="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/remove"
|
||||
android:title="@string/remove_scan"
|
||||
android:icon="@drawable/ic_action_remove"
|
||||
android:visible="false"
|
||||
material:showAsAction="always"/>
|
||||
<item
|
||||
android:id="@+id/copy_to_clipboard"
|
||||
android:title="@string/copy_to_clipboard"
|
||||
android:icon="@drawable/ic_action_copy"
|
||||
material:showAsAction="ifRoom"/>
|
||||
material:showAsAction="always"/>
|
||||
<item
|
||||
android:id="@+id/share"
|
||||
android:title="@string/share"
|
||||
android:icon="@drawable/ic_action_share"
|
||||
material:showAsAction="ifRoom"/>
|
||||
material:showAsAction="always"/>
|
||||
<item
|
||||
android:id="@+id/create"
|
||||
android:title="@string/compose_barcode"
|
||||
android:icon="@drawable/ic_action_create"
|
||||
material:showAsAction="ifRoom"/>
|
||||
material:showAsAction="always"/>
|
||||
</menu>
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<string name="view_barcode">Barcode ansehen</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="switch_camera">Kamera wechseln</string>
|
||||
<string name="bulk_mode">Fortlaufend Scannen</string>
|
||||
<string name="history">Gespeicherte Codes</string>
|
||||
<string name="preferences">Einstellungen</string>
|
||||
<string name="show_crop_handle">Bereich verkleinern anzeigen</string>
|
||||
@@ -83,6 +84,8 @@
|
||||
<string name="search_web">Suche im Internet</string>
|
||||
<string name="search_scan">Code suchen</string>
|
||||
<string name="export_to_file">In Datei exportieren</string>
|
||||
<string name="export_png">Als PNG exportieren</string>
|
||||
<string name="export_svg">Als SVG exportieren</string>
|
||||
<string name="export_as">Exportieren als?</string>
|
||||
<string name="export_csv_comma">CSV mit Kommas</string>
|
||||
<string name="export_csv_semicolon">CSV mit Semikolons</string>
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<string name="view_barcode">Ver código de barras</string>
|
||||
<string name="info">Información</string>
|
||||
<string name="switch_camera">Cambiar cámara</string>
|
||||
<string name="bulk_mode">Scan continuously</string>
|
||||
<string name="history">Historial</string>
|
||||
<string name="preferences">Configuración</string>
|
||||
<string name="show_crop_handle">Show cropping limiter</string>
|
||||
@@ -83,6 +84,8 @@
|
||||
<string name="search_web">Buscar en la web</string>
|
||||
<string name="search_scan">Buscar escaneos</string>
|
||||
<string name="export_to_file">Exportar a archivo</string>
|
||||
<string name="export_png">Export as PNG</string>
|
||||
<string name="export_svg">Export as SVG</string>
|
||||
<string name="export_as">¿Dónde desea exportar?</string>
|
||||
<string name="export_csv_comma">CSV con comas</string>
|
||||
<string name="export_csv_semicolon">CSV con punto y coma</string>
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<string name="view_barcode">Voir le code barre</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="switch_camera">Changer de camera</string>
|
||||
<string name="bulk_mode">Scan continuously</string>
|
||||
<string name="history">Historique</string>
|
||||
<string name="preferences">Preferences</string>
|
||||
<string name="show_crop_handle">Show cropping limiter</string>
|
||||
@@ -83,6 +84,8 @@
|
||||
<string name="search_web">Search the web</string>
|
||||
<string name="search_scan">Search scan</string>
|
||||
<string name="export_to_file">Export to file</string>
|
||||
<string name="export_png">Export as PNG</string>
|
||||
<string name="export_svg">Export as SVG</string>
|
||||
<string name="export_as">Export as?</string>
|
||||
<string name="export_csv_comma">CSV with commas</string>
|
||||
<string name="export_csv_semicolon">CSV with semicolons</string>
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<string name="view_barcode">Vonalkód megtekintése</string>
|
||||
<string name="info">Információ</string>
|
||||
<string name="switch_camera">Kamera átkapcsolása</string>
|
||||
<string name="bulk_mode">Scan continuously</string>
|
||||
<string name="history">Előzmények</string>
|
||||
<string name="preferences">Beállítások</string>
|
||||
<string name="show_crop_handle">Show cropping limiter</string>
|
||||
@@ -83,6 +84,8 @@
|
||||
<string name="search_web">Keresés a weben</string>
|
||||
<string name="search_scan">Search scan</string>
|
||||
<string name="export_to_file">Exportálás fájlba</string>
|
||||
<string name="export_png">Export as PNG</string>
|
||||
<string name="export_svg">Export as SVG</string>
|
||||
<string name="export_as">Export as?</string>
|
||||
<string name="export_csv_comma">CSV with commas</string>
|
||||
<string name="export_csv_semicolon">CSV with semicolons</string>
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
<string name="binary_data">(data biner)</string>
|
||||
<plurals name="barcode_info">
|
||||
<item quantity="other">%1$s, %2$d karakter</item>
|
||||
|
||||
</plurals>
|
||||
<string name="error_correction_level">Level koreksi kesalahan</string>
|
||||
<string name="issue_number">Nomor pemindaian</string>
|
||||
@@ -36,6 +35,7 @@
|
||||
<string name="view_barcode">Lihat barcode</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="switch_camera">Ganti kamera</string>
|
||||
<string name="bulk_mode">Scan continuously</string>
|
||||
<string name="history">Riwayat</string>
|
||||
<string name="preferences">Preferensi</string>
|
||||
<string name="show_crop_handle">Tampilkan pembatas pemotongan</string>
|
||||
@@ -83,6 +83,8 @@
|
||||
<string name="search_web">Cari secara daring</string>
|
||||
<string name="search_scan">Cari pindaian</string>
|
||||
<string name="export_to_file">Ekspor ke berkas</string>
|
||||
<string name="export_png">Ekspor sebagai PNG</string>
|
||||
<string name="export_svg">Ekspor sebagai SVG</string>
|
||||
<string name="export_as">Ekspor sebagai?</string>
|
||||
<string name="export_csv_comma">CSV dengan koma</string>
|
||||
<string name="export_csv_semicolon">CSV dengan titik koma</string>
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<string name="view_barcode">Vedi codice a barre</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="switch_camera">Cambia fotocamera</string>
|
||||
<string name="bulk_mode">Scan continuously</string>
|
||||
<string name="history">Cronologia</string>
|
||||
<string name="preferences">Preferenze</string>
|
||||
<string name="show_crop_handle">Show cropping limiter</string>
|
||||
@@ -83,6 +84,8 @@
|
||||
<string name="search_web">Cerca nel web</string>
|
||||
<string name="search_scan">Cerca scansione</string>
|
||||
<string name="export_to_file">Esporta su file</string>
|
||||
<string name="export_png">Export as PNG</string>
|
||||
<string name="export_svg">Export as SVG</string>
|
||||
<string name="export_as">Esportare come?</string>
|
||||
<string name="export_csv_comma">CSV con virgole</string>
|
||||
<string name="export_csv_semicolon">CSV con punti e virgola</string>
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<string name="view_barcode">Barcode bekijken</string>
|
||||
<string name="info">Informatie</string>
|
||||
<string name="switch_camera">Camera wisselen</string>
|
||||
<string name="bulk_mode">Scan continuously</string>
|
||||
<string name="history">Geschiedenis</string>
|
||||
<string name="preferences">Voorkeuren</string>
|
||||
<string name="show_crop_handle">Show cropping limiter</string>
|
||||
@@ -79,6 +80,8 @@
|
||||
<string name="search_web">Het net doorzoeken</string>
|
||||
<string name="search_scan">Search scan</string>
|
||||
<string name="export_to_file">Naar bestand exporteren</string>
|
||||
<string name="export_png">Export as PNG</string>
|
||||
<string name="export_svg">Export as SVG</string>
|
||||
<string name="export_as">Export as?</string>
|
||||
<string name="export_csv_comma">CSV with commas</string>
|
||||
<string name="export_csv_semicolon">CSV with semicolons</string>
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
<resources>
|
||||
<string name="open_url">Otwórz url</string>
|
||||
<string name="content">Zawartość</string>
|
||||
<string name="copied_to_clipboard">Skopiowano do schowka</string>
|
||||
<string name="copy_to_clipboard">Skopiuj do schowka</string>
|
||||
<string name="info">Informacje</string>
|
||||
<string name="vcard_add">Dodaj do kontaktów</string>
|
||||
<string name="vevent_add">Dodaj do kalendarza</string>
|
||||
<string name="shortcut_preferences">Ustawienia</string>
|
||||
<string name="share">Udostępnij</string>
|
||||
<string name="error_saving_file">Nie można zapisać pliku</string>
|
||||
<string name="clear_history">Wyczyść historię</string>
|
||||
<string name="compose_barcode">Utwórz kod kreskowy</string>
|
||||
<string name="history">Historia</string>
|
||||
<string name="format">Format</string>
|
||||
<string name="export_to_file">Eksportuj do pliku</string>
|
||||
<string name="export_png">Export as PNG</string>
|
||||
<string name="export_svg">Export as SVG</string>
|
||||
<string name="file_name">Nazwa pliku</string>
|
||||
<string name="export_json">Eksportuj jako JSON</string>
|
||||
<string name="pick_search_engine">Wybierz wyszukiwarkę</string>
|
||||
<string name="pick_file">Wybierz plik obrazu</string>
|
||||
<string name="pick_code_to_scan">Wybierz kod do zeskanowania</string>
|
||||
<string name="sms_send">Wyślij SMS</string>
|
||||
<string name="mail_send">Wyślij e-mail</string>
|
||||
<string name="saved_in_downloads">Zapisano w pobranych</string>
|
||||
<string name="separator_ruler">Linijka</string>
|
||||
<string name="scan_code">Skanuj kod</string>
|
||||
<string name="error_correction_level">Poziom korekcji błędów</string>
|
||||
<string name="export_csv_comma">CSV z przecinkami</string>
|
||||
<string name="export_csv_semicolon">CSV z średnikami</string>
|
||||
<string name="shortcut_encode">Stwórz kod kreskowy</string>
|
||||
<string name="error_file_exists">Plik już istnieje</string>
|
||||
<string name="no_barcode_found">Nie znaleziono kodu kreskowego</string>
|
||||
<string name="no_camera_no_fun">Ta aplikacja nie ma żadnego sensu bez dostępu do kamery. Do widzenia.</string>
|
||||
<string name="tel_dial">Wybierz numer</string>
|
||||
<string name="connect_to_wifi">Połącz z Wi-Fi</string>
|
||||
<string name="vcard_failed">Nie można dodać do kontaktów</string>
|
||||
<string name="tel_error">Nie można wybrać numeru</string>
|
||||
<string name="sms_error">Nie można wysłać SMS-a</string>
|
||||
<string name="mail_error">Nie można wysłać e-maila</string>
|
||||
<string name="rotate_image_cw">Obróć obraz zgodnie z ruchem wskazówek zegara</string>
|
||||
<string name="edit_scan">Edytuj etykietę</string>
|
||||
<string name="enter_name_hint">Nazwa skanu</string>
|
||||
<string name="camera_error">Nie można uzyskać dostępu do kamery, spróbuj ponownie</string>
|
||||
<string name="copy_scan">Skopiuj skan</string>
|
||||
<string name="vevent_failed">Nie można dodać do kalendarza</string>
|
||||
<string name="wifi_config_failed">Nie można skonfigurować Wi-Fi</string>
|
||||
<string name="size">Rozmiar w pikselach</string>
|
||||
<string name="export_as">Eksportuj jako?</string>
|
||||
<string name="really_remove_all_scans">Czy naprawdę usunąć wszystkie skany?</string>
|
||||
<string name="really_remove_scan">Czy naprawdę usunąć skan?</string>
|
||||
<string name="really_remove_selected_scans">Czy naprawdę usunąć wybrane skany?</string>
|
||||
<string name="binary_data">(dane binarne)</string>
|
||||
<string name="show_hex_dump">Pokazuj zrzut szesnastkowy</string>
|
||||
<string name="show_meta_data">Pokazuj metadane</string>
|
||||
<string name="switch_camera">Przełącz aparat</string>
|
||||
<string name="bulk_mode">Scan continuously</string>
|
||||
<string name="toggle_flash">Przełącz lampę błyskową</string>
|
||||
<string name="wifi_added">Wi-Fi dodane</string>
|
||||
<string name="zoom_by_swiping">Przybliżaj kamerę, przesuwając w górę/dół</string>
|
||||
<string name="view_barcode">Wyświetl kod kreskowy</string>
|
||||
<string name="orientation">Orientacja</string>
|
||||
<string name="error_encoding_barcode">Nie można utworzyć kodu kreskowego</string>
|
||||
<string name="otpauth_add">Dodaj 2FA</string>
|
||||
<string name="other_meta_data">Metadane</string>
|
||||
<string name="remove_scan">Usuń skan</string>
|
||||
<string name="separator_line_break">Przerwanie linii</string>
|
||||
<string name="shortcut_decode">Zeskanuj kod kreskowy</string>
|
||||
<string name="use_history">Zapisuj historię skanowania</string>
|
||||
<string name="vibrate">Wibruj po wykryciu</string>
|
||||
<string name="open_immediately">Pomiń kontrolę i natychmiast otwórz zawartość</string>
|
||||
<string name="show_crop_handle">Pokazuj ogranicznik przycinania</string>
|
||||
<string name="search_web">Szukaj w sieci</string>
|
||||
<string name="search_scan">Szukaj skanu</string>
|
||||
<string name="export_database">Eksportuj do bazy danych SQLite</string>
|
||||
<string name="ignore_consecutive_duplicates">Nie zapisuj kolejnych duplikatów</string>
|
||||
<string name="open_with_url">Otwieraj nieznane dane za pomocą adresu URL</string>
|
||||
<string name="copied_password_to_clipboard">Hasło skopiowane do schowka</string>
|
||||
<string name="pdf417_extra_metadata">Metadane PDF417</string>
|
||||
<string name="suggested_price">Sugerowana cena</string>
|
||||
<string name="preferences">Ustawienia</string>
|
||||
<string name="decode_barcode">Dekoduj kod kreskowy</string>
|
||||
<string name="upc_ean_extension">Dodatek UPC EAN</string>
|
||||
<string name="possible_country">Możliwy kraj produkcji</string>
|
||||
<string name="encode">ZAKODUJ</string>
|
||||
<string name="error_no_content">Brak zawartości</string>
|
||||
<string name="try_harder">Zoptymalizuj czytnik pod kątem dokładności, a nie prędkości</string>
|
||||
<string name="cannot_resolve_action">Żadna aplikacja nie może tego otworzyć</string>
|
||||
<string name="width_by_height">%1$d×%2$d</string>
|
||||
<string name="input_content_here">Wprowadź zawartość tutaj</string>
|
||||
<string name="pick_list_separator">Jak oddzielić elementy listy?</string>
|
||||
<string name="enter_name">Wprowadź nazwę opisującą skan</string>
|
||||
<string name="auto_rotate">Rozpoznawaj kody kreskowe 1D pionowo</string>
|
||||
<string name="save_as_file_name">Jak zapisać plik w pobranych?</string>
|
||||
<string name="issue_number">Numer wydania</string>
|
||||
</resources>
|
||||
@@ -36,6 +36,7 @@
|
||||
<string name="view_barcode">Ver código de barras</string>
|
||||
<string name="info">Informações</string>
|
||||
<string name="switch_camera">Trocar câmera</string>
|
||||
<string name="bulk_mode">Scan continuously</string>
|
||||
<string name="history">Histórico</string>
|
||||
<string name="preferences">Preferências</string>
|
||||
<string name="show_crop_handle">Show cropping limiter</string>
|
||||
@@ -83,6 +84,8 @@
|
||||
<string name="search_web">Search the web</string>
|
||||
<string name="search_scan">Search scan</string>
|
||||
<string name="export_to_file">Export to file</string>
|
||||
<string name="export_png">Export as PNG</string>
|
||||
<string name="export_svg">Export as SVG</string>
|
||||
<string name="export_as">Export as?</string>
|
||||
<string name="export_csv_comma">CSV with commas</string>
|
||||
<string name="export_csv_semicolon">CSV with semicolons</string>
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
<string name="content">Текст</string>
|
||||
<string name="binary_data">(двоичные данные)</string>
|
||||
<plurals name="barcode_info">
|
||||
<item quantity="zero">%2$d символов, %1$s</item>
|
||||
<item quantity="one">%2$d символ, %1$s</item>
|
||||
<item quantity="two">%2$d символа, %1$s</item>
|
||||
<item quantity="few">%2$d символа, %1$s</item>
|
||||
<item quantity="many">%2$d символов, %1$s</item>
|
||||
<item quantity="other">%2$d символов, %1$s</item>
|
||||
@@ -40,6 +38,7 @@
|
||||
<string name="view_barcode">Просмотр штрих-кода</string>
|
||||
<string name="info">Информация</string>
|
||||
<string name="switch_camera">Переключить камеру</string>
|
||||
<string name="bulk_mode">Scan continuously</string>
|
||||
<string name="history">История</string>
|
||||
<string name="preferences">Настройки</string>
|
||||
<string name="show_crop_handle">Show cropping limiter</string>
|
||||
@@ -87,6 +86,8 @@
|
||||
<string name="search_web">Искать в сети</string>
|
||||
<string name="search_scan">Поиск сканирования</string>
|
||||
<string name="export_to_file">Экспортировать в файл</string>
|
||||
<string name="export_png">Export as PNG</string>
|
||||
<string name="export_svg">Export as SVG</string>
|
||||
<string name="export_as">Формат экспортируемого файла</string>
|
||||
<string name="export_csv_comma">CSV (разделитель - запятая)</string>
|
||||
<string name="export_csv_semicolon">CSV (разделитель - точка с запятой)</string>
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
<string name="view_barcode">查看条形码</string>
|
||||
<string name="info">信息</string>
|
||||
<string name="switch_camera">切换摄像头</string>
|
||||
<string name="bulk_mode">Scan continuously</string>
|
||||
<string name="history">历史</string>
|
||||
<string name="preferences">首选项</string>
|
||||
<string name="show_crop_handle">Show cropping limiter</string>
|
||||
@@ -82,6 +83,8 @@
|
||||
<string name="search_web">在互联网中搜索</string>
|
||||
<string name="search_scan">Search scan</string>
|
||||
<string name="export_to_file">导出为CSV文件</string>
|
||||
<string name="export_png">Export as PNG</string>
|
||||
<string name="export_svg">Export as SVG</string>
|
||||
<string name="export_as">Export as?</string>
|
||||
<string name="export_csv_comma">CSV with commas</string>
|
||||
<string name="export_csv_semicolon">CSV with semicolons</string>
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
<string name="view_barcode">檢視條碼</string>
|
||||
<string name="info">資訊</string>
|
||||
<string name="switch_camera">切換相機</string>
|
||||
<string name="bulk_mode">Scan continuously</string>
|
||||
<string name="history">歷史</string>
|
||||
<string name="show_crop_handle">Show cropping limiter</string>
|
||||
<string name="zoom_by_swiping">Zoom camera by swiping up/down</string>
|
||||
@@ -82,6 +83,8 @@
|
||||
<string name="search_web">Search the web</string>
|
||||
<string name="search_scan">Search scan</string>
|
||||
<string name="export_to_file">Export to file</string>
|
||||
<string name="export_png">Export as PNG</string>
|
||||
<string name="export_svg">Export as SVG</string>
|
||||
<string name="export_as">Export as?</string>
|
||||
<string name="export_csv_comma">CSV with commas</string>
|
||||
<string name="export_csv_semicolon">CSV with semicolons</string>
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<string name="view_barcode">View barcode</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="switch_camera">Switch camera</string>
|
||||
<string name="bulk_mode">Scan continuously</string>
|
||||
<string name="history">History</string>
|
||||
<string name="preferences">Preferences</string>
|
||||
<string name="show_crop_handle">Show cropping limiter</string>
|
||||
@@ -83,6 +84,8 @@
|
||||
<string name="search_web">Search the web</string>
|
||||
<string name="search_scan">Search scan</string>
|
||||
<string name="export_to_file">Export to file</string>
|
||||
<string name="export_png">Export as PNG</string>
|
||||
<string name="export_svg">Export as SVG</string>
|
||||
<string name="export_as">Export as?</string>
|
||||
<string name="export_csv_comma">CSV with commas</string>
|
||||
<string name="export_csv_semicolon">CSV with semicolons</string>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
#pragma version(1)
|
||||
#pragma rs java_package_name(de.markusfisch.android.binaryeye.renderscript)
|
||||
#pragma rs java_package_name(de.markusfisch.android.binaryeye.rs)
|
||||
#pragma rs_fp_relaxed
|
||||
|
||||
rs_allocation inImage;
|
||||
int inWidth;
|
||||
int inHeight;
|
||||
|
||||
uchar RS_KERNEL rotate90(uchar in, uint32_t x, uint32_t y) {
|
||||
uchar RS_KERNEL rotate90(const uchar in, uint32_t x, uint32_t y) {
|
||||
const uchar *out = rsGetElementAt(inImage, y, inHeight - 1 - x);
|
||||
return *out;
|
||||
}
|
||||
|
||||
uchar RS_KERNEL rotate180(uchar in, uint32_t x, uint32_t y) {
|
||||
uchar RS_KERNEL rotate180(const uchar in, uint32_t x, uint32_t y) {
|
||||
const uchar *out = rsGetElementAt(
|
||||
inImage,
|
||||
inWidth - 1 - x,
|
||||
@@ -19,7 +19,7 @@ uchar RS_KERNEL rotate180(uchar in, uint32_t x, uint32_t y) {
|
||||
return *out;
|
||||
}
|
||||
|
||||
uchar RS_KERNEL rotate270(uchar in, uint32_t x, uint32_t y) {
|
||||
uchar RS_KERNEL rotate270(const uchar in, uint32_t x, uint32_t y) {
|
||||
const uchar *out = rsGetElementAt(inImage, inWidth - 1 - y, x);
|
||||
return *out;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
* Export SQLite database
|
||||
* Improve order of preferences
|
||||
* Update Italian translation
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
* Update Russian translation
|
||||
* Force compat mode if native RenderScript crashed
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
* Share history as CSV and JSON too
|
||||
* Export history in JSON format
|
||||
* Update Indonesian translation
|
||||
* Fix initializing of RenderScript automatically
|
||||
* Fix highlighting after landscape to landscape rotations
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
* Add a setting to enable reading of vertical 1D barcodes
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
* Add a handle to define a region of interest
|
||||
* Add a setting to show/hide cropping limiter
|
||||
* Add support for VCALENDAR types
|
||||
* Add spanish translation
|
||||
* Add copy to clipboard button to context menu in history listing
|
||||
* Make history actions work on current listing only
|
||||
* Keep camera selection over orientation changes
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
* Improve usability of cropping limiter
|
||||
* Draw round corners around region of interest
|
||||
* Update Indonesian translation
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
* Stop detection while region of interest is modified
|
||||
* Fix resetting region of interest
|
||||
* Add Polish translation
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M24 24H0V0h24v24z" fill="none"/>
|
||||
<path d="M 4.413 15.067 L 3.533 15.067 L 3.533 16.997 L 2.163 16.997 L 2.163 11.307 L 4.433 11.307 C 4.866 11.307 5.25 11.387 5.583 11.547 C 5.923 11.707 6.183 11.937 6.363 12.237 C 6.55 12.53 6.643 12.864 6.643 13.237 C 6.643 13.79 6.443 14.234 6.043 14.567 C 5.643 14.9 5.1 15.067 4.413 15.067 Z M 3.533 12.367 L 3.533 14.007 L 4.433 14.007 C 4.7 14.007 4.903 13.94 5.043 13.807 C 5.183 13.674 5.253 13.487 5.253 13.247 C 5.253 12.98 5.18 12.767 5.033 12.607 C 4.893 12.447 4.696 12.367 4.443 12.367 L 3.533 12.367 ZM 12.12 11.307 L 12.12 16.997 L 10.76 16.997 L 8.74 13.457 L 8.74 16.997 L 7.37 16.997 L 7.37 11.307 L 8.74 11.307 L 10.75 14.847 L 10.75 11.307 L 12.12 11.307 ZM 17.647 13.957 L 17.647 16.297 C 17.434 16.53 17.124 16.717 16.717 16.857 C 16.31 17.004 15.864 17.077 15.377 17.077 C 14.637 17.077 14.044 16.847 13.597 16.387 C 13.15 15.934 12.91 15.3 12.877 14.487 L 12.877 13.997 C 12.877 13.437 12.977 12.947 13.177 12.527 C 13.37 12.114 13.654 11.794 14.027 11.567 C 14.394 11.34 14.82 11.227 15.307 11.227 C 16.014 11.227 16.564 11.39 16.957 11.717 C 17.344 12.037 17.57 12.52 17.637 13.167 L 16.317 13.167 C 16.27 12.847 16.17 12.62 16.017 12.487 C 15.857 12.354 15.634 12.287 15.347 12.287 C 15.007 12.287 14.744 12.43 14.557 12.717 C 14.364 13.01 14.267 13.427 14.267 13.967 L 14.267 14.317 C 14.267 14.884 14.364 15.31 14.557 15.597 C 14.75 15.877 15.057 16.017 15.477 16.017 C 15.83 16.017 16.094 15.94 16.267 15.787 L 16.267 14.897 L 15.317 14.897 L 15.317 13.957 L 17.647 13.957 Z" transform="matrix(1, 0, 0, 1, 0, 0)" style="white-space: pre;"/>
|
||||
<path d="M 20.373 6.135 L 17.614 8.888 L 19.684 8.888 L 19.684 11.809 L 21.063 11.809 L 21.063 8.888 L 23.133 8.888 L 20.373 6.135 Z" style="" transform="matrix(0.707107, 0.707107, -0.707107, 0.707107, 12.311422, -11.778402)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M24 24H0V0h24v24z" fill="none"/>
|
||||
<path d="M 5.45 15.487 C 5.45 15.287 5.38 15.13 5.24 15.017 C 5.1 14.904 4.85 14.787 4.49 14.667 C 4.137 14.547 3.847 14.43 3.62 14.317 C 2.88 13.957 2.51 13.46 2.51 12.827 C 2.51 12.514 2.603 12.237 2.79 11.997 C 2.97 11.757 3.23 11.57 3.57 11.437 C 3.903 11.297 4.28 11.227 4.7 11.227 C 5.107 11.227 5.473 11.3 5.8 11.447 C 6.127 11.594 6.377 11.804 6.55 12.077 C 6.73 12.344 6.82 12.65 6.82 12.997 L 5.46 12.997 C 5.46 12.764 5.387 12.584 5.24 12.457 C 5.1 12.33 4.91 12.267 4.67 12.267 C 4.423 12.267 4.23 12.32 4.09 12.427 C 3.95 12.534 3.88 12.67 3.88 12.837 C 3.88 12.984 3.957 13.117 4.11 13.237 C 4.263 13.357 4.537 13.48 4.93 13.607 C 5.323 13.734 5.647 13.87 5.9 14.017 C 6.513 14.37 6.82 14.857 6.82 15.477 C 6.82 15.977 6.633 16.367 6.26 16.647 C 5.887 16.934 5.373 17.077 4.72 17.077 C 4.26 17.077 3.843 16.994 3.47 16.827 C 3.097 16.66 2.813 16.434 2.62 16.147 C 2.433 15.86 2.34 15.53 2.34 15.157 L 3.72 15.157 C 3.72 15.457 3.797 15.68 3.95 15.827 C 4.11 15.974 4.367 16.047 4.72 16.047 C 4.947 16.047 5.127 15.997 5.26 15.897 C 5.387 15.797 5.45 15.66 5.45 15.487 ZM 8.608 11.307 L 9.738 15.447 L 10.868 11.307 L 12.398 11.307 L 10.488 16.997 L 8.988 16.997 L 7.088 11.307 L 8.608 11.307 ZM 17.422 13.957 L 17.422 16.297 C 17.209 16.53 16.899 16.717 16.492 16.857 C 16.085 17.004 15.639 17.077 15.152 17.077 C 14.412 17.077 13.819 16.847 13.372 16.387 C 12.925 15.934 12.685 15.3 12.652 14.487 L 12.652 13.997 C 12.652 13.437 12.752 12.947 12.952 12.527 C 13.145 12.114 13.429 11.794 13.802 11.567 C 14.169 11.34 14.595 11.227 15.082 11.227 C 15.789 11.227 16.339 11.39 16.732 11.717 C 17.119 12.037 17.345 12.52 17.412 13.167 L 16.092 13.167 C 16.045 12.847 15.945 12.62 15.792 12.487 C 15.632 12.354 15.409 12.287 15.122 12.287 C 14.782 12.287 14.519 12.43 14.332 12.717 C 14.139 13.01 14.042 13.427 14.042 13.967 L 14.042 14.317 C 14.042 14.884 14.139 15.31 14.332 15.597 C 14.525 15.877 14.832 16.017 15.252 16.017 C 15.605 16.017 15.869 15.94 16.042 15.787 L 16.042 14.897 L 15.092 14.897 L 15.092 13.957 L 17.422 13.957 Z" transform="matrix(1, 0, 0, 1, 0, 0)" style="white-space: pre;"/>
|
||||
<path d="M 19.58 6.075 L 16.821 8.828 L 18.891 8.828 L 18.891 11.749 L 20.27 11.749 L 20.27 8.828 L 22.34 8.828 L 19.58 6.075 Z" style="" transform="matrix(0.707107, 0.707107, -0.707107, 0.707107, 12.036731, -11.23524)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |