Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af528df448 | ||
|
|
a9b5771d57 | ||
|
|
a161e8fc0f | ||
|
|
ec17d07695 | ||
|
|
be2fc2f559 | ||
|
|
de3aa752ff | ||
|
|
411cbb243f | ||
|
|
f76c1521fe | ||
|
|
9b9224b100 | ||
|
|
62d3fd7e94 | ||
|
|
acdac4ca30 | ||
|
|
221cf11576 | ||
|
|
d34fceb48f | ||
|
|
4cc4cef4ca | ||
|
|
969ffd3d0a | ||
|
|
81a505c752 | ||
|
|
eafcf0dba6 | ||
|
|
0539468d3d | ||
|
|
5e9361ac77 | ||
|
|
54fb31d6a5 | ||
|
|
0f7da91e77 | ||
|
|
13bbb3e435 | ||
|
|
e318385032 | ||
|
|
9b1445efb5 | ||
|
|
9a74098f11 | ||
|
|
e22d242248 | ||
|
|
77d419aa7b | ||
|
|
b2fbdceee4 | ||
|
|
8a625dc5d0 | ||
|
|
6d9c6aa53f | ||
|
|
6b03935b9a | ||
|
|
fcac0add8b | ||
|
|
f63b78cc28 | ||
|
|
f34194c5b5 | ||
|
|
f2a22d2312 | ||
|
|
746bcc0706 | ||
|
|
2525b693c7 | ||
|
|
ba9970eb4d | ||
|
|
0495985518 | ||
|
|
b662ba9307 | ||
|
|
c4fe713b6d | ||
|
|
3609c377d2 |
@@ -1,5 +1,24 @@
|
||||
# Change Log
|
||||
|
||||
## 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
|
||||
* Update Indonesian translation
|
||||
|
||||
## 1.30.0
|
||||
* 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
|
||||
|
||||
## 1.29.0
|
||||
* Add a setting to enable reading of vertical 1D barcodes
|
||||
|
||||
|
||||
+2
-2
@@ -9,8 +9,8 @@ android {
|
||||
minSdkVersion 9
|
||||
targetSdkVersion sdk_version
|
||||
|
||||
versionCode 65
|
||||
versionName '1.29.0'
|
||||
versionCode 68
|
||||
versionName '1.31.0'
|
||||
|
||||
// it's recommended to set this value to the lowest API level
|
||||
// able to provide all the functionality
|
||||
|
||||
@@ -34,7 +34,8 @@ class PreprocessorTest {
|
||||
val preprocessor = Preprocessor(
|
||||
InstrumentationRegistry.getTargetContext(),
|
||||
frameWidth.toInt(),
|
||||
frameHeight.toInt()
|
||||
frameHeight.toInt(),
|
||||
null
|
||||
)
|
||||
val outWidth: Int
|
||||
val outHeight: Int
|
||||
|
||||
@@ -6,17 +6,25 @@ object VTypeParser {
|
||||
private const val BACKSLASH_R_LEGACY =
|
||||
"(?:\u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029])"
|
||||
private val vTypeRegex =
|
||||
"""^BEGIN:(VCARD|VEVENT)(?:$BACKSLASH_R_LEGACY.+?:[\s\S]+?)+?${BACKSLASH_R_LEGACY}END:\1$BACKSLASH_R_LEGACY?$""".toRegex(
|
||||
"""^BEGIN:(V.+)(?:$BACKSLASH_R_LEGACY.+?:[\s\S]+?)+?${BACKSLASH_R_LEGACY}END:\1$BACKSLASH_R_LEGACY?$""".toRegex(
|
||||
RegexOption.IGNORE_CASE
|
||||
)
|
||||
private val propertyRegex = """^(.+?):([\s\S]*?)$""".toRegex(RegexOption.MULTILINE)
|
||||
private val propertyRegex = """^(.+?):([\s\S]*?)$""".toRegex(
|
||||
RegexOption.MULTILINE
|
||||
)
|
||||
|
||||
fun parseVType(data: String): String? =
|
||||
vTypeRegex.matchEntire(data)?.groupValues?.get(1)?.toUpperCase(Locale.US)
|
||||
fun parseVType(data: String): String? = vTypeRegex.matchEntire(
|
||||
data
|
||||
)?.groupValues?.get(1)?.toUpperCase(Locale.US)
|
||||
|
||||
fun parseMap(data: String): Map<String, List<VTypeProperty>> = propertyRegex.findAll(data).map {
|
||||
fun parseMap(
|
||||
data: String
|
||||
): Map<String, List<VTypeProperty>> = propertyRegex.findAll(data).map {
|
||||
it.groupValues[1].split(';').let { typeAndInfo ->
|
||||
typeAndInfo[0] to VTypeProperty(typeAndInfo.drop(1), it.groupValues[2])
|
||||
typeAndInfo[0] to VTypeProperty(
|
||||
typeAndInfo.drop(1),
|
||||
it.groupValues[2]
|
||||
)
|
||||
}
|
||||
}.groupBy({ it.first.toUpperCase(Locale.US) }) { it.second }
|
||||
}
|
||||
|
||||
+5
-2
@@ -21,8 +21,11 @@ object VEventAction : IntentAction() {
|
||||
get() = R.string.vevent_failed
|
||||
|
||||
override fun canExecuteOn(data: ByteArray): Boolean {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH
|
||||
&& VTypeParser.parseVType(String(data)) == "VEVENT"
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
return false
|
||||
}
|
||||
val type = VTypeParser.parseVType(String(data))
|
||||
return type == "VEVENT" || type == "VCALENDAR"
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
|
||||
@@ -4,10 +4,13 @@ import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Rect
|
||||
import android.graphics.RectF
|
||||
import android.hardware.Camera
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Vibrator
|
||||
import android.support.design.widget.FloatingActionButton
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.widget.Toolbar
|
||||
import android.support.v8.renderscript.RSRuntimeException
|
||||
@@ -21,11 +24,12 @@ 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
|
||||
import de.markusfisch.android.binaryeye.rs.Preprocessor
|
||||
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
|
||||
import de.markusfisch.android.binaryeye.widget.DetectorView
|
||||
import de.markusfisch.android.binaryeye.widget.toast
|
||||
import de.markusfisch.android.binaryeye.zxing.Zxing
|
||||
@@ -51,14 +55,16 @@ class CameraActivity : AppCompatActivity() {
|
||||
private lateinit var cameraView: CameraView
|
||||
private lateinit var detectorView: DetectorView
|
||||
private lateinit var zoomBar: SeekBar
|
||||
private lateinit var flashFab: View
|
||||
private lateinit var flashFab: FloatingActionButton
|
||||
|
||||
private var preprocessor: Preprocessor? = null
|
||||
private var nativeMapping: Mapping? = null
|
||||
private var rotatedMapping: Mapping? = null
|
||||
private var recreatePreprocessor = false
|
||||
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 fallbackBuffer: IntArray? = null
|
||||
@@ -107,13 +113,21 @@ class CameraActivity : AppCompatActivity() {
|
||||
cameraView = findViewById(R.id.camera_view) as CameraView
|
||||
detectorView = findViewById(R.id.detector_view) as DetectorView
|
||||
zoomBar = findViewById(R.id.zoom) as SeekBar
|
||||
flashFab = findViewById(R.id.flash)
|
||||
flashFab.setOnClickListener { toggleTorchMode() }
|
||||
flashFab = findViewById(R.id.flash) as FloatingActionButton
|
||||
|
||||
initCameraView()
|
||||
initZoomBar()
|
||||
restoreZoom()
|
||||
|
||||
detectorView.onRoiChange = {
|
||||
decoding = false
|
||||
}
|
||||
detectorView.onRoiChanged = {
|
||||
decoding = true
|
||||
recreatePreprocessor = true
|
||||
}
|
||||
detectorView.setPaddingFromWindowInsets()
|
||||
|
||||
if (intent?.action == Intent.ACTION_SEND &&
|
||||
intent.type == "text/plain"
|
||||
) {
|
||||
@@ -171,11 +185,13 @@ class CameraActivity : AppCompatActivity() {
|
||||
super.onRestoreInstanceState(savedState)
|
||||
zoomBar.max = savedState.getInt(ZOOM_MAX)
|
||||
zoomBar.progress = savedState.getInt(ZOOM_LEVEL)
|
||||
frontFacing = savedState.getBoolean(FRONT_FACING)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
outState.putInt(ZOOM_MAX, zoomBar.max)
|
||||
outState.putInt(ZOOM_LEVEL, zoomBar.progress)
|
||||
outState.putBoolean(FRONT_FACING, frontFacing)
|
||||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
@@ -187,7 +203,7 @@ class CameraActivity : AppCompatActivity() {
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.create -> {
|
||||
startActivity(MainActivity.getEncodeIntent(this))
|
||||
createBarcode()
|
||||
true
|
||||
}
|
||||
R.id.history -> {
|
||||
@@ -222,6 +238,10 @@ class CameraActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun createBarcode() {
|
||||
startActivity(MainActivity.getEncodeIntent(this))
|
||||
}
|
||||
|
||||
private fun switchCamera() {
|
||||
closeCamera()
|
||||
frontFacing = frontFacing xor true
|
||||
@@ -328,14 +348,14 @@ class CameraActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
override fun onCameraReady(camera: Camera) {
|
||||
val frameWidth = cameraView.frameWidth
|
||||
val frameHeight = cameraView.frameHeight
|
||||
val frameOrientation = cameraView.frameOrientation
|
||||
var decoding = true
|
||||
// reset preprocessor to make sure it fits the current
|
||||
// reset preprocessor to make sure it always fits the current
|
||||
// frame orientation; important for landscape to landscape
|
||||
// orientation changes
|
||||
resetPreProcessor()
|
||||
val frameWidth = cameraView.frameWidth
|
||||
val frameHeight = cameraView.frameHeight
|
||||
val frameOrientation = cameraView.frameOrientation
|
||||
decoding = true
|
||||
camera.setPreviewCallback { frameData, _ ->
|
||||
if (decoding) {
|
||||
decodeFrame(
|
||||
@@ -406,11 +426,13 @@ class CameraActivity : AppCompatActivity() {
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateFlashFab(available: Boolean) {
|
||||
flashFab.visibility = if (available) {
|
||||
View.GONE
|
||||
private fun updateFlashFab(unavailable: Boolean) {
|
||||
if (unavailable) {
|
||||
flashFab.setImageResource(R.drawable.ic_action_create)
|
||||
flashFab.setOnClickListener { createBarcode() }
|
||||
} else {
|
||||
View.VISIBLE
|
||||
flashFab.setImageResource(R.drawable.ic_action_flash)
|
||||
flashFab.setOnClickListener { toggleTorchMode() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,6 +469,10 @@ class CameraActivity : AppCompatActivity() {
|
||||
isPortrait(frameOrientation)
|
||||
}
|
||||
return try {
|
||||
if (recreatePreprocessor) {
|
||||
resetPreProcessor()
|
||||
recreatePreprocessor = false
|
||||
}
|
||||
val pp = preprocessor ?: createPreprocessorAndMapping(
|
||||
frameWidth,
|
||||
frameHeight,
|
||||
@@ -481,26 +507,86 @@ class CameraActivity : AppCompatActivity() {
|
||||
frameHeight: Int,
|
||||
frameOrientation: Int
|
||||
): Preprocessor {
|
||||
val frameRoi = calculateFrameRect(
|
||||
frameWidth,
|
||||
frameHeight,
|
||||
frameOrientation
|
||||
)
|
||||
val pp = Preprocessor(
|
||||
this,
|
||||
frameWidth,
|
||||
frameHeight
|
||||
frameHeight,
|
||||
frameRoi
|
||||
)
|
||||
val viewRect = if (frameRoi != null) {
|
||||
detectorView.roi
|
||||
} else {
|
||||
cameraView.previewRect
|
||||
}
|
||||
nativeMapping = frameToView(
|
||||
pp.outWidth,
|
||||
pp.outHeight,
|
||||
frameOrientation,
|
||||
cameraView.previewRect
|
||||
viewRect
|
||||
)
|
||||
rotatedMapping = frameToView(
|
||||
pp.outHeight,
|
||||
pp.outWidth,
|
||||
(frameOrientation - 90 + 360) % 360,
|
||||
cameraView.previewRect
|
||||
viewRect
|
||||
)
|
||||
return pp
|
||||
}
|
||||
|
||||
private fun calculateFrameRect(
|
||||
frameWidth: Int,
|
||||
frameHeight: Int,
|
||||
frameOrientation: Int
|
||||
): Rect? {
|
||||
val viewRoi = detectorView.roi
|
||||
return if (
|
||||
viewRoi.width() == 0 ||
|
||||
viewRoi.height() == 0
|
||||
) {
|
||||
null
|
||||
} else {
|
||||
// map ROI in detectorView to cameraView because cameraView may
|
||||
// be larger than the detectorView
|
||||
val previewRect = cameraView.previewRect
|
||||
val previewRoi = Rect(
|
||||
previewRect.left + viewRoi.left,
|
||||
previewRect.top + viewRoi.top,
|
||||
previewRect.left + viewRoi.right,
|
||||
previewRect.top + viewRoi.bottom
|
||||
)
|
||||
val previewRectWidth = previewRect.width().toFloat()
|
||||
val previewRectHeight = previewRect.height().toFloat()
|
||||
val normalizedRoi = RectF(
|
||||
previewRoi.left.toFloat() / previewRectWidth,
|
||||
previewRoi.top.toFloat() / previewRectHeight,
|
||||
previewRoi.right.toFloat() / previewRectWidth,
|
||||
previewRoi.bottom.toFloat() / previewRectHeight
|
||||
)
|
||||
// since the ROI is always centered and symmetric, we don't
|
||||
// need to distinguish between 0 and 180 or 90 and 270 degree
|
||||
if (isPortrait(frameOrientation)) {
|
||||
Rect(
|
||||
(normalizedRoi.top * frameWidth.toFloat()).roundToInt(),
|
||||
(normalizedRoi.left * frameHeight.toFloat()).roundToInt(),
|
||||
(normalizedRoi.bottom * frameWidth.toFloat()).roundToInt(),
|
||||
(normalizedRoi.right * frameHeight.toFloat()).roundToInt()
|
||||
)
|
||||
} else {
|
||||
Rect(
|
||||
(normalizedRoi.left * frameWidth.toFloat()).roundToInt(),
|
||||
(normalizedRoi.top * frameHeight.toFloat()).roundToInt(),
|
||||
(normalizedRoi.right * frameWidth.toFloat()).roundToInt(),
|
||||
(normalizedRoi.bottom * frameHeight.toFloat()).roundToInt()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun postResult(result: Result) {
|
||||
// get mapping for the current rotate value
|
||||
val mapping = getMapping()
|
||||
@@ -525,6 +611,7 @@ class CameraActivity : AppCompatActivity() {
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,7 +3,6 @@ package de.markusfisch.android.binaryeye.activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Point
|
||||
import android.graphics.Rect
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
@@ -70,34 +69,30 @@ class PickActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
val scannedRect = Rect()
|
||||
val scanWithinBounds: () -> Result? = {
|
||||
var result: Result? = null
|
||||
crop(
|
||||
bitmap,
|
||||
cropImageView.normalizedRectInBounds,
|
||||
cropImageView.imageRotation
|
||||
)?.also {
|
||||
scannedRect.set(0, 0, it.width, it.height)
|
||||
result = zxing.decodePositiveNegative(it)
|
||||
}
|
||||
result
|
||||
fun scanWithinBounds() = crop(
|
||||
bitmap,
|
||||
cropImageView.normalizedRectInBounds,
|
||||
cropImageView.imageRotation
|
||||
)?.let {
|
||||
scannedRect.set(0, 0, it.width, it.height)
|
||||
zxing.decodePositiveNegative(it)
|
||||
}
|
||||
|
||||
cropImageView = findViewById(R.id.image) as CropImageView
|
||||
cropImageView.setImageBitmap(bitmap)
|
||||
cropImageView.onScan = {
|
||||
var points: List<Point>? = null
|
||||
scanWithinBounds()?.let {
|
||||
points = mapResult(
|
||||
if (!isFinishing) {
|
||||
vibrator.vibrate()
|
||||
}
|
||||
mapResult(
|
||||
scannedRect.width(),
|
||||
scannedRect.height(),
|
||||
0,
|
||||
cropImageView.getBoundsRect(),
|
||||
it
|
||||
)
|
||||
vibrator.vibrate()
|
||||
}
|
||||
points
|
||||
}
|
||||
cropImageView.doOnApplyWindowInsets { v, insets ->
|
||||
(v as CropImageView).windowInsets.set(insets)
|
||||
|
||||
@@ -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) {
|
||||
@@ -31,6 +31,17 @@ class ScansAdapter(context: Context, cursor: Cursor) :
|
||||
selectedScanPosition = -1
|
||||
}
|
||||
|
||||
fun getSelectedContent() = if (selectedScanPosition < 0) {
|
||||
null
|
||||
} else {
|
||||
val cursor = getItem(selectedScanPosition)
|
||||
if (cursor is Cursor) {
|
||||
cursor.getString(contentIndex)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun newView(
|
||||
context: Context,
|
||||
cursor: Cursor,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,10 +11,4 @@ fun parseAndNormalizeUri(input: String): Uri = Uri.parse(input).let {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) it.normalizeScheme() else it
|
||||
}
|
||||
|
||||
fun ByteArray.toHexString(): String {
|
||||
val hex = StringBuilder()
|
||||
for (i in this.indices) {
|
||||
hex.append(String.format("%02X", this[i]))
|
||||
}
|
||||
return hex.toString()
|
||||
}
|
||||
fun ByteArray.toHexString() = joinToString("") { "%02X".format(it) }
|
||||
|
||||
@@ -45,7 +45,7 @@ private fun firstChildScrolled(listView: AbsListView): Boolean {
|
||||
|
||||
private fun lastChildOutOfView(listView: AbsListView): Boolean {
|
||||
val child = listView.getChildAt(listView.lastVisiblePosition)
|
||||
return child.bottom >= listView.height
|
||||
return child != null && child.bottom >= listView.height
|
||||
}
|
||||
|
||||
fun initSystemBars(activity: AppCompatActivity) {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package de.markusfisch.android.binaryeye.data
|
||||
package de.markusfisch.android.binaryeye.database
|
||||
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
+11
-6
@@ -1,4 +1,4 @@
|
||||
package de.markusfisch.android.binaryeye.data
|
||||
package de.markusfisch.android.binaryeye.database
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
@@ -50,14 +50,19 @@ class Database {
|
||||
""", getWhereArguments(query)
|
||||
)
|
||||
|
||||
private fun getWhereClause(query: String?) = if (query?.isNotEmpty() == true) {
|
||||
"""WHERE $SCANS_CONTENT LIKE ?
|
||||
private fun getWhereClause(
|
||||
query: String?,
|
||||
prefix: String = "WHERE"
|
||||
) = if (query?.isNotEmpty() == true) {
|
||||
"""$prefix $SCANS_CONTENT LIKE ?
|
||||
OR $SCANS_NAME LIKE ?"""
|
||||
} else {
|
||||
""
|
||||
}
|
||||
|
||||
private fun getWhereArguments(query: String?) = if (query?.isNotEmpty() == true) {
|
||||
private fun getWhereArguments(
|
||||
query: String?
|
||||
) = if (query?.isNotEmpty() == true) {
|
||||
val instr = "%$query%"
|
||||
arrayOf(instr, instr)
|
||||
} else {
|
||||
@@ -173,8 +178,8 @@ class Database {
|
||||
db.delete(SCANS, "$SCANS_ID = ?", arrayOf("$id"))
|
||||
}
|
||||
|
||||
fun removeScans() {
|
||||
db.delete(SCANS, null, null)
|
||||
fun removeScans(query: String? = null) {
|
||||
db.delete(SCANS, getWhereClause(query, ""), getWhereArguments(query))
|
||||
}
|
||||
|
||||
fun renameScan(id: Long, name: String) {
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package de.markusfisch.android.binaryeye.data
|
||||
package de.markusfisch.android.binaryeye.database
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Environment
|
||||
+1
-1
@@ -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
-1
@@ -1,4 +1,4 @@
|
||||
package de.markusfisch.android.binaryeye.data
|
||||
package de.markusfisch.android.binaryeye.database
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -71,6 +71,14 @@ class HistoryFragment : Fragment() {
|
||||
): Boolean {
|
||||
val ac = activity ?: return false
|
||||
return when (item.itemId) {
|
||||
R.id.copy_scan -> {
|
||||
scansAdapter?.getSelectedContent()?.let {
|
||||
ac.copyToClipboard(it)
|
||||
ac.toast(R.string.copied_to_clipboard)
|
||||
}
|
||||
closeActionMode()
|
||||
true
|
||||
}
|
||||
R.id.edit_scan -> {
|
||||
scansAdapter?.let {
|
||||
askForName(
|
||||
@@ -102,6 +110,8 @@ class HistoryFragment : Fragment() {
|
||||
private var listViewState: Parcelable? = null
|
||||
private var actionMode: ActionMode? = null
|
||||
private var filter: String? = null
|
||||
private var clearListMenuItem: MenuItem? = null
|
||||
private var exportHistoryMenuItem: MenuItem? = null
|
||||
|
||||
override fun onCreate(state: Bundle?) {
|
||||
super.onCreate(state)
|
||||
@@ -173,6 +183,8 @@ class HistoryFragment : Fragment() {
|
||||
inflater.inflate(R.menu.fragment_history, menu)
|
||||
initSearchView(menu.findItem(R.id.search))
|
||||
menu.setGroupVisible(R.id.scans_available, scansAdapter?.count != 0)
|
||||
clearListMenuItem = menu.findItem(R.id.clear)
|
||||
exportHistoryMenuItem = menu.findItem(R.id.export_history)
|
||||
}
|
||||
|
||||
private fun initSearchView(item: MenuItem?) {
|
||||
@@ -205,7 +217,7 @@ class HistoryFragment : Fragment() {
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.clear -> {
|
||||
askToRemoveAllScans(context)
|
||||
askToRemoveScans(context)
|
||||
true
|
||||
}
|
||||
R.id.export_history -> {
|
||||
@@ -243,6 +255,7 @@ class HistoryFragment : Fragment() {
|
||||
}
|
||||
ActivityCompat.invalidateOptionsMenu(ac)
|
||||
}
|
||||
enableMenuItems(hasScans)
|
||||
fab.visibility = if (hasScans) {
|
||||
View.VISIBLE
|
||||
} else {
|
||||
@@ -261,6 +274,11 @@ class HistoryFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun enableMenuItems(enabled: Boolean) {
|
||||
clearListMenuItem?.isEnabled = enabled
|
||||
exportHistoryMenuItem?.isEnabled = enabled
|
||||
}
|
||||
|
||||
private fun closeActionMode() {
|
||||
unlockStatusBarColor()
|
||||
scansAdapter?.clearSelection()
|
||||
@@ -321,11 +339,17 @@ class HistoryFragment : Fragment() {
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun askToRemoveAllScans(context: Context) {
|
||||
private fun askToRemoveScans(context: Context) {
|
||||
AlertDialog.Builder(context)
|
||||
.setMessage(R.string.really_remove_all_scans)
|
||||
.setMessage(
|
||||
if (filter == null) {
|
||||
R.string.really_remove_all_scans
|
||||
} else {
|
||||
R.string.really_remove_selected_scans
|
||||
}
|
||||
)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
db.removeScans()
|
||||
db.removeScans(filter)
|
||||
updateAndClearFilter()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
|
||||
@@ -12,6 +12,7 @@ import de.markusfisch.android.binaryeye.app.prefs
|
||||
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
|
||||
|
||||
class PreferencesFragment : Fragment() {
|
||||
private lateinit var showCropHandleSwitch: SwitchCompat
|
||||
private lateinit var zoomBySwipingSwitch: SwitchCompat
|
||||
private lateinit var autoResizeSwitch: SwitchCompat
|
||||
private lateinit var tryHarderSwitch: SwitchCompat
|
||||
@@ -36,6 +37,11 @@ class PreferencesFragment : Fragment() {
|
||||
false
|
||||
)
|
||||
|
||||
showCropHandleSwitch = view.findViewById(R.id.show_crop_handle)
|
||||
if (prefs.showCropHandle) {
|
||||
showCropHandleSwitch.toggle()
|
||||
}
|
||||
|
||||
zoomBySwipingSwitch = view.findViewById(R.id.zoom_by_swiping)
|
||||
if (prefs.zoomBySwiping) {
|
||||
zoomBySwipingSwitch.toggle()
|
||||
@@ -93,6 +99,7 @@ class PreferencesFragment : Fragment() {
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
prefs.showCropHandle = showCropHandleSwitch.isChecked
|
||||
prefs.zoomBySwiping = zoomBySwipingSwitch.isChecked
|
||||
prefs.autoRotate = autoResizeSwitch.isChecked
|
||||
prefs.tryHarder = tryHarderSwitch.isChecked
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package de.markusfisch.android.binaryeye.graphics
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.DashPathEffect
|
||||
import android.graphics.Paint
|
||||
import android.support.v4.content.ContextCompat
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
|
||||
fun Context.getDashedBorderPaint() = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
val dp = resources.displayMetrics.density
|
||||
color = ContextCompat.getColor(applicationContext, R.color.crop_bound)
|
||||
style = Paint.Style.STROKE
|
||||
strokeWidth = dp * 2f
|
||||
pathEffect = DashPathEffect(floatArrayOf(10f * dp, 10f * dp), 0f)
|
||||
}
|
||||
+2
-2
@@ -7,14 +7,14 @@ import android.graphics.Point
|
||||
import android.support.v4.content.ContextCompat
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
|
||||
class Candidates(context: Context) {
|
||||
class Dots(context: Context) {
|
||||
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
private val radius = 8f * context.resources.displayMetrics.density
|
||||
|
||||
init {
|
||||
paint.color = ContextCompat.getColor(
|
||||
context,
|
||||
R.color.candidate
|
||||
R.color.dot
|
||||
)
|
||||
paint.style = Paint.Style.FILL
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package de.markusfisch.android.binaryeye.graphics
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
|
||||
fun Resources.getBitmapFromDrawable(
|
||||
resId: Int
|
||||
): Bitmap = getBitmapFromDrawable(getDrawableCompat(resId))
|
||||
|
||||
fun Resources.getDrawableCompat(
|
||||
resId: Int
|
||||
): Drawable = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
getDrawable(resId, null)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
getDrawable(resId)
|
||||
}
|
||||
|
||||
private fun getBitmapFromDrawable(drawable: Drawable): Bitmap {
|
||||
if (drawable is BitmapDrawable) {
|
||||
return drawable.bitmap
|
||||
}
|
||||
val bitmap = Bitmap.createBitmap(
|
||||
drawable.intrinsicWidth,
|
||||
drawable.intrinsicHeight,
|
||||
Bitmap.Config.ARGB_8888
|
||||
)
|
||||
val canvas = Canvas(bitmap)
|
||||
drawable.setBounds(0, 0, canvas.width, canvas.height)
|
||||
drawable.draw(canvas)
|
||||
return bitmap
|
||||
}
|
||||
@@ -36,7 +36,7 @@ fun frameToView(
|
||||
viewRect.top
|
||||
)
|
||||
|
||||
inline fun isPortrait(orientation: Int) = orientation == 90 || orientation == 270
|
||||
fun isPortrait(orientation: Int) = orientation == 90 || orientation == 270
|
||||
|
||||
data class Mapping(
|
||||
val frameWidth: Int,
|
||||
|
||||
@@ -7,6 +7,11 @@ import android.preference.PreferenceManager
|
||||
class Preferences {
|
||||
lateinit var preferences: SharedPreferences
|
||||
|
||||
var showCropHandle = true
|
||||
set(value) {
|
||||
apply(SHOW_CROP_HANDLE, value)
|
||||
field = value
|
||||
}
|
||||
var zoomBySwiping = true
|
||||
set(value) {
|
||||
apply(ZOOM_BY_SWIPING, value)
|
||||
@@ -14,12 +19,16 @@ class Preferences {
|
||||
}
|
||||
var autoRotate = false
|
||||
set(value) {
|
||||
apply(AUTO_ROTATE, value)
|
||||
// immediately save this setting before it shouldn't change
|
||||
// on the fly while scanning
|
||||
commit(AUTO_ROTATE, value)
|
||||
field = value
|
||||
}
|
||||
var tryHarder = false
|
||||
set(value) {
|
||||
apply(TRY_HARDER, value)
|
||||
// immediately save this setting because it's only ever read
|
||||
// before the camera is opened
|
||||
commit(TRY_HARDER, value)
|
||||
field = value
|
||||
}
|
||||
var vibrate = true
|
||||
@@ -76,6 +85,7 @@ class Preferences {
|
||||
}
|
||||
|
||||
fun update() {
|
||||
showCropHandle = preferences.getBoolean(SHOW_CROP_HANDLE, showCropHandle)
|
||||
zoomBySwiping = preferences.getBoolean(ZOOM_BY_SWIPING, zoomBySwiping)
|
||||
autoRotate = preferences.getBoolean(AUTO_ROTATE, autoRotate)
|
||||
tryHarder = preferences.getBoolean(TRY_HARDER, tryHarder)
|
||||
@@ -121,6 +131,7 @@ class Preferences {
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val SHOW_CROP_HANDLE = "show_crop_handle"
|
||||
const val ZOOM_BY_SWIPING = "zoom_by_swiping"
|
||||
const val AUTO_ROTATE = "auto_rotate"
|
||||
const val TRY_HARDER = "try_harder"
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
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
|
||||
@@ -10,7 +12,8 @@ private const val SCALE_FACTOR = .75f
|
||||
class Preprocessor(
|
||||
context: Context,
|
||||
width: Int,
|
||||
height: Int
|
||||
height: Int,
|
||||
private val roi: Rect?
|
||||
) {
|
||||
var outWidth = 0
|
||||
var outHeight = 0
|
||||
@@ -21,6 +24,8 @@ class Preprocessor(
|
||||
|
||||
private var yuvType: Type? = null
|
||||
private var yuvAlloc: Allocation? = null
|
||||
private var roiType: Type? = null
|
||||
private var roiAlloc: Allocation? = null
|
||||
private var resizedType: Type? = null
|
||||
private var resizedAlloc: Allocation? = null
|
||||
private var rotatedType: Type? = null
|
||||
@@ -39,8 +44,36 @@ class Preprocessor(
|
||||
Allocation.USAGE_SCRIPT
|
||||
)
|
||||
|
||||
outWidth = (width * SCALE_FACTOR).roundToInt()
|
||||
outHeight = (height * SCALE_FACTOR).roundToInt()
|
||||
if (roi != null) {
|
||||
val roiWidth = roi.width()
|
||||
val roiHeight = roi.height()
|
||||
|
||||
roiType = Type.createXY(
|
||||
rs,
|
||||
Element.U8(rs),
|
||||
roiWidth,
|
||||
roiHeight
|
||||
)
|
||||
roiAlloc = Allocation.createTyped(
|
||||
rs,
|
||||
roiType,
|
||||
Allocation.USAGE_SCRIPT
|
||||
)
|
||||
|
||||
outWidth = (roiWidth * SCALE_FACTOR).roundToInt()
|
||||
outHeight = (roiHeight * SCALE_FACTOR).roundToInt()
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
resizedType = Type.createXY(
|
||||
rs,
|
||||
@@ -72,6 +105,10 @@ class Preprocessor(
|
||||
yuvType = null
|
||||
yuvAlloc?.destroy()
|
||||
yuvAlloc = null
|
||||
roiType?.destroy()
|
||||
roiType = null
|
||||
roiAlloc?.destroy()
|
||||
roiAlloc = null
|
||||
resizedType?.destroy()
|
||||
resizedType = null
|
||||
resizedAlloc?.destroy()
|
||||
@@ -105,7 +142,19 @@ class Preprocessor(
|
||||
|
||||
private fun resize(frame: ByteArray) {
|
||||
yuvAlloc?.copyFrom(frame)
|
||||
resizeScript.setInput(yuvAlloc)
|
||||
resizeScript.setInput(
|
||||
if (roi != null) {
|
||||
roiAlloc?.copy2DRangeFrom(
|
||||
0, 0,
|
||||
roi.width(), roi.height(),
|
||||
yuvAlloc,
|
||||
roi.left, roi.top
|
||||
)
|
||||
roiAlloc
|
||||
} else {
|
||||
yuvAlloc
|
||||
}
|
||||
)
|
||||
resizeScript.forEach_bicubic(resizedAlloc)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package de.markusfisch.android.binaryeye.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.*
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Point
|
||||
import android.graphics.Rect
|
||||
import android.graphics.RectF
|
||||
import android.util.AttributeSet
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
import de.markusfisch.android.binaryeye.graphics.Candidates
|
||||
import de.markusfisch.android.binaryeye.graphics.Dots
|
||||
import de.markusfisch.android.binaryeye.graphics.getDashedBorderPaint
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class CropImageView(context: Context, attr: AttributeSet) :
|
||||
@@ -14,25 +16,20 @@ class CropImageView(context: Context, attr: AttributeSet) :
|
||||
|
||||
var onScan: (() -> List<Point>?)? = null
|
||||
|
||||
private val candidates = Candidates(context)
|
||||
private val boundsPaint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
private val dots = Dots(context)
|
||||
private val boundsPaint = context.getDashedBorderPaint()
|
||||
private val lastMappedRect = RectF()
|
||||
private val dp = context.resources.displayMetrics.density
|
||||
private val padding: Int = (dp * 24f).roundToInt()
|
||||
private val padding: Int = (24f * context.resources.displayMetrics.density).roundToInt()
|
||||
private val onScanRunnable = Runnable {
|
||||
onScan?.invoke()?.also {
|
||||
candidatePoints = it
|
||||
onScan?.invoke()?.let {
|
||||
resultPoints = it
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
private var candidatePoints: List<Point>? = null
|
||||
private var resultPoints: List<Point>? = null
|
||||
|
||||
init {
|
||||
boundsPaint.color = ContextCompat.getColor(context, R.color.crop_bound)
|
||||
boundsPaint.style = Paint.Style.STROKE
|
||||
boundsPaint.strokeWidth = dp * 2f
|
||||
boundsPaint.pathEffect = DashPathEffect(floatArrayOf(dp * 10f, dp * 10f), 0f)
|
||||
scaleType = ScaleType.CENTER_CROP
|
||||
}
|
||||
|
||||
@@ -85,10 +82,10 @@ class CropImageView(context: Context, attr: AttributeSet) :
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
canvas.drawRect(bounds, boundsPaint)
|
||||
candidatePoints?.let {
|
||||
candidates.draw(canvas, it)
|
||||
resultPoints?.let {
|
||||
dots.draw(canvas, it)
|
||||
}
|
||||
candidatePoints = null
|
||||
resultPoints = null
|
||||
val mr = mappedRect ?: return
|
||||
if (mr != lastMappedRect) {
|
||||
removeCallbacks(onScanRunnable)
|
||||
|
||||
@@ -1,20 +1,66 @@
|
||||
package de.markusfisch.android.binaryeye.widget
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Point
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.*
|
||||
import android.os.Build
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import de.markusfisch.android.binaryeye.graphics.Candidates
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
import de.markusfisch.android.binaryeye.app.prefs
|
||||
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.min
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class DetectorView : View {
|
||||
private val candidates = Candidates(context)
|
||||
val roi = Rect()
|
||||
|
||||
var onRoiChange: (() -> Unit)? = null
|
||||
var onRoiChanged: (() -> Unit)? = null
|
||||
|
||||
private val dots = Dots(context)
|
||||
private val invalidateRunnable: Runnable = Runnable {
|
||||
marks = null
|
||||
invalidate()
|
||||
}
|
||||
private val roiPaint = context.getDashedBorderPaint()
|
||||
private val handleBitmap = resources.getBitmapFromDrawable(
|
||||
R.drawable.button_crop
|
||||
)
|
||||
private val handleXRadius = handleBitmap.width / 2
|
||||
private val handleYRadius = handleBitmap.height / 2
|
||||
private val handleHome = Point()
|
||||
private val handlePos = Point(-1, -1)
|
||||
private val center = Point()
|
||||
private val touchDown = Point()
|
||||
private val distToFull: Int
|
||||
private val minMoveThresholdSq: Int
|
||||
private val cornerRadius: Int
|
||||
private val fabHeight: Int
|
||||
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 shadeColor = 0
|
||||
|
||||
init {
|
||||
val dp = context.resources.displayMetrics.density
|
||||
distToFull = (24f * dp).roundToInt()
|
||||
val minMoveThreshold = (8f * dp).roundToInt()
|
||||
minMoveThresholdSq = minMoveThreshold * minMoveThreshold
|
||||
cornerRadius = (8f * dp).roundToInt()
|
||||
fabHeight = (92f * dp).roundToInt()
|
||||
padding = (20f * dp).roundToInt()
|
||||
isSaveEnabled = true
|
||||
}
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet) :
|
||||
super(context, attrs)
|
||||
@@ -29,10 +75,257 @@ class DetectorView : View {
|
||||
postDelayed(invalidateRunnable, 500)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(): Parcelable? {
|
||||
if (!handleMoved) {
|
||||
return super.onSaveInstanceState()
|
||||
}
|
||||
return SavedState(super.onSaveInstanceState()).apply {
|
||||
savedHandlePos.set(handlePos)
|
||||
savedOrientation = orientation
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
state.superState
|
||||
} else {
|
||||
state
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onTouchEvent(event: MotionEvent?): Boolean {
|
||||
event ?: return super.onTouchEvent(event)
|
||||
val x = event.x.roundToInt()
|
||||
val y = event.y.roundToInt()
|
||||
return when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
if (prefs.showCropHandle) {
|
||||
touchDown.set(x, y)
|
||||
handleGrabbed = abs(x - handlePos.x) < handleXRadius &&
|
||||
abs(y - handlePos.y) < handleYRadius
|
||||
if (handleGrabbed) {
|
||||
onRoiChange?.invoke()
|
||||
}
|
||||
handleGrabbed
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
if (handleGrabbed) {
|
||||
handlePos.set(x, y)
|
||||
if (distSq(handlePos, touchDown) > minMoveThresholdSq) {
|
||||
handleMoved = true
|
||||
}
|
||||
invalidate()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_CANCEL -> {
|
||||
if (handleGrabbed) {
|
||||
snap(x, y)
|
||||
handleGrabbed = false
|
||||
}
|
||||
false
|
||||
}
|
||||
MotionEvent.ACTION_UP -> {
|
||||
if (handleGrabbed) {
|
||||
if (!handleMoved) {
|
||||
handlePos.set(
|
||||
(center.x * 1.5f).roundToInt(),
|
||||
(center.y * 1.25f).roundToInt()
|
||||
)
|
||||
handleMoved = true
|
||||
invalidate()
|
||||
} else {
|
||||
snap(x, y)
|
||||
}
|
||||
onRoiChanged?.invoke()
|
||||
handleGrabbed = false
|
||||
}
|
||||
false
|
||||
}
|
||||
else -> super.onTouchEvent(event)
|
||||
}
|
||||
}
|
||||
|
||||
private fun snap(x: Int, y: Int) {
|
||||
if (abs(x - center.x) < distToFull ||
|
||||
abs(y - center.y) < distToFull
|
||||
) {
|
||||
handlePos.set(handleHome)
|
||||
handleMoved = false
|
||||
roi.set(0, 0, 0, 0)
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||
super.onLayout(changed, left, top, right, bottom)
|
||||
val width = right - left
|
||||
val height = bottom - top
|
||||
center.set(
|
||||
left + (width / 2),
|
||||
top + (height / 2)
|
||||
)
|
||||
handleHome.set(
|
||||
width - handleXRadius - paddingRight - padding,
|
||||
height - handleYRadius - paddingBottom - fabHeight
|
||||
)
|
||||
if (handlePos.x < 0) {
|
||||
handlePos.set(handleHome)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
canvas.drawColor(0, PorterDuff.Mode.CLEAR)
|
||||
if (handleMoved) {
|
||||
drawClip(canvas)
|
||||
}
|
||||
marks?.let {
|
||||
candidates.draw(canvas, it)
|
||||
dots.draw(canvas, it)
|
||||
}
|
||||
if (prefs.showCropHandle) {
|
||||
canvas.drawBitmap(
|
||||
handleBitmap,
|
||||
(handlePos.x - handleXRadius).toFloat(),
|
||||
(handlePos.y - handleYRadius).toFloat(),
|
||||
null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawClip(canvas: Canvas) {
|
||||
val minDist = updateClipRect()
|
||||
if (minDist < 1) {
|
||||
return
|
||||
}
|
||||
// canvas.clipRect() doesn't work reliably below KITKAT
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
val radius = min(minDist / 2, cornerRadius).toFloat()
|
||||
canvas.save()
|
||||
canvas.clipOutPathCompat(
|
||||
calculateRoundedRectPath(
|
||||
roi.left.toFloat(),
|
||||
roi.top.toFloat(),
|
||||
roi.right.toFloat(),
|
||||
roi.bottom.toFloat(),
|
||||
radius,
|
||||
radius
|
||||
)
|
||||
)
|
||||
canvas.drawColor(shadeColor, PorterDuff.Mode.SRC)
|
||||
canvas.restore()
|
||||
} else {
|
||||
canvas.drawRect(roi, roiPaint)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateClipRect(): Int {
|
||||
val dx = abs(handlePos.x - center.x)
|
||||
val dy = abs(handlePos.y - center.y)
|
||||
val d = min(dx, dy)
|
||||
shadeColor = (min(
|
||||
1f,
|
||||
d.toFloat() / distToFull.toFloat()
|
||||
) * 128f).toInt() shl 24
|
||||
roi.set(
|
||||
center.x - dx,
|
||||
center.y - dy,
|
||||
center.x + dx,
|
||||
center.y + dy
|
||||
)
|
||||
return d
|
||||
}
|
||||
|
||||
internal class SavedState : BaseSavedState {
|
||||
val savedHandlePos = Point()
|
||||
|
||||
var savedOrientation = 0
|
||||
|
||||
constructor(superState: Parcelable?) : super(superState)
|
||||
|
||||
private constructor(parcel: Parcel) : super(parcel) {
|
||||
savedHandlePos.set(
|
||||
parcel.readInt(),
|
||||
parcel.readInt()
|
||||
)
|
||||
savedOrientation = parcel.readInt()
|
||||
}
|
||||
|
||||
override fun writeToParcel(out: Parcel, flags: Int) {
|
||||
super.writeToParcel(out, flags)
|
||||
out.writeInt(savedHandlePos.x)
|
||||
out.writeInt(savedHandlePos.y)
|
||||
out.writeInt(savedOrientation)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val CREATOR = object : Parcelable.Creator<SavedState> {
|
||||
override fun createFromParcel(source: Parcel) = SavedState(source)
|
||||
override fun newArray(size: Int): Array<SavedState?> = arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun distSq(a: Point, b: Point): Int {
|
||||
val dx = a.x - b.x
|
||||
val dy = a.y - b.y
|
||||
return dx * dx + dy * dy
|
||||
}
|
||||
|
||||
private fun Point.set(point: Point) {
|
||||
x = point.x
|
||||
y = point.y
|
||||
}
|
||||
|
||||
private fun Canvas.clipOutPathCompat(path: Path) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
clipOutPath(path)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
clipPath(path, Region.Op.DIFFERENCE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateRoundedRectPath(
|
||||
left: Float,
|
||||
top: Float,
|
||||
right: Float,
|
||||
bottom: Float,
|
||||
rx: Float,
|
||||
ry: Float
|
||||
): Path {
|
||||
val width = right - left
|
||||
val height = bottom - top
|
||||
val widthMinusCorners = width - 2 * rx
|
||||
val heightMinusCorners = height - 2 * ry
|
||||
return Path().apply {
|
||||
moveTo(right, top + ry)
|
||||
rQuadTo(0f, -ry, -rx, -ry)
|
||||
rLineTo(-widthMinusCorners, 0f)
|
||||
rQuadTo(-rx, 0f, -rx, ry)
|
||||
rLineTo(0f, heightMinusCorners)
|
||||
rQuadTo(0f, ry, rx, ry)
|
||||
rLineTo(widthMinusCorners, 0f)
|
||||
rQuadTo(rx, 0f, rx, -ry)
|
||||
rLineTo(0f, -heightMinusCorners)
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +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="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>
|
||||
@@ -10,6 +10,12 @@
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp"
|
||||
android:orientation="vertical">
|
||||
<android.support.v7.widget.SwitchCompat
|
||||
android:id="@+id/show_crop_handle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:text="@string/show_crop_handle"/>
|
||||
<android.support.v7.widget.SwitchCompat
|
||||
android:id="@+id/zoom_by_swiping"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
<menu
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:material="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/copy_scan"
|
||||
android:title="@string/copy_scan"
|
||||
android:icon="@drawable/ic_action_copy"
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/edit_scan"
|
||||
android:title="@string/edit_scan"
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
<string name="switch_camera">Kamera wechseln</string>
|
||||
<string name="history">Gespeicherte Codes</string>
|
||||
<string name="preferences">Einstellungen</string>
|
||||
<string name="show_crop_handle">Bereich verkleinern anzeigen</string>
|
||||
<string name="zoom_by_swiping">Hoch/Runter streichen um zu zoomen</string>
|
||||
<string name="auto_rotate">Vertikale Erkennung von 1D Barcodes</string>
|
||||
<string name="try_harder">Suche länger und gründlicher nach Barcodes</string>
|
||||
@@ -50,7 +51,9 @@
|
||||
<string name="open_with_url">URL zum Öffnen unbekannter Daten</string>
|
||||
<string name="really_remove_scan">Code wirklich entfernen?</string>
|
||||
<string name="really_remove_all_scans">Alle Codes entfernen?</string>
|
||||
<string name="really_remove_selected_scans">Alle ausgewählten Codes entfernen?</string>
|
||||
<string name="clear_history">Gespeicherte Codes löschen</string>
|
||||
<string name="copy_scan">Code kopieren</string>
|
||||
<string name="edit_scan">Bezeichnung ändern</string>
|
||||
<string name="remove_scan">Code löschen</string>
|
||||
<string name="enter_name">Geben Sie eine Bezeichnung für den Code ein</string>
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
<resources>
|
||||
<string name="no_camera_no_fun">Esta aplicación no tendría sentido usar sin acceso a la cámara. Adiós.</string>
|
||||
<string name="camera_error">Sin acceso a la cámara, por favor, inténtelo de nuevo</string>
|
||||
<string name="scan_code">Escanear código de barras</string>
|
||||
<string name="compose_barcode">Componer código de barras</string>
|
||||
<string name="decode_barcode">Decodificar código de barras</string>
|
||||
<string name="content">Contenido</string>
|
||||
<string name="binary_data">(binario)</string>
|
||||
<plurals name="barcode_info">
|
||||
<item quantity="one">%2$d caracter, %1$s</item>
|
||||
<item quantity="other">%2$d caracteres, %1$s</item>
|
||||
</plurals>
|
||||
<string name="error_correction_level">Nivel de corrección de errores</string>
|
||||
<string name="issue_number">Número de emisión</string>
|
||||
<string name="orientation">Orientación</string>
|
||||
<string name="other_meta_data">Metadatos</string>
|
||||
<string name="pdf417_extra_metadata">Metadatos PDF417</string>
|
||||
<string name="possible_country">Posible país de fabricación</string>
|
||||
<string name="suggested_price">Precio sugerido</string>
|
||||
<string name="upc_ean_extension">UPC EAN extension</string>
|
||||
<string name="toggle_flash">Activar la linterna</string>
|
||||
<string name="share">Compartir</string>
|
||||
<string name="copy_to_clipboard">Copiar al portapapeles</string>
|
||||
<string name="copied_to_clipboard">Copiado al portapapeles</string>
|
||||
<string name="copied_password_to_clipboard">Contraseña copiada al portapapeles</string>
|
||||
<string name="open_url">Abrir URL</string>
|
||||
<string name="cannot_resolve_action">Ninguna aplicación puede abrir esto</string>
|
||||
<string name="pick_search_engine">Elija un motor de búsqueda</string>
|
||||
<string name="format">Formato</string>
|
||||
<string name="size">Tamaño en píxeles</string>
|
||||
<string name="width_by_height">%1$d×%2$d</string>
|
||||
<string name="input_content_here">Introduzca el contenido aquí</string>
|
||||
<string name="encode">CODIFICAR</string>
|
||||
<string name="error_no_content">Contenido no disponible</string>
|
||||
<string name="error_encoding_barcode">No pudo generar el código de barras</string>
|
||||
<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="history">Historial</string>
|
||||
<string name="preferences">Configuración</string>
|
||||
<string name="show_crop_handle">Show cropping limiter</string>
|
||||
<string name="zoom_by_swiping">Aumento de cámara al deslizar hacia arriba/abajo</string>
|
||||
<string name="auto_rotate">Reconocer los códigos de barras 1D verticalmente</string>
|
||||
<string name="try_harder">Optimizar el lector para la precisión, no la velocidad</string>
|
||||
<string name="vibrate">Vibración en la detección</string>
|
||||
<string name="use_history">Guardar historial de escaneados</string>
|
||||
<string name="ignore_consecutive_duplicates">No guardar duplicados consecutivos</string>
|
||||
<string name="open_immediately">Saltar la inspección y abrir el contenido inmediatamente</string>
|
||||
<string name="show_meta_data">Mostrar metadatos</string>
|
||||
<string name="show_hex_dump">Mostrar volcado hexagonal</string>
|
||||
<string name="open_with_url">Abrir datos desconocidos con URL</string>
|
||||
<string name="really_remove_scan">¿Realmente desea borrar el escaneo seleccionado?</string>
|
||||
<string name="really_remove_all_scans">¿Realmente desea borrar todo lo escaneado?</string>
|
||||
<string name="really_remove_selected_scans">¿Realmente desea borrar los escaneos seleccionados?</string>
|
||||
<string name="clear_history">Borrar historial</string>
|
||||
<string name="copy_scan">Copiar escaneo</string>
|
||||
<string name="edit_scan">Editar etiqueta</string>
|
||||
<string name="remove_scan">Borrar escaneo</string>
|
||||
<string name="enter_name">Introduzca un nombre para describir el escaneo</string>
|
||||
<string name="enter_name_hint">Nombre del escaneo</string>
|
||||
<string name="no_barcode_found">No se encontró ningún código de barras</string>
|
||||
<string name="pick_list_separator">¿Cómo separar los elementos de la lista?</string>
|
||||
<string name="separator_line_break">Línea separadora</string>
|
||||
<string name="separator_ruler">Regla</string>
|
||||
<string name="save_as_file_name">¿Desea guardar el archivo en Descargas?</string>
|
||||
<string name="file_name">Nombre del archivo</string>
|
||||
<string name="error_saving_file">No se puede guardar el archivo</string>
|
||||
<string name="error_file_exists">El archivo ya existe</string>
|
||||
<string name="connect_to_wifi">Conectar a WiFi</string>
|
||||
<string name="wifi_config_failed">No se pudo configurar el WiFi</string>
|
||||
<string name="wifi_added">WiFi añadido</string>
|
||||
<string name="sms_send">Enviar SMS</string>
|
||||
<string name="sms_error">No se puedo enviar SMS</string>
|
||||
<string name="tel_dial">Marcar número</string>
|
||||
<string name="tel_error">No se pudo marcar el número</string>
|
||||
<string name="mail_send">Enviar correo</string>
|
||||
<string name="mail_error">No se pudo enviar correo</string>
|
||||
<string name="vcard_add">Añadir a contactos</string>
|
||||
<string name="vcard_failed">No se pudo añadir a los contactos</string>
|
||||
<string name="vevent_add">Añadir se pudo calendario</string>
|
||||
<string name="vevent_failed">No se pudo añadir al calendario</string>
|
||||
<string name="otpauth_add">Añadir 2FA</string>
|
||||
<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_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>
|
||||
<string name="export_json">Exportar como JSON</string>
|
||||
<string name="export_database">Exportar base de datos SQLite</string>
|
||||
<string name="saved_in_downloads">Guardado en Descargas</string>
|
||||
<string name="rotate_image_cw">Rotar la imagen en el sentido de las agujas del reloj</string>
|
||||
<string name="pick_file">Seleccionar archivo de imagen</string>
|
||||
<string name="pick_code_to_scan">Escoge el código para escanear</string>
|
||||
<string name="shortcut_decode">Escanear un código de barras</string>
|
||||
<string name="shortcut_encode">Crear un código de barras</string>
|
||||
<string name="shortcut_preferences">Ajustes</string>
|
||||
</resources>
|
||||
@@ -38,6 +38,7 @@
|
||||
<string name="switch_camera">Changer de camera</string>
|
||||
<string name="history">Historique</string>
|
||||
<string name="preferences">Preferences</string>
|
||||
<string name="show_crop_handle">Show cropping limiter</string>
|
||||
<string name="zoom_by_swiping">Zoom camera by swiping up/down</string>
|
||||
<string name="auto_rotate">Recognize 1D barcodes vertically</string>
|
||||
<string name="try_harder">Optimize reader for accuracy, not speed</string>
|
||||
@@ -50,7 +51,9 @@
|
||||
<string name="open_with_url">Open unknown data with URL</string>
|
||||
<string name="really_remove_scan">Supprimer le scan?</string>
|
||||
<string name="really_remove_all_scans">Supprimer tous les scans?</string>
|
||||
<string name="really_remove_selected_scans">Really remove selected scans?</string>
|
||||
<string name="clear_history">Clear history</string>
|
||||
<string name="copy_scan">Copy scan</string>
|
||||
<string name="edit_scan">Edit label</string>
|
||||
<string name="remove_scan">Remove scan</string>
|
||||
<string name="enter_name">Enter a name to describe the scan</string>
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
<string name="switch_camera">Kamera átkapcsolása</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>
|
||||
<string name="zoom_by_swiping">Zoom camera by swiping up/down</string>
|
||||
<string name="auto_rotate">Recognize 1D barcodes vertically</string>
|
||||
<string name="try_harder">Optimize reader for accuracy, not speed</string>
|
||||
@@ -50,7 +51,9 @@
|
||||
<string name="open_with_url">Ismeretlen adat megnyitása URL-lel</string>
|
||||
<string name="really_remove_scan">Valóban eltávolítja a beolvasást?</string>
|
||||
<string name="really_remove_all_scans">Valóban eltávolítja az összes beolvasását?</string>
|
||||
<string name="really_remove_selected_scans">Really remove selected scans?</string>
|
||||
<string name="clear_history">Előzmények törlése</string>
|
||||
<string name="copy_scan">Copy scan</string>
|
||||
<string name="edit_scan">Edit label</string>
|
||||
<string name="remove_scan">Remove scan</string>
|
||||
<string name="enter_name">Enter a name to describe the scan</string>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<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>
|
||||
@@ -37,8 +38,9 @@
|
||||
<string name="switch_camera">Ganti kamera</string>
|
||||
<string name="history">Riwayat</string>
|
||||
<string name="preferences">Preferensi</string>
|
||||
<string name="show_crop_handle">Tampilkan pembatas pemotongan</string>
|
||||
<string name="zoom_by_swiping">Perbesar kamera dengan menggeser ke atas/bawah</string>
|
||||
<string name="auto_rotate">Recognize 1D barcodes vertically</string>
|
||||
<string name="auto_rotate">Kenali barcode 1D secara vertikal</string>
|
||||
<string name="try_harder">Optimalkan akurasi, bukan kecepatan</string>
|
||||
<string name="vibrate">Bergetar saat mendeteksi</string>
|
||||
<string name="use_history">Simpan riwayat pemindaian</string>
|
||||
@@ -49,7 +51,9 @@
|
||||
<string name="open_with_url">Buka data tak dikenal dengan URL</string>
|
||||
<string name="really_remove_scan">Yakin menghapus pemindaian?</string>
|
||||
<string name="really_remove_all_scans">Yakin menghapus semua pemindaian?</string>
|
||||
<string name="really_remove_selected_scans">Yakin menghapus semua pemindaian?</string>
|
||||
<string name="clear_history">Hapus riwayat</string>
|
||||
<string name="copy_scan">Salin pemindaian</string>
|
||||
<string name="edit_scan">Sunting label</string>
|
||||
<string name="remove_scan">Buang pindaian</string>
|
||||
<string name="enter_name">Masukkan keterangan pemindaian</string>
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
<string name="switch_camera">Cambia fotocamera</string>
|
||||
<string name="history">Cronologia</string>
|
||||
<string name="preferences">Preferenze</string>
|
||||
<string name="show_crop_handle">Show cropping limiter</string>
|
||||
<string name="zoom_by_swiping">Zoom fotocamera scorrendo su/giù</string>
|
||||
<string name="auto_rotate">Recognize 1D barcodes vertically</string>
|
||||
<string name="try_harder">Ottimizza il lettore per accuratezza, non velocità</string>
|
||||
@@ -50,7 +51,9 @@
|
||||
<string name="open_with_url">Apri dati sconosciuti con URL</string>
|
||||
<string name="really_remove_scan">Vuoi veramente rimuovere la scansione?</string>
|
||||
<string name="really_remove_all_scans">Vuoi veramente rimuovere tutte le scansioni?</string>
|
||||
<string name="really_remove_selected_scans">Really remove selected scans?</string>
|
||||
<string name="clear_history">Pulisci cronologia</string>
|
||||
<string name="copy_scan">Copy scan</string>
|
||||
<string name="edit_scan">Modifica etichetta</string>
|
||||
<string name="remove_scan">Rimuovi scansione</string>
|
||||
<string name="enter_name">Inserisci un nome per descrivere la scansione</string>
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
<string name="switch_camera">Camera wisselen</string>
|
||||
<string name="history">Geschiedenis</string>
|
||||
<string name="preferences">Voorkeuren</string>
|
||||
<string name="show_crop_handle">Show cropping limiter</string>
|
||||
<string name="zoom_by_swiping">Zoom camera by swiping up/down</string>
|
||||
<string name="auto_rotate">Recognize 1D barcodes vertically</string>
|
||||
<string name="try_harder">Optimize reader for accuracy, not speed</string>
|
||||
@@ -49,7 +50,9 @@
|
||||
<string name="show_hex_dump">Show hex dump</string>
|
||||
<string name="really_remove_scan">Scan echt verwijderen?</string>
|
||||
<string name="really_remove_all_scans">Echt alle scans verwijderen?</string>
|
||||
<string name="really_remove_selected_scans">Really remove selected scans?</string>
|
||||
<string name="clear_history">Geschiedenis wissen</string>
|
||||
<string name="copy_scan">Copy scan</string>
|
||||
<string name="edit_scan">Edit label</string>
|
||||
<string name="remove_scan">Remove scan</string>
|
||||
<string name="enter_name">Enter a name to describe the scan</string>
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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="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="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>
|
||||
@@ -38,6 +38,7 @@
|
||||
<string name="switch_camera">Trocar câmera</string>
|
||||
<string name="history">Histórico</string>
|
||||
<string name="preferences">Preferências</string>
|
||||
<string name="show_crop_handle">Show cropping limiter</string>
|
||||
<string name="zoom_by_swiping">Zoom camera by swiping up/down</string>
|
||||
<string name="auto_rotate">Recognize 1D barcodes vertically</string>
|
||||
<string name="try_harder">Optimize reader for accuracy, not speed</string>
|
||||
@@ -50,7 +51,9 @@
|
||||
<string name="open_with_url">Abrir dados desconhecidos com URL</string>
|
||||
<string name="really_remove_scan">Realmente deseja remover o escaneamento?</string>
|
||||
<string name="really_remove_all_scans">Realmente deseja remover todos escaneamentos?</string>
|
||||
<string name="really_remove_selected_scans">Really remove selected scans?</string>
|
||||
<string name="clear_history">Limpar histórico</string>
|
||||
<string name="copy_scan">Copy scan</string>
|
||||
<string name="edit_scan">Edit label</string>
|
||||
<string name="remove_scan">Remove scan</string>
|
||||
<string name="enter_name">Enter a name to describe the scan</string>
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
<string name="switch_camera">Переключить камеру</string>
|
||||
<string name="history">История</string>
|
||||
<string name="preferences">Настройки</string>
|
||||
<string name="show_crop_handle">Show cropping limiter</string>
|
||||
<string name="zoom_by_swiping">Масштабировать движением пальцем вверх/вниз</string>
|
||||
<string name="auto_rotate">Recognize 1D barcodes vertically</string>
|
||||
<string name="try_harder">Оптимизировать считывание на точность, а не скорость</string>
|
||||
@@ -54,7 +55,9 @@
|
||||
<string name="open_with_url">Открыть неизвестные данные с помощью URL</string>
|
||||
<string name="really_remove_scan">Действительно удалить сканирование?</string>
|
||||
<string name="really_remove_all_scans">Действительно удалить все сканирования?</string>
|
||||
<string name="really_remove_selected_scans">Really remove selected scans?</string>
|
||||
<string name="clear_history">Очистить историю</string>
|
||||
<string name="copy_scan">Copy scan</string>
|
||||
<string name="edit_scan">Редактировать</string>
|
||||
<string name="remove_scan">Удалить сканирование</string>
|
||||
<string name="enter_name">Укажите название сканирования</string>
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
<string name="switch_camera">切换摄像头</string>
|
||||
<string name="history">历史</string>
|
||||
<string name="preferences">首选项</string>
|
||||
<string name="show_crop_handle">Show cropping limiter</string>
|
||||
<string name="zoom_by_swiping">Zoom camera by swiping up/down</string>
|
||||
<string name="auto_rotate">Recognize 1D barcodes vertically</string>
|
||||
<string name="try_harder">Optimize reader for accuracy, not speed</string>
|
||||
@@ -49,7 +50,9 @@
|
||||
<string name="open_with_url">使用URL打开未知数据</string>
|
||||
<string name="really_remove_scan">确定删除记录?</string>
|
||||
<string name="really_remove_all_scans">确定删除全部记录?</string>
|
||||
<string name="really_remove_selected_scans">Really remove selected scans?</string>
|
||||
<string name="clear_history">删除记录</string>
|
||||
<string name="copy_scan">Copy scan</string>
|
||||
<string name="edit_scan">Edit label</string>
|
||||
<string name="remove_scan">Remove scan</string>
|
||||
<string name="enter_name">Enter a name to describe the scan</string>
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<string name="info">資訊</string>
|
||||
<string name="switch_camera">切換相機</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>
|
||||
<string name="auto_rotate">Recognize 1D barcodes vertically</string>
|
||||
<string name="try_harder">Optimize reader for accuracy, not speed</string>
|
||||
@@ -47,7 +48,9 @@
|
||||
<string name="show_hex_dump">Show hex dump</string>
|
||||
<string name="really_remove_scan">真的要移除掃描記錄?</string>
|
||||
<string name="really_remove_all_scans">真的要移除所有掃描記錄?</string>
|
||||
<string name="really_remove_selected_scans">Really remove selected scans?</string>
|
||||
<string name="clear_history">清除記錄</string>
|
||||
<string name="copy_scan">Copy scan</string>
|
||||
<string name="edit_scan">Edit label</string>
|
||||
<string name="remove_scan">Remove scan</string>
|
||||
<string name="enter_name">Enter a name to describe the scan</string>
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
<color name="selected_row">#222</color>
|
||||
<color name="separator">#333</color>
|
||||
<color name="crop_bound">#fff</color>
|
||||
<color name="candidate">#80b6d46f</color>
|
||||
<color name="dot">#80b6d46f</color>
|
||||
</resources>
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
<string name="switch_camera">Switch camera</string>
|
||||
<string name="history">History</string>
|
||||
<string name="preferences">Preferences</string>
|
||||
<string name="show_crop_handle">Show cropping limiter</string>
|
||||
<string name="zoom_by_swiping">Zoom camera by swiping up/down</string>
|
||||
<string name="auto_rotate">Recognize 1D barcodes vertically</string>
|
||||
<string name="try_harder">Optimize reader for accuracy, not speed</string>
|
||||
@@ -50,7 +51,9 @@
|
||||
<string name="open_with_url">Open unknown data with URL</string>
|
||||
<string name="really_remove_scan">Really remove scan?</string>
|
||||
<string name="really_remove_all_scans">Really remove all scans?</string>
|
||||
<string name="really_remove_selected_scans">Really remove selected scans?</string>
|
||||
<string name="clear_history">Clear history</string>
|
||||
<string name="copy_scan">Copy scan</string>
|
||||
<string name="edit_scan">Edit label</string>
|
||||
<string name="remove_scan">Remove scan</string>
|
||||
<string name="enter_name">Enter a name to describe the scan</string>
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package de.markusfisch.android.binaryeye.actions.vtype
|
||||
|
||||
import de.markusfisch.android.binaryeye.simpleFail
|
||||
import junit.framework.TestCase.*
|
||||
import org.junit.Test
|
||||
|
||||
class VTypeTest {
|
||||
@Test
|
||||
fun vcard() {
|
||||
val content = """BEGIN:VCARD
|
||||
N:Doe;John
|
||||
END:VCARD"""
|
||||
|
||||
assertEquals("VCARD", VTypeParser.parseVType(content))
|
||||
|
||||
val info = VTypeParser.parseMap(content)
|
||||
|
||||
info["N"]?.singleOrNull()?.also {
|
||||
assertEquals("Doe;John", it.value)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun vcalendar() {
|
||||
val content = """BEGIN:VCALENDAR
|
||||
BEGIN:VEVENT
|
||||
SUMMARY:foo
|
||||
DTSTART:20080504T123456Z
|
||||
DTEND:20080505T234555Z
|
||||
END:VEVENT
|
||||
END:VCALENDAR"""
|
||||
|
||||
assertEquals("VCALENDAR", VTypeParser.parseVType(content))
|
||||
|
||||
val info = VTypeParser.parseMap(content)
|
||||
|
||||
info["SUMMARY"]?.singleOrNull()?.also {
|
||||
assertEquals("foo", it.value)
|
||||
}
|
||||
info["DTSTART"]?.singleOrNull()?.also {
|
||||
assertEquals("20080504T123456Z", it.value)
|
||||
}
|
||||
info["DTEND"]?.singleOrNull()?.also {
|
||||
assertEquals("20080505T234555Z", it.value)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun vevent() {
|
||||
val content = """BEGIN:VEVENT
|
||||
SUMMARY:foo
|
||||
DTSTART:20080504T123456Z
|
||||
DTEND:20080505T234555Z
|
||||
END:VEVENT"""
|
||||
|
||||
assertEquals("VEVENT", VTypeParser.parseVType(content))
|
||||
|
||||
val info = VTypeParser.parseMap(content)
|
||||
|
||||
info["SUMMARY"]?.singleOrNull()?.also {
|
||||
assertEquals("foo", it.value)
|
||||
}
|
||||
info["DTSTART"]?.singleOrNull()?.also {
|
||||
assertEquals("20080504T123456Z", it.value)
|
||||
}
|
||||
info["DTEND"]?.singleOrNull()?.also {
|
||||
assertEquals("20080505T234555Z", it.value)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun veventWithCarriageReturn() {
|
||||
val content = """BEGIN:VEVENT${'\r'}
|
||||
SUMMARY:foo${'\r'}
|
||||
DTSTART:20080504T123456Z${'\r'}
|
||||
DTEND:20080505T234555Z${'\r'}
|
||||
END:VEVENT"""
|
||||
|
||||
assertEquals("VEVENT", VTypeParser.parseVType(content))
|
||||
|
||||
val info = VTypeParser.parseMap(content)
|
||||
|
||||
info["SUMMARY"]?.singleOrNull()?.also {
|
||||
assertEquals("foo", it.value)
|
||||
}
|
||||
info["DTSTART"]?.singleOrNull()?.also {
|
||||
assertEquals("20080504T123456Z", it.value)
|
||||
}
|
||||
info["DTEND"]?.singleOrNull()?.also {
|
||||
assertEquals("20080505T234555Z", it.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
buildscript {
|
||||
ext {
|
||||
kotlin_version = '1.3.72'
|
||||
tools_version = '3.6.3'
|
||||
tools_version = '4.0.0'
|
||||
build_tools_version = '29.0.3'
|
||||
sdk_version = 29
|
||||
support_version = '25.3.1'
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
Funciona en orientación vertical y horizontal, puede leer códigos invertidos,
|
||||
interfaz Material Design y también genera códigos de barras.
|
||||
|
||||
Utiliza la biblioteca de escaneo de códigos de barras ZXing ("Zebra Crossing").
|
||||
Los formatos de código de barras soportados son: AZTEC, CODABAR, CODE 39, CODE 93, CODE 128,
|
||||
DATA MATRIX, EAN 8, EAN 13, ITF, PDF417, QR CODE, RSS 14, RSS EXPANDED,
|
||||
UPC A, UPC E y UPC EAN EXTENSION.
|
||||
|
||||
Esto es de código abierto:
|
||||
https://github.com/markusfisch/BinaryEye
|
||||
@@ -0,0 +1 @@
|
||||
Otro escáner de código de barras para Android. Gratis, sin anuncios y de código abierto.
|
||||
@@ -0,0 +1 @@
|
||||
Binary Eye
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
#Tue May 07 14:37:45 CEST 2019
|
||||
#Sun May 31 13:11:56 CEST 2020
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg height="56px" viewBox="0 0 56 56" width="56px" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<radialGradient gradientUnits="userSpaceOnUse" cx="28" cy="30" r="24" id="gradient-0" gradientTransform="matrix(1, 0, 0, 1, 0, 2)">
|
||||
<stop offset="0" style="stop-color: rgb(0, 0, 0);"/>
|
||||
<stop offset="1" style="stop-color: rgb(0, 0, 0); stop-opacity: 0;"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
<circle style="filter: none; fill: url(#gradient-0);" cx="28" cy="32" r="24"/>
|
||||
<circle style="fill: rgb(182, 212, 111); filter: none;" cx="28" cy="28" r="24"/>
|
||||
<path d="M 33 31 L 35 31 L 35 23 C 35 21.9 34.1 21 33 21 L 25 21 L 25 23 L 33 23 L 33 31 Z M 23 33 L 23 17 L 21 17 L 21 21 L 17 21 L 17 23 L 21 23 L 21 33 C 21 34.1 21.9 35 23 35 L 33 35 L 33 39 L 35 39 L 35 35 L 39 35 L 39 33 L 23 33 Z" style="fill: rgb(255, 255, 255);"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 885 B |
Reference in New Issue
Block a user