Compare commits

..
82 Commits
Author SHA1 Message Date
Markus Fisch f4de041407 Advance version number to 1.53.1 2022-10-20 09:38:36 +02:00
Markus Fisch 73ba2914e8 Fix proguard configuration for ZXing C++ 2022-10-20 09:37:45 +02:00
Markus Fisch 2907c8c49c Advance version number to 1.53.0 2022-10-19 22:27:21 +02:00
Markus Fisch f28f956a8d Only generate barcode output when required 2022-10-19 21:45:15 +02:00
Markus Fisch 972eb6a0b0 Update to latest ZXing C++ wrapper 2022-10-19 21:43:48 +02:00
Markus Fisch 08bb3f2127 Replace RelativeLayout with LinearLayout
In decoding fragment.

All children were layed out linearly anyway and it's simpler
and more clear this way.
2022-10-19 19:54:33 +02:00
Markus Fisch b28e939f58 Refactor resolveFormat() and make it an extension 2022-10-19 13:04:48 +02:00
solokotandGitHub 2edb8112b7 Update Russian translation 2022-10-19 13:04:06 +02:00
Markus Fisch cf5a6dc8b8 Hide setting a custom locale from API 33 on
Android 13 allows to set per-app language preferences, so this
setting is no longer required from there on.

See:
https://developer.android.com/guide/topics/resources/app-languages
2022-10-15 18:43:40 +02:00
Markus Fisch 3ec6f18fe0 Hide sensitive content from clipboard
At the moment, this affects just the WiFi password.

See:
https://developer.android.com/about/versions/13/behavior-changes-all#copy-sensitive-content
2022-10-15 18:38:14 +02:00
Markus Fisch 3a8f6cb827 Target SDK 33, Android 13 2022-10-15 18:37:49 +02:00
Markus Fisch 65c7fe52ee Fix closing Cursor's below JELLY BEAN
Unfortunately, Cursor cannot be cast to Closeable below API level 16
JELLY BEAN. So we can't just use Kotlin's ".use" extension function
because it would implicitly cast Cursor to a Closeable.

So this adds a simple drop-in replacement that does exactly what ".use"
is doing but without casting Cursor.

Of course, this should be removed as soon as minSDK is increased to at
least JELLY_BEAN.
2022-10-13 23:02:32 +02:00
Markus Fisch 5d3b3eb195 Fix generating a date time string below API 18
Before API level 18, there was no 'H' but only 'k', which was
incorrectly implemented to produce what we now know as 'H'.
2022-10-13 22:39:52 +02:00
Markus Fisch 0e4c2963a5 Catch ParseException when parsing scan dates 2022-10-13 22:38:04 +02:00
Markus Fisch 791c50cd4a Update build tools 2022-10-13 22:37:00 +02:00
Markus Fisch c04b08ab87 Update to latest ZXing C++ wrapper
Which adds a ContentType so we can distinguish between text
and binary content.
2022-10-12 21:47:54 +02:00
Markus Fisch f07c4ac0f4 Update to latest Kotlin and coroutines version 2022-10-11 23:43:30 +02:00
Markus Fisch 05d63bd73f Fix vector image fill color for API < 21
`fillColor` cannot have Android resources below API 21.
2022-10-11 23:37:29 +02:00
Markus Fisch 558fd5212d Remove double support annotation 2022-10-11 23:34:54 +02:00
Markus Fisch b918be76e4 Fix getting version number from database 2022-10-11 23:33:59 +02:00
Markus Fisch 2136a8c73e Show version code for all supported barcodes
And the module count if it's a QR Code.
2022-10-11 23:29:59 +02:00
Markus Fisch 959c6e7d4d Always show QR Code version and remove setting
This comes no longer with a cost so it can be always there.
2022-10-11 23:02:47 +02:00
Markus Fisch 50930d07a6 Show binary data placeholder in edit name dialog
To show something instead of nothing in the dialog title.
Still not perfect, of course.
2022-10-11 22:47:14 +02:00
Markus Fisch 36e7aab27c Update to latest ZXing C++ AAR
Fixes getting HRI formatted result text instead of the real thing.
2022-10-11 22:39:14 +02:00
Markus Fisch bb4fc8ed08 Enable multi-select in history listing 2022-10-11 21:53:15 +02:00
Markus Fisch 7a33ebd1a3 Remove MicroQR from list of writable codes
ZXing C++ doesn't contain a writer for this format, yet.
2022-10-11 20:41:18 +02:00
solokotandGitHub c8444b8935 Update Russian translation 2022-10-10 12:22:06 +02:00
Markus Fisch 4c6d66a4aa Migrate to ZXing-C++
Replace the ZXing Java implementation with the C++ fork.

The C++ fork is faster, actively maintained and supports the MicroQR
Code barcode format.

The original Java implementation is in "Maintenance Mode Only",
what means that no new features or barcodes will be accepted.

Because the C++ fork is much faster, we can now drop RenderScript
which has been deprecated by Google anyway. Also, this removes a lot
of complications. RenderScript was never very robust.
2022-10-09 18:46:50 +02:00
Markus Fisch 39f95a1c04 Remove shortcut extra from EncodeFragment
And make it possible to open the EncodeFragment without content.
2022-10-04 21:19:47 +02:00
Markus Fisch 9e30b108e7 Reformat code and optimize imports
Let Android Studio clean up.
2022-09-29 18:42:00 +02:00
Markus Fisch 1b6b1cefe3 Convert charset for PDF417 in an extension
And run it from PickActivity too to apply this conversion when
scanning from still images too.
2022-09-29 18:39:01 +02:00
Mladen MilinkovicandGitHub b634370a0b Fix PDF-417 UTF-8 text encoding
This fixes reading PDF-417 barcodes containing international UTF-8 characters.
2022-09-29 18:23:07 +02:00
gonandGitHub f32d44c3a1 Update spanish translation 2022-09-27 20:59:12 +02:00
Markus Fisch 2e060d1ca8 Add Persian to list of custom languages
That can be changed in the app independently from the system settings.
2022-09-11 23:01:31 +02:00
Andreas KorbandGitHub 6acdce32f2 Add multidex dependency
Required on some systems/Android Studio versions.
2022-09-10 21:33:04 +02:00
Markus Fisch 092ec694f6 Fix terminating string tag in persian translation 2022-09-10 19:12:03 +02:00
7fed7877a0 Add persian translation
Co-authored-by: eshagh79 <eshagh094@gmail.com>
2022-09-10 19:09:05 +02:00
Markus Fisch b17393d5ef Invert plain text output
Because plain text will most likely end up being black text on a
white background on a mobile device.

But with night mode we can never be sure. So there's an "inverted"
parameter now if the text is bright on a dark background.
2022-08-21 00:36:49 +02:00
Markus Fisch 319ae53b3b Make conditional an argument
To avoid the repetition of the function invocation.

Also make bt a constant variable because it can be.
2022-08-20 19:03:29 +02:00
Markus Fisch 6a47a7e477 Fix indent of new code for generating text output
We're not mixing tabs and spaces.
2022-08-20 18:56:50 +02:00
Tom XinandGitHub 19fb733182 Improve plain text output of generated barcodes
Optimize the 'save as text' function to make its results more recognizable when
printing on the console.
2022-08-20 18:53:43 +02:00
Markus Fisch ce6c6f26d2 Advance version number to 1.52.0 2022-08-10 23:25:46 +02:00
Markus Fisch 3633af5e0a Fix parsing timezone in calendar dates
And add patterns for RFC 822 and ISO 8601 formats.

Before this 'Z' was quoted and therefore just a string in the pattern.
So the time zone was actually never really applied to the resulting date.
Now, it's pattern letter and the resulting date is adjusted accordingly.

This also fixes using dates without a time zone.
2022-08-10 23:07:17 +02:00
87502f7226 Update translation (#280)
* Syntax correction (omitted point)

* Update french translation

Co-authored-by: Grena <grena@grenabox.fr>
2022-06-22 13:30:53 +02:00
Markus Fisch f4af35ad46 Advance version number to 1.51.1 2022-06-16 16:51:39 +02:00
Markus Fisch 60f0d8bd87 Convert result text with ISO_8859_1 encoding
Important to suppress UTF-8 encoding.
2022-06-16 16:45:31 +02:00
Markus Fisch 1ccfcfcb8f Advance version number to 1.51.0 2022-06-16 13:37:44 +02:00
Markus Fisch 213689e9f9 Improve syntax for DP link generation 2022-06-16 13:30:07 +02:00
Markus Fisch 6e2f2cab92 Make drawing functions extensions in DetectorView
Because it's more idiomatic.
2022-06-16 13:20:32 +02:00
Markus Fisch 4b3d745154 Add and improve a few comments 2022-06-16 13:20:12 +02:00
Markus Fisch a2bcd67568 Use logical operator instead of bitwise 2022-06-16 13:19:14 +02:00
Markus Fisch 072eec5306 Simplify setting handleActive flag
And make sure it's false if handlePos is invalid.
2022-06-16 13:17:16 +02:00
ZahnstocherandGitHub d505c6b07d Add stamp tracking number 2022-06-14 18:41:41 +02:00
GiovanniandGitHub dbfbaab030 Update Italian translation 2022-06-14 14:26:07 +02:00
Markus Fisch 3d1402add4 Fix XML for zh-rCN translation 2022-06-12 20:29:20 +02:00
yzqzssandGitHub 1d2bd8d778 Update zh-rCN translation 2022-06-12 20:22:11 +02:00
Markus Fisch 2bb413e3e8 Add tests for MATMSG format 2022-06-07 20:43:15 +02:00
Markus Fisch 75a59bc3e9 Fix setting Intent.EXTRA_EMAIL for MatMsg contents
Remember, this must be an array.
2022-06-06 20:59:03 +02:00
inkhornandGitHub 9a15bf0871 Update ptbr translation including meta data 2022-05-31 20:28:03 +02:00
Markus Fisch 53248fd23d Allow free image rotation in pick activity
To make it easy to recognize barcodes at odd angles.
2022-05-23 23:33:23 +02:00
Markus Fisch 1dde8c2817 Advance version number to 1.50.0 2022-05-18 22:48:58 +02:00
Markus Fisch 57fea95b9b Add support for MATMSG format 2022-05-18 22:37:56 +02:00
solokotandGitHub 806dafbecd Updated Russian translation 2022-05-18 12:11:14 +02:00
Markus Fisch 44b788fa74 Add a setting to show the QR Code version
Which is determined by recreating the QR Code.

Knowing the exact version of a given QR Code can be useful sometimes.
2022-05-17 19:03:03 +02:00
ss11mikandGitHub 9f167e7a2b Update Czech translation
Add Czech translation for new strings and update some old ones
2022-05-15 13:25:46 +02:00
Markus Fisch 6852b65239 Recognize URLs in colloquial, incomplete form too
So "google.com" is recognized as web action.
2022-05-14 12:50:22 +02:00
Markus Fisch 3d7c9be7ba Update Kotlin version 2022-05-12 22:40:03 +02:00
Markus Fisch f9401a9dda Migrate to lint from lintOptions 2022-05-12 22:40:03 +02:00
Markus Fisch 79dd48b07a Move package from Android manifest to build files
Declaration of a project's namespace using the package attribute
of the Android manifest is deprecated in favour of a namespace
declaration in build files.
2022-05-12 22:40:03 +02:00
Markus Fisch 6a7ab2c6e5 Update build tools and gradle wrapper 2022-05-12 22:40:03 +02:00
NickoriginalandGitHub c76d96cbe4 Update Russian translation 2022-05-12 22:39:23 +02:00
Markus Fisch e1b3935659 Add timezone offset in calendar dates
VCalendar/VEvent dates may contain a timezone that was not actually
used, as Date.getTime() returns the number of milliseconds (since
January 1, 1970, 00:00:00 GMT) without taking the timezone into
account.
2022-05-09 21:47:51 +02:00
Markus Fisch b7a4db5f4b Update to latest ZXing version 2022-05-02 18:54:30 +02:00
Markus Fisch f23414b003 Remove unused getLinks() function
No longer used.
2022-04-30 20:25:06 +02:00
Markus Fisch 282d2499a8 Shorten encoding a barcode 2022-04-25 20:17:53 +02:00
Markus Fisch a6cc8ed32f Add sample for AndroidX Activity's to README
Because `startActivityForResult()` is deprecated there.
2022-04-25 18:11:12 +02:00
Markus Fisch 7b155369fd Fix closing end tag in Japanese translation 2022-04-24 15:16:54 +02:00
NPLandGitHub 55a69db2c5 Update Japanese translation 2022-04-24 15:14:17 +02:00
Balázs ÚrandGitHub 170878a795 Update Hungarian translation 2022-04-17 11:15:11 +02:00
Markus Fisch 18ade0fe33 Update icon and feature graphic for F-Droid 2022-04-10 13:30:37 +02:00
Markus Fisch 4565848344 Add changelog for latest version for f-droid 2022-04-08 12:12:37 +02:00
Markus Fisch 353e1b5494 Update tools version 2022-04-08 12:11:25 +02:00
105 changed files with 1959 additions and 1842 deletions
+42
View File
@@ -1,5 +1,47 @@
# Change Log
## 1.53.1
* Fix proguard configuration
## 1.53.0
* Migrate to ZXing-C++ and add support for Micro QR Codes
* Multi-select scans in history listing
* Remove preset content for Encode shortcut
* Always show QR Code version and remove setting
* Fix PDF-417 UTF-8 text encoding
* Hide sensitive content from clipboard
* Remove setting a custom language on Android 13+
* Condense text output of generated barcodes
* Update Russian translation
* Update Spanish translation
* Add Persian translation
## 1.52.0
* Fix parsing timezone in calendar dates
* Update French translation
## 1.51.1
* Fix extraction of raw bytes for some barcodes
## 1.51.0
* Add support for Deutsche Post Matrixcode stamp
* Allow free image rotation for scanning from images
* Fix setting TO field in MATMSG format
* Update Italian translation
* Update zh-rCN translation
* Update pt-br translation
## 1.50.0
* Add support for MATMSG format
* Add a setting to show the QR Code version
* Add timezone offset in calendar dates
* Recognize colloquial incomplete URLs
* Update ZXing library to latest version
* Update Czech translation
* Update Russian translation
* Update Japanese translation
* Update Hungarian translation
## 1.49.2
* Update design of app icon
* Update pt-br translation
-3
View File
@@ -20,9 +20,6 @@ infer: clean
test:
./gradlew test
cat:
./gradlew test cAT
testview:
adb shell am start -W -a android.intent.action.VIEW -d 'binaryeye://scan'
+48 -52
View File
@@ -7,8 +7,8 @@ This one is free, without any ads and open source.
Works in portrait and landscape orientation, can read inverted codes,
comes in Material Design and can also generate barcodes.
Binary Eye uses the [ZXing][zxing] ("Zebra Crossing") barcode scanning
library.
Binary Eye uses the [ZXing-C++][zxing_cpp] ("Zebra Crossing") barcode
scanning library.
## Screenshots
@@ -39,10 +39,10 @@ ZXing can read the following barcode formats:
* [EAN 8][ean_8]
* [EAN 13][ean_13]
* [ITF][itf]
* [MAXICODE][maxicode] (only when unrotated and unskewed, see [77][77],
because of which Binary Eye *cannot* read this barcode)
* [MAXICODE][maxicode] (partial)
* [PDF417][pdf417]
* [QR CODE][qr_code]
* [Micro QR Code][micro_qr_code]
* [RSS 14][rss]
* [RSS EXPANDED][rss]
* [UPC A][upc_a]
@@ -89,60 +89,54 @@ Supported symbols are:
### SCAN Intent
You can also use Binary Eye from other apps by using the
`com.google.zxing.client.android.SCAN` Intent with `startActivityForResult()`
like this:
`com.google.zxing.client.android.SCAN` Intent with
[startActivityForResult()][start_activity] like this:
startActivityForResult(
Intent("com.google.zxing.client.android.SCAN"),
SOME_NUMBER
)
```kotlin
startActivityForResult(
Intent("com.google.zxing.client.android.SCAN"),
SOME_NUMBER
)
```
And process the result in `onActivityResult()` of your `Activity`:
And process the result in [onActivityResult()][on_activity_result] of your
`Activity`:
override fun onActivityResult(
requestCode: Int,
resultCode: Int,
data: Intent?
) {
when (requestCode) {
SOME_NUMBER -> if (resultCode == RESULT_OK) {
val result = data.getStringExtra("SCAN_RESULT")
}
```kotlin
override fun onActivityResult(
requestCode: Int,
resultCode: Int,
data: Intent?
) {
when (requestCode) {
SOME_NUMBER -> if (resultCode == RESULT_OK) {
val result = data.getStringExtra("SCAN_RESULT")
}
}
}
```
If you're using AndroidX, this would be the new,
[recommended way][intent_result]:
```kotlin
class YourActivity : Activity() {
private val resultLauncher = registerForActivityResult(StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val scan = result.data?.getStringExtra("SCAN_RESULT")
}
}
## RenderScript
This app uses [RenderScript][rs] to resize and rotate the camera image.
Unfortunately, RenderScript has some nasty gotchas.
### RenderScript.forceCompat()
It's necessary to call `RenderScript.forceCompat()` on some devices/roms.
`RenderScript.forceCompat()` needs to be run before any other RenderScript
function and unfortunately there is no way to know if invoking `forceCompat()`
is necessary or not.
If `RenderScript.forceCompat()` is necessary, a `RSRuntimeException` will
be thrown and the only option is to restart the app, this time with calling
`forceCompat()` first.
Calling `RenderScript.forceCompat()` means the processing is done in
software so you probably don't want to enable it by default.
### 2D barcodes
If you want to fork this and are only interested in reading 2D barcodes
(like QR or Aztec), you may want to remove the custom rotation kernel
altogether as ZXing can read 2D barcodes in any orientation.
This will make your app a bit simpler and saves you from compiling a
custom RenderScript kernel for each architecture you want to support.
fun openScanner() {
resultLauncher.launch(Intent("com.google.zxing.client.android.SCAN"))
}
}
```
[play]: https://play.google.com/store/search?q=barcode%20scanner&c=apps
[zxing]: https://github.com/zxing/zxing
[zxing_cpp]: https://github.com/zxing-cpp/zxing-cpp
[kotlin]: http://kotlinlang.org/
[aztec]: https://en.wikipedia.org/wiki/Aztec_Code
[codabar]: https://en.wikipedia.org/wiki/Codabar
@@ -156,9 +150,11 @@ custom RenderScript kernel for each architecture you want to support.
[maxicode]: https://en.wikipedia.org/wiki/MaxiCode
[pdf417]: https://en.wikipedia.org/wiki/PDF417
[qr_code]: https://en.wikipedia.org/wiki/QR_code
[micro_qr_code]: https://en.wikipedia.org/wiki/QR_code#Micro_QR_code
[rss]: https://en.wikipedia.org/wiki/GS1_DataBar
[upc_a]: https://en.wikipedia.org/wiki/Universal_Product_Code
[upc_e]: https://en.wikipedia.org/wiki/Universal_Product_Code#UPC-E
[upc_ean]: https://en.wikipedia.org/wiki/Universal_Product_Code#EAN-13
[77]: https://github.com/markusfisch/BinaryEye/issues/77
[rs]: https://developer.android.com/guide/topics/renderscript/compute
[start_activity]: https://developer.android.com/reference/android/app/Activity#startActivityForResult(android.content.Intent,%20int)
[on_activity_result]: https://developer.android.com/reference/android/app/Activity#onActivityResult(int,%20int,%20android.content.Intent)
[intent_result]: https://developer.android.com/training/basics/intents/result
+6 -37
View File
@@ -2,25 +2,15 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
android {
namespace 'de.markusfisch.android.binaryeye'
compileSdkVersion sdk_version
defaultConfig {
minSdkVersion 9
targetSdkVersion sdk_version
versionCode 96
versionName '1.49.2'
// Required for desugaring.
multiDexEnabled true
// It's recommended to set this value to the lowest API level
// able to provide all the functionality.
renderscriptTargetApi 24
renderscriptSupportModeEnabled true
renderscriptSupportModeBlasEnabled true
testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
versionCode 102
versionName '1.53.1'
}
signingConfigs {
@@ -63,39 +53,18 @@ android {
enableSplit = false
}
}
compileOptions {
// To desugar fancy Java features in ZXing 3.4.+ that don't
// work on SDK < 24. See:
// https://github.com/zxing/zxing/issues/1170
coreLibraryDesugaringEnabled true
}
lintOptions {
// Because at the time of writing, even the latest version
// of RenderScript is raising this error no matter if
// renderscriptTargetApi matches targetSdkVersion.
disable 'GradleCompatible'
}
}
dependencies {
androidTestImplementation('com.android.support.test:runner:1.0.2') {
exclude module: 'support-annotations'
}
androidTestImplementation('com.android.support.test:rules:1.0.2') {
exclude module: 'support-annotations'
}
testImplementation 'junit:junit:4.13.2'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1"
implementation "com.android.support:appcompat-v7:$support_version"
implementation "com.android.support:design:$support_version"
implementation "com.android.support:preference-v7:$support_version"
implementation "com.android.support:preference-v14:$support_version"
implementation 'com.google.zxing:core:3.4.1'
implementation 'com.github.markusfisch:CameraView:1.9.1'
implementation 'com.github.markusfisch:ScalingImageView:1.3.0'
implementation 'com.github.markusfisch:ScalingImageView:1.4.0'
implementation 'com.github.markusfisch:zxing-cpp:v1.4.0.4'
}
+2 -1
View File
@@ -3,4 +3,5 @@
-dontwarn android.support.v8.**
-keep class android.support.v7.widget.SearchView { *; }
-keep class android.support.v8.renderscript.** { *; }
-keep class de.markusfisch.android.zxingcpp.** { *; }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

@@ -1,107 +0,0 @@
package de.markusfisch.android.binaryeye
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.support.test.InstrumentationRegistry
import android.support.test.runner.AndroidJUnit4
import android.support.v8.renderscript.RenderScript
import de.markusfisch.android.binaryeye.rs.Preprocessor
import de.markusfisch.android.binaryeye.zxing.Zxing
import org.junit.Assert.fail;
import org.junit.Test
import org.junit.runner.RunWith
import java.util.regex.Pattern
@RunWith(AndroidJUnit4::class)
class PreprocessorTest {
@Test
fun processSamples() {
val assets = InstrumentationRegistry.getInstrumentation()
.context.assets
val samples = assets.list("samples")
checkNotNull(samples) { "no samples found" }
val pattern = Pattern.compile(
"[0-9]+-([0-9]+)deg.jpg"
)
val rs = RenderScript.create(
InstrumentationRegistry.getTargetContext(),
)
val zxing = Zxing()
for (sample in samples) {
val m = pattern.matcher(sample)
if (!m.find() || m.groupCount() < 1) {
fail("invalid sample: $sample")
}
val bitmap = BitmapFactory.decodeStream(
assets.open("samples/$sample")
)
val frameData = getNV21(bitmap)
val frameWidth = bitmap.width
val frameHeight = bitmap.height
val frameOrientation = m.group(1) ?: return
val preprocessor = Preprocessor(
rs,
frameWidth,
frameHeight,
null
)
val outWidth: Int
val outHeight: Int
val orientation = frameOrientation.toInt()
if (orientation == 90 || orientation == 270) {
preprocessor.resizeAndRotate(frameData)
outWidth = preprocessor.outHeight
outHeight = preprocessor.outWidth
} else {
preprocessor.resizeOnly(frameData)
outWidth = preprocessor.outWidth
outHeight = preprocessor.outHeight
}
val result = zxing.decode(frameData, outWidth, outHeight)
preprocessor.destroy()
checkNotNull(result) { "no barcode found in $sample" }
}
rs.destroy()
}
}
fun getNV21(bitmap: Bitmap): ByteArray {
val width = bitmap.width
val height = bitmap.height
val argb = IntArray(width * height)
bitmap.getPixels(argb, 0, width, 0, 0, width, height)
return encodeYUV420SP(argb, width, height)
}
private fun encodeYUV420SP(argb: IntArray, width: Int, height: Int): ByteArray {
val yuv = ByteArray(ceilIfUneven(width) * ceilIfUneven(height) * 3 / 2)
var yIndex = 0
var uvIndex = width * height
var r: Int
var g: Int
var b: Int
var y: Int
var u: Int
var v: Int
var index = 0
for (t in 0 until height) {
for (s in 0 until width) {
val pixel = argb[index]
r = pixel and 0xff0000 shr 16
g = pixel and 0xff00 shr 8
b = pixel and 0xff
y = (66 * r + 129 * g + 25 * b + 128 shr 8) + 16
u = (-38 * r - 74 * g + 112 * b + 128 shr 8) + 128
v = (112 * r - 94 * g - 18 * b + 128 shr 8) + 128
yuv[yIndex++] = (if (y < 0) 0 else if (y > 255) 255 else y).toByte()
if (t % 2 == 0 && index % 2 == 0) {
yuv[uvIndex++] = (if (v < 0) 0 else if (v > 255) 255 else v).toByte()
yuv[uvIndex++] = (if (u < 0) 0 else if (u > 255) 255 else u).toByte()
}
++index
}
}
return yuv
}
private fun ceilIfUneven(n: Int) = if (n and 1 == 1) n + 1 else n
-1
View File
@@ -1,6 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="de.markusfisch.android.binaryeye"
android:installLocation="auto">
<supports-screens
android:largeScreens="true"
@@ -1,6 +1,7 @@
package de.markusfisch.android.binaryeye.actions
import de.markusfisch.android.binaryeye.actions.mail.MailAction
import de.markusfisch.android.binaryeye.actions.mail.MatMsgAction
import de.markusfisch.android.binaryeye.actions.otpauth.OtpauthAction
import de.markusfisch.android.binaryeye.actions.search.OpenOrSearchAction
import de.markusfisch.android.binaryeye.actions.sms.SmsAction
@@ -15,13 +16,16 @@ object ActionRegistry {
private val REGISTRY: Set<IAction> = setOf(
MailAction,
MatMsgAction,
OtpauthAction,
SmsAction,
TelAction,
VCardAction,
VEventAction,
WebAction,
WifiAction
WifiAction,
// Try WebAction last because recognizing colloquial URLs is
// very aggressive.
WebAction
)
fun getAction(data: ByteArray): IAction = REGISTRY.find {
@@ -39,8 +39,10 @@ abstract class SchemeAction : IAction {
final override fun canExecuteOn(data: ByteArray): Boolean {
val content = String(data)
return if (buildRegex) {
content.matches("""^$scheme://[\w\W]+$""".toRegex(
RegexOption.IGNORE_CASE)
content.matches(
"""^$scheme://[\w\W]+$""".toRegex(
RegexOption.IGNORE_CASE
)
)
} else {
content.startsWith("$scheme://", ignoreCase = true)
@@ -0,0 +1,55 @@
package de.markusfisch.android.binaryeye.actions.mail
import android.content.Context
import android.content.Intent
import android.net.Uri
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.actions.IntentAction
object MatMsgAction : IntentAction() {
override val iconResId: Int = R.drawable.ic_action_mail
override val titleResId: Int = R.string.mail_send
override val errorMsg: Int = R.string.mail_error
override fun canExecuteOn(data: ByteArray): Boolean {
return String(data).startsWith("MATMSG:")
}
override suspend fun createIntent(
context: Context,
data: ByteArray
): Intent? {
val mm = MatMsg(String(data))
// Allow incomplete but not completely missing data.
if (mm.isEmpty()) {
return null
}
return Intent(
Intent.ACTION_SENDTO,
Uri.parse("mailto:")
).apply {
mm.to?.let {
putExtra(Intent.EXTRA_EMAIL, arrayOf(mm.to))
}
mm.sub?.let {
putExtra(Intent.EXTRA_SUBJECT, mm.sub)
}
mm.body?.let {
putExtra(Intent.EXTRA_TEXT, mm.body)
}
}
}
}
// Public so this can be tested.
class MatMsg(encoded: String) {
// Allow arbitrary order.
val to = encoded.extractFirst("""[:;]TO:([\w.%@+-]+);""")
val sub = encoded.extractFirst("""[:;]SUB:([^;]+);""")
val body = encoded.extractFirst("""[:;]BODY:([^;]+);""")
fun isEmpty() = to == null && sub == null && body == null
}
private fun String.extractFirst(regex: String) =
regex.toRegex().find(this)?.groupValues?.get(1)
@@ -4,8 +4,8 @@ import android.content.Context
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.actions.IAction
import de.markusfisch.android.binaryeye.app.alertDialog
import de.markusfisch.android.binaryeye.app.urlEncode
import de.markusfisch.android.binaryeye.app.prefs
import de.markusfisch.android.binaryeye.app.urlEncode
import de.markusfisch.android.binaryeye.content.openUrl
object OpenOrSearchAction : IAction {
@@ -9,7 +9,6 @@ import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.actions.IntentAction
import de.markusfisch.android.binaryeye.actions.vtype.VTypeParser
import java.util.*
import kotlin.collections.ArrayList
object VCardAction : IntentAction() {
override val iconResId: Int
@@ -1,6 +1,5 @@
package de.markusfisch.android.binaryeye.actions.vtype.vevent
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.os.Build
@@ -45,38 +44,49 @@ object VEventAction : IntentAction() {
}
info["DTSTART"]?.singleOrNull()?.also { eventStart ->
dateFormats.simpleFindParse(eventStart.value)?.also {
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, it.time)
putExtra(
CalendarContract.EXTRA_EVENT_BEGIN_TIME,
it.time
)
}
}
info["DTEND"]?.singleOrNull()?.also { eventEnd ->
dateFormats.simpleFindParse(eventEnd.value)?.also {
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, it.time)
putExtra(
CalendarContract.EXTRA_EVENT_END_TIME,
it.time
)
}
}
}
}
// We definitely don't wan't the local format.
@SuppressLint("SimpleDateFormat")
private val dateFormats = listOf(
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"),
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"),
SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"),
SimpleDateFormat("yyyyMMdd'T'HHmmss"),
SimpleDateFormat("yyyy-MM-dd"),
SimpleDateFormat("yyyyMMdd")
)
private fun List<SimpleDateFormat>.simpleFindParse(data: String): Date? {
for (simpleDataFormat in this) {
return simpleDataFormat.simpleParse(data) ?: continue
}
return null
}
private fun SimpleDateFormat.simpleParse(data: String): Date? = try {
this.parse(data)
} catch (e: Exception) {
null
}
}
private val dateFormats = listOf(
"yyyy-MM-dd'T'HH:mm:ssXXX",
"yyyy-MM-dd'T'HH:mm:ssZ",
"yyyy-MM-dd'T'HH:mm:ssz",
"yyyy-MM-dd'T'HH:mm:ss",
"yyyyMMdd'T'HHmmssXXX",
"yyyyMMdd'T'HHmmssZ",
"yyyyMMdd'T'HHmmssz",
"yyyyMMdd'T'HHmmss",
"yyyy-MM-dd",
"yyyyMMdd"
)
private fun List<String>.simpleFindParse(date: String): Date? {
for (pattern in this) {
return SimpleDateFormat(
pattern,
Locale.getDefault()
).simpleParse(date) ?: continue
}
return null
}
private fun SimpleDateFormat.simpleParse(date: String): Date? = try {
parse(date)
} catch (e: Exception) {
null
}
@@ -1,11 +1,28 @@
package de.markusfisch.android.binaryeye.actions.web
import android.content.Context
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.actions.SchemeAction
import de.markusfisch.android.binaryeye.actions.IAction
import de.markusfisch.android.binaryeye.content.openUrl
object WebAction : IAction {
private val colloquialRegex =
"""^(http[s]*://)*([a-z0-9]+[a-z0-9-]*[a-z0-9]+[.])+[a-z]{2,}([?#/]+[\w/=&;._-]*)*$""".toRegex(
RegexOption.IGNORE_CASE
)
object WebAction : SchemeAction() {
override val iconResId: Int = R.drawable.ic_action_open
override val titleResId: Int = R.string.open_url
override val scheme: String = "https?"
override val buildRegex: Boolean = true
override fun canExecuteOn(data: ByteArray): Boolean {
return String(data).trim().matches(colloquialRegex)
}
override suspend fun execute(context: Context, data: ByteArray) {
var url = String(data).trim()
if (!url.startsWith("http") && !url.startsWith("ftp")) {
url = "http://${url}"
}
context.openUrl(url)
}
}
@@ -7,7 +7,6 @@ import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Matrix
import android.graphics.Rect
import android.graphics.RectF
import android.hardware.Camera
import android.media.AudioManager
import android.media.ToneGenerator
@@ -17,15 +16,11 @@ import android.os.Vibrator
import android.support.design.widget.FloatingActionButton
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import android.support.v8.renderscript.RSRuntimeException
import android.support.v8.renderscript.RenderScript
import android.view.Menu
import android.view.MenuItem
import android.view.MotionEvent
import android.view.View
import android.widget.SeekBar
import com.google.zxing.Result
import com.google.zxing.ResultMetadataType
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.adapter.prettifyFormatName
import de.markusfisch.android.binaryeye.app.*
@@ -33,50 +28,39 @@ import de.markusfisch.android.binaryeye.content.copyToClipboard
import de.markusfisch.android.binaryeye.content.execShareIntent
import de.markusfisch.android.binaryeye.content.openUrl
import de.markusfisch.android.binaryeye.database.toScan
import de.markusfisch.android.binaryeye.graphics.getFrameToViewMatrix
import de.markusfisch.android.binaryeye.graphics.map
import de.markusfisch.android.binaryeye.graphics.FrameMetrics
import de.markusfisch.android.binaryeye.graphics.mapPosition
import de.markusfisch.android.binaryeye.graphics.setFrameRoi
import de.markusfisch.android.binaryeye.graphics.setFrameToView
import de.markusfisch.android.binaryeye.net.sendAsync
import de.markusfisch.android.binaryeye.os.error
import de.markusfisch.android.binaryeye.os.getVibrator
import de.markusfisch.android.binaryeye.os.vibrate
import de.markusfisch.android.binaryeye.rs.Preprocessor
import de.markusfisch.android.binaryeye.view.initSystemBars
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
import de.markusfisch.android.binaryeye.widget.DetectorView
import de.markusfisch.android.binaryeye.widget.toast
import de.markusfisch.android.binaryeye.zxing.Zxing
import de.markusfisch.android.cameraview.widget.CameraView
import de.markusfisch.android.zxingcpp.ZxingCpp
import de.markusfisch.android.zxingcpp.ZxingCpp.Format
import de.markusfisch.android.zxingcpp.ZxingCpp.Result
import java.nio.charset.Charset
import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToInt
class CameraActivity : AppCompatActivity() {
private val zxing = Zxing { point ->
point ?: return@Zxing
val matrix = getMappingMatrix() ?: return@Zxing
detectorView.post {
detectorView.update(
matrix.map(
arrayOf(point),
detectorView.coordinates
)
)
}
}
private val frameRoi = Rect()
private val matrix = Matrix()
private lateinit var rs: RenderScript
private lateinit var vibrator: Vibrator
private lateinit var cameraView: CameraView
private lateinit var detectorView: DetectorView
private lateinit var zoomBar: SeekBar
private lateinit var flashFab: FloatingActionButton
private var preprocessor: Preprocessor? = null
private var nativeMappingMatrix: Matrix? = null
private var rotatedMappingMatrix: Matrix? = null
private var recreatePreprocessor = false
private var rotate = false
private var invert = false
private var formats = setOf<String>()
private var frameMetrics = FrameMetrics()
private var decoding = true
private var returnResult = false
private var returnUrlTemplate: String? = null
@@ -132,7 +116,6 @@ class CameraActivity : AppCompatActivity() {
// custom locale.
setTitle(R.string.scan_code)
rs = RenderScript.create(this)
vibrator = getVibrator()
initSystemBars(this)
@@ -156,11 +139,9 @@ class CameraActivity : AppCompatActivity() {
override fun onDestroy() {
super.onDestroy()
resetPreProcessor()
fallbackBuffer = null
saveZoom()
detectorView.saveCropHandlePos()
rs.destroy()
}
override fun onResume() {
@@ -180,7 +161,7 @@ class CameraActivity : AppCompatActivity() {
private fun updateHints() {
val restriction = restrictFormat
val formats = if (restriction != null) {
formats = if (restriction != null) {
title = getString(
R.string.scan_format,
prettifyFormatName(restriction)
@@ -190,7 +171,6 @@ class CameraActivity : AppCompatActivity() {
setTitle(R.string.scan_code)
prefs.barcodeFormats
}
zxing.updateHints(prefs.tryHarder, formats)
}
private fun setReturnTarget(intent: Intent?) {
@@ -205,11 +185,6 @@ class CameraActivity : AppCompatActivity() {
}
}
private fun resetPreProcessor() {
preprocessor?.destroy()
preprocessor = null
}
private fun openCamera() {
cameraView.openAsync(
CameraView.findCameraId(
@@ -441,25 +416,30 @@ class CameraActivity : AppCompatActivity() {
}
override fun onCameraReady(camera: Camera) {
// Reset preprocessor to make sure it always fits the current
// frame orientation. Important for landscape to landscape
// orientation changes.
resetPreProcessor()
val frameWidth = cameraView.frameWidth
val frameHeight = cameraView.frameHeight
val frameOrientation = cameraView.frameOrientation
frameMetrics = FrameMetrics(
cameraView.frameWidth,
cameraView.frameHeight,
cameraView.frameOrientation
)
updateFrameRoiAndMappingMatrix()
ignoreNext = null
decoding = true
camera.setPreviewCallback { frameData, _ ->
if (decoding) {
decodeFrame(
ZxingCpp.readByteArray(
frameData,
frameWidth,
frameHeight,
frameOrientation
frameMetrics.width,
frameRoi.left, frameRoi.top,
frameRoi.width(), frameRoi.height(),
frameMetrics.orientation,
formats.joinToString(),
prefs.tryHarder,
prefs.autoRotate,
tryInvert = true,
tryDownscale = true
)?.let { result ->
if (result.text != ignoreNext) {
postResult(result)
postResult(result.redact())
decoding = false
}
}
@@ -526,12 +506,23 @@ class CameraActivity : AppCompatActivity() {
}
detectorView.onRoiChanged = {
decoding = true
recreatePreprocessor = true
updateFrameRoiAndMappingMatrix()
}
detectorView.setPaddingFromWindowInsets()
detectorView.restoreCropHandlePos()
}
private fun updateFrameRoiAndMappingMatrix() {
val viewRect = cameraView.previewRect
val viewRoi = if (detectorView.roi.width() < 1) {
viewRect
} else {
detectorView.roi
}
frameRoi.setFrameRoi(frameMetrics, viewRect, viewRoi)
matrix.setFrameToView(frameMetrics, viewRect, viewRoi)
}
private fun updateFlashFab(unavailable: Boolean) {
if (unavailable) {
flashFab.setImageResource(R.drawable.ic_action_create)
@@ -560,158 +551,11 @@ class CameraActivity : AppCompatActivity() {
}
}
private fun decodeFrame(
frameData: ByteArray?,
frameWidth: Int,
frameHeight: Int,
frameOrientation: Int
): Result? {
frameData ?: return null
rotate = if (prefs.autoRotate) {
rotate xor true
} else {
invert = invert xor true
isPortrait(frameOrientation)
}
return try {
if (recreatePreprocessor) {
resetPreProcessor()
recreatePreprocessor = false
}
val pp = preprocessor ?: createPreprocessorAndMapping(
frameWidth,
frameHeight,
frameOrientation
)
val w: Int
val h: Int
if (rotate) {
pp.resizeAndRotate(frameData)
w = pp.outHeight
h = pp.outWidth
} else {
invert = invert xor true
pp.resizeOnly(frameData)
w = pp.outWidth
h = pp.outHeight
}
preprocessor = pp
zxing.decode(frameData, w, h, invert)
} catch (e: RSRuntimeException) {
prefs.forceCompat = prefs.forceCompat xor true
// Now the only option is to restart the app because
// RenderScript.forceCompat() needs to be called before
// RenderScript is initialized.
restartApp()
null
}
}
private fun createPreprocessorAndMapping(
frameWidth: Int,
frameHeight: Int,
frameOrientation: Int
): Preprocessor {
val frameRoi = calculateFrameRect(
frameWidth,
frameHeight,
frameOrientation
)
val pp = Preprocessor(
rs,
frameWidth,
frameHeight,
frameRoi
)
val viewRect = if (frameRoi != null) {
detectorView.roi
} else {
cameraView.previewRect
}
nativeMappingMatrix = getFrameToViewMatrix(
pp.outWidth,
pp.outHeight,
frameOrientation,
viewRect
)
rotatedMappingMatrix = getFrameToViewMatrix(
pp.outHeight,
pp.outWidth,
(frameOrientation - 90 + 360) % 360,
viewRect
)
return pp
}
private fun calculateFrameRect(
frameWidth: Int,
frameHeight: Int,
frameOrientation: Int
): Rect? {
val viewRoi = detectorView.roi
return if (
viewRoi.width() == 0 ||
viewRoi.height() == 0
) {
null
} else {
// Map ROI in detectorView to cameraView.
val previewRect = cameraView.previewRect
// previewRect may be larger than the screen (and thus as the
// detectorView) in which case its left and/or top coordinate
// will be negative which must then be clamped to the
// screen/DetectorView.
val previewLeft = max(0, previewRect.left)
val previewTop = max(0, previewRect.top)
val previewRoi = Rect(
previewLeft + viewRoi.left,
previewTop + viewRoi.top,
previewLeft + viewRoi.right,
previewTop + viewRoi.bottom
)
val previewRectWidth = previewRect.width().toFloat()
val previewRectHeight = previewRect.height().toFloat()
val normalizedRoi = RectF(
previewRoi.left.toFloat() / previewRectWidth,
previewRoi.top.toFloat() / previewRectHeight,
previewRoi.right.toFloat() / previewRectWidth,
previewRoi.bottom.toFloat() / previewRectHeight
)
// Since the ROI is always centered and symmetric, we don't
// need to distinguish between 0 and 180 or 90 and 270 degree.
val errectedRoi = if (isPortrait(frameOrientation)) {
RectF(
normalizedRoi.top,
normalizedRoi.left,
normalizedRoi.bottom,
normalizedRoi.right
)
} else {
normalizedRoi
}
Rect(
(errectedRoi.left * frameWidth.toFloat()).roundToInt(),
(errectedRoi.top * frameHeight.toFloat()).roundToInt(),
(errectedRoi.right * frameWidth.toFloat()).roundToInt(),
(errectedRoi.bottom * frameHeight.toFloat()).roundToInt()
)
}
}
private fun postResult(result: Result) {
// Get matrix for the current rotate value.
val matrix = getMappingMatrix()
val resultPoints = result.resultPoints
if (matrix == null ||
resultPoints == null ||
resultPoints.isEmpty()
) {
return
}
cameraView.post {
detectorView.update(
matrix.map(
resultPoints,
matrix.mapPosition(
result.position,
detectorView.coordinates
)
)
@@ -761,12 +605,6 @@ class CameraActivity : AppCompatActivity() {
}
}
private fun getMappingMatrix() = if (rotate) {
rotatedMappingMatrix
} else {
nativeMappingMatrix
}
companion object {
private const val PICK_FILE_RESULT_CODE = 1
private const val ZOOM_MAX = "zoom_max"
@@ -777,6 +615,34 @@ class CameraActivity : AppCompatActivity() {
}
}
fun Result.redact() = if (
format == Format.PDF_417.name &&
text.isNotEmpty()
) {
Result(
format,
contentType,
String(
text.toByteArray(Charset.forName("ISO-8859-1")),
Charset.forName("UTF-8")
),
position,
orientation,
rawBytes,
ecLevel,
symbologyIdentifier,
sequenceSize,
sequenceIndex,
sequenceId,
readerInit,
lineCount,
versionNumber,
gtin
)
} else {
this
}
fun showResult(
activity: Activity,
result: Result,
@@ -821,45 +687,14 @@ fun showResult(
}
}
fun getReturnIntent(result: Result): Intent {
val intent = Intent()
intent.putExtra("SCAN_RESULT", result.text)
intent.putExtra(
"SCAN_RESULT_FORMAT",
result.barcodeFormat.toString()
)
if (result.rawBytes?.isNotEmpty() == true) {
intent.putExtra("SCAN_RESULT_BYTES", result.rawBytes)
private fun getReturnIntent(result: Result) = Intent().apply {
putExtra("SCAN_RESULT", result.text)
putExtra("SCAN_RESULT_FORMAT", result.format)
putExtra("SCAN_RESULT_ORIENTATION", result.orientation)
putExtra("SCAN_RESULT_ERROR_CORRECTION_LEVEL", result.ecLevel)
if (result.rawBytes.isNotEmpty()) {
putExtra("SCAN_RESULT_BYTES", result.rawBytes)
}
result.resultMetadata?.let { metadata ->
metadata[ResultMetadataType.ORIENTATION]?.let {
intent.putExtra(
"SCAN_RESULT_ORIENTATION",
it.toString()
)
}
metadata[ResultMetadataType.ERROR_CORRECTION_LEVEL]?.let {
intent.putExtra(
"SCAN_RESULT_ERROR_CORRECTION_LEVEL",
it.toString()
)
}
metadata[ResultMetadataType.UPC_EAN_EXTENSION]?.let {
intent.putExtra(
"SCAN_RESULT_UPC_EAN_EXTENSION",
it.toString()
)
}
metadata[ResultMetadataType.BYTE_SEGMENTS]?.let {
var i = 0
@Suppress("UNCHECKED_CAST")
for (seg in it as Iterable<ByteArray>) {
intent.putExtra("SCAN_RESULT_BYTE_SEGMENTS_$i", seg)
++i
}
}
}
return intent
}
private fun String.isReturnUrl() = listOf(
@@ -871,12 +706,9 @@ private fun String.isReturnUrl() = listOf(
private fun completeUrl(urlTemplate: String, result: Result) = Uri.parse(
urlTemplate
.replace("{RESULT}", result.text.urlEncode())
.replace("{RESULT_BYTES}", result.rawBytes?.toHexString() ?: "")
.replace("{RESULT_BYTES}", result.rawBytes.toHexString())
.replace(
"{FORMAT}", result.barcodeFormat.toString().urlEncode()
)
.replace(
"{META}", result.resultMetadata?.toString()?.urlEncode() ?: ""
"{FORMAT}", result.format.urlEncode()
)
// And support {CODE} from the old ZXing app, too.
.replace("{CODE}", result.text.urlEncode())
@@ -897,7 +729,3 @@ private fun beepBeepBeep() {
1000
)
}
private fun isPortrait(
orientation: Int
) = orientation == 90 || orientation == 270
@@ -80,9 +80,7 @@ class MainActivity : AppCompatActivity() {
return when {
intent.hasExtra(PREFERENCES) -> PreferencesFragment()
intent.hasExtra(HISTORY) -> HistoryFragment()
intent.hasExtra(ENCODE) -> EncodeFragment.newInstance(
intent.getStringExtra(ENCODE)!!
)
intent.hasExtra(ENCODE) -> EncodeFragment.newInstance()
intent.hasExtra(DECODED) -> DecodeFragment.newInstance(
intent.getParcelableExtra(DECODED)!!
)
@@ -3,6 +3,7 @@ package de.markusfisch.android.binaryeye.activity
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Matrix
import android.graphics.Rect
import android.graphics.RectF
import android.net.Uri
@@ -11,19 +12,17 @@ import android.os.Parcelable
import android.os.Vibrator
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import android.support.v8.renderscript.RenderScript
import android.view.Menu
import android.view.MenuItem
import com.google.zxing.Result
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.app.applyLocale
import de.markusfisch.android.binaryeye.app.prefs
import de.markusfisch.android.binaryeye.graphics.crop
import de.markusfisch.android.binaryeye.graphics.fixTransparency
import de.markusfisch.android.binaryeye.graphics.loadImageUri
import de.markusfisch.android.binaryeye.graphics.mapResultToView
import de.markusfisch.android.binaryeye.graphics.mapPosition
import de.markusfisch.android.binaryeye.os.getVibrator
import de.markusfisch.android.binaryeye.os.vibrate
import de.markusfisch.android.binaryeye.rs.fixTransparency
import de.markusfisch.android.binaryeye.view.colorSystemAndToolBars
import de.markusfisch.android.binaryeye.view.initSystemBars
import de.markusfisch.android.binaryeye.view.recordToolbarHeight
@@ -31,15 +30,18 @@ import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
import de.markusfisch.android.binaryeye.widget.CropImageView
import de.markusfisch.android.binaryeye.widget.DetectorView
import de.markusfisch.android.binaryeye.widget.toast
import de.markusfisch.android.binaryeye.zxing.Zxing
import de.markusfisch.android.zxingcpp.ZxingCpp
import de.markusfisch.android.zxingcpp.ZxingCpp.Result
import kotlinx.coroutines.*
import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToInt
class PickActivity : AppCompatActivity() {
private val zxing = Zxing()
private val matrix = Matrix()
private val parentJob = Job()
private val scope = CoroutineScope(Dispatchers.IO + parentJob)
private lateinit var rs: RenderScript
private lateinit var vibrator: Vibrator
private lateinit var cropImageView: CropImageView
private lateinit var detectorView: DetectorView
@@ -59,11 +61,6 @@ class PickActivity : AppCompatActivity() {
// locale.
setTitle(R.string.pick_code_to_scan)
zxing.updateHints(
true,
prefs.barcodeFormats
)
rs = RenderScript.create(this)
vibrator = getVibrator()
initSystemBars(this)
@@ -75,7 +72,7 @@ class PickActivity : AppCompatActivity() {
colorSystemAndToolBars(this@PickActivity)
}
val bitmap = fixTransparency(rs, getBitmapFromIntent())
val bitmap = getBitmapFromIntent()?.fixTransparency()
if (bitmap == null) {
applicationContext.toast(R.string.error_no_content)
finish()
@@ -84,6 +81,7 @@ class PickActivity : AppCompatActivity() {
cropImageView = findViewById(R.id.image) as CropImageView
cropImageView.restrictTranslation = false
cropImageView.freeRotation = true
cropImageView.setImageBitmap(bitmap)
cropImageView.onScan = {
scanWithinBounds(bitmap)
@@ -116,38 +114,55 @@ class PickActivity : AppCompatActivity() {
}
private fun scanWithinBounds(bitmap: Bitmap) {
val roi = Rect(detectorView.roi)
val rectInView = if (roi.width() < 1) {
val viewRoi = if (detectorView.roi.width() < 1) {
cropImageView.getBoundsRect()
} else {
roi
detectorView.roi
}
val imageRect = cropImageView.mappedRect
val rectInImage = normalizeRoi(imageRect, rectInView)
val mappedRect = cropImageView.mappedRect
val cropped = bitmap.crop(
rectInImage,
cropImageView.imageRotation
getNormalizedRoi(mappedRect, viewRoi),
cropImageView.imageRotation,
cropImageView.pivotX,
cropImageView.pivotY
) ?: return
val croppedInView = Rect(
max(viewRoi.left, mappedRect.left.roundToInt()),
max(viewRoi.top, mappedRect.top.roundToInt()),
min(viewRoi.right, mappedRect.right.roundToInt()),
min(viewRoi.bottom, mappedRect.bottom.roundToInt())
)
matrix.apply {
setScale(
croppedInView.width().toFloat() / cropped.width,
croppedInView.height().toFloat() / cropped.height
)
postTranslate(
croppedInView.left.toFloat(),
croppedInView.top.toFloat()
)
}
scope.launch {
result = zxing.decodePositiveNegative(cropped)
result?.let {
ZxingCpp.readBitmap(
cropped,
0, 0,
cropped.width, cropped.height,
0,
prefs.barcodeFormats.joinToString(),
tryHarder = true,
tryRotate = true,
tryInvert = true,
tryDownscale = true
)?.let {
withContext(Dispatchers.Main) {
if (isFinishing) {
return@withContext
}
result = it
vibrator.vibrate()
val rc = Rect()
imageRect.round(rc)
if (!rectInView.intersect(rc)) {
return@withContext
}
detectorView.update(
mapResultToView(
cropped.width,
cropped.height,
0,
rectInView,
it.resultPoints,
matrix.mapPosition(
it.position,
detectorView.coordinates
)
)
@@ -159,7 +174,6 @@ class PickActivity : AppCompatActivity() {
override fun onDestroy() {
super.onDestroy()
detectorView.saveCropHandlePos()
rs.destroy()
parentJob.cancel()
}
@@ -197,7 +211,7 @@ class PickActivity : AppCompatActivity() {
private fun showResult() {
val r = result
if (r != null) {
showResult(this, r, vibrator)
showResult(this, r.redact(), vibrator)
finish()
} else {
applicationContext.toast(R.string.no_barcode_found)
@@ -205,7 +219,7 @@ class PickActivity : AppCompatActivity() {
}
}
private fun normalizeRoi(imageRect: RectF, roi: Rect): RectF {
private fun getNormalizedRoi(imageRect: RectF, roi: Rect): RectF {
val w = imageRect.width()
val h = imageRect.height()
return RectF(
@@ -3,7 +3,6 @@ package de.markusfisch.android.binaryeye.activity
import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import de.markusfisch.android.binaryeye.app.setRestartCount
class SplashActivity : AppCompatActivity() {
override fun onCreate(state: Bundle?) {
@@ -13,7 +12,6 @@ class SplashActivity : AppCompatActivity() {
// because that would happen after the app is fully
// initialized what is too late.
intent?.setRestartCount()
startActivity(Intent(applicationContext, CameraActivity::class.java))
finish()
}
@@ -2,6 +2,7 @@ package de.markusfisch.android.binaryeye.adapter
import android.content.Context
import android.database.Cursor
import android.support.v4.content.ContextCompat
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -10,41 +11,71 @@ import android.widget.TextView
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.database.Database
import java.text.DateFormat
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.*
class ScansAdapter(context: Context, cursor: Cursor) :
CursorAdapter(context, cursor, false) {
var selectedScanId = 0L
var selectedScanPosition = -1
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 formatIndex = cursor.getColumnIndex(Database.SCANS_FORMAT)
private val selections = mutableMapOf<Long, Int>()
private val selectedColor = ContextCompat.getColor(
context, R.color.selected_row
)
fun select(id: Long, position: Int) {
selectedScanId = id
selectedScanPosition = position
fun select(view: View, id: Long, position: Int) {
view.select(
if (selections[id] == null) {
selections[id] = position
true
} else {
selections.remove(id)
false
}
)
}
fun clearSelection() {
selectedScanId = 0L
selectedScanPosition = -1
selections.clear()
}
fun getSelectedContent() = if (selectedScanPosition < 0) {
null
} else {
val cursor = getItem(selectedScanPosition)
if (cursor is Cursor) {
cursor.getString(contentIndex)
} else {
null
fun forSelection(callback: (Long, Int) -> Unit) {
selections.forEach {
callback.invoke(it.key, it.value)
}
}
fun getSelectedIds(): List<Long> = mutableListOf<Long>().apply {
forSelection { id, _ ->
add(id)
}
}
fun getSelectedContent(separator: String): String {
val sb = StringBuilder()
forSelection { _, position ->
getContent(position)?.let {
if (sb.isNotEmpty()) {
sb.append(separator)
}
sb.append(it)
}
}
return sb.toString()
}
fun getName(
position: Int
) = (getItem(position) as Cursor?)?.getString(nameIndex)
fun getContent(
position: Int
) = (getItem(position) as Cursor?)?.getString(contentIndex)
override fun newView(
context: Context,
cursor: Cursor,
@@ -78,13 +109,17 @@ class ScansAdapter(context: Context, cursor: Cursor) :
)
formatView.text = prettifyFormatName(cursor.getString(formatIndex))
}
// view.isSelected needs to be put on the queue to work.
val selected = cursor.getLong(idIndex) == selectedScanId
val selected = selections[cursor.getLong(idIndex)] != null
view.post {
view.isSelected = selected
// Needs to be put on the queue to work.
view.select(selected)
}
}
private fun View.select(selected: Boolean) {
setBackgroundColor(if (selected) selectedColor else 0)
}
private fun getViewHolder(
view: View
): ViewHolder = view.tag as ViewHolder? ?: ViewHolder(
@@ -105,11 +140,15 @@ class ScansAdapter(context: Context, cursor: Cursor) :
fun prettifyFormatName(format: String) = format.replace("_", " ")
private fun formatDateTime(rfc: String): String {
SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US).parse(rfc)?.let {
return DateFormat.getDateTimeInstance(
DateFormat.LONG,
DateFormat.SHORT
).format(it)
try {
SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US).parse(rfc)?.let {
return DateFormat.getDateTimeInstance(
DateFormat.LONG,
DateFormat.SHORT
).format(it)
}
} catch (e: ParseException) {
// Ignore and fall through.
}
return rfc
}
@@ -1,7 +1,7 @@
package de.markusfisch.android.binaryeye.app
import android.content.Context
import android.app.AlertDialog
import android.content.Context
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlin.coroutines.resume
@@ -1,21 +1,16 @@
package de.markusfisch.android.binaryeye.app
import android.support.multidex.MultiDexApplication;
import android.support.v8.renderscript.RenderScript
import android.app.Application
import de.markusfisch.android.binaryeye.database.Database
import de.markusfisch.android.binaryeye.preference.Preferences
val db = Database()
val prefs = Preferences()
class BinaryEyeApp : MultiDexApplication() {
class BinaryEyeApp : Application() {
override fun onCreate() {
super.onCreate()
db.open(this)
prefs.init(this)
if (prefs.forceCompat) {
RenderScript.forceCompat()
}
}
}
@@ -5,9 +5,6 @@ import android.os.Build
import java.io.UnsupportedEncodingException
import java.net.URLEncoder
private val nonPrintable = "[\\x00-\\x08\\x0e-\\x1f]".toRegex()
fun String.hasNonPrintableCharacters() = nonPrintable.containsMatchIn(this)
private val nonAlNum = "[^a-zA-Z0-9]".toRegex()
private val multipleDots = "[…]+".toRegex()
fun String.foldNonAlNum() = replace(nonAlNum, "")
@@ -1,29 +0,0 @@
package de.markusfisch.android.binaryeye.app
import java.util.regex.Pattern
private val urlPattern: Pattern = Pattern.compile(
"(?:^|[\\W])((ht|f)tp(s?):\\/\\/|www\\.)" +
"(([\\w\\-]+\\.){1,}?([\\w\\-.~]+\\/?)*" +
"[\\p{Alnum}.,%_=?&#\\-+()\\[\\]\\*$~@!:/{};']*)",
Pattern.CASE_INSENSITIVE or Pattern.MULTILINE or Pattern.DOTALL
)
fun getLinks(text: String): List<String> {
val matcher = urlPattern.matcher(text)
val urls = mutableListOf<String>()
while (matcher.find()) {
val matchStart = matcher.start(1)
val matchEnd = matcher.end()
var url = text.substring(matchStart, matchEnd)
if (!url.startsWith("http://") && !url.startsWith("https://")) {
url = "https://$url"
}
urls.add(url)
}
return urls
}
@@ -1,24 +0,0 @@
package de.markusfisch.android.binaryeye.app
import android.app.Activity
import android.content.Intent
import de.markusfisch.android.binaryeye.activity.SplashActivity
private const val RESTART_COUNT = "restart_count"
private var restartCount = 0
fun Intent.setRestartCount() {
restartCount = getIntExtra(RESTART_COUNT, restartCount)
}
fun Activity.restartApp() {
if (restartCount < 3) {
startActivity(Intent(this, SplashActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
putExtra(RESTART_COUNT, ++restartCount)
})
finish()
}
Runtime.getRuntime().exit(0)
}
@@ -1,17 +1,28 @@
package de.markusfisch.android.binaryeye.content
import android.content.ClipData
import android.content.ClipDescription
import android.content.Context
import android.os.Build
import android.os.PersistableBundle
fun Context.copyToClipboard(text: String) {
fun Context.copyToClipboard(text: String, isSensitive: Boolean = false) {
val service = getSystemService(Context.CLIPBOARD_SERVICE)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
@Suppress("DEPRECATION")
(service as android.text.ClipboardManager).text = text
} else {
(service as android.content.ClipboardManager).setPrimaryClip(
ClipData.newPlainText("plain text", text)
ClipData.newPlainText("plain text", text).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
description.extras = PersistableBundle().apply {
putBoolean(
ClipDescription.EXTRA_IS_SENSITIVE,
isSensitive
)
}
}
}
)
}
}
}
@@ -34,13 +34,14 @@ private fun exportCsv(
Database.SCANS_FORMAT,
Database.SCANS_CONTENT,
Database.SCANS_ERROR_CORRECTION_LEVEL,
Database.SCANS_ISSUE_NUMBER,
Database.SCANS_ORIENTATION,
Database.SCANS_OTHER_META_DATA,
Database.SCANS_PDF417_EXTRA_METADATA,
Database.SCANS_POSSIBLE_COUNTRY,
Database.SCANS_SUGGESTED_PRICE,
Database.SCANS_UPC_EAN_EXTENSION
Database.SCANS_VERSION_NUMBER,
Database.SCANS_SEQUENCE_SIZE,
Database.SCANS_SEQUENCE_INDEX,
Database.SCANS_SEQUENCE_ID,
Database.SCANS_GTIN_COUNTRY,
Database.SCANS_GTIN_ADD_ON,
Database.SCANS_GTIN_PRICE,
Database.SCANS_GTIN_ISSUE_NUMBER
)
val indices = columns.map {
cursor.getColumnIndex(it)
@@ -2,6 +2,25 @@ package de.markusfisch.android.binaryeye.database
import android.database.Cursor
// Overwrite Kotlin's ".use" function for Cursor because Cursor cannot
// be cast to Closeable below API level 16. This should be removed when
// the minSDK is increased to at least JELLY_BEAN (16).
inline fun <R> Cursor.use(block: (Cursor) -> R): R = try {
block.invoke(this)
} finally {
close()
}
fun Cursor.getInt(name: String): Int {
val idx = getColumnIndex(name)
return if (idx < 0) 0 else getInt(idx)
}
fun Cursor.getLong(name: String): Long {
val idx = getColumnIndex(name)
return if (idx < 0) 0L else getLong(idx)
}
fun Cursor.getString(name: String): String {
val idx = getColumnIndex(name)
return if (idx < 0) "" else getString(idx) ?: ""
@@ -11,8 +30,3 @@ fun Cursor.getBlob(name: String): ByteArray? {
val idx = getColumnIndex(name)
return if (idx < 0) null else getBlob(idx)
}
fun Cursor.getLong(name: String): Long {
val idx = getColumnIndex(name)
return if (idx < 0) 0L else getLong(idx)
}
@@ -36,13 +36,14 @@ class Database {
$SCANS_RAW,
$SCANS_FORMAT,
$SCANS_ERROR_CORRECTION_LEVEL,
$SCANS_ISSUE_NUMBER,
$SCANS_ORIENTATION,
$SCANS_OTHER_META_DATA,
$SCANS_PDF417_EXTRA_METADATA,
$SCANS_POSSIBLE_COUNTRY,
$SCANS_SUGGESTED_PRICE,
$SCANS_UPC_EAN_EXTENSION
$SCANS_VERSION_NUMBER,
$SCANS_SEQUENCE_SIZE,
$SCANS_SEQUENCE_INDEX,
$SCANS_SEQUENCE_ID,
$SCANS_GTIN_COUNTRY,
$SCANS_GTIN_ADD_ON,
$SCANS_GTIN_PRICE,
$SCANS_GTIN_ISSUE_NUMBER
FROM $SCANS
${getWhereClause(query)}
ORDER BY $SCANS_DATETIME DESC
@@ -77,13 +78,14 @@ class Database {
$SCANS_RAW,
$SCANS_FORMAT,
$SCANS_ERROR_CORRECTION_LEVEL,
$SCANS_ISSUE_NUMBER,
$SCANS_ORIENTATION,
$SCANS_OTHER_META_DATA,
$SCANS_PDF417_EXTRA_METADATA,
$SCANS_POSSIBLE_COUNTRY,
$SCANS_SUGGESTED_PRICE,
$SCANS_UPC_EAN_EXTENSION
$SCANS_VERSION_NUMBER,
$SCANS_SEQUENCE_SIZE,
$SCANS_SEQUENCE_INDEX,
$SCANS_SEQUENCE_ID,
$SCANS_GTIN_COUNTRY,
$SCANS_GTIN_ADD_ON,
$SCANS_GTIN_PRICE,
$SCANS_GTIN_ISSUE_NUMBER
FROM $SCANS
WHERE $SCANS_ID = ?
""".trimMargin(), arrayOf("$id")
@@ -94,13 +96,14 @@ class Database {
it.getBlob(SCANS_RAW),
it.getString(SCANS_FORMAT),
it.getString(SCANS_ERROR_CORRECTION_LEVEL),
it.getString(SCANS_ISSUE_NUMBER),
it.getString(SCANS_ORIENTATION),
it.getString(SCANS_OTHER_META_DATA),
it.getString(SCANS_PDF417_EXTRA_METADATA),
it.getString(SCANS_POSSIBLE_COUNTRY),
it.getString(SCANS_SUGGESTED_PRICE),
it.getString(SCANS_UPC_EAN_EXTENSION),
it.getInt(SCANS_VERSION_NUMBER),
it.getInt(SCANS_SEQUENCE_SIZE),
it.getInt(SCANS_SEQUENCE_INDEX),
it.getString(SCANS_SEQUENCE_ID),
it.getString(SCANS_GTIN_COUNTRY),
it.getString(SCANS_GTIN_ADD_ON),
it.getString(SCANS_GTIN_PRICE),
it.getString(SCANS_GTIN_ISSUE_NUMBER),
it.getString(SCANS_DATETIME),
it.getLong(SCANS_ID)
)
@@ -125,13 +128,14 @@ class Database {
scan.errorCorrectionLevel?.let {
put(SCANS_ERROR_CORRECTION_LEVEL, it)
}
scan.issueNumber?.let { put(SCANS_ISSUE_NUMBER, it) }
scan.orientation?.let { put(SCANS_ORIENTATION, it) }
scan.otherMetaData?.let { put(SCANS_OTHER_META_DATA, it) }
scan.pdf417ExtraMetaData?.let { put(SCANS_PDF417_EXTRA_METADATA, it) }
scan.possibleCountry?.let { put(SCANS_POSSIBLE_COUNTRY, it) }
scan.suggestedPrice?.let { put(SCANS_SUGGESTED_PRICE, it) }
scan.upcEanExtension?.let { put(SCANS_UPC_EAN_EXTENSION, it) }
put(SCANS_VERSION_NUMBER, scan.versionNumber)
put(SCANS_SEQUENCE_SIZE, scan.sequenceSize)
put(SCANS_SEQUENCE_INDEX, scan.sequenceIndex)
put(SCANS_SEQUENCE_ID, scan.sequenceId)
scan.country?.let { put(SCANS_GTIN_COUNTRY, it) }
scan.addOn?.let { put(SCANS_GTIN_ADD_ON, it) }
scan.price?.let { put(SCANS_GTIN_PRICE, it) }
scan.issueNumber?.let { put(SCANS_GTIN_ISSUE_NUMBER, it) }
if (prefs.ignoreConsecutiveDuplicates) {
val id = getIdOfLastScan(
get(SCANS_CONTENT) as String,
@@ -188,7 +192,7 @@ class Database {
}
private class OpenHelper(context: Context) :
SQLiteOpenHelper(context, FILE_NAME, null, 4) {
SQLiteOpenHelper(context, FILE_NAME, null, 5) {
override fun onCreate(db: SQLiteDatabase) {
createScans(db)
}
@@ -207,6 +211,9 @@ class Database {
if (oldVersion < 4) {
addNameColumn(db)
}
if (oldVersion < 5) {
migrateToZxingCpp(db)
}
}
}
@@ -220,6 +227,7 @@ class Database {
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_ISSUE_NUMBER = "issue_number"
const val SCANS_ORIENTATION = "orientation"
const val SCANS_OTHER_META_DATA = "other_meta_data"
@@ -227,6 +235,13 @@ class Database {
const val SCANS_POSSIBLE_COUNTRY = "possible_country"
const val SCANS_SUGGESTED_PRICE = "suggested_price"
const val SCANS_UPC_EAN_EXTENSION = "upc_ean_extension"
const val SCANS_SEQUENCE_SIZE = "sequence_size"
const val SCANS_SEQUENCE_INDEX = "sequence_index"
const val SCANS_SEQUENCE_ID = "sequence_id"
const val SCANS_GTIN_COUNTRY = "gtin_country"
const val SCANS_GTIN_ADD_ON = "gtin_add_on"
const val SCANS_GTIN_PRICE = "gtin_price"
const val SCANS_GTIN_ISSUE_NUMBER = "gtin_issue_number"
private fun createScans(db: SQLiteDatabase) {
db.execSQL("DROP TABLE IF EXISTS $SCANS".trimMargin())
@@ -239,13 +254,14 @@ class Database {
$SCANS_RAW BLOB,
$SCANS_FORMAT TEXT NOT NULL,
$SCANS_ERROR_CORRECTION_LEVEL TEXT,
$SCANS_ISSUE_NUMBER INT,
$SCANS_ORIENTATION INT,
$SCANS_OTHER_META_DATA TEXT,
$SCANS_PDF417_EXTRA_METADATA TEXT,
$SCANS_POSSIBLE_COUNTRY TEXT,
$SCANS_SUGGESTED_PRICE TEXT,
$SCANS_UPC_EAN_EXTENSION TEXT
$SCANS_VERSION_NUMBER INTEGER,
$SCANS_SEQUENCE_SIZE INTEGER,
$SCANS_SEQUENCE_INDEX INTEGER,
$SCANS_SEQUENCE_ID TEXT,
$SCANS_GTIN_COUNTRY TEXT,
$SCANS_GTIN_ADD_ON TEXT,
$SCANS_GTIN_PRICE TEXT,
$SCANS_GTIN_ISSUE_NUMBER TEXT
)""".trimMargin()
)
}
@@ -270,5 +286,24 @@ class Database {
private fun addNameColumn(db: SQLiteDatabase) {
db.execSQL("ALTER TABLE $SCANS ADD COLUMN $SCANS_NAME TEXT".trimMargin())
}
private fun migrateToZxingCpp(db: SQLiteDatabase) {
db.apply {
execSQL("ALTER TABLE $SCANS ADD COLUMN $SCANS_VERSION_NUMBER INTEGER".trimMargin())
execSQL("ALTER TABLE $SCANS ADD COLUMN $SCANS_SEQUENCE_SIZE INTEGER".trimMargin())
execSQL("ALTER TABLE $SCANS ADD COLUMN $SCANS_SEQUENCE_INDEX INTEGER".trimMargin())
execSQL("ALTER TABLE $SCANS ADD COLUMN $SCANS_SEQUENCE_ID TEXT".trimMargin())
execSQL("ALTER TABLE $SCANS ADD COLUMN $SCANS_GTIN_COUNTRY TEXT".trimMargin())
execSQL("ALTER TABLE $SCANS ADD COLUMN $SCANS_GTIN_ADD_ON TEXT".trimMargin())
execSQL("ALTER TABLE $SCANS ADD COLUMN $SCANS_GTIN_PRICE TEXT".trimMargin())
execSQL("ALTER TABLE $SCANS ADD COLUMN $SCANS_GTIN_ISSUE_NUMBER TEXT".trimMargin())
execSQL("UPDATE $SCANS SET $SCANS_SEQUENCE_SIZE = -1")
execSQL("UPDATE $SCANS SET $SCANS_SEQUENCE_INDEX = -1")
execSQL("UPDATE $SCANS SET $SCANS_GTIN_COUNTRY = $SCANS_POSSIBLE_COUNTRY")
execSQL("UPDATE $SCANS SET $SCANS_GTIN_ADD_ON = $SCANS_UPC_EAN_EXTENSION")
execSQL("UPDATE $SCANS SET $SCANS_GTIN_PRICE = $SCANS_SUGGESTED_PRICE")
execSQL("UPDATE $SCANS SET $SCANS_GTIN_ISSUE_NUMBER = $SCANS_ISSUE_NUMBER")
}
}
}
}
@@ -26,13 +26,14 @@ fun Cursor.exportJson(): String? {
Database.SCANS_FORMAT,
Database.SCANS_CONTENT,
Database.SCANS_ERROR_CORRECTION_LEVEL,
Database.SCANS_ISSUE_NUMBER,
Database.SCANS_ORIENTATION,
Database.SCANS_OTHER_META_DATA,
Database.SCANS_PDF417_EXTRA_METADATA,
Database.SCANS_POSSIBLE_COUNTRY,
Database.SCANS_SUGGESTED_PRICE,
Database.SCANS_UPC_EAN_EXTENSION
Database.SCANS_VERSION_NUMBER,
Database.SCANS_SEQUENCE_SIZE,
Database.SCANS_SEQUENCE_INDEX,
Database.SCANS_SEQUENCE_ID,
Database.SCANS_GTIN_COUNTRY,
Database.SCANS_GTIN_ADD_ON,
Database.SCANS_GTIN_PRICE,
Database.SCANS_GTIN_ISSUE_NUMBER
)
val indices = columns.map {
Pair(getColumnIndex(it), it)
@@ -1,29 +1,30 @@
package de.markusfisch.android.binaryeye.database
import android.os.Build
import android.os.Parcel
import android.os.Parcelable
import android.text.format.DateFormat
import com.google.zxing.Result
import com.google.zxing.ResultMetadataType
import de.markusfisch.android.binaryeye.app.hasNonPrintableCharacters
import de.markusfisch.android.zxingcpp.ZxingCpp.ContentType
import de.markusfisch.android.zxingcpp.ZxingCpp.Result
data class Scan(
val content: String,
val raw: ByteArray?,
val format: String,
val errorCorrectionLevel: String?,
val issueNumber: String?,
val orientation: String?,
val otherMetaData: String?,
val pdf417ExtraMetaData: String?,
val possibleCountry: String?,
val suggestedPrice: String?,
val upcEanExtension: String?,
val errorCorrectionLevel: String? = null,
val versionNumber: Int = 0,
val sequenceSize: Int = -1,
val sequenceIndex: Int = -1,
val sequenceId: String = "",
val country: String? = null,
val addOn: String? = null,
val price: String? = null,
val issueNumber: String? = null,
val dateTime: String = getDateTime(),
var id: Long = 0L
) : Parcelable {
// Needs to be overwritten manually, as ByteArray is an array and
// this isn't handled well by Kotlin
// this isn't handled well by Kotlin.
override fun equals(other: Any?): Boolean {
if (this === other) {
return true
@@ -41,17 +42,18 @@ data class Scan(
(raw != null && other.raw != null && raw.contentEquals(other.raw))) &&
format == other.format &&
errorCorrectionLevel == other.errorCorrectionLevel &&
issueNumber == other.issueNumber &&
orientation == other.orientation &&
otherMetaData == other.otherMetaData &&
pdf417ExtraMetaData == other.pdf417ExtraMetaData &&
possibleCountry == other.possibleCountry &&
suggestedPrice == other.suggestedPrice &&
upcEanExtension == other.upcEanExtension
versionNumber == other.versionNumber &&
sequenceSize == other.sequenceSize &&
sequenceIndex == other.sequenceIndex &&
sequenceId == other.sequenceId &&
country == other.country &&
addOn == other.addOn &&
price == other.price &&
issueNumber == other.issueNumber
}
// Needs to be overwritten manually, as ByteArray is an array and
// this isn't handled well by Kotlin
// this isn't handled well by Kotlin.
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + dateTime.hashCode()
@@ -59,13 +61,14 @@ data class Scan(
result = 31 * result + (raw?.contentHashCode() ?: 0)
result = 31 * result + format.hashCode()
result = 31 * result + (errorCorrectionLevel?.hashCode() ?: 0)
result = 31 * result + versionNumber
result = 31 * result + sequenceSize
result = 31 * result + sequenceIndex
result = 31 * result + sequenceId.hashCode()
result = 31 * result + (country?.hashCode() ?: 0)
result = 31 * result + (addOn?.hashCode() ?: 0)
result = 31 * result + (price?.hashCode() ?: 0)
result = 31 * result + (issueNumber?.hashCode() ?: 0)
result = 31 * result + (orientation?.hashCode() ?: 0)
result = 31 * result + (otherMetaData?.hashCode() ?: 0)
result = 31 * result + (pdf417ExtraMetaData?.hashCode() ?: 0)
result = 31 * result + (possibleCountry?.hashCode() ?: 0)
result = 31 * result + (suggestedPrice?.hashCode() ?: 0)
result = 31 * result + (upcEanExtension?.hashCode() ?: 0)
return result
}
@@ -74,13 +77,14 @@ data class Scan(
raw = parcel.readSizedByteArray(),
format = parcel.readString() ?: "",
errorCorrectionLevel = parcel.readString(),
versionNumber = parcel.readInt(),
sequenceSize = parcel.readInt(),
sequenceIndex = parcel.readInt(),
sequenceId = parcel.readString() ?: "",
country = parcel.readString(),
addOn = parcel.readString(),
price = parcel.readString(),
issueNumber = parcel.readString(),
orientation = parcel.readString(),
otherMetaData = parcel.readString(),
pdf417ExtraMetaData = parcel.readString(),
possibleCountry = parcel.readString(),
suggestedPrice = parcel.readString(),
upcEanExtension = parcel.readString(),
dateTime = parcel.readString() ?: "",
id = parcel.readLong()
)
@@ -91,13 +95,14 @@ data class Scan(
writeSizedByteArray(raw)
writeString(format)
writeString(errorCorrectionLevel)
writeInt(versionNumber)
writeInt(sequenceSize)
writeInt(sequenceIndex)
writeString(sequenceId)
writeString(country)
writeString(addOn)
writeString(price)
writeString(issueNumber)
writeString(orientation)
writeString(otherMetaData)
writeString(pdf417ExtraMetaData)
writeString(possibleCountry)
writeString(suggestedPrice)
writeString(upcEanExtension)
writeString(dateTime)
writeLong(id)
}
@@ -114,8 +119,16 @@ data class Scan(
}
}
private fun getDateTime(time: Long = System.currentTimeMillis()) = DateFormat.format(
"yyyy-MM-dd HH:mm:ss",
private fun getDateTime(
time: Long = System.currentTimeMillis()
) = DateFormat.format(
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
// Up to API level 17, 'k' was interpreted as 'H', which wasn't
// implemented until API level 18.
"yyyy-MM-dd kk:mm:ss"
} else {
"yyyy-MM-dd HH:mm:ss"
},
time
).toString()
@@ -141,47 +154,28 @@ private fun Parcel.readSizedByteArray(): ByteArray? {
fun Result.toScan(): Scan {
val content: String
val raw: ByteArray?
if (text.hasNonPrintableCharacters()) {
content = ""
raw = getRawData() ?: text.toByteArray()
} else {
content = text
raw = null
when (contentType) {
ContentType.TEXT -> {
content = text
raw = null
}
else -> {
content = ""
raw = rawBytes
}
}
return Scan(
content,
raw,
barcodeFormat.toString(),
getMetaString(ResultMetadataType.ERROR_CORRECTION_LEVEL),
getMetaString(ResultMetadataType.ISSUE_NUMBER),
getMetaString(ResultMetadataType.ORIENTATION),
getMetaString(ResultMetadataType.OTHER),
getMetaString(ResultMetadataType.PDF417_EXTRA_METADATA),
getMetaString(ResultMetadataType.POSSIBLE_COUNTRY),
getMetaString(ResultMetadataType.SUGGESTED_PRICE),
getMetaString(ResultMetadataType.UPC_EAN_EXTENSION)
format,
ecLevel,
versionNumber,
sequenceSize,
sequenceIndex,
sequenceId,
gtin?.country,
gtin?.addOn,
gtin?.price,
gtin?.issueNumber
)
}
private fun Result.getRawData(): ByteArray? {
val metadata = resultMetadata ?: return null
val segments = metadata[ResultMetadataType.BYTE_SEGMENTS] ?: return null
var bytes = ByteArray(0)
@Suppress("UNCHECKED_CAST")
for (seg in segments as Iterable<ByteArray>) {
bytes += seg
}
// If the byte segments are shorter than the converted string, the
// content of the QR Code has been encoded with different encoding
// modes (e.g. some parts in alphanumeric, some in byte encoding).
// This is because Zxing only records byte segments for byte encoded
// parts. Please note the byte segments can actually be longer than
// the string because Zxing cuts off prefixes like "WIFI:".
return if (bytes.size >= text.length) bytes else null
}
private fun Result.getMetaString(
key: ResultMetadataType
): String? = this.resultMetadata?.let {
it[key]?.toString()
}
@@ -9,8 +9,6 @@ import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.*
import android.widget.EditText
import com.google.zxing.BarcodeFormat
import com.google.zxing.EncodeHintType
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.app.hasWritePermission
import de.markusfisch.android.binaryeye.content.copyToClipboard
@@ -23,9 +21,8 @@ import de.markusfisch.android.binaryeye.view.doOnApplyWindowInsets
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
import de.markusfisch.android.binaryeye.widget.ConfinedScalingImageView
import de.markusfisch.android.binaryeye.widget.toast
import de.markusfisch.android.binaryeye.zxing.encodeAsBitmap
import de.markusfisch.android.binaryeye.zxing.encodeAsSvg
import de.markusfisch.android.binaryeye.zxing.encodeAsText
import de.markusfisch.android.zxingcpp.ZxingCpp
import de.markusfisch.android.zxingcpp.ZxingCpp.Format
import kotlinx.coroutines.*
import java.io.File
import java.io.FileOutputStream
@@ -41,11 +38,7 @@ class BarcodeFragment : Fragment() {
private val parentJob = Job()
private val scope = CoroutineScope(Dispatchers.IO + parentJob)
private var barcodeBitmap: Bitmap? = null
private var barcodeSvg: String? = null
private var barcodeTxt: String? = null
private var content: String = ""
private var format: BarcodeFormat? = null
private lateinit var barcode: Barcode
override fun onCreate(state: Bundle?) {
super.onCreate(state)
@@ -66,15 +59,13 @@ class BarcodeFragment : Fragment() {
false
)
val args = arguments ?: return view
val content = args.getString(CONTENT) ?: return view
val format = args.getSerializable(FORMAT) as BarcodeFormat? ?: return view
val hints = args.getSerializable(HINTS) as EnumMap<EncodeHintType, Any>?
val size = args.getInt(SIZE)
val bitmap: Bitmap
try {
barcodeBitmap = encodeAsBitmap(content, format, size, size, hints)
barcodeSvg = encodeAsSvg(content, format, hints)
barcodeTxt = encodeAsText(content, format, hints)
barcode = arguments?.toProperties() ?: throw IllegalArgumentException(
"Illegal arguments"
)
// Catch exceptions from encoding.
bitmap = barcode.bitmap
} catch (e: Exception) {
var message = e.message
if (message == null || message.isEmpty()) {
@@ -86,13 +77,11 @@ class BarcodeFragment : Fragment() {
fragmentManager.popBackStack()
return null
}
this.content = content
this.format = format
val imageView = view.findViewById<ConfinedScalingImageView>(
R.id.barcode
)
imageView.setImageBitmap(barcodeBitmap)
imageView.setImageBitmap(bitmap)
imageView.post {
// Make sure to invoke this after ScalingImageView.onLayout().
imageView.minWidth /= 2f
@@ -114,6 +103,19 @@ class BarcodeFragment : Fragment() {
return view
}
private fun Bundle.toProperties() = Barcode(
getString(CONTENT) ?: throw IllegalArgumentException(
"content cannot be null"
),
Format.valueOf(
getString(FORMAT) ?: throw IllegalArgumentException(
"format cannot be null"
)
),
getInt(SIZE),
getInt(EC_LEVEL)
)
override fun onDestroyView() {
super.onDestroyView()
parentJob.cancel()
@@ -126,9 +128,9 @@ class BarcodeFragment : Fragment() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.copy_to_clipboard -> {
barcodeTxt?.let {
context.copyToClipboard(it)
context.toast(R.string.copied_to_clipboard)
context.apply {
copyToClipboard(barcode.text)
toast(R.string.copied_to_clipboard)
}
true
}
@@ -167,7 +169,9 @@ class BarcodeFragment : Fragment() {
}
val view = ac.layoutInflater.inflate(R.layout.dialog_save_file, null)
val editText = view.findViewById<EditText>(R.id.file_name)
editText.setText(encodeFileName("${format.toString()}_$content"))
editText.setText(
encodeFileName("${barcode.format}_${barcode.content}")
)
AlertDialog.Builder(ac)
.setView(view)
.setPositiveButton(android.R.string.ok) { _, _ ->
@@ -177,23 +181,19 @@ class BarcodeFragment : Fragment() {
addSuffixIfNotGiven(fileName, ".png"),
MIME_PNG
) {
barcodeBitmap?.saveAsPng(it)
barcode.bitmap.saveAsPng(it)
}
FileType.SVG -> saveAs(
addSuffixIfNotGiven(fileName, ".svg"),
MIME_SVG
) { outputStream ->
barcodeSvg?.let {
outputStream.write(it.toByteArray())
}
outputStream.write(barcode.svg.toByteArray())
}
FileType.TXT -> saveAs(
addSuffixIfNotGiven(fileName, ".txt"),
MIME_TXT
) { outputStream ->
barcodeTxt?.let {
outputStream.write(it.toByteArray())
}
outputStream.write(barcode.text.toByteArray())
}
}
}
@@ -218,9 +218,9 @@ class BarcodeFragment : Fragment() {
private fun Context.shareAs(fileType: FileType) {
when (fileType) {
FileType.PNG -> barcodeBitmap?.let { share(it) }
FileType.SVG -> barcodeSvg?.let { shareText(it, MIME_SVG) }
FileType.TXT -> barcodeTxt?.let { shareText(it) }
FileType.PNG -> share(barcode.bitmap)
FileType.SVG -> shareText(barcode.svg, MIME_SVG)
FileType.TXT -> shareText(barcode.text)
}
}
@@ -251,25 +251,23 @@ class BarcodeFragment : Fragment() {
companion object {
private const val CONTENT = "content"
private const val FORMAT = "format"
private const val HINTS = "hints"
private const val SIZE = "size"
private const val EC_LEVEL = "ec_level"
private const val MIME_PNG = "image/png"
private const val MIME_SVG = "image/svg+xmg"
private const val MIME_TXT = "text/plain"
fun newInstance(
content: String,
format: BarcodeFormat,
format: Format,
size: Int,
hints: EnumMap<EncodeHintType, Any>? = null
ecLevel: Int = -1
): Fragment {
val args = Bundle()
args.putString(CONTENT, content)
args.putSerializable(FORMAT, format)
hints?.let {
args.putSerializable(HINTS, it)
}
args.putString(FORMAT, format.name)
args.putInt(SIZE, size)
args.putInt(EC_LEVEL, ecLevel)
val fragment = BarcodeFragment()
fragment.arguments = args
return fragment
@@ -277,6 +275,26 @@ class BarcodeFragment : Fragment() {
}
}
private data class Barcode(
val content: String,
val format: Format,
val size: Int,
val ecLevel: Int
) {
val bitmap: Bitmap
get() = ZxingCpp.encodeAsBitmap(
content, format, size, size, -1, ecLevel
)
val svg: String
get() = ZxingCpp.encodeAsSvg(
content, format, -1, ecLevel
)
val text: String
get() = ZxingCpp.encodeAsText(
content, format, -1, ecLevel
)
}
private fun Bitmap.saveAsPng(outputStream: OutputStream, quality: Int = 90) =
compress(Bitmap.CompressFormat.PNG, quality, outputStream)
@@ -5,7 +5,9 @@ import android.os.Bundle
import android.support.design.widget.FloatingActionButton
import android.support.v4.app.Fragment
import android.text.Editable
import android.text.Html
import android.text.TextWatcher
import android.text.method.LinkMovementMethod
import android.view.*
import android.widget.EditText
import android.widget.TableLayout
@@ -38,6 +40,7 @@ class DecodeFragment : Fragment() {
private lateinit var metaView: TableLayout
private lateinit var hexView: TextView
private lateinit var format: String
private lateinit var stampView: TextView
private lateinit var fab: FloatingActionButton
private val parentJob = Job()
@@ -82,6 +85,7 @@ class DecodeFragment : Fragment() {
format = scan.format
contentView = view.findViewById(R.id.content)
stampView = view.findViewById(R.id.stamp)
fab = view.findViewById(R.id.open)
if (!isBinary) {
@@ -122,6 +126,17 @@ class DecodeFragment : Fragment() {
}
}
val trackingLink = generateDpTrackingLink(raw, scan.format)
if (trackingLink != null) {
stampView.apply {
text = Html.fromHtml(trackingLink)
isClickable = true
movementMethod = LinkMovementMethod.getInstance()
}
} else {
stampView.visibility = View.GONE
}
formatView = view.findViewById(R.id.format)
dataView = view.findViewById(R.id.data)
metaView = view.findViewById(R.id.meta)
@@ -196,24 +211,43 @@ class DecodeFragment : Fragment() {
}
private fun fillMetaView(tableLayout: TableLayout, scan: Scan) {
fillDataTable(
tableLayout, linkedMapOf(
R.string.error_correction_level to scan.errorCorrectionLevel,
R.string.issue_number to scan.issueNumber,
R.string.orientation to scan.orientation,
R.string.other_meta_data to scan.otherMetaData,
R.string.pdf417_extra_metadata to scan.pdf417ExtraMetaData,
R.string.possible_country to scan.possibleCountry,
R.string.suggested_price to scan.suggestedPrice,
R.string.upc_ean_extension to scan.upcEanExtension
)
val items = linkedMapOf(
R.string.error_correction_level to scan.errorCorrectionLevel,
R.string.sequence_size to scan.sequenceSize.positiveToString(),
R.string.sequence_index to scan.sequenceIndex.positiveToString(),
R.string.sequence_id to scan.sequenceId,
R.string.gtin_country to scan.country,
R.string.gtin_add_on to scan.addOn,
R.string.gtin_price to scan.price,
R.string.gtin_issue_number to scan.issueNumber,
)
if (scan.versionNumber > 0) {
val versionString = if (scan.format == "QR_CODE") {
getString(
R.string.qr_version_and_modules,
scan.versionNumber,
scan.versionNumber * 4 + 17
)
} else {
scan.versionNumber.toString()
}
items.putAll(
linkedMapOf(
R.string.barcode_version_number to versionString
)
)
}
fillDataTable(tableLayout, items)
}
private fun fillDataTable(
tableLayout: TableLayout,
items: LinkedHashMap<Int, String?>
) {
if (items.isEmpty()) {
tableLayout.visibility = View.GONE
return
}
val ctx = tableLayout.context
val spaceBetween = (16f * ctx.resources.displayMetrics.density).toInt()
items.forEach { item ->
@@ -230,9 +264,6 @@ class DecodeFragment : Fragment() {
tableLayout.addView(tr)
}
}
if (tableLayout.childCount > 0) {
tableLayout.setPadding(0, 0, 0, spaceBetween)
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
@@ -294,16 +325,16 @@ class DecodeFragment : Fragment() {
if (ac is WifiAction) {
ac.password?.let { password ->
activity?.apply {
copyToClipboard(password)
copyToClipboard(password, isSensitive = true)
toast(R.string.copied_password_to_clipboard)
}
}
}
}
private fun copyToClipboard(text: String) {
private fun copyToClipboard(text: String, isSensitive: Boolean = false) {
activity?.apply {
copyToClipboard(text)
copyToClipboard(text, isSensitive)
toast(R.string.copied_to_clipboard)
}
}
@@ -372,6 +403,8 @@ class DecodeFragment : Fragment() {
}
}
private fun Int.positiveToString() = if (this > -1) this.toString() else ""
private fun hexDump(bytes: ByteArray, charsPerLine: Int = 33): String {
if (charsPerLine < 4 || bytes.isEmpty()) {
return ""
@@ -410,3 +443,70 @@ private fun hexDump(bytes: ByteArray, charsPerLine: Int = 33): String {
}
return dump.toString()
}
private fun generateDpTrackingLink(raw: ByteArray, format: String): String? {
// Check for Deutsche Post Matrixcode stamp.
var isStamp = false
var rawData = raw
if (format == "DATA_MATRIX" &&
raw.toString(Charsets.ISO_8859_1).startsWith("DEA5")
) {
if (raw.size == 47) {
isStamp = true
} else if (raw.size > 47) {
// Transform back to original data.
rawData = raw.toString(Charsets.UTF_8).toByteArray(
Charsets.ISO_8859_1
)
if (rawData.size == 47) {
isStamp = true
}
}
}
if (!isStamp) {
return null
}
val hex = StringBuilder()
hex.append(String.format("%02X", rawData[9]))
hex.append(String.format("%02X", rawData[10]))
hex.append(String.format("%02X", rawData[11]))
hex.append(String.format("%02X", rawData[12]))
hex.append(String.format("%02X", rawData[13]))
hex.append(String.format("%X", (rawData[4].toInt() and 0x0f).toByte()))
hex.append(String.format("%02X", rawData[5]))
hex.append(String.format("%02X", rawData[6]))
hex.append(String.format("%02X", rawData[7]))
hex.append(String.format("%02X", rawData[8]))
val hexString = hex.toString()
val trackingNumber = hexString + String.format(
"%X",
crc4(hexString.toByteArray(Charsets.ISO_8859_1))
)
return "<a href=\"https://www.deutschepost.de/de/s/sendungsverfolgung/verfolgen.html?piececode=$trackingNumber\">Deutsche Post: $trackingNumber</a>"
}
// CRC-4 with polynomial x^4 + x + 1.
private fun crc4(input: ByteArray): Int {
var crc = 0
var i = 0
while (i < input.size) {
val c = input[i].toInt()
var j = 0x80
while (j != 0) {
var bit = crc and 0x8
crc = crc shl 1
if (c and j != 0) {
bit = bit xor 0x8
}
if (bit != 0) {
crc = crc xor 0x3
}
j = j ushr 1
}
++i
}
crc = crc and 0xF
return crc
}
@@ -6,16 +6,13 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import com.google.zxing.BarcodeFormat
import com.google.zxing.EncodeHintType
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
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.view.hideSoftKeyboard
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
import java.util.*
import de.markusfisch.android.zxingcpp.ZxingCpp.Format
class EncodeFragment : Fragment() {
private lateinit var formatView: Spinner
@@ -26,17 +23,17 @@ class EncodeFragment : Fragment() {
private lateinit var contentView: EditText
private val writers = arrayListOf(
BarcodeFormat.AZTEC,
BarcodeFormat.CODABAR,
BarcodeFormat.CODE_39,
BarcodeFormat.CODE_128,
BarcodeFormat.DATA_MATRIX,
BarcodeFormat.EAN_8,
BarcodeFormat.EAN_13,
BarcodeFormat.ITF,
BarcodeFormat.PDF_417,
BarcodeFormat.QR_CODE,
BarcodeFormat.UPC_A
Format.AZTEC,
Format.CODABAR,
Format.CODE_39,
Format.CODE_128,
Format.DATA_MATRIX,
Format.EAN_8,
Format.EAN_13,
Format.ITF,
Format.PDF_417,
Format.QR_CODE,
Format.UPC_A
)
override fun onCreateView(
@@ -71,7 +68,7 @@ class EncodeFragment : Fragment() {
id: Long
) {
val visibility = if (
writers[position] == BarcodeFormat.QR_CODE
writers[position] == Format.QR_CODE
) {
View.VISIBLE
} else {
@@ -94,14 +91,14 @@ class EncodeFragment : Fragment() {
contentView = view.findViewById(R.id.content)
val args = arguments
args?.also {
contentView.setText(it.getString(CONTENT))
args?.getString(CONTENT)?.let {
contentView.setText(it)
}
val barcodeFormat = args?.getString(FORMAT)
if (barcodeFormat != null) {
formatView.setSelection(
writers.indexOf(resolveFormat(barcodeFormat))
writers.indexOf(barcodeFormat.toFormat())
)
} else if (state == null) {
formatView.post {
@@ -125,17 +122,6 @@ class EncodeFragment : Fragment() {
}
private fun encode() {
val hints = EnumMap<EncodeHintType, Any>(EncodeHintType::class.java)
val format = writers[formatView.selectedItemPosition]
if (format == BarcodeFormat.QR_CODE) {
hints[EncodeHintType.ERROR_CORRECTION] = arrayListOf(
ErrorCorrectionLevel.L,
ErrorCorrectionLevel.M,
ErrorCorrectionLevel.Q,
ErrorCorrectionLevel.H
)[errorCorrectionLevel.selectedItemPosition]
}
val size = getSize(sizeBarView.progress)
val content = contentView.text.toString()
if (content.isEmpty()) {
Toast.makeText(
@@ -146,7 +132,12 @@ class EncodeFragment : Fragment() {
} else {
activity?.hideSoftKeyboard(contentView)
fragmentManager?.addFragment(
BarcodeFragment.newInstance(content, format, size, hints)
BarcodeFragment.newInstance(
content,
writers[formatView.selectedItemPosition],
getSize(sizeBarView.progress),
errorCorrectionLevel.selectedItemPosition
)
)
}
}
@@ -181,11 +172,11 @@ class EncodeFragment : Fragment() {
private const val FORMAT = "format"
fun newInstance(
content: String,
content: String? = null,
format: String? = null
): Fragment {
val args = Bundle()
args.putString(CONTENT, content)
content?.let { args.putString(CONTENT, content) }
format?.let { args.putString(FORMAT, it) }
val fragment = EncodeFragment()
fragment.arguments = args
@@ -194,8 +185,8 @@ class EncodeFragment : Fragment() {
}
}
private fun resolveFormat(name: String): BarcodeFormat = try {
BarcodeFormat.valueOf(name)
private fun String.toFormat(default: Format = Format.QR_CODE): Format = try {
Format.valueOf(this)
} catch (_: IllegalArgumentException) {
BarcodeFormat.QR_CODE
default
}
@@ -24,13 +24,14 @@ import de.markusfisch.android.binaryeye.app.*
import de.markusfisch.android.binaryeye.content.copyToClipboard
import de.markusfisch.android.binaryeye.content.shareText
import de.markusfisch.android.binaryeye.database.Database
import de.markusfisch.android.binaryeye.database.getString
import de.markusfisch.android.binaryeye.database.exportCsv
import de.markusfisch.android.binaryeye.database.exportDatabase
import de.markusfisch.android.binaryeye.database.exportJson
import de.markusfisch.android.binaryeye.database.use
import de.markusfisch.android.binaryeye.io.askForFileName
import de.markusfisch.android.binaryeye.io.toSaveResult
import de.markusfisch.android.binaryeye.view.*
import de.markusfisch.android.binaryeye.widget.ellipsize
import de.markusfisch.android.binaryeye.widget.toast
import kotlinx.coroutines.*
@@ -76,7 +77,7 @@ class HistoryFragment : Fragment() {
val ac = activity ?: return false
return when (item.itemId) {
R.id.copy_scan -> {
scansAdapter?.getSelectedContent()?.let {
scansAdapter?.getSelectedContent("\n")?.let {
ac.copyToClipboard(it)
ac.toast(R.string.copied_to_clipboard)
}
@@ -84,19 +85,21 @@ class HistoryFragment : Fragment() {
true
}
R.id.edit_scan -> {
scansAdapter?.let {
askForName(
ac,
it.selectedScanId,
getScanName(it.selectedScanPosition)
scansAdapter?.forSelection { id, position ->
ac.askForName(
id,
scansAdapter?.getName(position),
scansAdapter?.getContent(position)
)
}
closeActionMode()
true
}
R.id.remove_scan -> {
scansAdapter?.let {
askToRemoveScan(ac, it.selectedScanId)
scansAdapter?.getSelectedIds()?.let {
if (it.isNotEmpty()) {
ac.askToRemoveScans(it)
}
}
closeActionMode()
true
@@ -146,8 +149,7 @@ class HistoryFragment : Fragment() {
showScan(id)
}
listView.setOnItemLongClickListener { _, v, position, id ->
v.isSelected = true
scansAdapter?.select(id, position)
scansAdapter?.select(v, id, position)
if (actionMode == null && ac is AppCompatActivity) {
actionMode = ac.delegate.startSupportActionMode(
actionModeCallback
@@ -159,7 +161,7 @@ class HistoryFragment : Fragment() {
fab = view.findViewById(R.id.share)
fab.setOnClickListener { v ->
pickListSeparatorAndShare(v.context)
v.context.pickListSeparatorAndShare()
}
progressView = view.findViewById(R.id.progress_view)
@@ -221,7 +223,7 @@ class HistoryFragment : Fragment() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.clear -> {
askToRemoveScans(context)
context.askToRemoveScans()
true
}
R.id.export_history -> {
@@ -300,20 +302,22 @@ class HistoryFragment : Fragment() {
}
}
private fun getScanName(position: Int): String? {
val cursor = scansAdapter?.getItem(position) as Cursor?
return cursor?.getString(Database.SCANS_NAME)
}
// Dialogs don't have a parent layout.
@SuppressLint("InflateParams")
private fun askForName(context: Context, id: Long, text: String?) {
val view = LayoutInflater.from(context).inflate(
private fun Context.askForName(id: Long, text: String?, content: String?) {
val view = LayoutInflater.from(this).inflate(
R.layout.dialog_enter_name, null
)
val nameView = view.findViewById<EditText>(R.id.name)
nameView.setText(text)
AlertDialog.Builder(context)
AlertDialog.Builder(this)
.setTitle(
if (content.isNullOrEmpty()) {
getString(R.string.binary_data)
} else {
content.ellipsize(64)
}
)
.setView(view)
.setPositiveButton(android.R.string.ok) { _, _ ->
val name = nameView.text.toString()
@@ -324,11 +328,17 @@ class HistoryFragment : Fragment() {
.show()
}
private fun askToRemoveScan(context: Context, id: Long) {
AlertDialog.Builder(context)
.setMessage(R.string.really_remove_scan)
private fun Context.askToRemoveScans(ids: List<Long>) {
AlertDialog.Builder(this)
.setMessage(
if (ids.size > 1) {
R.string.really_remove_selected_scans
} else {
R.string.really_remove_scan
}
)
.setPositiveButton(android.R.string.ok) { _, _ ->
db.removeScan(id)
ids.forEach { db.removeScan(it) }
if (scansAdapter?.count == 1) {
updateAndClearFilter()
} else {
@@ -340,8 +350,8 @@ class HistoryFragment : Fragment() {
.show()
}
private fun askToRemoveScans(context: Context) {
AlertDialog.Builder(context)
private fun Context.askToRemoveScans() {
AlertDialog.Builder(this)
.setMessage(
if (filter == null) {
R.string.really_remove_all_scans
@@ -402,11 +412,11 @@ class HistoryFragment : Fragment() {
}
}
private fun pickListSeparatorAndShare(context: Context) {
val separators = context.resources.getStringArray(
private fun Context.pickListSeparatorAndShare() {
val separators = resources.getStringArray(
R.array.list_separators_values
)
AlertDialog.Builder(context)
AlertDialog.Builder(this)
.setTitle(R.string.pick_list_separator)
.setItems(R.array.list_separators_names) { _, which ->
shareScans(separators[which])
@@ -52,28 +52,28 @@ class PreferencesFragment : PreferenceFragmentCompat() {
pref.isVisible = false
} else {
pref.setOnPreferenceClickListener {
askToClearNetworkSuggestions(context)
context.askToClearNetworkSuggestions()
true
}
}
}
@RequiresApi(Build.VERSION_CODES.Q)
private fun askToClearNetworkSuggestions(context: Context) {
AlertDialog.Builder(context)
private fun Context.askToClearNetworkSuggestions() {
AlertDialog.Builder(this)
.setMessage(R.string.really_remove_all_networks)
.setPositiveButton(android.R.string.ok) { _, _ ->
clearNetworkSuggestions(context)
clearNetworkSuggestions()
}
.setNegativeButton(android.R.string.cancel) { _, _ -> }
.show()
}
@RequiresApi(Build.VERSION_CODES.Q)
private fun clearNetworkSuggestions(context: Context) {
context.toast(
private fun Context.clearNetworkSuggestions() {
toast(
if (
removeNetworkSuggestions(context) == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS
removeNetworkSuggestions(this) == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS
) {
R.string.clear_network_suggestions_success
} else {
@@ -50,15 +50,7 @@ class UrlDialogFragment : PreferenceDialogFragmentCompat() {
Scan(
"test",
null,
"none",
null,
null,
null,
null,
null,
null,
null,
null
"none"
).sendAsync(url, prefs.sendScanType) { code, body ->
textView.text = when {
code != null -> "$code"
@@ -49,8 +49,13 @@ private fun calculateInSampleSize(
return inSampleSize
}
fun Bitmap.crop(rect: RectF, rotation: Float) = try {
val erected = erect(rotation)
fun Bitmap.crop(
rect: RectF,
rotation: Float,
pivotX: Float,
pivotY: Float
) = try {
val erected = erect(rotation, pivotX, pivotY)
val w = erected.width
val h = erected.height
val x = max(0, (rect.left * w).roundToInt())
@@ -68,7 +73,11 @@ fun Bitmap.crop(rect: RectF, rotation: Float) = try {
null
}
private fun Bitmap.erect(rotation: Float): Bitmap = if (
private fun Bitmap.erect(
rotation: Float,
pivotX: Float,
pivotY: Float
): Bitmap = if (
rotation % 360f != 0f
) {
Bitmap.createBitmap(
@@ -78,7 +87,7 @@ private fun Bitmap.erect(rotation: Float): Bitmap = if (
width,
height,
Matrix().apply {
setRotate(rotation)
setRotate(rotation, pivotX, pivotY)
},
true
)
@@ -2,61 +2,86 @@ package de.markusfisch.android.binaryeye.graphics
import android.graphics.Matrix
import android.graphics.Rect
import com.google.zxing.ResultPoint
import android.graphics.RectF
import de.markusfisch.android.zxingcpp.ZxingCpp.Position
import kotlin.math.roundToInt
fun mapResultToView(
frameWidth: Int,
frameHeight: Int,
frameOrientation: Int,
viewRect: Rect,
resultPoints: Array<ResultPoint?>,
targetArray: FloatArray
): Int = getFrameToViewMatrix(
frameWidth,
frameHeight,
frameOrientation,
viewRect
).map(
resultPoints,
targetArray
data class FrameMetrics(
var width: Int = 0,
var height: Int = 0,
var orientation: Int = 0
)
fun getFrameToViewMatrix(
frameWidth: Int,
frameHeight: Int,
frameOrientation: Int,
viewRect: Rect
) = Matrix().apply {
// Normalize to frame dimensions for rotation.
postScale(
1f / frameWidth,
1f / frameHeight
)
// Rotate around center.
postRotate(frameOrientation.toFloat(), 0.5f, 0.5f)
// Scale up to view size.
postScale(viewRect.width().toFloat(), viewRect.height().toFloat())
// Apply view displacement.
postTranslate(viewRect.left.toFloat(), viewRect.top.toFloat())
fun Rect.setFrameRoi(
frameMetrics: FrameMetrics,
viewRect: Rect,
viewRoi: Rect
) {
Matrix().apply {
// Map ROI from view coordinates to frame coordinates.
setScale(1f / viewRect.width(), 1f / viewRect.height())
postRotate(-frameMetrics.orientation.toFloat(), .5f, .5f)
postScale(frameMetrics.width.toFloat(), frameMetrics.height.toFloat())
postTranslate(-viewRect.left.toFloat(), -viewRect.top.toFloat())
val frameRoiF = RectF()
val viewRoiF = RectF(
viewRoi.left.toFloat(),
viewRoi.top.toFloat(),
viewRoi.right.toFloat(),
viewRoi.bottom.toFloat()
)
mapRect(frameRoiF, viewRoiF)
set(
frameRoiF.left.roundToInt(),
frameRoiF.top.roundToInt(),
frameRoiF.right.roundToInt(),
frameRoiF.bottom.roundToInt()
)
}
}
fun Matrix.map(
resultPoints: Array<ResultPoint?>,
targetArray: FloatArray
): Int {
val max = targetArray.size
var i = 0
for (resultPoint in resultPoints) {
// Because ZXing apparently returns null in this array sometimes.
if (resultPoint == null) {
continue
fun Matrix.setFrameToView(
frameMetrics: FrameMetrics,
viewRect: Rect,
viewRoi: Rect? = null
) {
// Configure this matrix to map points in frame coordinates to
// view coordinates.
val uprightWidth: Int
val uprightHeight: Int
when (frameMetrics.orientation) {
90, 270 -> {
uprightWidth = frameMetrics.height
uprightHeight = frameMetrics.width
}
targetArray[i++] = resultPoint.x
targetArray[i++] = resultPoint.y
if (i >= max) {
break
else -> {
uprightWidth = frameMetrics.width
uprightHeight = frameMetrics.height
}
}
mapPoints(targetArray, 0, targetArray, 0, i)
setScale(
viewRect.width().toFloat() / uprightWidth,
viewRect.height().toFloat() / uprightHeight
)
viewRoi?.let {
postTranslate(viewRoi.left.toFloat(), viewRoi.top.toFloat())
}
}
fun Matrix.mapPosition(
position: Position,
coords: FloatArray
): Int {
var i = 0
setOf(
position.topLeft,
position.topRight,
position.bottomRight,
position.bottomLeft
).forEach {
coords[i++] = it.x.toFloat()
coords[i++] = it.y.toFloat()
}
mapPoints(coords)
return i
}
@@ -0,0 +1,57 @@
package de.markusfisch.android.binaryeye.graphics
import android.graphics.Bitmap
import android.graphics.Canvas
fun Bitmap.fixTransparency(): Bitmap {
val result = analyzeTransparency()
return if (result.hasTransparentPixels) {
val copy = Bitmap.createBitmap(
width,
height,
config
)
Canvas(copy).apply {
drawColor(result.backgroundColor)
drawBitmap(this@fixTransparency, 0f, 0f, null)
}
copy
} else {
this
}
}
private fun Bitmap.analyzeTransparency(): Result {
val pixels = IntArray(width * height)
getPixels(pixels, 0, width, 0, 0, width, height)
var transparent = 0
var visible = 0
var bright = 0
for (pixel in pixels) {
val alpha = (pixel shr 24) and 0xff
if (alpha < 0xff) {
++transparent
}
if (alpha > 0) {
++visible
if (
((pixel shr 16) and 0xff) +
((pixel shr 8) and 0xff) +
(pixel and 0xff) > 128
) ++bright
}
}
return Result(
transparent > 0,
if (bright > 0 && visible / bright < 2) {
0xff000000.toInt()
} else {
0xffffffff.toInt()
}
)
}
private data class Result(
val hasTransparentPixels: Boolean,
val backgroundColor: Int
)
@@ -69,13 +69,14 @@ private fun Scan.getMap(): Map<String, String> = mapOf(
"raw" to raw?.toHexString(),
"format" to format,
"errorCorrectionLevel" to errorCorrectionLevel,
"versionNumber" to versionNumber.toString(),
"sequenceSize" to sequenceSize.toString(),
"sequenceIndex" to sequenceIndex.toString(),
"sequenceId" to sequenceId,
"country" to country,
"addOn" to addOn,
"price" to price,
"issueNumber" to issueNumber,
"orientation" to orientation,
"otherMetaData" to otherMetaData,
"pdf417ExtraMetaData" to pdf417ExtraMetaData,
"possibleCountry" to possibleCountry,
"suggestedPrice" to suggestedPrice,
"upcEanExtension" to upcEanExtension,
"timestamp" to dateTime
).filterNullValues()
@@ -5,28 +5,29 @@ import android.content.SharedPreferences
import android.os.Build
import android.preference.PreferenceManager
import android.support.annotation.RequiresApi
import de.markusfisch.android.zxingcpp.ZxingCpp.Format
class Preferences {
lateinit var preferences: SharedPreferences
var barcodeFormats = setOf(
"AZTEC",
"CODABAR",
"CODE_39",
"CODE_93",
"CODE_128",
"DATA_MATRIX",
"EAN_8",
"EAN_13",
"ITF",
"MAXICODE",
"PDF_417",
"QR_CODE",
"RSS_14",
"RSS_EXPANDED",
"UPC_A",
"UPC_E",
"UPC_EAN_EXTENSION"
Format.AZTEC.name,
Format.CODABAR.name,
Format.CODE_39.name,
Format.CODE_93.name,
Format.CODE_128.name,
Format.DATA_BAR.name,
Format.DATA_BAR_EXPANDED.name,
Format.DATA_MATRIX.name,
Format.EAN_8.name,
Format.EAN_13.name,
Format.ITF.name,
Format.MAXICODE.name,
Format.PDF_417.name,
Format.QR_CODE.name,
Format.MICRO_QR_CODE.name,
Format.UPC_A.name,
Format.UPC_E.name,
)
@RequiresApi(Build.VERSION_CODES.HONEYCOMB)
set(value) {
@@ -287,7 +288,7 @@ class Preferences {
}
companion object {
private const val BARCODE_FORMATS = "barcode_formats"
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"
@@ -1,42 +0,0 @@
package de.markusfisch.android.binaryeye.rs
import android.graphics.Bitmap
import android.graphics.Canvas
import android.support.v8.renderscript.Allocation
import android.support.v8.renderscript.RenderScript
fun fixTransparency(rs: RenderScript, bitmap: Bitmap?): Bitmap? {
bitmap ?: return null
val result = analyze(rs, bitmap)
return if (result.hasTransparentPixels) {
val copy = Bitmap.createBitmap(
bitmap.width,
bitmap.height,
bitmap.config
)
Canvas(copy).apply {
drawColor(result.backgroundColor)
drawBitmap(bitmap, 0f, 0f, null)
}
copy
} else {
bitmap
}
}
private fun analyze(rs: RenderScript, bitmap: Bitmap): Result {
val analyzeScript = ScriptC_analyze(rs)
val bitmapAllocation = Allocation.createFromBitmap(rs, bitmap)
val result = analyzeScript.reduce_analyze(bitmapAllocation).get()
bitmapAllocation.destroy()
analyzeScript.destroy()
return Result(
result.x > 0,
if (result.y > 0) 0xff000000.toInt() else 0xffffffff.toInt()
)
}
private data class Result(
val hasTransparentPixels: Boolean,
val backgroundColor: Int
)
@@ -1,156 +0,0 @@
package de.markusfisch.android.binaryeye.rs
import android.graphics.Rect
import android.support.v8.renderscript.*
import kotlin.math.max
import kotlin.math.roundToInt
private const val SCALE_FACTOR = .75f
class Preprocessor(
rs: RenderScript,
width: Int,
height: Int,
private val roi: Rect?
) {
var outWidth = 0
var outHeight = 0
private val resizeScript = ScriptIntrinsicResize.create(rs)
private val rotateScript = ScriptC_rotate(rs)
private var yuvType: Type? = null
private var yuvAlloc: Allocation? = null
private var roiType: Type? = null
private var roiAlloc: Allocation? = null
private var resizedType: Type? = null
private var resizedAlloc: Allocation? = null
private var rotatedType: Type? = null
private var rotatedAlloc: Allocation? = null
init {
yuvType = Type.createXY(
rs,
Element.U8(rs),
width,
height // use only grayscale part
)
yuvAlloc = Allocation.createTyped(
rs,
yuvType,
Allocation.USAGE_SCRIPT
)
if (roi != null) {
val roiWidth = roi.width()
val roiHeight = roi.height()
roiType = Type.createXY(
rs,
Element.U8(rs),
roiWidth,
roiHeight
)
roiAlloc = Allocation.createTyped(
rs,
roiType,
Allocation.USAGE_SCRIPT
)
outWidth = (roiWidth * SCALE_FACTOR).roundToInt()
outHeight = (roiHeight * SCALE_FACTOR).roundToInt()
// Make sure the dimensions are always a multiple of 4.
outWidth -= outWidth % 4
outHeight -= outHeight % 4
// Make sure the dimensions are always greater than 4.
outWidth = max(4, outWidth)
outHeight = max(4, outHeight)
} else {
outWidth = (width * SCALE_FACTOR).roundToInt()
outHeight = (height * SCALE_FACTOR).roundToInt()
}
resizedType = Type.createXY(
rs,
Element.U8(rs),
outWidth,
outHeight
)
resizedAlloc = Allocation.createTyped(
rs,
resizedType,
Allocation.USAGE_SCRIPT
)
rotatedType = Type.createXY(
rs,
Element.U8(rs),
outHeight,
outWidth
)
rotatedAlloc = Allocation.createTyped(
rs,
rotatedType,
Allocation.USAGE_SCRIPT
)
}
fun destroy() {
yuvType?.destroy()
yuvType = null
yuvAlloc?.destroy()
yuvAlloc = null
roiType?.destroy()
roiType = null
roiAlloc?.destroy()
roiAlloc = null
resizedType?.destroy()
resizedType = null
resizedAlloc?.destroy()
resizedAlloc = null
rotatedType?.destroy()
rotatedType = null
rotatedAlloc?.destroy()
rotatedAlloc = null
resizeScript.destroy()
rotateScript.destroy()
}
fun resizeOnly(frame: ByteArray) {
resize(frame)
resizedAlloc?.copyTo(frame)
}
fun resizeAndRotate(frame: ByteArray) {
resize(frame)
val t = resizedType ?: return
rotateScript._inImage = resizedAlloc
rotateScript._inWidth = t.x
rotateScript._inHeight = t.y
rotateScript.forEach_rotate90(
rotatedAlloc, // Ignored in kernel, just to satisfy forEach.
rotatedAlloc
)
rotatedAlloc?.copyTo(frame)
}
private fun resize(frame: ByteArray) {
yuvAlloc?.copyFrom(frame)
resizeScript.setInput(
if (roi != null) {
roiAlloc?.copy2DRangeFrom(
0, 0,
roi.width(), roi.height(),
yuvAlloc,
max(0, roi.left), max(0, roi.top)
)
roiAlloc
} else {
yuvAlloc
}
)
resizeScript.forEach_bicubic(resizedAlloc)
}
}
@@ -8,11 +8,10 @@ import de.markusfisch.android.binaryeye.activity.CameraActivity
@RequiresApi(Build.VERSION_CODES.N)
class ScanTileService : TileService() {
@RequiresApi(Build.VERSION_CODES.N)
override fun onClick() {
super.onClick()
val intent = Intent(applicationContext, CameraActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivityAndCollapse(intent)
}
}
}
@@ -100,14 +100,15 @@ class DetectorView : View {
}
private fun setCropHandlePos(x: Int, y: Int, orientation: Int) {
// Always set handlePos even if it's invalid because
// handlePos.x may be -2 which is a signal to set the
// default ROI.
if (orientation == currentOrientation) {
handlePos.set(x, y)
} else {
handlePos.set(y, x)
}
if (x > -1) {
handleActive = true
}
handleActive = handlePos.x > -1
}
fun update(numberOfCoordinates: Int) {
@@ -164,8 +165,8 @@ class DetectorView : View {
MotionEvent.ACTION_MOVE -> {
if (handleGrabbed) {
handlePos.set(x, y)
handleActive =
handleActive or (distSq(handlePos, touchDown) > minMoveThresholdSq)
handleActive = handleActive ||
distSq(handlePos, touchDown) > minMoveThresholdSq
if (handleActive) {
updateClipRect()
invalidate()
@@ -216,10 +217,10 @@ class DetectorView : View {
val cy = clampY(y)
val dx = abs(cx - center.x)
val dy = abs(cy - center.y)
// check if handle is close to the vertical or horizontal center line
// Check if handle is close to the vertical or horizontal center line.
if (dx < distToFull ||
dy < distToFull ||
// check if handle is close to a screen corner
// Check if handle is close to a screen corner.
((abs(cy - minY) < distToFull || abs(maxY - cy) < distToFull) &&
abs(dx - center.x) < distToFull)
) {
@@ -260,23 +261,18 @@ class DetectorView : View {
override fun onDraw(canvas: Canvas) {
if (handleActive) {
drawClip(canvas)
canvas.drawClip()
}
drawDots(canvas)
canvas.drawDots()
if (prefs.showCropHandle) {
canvas.drawBitmap(
handleBitmap,
(handlePos.x - handleXRadius).toFloat(),
(handlePos.y - handleYRadius).toFloat(),
null
)
canvas.drawHandle()
}
}
private fun drawDots(canvas: Canvas) {
private fun Canvas.drawDots() {
var i = 0
while (i < coordinatesLast) {
canvas.drawCircle(
drawCircle(
coordinates[i++],
coordinates[i++],
dotRadius,
@@ -285,15 +281,15 @@ class DetectorView : View {
}
}
private fun drawClip(canvas: Canvas) {
private fun Canvas.drawClip() {
if (minDist < 1) {
return
}
// canvas.clipRect() doesn't work reliably below KITKAT.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
val radius = min(minDist / 2, cornerRadius).toFloat()
canvas.save()
canvas.clipOutPathCompat(
save()
clipOutPathCompat(
calculateRoundedRectPath(
roi.left.toFloat(),
roi.top.toFloat(),
@@ -303,13 +299,22 @@ class DetectorView : View {
radius
)
)
canvas.drawColor(shadeColor)
canvas.restore()
drawColor(shadeColor)
restore()
} else {
canvas.drawRect(roi, roiPaint)
drawRect(roi, roiPaint)
}
}
private fun Canvas.drawHandle() {
drawBitmap(
handleBitmap,
(handlePos.x - handleXRadius).toFloat(),
(handlePos.y - handleYRadius).toFloat(),
null
)
}
private fun updateClipRect() {
clampHandlePos()
val dx = abs(handlePos.x - center.x)
@@ -15,7 +15,7 @@ fun Context.toast(message: String) = Toast.makeText(
Toast.LENGTH_LONG
).show()
private fun String.ellipsize(max: Int) = if (length < max) {
fun String.ellipsize(max: Int) = if (length < max) {
this
} else {
"${take(max)}"
@@ -1,201 +0,0 @@
package de.markusfisch.android.binaryeye.zxing
import android.graphics.Bitmap
import com.google.zxing.*
import com.google.zxing.common.BitMatrix
import com.google.zxing.common.HybridBinarizer
import java.util.*
class Zxing(possibleResultPoint: ResultPointCallback? = null) {
private val hints = EnumMap<DecodeHintType, Any>(DecodeHintType::class.java)
private val multiFormatReader = MultiFormatReader()
init {
possibleResultPoint?.let {
hints[DecodeHintType.NEED_RESULT_POINT_CALLBACK] = it
}
}
fun updateHints(tryHarder: Boolean, formats: Set<String>) {
if (tryHarder) {
hints[DecodeHintType.TRY_HARDER] = java.lang.Boolean.TRUE
} else {
hints.remove(DecodeHintType.TRY_HARDER)
}
setDecodeFormats(formats)
}
private fun setDecodeFormats(formats: Set<String>) {
hints[DecodeHintType.POSSIBLE_FORMATS] = EnumSet.noneOf(
BarcodeFormat::class.java
).apply {
addAll(
EnumSet.copyOf(
formats.map { BarcodeFormat.valueOf(it) }
)
)
}
}
fun decode(
yuvData: ByteArray,
width: Int,
height: Int,
invert: Boolean = false
): Result? = decodeLuminanceSource(
PlanarYUVLuminanceSource(
yuvData,
width,
height,
0,
0,
width,
height,
false
),
invert
)
fun decodePositiveNegative(bitmap: Bitmap): Result? =
decode(bitmap, false) ?: decode(bitmap, true)
private fun decode(
bitmap: Bitmap,
invert: Boolean = false
): Result? {
val width = bitmap.width
val height = bitmap.height
val pixels = IntArray(width * height)
if (bitmap.config != Bitmap.Config.ARGB_8888) {
bitmap.copy(Bitmap.Config.ARGB_8888, true)
} else {
bitmap
}.getPixels(pixels, 0, width, 0, 0, width, height)
return decodeLuminanceSource(
RGBLuminanceSource(width, height, pixels),
invert
)
}
private fun decodeLuminanceSource(
source: LuminanceSource,
invert: Boolean
): Result? = decodeLuminanceSource(
if (invert) {
source.invert()
} else {
source
}
)
private fun decodeLuminanceSource(source: LuminanceSource): Result? = try {
multiFormatReader.decode(
BinaryBitmap(HybridBinarizer(source)),
hints
)
} catch (e: Exception) {
// Usually it's bad practice to blindly catch *all* exceptions
// because this can hide problems we want to know about. But
// since ZXing has some errors (like all software) and I don't
// want this app to break in the hands of my users, which won't
// see the stack trace anyway, it's better to just catch all
// exceptions here and live on.
null
} finally {
multiFormatReader.reset()
}
}
private const val BLACK = 0xff000000.toInt()
private const val WHITE = 0xffffffff.toInt()
fun encodeAsBitmap(
text: String,
format: BarcodeFormat,
width: Int,
height: Int,
hints: EnumMap<EncodeHintType, Any>? = null
): Bitmap? {
val bitMatrix = encode(text, format, hints, width, height)
val w = bitMatrix.width
val h = bitMatrix.height
val pixels = IntArray(w * h)
var offset = 0
for (y in 0 until h) {
for (x in 0 until w) {
pixels[offset + x] = if (bitMatrix.get(x, y)) {
BLACK
} else {
WHITE
}
}
offset += w
}
val bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
bitmap.setPixels(pixels, 0, w, 0, 0, w, h)
return bitmap
}
fun encodeAsSvg(
text: String,
format: BarcodeFormat,
hints: EnumMap<EncodeHintType, Any>? = null
): String {
val bitMatrix = encode(text, format, hints)
val sb = StringBuilder()
val w = bitMatrix.width
var h = bitMatrix.height
val moduleHeight = if (h == 1) w / 2 else 1
for (y in 0 until h) {
for (x in 0 until w) {
if (bitMatrix.get(x, y)) {
sb.append(" M${x},${y}h1v${moduleHeight}h-1z")
}
}
}
h *= moduleHeight
return """<svg width="$w" height="$h"
viewBox="0 0 $w $h"
xmlns="http://www.w3.org/2000/svg">
<path d="$sb"/>
</svg>
"""
}
fun encodeAsText(
text: String,
format: BarcodeFormat,
hints: EnumMap<EncodeHintType, Any>? = null
): String {
val bitMatrix = encode(text, format, hints)
val w = bitMatrix.width
val h = bitMatrix.height
val sb = StringBuilder()
for (y in 0 until h) {
for (x in 0 until w) {
sb.append(if (bitMatrix.get(x, y)) "" else " ")
}
sb.append("\n")
}
return sb.toString()
}
private fun encode(
text: String,
format: BarcodeFormat,
encodeHints: EnumMap<EncodeHintType, Any>? = null,
width: Int = 0,
height: Int = 0
): BitMatrix {
val hints = encodeHints ?: EnumMap<EncodeHintType, Any>(EncodeHintType::class.java)
if (!hints.contains(EncodeHintType.CHARACTER_SET)) {
hints[EncodeHintType.CHARACTER_SET] = "utf-8"
}
return MultiFormatWriter().encode(
text,
format,
width,
height,
hints
)
}
@@ -1,3 +1,3 @@
<vector android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M3 11h8V3H3v8zm2-6h4v4H5V5zM3 21h8v-8H3v8zm2-6h4v4H5v-4zm8-12v8h8V3h-8zm6 6h-4V5h4v4zm0 10h2v2h-2zm-6-6h2v2h-2zm2 2h2v2h-2zm-2 2h2v2h-2zm2 2h2v2h-2zm2-2h2v2h-2zm0-4h2v2h-2zm2 2h2v2h-2z"/>
<path android:fillColor="#FF000000" android:pathData="M3 11h8V3H3v8zm2-6h4v4H5V5zM3 21h8v-8H3v8zm2-6h4v4H5v-4zm8-12v8h8V3h-8zm6 6h-4V5h4v4zm0 10h2v2h-2zm-6-6h2v2h-2zm2 2h2v2h-2zm-2 2h2v2h-2zm2 2h2v2h-2zm2-2h2v2h-2zm0-4h2v2h-2zm2 2h2v2h-2z"/>
</vector>
+1 -3
View File
@@ -1,7 +1,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/selected_row"
<item android:drawable="@color/pressed_row"
android:state_pressed="true"/>
<item android:drawable="@color/selected_row"
android:state_selected="true"/>
<item android:drawable="@android:color/transparent"/>
</selector>
+14 -26
View File
@@ -8,18 +8,15 @@
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<EditText
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:gravity="start|top"
android:hint="@string/content"
android:imeOptions="flagNoExtractUi"
@@ -32,43 +29,34 @@
android:id="@+id/format"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/content"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
android:gravity="end|top"/>
<TableLayout
android:id="@+id/data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/format"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"/>
android:layout_marginBottom="16dp"/>
<TableLayout
android:id="@+id/meta"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/data"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"/>
android:layout_marginBottom="16dp"/>
<TextView
android:id="@+id/hex"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/meta"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
android:typeface="monospace"
android:textSize="12sp"
tools:text="54 65 73 74 20 51 52 20 Test QR\n43 6F 64 65 Code"/>
</RelativeLayout>
<TextView
android:id="@+id/stamp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:textSize="12sp"
android:typeface="monospace"/>
</LinearLayout>
</de.markusfisch.android.binaryeye.widget.ConfinedScrollView>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/inset_layout"
+1 -1
View File
@@ -6,7 +6,7 @@
android:id="@+id/scans"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:choiceMode="singleChoice"
android:choiceMode="multipleChoice"
android:clipToPadding="false"
android:divider="@color/separator"
android:dividerHeight="1px"/>
+9 -7
View File
@@ -15,13 +15,15 @@
<string name="error_correction_level_m" formatted="false">মধ্যম (~১৫&#37; ঠিককরণ)</string>
<string name="error_correction_level_q" formatted="false">বেশি (~২৫&#37; ঠিককরণ)</string>
<string name="error_correction_level_h" formatted="false">অনেক বেশি (~৩০&#37; ঠিককরণ)</string>
<string name="issue_number">সমস্যা ক্রম</string>
<string name="orientation">দিক</string>
<string name="other_meta_data">মেটাতথ্য</string>
<string name="pdf417_extra_metadata">পিডিএফ৪১৭ মেটাতথ্য</string>
<string name="possible_country">তৈরির সম্ভাব্য দেশ</string>
<string name="suggested_price">প্রস্তসবিত দাম</string>
<string name="upc_ean_extension">ইউপিসি ইএএন বর্ধিত</string>
<string name="sequence_size">Sequence size</string>
<string name="sequence_index">Sequence index</string>
<string name="sequence_id">Sequence ID</string>
<string name="gtin_issue_number">সমস্যা ক্রম</string>
<string name="gtin_country">তৈরির সম্ভাব্য দেশ</string>
<string name="gtin_price">প্রস্তসবিত দাম</string>
<string name="gtin_add_on">ইউপিসি ইএএন বর্ধিত</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$d (%2$d modules)</string>
<string name="toggle_flash">আলো</string>
<string name="error_flash">Error toggling flash</string>
<string name="share">ভাগ করো</string>
+51 -49
View File
@@ -1,7 +1,7 @@
<resources>
<string name="no_camera_no_fun">Bez oprávnění k fotoaparátu tato aplikace nemá smysl. Na viděnou.</string>
<string name="camera_error">Nepodařilo se otevřít fotoaparát, prosím zkuste to znovu</string>
<string name="scan_code">Skenovat kód</string>
<string name="scan_code">Naskenovat kód</string>
<string name="compose_barcode">Vytvořit čárový kód</string>
<string name="decode_barcode">Dekódovat čárový kód</string>
<string name="content">Obsah</string>
@@ -17,15 +17,17 @@
<string name="error_correction_level_m" formatted="false">Střední (~15&#37; korekce)</string>
<string name="error_correction_level_q" formatted="false">Čtvrtinový (~25&#37; korekce)</string>
<string name="error_correction_level_h" formatted="false">Vysoký (~30&#37; korekce)</string>
<string name="issue_number">Issue number</string>
<string name="orientation">Orientace</string>
<string name="other_meta_data">Metadata</string>
<string name="pdf417_extra_metadata">PDF417 metadata</string>
<string name="possible_country">Možná země původu</string>
<string name="suggested_price">Doporučená cena</string>
<string name="upc_ean_extension">rozšíření UPC EAN</string>
<string name="sequence_size">Sequence size</string>
<string name="sequence_index">Sequence index</string>
<string name="sequence_id">Sequence ID</string>
<string name="gtin_issue_number">Číslo edice</string>
<string name="gtin_country">Možná země původu</string>
<string name="gtin_price">Doporučená cena</string>
<string name="gtin_add_on">rozšíření UPC EAN</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$d (%2$d modules)</string>
<string name="toggle_flash">Zap/vyp blesk</string>
<string name="error_flash">Error toggling flash</string>
<string name="error_flash">Zapnutí/vypnutí blesku selhalo</string>
<string name="share">Sdílet</string>
<string name="share_as">Sdílet jako</string>
<string name="copy_to_clipboard">Zkopírovat do schránky</string>
@@ -33,7 +35,7 @@
<string name="copy_password">Zkopírovat heslo do schránky</string>
<string name="copied_password_to_clipboard">Heslo zkopírováno do schránky</string>
<string name="open_url">Otevřít URL</string>
<string name="cannot_resolve_action">Nemáte aplikaci, která by to uměla otevřít</string>
<string name="cannot_resolve_action">Nemáte aplikaci, která by toto uměla otevřít</string>
<string name="pick_search_engine">Zvolte vyhledávač</string>
<string name="format">Formát</string>
<string name="size">Velikost v pixelech</string>
@@ -46,34 +48,34 @@
<string name="info">Info</string>
<string name="switch_camera">Přepnout kameru</string>
<string name="bulk_mode">Skenovat nepřetržitě</string>
<string name="bulk_mode_summary">Always start scanning continuously.</string>
<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="scan_format">Scan %s</string>
<string name="quarter_second">A quarter second</string>
<string name="half_second">Half a second</string>
<string name="a_second">One second</string>
<string name="two_seconds">Two seconds</string>
<string name="five_seconds">Five seconds</string>
<string name="bulk_mode_summary">Vždy začít v módu nepřetržitého skenování.</string>
<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="scan_format">Naskenovat %s</string>
<string name="quarter_second">Čtvrt sekundy</string>
<string name="half_second">Půl sekundy</string>
<string name="a_second">Jedna sekunda</string>
<string name="two_seconds">Dvě sekundy</string>
<string name="five_seconds">Pět sekund</string>
<string name="history">Historie</string>
<string name="preferences">Nastavení</string>
<string name="scan_category">Skenování</string>
<string name="content_category">Obsah</string>
<string name="locale_category">Jazyk</string>
<string name="custom_locale">Zvolit jazyk</string>
<string name="follow_system_settings">Řídit se nastavením systému</string>
<string name="custom_locale">Nastavit jazyk</string>
<string name="follow_system_settings">Podle systému</string>
<string name="show_crop_handle">Zobrazovat hrany výřezu</string>
<string name="show_crop_handle_summary">Omezí skenování na zvolenou oblast.</string>
<string name="barcode_formats">Barcode formats</string>
<string name="barcode_formats">Formáty čárových kódů</string>
<string name="zoom_by_swiping">Potáhnutím nahoru/dolů přiblížit</string>
<string name="zoom_by_swiping_summary">Přetáhnutím nahoru nebo dolů se zvětší / zmenší úroveň přiblížení.</string>
<string name="auto_rotate">Rozpoznávat otočené 1D čárové kódy</string>
<string name="auto_rotate_summary">Automaticky otočí obraz a tak detekuje 1D čárové kódy na výšku. Na některých zařízeních může snížit výkonnost.</string>
<string name="try_harder">Optimalizovat pro přesnost</string>
<string name="try_harder_summary">Použijte pro velmi nečitelné kódy. Sníží výkonnost.</string>
<string name="show_toast_in_bulk_mode">Show data in continuous mode</string>
<string name="show_toast_in_bulk_mode_summary">Briefly show the scanned data when scanning continuously.</string>
<string name="show_toast_in_bulk_mode">Zobrazovat data v módu nepřetržitého skenování</string>
<string name="show_toast_in_bulk_mode_summary">Krátce zobrazit data v módu nepřetržitého skenování.</string>
<string name="vibrate">Zavibrovat při detekování</string>
<string name="vibrate_summary">Zařízení zavibruje, když rozpozná čárový kód.</string>
<string name="use_history">Ukládat historii skenování</string>
@@ -90,24 +92,24 @@
<string name="show_hex_dump_summary">Zobrazí hex dump naskenovaného obsahu.</string>
<string name="close_automatically">Vrátit se po zkopírování/sdílení</string>
<string name="close_automatically_summary">Po zkopírovaní či sdílení se automaticky vrátí na skenovací obrazovku.</string>
<string name="default_search_engine">Open unknown data in</string>
<string name="always_ask">Always ask</string>
<string name="open_with_url">Otevřít neznámá data pomocí URL</string>
<string name="clear_network_suggestions">Clear network suggestions</string>
<string name="clear_network_suggestions_summary">Remove all of the network suggestions that were previously provided by this app.</string>
<string name="really_remove_all_networks">Really remove all networks?</string>
<string name="clear_network_suggestions_success">Network suggestions cleared</string>
<string name="clear_network_suggestions_nothing_to_remove">Nothing to remove</string>
<string name="send_category">Přesměrování</string>
<string name="send_scan_active">Forward scans</string>
<string name="send_scan_active_summary">Enable to forward scans to a the given URL</string>
<string name="default_search_engine">Nerozpoznaná data otevřít v</string>
<string name="always_ask">Vždy se zeptat</string>
<string name="open_with_url">Otevřít nerozpoznaná data pomocí URL</string>
<string name="clear_network_suggestions">Smazat navrhované sítě</string>
<string name="clear_network_suggestions_summary">Smazat všechny sítě, které byly navrženy touto aplikací.</string>
<string name="really_remove_all_networks">Opravdu smazat všechny sítě?</string>
<string name="clear_network_suggestions_success">Navrhované sítě smazány</string>
<string name="clear_network_suggestions_nothing_to_remove">Nic k smazání</string>
<string name="send_category">Převání</string>
<string name="send_scan_active">Předávat skeny</string>
<string name="send_scan_active_summary">Zapnout předávání skenů na danou URL</string>
<string name="send_scan_url">Posílat každý sken na URL</string>
<string name="send_scan_type">Typ requestu pro každý sken</string>
<string name="send_type_get_add_content">GET a simply add content</string>
<string name="send_type_get_query_string">GET with complete query string</string>
<string name="send_type_post_form">POST application/x-www-form-urlencoded</string>
<string name="send_type_post_json">POST application/json</string>
<string name="send_type_external_browser">Open in external browser</string>
<string name="send_type_external_browser">Otevřít v prohlížeči</string>
<string name="test_url">Otestovat URL</string>
<string name="really_remove_scan">Opravdu odstranit sken?</string>
<string name="really_remove_all_scans">Opravdu odstranit všechny skeny?</string>
@@ -150,20 +152,20 @@
<string name="export_database">SQLite databáze</string>
<string name="saved_in_downloads">Uloženo do složky Stahování</string>
<string name="rotate_image_cw">Otočit obrázek po směru hodinových ručiček</string>
<string name="pick_file">Zvolte obrázek</string>
<string name="pick_code_to_scan">Zvolte kód k naskenování</string>
<string name="pick_file">Vybrat obrázek</string>
<string name="pick_code_to_scan">Vyberte kód k naskenování</string>
<string name="shortcut_decode">Skenovat čárový kód</string>
<string name="shortcut_encode">Vytvořit čárový kód</string>
<string name="shortcut_preferences">Nastavení</string>
<string name="background_request_failed">Požadavek na pozadí selhal</string>
<string name="entry_type">Type</string>
<string name="wifi_network">Wi-Fi network</string>
<string name="wifi_ssid">Name</string>
<string name="wifi_type">Authentication type</string>
<string name="wifi_password">Password</string>
<string name="wifi_hidden">Hidden network</string>
<string name="wifi_eap">EAP method</string>
<string name="wifi_anonymous_identity">Anonymous identity</string>
<string name="wifi_identity">Identity</string>
<string name="wifi_phase2">Phase 2 method</string>
<string name="entry_type">Typ</string>
<string name="wifi_network">Wi-Fi síť</string>
<string name="wifi_ssid">Název</string>
<string name="wifi_type">Zabezpečení</string>
<string name="wifi_password">Heslo</string>
<string name="wifi_hidden">Skrytá síť</string>
<string name="wifi_eap">Metoda EAP</string>
<string name="wifi_anonymous_identity">Anonym identita</string>
<string name="wifi_identity">Identita</string>
<string name="wifi_phase2">Ověření Phase 2</string>
</resources>
+9 -7
View File
@@ -15,13 +15,15 @@
<string name="error_correction_level_m" formatted="false">Medium (~15&#37; wiederherstellbar)</string>
<string name="error_correction_level_q" formatted="false">Quartile (~25&#37; wiederherstellbar)</string>
<string name="error_correction_level_h" formatted="false">High (~30&#37; wiederherstellbar)</string>
<string name="issue_number">Ausgabe Nummer</string>
<string name="orientation">Orientierung</string>
<string name="other_meta_data">Metadaten</string>
<string name="pdf417_extra_metadata">PDF417 Metadaten</string>
<string name="possible_country">Mögliches Ursprungsland</string>
<string name="suggested_price">Preisvorschlag</string>
<string name="upc_ean_extension">UPC EAN Erweiterung</string>
<string name="sequence_size">Sequence size</string>
<string name="sequence_index">Sequence index</string>
<string name="sequence_id">Sequence ID</string>
<string name="gtin_issue_number">Ausgabe Nummer</string>
<string name="gtin_country">Mögliches Ursprungsland</string>
<string name="gtin_price">Preisvorschlag</string>
<string name="gtin_add_on">UPC EAN Erweiterung</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$d (%2$d Module)</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>
+29 -27
View File
@@ -1,5 +1,5 @@
<resources>
<string name="no_camera_no_fun">Esta aplicación no tiene sentido usar sin acceso a la cámara. Adiós.</string>
<string name="no_camera_no_fun">No tiene sentido usar esta aplicación sin acceso a la cámara. Adiós.</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 de barras</string>
<string name="compose_barcode">Componer código de barras</string>
@@ -15,14 +15,16 @@
<string name="error_correction_level_m" formatted="false">Medio (~15&#37; de correción)</string>
<string name="error_correction_level_q" formatted="false">Cuartil (~25&#37; de correción)</string>
<string name="error_correction_level_h" formatted="false">Alto (~30&#37; de correción)</string>
<string name="issue_number">Número de emisión</string>
<string name="orientation">Orientación</string>
<string name="other_meta_data">Metadatos</string>
<string name="pdf417_extra_metadata">Metadatos PDF417</string>
<string name="possible_country">Posible país de fabricación</string>
<string name="suggested_price">Precio sugerido</string>
<string name="upc_ean_extension">Extensión UPC EAN</string>
<string name="toggle_flash">Activar la linterna</string>
<string name="sequence_size">Sequence size</string>
<string name="sequence_index">Sequence index</string>
<string name="sequence_id">Sequence ID</string>
<string name="gtin_issue_number">Número de emisión</string>
<string name="gtin_country">Posible país de fabricación</string>
<string name="gtin_price">Precio sugerido</string>
<string name="gtin_add_on">Extensión UPC EAN</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$d (%2$d modules)</string>
<string name="toggle_flash">Activar el flash</string>
<string name="error_flash">Error toggling flash</string>
<string name="share">Compartir</string>
<string name="share_as">¿Compartir como?</string>
@@ -45,15 +47,15 @@
<string name="switch_camera">Cambiar cámara</string>
<string name="bulk_mode">Escanear constantemente</string>
<string name="bulk_mode_summary">Siempre iniciar escaneando constantemente</string>
<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="scan_format">Scan %s</string>
<string name="quarter_second">A quarter second</string>
<string name="half_second">Half a second</string>
<string name="a_second">One second</string>
<string name="two_seconds">Two seconds</string>
<string name="five_seconds">Five seconds</string>
<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="scan_format">Escanear %s</string>
<string name="quarter_second">Un cuarto de segundo</string>
<string name="half_second">Medio segundo</string>
<string name="a_second">Un segundo</string>
<string name="two_seconds">Dos segundos</string>
<string name="five_seconds">Cinco segundos</string>
<string name="history">Historial</string>
<string name="preferences">Configuración</string>
<string name="scan_category">Escanear</string>
@@ -105,7 +107,7 @@
<string name="send_type_get_query_string">GET con consulta completa</string>
<string name="send_type_post_form">POST application/x-www-form-urlencoded</string>
<string name="send_type_post_json">POST application/json</string>
<string name="send_type_external_browser">Open in external browser</string>
<string name="send_type_external_browser">Abrir en el navegador</string>
<string name="test_url">URL de prueba</string>
<string name="really_remove_scan">¿Realmente desea borrar el escaneo seleccionado?</string>
<string name="really_remove_all_scans">¿Realmente desea borrar todo lo escaneado?</string>
@@ -155,13 +157,13 @@
<string name="shortcut_preferences">Ajustes</string>
<string name="background_request_failed">Solicitud de fondo fallida</string>
<string name="entry_type">Type</string>
<string name="wifi_network">Wi-Fi network</string>
<string name="wifi_ssid">Name</string>
<string name="wifi_type">Authentication type</string>
<string name="wifi_password">Password</string>
<string name="wifi_hidden">Hidden network</string>
<string name="wifi_eap">EAP method</string>
<string name="wifi_anonymous_identity">Anonymous identity</string>
<string name="wifi_identity">Identity</string>
<string name="wifi_network">Red wi-fi</string>
<string name="wifi_ssid">Nombre</string>
<string name="wifi_type">Tipo de autenticación</string>
<string name="wifi_password">Contraseña</string>
<string name="wifi_hidden">Red oculta</string>
<string name="wifi_eap">Método EAP</string>
<string name="wifi_anonymous_identity">Identidad anónima</string>
<string name="wifi_identity">Identidad</string>
<string name="wifi_phase2">Phase 2 method</string>
</resources>
@@ -0,0 +1,24 @@
<resources>
<string-array name="search_engines_names">
<item>@string/always_ask</item>
<item>گوگل</item>
<item>داک‌داک‌گو</item>
<item>Qwant</item>
<item>بینگ</item>
<item>یاندکس</item>
<item>OpenFoodFacts.org</item>
<item>OpenBeautyFacts.org</item>
<item>OpenPetFoodFacts.org</item>
</string-array>
<string-array name="search_engines_values">
<item></item>
<item>https://www.google.com/search?q=</item>
<item>https://duckduckgo.com/?q=</item>
<item>https://www.qwant.com/?q=</item>
<item>https://bing.com/?q=</item>
<item>https://yandex.com/?q=</item>
<item>https://world.openfoodfacts.org/cgi/search.pl?search_terms=</item>
<item>https://world.openbeautyfacts.org/cgi/search.pl?search_terms=</item>
<item>https://world.openpetfoodfacts.org/cgi/search.pl?search_terms=</item>
</string-array>
</resources>
+169
View File
@@ -0,0 +1,169 @@
<resources>
<string name="no_camera_no_fun">این برنامه بدون دسترسی به دوربین هیچ کارایی ندارد. خدانگهدار.</string>
<string name="camera_error">نمی‌توان به دوربین دسترسی داشت، لطفاً دوباره تلاش کنید</string>
<string name="scan_code">پویش کد</string>
<string name="compose_barcode">ایجاد بارکد</string>
<string name="decode_barcode">رمزگشایی بارکد</string>
<string name="content">محتویات</string>
<string name="binary_data">(دادهٔ دودویی)</string>
<plurals name="barcode_info">
<item quantity="one">%2$d نویسه، %1$s</item>
<item quantity="other">%2$d نویسه، %1$s</item>
</plurals>
<string name="error_correction_level">سطح تصحیح خطا</string>
<string name="error_correction_level_l" formatted="false">کم (~۷٪⁩ تصحیح)</string>
<string name="error_correction_level_m" formatted="false">متوسط (⁦~۱۵٪⁩ تصحیح)</string>
<string name="error_correction_level_q" formatted="false">یک‌چهارم (⁦~۲۵٪⁩ تصحیح)</string>
<string name="error_correction_level_h" formatted="false">زیاد (~۳۰٪⁩ تصحیح)</string>
<string name="sequence_size">Sequence size</string>
<string name="sequence_index">Sequence index</string>
<string name="sequence_id">Sequence ID</string>
<string name="gtin_issue_number">شمارهٔ اشکال</string>
<string name="gtin_country">کشور احتمالی سازنده</string>
<string name="gtin_price">قیمت پیشنهادی</string>
<string name="gtin_add_on">UPC EAN extension</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$d (%2$d modules)</string>
<string name="toggle_flash">روشن/خاموش کردن چراغ</string>
<string name="error_flash">خطا هنگام روشن/خاموش کردن چراغ</string>
<string name="share">هم‌رسانی</string>
<string name="share_as">هم‌رسانی به عنوان؟</string>
<string name="copy_to_clipboard">رونوشت به تخه‌گیره</string>
<string name="copied_to_clipboard">به تخته‌گیره رونوشت شد</string>
<string name="copy_password">رونوشت گذرواژه به تخته‌گیره</string>
<string name="copied_password_to_clipboard">گذرواژه به تخته‌گیره رونوشت شد</string>
<string name="open_url">گشودن نشانی</string>
<string name="cannot_resolve_action">هیچ برنامه‌ای نمی‌تواند آن را بگشاید</string>
<string name="pick_search_engine">یک موتور جست‌وجو برگزینید</string>
<string name="format">قالب</string>
<string name="size">اندازه به پیکسل</string>
<string name="width_by_height">%1$d×%2$d</string>
<string name="input_content_here">محتویات ورودی را اینجا بنویسید</string>
<string name="encode">رمزگذاری</string>
<string name="error_no_content">محتویاتی نیست</string>
<string name="error_encoding_barcode">بارکد نمی‌تواند ایجاد شود</string>
<string name="view_barcode">نمایش بارکد</string>
<string name="info">اطّلاعات</string>
<string name="switch_camera">تغییر دوربین</string>
<string name="bulk_mode">به طور مداوم پویش کن</string>
<string name="bulk_mode_summary">همیشه پویش را به طور مداوم شروع می‌کند.</string>
<string name="bulk_mode_delay">تأخیر بین پویش‌های مداوم</string>
<string name="restrict_format">محدود کردن قالب</string>
<string name="remove_restriction">برداشتن محدودیت</string>
<string name="scan_format">پویش %s</string>
<string name="quarter_second">یک‌چهارم ثانیه</string>
<string name="half_second">نیم ثانیه</string>
<string name="a_second">یک ثانیه</string>
<string name="two_seconds">دو ثانیه</string>
<string name="five_seconds">پنج ثانیه</string>
<string name="history">تاریخچه</string>
<string name="preferences">ترجیحات</string>
<string name="scan_category">پویش</string>
<string name="content_category">محتویات</string>
<string name="locale_category">زبان</string>
<string name="custom_locale">زبان سفارشی</string>
<string name="follow_system_settings">پیروی از زبان سامانه</string>
<string name="show_crop_handle">نمایش محدود کننده برش</string>
<string name="show_crop_handle_summary">پویش را به یک منطقه قابل تنظیم محدود می‌کند.</string>
<string name="barcode_formats">قالب‌های بارکد</string>
<string name="zoom_by_swiping">برای بزرگنمایی به بالا یا پایین بکشید</string>
<string name="zoom_by_swiping_summary">برای کنترل بزرگنمایی دوربین، در صفحهٔ دوربین به بالا یا پایین بکشید.</string>
<string name="auto_rotate">تشخیص بارکدهای یک بعدی عمودی</string>
<string name="auto_rotate_summary">قاب دوربین را به صورت خودکار بچرخانید تا بارکدهای یک بعدی عمودی را تشخیص دهید. بسته به دستگاه، این ممکن است بر عملکرد تأثیر بگذارد.</string>
<string name="try_harder">بهینه کردن خواننده برای دقت بیشتر</string>
<string name="try_harder_summary">این گزینه را برای کدهایی که بسیار سخت خوانده می‌شوند، فعال کنید. بر عملکرد تأثیر خواهد گذاشت.</string>
<string name="show_toast_in_bulk_mode">نمایش داده‌ها در حالت پیوسته</string>
<string name="show_toast_in_bulk_mode_summary">هنگام پویش مداوم، داده‌های پویش شده را به طور خلاصه نشان می‌دهید.</string>
<string name="vibrate">لرزش هنگام تشخیص</string>
<string name="vibrate_summary">اگر می‌خواهید دستگاهتان هنگام شناسایی کد بلرزد، این را فعال کنید.</string>
<string name="use_history">ذخیرهٔ تاریخچهٔ پویش</string>
<string name="use_history_summary">کدهای شناسایی شده را در همین دستگاه ذخیره می‌کند.</string>
<string name="ignore_consecutive_duplicates">نادیده گرفتن موارد تکراری</string>
<string name="ignore_consecutive_duplicates_summary">موارد تکراری متوالی را در تاریخچهٔ پویش ذخیره نمی‌کند.</string>
<string name="open_immediately">گشودن بلافاصله</string>
<string name="open_immediately_summary">بازرسی را رها کنید و محتویات را فورا باز کنید. در صورت فعال بودن ممکن است محتوای مضر را باز کند.</string>
<string name="copy_immediately">قرار دادن در تخته‌گیره</string>
<string name="copy_immediately_summary">به طور خودکار محتوای پویش شده را در تخته‌گیره رونوشت می‌کند. لطفاً توجه داشته باشید که برنامه‌های دیگر ممکن است در پس‌زمینه تخته‌گیرهٔ شما را شنود کنند.</string>
<string name="show_meta_data">نمایش فراداده</string>
<string name="show_meta_data_summary">نمایش داده‌های اضافی در مورد بارکد پویش شده.</string>
<string name="show_hex_dump">نمایش کد هگز استخراج شده</string>
<string name="show_hex_dump_summary">نمایش کد هگز استخراج شده از محتویات پویش شده.</string>
<string name="close_automatically">بازگشت پس از رونوشت یا هم‌رسانی</string>
<string name="close_automatically_summary">پس از رونوشت یا هم‌رسانی مطالب خوانده شده، به طور خودکار به صفحهٔ پویش بازگردید.</string>
<string name="default_search_engine">گشودن دادهٔ نامشخص در</string>
<string name="always_ask">همیشه بپرس</string>
<string name="open_with_url">داده‌های ناشناخته را با URL باز کنید</string>
<string name="clear_network_suggestions">پاک کردن پیشنهادات شبکه</string>
<string name="clear_network_suggestions_summary">همه پیشنهادات شبکه را که قبلاً توسط این برنامه ارائه شده است حذف کنید.</string>
<string name="really_remove_all_networks">واقعاً همه شبکه‌ها را حذف می‌کنید؟</string>
<string name="clear_network_suggestions_success">پیشنهادات شبکه پاک شد</string>
<string name="clear_network_suggestions_nothing_to_remove">چیزی برای حذف نیست</string>
<string name="send_category">هدایت کردن</string>
<string name="send_scan_active">هدایت پویش‌ها</string>
<string name="send_scan_active_summary">برای هدایت پویش‌ها به نشانی مشخص شده، آن را به کار بیاندازید.</string>
<string name="send_scan_url">نشانی برای هدایت به آن</string>
<string name="send_scan_type">گونهٔ درخواست</string>
<string name="send_type_get_add_content">GET و به سادگی محتوا را اضافه کنید</string>
<string name="send_type_get_query_string">GET با رشته پرس‌وجو کامل</string>
<string name="send_type_post_form">POST application/x-www-form-urlencoded</string>
<string name="send_type_post_json">POST application/json</string>
<string name="send_type_external_browser">گشودن در مرورگر خارجی</string>
<string name="test_url">آزمودن نشانی</string>
<string name="really_remove_scan">برای برداشتن پویش مطمئنید؟</string>
<string name="really_remove_all_scans">برای برداشتن همهٔ پویش‌ها مطمئنید؟</string>
<string name="really_remove_selected_scans">برای برداشتن پویش‌های برگزیده شده مطمئنید؟</string>
<string name="clear_history">پاک کردن تاریخچه</string>
<string name="copy_scan">رونوشت پویش</string>
<string name="edit_scan">ویرایش عنوان</string>
<string name="remove_scan">برداشتن پویش</string>
<string name="enter_name">یک نام برای توضیح پویش وارد کنید</string>
<string name="enter_name_hint">نام پویش</string>
<string name="no_barcode_found">بارکدی یافت نشد</string>
<string name="pick_list_separator">چگونه موارد فهرست را جدا کنیم؟</string>
<string name="separator_line_break">خط بعدی</string>
<string name="separator_ruler">خط كش</string>
<string name="save_as_file_name">به عنوان پرونده در بارگیری‌ها ذخیره شود؟</string>
<string name="file_name">نام پرونده</string>
<string name="error_saving_file">نمی‌توان پرونده را ذخیره کرد</string>
<string name="error_file_exists">پرونده هم‌اکنون وجود دارد</string>
<string name="connect_to_wifi">اتّصال به وای‌فای</string>
<string name="wifi_config_failed">نمی‌توان وای‌فای را پیکربندی کرد</string>
<string name="wifi_added">وای‌فای اضافه شد</string>
<string name="sms_send">ارسال اس‌ام‌اس</string>
<string name="sms_error">نمی‌توان اس‌ام‌اس فرستاد</string>
<string name="tel_dial">تماس با شماره</string>
<string name="tel_error">نمی‌توان با شماره تماس گرفت</string>
<string name="mail_send">ارسال رایانامه</string>
<string name="mail_error">نمی‌توان رایانامه فرستاد</string>
<string name="vcard_add">افزودن به مخاطبین</string>
<string name="vcard_failed">نمی‌توان به مخاطبین اضافه کرد</string>
<string name="vevent_add">افزودن به تقویم</string>
<string name="vevent_failed">نمی‌توان به تقویم اضافه کرد</string>
<string name="otpauth_add">افزودن احراز هویت چندعاملی(2FA)</string>
<string name="search_web">جست‌وجوی وب</string>
<string name="search_scan">جست‌وجوی پویش</string>
<string name="export_to_file">برون‌ریزی به پرونده</string>
<string name="export_as">برون‌ریزی به عنوان؟</string>
<string name="export_csv_comma">پروندهٔ سی‌اس‌وی با ویرگول</string>
<string name="export_csv_semicolon">پروندهٔ سی‌اس‌وی با نقطه‌ویرگول</string>
<string name="export_json">پروندهٔ جی‌سون</string>
<string name="export_database">پایگاه‌دادهٔ اس‌کیولایت</string>
<string name="saved_in_downloads">در بارگیری‌ها ذخیره شد</string>
<string name="rotate_image_cw">تصویر را در جهت ساعتگرد بچرخانید</string>
<string name="pick_file">پروندهٔ تصویری را برگزینید</string>
<string name="pick_code_to_scan">کدی را برای پویش برگزینید</string>
<string name="shortcut_decode">پویش یک بارکد</string>
<string name="shortcut_encode">ایجاد یک بارکد</string>
<string name="shortcut_preferences">تنظیمات</string>
<string name="background_request_failed">درخواست پس‌زمینه انجام نشد</string>
<string name="entry_type">گونه</string>
<string name="wifi_network">شبکهٔ وای‌فای</string>
<string name="wifi_ssid">نام</string>
<string name="wifi_type">گونهٔ تأیید اعتبار</string>
<string name="wifi_password">گذرواژه</string>
<string name="wifi_hidden">شبکهٔ پنهان</string>
<string name="wifi_eap">روش EAP</string>
<string name="wifi_anonymous_identity">هویت ناشناس</string>
<string name="wifi_identity">هویت</string>
<string name="wifi_phase2">روش دو مرحله‌ای</string>
</resources>
+27 -25
View File
@@ -16,15 +16,17 @@
<string name="error_correction_level_m" formatted="false">Moyen (~15% de correction)</string>
<string name="error_correction_level_q" formatted="false">Quartile (~25% de correction)</string>
<string name="error_correction_level_h" formatted="false">Élevé (~30% de correction)</string>
<string name="issue_number">Issue number</string>
<string name="orientation">Orientation</string>
<string name="other_meta_data">Métadonnées</string>
<string name="pdf417_extra_metadata">Métadonnées PDF417</string>
<string name="possible_country">Pays de fabrication possible</string>
<string name="suggested_price">Prix suggéré</string>
<string name="upc_ean_extension">Extension UPC EAN</string>
<string name="sequence_size">Sequence size</string>
<string name="sequence_index">Sequence index</string>
<string name="sequence_id">Sequence ID</string>
<string name="gtin_issue_number">Numéro du problème</string>
<string name="gtin_country">Pays de fabrication possible</string>
<string name="gtin_price">Prix suggéré</string>
<string name="gtin_add_on">Extension UPC EAN</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$d (%2$d modules)</string>
<string name="toggle_flash">Activer le flash</string>
<string name="error_flash">Error toggling flash</string>
<string name="error_flash">Erreur de basculement du flash</string>
<string name="share">Partager</string>
<string name="share_as">Partager comme ?</string>
<string name="copy_to_clipboard">Copier dans le presse-papiers</string>
@@ -45,10 +47,10 @@
<string name="info">Info</string>
<string name="switch_camera">Changer de camera</string>
<string name="bulk_mode">Scan continu</string>
<string name="bulk_mode_summary">Toujours démarrer en scannant en continu.</string>
<string name="bulk_mode_summary">Démarre toujours en scan continu.</string>
<string name="bulk_mode_delay">Délai entre les scans continus</string>
<string name="restrict_format">Restrict format</string>
<string name="remove_restriction">Remove restriction</string>
<string name="restrict_format">Restreindre le format</string>
<string name="remove_restriction">Supprimer la restriction</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>
@@ -63,45 +65,45 @@
<string name="custom_locale">Langue personnalisée</string>
<string name="follow_system_settings">Suivre les réglages du système</string>
<string name="show_crop_handle">Afficher le bouton de rognage</string>
<string name="show_crop_handle_summary">Restreindre le scan à une région personnalisée.</string>
<string name="show_crop_handle_summary">Restreint le scan à une région personnalisée.</string>
<string name="barcode_formats">Formats de codes-barres</string>
<string name="zoom_by_swiping">Balayage haut/bas pour zoomer</string>
<string name="zoom_by_swiping_summary">Balayez vers le haut ou vers le bas dans l’écran de la caméra pour contrôler le zoom de la caméra.</string>
<string name="auto_rotate">Reconnaître les codes-barres 1D verticaux</string>
<string name="auto_rotate_summary">Tourner automatiquement le cadre de la caméra pour reconnaître les codes-barres 1D verticaux. En fonction de l\'appareil, cela peut affecter les performances.</string>
<string name="auto_rotate_summary">Tourne automatiquement le cadre de la caméra pour reconnaître les codes-barres 1D verticaux. En fonction de l\'appareil, cela peut affecter les performances.</string>
<string name="try_harder">Optimiser la précision du lecteur</string>
<string name="try_harder_summary">Activez cette option pour les codes très difficiles à lire. Affectera les performances.</string>
<string name="show_toast_in_bulk_mode">Afficher les données en mode continu</string>
<string name="show_toast_in_bulk_mode_summary">Afficher brièvement les données lors d\'un scan en continu.</string>
<string name="vibrate">Vibrer à la détection</string>
<string name="vibrate_summary">Activez ceci si vous voulez que votre appareil vibre lorsquun code est reconnu.</string>
<string name="vibrate_summary">Fait vibrer l\'appareil lorsqu\'un code est reconnu.</string>
<string name="use_history">Sauvegarder l\'historique des scans</string>
<string name="use_history_summary">Sauvegarder les codes reconnus sur votre appareil.</string>
<string name="use_history_summary">Sauvegarde les codes reconnus sur votre appareil.</string>
<string name="ignore_consecutive_duplicates">Ignorer les doublons</string>
<string name="ignore_consecutive_duplicates_summary">Ne pas sauvegarder les doublons dans l\'historique des scans.</string>
<string name="ignore_consecutive_duplicates_summary">Ne sauvegarde pas les doublons dans l\'historique des scans.</string>
<string name="open_immediately">Ouvrir immédiatement</string>
<string name="open_immediately_summary">Sauter l\'inspection et ouvrir le contenu immédiatement. Si activé, cela peut ouvrir un contenu nocif.</string>
<string name="open_immediately_summary">Saute l\'inspection et ouvre le contenu immédiatement. Si activé, cela peut ouvrir un contenu nocif.</string>
<string name="copy_immediately">Mettre dans le presse-papiers</string>
<string name="copy_immediately_summary">Copier automatiquement les contenus scannés dans le presse-papiers. Veuillez noter que les autres applications peuvent fouiner dans votre presse-papiers en arrière-plan.</string>
<string name="copy_immediately_summary">Copie automatiquement les contenus scannés dans le presse-papiers. Veuillez noter que les autres applications peuvent fouiner dans votre presse-papiers en arrière-plan.</string>
<string name="show_meta_data">Afficher les méta-données</string>
<string name="show_meta_data_summary">Afficher les données supplémentaires sur le code-barres scanné.</string>
<string name="show_meta_data_summary">Affiche les données supplémentaires sur le code-barres scanné.</string>
<string name="show_hex_dump">Afficher le dump hexa</string>
<string name="show_hex_dump_summary">Affiche un dump hexadécimal du contenu scanné.</string>
<string name="show_hex_dump_summary">Affiche le dump hexadécimal du contenu scanné.</string>
<string name="close_automatically">Revenir après une copie/partage</string>
<string name="close_automatically_summary">Revient automatiquement sur l\'écran de scan après avoir copié ou partagé le contenu lu.</string>
<string name="default_search_engine">Ouvrir les données inconnues dans</string>
<string name="always_ask">Toujours demander</string>
<string name="open_with_url">Ouvrir des données inconnues avec lURL</string>
<string name="clear_network_suggestions">Supprimer les suggestions de réseau</string>
<string name="clear_network_suggestions_summary">Supprimez toutes les suggestions de réseau précédemment fournies par cette application.</string>
<string name="clear_network_suggestions_summary">Supprime toutes les suggestions de réseau précédemment fournies par cette application.</string>
<string name="really_remove_all_networks">Supprimer vraiment tous les réseaux ?</string>
<string name="clear_network_suggestions_success">Suggestions de réseau supprimées</string>
<string name="clear_network_suggestions_nothing_to_remove">Rien à supprimer</string>
<string name="send_category">Transfert</string>
<string name="send_scan_active">Forward scans</string>
<string name="send_scan_active_summary">Enable to forward scans to a the given URL</string>
<string name="send_scan_url">Envoyer chaque scan vers une URL</string>
<string name="send_scan_type">Type de requête pour chaque scan</string>
<string name="send_scan_active">Transférer les scans</string>
<string name="send_scan_active_summary">Transfère les scans vers une URL donnée.</string>
<string name="send_scan_url">URL de transfert</string>
<string name="send_scan_type">Type de requête</string>
<string name="send_type_get_add_content">GET et ajouter simplement le contenu</string>
<string name="send_type_get_query_string">GET avec chaîne de requête complète</string>
<string name="send_type_post_form">POST application/x-www-form-urlencoded</string>
+17 -15
View File
@@ -15,15 +15,17 @@
<string name="error_correction_level_m" formatted="false">Közepes (~15&#37; javítás)</string>
<string name="error_correction_level_q" formatted="false">Kvartilis (~25&#37; javítás)</string>
<string name="error_correction_level_h" formatted="false">Magas (~30&#37; javítás)</string>
<string name="issue_number">Hibaszám</string>
<string name="orientation">Tájolás</string>
<string name="other_meta_data">Metaadat</string>
<string name="pdf417_extra_metadata">PDF417 metaadat</string>
<string name="possible_country">Gyártó lehetséges országa</string>
<string name="suggested_price">Ajánlott ár</string>
<string name="upc_ean_extension">UPC EAN kiterjesztés</string>
<string name="sequence_size">Sequence size</string>
<string name="sequence_index">Sequence index</string>
<string name="sequence_id">Sequence ID</string>
<string name="gtin_issue_number">Hibaszám</string>
<string name="gtin_country">Gyártó lehetséges országa</string>
<string name="gtin_price">Ajánlott ár</string>
<string name="gtin_add_on">UPC EAN kiterjesztés</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$d (%2$d modules)</string>
<string name="toggle_flash">Vaku be- vagy kikapcsolása</string>
<string name="error_flash">Error toggling flash</string>
<string name="error_flash">Hiba a vaku be- vagy kikapcsolásakor</string>
<string name="share">Megosztás</string>
<string name="share_as">Megosztja másként?</string>
<string name="copy_to_clipboard">Másolás a vágólapra</string>
@@ -46,9 +48,9 @@
<string name="bulk_mode">Beolvasás folyamatosan</string>
<string name="bulk_mode_summary">Mindig folyamatos beolvasást indítson.</string>
<string name="bulk_mode_delay">Folyamatos beolvasások közti késleltetés</string>
<string name="restrict_format">Restrict format</string>
<string name="remove_restriction">Remove restriction</string>
<string name="scan_format">Scan %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="scan_format">%s beolvasása</string>
<string name="quarter_second">Negyed másodperc</string>
<string name="half_second">Fél másodperc</string>
<string name="a_second">Egy másodperc</string>
@@ -97,16 +99,16 @@
<string name="clear_network_suggestions_success">Hálózati javaslatok törölve</string>
<string name="clear_network_suggestions_nothing_to_remove">Nincs mit eltávolítani</string>
<string name="send_category">Továbbítás</string>
<string name="send_scan_active">Forward scans</string>
<string name="send_scan_active_summary">Enable to forward scans to a the given URL</string>
<string name="send_scan_active">Beolvasások továbbítása</string>
<string name="send_scan_active_summary">A beolvasásoknak az adott URL-re történő továbbításának engedélyezése</string>
<string name="send_scan_url">Összes beolvasás küldése egy URL-re</string>
<string name="send_scan_type">Kérés típusa az egyes beolvasásoknál</string>
<string name="send_type_get_add_content">GET és a tartalom egyszerű hozzáadása</string>
<string name="send_type_get_query_string">GET a teljes lekérdezési karakterlánccal</string>
<string name="send_type_post_form">POST application/x-www-form-urlencoded</string>
<string name="send_type_post_json">POST application/json</string>
<string name="send_type_external_browser">Open in external browser</string>
<string name="test_url">Test URL</string>
<string name="send_type_external_browser">Megnyitás külső böngészőben</string>
<string name="test_url">Teszt URL</string>
<string name="really_remove_scan">Valóban eltávolítja a beolvasást?</string>
<string name="really_remove_all_scans">Valóban eltávolítja az összes beolvasását?</string>
<string name="really_remove_selected_scans">Valóban eltávolítja a kijelölt beolvasásokat?</string>
+9 -7
View File
@@ -14,13 +14,15 @@
<string name="error_correction_level_m" formatted="false">Medium (~15&#37; koreksi)</string>
<string name="error_correction_level_q" formatted="false">Kuartil (~25&#37; koreksi)</string>
<string name="error_correction_level_h" formatted="false">Tinggi (~30&#37; koreksi)</string>
<string name="issue_number">Nomor pindaian</string>
<string name="orientation">Orientasi</string>
<string name="other_meta_data">Metadata</string>
<string name="pdf417_extra_metadata">Metadata PDF417</string>
<string name="possible_country">Kemungkinan negara pembuat</string>
<string name="suggested_price">Harga yang disarankan</string>
<string name="upc_ean_extension">Ekstensi UPC EAN</string>
<string name="sequence_size">Sequence size</string>
<string name="sequence_index">Sequence index</string>
<string name="sequence_id">Sequence ID</string>
<string name="gtin_issue_number">Nomor pindaian</string>
<string name="gtin_country">Kemungkinan negara pembuat</string>
<string name="gtin_price">Harga yang disarankan</string>
<string name="gtin_add_on">Ekstensi UPC EAN</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$d (%2$d modules)</string>
<string name="toggle_flash">Nyala/matikan blitz</string>
<string name="error_flash">Error toggling flash</string>
<string name="share">Bagikan</string>
+63 -61
View File
@@ -10,22 +10,24 @@
<item quantity="one">%2$d carattere, %1$s</item>
<item quantity="other">%2$d caratteri, %1$s</item>
</plurals>
<string name="error_correction_level">Livello di correzione errori</string>
<string name="error_correction_level_l" formatted="false">Low (~7&#37; correction)</string>
<string name="error_correction_level_m" formatted="false">Medium (~15&#37; correction)</string>
<string name="error_correction_level_q" formatted="false">Quartile (~25&#37; correction)</string>
<string name="error_correction_level_h" formatted="false">High (~30&#37; correction)</string>
<string name="issue_number">Numero di edizione</string>
<string name="orientation">Orientamento</string>
<string name="other_meta_data">Metadati</string>
<string name="pdf417_extra_metadata">Metadati PDF417</string>
<string name="possible_country">Probabile Paese di fabbricazione</string>
<string name="suggested_price">Prezzo consigliato</string>
<string name="upc_ean_extension">Estensione UPC EAN</string>
<string name="toggle_flash">Flash</string>
<string name="error_flash">Error toggling flash</string>
<string name="error_correction_level">Livello di correzione degli errori</string>
<string name="error_correction_level_l" formatted="false">Basso (Correzione del ~7&#37;)</string>
<string name="error_correction_level_m" formatted="false">Medio (Correzione del ~15&#37;)</string>
<string name="error_correction_level_q" formatted="false">Quartile (Correzione del ~25&#37;)</string>
<string name="error_correction_level_h" formatted="false">Alto (Correzione del ~30&#37;)</string>
<string name="sequence_size">Sequence size</string>
<string name="sequence_index">Sequence index</string>
<string name="sequence_id">Sequence ID</string>
<string name="gtin_issue_number">Numero di edizione</string>
<string name="gtin_country">Probabile Paese di fabbricazione</string>
<string name="gtin_price">Prezzo consigliato</string>
<string name="gtin_add_on">Estensione UPC EAN</string>
<string name="barcode_version_number">Versione</string>
<string name="qr_version_and_modules">%1$d (%2$d modules)</string>
<string name="toggle_flash">Attiva/Disattiva flash</string>
<string name="error_flash">Errore durante l\'attivazione/disattivazione del flash</string>
<string name="share">Condividi</string>
<string name="share_as">Share as?</string>
<string name="share_as">Condividi come?</string>
<string name="copy_to_clipboard">Copia negli appunti</string>
<string name="copied_to_clipboard">Copiato negli appunti</string>
<string name="copy_password">Copia password negli appunti</string>
@@ -44,68 +46,68 @@
<string name="info">Info</string>
<string name="switch_camera">Cambia fotocamera</string>
<string name="bulk_mode">Scansione continua</string>
<string name="bulk_mode_summary">Always start scanning continuously.</string>
<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="scan_format">Scan %s</string>
<string name="quarter_second">A quarter second</string>
<string name="half_second">Half a second</string>
<string name="a_second">One second</string>
<string name="two_seconds">Two seconds</string>
<string name="five_seconds">Five seconds</string>
<string name="bulk_mode_summary">Avvia sempre la scansione in modo continuo.</string>
<string name="bulk_mode_delay">Ritardo tra scansioni continue</string>
<string name="restrict_format">Limita formato</string>
<string name="remove_restriction">Rimuovi la limitazione</string>
<string name="scan_format">Scansiona %s</string>
<string name="quarter_second">Un quarto di secondo</string>
<string name="half_second">Mezzo secondo</string>
<string name="a_second">Un secondo</string>
<string name="two_seconds">Due secondi</string>
<string name="five_seconds">Cinque secondi</string>
<string name="history">Cronologia</string>
<string name="preferences">Preferenze</string>
<string name="scan_category">Scansione</string>
<string name="content_category">Contenuto</string>
<string name="locale_category">Language</string>
<string name="custom_locale">Custom language</string>
<string name="follow_system_settings">Follow system settings</string>
<string name="locale_category">Lingua</string>
<string name="custom_locale">Lingua personalizzata</string>
<string name="follow_system_settings">Segui le impostazioni di sistema</string>
<string name="show_crop_handle">Mostra riquadro di limite</string>
<string name="show_crop_handle_summary">Limita la scansione ad una regione personalizzata.</string>
<string name="barcode_formats">Barcode formats</string>
<string name="barcode_formats">Formati di codici a barre</string>
<string name="zoom_by_swiping">Zoom fotocamera scorrendo su/giù</string>
<string name="zoom_by_swiping_summary">Scorri su o giù per controllare lo zoom della fotocamera.</string>
<string name="auto_rotate">Riconosci i codici a barre 1D verticalmente</string>
<string name="auto_rotate_summary">Ruota automaticamente la fotocamera per riconoscere i codici a barre 1D verticali. In base al dispositivo, potrebbe ridurre le prestazioni.</string>
<string name="try_harder">Ottimizza il lettore per accuratezza, non velocità</string>
<string name="try_harder">Ottimizza il lettore per accuratezza</string>
<string name="try_harder_summary">Attiva questa opzione per codici molto difficili da leggere. Riduce le prestazioni.</string>
<string name="show_toast_in_bulk_mode">Show data in continuous mode</string>
<string name="show_toast_in_bulk_mode_summary">Briefly show the scanned data when scanning continuously.</string>
<string name="show_toast_in_bulk_mode">Mostra i dati in modalità continua</string>
<string name="show_toast_in_bulk_mode_summary">Mostra brevemente i dati scansionati durante la scansione continua.</string>
<string name="vibrate">Vibra quando rilevato</string>
<string name="vibrate_summary">Attivalo se vuoi che il dispositivo vibri quando viene riconosciuto un codice.</string>
<string name="use_history">Salva cronologia delle scansioni</string>
<string name="use_history_summary">Salva i codici riconosciuti sul dispositivo.</string>
<string name="ignore_consecutive_duplicates">Non salvare doppi consecutivi</string>
<string name="ignore_consecutive_duplicates_summary">Non salvare doppi consecutivi nella cronologia di scansione.</string>
<string name="open_immediately">Salta l\'analisi e apri i contenuti direttamente</string>
<string name="open_immediately_summary">Salta l\'analisi e apri i contenuti direttamente. Se attivo potrebbe aprire contenuti dannosi.</string>
<string name="ignore_consecutive_duplicates">Ignora i duplicati</string>
<string name="ignore_consecutive_duplicates_summary">Non salvare duplicati consecutivi nella cronologia di scansione.</string>
<string name="open_immediately">Apri subito</string>
<string name="open_immediately_summary">Salta l\'ispezione e apri i contenuti immediatamente. Se abilitato può aprire contenuti dannosi.</string>
<string name="copy_immediately">Copia negli appunti</string>
<string name="copy_immediately_summary">Copia automaticamente i contenuti scansionati negli appunti. Nota che altre app potrebbero leggere gli appunti in secondo piano.</string>
<string name="show_meta_data">Mostra metadati</string>
<string name="show_meta_data_summary">Mostra dati aggiuntivi sul codice a barre scansionato.</string>
<string name="show_hex_dump">Mostra dump esadecimale</string>
<string name="show_hex_dump_summary">Mostra un dump esadecimale dei contenuti scansionati.</string>
<string name="close_automatically">Go back after Copy/Share</string>
<string name="close_automatically_summary">Automatically return to the scan screen after copying or sharing the read contents.</string>
<string name="default_search_engine">Open unknown data in</string>
<string name="always_ask">Always ask</string>
<string name="close_automatically">Vai indietro dopo la Copia/Condivisione</string>
<string name="close_automatically_summary">Ritorna automaticamente alla schermata di scansione dopo aver copiato o condiviso i contenuti letti.</string>
<string name="default_search_engine">Apri dati sconosciuti in</string>
<string name="always_ask">Chiedi sempre</string>
<string name="open_with_url">Apri dati sconosciuti con URL</string>
<string name="clear_network_suggestions">Clear network suggestions</string>
<string name="clear_network_suggestions_summary">Remove all of the network suggestions that were previously provided by this app.</string>
<string name="really_remove_all_networks">Really remove all networks?</string>
<string name="clear_network_suggestions_success">Network suggestions cleared</string>
<string name="clear_network_suggestions_nothing_to_remove">Nothing to remove</string>
<string name="clear_network_suggestions">Cancella suggerimenti di rete</string>
<string name="clear_network_suggestions_summary">Rimuovi tutti i suggerimenti di rete precedentemente forniti da questa app.</string>
<string name="really_remove_all_networks">Rimuovere davvero tutte le reti?</string>
<string name="clear_network_suggestions_success">Suggerimenti di rete cancellati</string>
<string name="clear_network_suggestions_nothing_to_remove">Niente da rimuovere</string>
<string name="send_category">Inoltro</string>
<string name="send_scan_active">Forward scans</string>
<string name="send_scan_active_summary">Enable to forward scans to a the given URL</string>
<string name="send_scan_url">Invia ogni scansione ad un URL</string>
<string name="send_scan_type">Tipo di richiesta per ogni scansione</string>
<string name="send_scan_active">Inoltra le scansioni</string>
<string name="send_scan_active_summary">Abilita per inoltrare le scansioni a un URL specificato</string>
<string name="send_scan_url">URL a cui inoltrare</string>
<string name="send_scan_type">Tipo di richiesta</string>
<string name="send_type_get_add_content">GET e aggiungi il contenuto</string>
<string name="send_type_get_query_string">GET con stringa completa della richiesta</string>
<string name="send_type_post_form">POST application/x-www-form-urlencoded</string>
<string name="send_type_post_json">POST application/json</string>
<string name="send_type_external_browser">Open in external browser</string>
<string name="send_type_external_browser">Apri nel browser esterno</string>
<string name="test_url">Test URL</string>
<string name="really_remove_scan">Vuoi veramente rimuovere la scansione?</string>
<string name="really_remove_all_scans">Vuoi veramente rimuovere tutte le scansioni?</string>
@@ -120,7 +122,7 @@
<string name="pick_list_separator">Come separare elementi della lista?</string>
<string name="separator_line_break">Interruzione di linea</string>
<string name="separator_ruler">Righello</string>
<string name="save_as_file_name">Salvare come file?</string>
<string name="save_as_file_name">Salvare come file nei Download?</string>
<string name="file_name">Nome del file</string>
<string name="error_saving_file">Impossibile salvare il file</string>
<string name="error_file_exists">Il file esiste già</string>
@@ -144,8 +146,8 @@
<string name="export_as">Esportare come?</string>
<string name="export_csv_comma">CSV con virgole</string>
<string name="export_csv_semicolon">CSV con punti e virgola</string>
<string name="export_json">Esporta come JSON</string>
<string name="export_database">Esporta database SQLite</string>
<string name="export_json">File JSON</string>
<string name="export_database">Database SQLite</string>
<string name="saved_in_downloads">Salvato nei download</string>
<string name="rotate_image_cw">Ruota immagine in senso orario</string>
<string name="pick_file">Scegli file immagine</string>
@@ -154,14 +156,14 @@
<string name="shortcut_encode">Crea un codice a barre</string>
<string name="shortcut_preferences">Impostazioni</string>
<string name="background_request_failed">Richiesta in secondo piano fallita</string>
<string name="entry_type">Type</string>
<string name="wifi_network">Wi-Fi network</string>
<string name="wifi_ssid">Name</string>
<string name="wifi_type">Authentication type</string>
<string name="entry_type">Tipo</string>
<string name="wifi_network">Rete Wi-Fi</string>
<string name="wifi_ssid">Nome</string>
<string name="wifi_type">Tipo di autenticazione</string>
<string name="wifi_password">Password</string>
<string name="wifi_hidden">Hidden network</string>
<string name="wifi_hidden">Rete nascosta</string>
<string name="wifi_eap">EAP method</string>
<string name="wifi_anonymous_identity">Anonymous identity</string>
<string name="wifi_identity">Identity</string>
<string name="wifi_phase2">Phase 2 method</string>
<string name="wifi_anonymous_identity">Identità anonima</string>
<string name="wifi_identity">Identità</string>
<string name="wifi_phase2">Metodo di fase 2</string>
</resources>
+29 -27
View File
@@ -14,15 +14,17 @@
<string name="error_correction_level_m" formatted="false">中 (~15&#37; の正確性)</string>
<string name="error_correction_level_q" formatted="false">高 (~25&#37; の正確性)</string>
<string name="error_correction_level_h" formatted="false">最高 (~30&#37; の正確性)</string>
<string name="issue_number">発行番号</string>
<string name="orientation">方向</string>
<string name="other_meta_data">メタデータ</string>
<string name="pdf417_extra_metadata">PDF417 メタデータ</string>
<string name="possible_country">製造国</string>
<string name="suggested_price">価格</string>
<string name="upc_ean_extension">UPC/EAN 拡張型</string>
<string name="toggle_flash">フラッシュを切り替え</string>
<string name="error_flash">Error toggling flash</string>
<string name="sequence_size">Sequence size</string>
<string name="sequence_index">Sequence index</string>
<string name="sequence_id">Sequence ID</string>
<string name="gtin_issue_number">発行番号</string>
<string name="gtin_country">製造国</string>
<string name="gtin_price">価格</string>
<string name="gtin_add_on">UPC/EAN 拡張型</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$d (%2$d modules)</string>
<string name="toggle_flash">フラッシュの切り替え</string>
<string name="error_flash">フラッシュの切り替えエラー</string>
<string name="share">共有</string>
<string name="share_as">画像形式を選択</string>
<string name="copy_to_clipboard">クリップボードにコピー</string>
@@ -44,15 +46,15 @@
<string name="switch_camera">カメラを切り替え</string>
<string name="bulk_mode">まとめてスキャンする</string>
<string name="bulk_mode_summary">常に連続してスキャンを行います。</string>
<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="scan_format">Scan %s</string>
<string name="quarter_second">A quarter second</string>
<string name="half_second">Half a second</string>
<string name="a_second">One second</string>
<string name="two_seconds">Two seconds</string>
<string name="five_seconds">Five seconds</string>
<string name="bulk_mode_delay">連続スキャンの間隔</string>
<string name="restrict_format">フォーマットの制限</string>
<string name="remove_restriction">制限を解除</string>
<string name="scan_format">%sをスキャン</string>
<string name="quarter_second">0.25秒</string>
<string name="half_second">0.5秒</string>
<string name="a_second">1秒</string>
<string name="two_seconds">2秒</string>
<string name="five_seconds">5秒</string>
<string name="history">履歴</string>
<string name="preferences">設定</string>
<string name="scan_category">スキャン</string>
@@ -96,15 +98,15 @@
<string name="clear_network_suggestions_success">ネットワークの提案は消去されました</string>
<string name="clear_network_suggestions_nothing_to_remove">消去するものがありませんでした</string>
<string name="send_category">転送</string>
<string name="send_scan_active">Forward scans</string>
<string name="send_scan_active_summary">Enable to forward scans to a the given URL</string>
<string name="send_scan_active">スキャンしたデータを転送</string>
<string name="send_scan_active_summary">指定されたURLにスキャンしたデータを転送します</string>
<string name="send_scan_url">スキャンしたデータを次のURLから開く</string>
<string name="send_scan_type">リクエスト方式</string>
<string name="send_type_get_add_content">GET でコンテンツを付加</string>
<string name="send_type_get_query_string">GET で文字列のコンテンツを付加</string>
<string name="send_type_post_form">POST application/x-www-form-urlencoded</string>
<string name="send_type_post_json">POST application/json</string>
<string name="send_type_external_browser">Open in external browser</string>
<string name="send_type_external_browser">外部ブラウザで開く</string>
<string name="test_url">URLをテスト</string>
<string name="really_remove_scan">このスキャン履歴を削除しても宜しいですか?</string>
<string name="really_remove_all_scans">全てのスキャン履歴を削除しても宜しいですか?</string>
@@ -153,12 +155,12 @@
<string name="shortcut_encode">バーコードを作成</string>
<string name="shortcut_preferences">設定</string>
<string name="background_request_failed">バックグラウンド要求に失敗しました</string>
<string name="entry_type">Type</string>
<string name="wifi_network">Wi-Fi network</string>
<string name="wifi_ssid">Name</string>
<string name="wifi_type">Authentication type</string>
<string name="wifi_password">Password</string>
<string name="wifi_hidden">Hidden network</string>
<string name="entry_type">種類</string>
<string name="wifi_network">Wi-Fiネットワーク</string>
<string name="wifi_ssid">SSID</string>
<string name="wifi_type">認証方式</string>
<string name="wifi_password">パスワード</string>
<string name="wifi_hidden">非表示のネットワーク</string>
<string name="wifi_eap">EAP method</string>
<string name="wifi_anonymous_identity">Anonymous identity</string>
<string name="wifi_identity">Identity</string>
+9 -7
View File
@@ -15,13 +15,15 @@
<string name="error_correction_level_m" formatted="false">Medium (~15&#37; correction)</string>
<string name="error_correction_level_q" formatted="false">Quartile (~25&#37; correction)</string>
<string name="error_correction_level_h" formatted="false">High (~30&#37; correction)</string>
<string name="issue_number">გამოშვების ნომერი</string>
<string name="orientation">ორიენტაცია</string>
<string name="other_meta_data">მეტამონაცემები</string>
<string name="pdf417_extra_metadata">PDF417 მეტამონაცემე</string>
<string name="possible_country">შესაძლო მწარმოებელი ქვეყანა</string>
<string name="suggested_price">დაახლოებითი ფასი</string>
<string name="upc_ean_extension">UPC EAN გაფართოება</string>
<string name="sequence_size">Sequence size</string>
<string name="sequence_index">Sequence index</string>
<string name="sequence_id">Sequence ID</string>
<string name="gtin_issue_number">გამოშვების ნომე</string>
<string name="gtin_country">შესაძლო მწარმოებელი ქვეყანა</string>
<string name="gtin_price">დაახლოებითი ფასი</string>
<string name="gtin_add_on">UPC EAN გაფართოება</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$d (%2$d modules)</string>
<string name="toggle_flash">განათების ჩართვა</string>
<string name="error_flash">Error toggling flash</string>
<string name="share">გაზიარება</string>
+9 -7
View File
@@ -15,13 +15,15 @@
<string name="error_correction_level_m" formatted="false">Middelhoog (~15&#37; correction)</string>
<string name="error_correction_level_q" formatted="false">Kwartiel (~25&#37; correction)</string>
<string name="error_correction_level_h" formatted="false">Hoog (~30&#37; correction)</string>
<string name="issue_number">Uitgiftenummer</string>
<string name="orientation">Oriëntatie</string>
<string name="other_meta_data">Metadata</string>
<string name="pdf417_extra_metadata">PDF417 metadata</string>
<string name="possible_country">Mogelijk land van fabricage</string>
<string name="suggested_price">Adviesprijs</string>
<string name="upc_ean_extension">UPC EAN extensie</string>
<string name="sequence_size">Sequence size</string>
<string name="sequence_index">Sequence index</string>
<string name="sequence_id">Sequence ID</string>
<string name="gtin_issue_number">Uitgiftenummer</string>
<string name="gtin_country">Mogelijk land van fabricage</string>
<string name="gtin_price">Adviesprijs</string>
<string name="gtin_add_on">UPC EAN extensie</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$d (%2$d modules)</string>
<string name="toggle_flash">Zaklamp aan/uit</string>
<string name="error_flash">Error toggling flash</string>
<string name="share">Delen</string>
+9 -7
View File
@@ -30,6 +30,9 @@
<string name="error_correction_level_m" formatted="false">Średni (~15&#37; korekcji)</string>
<string name="error_correction_level_q" formatted="false">Kwartyl (~25&#37; korekcji)</string>
<string name="error_correction_level_h" formatted="false">Wysoki (~30&#37; korekcji)</string>
<string name="sequence_size">Sequence size</string>
<string name="sequence_index">Sequence index</string>
<string name="sequence_id">Sequence ID</string>
<string name="export_csv_comma">CSV z przecinkami</string>
<string name="export_csv_semicolon">CSV ze średnikami</string>
<string name="shortcut_encode">Stwórz kod kreskowy</string>
@@ -79,10 +82,8 @@
<string name="zoom_by_swiping">Przybliżaj kamerę, przesuwając w górę/dół</string>
<string name="zoom_by_swiping_summary">Przesuń palcem w górę lub w dół na ekranie aparatu, aby sterować powiększeniem aparatu.</string>
<string name="view_barcode">Wyświetl kod kreskowy</string>
<string name="orientation">Orientacja</string>
<string name="error_encoding_barcode">Nie można utworzyć kodu kreskowego</string>
<string name="otpauth_add">Dodaj 2FA</string>
<string name="other_meta_data">Metadane</string>
<string name="remove_scan">Usuń skan</string>
<string name="separator_line_break">Przerwanie linii</string>
<string name="shortcut_decode">Zeskanuj kod kreskowy</string>
@@ -123,8 +124,7 @@
<string name="test_url">Testuj URL</string>
<string name="copy_password">Skopiuj hasło do schowka</string>
<string name="copied_password_to_clipboard">Hasło skopiowane do schowka</string>
<string name="pdf417_extra_metadata">Metadane PDF417</string>
<string name="suggested_price">Sugerowana cena</string>
<string name="gtin_price">Sugerowana cena</string>
<string name="preferences">Ustawienia</string>
<string name="scan_category">Skanowanie</string>
<string name="content_category">Zawarość</string>
@@ -132,8 +132,10 @@
<string name="custom_locale">Język niestandardowy</string>
<string name="follow_system_settings">Zgodny z ustawieniami systemu</string>
<string name="decode_barcode">Dekoduj kod kreskowy</string>
<string name="upc_ean_extension">Dodatek UPC EAN</string>
<string name="possible_country">Możliwy kraj produkcji</string>
<string name="gtin_add_on">Dodatek UPC EAN</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$d (%2$d modules)</string>
<string name="gtin_country">Możliwy kraj produkcji</string>
<string name="encode">ZAKODUJ</string>
<string name="error_no_content">Brak zawartości</string>
<string name="try_harder">Zoptymalizuj czytnik pod kątem dokładności, a nie prędkości</string>
@@ -148,7 +150,7 @@
<string name="auto_rotate">Rozpoznawaj kody kreskowe 1D pionowo</string>
<string name="auto_rotate_summary">Automatycznie obracaj ramkę aparatu, aby rozpoznać pionowe kody kreskowe 1D. W zależności od urządzenia może to mieć wpływ na wydajność.</string>
<string name="save_as_file_name">Jak zapisać plik w pobranych?</string>
<string name="issue_number">Numer wydania</string>
<string name="gtin_issue_number">Numer wydania</string>
<string name="background_request_failed">Żądanie w tle nie powiodło się</string>
<string name="entry_type">Typ</string>
<string name="wifi_network">Sieć Wi-Fi</string>
+36 -34
View File
@@ -1,5 +1,5 @@
<resources>
<string name="no_camera_no_fun">Infelizmente você não poderá usar este app sem acesso à câmera.</string>
<string name="no_camera_no_fun">Infelizmente você não poderá usar este app sem conceder o acesso à câmera.</string>
<string name="camera_error">Não foi possível acessar a câmera, por favor tente novamente</string>
<string name="scan_code">Escaneie código</string>
<string name="compose_barcode">Criar código</string>
@@ -15,17 +15,19 @@
<string name="error_correction_level_m" formatted="false">Médio (~15&#37; correção)</string>
<string name="error_correction_level_q" formatted="false">Quartil (~25&#37; correção)</string>
<string name="error_correction_level_h" formatted="false">Alto (~30&#37; correção)</string>
<string name="issue_number">Número de emissão</string>
<string name="orientation">Orientação</string>
<string name="other_meta_data">Metadados</string>
<string name="pdf417_extra_metadata">Metadados PDF417</string>
<string name="possible_country">Possível país de fabricação</string>
<string name="suggested_price">Preço sugerido</string>
<string name="upc_ean_extension">Extensão UPC EAN</string>
<string name="sequence_size">Sequence size</string>
<string name="sequence_index">Sequence index</string>
<string name="sequence_id">Sequence ID</string>
<string name="gtin_issue_number">Número de emissão</string>
<string name="gtin_country">Possível país de fabricação</string>
<string name="gtin_price">Preço sugerido</string>
<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$d (%2$d modules)</string>
<string name="toggle_flash">Ativar lanterna</string>
<string name="error_flash">Falha ao ligar a lanterna</string>
<string name="share">Compartilhar</string>
<string name="share_as">Compartilhar como?</string>
<string name="share_as">Compartilhar em qual formato?</string>
<string name="copy_to_clipboard">Copiar ao clipboard</string>
<string name="copied_to_clipboard">Copiado ao clipboard</string>
<string name="copy_password">Copiar senha ao clipboard</string>
@@ -45,7 +47,7 @@
<string name="switch_camera">Alterar câmera</string>
<string name="bulk_mode">Digitalização contínua</string>
<string name="bulk_mode_summary">Sempre executar em modo contínuo.</string>
<string name="bulk_mode_delay">Adiar em escaneamentos contínuos</string>
<string name="bulk_mode_delay">Adiar durante digitalização contínua</string>
<string name="restrict_format">Limitar formato</string>
<string name="remove_restriction">Remover limitação</string>
<string name="scan_format">Escanear %s</string>
@@ -61,10 +63,10 @@
<string name="locale_category">Idioma</string>
<string name="custom_locale">Idioma da interface</string>
<string name="follow_system_settings">Adaptar do sistema</string>
<string name="show_crop_handle">Mostrar limitador de corte</string>
<string name="show_crop_handle_summary">Limitar a uma área personalizável.</string>
<string name="show_crop_handle">Mostrar limitador customizável</string>
<string name="show_crop_handle_summary">Permite limitar a uma área específica.</string>
<string name="barcode_formats">Tipos de código</string>
<string name="zoom_by_swiping">Deslize para baixo/cima para ajustar zoom</string>
<string name="zoom_by_swiping">Deslize para ajustar zoom</string>
<string name="zoom_by_swiping_summary">Deslize para baixo/cima na tela para ajustar zoom da câmera.</string>
<string name="auto_rotate">Reconhecer códigos 1D verticais</string>
<string name="auto_rotate_summary">Girar automaticamente a câmera para reconhecer códigos 1D verticais. Dependendo do dispositivo, isto pode afetar o desempenho.</string>
@@ -73,43 +75,43 @@
<string name="show_toast_in_bulk_mode">Mostrar dados em modo contínuo</string>
<string name="show_toast_in_bulk_mode_summary">Mostrar brevemente os dados durante digitalização contínua.</string>
<string name="vibrate">Vibrar ao reconhecer</string>
<string name="vibrate_summary">Ative a opção se quiser que seu dispositivo vibre quando um código for reconhecido.</string>
<string name="use_history">Salvar histórico de escaneamentos</string>
<string name="vibrate_summary">Ative esta opção se quiser que seu dispositivo vibre quando um código for reconhecido.</string>
<string name="use_history">Salvar histórico de digitalizações</string>
<string name="use_history_summary">Salve códigos reconhecidos no seu dispositivo.</string>
<string name="ignore_consecutive_duplicates">Ignorar duplicações</string>
<string name="ignore_consecutive_duplicates_summary">Não salvar duplicações consecutivas no histórico de escaneamentos.</string>
<string name="ignore_consecutive_duplicates_summary">Não salvar duplicações consecutivas no histórico de digitalizações.</string>
<string name="open_immediately">Abrir imediatamente</string>
<string name="open_immediately_summary">Pular a inspeção e abrir o conteúdo imediatamente. Se ativado pode abrir conteúdo perigoso.</string>
<string name="open_immediately_summary">Pular a inspeção e abrir o conteúdo no mesmo instante. Quando ativado, pode abrir conteúdo perigoso.</string>
<string name="copy_immediately">Copiar ao clipboard</string>
<string name="copy_immediately_summary">Copiar automaticamente o conteúdo escaneado ao clipboard. Tem cuidado porque outros apps podem examinar seu clipboard em segundo plano.</string>
<string name="copy_immediately_summary">Copiar automaticamente ao clipboard o conteúdo digitalizado. Tem cuidado porque outros apps podem examinar seu clipboard em segundo plano.</string>
<string name="show_meta_data">Mostrar metadados</string>
<string name="show_meta_data_summary">Mostrar dados adicionais de código escaneado.</string>
<string name="show_meta_data_summary">Mostrar dados adicionais de código digitalizado.</string>
<string name="show_hex_dump">Mostrar impressão hexadecimal</string>
<string name="show_hex_dump_summary">Mostrar a impressão hexadecimal de conteúdo escaneado.</string>
<string name="show_hex_dump_summary">Mostrar a impressão hexadecimal do conteúdo digitalizado.</string>
<string name="close_automatically">Voltar após copiar/compartilhar</string>
<string name="close_automatically_summary">Voltar automaticamente à tela de digitalização após copiar ou compartilhar o código consultado.</string>
<string name="close_automatically_summary">Voltar automaticamente à tela de digitalização após copiar ou compartilhar o código examinado.</string>
<string name="default_search_engine">Abrir dados desconhecidos em</string>
<string name="always_ask">Sempre perguntar</string>
<string name="open_with_url">Abrir dados desconhecidos via URL</string>
<string name="clear_network_suggestions">Remover sugestões da rede</string>
<string name="clear_network_suggestions_summary">Remover todas as sugestões da rede que foram anteriormente fornecidas pelo app.</string>
<string name="really_remove_all_networks">Realmente remover todas as redes?</string>
<string name="clear_network_suggestions_success">Sugestões da rede removidas</string>
<string name="clear_network_suggestions_success">Sugestões da rede foram removidas</string>
<string name="clear_network_suggestions_nothing_to_remove">Nada para remover</string>
<string name="send_category">Encaminhamento</string>
<string name="send_scan_active">Encaminhar escaneamentos</string>
<string name="send_scan_active_summary">Ativar para encaminhar escaneamentos à URL definida</string>
<string name="send_scan_active">Encaminhar conteúdo digitalizado</string>
<string name="send_scan_active_summary">Ative para encaminhar conteúdo digitalizado à URL definida</string>
<string name="send_scan_url">URL à qual encaminhar</string>
<string name="send_scan_type">Tipo de solicitação</string>
<string name="send_type_get_add_content">GET e apenas adicionar o conteúdo</string>
<string name="send_type_get_add_content">GET e somente adicionar o conteúdo</string>
<string name="send_type_get_query_string">GET com sequência completa da consulta</string>
<string name="send_type_post_form">POST application/x-www-form-urlencoded</string>
<string name="send_type_post_json">POST application/json</string>
<string name="send_type_external_browser">Abrir num navegador externo</string>
<string name="send_type_external_browser">Abrir em navegador externo</string>
<string name="test_url">Testar a URL</string>
<string name="really_remove_scan">Realmente quer remover este escaneamento?</string>
<string name="really_remove_all_scans">Realmente quer remover todos os escaneamentos?</string>
<string name="really_remove_selected_scans">Realmente remover escaneamentos selecionados?</string>
<string name="really_remove_scan">Realmente quer remover esta digitalização?</string>
<string name="really_remove_all_scans">Realmente quer remover todas as digitalizações?</string>
<string name="really_remove_selected_scans">Realmente remover digitalizações selecionados?</string>
<string name="clear_history">Limpar histórico</string>
<string name="copy_scan">Copiar digitalização</string>
<string name="edit_scan">Editar identificação</string>
@@ -120,7 +122,7 @@
<string name="pick_list_separator">Como separar itens da lista?</string>
<string name="separator_line_break">Quebra de linha</string>
<string name="separator_ruler">Régua</string>
<string name="save_as_file_name">Salvar em Downloads?</string>
<string name="save_as_file_name">Salvar para Downloads?</string>
<string name="file_name">Nome do arquivo</string>
<string name="error_saving_file">Não foi possível salvar o arquivo</string>
<string name="error_file_exists">Arquivo já existe</string>
@@ -130,10 +132,10 @@
<string name="sms_send">Envie SMS</string>
<string name="sms_error">Não foi possível enviar SMS</string>
<string name="tel_dial">Disque número</string>
<string name="tel_error">Não foi possível discar número</string>
<string name="tel_error">Não foi possível discar o número</string>
<string name="mail_send">Envie e-mail</string>
<string name="mail_error">Não foi possível enviar e-mail</string>
<string name="vcard_add">Adicionar para contatos</string>
<string name="mail_error">Não foi possível enviar o e-mail</string>
<string name="vcard_add">Adicionar aos contatos</string>
<string name="vcard_failed">Não foi possível adicionar aos contatos</string>
<string name="vevent_add">Adicionar ao calendário</string>
<string name="vevent_failed">Não foi possível adicionar ao calendário</string>
@@ -141,7 +143,7 @@
<string name="search_web">Pesquisar na internet</string>
<string name="search_scan">Pesquisar em conteúdo já digitalizado</string>
<string name="export_to_file">Exportar para arquivo</string>
<string name="export_as">Exportar como?</string>
<string name="export_as">Exportar em qual formato?</string>
<string name="export_csv_comma">Arquivo CSV com vírgulas</string>
<string name="export_csv_semicolon">Arquivo CSV com ponto-e-vírgula</string>
<string name="export_json">Arquivo JSON</string>
+11 -9
View File
@@ -17,13 +17,15 @@
<string name="error_correction_level_m" formatted="false">Средний (≈15&#37; исправлений)</string>
<string name="error_correction_level_q" formatted="false">Квартиль (≈25&#37; исправлений)</string>
<string name="error_correction_level_h" formatted="false">Высокий (≈30&#37; исправлений)</string>
<string name="issue_number">Номер выпуска</string>
<string name="orientation">Ориентация</string>
<string name="other_meta_data">Метаданные</string>
<string name="pdf417_extra_metadata">Метаданные PDF417</string>
<string name="possible_country">Возможная страна изготовления</string>
<string name="suggested_price">Предположительная цена</string>
<string name="upc_ean_extension">Расширение UPC EAN</string>
<string name="sequence_size">Размер последовательности</string>
<string name="sequence_index">Индекс последовательности</string>
<string name="sequence_id">Идентификатор последовательности</string>
<string name="gtin_issue_number">Номер выпуска</string>
<string name="gtin_country">Возможная страна изготовления</string>
<string name="gtin_price">Предположительная цена</string>
<string name="gtin_add_on">Расширение UPC EAN</string>
<string name="barcode_version_number">Версия</string>
<string name="qr_version_and_modules">%1$d (модули: %2$d)</string>
<string name="toggle_flash">Включить вспышку</string>
<string name="error_flash">Ошибка при переключении вспышки</string>
<string name="share">Поделиться</string>
@@ -78,8 +80,8 @@
<string name="vibrate_summary">Включите эту настройку, если хотите, чтобы устройство вибрировало после распознавания штрих-кода.</string>
<string name="use_history">Сохранять историю сканирования</string>
<string name="use_history_summary">Сохранять распознанные штрих-коды на устройстве.</string>
<string name="ignore_consecutive_duplicates">Не хранить последовательные дубликаты</string>
<string name="ignore_consecutive_duplicates_summary">Не сохранять последовательные дубликаты в истории сканирования.</string>
<string name="ignore_consecutive_duplicates">Не хранить дубликаты</string>
<string name="ignore_consecutive_duplicates_summary">Не сохранять повторяющиеся штрих-коды в истории сканирования.</string>
<string name="open_immediately">Сразу открывать содержимое</string>
<string name="open_immediately_summary">Пропускать проверку и сразу открыть содержимое. Может привести к открытию небезопасного содержимого.</string>
<string name="copy_immediately">Автокопирование в буфер обмена</string>
+9 -7
View File
@@ -15,13 +15,15 @@
<string name="error_correction_level_m" formatted="false">Orta (~&#37;15 düzeltim)</string>
<string name="error_correction_level_q" formatted="false">Dörtte Bir (~&#37;25 düzeltim)</string>
<string name="error_correction_level_h" formatted="false">Yüksek (~&#37;30 düzeltim)</string>
<string name="issue_number">Basım sayısı</string>
<string name="orientation">Yönelim</string>
<string name="other_meta_data">Üst veri</string>
<string name="pdf417_extra_metadata">PDF417 üst verisi</string>
<string name="possible_country">Olası üretim yeri</string>
<string name="suggested_price">Önerilen fiyat</string>
<string name="upc_ean_extension">UPC EAN eklentisi</string>
<string name="sequence_size">Sequence size</string>
<string name="sequence_index">Sequence index</string>
<string name="sequence_id">Sequence ID</string>
<string name="gtin_issue_number">Basım sayısı</string>
<string name="gtin_country">Olası üretim yeri</string>
<string name="gtin_price">Önerilen fiyat</string>
<string name="gtin_add_on">UPC EAN eklentisi</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$d (%2$d modules)</string>
<string name="toggle_flash">Flaşı aç</string>
<string name="error_flash">Error toggling flash</string>
<string name="share">Paylaş</string>
+9 -7
View File
@@ -16,13 +16,15 @@
<string name="error_correction_level_m" formatted="false">Середній (~15&#37; виправлення)</string>
<string name="error_correction_level_q" formatted="false">Квартиль (~25&#37; виправлення)</string>
<string name="error_correction_level_h" formatted="false">Високий (~30&#37; виправлення)</string>
<string name="issue_number">Номер випуску</string>
<string name="orientation">Орієнтація</string>
<string name="other_meta_data">Метадані</string>
<string name="pdf417_extra_metadata">Метадані PDF417</string>
<string name="possible_country">Можлива країна-виробник</string>
<string name="suggested_price">Рекомендована ціна</string>
<string name="upc_ean_extension">Розширення UPC EAN</string>
<string name="sequence_size">Sequence size</string>
<string name="sequence_index">Sequence index</string>
<string name="sequence_id">Sequence ID</string>
<string name="gtin_issue_number">Номер випуску</string>
<string name="gtin_country">Можлива країна-виробник</string>
<string name="gtin_price">Рекомендована ціна</string>
<string name="gtin_add_on">Розширення UPC EAN</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$d (%2$d modules)</string>
<string name="toggle_flash">Увімкнути спалах</string>
<string name="error_flash">Error toggling flash</string>
<string name="share">Поділитися</string>
+9 -7
View File
@@ -14,13 +14,15 @@
<string name="error_correction_level_m" formatted="false">Trung bình (sửa ~15&#37;)</string>
<string name="error_correction_level_q" formatted="false">Một phần tư (sửa ~25&#37;)</string>
<string name="error_correction_level_h" formatted="false">Cao (sửa ~30&#37;)</string>
<string name="issue_number">Số</string>
<string name="orientation">Hướng</string>
<string name="other_meta_data">Dữ liệu</string>
<string name="pdf417_extra_metadata">Dữ liệu PDF417</string>
<string name="possible_country">Có thể được sản xuất ở nước</string>
<string name="suggested_price">Giá được gợi ý</string>
<string name="upc_ean_extension">Phần mở rộng UPC EAN</string>
<string name="sequence_size">Sequence size</string>
<string name="sequence_index">Sequence index</string>
<string name="sequence_id">Sequence ID</string>
<string name="gtin_issue_number">Số</string>
<string name="gtin_country">Có thể được sản xuất ở nước</string>
<string name="gtin_price">Giá được gợi ý</string>
<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$d (%2$d modules)</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>
+10 -8
View File
@@ -14,13 +14,15 @@
<string name="error_correction_level_m" formatted="false">Medium (~15&#37; 准确度)</string>
<string name="error_correction_level_q" formatted="false">Quartile (~25&#37; 准确度)</string>
<string name="error_correction_level_h" formatted="false">High (~30&#37; 准确度)</string>
<string name="issue_number">发行编号</string>
<string name="orientation">方向</string>
<string name="other_meta_data">元数据</string>
<string name="pdf417_extra_metadata">PDF417 元数据</string>
<string name="possible_country">可能产地</string>
<string name="suggested_price">建议价格</string>
<string name="upc_ean_extension">UPC EAN 扩展</string>
<string name="sequence_size">Sequence size</string>
<string name="sequence_index">Sequence index</string>
<string name="sequence_id">Sequence ID</string>
<string name="gtin_issue_number">发行编号</string>
<string name="gtin_country">可能产地</string>
<string name="gtin_price">建议价格</string>
<string name="gtin_add_on">UPC EAN 扩展</string>
<string name="barcode_version_number">版本</string>
<string name="qr_version_and_modules">%1$d (%2$d modules)</string>
<string name="toggle_flash">开关闪光灯</string>
<string name="error_flash">开关闪光灯出错</string>
<string name="share">分享</string>
@@ -45,7 +47,7 @@
<string name="bulk_mode">连续扫描</string>
<string name="bulk_mode_summary">始终开启连续扫描</string>
<string name="bulk_mode_delay">连续扫描之间的延迟</string>
<string name="restrict_format">受限格式</string>
<string name="restrict_format">严格模式(扫描指定编码格式)</string>
<string name="remove_restriction">解除限制</string>
<string name="scan_format">扫描 %s</string>
<string name="quarter_second">四分之一秒</string>
+9 -7
View File
@@ -14,13 +14,15 @@
<string name="error_correction_level_m" formatted="false">中 (~15&#37; 準確度)</string>
<string name="error_correction_level_q" formatted="false">Quartile (~25&#37; 準確度)</string>
<string name="error_correction_level_h" formatted="false">高 (~30&#37; 準確度)</string>
<string name="issue_number">發行編號</string>
<string name="orientation">方向</string>
<string name="other_meta_data">Metadata</string>
<string name="pdf417_extra_metadata">PDF417 metadata</string>
<string name="possible_country">可能製作國家</string>
<string name="suggested_price">建議價格</string>
<string name="upc_ean_extension">UPC EAN 擴充</string>
<string name="sequence_size">Sequence size</string>
<string name="sequence_index">Sequence index</string>
<string name="sequence_id">Sequence ID</string>
<string name="gtin_issue_number">發行編號</string>
<string name="gtin_country">可能製作國家</string>
<string name="gtin_price">建議價格</string>
<string name="gtin_add_on">UPC EAN 擴充</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$d (%2$d modules)</string>
<string name="toggle_flash">開關閃光燈</string>
<string name="error_flash">Error toggling flash</string>
<string name="share">分享</string>
+6 -6
View File
@@ -5,6 +5,8 @@
<item>CODE 39</item>
<item>CODE 93</item>
<item>CODE 128</item>
<item>DATA BAR</item>
<item>DATA BAR EXPANDED</item>
<item>DATA MATRIX</item>
<item>EAN 8</item>
<item>EAN 13</item>
@@ -12,11 +14,9 @@
<item>MAXICODE</item>
<item>PDF 417</item>
<item>QR CODE</item>
<item>RSS 14</item>
<item>RSS EXPANDED</item>
<item>MICRO QR CODE</item>
<item>UPC A</item>
<item>UPC E</item>
<item>UPC EAN EXTENSION</item>
</string-array>
<string-array name="barcode_formats_values">
<item>AZTEC</item>
@@ -24,6 +24,8 @@
<item>CODE_39</item>
<item>CODE_93</item>
<item>CODE_128</item>
<item>DATA_BAR</item>
<item>DATA_BAR_EXPANDED</item>
<item>DATA_MATRIX</item>
<item>EAN_8</item>
<item>EAN_13</item>
@@ -31,10 +33,8 @@
<item>MAXICODE</item>
<item>PDF_417</item>
<item>QR_CODE</item>
<item>RSS_14</item>
<item>RSS_EXPANDED</item>
<item>MICRO_QR_CODE</item>
<item>UPC_A</item>
<item>UPC_E</item>
<item>UPC_EAN_EXTENSION</item>
</string-array>
</resources>
+2 -1
View File
@@ -6,7 +6,8 @@
<color name="background_color">#111</color>
<color name="background_progress">#8000</color>
<color name="button_text">#fff</color>
<color name="selected_row">#222</color>
<color name="selected_row">#8666</color>
<color name="pressed_row">#8222</color>
<color name="separator">#333</color>
<color name="crop_bound">#fff</color>
<color name="dot">#80b6d46f</color>
+3 -1
View File
@@ -10,8 +10,9 @@
<item>Italian</item>
<item>Georgian</item>
<item>Dutch</item>
<item>Persian</item>
<item>Polski</item>
<item>Portuguese (Brazil)</item>
<item>Português do Brasil</item>
<item>Russian</item>
<item>Turkish</item>
<item>Ukrainian</item>
@@ -32,6 +33,7 @@
<item>it</item>
<item>ka</item>
<item>nl</item>
<item>fa</item>
<item>pl</item>
<item>pt-rBR</item>
<item>ru-rRU</item>
+10 -8
View File
@@ -15,13 +15,15 @@
<string name="error_correction_level_m" formatted="false">Medium (~15&#37; correction)</string>
<string name="error_correction_level_q" formatted="false">Quartile (~25&#37; correction)</string>
<string name="error_correction_level_h" formatted="false">High (~30&#37; correction)</string>
<string name="issue_number">Issue number</string>
<string name="orientation">Orientation</string>
<string name="other_meta_data">Metadata</string>
<string name="pdf417_extra_metadata">PDF417 metadata</string>
<string name="possible_country">Possible country of manufacture</string>
<string name="suggested_price">Suggested price</string>
<string name="upc_ean_extension">UPC EAN extension</string>
<string name="sequence_size">Sequence size</string>
<string name="sequence_index">Sequence index</string>
<string name="sequence_id">Sequence ID</string>
<string name="gtin_issue_number">Issue number</string>
<string name="gtin_country">Possible country of manufacture</string>
<string name="gtin_price">Suggested price</string>
<string name="gtin_add_on">UPC EAN extension</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$d (%2$d modules)</string>
<string name="toggle_flash">Toggle flash</string>
<string name="error_flash">Error toggling flash</string>
<string name="share">Share</string>
@@ -98,7 +100,7 @@
<string name="clear_network_suggestions_nothing_to_remove">Nothing to remove</string>
<string name="send_category">Forwarding</string>
<string name="send_scan_active">Forward scans</string>
<string name="send_scan_active_summary">Enable to forward scans to a the given URL</string>
<string name="send_scan_active_summary">Enable to forward scans to a the given URL.</string>
<string name="send_scan_url">URL to forward to</string>
<string name="send_scan_type">Request type</string>
<string name="send_type_get_add_content">GET and simply add content</string>
+119
View File
@@ -0,0 +1,119 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/preferences"
android:key="preferences">
<PreferenceCategory android:title="@string/scan_category">
<MultiSelectListPreference
android:key="barcode_formats"
android:title="@string/barcode_formats"
android:entries="@array/barcode_formats_names"
android:entryValues="@array/barcode_formats_values"/>
<android.support.v7.preference.SwitchPreferenceCompat
android:defaultValue="true"
android:key="show_crop_handle"
android:title="@string/show_crop_handle"
android:summary="@string/show_crop_handle_summary"/>
<android.support.v7.preference.SwitchPreferenceCompat
android:defaultValue="true"
android:key="zoom_by_swiping"
android:title="@string/zoom_by_swiping"
android:summary="@string/zoom_by_swiping_summary"/>
<android.support.v7.preference.SwitchPreferenceCompat
android:defaultValue="false"
android:key="auto_rotate"
android:title="@string/auto_rotate"
android:summary="@string/auto_rotate_summary"/>
<android.support.v7.preference.SwitchPreferenceCompat
android:defaultValue="false"
android:key="try_harder"
android:title="@string/try_harder"
android:summary="@string/try_harder_summary"/>
<android.support.v7.preference.SwitchPreferenceCompat
android:defaultValue="false"
android:key="bulk_mode"
android:title="@string/bulk_mode"
android:summary="@string/bulk_mode_summary"/>
<ListPreference
android:key="bulk_mode_delay"
android:title="@string/bulk_mode_delay"
android:entries="@array/bulk_mode_delays_names"
android:entryValues="@array/bulk_mode_delays_values"
android:defaultValue="500"/>
<android.support.v7.preference.SwitchPreferenceCompat
android:defaultValue="true"
android:key="show_toast_in_bulk_mode"
android:title="@string/show_toast_in_bulk_mode"
android:summary="@string/show_toast_in_bulk_mode_summary"/>
<android.support.v7.preference.SwitchPreferenceCompat
android:defaultValue="true"
android:key="vibrate"
android:title="@string/vibrate"
android:summary="@string/vibrate_summary"/>
<android.support.v7.preference.SwitchPreferenceCompat
android:defaultValue="true"
android:key="use_history"
android:title="@string/use_history"
android:summary="@string/use_history_summary"/>
<android.support.v7.preference.SwitchPreferenceCompat
android:defaultValue="true"
android:key="ignore_consecutive_duplicates"
android:title="@string/ignore_consecutive_duplicates"
android:summary="@string/ignore_consecutive_duplicates_summary"/>
<android.support.v7.preference.SwitchPreferenceCompat
android:defaultValue="false"
android:key="copy_immediately"
android:title="@string/copy_immediately"
android:summary="@string/copy_immediately_summary"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/send_category">
<android.support.v7.preference.SwitchPreferenceCompat
android:defaultValue="true"
android:key="send_scan_active"
android:title="@string/send_scan_active"
android:summary="@string/send_scan_active_summary"/>
<de.markusfisch.android.binaryeye.preference.UrlPreference
android:key="send_scan_url"
android:title="@string/send_scan_url"/>
<ListPreference
android:key="send_scan_type"
android:title="@string/send_scan_type"
android:entries="@array/send_type_names"
android:entryValues="@array/send_type_values"
android:defaultValue="0"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/content_category">
<android.support.v7.preference.SwitchPreferenceCompat
android:defaultValue="false"
android:key="open_immediately"
android:title="@string/open_immediately"
android:summary="@string/open_immediately_summary"/>
<android.support.v7.preference.SwitchPreferenceCompat
android:defaultValue="true"
android:key="show_meta_data"
android:title="@string/show_meta_data"
android:summary="@string/show_meta_data_summary"/>
<android.support.v7.preference.SwitchPreferenceCompat
android:defaultValue="true"
android:key="show_hex_dump"
android:title="@string/show_hex_dump"
android:summary="@string/show_hex_dump_summary"/>
<android.support.v7.preference.SwitchPreferenceCompat
android:defaultValue="true"
android:key="close_automatically"
android:title="@string/close_automatically"
android:summary="@string/close_automatically_summary"/>
<ListPreference
android:key="default_search_url"
android:title="@string/default_search_engine"
android:entries="@array/search_engines_names"
android:entryValues="@array/search_engines_values"
android:defaultValue=""/>
<de.markusfisch.android.binaryeye.preference.UrlPreference
android:key="open_with_url"
android:title="@string/open_with_url"/>
<Preference
android:key="clear_network_suggestions"
android:title="@string/clear_network_suggestions"
android:summary="@string/clear_network_suggestions_summary"/>
</PreferenceCategory>
</PreferenceScreen>
-40
View File
@@ -1,40 +0,0 @@
#pragma version(1)
#pragma rs java_package_name(de.markusfisch.android.binaryeye.rs)
#pragma rs reduce(analyze) initializer(analyzeInit) accumulator(analyzeAccumulator) combiner(analyzeCombiner) outconverter(analyzeConverter)
const static float3 relativeLuminance = {0.2126f, 0.7152f, 0.0722f};
typedef struct {
int transparent;
int visible;
int bright;
} Report;
static void analyzeInit(Report *report) {
report->transparent = 0;
report->visible = 0;
report->bright = 0;
}
static void analyzeAccumulator(Report *accum, uchar4 val) {
if (val.a < 255) {
accum->transparent = 1;
}
if (val.a > 0) {
++accum->visible;
float4 f4 = rsUnpackColor8888(val);
float3 result = dot(f4.rgb, relativeLuminance);
accum->bright += step(0.5f, result.r + result.g + result.b);
}
}
static void analyzeCombiner(Report *accum, const Report *val) {
accum->transparent += val->transparent;
accum->visible += val->visible;
accum->bright += val->bright;
}
static void analyzeConverter(int2 *result, const Report *report) {
result->x = report->transparent;
result->y = report->bright > 0 && report->visible / report->bright < 2;
}
-25
View File
@@ -1,25 +0,0 @@
#pragma version(1)
#pragma rs java_package_name(de.markusfisch.android.binaryeye.rs)
#pragma rs_fp_relaxed
rs_allocation inImage;
int inWidth;
int inHeight;
uchar RS_KERNEL rotate90(const uchar in, uint32_t x, uint32_t y) {
const uchar *out = rsGetElementAt(inImage, y, inHeight - 1 - x);
return *out;
}
uchar RS_KERNEL rotate180(const uchar in, uint32_t x, uint32_t y) {
const uchar *out = rsGetElementAt(
inImage,
inWidth - 1 - x,
inHeight - 1 - y);
return *out;
}
uchar RS_KERNEL rotate270(const uchar in, uint32_t x, uint32_t y) {
const uchar *out = rsGetElementAt(inImage, inWidth - 1 - y, x);
return *out;
}
@@ -0,0 +1,43 @@
package de.markusfisch.android.binaryeye.actions.mail
import junit.framework.TestCase.assertEquals
import org.junit.Test
class MatMsgTest {
@Test
fun to() {
val mm = MatMsg(
"MATMSG:TO:someone@example.org;;"
)
assertEquals(mm.to, "someone@example.org")
}
@Test
fun toSub() {
val mm = MatMsg(
"MATMSG:TO:someone@example.org;SUB:Stuff;;"
)
assertEquals(mm.to, "someone@example.org")
assertEquals(mm.sub, "Stuff")
}
@Test
fun toSubBody() {
val mm = MatMsg(
"MATMSG:TO:someone@example.org;SUB:Stuff;BODY:This is some text;;"
)
assertEquals(mm.to, "someone@example.org")
assertEquals(mm.sub, "Stuff")
assertEquals(mm.body, "This is some text")
}
@Test
fun differentOrder() {
val mm = MatMsg(
"MATMSG:SUB:Stuff;BODY:This is some text;TO:someone@example.org;;"
)
assertEquals(mm.to, "someone@example.org")
assertEquals(mm.sub, "Stuff")
assertEquals(mm.body, "This is some text")
}
}
@@ -1,7 +1,6 @@
package de.markusfisch.android.binaryeye.actions.vtype
import de.markusfisch.android.binaryeye.simpleFail
import junit.framework.TestCase.*
import junit.framework.TestCase.assertEquals
import org.junit.Test
class VTypeTest {
@@ -0,0 +1,74 @@
package de.markusfisch.android.binaryeye.actions.web
import junit.framework.TestCase.assertEquals
import org.junit.Test
class WebTest {
@Test
fun correct() {
val urls = listOf(
"https://google.com",
"https://markusfisch.de",
"http://markusfisch.de",
"http://www.markusfisch.de",
"http://www.markusfisch.de/",
"http://www.markusfisch.de/?",
"http://www.markusfisch.de/?foo=bar",
"https://markusfisch.de/foo",
"https://markusfisch.de/foo#bar",
"https://foo.example.com/",
"http://example.com/",
)
for (url in urls) {
assertEquals(url, resolve(url))
}
}
@Test
fun colloquial() {
val urls = listOf(
"google.com",
"GOOGLE.com",
"GOOGLE.COM",
" foo.com",
"foo.com ",
" foo.com ",
" foo.com ",
"foo.com",
"foo.com/bar",
"foo.com?bar",
"foo.com#bar",
)
for (url in urls) {
assertEquals(url, resolve(url))
}
}
@Test
fun noUrls() {
val urls = listOf(
"",
"foo",
"foo . bar",
"exa mple.com",
"example.co m",
"some string",
"some string.",
".foo",
"..foo",
"...foo",
"foo.",
"foo..",
"foo...",
"foo. foo",
)
for (url in urls) {
assertEquals(null, resolve(url))
}
}
}
private fun resolve(s: String) = if (
WebAction.canExecuteOn(s.toByteArray())
) s else null
+3 -3
View File
@@ -1,8 +1,8 @@
buildscript {
ext {
kotlin_version = '1.6.10'
tools_version = '7.1.2'
sdk_version = 32
kotlin_version = '1.7.10'
tools_version = '7.3.0'
sdk_version = 33
support_version = '25.3.1'
}
@@ -0,0 +1,3 @@
* Update design of app icon
* Update pt-br translation
@@ -0,0 +1,10 @@
* Add support for MATMSG format
* Add a setting to show the QR Code version
* Add timezone offset in calendar dates
* Recognize colloquial incomplete URLs
* Update ZXing library to latest version
* Update Czech translation
* Update Russian translation
* Update Japanese translation
* Update Hungarian translation
Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 11 KiB

@@ -0,0 +1,10 @@
* افزودن پشتیبانی از قالب MATMSG
* افزودن قابلیت نمایش نگارش کیوآرکد در تنظیمات
* Add timezone offset in calendar dates
* Recognize colloquial incomplete URLs
* به‌روز رسانی کتابخانهٔ ZXing به آخرین نگارش
* به‌روز رسانی ترجمهٔ چکوسلواکی
* به‌روز رسانی ترجمهٔ روسی
* به‌روز رسانی ترجمهٔ ژاپنی
* به‌روز رسانی ترجمهٔ مجارستانی
@@ -0,0 +1,11 @@
در هر دو جهت افقی و عمودی کار می‌کند، می‌تواند بارکدهای وارونه را بخواند،
از طراحی متریال پیروی می‌کند و همچنین قابلیت ایجاد بارکد را نیز داراست.
از کتابخانهٔ پویش بارکد ZXing ("Zebra Crossing") استفاده می‌کند.
قالب‌های بارکد پشتیبانی‌شده:
AZTEC, CODABAR, CODE 39, CODE 93, CODE 128, RSS 14,
DATA MATRIX, EAN 8, EAN 13, ITF, PDF417, QR CODE,
RSS EXPANDED, UPC A, UPC E, UPC EAN EXTENSION.
این کاره متن‌باز است:
https://github.com/markusfisch/BinaryEye
@@ -0,0 +1 @@
یک پویشگر بارکد دیگر برای اندروید. آزاد، بدون تبلیغات و متن‌باز.
+1
View File
@@ -0,0 +1 @@
یک پویشگر بارکد دیگر
+1
View File
@@ -0,0 +1 @@
باینری آی
@@ -0,0 +1,10 @@
Funziona con orientamento verticale e orizzontale, può leggere codici invertiti,
è in Material Design e può anche generare codici a barre.
Usa la libreria di scansione di codici a barre ZXing ("Zebra Crossing").
I formati di codici a barre supportati sono: AZTEC, CODABAR, CODE 39, CODE 93, CODE 128,
DATA MATRIX, EAN 8, EAN 13, ITF, PDF417, QR CODE, RSS 14, RSS EXPANDED,
UPC A, UPC E e UPC EAN EXTENSION.
Questa è open source:
https://github.com/markusfisch/BinaryEye
@@ -0,0 +1 @@
Ancora un altro lettore di codici a barre per Android. Gratuito, senza pubblicità e open source.

Some files were not shown because too many files have changed in this diff Show More