Compare commits

...
77 Commits
Author SHA1 Message Date
Markus Fisch 20b4f7aac5 Advance version number to 1.65.2 2025-03-12 18:55:50 +01:00
Markus Fisch bc6e350271 Sanity check view, ROI and frame metrics
Because `roundToInt` in `Rect.setFrameRoi()` throws an
`IllegalArgumentException: Cannot round NaN value` for
some people out there.
2025-03-12 18:52:27 +01:00
Markus Fisch c9c1b2f5bf Advance version number to 1.65.1 2025-03-12 12:23:43 +01:00
Markus Fisch 1dbc0f0fa1 Fix fastlane changelogs
And insert the missing log for version code 141.
2025-03-12 12:20:20 +01:00
solokotandGitHub ab8170ee72 Update Russian translation
Fix machine translation
2025-03-12 12:11:05 +01:00
Markus Fisch 551464dc6f Refactor for an early return when content is empty
Easier to read and less indent.
2025-03-12 00:54:59 +01:00
Markus Fisch c3aac0a1e1 Set a default search URL for simple mode
So any (otherwise unregistered) content is always redirected to
the browser.
2025-03-12 00:53:37 +01:00
Markus Fisch d678e1cfd0 Hide keyboard when expanding recreated barcode
So the layout of `BarcodeFragment` isn't affected by it.
2025-03-12 00:49:30 +01:00
Markus Fisch a445ade877 Reduce height for 1D barcodes
1D barcodes don't need as much vertical space.
2025-03-12 00:48:20 +01:00
Markus Fisch 1fd0b7637f Add execute argument for binaryeye://encode
For immediate code generation.
2025-03-12 00:37:47 +01:00
Markus Fisch fdc88df897 Remember quiet zone settings
And use a quiet zone by default because it's recommended.
2025-03-12 00:19:41 +01:00
Markus Fisch d0f133ae00 Fix showing a scan from history
When `openImmediately` is `true`.

Of course, the action should only be executed when we're coming
from the scanner, not the history.
2025-03-12 00:11:14 +01:00
Markus Fisch 67646261e7 Remove unnecessary id field 2025-03-12 00:08:11 +01:00
Markus Fisch faeae9e904 Advance version number to 1.65.0 2025-03-11 23:23:55 +01:00
Markus Fisch 77b21a9e83 Add a welcome screen to choose a mode
To address these bad reviews when people don't understand that
they have to press this little green button.
2025-03-11 23:17:36 +01:00
Markus Fisch cbe7860a3d Add a deep link to encode a barcode 2025-03-11 22:58:45 +01:00
Markus Fisch fc29267d3d Update ZXing-C++ library 2025-03-11 22:58:10 +01:00
Markus Fisch 890506328b Fix show crop handle setting
This switch isn't part of the crop configuration, but controls if
the crop handle is actually visible or not.
2025-03-11 22:58:10 +01:00
Markus Fisch 04036b6cd6 Use apply scope function to initialize views
And remove all this repetition of names.
2025-03-11 22:58:10 +01:00
Markus Fisch 2602021c1c Remove unused import from PickActivity 2025-03-11 22:58:10 +01:00
Markus Fisch 17a0bdc0a9 Allow shrinking of image samples too
So they can fit in the crop window.
2025-03-11 22:58:10 +01:00
Markus Fisch d71a220eca Remember zoom level of barcodes in view
Solves #490.
2025-03-11 22:58:10 +01:00
Markus Fisch 0c2e711d79 Add a comment why Scan.text can be an empty string
So it's immediately clear why `Scan.text` can be empty.
2025-03-11 22:58:10 +01:00
Markus Fisch ca342a83d3 Configure ZXingCpp to return plain text only
And remove the HRI decoration because it's confusing people and
doesn't work well when the barcode is re-generated from this
content.

Alternatively, we could filter the HRI decoration for generation,
but that's probably overkill.

Solves #506.
2025-03-11 22:58:10 +01:00
Markus Fisch 3c44ff56eb Refactor SCAN_CONTENT to SCAN_TEXT
To match the refactoring of `Scan.text` in 5189fc.
2025-03-11 22:58:10 +01:00
bovirusandGitHub bcae64c0d0 Italian language update 2025-03-11 22:57:50 +01:00
Markus Fisch c2f673729b Advance version number to 1.64.2 2025-03-10 21:23:52 +01:00
Markus Fisch 2043e0f349 Use separate crop handle config for image scanning
These are two different use cases.
2025-03-10 21:11:41 +01:00
Markus Fisch b93eb5963e Use raw content for Barcode<*> when text is empty
So binary content is supported here, too.
2025-03-10 11:01:15 +01:00
Markus Fisch 5189fc9c5a Refactor Scan.content to Scan.text
Because `text` is more specific, and the term `content` is often
used for generics.
2025-03-10 10:54:44 +01:00
Markus Fisch 2d311ff92a Fix initializing dataMask
And show this item for QR Codes only.
2025-03-10 19:50:19 +01:00
solokotandGitHub 14bb08003d Update Russian translation 2025-03-10 10:31:50 +01:00
Markus Fisch 42f9887030 Advance version number to 1.64.1 2025-03-09 22:39:37 +01:00
Markus Fisch a59d241b50 Handle empty input for recreation 2025-03-09 22:35:28 +01:00
Markus Fisch 9f31160c7b Add missing symbol fields to initial db creation 2025-03-09 22:14:06 +01:00
Markus Fisch 2eb4e51c17 Advance version number to 1.64.0 2025-03-09 19:43:00 +01:00
Markus Fisch 53a554e7a2 Add a menu item to find a specific barcode
The searched barcode needs to include the given term.

Solves #516.
2025-03-09 19:09:19 +01:00
Markus Fisch 7f6887716a Use Barcode for recreation too
And move `Barcode` into its own file.

So a given `BitMatrix` can be used for `BarcodeFragment` (and sharing
and exporting) as well.
2025-03-09 14:19:27 +01:00
Markus Fisch 5ef93802ce Make margin optional for BarcodeFragment
Just like `ecLevel` and `colors`.
2025-03-09 12:57:23 +01:00
Markus Fisch c1eff08bb9 Remove unnecessary semicolons
Too much context change.
2025-03-09 12:56:48 +01:00
Markus Fisch 8798d44ec7 Fix showing QR Code data mask
The first data mask is 0.
2025-03-09 12:55:55 +01:00
Markus Fisch d4e31ff97e Update ZXing-C++ library
To fix data mask initialization.
2025-03-09 12:55:28 +01:00
solokotandGitHub ae8b08a85a Update Russian translation 2025-03-09 12:10:10 +01:00
Markus Fisch 09430d776e Replace margin with add quiet zone switch
Giving a margin in pixels doesn't scale with (the size) of the
barcode.
2025-03-08 22:18:04 +01:00
Markus Fisch ef88c3179d Use detected barcode matrix for recreation
If it is provided by ZXing-C++. Currently, this is only supported
for some 2D barcodes. But it's probably only really useful for them
because there's padding and and other stuff that 1D barcodes don't
have.

This may solve #507.
2025-03-08 21:57:13 +01:00
Markus Fisch c732ec726b Show data mask of QR Codes 2025-03-08 21:54:54 +01:00
Markus Fisch 801c768df4 Update ZXing-C++ library 2025-03-08 21:53:19 +01:00
Markus Fisch 552a2f6d57 Only color status bar in context mode before SDK35
Has no effect from SDK35 on anyway.
2025-03-04 21:31:55 +01:00
Markus Fisch 7fa8810197 Make getIdOfScanByContent() private
Because it can be.
2025-03-04 21:27:12 +01:00
Markus Fisch 254af25bbb Update database functions to non-nullable Cursors
Because `SQLiteDatabase.rawQuery` returns a non-nullable `Cursor` now.
2025-03-07 16:55:58 +01:00
Markus Fisch a27656c174 Add a translucent nav bar background on SDK35+
Because the system doesn't draw one anymore.
2025-03-05 15:24:44 +01:00
Markus Fisch 2ca55ebccc Fix setting window insets on SDK35+
The bottom padding from the navigation bar needs to be set, too.
2025-03-04 19:47:41 +01:00
Markus Fisch ec1d40942d Optimize vector drawables with avocado 2025-03-04 00:21:30 +01:00
Markus Fisch 7852e5ce9d Use color literal in vector drawable
Because resource references will not work correctly in images
generated for API < 21.
2025-03-04 00:20:31 +01:00
Markus Fisch f36a81d4a0 Update Kotlin version 2025-03-04 12:50:50 +01:00
Markus Fisch d86ea4073c Remove unused quantity for Brazilian Portuguese
Following the corresponding lint warning.
2025-03-04 12:50:37 +01:00
Markus Fisch a69530621d Add autoVerify for URL intent filter 2025-03-04 12:50:28 +01:00
Markus Fisch 3c0f29a768 Add targetApi notation for quick settings tile
Since this is only available from Android N+.
2025-03-04 00:09:12 +01:00
Markus Fisch ccf5157fa7 Mark autofocus as optional
As it isn't strictly required.
2025-03-04 00:08:58 +01:00
Markus Fisch c1ebb1c154 Specify an explicit locale for String.format()
To silent the corresponding lint warning.

Doesn't really matter here probably.
2025-03-04 00:07:59 +01:00
Markus Fisch a4778bcc02 Replace removeFirst with removeAt
Because `java.util.List#removeFirst` requires SDK35, and resolved
to a Kotlin stdlib extension function before.
2025-03-04 00:04:04 +01:00
Markus Fisch 24d2c6d8d6 Only color system bars before SDK35
And ignore deprecation warning in this branch.

System bars no longer have a background from SDK35+.
2025-03-03 23:59:14 +01:00
Markus Fisch 0f3a07f602 Name SDK that requires a change explicitly
Instead of using greater than, because this way we can search
for VANILLA_ICE_CREAM changes.
2025-03-04 12:34:33 +01:00
Markus Fisch 751d8932f8 Immediately replace toasts
Instead of showing them in sequence.

Because in this app, only the last message is important.

Speeds up continuous scanning. Especially when the scanned code is
also forwarded and the server result is displayed in the next toast
(see #523).
2025-03-03 23:47:16 +01:00
Markus Fisch 9c2d063c24 Update tools version 2025-03-03 20:55:16 +01:00
Markus Fisch 0f9bbf5b2f Extend toolbar under status bar on SDK35+
Because SDK 35 is edge-to-edge by default, and doesn't color the
status or navigation bar anymore.
2025-02-18 21:34:27 +01:00
Markus Fisch 530280981e Fix image rotation calculation 2025-02-18 21:30:46 +01:00
Markus Fisch ddfbb305c7 Check Bitmap.getConfig() for null before use 2025-02-18 21:27:30 +01:00
Markus Fisch 8d1a29922b Update to SDK 35 2025-02-18 21:26:52 +01:00
Markus Fisch c710f7c6ab Move Toolate link below the reference section
Reference-style links should be derefenced after the content.

Also add a proper header for the matter.
2025-02-14 20:40:33 +01:00
Yurt PageandGitHub 111c587eef Add link to Toolate in CONTRIBUTING 2025-02-14 20:39:35 +01:00
Markus Fisch 264e494487 Update tools version and gradle wrapper 2025-01-23 19:59:08 +01:00
Markus Fisch 8135b2a3dc Update build tools 2024-12-19 22:23:45 +01:00
kraloandGitHub dce62b70d3 Update README to explain non-clickable binaryeye:// url 2024-12-05 21:56:06 +01:00
Markus Fisch 533812c638 Revert removing http deep linking intent filter
This is required for "http(s)://markusfisch.de/BinaryEye?ret=".

This reverts commit 12554e63e2.
2024-12-05 19:52:56 +01:00
Markus Fisch 5dc5bed281 Fix indent in Portuguese (Brazil) translation 2024-12-01 17:58:36 +01:00
Too Late (bot)andGitHub 2a13561962 Update Portuguese (Brazil) translation 2024-12-01 17:57:46 +01:00
84 changed files with 1517 additions and 497 deletions
+36
View File
@@ -1,5 +1,41 @@
# Change Log
## 1.65.2
* Sanity check ROI metrics
## 1.65.1
* Improve simple mode
* Remember quiet zone settings
* Reduce height for 1D barcodes
* Add execute argument for binaryeye://encode
* Update Russian translation
## 1.65.0
* Add a welcome screen to choose a mode
* Add a deep link to encode a barcode
* Remember zoom level of generated barcodes
* Remove Human Readable Interpretation (HRI) decoration
* Update Italian translation
## 1.64.2
* Separate crop window settings for image scanning
* Fix initializing dataMask
* Update Russian translation
## 1.64.1
* Handle empty input for recreation
* Fix setting up database
## 1.64.0
* Find barcode by content
* Immediately replace toasts
* Use detected barcode for recreation
* Show data mask of QR Codes
* Fix deep linking
* Replace margin with quiet zone switch when generating barcodes
* Update Russian translation
* Update Portuguese (Brazil) translation
## 1.63.13
* Fix UTF-8 conversion of binary escape sequences when creating barcodes
* Fix scan order for intervals under one second
+5
View File
@@ -16,5 +16,10 @@ to squash when merging into master:
Then write a [good commit message][commit_messages] to keep the history
meaningful and useful. One feature, one commit.
## Translations
For translations use [Toolate][toolate].
[android_best_practices]: https://developer.android.com/distribute/best-practices/develop/
[commit_messages]: https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#_commit_guidelines
[toolate]: https://toolate.othing.xyz/projects/binaryeye/
+10
View File
@@ -28,6 +28,16 @@ testscan:
-c android.intent.category.BROWSABLE \
-d 'binaryeye://scan/?ret=http%3A%2F%2Fmarkusfisch.de%2F%3Fresult%3D{RESULT}'
testencode:
adb shell am start -a android.intent.action.VIEW \
-c android.intent.category.BROWSABLE \
-d 'binaryeye://encode?content=Test\&format=QRCode'
testencodeexec:
adb shell am start -a android.intent.action.VIEW \
-c android.intent.category.BROWSABLE \
-d 'binaryeye://encode?content=Test\&format=QRCode\&execute=true'
testurl:
adb shell am start -a android.intent.action.VIEW \
-c android.intent.category.BROWSABLE \
+15 -2
View File
@@ -88,9 +88,11 @@ ZXing can generate the following barcode formats:
### Deep Links
You can invoke Binary Eye with a web URI intent from anything that can
open URIs. There are two options:
open URIs.
1. [binaryeye://scan](binaryeye://scan)
#### Decoding
1. [binaryeye://scan](binaryeye://scan) (note: GitHub does not render this as clickable link)
2. [http(s)://markusfisch.de/BinaryEye](http://markusfisch.de/BinaryEye)
If you want to get the scanned contents, you can add a `ret` query
@@ -104,6 +106,17 @@ Supported symbols are:
* `RESULT_BYTES` - raw result as a hex string
* `FORMAT` - barcode format
#### Encoding
1. [binaryeye://encode](binaryeye://encode)
You can use the URL arguments `content` and `format` to automatically
preset this data. For example:
[binaryeye://encode?content=Test&format=QRCODE](binaryeye://encode?content=Test&format=QRCODE)
If you want the code to be generated immediately, add `execute=true`.
### SCAN Intent
You can also use Binary Eye from other apps by using the
+3 -3
View File
@@ -9,8 +9,8 @@ android {
minSdk 9
targetSdk sdk_version
versionCode 141
versionName '1.63.13'
versionCode 147
versionName '1.65.2'
}
signingConfigs {
@@ -82,7 +82,7 @@ dependencies {
// External
implementation 'com.github.markusfisch:CameraView:1.10.0'
implementation 'com.github.markusfisch:ScalingImageView:1.4.1'
implementation 'com.github.markusfisch:zxing-cpp:v2.2.0.5'
implementation 'com.github.markusfisch:zxing-cpp:v2.3.0.3'
// Testing
testImplementation 'junit:junit:4.13.2'
+24 -3
View File
@@ -19,7 +19,8 @@
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-feature android:name="android.hardware.camera"/>
<uses-feature android:name="android.hardware.camera.autofocus"/>
<uses-feature android:name="android.hardware.camera.autofocus"
android:required="false" />
<uses-sdk tools:overrideLibrary="android.support.v14.preference"/>
<queries>
<intent>
@@ -75,6 +76,15 @@
<data android:host="details" android:scheme="market"
android:pathSuffix="id=com.xiaomi.scanner"/>
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:host="markusfisch.de"/>
<data android:pathPrefix="/BinaryEye"/>
<data android:scheme="http"/>
<data android:scheme="https"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
@@ -99,14 +109,25 @@
</intent-filter>
</activity>
<activity
android:name=".activity.MainActivity"
android:name=".activity.WelcomeActivity"
android:exported="true"/>
<activity
android:name=".activity.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="binaryeye" android:host="encode"/>
</intent-filter>
</activity>
<service
android:name=".service.ScanTileService"
android:icon="@drawable/ic_action_scan"
android:label="@string/scan_code"
android:exported="true"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
tools:targetApi="n">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE"/>
</intent-filter>
@@ -46,8 +46,8 @@ object OpenOrSearchAction : IAction {
).toMutableList()
// Remove the "Always ask" entry. The arrays search_engines_*
// are used in the preferences too.
names.removeFirst()
urls.removeFirst()
names.removeAt(0)
urls.removeAt(0)
if (prefs.openWithUrl.isNotEmpty()) {
names.add(prefs.openWithUrl)
urls.add(prefs.openWithUrl)
@@ -1,5 +1,6 @@
package de.markusfisch.android.binaryeye.activity
import android.annotation.SuppressLint
import android.app.Activity
import android.app.AlertDialog
import android.content.Context
@@ -13,11 +14,11 @@ import android.os.Build
import android.os.Bundle
import android.support.design.widget.FloatingActionButton
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import android.view.Menu
import android.view.MenuItem
import android.view.MotionEvent
import android.view.View
import android.widget.EditText
import android.widget.SeekBar
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.adapter.prettifyFormatName
@@ -42,7 +43,7 @@ import de.markusfisch.android.binaryeye.net.sendAsync
import de.markusfisch.android.binaryeye.net.urlEncode
import de.markusfisch.android.binaryeye.preference.Preferences
import de.markusfisch.android.binaryeye.view.errorFeedback
import de.markusfisch.android.binaryeye.view.initSystemBars
import de.markusfisch.android.binaryeye.view.initBars
import de.markusfisch.android.binaryeye.view.scanFeedback
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
import de.markusfisch.android.binaryeye.widget.DetectorView
@@ -53,12 +54,14 @@ import de.markusfisch.android.zxingcpp.ZxingCpp.BarcodeFormat
import de.markusfisch.android.zxingcpp.ZxingCpp.Binarizer
import de.markusfisch.android.zxingcpp.ZxingCpp.ReaderOptions
import de.markusfisch.android.zxingcpp.ZxingCpp.Result
import de.markusfisch.android.zxingcpp.ZxingCpp.TextMode
import java.io.FileInputStream
import java.util.Scanner
import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToInt
class CameraActivity : AppCompatActivity() {
private val frameRoi = Rect()
private val matrix = Matrix()
@@ -77,6 +80,7 @@ class CameraActivity : AppCompatActivity() {
private var frontFacing = false
private var bulkMode = prefs.bulkMode
private var restrictFormat: String? = null
private var searchTerm: String? = null
private var ignoreNext: String? = null
private var fallbackBuffer: IntArray? = null
private var requestCameraPermission = true
@@ -125,8 +129,7 @@ class CameraActivity : AppCompatActivity() {
// custom locale.
setTitle(R.string.scan_code)
initSystemBars(this)
setSupportActionBar(findViewById(R.id.toolbar) as Toolbar)
initBars()
cameraView = findViewById(R.id.camera_view) as CameraView
detectorView = findViewById(R.id.detector_view) as DetectorView
@@ -148,14 +151,18 @@ class CameraActivity : AppCompatActivity() {
super.onDestroy()
fallbackBuffer = null
saveZoom()
detectorView.saveCropHandlePos()
detectorView.storeCropHandlePos(CAMERA_CROP_HANDLE)
releaseToneGenerators()
}
override fun onResume() {
super.onResume()
if (firstStart()) {
startActivity(Intent(this, WelcomeActivity::class.java))
return
}
System.gc()
updateHints()
updateHintsAndTitle()
if (prefs.bulkMode && bulkMode != prefs.bulkMode) {
bulkMode = prefs.bulkMode
invalidateOptionsMenu()
@@ -171,18 +178,43 @@ class CameraActivity : AppCompatActivity() {
requestCameraPermission = false
}
private fun updateHints() {
private fun Context.firstStart(): Boolean {
val welcomeShownName = "welcome_shown"
if (prefs.preferences.getBoolean(welcomeShownName, false)) {
android.util.Log.d("mfdbg", "mfdbg: already welcome shown")
return false
}
prefs.preferences.edit().putBoolean(welcomeShownName, true).apply()
val packageInfo = packageManager.getPackageInfo(packageName, 0)
val installedSince = System.currentTimeMillis() - packageInfo.firstInstallTime
android.util.Log.d("mfdbg", "mfdbg: installed since is $installedSince")
return installedSince < 86400000
}
private fun updateHintsAndTitle() {
val restriction = restrictFormat
formatsToRead = if (restriction != null) {
title = getString(
R.string.scan_format,
prettifyFormatName(restriction)
)
setOf(restriction)
} else {
setTitle(R.string.scan_code)
prefs.barcodeFormats
}.toFormatSet()
updateTitle()
}
private fun updateTitle() {
if (searchTerm != null || restrictFormat != null) {
title = getString(
R.string.scan_format,
prettifyFormatName(
listOfNotNull(
restrictFormat,
searchTerm
).joinToString(",")
)
)
} else {
setTitle(R.string.scan_code)
}
}
private fun setReturnTarget(intent: Intent?) {
@@ -227,6 +259,7 @@ class CameraActivity : AppCompatActivity() {
frontFacing = savedState.getBoolean(FRONT_FACING)
bulkMode = savedState.getBoolean(BULK_MODE)
restrictFormat = savedState.getString(RESTRICT_FORMAT)
searchTerm = savedState.getString(SEARCH_TERM)
}
override fun onSaveInstanceState(outState: Bundle) {
@@ -235,6 +268,7 @@ class CameraActivity : AppCompatActivity() {
outState.putBoolean(FRONT_FACING, frontFacing)
outState.putBoolean(BULK_MODE, bulkMode)
outState.putString(RESTRICT_FORMAT, restrictFormat)
outState.putString(SEARCH_TERM, searchTerm)
super.onSaveInstanceState(outState)
}
@@ -295,6 +329,11 @@ class CameraActivity : AppCompatActivity() {
true
}
R.id.find_code -> {
askForCode()
true
}
R.id.preferences -> {
startActivity(MainActivity.getPreferencesIntent(this))
true
@@ -334,12 +373,36 @@ class CameraActivity : AppCompatActivity() {
setTitle(R.string.restrict_format)
setItems(names.toTypedArray()) { _, which ->
restrictFormat = formats[which]
updateHints()
updateHintsAndTitle()
}
show()
}
}
// Dialogs do not have a parent view.
@SuppressLint("InflateParams")
private fun askForCode() {
val view = layoutInflater.inflate(R.layout.dialog_find_code, null)
val editText = view.findViewById<EditText>(R.id.term)
searchTerm.let {
editText.setText(it)
}
AlertDialog.Builder(this)
.setView(view)
.setPositiveButton(android.R.string.ok) { _, _ ->
searchTerm = editText.text.toString()
if (searchTerm?.isEmpty() == true) {
searchTerm = null
}
updateTitle()
}
.setNegativeButton(android.R.string.cancel) { _, _ ->
searchTerm = null
updateTitle()
}
.show()
}
private fun openReadme() {
val intent = Intent(
Intent.ACTION_VIEW,
@@ -478,7 +541,8 @@ class CameraActivity : AppCompatActivity() {
tryInvert = true,
tryDownscale = true,
returnCodabarStartEnd = true,
maxNumberOfSymbols = 1
maxNumberOfSymbols = 1,
textMode = TextMode.PLAIN
)
var useLocalAverage = false
camera.setPreviewCallback { frameData, _ ->
@@ -503,7 +567,11 @@ class CameraActivity : AppCompatActivity() {
}
)?.let { results ->
val result = results.first()
if (result.text != ignoreNext) {
val text = result.text
val term = searchTerm
if (text != ignoreNext &&
(term == null || text.contains(term))
) {
postResult(result)
decoding = false
}
@@ -574,12 +642,21 @@ class CameraActivity : AppCompatActivity() {
updateFrameRoiAndMappingMatrix()
}
detectorView.setPaddingFromWindowInsets()
detectorView.restoreCropHandlePos()
detectorView.restoreCropHandlePos(CAMERA_CROP_HANDLE)
}
private fun updateFrameRoiAndMappingMatrix() {
val viewRect = cameraView.previewRect
val viewRoi = if (detectorView.roi.width() < 1) {
if (viewRect.width() < 1 ||
viewRect.height() < 1 ||
!frameMetrics.isValid()
) {
return
}
val viewRoi = if (
detectorView.roi.width() < 1 ||
detectorView.roi.height() < 1
) {
viewRect
} else {
detectorView.roi
@@ -674,12 +751,15 @@ class CameraActivity : AppCompatActivity() {
}
companion object {
const val CAMERA_CROP_HANDLE = "camera_crop_handle"
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"
private const val RESTRICT_FORMAT = "restrict_format"
private const val SEARCH_TERM = "search_term"
}
}
@@ -701,7 +781,7 @@ fun Activity.showResult(
if (prefs.sendScanActive && prefs.sendScanUrl.isNotEmpty()) {
if (prefs.sendScanType == "4") {
openUrl(
prefs.sendScanUrl + scan.content.urlEncode()
prefs.sendScanUrl + scan.text.urlEncode()
)
return
}
@@ -3,11 +3,11 @@ package de.markusfisch.android.binaryeye.activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.app.PERMISSION_LOCATION
import de.markusfisch.android.binaryeye.app.PERMISSION_WRITE
@@ -21,8 +21,7 @@ import de.markusfisch.android.binaryeye.fragment.EncodeFragment
import de.markusfisch.android.binaryeye.fragment.HistoryFragment
import de.markusfisch.android.binaryeye.fragment.PreferencesFragment
import de.markusfisch.android.binaryeye.view.colorSystemAndToolBars
import de.markusfisch.android.binaryeye.view.initSystemBars
import de.markusfisch.android.binaryeye.view.recordToolbarHeight
import de.markusfisch.android.binaryeye.view.initBars
class MainActivity : AppCompatActivity() {
override fun onRequestPermissionsResult(
@@ -59,10 +58,7 @@ class MainActivity : AppCompatActivity() {
super.onCreate(state)
setContentView(R.layout.activity_main)
initSystemBars(this)
val toolbar = findViewById(R.id.toolbar) as Toolbar
recordToolbarHeight(toolbar)
setSupportActionBar(toolbar)
initBars()
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportFragmentManager.addOnBackStackChangedListener {
@@ -85,6 +81,16 @@ class MainActivity : AppCompatActivity() {
return when {
intent.hasExtra(PREFERENCES) -> PreferencesFragment()
intent.hasExtra(HISTORY) -> HistoryFragment()
intent.dataString?.startsWith("binaryeye://encode") == true -> {
val uri = Uri.parse(intent.dataString)
EncodeFragment.newInstance(
uri.getQueryParameter("content"),
uri.getQueryParameter("format"),
!uri.getQueryParameter("execute").isNullOrEmpty()
)
}
intent.hasExtra(ENCODE) -> EncodeFragment.newInstance(
intent.getStringExtra(ENCODE)
)
@@ -10,7 +10,6 @@ import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import android.view.Menu
import android.view.MenuItem
import de.markusfisch.android.binaryeye.R
@@ -22,8 +21,7 @@ import de.markusfisch.android.binaryeye.graphics.loadImageUri
import de.markusfisch.android.binaryeye.graphics.mapPosition
import de.markusfisch.android.binaryeye.media.releaseToneGenerators
import de.markusfisch.android.binaryeye.view.colorSystemAndToolBars
import de.markusfisch.android.binaryeye.view.initSystemBars
import de.markusfisch.android.binaryeye.view.recordToolbarHeight
import de.markusfisch.android.binaryeye.view.initBars
import de.markusfisch.android.binaryeye.view.scanFeedback
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
import de.markusfisch.android.binaryeye.widget.CropImageView
@@ -75,10 +73,7 @@ class PickActivity : AppCompatActivity() {
// locale.
setTitle(R.string.pick_code_to_scan)
initSystemBars(this)
val toolbar = findViewById(R.id.toolbar) as Toolbar
recordToolbarHeight(toolbar)
setSupportActionBar(toolbar)
initBars()
supportFragmentManager.addOnBackStackChangedListener {
colorSystemAndToolBars(this@PickActivity)
@@ -91,20 +86,28 @@ class PickActivity : AppCompatActivity() {
return
}
cropImageView = findViewById(R.id.image) as CropImageView
cropImageView.restrictTranslation = false
cropImageView.freeRotation = prefs.freeRotation
cropImageView.setImageBitmap(bitmap)
cropImageView.onScan = {
scanWithinBounds(bitmap)
cropImageView = (findViewById(R.id.image) as CropImageView).apply {
restrictTranslation = false
freeRotation = prefs.freeRotation
setImageBitmap(bitmap)
onScan = {
scanWithinBounds(bitmap)
}
runAfterLayout = {
minWidth = min(
minWidth / 2f,
max(bitmap.width, bitmap.height).toFloat()
)
}
}
detectorView = findViewById(R.id.detector_view) as DetectorView
detectorView.onRoiChanged = {
scanWithinBounds(bitmap)
detectorView = (findViewById(R.id.detector_view) as DetectorView).apply {
onRoiChanged = {
scanWithinBounds(bitmap)
}
setPaddingFromWindowInsets()
restoreCropHandlePos(PICKER_CROP_HANDLE)
}
detectorView.setPaddingFromWindowInsets()
detectorView.restoreCropHandlePos()
findViewById(R.id.scan).setOnClickListener {
showResult()
@@ -200,7 +203,7 @@ class PickActivity : AppCompatActivity() {
override fun onDestroy() {
super.onDestroy()
detectorView.saveCropHandlePos()
detectorView.storeCropHandlePos(PICKER_CROP_HANDLE)
parentJob.cancel()
releaseToneGenerators()
}
@@ -242,7 +245,7 @@ class PickActivity : AppCompatActivity() {
}
private fun rotateClockwise() {
cropImageView.imageRotation = cropImageView.imageRotation + 90 % 360
cropImageView.imageRotation = (cropImageView.imageRotation + 90) % 360
}
private fun loadSentImage(intent: Intent): Bitmap? {
@@ -264,6 +267,10 @@ class PickActivity : AppCompatActivity() {
applicationContext.toast(R.string.no_barcode_found)
}
}
companion object {
private const val PICKER_CROP_HANDLE = "picker_crop_handle"
}
}
private fun getNormalizedRoi(imageRect: RectF, roi: Rect): RectF {
@@ -0,0 +1,32 @@
package de.markusfisch.android.binaryeye.activity
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.app.prefs
class WelcomeActivity : AppCompatActivity() {
override fun onCreate(state: Bundle?) {
super.onCreate(state)
setContentView(R.layout.activity_welcome)
findViewById(R.id.expert).setOnClickListener {
finish()
}
findViewById(R.id.simple).setOnClickListener {
prefs.apply {
showCropHandle = false
zoomBySwiping = false
beep = true
copyImmediately = true
openImmediately = true
showMetaData = false
showHexDump = false
defaultSearchUrl = "https://www.google.com/search?q="
openWithUrl = defaultSearchUrl
expandEscapeSequences = false
brightenScreen = true
}
finish()
}
}
}
@@ -20,7 +20,7 @@ class ScansAdapter(context: Context, cursor: Cursor) :
private val idIndex = cursor.getColumnIndex(Database.SCANS_ID)
private val timeIndex = cursor.getColumnIndex(Database.SCANS_DATETIME)
private val nameIndex = cursor.getColumnIndex(Database.SCANS_NAME)
private val contentIndex = cursor.getColumnIndex(Database.SCANS_CONTENT)
private val textIndex = cursor.getColumnIndex(Database.SCANS_TEXT)
private val formatIndex = cursor.getColumnIndex(Database.SCANS_FORMAT)
private val selections = mutableMapOf<Long, Int>()
private val selectedColor = ContextCompat.getColor(
@@ -74,7 +74,7 @@ class ScansAdapter(context: Context, cursor: Cursor) :
fun getContent(
position: Int
) = (getItem(position) as Cursor?)?.getString(contentIndex)
) = (getItem(position) as Cursor?)?.getString(textIndex)
override fun newView(
context: Context,
@@ -92,7 +92,7 @@ class ScansAdapter(context: Context, cursor: Cursor) :
getViewHolder(view).apply {
timeView.text = formatDateTime(cursor.getString(timeIndex))
val name = cursor.getString(nameIndex)
val content = cursor.getString(contentIndex)
val text = cursor.getString(textIndex)
var icon = 0
contentView.text = when {
name?.isNotEmpty() == true -> {
@@ -100,11 +100,11 @@ class ScansAdapter(context: Context, cursor: Cursor) :
name
}
content.isNullOrEmpty() -> context.getString(
text.isNullOrEmpty() -> context.getString(
R.string.binary_data
)
else -> content
else -> text
}
contentView.setCompoundDrawablesWithIntrinsicBounds(
icon, 0, 0, 0
@@ -24,7 +24,7 @@ fun Scan.sendBluetoothAsync(
connect(host, true)
}
val sent = if (connected) {
send(content, host, true)
send(text, host, true)
} else {
false
}
@@ -0,0 +1,197 @@
package de.markusfisch.android.binaryeye.content
import android.graphics.Bitmap
import de.markusfisch.android.binaryeye.database.Scan
import de.markusfisch.android.binaryeye.graphics.COLOR_BLACK
import de.markusfisch.android.binaryeye.graphics.COLOR_WHITE
import de.markusfisch.android.zxingcpp.ZxingCpp
import de.markusfisch.android.zxingcpp.ZxingCpp.BarcodeFormat
import de.markusfisch.android.zxingcpp.ZxingCpp.BitMatrix
import de.markusfisch.android.zxingcpp.ZxingCpp.toBitmap
import de.markusfisch.android.zxingcpp.ZxingCpp.toSvg
import de.markusfisch.android.zxingcpp.ZxingCpp.toText
import java.util.Arrays
enum class BarcodeColors {
BLACK_ON_WHITE,
WHITE_ON_BLACK,
BLACK_ON_TRANSPARENT,
WHITE_ON_TRANSPARENT;
fun foregroundColor(): Int = when (this) {
BLACK_ON_WHITE,
BLACK_ON_TRANSPARENT -> COLOR_BLACK
WHITE_ON_BLACK,
WHITE_ON_TRANSPARENT -> COLOR_WHITE
}
fun backgroundColor(): Int = when (this) {
BLACK_ON_WHITE -> COLOR_WHITE
WHITE_ON_BLACK -> COLOR_BLACK
BLACK_ON_TRANSPARENT,
WHITE_ON_TRANSPARENT -> 0
}
}
fun Scan.toBarcode(): Barcode<*> {
val content = text.ifEmpty {
raw
}
return if (symbol != null) {
BitMatrixBarcode(
symbol,
content,
format
)
} else {
ContentBarcode(
content,
format,
errorCorrectionLevel.toErrorCorrectionInt()
)
}
}
fun String?.toErrorCorrectionInt() = when (this) {
"L" -> 0
"M" -> 4
"Q" -> 6
"H" -> 8
else -> -1
}
abstract class Barcode<T>(
val content: T,
val format: BarcodeFormat,
val colors: BarcodeColors
) {
private var _bitmap: Bitmap? = null
fun bitmap(): Bitmap {
val b = _bitmap ?: bitmap(512)
_bitmap = b
return b
}
fun bitmap(size: Int): Bitmap {
return toBitmap(size)
}
protected abstract fun toBitmap(size: Int): Bitmap
private var _svg: String? = null
fun svg(): String {
val s = _svg ?: toSvg()
_svg = s
return s
}
protected abstract fun toSvg(): String
private var _text: String? = null
fun text(): String {
val t = _text ?: toText()
_text = t
return t
}
protected abstract fun toText(): String
}
class ContentBarcode<T>(
content: T,
format: BarcodeFormat,
val ecLevel: Int = -1,
val margin: Int = -1,
colors: BarcodeColors = BarcodeColors.BLACK_ON_WHITE
) : Barcode<T>(content, format, colors) {
override fun toBitmap(size: Int) = ZxingCpp.encodeAsBitmap(
content, format, size, format.height(size), margin, ecLevel,
setColor = colors.foregroundColor(),
unsetColor = colors.backgroundColor()
)
override fun toSvg() = ZxingCpp.encodeAsSvg(
content, format, margin, ecLevel
)
override fun toText() = ZxingCpp.encodeAsText(
content, format, margin, ecLevel,
inverted = colors == BarcodeColors.BLACK_ON_WHITE
)
}
class BitMatrixBarcode<T>(
val bitMatrix: BitMatrix,
content: T,
format: BarcodeFormat,
colors: BarcodeColors = BarcodeColors.BLACK_ON_WHITE
) : Barcode<T>(content, format, colors) {
override fun toBitmap(size: Int) = bitMatrix.inflate(size).toBitmap(
setColor = colors.foregroundColor(),
unsetColor = colors.backgroundColor()
)
override fun toSvg() = bitMatrix.inflate().toSvg()
override fun toText() = bitMatrix.inflate().toText(
inverted = colors == BarcodeColors.BLACK_ON_WHITE
)
}
private fun BitMatrix.inflate(
size: Int = 0,
quietZone: Boolean = true
): BitMatrix {
val extraModules = if (quietZone) 2 else 0
val codeWidth = width + extraModules
val codeHeight = height + extraModules
var scale = size / codeWidth
if (scale < 1) {
if (size > 0 && !quietZone) {
return this
}
scale = 1
}
val outWidth = codeWidth * scale
val outHeight = codeHeight * scale
val scaled = BitMatrix(
outWidth,
outHeight,
ByteArray(outWidth * outHeight)
)
Arrays.fill(scaled.data, 1)
val padding = extraModules / 2
val left = padding * scale
val top = padding * scale
val right = outWidth - left
val bottom = outHeight - top
for (y in top until bottom) {
var dst = y * outWidth + left
val offset = (y - top) / scale * width
for (x in left until right) {
scaled.data[dst++] = data[
offset + (x - left) / scale
]
}
}
return scaled
}
private fun BarcodeFormat.height(size: Int) = when (this) {
// 1D barcodes don't need as much vertical space.
BarcodeFormat.CODABAR,
BarcodeFormat.CODE_39,
BarcodeFormat.CODE_93,
BarcodeFormat.CODE_128,
BarcodeFormat.DATA_BAR,
BarcodeFormat.DATA_BAR_EXPANDED,
BarcodeFormat.DX_FILM_EDGE,
BarcodeFormat.EAN_8,
BarcodeFormat.EAN_13,
BarcodeFormat.ITF,
BarcodeFormat.UPC_A,
BarcodeFormat.UPC_E -> size / 3
else -> size
}
@@ -33,7 +33,7 @@ private fun exportCsv(
Database.SCANS_DATETIME,
Database.SCANS_FORMAT,
Database.SCANS_NAME,
Database.SCANS_CONTENT,
Database.SCANS_TEXT,
Database.SCANS_ERROR_CORRECTION_LEVEL,
Database.SCANS_VERSION,
Database.SCANS_SEQUENCE_SIZE,
@@ -47,7 +47,7 @@ private fun exportCsv(
val indices = columns.map {
cursor.getColumnIndex(it)
}
val contentIndex = cursor.getColumnIndex(Database.SCANS_CONTENT)
val contentIndex = cursor.getColumnIndex(Database.SCANS_TEXT)
val rawIndex = cursor.getColumnIndex(Database.SCANS_RAW)
outputStream.write(
columns.joinToString(
@@ -1,6 +1,7 @@
package de.markusfisch.android.binaryeye.database
import android.database.Cursor
import de.markusfisch.android.zxingcpp.ZxingCpp
// Overwrite Kotlin's ".use" function for Cursor because Cursor cannot
// be cast to Closeable below API level 16. This should be removed when
@@ -30,3 +31,18 @@ fun Cursor.getBlob(name: String): ByteArray? {
val idx = getColumnIndex(name)
return if (idx < 0) null else getBlob(idx)
}
fun Cursor.getBitMatrix(
widthName: String,
heightName: String,
dataName: String
): ZxingCpp.BitMatrix? {
val width = getInt(widthName)
val height = getInt(heightName)
val data = getBlob(dataName)
return if (width > 0 && height > 0 && data != null) {
ZxingCpp.BitMatrix(width, height, data)
} else {
null
}
}
@@ -16,12 +16,12 @@ class Database {
db = OpenHelper(context).writableDatabase
}
fun getScans(query: String? = null): Cursor? = db.rawQuery(
fun getScans(query: String? = null): Cursor = db.rawQuery(
"""SELECT
$SCANS_ID,
$SCANS_DATETIME,
$SCANS_NAME,
$SCANS_CONTENT,
$SCANS_TEXT,
$SCANS_FORMAT
FROM $SCANS
${getWhereClause(query)}
@@ -29,12 +29,12 @@ class Database {
""".trimMargin(), getWhereArguments(query)
)
fun getScansDetailed(query: String? = null): Cursor? = db.rawQuery(
fun getScansDetailed(query: String? = null): Cursor = db.rawQuery(
"""SELECT
$SCANS_ID,
$SCANS_DATETIME,
$SCANS_NAME,
$SCANS_CONTENT,
$SCANS_TEXT,
$SCANS_RAW,
$SCANS_FORMAT,
$SCANS_ERROR_CORRECTION_LEVEL,
@@ -56,7 +56,7 @@ class Database {
query: String?,
prefix: String = "WHERE"
) = if (query?.isNotEmpty() == true) {
"""$prefix $SCANS_CONTENT LIKE ?
"""$prefix $SCANS_TEXT LIKE ?
OR $SCANS_NAME LIKE ?"""
} else {
""
@@ -71,12 +71,12 @@ class Database {
null
}
fun getScansDetailed(ids: LongArray): Cursor? = db.rawQuery(
fun getScansDetailed(ids: LongArray): Cursor = db.rawQuery(
"""SELECT
$SCANS_ID,
$SCANS_DATETIME,
$SCANS_NAME,
$SCANS_CONTENT,
$SCANS_TEXT,
$SCANS_RAW,
$SCANS_FORMAT,
$SCANS_ERROR_CORRECTION_LEVEL,
@@ -99,11 +99,15 @@ class Database {
$SCANS_ID,
$SCANS_DATETIME,
$SCANS_NAME,
$SCANS_CONTENT,
$SCANS_TEXT,
$SCANS_RAW,
$SCANS_FORMAT,
$SCANS_ERROR_CORRECTION_LEVEL,
$SCANS_VERSION,
$SCANS_DATA_MASK,
$SCANS_SYMBOL_WIDTH,
$SCANS_SYMBOL_HEIGHT,
$SCANS_SYMBOL_DATA,
$SCANS_SEQUENCE_SIZE,
$SCANS_SEQUENCE_INDEX,
$SCANS_SEQUENCE_ID,
@@ -114,14 +118,20 @@ class Database {
FROM $SCANS
WHERE $SCANS_ID = ?
""".trimMargin(), arrayOf("$id")
)?.use {
).use {
if (it.moveToFirst()) {
Scan(
it.getString(SCANS_CONTENT),
it.getString(SCANS_TEXT),
it.getBlob(SCANS_RAW),
BarcodeFormat.valueOf(it.getString(SCANS_FORMAT)),
it.getString(SCANS_ERROR_CORRECTION_LEVEL),
it.getString(SCANS_VERSION),
it.getInt(SCANS_DATA_MASK),
it.getBitMatrix(
SCANS_SYMBOL_WIDTH,
SCANS_SYMBOL_HEIGHT,
SCANS_SYMBOL_DATA
),
it.getInt(SCANS_SEQUENCE_SIZE),
it.getInt(SCANS_SEQUENCE_INDEX),
it.getString(SCANS_SEQUENCE_ID),
@@ -142,7 +152,7 @@ class Database {
when (prefs.ignoreDuplicates()) {
Preferences.Companion.IgnoreDuplicates.Consecutive -> {
val id = getIdOfLastScan(
scan.content,
scan.text,
scan.raw,
scan.format
)
@@ -152,7 +162,7 @@ class Database {
}
Preferences.Companion.IgnoreDuplicates.Any -> {
val id = getIdOfScanByContent(scan.content, scan.format)
val id = getIdOfScanByContent(scan.text, scan.format)
if (id > 0L) {
return id
}
@@ -165,7 +175,7 @@ class Database {
null,
ContentValues().apply {
put(SCANS_DATETIME, scan.dateTime)
put(SCANS_CONTENT, scan.content)
put(SCANS_TEXT, scan.text)
if (scan.raw != null) {
put(SCANS_RAW, scan.raw)
}
@@ -174,6 +184,12 @@ class Database {
put(SCANS_ERROR_CORRECTION_LEVEL, it)
}
put(SCANS_VERSION, scan.version)
put(SCANS_DATA_MASK, scan.dataMask)
if (scan.symbol != null) {
put(SCANS_SYMBOL_WIDTH, scan.symbol.width)
put(SCANS_SYMBOL_HEIGHT, scan.symbol.height)
put(SCANS_SYMBOL_DATA, scan.symbol.data)
}
put(SCANS_SEQUENCE_SIZE, scan.sequenceSize)
put(SCANS_SEQUENCE_INDEX, scan.sequenceIndex)
put(SCANS_SEQUENCE_ID, scan.sequenceId)
@@ -192,17 +208,17 @@ class Database {
): Long = db.rawQuery(
"""SELECT
$SCANS_ID,
$SCANS_CONTENT,
$SCANS_TEXT,
$SCANS_RAW,
$SCANS_FORMAT
FROM $SCANS
ORDER BY $SCANS_ID DESC
LIMIT 1
""".trimMargin(), null
)?.use {
).use {
if (it.count > 0 &&
it.moveToFirst() &&
it.getString(SCANS_CONTENT) == content &&
it.getString(SCANS_TEXT) == content &&
(raw == null || it.getBlob(SCANS_RAW)
?.contentEquals(raw) == true) &&
it.getString(SCANS_FORMAT) == format.name
@@ -211,26 +227,26 @@ class Database {
} else {
0L
}
} ?: 0L
}
fun getIdOfScanByContent(
private fun getIdOfScanByContent(
content: String,
format: BarcodeFormat
): Long = db.rawQuery(
"""SELECT
$SCANS_ID
FROM $SCANS
WHERE $SCANS_CONTENT = ?
WHERE $SCANS_TEXT = ?
AND $SCANS_FORMAT = ?
LIMIT 1
""".trimMargin(), arrayOf(content, format.name)
)?.use {
).use {
if (it.moveToFirst()) {
it.getLong(SCANS_ID)
} else {
0L
}
} ?: 0L
}
fun removeScan(id: Long) {
db.delete(SCANS, "$SCANS_ID = ?", arrayOf("$id"))
@@ -247,7 +263,7 @@ class Database {
}
private class OpenHelper(context: Context) :
SQLiteOpenHelper(context, FILE_NAME, null, 6) {
SQLiteOpenHelper(context, FILE_NAME, null, 8) {
override fun onCreate(db: SQLiteDatabase) {
db.createScans()
}
@@ -272,6 +288,12 @@ class Database {
if (oldVersion < 6) {
db.migrateToVersionString()
}
if (oldVersion < 7) {
db.addDataMaskColumn()
}
if (oldVersion < 8) {
db.addSymbolColumns()
}
}
}
@@ -281,12 +303,16 @@ class Database {
const val SCANS_ID = "_id"
const val SCANS_DATETIME = "_datetime"
const val SCANS_NAME = "name"
const val SCANS_CONTENT = "content"
const val SCANS_TEXT = "content"
const val SCANS_RAW = "raw"
const val SCANS_FORMAT = "format"
const val SCANS_ERROR_CORRECTION_LEVEL = "error_correction_level"
const val SCANS_VERSION_NUMBER = "version_number"
const val SCANS_VERSION = "version"
const val SCANS_DATA_MASK = "data_mask"
const val SCANS_SYMBOL_WIDTH = "symbol_width"
const val SCANS_SYMBOL_HEIGHT = "symbol_height"
const val SCANS_SYMBOL_DATA = "symbol_data"
const val SCANS_ISSUE_NUMBER = "issue_number"
const val SCANS_ORIENTATION = "orientation"
const val SCANS_OTHER_META_DATA = "other_meta_data"
@@ -309,11 +335,15 @@ class Database {
$SCANS_ID INTEGER PRIMARY KEY AUTOINCREMENT,
$SCANS_DATETIME DATETIME NOT NULL,
$SCANS_NAME TEXT,
$SCANS_CONTENT TEXT NOT NULL,
$SCANS_TEXT TEXT NOT NULL,
$SCANS_RAW BLOB,
$SCANS_FORMAT TEXT NOT NULL,
$SCANS_ERROR_CORRECTION_LEVEL TEXT,
$SCANS_VERSION TEXT,
$SCANS_DATA_MASK INTEGER,
$SCANS_SYMBOL_WIDTH INTEGER,
$SCANS_SYMBOL_HEIGHT INTEGER,
$SCANS_SYMBOL_DATA BLOB,
$SCANS_SEQUENCE_SIZE INTEGER,
$SCANS_SEQUENCE_INDEX INTEGER,
$SCANS_SEQUENCE_ID TEXT,
@@ -341,7 +371,7 @@ class Database {
}
private fun SQLiteDatabase.addNameColumn() {
execSQL("ALTER TABLE $SCANS ADD COLUMN $SCANS_NAME TEXT".trimMargin())
execSQL("ALTER TABLE $SCANS ADD COLUMN $SCANS_NAME TEXT")
}
private fun SQLiteDatabase.migrateToZxingCpp() {
@@ -365,5 +395,16 @@ class Database {
execSQL("ALTER TABLE $SCANS ADD $SCANS_VERSION TEXT")
execSQL("UPDATE $SCANS SET $SCANS_VERSION = $SCANS_VERSION_NUMBER")
}
private fun SQLiteDatabase.addDataMaskColumn() {
execSQL("ALTER TABLE $SCANS ADD $SCANS_DATA_MASK INTEGER")
execSQL("UPDATE $SCANS SET $SCANS_DATA_MASK = -1")
}
private fun SQLiteDatabase.addSymbolColumns() {
execSQL("ALTER TABLE $SCANS ADD $SCANS_SYMBOL_WIDTH INTEGER")
execSQL("ALTER TABLE $SCANS ADD $SCANS_SYMBOL_HEIGHT INTEGER")
execSQL("ALTER TABLE $SCANS ADD $SCANS_SYMBOL_DATA BLOB")
}
}
}
@@ -25,7 +25,7 @@ fun Cursor.exportJson(): String? {
Database.SCANS_DATETIME,
Database.SCANS_FORMAT,
Database.SCANS_NAME,
Database.SCANS_CONTENT,
Database.SCANS_TEXT,
Database.SCANS_ERROR_CORRECTION_LEVEL,
Database.SCANS_VERSION,
Database.SCANS_SEQUENCE_SIZE,
@@ -39,7 +39,7 @@ fun Cursor.exportJson(): String? {
val indices = columns.map {
Pair(getColumnIndex(it), it)
}
val contentIndex = getColumnIndex(Database.SCANS_CONTENT)
val contentIndex = getColumnIndex(Database.SCANS_TEXT)
val rawIndex = getColumnIndex(Database.SCANS_RAW)
val root = JSONArray()
do {
@@ -1,21 +1,23 @@
package de.markusfisch.android.binaryeye.database
import android.graphics.Bitmap
import android.os.Build
import android.os.Parcel
import android.os.Parcelable
import android.text.format.DateFormat
import de.markusfisch.android.zxingcpp.ZxingCpp
import de.markusfisch.android.zxingcpp.ZxingCpp.BarcodeFormat
import de.markusfisch.android.zxingcpp.ZxingCpp.BitMatrix
import de.markusfisch.android.zxingcpp.ZxingCpp.ContentType
import de.markusfisch.android.zxingcpp.ZxingCpp.Result
import java.util.Locale
data class Scan(
val content: String,
val text: String,
val raw: ByteArray?,
val format: BarcodeFormat,
val errorCorrectionLevel: String? = null,
val version: String? = null,
val dataMask: Int = -1,
val symbol: BitMatrix? = null,
val sequenceSize: Int = -1,
val sequenceIndex: Int = -1,
val sequenceId: String = "",
@@ -41,12 +43,13 @@ data class Scan(
return id == other.id &&
dateTime == other.dateTime &&
content == other.content &&
text == other.text &&
((raw == null && other.raw == null) ||
(raw != null && other.raw != null && raw.contentEquals(other.raw))) &&
format == other.format &&
errorCorrectionLevel == other.errorCorrectionLevel &&
version == other.version &&
dataMask == other.dataMask &&
sequenceSize == other.sequenceSize &&
sequenceIndex == other.sequenceIndex &&
sequenceId == other.sequenceId &&
@@ -61,11 +64,12 @@ data class Scan(
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + dateTime.hashCode()
result = 31 * result + content.hashCode()
result = 31 * result + text.hashCode()
result = 31 * result + (raw?.contentHashCode() ?: 0)
result = 31 * result + format.hashCode()
result = 31 * result + (errorCorrectionLevel?.hashCode() ?: 0)
result = 31 * result + version.hashCode()
result = 31 * result + dataMask
result = 31 * result + sequenceSize
result = 31 * result + sequenceIndex
result = 31 * result + sequenceId.hashCode()
@@ -77,11 +81,13 @@ data class Scan(
}
private constructor(parcel: Parcel) : this(
content = parcel.readString() ?: "",
text = parcel.readString() ?: "",
raw = parcel.readSizedByteArray(),
format = BarcodeFormat.valueOf(parcel.readString() ?: ""),
errorCorrectionLevel = parcel.readString(),
version = parcel.readString(),
dataMask = parcel.readInt(),
symbol = parcel.readBitMatrix(),
sequenceSize = parcel.readInt(),
sequenceIndex = parcel.readInt(),
sequenceId = parcel.readString() ?: "",
@@ -96,11 +102,13 @@ data class Scan(
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.apply {
writeString(content)
writeString(text)
writeSizedByteArray(raw)
writeString(format.name)
writeString(errorCorrectionLevel)
writeString(version)
writeInt(dataMask)
writeBitMatrix(symbol)
writeInt(sequenceSize)
writeInt(sequenceIndex)
writeString(sequenceId)
@@ -132,12 +140,14 @@ fun Result.toScan(): Scan {
ContentType.ISO15434,
ContentType.TEXT -> text
else -> ""
else -> "" // Show as binary.
},
rawBytes,
format,
ecLevel,
version,
dataMask,
symbol,
sequenceSize,
sequenceIndex,
sequenceId,
@@ -148,37 +158,6 @@ fun Result.toScan(): Scan {
)
}
data class Recreation(
val format: BarcodeFormat,
val ecLevel: Int,
val size: Int,
val margin: Int
) {
fun <T> encode(content: T): Bitmap? = try {
ZxingCpp.encodeAsBitmap(
content, format, size, size, margin, ecLevel
)
} catch (_: RuntimeException) {
null
}
}
fun Scan.toRecreation(
size: Int = 128,
margin: Int = -1
) = Recreation(
format,
when (errorCorrectionLevel) {
"L" -> 0
"M" -> 4
"Q" -> 6
"H" -> 8
else -> -1
},
size,
margin
)
private fun getDateTime(
time: Long = System.currentTimeMillis()
) = DateFormat.format(
@@ -190,7 +169,11 @@ private fun getDateTime(
"yyyy-MM-dd HH:mm:ss"
},
time
).toString() + String.format(":%03d", (time % 1000))
).toString() + String.format(
Locale.getDefault(),
":%03d",
time % 1000
)
private fun Parcel.writeSizedByteArray(array: ByteArray?) {
val size = array?.size ?: 0
@@ -210,3 +193,21 @@ private fun Parcel.readSizedByteArray(): ByteArray? {
null
}
}
private fun Parcel.writeBitMatrix(bm: BitMatrix?) {
writeInt(bm?.width ?: 0)
bm?.let {
writeInt(bm.height)
writeSizedByteArray(bm.data)
}
}
private fun Parcel.readBitMatrix(): BitMatrix? {
val width = readInt()
if (width < 1) {
return null
}
val height = readInt()
val data = readSizedByteArray() ?: return null
return BitMatrix(width, height, data)
}
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Matrix
import android.os.Build
import android.os.Bundle
import android.support.v4.app.Fragment
@@ -14,18 +15,21 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.ImageView
import android.widget.SeekBar
import android.widget.TextView
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.app.db
import de.markusfisch.android.binaryeye.app.hasWritePermission
import de.markusfisch.android.binaryeye.app.prefs
import de.markusfisch.android.binaryeye.content.Barcode
import de.markusfisch.android.binaryeye.content.BarcodeColors
import de.markusfisch.android.binaryeye.content.BitMatrixBarcode
import de.markusfisch.android.binaryeye.content.ContentBarcode
import de.markusfisch.android.binaryeye.content.copyToClipboard
import de.markusfisch.android.binaryeye.content.shareFile
import de.markusfisch.android.binaryeye.content.shareText
import de.markusfisch.android.binaryeye.database.toScan
import de.markusfisch.android.binaryeye.graphics.COLOR_BLACK
import de.markusfisch.android.binaryeye.graphics.COLOR_WHITE
import de.markusfisch.android.binaryeye.io.addSuffixIfNotGiven
import de.markusfisch.android.binaryeye.io.toSaveResult
import de.markusfisch.android.binaryeye.io.writeExternalFile
@@ -58,11 +62,12 @@ class BarcodeFragment : Fragment() {
private val parentJob = Job()
private val scope = CoroutineScope(Dispatchers.IO + parentJob)
private lateinit var imageView: ConfinedScalingImageView
private lateinit var barcode: Barcode<*>
private lateinit var addToHistoryItem: MenuItem
private lateinit var brightenScreenItem: MenuItem
private var currentBrightness = -1f;
private var currentBrightness = -1f
override fun onCreate(state: Bundle?) {
super.onCreate(state)
@@ -83,6 +88,9 @@ class BarcodeFragment : Fragment() {
false
)
// Make `imageView` available for `onPause()`.
imageView = view.findViewById(R.id.barcode)
val bitmap: Bitmap
try {
barcode = arguments?.toBarcode() ?: throw IllegalArgumentException(
@@ -102,12 +110,17 @@ class BarcodeFragment : Fragment() {
return null
}
val imageView = view.findViewById<ConfinedScalingImageView>(
R.id.barcode
)
imageView.setImageBitmap(bitmap)
imageView.post {
// Make sure to invoke this after ScalingImageView.onLayout().
imageView.runAfterLayout = {
val scale = prefs.previewScale
if (scale > 0) {
imageView.setScale(
scale,
bitmap.width / 2f,
bitmap.height / 2f
)
}
imageView.minWidth = min(
imageView.minWidth / 2f,
max(bitmap.width, bitmap.height).toFloat()
@@ -123,26 +136,46 @@ class BarcodeFragment : Fragment() {
}
view.findViewById<View>(R.id.inset_layout).setPaddingFromWindowInsets()
imageView.doOnApplyWindowInsets { v, insets ->
(v as ConfinedScalingImageView).insets.set(insets)
imageView.doOnApplyWindowInsets { _, insets ->
imageView.insets.set(insets)
}
return view
}
private fun Bundle.toBarcode() = Barcode(
getString(CONTENT_TEXT) ?: getByteArray(CONTENT_RAW) ?: throw IllegalArgumentException(
"content cannot be null"
),
BarcodeFormat.valueOf(
private fun Bundle.toBarcode(): Barcode<*> {
val content =
getString(CONTENT_TEXT) ?: getByteArray(CONTENT_RAW) ?: throw IllegalArgumentException(
"content cannot be null"
)
val barcodeFormat = BarcodeFormat.valueOf(
getString(FORMAT) ?: throw IllegalArgumentException(
"format cannot be null"
)
),
getInt(MARGIN),
getInt(EC_LEVEL),
Colors.entries[getInt(COLORS)]
)
)
val colors = BarcodeColors.entries[getInt(COLORS)]
val data = getByteArray(BIT_MATRIX_DATA)
return if (data != null) {
BitMatrixBarcode(
ZxingCpp.BitMatrix(
getInt(BIT_MATRIX_WIDTH),
getInt(BIT_MATRIX_HEIGHT),
data
),
content,
barcodeFormat,
colors
)
} else {
ContentBarcode(
content,
barcodeFormat,
getInt(EC_LEVEL),
getInt(MARGIN),
colors
)
}
}
override fun onResume() {
super.onResume()
@@ -159,6 +192,9 @@ class BarcodeFragment : Fragment() {
if (currentBrightness > -1f) {
restoreScreenBrightness()
}
if (imageView.isInBounds()) {
prefs.previewScale = imageView.getScale()
}
}
override fun onDestroyView() {
@@ -398,46 +434,90 @@ class BarcodeFragment : Fragment() {
private fun brightenScreen() {
activity?.let {
currentBrightness = it.getScreenBrightness()
it.setScreenBrightness(1f);
it.setScreenBrightness(1f)
brightenScreenItem.isChecked = true
}
}
private fun restoreScreenBrightness() {
if (currentBrightness > -1f) {
activity?.setScreenBrightness(currentBrightness);
activity?.setScreenBrightness(currentBrightness)
currentBrightness = -1f
brightenScreenItem.isChecked = false
}
}
companion object {
private const val BIT_MATRIX_WIDTH = "bit_matrix_width"
private const val BIT_MATRIX_HEIGHT = "bit_matrix_height"
private const val BIT_MATRIX_DATA = "bit_matrix_data"
private const val CONTENT_TEXT = "content_text"
private const val CONTENT_RAW = "content_raw"
private const val FORMAT = "format"
private const val MARGIN = "margin"
private const val EC_LEVEL = "ec_level"
private const val MARGIN = "margin"
private const val COLORS = "colors"
private const val MIME_PNG = "image/png"
private const val MIME_JPG = "image/jpeg"
private const val MIME_SVG = "image/svg+xmg"
private const val MIME_TXT = "text/plain"
fun <T> newInstance(
content: T,
format: BarcodeFormat,
margin: Int,
ecLevel: Int = -1,
fun newInstance(
barcode: Barcode<*>,
colors: Int = 0
): Fragment {
val args = Bundle()
when (barcode) {
is ContentBarcode -> args.putEcLevelMargin(
barcode.ecLevel,
barcode.margin
)
is BitMatrixBarcode -> {
args.putInt(BIT_MATRIX_WIDTH, barcode.bitMatrix.width)
args.putInt(BIT_MATRIX_HEIGHT, barcode.bitMatrix.height)
args.putByteArray(BIT_MATRIX_DATA, barcode.bitMatrix.data)
}
else -> throw IllegalArgumentException("unknown barcode")
}
args.putContentFormatColors(
barcode.content,
barcode.format,
colors
)
val fragment = BarcodeFragment()
fragment.arguments = args
return fragment
}
fun <T> newInstance(
content: T,
format: BarcodeFormat,
ecLevel: Int = -1,
margin: Int = -1,
colors: Int = 0
): Fragment {
val args = Bundle()
args.putContentFormatColors(content, format, colors)
args.putEcLevelMargin(ecLevel, margin)
val fragment = BarcodeFragment()
fragment.arguments = args
return fragment
}
private fun <T> Bundle.putContentFormatColors(
content: T,
format: BarcodeFormat,
colors: Int
) {
when (content) {
is String -> {
args.putString(CONTENT_TEXT, content)
putString(CONTENT_TEXT, content)
}
is ByteArray -> {
args.putByteArray(CONTENT_RAW, content)
putByteArray(CONTENT_RAW, content)
}
else -> {
@@ -446,79 +526,32 @@ class BarcodeFragment : Fragment() {
)
}
}
args.putString(FORMAT, format.name)
args.putInt(MARGIN, margin)
args.putInt(EC_LEVEL, ecLevel)
args.putInt(COLORS, colors)
val fragment = BarcodeFragment()
fragment.arguments = args
return fragment
putString(FORMAT, format.name)
putInt(COLORS, colors)
}
private fun Bundle.putEcLevelMargin(
ecLevel: Int,
margin: Int
) {
putInt(EC_LEVEL, ecLevel)
putInt(MARGIN, margin)
}
}
}
private data class Barcode<T>(
val content: T,
val format: BarcodeFormat,
val margin: Int,
val ecLevel: Int,
val colors: Colors
) {
private var _bitmap: Bitmap? = null
fun bitmap(): Bitmap {
val b = _bitmap ?: bitmap(512)
_bitmap = b
return b
}
fun bitmap(size: Int): Bitmap {
return ZxingCpp.encodeAsBitmap(
content, format, size, size, margin, ecLevel,
setColor = colors.foregroundColor(),
unsetColor = colors.backgroundColor()
)
}
private var _svg: String? = null
fun svg(): String {
val s = _svg ?: ZxingCpp.encodeAsSvg(
content, format, margin, ecLevel
)
_svg = s
return s
}
private var _text: String? = null
fun text(): String {
val t = _text ?: ZxingCpp.encodeAsText(
content, format, margin, ecLevel,
inverted = colors == Colors.BLACK_ON_WHITE
)
_text = t
return t
private fun ImageView.setScale(scale: Float, x: Float, y: Float) {
imageMatrix = Matrix().apply {
setTranslate(-x, -y)
postScale(scale, scale)
postTranslate(x, y)
}
}
private enum class Colors {
BLACK_ON_WHITE,
WHITE_ON_BLACK,
BLACK_ON_TRANSPARENT,
WHITE_ON_TRANSPARENT;
fun foregroundColor(): Int = when (this) {
BLACK_ON_WHITE,
BLACK_ON_TRANSPARENT -> COLOR_BLACK
WHITE_ON_BLACK,
WHITE_ON_TRANSPARENT -> COLOR_WHITE
}
fun backgroundColor(): Int = when (this) {
BLACK_ON_WHITE -> COLOR_WHITE
WHITE_ON_BLACK -> COLOR_BLACK
BLACK_ON_TRANSPARENT,
WHITE_ON_TRANSPARENT -> 0
}
private fun ImageView.getScale(): Float {
val values = FloatArray(9)
imageMatrix.getValues(values)
return values[Matrix.MSCALE_X]
}
private fun Bitmap.saveAsPng(outputStream: OutputStream, quality: Int = 90) =
@@ -33,19 +33,22 @@ import de.markusfisch.android.binaryeye.app.db
import de.markusfisch.android.binaryeye.app.hasLocationPermission
import de.markusfisch.android.binaryeye.app.hasWritePermission
import de.markusfisch.android.binaryeye.app.prefs
import de.markusfisch.android.binaryeye.content.ContentBarcode
import de.markusfisch.android.binaryeye.content.copyToClipboard
import de.markusfisch.android.binaryeye.content.shareAsFile
import de.markusfisch.android.binaryeye.content.shareText
import de.markusfisch.android.binaryeye.content.toBarcode
import de.markusfisch.android.binaryeye.content.toErrorCorrectionInt
import de.markusfisch.android.binaryeye.content.toHexString
import de.markusfisch.android.binaryeye.content.wipeLastShareFile
import de.markusfisch.android.binaryeye.database.Recreation
import de.markusfisch.android.binaryeye.database.Scan
import de.markusfisch.android.binaryeye.database.toRecreation
import de.markusfisch.android.binaryeye.io.askForFileName
import de.markusfisch.android.binaryeye.io.toSaveResult
import de.markusfisch.android.binaryeye.io.writeExternalFile
import de.markusfisch.android.binaryeye.view.hideSoftKeyboard
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
import de.markusfisch.android.binaryeye.widget.toast
import de.markusfisch.android.zxingcpp.ZxingCpp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -64,6 +67,7 @@ class DecodeFragment : Fragment() {
private lateinit var recreationView: ImageView
private lateinit var labelView: EditText
private lateinit var fab: FloatingActionButton
private lateinit var scan: Scan
private lateinit var format: String
private val parentJob = Job()
@@ -77,9 +81,8 @@ class DecodeFragment : Fragment() {
private var action = ActionRegistry.DEFAULT_ACTION
private var isBinary = false
private var originalBytes: ByteArray = ByteArray(0)
private var id = 0L
private var recreation: Recreation? = null
private var label: String? = null
private var recreationSize = 0
override fun onCreate(state: Bundle?) {
super.onCreate(state)
@@ -99,21 +102,20 @@ class DecodeFragment : Fragment() {
false
)
closeAutomatically = prefs.closeAutomatically &&
activity?.intent?.hasExtra(MainActivity.DECODED) == true
val justScanned = activity?.intent?.hasExtra(MainActivity.DECODED) == true
closeAutomatically = prefs.closeAutomatically && justScanned
val scan = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
scan = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
arguments?.getParcelable(SCAN, Scan::class.java)
} else {
@Suppress("DEPRECATION")
arguments?.getParcelable(SCAN) as Scan?
} ?: throw IllegalArgumentException("DecodeFragment needs a Scan")
val originalContent = scan.content
isBinary = scan.content.isEmpty()
originalBytes = scan.raw ?: originalContent.toByteArray()
isBinary = scan.text.isEmpty()
originalBytes = scan.raw ?: scan.text.toByteArray()
format = scan.format.name
id = scan.id
recreationSize = (200f * dp).roundToInt()
contentView = view.findViewById(R.id.content)
formatView = view.findViewById(R.id.format)
@@ -125,18 +127,13 @@ class DecodeFragment : Fragment() {
labelView = view.findViewById(R.id.label)
fab = view.findViewById(R.id.open)
initContentAndFab(originalContent)
initContentAndFab(justScanned)
if (prefs.showMetaData) {
metaView.fillMetaView(scan)
} else {
metaView.visibility = View.GONE
}
if (prefs.showRecreation) {
recreation = scan.toRecreation(
(200f * dp).roundToInt()
)
}
if (id > 0) {
scan.label?.let {
labelView.setText(it)
@@ -146,7 +143,7 @@ class DecodeFragment : Fragment() {
labelView.visibility = View.GONE
}
updateViewsAndFab(originalContent, originalBytes)
updateViewsAndFab(scan.text, originalBytes)
view.findViewById<View>(R.id.inset_layout).setPaddingFromWindowInsets()
view.findViewById<View>(R.id.scroll_view).setPaddingFromWindowInsets()
@@ -156,6 +153,7 @@ class DecodeFragment : Fragment() {
override fun onPause() {
super.onPause()
val id = scan.id
if (id > 0) {
val newLabel = labelView.text.toString().trim()
if (newLabel != (label ?: "")) {
@@ -171,7 +169,7 @@ class DecodeFragment : Fragment() {
wipeLastShareFile()
}
private fun initContentAndFab(originalContent: String) {
private fun initContentAndFab(justScanned: Boolean) {
if (isBinary) {
contentView.setText(String(originalBytes).foldNonAlNum())
contentView.isEnabled = false
@@ -180,7 +178,7 @@ class DecodeFragment : Fragment() {
askForFileNameAndSave(originalBytes)
}
} else {
contentView.setText(originalContent)
contentView.setText(scan.text)
contentView.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
updateViewsAndFab(s?.toString() ?: "")
@@ -201,10 +199,11 @@ class DecodeFragment : Fragment() {
) = Unit
})
fab.setOnClickListener {
executeAction(content)
executeAction(this.content)
}
if (prefs.openImmediately) {
executeAction(content)
if (justScanned && prefs.openImmediately) {
executeAction(this.content)
backOrFinish()
}
}
}
@@ -245,24 +244,56 @@ class DecodeFragment : Fragment() {
v.text = hexDump(bytes)
}
recreationView.showIf(prefs.showRecreation) { v ->
val r = recreation ?: return@showIf
val content = if (isBinary) bytes else text
v.setImageBitmap(r.encode(content))
v.setOnClickListener {
fragmentManager?.addFragment(
BarcodeFragment.newInstance(
content,
r.format,
r.margin,
r.ecLevel
)
val unmodified: Boolean
val content = if (isBinary) {
if (bytes.isEmpty()) {
clearRecreation()
return@showIf
}
unmodified = bytes.contentEquals(originalBytes)
bytes
} else {
if (text.isEmpty()) {
clearRecreation()
return@showIf
}
unmodified = text == scan.text
text
}
val barcode = if (unmodified) {
scan.toBarcode()
} else {
ContentBarcode(
content,
scan.format,
scan.errorCorrectionLevel.toErrorCorrectionInt()
)
}
try {
v.setImageBitmap(barcode.bitmap(recreationSize))
v.setOnClickListener {
v.context.hideSoftKeyboard(contentView)
v.post {
fragmentManager?.addFragment(
BarcodeFragment.newInstance(barcode)
)
}
}
} catch (e: RuntimeException) {
clearRecreation()
}
}
dataView.fillDataView(text, bytes)
stampView.setTrackingLink(bytes, format)
}
private fun clearRecreation() {
recreationView.apply {
setImageBitmap(null)
setOnClickListener(null)
}
}
private fun TableLayout.fillDataView(text: String, bytes: ByteArray) {
val items = LinkedHashMap<Int, CharSequence?>()
when (prefs.showChecksum) {
@@ -319,6 +350,15 @@ class DecodeFragment : Fragment() {
)
)
}
if (scan.format == ZxingCpp.BarcodeFormat.QR_CODE &&
scan.dataMask > -1
) {
items.putAll(
linkedMapOf(
R.string.qr_data_mask to scan.dataMask.toString()
)
)
}
fill(items)
}
@@ -403,7 +443,7 @@ class DecodeFragment : Fragment() {
}
R.id.remove -> {
db.removeScan(id)
db.removeScan(scan.id)
backOrFinish()
true
}
@@ -454,17 +494,18 @@ class DecodeFragment : Fragment() {
private fun executeAction(content: String) {
val ac = activity ?: return
if (content.isNotEmpty()) {
if (action is WifiAction &&
Build.VERSION.SDK_INT < Build.VERSION_CODES.Q &&
!ac.hasLocationPermission { executeAction(content) }
) {
return
}
scope.launch {
action.execute(ac, content.toByteArray())
maybeBackOrFinish()
}
if (content.isEmpty()) {
return
}
if (action is WifiAction &&
Build.VERSION.SDK_INT < Build.VERSION_CODES.Q &&
!ac.hasLocationPermission { executeAction(content) }
) {
return
}
scope.launch {
action.execute(ac, content.toByteArray())
maybeBackOrFinish()
}
}
@@ -3,9 +3,9 @@ package de.markusfisch.android.binaryeye.fragment
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.SwitchCompat
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
@@ -16,20 +16,19 @@ import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.CheckBox
import android.widget.EditText
import android.widget.SeekBar
import android.widget.Spinner
import android.widget.TextView
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.adapter.prettifyFormatName
import de.markusfisch.android.binaryeye.app.addFragment
import de.markusfisch.android.binaryeye.app.prefs
import de.markusfisch.android.binaryeye.app.setFragment
import de.markusfisch.android.binaryeye.text.unescape
import de.markusfisch.android.binaryeye.view.hideSoftKeyboard
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
import de.markusfisch.android.binaryeye.widget.toast
import de.markusfisch.android.zxingcpp.ZxingCpp.BarcodeFormat
import java.io.InputStream
import kotlin.math.max
import kotlin.math.min
class EncodeFragment : Fragment() {
@@ -38,9 +37,7 @@ class EncodeFragment : Fragment() {
private lateinit var ecSpinner: Spinner
private lateinit var colorsLabel: TextView
private lateinit var colorsSpinner: Spinner
private lateinit var marginLayout: View
private lateinit var marginView: TextView
private lateinit var marginBarView: SeekBar
private lateinit var addQuietZoneSwitch: SwitchCompat
private lateinit var contentView: EditText
private lateinit var unescapeCheckBox: CheckBox
@@ -58,7 +55,6 @@ class EncodeFragment : Fragment() {
BarcodeFormat.UPC_A
)
private var minMargin = 0
private var bytes: ByteArray? = null
override fun onActivityResult(
@@ -143,12 +139,13 @@ class EncodeFragment : Fragment() {
colorsLabel, colorsSpinner
)
when (format) {
BarcodeFormat.AZTEC -> setMarginBar(4, 4)
BarcodeFormat.DATA_MATRIX -> setMarginBar(1, 1)
BarcodeFormat.QR_CODE -> setMarginBar(4, 4)
BarcodeFormat.PDF_417 -> setMarginBar(0, 30)
BarcodeFormat.AZTEC,
BarcodeFormat.DATA_MATRIX,
BarcodeFormat.QR_CODE,
BarcodeFormat.PDF_417 -> true
else -> false
}.setVisibilityFor(marginLayout)
}.setVisibilityFor(addQuietZoneSwitch)
}
override fun onNothingSelected(parentView: AdapterView<*>?) {}
@@ -175,22 +172,22 @@ class EncodeFragment : Fragment() {
colorsLabel = view.findViewById(R.id.colors_label)
colorsSpinner = view.findViewById(R.id.colors)
marginLayout = view.findViewById(R.id.margin)
marginView = view.findViewById(R.id.margin_display)
marginBarView = view.findViewById(R.id.margin_bar)
initMarginBar()
addQuietZoneSwitch = view.findViewById(R.id.add_quiet_zone)
addQuietZoneSwitch.isChecked = prefs.addQuietZone
contentView = view.findViewById(R.id.content)
unescapeCheckBox = view.findViewById(R.id.unescape)
unescapeCheckBox.isChecked = prefs.expandEscapeSequences
var complete = false
val args = arguments
args?.getString(CONTENT_TEXT)?.let {
contentView.setText(it)
complete = it.isNotEmpty()
}
args?.getByteArray(CONTENT_RAW)?.let {
bytes = it
complete = it.isNotEmpty()
setEncodeByteArray()
}
@@ -212,6 +209,15 @@ class EncodeFragment : Fragment() {
view.findViewById<View>(R.id.inset_layout).setPaddingFromWindowInsets()
view.findViewById<View>(R.id.scroll_view).setPaddingFromWindowInsets()
if (complete &&
args?.getBoolean(EXECUTE, false) == true
) {
view.post {
val content = view.context.getContent() ?: return@post
fragmentManager?.setFragment(newEncodeFragment(content))
}
}
return view
}
@@ -242,30 +248,36 @@ class EncodeFragment : Fragment() {
super.onPause()
prefs.indexOfLastSelectedFormat = formatView.selectedItemPosition
prefs.expandEscapeSequences = unescapeCheckBox.isChecked
prefs.lastMargin = marginBarView.progress
prefs.addQuietZone = addQuietZoneSwitch.isChecked
}
private fun Context.encode() {
hideSoftKeyboard(contentView)
val content = getContent() ?: return
val format = formats[formatView.selectedItemPosition]
fragmentManager?.addFragment(
BarcodeFragment.newInstance(
content,
format,
when (format) {
BarcodeFormat.AZTEC,
BarcodeFormat.DATA_MATRIX,
BarcodeFormat.QR_CODE,
BarcodeFormat.PDF_417 -> marginBarView.progress
contentView.post {
fragmentManager?.addFragment(newEncodeFragment(content))
}
}
else -> -1
},
format.getErrorCorrectionLevel(ecSpinner.selectedItemPosition),
if (format.canBeInverted()) {
colorsSpinner.selectedItemPosition
} else 0
)
private fun newEncodeFragment(content: Any): Fragment {
val format = formats[formatView.selectedItemPosition]
return BarcodeFragment.newInstance(
content,
format,
format.getErrorCorrectionLevel(ecSpinner.selectedItemPosition),
when (format) {
BarcodeFormat.AZTEC,
BarcodeFormat.DATA_MATRIX,
BarcodeFormat.QR_CODE,
BarcodeFormat.PDF_417 -> if (
addQuietZoneSwitch.isChecked
) 1 else 0
else -> -1
},
if (format.canBeInverted()) {
colorsSpinner.selectedItemPosition
} else 0
)
}
@@ -297,47 +309,6 @@ class EncodeFragment : Fragment() {
}
}
private fun initMarginBar() {
updateMargin(marginBarView.progress)
marginBarView.setOnSeekBarChangeListener(
object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(
seekBar: SeekBar,
progressValue: Int,
fromUser: Boolean
) {
if (progressValue >= minMargin) {
updateMargin(progressValue)
}
}
override fun onStartTrackingTouch(seekBar: SeekBar) {}
override fun onStopTrackingTouch(seekBar: SeekBar) {}
})
}
private fun updateMargin(margin: Int) {
marginView.text = getString(R.string.margin_size, margin)
}
private fun setMarginBar(minimum: Int, value: Int): Boolean {
minMargin = minimum
marginBarView.apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
min = minimum
}
progress = max(
minimum, if (prefs.lastMargin > -1) {
prefs.lastMargin
} else {
value
}
)
}
return true
}
private fun setEncodeByteArray() {
contentView.text = null
contentView.hint = getString(R.string.binary_data)
@@ -349,11 +320,13 @@ class EncodeFragment : Fragment() {
private const val CONTENT_TEXT = "content_text"
private const val CONTENT_RAW = "content_raw"
private const val FORMAT = "format"
private const val EXECUTE = "execute"
private const val PICK_FILE_RESULT_CODE = 1
fun <T> newInstance(
content: T? = null,
format: String? = null
format: String? = null,
execute: Boolean = false
): Fragment {
val args = Bundle()
content?.let {
@@ -364,6 +337,7 @@ class EncodeFragment : Fragment() {
"content must be a String or a ByteArray"
)
}
args.putBoolean(EXECUTE, execute)
}
format?.let { args.putString(FORMAT, it) }
val fragment = EncodeFragment()
@@ -70,9 +70,13 @@ class HistoryFragment : Fragment() {
R.menu.fragment_history_edit,
menu
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
// SDK35+ doesn't draw a status bar background anymore.
Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM
) {
lockStatusBarColor()
val ac = activity ?: return false
@Suppress("DEPRECATION")
ac.window.statusBarColor = ContextCompat.getColor(
ac,
R.color.accent_dark
@@ -297,7 +301,7 @@ class HistoryFragment : Fragment() {
val cursor = db.getScans(filter)
withContext(Dispatchers.Main) {
val ac = activity ?: return@withContext
val hasScans = cursor != null && cursor.count > 0
val hasScans = cursor.count > 0
if (filter == null) {
if (!hasScans) {
listView.emptyView = useHistorySwitch
@@ -310,7 +314,7 @@ class HistoryFragment : Fragment() {
} else {
View.GONE
}
cursor?.let { cursor ->
cursor.let { cursor ->
// Close previous cursor.
scansAdapter?.also { it.changeCursor(null) }
scansAdapter = ScansAdapter(ac, cursor)
@@ -442,12 +446,12 @@ class HistoryFragment : Fragment() {
} ?: return@useVisibility
val message = when (delimiter) {
"db" -> ac.exportDatabase(name)
else -> db.getScansDetailed(filter)?.use {
else -> db.getScansDetailed(filter).use {
when (delimiter) {
"json" -> ac.exportJson(name, it)
else -> ac.exportCsv(name, it, delimiter)
}
} ?: false
}
}.toSaveResult()
withContext(Dispatchers.Main) {
ac.toast(message)
@@ -475,14 +479,14 @@ class HistoryFragment : Fragment() {
db.getScansDetailed(selectedIds.toLongArray())
} else {
db.getScansDetailed(filter)
}?.use { cursor ->
}.use { cursor ->
val details = format.split(":")
when (details[0]) {
"text" -> Pair(cursor.exportText(details[1]), "txt")
"csv" -> Pair(cursor.exportCsv(details[1]), "csv")
else -> Pair(cursor.exportJson(), "json")
}
}?.let { (text, ext) ->
}.let { (text, ext) ->
text?.let {
withContext(Dispatchers.Main) {
if (asFile) {
@@ -502,7 +506,7 @@ class HistoryFragment : Fragment() {
private fun Cursor.exportText(separator: String): String {
val sb = StringBuilder()
val contentIndex = getColumnIndex(Database.SCANS_CONTENT)
val contentIndex = getColumnIndex(Database.SCANS_TEXT)
if (contentIndex > -1 && moveToFirst()) {
do {
val content = getString(contentIndex)
@@ -10,7 +10,9 @@ data class FrameMetrics(
var width: Int = 0,
var height: Int = 0,
var orientation: Int = 0
)
) {
fun isValid() = width > 0 && height > 0
}
fun Rect.setFrameRoi(
frameMetrics: FrameMetrics,
@@ -5,11 +5,12 @@ import android.graphics.Canvas
fun Bitmap.fixTransparency(): Bitmap {
val result = analyzeTransparency()
return if (result.hasTransparentPixels) {
val cfg = config
return if (cfg != null && result.hasTransparentPixels) {
val copy = Bitmap.createBitmap(
width,
height,
config
cfg
)
Canvas(copy).apply {
drawColor(result.backgroundColor)
@@ -53,7 +53,7 @@ private fun Scan.send(url: String, type: String): Response {
}
else -> request(
url + content.urlEncode()
url + text.urlEncode()
)
}
}
@@ -67,7 +67,7 @@ private fun Scan.asJson() = JSONObject().apply {
}
private fun Scan.getMap(): Map<String, String> = mapOf(
"content" to content,
"content" to text,
"raw" to raw?.toHexString(),
"format" to format.name,
"errorCorrectionLevel" to errorCorrectionLevel,
@@ -0,0 +1,41 @@
package de.markusfisch.android.binaryeye.preference
import android.content.SharedPreferences
import org.json.JSONObject
data class CropHandle(
val x: Int = -2, // -2 means set default roi.
val y: Int = -2,
val orientation: Int = 0
)
fun SharedPreferences.storeCropHandle(
name: String,
cropHandle: CropHandle
) {
edit().putString(
name,
JSONObject().apply {
put("x", cropHandle.x)
put("y", cropHandle.y)
put("orientation", cropHandle.orientation)
}.toString()
).apply()
}
fun SharedPreferences.restoreCropHandle(
name: String,
default: CropHandle = CropHandle()
): CropHandle {
val s = getString(name, null) ?: return default
return try {
val json = JSONObject(s)
CropHandle(
json.getInt("x"),
json.getInt("y"),
json.getInt("orientation")
)
} catch (e: Exception) {
return default
}
}
@@ -7,8 +7,10 @@ import android.media.ToneGenerator
import android.os.Build
import android.preference.PreferenceManager
import android.support.annotation.RequiresApi
import de.markusfisch.android.binaryeye.activity.CameraActivity
import de.markusfisch.android.zxingcpp.ZxingCpp.BarcodeFormat
class Preferences {
lateinit var preferences: SharedPreferences
@@ -38,21 +40,6 @@ class Preferences {
apply(BARCODE_FORMATS, value)
field = value
}
var cropHandleX = -2 // -2 means set default roi.
set(value) {
apply(CROP_HANDLE_X, value)
field = value
}
var cropHandleY = -2
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)
@@ -200,11 +187,6 @@ class Preferences {
apply(INDEX_OF_LAST_SELECTED_EC_LEVEL, value)
field = value
}
var lastMargin: Int = -1
set(value) {
apply(LAST_MARGIN, value)
field = value
}
var freeRotation = false
set(value) {
apply(FREE_ROTATION, value)
@@ -215,11 +197,21 @@ class Preferences {
apply(EXPAND_ESCAPE_SEQUENCES, value)
field = value
}
var addQuietZone = true
set(value) {
apply(ADD_QUIET_ZONE, value)
field = value
}
var brightenScreen = false
set(value) {
apply(BRIGHTEN_SCREEN, value)
field = value
}
var previewScale = 0f
set(value) {
apply(PREVIEW_SCALE, value)
field = value
}
fun init(context: Context) {
preferences = PreferenceManager.getDefaultSharedPreferences(context)
@@ -237,12 +229,8 @@ class Preferences {
}
}
cropHandleX = preferences.getInt(CROP_HANDLE_X, cropHandleX)
cropHandleY = preferences.getInt(CROP_HANDLE_Y, cropHandleY)
cropHandleOrientation = preferences.getInt(
CROP_HANDLE_ORIENTATION,
cropHandleOrientation
)
migrateCropHandle()
showCropHandle = preferences.getBoolean(
SHOW_CROP_HANDLE,
showCropHandle
@@ -331,19 +319,20 @@ class Preferences {
INDEX_OF_LAST_SELECTED_EC_LEVEL,
indexOfLastSelectedEcLevel
)
lastMargin = preferences.getInt(
LAST_MARGIN,
lastMargin
)
freeRotation = preferences.getBoolean(FREE_ROTATION, freeRotation)
expandEscapeSequences = preferences.getBoolean(
EXPAND_ESCAPE_SEQUENCES,
expandEscapeSequences
)
addQuietZone = preferences.getBoolean(ADD_QUIET_ZONE, addQuietZone)
brightenScreen = preferences.getBoolean(
BRIGHTEN_SCREEN,
brightenScreen
)
previewScale = preferences.getFloat(
PREVIEW_SCALE,
previewScale
)
}
private fun addFormatsOnUpdate(
@@ -359,6 +348,31 @@ class Preferences {
}
}
private fun migrateCropHandle() {
val cropHandleXName = "crop_handle_x"
val def = CropHandle()
if (preferences.getInt(cropHandleXName, def.x) == def.x) {
return;
}
storeCropHandle(
CameraActivity.CAMERA_CROP_HANDLE,
CropHandle(
preferences.getInt(cropHandleXName, def.x),
preferences.getInt("crop_handle_y", def.y),
preferences.getInt("crop_handle_orientation", def.orientation)
)
)
preferences.edit().putInt(cropHandleXName, def.x).apply()
}
fun restoreCropHandle(
name: String
) = preferences.restoreCropHandle(name)
fun storeCropHandle(name: String, cropHandle: CropHandle) {
preferences.storeCropHandle(name, cropHandle)
}
fun beepTone() = when (beepToneName) {
"tone_cdma_confirm" -> ToneGenerator.TONE_CDMA_CONFIRM
"tone_sup_radio_ack" -> ToneGenerator.TONE_SUP_RADIO_ACK
@@ -377,9 +391,6 @@ class Preferences {
private fun put(label: String, value: Boolean) =
preferences.edit().putBoolean(label, value)
private fun put(label: String, value: String) =
preferences.edit().putString(label, value)
private fun apply(label: String, value: Boolean) {
put(label, value).apply()
}
@@ -392,6 +403,10 @@ class Preferences {
preferences.edit().putInt(label, value).apply()
}
private fun apply(label: String, value: Float) {
preferences.edit().putFloat(label, value).apply()
}
@SuppressLint("ApplySharedPref")
private fun commit(label: String, value: String) {
preferences.edit().putString(label, value).commit()
@@ -408,9 +423,6 @@ class Preferences {
}
private const val BARCODE_FORMATS = "formats"
private const val CROP_HANDLE_X = "crop_handle_x"
private const val CROP_HANDLE_Y = "crop_handle_y"
private const val CROP_HANDLE_ORIENTATION = "crop_handle_orientation"
private const val SHOW_CROP_HANDLE = "show_crop_handle"
private const val ZOOM_BY_SWIPING = "zoom_by_swiping"
private const val AUTO_ROTATE = "auto_rotate"
@@ -440,9 +452,10 @@ class Preferences {
private const val CUSTOM_LOCALE = "custom_locale"
private const val INDEX_OF_LAST_SELECTED_FORMAT = "index_of_last_selected_format"
private const val INDEX_OF_LAST_SELECTED_EC_LEVEL = "index_of_last_selected_ec_level"
private const val LAST_MARGIN = "last_margin"
private const val FREE_ROTATION = "free_rotation"
private const val EXPAND_ESCAPE_SEQUENCES = "expand_escape_sequences"
private const val ADD_QUIET_ZONE = "add_quiet_zone"
private const val BRIGHTEN_SCREEN = "brighten_screen"
private const val PREVIEW_SCALE = "preview_scale"
}
}
@@ -8,6 +8,7 @@ import android.support.v4.content.ContextCompat
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.support.v7.widget.Toolbar
import android.view.View
import android.view.WindowManager
import android.widget.AbsListView
@@ -57,10 +58,9 @@ private fun lastChildOutOfView(listView: AbsListView): Boolean {
return child != null && child.bottom >= listView.height
}
fun initSystemBars(activity: AppCompatActivity) {
fun AppCompatActivity.initBars() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// Keeps the soft keyboard from repositioning the layout.
val window = activity.window
window.setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
)
@@ -69,7 +69,16 @@ fun initSystemBars(activity: AppCompatActivity) {
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
}
colorSystemAndToolBars(activity)
colorSystemAndToolBars(this)
setPaddingFromWindowInsets(
findViewById(R.id.main),
(findViewById(R.id.toolbar) as Toolbar).apply {
setSupportActionBar(this)
},
findViewById(R.id.navbar)?.apply {
setBackgroundColor(translucentPrimaryColor)
}
)
}
private var statusBarColorLocked = false
@@ -97,14 +106,27 @@ fun colorSystemAndToolBars(
val topColor = if (scrolled) translucentPrimaryColor else 0
val activity = getAppCompatActivity(context) ?: return
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val window = activity.window
if (!statusBarColorLocked) {
window.statusBarColor = topColor
}
window.navigationBarColor = if (scrolled || scrollable) {
translucentPrimaryColor
// System bars no longer have a background from SDK35+.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
val window = activity.window
if (!statusBarColorLocked) {
@Suppress("DEPRECATION")
window.statusBarColor = topColor
}
@Suppress("DEPRECATION")
window.navigationBarColor = if (scrolled || scrollable) {
translucentPrimaryColor
} else {
0
}
} else {
0
activity.findViewById(R.id.navbar)?.apply {
visibility = if (scrolled || scrollable) {
View.VISIBLE
} else {
View.GONE
}
}
}
}
activity.supportActionBar?.setBackgroundDrawable(
@@ -7,10 +7,37 @@ import android.support.v4.view.ViewCompat
import android.support.v4.view.WindowInsetsCompat
import android.support.v7.widget.Toolbar
import android.view.View
import android.view.WindowInsets
private var toolbarHeight = 0
fun recordToolbarHeight(toolbar: Toolbar) {
fun setPaddingFromWindowInsets(mainLayout: View, toolbar: Toolbar, navbar: View?) {
toolbarHeight = toolbar.layoutParams.height
// SDK 35+ is edge-to-edge by default, so the toolbar needs to extend
// below the status bar.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
return
}
mainLayout.setOnApplyWindowInsetsListener { _, insets ->
val systemBarInsets = insets.getInsets(
WindowInsets.Type.systemBars()
)
val statusBarTop = systemBarInsets.top
if (toolbar.paddingTop == 0 && statusBarTop > 0) {
toolbar.setPadding(0, statusBarTop, 0, 0)
toolbar.layoutParams.height += statusBarTop
}
val navBarBottom = systemBarInsets.bottom
if (navBarBottom > 0) {
mainLayout.setPadding(0, 0, 0, navBarBottom);
navbar?.apply {
layoutParams.height = navBarBottom
}
}
insets
}
}
fun View.setPaddingFromWindowInsets() {
@@ -10,6 +10,8 @@ import de.markusfisch.android.scalingimageview.widget.ScalingImageView
open class ConfinedScalingImageView : ScalingImageView {
val insets = Rect()
var runAfterLayout: (() -> Unit)? = null
constructor(context: Context, attrs: AttributeSet, defStyle: Int) :
super(context, attrs, defStyle)
@@ -37,5 +39,8 @@ open class ConfinedScalingImageView : ScalingImageView {
)
}
centerRemap()
runAfterLayout?.invoke()
}
fun isInBounds(): Boolean = inBounds()
}
@@ -19,6 +19,7 @@ import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.app.prefs
import de.markusfisch.android.binaryeye.graphics.getBitmapFromDrawable
import de.markusfisch.android.binaryeye.graphics.getDashedBorderPaint
import de.markusfisch.android.binaryeye.preference.CropHandle
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
@@ -83,11 +84,16 @@ class DetectorView : View {
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) :
super(context, attrs, defStyleAttr)
fun saveCropHandlePos() {
fun storeCropHandlePos(name: String) {
val pos = getCropHandlePos()
prefs.cropHandleX = pos.x
prefs.cropHandleY = pos.y
prefs.cropHandleOrientation = currentOrientation
prefs.storeCropHandle(
name,
CropHandle(
pos.x,
pos.y,
currentOrientation
)
)
}
private fun getCropHandlePos() = if (handleActive) {
@@ -96,11 +102,12 @@ class DetectorView : View {
Point(-1, -1)
}
fun restoreCropHandlePos() {
fun restoreCropHandlePos(name: String) {
val cropHandle = prefs.restoreCropHandle(name)
setCropHandlePos(
prefs.cropHandleX,
prefs.cropHandleY,
prefs.cropHandleOrientation
cropHandle.x,
cropHandle.y,
cropHandle.orientation
)
}
@@ -3,19 +3,29 @@ package de.markusfisch.android.binaryeye.widget
import android.content.Context
import android.widget.Toast
fun Context.toast(message: Int) = Toast.makeText(
applicationContext,
message,
Toast.LENGTH_LONG
).show()
private var lastToast: Toast? = null
fun Context.toast(message: String) = Toast.makeText(
applicationContext,
message.ellipsize(128),
Toast.LENGTH_LONG
).show()
fun <T> Context.toast(message: T) {
lastToast?.cancel()
lastToast = when (message) {
is String -> Toast.makeText(
applicationContext,
message.ellipsize(),
Toast.LENGTH_LONG
)
private fun String.ellipsize(max: Int) = if (length < max) {
is Int -> Toast.makeText(
applicationContext,
message,
Toast.LENGTH_LONG
)
else -> return
}
lastToast?.show()
}
private fun String.ellipsize(max: Int = 128) = if (length < max) {
this
} else {
"${take(max)}"
@@ -0,0 +1,16 @@
<selector
xmlns:android="http://schemas.android.com/apk/res/android"
android:constantSize="true">
<item android:state_pressed="false">
<shape android:shape="rectangle">
<corners android:radius="2dp"/>
<solid android:color="@color/selected_row"/>
</shape>
</item>
<item android:state_pressed="true">
<shape android:shape="rectangle">
<corners android:radius="2dp"/>
<solid android:color="@color/pressed_row"/>
</shape>
</item>
</selector>
@@ -1,5 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M20,8.69L20,4h-4.69L12,0.69 8.69,4L4,4v4.69L0.69,12 4,15.31L4,20h4.69L12,23.31 15.31,20L20,20v-4.69L23.31,12 20,8.69zM12,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6zM12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4z"/>
<path android:fillColor="#fff" android:pathData="M20 8.69V4h-4.69L12 0.69 8.69 4H4v4.69L0.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69zM12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6zm0-10c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4z"/>
</vector>
@@ -1,3 +1,3 @@
<vector android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M14.54,18.49L14.54,16.51L17.51,16.51L17.51,13.54L19.49,13.54L19.49,16.51L22.46,16.51L22.46,18.49L19.508,18.508L19.49,21.46L17.51,21.46L17.492,18.508L14.54,18.49ZM13.35,14.75L11,12.4L11,7L13,7L13,11.6L14.4,13L13.35,14.75ZM3,10L3,4L5,4L5,6.35C5.85,5.283 6.888,4.458 8.113,3.875C9.338,3.292 10.633,3 12,3C14.5,3 16.625,3.875 18.375,5.625C20.125,7.375 21,9.5 21,12L19,12C19,10.05 18.321,8.396 16.963,7.037C15.604,5.679 13.95,5 12,5C10.85,5 9.775,5.267 8.775,5.8C7.775,6.333 6.933,7.067 6.25,8L9,8L9,10L3,10ZM12,21C9.7,21 7.696,20.237 5.988,18.712C4.279,17.187 3.3,15.283 3.05,13L5.1,13C5.333,14.733 6.104,16.167 7.413,17.3C8.721,18.433 10.25,19 12,19C12.183,19 12.354,18.996 12.513,18.987C12.671,18.979 12.833,18.958 13,18.925L13,20.95C12.833,20.967 12.671,20.979 12.513,20.987C12.354,20.996 12.183,21 12,21Z"/>
<path android:fillColor="#FF000000" android:pathData="M14.54 18.49v-1.98h2.97v-2.97h1.98v2.97h2.97v1.98l-2.952 0.018-0.018 2.952h-1.98l-0.018-2.952-2.952-0.018zm-1.19-3.74L11 12.4V7h2v4.6l1.4 1.4-1.05 1.75zM3 10V4h2v2.35c0.85-1.067 1.888-1.892 3.113-2.475C9.338 3.292 10.633 3 12 3c2.5 0 4.625 0.875 6.375 2.625S21 9.5 21 12h-2c0-1.95-0.679-3.604-2.037-4.963C15.604 5.679 13.95 5 12 5c-1.15 0-2.225 0.267-3.225 0.8S6.933 7.067 6.25 8H9v2H3zm9 11c-2.3 0-4.304-0.763-6.012-2.288C4.279 17.187 3.3 15.283 3.05 13H5.1c0.233 1.733 1.004 3.167 2.313 4.3C8.721 18.433 10.25 19 12 19c0.183 0 0.354-0.004 0.513-0.013 0.158-0.008 0.32-0.029 0.487-0.062v2.025c-0.167 0.017-0.329 0.029-0.487 0.037C12.354 20.996 12.183 21 12 21z"/>
</vector>
@@ -1,9 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:pathData="M54,72.9C49.17,72.9 44.96,71.3 41.37,68.1C37.78,64.89 35.73,60.9 35.2,56.1L39.51,56.1C40,59.74 41.62,62.75 44.37,65.13C47.11,67.51 50.32,68.7 54,68.7C58.1,68.7 61.57,67.28 64.42,64.42C67.28,61.57 68.7,58.1 68.7,54C68.7,49.9 67.28,46.43 64.42,43.58C61.57,40.72 58.1,39.3 54,39.3C51.58,39.3 49.33,39.86 47.23,40.98C45.13,42.1 43.36,43.64 41.92,45.6L47.7,45.6L47.7,49.8L35.1,49.8L35.1,37.2L39.3,37.2L39.3,42.13C41.08,39.89 43.26,38.16 45.84,36.94C48.41,35.71 51.13,35.1 54,35.1C56.63,35.1 59.08,35.6 61.38,36.59C63.67,37.59 65.67,38.94 67.36,40.64C69.06,42.33 70.41,44.33 71.41,46.62C72.4,48.92 72.9,51.37 72.9,54C72.9,56.63 72.4,59.08 71.41,61.38C70.41,63.67 69.06,65.67 67.36,67.36C65.67,69.06 63.67,70.41 61.38,71.41C59.08,72.4 56.63,72.9 54,72.9ZM59.88,62.82L51.9,54.84L51.9,43.5L56.1,43.5L56.1,53.16L62.82,59.88L59.88,62.82Z"
android:fillColor="#FFFFFF"/>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="108dp" android:height="108dp" android:viewportWidth="108" android:viewportHeight="108">
<path android:pathData="M54 72.9c-4.83 0-9.04-1.6-12.63-4.8-3.59-3.21-5.64-7.2-6.17-12h4.31c0.49 3.64 2.11 6.65 4.86 9.03 2.74 2.38 5.95 3.57 9.63 3.57 4.1 0 7.57-1.42 10.42-4.28C67.28 61.57 68.7 58.1 68.7 54s-1.42-7.57-4.28-10.42C61.57 40.72 58.1 39.3 54 39.3c-2.42 0-4.67 0.56-6.77 1.68-2.1 1.12-3.87 2.66-5.31 4.62h5.78v4.2H35.1V37.2h4.2v4.93c1.78-2.24 3.96-3.97 6.54-5.19 2.57-1.23 5.29-1.84 8.16-1.84 2.63 0 5.08 0.5 7.38 1.49 2.29 1 4.29 2.35 5.98 4.05 1.7 1.69 3.05 3.69 4.05 5.98 0.99 2.3 1.49 4.75 1.49 7.38s-0.5 5.08-1.49 7.38c-1 2.29-2.35 4.29-4.05 5.98-1.69 1.7-3.69 3.05-5.98 4.05-2.3 0.99-4.75 1.49-7.38 1.49zm5.88-10.08l-7.98-7.98V43.5h4.2v9.66l6.72 6.72-2.94 2.94z" android:fillColor="#FFFFFF"/>
</vector>
+2 -1
View File
@@ -8,9 +8,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
android:fitsSystemWindows="@bool/fitsSystemWindows">
<include layout="@layout/toolbar"/>
<SeekBar
android:id="@+id/zoom"
+3 -1
View File
@@ -4,9 +4,11 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<FrameLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
android:fitsSystemWindows="@bool/fitsSystemWindows">
<include layout="@layout/toolbar"/>
</FrameLayout>
<include layout="@layout/navbar"/>
</merge>
+3 -1
View File
@@ -9,9 +9,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
android:fitsSystemWindows="@bool/fitsSystemWindows">
<include layout="@layout/toolbar"/>
<android.support.design.widget.FloatingActionButton
style="@style/FAB"
@@ -19,4 +20,5 @@
android:contentDescription="@string/scan_code"
android:src="@drawable/ic_action_next"/>
</android.support.design.widget.CoordinatorLayout>
<include layout="@layout/navbar"/>
</merge>
@@ -0,0 +1,57 @@
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<!-- The FrameLayout is necessary because this LinearLayout
should have a padding which would be reset by the window
insets from `fitsSystemWindows`. -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="bottom"
android:padding="24dp"
android:orientation="vertical"
tools:ignore="UselessParent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:drawableTop="@drawable/logo"
android:drawablePadding="16dp"
android:gravity="center"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="@string/welcome"/>
<LinearLayout
style="@style/WelcomeButton"
android:id="@+id/expert"
android:layout_marginBottom="16dp"
android:orientation="vertical">
<TextView
style="@style/WelcomeButtonText"
android:text="@string/mode_expert"/>
<TextView
style="@style/WelcomeButtonSubText"
android:text="@string/mode_expert_description"/>
</LinearLayout>
<LinearLayout
style="@style/WelcomeButton"
android:id="@+id/simple"
android:orientation="vertical">
<TextView
style="@style/WelcomeButtonText"
android:text="@string/mode_simple"/>
<TextView
style="@style/WelcomeButtonSubText"
android:text="@string/mode_simple_description"/>
</LinearLayout>
</LinearLayout>
</FrameLayout>
</ScrollView>
@@ -0,0 +1,22 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/find_code_with_content"/>
<EditText
android:id="@+id/term"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textShortMessage"
android:imeOptions="actionDone"
android:hint="@string/find_code_hint"
android:maxLines="1"
android:maxLength="1024"
android:importantForAutofill="no"/>
</LinearLayout>
+4 -19
View File
@@ -65,27 +65,12 @@
android:id="@+id/colors"
android:layout_marginBottom="16dp"
android:entries="@array/colors"/>
<LinearLayout
android:id="@+id/margin"
<android.support.v7.widget.SwitchCompat
android:visibility="gone"
android:id="@+id/add_quiet_zone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:id="@+id/margin_display"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"/>
<SeekBar
style="@style/Widget.AppCompat.SeekBar.Discrete"
android:id="@+id/margin_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:max="32"/>
</LinearLayout>
android:text="@string/add_quiet_zone"/>
</LinearLayout>
</de.markusfisch.android.binaryeye.widget.ConfinedScrollView>
<android.support.design.widget.CoordinatorLayout
+8
View File
@@ -0,0 +1,8 @@
<!-- Navigation bar background from SDK35+ -->
<View
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/navbar"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="bottom"
android:visibility="gone"/>
@@ -32,6 +32,11 @@
android:title="@string/restrict_format"
android:icon="@drawable/ic_action_restrict_format"
material:showAsAction="ifRoom"/>
<item
android:id="@+id/find_code"
android:title="@string/find_code"
android:icon="@drawable/ic_action_search"
material:showAsAction="ifRoom"/>
<item
android:id="@+id/preferences"
android:title="@string/preferences"
+10 -1
View File
@@ -1,4 +1,9 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="welcome">مرحبًا!</string>
<string name="mode_expert">متقدم</string>
<string name="mode_expert_description">أود التحقق من المحتوى المقروء وفتحه يدويًا.</string>
<string name="mode_simple">بسيط</string>
<string name="mode_simple_description">أريد فقط قراءة الباركود وفتحه فورًا.</string>
<string name="camera_error">يتعذر الوصول إلى الكاميرا، يرجى المحاولة مرة أخرى</string>
<string name="scan_code">مسح الرمز</string>
<string name="compose_barcode">إنشاء رمز Bardcode</string>
@@ -33,6 +38,7 @@
<string name="gtin_add_on">إضافة UPC EAN</string>
<string name="barcode_version_number">الإصدار</string>
<string name="qr_version_and_modules" tools:ignore="PluralsCandidate">%1$s (%2$d وحدات)</string>
<string name="qr_data_mask">Data mask</string>
<string name="toggle_flash">تشغيل/إيقاف الوميض</string>
<string name="error_flash">خطأ في تشغيل الوميض</string>
<string name="share">مشاركة</string>
@@ -48,7 +54,7 @@
<string name="format">التنسيق</string>
<string name="size_width_by_height">الحجم بالبكسل: %1$d×%2$d</string>
<string name="size_no_magnification">بدون تكبير</string>
<string name="margin_size">الهامش بالبكسل: %1$d</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">أدخل المحتوى هنا</string>
<string name="unescape">توسيع رموز الهروب (escape sequences)</string>
<string name="encode">ترميز</string>
@@ -62,6 +68,9 @@
<string name="bulk_mode_delay">الفارق الزمني بين عمليات المسح المستمرة</string>
<string name="restrict_format">تحديد التنسيق</string>
<string name="remove_restriction">إزالة التحديد</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">مسح %s</string>
<string name="quarter_second">ربع ثانية</string>
<string name="half_second">نصف ثانية</string>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">Добре дошли!</string>
<string name="mode_expert">Разширен</string>
<string name="mode_expert_description">Искам да проверя прочетеното съдържание и да го отворя ръчно.</string>
<string name="mode_simple">Опростен</string>
<string name="mode_simple_description">Искам просто да сканирам баркодове и да ги отварям веднага.</string>
<string name="camera_error">Няма достъп до камерата, моля, опитайте отново</string>
<string name="scan_code">Сканирайте код</string>
<string name="compose_barcode">Създаване на баркод</string>
@@ -29,6 +34,7 @@
<string name="gtin_add_on">UPC EAN разширение</string>
<string name="barcode_version_number">Версия</string>
<string name="qr_version_and_modules">%1$s (%2$d модули)</string>
<string name="qr_data_mask">Data mask</string>
<string name="toggle_flash">Превключване на светкавицата</string>
<string name="error_flash">Грешка при превключването на светкавицата</string>
<string name="share">Сподели</string>
@@ -44,7 +50,7 @@
<string name="format">Формат</string>
<string name="size_width_by_height">Размер в пиксели: %1$d×%2$d</string>
<string name="size_no_magnification">No magnification</string>
<string name="margin_size">Марж в пиксели: %1$d</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">Въведете съдържание тук</string>
<string name="unescape">Разширете последователности за извеждане</string>
<string name="encode">КОДИРАНЕ</string>
@@ -58,6 +64,9 @@
<string name="bulk_mode_delay">Забавяне между непрекъснатите сканирания</string>
<string name="restrict_format">Ограничаване на формат</string>
<string name="remove_restriction">Премахване на ограничението</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">Сканиране %s</string>
<string name="quarter_second">Червърт секунда</string>
<string name="half_second">Половин секунда</string>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">স্বাগতম!</string>
<string name="mode_expert">উন্নত</string>
<string name="mode_expert_description">আমি পড়া বিষয়বস্তু যাচাই করতে চাই এবং এটি নিজে খুলতে চাই।</string>
<string name="mode_simple">সহজ</string>
<string name="mode_simple_description">আমি কেবল বারকোড স্ক্যান করতে চাই এবং তা সঙ্গে সঙ্গে খুলতে চাই।</string>
<string name="camera_error">ক্যামেরা পাওয়া যাচ্ছে না, পরে চেষ্টা করো</string>
<string name="scan_code">সংকেতের পাঠোদ্ধার</string>
<string name="compose_barcode">বারকোড বানাও</string>
@@ -29,6 +34,7 @@
<string name="gtin_add_on">ইউপিসি ইএএন বর্ধিত</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$s (%2$d modules)</string>
<string name="qr_data_mask">Data mask</string>
<string name="toggle_flash">আলো</string>
<string name="error_flash">Error toggling flash</string>
<string name="share">ভাগ করো</string>
@@ -44,7 +50,7 @@
<string name="format">ধরণ</string>
<string name="size_width_by_height">পিক্সেলে আকা:%1$d×%2$d</string>
<string name="size_no_magnification">No magnification</string>
<string name="margin_size">Margin in pixels: %1$d</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">এখানে তথ্য দাও</string>
<string name="unescape">Expand escape sequences</string>
<string name="encode">সংকেতকরণ</string>
@@ -58,6 +64,9 @@
<string name="bulk_mode_delay">Delay between continuous scans</string>
<string name="restrict_format">Restrict format</string>
<string name="remove_restriction">Remove restriction</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">Scan %s</string>
<string name="quarter_second">A quarter second</string>
<string name="half_second">Half a second</string>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">Vítejte!</string>
<string name="mode_expert">Pokročilý</string>
<string name="mode_expert_description">Chci zkontrolovat načtený obsah a otevřít jej ručně.</string>
<string name="mode_simple">Jednoduchý</string>
<string name="mode_simple_description">Chci jen skenovat čárové kódy a okamžitě je otevírat.</string>
<string name="camera_error">Nepodařilo se otevřít fotoaparát, prosím zkuste to znovu</string>
<string name="scan_code">Naskenovat kód</string>
<string name="compose_barcode">Vytvořit čárový kód</string>
@@ -31,6 +36,7 @@
<string name="gtin_add_on">rozšíření UPC EAN</string>
<string name="barcode_version_number">Verze</string>
<string name="qr_version_and_modules">%1$s (%2$d modulů)</string>
<string name="qr_data_mask">Data mask</string>
<string name="toggle_flash">Přepnout blesk</string>
<string name="error_flash">Zapnutí/vypnutí blesku selhalo</string>
<string name="share">Sdílet</string>
@@ -46,7 +52,7 @@
<string name="format">Formát</string>
<string name="size_width_by_height">Velikost v pixelech: %1$d×%2$d</string>
<string name="size_no_magnification">No magnification</string>
<string name="margin_size">Okraje v pixelech: %1$d</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">Sem vložte obsah</string>
<string name="unescape">Rozšířit escape sekvence</string>
<string name="encode">ZAKÓDOVAT</string>
@@ -60,6 +66,9 @@
<string name="bulk_mode_delay">Prodleva mezi skeny</string>
<string name="restrict_format">Omezit na formát</string>
<string name="remove_restriction">Zrušit omezení formátu</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">Naskenovat %s</string>
<string name="quarter_second">Čtvrt sekundy</string>
<string name="half_second">Půl sekundy</string>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">Velkommen!</string>
<string name="mode_expert">Avanceret</string>
<string name="mode_expert_description">Jeg vil kontrollere det læste indhold og åbne det manuelt.</string>
<string name="mode_simple">Simpel</string>
<string name="mode_simple_description">Jeg vil bare scanne stregkoder og få dem åbnet med det samme.</string>
<string name="camera_error">Kan ikke få adgang til kamera, prøv igen</string>
<string name="scan_code">Scan kode</string>
<string name="compose_barcode">Opret stregkode</string>
@@ -29,6 +34,7 @@
<string name="gtin_add_on">UPC EAN udvidelse</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$s (%2$d moduler)</string>
<string name="qr_data_mask">Data mask</string>
<string name="toggle_flash">Slå blitz til/fra</string>
<string name="error_flash">Der opstod en fejl ved at slå blitz til/fra</string>
<string name="share">Del</string>
@@ -44,7 +50,7 @@
<string name="format">Format</string>
<string name="size_width_by_height">Størrelse i pixels: %1$d×%2$d</string>
<string name="size_no_magnification">No magnification</string>
<string name="margin_size">Margin in pixels: %1$d</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">Indtast indhold her</string>
<string name="unescape">Expand escape sequences</string>
<string name="encode">INDKOD</string>
@@ -58,6 +64,9 @@
<string name="bulk_mode_delay">Forsinkelse mellem kontinuerlige scanninger</string>
<string name="restrict_format">Begræns format</string>
<string name="remove_restriction">Fjern begrænsningen</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">Scan %s</string>
<string name="quarter_second">Et kvart sekund</string>
<string name="half_second">Et halvt sekund</string>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">Willkommen!</string>
<string name="mode_expert">Erweitert</string>
<string name="mode_expert_description">Ich möchte den gelesenen Inhalt überprüfen und manuell öffnen.</string>
<string name="mode_simple">Einfach</string>
<string name="mode_simple_description">Ich möchte nur Barcodes scannen und sie sofort öffnen.</string>
<string name="camera_error">Auf die Kamera kann nicht zugegriffen werden, bitte versuche es nochmal</string>
<string name="scan_code">Code scannen</string>
<string name="compose_barcode">Barcode erstellen</string>
@@ -29,6 +34,7 @@
<string name="gtin_add_on">UPC EAN Erweiterung</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$s (%2$d Module)</string>
<string name="qr_data_mask">Maske</string>
<string name="toggle_flash">Licht an/ausschalten</string>
<string name="error_flash">Fehler beim ein/ausschalten des Lichts</string>
<string name="share">Teilen</string>
@@ -44,7 +50,7 @@
<string name="format">Format</string>
<string name="size_width_by_height">Größe in Pixel: %1$d×%2$d</string>
<string name="size_no_magnification">Keine Vergrößerung</string>
<string name="margin_size">Randgröße in Pixel: %1$d</string>
<string name="add_quiet_zone">Ruhezone hinzufügen</string>
<string name="input_content_here">Gib den Inhalt hier ein</string>
<string name="unescape">Escape-Sequenzen interpretieren</string>
<string name="encode">KODIEREN</string>
@@ -58,6 +64,9 @@
<string name="bulk_mode_delay">Pause zwischen fortlaufenden Scans</string>
<string name="restrict_format">Format eingrenzen</string>
<string name="remove_restriction">Einschränkung entfernen</string>
<string name="find_code">Barcode finden</string>
<string name="find_code_with_content">Mit welchem Inhalt?</string>
<string name="find_code_hint">Begriff eingeben</string>
<string name="scan_format">%s scannen</string>
<string name="quarter_second">Eine Viertelsekunde</string>
<string name="half_second">Eine halbe Sekunde</string>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">¡Bienvenido!</string>
<string name="mode_expert">Avanzado</string>
<string name="mode_expert_description">Quiero verificar el contenido leído y abrirlo manualmente.</string>
<string name="mode_simple">Simple</string>
<string name="mode_simple_description">Solo quiero escanear códigos de barras y abrirlos de inmediato.</string>
<string name="camera_error">No se puede acceder a la cámara, por favor, inténtelo de nuevo</string>
<string name="scan_code">Escanear código</string>
<string name="compose_barcode">Componer código</string>
@@ -29,6 +34,7 @@
<string name="gtin_add_on">Extensión UPC EAN</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$s (%2$d modulos)</string>
<string name="qr_data_mask">Data mask</string>
<string name="toggle_flash">Activar el flash</string>
<string name="error_flash">Error activando flash</string>
<string name="share">Compartir</string>
@@ -44,7 +50,7 @@
<string name="format">Formato</string>
<string name="size_width_by_height">Tamaño en píxeles: %1$d×%2$d</string>
<string name="size_no_magnification">No magnification</string>
<string name="margin_size">Margen en pixeles: %1$d</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">Introduzca el contenido aquí</string>
<string name="unescape">Expandir secuencia de escape</string>
<string name="encode">CODIFICAR</string>
@@ -58,6 +64,9 @@
<string name="bulk_mode_delay">Demora entre escaneos continuos</string>
<string name="restrict_format">Restringir formato</string>
<string name="remove_restriction">Eliminar restricción</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">Escanear %s</string>
<string name="quarter_second">Un cuarto de segundo</string>
<string name="half_second">Medio segundo</string>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">خوش آمدید!</string>
<string name="mode_expert">پیشرفته</string>
<string name="mode_expert_description">می‌خواهم محتوای خوانده شده را بررسی کرده و به صورت دستی باز کنم.</string>
<string name="mode_simple">ساده</string>
<string name="mode_simple_description">فقط می‌خواهم بارکدها را اسکن کنم و فوراً باز شوند.</string>
<string name="camera_error">نمی‌توان به دوربین دسترسی داشت، لطفاً دوباره تلاش کنید</string>
<string name="scan_code">پویش کد</string>
<string name="compose_barcode">ایجاد بارکد</string>
@@ -29,6 +34,7 @@
<string name="gtin_add_on">UPC EAN extension</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$s (%2$d modules)</string>
<string name="qr_data_mask">Data mask</string>
<string name="toggle_flash">روشن/خاموش کردن چراغ</string>
<string name="error_flash">خطا هنگام روشن/خاموش کردن چراغ</string>
<string name="share">هم‌رسانی</string>
@@ -44,7 +50,7 @@
<string name="format">قالب</string>
<string name="size_width_by_height">اندازه به پیکسل: %1$d×%2$d</string>
<string name="size_no_magnification">No magnification</string>
<string name="margin_size">Margin in pixels: %1$d</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">محتویات ورودی را اینجا بنویسید</string>
<string name="unescape">Expand escape sequences</string>
<string name="encode">رمزگذاری</string>
@@ -58,6 +64,9 @@
<string name="bulk_mode_delay">تأخیر بین پویش‌های مداوم</string>
<string name="restrict_format">محدود کردن قالب</string>
<string name="remove_restriction">برداشتن محدودیت</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">پویش %s</string>
<string name="quarter_second">یک‌چهارم ثانیه</string>
<string name="half_second">نیم ثانیه</string>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">Bienvenue !</string>
<string name="mode_expert">Avancé</string>
<string name="mode_expert_description">Je souhaite vérifier le contenu lu et l\'ouvrir manuellement.</string>
<string name="mode_simple">Simple</string>
<string name="mode_simple_description">Je veux simplement scanner les codes-barres et les ouvrir immédiatement.</string>
<string name="camera_error">Impossible d\'accéder à la caméra, veuillez réessayer</string>
<string name="scan_code">Scanner un code</string>
<string name="compose_barcode">Créer un code-barres</string>
@@ -29,6 +34,7 @@
<string name="gtin_add_on">Extension UPC EAN</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$s (%2$d modules)</string>
<string name="qr_data_mask">Data mask</string>
<string name="toggle_flash">Activer/Désactiver le flash</string>
<string name="error_flash">Erreur de changement d\'état du flash</string>
<string name="share">Partager</string>
@@ -44,7 +50,7 @@
<string name="format">Format</string>
<string name="size_width_by_height">Taille en pixels : %1$d×%2$d</string>
<string name="size_no_magnification">Pas de grossissement</string>
<string name="margin_size">Marge en pixels : %1$d</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">Insérer le contenu ici</string>
<string name="unescape">Étendre les caractères d\'échappement</string>
<string name="encode">ENCODER</string>
@@ -58,6 +64,9 @@
<string name="bulk_mode_delay">Délai entre les scans continus</string>
<string name="restrict_format">Restreindre le format</string>
<string name="remove_restriction">Supprimer la restriction</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">Scan %s</string>
<string name="quarter_second">Un quart de seconde</string>
<string name="half_second">Une demi-seconde</string>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">Üdvözöljük!</string>
<string name="mode_expert">Haladó</string>
<string name="mode_expert_description">Szeretném ellenőrizni az olvasott tartalmat és kézzel megnyitni.</string>
<string name="mode_simple">Egyszerű</string>
<string name="mode_simple_description">Csak be akarom olvasni a vonalkódokat, és azonnal megnyitni őket.</string>
<string name="camera_error">Nem lehet hozzáférni a kamerához, próbálja meg újra</string>
<string name="scan_code">Kód beolvasása</string>
<string name="compose_barcode">Vonalkód létrehozása</string>
@@ -29,6 +34,7 @@
<string name="gtin_add_on">UPC EAN kiterjesztés</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$s (%2$d modules)</string>
<string name="qr_data_mask">Data mask</string>
<string name="toggle_flash">Vaku be- vagy kikapcsolása</string>
<string name="error_flash">Hiba a vaku be- vagy kikapcsolásakor</string>
<string name="share">Megosztás</string>
@@ -44,7 +50,7 @@
<string name="format">Formátum</string>
<string name="size_width_by_height">Méret képpontban: %1$d×%2$d</string>
<string name="size_no_magnification">No magnification</string>
<string name="margin_size">Margin in pixels: %1$d</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">Itt adja meg a tartalmat</string>
<string name="unescape">Expand escape sequences</string>
<string name="encode">KÓDOLÁS</string>
@@ -58,6 +64,9 @@
<string name="bulk_mode_delay">Folyamatos beolvasások közti késleltetés</string>
<string name="restrict_format">Formátum korlátozása</string>
<string name="remove_restriction">Korlátozás eltávolítása</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">%s beolvasása</string>
<string name="quarter_second">Negyed másodperc</string>
<string name="half_second">Fél másodperc</string>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">Selamat datang!</string>
<string name="mode_expert">Lanjutan</string>
<string name="mode_expert_description">Saya ingin memeriksa konten yang dibaca dan membukanya secara manual.</string>
<string name="mode_simple">Sederhana</string>
<string name="mode_simple_description">Saya hanya ingin memindai kode batang dan langsung membukanya.</string>
<string name="camera_error">Tidak bisa mengakses kamera, silakan coba lagi</string>
<string name="scan_code">Pindai kode</string>
<string name="compose_barcode">Buat barcode</string>
@@ -28,6 +33,7 @@
<string name="gtin_add_on">Ekstensi UPC EAN</string>
<string name="barcode_version_number">Versi</string>
<string name="qr_version_and_modules">%1$s (%2$d modul)</string>
<string name="qr_data_mask">Data mask</string>
<string name="toggle_flash">Nyala/matikan blitz</string>
<string name="error_flash">Galat nyala/matikan blitz</string>
<string name="share">Bagikan</string>
@@ -43,7 +49,7 @@
<string name="format">Format</string>
<string name="size_width_by_height">Ukuran dalam piksel: %1$d×%2$d</string>
<string name="size_no_magnification">No magnification</string>
<string name="margin_size">Margin in pixels: %1$d</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">Masukkan konten di sini</string>
<string name="unescape">Expand escape sequences</string>
<string name="encode">ENKODE</string>
@@ -57,6 +63,9 @@
<string name="bulk_mode_delay">Tundaan antara pemindaian tanpa henti</string>
<string name="restrict_format">Batasi format</string>
<string name="remove_restriction">Hapus pembatasan</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">Pindai %s</string>
<string name="quarter_second">Seperempat detik</string>
<string name="half_second">Setengah detik</string>
+11 -2
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">Benvenuto!</string>
<string name="mode_expert">Avanzato</string>
<string name="mode_expert_description">Voglio controllare il contenuto letto e aprirlo manualmente.</string>
<string name="mode_simple">Semplice</string>
<string name="mode_simple_description">Voglio solo scansionare codici a barre e aprirli immediatamente.</string>
<string name="camera_error">Impossibile accedere alla fotocamera. Riprova</string>
<string name="scan_code">Scansione codice</string>
<string name="compose_barcode">Crea codice a barre</string>
@@ -29,6 +34,7 @@
<string name="gtin_add_on">Estensione UPC EAN</string>
<string name="barcode_version_number">Versione</string>
<string name="qr_version_and_modules">%1$s (%2$d moduli)</string>
<string name="qr_data_mask">Maschera dati</string>
<string name="toggle_flash">Attiva/disattiva flash</string>
<string name="error_flash">Errore durante attivazione/disattivazione flash</string>
<string name="share">Condividi</string>
@@ -44,7 +50,7 @@
<string name="format">Formato</string>
<string name="size_width_by_height">Dimensioni (pixel): %1$d×%2$d</string>
<string name="size_no_magnification">Nessun ingrandimento</string>
<string name="margin_size">Margine (pixel): %1$d</string>
<string name="add_quiet_zone">Aggiungi zona tranquilla</string>
<string name="input_content_here">Inserisci qui il contenuto</string>
<string name="unescape">Espandi sequenze escape</string>
<string name="encode">CODIFICA</string>
@@ -58,6 +64,9 @@
<string name="bulk_mode_delay">Ritardo tra scansioni continue</string>
<string name="restrict_format">Limita formato</string>
<string name="remove_restriction">Rimuovi limitazione</string>
<string name="find_code">Trova codice</string>
<string name="find_code_with_content">Quale contenuto?</string>
<string name="find_code_hint">Inserisci un termine</string>
<string name="scan_format">Scansiona %s</string>
<string name="quarter_second">Un quarto di secondo</string>
<string name="half_second">Mezzo secondo</string>
@@ -106,7 +115,7 @@
<string name="show_meta_data">Visualizza metadati</string>
<string name="show_meta_data_summary">Visualizza dati aggiuntivi codice a barre scansionato.</string>
<string name="show_hex_dump">Visualizza dump esadecimale</string>
<string name="show_hex_dump_summary">Visualizza un dump esadecimale dei contenuti scansionati.</string>
<string name="show_hex_dump_summary">Visualizza dump esadecimale contenuti scansionati.</string>
<string name="show_checksum">Visualizza checksum</string>
<string name="no_checksum">Nessuna checksum</string>
<string name="show_recreation">Visualizza codice a barre ricreato</string>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">ようこそ!</string>
<string name="mode_expert">上級</string>
<string name="mode_expert_description">読み取った内容を確認し、手動で開きたい。</string>
<string name="mode_simple">簡単</string>
<string name="mode_simple_description">バーコードをスキャンしてすぐに開きたい。</string>
<string name="camera_error">カメラに接続できませんでした。再試行してください。</string>
<string name="scan_code">バーコードをスキャン</string>
<string name="compose_barcode">バーコードを作成</string>
@@ -28,6 +33,7 @@
<string name="gtin_add_on">UPC/EAN 拡張型</string>
<string name="barcode_version_number">バージョン</string>
<string name="qr_version_and_modules">%1$s (%2$d modules)</string>
<string name="qr_data_mask">Data mask</string>
<string name="toggle_flash">フラッシュの切り替え</string>
<string name="error_flash">フラッシュの切り替えエラー</string>
<string name="share">共有</string>
@@ -43,7 +49,7 @@
<string name="format">フォーマット</string>
<string name="size_width_by_height">大きさ(ピクセル): %1$d×%2$d</string>
<string name="size_no_magnification">No magnification</string>
<string name="margin_size">Margin in pixels: %1$d</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">ここに内容を入力</string>
<string name="unescape">Expand escape sequences</string>
<string name="encode">エンコード</string>
@@ -57,6 +63,9 @@
<string name="bulk_mode_delay">連続スキャンの間隔</string>
<string name="restrict_format">フォーマットの制限</string>
<string name="remove_restriction">制限を解除</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">%sをスキャン</string>
<string name="quarter_second">0.25秒</string>
<string name="half_second">0.5秒</string>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">მოგესალმებით!</string>
<string name="mode_expert">გაფართოებული</string>
<string name="mode_expert_description">მინდა გადავამოწმო წაკითხული შინაარსი და გავხსნა ხელით.</string>
<string name="mode_simple">მარტივი</string>
<string name="mode_simple_description">მხოლოდ მინდა დავასკანერო ბარკოდი და გავხსნა დაუყოვნებლივ.</string>
<string name="camera_error">კამერაზე წვდომის მოპოვება ვერ ხერხდება, გთხოვთ, კიდევ სცადეთ</string>
<string name="scan_code">შტრიხ-კოდის სკანირება</string>
<string name="compose_barcode">შტრიხ-კოდის შექმნა</string>
@@ -29,6 +34,7 @@
<string name="gtin_add_on">UPC EAN გაფართოება</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$s (%2$d modules)</string>
<string name="qr_data_mask">Data mask</string>
<string name="toggle_flash">განათების ჩართვა</string>
<string name="error_flash">Error toggling flash</string>
<string name="share">გაზიარება</string>
@@ -44,7 +50,7 @@
<string name="format">ფორმატი</string>
<string name="size_width_by_height">ზომა პიქსელებში: %1$d×%2$d</string>
<string name="size_no_magnification">No magnification</string>
<string name="margin_size">Margin in pixels: %1$d</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">შეიყვანეთ ტექსტი აქ</string>
<string name="unescape">Expand escape sequences</string>
<string name="encode">შექმნა"</string>
@@ -58,6 +64,9 @@
<string name="bulk_mode_delay">Delay between continuous scans</string>
<string name="restrict_format">Restrict format</string>
<string name="remove_restriction">Remove restriction</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">Scan %s</string>
<string name="quarter_second">A quarter second</string>
<string name="half_second">Half a second</string>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">환영합니다!</string>
<string name="mode_expert">고급</string>
<string name="mode_expert_description">읽은 내용을 확인하고 수동으로 열고 싶습니다.</string>
<string name="mode_simple">간단</string>
<string name="mode_simple_description">바코드를 스캔하고 즉시 열고 싶습니다.</string>
<string name="camera_error">카메라를 접근할 수 없습니다. 다시 시도하세요</string>
<string name="scan_code">Scan code</string>
<string name="compose_barcode">Compose barcode</string>
@@ -28,6 +33,7 @@
<string name="gtin_add_on">UPC EAN extension</string>
<string name="barcode_version_number">버전</string>
<string name="qr_version_and_modules">%1$s (%2$d modules)</string>
<string name="qr_data_mask">Data mask</string>
<string name="toggle_flash">Toggle flash</string>
<string name="error_flash">Error toggling flash</string>
<string name="share">Share</string>
@@ -43,7 +49,7 @@
<string name="format">Format</string>
<string name="size_width_by_height">Size in pixels: %1$d×%2$d</string>
<string name="size_no_magnification">No magnification</string>
<string name="margin_size">Margin in pixels: %1$d</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">Input content here</string>
<string name="unescape">Expand escape sequences</string>
<string name="encode">ENCODE</string>
@@ -57,6 +63,9 @@
<string name="bulk_mode_delay">Delay between continuous scans</string>
<string name="restrict_format">Restrict format</string>
<string name="remove_restriction">Remove restriction</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">Scan %s</string>
<string name="quarter_second">A quarter second</string>
<string name="half_second">Half a second</string>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">Welkom!</string>
<string name="mode_expert">Geavanceerd</string>
<string name="mode_expert_description">Ik wil de gelezen inhoud controleren en handmatig openen.</string>
<string name="mode_simple">Eenvoudig</string>
<string name="mode_simple_description">Ik wil gewoon barcodes scannen en direct openen.</string>
<string name="camera_error">Geen toegang tot camera, probeer opnieuw.</string>
<string name="scan_code">Scan een code</string>
<string name="compose_barcode">Barcode scannen</string>
@@ -29,6 +34,7 @@
<string name="gtin_add_on">UPC EAN extensie</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$s (%2$d modules)</string>
<string name="qr_data_mask">Data mask</string>
<string name="toggle_flash">Zaklamp aan/uit</string>
<string name="error_flash">Error toggling flash</string>
<string name="share">Delen</string>
@@ -44,7 +50,7 @@
<string name="format">Format</string>
<string name="size_width_by_height">Afmeting in pixels: %1$d×%2$d</string>
<string name="size_no_magnification">No magnification</string>
<string name="margin_size">Margin in pixels: %1$d</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">Voer hier de inhoud in</string>
<string name="unescape">Expand escape sequences</string>
<string name="encode">Coderen</string>
@@ -58,6 +64,9 @@
<string name="bulk_mode_delay">Vertraging tussen ononderbroken scans</string>
<string name="restrict_format">Restrict format</string>
<string name="remove_restriction">Remove restriction</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">Scan %s</string>
<string name="quarter_second">¼ seconde</string>
<string name="half_second">½ seconde</string>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">Witamy!</string>
<string name="mode_expert">Zaawansowany</string>
<string name="mode_expert_description">Chcę sprawdzić odczytaną treść i otworzyć ją ręcznie.</string>
<string name="mode_simple">Prosty</string>
<string name="mode_simple_description">Chcę tylko skanować kody kreskowe i otwierać je natychmiast.</string>
<string name="open_url">Otwórz URL</string>
<string name="content">Zawartość</string>
<string name="copied_to_clipboard">Skopiowano do schowka</string>
@@ -90,6 +95,9 @@
<string name="bulk_mode_delay">Opóźnienie między kolejnymi skanami</string>
<string name="restrict_format">Ogranicz format</string>
<string name="remove_restriction">Usuń ograniczenie</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">Skan %s</string>
<string name="quarter_second">Ćwierć sekundy</string>
<string name="half_second">Pół sekundy</string>
@@ -176,6 +184,7 @@
<string name="gtin_add_on">Dodatek UPC EAN</string>
<string name="barcode_version_number">Wersja</string>
<string name="qr_version_and_modules">%1$s (%2$d modules)</string>
<string name="qr_data_mask">Data mask</string>
<string name="gtin_country">Możliwy kraj produkcji</string>
<string name="encode">ZAKODUJ</string>
<string name="error_no_content">Brak zawartości</string>
@@ -186,7 +195,7 @@
<string name="cannot_resolve_action">Żadna aplikacja nie może tego otworzyć</string>
<string name="size_width_by_height">Rozmiar w pikselach: %1$d×%2$d</string>
<string name="size_no_magnification">No magnification</string>
<string name="margin_size">Margines w pikselach: %1$d</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">Wprowadź zawartość tutaj</string>
<string name="unescape">Rozwiń sekwencje ucieczki</string>
<string name="pick_list_separator">Jak oddzielić elementy listy?</string>
+11 -3
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">Bem-vindo!</string>
<string name="mode_expert">Avançado</string>
<string name="mode_expert_description">Quero verificar o conteúdo lido e abri-lo manualmente.</string>
<string name="mode_simple">Simples</string>
<string name="mode_simple_description">Quero apenas ler códigos de barras e abri-los imediatamente.</string>
<string name="camera_error">Não foi possível acessar a câmera, tente novamente</string>
<string name="scan_code">Escanear código</string>
<string name="compose_barcode">Compor código de barras</string>
@@ -7,7 +12,6 @@
<string name="binary_data">(dados em binário)</string>
<plurals name="barcode_info">
<item quantity="one">%2$d caractere, %1$s</item>
<item quantity="many">%2$d caracteres, %1$s</item>
<item quantity="other">%2$d caracteres, %1$s</item>
</plurals>
<string name="error_correction_level">Nível de correção de erro</string>
@@ -30,6 +34,7 @@
<string name="gtin_add_on">Extensão UPC EAN</string>
<string name="barcode_version_number">Versão</string>
<string name="qr_version_and_modules">%1$s (%2$d módulos)</string>
<string name="qr_data_mask">Data mask</string>
<string name="toggle_flash">Ligar lanterna</string>
<string name="error_flash">Falha ao ligar a lanterna</string>
<string name="share">Compartilhar</string>
@@ -44,8 +49,8 @@
<string name="pick_search_engine">Escolha um motor de pesquisa</string>
<string name="format">Formato</string>
<string name="size_width_by_height">Tamanho em pixels: %1$d×%2$d</string>
<string name="size_no_magnification">No magnification</string>
<string name="margin_size">Margem em pixels: %1$d</string>
<string name="size_no_magnification">Sem magnificação</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">Digite algum conteúdo aqui</string>
<string name="unescape">Expandir sequências de escape</string>
<string name="encode">CODIFICAR</string>
@@ -59,6 +64,9 @@
<string name="bulk_mode_delay">Espera entre digitalizações contínuas</string>
<string name="restrict_format">Restringir formato</string>
<string name="remove_restriction">Remover restrição</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">Digitalizar %s</string>
<string name="quarter_second">Um quarto de segundo</string>
<string name="half_second">Meio segundo</string>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">Добро пожаловать!</string>
<string name="mode_expert">Расширенный</string>
<string name="mode_expert_description">Я хочу проверять считанные данные и открывать их вручную.</string>
<string name="mode_simple">Простой</string>
<string name="mode_simple_description">Я просто хочу сканировать штрих-коды и сразу их открывать.</string>
<string name="camera_error">Невозможно получить доступ к камере, попробуйте ещё раз</string>
<string name="scan_code">Сканировать штрих-код</string>
<string name="compose_barcode">Создать штрих-код</string>
@@ -31,6 +36,7 @@
<string name="gtin_add_on">Расширение UPC EAN</string>
<string name="barcode_version_number">Версия</string>
<string name="qr_version_and_modules">%1$s (модули: %2$d)</string>
<string name="qr_data_mask">Маска данных</string>
<string name="toggle_flash">Включить вспышку</string>
<string name="error_flash">Ошибка при переключении вспышки</string>
<string name="share">Поделиться</string>
@@ -46,7 +52,7 @@
<string name="format">Формат</string>
<string name="size_width_by_height">Размер в пикселях: %1$d×%2$d</string>
<string name="size_no_magnification">Без увеличения</string>
<string name="margin_size">Отступ в пикселях: %1$d</string>
<string name="add_quiet_zone">Добавить отступы</string>
<string name="input_content_here">Введите текст здесь</string>
<string name="unescape">Раскрывать управляющие последовательности</string>
<string name="encode">Создать</string>
@@ -60,6 +66,9 @@
<string name="bulk_mode_delay">Задержка непрерывного сканирования</string>
<string name="restrict_format">Исключить формат</string>
<string name="remove_restriction">Включить формат</string>
<string name="find_code">Поиск кода</string>
<string name="find_code_with_content">Что искать?</string>
<string name="find_code_hint">Введите поисковый запрос</string>
<string name="scan_format">Сканировать %s</string>
<string name="quarter_second">Четверть секунды</string>
<string name="half_second">Полсекунды</string>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">Hoş geldiniz!</string>
<string name="mode_expert">Gelişmiş</string>
<string name="mode_expert_description">Okunan içeriği kontrol etmek ve manuel olarak açmak istiyorum.</string>
<string name="mode_simple">Basit</string>
<string name="mode_simple_description">Barkodları taramak ve hemen açmak istiyorum.</string>
<string name="camera_error">Kameraya erişilemiyor, lütfen yeniden deneyin</string>
<string name="scan_code">Kod tara</string>
<string name="compose_barcode">Barkod oluştur</string>
@@ -29,6 +34,7 @@
<string name="gtin_add_on">UPC EAN eklentisi</string>
<string name="barcode_version_number">Sürüm</string>
<string name="qr_version_and_modules">%1$s (%2$d modül)</string>
<string name="qr_data_mask">Data mask</string>
<string name="toggle_flash">Flaşı aç</string>
<string name="error_flash">Flaş açılırken hata oluştu</string>
<string name="share">Paylaş</string>
@@ -44,7 +50,7 @@
<string name="format">Biçim</string>
<string name="size_width_by_height">Piksel türünde boyut: %1$d×%2$d</string>
<string name="size_no_magnification">No magnification</string>
<string name="margin_size">Piksel türünde kenar boşluğu: %1$d</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">İçeriği buraya gir</string>
<string name="unescape">Kaçış dizilerini genişlet</string>
<string name="encode">KODLA</string>
@@ -58,6 +64,9 @@
<string name="bulk_mode_delay">Sürekli taramalar arasındaki gecikme</string>
<string name="restrict_format">Biçimi kısıtla</string>
<string name="remove_restriction">Kısıtlamayı kaldır</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">%s tara</string>
<string name="quarter_second">Çeyrek saniye</string>
<string name="half_second">Yarım saniye</string>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">Ласкаво просимо!</string>
<string name="mode_expert">Розширений</string>
<string name="mode_expert_description">Я хочу перевірити прочитаний вміст і відкрити його вручну.</string>
<string name="mode_simple">Простий</string>
<string name="mode_simple_description">Я просто хочу сканувати штрих-коди і відкривати їх одразу.</string>
<string name="camera_error">Неможливо отримати доступ до камери, будь ласка, спробуйте ще раз</string>
<string name="scan_code">Сканування коду</string>
<string name="compose_barcode">Створити штрих-код</string>
@@ -30,6 +35,7 @@
<string name="gtin_add_on">Розширення UPC EAN</string>
<string name="barcode_version_number">Версія</string>
<string name="qr_version_and_modules">%1$s (%2$d модулі)</string>
<string name="qr_data_mask">Data mask</string>
<string name="toggle_flash">Увімкнути спалах</string>
<string name="error_flash">Помилка під час увімкнення спалаху</string>
<string name="share">Поділитися</string>
@@ -45,7 +51,7 @@
<string name="format">Формат</string>
<string name="size_width_by_height">Розмір у пікселях: %1$d×%2$d</string>
<string name="size_no_magnification">Без збільшення</string>
<string name="margin_size">Відступ у пікселях: %1$d</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">Введіть вміст сюди</string>
<string name="unescape">Розгорнути спеціальні послідовності</string>
<string name="encode">КОДУВАТИ</string>
@@ -59,6 +65,9 @@
<string name="bulk_mode_delay">Затримка між безперервним скануванням</string>
<string name="restrict_format">Обмежити формат</string>
<string name="remove_restriction">Скасувати обмеження</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">Сканувати %s</string>
<string name="quarter_second">Чверть секунди</string>
<string name="half_second">Півсекунди</string>
+3
View File
@@ -0,0 +1,3 @@
<resources>
<bool name="fitsSystemWindows">false</bool>
</resources>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">Chào mừng!</string>
<string name="mode_expert">Nâng cao</string>
<string name="mode_expert_description">Tôi muốn kiểm tra nội dung đã đọc và mở nó theo cách thủ công.</string>
<string name="mode_simple">Đơn giản</string>
<string name="mode_simple_description">Tôi chỉ muốn quét mã vạch và mở ngay lập tức.</string>
<string name="camera_error">Không thể mở camera, hãy thử lại</string>
<string name="scan_code">Quét mã</string>
<string name="compose_barcode">Tạo mã vạch</string>
@@ -28,6 +33,7 @@
<string name="gtin_add_on">Phần mở rộng UPC EAN</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$s (%2$d modules)</string>
<string name="qr_data_mask">Data mask</string>
<string name="toggle_flash">Bật/tắt đèn</string>
<string name="error_flash">Error toggling flash</string>
<string name="share">Chia sẻ</string>
@@ -43,7 +49,7 @@
<string name="format">Định dạng</string>
<string name="size_width_by_height">Kích cỡ theo pixel: %1$d×%2$d</string>
<string name="size_no_magnification">No magnification</string>
<string name="margin_size">Margin in pixels: %1$d</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">Nhập nội dung vào đây</string>
<string name="unescape">Expand escape sequences</string>
<string name="encode">MÃ HOÁ</string>
@@ -57,6 +63,9 @@
<string name="bulk_mode_delay">Thời gian giữa các lần quét</string>
<string name="restrict_format">Restrict format</string>
<string name="remove_restriction">Remove restriction</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">Scan %s</string>
<string name="quarter_second">Một phần tư giây</string>
<string name="half_second">Nửa giây</string>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">欢迎!</string>
<string name="mode_expert">高级</string>
<string name="mode_expert_description">我想检查读取的内容并手动打开。</string>
<string name="mode_simple">简单</string>
<string name="mode_simple_description">我只想扫描条形码并立即打开。</string>
<string name="camera_error">无法访问相机,请重试</string>
<string name="scan_code">扫码</string>
<string name="compose_barcode">生成条码</string>
@@ -28,6 +33,7 @@
<string name="gtin_add_on">UPC EAN 扩展</string>
<string name="barcode_version_number">版本</string>
<string name="qr_version_and_modules">%1$s (%2$d modules)</string>
<string name="qr_data_mask">Data mask</string>
<string name="toggle_flash">开关闪光灯</string>
<string name="error_flash">开关闪光灯出错</string>
<string name="share">分享</string>
@@ -43,7 +49,7 @@
<string name="format">编码格式</string>
<string name="size_width_by_height">图片尺寸: %1$d×%2$d</string>
<string name="size_no_magnification">No magnification</string>
<string name="margin_size">边距(像素):%1$d</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">在这里输入内容</string>
<string name="unescape">展开转义字符</string>
<string name="encode">编码</string>
@@ -57,6 +63,9 @@
<string name="bulk_mode_delay">连续扫描之间的延迟</string>
<string name="restrict_format">严格模式(扫描指定编码格式)</string>
<string name="remove_restriction">解除限制</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">扫描 %s</string>
<string name="quarter_second">四分之一秒</string>
<string name="half_second">半秒</string>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">歡迎!</string>
<string name="mode_expert">進階</string>
<string name="mode_expert_description">我想檢查讀取的內容並手動開啟。</string>
<string name="mode_simple">簡單</string>
<string name="mode_simple_description">我只想掃描條碼並立即開啟。</string>
<string name="camera_error">無法開啟相機,請再試一次</string>
<string name="scan_code">掃描條碼</string>
<string name="compose_barcode">製作條碼</string>
@@ -28,6 +33,7 @@
<string name="gtin_add_on">UPC EAN 擴充</string>
<string name="barcode_version_number">版本</string>
<string name="qr_version_and_modules">%1$s (%2$d 模組)</string>
<string name="qr_data_mask">Data mask</string>
<string name="toggle_flash">控制閃光燈</string>
<string name="error_flash">控制閃光燈時發生錯誤</string>
<string name="share">分享</string>
@@ -43,7 +49,7 @@
<string name="format">格式</string>
<string name="size_width_by_height">圖片尺寸: %1$d×%2$d</string>
<string name="size_no_magnification">No magnification</string>
<string name="margin_size">間距大小: %1$d</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">請在這裡輸入內容</string>
<string name="unescape">展開逸出序列</string>
<string name="encode">編碼</string>
@@ -57,6 +63,9 @@
<string name="bulk_mode_delay">連續掃描間的延遲</string>
<string name="restrict_format">限制格式</string>
<string name="remove_restriction">刪除限制</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">掃描 %s</string>
<string name="quarter_second">四分之一秒</string>
<string name="half_second">半秒</string>
+3
View File
@@ -0,0 +1,3 @@
<resources>
<bool name="fitsSystemWindows">true</bool>
</resources>
+10 -1
View File
@@ -1,4 +1,9 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="welcome">Welcome!</string>
<string name="mode_expert">Advanced</string>
<string name="mode_expert_description">I would like to check the read content and open it manually.</string>
<string name="mode_simple">Simple</string>
<string name="mode_simple_description">I just want to read barcodes and have them opened immediately.</string>
<string name="camera_error">Can\'t access camera, please try again</string>
<string name="scan_code">Scan code</string>
<string name="compose_barcode">Compose barcode</string>
@@ -29,6 +34,7 @@
<string name="gtin_add_on">UPC EAN extension</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules" tools:ignore="PluralsCandidate">%1$s (%2$d modules)</string>
<string name="qr_data_mask">Data mask</string>
<string name="toggle_flash">Toggle flash</string>
<string name="error_flash">Error toggling flash</string>
<string name="share">Share</string>
@@ -44,7 +50,7 @@
<string name="format">Format</string>
<string name="size_width_by_height">Size in pixels: %1$d×%2$d</string>
<string name="size_no_magnification">No magnification</string>
<string name="margin_size">Margin in pixels: %1$d</string>
<string name="add_quiet_zone">Add quiet zone</string>
<string name="input_content_here">Input content here</string>
<string name="unescape">Expand escape sequences</string>
<string name="encode">ENCODE</string>
@@ -58,6 +64,9 @@
<string name="bulk_mode_delay">Delay between continuous scans</string>
<string name="restrict_format">Restrict format</string>
<string name="remove_restriction">Remove restriction</string>
<string name="find_code">Find code</string>
<string name="find_code_with_content">What content?</string>
<string name="find_code_hint">Enter a term</string>
<string name="scan_format">Scan %s</string>
<string name="quarter_second">A quarter second</string>
<string name="half_second">Half a second</string>
+20
View File
@@ -26,6 +26,26 @@
<item name="android:paddingRight">@dimen/button_padding</item>
<item name="android:maxLines">1</item>
</style>
<style name="WelcomeButton" parent="Button">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:background">@drawable/button_welcome</item>
<item name="android:padding">24dp</item>
</style>
<style name="WelcomeButtonText">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginBottom">8dp</item>
<item name="android:gravity">center</item>
<item name="android:textStyle">bold</item>
<item name="android:textAppearance">?android:attr/textAppearanceLarge</item>
</style>
<style name="WelcomeButtonSubText">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:gravity">center</item>
<item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
</style>
<style name="Spinner">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
+3 -3
View File
@@ -1,8 +1,8 @@
buildscript {
ext {
kotlin_version = '2.0.21'
tools_version = '8.5.1'
sdk_version = 34
kotlin_version = '2.1.0'
tools_version = '8.8.1'
sdk_version = 35
support_version = '25.3.1'
}
@@ -0,0 +1,5 @@
* Fix UTF-8 conversion of binary escape sequences when creating barcodes
* Fix scan order for intervals under one second
* Add arabic translation
* Update French translation
@@ -0,0 +1,9 @@
* Find barcode by content
* Immediately replace toasts
* Use detected barcode for recreation
* Show data mask of QR Codes
* Fix deep linking
* Replace margin with quiet zone switch when generating barcodes
* Update Russian translation
* Update Portuguese (Brazil) translation
@@ -0,0 +1,3 @@
* Handle empty input for recreation
* Fix setting up database
@@ -0,0 +1,4 @@
* Separate crop window settings for image scanning
* Fix initializing dataMask
* Update Russian translation
@@ -0,0 +1,6 @@
* Add a welcome screen to choose a mode
* Add a deep link to encode a barcode
* Remember zoom level of generated barcodes
* Remove Human Readable Interpretation (HRI) decoration
* Update Italian translation
@@ -0,0 +1,6 @@
* Improve simple mode
* Remember quiet zone settings
* Reduce height for 1D barcodes
* Add execute argument for binaryeye://encode
* Update Russian translation
@@ -0,0 +1,2 @@
* Sanity check ROI metrics
@@ -1 +1 @@
Mais um leitor de códigos QR para Android. Sem anúncios e de software livre.
Mais um leitor de códigos QR para Android. Sem anúncios e de código-aberto.
+1 -1
View File
@@ -1 +1 @@
Mais um leitor de códigos QR etc
Mais um leitor de códigos QR
+1 -1
View File
@@ -1,6 +1,6 @@
#Wed Jan 26 11:58:22 CET 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
distributionPath=wrapper/dists
networkTimeout=10000
zipStorePath=wrapper/dists