Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0d09e81b4 | ||
|
|
771258cb5c | ||
|
|
9482086666 | ||
|
|
bcad50c3e8 | ||
|
|
dbd055699e | ||
|
|
d67f8273d6 | ||
|
|
649be0ceb6 | ||
|
|
f5eb5dd8d2 | ||
|
|
eb805dad2f | ||
|
|
202bb32b29 | ||
|
|
50b8a4d329 | ||
|
|
fa15478fb4 | ||
|
|
20c2a5a8aa | ||
|
|
00b95678ed | ||
|
|
5df3517563 | ||
|
|
fd6946d2c2 | ||
|
|
c74d229e1f | ||
|
|
fa99a19db5 | ||
|
|
b083fcf213 | ||
|
|
46930ddea6 | ||
|
|
7bef77b96e | ||
|
|
c5f4c088b4 | ||
|
|
73f7210e18 | ||
|
|
469429e663 | ||
|
|
1a7ac59740 | ||
|
|
c13c59e728 | ||
|
|
bc17e25717 | ||
|
|
5017bd4c8d | ||
|
|
a44a913eca | ||
|
|
007e83e243 | ||
|
|
de8f97c246 | ||
|
|
1287591e53 | ||
|
|
8c17238572 | ||
|
|
5dbb36e6a9 | ||
|
|
ca7dfb3a33 | ||
|
|
419b7d5765 | ||
|
|
fa38866669 | ||
|
|
19ca521f59 | ||
|
|
be657d86cf | ||
|
|
5199a4a526 | ||
|
|
ff48a543c9 | ||
|
|
2eadd01a87 | ||
|
|
dd2abe7d89 | ||
|
|
8e05caac70 | ||
|
|
46aeced09f | ||
|
|
3681c5d011 | ||
|
|
20b4f7aac5 | ||
|
|
bc6e350271 | ||
|
|
c9c1b2f5bf | ||
|
|
1dbc0f0fa1 | ||
|
|
ab8170ee72 | ||
|
|
551464dc6f | ||
|
|
c3aac0a1e1 | ||
|
|
d678e1cfd0 | ||
|
|
a445ade877 | ||
|
|
1fd0b7637f | ||
|
|
fdc88df897 | ||
|
|
d0f133ae00 | ||
|
|
67646261e7 | ||
|
|
faeae9e904 | ||
|
|
77b21a9e83 | ||
|
|
cbe7860a3d | ||
|
|
fc29267d3d | ||
|
|
890506328b | ||
|
|
04036b6cd6 | ||
|
|
2602021c1c | ||
|
|
17a0bdc0a9 | ||
|
|
d71a220eca | ||
|
|
0c2e711d79 | ||
|
|
ca342a83d3 | ||
|
|
3c44ff56eb | ||
|
|
bcae64c0d0 | ||
|
|
c2f673729b | ||
|
|
2043e0f349 | ||
|
|
b93eb5963e | ||
|
|
5189fc9c5a | ||
|
|
2d311ff92a | ||
|
|
14bb08003d | ||
|
|
42f9887030 | ||
|
|
a59d241b50 | ||
|
|
9f31160c7b | ||
|
|
2eb4e51c17 | ||
|
|
53a554e7a2 | ||
|
|
7f6887716a | ||
|
|
5ef93802ce | ||
|
|
c1eff08bb9 | ||
|
|
8798d44ec7 | ||
|
|
d4e31ff97e | ||
|
|
ae8b08a85a | ||
|
|
09430d776e | ||
|
|
ef88c3179d | ||
|
|
c732ec726b | ||
|
|
801c768df4 | ||
|
|
552a2f6d57 | ||
|
|
7fa8810197 | ||
|
|
254af25bbb | ||
|
|
a27656c174 | ||
|
|
2ca55ebccc | ||
|
|
ec1d40942d | ||
|
|
7852e5ce9d | ||
|
|
f36a81d4a0 | ||
|
|
d86ea4073c | ||
|
|
a69530621d | ||
|
|
3c0f29a768 | ||
|
|
ccf5157fa7 | ||
|
|
c1ebb1c154 | ||
|
|
a4778bcc02 | ||
|
|
24d2c6d8d6 | ||
|
|
0f3a07f602 | ||
|
|
751d8932f8 | ||
|
|
9c2d063c24 | ||
|
|
0f9bbf5b2f | ||
|
|
530280981e | ||
|
|
ddfbb305c7 | ||
|
|
8d1a29922b | ||
|
|
c710f7c6ab | ||
|
|
111c587eef | ||
|
|
264e494487 | ||
|
|
8135b2a3dc | ||
|
|
dce62b70d3 | ||
|
|
533812c638 | ||
|
|
5dc5bed281 | ||
|
|
2a13561962 | ||
|
|
1476f41cda | ||
|
|
0da1ffbe02 | ||
|
|
980679b6a3 | ||
|
|
28cbb55299 | ||
|
|
1a4e7d100a | ||
|
|
265e43b439 | ||
|
|
03a58c3412 | ||
|
|
327cad48f0 | ||
|
|
8e64fe2f56 | ||
|
|
368d4dbb66 | ||
|
|
c8c3c094db | ||
|
|
aa4aefe35f | ||
|
|
536b3d77c1 | ||
|
|
bbf2a80981 | ||
|
|
ae99214d6c | ||
|
|
73b5a0969c | ||
|
|
bf5dc96ac9 | ||
|
|
301d9c2513 | ||
|
|
0a11611b8f | ||
|
|
249e310e43 | ||
|
|
6279c5731a | ||
|
|
df98637951 | ||
|
|
08b3558e46 | ||
|
|
648ef6fdf8 | ||
|
|
807f075a51 | ||
|
|
27789beeed | ||
|
|
97bee27827 | ||
|
|
3fee49301e | ||
|
|
8a6073291b | ||
|
|
47151de4a4 | ||
|
|
248c4edca1 | ||
|
|
2c7386d536 | ||
|
|
e19cad4c87 | ||
|
|
585e0f3372 | ||
|
|
2b7f611774 | ||
|
|
05b2a0d9c9 | ||
|
|
e7287eb088 | ||
|
|
4b45ad3fe7 | ||
|
|
449d8fdbbd | ||
|
|
f44cff9912 | ||
|
|
24730dfde3 | ||
|
|
be6539d41f | ||
|
|
60dd40554e | ||
|
|
837119b6ec | ||
|
|
abe11b82e5 | ||
|
|
4fc1e36d04 | ||
|
|
54aa15e1be | ||
|
|
876d8fc7b3 | ||
|
|
7bdcdaaa5d | ||
|
|
fd44728e89 | ||
|
|
8242520a0b | ||
|
|
2e7e9a942b | ||
|
|
dcc9d5b3ff | ||
|
|
607aab854c | ||
|
|
cc2b2f076b | ||
|
|
90e9723de5 | ||
|
|
ab6787e60b | ||
|
|
698686a45a | ||
|
|
95bdaa1252 | ||
|
|
f0c02e5156 | ||
|
|
a8c50308d0 | ||
|
|
6b359d4c65 | ||
|
|
863caed387 | ||
|
|
e4f17b9bdf | ||
|
|
c2071d8d81 | ||
|
|
4758c2d337 | ||
|
|
91336fe15c | ||
|
|
2c437f3771 | ||
|
|
cb082b20fe | ||
|
|
bce3630686 | ||
|
|
4feb4d066c | ||
|
|
e7d84cd916 | ||
|
|
84e2917694 | ||
|
|
b87464ec40 | ||
|
|
269e0f41a6 | ||
|
|
aefbc4a296 | ||
|
|
c64984dc6a | ||
|
|
24c7ad0ba4 | ||
|
|
643a935852 | ||
|
|
4fcb836b53 | ||
|
|
48ed5f1a2c | ||
|
|
3aa2371eeb | ||
|
|
88f92943c0 | ||
|
|
39710d4ab9 | ||
|
|
93f7cd3cb8 | ||
|
|
69baa3b824 | ||
|
|
bb05e38278 | ||
|
|
554f5a5e73 | ||
|
|
2dcf7772f2 | ||
|
|
3c7012e3c4 | ||
|
|
08e11c3c40 | ||
|
|
31854dab0c | ||
|
|
4f12c3e270 | ||
|
|
3ec43dabce | ||
|
|
0d4f536f71 | ||
|
|
b064a293fb | ||
|
|
d124c29919 | ||
|
|
2e89f1530a | ||
|
|
ef7e2a4b0d | ||
|
|
f680533819 | ||
|
|
a8c977c40e | ||
|
|
1e89b22953 | ||
|
|
11f028e0f3 | ||
|
|
1f7f4a23b5 | ||
|
|
41581d07ec | ||
|
|
dc819a6e1a | ||
|
|
5b51072f1a | ||
|
|
1b10e82cb2 | ||
|
|
eeae1eb0ad | ||
|
|
21c29ca373 | ||
|
|
8bcb72e91b | ||
|
|
8f345fbfbe | ||
|
|
2c88dbca02 | ||
|
|
bd1c629c85 | ||
|
|
f89e6612c4 | ||
|
|
7a0cd0ce19 | ||
|
|
8544affa1e | ||
|
|
48d3588341 | ||
|
|
783f6e4892 | ||
|
|
032cd64daf | ||
|
|
96626e6b00 | ||
|
|
11ae65ec3e | ||
|
|
91e3acb647 | ||
|
|
40c2f5947b | ||
|
|
58b9466cfc | ||
|
|
12210e1768 | ||
|
|
ad2ac457c9 | ||
|
|
634b6e8d2b | ||
|
|
6dd47f4dd0 | ||
|
|
8426f66cfb | ||
|
|
3dfa84a33e | ||
|
|
38f658e253 | ||
|
|
287d99e2b4 | ||
|
|
5cfcde9d4a | ||
|
|
b2730259fc | ||
|
|
61a2d3023c | ||
|
|
239ac7bd1a | ||
|
|
4048314cf7 | ||
|
|
3dbd772847 | ||
|
|
3a08465c35 | ||
|
|
92e3669ee5 | ||
|
|
8b297f4768 | ||
|
|
42b7f1179b | ||
|
|
9d6854c282 | ||
|
|
d943b7deff | ||
|
|
a138da9d9b | ||
|
|
194338a0d2 | ||
|
|
7eb8fdcce1 | ||
|
|
2bf1796340 | ||
|
|
cc42015da2 | ||
|
|
648c39c439 | ||
|
|
8c25b05cda | ||
|
|
780bebc9d1 | ||
|
|
bf7eb7efd5 | ||
|
|
d00d08bae3 | ||
|
|
379256a7fe | ||
|
|
180339269f | ||
|
|
49af17cba8 | ||
|
|
a03a187d2d | ||
|
|
84806e70d7 | ||
|
|
058de5a141 | ||
|
|
d330fbd195 | ||
|
|
f5bc6f664f | ||
|
|
464279807c | ||
|
|
f7e5ef473a | ||
|
|
ff733ef59e | ||
|
|
35b110a472 | ||
|
|
58eabf4754 | ||
|
|
1f273a48a5 | ||
|
|
785a64725d | ||
|
|
7a42e1de73 | ||
|
|
9856ab7b6b | ||
|
|
746ff143d8 | ||
|
|
b0d4d94973 | ||
|
|
c604ad5ef2 | ||
|
|
91c2b324ac | ||
|
|
df59fd7446 | ||
|
|
449fd1f26c | ||
|
|
72d839c6e2 | ||
|
|
ab8c993a81 | ||
|
|
5f5bd913d6 | ||
|
|
92fe4dd55f | ||
|
|
dab95176f0 | ||
|
|
650503ba70 | ||
|
|
61078f214e | ||
|
|
a4b639edfe | ||
|
|
f37878dbb8 | ||
|
|
10fde7c3d6 | ||
|
|
a098fcb48c | ||
|
|
f162977640 | ||
|
|
66cab58d7b | ||
|
|
e99e2d7dcc | ||
|
|
9045a27a5d | ||
|
|
9e1208c72b | ||
|
|
90410f232a | ||
|
|
6769fa7e37 | ||
|
|
72444f5081 | ||
|
|
9a3ca4ffa3 | ||
|
|
f572923720 |
+161
@@ -1,5 +1,166 @@
|
||||
# Change Log
|
||||
|
||||
## 1.66.2
|
||||
* Remove forcing `?content=` on forwarding URL
|
||||
* Update Spanish translation
|
||||
|
||||
## 1.66.1
|
||||
* Show elements of invalid Wifi configurations too
|
||||
* Update translations
|
||||
|
||||
## 1.66.0
|
||||
* Support deep links to encode content via web links
|
||||
* Parse International Driver Licenses (IDL) in PDF417 barcodes
|
||||
* Add WiFis from system settings
|
||||
* Show parsed data items
|
||||
* Update translations
|
||||
|
||||
## 1.65.2
|
||||
* Sanity check ROI metrics
|
||||
|
||||
## 1.65.1
|
||||
* Improve simple mode
|
||||
* Remember quiet zone settings
|
||||
* Reduce height for 1D barcodes
|
||||
* Add execute argument for binaryeye://encode
|
||||
* Update Russian translation
|
||||
|
||||
## 1.65.0
|
||||
* Add a welcome screen to choose a mode
|
||||
* Add a deep link to encode a barcode
|
||||
* Remember zoom level of generated barcodes
|
||||
* Remove Human Readable Interpretation (HRI) decoration
|
||||
* Update Italian translation
|
||||
|
||||
## 1.64.2
|
||||
* Separate crop window settings for image scanning
|
||||
* Fix initializing dataMask
|
||||
* Update Russian translation
|
||||
|
||||
## 1.64.1
|
||||
* Handle empty input for recreation
|
||||
* Fix setting up database
|
||||
|
||||
## 1.64.0
|
||||
* Find barcode by content
|
||||
* Immediately replace toasts
|
||||
* Use detected barcode for recreation
|
||||
* Show data mask of QR Codes
|
||||
* Fix deep linking
|
||||
* Replace margin with quiet zone switch when generating barcodes
|
||||
* Update Russian translation
|
||||
* Update Portuguese (Brazil) translation
|
||||
|
||||
## 1.63.13
|
||||
* Fix UTF-8 conversion of binary escape sequences when creating barcodes
|
||||
* Fix scan order for intervals under one second
|
||||
* Add arabic translation
|
||||
* Update French translation
|
||||
|
||||
## 1.63.12
|
||||
* Fix magnifying barcode recreation
|
||||
|
||||
## 1.63.11
|
||||
* Update parcel tracking URL
|
||||
* Update multiple translations
|
||||
* Only ask for barcode size when exporting/sharing
|
||||
|
||||
## 1.63.10
|
||||
* Use maximum brightness when showing a generated barcode
|
||||
* Allow file input from share menu
|
||||
* Update multiple translations
|
||||
|
||||
## 1.63.9
|
||||
* Export/share as JPEG
|
||||
* Ignore "URL:" before URLs in barcode content
|
||||
* Fix exporting/sharing multiple history items
|
||||
* Update multiple translations
|
||||
|
||||
## 1.63.8
|
||||
* Add an option to show a checksum for the decoded content
|
||||
* Add support for encoding escape sequences for non-printable characters
|
||||
* Update Italian translation
|
||||
|
||||
## 1.63.7
|
||||
* Update ZXingCpp for Aztec Runes
|
||||
* Update Italian translation
|
||||
|
||||
## 1.63.6
|
||||
* Add history shortcut
|
||||
* Return Codabar start/stop codes
|
||||
* Update French, Russian, Spanish and Ukrainian translations
|
||||
|
||||
## 1.63.5
|
||||
* Add an option to ignore any duplicates
|
||||
* Fix invalid bluetooth connection
|
||||
* Update Russian translation
|
||||
* Update Ukrainian translation
|
||||
|
||||
## 1.63.4
|
||||
* Update Chinese translation
|
||||
* Improve support for old devices
|
||||
|
||||
## 1.63.3
|
||||
* Fix SCAN Intent
|
||||
|
||||
## 1.63.2
|
||||
* Add DX Film Edge barcode format
|
||||
* Update Turkish translation
|
||||
* Update Ukrainian translation
|
||||
|
||||
## 1.63.1
|
||||
* Include scan label in exports/shares
|
||||
* Update Russian translation
|
||||
|
||||
## 1.63.0
|
||||
* Edit scan label in decoding screen
|
||||
* Add a button to add generated codes to the history
|
||||
* Improve scanning from images
|
||||
* Improve asking for camera permission
|
||||
* Fix margin for recreated barcodes
|
||||
* Limit duration of error beep to 1s
|
||||
* Update Ukrainian translation
|
||||
* Update Spanish translation
|
||||
|
||||
## 1.62.3
|
||||
* Extend selection with single taps in history listing
|
||||
* Update Brazilian Portuguese translation
|
||||
|
||||
## 1.62.2
|
||||
* Include rMQR Code for updates
|
||||
|
||||
## 1.62.1
|
||||
* Make rMQR Code selectable
|
||||
|
||||
## 1.62.0
|
||||
* Add support for rMQR (rectangular Micro QR) Code
|
||||
* Share history as a file
|
||||
* Only share selected scans from history
|
||||
|
||||
## 1.61.2
|
||||
* Fix quick settings tile for Android 14
|
||||
* Fix Xiaomi MIUI lockscreen barcode shortcut
|
||||
* Update French translation
|
||||
* Update Czech translation
|
||||
* Update Russian translation
|
||||
|
||||
## 1.61.1
|
||||
* Share scanned contents as a file
|
||||
* Recreate binary barcodes too
|
||||
* Add Bulgarian translation and Brave search engine
|
||||
* Update Russian translation
|
||||
* Fix general format restriction from preferences
|
||||
|
||||
## 1.61.0
|
||||
* Support encoding binary data
|
||||
* Add margin parameter for encoding
|
||||
* Add copying binary data as hex dump
|
||||
* Fix parsing TYPE argument in VCARDs
|
||||
* Update Russian translation
|
||||
* Update Brazilian Portuguese translation
|
||||
* Update Ukrainian translation
|
||||
* Update Chinese translation
|
||||
|
||||
## 1.60.3
|
||||
* Fix saving custom language
|
||||
* Update Brazilian Portuguese translation
|
||||
|
||||
@@ -16,5 +16,10 @@ to squash when merging into master:
|
||||
Then write a [good commit message][commit_messages] to keep the history
|
||||
meaningful and useful. One feature, one commit.
|
||||
|
||||
## Translations
|
||||
|
||||
For translations use [Toolate][toolate].
|
||||
|
||||
[android_best_practices]: https://developer.android.com/distribute/best-practices/develop/
|
||||
[commit_messages]: https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#_commit_guidelines
|
||||
[toolate]: https://toolate.othing.xyz/projects/binaryeye/
|
||||
|
||||
@@ -28,11 +28,36 @@ testscan:
|
||||
-c android.intent.category.BROWSABLE \
|
||||
-d 'binaryeye://scan/?ret=http%3A%2F%2Fmarkusfisch.de%2F%3Fresult%3D{RESULT}'
|
||||
|
||||
testencode:
|
||||
adb shell am start -a android.intent.action.VIEW \
|
||||
-c android.intent.category.BROWSABLE \
|
||||
-d 'binaryeye://encode?content=Test\&format=QR_CODE'
|
||||
|
||||
testencodenotqr:
|
||||
adb shell am start -a android.intent.action.VIEW \
|
||||
-c android.intent.category.BROWSABLE \
|
||||
-d 'binaryeye://encode?content=Test\&format=DATA_MATRIX'
|
||||
|
||||
testencodeexec:
|
||||
adb shell am start -a android.intent.action.VIEW \
|
||||
-c android.intent.category.BROWSABLE \
|
||||
-d 'binaryeye://encode?content=Test\&format=QR_CODE\&execute'
|
||||
|
||||
testencodeurl:
|
||||
adb shell am start -a android.intent.action.VIEW \
|
||||
-c android.intent.category.BROWSABLE \
|
||||
-d 'http://markusfisch.de/encode?content=Test\&format=QR_CODE'
|
||||
|
||||
testurl:
|
||||
adb shell am start -a android.intent.action.VIEW \
|
||||
-c android.intent.category.BROWSABLE \
|
||||
-d 'http://markusfisch.de/BinaryEye?ret=http%3A%2F%2Fmarkusfisch.de%2F%3Fresult%3D{RESULT}'
|
||||
|
||||
testxiaomi:
|
||||
adb shell am start -a android.intent.action.VIEW \
|
||||
-c android.intent.category.BROWSABLE \
|
||||
-d 'market://details?id=com.xiaomi.scanner'
|
||||
|
||||
install:
|
||||
adb $(TARGET) install -r app/build/outputs/apk/debug/app-debug.apk
|
||||
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ Before Android Q, this feature requires using
|
||||
[WifiManager.getConfiguredNetworks][getConfiguredNetworks]
|
||||
which requires `ACCESS_FINE_LOCATION`.
|
||||
|
||||
On Android Q an better, this permission is not requested nor required.
|
||||
On Android Q and better, this permission is not requested nor required.
|
||||
|
||||
### [BLUETOOTH][BLUETOOTH], [BLUETOOTH_ADMIN][BLUETOOTH_ADMIN] and [BLUETOOTH_CONNECT][BLUETOOTH_CONNECT]
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Yet another barcode scanner for Android. As if there weren't [enough][play].
|
||||
|
||||
This one is free, without any ads and open source.
|
||||
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.
|
||||
@@ -10,6 +10,23 @@ comes in Material Design and can also generate barcodes.
|
||||
Binary Eye uses the [ZXing-C++][zxing_cpp] ("Zebra Crossing") barcode
|
||||
scanning library.
|
||||
|
||||
If you find this app useful and wish to support its continued development,
|
||||
you can [buy me a coffee](https://www.buymeacoffee.com/markusfisch) or
|
||||
send some Bitcoin decimals to `bc1q2guk2rpll587aymrfadkdtpq32448x5khk5j8z`.
|
||||
|
||||
<a href="https://www.buymeacoffee.com/markusfisch" target="_blank"><img
|
||||
src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png"
|
||||
alt="Buy Me A Coffee"
|
||||
style="height: 60px !important;width: 217px !important;"/></a> <a
|
||||
href="https://liberapay.com/markusfisch/" target="_blank"><img
|
||||
src="https://liberapay.com/assets/widgets/donate.svg"
|
||||
alt="Liberapay"
|
||||
style="height: 60px !important"/></a> <a
|
||||
href="https://ko-fi.com/markusfisch" target="_blank"><img
|
||||
src="https://storage.ko-fi.com/cdn/brandasset/kofi_s_tag_dark.png"
|
||||
alt="Ko-fi"
|
||||
style="height: 60px !important"/></a>
|
||||
|
||||
## Screenshots
|
||||
|
||||
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/screencap-scanning.png"
|
||||
@@ -36,6 +53,7 @@ ZXing can read the following barcode formats:
|
||||
* [CODE 93][code_93]
|
||||
* [CODE 128][code_128]
|
||||
* [DATA MATRIX][data_matrix]
|
||||
* [DX FILM EDGE][dx_film_edge]
|
||||
* [EAN 8][ean_8]
|
||||
* [EAN 13][ean_13]
|
||||
* [ITF][itf]
|
||||
@@ -43,6 +61,7 @@ ZXing can read the following barcode formats:
|
||||
* [PDF417][pdf417]
|
||||
* [QR CODE][qr_code]
|
||||
* [Micro QR Code][micro_qr_code]
|
||||
* [rMQR Code][rmqr_code]
|
||||
* [RSS 14][rss]
|
||||
* [RSS EXPANDED][rss]
|
||||
* [UPC A][upc_a]
|
||||
@@ -64,14 +83,14 @@ ZXing can generate the following barcode formats:
|
||||
* [QR CODE][qr_code]
|
||||
* [UPC A][upc_a]
|
||||
|
||||
## Deep Links and Intents
|
||||
|
||||
### Deep Links
|
||||
## Deep Links
|
||||
|
||||
You can invoke Binary Eye with a web URI intent from anything that can
|
||||
open URIs. There are two options:
|
||||
open URIs.
|
||||
|
||||
1. [binaryeye://scan](binaryeye://scan)
|
||||
### Decoding
|
||||
|
||||
1. [binaryeye://scan](binaryeye://scan) (note: GitHub does not render this as clickable link)
|
||||
2. [http(s)://markusfisch.de/BinaryEye](http://markusfisch.de/BinaryEye)
|
||||
|
||||
If you want to get the scanned contents, you can add a `ret` query
|
||||
@@ -85,10 +104,31 @@ Supported symbols are:
|
||||
* `RESULT_BYTES` - raw result as a hex string
|
||||
* `FORMAT` - barcode format
|
||||
|
||||
### Encoding
|
||||
|
||||
1. [binaryeye://encode](binaryeye://encode)
|
||||
|
||||
You can use the URL arguments `content` and `format` to automatically
|
||||
preset this data. For example:
|
||||
|
||||
1. [binaryeye://encode?content=Test&format=QR_CODE](binaryeye://encode?content=Test&format=QR_CODE)
|
||||
2. [http(s)://markusfisch.de/encode?content=Test&format=QR_CODE](http://markusfisch.de/encode?content=Test&format=QR_CODE)
|
||||
3. [http(s)://markusfisch.de/encode?content=Test&format=DATA_MATRIX](http://markusfisch.de/encode?content=Test2&format=DATA_MATRIX&execute)
|
||||
|
||||
If you want the code to be generated immediately, add `execute=true` (or just `execute`).
|
||||
|
||||
## Intents
|
||||
|
||||
You can also use Binary Eye from other apps by using an [Intent][intent].
|
||||
|
||||
If you prefer to integrate a barcode scanner into your app, take a look at
|
||||
at [BarcodeScannerView][barcode_scanner_view] (if you also want to use
|
||||
[ZXing-C++][zxing_cpp]) or read how to
|
||||
[scan barcodes with ML Kit on Android][mlkit_barcode_scanning].
|
||||
|
||||
### SCAN Intent
|
||||
|
||||
You can also use Binary Eye from other apps by using the
|
||||
`com.google.zxing.client.android.SCAN` Intent with
|
||||
Use the `com.google.zxing.client.android.SCAN` `Intent` with
|
||||
[startActivityForResult()][start_activity] like this:
|
||||
|
||||
```kotlin
|
||||
@@ -120,10 +160,12 @@ 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")
|
||||
class YourActivity : AppCompatActivity() {
|
||||
private val resultLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
val content = result.data?.getStringExtra("SCAN_RESULT")
|
||||
…
|
||||
}
|
||||
}
|
||||
@@ -134,6 +176,21 @@ class YourActivity : Activity() {
|
||||
}
|
||||
```
|
||||
|
||||
## Forwarding scans via Bluetooth
|
||||
|
||||
**Note:** The companion apps aren't developed nor maintained by the
|
||||
author of Binary Eye.
|
||||
|
||||
In order to set up Bluetooth forwarding to a computer:
|
||||
|
||||
1. Pair your devices (phone and pc) if not paired already
|
||||
2. Download and run a companion app:
|
||||
- Windows: https://github.com/KamaleiZestri/BinaryReceptorWindows
|
||||
- Linux: https://github.com/sean666888/bin_eye_bt_receiver
|
||||
3. In the settings of Binary Eye, enable "Forward scans with Bluetooth"
|
||||
and select the appropriate target host device that is running the
|
||||
companion application.
|
||||
|
||||
[play]: https://play.google.com/store/search?q=barcode%20scanner&c=apps
|
||||
[zxing_cpp]: https://github.com/zxing-cpp/zxing-cpp
|
||||
[kotlin]: http://kotlinlang.org/
|
||||
@@ -143,6 +200,7 @@ class YourActivity : Activity() {
|
||||
[code_93]: https://en.wikipedia.org/wiki/Code_93
|
||||
[code_128]: https://en.wikipedia.org/wiki/Code_128
|
||||
[data_matrix]: https://en.wikipedia.org/wiki/Data_Matrix
|
||||
[dx_film_edge]: https://en.wikipedia.org/wiki/DX_encoding
|
||||
[ean_8]: https://en.wikipedia.org/wiki/EAN-8
|
||||
[ean_13]: https://en.wikipedia.org/wiki/International_Article_Number
|
||||
[itf]: https://en.wikipedia.org/wiki/Interleaved_2_of_5
|
||||
@@ -150,10 +208,14 @@ class YourActivity : Activity() {
|
||||
[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
|
||||
[rmqr_code]: https://www.qrcode.com/en/codes/rmqr.html
|
||||
[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
|
||||
[intent]: https://developer.android.com/reference/android/content/Intent
|
||||
[barcode_scanner_view]: https://github.com/markusfisch/BarcodeScannerView
|
||||
[mlkit_barcode_scanning]: https://developers.google.com/ml-kit/vision/barcode-scanning/android
|
||||
[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
|
||||
|
||||
+33
-12
@@ -2,14 +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
|
||||
namespace "de.markusfisch.android.binaryeye"
|
||||
compileSdk sdk_version
|
||||
|
||||
versionCode 120
|
||||
versionName '1.60.3'
|
||||
defaultConfig {
|
||||
minSdk 9
|
||||
targetSdk sdk_version
|
||||
|
||||
versionCode 150
|
||||
versionName '1.66.2'
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
@@ -41,6 +42,10 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
buildConfig true
|
||||
}
|
||||
|
||||
bundle {
|
||||
language {
|
||||
// To make the app bundle contain all language resources
|
||||
@@ -52,18 +57,34 @@ android {
|
||||
enableSplit = false
|
||||
}
|
||||
}
|
||||
|
||||
// Required since Gradle 8.
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
|
||||
// Kotlin
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0"
|
||||
|
||||
// Support
|
||||
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.github.markusfisch:CameraView:1.9.1'
|
||||
|
||||
// External
|
||||
implementation 'com.github.markusfisch:CameraView:1.10.0'
|
||||
implementation 'com.github.markusfisch:ScalingImageView:1.4.1'
|
||||
implementation 'com.github.markusfisch:zxing-cpp:v2.0.0.1'
|
||||
implementation 'com.github.markusfisch:zxing-cpp:v2.3.0.3'
|
||||
|
||||
// Testing
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.robolectric:robolectric:4.14'
|
||||
}
|
||||
|
||||
Vendored
-2
@@ -3,5 +3,3 @@
|
||||
-dontwarn android.support.v8.**
|
||||
|
||||
-keep class android.support.v7.widget.SearchView { *; }
|
||||
|
||||
-keep class de.markusfisch.android.zxingcpp.** { *; }
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
<uses-feature android:name="android.hardware.camera"/>
|
||||
<uses-feature android:name="android.hardware.camera.autofocus"/>
|
||||
<uses-feature android:name="android.hardware.camera.autofocus"
|
||||
android:required="false" />
|
||||
<uses-sdk tools:overrideLibrary="android.support.v14.preference"/>
|
||||
<queries>
|
||||
<intent>
|
||||
@@ -62,12 +63,28 @@
|
||||
<intent-filter android:label="@string/compose_barcode">
|
||||
<action android:name="android.intent.action.SEND"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:mimeType="text/plain"/>
|
||||
<data android:mimeType="text/*"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="com.google.zxing.client.android.SCAN"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:host="details" android:scheme="market"
|
||||
android:pathSuffix="id=com.xiaomi.scanner"/>
|
||||
</intent-filter>
|
||||
<intent-filter android:autoVerify="true">
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:host="markusfisch.de"/>
|
||||
<data android:pathPrefix="/BinaryEye"/>
|
||||
<data android:scheme="http"/>
|
||||
<data android:scheme="https"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
@@ -92,14 +109,34 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.MainActivity"
|
||||
android:name=".activity.WelcomeActivity"
|
||||
android:exported="true"/>
|
||||
<activity
|
||||
android:name=".activity.MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter android:autoVerify="true">
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:host="markusfisch.de"/>
|
||||
<data android:pathPrefix="/encode"/>
|
||||
<data android:scheme="http"/>
|
||||
<data android:scheme="https"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="binaryeye" android:host="encode"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<service
|
||||
android:name=".service.ScanTileService"
|
||||
android:icon="@drawable/ic_action_scan"
|
||||
android:label="@string/scan_code"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
|
||||
tools:targetApi="n">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE"/>
|
||||
</intent-filter>
|
||||
|
||||
+44
-44
@@ -15,49 +15,49 @@ object OpenOrSearchAction : IAction {
|
||||
override fun canExecuteOn(data: ByteArray): Boolean = false
|
||||
|
||||
override suspend fun execute(context: Context, data: ByteArray) {
|
||||
view(context, String(data), true)
|
||||
}
|
||||
|
||||
private suspend fun view(
|
||||
context: Context,
|
||||
url: String,
|
||||
search: Boolean
|
||||
) {
|
||||
if (!context.openUrl(url, silent = true) && search) {
|
||||
openSearch(context, url)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun openSearch(context: Context, query: String) {
|
||||
val defaultSearchUrl = prefs.defaultSearchUrl
|
||||
if (defaultSearchUrl.isNotEmpty()) {
|
||||
view(
|
||||
context,
|
||||
defaultSearchUrl + query.urlEncode(),
|
||||
false
|
||||
)
|
||||
return
|
||||
}
|
||||
val names = context.resources.getStringArray(
|
||||
R.array.search_engines_names
|
||||
).toMutableList()
|
||||
val urls = context.resources.getStringArray(
|
||||
R.array.search_engines_values
|
||||
).toMutableList()
|
||||
// Remove the "Always ask" entry. The arrays search_engines_*
|
||||
// are used in the preferences too.
|
||||
names.removeFirst()
|
||||
urls.removeFirst()
|
||||
if (prefs.openWithUrl.isNotEmpty()) {
|
||||
names.add(prefs.openWithUrl)
|
||||
urls.add(prefs.openWithUrl)
|
||||
}
|
||||
val queryUri = alertDialog<String>(context) { resume ->
|
||||
setTitle(R.string.pick_search_engine)
|
||||
setItems(names.toTypedArray()) { _, which ->
|
||||
resume(urls[which] + query.urlEncode())
|
||||
}
|
||||
} ?: return
|
||||
view(context, queryUri, false)
|
||||
openUrlOrSearch(context, String(data))
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun openUrlOrSearch(
|
||||
context: Context,
|
||||
url: String,
|
||||
search: Boolean = true
|
||||
) {
|
||||
if (context.openUrl(url, silent = true) || !search) {
|
||||
return
|
||||
}
|
||||
prependSearchUrl(context, url)?.let {
|
||||
openUrlOrSearch(context, it, false)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun prependSearchUrl(
|
||||
context: Context,
|
||||
query: String
|
||||
): String? {
|
||||
val defaultSearchUrl = prefs.defaultSearchUrl
|
||||
if (defaultSearchUrl.isNotEmpty()) {
|
||||
return defaultSearchUrl + query.urlEncode()
|
||||
}
|
||||
val names = context.resources.getStringArray(
|
||||
R.array.search_engines_names
|
||||
).toMutableList()
|
||||
val urls = context.resources.getStringArray(
|
||||
R.array.search_engines_values
|
||||
).toMutableList()
|
||||
// Remove the "Always ask" entry. The arrays search_engines_*
|
||||
// are used in the preferences too.
|
||||
names.removeAt(0)
|
||||
urls.removeAt(0)
|
||||
if (prefs.openWithUrl.isNotEmpty()) {
|
||||
names.add(prefs.openWithUrl)
|
||||
urls.add(prefs.openWithUrl)
|
||||
}
|
||||
return alertDialog(context) { resume ->
|
||||
setTitle(R.string.pick_search_engine)
|
||||
setItems(names.toTypedArray()) { _, which ->
|
||||
resume(urls[which] + query.urlEncode())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package de.markusfisch.android.binaryeye.actions.vtype
|
||||
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
|
||||
object VTypeParser {
|
||||
private const val BACKSLASH_R_LEGACY =
|
||||
@@ -39,6 +39,6 @@ data class VTypeProperty(
|
||||
} ?: this.info.firstOrNull()
|
||||
|
||||
companion object {
|
||||
private val typeRegex = """^(?:TYPE=)(.+?)[:;]?.*$""".toRegex(RegexOption.IGNORE_CASE)
|
||||
private val typeRegex = """^TYPE=([A-Z_]+)[:;]?.*$""".toRegex(RegexOption.IGNORE_CASE)
|
||||
}
|
||||
}
|
||||
|
||||
+8
-2
@@ -8,7 +8,7 @@ import android.provider.ContactsContract
|
||||
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 java.util.Locale
|
||||
|
||||
object VCardAction : IntentAction() {
|
||||
override val iconResId: Int
|
||||
@@ -45,10 +45,13 @@ object VCardAction : IntentAction() {
|
||||
val (extraPhoneType, extraPhone) = when (index) {
|
||||
0 -> ContactsContract.Intents.Insert.PHONE_TYPE to
|
||||
ContactsContract.Intents.Insert.PHONE
|
||||
|
||||
1 -> ContactsContract.Intents.Insert.SECONDARY_PHONE_TYPE to
|
||||
ContactsContract.Intents.Insert.SECONDARY_PHONE
|
||||
|
||||
2 -> ContactsContract.Intents.Insert.TERTIARY_PHONE_TYPE to
|
||||
ContactsContract.Intents.Insert.TERTIARY_PHONE_TYPE
|
||||
|
||||
else -> return@forEachIndexed
|
||||
}
|
||||
phoneProperty.firstTypeOrFirstInfo?.also {
|
||||
@@ -65,10 +68,13 @@ object VCardAction : IntentAction() {
|
||||
val (extraMailType, extraMail) = when (index) {
|
||||
0 -> ContactsContract.Intents.Insert.EMAIL_TYPE to
|
||||
ContactsContract.Intents.Insert.EMAIL
|
||||
|
||||
1 -> ContactsContract.Intents.Insert.SECONDARY_EMAIL_TYPE to
|
||||
ContactsContract.Intents.Insert.SECONDARY_EMAIL
|
||||
|
||||
2 -> ContactsContract.Intents.Insert.TERTIARY_EMAIL_TYPE to
|
||||
ContactsContract.Intents.Insert.TERTIARY_EMAIL
|
||||
|
||||
else -> return@forEachIndexed
|
||||
}
|
||||
mailProperty.firstTypeOrFirstInfo?.also {
|
||||
@@ -184,7 +190,7 @@ object VCardAction : IntentAction() {
|
||||
}
|
||||
|
||||
private val String.addressType: Int?
|
||||
get() = when (this.toUpperCase(Locale.US)) {
|
||||
get() = when (this.uppercase()) {
|
||||
"HOME" -> ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME
|
||||
"WORK" -> ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK
|
||||
"OTHER" -> ContactsContract.CommonDataKinds.StructuredPostal.TYPE_OTHER
|
||||
|
||||
+3
-2
@@ -9,7 +9,8 @@ import de.markusfisch.android.binaryeye.R
|
||||
import de.markusfisch.android.binaryeye.actions.IntentAction
|
||||
import de.markusfisch.android.binaryeye.actions.vtype.VTypeParser
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
object VEventAction : IntentAction() {
|
||||
override val iconResId: Int
|
||||
@@ -87,6 +88,6 @@ private fun List<String>.simpleFindParse(date: String): Date? {
|
||||
|
||||
private fun SimpleDateFormat.simpleParse(date: String): Date? = try {
|
||||
parse(date)
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import de.markusfisch.android.binaryeye.content.openUrl
|
||||
|
||||
object WebAction : IAction {
|
||||
private val colloquialRegex =
|
||||
"^(http[s]*://)*[A-Za-z0-9]{3,}\\.[A-Za-z]{2,}[^ \t\r\n]*$".toRegex()
|
||||
"^(URL:[ ]*)*(http[s]*://)*[A-Za-z0-9-]{3,}\\.[A-Za-z]{2,}[^ \t\r\n]*$".toRegex()
|
||||
|
||||
override val iconResId: Int = R.drawable.ic_action_open
|
||||
override val titleResId: Int = R.string.open_url
|
||||
@@ -18,6 +18,9 @@ object WebAction : IAction {
|
||||
|
||||
override suspend fun execute(context: Context, data: ByteArray) {
|
||||
var url = String(data).trim()
|
||||
if (url.startsWith("URL:")) {
|
||||
url = url.substring(4).trim()
|
||||
}
|
||||
if (!url.startsWith("http") && !url.startsWith("ftp")) {
|
||||
url = "http://${url}"
|
||||
}
|
||||
|
||||
@@ -13,19 +13,21 @@ object WifiAction : IAction {
|
||||
|
||||
var password: String? = null
|
||||
|
||||
override fun canExecuteOn(data: ByteArray): Boolean =
|
||||
WifiConnector.parse(String(data)) {
|
||||
password = it
|
||||
} != null
|
||||
override fun canExecuteOn(data: ByteArray): Boolean {
|
||||
// Just check if the data can be parsed as the app should also
|
||||
// show invalid configurations so users can still access them.
|
||||
val inputMap = WifiConnector.parseMap(String(data)) ?: return false
|
||||
password = inputMap["P"]
|
||||
return !inputMap["S"].isNullOrEmpty()
|
||||
}
|
||||
|
||||
override suspend fun execute(
|
||||
context: Context,
|
||||
data: ByteArray
|
||||
) = withContext(Dispatchers.IO) {
|
||||
val wifiConfig = WifiConnector.parse(
|
||||
String(data)
|
||||
) ?: return@withContext
|
||||
val message = WifiConnector.addNetwork(context, wifiConfig)
|
||||
val message = WifiConnector.parse(String(data))?.let {
|
||||
WifiConnector.addNetwork(context, it)
|
||||
} ?: R.string.wifi_config_failed
|
||||
withContext(Dispatchers.Main) {
|
||||
context.toast(message)
|
||||
}
|
||||
|
||||
+55
-25
@@ -1,14 +1,16 @@
|
||||
package de.markusfisch.android.binaryeye.actions.wifi
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.wifi.WifiConfiguration
|
||||
import android.net.wifi.WifiEnterpriseConfig
|
||||
import android.net.wifi.WifiManager
|
||||
import android.net.wifi.WifiNetworkSuggestion
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import android.support.annotation.RequiresApi
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Normal:
|
||||
@@ -35,27 +37,35 @@ import java.util.*
|
||||
* The fields can appear in any order. Only "S:" is required.
|
||||
*/
|
||||
object WifiConnector {
|
||||
fun parse(
|
||||
input: String,
|
||||
passwordBlock: ((password: String?) -> Unit)? = null
|
||||
): Any? {
|
||||
fun parse(input: String): Any? {
|
||||
val inputMap = parseMap(input) ?: return null
|
||||
val parsedData = SimpleDataAccessor.of(inputMap) ?: return null
|
||||
passwordBlock?.apply {
|
||||
invoke(parsedData.password)
|
||||
}
|
||||
return if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
|
||||
// WifiConfiguration is deprecated in Android Q but
|
||||
// because it's only possible in R to query network
|
||||
// suggestions, we still use the deprecated API on Q.
|
||||
@Suppress("DEPRECATION")
|
||||
WifiConfiguration().apply(parsedData)
|
||||
} else {
|
||||
WifiNetworkSuggestion.Builder().apply(parsedData)
|
||||
return try {
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
|
||||
// WifiConfiguration is deprecated in Android Q but
|
||||
// because it's only possible in R to query network
|
||||
// suggestions, we still use the deprecated API on Q.
|
||||
@Suppress("DEPRECATION")
|
||||
WifiConfiguration().apply(parsedData)
|
||||
} else {
|
||||
WifiNetworkSuggestion.Builder().apply(parsedData)
|
||||
}
|
||||
} catch (_: IllegalArgumentException) {
|
||||
// Some arguments may be out of range, e.g. SSIDs cannot
|
||||
// be longer than 32 characters in some versions of Android.
|
||||
// See: https://github.com/markusfisch/BinaryEye/issues/402
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun addNetwork(context: Context, config: Any): Int {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
|
||||
config is WifiNetworkSuggestion.Builder
|
||||
) {
|
||||
context.suggestNetworkFromBuilder(config)
|
||||
return R.string.wifi_added
|
||||
}
|
||||
|
||||
val wifiManager = context.applicationContext.getSystemService(
|
||||
Context.WIFI_SERVICE
|
||||
) as WifiManager
|
||||
@@ -86,7 +96,7 @@ object WifiConnector {
|
||||
input.trimAndTerminate()
|
||||
)?.groupValues?.get(1)?.let { pairs ->
|
||||
pairRegex.findAll(pairs).map { pair ->
|
||||
pair.groupValues[1].uppercase(Locale.US) to pair.groupValues[2]
|
||||
pair.groupValues[1].uppercase(Locale.US) to pair.groupValues[2].unescape
|
||||
}.toMap()
|
||||
}
|
||||
}
|
||||
@@ -96,17 +106,17 @@ object WifiConnector {
|
||||
) {
|
||||
// This should be private but because of testing it isn't possible.
|
||||
internal val ssid: String
|
||||
get() = inputMap.getValue("S").unescape
|
||||
get() = inputMap.getValue("S")
|
||||
internal val securityType: String
|
||||
get() = inputMap["T"]?.unescape ?: ""
|
||||
get() = inputMap["T"] ?: ""
|
||||
internal val password: String?
|
||||
get() = inputMap["P"]?.unescape
|
||||
get() = inputMap["P"]
|
||||
internal val hidden: Boolean
|
||||
get() = inputMap["H"]?.unescape == "true"
|
||||
get() = inputMap["H"] == "true"
|
||||
internal val anonymousIdentity: String
|
||||
get() = inputMap["AI"]?.unescape ?: inputMap["A"]?.unescape ?: ""
|
||||
get() = inputMap["AI"] ?: inputMap["A"] ?: ""
|
||||
internal val identity: String
|
||||
get() = inputMap["I"]?.unescape ?: ""
|
||||
get() = inputMap["I"] ?: ""
|
||||
internal val eapMethod: Int?
|
||||
get() = if (inputMap["E"].isNullOrEmpty()) {
|
||||
requireSdk(Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
@@ -169,6 +179,7 @@ object WifiConnector {
|
||||
"WPA", "WPA2" -> {
|
||||
data.password?.let { setWpa2Passphrase(it) }
|
||||
}
|
||||
|
||||
"WPA2-EAP" -> {
|
||||
setWpa2EnterpriseConfig(WifiEnterpriseConfig().apply {
|
||||
identity = data.identity
|
||||
@@ -178,9 +189,11 @@ object WifiConnector {
|
||||
data.phase2Method?.let { phase2Method = it }
|
||||
})
|
||||
}
|
||||
|
||||
"WPA3" -> {
|
||||
data.password?.let { setWpa3Passphrase(it) }
|
||||
}
|
||||
|
||||
"WPA3-EAP" -> {
|
||||
setWpa3EnterpriseConfig(WifiEnterpriseConfig().apply {
|
||||
identity = data.identity
|
||||
@@ -191,7 +204,7 @@ object WifiConnector {
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (e: IllegalArgumentException) {
|
||||
} catch (_: IllegalArgumentException) {
|
||||
// Don't accept passphrases that aren't ASCII encodeable.
|
||||
return null
|
||||
}
|
||||
@@ -240,6 +253,7 @@ object WifiConnector {
|
||||
allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40)
|
||||
allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104)
|
||||
}
|
||||
|
||||
"WPA" -> {
|
||||
passwordWithQuotes?.let {
|
||||
preSharedKey = it
|
||||
@@ -256,6 +270,7 @@ object WifiConnector {
|
||||
allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP)
|
||||
allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP)
|
||||
}
|
||||
|
||||
"WPA2-EAP" -> requireSdk(Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
passwordWithQuotes?.let {
|
||||
preSharedKey = it
|
||||
@@ -301,6 +316,21 @@ private fun WifiManager.addNetworkFromBuilder(
|
||||
return result == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
private fun Context.suggestNetworkFromBuilder(
|
||||
builder: WifiNetworkSuggestion.Builder
|
||||
) {
|
||||
val suggestion = builder.build()
|
||||
startActivity(
|
||||
Intent(Settings.ACTION_WIFI_ADD_NETWORKS).apply {
|
||||
putParcelableArrayListExtra(
|
||||
Settings.EXTRA_WIFI_NETWORK_LIST,
|
||||
arrayListOf(suggestion)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Make sure the string ends with a semicolon.
|
||||
private fun String.trimAndTerminate(): String {
|
||||
var trimmed = trim()
|
||||
@@ -354,7 +384,7 @@ private fun WifiManager.removeOldNetwork(
|
||||
}?.networkId?.also {
|
||||
removeNetwork(it)
|
||||
}
|
||||
} catch (e: SecurityException) {
|
||||
} catch (_: SecurityException) {
|
||||
// The user didn't allow ACCESS_FINE_LOCATION which is
|
||||
// required to access configuredNetworks and that's fine.
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package de.markusfisch.android.binaryeye.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
@@ -9,18 +10,24 @@ import android.graphics.Matrix
|
||||
import android.graphics.Rect
|
||||
import android.hardware.Camera
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.support.design.widget.FloatingActionButton
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.widget.Toolbar
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import android.widget.SeekBar
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
import de.markusfisch.android.binaryeye.adapter.prettifyFormatName
|
||||
import de.markusfisch.android.binaryeye.app.*
|
||||
import de.markusfisch.android.binaryeye.app.PERMISSION_CAMERA
|
||||
import de.markusfisch.android.binaryeye.app.applyLocale
|
||||
import de.markusfisch.android.binaryeye.app.db
|
||||
import de.markusfisch.android.binaryeye.app.hasBluetoothPermission
|
||||
import de.markusfisch.android.binaryeye.app.hasCameraPermission
|
||||
import de.markusfisch.android.binaryeye.app.prefs
|
||||
import de.markusfisch.android.binaryeye.bluetooth.sendBluetoothAsync
|
||||
import de.markusfisch.android.binaryeye.content.copyToClipboard
|
||||
import de.markusfisch.android.binaryeye.content.execShareIntent
|
||||
@@ -32,21 +39,25 @@ 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.media.releaseToneGenerators
|
||||
import de.markusfisch.android.binaryeye.net.isScanDeeplink
|
||||
import de.markusfisch.android.binaryeye.net.sendAsync
|
||||
import de.markusfisch.android.binaryeye.net.urlEncode
|
||||
import de.markusfisch.android.binaryeye.preference.Preferences
|
||||
import de.markusfisch.android.binaryeye.view.errorFeedback
|
||||
import de.markusfisch.android.binaryeye.view.initSystemBars
|
||||
import de.markusfisch.android.binaryeye.view.initBars
|
||||
import de.markusfisch.android.binaryeye.view.scanFeedback
|
||||
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
|
||||
import de.markusfisch.android.binaryeye.widget.DetectorView
|
||||
import de.markusfisch.android.binaryeye.widget.toast
|
||||
import de.markusfisch.android.cameraview.widget.CameraView
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.BarcodeFormat
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.Binarizer
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.DecodeHints
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.Format
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.ReaderOptions
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.Result
|
||||
import java.nio.charset.Charset
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.TextMode
|
||||
import java.io.FileInputStream
|
||||
import java.util.Scanner
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
@@ -60,7 +71,7 @@ class CameraActivity : AppCompatActivity() {
|
||||
private lateinit var zoomBar: SeekBar
|
||||
private lateinit var flashFab: FloatingActionButton
|
||||
|
||||
private var formatsToRead = setOf<String>()
|
||||
private var formatsToRead = setOf<BarcodeFormat>()
|
||||
private var frameMetrics = FrameMetrics()
|
||||
private var decoding = true
|
||||
private var returnResult = false
|
||||
@@ -69,8 +80,10 @@ class CameraActivity : AppCompatActivity() {
|
||||
private var frontFacing = false
|
||||
private var bulkMode = prefs.bulkMode
|
||||
private var restrictFormat: String? = null
|
||||
private var searchTerm: String? = null
|
||||
private var ignoreNext: String? = null
|
||||
private var fallbackBuffer: IntArray? = null
|
||||
private var requestCameraPermission = true
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
@@ -116,8 +129,7 @@ class CameraActivity : AppCompatActivity() {
|
||||
// custom locale.
|
||||
setTitle(R.string.scan_code)
|
||||
|
||||
initSystemBars(this)
|
||||
setSupportActionBar(findViewById(R.id.toolbar) as Toolbar)
|
||||
initBars()
|
||||
|
||||
cameraView = findViewById(R.id.camera_view) as CameraView
|
||||
detectorView = findViewById(R.id.detector_view) as DetectorView
|
||||
@@ -129,7 +141,7 @@ class CameraActivity : AppCompatActivity() {
|
||||
initDetectorView()
|
||||
|
||||
if (intent?.action == Intent.ACTION_SEND &&
|
||||
intent.type == "text/plain"
|
||||
intent.type?.startsWith("text/") == true
|
||||
) {
|
||||
handleSendText(intent)
|
||||
}
|
||||
@@ -139,36 +151,68 @@ class CameraActivity : AppCompatActivity() {
|
||||
super.onDestroy()
|
||||
fallbackBuffer = null
|
||||
saveZoom()
|
||||
detectorView.saveCropHandlePos()
|
||||
detectorView.storeCropHandlePos(CAMERA_CROP_HANDLE)
|
||||
releaseToneGenerators()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (firstStart()) {
|
||||
startActivity(Intent(this, WelcomeActivity::class.java))
|
||||
return
|
||||
}
|
||||
System.gc()
|
||||
updateHints()
|
||||
updateHintsAndTitle()
|
||||
if (prefs.bulkMode && bulkMode != prefs.bulkMode) {
|
||||
bulkMode = prefs.bulkMode
|
||||
invalidateOptionsMenu()
|
||||
ignoreNext = null
|
||||
}
|
||||
setReturnTarget(intent)
|
||||
if (hasCameraPermission()) {
|
||||
// Avoid asking multiple times when the user has denied access
|
||||
// for this session. Otherwise ActivityCompat.requestPermissions()
|
||||
// will trigger onResume() again and again.
|
||||
if (hasCameraPermission(requestCameraPermission)) {
|
||||
openCamera()
|
||||
}
|
||||
requestCameraPermission = false
|
||||
}
|
||||
|
||||
private fun updateHints() {
|
||||
private fun Context.firstStart(): Boolean {
|
||||
val welcomeShownName = "welcome_shown"
|
||||
if (prefs.preferences.getBoolean(welcomeShownName, false)) {
|
||||
return false
|
||||
}
|
||||
prefs.preferences.edit().putBoolean(welcomeShownName, true).apply()
|
||||
val packageInfo = packageManager.getPackageInfo(packageName, 0)
|
||||
val installedSince = System.currentTimeMillis() -
|
||||
packageInfo.firstInstallTime
|
||||
return installedSince < 86400000
|
||||
}
|
||||
|
||||
private fun updateHintsAndTitle() {
|
||||
val restriction = restrictFormat
|
||||
formatsToRead = if (restriction != null) {
|
||||
title = getString(
|
||||
R.string.scan_format,
|
||||
prettifyFormatName(restriction)
|
||||
)
|
||||
setOf(restriction)
|
||||
} else {
|
||||
setTitle(R.string.scan_code)
|
||||
prefs.barcodeFormats
|
||||
}.toFormatSet()
|
||||
updateTitle()
|
||||
}
|
||||
|
||||
private fun updateTitle() {
|
||||
if (searchTerm != null || restrictFormat != null) {
|
||||
title = getString(
|
||||
R.string.scan_format,
|
||||
prettifyFormatName(
|
||||
listOfNotNull(
|
||||
restrictFormat,
|
||||
searchTerm
|
||||
).joinToString(",")
|
||||
)
|
||||
)
|
||||
} else {
|
||||
setTitle(R.string.scan_code)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +221,8 @@ class CameraActivity : AppCompatActivity() {
|
||||
intent?.action == "com.google.zxing.client.android.SCAN" -> {
|
||||
returnResult = true
|
||||
}
|
||||
intent?.dataString?.isReturnUrl() == true -> {
|
||||
|
||||
intent?.dataString?.let(::isScanDeeplink) == true -> {
|
||||
finishAfterShowingResult = true
|
||||
returnUrlTemplate = intent.data?.getQueryParameter("ret")
|
||||
}
|
||||
@@ -213,6 +258,7 @@ class CameraActivity : AppCompatActivity() {
|
||||
frontFacing = savedState.getBoolean(FRONT_FACING)
|
||||
bulkMode = savedState.getBoolean(BULK_MODE)
|
||||
restrictFormat = savedState.getString(RESTRICT_FORMAT)
|
||||
searchTerm = savedState.getString(SEARCH_TERM)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
@@ -221,6 +267,7 @@ class CameraActivity : AppCompatActivity() {
|
||||
outState.putBoolean(FRONT_FACING, frontFacing)
|
||||
outState.putBoolean(BULK_MODE, bulkMode)
|
||||
outState.putString(RESTRICT_FORMAT, restrictFormat)
|
||||
outState.putString(SEARCH_TERM, searchTerm)
|
||||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
@@ -245,44 +292,57 @@ class CameraActivity : AppCompatActivity() {
|
||||
createBarcode()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.history -> {
|
||||
startActivity(MainActivity.getHistoryIntent(this))
|
||||
true
|
||||
}
|
||||
|
||||
R.id.pick_file -> {
|
||||
startActivityForResult(
|
||||
Intent.createChooser(
|
||||
Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||
type = "image/*"
|
||||
},
|
||||
getString(R.string.pick_file)
|
||||
getString(R.string.pick_image_file)
|
||||
),
|
||||
PICK_FILE_RESULT_CODE
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
R.id.switch_camera -> {
|
||||
switchCamera()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.bulk_mode -> {
|
||||
bulkMode = bulkMode xor true
|
||||
item.isChecked = bulkMode
|
||||
ignoreNext = null
|
||||
true
|
||||
}
|
||||
|
||||
R.id.restrict_format -> {
|
||||
showRestrictionDialog()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.find_code -> {
|
||||
askForCode()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.preferences -> {
|
||||
startActivity(MainActivity.getPreferencesIntent(this))
|
||||
true
|
||||
}
|
||||
|
||||
R.id.info -> {
|
||||
openReadme()
|
||||
true
|
||||
}
|
||||
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
@@ -312,12 +372,36 @@ class CameraActivity : AppCompatActivity() {
|
||||
setTitle(R.string.restrict_format)
|
||||
setItems(names.toTypedArray()) { _, which ->
|
||||
restrictFormat = formats[which]
|
||||
updateHints()
|
||||
updateHintsAndTitle()
|
||||
}
|
||||
show()
|
||||
}
|
||||
}
|
||||
|
||||
// Dialogs do not have a parent view.
|
||||
@SuppressLint("InflateParams")
|
||||
private fun askForCode() {
|
||||
val view = layoutInflater.inflate(R.layout.dialog_find_code, null)
|
||||
val editText = view.findViewById<EditText>(R.id.term)
|
||||
searchTerm.let {
|
||||
editText.setText(it)
|
||||
}
|
||||
AlertDialog.Builder(this)
|
||||
.setView(view)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
searchTerm = editText.text.toString()
|
||||
if (searchTerm?.isEmpty() == true) {
|
||||
searchTerm = null
|
||||
}
|
||||
updateTitle()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
searchTerm = null
|
||||
updateTitle()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun openReadme() {
|
||||
val intent = Intent(
|
||||
Intent.ACTION_VIEW,
|
||||
@@ -328,9 +412,34 @@ class CameraActivity : AppCompatActivity() {
|
||||
|
||||
private fun handleSendText(intent: Intent) {
|
||||
val text = intent.getStringExtra(Intent.EXTRA_TEXT)
|
||||
|
||||
if (text?.isEmpty() == false) {
|
||||
startActivity(MainActivity.getEncodeIntent(this, text, true))
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
// Read text from file.
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
@Suppress("DEPRECATION")
|
||||
intent.getParcelableExtra(Intent.EXTRA_STREAM) as Uri?
|
||||
} else {
|
||||
intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
|
||||
}?.let { textUri ->
|
||||
val file = contentResolver.openFileDescriptor(textUri, "r")
|
||||
if (file != null) {
|
||||
val fs = FileInputStream(file.fileDescriptor)
|
||||
val scn = Scanner(fs).useDelimiter("\\A")
|
||||
if (scn.hasNext()) {
|
||||
startActivity(
|
||||
MainActivity.getEncodeIntent(
|
||||
this, scn.next(), true
|
||||
)
|
||||
)
|
||||
finish()
|
||||
}
|
||||
file.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,12 +460,14 @@ class CameraActivity : AppCompatActivity() {
|
||||
progress = zoomBar.progress
|
||||
return true
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
if (prefs.zoomBySwiping) {
|
||||
v ?: return false
|
||||
val dist = offset - pos
|
||||
val maxValue = zoomBar.max
|
||||
val change = maxValue / v.height.toFloat() * 2f * dist
|
||||
val change = maxValue /
|
||||
v.height.toFloat() * 2f * dist
|
||||
zoomBar.progress = min(
|
||||
maxValue,
|
||||
max(progress + change.roundToInt(), 0)
|
||||
@@ -364,6 +475,7 @@ class CameraActivity : AppCompatActivity() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP -> {
|
||||
// Stop calling focusTo() as soon as it returns false
|
||||
// to avoid throwing and catching future exceptions.
|
||||
@@ -423,12 +535,14 @@ class CameraActivity : AppCompatActivity() {
|
||||
ignoreNext = null
|
||||
decoding = true
|
||||
// These settings can't change while the camera is open.
|
||||
val decodeHints = DecodeHints(
|
||||
val options = ReaderOptions(
|
||||
tryHarder = prefs.tryHarder,
|
||||
tryRotate = prefs.autoRotate,
|
||||
tryInvert = true,
|
||||
tryDownscale = true,
|
||||
maxNumberOfSymbols = 1
|
||||
returnCodabarStartEnd = true,
|
||||
maxNumberOfSymbols = 1,
|
||||
textMode = TextMode.PLAIN
|
||||
)
|
||||
var useLocalAverage = false
|
||||
camera.setPreviewCallback { frameData, _ ->
|
||||
@@ -440,7 +554,7 @@ class CameraActivity : AppCompatActivity() {
|
||||
frameRoi.left, frameRoi.top,
|
||||
frameRoi.width(), frameRoi.height(),
|
||||
frameMetrics.orientation,
|
||||
decodeHints.apply {
|
||||
options.apply {
|
||||
// By default, ZXing uses LOCAL_AVERAGE, but
|
||||
// this does not work well with inverted
|
||||
// barcodes on low-contrast backgrounds.
|
||||
@@ -449,11 +563,15 @@ class CameraActivity : AppCompatActivity() {
|
||||
} else {
|
||||
Binarizer.GLOBAL_HISTOGRAM
|
||||
}
|
||||
formats = formatsToRead.joinToString()
|
||||
formats = formatsToRead
|
||||
}
|
||||
)?.let { results ->
|
||||
val result = results.first()
|
||||
if (result.text != ignoreNext) {
|
||||
val text = result.text
|
||||
val term = searchTerm
|
||||
if (text != ignoreNext &&
|
||||
(term == null || text.contains(term))
|
||||
) {
|
||||
postResult(result)
|
||||
decoding = false
|
||||
}
|
||||
@@ -495,7 +613,7 @@ class CameraActivity : AppCompatActivity() {
|
||||
val params = parameters
|
||||
params.zoom = zoom
|
||||
parameters = params
|
||||
} catch (e: RuntimeException) {
|
||||
} catch (_: RuntimeException) {
|
||||
// Ignore. There's nothing we can do.
|
||||
}
|
||||
}
|
||||
@@ -524,12 +642,21 @@ class CameraActivity : AppCompatActivity() {
|
||||
updateFrameRoiAndMappingMatrix()
|
||||
}
|
||||
detectorView.setPaddingFromWindowInsets()
|
||||
detectorView.restoreCropHandlePos()
|
||||
detectorView.restoreCropHandlePos(CAMERA_CROP_HANDLE)
|
||||
}
|
||||
|
||||
private fun updateFrameRoiAndMappingMatrix() {
|
||||
val viewRect = cameraView.previewRect
|
||||
val viewRoi = if (detectorView.roi.width() < 1) {
|
||||
if (viewRect.width() < 1 ||
|
||||
viewRect.height() < 1 ||
|
||||
!frameMetrics.isValid()
|
||||
) {
|
||||
return
|
||||
}
|
||||
val viewRoi = if (
|
||||
detectorView.roi.width() < 1 ||
|
||||
detectorView.roi.height() < 1
|
||||
) {
|
||||
viewRect
|
||||
} else {
|
||||
detectorView.roi
|
||||
@@ -590,9 +717,11 @@ class CameraActivity : AppCompatActivity() {
|
||||
setResult(Activity.RESULT_OK, getReturnIntent(result))
|
||||
finish()
|
||||
}
|
||||
|
||||
returnUri != null -> execShareIntent(
|
||||
Intent(Intent.ACTION_VIEW, returnUri)
|
||||
)
|
||||
|
||||
else -> {
|
||||
showResult(result, bulkMode)
|
||||
// If this app was invoked via a deep link but without
|
||||
@@ -604,8 +733,12 @@ class CameraActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
if (bulkMode) {
|
||||
if (prefs.ignoreConsecutiveDuplicates) {
|
||||
ignoreNext = result.text
|
||||
when (prefs.ignoreDuplicates()) {
|
||||
Preferences.Companion.IgnoreDuplicates.Consecutive,
|
||||
Preferences.Companion.IgnoreDuplicates.Any ->
|
||||
ignoreNext = result.text
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
if (prefs.showToastInBulkMode) {
|
||||
toast(result.text)
|
||||
@@ -618,15 +751,22 @@ class CameraActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val CAMERA_CROP_HANDLE = "camera_crop_handle"
|
||||
|
||||
private const val PICK_FILE_RESULT_CODE = 1
|
||||
private const val ZOOM_MAX = "zoom_max"
|
||||
private const val ZOOM_LEVEL = "zoom_level"
|
||||
private const val FRONT_FACING = "front_facing"
|
||||
private const val BULK_MODE = "bulk_mode"
|
||||
private const val RESTRICT_FORMAT = "restrict_format"
|
||||
private const val SEARCH_TERM = "search_term"
|
||||
}
|
||||
}
|
||||
|
||||
fun Set<String>.toFormatSet(): Set<BarcodeFormat> = map {
|
||||
BarcodeFormat.valueOf(it)
|
||||
}.toSet()
|
||||
|
||||
fun Activity.showResult(
|
||||
result: Result,
|
||||
bulkMode: Boolean = false,
|
||||
@@ -641,7 +781,7 @@ fun Activity.showResult(
|
||||
if (prefs.sendScanActive && prefs.sendScanUrl.isNotEmpty()) {
|
||||
if (prefs.sendScanType == "4") {
|
||||
openUrl(
|
||||
prefs.sendScanUrl + scan.content.urlEncode()
|
||||
prefs.sendScanUrl + scan.text.urlEncode()
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -652,7 +792,7 @@ fun Activity.showResult(
|
||||
if (code == null || code < 200 || code > 299) {
|
||||
errorFeedback()
|
||||
}
|
||||
if (body != null && body.isNotEmpty()) {
|
||||
if (!body.isNullOrEmpty()) {
|
||||
toast(body)
|
||||
} else if (code == null || code > 299) {
|
||||
toast(R.string.background_request_failed)
|
||||
@@ -672,10 +812,12 @@ fun Activity.showResult(
|
||||
errorFeedback()
|
||||
R.string.bluetooth_connect_fail
|
||||
}
|
||||
|
||||
!sent -> {
|
||||
errorFeedback()
|
||||
R.string.bluetooth_send_fail
|
||||
}
|
||||
|
||||
else -> R.string.bluetooth_send_success
|
||||
}
|
||||
)
|
||||
@@ -690,7 +832,7 @@ fun Activity.showResult(
|
||||
|
||||
private fun getReturnIntent(result: Result) = Intent().apply {
|
||||
putExtra("SCAN_RESULT", result.text)
|
||||
putExtra("SCAN_RESULT_FORMAT", result.format)
|
||||
putExtra("SCAN_RESULT_FORMAT", result.format.toString())
|
||||
putExtra("SCAN_RESULT_ORIENTATION", result.orientation)
|
||||
putExtra("SCAN_RESULT_ERROR_CORRECTION_LEVEL", result.ecLevel)
|
||||
if (result.rawBytes.isNotEmpty()) {
|
||||
@@ -698,19 +840,11 @@ private fun getReturnIntent(result: Result) = Intent().apply {
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.isReturnUrl() = listOf(
|
||||
"binaryeye://scan",
|
||||
"http://markusfisch.de/BinaryEye",
|
||||
"https://markusfisch.de/BinaryEye"
|
||||
).firstOrNull { startsWith(it) } != null
|
||||
|
||||
private fun completeUrl(urlTemplate: String, result: Result) = Uri.parse(
|
||||
urlTemplate
|
||||
.replace("{RESULT}", result.text.urlEncode())
|
||||
.replace("{RESULT_BYTES}", result.rawBytes.toHexString())
|
||||
.replace(
|
||||
"{FORMAT}", result.format.urlEncode()
|
||||
)
|
||||
.replace("{FORMAT}", result.format.name.urlEncode())
|
||||
// And support {CODE} from the old ZXing app, too.
|
||||
.replace("{CODE}", result.text.urlEncode())
|
||||
)
|
||||
|
||||
@@ -3,21 +3,26 @@ package de.markusfisch.android.binaryeye.activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.Fragment
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.widget.Toolbar
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
import de.markusfisch.android.binaryeye.app.*
|
||||
import de.markusfisch.android.binaryeye.app.PERMISSION_LOCATION
|
||||
import de.markusfisch.android.binaryeye.app.PERMISSION_WRITE
|
||||
import de.markusfisch.android.binaryeye.app.applyLocale
|
||||
import de.markusfisch.android.binaryeye.app.permissionGrantedCallback
|
||||
import de.markusfisch.android.binaryeye.app.prefs
|
||||
import de.markusfisch.android.binaryeye.app.setFragment
|
||||
import de.markusfisch.android.binaryeye.database.Scan
|
||||
import de.markusfisch.android.binaryeye.fragment.DecodeFragment
|
||||
import de.markusfisch.android.binaryeye.fragment.EncodeFragment
|
||||
import de.markusfisch.android.binaryeye.fragment.HistoryFragment
|
||||
import de.markusfisch.android.binaryeye.fragment.PreferencesFragment
|
||||
import de.markusfisch.android.binaryeye.net.isEncodeDeeplink
|
||||
import de.markusfisch.android.binaryeye.view.colorSystemAndToolBars
|
||||
import de.markusfisch.android.binaryeye.view.initSystemBars
|
||||
import de.markusfisch.android.binaryeye.view.recordToolbarHeight
|
||||
import de.markusfisch.android.binaryeye.view.initBars
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
override fun onRequestPermissionsResult(
|
||||
@@ -26,7 +31,8 @@ class MainActivity : AppCompatActivity() {
|
||||
grantResults: IntArray
|
||||
) {
|
||||
when (requestCode) {
|
||||
PERMISSION_LOCATION, PERMISSION_WRITE -> if (grantResults.isNotEmpty() &&
|
||||
PERMISSION_LOCATION, PERMISSION_WRITE -> if (
|
||||
grantResults.isNotEmpty() &&
|
||||
grantResults[0] == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
permissionGrantedCallback?.invoke()
|
||||
@@ -54,10 +60,7 @@ class MainActivity : AppCompatActivity() {
|
||||
super.onCreate(state)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
initSystemBars(this)
|
||||
val toolbar = findViewById(R.id.toolbar) as Toolbar
|
||||
recordToolbarHeight(toolbar)
|
||||
setSupportActionBar(toolbar)
|
||||
initBars()
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
|
||||
supportFragmentManager.addOnBackStackChangedListener {
|
||||
@@ -80,12 +83,25 @@ class MainActivity : AppCompatActivity() {
|
||||
return when {
|
||||
intent.hasExtra(PREFERENCES) -> PreferencesFragment()
|
||||
intent.hasExtra(HISTORY) -> HistoryFragment()
|
||||
|
||||
intent.dataString?.let(::isEncodeDeeplink) == true -> {
|
||||
val uri = Uri.parse(intent.dataString)
|
||||
EncodeFragment.newInstance(
|
||||
content = uri.getQueryParameter("content"),
|
||||
format = uri.getQueryParameter("format")?.uppercase(),
|
||||
execute = uri.getQueryParameter("execute")
|
||||
.let { it == "" || it.toBoolean() }
|
||||
)
|
||||
}
|
||||
|
||||
intent.hasExtra(ENCODE) -> EncodeFragment.newInstance(
|
||||
intent.getStringExtra(ENCODE)
|
||||
)
|
||||
intent.hasExtra(DECODED) -> DecodeFragment.newInstance(
|
||||
intent.getParcelableExtra(DECODED)!!
|
||||
)
|
||||
|
||||
intent.hasExtra(DECODED) -> intent.getScanExtra(DECODED)?.let {
|
||||
DecodeFragment.newInstance(it)
|
||||
} ?: DecodeFragment()
|
||||
|
||||
else -> PreferencesFragment()
|
||||
}
|
||||
}
|
||||
@@ -110,10 +126,11 @@ class MainActivity : AppCompatActivity() {
|
||||
val intent = Intent(context, MainActivity::class.java)
|
||||
intent.putExtra(ENCODE, text)
|
||||
if (isExternal) {
|
||||
val flagActivityClearTask =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
} else 0
|
||||
val flagActivityClearTask = if (
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB
|
||||
) {
|
||||
Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
} else 0
|
||||
intent.addFlags(
|
||||
Intent.FLAG_ACTIVITY_NO_HISTORY or
|
||||
flagActivityClearTask or
|
||||
@@ -130,3 +147,12 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Intent.getScanExtra(name: String): Scan? = if (
|
||||
Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU
|
||||
) {
|
||||
@Suppress("DEPRECATION")
|
||||
getParcelableExtra(name)
|
||||
} else {
|
||||
getParcelableExtra(name, Scan::class.java)
|
||||
}
|
||||
|
||||
@@ -7,10 +7,9 @@ import android.graphics.Matrix
|
||||
import android.graphics.Rect
|
||||
import android.graphics.RectF
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.widget.Toolbar
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
@@ -21,15 +20,22 @@ import de.markusfisch.android.binaryeye.graphics.fixTransparency
|
||||
import de.markusfisch.android.binaryeye.graphics.loadImageUri
|
||||
import de.markusfisch.android.binaryeye.graphics.mapPosition
|
||||
import de.markusfisch.android.binaryeye.media.releaseToneGenerators
|
||||
import de.markusfisch.android.binaryeye.view.*
|
||||
import de.markusfisch.android.binaryeye.view.colorSystemAndToolBars
|
||||
import de.markusfisch.android.binaryeye.view.initBars
|
||||
import de.markusfisch.android.binaryeye.view.scanFeedback
|
||||
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
|
||||
import de.markusfisch.android.binaryeye.widget.CropImageView
|
||||
import de.markusfisch.android.binaryeye.widget.DetectorView
|
||||
import de.markusfisch.android.binaryeye.widget.toast
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.Binarizer
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.DecodeHints
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.ReaderOptions
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.Result
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
@@ -38,13 +44,14 @@ class PickActivity : AppCompatActivity() {
|
||||
private val matrix = Matrix()
|
||||
private val parentJob = Job()
|
||||
private val scope = CoroutineScope(Dispatchers.IO + parentJob)
|
||||
private val decodeHints = DecodeHints(
|
||||
private val options = ReaderOptions(
|
||||
tryHarder = true,
|
||||
tryRotate = true,
|
||||
tryInvert = true,
|
||||
tryDownscale = true,
|
||||
returnCodabarStartEnd = true,
|
||||
maxNumberOfSymbols = 1,
|
||||
formats = prefs.barcodeFormats.joinToString()
|
||||
formats = prefs.barcodeFormats.toFormatSet()
|
||||
)
|
||||
|
||||
private lateinit var cropImageView: CropImageView
|
||||
@@ -66,10 +73,7 @@ class PickActivity : AppCompatActivity() {
|
||||
// locale.
|
||||
setTitle(R.string.pick_code_to_scan)
|
||||
|
||||
initSystemBars(this)
|
||||
val toolbar = findViewById(R.id.toolbar) as Toolbar
|
||||
recordToolbarHeight(toolbar)
|
||||
setSupportActionBar(toolbar)
|
||||
initBars()
|
||||
|
||||
supportFragmentManager.addOnBackStackChangedListener {
|
||||
colorSystemAndToolBars(this@PickActivity)
|
||||
@@ -82,20 +86,28 @@ class PickActivity : AppCompatActivity() {
|
||||
return
|
||||
}
|
||||
|
||||
cropImageView = findViewById(R.id.image) as CropImageView
|
||||
cropImageView.restrictTranslation = false
|
||||
cropImageView.freeRotation = prefs.freeRotation
|
||||
cropImageView.setImageBitmap(bitmap)
|
||||
cropImageView.onScan = {
|
||||
scanWithinBounds(bitmap)
|
||||
cropImageView = (findViewById(R.id.image) as CropImageView).apply {
|
||||
restrictTranslation = false
|
||||
freeRotation = prefs.freeRotation
|
||||
setImageBitmap(bitmap)
|
||||
onScan = {
|
||||
scanWithinBounds(bitmap)
|
||||
}
|
||||
runAfterLayout = {
|
||||
minWidth = min(
|
||||
minWidth / 2f,
|
||||
max(bitmap.width, bitmap.height).toFloat()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
detectorView = findViewById(R.id.detector_view) as DetectorView
|
||||
detectorView.onRoiChanged = {
|
||||
scanWithinBounds(bitmap)
|
||||
detectorView = (findViewById(R.id.detector_view) as DetectorView).apply {
|
||||
onRoiChanged = {
|
||||
scanWithinBounds(bitmap)
|
||||
}
|
||||
setPaddingFromWindowInsets()
|
||||
restoreCropHandlePos(PICKER_CROP_HANDLE)
|
||||
}
|
||||
detectorView.setPaddingFromWindowInsets()
|
||||
detectorView.restoreCropHandlePos()
|
||||
|
||||
findViewById(R.id.scan).setOnClickListener {
|
||||
showResult()
|
||||
@@ -117,35 +129,40 @@ class PickActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun scanWithinBounds(bitmap: Bitmap) {
|
||||
val viewRoi = if (detectorView.roi.width() < 1) {
|
||||
cropImageView.getBoundsRect()
|
||||
} else {
|
||||
detectorView.roi
|
||||
}
|
||||
val mappedRect = cropImageView.mappedRect
|
||||
val cropped = bitmap.crop(
|
||||
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())
|
||||
val viewRoi = Rect(
|
||||
if (detectorView.roi.width() < 1) {
|
||||
cropImageView.getBoundsRect()
|
||||
} else {
|
||||
detectorView.roi
|
||||
}
|
||||
)
|
||||
matrix.apply {
|
||||
setScale(
|
||||
croppedInView.width().toFloat() / cropped.width,
|
||||
croppedInView.height().toFloat() / cropped.height
|
||||
)
|
||||
postTranslate(
|
||||
croppedInView.left.toFloat(),
|
||||
croppedInView.top.toFloat()
|
||||
)
|
||||
}
|
||||
val mappedRect = RectF(cropImageView.mappedRect)
|
||||
val imageRotation = cropImageView.imageRotation
|
||||
val pivotX = cropImageView.pivotX
|
||||
val pivotY = cropImageView.pivotY
|
||||
scope.launch {
|
||||
val cropped = bitmap.crop(
|
||||
getNormalizedRoi(mappedRect, viewRoi),
|
||||
imageRotation,
|
||||
pivotX,
|
||||
pivotY
|
||||
) ?: return@launch
|
||||
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()
|
||||
)
|
||||
}
|
||||
cropped.decode()?.first()?.let {
|
||||
withContext(Dispatchers.Main) {
|
||||
if (isFinishing) {
|
||||
@@ -171,7 +188,7 @@ class PickActivity : AppCompatActivity() {
|
||||
0, 0,
|
||||
width, height,
|
||||
0,
|
||||
decodeHints.apply {
|
||||
options.apply {
|
||||
binarizer = Binarizer.LOCAL_AVERAGE
|
||||
}
|
||||
) ?: ZxingCpp.readBitmap(
|
||||
@@ -179,14 +196,14 @@ class PickActivity : AppCompatActivity() {
|
||||
0, 0,
|
||||
width, height,
|
||||
0,
|
||||
decodeHints.apply {
|
||||
options.apply {
|
||||
binarizer = Binarizer.GLOBAL_HISTOGRAM
|
||||
}
|
||||
)
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
detectorView.saveCropHandlePos()
|
||||
detectorView.storeCropHandlePos(PICKER_CROP_HANDLE)
|
||||
parentJob.cancel()
|
||||
releaseToneGenerators()
|
||||
}
|
||||
@@ -205,12 +222,14 @@ class PickActivity : AppCompatActivity() {
|
||||
rotateClockwise()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.toggle_free -> {
|
||||
prefs.freeRotation = prefs.freeRotation xor true
|
||||
cropImageView.freeRotation = prefs.freeRotation
|
||||
freeRotationItem.updateFreeRotationIcon()
|
||||
true
|
||||
}
|
||||
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
@@ -226,13 +245,11 @@ class PickActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun rotateClockwise() {
|
||||
cropImageView.imageRotation = cropImageView.imageRotation + 90 % 360
|
||||
cropImageView.imageRotation = (cropImageView.imageRotation + 90) % 360
|
||||
}
|
||||
|
||||
private fun loadSentImage(intent: Intent): Bitmap? {
|
||||
val uri = intent.getParcelableExtra<Parcelable>(
|
||||
Intent.EXTRA_STREAM
|
||||
) as? Uri ?: return null
|
||||
val uri = intent.getUriExtra() ?: return null
|
||||
return contentResolver.loadImageUri(uri)
|
||||
}
|
||||
|
||||
@@ -250,6 +267,10 @@ class PickActivity : AppCompatActivity() {
|
||||
applicationContext.toast(R.string.no_barcode_found)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PICKER_CROP_HANDLE = "picker_crop_handle"
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNormalizedRoi(imageRect: RectF, roi: Rect): RectF {
|
||||
@@ -262,3 +283,12 @@ private fun getNormalizedRoi(imageRect: RectF, roi: Rect): RectF {
|
||||
(roi.bottom - imageRect.top) / h
|
||||
)
|
||||
}
|
||||
|
||||
private fun Intent.getUriExtra(): Uri? = if (
|
||||
Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU
|
||||
) {
|
||||
@Suppress("DEPRECATION")
|
||||
getParcelableExtra(Intent.EXTRA_STREAM)
|
||||
} else {
|
||||
getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package de.markusfisch.android.binaryeye.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
import de.markusfisch.android.binaryeye.app.prefs
|
||||
|
||||
class WelcomeActivity : AppCompatActivity() {
|
||||
override fun onCreate(state: Bundle?) {
|
||||
super.onCreate(state)
|
||||
setContentView(R.layout.activity_welcome)
|
||||
findViewById(R.id.expert).setOnClickListener {
|
||||
finish()
|
||||
}
|
||||
findViewById(R.id.simple).setOnClickListener {
|
||||
prefs.apply {
|
||||
showCropHandle = false
|
||||
zoomBySwiping = false
|
||||
beep = true
|
||||
copyImmediately = true
|
||||
openImmediately = true
|
||||
showMetaData = false
|
||||
showHexDump = false
|
||||
defaultSearchUrl = "https://www.google.com/search?q="
|
||||
openWithUrl = defaultSearchUrl
|
||||
expandEscapeSequences = false
|
||||
brightenScreen = true
|
||||
}
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,14 +13,14 @@ import de.markusfisch.android.binaryeye.database.Database
|
||||
import java.text.DateFormat
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
|
||||
class ScansAdapter(context: Context, cursor: Cursor) :
|
||||
CursorAdapter(context, cursor, false) {
|
||||
private val idIndex = cursor.getColumnIndex(Database.SCANS_ID)
|
||||
private val timeIndex = cursor.getColumnIndex(Database.SCANS_DATETIME)
|
||||
private val nameIndex = cursor.getColumnIndex(Database.SCANS_NAME)
|
||||
private val contentIndex = cursor.getColumnIndex(Database.SCANS_CONTENT)
|
||||
private val textIndex = cursor.getColumnIndex(Database.SCANS_TEXT)
|
||||
private val formatIndex = cursor.getColumnIndex(Database.SCANS_FORMAT)
|
||||
private val selections = mutableMapOf<Long, Int>()
|
||||
private val selectedColor = ContextCompat.getColor(
|
||||
@@ -74,7 +74,7 @@ class ScansAdapter(context: Context, cursor: Cursor) :
|
||||
|
||||
fun getContent(
|
||||
position: Int
|
||||
) = (getItem(position) as Cursor?)?.getString(contentIndex)
|
||||
) = (getItem(position) as Cursor?)?.getString(textIndex)
|
||||
|
||||
override fun newView(
|
||||
context: Context,
|
||||
@@ -92,17 +92,19 @@ class ScansAdapter(context: Context, cursor: Cursor) :
|
||||
getViewHolder(view).apply {
|
||||
timeView.text = formatDateTime(cursor.getString(timeIndex))
|
||||
val name = cursor.getString(nameIndex)
|
||||
val content = cursor.getString(contentIndex)
|
||||
val text = cursor.getString(textIndex)
|
||||
var icon = 0
|
||||
contentView.text = when {
|
||||
name?.isNotEmpty() == true -> {
|
||||
icon = R.drawable.ic_label
|
||||
name
|
||||
}
|
||||
content?.isEmpty() == true -> context.getString(
|
||||
|
||||
text.isNullOrEmpty() -> context.getString(
|
||||
R.string.binary_data
|
||||
)
|
||||
else -> content
|
||||
|
||||
else -> text
|
||||
}
|
||||
contentView.setCompoundDrawablesWithIntrinsicBounds(
|
||||
icon, 0, 0, 0
|
||||
@@ -147,7 +149,7 @@ private fun formatDateTime(rfc: String): String {
|
||||
DateFormat.SHORT
|
||||
).format(it)
|
||||
}
|
||||
} catch (e: ParseException) {
|
||||
} catch (_: ParseException) {
|
||||
// Ignore and fall through.
|
||||
}
|
||||
return rfc
|
||||
|
||||
@@ -2,7 +2,7 @@ package de.markusfisch.android.binaryeye.app
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
|
||||
fun Context.applyLocale(localeName: String) {
|
||||
if (localeName.isEmpty()) {
|
||||
|
||||
@@ -10,9 +10,9 @@ import android.support.v4.content.ContextCompat
|
||||
var permissionGrantedCallback: (() -> Any)? = null
|
||||
|
||||
const val PERMISSION_CAMERA = 1
|
||||
fun Activity.hasCameraPermission() = hasPermission(
|
||||
fun Activity.hasCameraPermission(request: Boolean = true) = hasPermission(
|
||||
Manifest.permission.CAMERA,
|
||||
PERMISSION_CAMERA
|
||||
if (request) PERMISSION_CAMERA else 0
|
||||
)
|
||||
|
||||
const val PERMISSION_WRITE = 2
|
||||
@@ -53,11 +53,13 @@ private fun Activity.hasPermission(
|
||||
permission
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
arrayOf(permission),
|
||||
requestCode
|
||||
)
|
||||
if (requestCode > 0) {
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
arrayOf(permission),
|
||||
requestCode
|
||||
)
|
||||
}
|
||||
false
|
||||
} else {
|
||||
true
|
||||
|
||||
@@ -11,7 +11,7 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.IOException
|
||||
import java.io.OutputStreamWriter
|
||||
import java.util.*
|
||||
import java.util.UUID
|
||||
|
||||
fun Scan.sendBluetoothAsync(
|
||||
host: String,
|
||||
@@ -21,10 +21,10 @@ fun Scan.sendBluetoothAsync(
|
||||
val connected = if (isConnected) {
|
||||
true
|
||||
} else {
|
||||
connect(host)
|
||||
connect(host, true)
|
||||
}
|
||||
val sent = if (connected) {
|
||||
send(content)
|
||||
send(text, host)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@@ -35,21 +35,20 @@ fun Scan.sendBluetoothAsync(
|
||||
}
|
||||
|
||||
fun setBluetoothHosts(listPref: ListPreference) {
|
||||
val devices = try {
|
||||
BluetoothAdapter.getDefaultAdapter().bondedDevices
|
||||
} catch (e: SecurityException) {
|
||||
try {
|
||||
val devices = blue.bondedDevices ?: return
|
||||
listPref.entries = devices.map {
|
||||
it.name
|
||||
}.toTypedArray()
|
||||
listPref.entryValues = devices.map {
|
||||
it.address
|
||||
}.toTypedArray()
|
||||
listPref.callChangeListener(listPref.value)
|
||||
} catch (_: SecurityException) {
|
||||
// Do nothing, either the user has denied Bluetooth access
|
||||
// or the permission was removed by the system. We're catching
|
||||
// the exception to keep the app from crashing.
|
||||
null
|
||||
} ?: return
|
||||
listPref.entries = devices.map {
|
||||
it.name
|
||||
}.toTypedArray()
|
||||
listPref.entryValues = devices.map {
|
||||
it.address
|
||||
}.toTypedArray()
|
||||
listPref.callChangeListener(listPref.value)
|
||||
}
|
||||
}
|
||||
|
||||
private var socket: BluetoothSocket? = null
|
||||
@@ -62,7 +61,7 @@ private val uuid = UUID.fromString(
|
||||
|
||||
private var isConnected = false
|
||||
|
||||
private fun connect(deviceName: String): Boolean = try {
|
||||
private fun connect(deviceName: String, onceMore: Boolean): Boolean = try {
|
||||
val device = findByName(deviceName) ?: throw RuntimeException(
|
||||
"Bluetooth device not found"
|
||||
)
|
||||
@@ -71,32 +70,41 @@ private fun connect(deviceName: String): Boolean = try {
|
||||
writer = socket?.outputStream?.writer()
|
||||
isConnected = true
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
close()
|
||||
} catch (_: SecurityException) {
|
||||
if (onceMore) {
|
||||
connect(deviceName, false)
|
||||
} else {
|
||||
close()
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
private fun send(message: String): Boolean = try {
|
||||
private fun send(message: String, host: String): Boolean = try {
|
||||
writer?.apply {
|
||||
write(message)
|
||||
write("\n")
|
||||
flush()
|
||||
}
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
close()
|
||||
false
|
||||
|
||||
if (connect(host, false)) {
|
||||
send(message, host)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun close() {
|
||||
try {
|
||||
writer?.close()
|
||||
} catch (e: IOException) {
|
||||
} catch (_: IOException) {
|
||||
// Catch exception if writer wasn't initialized.
|
||||
}
|
||||
try {
|
||||
socket?.close()
|
||||
} catch (e: IOException) {
|
||||
} catch (_: IOException) {
|
||||
// Nothing we can do about this but keep
|
||||
// the app from crashing.
|
||||
}
|
||||
@@ -106,11 +114,15 @@ private fun close() {
|
||||
}
|
||||
|
||||
private fun findByName(findableName: String): BluetoothDevice? {
|
||||
val deviceList = blue.bondedDevices
|
||||
for (device in deviceList) {
|
||||
if (device.address == findableName) {
|
||||
return device
|
||||
try {
|
||||
val deviceList = blue.bondedDevices
|
||||
for (device in deviceList) {
|
||||
if (device.address == findableName) {
|
||||
return device
|
||||
}
|
||||
}
|
||||
} catch (_: SecurityException) {
|
||||
// Fall through.
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
package de.markusfisch.android.binaryeye.content
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import de.markusfisch.android.binaryeye.database.Scan
|
||||
import de.markusfisch.android.binaryeye.graphics.COLOR_BLACK
|
||||
import de.markusfisch.android.binaryeye.graphics.COLOR_WHITE
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.BarcodeFormat
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.BitMatrix
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.toBitmap
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.toSvg
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.toText
|
||||
import java.util.Arrays
|
||||
|
||||
enum class BarcodeColors {
|
||||
BLACK_ON_WHITE,
|
||||
WHITE_ON_BLACK,
|
||||
BLACK_ON_TRANSPARENT,
|
||||
WHITE_ON_TRANSPARENT;
|
||||
|
||||
fun foregroundColor(): Int = when (this) {
|
||||
BLACK_ON_WHITE,
|
||||
BLACK_ON_TRANSPARENT -> COLOR_BLACK
|
||||
|
||||
WHITE_ON_BLACK,
|
||||
WHITE_ON_TRANSPARENT -> COLOR_WHITE
|
||||
}
|
||||
|
||||
fun backgroundColor(): Int = when (this) {
|
||||
BLACK_ON_WHITE -> COLOR_WHITE
|
||||
WHITE_ON_BLACK -> COLOR_BLACK
|
||||
BLACK_ON_TRANSPARENT,
|
||||
WHITE_ON_TRANSPARENT -> 0
|
||||
}
|
||||
}
|
||||
|
||||
fun Scan.toBarcode(): Barcode<*> {
|
||||
val content = text.ifEmpty {
|
||||
raw
|
||||
}
|
||||
return if (symbol != null) {
|
||||
BitMatrixBarcode(
|
||||
symbol,
|
||||
content,
|
||||
format
|
||||
)
|
||||
} else {
|
||||
ContentBarcode(
|
||||
content,
|
||||
format,
|
||||
errorCorrectionLevel.toErrorCorrectionInt()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun String?.toErrorCorrectionInt() = when (this) {
|
||||
"L" -> 0
|
||||
"M" -> 4
|
||||
"Q" -> 6
|
||||
"H" -> 8
|
||||
else -> -1
|
||||
}
|
||||
|
||||
sealed class Barcode<T>(
|
||||
val content: T,
|
||||
val format: BarcodeFormat,
|
||||
val colors: BarcodeColors
|
||||
) {
|
||||
private var _bitmap: Bitmap? = null
|
||||
fun bitmap(): Bitmap {
|
||||
val b = _bitmap ?: bitmap(512)
|
||||
_bitmap = b
|
||||
return b
|
||||
}
|
||||
|
||||
fun bitmap(size: Int): Bitmap {
|
||||
return toBitmap(size)
|
||||
}
|
||||
|
||||
protected abstract fun toBitmap(size: Int): Bitmap
|
||||
|
||||
private var _svg: String? = null
|
||||
fun svg(): String {
|
||||
val s = _svg ?: toSvg()
|
||||
_svg = s
|
||||
return s
|
||||
}
|
||||
|
||||
protected abstract fun toSvg(): String
|
||||
|
||||
private var _text: String? = null
|
||||
fun text(): String {
|
||||
val t = _text ?: toText()
|
||||
_text = t
|
||||
return t
|
||||
}
|
||||
|
||||
protected abstract fun toText(): String
|
||||
|
||||
fun textOrHex(): String = when (content) {
|
||||
is String -> content
|
||||
is ByteArray -> content.toHexString()
|
||||
else -> throw IllegalArgumentException("Illegal arguments")
|
||||
}
|
||||
}
|
||||
|
||||
class ContentBarcode<T>(
|
||||
content: T,
|
||||
format: BarcodeFormat,
|
||||
val ecLevel: Int = -1,
|
||||
val margin: Int = -1,
|
||||
colors: BarcodeColors = BarcodeColors.BLACK_ON_WHITE
|
||||
) : Barcode<T>(content, format, colors) {
|
||||
override fun toBitmap(size: Int) = ZxingCpp.encodeAsBitmap(
|
||||
content, format, size, format.height(size), margin, ecLevel,
|
||||
setColor = colors.foregroundColor(),
|
||||
unsetColor = colors.backgroundColor()
|
||||
)
|
||||
|
||||
override fun toSvg() = ZxingCpp.encodeAsSvg(
|
||||
content, format, margin, ecLevel
|
||||
)
|
||||
|
||||
override fun toText() = ZxingCpp.encodeAsText(
|
||||
content, format, margin, ecLevel,
|
||||
inverted = colors == BarcodeColors.BLACK_ON_WHITE
|
||||
)
|
||||
}
|
||||
|
||||
class BitMatrixBarcode<T>(
|
||||
val bitMatrix: BitMatrix,
|
||||
content: T,
|
||||
format: BarcodeFormat,
|
||||
colors: BarcodeColors = BarcodeColors.BLACK_ON_WHITE
|
||||
) : Barcode<T>(content, format, colors) {
|
||||
override fun toBitmap(size: Int) = bitMatrix.inflate(size).toBitmap(
|
||||
setColor = colors.foregroundColor(),
|
||||
unsetColor = colors.backgroundColor()
|
||||
)
|
||||
|
||||
override fun toSvg() = bitMatrix.inflate().toSvg()
|
||||
|
||||
override fun toText() = bitMatrix.inflate().toText(
|
||||
inverted = colors == BarcodeColors.BLACK_ON_WHITE
|
||||
)
|
||||
}
|
||||
|
||||
private fun BitMatrix.inflate(
|
||||
size: Int = 0,
|
||||
quietZone: Boolean = true
|
||||
): BitMatrix {
|
||||
val extraModules = if (quietZone) 2 else 0
|
||||
val codeWidth = width + extraModules
|
||||
val codeHeight = height + extraModules
|
||||
var scale = size / codeWidth
|
||||
if (scale < 1) {
|
||||
if (size > 0 && !quietZone) {
|
||||
return this
|
||||
}
|
||||
scale = 1
|
||||
}
|
||||
val outWidth = codeWidth * scale
|
||||
val outHeight = codeHeight * scale
|
||||
val scaled = BitMatrix(
|
||||
outWidth,
|
||||
outHeight,
|
||||
ByteArray(outWidth * outHeight)
|
||||
)
|
||||
Arrays.fill(scaled.data, 1)
|
||||
val padding = extraModules / 2
|
||||
val left = padding * scale
|
||||
val top = padding * scale
|
||||
val right = outWidth - left
|
||||
val bottom = outHeight - top
|
||||
for (y in top until bottom) {
|
||||
var dst = y * outWidth + left
|
||||
val offset = (y - top) / scale * width
|
||||
for (x in left until right) {
|
||||
scaled.data[dst++] = data[
|
||||
offset + (x - left) / scale
|
||||
]
|
||||
}
|
||||
}
|
||||
return scaled
|
||||
}
|
||||
|
||||
private fun BarcodeFormat.height(size: Int) = when (this) {
|
||||
// 1D barcodes don't need as much vertical space.
|
||||
BarcodeFormat.CODABAR,
|
||||
BarcodeFormat.CODE_39,
|
||||
BarcodeFormat.CODE_93,
|
||||
BarcodeFormat.CODE_128,
|
||||
BarcodeFormat.DATA_BAR,
|
||||
BarcodeFormat.DATA_BAR_EXPANDED,
|
||||
BarcodeFormat.DX_FILM_EDGE,
|
||||
BarcodeFormat.EAN_8,
|
||||
BarcodeFormat.EAN_13,
|
||||
BarcodeFormat.ITF,
|
||||
BarcodeFormat.UPC_A,
|
||||
BarcodeFormat.UPC_E -> size / 3
|
||||
|
||||
else -> size
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package de.markusfisch.android.binaryeye.content
|
||||
|
||||
import android.content.Context
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
|
||||
private val idToLabel = hashMapOf(
|
||||
"DCA" to R.string.idl_vehicle_class,
|
||||
"DCB" to R.string.idl_restriction_codes,
|
||||
"DCD" to R.string.idl_endorsement_codes,
|
||||
"DBA" to R.string.idl_date_of_expiry,
|
||||
"DCS" to R.string.idl_customer_family_name,
|
||||
"DAC" to R.string.idl_customer_first_name,
|
||||
"DCT" to R.string.idl_customer_first_name_alt,
|
||||
"DAD" to R.string.idl_customer_middle_name,
|
||||
"DBD" to R.string.idl_date_of_issue,
|
||||
"DBB" to R.string.idl_date_of_birth,
|
||||
"DBC" to R.string.idl_sex,
|
||||
"DAY" to R.string.idl_eye_color,
|
||||
"DAU" to R.string.idl_body_height,
|
||||
"DAG" to R.string.idl_address_street,
|
||||
"DAI" to R.string.idl_address_city,
|
||||
"DAJ" to R.string.idl_address_jurisdiction_code,
|
||||
"DAK" to R.string.idl_address_postal_code,
|
||||
"DAQ" to R.string.idl_customer_id_number,
|
||||
"DCF" to R.string.idl_document_discriminator,
|
||||
"DCG" to R.string.idl_country,
|
||||
"DDE" to R.string.idl_family_name_truncation,
|
||||
"DDF" to R.string.idl_first_name_truncation,
|
||||
"DDG" to R.string.idl_middle_name_truncation,
|
||||
"DAH" to R.string.idl_address_street_2,
|
||||
"DAZ" to R.string.idl_hair_color,
|
||||
"DCI" to R.string.idl_place_of_birth,
|
||||
"DCJ" to R.string.idl_audit_information,
|
||||
"DCK" to R.string.idl_inventory_control_number,
|
||||
"DBN" to R.string.idl_alias_family_name,
|
||||
"DBG" to R.string.idl_alias_given_name,
|
||||
"DBS" to R.string.idl_alias_suffix_name,
|
||||
"DCU" to R.string.idl_name_suffix,
|
||||
"DCE" to R.string.idl_weight_range,
|
||||
"DCL" to R.string.idl_ethnicity,
|
||||
"DCM" to R.string.idl_standard_vehicle_classification,
|
||||
"DCN" to R.string.idl_standard_endorsement_code,
|
||||
"DCO" to R.string.idl_standard_restriction_code,
|
||||
"DCP" to R.string.idl_vehicle_classification_description,
|
||||
"DCQ" to R.string.idl_endorsement_code_description,
|
||||
"DCR" to R.string.idl_restriction_code_description,
|
||||
"DDA" to R.string.idl_compliance_type,
|
||||
"DDB" to R.string.idl_card_revision_date,
|
||||
"DDC" to R.string.idl_hazmat_endorsement_expiration_date,
|
||||
"DDD" to R.string.idl_limited_duration_document_indicator,
|
||||
"DAW" to R.string.idl_weight_pounds,
|
||||
"DAX" to R.string.idl_weight_kilograms,
|
||||
"DDH" to R.string.idl_under_18_until_date,
|
||||
"DDI" to R.string.idl_under_19_until_date,
|
||||
"DDJ" to R.string.idl_under_21_until_date,
|
||||
"DDK" to R.string.idl_organ_donor_indicator,
|
||||
"DDL" to R.string.idl_veteran_indicator
|
||||
)
|
||||
|
||||
fun Context.idlToRes(id: String): String {
|
||||
val resId = idToLabel[id]
|
||||
return if (resId != null) {
|
||||
getString(resId)
|
||||
} else {
|
||||
id
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package de.markusfisch.android.binaryeye.content
|
||||
|
||||
data class IdlInfo(
|
||||
var iin: String? = null,
|
||||
val elements: MutableMap<String, String> = linkedMapOf()
|
||||
)
|
||||
|
||||
object IdlParser {
|
||||
private fun resolveSex(value: String): String = when (value.firstOrNull()) {
|
||||
'1' -> "M"
|
||||
'2' -> "F"
|
||||
'9' -> "X"
|
||||
else -> value
|
||||
}
|
||||
|
||||
private fun skipWhitespace(s: String, p: Int): Int {
|
||||
var i = p
|
||||
while (i < s.length && s[i].isWhitespace()) {
|
||||
++i;
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
private fun findSubType(s: String, idlInfo: IdlInfo): String {
|
||||
val len = s.length
|
||||
var p = skipWhitespace(s, 0)
|
||||
|
||||
if (p < len && s[p] == '@') {
|
||||
while (p < len) {
|
||||
if (!s[p].isDigit()) {
|
||||
++p
|
||||
continue
|
||||
}
|
||||
var d = p
|
||||
while (d < len && s[d].isDigit()) {
|
||||
++d
|
||||
}
|
||||
if (d - p < 8 || len - d < 3) {
|
||||
p = d
|
||||
continue
|
||||
}
|
||||
|
||||
val code = s.substring(d, d + 2)
|
||||
if (code != "DL" && code != "ID") {
|
||||
p = d + 2
|
||||
continue
|
||||
}
|
||||
|
||||
idlInfo.elements["DL"] = code
|
||||
d += 2
|
||||
|
||||
while (d < len) {
|
||||
while (d < len && s[d] !in "DI") {
|
||||
++d
|
||||
}
|
||||
if (len - d < 2) break
|
||||
|
||||
val sec = s.substring(d, d + 2)
|
||||
if (sec == "DL" || sec == "ID") {
|
||||
return s.substring(d + 2)
|
||||
}
|
||||
++d
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
private fun findIIN(s: String): String? {
|
||||
val ansi = s.indexOf("ANSI").takeIf { it >= 0 } ?: return null
|
||||
var p = ansi + 4
|
||||
val len = s.length
|
||||
val whitespaceSkipped = skipWhitespace(s, p)
|
||||
if (whitespaceSkipped == p) {
|
||||
return null
|
||||
}
|
||||
p = whitespaceSkipped
|
||||
val iinStart = p
|
||||
var digits = 0
|
||||
while (digits < 6 && p < len && s[p].isDigit()) {
|
||||
++p
|
||||
++digits
|
||||
}
|
||||
return s.substring(iinStart, p)
|
||||
}
|
||||
|
||||
fun parse(s: String?): IdlInfo? {
|
||||
if (s.isNullOrEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
val idlInfo = IdlInfo().apply {
|
||||
iin = findIIN(s)
|
||||
}
|
||||
val data = findSubType(s, idlInfo)
|
||||
val len = data.length
|
||||
var start = 0
|
||||
|
||||
for (p in 0..len) {
|
||||
if (p < len && data[p] > '\u001f') {
|
||||
continue
|
||||
}
|
||||
|
||||
if (p - start > 3) {
|
||||
val vp = start + 3
|
||||
val key = data.substring(start, vp)
|
||||
|
||||
if (key.matches(Regex("[DZ][A-Z]{2}"))) {
|
||||
var value = data.substring(vp, p)
|
||||
if (key == "DBC") {
|
||||
value = resolveSex(value)
|
||||
}
|
||||
idlInfo.elements[key] = value.trim()
|
||||
}
|
||||
}
|
||||
start = p + 1
|
||||
}
|
||||
|
||||
return if (idlInfo.elements.isEmpty()) null else idlInfo
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import de.markusfisch.android.binaryeye.BuildConfig
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
import de.markusfisch.android.binaryeye.widget.toast
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
|
||||
fun Context.execShareIntent(intent: Intent): Boolean = if (
|
||||
!startIntent(intent)
|
||||
@@ -31,7 +33,7 @@ fun Context.startIntent(intent: Intent): Boolean = try {
|
||||
// necessary.
|
||||
startActivity(intent)
|
||||
true
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
false
|
||||
}
|
||||
|
||||
@@ -59,6 +61,44 @@ fun Context.shareText(text: String, mimeType: String = "text/plain") {
|
||||
})
|
||||
}
|
||||
|
||||
private var lastShareFile: File? = null
|
||||
fun wipeLastShareFile() {
|
||||
lastShareFile?.let {
|
||||
it.delete()
|
||||
lastShareFile = null
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> Context.shareAsFile(content: T, fileName: String) {
|
||||
val (bytes, mimeType) = when (content) {
|
||||
is ByteArray -> Pair(
|
||||
content,
|
||||
"text/plain"
|
||||
)
|
||||
|
||||
is String -> Pair(
|
||||
content.toByteArray(),
|
||||
"application/octet-stream"
|
||||
)
|
||||
|
||||
else -> throw IllegalArgumentException(
|
||||
"Unsupported content text for shareAsFile()"
|
||||
)
|
||||
}
|
||||
wipeLastShareFile()
|
||||
val file = File(externalCacheDir, fileName)
|
||||
try {
|
||||
FileOutputStream(file).use {
|
||||
it.write(bytes)
|
||||
}
|
||||
} catch (_: IOException) {
|
||||
toast(R.string.error_saving_file)
|
||||
return
|
||||
}
|
||||
lastShareFile = file
|
||||
shareFile(file, mimeType)
|
||||
}
|
||||
|
||||
fun Context.shareFile(file: File, mimeType: String) {
|
||||
getUriForFile(file)?.let {
|
||||
shareUri(it, mimeType)
|
||||
|
||||
@@ -32,7 +32,8 @@ private fun exportCsv(
|
||||
val columns = arrayOf(
|
||||
Database.SCANS_DATETIME,
|
||||
Database.SCANS_FORMAT,
|
||||
Database.SCANS_CONTENT,
|
||||
Database.SCANS_NAME,
|
||||
Database.SCANS_TEXT,
|
||||
Database.SCANS_ERROR_CORRECTION_LEVEL,
|
||||
Database.SCANS_VERSION,
|
||||
Database.SCANS_SEQUENCE_SIZE,
|
||||
@@ -46,7 +47,7 @@ private fun exportCsv(
|
||||
val indices = columns.map {
|
||||
cursor.getColumnIndex(it)
|
||||
}
|
||||
val contentIndex = cursor.getColumnIndex(Database.SCANS_CONTENT)
|
||||
val contentIndex = cursor.getColumnIndex(Database.SCANS_TEXT)
|
||||
val rawIndex = cursor.getColumnIndex(Database.SCANS_RAW)
|
||||
outputStream.write(
|
||||
columns.joinToString(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.markusfisch.android.binaryeye.database
|
||||
|
||||
import android.database.Cursor
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp
|
||||
|
||||
// Overwrite Kotlin's ".use" function for Cursor because Cursor cannot
|
||||
// be cast to Closeable below API level 16. This should be removed when
|
||||
@@ -30,3 +31,18 @@ fun Cursor.getBlob(name: String): ByteArray? {
|
||||
val idx = getColumnIndex(name)
|
||||
return if (idx < 0) null else getBlob(idx)
|
||||
}
|
||||
|
||||
fun Cursor.getBitMatrix(
|
||||
widthName: String,
|
||||
heightName: String,
|
||||
dataName: String
|
||||
): ZxingCpp.BitMatrix? {
|
||||
val width = getInt(widthName)
|
||||
val height = getInt(heightName)
|
||||
val data = getBlob(dataName)
|
||||
return if (width > 0 && height > 0 && data != null) {
|
||||
ZxingCpp.BitMatrix(width, height, data)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import android.database.Cursor
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import de.markusfisch.android.binaryeye.app.prefs
|
||||
import de.markusfisch.android.binaryeye.preference.Preferences
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.BarcodeFormat
|
||||
|
||||
class Database {
|
||||
private lateinit var db: SQLiteDatabase
|
||||
@@ -14,12 +16,12 @@ class Database {
|
||||
db = OpenHelper(context).writableDatabase
|
||||
}
|
||||
|
||||
fun getScans(query: String? = null): Cursor? = db.rawQuery(
|
||||
fun getScans(query: String? = null): Cursor = db.rawQuery(
|
||||
"""SELECT
|
||||
$SCANS_ID,
|
||||
$SCANS_DATETIME,
|
||||
$SCANS_NAME,
|
||||
$SCANS_CONTENT,
|
||||
$SCANS_TEXT,
|
||||
$SCANS_FORMAT
|
||||
FROM $SCANS
|
||||
${getWhereClause(query)}
|
||||
@@ -27,12 +29,12 @@ class Database {
|
||||
""".trimMargin(), getWhereArguments(query)
|
||||
)
|
||||
|
||||
fun getScansDetailed(query: String? = null): Cursor? = db.rawQuery(
|
||||
fun getScansDetailed(query: String? = null): Cursor = db.rawQuery(
|
||||
"""SELECT
|
||||
$SCANS_ID,
|
||||
$SCANS_DATETIME,
|
||||
$SCANS_NAME,
|
||||
$SCANS_CONTENT,
|
||||
$SCANS_TEXT,
|
||||
$SCANS_RAW,
|
||||
$SCANS_FORMAT,
|
||||
$SCANS_ERROR_CORRECTION_LEVEL,
|
||||
@@ -54,7 +56,7 @@ class Database {
|
||||
query: String?,
|
||||
prefix: String = "WHERE"
|
||||
) = if (query?.isNotEmpty() == true) {
|
||||
"""$prefix $SCANS_CONTENT LIKE ?
|
||||
"""$prefix $SCANS_TEXT LIKE ?
|
||||
OR $SCANS_NAME LIKE ?"""
|
||||
} else {
|
||||
""
|
||||
@@ -69,12 +71,12 @@ class Database {
|
||||
null
|
||||
}
|
||||
|
||||
fun getScan(id: Long): Scan? = db.rawQuery(
|
||||
fun getScansDetailed(ids: LongArray): Cursor = db.rawQuery(
|
||||
"""SELECT
|
||||
$SCANS_ID,
|
||||
$SCANS_DATETIME,
|
||||
$SCANS_NAME,
|
||||
$SCANS_CONTENT,
|
||||
$SCANS_TEXT,
|
||||
$SCANS_RAW,
|
||||
$SCANS_FORMAT,
|
||||
$SCANS_ERROR_CORRECTION_LEVEL,
|
||||
@@ -87,16 +89,49 @@ class Database {
|
||||
$SCANS_GTIN_PRICE,
|
||||
$SCANS_GTIN_ISSUE_NUMBER
|
||||
FROM $SCANS
|
||||
WHERE $SCANS_ID IN (${ids.joinToString(",")})
|
||||
ORDER BY $SCANS_DATETIME DESC
|
||||
""".trimMargin(), null
|
||||
)
|
||||
|
||||
fun getScan(id: Long): Scan? = db.rawQuery(
|
||||
"""SELECT
|
||||
$SCANS_ID,
|
||||
$SCANS_DATETIME,
|
||||
$SCANS_NAME,
|
||||
$SCANS_TEXT,
|
||||
$SCANS_RAW,
|
||||
$SCANS_FORMAT,
|
||||
$SCANS_ERROR_CORRECTION_LEVEL,
|
||||
$SCANS_VERSION,
|
||||
$SCANS_DATA_MASK,
|
||||
$SCANS_SYMBOL_WIDTH,
|
||||
$SCANS_SYMBOL_HEIGHT,
|
||||
$SCANS_SYMBOL_DATA,
|
||||
$SCANS_SEQUENCE_SIZE,
|
||||
$SCANS_SEQUENCE_INDEX,
|
||||
$SCANS_SEQUENCE_ID,
|
||||
$SCANS_GTIN_COUNTRY,
|
||||
$SCANS_GTIN_ADD_ON,
|
||||
$SCANS_GTIN_PRICE,
|
||||
$SCANS_GTIN_ISSUE_NUMBER
|
||||
FROM $SCANS
|
||||
WHERE $SCANS_ID = ?
|
||||
""".trimMargin(), arrayOf("$id")
|
||||
)?.use {
|
||||
).use {
|
||||
if (it.moveToFirst()) {
|
||||
Scan(
|
||||
it.getString(SCANS_CONTENT),
|
||||
it.getString(SCANS_TEXT),
|
||||
it.getBlob(SCANS_RAW),
|
||||
it.getString(SCANS_FORMAT),
|
||||
BarcodeFormat.valueOf(it.getString(SCANS_FORMAT)),
|
||||
it.getString(SCANS_ERROR_CORRECTION_LEVEL),
|
||||
it.getString(SCANS_VERSION),
|
||||
it.getInt(SCANS_DATA_MASK),
|
||||
it.getBitMatrix(
|
||||
SCANS_SYMBOL_WIDTH,
|
||||
SCANS_SYMBOL_HEIGHT,
|
||||
SCANS_SYMBOL_DATA
|
||||
),
|
||||
it.getInt(SCANS_SEQUENCE_SIZE),
|
||||
it.getInt(SCANS_SEQUENCE_INDEX),
|
||||
it.getString(SCANS_SEQUENCE_ID),
|
||||
@@ -105,77 +140,113 @@ class Database {
|
||||
it.getString(SCANS_GTIN_PRICE),
|
||||
it.getString(SCANS_GTIN_ISSUE_NUMBER),
|
||||
it.getString(SCANS_DATETIME),
|
||||
it.getLong(SCANS_ID)
|
||||
it.getLong(SCANS_ID),
|
||||
it.getString(SCANS_NAME),
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun insertScan(scan: Scan): Long = db.insert(
|
||||
SCANS,
|
||||
null,
|
||||
ContentValues().apply {
|
||||
put(SCANS_DATETIME, scan.dateTime)
|
||||
val isRaw = scan.raw != null
|
||||
if (isRaw) {
|
||||
put(SCANS_CONTENT, "")
|
||||
put(SCANS_RAW, scan.raw)
|
||||
} else {
|
||||
put(SCANS_CONTENT, scan.content)
|
||||
}
|
||||
put(SCANS_FORMAT, scan.format)
|
||||
scan.errorCorrectionLevel?.let {
|
||||
put(SCANS_ERROR_CORRECTION_LEVEL, it)
|
||||
}
|
||||
put(SCANS_VERSION, scan.version)
|
||||
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) {
|
||||
fun insertScan(scan: Scan): Long {
|
||||
when (prefs.ignoreDuplicates()) {
|
||||
Preferences.Companion.IgnoreDuplicates.Consecutive -> {
|
||||
val id = getIdOfLastScan(
|
||||
get(SCANS_CONTENT) as String,
|
||||
if (isRaw) get(SCANS_RAW) as ByteArray else null,
|
||||
scan.text,
|
||||
scan.raw,
|
||||
scan.format
|
||||
)
|
||||
if (id > 0L) {
|
||||
return id
|
||||
}
|
||||
}
|
||||
|
||||
Preferences.Companion.IgnoreDuplicates.Any -> {
|
||||
val id = getIdOfScanByContent(scan.text, scan.format)
|
||||
if (id > 0L) {
|
||||
return id
|
||||
}
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
)
|
||||
return db.insert(
|
||||
SCANS,
|
||||
null,
|
||||
ContentValues().apply {
|
||||
put(SCANS_DATETIME, scan.dateTime)
|
||||
put(SCANS_TEXT, scan.text)
|
||||
if (scan.raw != null) {
|
||||
put(SCANS_RAW, scan.raw)
|
||||
}
|
||||
put(SCANS_FORMAT, scan.format.name)
|
||||
scan.errorCorrectionLevel?.let {
|
||||
put(SCANS_ERROR_CORRECTION_LEVEL, it)
|
||||
}
|
||||
put(SCANS_VERSION, scan.version)
|
||||
put(SCANS_DATA_MASK, scan.dataMask)
|
||||
if (scan.symbol != null) {
|
||||
put(SCANS_SYMBOL_WIDTH, scan.symbol.width)
|
||||
put(SCANS_SYMBOL_HEIGHT, scan.symbol.height)
|
||||
put(SCANS_SYMBOL_DATA, scan.symbol.data)
|
||||
}
|
||||
put(SCANS_SEQUENCE_SIZE, scan.sequenceSize)
|
||||
put(SCANS_SEQUENCE_INDEX, scan.sequenceIndex)
|
||||
put(SCANS_SEQUENCE_ID, scan.sequenceId)
|
||||
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) }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun getIdOfLastScan(
|
||||
content: String,
|
||||
raw: ByteArray?,
|
||||
format: String
|
||||
format: BarcodeFormat
|
||||
): Long = db.rawQuery(
|
||||
"""SELECT
|
||||
$SCANS_ID,
|
||||
$SCANS_CONTENT,
|
||||
$SCANS_TEXT,
|
||||
$SCANS_RAW,
|
||||
$SCANS_FORMAT
|
||||
FROM $SCANS
|
||||
ORDER BY $SCANS_ID DESC
|
||||
LIMIT 1
|
||||
""".trimMargin(), null
|
||||
)?.use {
|
||||
).use {
|
||||
if (it.count > 0 &&
|
||||
it.moveToFirst() &&
|
||||
it.getString(SCANS_CONTENT) == content &&
|
||||
it.getString(SCANS_TEXT) == content &&
|
||||
(raw == null || it.getBlob(SCANS_RAW)
|
||||
?.contentEquals(raw) == true) &&
|
||||
it.getString(SCANS_FORMAT) == format
|
||||
it.getString(SCANS_FORMAT) == format.name
|
||||
) {
|
||||
it.getLong(SCANS_ID)
|
||||
} else {
|
||||
0L
|
||||
}
|
||||
} ?: 0L
|
||||
}
|
||||
|
||||
private fun getIdOfScanByContent(
|
||||
content: String,
|
||||
format: BarcodeFormat
|
||||
): Long = db.rawQuery(
|
||||
"""SELECT
|
||||
$SCANS_ID
|
||||
FROM $SCANS
|
||||
WHERE $SCANS_TEXT = ?
|
||||
AND $SCANS_FORMAT = ?
|
||||
LIMIT 1
|
||||
""".trimMargin(), arrayOf(content, format.name)
|
||||
).use {
|
||||
if (it.moveToFirst()) {
|
||||
it.getLong(SCANS_ID)
|
||||
} else {
|
||||
0L
|
||||
}
|
||||
}
|
||||
|
||||
fun removeScan(id: Long) {
|
||||
db.delete(SCANS, "$SCANS_ID = ?", arrayOf("$id"))
|
||||
@@ -192,7 +263,7 @@ class Database {
|
||||
}
|
||||
|
||||
private class OpenHelper(context: Context) :
|
||||
SQLiteOpenHelper(context, FILE_NAME, null, 6) {
|
||||
SQLiteOpenHelper(context, FILE_NAME, null, 8) {
|
||||
override fun onCreate(db: SQLiteDatabase) {
|
||||
db.createScans()
|
||||
}
|
||||
@@ -217,6 +288,12 @@ class Database {
|
||||
if (oldVersion < 6) {
|
||||
db.migrateToVersionString()
|
||||
}
|
||||
if (oldVersion < 7) {
|
||||
db.addDataMaskColumn()
|
||||
}
|
||||
if (oldVersion < 8) {
|
||||
db.addSymbolColumns()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,12 +303,16 @@ class Database {
|
||||
const val SCANS_ID = "_id"
|
||||
const val SCANS_DATETIME = "_datetime"
|
||||
const val SCANS_NAME = "name"
|
||||
const val SCANS_CONTENT = "content"
|
||||
const val SCANS_TEXT = "content"
|
||||
const val SCANS_RAW = "raw"
|
||||
const val SCANS_FORMAT = "format"
|
||||
const val SCANS_ERROR_CORRECTION_LEVEL = "error_correction_level"
|
||||
const val SCANS_VERSION_NUMBER = "version_number"
|
||||
const val SCANS_VERSION = "version"
|
||||
const val SCANS_DATA_MASK = "data_mask"
|
||||
const val SCANS_SYMBOL_WIDTH = "symbol_width"
|
||||
const val SCANS_SYMBOL_HEIGHT = "symbol_height"
|
||||
const val SCANS_SYMBOL_DATA = "symbol_data"
|
||||
const val SCANS_ISSUE_NUMBER = "issue_number"
|
||||
const val SCANS_ORIENTATION = "orientation"
|
||||
const val SCANS_OTHER_META_DATA = "other_meta_data"
|
||||
@@ -254,11 +335,15 @@ class Database {
|
||||
$SCANS_ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
$SCANS_DATETIME DATETIME NOT NULL,
|
||||
$SCANS_NAME TEXT,
|
||||
$SCANS_CONTENT TEXT NOT NULL,
|
||||
$SCANS_TEXT TEXT NOT NULL,
|
||||
$SCANS_RAW BLOB,
|
||||
$SCANS_FORMAT TEXT NOT NULL,
|
||||
$SCANS_ERROR_CORRECTION_LEVEL TEXT,
|
||||
$SCANS_VERSION TEXT,
|
||||
$SCANS_DATA_MASK INTEGER,
|
||||
$SCANS_SYMBOL_WIDTH INTEGER,
|
||||
$SCANS_SYMBOL_HEIGHT INTEGER,
|
||||
$SCANS_SYMBOL_DATA BLOB,
|
||||
$SCANS_SEQUENCE_SIZE INTEGER,
|
||||
$SCANS_SEQUENCE_INDEX INTEGER,
|
||||
$SCANS_SEQUENCE_ID TEXT,
|
||||
@@ -286,7 +371,7 @@ class Database {
|
||||
}
|
||||
|
||||
private fun SQLiteDatabase.addNameColumn() {
|
||||
execSQL("ALTER TABLE $SCANS ADD COLUMN $SCANS_NAME TEXT".trimMargin())
|
||||
execSQL("ALTER TABLE $SCANS ADD COLUMN $SCANS_NAME TEXT")
|
||||
}
|
||||
|
||||
private fun SQLiteDatabase.migrateToZxingCpp() {
|
||||
@@ -310,5 +395,16 @@ class Database {
|
||||
execSQL("ALTER TABLE $SCANS ADD $SCANS_VERSION TEXT")
|
||||
execSQL("UPDATE $SCANS SET $SCANS_VERSION = $SCANS_VERSION_NUMBER")
|
||||
}
|
||||
|
||||
private fun SQLiteDatabase.addDataMaskColumn() {
|
||||
execSQL("ALTER TABLE $SCANS ADD $SCANS_DATA_MASK INTEGER")
|
||||
execSQL("UPDATE $SCANS SET $SCANS_DATA_MASK = -1")
|
||||
}
|
||||
|
||||
private fun SQLiteDatabase.addSymbolColumns() {
|
||||
execSQL("ALTER TABLE $SCANS ADD $SCANS_SYMBOL_WIDTH INTEGER")
|
||||
execSQL("ALTER TABLE $SCANS ADD $SCANS_SYMBOL_HEIGHT INTEGER")
|
||||
execSQL("ALTER TABLE $SCANS ADD $SCANS_SYMBOL_DATA BLOB")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@ fun Cursor.exportJson(): String? {
|
||||
val columns = arrayOf(
|
||||
Database.SCANS_DATETIME,
|
||||
Database.SCANS_FORMAT,
|
||||
Database.SCANS_CONTENT,
|
||||
Database.SCANS_NAME,
|
||||
Database.SCANS_TEXT,
|
||||
Database.SCANS_ERROR_CORRECTION_LEVEL,
|
||||
Database.SCANS_VERSION,
|
||||
Database.SCANS_SEQUENCE_SIZE,
|
||||
@@ -38,7 +39,7 @@ fun Cursor.exportJson(): String? {
|
||||
val indices = columns.map {
|
||||
Pair(getColumnIndex(it), it)
|
||||
}
|
||||
val contentIndex = getColumnIndex(Database.SCANS_CONTENT)
|
||||
val contentIndex = getColumnIndex(Database.SCANS_TEXT)
|
||||
val rawIndex = getColumnIndex(Database.SCANS_RAW)
|
||||
val root = JSONArray()
|
||||
do {
|
||||
@@ -69,7 +70,7 @@ private fun Cursor.toJsonObject(
|
||||
obj.put(it.second, value ?: "")
|
||||
}
|
||||
obj
|
||||
} catch (e: JSONException) {
|
||||
} catch (_: JSONException) {
|
||||
obj
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
package de.markusfisch.android.binaryeye.database
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Build
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.text.format.DateFormat
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.BarcodeFormat
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.BitMatrix
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.ContentType
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.Format
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.Result
|
||||
import java.util.Locale
|
||||
|
||||
data class Scan(
|
||||
val content: String,
|
||||
val text: String,
|
||||
val raw: ByteArray?,
|
||||
val format: String,
|
||||
val format: BarcodeFormat,
|
||||
val errorCorrectionLevel: String? = null,
|
||||
val version: String? = null,
|
||||
val dataMask: Int = -1,
|
||||
val symbol: BitMatrix? = null,
|
||||
val sequenceSize: Int = -1,
|
||||
val sequenceIndex: Int = -1,
|
||||
val sequenceId: String = "",
|
||||
@@ -24,7 +26,8 @@ data class Scan(
|
||||
val price: String? = null,
|
||||
val issueNumber: String? = null,
|
||||
val dateTime: String = getDateTime(),
|
||||
var id: Long = 0L
|
||||
var id: Long = 0L,
|
||||
val label: String? = null
|
||||
) : Parcelable {
|
||||
// Needs to be overwritten manually, as ByteArray is an array and
|
||||
// this isn't handled well by Kotlin.
|
||||
@@ -40,12 +43,14 @@ data class Scan(
|
||||
|
||||
return id == other.id &&
|
||||
dateTime == other.dateTime &&
|
||||
content == other.content &&
|
||||
text == other.text &&
|
||||
((raw == null && other.raw == null) ||
|
||||
(raw != null && other.raw != null && raw.contentEquals(other.raw))) &&
|
||||
(raw != null && other.raw != null &&
|
||||
raw.contentEquals(other.raw))) &&
|
||||
format == other.format &&
|
||||
errorCorrectionLevel == other.errorCorrectionLevel &&
|
||||
version == other.version &&
|
||||
dataMask == other.dataMask &&
|
||||
sequenceSize == other.sequenceSize &&
|
||||
sequenceIndex == other.sequenceIndex &&
|
||||
sequenceId == other.sequenceId &&
|
||||
@@ -60,11 +65,12 @@ data class Scan(
|
||||
override fun hashCode(): Int {
|
||||
var result = id.hashCode()
|
||||
result = 31 * result + dateTime.hashCode()
|
||||
result = 31 * result + content.hashCode()
|
||||
result = 31 * result + text.hashCode()
|
||||
result = 31 * result + (raw?.contentHashCode() ?: 0)
|
||||
result = 31 * result + format.hashCode()
|
||||
result = 31 * result + (errorCorrectionLevel?.hashCode() ?: 0)
|
||||
result = 31 * result + version.hashCode()
|
||||
result = 31 * result + dataMask
|
||||
result = 31 * result + sequenceSize
|
||||
result = 31 * result + sequenceIndex
|
||||
result = 31 * result + sequenceId.hashCode()
|
||||
@@ -76,11 +82,13 @@ data class Scan(
|
||||
}
|
||||
|
||||
private constructor(parcel: Parcel) : this(
|
||||
content = parcel.readString() ?: "",
|
||||
text = parcel.readString() ?: "",
|
||||
raw = parcel.readSizedByteArray(),
|
||||
format = parcel.readString() ?: "",
|
||||
format = BarcodeFormat.valueOf(parcel.readString() ?: ""),
|
||||
errorCorrectionLevel = parcel.readString(),
|
||||
version = parcel.readString(),
|
||||
dataMask = parcel.readInt(),
|
||||
symbol = parcel.readBitMatrix(),
|
||||
sequenceSize = parcel.readInt(),
|
||||
sequenceIndex = parcel.readInt(),
|
||||
sequenceId = parcel.readString() ?: "",
|
||||
@@ -89,16 +97,19 @@ data class Scan(
|
||||
price = parcel.readString(),
|
||||
issueNumber = parcel.readString(),
|
||||
dateTime = parcel.readString() ?: "",
|
||||
id = parcel.readLong()
|
||||
id = parcel.readLong(),
|
||||
label = parcel.readString()
|
||||
)
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.apply {
|
||||
writeString(content)
|
||||
writeString(text)
|
||||
writeSizedByteArray(raw)
|
||||
writeString(format)
|
||||
writeString(format.name)
|
||||
writeString(errorCorrectionLevel)
|
||||
writeString(version)
|
||||
writeInt(dataMask)
|
||||
writeBitMatrix(symbol)
|
||||
writeInt(sequenceSize)
|
||||
writeInt(sequenceIndex)
|
||||
writeString(sequenceId)
|
||||
@@ -108,6 +119,7 @@ data class Scan(
|
||||
writeString(issueNumber)
|
||||
writeString(dateTime)
|
||||
writeLong(id)
|
||||
writeString(label)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,26 +135,20 @@ data class Scan(
|
||||
}
|
||||
|
||||
fun Result.toScan(): Scan {
|
||||
val content: String
|
||||
val raw: ByteArray?
|
||||
when (contentType) {
|
||||
ContentType.GS1,
|
||||
ContentType.ISO15434,
|
||||
ContentType.TEXT -> {
|
||||
content = text
|
||||
raw = null
|
||||
}
|
||||
else -> {
|
||||
content = ""
|
||||
raw = rawBytes
|
||||
}
|
||||
}
|
||||
return Scan(
|
||||
content,
|
||||
raw,
|
||||
when (contentType) {
|
||||
ContentType.GS1,
|
||||
ContentType.ISO15434,
|
||||
ContentType.TEXT -> text
|
||||
|
||||
else -> "" // Show as binary.
|
||||
},
|
||||
rawBytes,
|
||||
format,
|
||||
ecLevel,
|
||||
version,
|
||||
dataMask,
|
||||
symbol,
|
||||
sequenceSize,
|
||||
sequenceIndex,
|
||||
sequenceId,
|
||||
@@ -153,41 +159,6 @@ fun Result.toScan(): Scan {
|
||||
)
|
||||
}
|
||||
|
||||
data class Recreation(
|
||||
val format: Format,
|
||||
val ecLevel: Int,
|
||||
val size: Int,
|
||||
val margin: Int
|
||||
) {
|
||||
fun encode(content: String): Bitmap? = try {
|
||||
ZxingCpp.encodeAsBitmap(
|
||||
content, format, size, size, margin, ecLevel
|
||||
)
|
||||
} catch (_: RuntimeException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun Scan.toRecreation(
|
||||
size: Int = 128,
|
||||
margin: Int = -1
|
||||
) = if (content.isEmpty()) {
|
||||
null
|
||||
} else {
|
||||
Recreation(
|
||||
Format.valueOf(format),
|
||||
when (errorCorrectionLevel) {
|
||||
"L" -> 0
|
||||
"M" -> 4
|
||||
"Q" -> 6
|
||||
"H" -> 8
|
||||
else -> -1
|
||||
},
|
||||
size,
|
||||
margin
|
||||
)
|
||||
}
|
||||
|
||||
private fun getDateTime(
|
||||
time: Long = System.currentTimeMillis()
|
||||
) = DateFormat.format(
|
||||
@@ -199,7 +170,11 @@ private fun getDateTime(
|
||||
"yyyy-MM-dd HH:mm:ss"
|
||||
},
|
||||
time
|
||||
).toString()
|
||||
).toString() + String.format(
|
||||
Locale.getDefault(),
|
||||
":%03d",
|
||||
time % 1000
|
||||
)
|
||||
|
||||
private fun Parcel.writeSizedByteArray(array: ByteArray?) {
|
||||
val size = array?.size ?: 0
|
||||
@@ -219,3 +194,21 @@ private fun Parcel.readSizedByteArray(): ByteArray? {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun Parcel.writeBitMatrix(bm: BitMatrix?) {
|
||||
writeInt(bm?.width ?: 0)
|
||||
bm?.let {
|
||||
writeInt(bm.height)
|
||||
writeSizedByteArray(bm.data)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Parcel.readBitMatrix(): BitMatrix? {
|
||||
val width = readInt()
|
||||
if (width < 1) {
|
||||
return null
|
||||
}
|
||||
val height = readInt()
|
||||
val data = readSizedByteArray() ?: return null
|
||||
return BitMatrix(width, height, data)
|
||||
}
|
||||
|
||||
+377
-116
@@ -4,44 +4,71 @@ import android.annotation.SuppressLint
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Matrix
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.Fragment
|
||||
import android.view.*
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageView
|
||||
import android.widget.SeekBar
|
||||
import android.widget.TextView
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
import de.markusfisch.android.binaryeye.app.db
|
||||
import de.markusfisch.android.binaryeye.app.hasWritePermission
|
||||
import de.markusfisch.android.binaryeye.app.prefs
|
||||
import de.markusfisch.android.binaryeye.content.Barcode
|
||||
import de.markusfisch.android.binaryeye.content.BarcodeColors
|
||||
import de.markusfisch.android.binaryeye.content.BitMatrixBarcode
|
||||
import de.markusfisch.android.binaryeye.content.ContentBarcode
|
||||
import de.markusfisch.android.binaryeye.content.copyToClipboard
|
||||
import de.markusfisch.android.binaryeye.content.shareFile
|
||||
import de.markusfisch.android.binaryeye.content.shareText
|
||||
import de.markusfisch.android.binaryeye.graphics.COLOR_BLACK
|
||||
import de.markusfisch.android.binaryeye.graphics.COLOR_WHITE
|
||||
import de.markusfisch.android.binaryeye.database.toScan
|
||||
import de.markusfisch.android.binaryeye.io.addSuffixIfNotGiven
|
||||
import de.markusfisch.android.binaryeye.io.toSaveResult
|
||||
import de.markusfisch.android.binaryeye.io.writeExternalFile
|
||||
import de.markusfisch.android.binaryeye.net.createEncodeDeeplink
|
||||
import de.markusfisch.android.binaryeye.os.getScreenBrightness
|
||||
import de.markusfisch.android.binaryeye.os.setScreenBrightness
|
||||
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.zxingcpp.ZxingCpp
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.Format
|
||||
import kotlinx.coroutines.*
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.BarcodeFormat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.OutputStream
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
class BarcodeFragment : Fragment() {
|
||||
private enum class FileType {
|
||||
PNG, SVG, TXT
|
||||
PNG, JPG, SVG, TXT
|
||||
}
|
||||
|
||||
private val parentJob = Job()
|
||||
private val scope = CoroutineScope(Dispatchers.IO + parentJob)
|
||||
|
||||
private lateinit var barcode: Barcode
|
||||
private lateinit var imageView: ConfinedScalingImageView
|
||||
private lateinit var barcode: Barcode<*>
|
||||
private lateinit var addToHistoryItem: MenuItem
|
||||
private lateinit var brightenScreenItem: MenuItem
|
||||
|
||||
private var currentBrightness = -1f
|
||||
|
||||
override fun onCreate(state: Bundle?) {
|
||||
super.onCreate(state)
|
||||
@@ -62,6 +89,9 @@ class BarcodeFragment : Fragment() {
|
||||
false
|
||||
)
|
||||
|
||||
// Make `imageView` available for `onPause()`.
|
||||
imageView = view.findViewById(R.id.barcode)
|
||||
|
||||
val bitmap: Bitmap
|
||||
try {
|
||||
barcode = arguments?.toBarcode() ?: throw IllegalArgumentException(
|
||||
@@ -71,7 +101,7 @@ class BarcodeFragment : Fragment() {
|
||||
bitmap = barcode.bitmap()
|
||||
} catch (e: Exception) {
|
||||
var message = e.message
|
||||
if (message == null || message.isEmpty()) {
|
||||
if (message.isNullOrEmpty()) {
|
||||
message = getString(R.string.error_encoding_barcode)
|
||||
}
|
||||
message?.let {
|
||||
@@ -81,15 +111,20 @@ class BarcodeFragment : Fragment() {
|
||||
return null
|
||||
}
|
||||
|
||||
val imageView = view.findViewById<ConfinedScalingImageView>(
|
||||
R.id.barcode
|
||||
)
|
||||
imageView.setImageBitmap(bitmap)
|
||||
imageView.post {
|
||||
// Make sure to invoke this after ScalingImageView.onLayout().
|
||||
imageView.runAfterLayout = {
|
||||
val scale = prefs.previewScale
|
||||
if (scale > 0) {
|
||||
imageView.setScale(
|
||||
scale,
|
||||
bitmap.width / 2f,
|
||||
bitmap.height / 2f
|
||||
)
|
||||
}
|
||||
|
||||
imageView.minWidth = min(
|
||||
imageView.minWidth / 2f,
|
||||
barcode.size.toFloat()
|
||||
max(bitmap.width, bitmap.height).toFloat()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -101,27 +136,67 @@ class BarcodeFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
(view.findViewById(R.id.inset_layout) as View).setPaddingFromWindowInsets()
|
||||
imageView.doOnApplyWindowInsets { v, insets ->
|
||||
(v as ConfinedScalingImageView).insets.set(insets)
|
||||
view.findViewById<View>(R.id.inset_layout).setPaddingFromWindowInsets()
|
||||
imageView.doOnApplyWindowInsets { _, insets ->
|
||||
imageView.insets.set(insets)
|
||||
}
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
private fun Bundle.toBarcode() = Barcode(
|
||||
getString(CONTENT) ?: throw IllegalArgumentException(
|
||||
"content cannot be null"
|
||||
),
|
||||
Format.valueOf(
|
||||
private fun Bundle.toBarcode(): Barcode<*> {
|
||||
val content =
|
||||
getString(CONTENT_TEXT) ?: getByteArray(CONTENT_RAW) ?: throw IllegalArgumentException(
|
||||
"content cannot be null"
|
||||
)
|
||||
val barcodeFormat = BarcodeFormat.valueOf(
|
||||
getString(FORMAT) ?: throw IllegalArgumentException(
|
||||
"format cannot be null"
|
||||
)
|
||||
),
|
||||
getInt(SIZE),
|
||||
getInt(EC_LEVEL),
|
||||
Colors.values()[getInt(COLORS)]
|
||||
)
|
||||
)
|
||||
val colors = BarcodeColors.entries[getInt(COLORS)]
|
||||
val data = getByteArray(BIT_MATRIX_DATA)
|
||||
return if (data != null) {
|
||||
BitMatrixBarcode(
|
||||
ZxingCpp.BitMatrix(
|
||||
getInt(BIT_MATRIX_WIDTH),
|
||||
getInt(BIT_MATRIX_HEIGHT),
|
||||
data
|
||||
),
|
||||
content,
|
||||
barcodeFormat,
|
||||
colors
|
||||
)
|
||||
} else {
|
||||
ContentBarcode(
|
||||
content,
|
||||
barcodeFormat,
|
||||
getInt(EC_LEVEL),
|
||||
getInt(MARGIN),
|
||||
colors
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (prefs.brightenScreen) {
|
||||
// Post to make sure brightenScreenItem is initialized.
|
||||
view?.post {
|
||||
brightenScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
if (currentBrightness > -1f) {
|
||||
restoreScreenBrightness()
|
||||
}
|
||||
if (imageView.isInBounds()) {
|
||||
prefs.previewScale = imageView.getScale()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
@@ -130,32 +205,78 @@ class BarcodeFragment : Fragment() {
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.fragment_barcode, menu)
|
||||
addToHistoryItem = menu.findItem(R.id.add_to_history)
|
||||
brightenScreenItem = menu.findItem(R.id.brighten_screen)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.add_to_history -> {
|
||||
readAndAddToHistory(
|
||||
barcode.bitmap(),
|
||||
barcode.format
|
||||
)
|
||||
addToHistoryItem.isVisible = false
|
||||
context.toast(R.string.added_to_history)
|
||||
true
|
||||
}
|
||||
|
||||
R.id.copy_to_clipboard -> {
|
||||
context.apply {
|
||||
copyToClipboard(barcode.text())
|
||||
toast(R.string.copied_to_clipboard)
|
||||
}
|
||||
copyToClipboard(barcode.text())
|
||||
true
|
||||
}
|
||||
|
||||
R.id.copy_as_deeplink -> {
|
||||
copyToClipboard(deeplinkToCopy())
|
||||
true
|
||||
}
|
||||
|
||||
R.id.export_to_file -> {
|
||||
context.pickFileType(R.string.export_as) {
|
||||
askForFileNameAndSave(it)
|
||||
context.pickFileType(R.string.export_as) { fileType ->
|
||||
when (fileType) {
|
||||
FileType.PNG,
|
||||
FileType.JPG -> askForSize { size ->
|
||||
askForFileNameAndSave(fileType, size)
|
||||
}
|
||||
|
||||
else -> askForFileNameAndSave(fileType, 0)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
R.id.brighten_screen -> {
|
||||
prefs.brightenScreen = if (currentBrightness > -1f) {
|
||||
restoreScreenBrightness()
|
||||
false
|
||||
} else {
|
||||
brightenScreen()
|
||||
true
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyToClipboard(text: String, isSensitive: Boolean = false) {
|
||||
activity?.apply {
|
||||
copyToClipboard(text, isSensitive)
|
||||
toast(R.string.copied_to_clipboard)
|
||||
}
|
||||
}
|
||||
|
||||
private fun deeplinkToCopy() = createEncodeDeeplink(
|
||||
format = barcode.format.name,
|
||||
content = barcode.textOrHex(),
|
||||
)
|
||||
|
||||
private fun Context.pickFileType(
|
||||
title: Int,
|
||||
action: (FileType) -> Unit
|
||||
) {
|
||||
val fileTypes = FileType.values()
|
||||
val fileTypes = FileType.entries.toTypedArray()
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(title)
|
||||
.setItems(fileTypes.map { it.name }.toTypedArray()) { _, which ->
|
||||
@@ -166,11 +287,13 @@ class BarcodeFragment : Fragment() {
|
||||
|
||||
// Dialogs do not have a parent view.
|
||||
@SuppressLint("InflateParams")
|
||||
private fun askForFileNameAndSave(fileType: FileType) {
|
||||
private fun askForFileNameAndSave(fileType: FileType, bitmapSize: Int) {
|
||||
val ac = activity ?: return
|
||||
// Write permission is only required before Android Q.
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q &&
|
||||
!ac.hasWritePermission { askForFileNameAndSave(fileType) }
|
||||
!ac.hasWritePermission {
|
||||
askForFileNameAndSave(fileType, bitmapSize)
|
||||
}
|
||||
) {
|
||||
return
|
||||
}
|
||||
@@ -188,19 +311,28 @@ class BarcodeFragment : Fragment() {
|
||||
addSuffixIfNotGiven(fileName, ".png"),
|
||||
MIME_PNG
|
||||
) {
|
||||
barcode.bitmap().saveAsPng(it)
|
||||
barcode.bitmap(bitmapSize).saveAsPng(it)
|
||||
}
|
||||
|
||||
FileType.JPG -> saveAs(
|
||||
addSuffixIfNotGiven(fileName, ".jpg"),
|
||||
MIME_JPG
|
||||
) {
|
||||
barcode.bitmap(bitmapSize).saveAsJpg(it)
|
||||
}
|
||||
|
||||
FileType.SVG -> saveAs(
|
||||
addSuffixIfNotGiven(fileName, ".svg"),
|
||||
MIME_SVG
|
||||
) { outputStream ->
|
||||
outputStream.write(barcode.svg().toByteArray())
|
||||
) {
|
||||
it.write(barcode.svg().toByteArray())
|
||||
}
|
||||
|
||||
FileType.TXT -> saveAs(
|
||||
addSuffixIfNotGiven(fileName, ".txt"),
|
||||
MIME_TXT
|
||||
) { outputStream ->
|
||||
outputStream.write(barcode.text().toByteArray())
|
||||
) {
|
||||
it.write(barcode.text().toByteArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,6 +341,52 @@ class BarcodeFragment : Fragment() {
|
||||
.show()
|
||||
}
|
||||
|
||||
// Dialogs do not have a parent view.
|
||||
@SuppressLint("InflateParams")
|
||||
private fun askForSize(write: (size: Int) -> Unit) {
|
||||
val ac = activity ?: return
|
||||
val view = ac.layoutInflater.inflate(R.layout.dialog_size, null)
|
||||
val sizeView = view.findViewById<TextView>(R.id.size_display)
|
||||
val sizeBarView = view.findViewById<SeekBar>(R.id.size_bar)
|
||||
sizeBarView.initSizeBar(sizeView)
|
||||
AlertDialog.Builder(ac)
|
||||
.setView(view)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
write(getSize(sizeBarView.progress))
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun SeekBar.initSizeBar(sizeView: TextView) {
|
||||
sizeView.updateSize(progress)
|
||||
setOnSeekBarChangeListener(
|
||||
object : SeekBar.OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(
|
||||
seekBar: SeekBar,
|
||||
progressValue: Int,
|
||||
fromUser: Boolean
|
||||
) {
|
||||
sizeView.updateSize(progressValue)
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun TextView.updateSize(power: Int) {
|
||||
val size = getSize(power)
|
||||
text = if (size > 0) {
|
||||
getString(R.string.size_width_by_height, size, size)
|
||||
} else {
|
||||
getString(R.string.size_no_magnification)
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveAs(
|
||||
fileName: String,
|
||||
mimeType: String,
|
||||
@@ -216,7 +394,9 @@ class BarcodeFragment : Fragment() {
|
||||
) {
|
||||
val ac = activity ?: return
|
||||
scope.launch(Dispatchers.IO) {
|
||||
val message = ac.writeExternalFile(fileName, mimeType, write).toSaveResult()
|
||||
val message = ac.writeExternalFile(
|
||||
fileName, mimeType, write
|
||||
).toSaveResult()
|
||||
withContext(Dispatchers.Main) {
|
||||
ac.toast(message)
|
||||
}
|
||||
@@ -225,29 +405,42 @@ class BarcodeFragment : Fragment() {
|
||||
|
||||
private fun Context.shareAs(fileType: FileType) {
|
||||
when (fileType) {
|
||||
FileType.PNG -> share(barcode.bitmap())
|
||||
FileType.PNG -> askForSize {
|
||||
share(barcode.bitmap(it), MIME_PNG, "png")
|
||||
}
|
||||
|
||||
FileType.JPG -> askForSize {
|
||||
share(barcode.bitmap(it), MIME_JPG, "jpg")
|
||||
}
|
||||
|
||||
FileType.SVG -> shareText(barcode.svg(), MIME_SVG)
|
||||
FileType.TXT -> shareText(barcode.text())
|
||||
}
|
||||
}
|
||||
|
||||
private fun Context.share(bitmap: Bitmap) {
|
||||
private fun Context.share(bitmap: Bitmap, mimeType: String, ext: String) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
val file = File(
|
||||
externalCacheDir,
|
||||
"shared_barcode.png"
|
||||
"shared_barcode.$ext"
|
||||
)
|
||||
val success = try {
|
||||
FileOutputStream(file).use {
|
||||
bitmap.saveAsPng(it)
|
||||
when (mimeType) {
|
||||
MIME_PNG -> bitmap.saveAsPng(it)
|
||||
MIME_JPG -> bitmap.saveAsJpg(it)
|
||||
else -> throw IllegalArgumentException(
|
||||
"Invalid mime type"
|
||||
)
|
||||
}
|
||||
}
|
||||
true
|
||||
} catch (e: IOException) {
|
||||
} catch (_: IOException) {
|
||||
false
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (success) {
|
||||
shareFile(file, "image/png")
|
||||
shareFile(file, mimeType)
|
||||
} else {
|
||||
toast(R.string.error_saving_file)
|
||||
}
|
||||
@@ -255,98 +448,166 @@ class BarcodeFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun brightenScreen() {
|
||||
activity?.let {
|
||||
currentBrightness = it.getScreenBrightness()
|
||||
it.setScreenBrightness(1f)
|
||||
brightenScreenItem.isChecked = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun restoreScreenBrightness() {
|
||||
if (currentBrightness > -1f) {
|
||||
activity?.setScreenBrightness(currentBrightness)
|
||||
currentBrightness = -1f
|
||||
brightenScreenItem.isChecked = false
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val CONTENT = "content"
|
||||
private const val BIT_MATRIX_WIDTH = "bit_matrix_width"
|
||||
private const val BIT_MATRIX_HEIGHT = "bit_matrix_height"
|
||||
private const val BIT_MATRIX_DATA = "bit_matrix_data"
|
||||
private const val CONTENT_TEXT = "content_text"
|
||||
private const val CONTENT_RAW = "content_raw"
|
||||
private const val FORMAT = "format"
|
||||
private const val SIZE = "size"
|
||||
private const val EC_LEVEL = "ec_level"
|
||||
private const val MARGIN = "margin"
|
||||
private const val COLORS = "colors"
|
||||
private const val MIME_PNG = "image/png"
|
||||
private const val MIME_JPG = "image/jpeg"
|
||||
private const val MIME_SVG = "image/svg+xmg"
|
||||
private const val MIME_TXT = "text/plain"
|
||||
|
||||
fun newInstance(
|
||||
content: String,
|
||||
format: Format,
|
||||
size: Int,
|
||||
ecLevel: Int = -1,
|
||||
barcode: Barcode<*>,
|
||||
colors: Int = 0
|
||||
): Fragment {
|
||||
val args = Bundle()
|
||||
args.putString(CONTENT, content)
|
||||
args.putString(FORMAT, format.name)
|
||||
args.putInt(SIZE, size)
|
||||
args.putInt(EC_LEVEL, ecLevel)
|
||||
args.putInt(COLORS, colors)
|
||||
when (barcode) {
|
||||
is ContentBarcode -> args.putEcLevelMargin(
|
||||
barcode.ecLevel,
|
||||
barcode.margin
|
||||
)
|
||||
|
||||
is BitMatrixBarcode -> {
|
||||
args.putInt(BIT_MATRIX_WIDTH, barcode.bitMatrix.width)
|
||||
args.putInt(BIT_MATRIX_HEIGHT, barcode.bitMatrix.height)
|
||||
args.putByteArray(BIT_MATRIX_DATA, barcode.bitMatrix.data)
|
||||
}
|
||||
|
||||
else -> throw IllegalArgumentException("unknown barcode")
|
||||
}
|
||||
args.putContentFormatColors(
|
||||
barcode.content,
|
||||
barcode.format,
|
||||
colors
|
||||
)
|
||||
val fragment = BarcodeFragment()
|
||||
fragment.arguments = args
|
||||
return fragment
|
||||
}
|
||||
|
||||
fun <T> newInstance(
|
||||
content: T,
|
||||
format: BarcodeFormat,
|
||||
ecLevel: Int = -1,
|
||||
margin: Int = -1,
|
||||
colors: Int = 0
|
||||
): Fragment {
|
||||
val args = Bundle()
|
||||
args.putContentFormatColors(content, format, colors)
|
||||
args.putEcLevelMargin(ecLevel, margin)
|
||||
val fragment = BarcodeFragment()
|
||||
fragment.arguments = args
|
||||
return fragment
|
||||
}
|
||||
|
||||
private fun <T> Bundle.putContentFormatColors(
|
||||
content: T,
|
||||
format: BarcodeFormat,
|
||||
colors: Int
|
||||
) {
|
||||
when (content) {
|
||||
is String -> {
|
||||
putString(CONTENT_TEXT, content)
|
||||
}
|
||||
|
||||
is ByteArray -> {
|
||||
putByteArray(CONTENT_RAW, content)
|
||||
}
|
||||
|
||||
else -> {
|
||||
throw IllegalArgumentException(
|
||||
"content must be a String of a ByteArray"
|
||||
)
|
||||
}
|
||||
}
|
||||
putString(FORMAT, format.name)
|
||||
putInt(COLORS, colors)
|
||||
}
|
||||
|
||||
private fun Bundle.putEcLevelMargin(
|
||||
ecLevel: Int,
|
||||
margin: Int
|
||||
) {
|
||||
putInt(EC_LEVEL, ecLevel)
|
||||
putInt(MARGIN, margin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data class Barcode(
|
||||
val content: String,
|
||||
val format: Format,
|
||||
val size: Int,
|
||||
val ecLevel: Int,
|
||||
val colors: Colors
|
||||
) {
|
||||
private var _bitmap: Bitmap? = null
|
||||
fun bitmap(): Bitmap {
|
||||
val b = _bitmap ?: ZxingCpp.encodeAsBitmap(
|
||||
content, format, size, size, -1, ecLevel,
|
||||
setColor = colors.foregroundColor(),
|
||||
unsetColor = colors.backgroundColor()
|
||||
)
|
||||
_bitmap = b
|
||||
return b
|
||||
}
|
||||
|
||||
private var _svg: String? = null
|
||||
fun svg(): String {
|
||||
val s = _svg ?: ZxingCpp.encodeAsSvg(
|
||||
content, format, -1, ecLevel
|
||||
)
|
||||
_svg = s
|
||||
return s
|
||||
}
|
||||
|
||||
private var _text: String? = null
|
||||
fun text(): String {
|
||||
val t = _text ?: ZxingCpp.encodeAsText(
|
||||
content, format, -1, ecLevel,
|
||||
inverted = colors == Colors.BLACK_ON_WHITE
|
||||
)
|
||||
_text = t
|
||||
return t
|
||||
private fun ImageView.setScale(scale: Float, x: Float, y: Float) {
|
||||
imageMatrix = Matrix().apply {
|
||||
setTranslate(-x, -y)
|
||||
postScale(scale, scale)
|
||||
postTranslate(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
private enum class Colors {
|
||||
BLACK_ON_WHITE,
|
||||
WHITE_ON_BLACK,
|
||||
BLACK_ON_TRANSPARENT,
|
||||
WHITE_ON_TRANSPARENT;
|
||||
|
||||
fun foregroundColor(): Int = when (this) {
|
||||
BLACK_ON_WHITE,
|
||||
BLACK_ON_TRANSPARENT -> COLOR_BLACK
|
||||
WHITE_ON_BLACK,
|
||||
WHITE_ON_TRANSPARENT -> COLOR_WHITE
|
||||
}
|
||||
|
||||
fun backgroundColor(): Int = when (this) {
|
||||
BLACK_ON_WHITE -> COLOR_WHITE
|
||||
WHITE_ON_BLACK -> COLOR_BLACK
|
||||
BLACK_ON_TRANSPARENT,
|
||||
WHITE_ON_TRANSPARENT -> 0
|
||||
}
|
||||
private fun ImageView.getScale(): Float {
|
||||
val values = FloatArray(9)
|
||||
imageMatrix.getValues(values)
|
||||
return values[Matrix.MSCALE_X]
|
||||
}
|
||||
|
||||
private fun Bitmap.saveAsPng(outputStream: OutputStream, quality: Int = 90) =
|
||||
compress(Bitmap.CompressFormat.PNG, quality, outputStream)
|
||||
|
||||
private fun Bitmap.saveAsJpg(outputStream: OutputStream, quality: Int = 90) =
|
||||
compress(Bitmap.CompressFormat.JPEG, quality, outputStream)
|
||||
|
||||
private val fileNameCharacters = "[^A-Za-z0-9]".toRegex()
|
||||
private fun encodeFileName(name: String): String =
|
||||
fileNameCharacters.replace(name, "_").take(16).trim('_').lowercase(Locale.getDefault())
|
||||
private fun encodeFileName(name: String): String = fileNameCharacters
|
||||
.replace(name, "_")
|
||||
.take(16)
|
||||
.trim('_')
|
||||
.lowercase(Locale.getDefault())
|
||||
|
||||
private fun getSize(step: Int) = when (step) {
|
||||
0 -> 0
|
||||
else -> 128 shl (step - 1)
|
||||
}
|
||||
|
||||
private fun readAndAddToHistory(
|
||||
bitmap: Bitmap,
|
||||
format: BarcodeFormat
|
||||
) {
|
||||
ZxingCpp.readBitmap(
|
||||
bitmap,
|
||||
0, 0,
|
||||
bitmap.width, bitmap.height,
|
||||
0,
|
||||
ZxingCpp.ReaderOptions(
|
||||
tryHarder = true,
|
||||
tryRotate = true,
|
||||
tryInvert = true,
|
||||
tryDownscale = true,
|
||||
maxNumberOfSymbols = 1,
|
||||
formats = setOf(format)
|
||||
)
|
||||
)?.first {
|
||||
db.insertScan(it.toScan())
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,70 @@
|
||||
package de.markusfisch.android.binaryeye.fragment
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
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.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.TextWatcher
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.view.*
|
||||
import android.widget.*
|
||||
import android.text.style.TypefaceSpan
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageView
|
||||
import android.widget.TableLayout
|
||||
import android.widget.TableRow
|
||||
import android.widget.TextView
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
import de.markusfisch.android.binaryeye.actions.ActionRegistry
|
||||
import de.markusfisch.android.binaryeye.actions.IAction
|
||||
import de.markusfisch.android.binaryeye.actions.mail.MatMsg
|
||||
import de.markusfisch.android.binaryeye.actions.mail.MatMsgAction
|
||||
import de.markusfisch.android.binaryeye.actions.vtype.VTypeParser
|
||||
import de.markusfisch.android.binaryeye.actions.vtype.vcard.VCardAction
|
||||
import de.markusfisch.android.binaryeye.actions.vtype.vevent.VEventAction
|
||||
import de.markusfisch.android.binaryeye.actions.web.WebAction
|
||||
import de.markusfisch.android.binaryeye.actions.wifi.WifiAction
|
||||
import de.markusfisch.android.binaryeye.actions.wifi.WifiConnector
|
||||
import de.markusfisch.android.binaryeye.activity.MainActivity
|
||||
import de.markusfisch.android.binaryeye.adapter.prettifyFormatName
|
||||
import de.markusfisch.android.binaryeye.app.*
|
||||
import de.markusfisch.android.binaryeye.app.addFragment
|
||||
import de.markusfisch.android.binaryeye.app.db
|
||||
import de.markusfisch.android.binaryeye.app.hasLocationPermission
|
||||
import de.markusfisch.android.binaryeye.app.hasWritePermission
|
||||
import de.markusfisch.android.binaryeye.app.prefs
|
||||
import de.markusfisch.android.binaryeye.content.ContentBarcode
|
||||
import de.markusfisch.android.binaryeye.content.IdlParser
|
||||
import de.markusfisch.android.binaryeye.content.copyToClipboard
|
||||
import de.markusfisch.android.binaryeye.content.idlToRes
|
||||
import de.markusfisch.android.binaryeye.content.shareAsFile
|
||||
import de.markusfisch.android.binaryeye.content.shareText
|
||||
import de.markusfisch.android.binaryeye.content.toBarcode
|
||||
import de.markusfisch.android.binaryeye.content.toErrorCorrectionInt
|
||||
import de.markusfisch.android.binaryeye.content.toHexString
|
||||
import de.markusfisch.android.binaryeye.database.Recreation
|
||||
import de.markusfisch.android.binaryeye.content.wipeLastShareFile
|
||||
import de.markusfisch.android.binaryeye.database.Scan
|
||||
import de.markusfisch.android.binaryeye.database.toRecreation
|
||||
import de.markusfisch.android.binaryeye.io.askForFileName
|
||||
import de.markusfisch.android.binaryeye.io.toSaveResult
|
||||
import de.markusfisch.android.binaryeye.io.writeExternalFile
|
||||
import de.markusfisch.android.binaryeye.net.createEncodeDeeplink
|
||||
import de.markusfisch.android.binaryeye.view.hideSoftKeyboard
|
||||
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
|
||||
import de.markusfisch.android.binaryeye.widget.toast
|
||||
import kotlinx.coroutines.*
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.security.MessageDigest
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class DecodeFragment : Fragment() {
|
||||
@@ -40,11 +75,15 @@ class DecodeFragment : Fragment() {
|
||||
private lateinit var hexView: TextView
|
||||
private lateinit var stampView: TextView
|
||||
private lateinit var recreationView: ImageView
|
||||
private lateinit var labelView: EditText
|
||||
private lateinit var fab: FloatingActionButton
|
||||
private lateinit var scan: Scan
|
||||
private lateinit var format: String
|
||||
|
||||
private val parentJob = Job()
|
||||
private val scope: CoroutineScope = CoroutineScope(Dispatchers.Main + parentJob)
|
||||
private val scope: CoroutineScope = CoroutineScope(
|
||||
Dispatchers.Main + parentJob
|
||||
)
|
||||
private val dp: Float
|
||||
get() = resources.displayMetrics.density
|
||||
private val content: String
|
||||
@@ -54,8 +93,8 @@ class DecodeFragment : Fragment() {
|
||||
private var action = ActionRegistry.DEFAULT_ACTION
|
||||
private var isBinary = false
|
||||
private var originalBytes: ByteArray = ByteArray(0)
|
||||
private var id = 0L
|
||||
private var recreation: Recreation? = null
|
||||
private var label: String? = null
|
||||
private var recreationSize = 0
|
||||
|
||||
override fun onCreate(state: Bundle?) {
|
||||
super.onCreate(state)
|
||||
@@ -75,21 +114,22 @@ class DecodeFragment : Fragment() {
|
||||
false
|
||||
)
|
||||
|
||||
closeAutomatically = prefs.closeAutomatically &&
|
||||
activity?.intent?.hasExtra(MainActivity.DECODED) == true
|
||||
val justScanned = activity?.intent?.hasExtra(
|
||||
MainActivity.DECODED
|
||||
) == true
|
||||
closeAutomatically = prefs.closeAutomatically && justScanned
|
||||
|
||||
val scan = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
scan = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
arguments?.getParcelable(SCAN, Scan::class.java)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
arguments?.getParcelable(SCAN) as Scan?
|
||||
arguments?.getParcelable(SCAN)
|
||||
} ?: throw IllegalArgumentException("DecodeFragment needs a Scan")
|
||||
|
||||
val originalContent = scan.content
|
||||
isBinary = scan.raw != null
|
||||
originalBytes = scan.raw ?: originalContent.toByteArray()
|
||||
format = scan.format
|
||||
id = scan.id
|
||||
isBinary = scan.text.isEmpty()
|
||||
originalBytes = scan.raw ?: scan.text.toByteArray()
|
||||
format = scan.format.name
|
||||
recreationSize = (200f * dp).roundToInt()
|
||||
|
||||
contentView = view.findViewById(R.id.content)
|
||||
formatView = view.findViewById(R.id.format)
|
||||
@@ -98,35 +138,52 @@ class DecodeFragment : Fragment() {
|
||||
hexView = view.findViewById(R.id.hex)
|
||||
stampView = view.findViewById(R.id.stamp)
|
||||
recreationView = view.findViewById(R.id.recreation)
|
||||
labelView = view.findViewById(R.id.label)
|
||||
fab = view.findViewById(R.id.open)
|
||||
|
||||
initContentAndFab(originalContent)
|
||||
|
||||
if (prefs.showMetaData) {
|
||||
metaView.fillMetaView(scan)
|
||||
} else {
|
||||
metaView.visibility = View.GONE
|
||||
}
|
||||
if (prefs.showRecreation) {
|
||||
recreation = scan.toRecreation(
|
||||
(200f * dp).roundToInt()
|
||||
)
|
||||
|
||||
if (scan.id > 0) {
|
||||
scan.label?.let {
|
||||
labelView.setText(it)
|
||||
label = it
|
||||
}
|
||||
} else {
|
||||
labelView.visibility = View.GONE
|
||||
}
|
||||
|
||||
updateViewsAndFab(originalContent, originalBytes)
|
||||
view.findViewById<View>(R.id.inset_layout).setPaddingFromWindowInsets()
|
||||
view.findViewById<View>(R.id.scroll_view).setPaddingFromWindowInsets()
|
||||
|
||||
(view.findViewById(R.id.inset_layout) as View).setPaddingFromWindowInsets()
|
||||
(view.findViewById(R.id.scroll_view) as View).setPaddingFromWindowInsets()
|
||||
initContentAndFab(justScanned)
|
||||
updateViewsAndFab(scan.text, originalBytes)
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
val id = scan.id
|
||||
if (id > 0) {
|
||||
val newLabel = labelView.text.toString().trim()
|
||||
if (newLabel != (label ?: "")) {
|
||||
db.renameScan(id, newLabel)
|
||||
label = newLabel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
parentJob.cancel()
|
||||
wipeLastShareFile()
|
||||
}
|
||||
|
||||
private fun initContentAndFab(originalContent: String) {
|
||||
private fun initContentAndFab(justScanned: Boolean) {
|
||||
if (isBinary) {
|
||||
contentView.setText(String(originalBytes).foldNonAlNum())
|
||||
contentView.isEnabled = false
|
||||
@@ -135,7 +192,7 @@ class DecodeFragment : Fragment() {
|
||||
askForFileNameAndSave(originalBytes)
|
||||
}
|
||||
} else {
|
||||
contentView.setText(originalContent)
|
||||
contentView.setText(scan.text)
|
||||
contentView.addTextChangedListener(object : TextWatcher {
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
updateViewsAndFab(s?.toString() ?: "")
|
||||
@@ -156,10 +213,11 @@ class DecodeFragment : Fragment() {
|
||||
) = Unit
|
||||
})
|
||||
fab.setOnClickListener {
|
||||
executeAction(content)
|
||||
executeAction(this.content)
|
||||
}
|
||||
if (prefs.openImmediately) {
|
||||
executeAction(content)
|
||||
if (justScanned && prefs.openImmediately) {
|
||||
executeAction(this.content)
|
||||
backOrFinish()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,17 +229,16 @@ class DecodeFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun resolveActionAndUpdateFab(bytes: ByteArray) {
|
||||
val prevAction = action
|
||||
if (!prevAction.canExecuteOn(bytes)) {
|
||||
action = ActionRegistry.getAction(bytes)
|
||||
if (!isBinary) {
|
||||
val prevAction = action
|
||||
if (!prevAction.canExecuteOn(bytes)) {
|
||||
action = ActionRegistry.getAction(bytes)
|
||||
}
|
||||
if (prevAction !== action) {
|
||||
fab.setImageResource(action.iconResId)
|
||||
}
|
||||
}
|
||||
if (prevAction !== action) {
|
||||
updateFab(action)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateFab(action: IAction) {
|
||||
fab.setImageResource(action.iconResId)
|
||||
// Run this for the initial action too.
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
fab.setOnLongClickListener { v ->
|
||||
v.context.toast(action.titleResId)
|
||||
@@ -203,27 +260,112 @@ class DecodeFragment : Fragment() {
|
||||
v.text = hexDump(bytes)
|
||||
}
|
||||
recreationView.showIf(prefs.showRecreation) { v ->
|
||||
val r = recreation ?: return@showIf
|
||||
v.setImageBitmap(r.encode(text))
|
||||
v.setOnClickListener {
|
||||
fragmentManager?.addFragment(
|
||||
BarcodeFragment.newInstance(
|
||||
text,
|
||||
r.format,
|
||||
r.size,
|
||||
r.ecLevel
|
||||
)
|
||||
val unmodified: Boolean
|
||||
val content = if (isBinary) {
|
||||
if (bytes.isEmpty()) {
|
||||
clearRecreation()
|
||||
return@showIf
|
||||
}
|
||||
unmodified = bytes.contentEquals(originalBytes)
|
||||
bytes
|
||||
} else {
|
||||
if (text.isEmpty()) {
|
||||
clearRecreation()
|
||||
return@showIf
|
||||
}
|
||||
unmodified = text == scan.text
|
||||
text
|
||||
}
|
||||
val barcode = if (unmodified) {
|
||||
scan.toBarcode()
|
||||
} else {
|
||||
ContentBarcode(
|
||||
content,
|
||||
scan.format,
|
||||
scan.errorCorrectionLevel.toErrorCorrectionInt()
|
||||
)
|
||||
}
|
||||
try {
|
||||
v.setImageBitmap(barcode.bitmap(recreationSize))
|
||||
v.setOnClickListener {
|
||||
v.context.hideSoftKeyboard(contentView)
|
||||
v.post {
|
||||
fragmentManager?.addFragment(
|
||||
BarcodeFragment.newInstance(barcode)
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (_: RuntimeException) {
|
||||
clearRecreation()
|
||||
}
|
||||
}
|
||||
dataView.fillDataView(text)
|
||||
dataView.fillDataView(text, bytes)
|
||||
stampView.setTrackingLink(bytes, format)
|
||||
}
|
||||
|
||||
private fun TableLayout.fillDataView(text: String) {
|
||||
val items = LinkedHashMap<Int, String?>()
|
||||
if (action is WifiAction) {
|
||||
WifiConnector.parseMap(text)?.let { wifiData ->
|
||||
private fun clearRecreation() {
|
||||
recreationView.apply {
|
||||
setImageBitmap(null)
|
||||
setOnClickListener(null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun TableLayout.fillDataView(text: String, bytes: ByteArray) {
|
||||
val items = LinkedHashMap<Any, CharSequence?>()
|
||||
when (prefs.showChecksum) {
|
||||
"CRC4" -> items[R.string.crc4] = String.format("%X", crc4(bytes))
|
||||
"MD5" -> items[R.string.md5] = bytes.md5().toHexString().fold()
|
||||
"SHA1" -> items[R.string.sha1] = bytes.sha1().toHexString().fold()
|
||||
"SHA256" -> items[R.string.sha256] =
|
||||
bytes.sha256().toHexString().fold()
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
IdlParser.parse(String(bytes))?.let {
|
||||
items.putAll(mapOf("IIN" to it.iin))
|
||||
items.putAll(
|
||||
it.elements.entries.associate { (id, value) ->
|
||||
context.idlToRes(id) to value
|
||||
}
|
||||
)
|
||||
}
|
||||
when (action) {
|
||||
is MatMsgAction -> items.putAll(
|
||||
MatMsg(text).run {
|
||||
mapOf(
|
||||
R.string.email_to to to,
|
||||
R.string.email_subject to sub,
|
||||
R.string.email_body to body
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
is VCardAction,
|
||||
is VEventAction -> VTypeParser.parseMap(text).let { vData ->
|
||||
items.putAll(
|
||||
vData.map { item ->
|
||||
item.key to item.value.joinToString("\n") {
|
||||
it.value
|
||||
}
|
||||
}.toMap()
|
||||
)
|
||||
}
|
||||
|
||||
is WebAction -> try {
|
||||
items.putAll(
|
||||
Uri.parse(text).run {
|
||||
mapOf(
|
||||
R.string.scheme to scheme,
|
||||
R.string.host to host,
|
||||
R.string.query to query
|
||||
)
|
||||
}
|
||||
)
|
||||
} catch (_: Exception) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
is WifiAction -> WifiConnector.parseMap(text)?.let { wifiData ->
|
||||
items.putAll(
|
||||
linkedMapOf(
|
||||
R.string.entry_type to getString(R.string.wifi_network),
|
||||
@@ -243,7 +385,7 @@ class DecodeFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun TableLayout.fillMetaView(scan: Scan) {
|
||||
val items = linkedMapOf(
|
||||
val items = linkedMapOf<Any, CharSequence?>(
|
||||
R.string.error_correction_level to scan.errorCorrectionLevel,
|
||||
R.string.sequence_size to scan.sequenceSize.positiveToString(),
|
||||
R.string.sequence_index to scan.sequenceIndex.positiveToString(),
|
||||
@@ -254,7 +396,7 @@ class DecodeFragment : Fragment() {
|
||||
R.string.gtin_issue_number to scan.issueNumber,
|
||||
)
|
||||
if (!scan.version.isNullOrBlank()) {
|
||||
val versionString = if (scan.format == "QR_CODE") {
|
||||
val versionString = if (scan.format.name == "QR_CODE") {
|
||||
getString(
|
||||
R.string.qr_version_and_modules,
|
||||
scan.version,
|
||||
@@ -269,11 +411,20 @@ class DecodeFragment : Fragment() {
|
||||
)
|
||||
)
|
||||
}
|
||||
if (scan.format == ZxingCpp.BarcodeFormat.QR_CODE &&
|
||||
scan.dataMask > -1
|
||||
) {
|
||||
items.putAll(
|
||||
linkedMapOf(
|
||||
R.string.qr_data_mask to scan.dataMask.toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
fill(items)
|
||||
}
|
||||
|
||||
private fun TableLayout.fill(
|
||||
items: LinkedHashMap<Int, String?>
|
||||
items: LinkedHashMap<Any, CharSequence?>
|
||||
) {
|
||||
removeAllViews()
|
||||
visibility = if (items.isEmpty()) View.GONE else {
|
||||
@@ -284,10 +435,18 @@ class DecodeFragment : Fragment() {
|
||||
if (!text.isNullOrBlank()) {
|
||||
val tr = TableRow(ctx)
|
||||
val keyView = TextView(ctx)
|
||||
keyView.setText(item.key)
|
||||
val valueView = TextView(ctx)
|
||||
valueView.setPadding(spaceBetween, 0, 0, 0)
|
||||
valueView.text = text
|
||||
when (val key = item.key) {
|
||||
is Int -> keyView.setText(key)
|
||||
is String -> keyView.text = key
|
||||
else -> keyView.text = key.toString()
|
||||
}
|
||||
val valueView = TextView(ctx).apply {
|
||||
setPadding(spaceBetween, 0, 0, 0)
|
||||
this.text = text
|
||||
setOnClickListener {
|
||||
copyToClipboard(text.toString())
|
||||
}
|
||||
}
|
||||
tr.addView(keyView)
|
||||
tr.addView(valueView)
|
||||
addView(tr)
|
||||
@@ -299,10 +458,6 @@ class DecodeFragment : Fragment() {
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.fragment_decode, menu)
|
||||
if (isBinary) {
|
||||
menu.findItem(R.id.copy_to_clipboard).isVisible = false
|
||||
menu.findItem(R.id.create).isVisible = false
|
||||
}
|
||||
if (id > 0L) {
|
||||
menu.findItem(R.id.remove).isVisible = true
|
||||
}
|
||||
@@ -318,11 +473,19 @@ class DecodeFragment : Fragment() {
|
||||
maybeBackOrFinish()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.copy_to_clipboard -> {
|
||||
copyToClipboard(textOrHex())
|
||||
maybeBackOrFinish()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.copy_as_deeplink -> {
|
||||
copyToClipboard(deeplinkToCopy())
|
||||
maybeBackOrFinish()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.share -> {
|
||||
context?.apply {
|
||||
shareText(textOrHex())
|
||||
@@ -330,17 +493,36 @@ class DecodeFragment : Fragment() {
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
R.id.share_file -> {
|
||||
context?.apply {
|
||||
val (data, name) = if (isBinary) {
|
||||
Pair(originalBytes, "barcode_content.bin")
|
||||
} else {
|
||||
Pair(content, "barcode_content.txt")
|
||||
}
|
||||
shareAsFile(data, name)
|
||||
maybeBackOrFinish()
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
R.id.create -> {
|
||||
fragmentManager?.addFragment(
|
||||
EncodeFragment.newInstance(content, format)
|
||||
EncodeFragment.newInstance(
|
||||
if (isBinary) originalBytes else content,
|
||||
format
|
||||
)
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
R.id.remove -> {
|
||||
db.removeScan(id)
|
||||
db.removeScan(scan.id)
|
||||
backOrFinish()
|
||||
true
|
||||
}
|
||||
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
@@ -351,14 +533,16 @@ class DecodeFragment : Fragment() {
|
||||
content
|
||||
}
|
||||
|
||||
private fun deeplinkToCopy() = createEncodeDeeplink(
|
||||
format = format,
|
||||
content = textOrHex(),
|
||||
)
|
||||
|
||||
private fun copyPasswordToClipboard() {
|
||||
val ac = action
|
||||
if (ac is WifiAction) {
|
||||
ac.password?.let { password ->
|
||||
activity?.apply {
|
||||
copyToClipboard(password, isSensitive = true)
|
||||
toast(R.string.copied_password_to_clipboard)
|
||||
}
|
||||
copyToClipboard(password, isSensitive = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -366,7 +550,10 @@ class DecodeFragment : Fragment() {
|
||||
private fun copyToClipboard(text: String, isSensitive: Boolean = false) {
|
||||
activity?.apply {
|
||||
copyToClipboard(text, isSensitive)
|
||||
toast(R.string.copied_to_clipboard)
|
||||
// There's a clipboard popup from Android 13 on.
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
toast(R.string.copied_to_clipboard)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,17 +574,18 @@ class DecodeFragment : Fragment() {
|
||||
|
||||
private fun executeAction(content: String) {
|
||||
val ac = activity ?: return
|
||||
if (content.isNotEmpty()) {
|
||||
if (action is WifiAction &&
|
||||
Build.VERSION.SDK_INT < Build.VERSION_CODES.Q &&
|
||||
!ac.hasLocationPermission { executeAction(content) }
|
||||
) {
|
||||
return
|
||||
}
|
||||
scope.launch {
|
||||
action.execute(ac, content.toByteArray())
|
||||
maybeBackOrFinish()
|
||||
}
|
||||
if (content.isEmpty()) {
|
||||
return
|
||||
}
|
||||
if (action is WifiAction &&
|
||||
Build.VERSION.SDK_INT < Build.VERSION_CODES.Q &&
|
||||
!ac.hasLocationPermission { executeAction(content) }
|
||||
) {
|
||||
return
|
||||
}
|
||||
scope.launch {
|
||||
action.execute(ac, content.toByteArray())
|
||||
maybeBackOrFinish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,7 +639,7 @@ private inline fun <T : View> T.showIf(
|
||||
}
|
||||
|
||||
private val nonAlNum = "[^a-zA-Z0-9]".toRegex()
|
||||
private val multipleDots = "[…]+".toRegex()
|
||||
private val multipleDots = "…+".toRegex()
|
||||
private fun String.foldNonAlNum() = replace(nonAlNum, "…")
|
||||
.replace(multipleDots, "…")
|
||||
|
||||
@@ -547,7 +735,7 @@ private fun generateDpTrackingLink(bytes: ByteArray, format: String): String? {
|
||||
"%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>"
|
||||
return "<a href=\"https://www.deutschepost.de/de/s/sendungsverfolgung.html?piececode=$trackingNumber\">Deutsche Post: $trackingNumber</a>"
|
||||
}
|
||||
|
||||
// CRC-4 with polynomial x^4 + x + 1.
|
||||
@@ -582,3 +770,41 @@ private fun String.fromHtml() = if (
|
||||
@Suppress("DEPRECATION")
|
||||
Html.fromHtml(this)
|
||||
}
|
||||
|
||||
private fun ByteArray.md5(): ByteArray = MessageDigest.getInstance(
|
||||
"MD5"
|
||||
).run {
|
||||
update(this@md5)
|
||||
digest()
|
||||
}
|
||||
|
||||
private fun ByteArray.sha1(): ByteArray = MessageDigest.getInstance(
|
||||
"SHA-1"
|
||||
).run {
|
||||
update(this@sha1)
|
||||
digest()
|
||||
}
|
||||
|
||||
private fun ByteArray.sha256(): ByteArray = MessageDigest.getInstance(
|
||||
"SHA-256"
|
||||
).run {
|
||||
update(this@sha256)
|
||||
digest()
|
||||
}
|
||||
|
||||
private fun String.fold(): CharSequence {
|
||||
val l = length
|
||||
return if (l > 10) {
|
||||
val half = l / 2
|
||||
val s = substring(0, half) + "\n" + substring(half)
|
||||
SpannableString(s).apply {
|
||||
setSpan(
|
||||
TypefaceSpan("monospace"),
|
||||
0, s.length,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,35 @@
|
||||
package de.markusfisch.android.binaryeye.fragment
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.Fragment
|
||||
import android.support.v7.widget.SwitchCompat
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.*
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.CheckBox
|
||||
import android.widget.EditText
|
||||
import android.widget.Spinner
|
||||
import android.widget.TextView
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
import de.markusfisch.android.binaryeye.adapter.prettifyFormatName
|
||||
import de.markusfisch.android.binaryeye.app.addFragment
|
||||
import de.markusfisch.android.binaryeye.app.prefs
|
||||
import de.markusfisch.android.binaryeye.app.setFragment
|
||||
import de.markusfisch.android.binaryeye.text.unescape
|
||||
import de.markusfisch.android.binaryeye.view.hideSoftKeyboard
|
||||
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
|
||||
import de.markusfisch.android.binaryeye.widget.toast
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.Format
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.BarcodeFormat
|
||||
import java.io.InputStream
|
||||
import kotlin.math.min
|
||||
|
||||
class EncodeFragment : Fragment() {
|
||||
private lateinit var formatView: Spinner
|
||||
@@ -23,25 +37,54 @@ class EncodeFragment : Fragment() {
|
||||
private lateinit var ecSpinner: Spinner
|
||||
private lateinit var colorsLabel: TextView
|
||||
private lateinit var colorsSpinner: Spinner
|
||||
private lateinit var sizeView: TextView
|
||||
private lateinit var sizeBarView: SeekBar
|
||||
private lateinit var addQuietZoneSwitch: SwitchCompat
|
||||
private lateinit var contentView: EditText
|
||||
private lateinit var unescapeCheckBox: CheckBox
|
||||
|
||||
private val formats = arrayListOf(
|
||||
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
|
||||
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
|
||||
)
|
||||
|
||||
private var bytes: ByteArray? = null
|
||||
|
||||
override fun onActivityResult(
|
||||
requestCode: Int,
|
||||
resultCode: Int,
|
||||
resultData: Intent?
|
||||
) {
|
||||
when (requestCode) {
|
||||
PICK_FILE_RESULT_CODE -> {
|
||||
if (resultCode == Activity.RESULT_OK &&
|
||||
resultData != null &&
|
||||
resultData.data != null
|
||||
) {
|
||||
val ac = activity ?: return
|
||||
ac.hideSoftKeyboard(contentView)
|
||||
val uri = resultData.data ?: return
|
||||
bytes = ac.contentResolver?.openInputStream(uri)?.use {
|
||||
it.readBytesMax(4096)
|
||||
} ?: return
|
||||
setEncodeByteArray()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(state: Bundle?) {
|
||||
super.onCreate(state)
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
@@ -75,9 +118,9 @@ class EncodeFragment : Fragment() {
|
||||
) {
|
||||
val format = formats[position]
|
||||
val arrayId = when (format) {
|
||||
Format.AZTEC -> R.array.aztec_error_correction_levels
|
||||
Format.QR_CODE -> R.array.qr_error_correction_levels
|
||||
Format.PDF_417 -> R.array.pdf417_error_correction_levels
|
||||
BarcodeFormat.AZTEC -> R.array.aztec_error_correction_levels
|
||||
BarcodeFormat.QR_CODE -> R.array.qr_error_correction_levels
|
||||
BarcodeFormat.PDF_417 -> R.array.pdf417_error_correction_levels
|
||||
else -> 0
|
||||
}
|
||||
if (arrayId > 0) {
|
||||
@@ -91,10 +134,18 @@ class EncodeFragment : Fragment() {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}.setVisibility(ecLabel, ecSpinner)
|
||||
format.canBeInverted().setVisibility(
|
||||
}.setVisibilityFor(ecLabel, ecSpinner)
|
||||
format.canBeInverted().setVisibilityFor(
|
||||
colorsLabel, colorsSpinner
|
||||
)
|
||||
when (format) {
|
||||
BarcodeFormat.AZTEC,
|
||||
BarcodeFormat.DATA_MATRIX,
|
||||
BarcodeFormat.QR_CODE,
|
||||
BarcodeFormat.PDF_417 -> true
|
||||
|
||||
else -> false
|
||||
}.setVisibilityFor(addQuietZoneSwitch)
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parentView: AdapterView<*>?) {}
|
||||
@@ -121,18 +172,23 @@ class EncodeFragment : Fragment() {
|
||||
|
||||
colorsLabel = view.findViewById(R.id.colors_label)
|
||||
colorsSpinner = view.findViewById(R.id.colors)
|
||||
|
||||
sizeView = view.findViewById(R.id.size_display)
|
||||
sizeBarView = view.findViewById(R.id.size_bar)
|
||||
initSizeBar()
|
||||
addQuietZoneSwitch = view.findViewById(R.id.add_quiet_zone)
|
||||
addQuietZoneSwitch.isChecked = prefs.addQuietZone
|
||||
|
||||
contentView = view.findViewById(R.id.content)
|
||||
unescapeCheckBox = view.findViewById(R.id.unescape)
|
||||
unescapeCheckBox.isChecked = prefs.expandEscapeSequences
|
||||
|
||||
var complete = false
|
||||
val args = arguments
|
||||
args?.getString(CONTENT)?.let {
|
||||
args?.getString(CONTENT_TEXT)?.let {
|
||||
contentView.setText(it)
|
||||
complete = it.isNotEmpty()
|
||||
}
|
||||
args?.getByteArray(CONTENT_RAW)?.let {
|
||||
bytes = it
|
||||
complete = it.isNotEmpty()
|
||||
setEncodeByteArray()
|
||||
}
|
||||
|
||||
val barcodeFormat = args?.getString(FORMAT)
|
||||
@@ -150,80 +206,139 @@ class EncodeFragment : Fragment() {
|
||||
it.context.encode()
|
||||
}
|
||||
|
||||
(view.findViewById(R.id.inset_layout) as View).setPaddingFromWindowInsets()
|
||||
(view.findViewById(R.id.scroll_view) as View).setPaddingFromWindowInsets()
|
||||
view.findViewById<View>(R.id.inset_layout).setPaddingFromWindowInsets()
|
||||
view.findViewById<View>(R.id.scroll_view).setPaddingFromWindowInsets()
|
||||
|
||||
if (complete &&
|
||||
args?.getBoolean(EXECUTE, false) == true
|
||||
) {
|
||||
view.post {
|
||||
val content = view.context.getContent() ?: return@post
|
||||
fragmentManager?.setFragment(newEncodeFragment(content))
|
||||
}
|
||||
}
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.fragment_encode, menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.pick_file -> {
|
||||
startActivityForResult(
|
||||
Intent.createChooser(
|
||||
Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||
type = "*/*"
|
||||
},
|
||||
getString(R.string.pick_file)
|
||||
),
|
||||
PICK_FILE_RESULT_CODE
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
prefs.indexOfLastSelectedFormat = formatView.selectedItemPosition
|
||||
prefs.expandEscapeSequences = unescapeCheckBox.isChecked
|
||||
prefs.addQuietZone = addQuietZoneSwitch.isChecked
|
||||
}
|
||||
|
||||
private fun Context.encode() {
|
||||
var content = contentView.text.toString()
|
||||
if (unescapeCheckBox.isChecked) {
|
||||
try {
|
||||
content = content.unescape()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
toast(e.message ?: "Invalid escape sequence")
|
||||
return
|
||||
}
|
||||
}
|
||||
if (content.isEmpty()) {
|
||||
toast(R.string.error_no_content)
|
||||
return
|
||||
}
|
||||
hideSoftKeyboard(contentView)
|
||||
val content = getContent() ?: return
|
||||
contentView.post {
|
||||
fragmentManager?.addFragment(newEncodeFragment(content))
|
||||
}
|
||||
}
|
||||
|
||||
private fun newEncodeFragment(content: Any): Fragment {
|
||||
val format = formats[formatView.selectedItemPosition]
|
||||
fragmentManager?.addFragment(
|
||||
BarcodeFragment.newInstance(
|
||||
content,
|
||||
format,
|
||||
getSize(sizeBarView.progress),
|
||||
format.getErrorCorrectionLevel(ecSpinner.selectedItemPosition),
|
||||
if (format.canBeInverted()) {
|
||||
colorsSpinner.selectedItemPosition
|
||||
} else 0
|
||||
)
|
||||
return BarcodeFragment.newInstance(
|
||||
content,
|
||||
format,
|
||||
format.getErrorCorrectionLevel(ecSpinner.selectedItemPosition),
|
||||
when (format) {
|
||||
BarcodeFormat.AZTEC,
|
||||
BarcodeFormat.DATA_MATRIX,
|
||||
BarcodeFormat.QR_CODE,
|
||||
BarcodeFormat.PDF_417 -> if (
|
||||
addQuietZoneSwitch.isChecked
|
||||
) 1 else 0
|
||||
|
||||
else -> -1
|
||||
},
|
||||
if (format.canBeInverted()) {
|
||||
colorsSpinner.selectedItemPosition
|
||||
} else 0
|
||||
)
|
||||
}
|
||||
|
||||
private fun initSizeBar() {
|
||||
updateSize(sizeBarView.progress)
|
||||
sizeBarView.setOnSeekBarChangeListener(
|
||||
object : SeekBar.OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(
|
||||
seekBar: SeekBar,
|
||||
progressValue: Int,
|
||||
fromUser: Boolean
|
||||
) {
|
||||
updateSize(progressValue)
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {}
|
||||
})
|
||||
private fun Context.getContent(): Any? {
|
||||
if (bytes != null) {
|
||||
return bytes
|
||||
}
|
||||
var text = contentView.text.toString()
|
||||
if (text.isEmpty()) {
|
||||
toast(R.string.error_no_content)
|
||||
return null
|
||||
}
|
||||
if (!unescapeCheckBox.isChecked) {
|
||||
return text
|
||||
}
|
||||
try {
|
||||
text = text.unescape()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
toast(e.message ?: "Invalid escape sequence")
|
||||
return null
|
||||
}
|
||||
// Return a ByteArray if there were escape sequences for
|
||||
// non-printable characters.
|
||||
return if (text.any { it.code < 32 && it.code !in setOf(9, 10, 13) }) {
|
||||
// Do not expand UTF-8 sequences in binary data.
|
||||
text.toByteArray(Charsets.ISO_8859_1)
|
||||
} else {
|
||||
text
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSize(power: Int) {
|
||||
val size = getSize(power)
|
||||
sizeView.text = getString(R.string.width_by_height, size, size)
|
||||
private fun setEncodeByteArray() {
|
||||
contentView.text = null
|
||||
contentView.hint = getString(R.string.binary_data)
|
||||
contentView.isEnabled = false
|
||||
unescapeCheckBox.isEnabled = false
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val CONTENT = "content"
|
||||
private const val CONTENT_TEXT = "content_text"
|
||||
private const val CONTENT_RAW = "content_raw"
|
||||
private const val FORMAT = "format"
|
||||
private const val EXECUTE = "execute"
|
||||
private const val PICK_FILE_RESULT_CODE = 1
|
||||
|
||||
fun newInstance(
|
||||
content: String? = null,
|
||||
format: String? = null
|
||||
fun <T> newInstance(
|
||||
content: T? = null,
|
||||
format: String? = null,
|
||||
execute: Boolean = false
|
||||
): Fragment {
|
||||
val args = Bundle()
|
||||
content?.let { args.putString(CONTENT, content) }
|
||||
content?.let {
|
||||
when (content) {
|
||||
is String -> args.putString(CONTENT_TEXT, content)
|
||||
is ByteArray -> args.putByteArray(CONTENT_RAW, content)
|
||||
else -> throw IllegalArgumentException(
|
||||
"content must be a String or a ByteArray"
|
||||
)
|
||||
}
|
||||
args.putBoolean(EXECUTE, execute)
|
||||
}
|
||||
format?.let { args.putString(FORMAT, it) }
|
||||
val fragment = EncodeFragment()
|
||||
fragment.arguments = args
|
||||
@@ -232,45 +347,48 @@ class EncodeFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun Boolean.setVisibility(vararg views: View) {
|
||||
private fun Boolean.setVisibilityFor(vararg views: View) {
|
||||
val visibility = if (this) View.VISIBLE else View.GONE
|
||||
for (view in views) {
|
||||
view.visibility = visibility
|
||||
}
|
||||
}
|
||||
|
||||
private fun Format.packEcLevel(packed: Int, level: Int): Int {
|
||||
private fun BarcodeFormat.packEcLevel(packed: Int, level: Int): Int {
|
||||
val s = ecLevelShift()
|
||||
return (level shl s) or (packed and (15 shl s).inv())
|
||||
}
|
||||
|
||||
private fun Format.unpackEcLevel(packed: Int) =
|
||||
private fun BarcodeFormat.unpackEcLevel(packed: Int) =
|
||||
(packed shr ecLevelShift()) and 15
|
||||
|
||||
private fun Format.ecLevelShift() = when (this) {
|
||||
Format.AZTEC -> 0
|
||||
Format.QR_CODE -> 4
|
||||
Format.PDF_417 -> 8
|
||||
private fun BarcodeFormat.ecLevelShift() = when (this) {
|
||||
BarcodeFormat.AZTEC -> 0
|
||||
BarcodeFormat.QR_CODE -> 4
|
||||
BarcodeFormat.PDF_417 -> 8
|
||||
else -> throw IllegalArgumentException("$this does not have error levels")
|
||||
}
|
||||
|
||||
private fun Format.canBeInverted() = when (this) {
|
||||
Format.AZTEC,
|
||||
Format.DATA_MATRIX,
|
||||
Format.QR_CODE -> true
|
||||
private fun BarcodeFormat.canBeInverted() = when (this) {
|
||||
BarcodeFormat.AZTEC,
|
||||
BarcodeFormat.DATA_MATRIX,
|
||||
BarcodeFormat.QR_CODE -> true
|
||||
|
||||
else -> false
|
||||
}
|
||||
|
||||
private fun String.toFormat(default: Format = Format.QR_CODE): Format = try {
|
||||
Format.valueOf(this)
|
||||
private fun String.toFormat(
|
||||
default: BarcodeFormat = BarcodeFormat.QR_CODE
|
||||
): BarcodeFormat = try {
|
||||
BarcodeFormat.valueOf(this)
|
||||
} catch (_: IllegalArgumentException) {
|
||||
default
|
||||
}
|
||||
|
||||
private fun Format.getErrorCorrectionLevel(position: Int) = when (this) {
|
||||
Format.AZTEC -> position
|
||||
Format.QR_CODE -> (position + 1) * 2
|
||||
Format.PDF_417 -> position
|
||||
private fun BarcodeFormat.getErrorCorrectionLevel(position: Int) = when (this) {
|
||||
BarcodeFormat.AZTEC -> position
|
||||
BarcodeFormat.QR_CODE -> (position + 1) * 2
|
||||
BarcodeFormat.PDF_417 -> position
|
||||
else -> 0
|
||||
}.coerceIn(0, 8)
|
||||
|
||||
@@ -285,4 +403,17 @@ private fun Spinner.setEntries(resId: Int) = ArrayAdapter.createFromResource(
|
||||
adapter = aa
|
||||
}
|
||||
|
||||
private fun getSize(power: Int) = 128 * (power + 1)
|
||||
private fun InputStream.readBytesMax(max: Int): ByteArray {
|
||||
var offset = 0
|
||||
var remaining = min(available(), max)
|
||||
val result = ByteArray(remaining)
|
||||
while (remaining > 0) {
|
||||
val read = read(result, offset, remaining)
|
||||
if (read < 0) {
|
||||
break
|
||||
}
|
||||
remaining -= read
|
||||
offset += read
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -15,20 +15,43 @@ import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.view.ActionMode
|
||||
import android.support.v7.widget.SearchView
|
||||
import android.support.v7.widget.SwitchCompat
|
||||
import android.view.*
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.EditText
|
||||
import android.widget.ListView
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
import de.markusfisch.android.binaryeye.adapter.ScansAdapter
|
||||
import de.markusfisch.android.binaryeye.app.*
|
||||
import de.markusfisch.android.binaryeye.app.addFragment
|
||||
import de.markusfisch.android.binaryeye.app.alertDialog
|
||||
import de.markusfisch.android.binaryeye.app.db
|
||||
import de.markusfisch.android.binaryeye.app.hasWritePermission
|
||||
import de.markusfisch.android.binaryeye.app.prefs
|
||||
import de.markusfisch.android.binaryeye.content.copyToClipboard
|
||||
import de.markusfisch.android.binaryeye.content.shareAsFile
|
||||
import de.markusfisch.android.binaryeye.content.shareText
|
||||
import de.markusfisch.android.binaryeye.database.*
|
||||
import de.markusfisch.android.binaryeye.content.wipeLastShareFile
|
||||
import de.markusfisch.android.binaryeye.database.Database
|
||||
import de.markusfisch.android.binaryeye.database.exportCsv
|
||||
import de.markusfisch.android.binaryeye.database.exportDatabase
|
||||
import de.markusfisch.android.binaryeye.database.exportJson
|
||||
import de.markusfisch.android.binaryeye.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.view.lockStatusBarColor
|
||||
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
|
||||
import de.markusfisch.android.binaryeye.view.systemBarListViewScrollListener
|
||||
import de.markusfisch.android.binaryeye.view.unlockStatusBarColor
|
||||
import de.markusfisch.android.binaryeye.view.useVisibility
|
||||
import de.markusfisch.android.binaryeye.widget.toast
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class HistoryFragment : Fragment() {
|
||||
private lateinit var useHistorySwitch: SwitchCompat
|
||||
@@ -47,9 +70,13 @@ class HistoryFragment : Fragment() {
|
||||
R.menu.fragment_history_edit,
|
||||
menu
|
||||
)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
|
||||
// SDK35+ doesn't draw a status bar background anymore.
|
||||
Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM
|
||||
) {
|
||||
lockStatusBarColor()
|
||||
val ac = activity ?: return false
|
||||
@Suppress("DEPRECATION")
|
||||
ac.window.statusBarColor = ContextCompat.getColor(
|
||||
ac,
|
||||
R.color.accent_dark
|
||||
@@ -79,7 +106,8 @@ class HistoryFragment : Fragment() {
|
||||
closeActionMode()
|
||||
true
|
||||
}
|
||||
R.id.edit_scan -> {
|
||||
|
||||
R.id.edit_label -> {
|
||||
scansAdapter?.forSelection { id, position ->
|
||||
ac.askForName(
|
||||
id,
|
||||
@@ -90,6 +118,7 @@ class HistoryFragment : Fragment() {
|
||||
closeActionMode()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.remove_scan -> {
|
||||
scansAdapter?.getSelectedIds()?.let {
|
||||
if (it.isNotEmpty()) {
|
||||
@@ -99,6 +128,7 @@ class HistoryFragment : Fragment() {
|
||||
closeActionMode()
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
@@ -114,6 +144,7 @@ class HistoryFragment : Fragment() {
|
||||
private var filter: String? = null
|
||||
private var clearListMenuItem: MenuItem? = null
|
||||
private var exportHistoryMenuItem: MenuItem? = null
|
||||
private var shareAsFileMenuItem: MenuItem? = null
|
||||
|
||||
override fun onCreate(state: Bundle?) {
|
||||
super.onCreate(state)
|
||||
@@ -136,12 +167,16 @@ class HistoryFragment : Fragment() {
|
||||
|
||||
useHistorySwitch = view.findViewById(
|
||||
R.id.use_history
|
||||
) as SwitchCompat
|
||||
)
|
||||
initHistorySwitch(useHistorySwitch)
|
||||
|
||||
listView = view.findViewById(R.id.scans)
|
||||
listView.setOnItemClickListener { _, _, _, id ->
|
||||
showScan(id)
|
||||
listView.setOnItemClickListener { _, v, position, id ->
|
||||
if (actionMode != null) {
|
||||
scansAdapter?.select(v, id, position)
|
||||
} else {
|
||||
showScan(id)
|
||||
}
|
||||
}
|
||||
listView.setOnItemLongClickListener { _, v, position, id ->
|
||||
scansAdapter?.select(v, id, position)
|
||||
@@ -156,12 +191,12 @@ class HistoryFragment : Fragment() {
|
||||
|
||||
fab = view.findViewById(R.id.share)
|
||||
fab.setOnClickListener { v ->
|
||||
v.context.pickListSeparatorAndShare()
|
||||
v.context.pickListSeparatorAndShare(false)
|
||||
}
|
||||
|
||||
progressView = view.findViewById(R.id.progress_view)
|
||||
|
||||
(view.findViewById(R.id.inset_layout) as View).setPaddingFromWindowInsets()
|
||||
view.findViewById<View>(R.id.inset_layout).setPaddingFromWindowInsets()
|
||||
listView.setPaddingFromWindowInsets()
|
||||
|
||||
update()
|
||||
@@ -173,6 +208,7 @@ class HistoryFragment : Fragment() {
|
||||
super.onDestroy()
|
||||
scansAdapter?.changeCursor(null)
|
||||
parentJob.cancel()
|
||||
wipeLastShareFile()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
@@ -182,10 +218,19 @@ class HistoryFragment : Fragment() {
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.fragment_history, menu)
|
||||
initSearchView(menu.findItem(R.id.search))
|
||||
menu.setGroupVisible(R.id.scans_available, scansAdapter?.count != 0)
|
||||
clearListMenuItem = menu.findItem(R.id.clear)
|
||||
exportHistoryMenuItem = menu.findItem(R.id.export_history)
|
||||
val searchMenuItem = menu.findItem(R.id.search)
|
||||
initSearchView(searchMenuItem)
|
||||
val visible = scansAdapter?.count != 0
|
||||
searchMenuItem?.isVisible = visible
|
||||
exportHistoryMenuItem = menu.findItem(R.id.export_history)?.apply {
|
||||
isVisible = visible
|
||||
}
|
||||
shareAsFileMenuItem = menu.findItem(R.id.share_as_file)?.apply {
|
||||
isVisible = visible
|
||||
}
|
||||
clearListMenuItem = menu.findItem(R.id.clear)?.apply {
|
||||
isVisible = visible
|
||||
}
|
||||
}
|
||||
|
||||
private fun initSearchView(item: MenuItem?) {
|
||||
@@ -221,10 +266,17 @@ class HistoryFragment : Fragment() {
|
||||
context.askToRemoveScans()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.export_history -> {
|
||||
askToExportToFile()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.share_as_file -> {
|
||||
context.pickListSeparatorAndShare(true)
|
||||
true
|
||||
}
|
||||
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
@@ -249,7 +301,7 @@ class HistoryFragment : Fragment() {
|
||||
val cursor = db.getScans(filter)
|
||||
withContext(Dispatchers.Main) {
|
||||
val ac = activity ?: return@withContext
|
||||
val hasScans = cursor != null && cursor.count > 0
|
||||
val hasScans = cursor.count > 0
|
||||
if (filter == null) {
|
||||
if (!hasScans) {
|
||||
listView.emptyView = useHistorySwitch
|
||||
@@ -262,7 +314,7 @@ class HistoryFragment : Fragment() {
|
||||
} else {
|
||||
View.GONE
|
||||
}
|
||||
cursor?.let { cursor ->
|
||||
cursor.let { cursor ->
|
||||
// Close previous cursor.
|
||||
scansAdapter?.also { it.changeCursor(null) }
|
||||
scansAdapter = ScansAdapter(ac, cursor)
|
||||
@@ -278,6 +330,7 @@ class HistoryFragment : Fragment() {
|
||||
private fun enableMenuItems(enabled: Boolean) {
|
||||
clearListMenuItem?.isEnabled = enabled
|
||||
exportHistoryMenuItem?.isEnabled = enabled
|
||||
shareAsFileMenuItem?.isEnabled = enabled
|
||||
}
|
||||
|
||||
private fun closeActionMode() {
|
||||
@@ -292,7 +345,7 @@ class HistoryFragment : Fragment() {
|
||||
closeActionMode()
|
||||
try {
|
||||
fragmentManager?.addFragment(DecodeFragment.newInstance(scan))
|
||||
} catch (e: IllegalArgumentException) {
|
||||
} catch (_: IllegalArgumentException) {
|
||||
// Ignore, can never happen.
|
||||
}
|
||||
}
|
||||
@@ -393,12 +446,12 @@ class HistoryFragment : Fragment() {
|
||||
} ?: return@useVisibility
|
||||
val message = when (delimiter) {
|
||||
"db" -> ac.exportDatabase(name)
|
||||
else -> db.getScansDetailed(filter)?.use {
|
||||
else -> db.getScansDetailed(filter).use {
|
||||
when (delimiter) {
|
||||
"json" -> ac.exportJson(name, it)
|
||||
else -> ac.exportCsv(name, it, delimiter)
|
||||
}
|
||||
} ?: false
|
||||
}
|
||||
}.toSaveResult()
|
||||
withContext(Dispatchers.Main) {
|
||||
ac.toast(message)
|
||||
@@ -407,32 +460,44 @@ class HistoryFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun Context.pickListSeparatorAndShare() {
|
||||
private fun Context.pickListSeparatorAndShare(asFile: Boolean) {
|
||||
val separators = resources.getStringArray(
|
||||
R.array.list_separators_values
|
||||
)
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.pick_list_separator)
|
||||
.setItems(R.array.list_separators_names) { _, which ->
|
||||
shareScans(separators[which])
|
||||
shareScans(separators[which], asFile)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun shareScans(format: String) = scope.launch {
|
||||
private fun shareScans(format: String, asFile: Boolean) = scope.launch {
|
||||
progressView.useVisibility {
|
||||
var text: String? = null
|
||||
db.getScansDetailed(filter)?.use { cursor ->
|
||||
val selectedIds = scansAdapter?.getSelectedIds()
|
||||
if (selectedIds?.isNotEmpty() == true) {
|
||||
db.getScansDetailed(selectedIds.toLongArray())
|
||||
} else {
|
||||
db.getScansDetailed(filter)
|
||||
}.use { cursor ->
|
||||
val details = format.split(":")
|
||||
text = when (details[0]) {
|
||||
"text" -> cursor.exportText(details[1])
|
||||
"csv" -> cursor.exportCsv(details[1])
|
||||
else -> cursor.exportJson()
|
||||
when (details[0]) {
|
||||
"text" -> Pair(cursor.exportText(details[1]), "txt")
|
||||
"csv" -> Pair(cursor.exportCsv(details[1]), "csv")
|
||||
else -> Pair(cursor.exportJson(), "json")
|
||||
}
|
||||
}
|
||||
text?.let {
|
||||
withContext(Dispatchers.Main) {
|
||||
context.shareText(it)
|
||||
}.let { (text, ext) ->
|
||||
text?.let {
|
||||
withContext(Dispatchers.Main) {
|
||||
if (asFile) {
|
||||
context.shareAsFile(
|
||||
it,
|
||||
String.format("scans.%s", ext)
|
||||
)
|
||||
} else {
|
||||
context.shareText(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -441,7 +506,7 @@ class HistoryFragment : Fragment() {
|
||||
|
||||
private fun Cursor.exportText(separator: String): String {
|
||||
val sb = StringBuilder()
|
||||
val contentIndex = getColumnIndex(Database.SCANS_CONTENT)
|
||||
val contentIndex = getColumnIndex(Database.SCANS_TEXT)
|
||||
if (contentIndex > -1 && moveToFirst()) {
|
||||
do {
|
||||
val content = getString(contentIndex)
|
||||
|
||||
+4
-2
@@ -7,7 +7,9 @@ import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.support.annotation.RequiresApi
|
||||
import android.support.v4.app.Fragment
|
||||
import android.view.*
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.ListView
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
@@ -49,7 +51,7 @@ class NetworkSuggestionsFragment : Fragment() {
|
||||
listView.emptyView = view.findViewById(R.id.no_suggestions)
|
||||
listView.adapter = suggestionArrayAdapter
|
||||
listView.setOnScrollListener(systemBarListViewScrollListener)
|
||||
(view.findViewById(R.id.inset_layout) as View).setPaddingFromWindowInsets()
|
||||
view.findViewById<View>(R.id.inset_layout).setPaddingFromWindowInsets()
|
||||
listView.setPaddingFromWindowInsets()
|
||||
|
||||
view.findViewById<View>(R.id.remove).setOnClickListener {
|
||||
|
||||
+9
-3
@@ -20,19 +20,20 @@ import de.markusfisch.android.binaryeye.activity.SplashActivity
|
||||
import de.markusfisch.android.binaryeye.app.addFragment
|
||||
import de.markusfisch.android.binaryeye.app.hasBluetoothPermission
|
||||
import de.markusfisch.android.binaryeye.app.prefs
|
||||
import de.markusfisch.android.binaryeye.bluetooth.setBluetoothHosts
|
||||
import de.markusfisch.android.binaryeye.media.beepConfirm
|
||||
import de.markusfisch.android.binaryeye.preference.UrlPreference
|
||||
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
|
||||
import de.markusfisch.android.binaryeye.view.systemBarRecyclerViewScrollListener
|
||||
import de.markusfisch.android.binaryeye.widget.toast
|
||||
import de.markusfisch.android.binaryeye.bluetooth.setBluetoothHosts
|
||||
|
||||
class PreferencesFragment : PreferenceFragmentCompat() {
|
||||
private val changeListener = object : OnSharedPreferenceChangeListener {
|
||||
override fun onSharedPreferenceChanged(
|
||||
sharedPreferences: SharedPreferences,
|
||||
key: String
|
||||
sharedPreferences: SharedPreferences?,
|
||||
key: String?
|
||||
) {
|
||||
key ?: return
|
||||
val preference = findPreference(key) ?: return
|
||||
prefs.update()
|
||||
when (preference.key) {
|
||||
@@ -41,6 +42,7 @@ class PreferencesFragment : PreferenceFragmentCompat() {
|
||||
beepConfirm()
|
||||
setSummary(preference)
|
||||
}
|
||||
|
||||
"send_scan_bluetooth" -> {
|
||||
if (prefs.sendScanBluetooth &&
|
||||
activity?.hasBluetoothPermission() == false
|
||||
@@ -49,6 +51,7 @@ class PreferencesFragment : PreferenceFragmentCompat() {
|
||||
}
|
||||
setSummary(preference)
|
||||
}
|
||||
|
||||
else -> setSummary(preference)
|
||||
}
|
||||
}
|
||||
@@ -166,9 +169,11 @@ class PreferencesFragment : PreferenceFragmentCompat() {
|
||||
is UrlPreference -> {
|
||||
preference.setSummary(preference.getUrl())
|
||||
}
|
||||
|
||||
is ListPreference -> {
|
||||
preference.setSummary(preference.entry)
|
||||
}
|
||||
|
||||
is MultiSelectListPreference -> {
|
||||
preference.setSummary(
|
||||
preference.values.joinToString(", ") {
|
||||
@@ -176,6 +181,7 @@ class PreferencesFragment : PreferenceFragmentCompat() {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
is PreferenceGroup -> {
|
||||
setSummaries(preference)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import de.markusfisch.android.binaryeye.app.prefs
|
||||
import de.markusfisch.android.binaryeye.database.Scan
|
||||
import de.markusfisch.android.binaryeye.net.sendAsync
|
||||
import de.markusfisch.android.binaryeye.preference.UrlPreference
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.BarcodeFormat
|
||||
|
||||
class UrlDialogFragment : PreferenceDialogFragmentCompat() {
|
||||
private var urlView: TextView? = null
|
||||
@@ -50,7 +51,7 @@ class UrlDialogFragment : PreferenceDialogFragmentCompat() {
|
||||
Scan(
|
||||
"test",
|
||||
null,
|
||||
"none"
|
||||
BarcodeFormat.NONE
|
||||
).sendAsync(url, prefs.sendScanType) { code, body ->
|
||||
textView.text = when {
|
||||
code != null -> "$code"
|
||||
@@ -79,10 +80,5 @@ private fun completeUrl(template: String): String {
|
||||
if (!s.startsWith("http")) {
|
||||
s = "http://${s}"
|
||||
}
|
||||
if (prefs.sendScanType == "0" &&
|
||||
!s.matches(".*/[a-zA-Z._-]*\\?[a-zA-Z0-9&=_-]+=$".toRegex())
|
||||
) {
|
||||
s = "${s}?content="
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ fun ContentResolver.loadImageUri(uri: Uri): Bitmap? = try {
|
||||
openInputStream(uri)?.use {
|
||||
BitmapFactory.decodeStream(it, null, options)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
} catch (_: IOException) {
|
||||
null
|
||||
}
|
||||
|
||||
@@ -55,17 +55,17 @@ fun Bitmap.crop(
|
||||
pivotX: Float,
|
||||
pivotY: Float
|
||||
) = try {
|
||||
val erected = erect(rotation, pivotX, pivotY)
|
||||
val w = erected.width
|
||||
val h = erected.height
|
||||
val b = rotate(rotation, pivotX, pivotY)
|
||||
val w = b.width
|
||||
val h = b.height
|
||||
val x = max(0, (rect.left * w).roundToInt())
|
||||
val y = max(0, (rect.top * h).roundToInt())
|
||||
Bitmap.createBitmap(
|
||||
erected,
|
||||
b,
|
||||
x,
|
||||
y,
|
||||
min(w - x, (rect.right * w).roundToInt() - x),
|
||||
min(h - y, (rect.bottom * h).roundToInt() - y)
|
||||
min(w, (rect.right * w).roundToInt()) - x,
|
||||
min(h, (rect.bottom * h).roundToInt()) - y
|
||||
)
|
||||
} catch (e: OutOfMemoryError) {
|
||||
null
|
||||
@@ -73,13 +73,11 @@ fun Bitmap.crop(
|
||||
null
|
||||
}
|
||||
|
||||
private fun Bitmap.erect(
|
||||
private fun Bitmap.rotate(
|
||||
rotation: Float,
|
||||
pivotX: Float,
|
||||
pivotY: Float
|
||||
): Bitmap = if (
|
||||
rotation % 360f != 0f
|
||||
) {
|
||||
): Bitmap = if (rotation % 360f != 0f) {
|
||||
Bitmap.createBitmap(
|
||||
this,
|
||||
0,
|
||||
|
||||
@@ -10,7 +10,9 @@ data class FrameMetrics(
|
||||
var width: Int = 0,
|
||||
var height: Int = 0,
|
||||
var orientation: Int = 0
|
||||
)
|
||||
) {
|
||||
fun isValid() = width > 0 && height > 0
|
||||
}
|
||||
|
||||
fun Rect.setFrameRoi(
|
||||
frameMetrics: FrameMetrics,
|
||||
@@ -54,6 +56,7 @@ fun Matrix.setFrameToView(
|
||||
uprightWidth = frameMetrics.height
|
||||
uprightHeight = frameMetrics.width
|
||||
}
|
||||
|
||||
else -> {
|
||||
uprightWidth = frameMetrics.width
|
||||
uprightHeight = frameMetrics.height
|
||||
|
||||
@@ -5,11 +5,12 @@ import android.graphics.Canvas
|
||||
|
||||
fun Bitmap.fixTransparency(): Bitmap {
|
||||
val result = analyzeTransparency()
|
||||
return if (result.hasTransparentPixels) {
|
||||
val cfg = config
|
||||
return if (cfg != null && result.hasTransparentPixels) {
|
||||
val copy = Bitmap.createBitmap(
|
||||
width,
|
||||
height,
|
||||
config
|
||||
cfg
|
||||
)
|
||||
Canvas(copy).apply {
|
||||
drawColor(result.backgroundColor)
|
||||
|
||||
@@ -53,7 +53,7 @@ fun Context.writeExternalFile(
|
||||
): Boolean = try {
|
||||
openExternalOutputStream(fileName, mimeType).use { write(it) }
|
||||
true
|
||||
} catch (e: IOException) {
|
||||
} catch (_: IOException) {
|
||||
false
|
||||
}
|
||||
|
||||
@@ -61,7 +61,6 @@ private fun Context.openExternalOutputStream(
|
||||
fileName: String,
|
||||
mimeType: String
|
||||
): OutputStream = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
@Suppress("DEPRECATION")
|
||||
val file = File(
|
||||
Environment.getExternalStoragePublicDirectory(
|
||||
Environment.DIRECTORY_DOWNLOADS
|
||||
|
||||
@@ -22,7 +22,7 @@ fun beepError() {
|
||||
ToneGenerator.MAX_VOLUME
|
||||
)
|
||||
errorToneGenerator = tg
|
||||
tg.startTone(ToneGenerator.TONE_SUP_ERROR)
|
||||
tg.startTone(ToneGenerator.TONE_SUP_ERROR, 1000)
|
||||
}
|
||||
|
||||
fun releaseToneGenerators() {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package de.markusfisch.android.binaryeye.net
|
||||
|
||||
import android.net.Uri
|
||||
|
||||
private const val SCHEME = "binaryeye"
|
||||
private const val HOSTNAME = "markusfisch.de"
|
||||
private const val ENCODE_PATH = "encode"
|
||||
|
||||
fun isScanDeeplink(text: String) = listOf(
|
||||
"$SCHEME://scan",
|
||||
"http://$HOSTNAME/BinaryEye",
|
||||
"https://$HOSTNAME/BinaryEye"
|
||||
).any { text.startsWith(it) }
|
||||
|
||||
fun isEncodeDeeplink(text: String) = listOf(
|
||||
"$SCHEME://$ENCODE_PATH",
|
||||
"http://$HOSTNAME/$ENCODE_PATH",
|
||||
"https://$HOSTNAME/$ENCODE_PATH"
|
||||
).any { text.startsWith(it) }
|
||||
|
||||
fun createEncodeDeeplink(format: String, content: String): String {
|
||||
return Uri.Builder()
|
||||
.scheme("https")
|
||||
.authority(HOSTNAME)
|
||||
.appendPath(ENCODE_PATH)
|
||||
.appendQueryParameter("format", format)
|
||||
.appendQueryParameter("content", content)
|
||||
.build()
|
||||
.toString()
|
||||
}
|
||||
@@ -33,14 +33,19 @@ private fun Scan.send(url: String, type: String): Response {
|
||||
"1" -> request(
|
||||
url + "?" + asUrlArguments()
|
||||
)
|
||||
|
||||
"2" -> request(url) { con ->
|
||||
con.requestMethod = "POST"
|
||||
con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded")
|
||||
con.setRequestProperty(
|
||||
"Content-Type",
|
||||
"application/x-www-form-urlencoded"
|
||||
)
|
||||
con.outputStream.apply {
|
||||
write(asUrlArguments().toByteArray())
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
"3" -> request(url) { con ->
|
||||
con.requestMethod = "POST"
|
||||
con.setRequestProperty("Content-Type", "application/json")
|
||||
@@ -49,8 +54,9 @@ private fun Scan.send(url: String, type: String): Response {
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
else -> request(
|
||||
url + content.urlEncode()
|
||||
url + text.urlEncode()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -64,9 +70,9 @@ private fun Scan.asJson() = JSONObject().apply {
|
||||
}
|
||||
|
||||
private fun Scan.getMap(): Map<String, String> = mapOf(
|
||||
"content" to content,
|
||||
"content" to text,
|
||||
"raw" to raw?.toHexString(),
|
||||
"format" to format,
|
||||
"format" to format.name,
|
||||
"errorCorrectionLevel" to errorCorrectionLevel,
|
||||
"version" to version,
|
||||
"sequenceSize" to sequenceSize.toString(),
|
||||
|
||||
@@ -5,6 +5,6 @@ import java.net.URLEncoder
|
||||
|
||||
fun String.urlEncode(): String = try {
|
||||
URLEncoder.encode(this, "UTF-8")
|
||||
} catch (e: UnsupportedEncodingException) {
|
||||
} catch (_: UnsupportedEncodingException) {
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package de.markusfisch.android.binaryeye.os
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.WindowManager
|
||||
|
||||
fun Activity.getScreenBrightness(): Float {
|
||||
val layoutParams = window.attributes
|
||||
return if (layoutParams.screenBrightness ==
|
||||
WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
|
||||
) {
|
||||
android.provider.Settings.System.getInt(
|
||||
contentResolver,
|
||||
android.provider.Settings.System.SCREEN_BRIGHTNESS
|
||||
) / 255.0f
|
||||
} else {
|
||||
layoutParams.screenBrightness
|
||||
}
|
||||
}
|
||||
|
||||
fun Activity.setScreenBrightness(brightness: Float) {
|
||||
val layoutParams = window.attributes
|
||||
layoutParams.screenBrightness = brightness
|
||||
window.attributes = layoutParams
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package de.markusfisch.android.binaryeye.preference
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import org.json.JSONObject
|
||||
|
||||
data class CropHandle(
|
||||
val x: Int = -2, // -2 means set default roi.
|
||||
val y: Int = -2,
|
||||
val orientation: Int = 0
|
||||
)
|
||||
|
||||
fun SharedPreferences.storeCropHandle(
|
||||
name: String,
|
||||
cropHandle: CropHandle
|
||||
) {
|
||||
edit().putString(
|
||||
name,
|
||||
JSONObject().apply {
|
||||
put("x", cropHandle.x)
|
||||
put("y", cropHandle.y)
|
||||
put("orientation", cropHandle.orientation)
|
||||
}.toString()
|
||||
).apply()
|
||||
}
|
||||
|
||||
fun SharedPreferences.restoreCropHandle(
|
||||
name: String,
|
||||
default: CropHandle = CropHandle()
|
||||
): CropHandle {
|
||||
val s = getString(name, null) ?: return default
|
||||
return try {
|
||||
val json = JSONObject(s)
|
||||
CropHandle(
|
||||
json.getInt("x"),
|
||||
json.getInt("y"),
|
||||
json.getInt("orientation")
|
||||
)
|
||||
} catch (_: Exception) {
|
||||
return default
|
||||
}
|
||||
}
|
||||
@@ -1,55 +1,44 @@
|
||||
package de.markusfisch.android.binaryeye.preference
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.media.ToneGenerator
|
||||
import android.os.Build
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.annotation.RequiresApi
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.Format
|
||||
import de.markusfisch.android.binaryeye.activity.CameraActivity
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.BarcodeFormat
|
||||
|
||||
class Preferences {
|
||||
lateinit var preferences: SharedPreferences
|
||||
|
||||
var barcodeFormats = setOf(
|
||||
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,
|
||||
BarcodeFormat.AZTEC.name,
|
||||
BarcodeFormat.CODABAR.name,
|
||||
BarcodeFormat.CODE_39.name,
|
||||
BarcodeFormat.CODE_93.name,
|
||||
BarcodeFormat.CODE_128.name,
|
||||
BarcodeFormat.DATA_BAR.name,
|
||||
BarcodeFormat.DATA_BAR_EXPANDED.name,
|
||||
BarcodeFormat.DATA_MATRIX.name,
|
||||
BarcodeFormat.DX_FILM_EDGE.name,
|
||||
BarcodeFormat.EAN_8.name,
|
||||
BarcodeFormat.EAN_13.name,
|
||||
BarcodeFormat.ITF.name,
|
||||
BarcodeFormat.MAXICODE.name,
|
||||
BarcodeFormat.PDF_417.name,
|
||||
BarcodeFormat.QR_CODE.name,
|
||||
BarcodeFormat.MICRO_QR_CODE.name,
|
||||
BarcodeFormat.RMQR_CODE.name,
|
||||
BarcodeFormat.UPC_A.name,
|
||||
BarcodeFormat.UPC_E.name,
|
||||
)
|
||||
@RequiresApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
set(value) {
|
||||
apply(BARCODE_FORMATS, value)
|
||||
field = value
|
||||
}
|
||||
var cropHandleX = -2 // -2 means set default roi.
|
||||
set(value) {
|
||||
apply(CROP_HANDLE_X, value)
|
||||
field = value
|
||||
}
|
||||
var cropHandleY = -2
|
||||
set(value) {
|
||||
apply(CROP_HANDLE_Y, value)
|
||||
field = value
|
||||
}
|
||||
var cropHandleOrientation = 0
|
||||
set(value) {
|
||||
apply(CROP_HANDLE_ORIENTATION, value)
|
||||
field = value
|
||||
}
|
||||
var showCropHandle = true
|
||||
set(value) {
|
||||
apply(SHOW_CROP_HANDLE, value)
|
||||
@@ -105,9 +94,9 @@ class Preferences {
|
||||
apply(USE_HISTORY, value)
|
||||
field = value
|
||||
}
|
||||
var ignoreConsecutiveDuplicates = true
|
||||
var ignoreDuplicatesName = "ignore_consecutive_duplicates"
|
||||
set(value) {
|
||||
apply(IGNORE_CONSECUTIVE_DUPLICATES, value)
|
||||
apply(IGNORE_DUPLICATES_NAME, value)
|
||||
field = value
|
||||
}
|
||||
var copyImmediately = false
|
||||
@@ -130,6 +119,11 @@ class Preferences {
|
||||
apply(SHOW_HEX_DUMP, value)
|
||||
field = value
|
||||
}
|
||||
var showChecksum = ""
|
||||
set(value) {
|
||||
apply(SHOW_CHECKSUM, value)
|
||||
field = value
|
||||
}
|
||||
var showRecreation = true
|
||||
set(value) {
|
||||
apply(SHOW_RECREATION, value)
|
||||
@@ -150,7 +144,7 @@ class Preferences {
|
||||
apply(OPEN_WITH_URL, value)
|
||||
field = value
|
||||
}
|
||||
var sendScanActive = true
|
||||
var sendScanActive = false
|
||||
set(value) {
|
||||
apply(SEND_SCAN_ACTIVE, value)
|
||||
field = value
|
||||
@@ -192,7 +186,7 @@ class Preferences {
|
||||
apply(INDEX_OF_LAST_SELECTED_EC_LEVEL, value)
|
||||
field = value
|
||||
}
|
||||
var freeRotation = true
|
||||
var freeRotation = false
|
||||
set(value) {
|
||||
apply(FREE_ROTATION, value)
|
||||
field = value
|
||||
@@ -202,6 +196,21 @@ class Preferences {
|
||||
apply(EXPAND_ESCAPE_SEQUENCES, value)
|
||||
field = value
|
||||
}
|
||||
var addQuietZone = true
|
||||
set(value) {
|
||||
apply(ADD_QUIET_ZONE, value)
|
||||
field = value
|
||||
}
|
||||
var brightenScreen = false
|
||||
set(value) {
|
||||
apply(BRIGHTEN_SCREEN, value)
|
||||
field = value
|
||||
}
|
||||
var previewScale = 0f
|
||||
set(value) {
|
||||
apply(PREVIEW_SCALE, value)
|
||||
field = value
|
||||
}
|
||||
|
||||
fun init(context: Context) {
|
||||
preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
@@ -211,15 +220,16 @@ class Preferences {
|
||||
fun update() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
preferences.getStringSet(BARCODE_FORMATS, barcodeFormats)?.let {
|
||||
barcodeFormats = it
|
||||
barcodeFormats = addFormatsOnUpdate(
|
||||
it,
|
||||
BarcodeFormat.RMQR_CODE,
|
||||
BarcodeFormat.DX_FILM_EDGE
|
||||
)
|
||||
}
|
||||
}
|
||||
cropHandleX = preferences.getInt(CROP_HANDLE_X, cropHandleX)
|
||||
cropHandleY = preferences.getInt(CROP_HANDLE_Y, cropHandleY)
|
||||
cropHandleOrientation = preferences.getInt(
|
||||
CROP_HANDLE_ORIENTATION,
|
||||
cropHandleOrientation
|
||||
)
|
||||
|
||||
migrateCropHandle()
|
||||
|
||||
showCropHandle = preferences.getBoolean(
|
||||
SHOW_CROP_HANDLE,
|
||||
showCropHandle
|
||||
@@ -242,10 +252,20 @@ class Preferences {
|
||||
beepToneName = it
|
||||
}
|
||||
useHistory = preferences.getBoolean(USE_HISTORY, useHistory)
|
||||
ignoreConsecutiveDuplicates = preferences.getBoolean(
|
||||
IGNORE_CONSECUTIVE_DUPLICATES,
|
||||
ignoreConsecutiveDuplicates
|
||||
)
|
||||
|
||||
// Map old setting to new one if it wasn't on default value.
|
||||
val ignoreConsecutiveDuplicates = "ignore_consecutive_duplicates"
|
||||
if (!preferences.getBoolean(ignoreConsecutiveDuplicates, true)) {
|
||||
preferences.edit().remove(ignoreConsecutiveDuplicates).apply()
|
||||
ignoreDuplicatesName = "accept_duplicates"
|
||||
}
|
||||
|
||||
preferences.getString(
|
||||
IGNORE_DUPLICATES_NAME,
|
||||
ignoreDuplicatesName
|
||||
)?.also {
|
||||
ignoreDuplicatesName = it
|
||||
}
|
||||
copyImmediately = preferences.getBoolean(
|
||||
COPY_IMMEDIATELY,
|
||||
copyImmediately
|
||||
@@ -256,6 +276,9 @@ class Preferences {
|
||||
)
|
||||
showMetaData = preferences.getBoolean(SHOW_META_DATA, showMetaData)
|
||||
showHexDump = preferences.getBoolean(SHOW_HEX_DUMP, showHexDump)
|
||||
preferences.getString(SHOW_CHECKSUM, showChecksum)?.also {
|
||||
showChecksum = it
|
||||
}
|
||||
showRecreation = preferences.getBoolean(SHOW_RECREATION, showRecreation)
|
||||
closeAutomatically = preferences.getBoolean(
|
||||
CLOSE_AUTOMATICALLY,
|
||||
@@ -303,6 +326,53 @@ class Preferences {
|
||||
EXPAND_ESCAPE_SEQUENCES,
|
||||
expandEscapeSequences
|
||||
)
|
||||
addQuietZone = preferences.getBoolean(ADD_QUIET_ZONE, addQuietZone)
|
||||
brightenScreen = preferences.getBoolean(
|
||||
BRIGHTEN_SCREEN,
|
||||
brightenScreen
|
||||
)
|
||||
previewScale = preferences.getFloat(
|
||||
PREVIEW_SCALE,
|
||||
previewScale
|
||||
)
|
||||
}
|
||||
|
||||
private fun addFormatsOnUpdate(
|
||||
restored: Set<String>,
|
||||
vararg formats: BarcodeFormat
|
||||
) = restored.toMutableSet().apply {
|
||||
for (format in formats) {
|
||||
val name = "${format.name}_added"
|
||||
if (!preferences.getBoolean(name, false)) {
|
||||
preferences.edit().putBoolean(name, true).apply()
|
||||
add(format.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun migrateCropHandle() {
|
||||
val cropHandleXName = "crop_handle_x"
|
||||
val def = CropHandle()
|
||||
if (preferences.getInt(cropHandleXName, def.x) == def.x) {
|
||||
return;
|
||||
}
|
||||
storeCropHandle(
|
||||
CameraActivity.CAMERA_CROP_HANDLE,
|
||||
CropHandle(
|
||||
preferences.getInt(cropHandleXName, def.x),
|
||||
preferences.getInt("crop_handle_y", def.y),
|
||||
preferences.getInt("crop_handle_orientation", def.orientation)
|
||||
)
|
||||
)
|
||||
preferences.edit().putInt(cropHandleXName, def.x).apply()
|
||||
}
|
||||
|
||||
fun restoreCropHandle(
|
||||
name: String
|
||||
) = preferences.restoreCropHandle(name)
|
||||
|
||||
fun storeCropHandle(name: String, cropHandle: CropHandle) {
|
||||
preferences.storeCropHandle(name, cropHandle)
|
||||
}
|
||||
|
||||
fun beepTone() = when (beepToneName) {
|
||||
@@ -314,12 +384,15 @@ class Preferences {
|
||||
else -> ToneGenerator.TONE_PROP_BEEP
|
||||
}
|
||||
|
||||
fun ignoreDuplicates() = when (ignoreDuplicatesName) {
|
||||
"ignore_consecutive_duplicates" -> IgnoreDuplicates.Consecutive
|
||||
"ignore_any_duplicates" -> IgnoreDuplicates.Any
|
||||
else -> IgnoreDuplicates.Never
|
||||
}
|
||||
|
||||
private fun put(label: String, value: Boolean) =
|
||||
preferences.edit().putBoolean(label, value)
|
||||
|
||||
private fun put(label: String, value: String) =
|
||||
preferences.edit().putString(label, value)
|
||||
|
||||
private fun apply(label: String, value: Boolean) {
|
||||
put(label, value).apply()
|
||||
}
|
||||
@@ -332,6 +405,11 @@ class Preferences {
|
||||
preferences.edit().putInt(label, value).apply()
|
||||
}
|
||||
|
||||
private fun apply(label: String, value: Float) {
|
||||
preferences.edit().putFloat(label, value).apply()
|
||||
}
|
||||
|
||||
@SuppressLint("ApplySharedPref")
|
||||
private fun commit(label: String, value: String) {
|
||||
preferences.edit().putString(label, value).commit()
|
||||
}
|
||||
@@ -342,10 +420,11 @@ class Preferences {
|
||||
}
|
||||
|
||||
companion object {
|
||||
enum class IgnoreDuplicates {
|
||||
Consecutive, Any, Never
|
||||
}
|
||||
|
||||
private const val BARCODE_FORMATS = "formats"
|
||||
private const val CROP_HANDLE_X = "crop_handle_x"
|
||||
private const val CROP_HANDLE_Y = "crop_handle_y"
|
||||
private const val CROP_HANDLE_ORIENTATION = "crop_handle_orientation"
|
||||
private const val SHOW_CROP_HANDLE = "show_crop_handle"
|
||||
private const val ZOOM_BY_SWIPING = "zoom_by_swiping"
|
||||
private const val AUTO_ROTATE = "auto_rotate"
|
||||
@@ -357,11 +436,12 @@ class Preferences {
|
||||
private const val BEEP = "beep"
|
||||
private const val BEEP_TONE_NAME = "beep_tone_name"
|
||||
private const val USE_HISTORY = "use_history"
|
||||
private const val IGNORE_CONSECUTIVE_DUPLICATES = "ignore_consecutive_duplicates"
|
||||
private const val IGNORE_DUPLICATES_NAME = "ignore_duplicates_name"
|
||||
private const val OPEN_IMMEDIATELY = "open_immediately"
|
||||
private const val COPY_IMMEDIATELY = "copy_immediately"
|
||||
private const val SHOW_META_DATA = "show_meta_data"
|
||||
private const val SHOW_HEX_DUMP = "show_hex_dump"
|
||||
private const val SHOW_CHECKSUM = "show_checksum"
|
||||
private const val SHOW_RECREATION = "show_recreation"
|
||||
private const val CLOSE_AUTOMATICALLY = "close_automatically"
|
||||
private const val DEFAULT_SEARCH_URL = "default_search_url"
|
||||
@@ -376,5 +456,8 @@ class Preferences {
|
||||
private const val INDEX_OF_LAST_SELECTED_EC_LEVEL = "index_of_last_selected_ec_level"
|
||||
private const val FREE_ROTATION = "free_rotation"
|
||||
private const val EXPAND_ESCAPE_SEQUENCES = "expand_escape_sequences"
|
||||
private const val ADD_QUIET_ZONE = "add_quiet_zone"
|
||||
private const val BRIGHTEN_SCREEN = "brighten_screen"
|
||||
private const val PREVIEW_SCALE = "preview_scale"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package de.markusfisch.android.binaryeye.service
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.PendingIntent
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.service.quicksettings.TileService
|
||||
@@ -12,6 +14,21 @@ class ScanTileService : TileService() {
|
||||
super.onClick()
|
||||
val intent = Intent(applicationContext, CameraActivity::class.java)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
startActivityAndCollapse(intent)
|
||||
// TileService#startActivityAndCollapse(Intent) is only ever run
|
||||
// below UpsideDownCake, but Lint doesn't understand this yet.
|
||||
@SuppressLint("StartActivityAndCollapseDeprecated")
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
@Suppress("DEPRECATION")
|
||||
startActivityAndCollapse(intent)
|
||||
} else {
|
||||
startActivityAndCollapse(
|
||||
PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ fun String.unescape(): String {
|
||||
i += l
|
||||
substring(from, i).toInt(8).toChar()
|
||||
}
|
||||
|
||||
'x', 'u' -> {
|
||||
val l = if (ec == 'u') 4 else 2
|
||||
val hexCode = try {
|
||||
@@ -60,6 +61,7 @@ fun String.unescape(): String {
|
||||
i += l
|
||||
unicodeValue.toChar()
|
||||
}
|
||||
|
||||
else -> throw IllegalArgumentException(
|
||||
"Unknown escape code [$ec] at index $i"
|
||||
)
|
||||
@@ -68,7 +70,13 @@ fun String.unescape(): String {
|
||||
val sb = StringBuilder()
|
||||
while (i < len) {
|
||||
val ch = get(i++)
|
||||
sb.append(if (ch == '\\' && i < len) expand() else ch)
|
||||
sb.append(
|
||||
if (ch == '\\' && i < len) {
|
||||
expand()
|
||||
} else {
|
||||
ch
|
||||
}
|
||||
)
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ private fun Context.isSilent(): Boolean {
|
||||
return when (am.ringerMode) {
|
||||
AudioManager.RINGER_MODE_SILENT,
|
||||
AudioManager.RINGER_MODE_VIBRATE -> true
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import android.support.v4.content.ContextCompat
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.support.v7.widget.Toolbar
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.widget.AbsListView
|
||||
@@ -25,7 +26,8 @@ val systemBarListViewScrollListener = object : AbsListView.OnScrollListener {
|
||||
view.post {
|
||||
val scrolled = firstVisibleItem > 0 ||
|
||||
(totalItemCount > 0 && firstChildScrolled(view))
|
||||
val scrollable = scrolled || totalItemCount > 0 && lastChildOutOfView(view)
|
||||
val scrollable = scrolled ||
|
||||
totalItemCount > 0 && lastChildOutOfView(view)
|
||||
colorSystemAndToolBars(view.context, scrolled, scrollable)
|
||||
}
|
||||
}
|
||||
@@ -37,8 +39,10 @@ val systemBarListViewScrollListener = object : AbsListView.OnScrollListener {
|
||||
val systemBarRecyclerViewScrollListener = object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
|
||||
val scrolled = layoutManager.findFirstCompletelyVisibleItemPosition() != 0
|
||||
val scrollable = scrolled || layoutManager.findLastVisibleItemPosition() <
|
||||
val scrolled =
|
||||
layoutManager.findFirstCompletelyVisibleItemPosition() != 0
|
||||
val scrollable = scrolled ||
|
||||
layoutManager.findLastVisibleItemPosition() <
|
||||
recyclerView.adapter.itemCount - 1
|
||||
colorSystemAndToolBars(recyclerView.context, scrolled, scrollable)
|
||||
}
|
||||
@@ -57,10 +61,9 @@ private fun lastChildOutOfView(listView: AbsListView): Boolean {
|
||||
return child != null && child.bottom >= listView.height
|
||||
}
|
||||
|
||||
fun initSystemBars(activity: AppCompatActivity) {
|
||||
fun AppCompatActivity.initBars() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
// Keeps the soft keyboard from repositioning the layout.
|
||||
val window = activity.window
|
||||
window.setSoftInputMode(
|
||||
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
|
||||
)
|
||||
@@ -69,7 +72,16 @@ fun initSystemBars(activity: AppCompatActivity) {
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
}
|
||||
colorSystemAndToolBars(activity)
|
||||
colorSystemAndToolBars(this)
|
||||
setPaddingFromWindowInsets(
|
||||
findViewById(R.id.main),
|
||||
(findViewById(R.id.toolbar) as Toolbar).apply {
|
||||
setSupportActionBar(this)
|
||||
},
|
||||
findViewById(R.id.navbar)?.apply {
|
||||
setBackgroundColor(translucentPrimaryColor)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private var statusBarColorLocked = false
|
||||
@@ -97,14 +109,27 @@ fun colorSystemAndToolBars(
|
||||
val topColor = if (scrolled) translucentPrimaryColor else 0
|
||||
val activity = getAppCompatActivity(context) ?: return
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
val window = activity.window
|
||||
if (!statusBarColorLocked) {
|
||||
window.statusBarColor = topColor
|
||||
}
|
||||
window.navigationBarColor = if (scrolled || scrollable) {
|
||||
translucentPrimaryColor
|
||||
// System bars no longer have a background from SDK35+.
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||
val window = activity.window
|
||||
if (!statusBarColorLocked) {
|
||||
@Suppress("DEPRECATION")
|
||||
window.statusBarColor = topColor
|
||||
}
|
||||
@Suppress("DEPRECATION")
|
||||
window.navigationBarColor = if (scrolled || scrollable) {
|
||||
translucentPrimaryColor
|
||||
} else {
|
||||
0
|
||||
}
|
||||
} else {
|
||||
0
|
||||
activity.findViewById(R.id.navbar)?.apply {
|
||||
visibility = if (scrolled || scrollable) {
|
||||
View.VISIBLE
|
||||
} else {
|
||||
View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
activity.supportActionBar?.setBackgroundDrawable(
|
||||
|
||||
@@ -7,10 +7,41 @@ import android.support.v4.view.ViewCompat
|
||||
import android.support.v4.view.WindowInsetsCompat
|
||||
import android.support.v7.widget.Toolbar
|
||||
import android.view.View
|
||||
import android.view.WindowInsets
|
||||
|
||||
private var toolbarHeight = 0
|
||||
fun recordToolbarHeight(toolbar: Toolbar) {
|
||||
fun setPaddingFromWindowInsets(
|
||||
mainLayout: View,
|
||||
toolbar: Toolbar,
|
||||
navbar: View?
|
||||
) {
|
||||
toolbarHeight = toolbar.layoutParams.height
|
||||
// SDK 35+ is edge-to-edge by default, so the toolbar needs to extend
|
||||
// below the status bar.
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||
return
|
||||
}
|
||||
mainLayout.setOnApplyWindowInsetsListener { _, insets ->
|
||||
val systemBarInsets = insets.getInsets(
|
||||
WindowInsets.Type.systemBars()
|
||||
)
|
||||
|
||||
val statusBarTop = systemBarInsets.top
|
||||
if (toolbar.paddingTop == 0 && statusBarTop > 0) {
|
||||
toolbar.setPadding(0, statusBarTop, 0, 0)
|
||||
toolbar.layoutParams.height += statusBarTop
|
||||
}
|
||||
|
||||
val navBarBottom = systemBarInsets.bottom
|
||||
if (navBarBottom > 0) {
|
||||
mainLayout.setPadding(0, 0, 0, navBarBottom);
|
||||
navbar?.apply {
|
||||
layoutParams.height = navBarBottom
|
||||
}
|
||||
}
|
||||
|
||||
insets
|
||||
}
|
||||
}
|
||||
|
||||
fun View.setPaddingFromWindowInsets() {
|
||||
|
||||
+5
@@ -10,6 +10,8 @@ import de.markusfisch.android.scalingimageview.widget.ScalingImageView
|
||||
open class ConfinedScalingImageView : ScalingImageView {
|
||||
val insets = Rect()
|
||||
|
||||
var runAfterLayout: (() -> Unit)? = null
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet, defStyle: Int) :
|
||||
super(context, attrs, defStyle)
|
||||
|
||||
@@ -37,5 +39,8 @@ open class ConfinedScalingImageView : ScalingImageView {
|
||||
)
|
||||
}
|
||||
centerRemap()
|
||||
runAfterLayout?.invoke()
|
||||
}
|
||||
|
||||
fun isInBounds(): Boolean = inBounds()
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@ class ConfinedScrollView : ScrollView {
|
||||
// not putting it on the queue makes it only work sometimes.
|
||||
post {
|
||||
getChildAt(0)?.also { child ->
|
||||
scrollable = height < child.height + paddingTop + paddingBottom
|
||||
scrollable = height <
|
||||
child.height + paddingTop + paddingBottom
|
||||
colorSystemAndToolBars(
|
||||
context,
|
||||
scrollY > 0,
|
||||
|
||||
@@ -2,7 +2,12 @@ package de.markusfisch.android.binaryeye.widget
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.*
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Path
|
||||
import android.graphics.Point
|
||||
import android.graphics.Rect
|
||||
import android.graphics.Region
|
||||
import android.os.Build
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
@@ -14,6 +19,7 @@ import de.markusfisch.android.binaryeye.R
|
||||
import de.markusfisch.android.binaryeye.app.prefs
|
||||
import de.markusfisch.android.binaryeye.graphics.getBitmapFromDrawable
|
||||
import de.markusfisch.android.binaryeye.graphics.getDashedBorderPaint
|
||||
import de.markusfisch.android.binaryeye.preference.CropHandle
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
@@ -78,11 +84,16 @@ class DetectorView : View {
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) :
|
||||
super(context, attrs, defStyleAttr)
|
||||
|
||||
fun saveCropHandlePos() {
|
||||
fun storeCropHandlePos(name: String) {
|
||||
val pos = getCropHandlePos()
|
||||
prefs.cropHandleX = pos.x
|
||||
prefs.cropHandleY = pos.y
|
||||
prefs.cropHandleOrientation = currentOrientation
|
||||
prefs.storeCropHandle(
|
||||
name,
|
||||
CropHandle(
|
||||
pos.x,
|
||||
pos.y,
|
||||
currentOrientation
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun getCropHandlePos() = if (handleActive) {
|
||||
@@ -91,11 +102,12 @@ class DetectorView : View {
|
||||
Point(-1, -1)
|
||||
}
|
||||
|
||||
fun restoreCropHandlePos() {
|
||||
fun restoreCropHandlePos(name: String) {
|
||||
val cropHandle = prefs.restoreCropHandle(name)
|
||||
setCropHandlePos(
|
||||
prefs.cropHandleX,
|
||||
prefs.cropHandleY,
|
||||
prefs.cropHandleOrientation
|
||||
cropHandle.x,
|
||||
cropHandle.y,
|
||||
cropHandle.orientation
|
||||
)
|
||||
}
|
||||
|
||||
@@ -162,6 +174,7 @@ class DetectorView : View {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
if (handleGrabbed) {
|
||||
handlePos.set(x, y)
|
||||
@@ -176,6 +189,7 @@ class DetectorView : View {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_CANCEL -> {
|
||||
if (handleGrabbed) {
|
||||
snap(x, y)
|
||||
@@ -183,6 +197,7 @@ class DetectorView : View {
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP -> {
|
||||
if (handleGrabbed) {
|
||||
if (!handleActive) {
|
||||
@@ -199,6 +214,7 @@ class DetectorView : View {
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
else -> super.onTouchEvent(event)
|
||||
}
|
||||
}
|
||||
@@ -235,7 +251,13 @@ class DetectorView : View {
|
||||
roi.set(0, 0, 0, 0)
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||
override fun onLayout(
|
||||
changed: Boolean,
|
||||
left: Int,
|
||||
top: Int,
|
||||
right: Int,
|
||||
bottom: Int
|
||||
) {
|
||||
super.onLayout(changed, left, top, right, bottom)
|
||||
val width = right - left
|
||||
val height = bottom - top
|
||||
@@ -366,8 +388,11 @@ class DetectorView : View {
|
||||
companion object {
|
||||
@JvmField
|
||||
val CREATOR = object : Parcelable.Creator<SavedState> {
|
||||
override fun createFromParcel(source: Parcel) = SavedState(source)
|
||||
override fun newArray(size: Int): Array<SavedState?> = arrayOfNulls(size)
|
||||
override fun createFromParcel(source: Parcel) =
|
||||
SavedState(source)
|
||||
|
||||
override fun newArray(size: Int): Array<SavedState?> =
|
||||
arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,19 +3,29 @@ package de.markusfisch.android.binaryeye.widget
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
|
||||
fun Context.toast(message: Int) = Toast.makeText(
|
||||
applicationContext,
|
||||
message,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
private var lastToast: Toast? = null
|
||||
|
||||
fun Context.toast(message: String) = Toast.makeText(
|
||||
applicationContext,
|
||||
message.ellipsize(128),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
fun <T> Context.toast(message: T) {
|
||||
lastToast?.cancel()
|
||||
lastToast = when (message) {
|
||||
is String -> Toast.makeText(
|
||||
applicationContext,
|
||||
message.ellipsize(),
|
||||
Toast.LENGTH_LONG
|
||||
)
|
||||
|
||||
private fun String.ellipsize(max: Int) = if (length < max) {
|
||||
is Int -> Toast.makeText(
|
||||
applicationContext,
|
||||
message,
|
||||
Toast.LENGTH_LONG
|
||||
)
|
||||
|
||||
else -> return
|
||||
}
|
||||
lastToast?.show()
|
||||
}
|
||||
|
||||
private fun String.ellipsize(max: Int = 128) = if (length < max) {
|
||||
this
|
||||
} else {
|
||||
"${take(max)}…"
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/accent"/>
|
||||
<foreground android:drawable="@drawable/ic_shortcut_history_foreground"/>
|
||||
</adaptive-icon>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 835 B |
Binary file not shown.
|
After Width: | Height: | Size: 471 B |
Binary file not shown.
|
After Width: | Height: | Size: 590 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,16 @@
|
||||
<selector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:constantSize="true">
|
||||
<item android:state_pressed="false">
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="2dp"/>
|
||||
<solid android:color="@color/selected_row"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item android:state_pressed="true">
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="2dp"/>
|
||||
<solid android:color="@color/pressed_row"/>
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
<path android:fillColor="#fff" android:pathData="M20 8.69V4h-4.69L12 0.69 8.69 4H4v4.69L0.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69zM12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6zm0-10c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,3 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M14.54 18.49v-1.98h2.97v-2.97h1.98v2.97h2.97v1.98l-2.952 0.018-0.018 2.952h-1.98l-0.018-2.952-2.952-0.018zm-1.19-3.74L11 12.4V7h2v4.6l1.4 1.4-1.05 1.75zM3 10V4h2v2.35c0.85-1.067 1.888-1.892 3.113-2.475C9.338 3.292 10.633 3 12 3c2.5 0 4.625 0.875 6.375 2.625S21 9.5 21 12h-2c0-1.95-0.679-3.604-2.037-4.963C15.604 5.679 13.95 5 12 5c-1.15 0-2.225 0.267-3.225 0.8S6.933 7.067 6.25 8H9v2H3zm9 11c-2.3 0-4.304-0.763-6.012-2.288C4.279 17.187 3.3 15.283 3.05 13H5.1c0.233 1.733 1.004 3.167 2.313 4.3C8.721 18.433 10.25 19 12 19c0.183 0 0.354-0.004 0.513-0.013 0.158-0.008 0.32-0.029 0.487-0.062v2.025c-0.167 0.017-0.329 0.029-0.487 0.037C12.354 20.996 12.183 21 12 21z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,3 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M3.9,12c0,-1.71 1.39,-3.1 3.1,-3.1h4L11,7L7,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5h4v-1.9L7,15.1c-1.71,0 -3.1,-1.39 -3.1,-3.1zM8,13h8v-2L8,11v2zM17,7h-4v1.9h4c1.71,0 3.1,1.39 3.1,3.1s-1.39,3.1 -3.1,3.1h-4L13,17h4c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="108dp" android:height="108dp" android:viewportWidth="108" android:viewportHeight="108">
|
||||
<path android:pathData="M54 72.9c-4.83 0-9.04-1.6-12.63-4.8-3.59-3.21-5.64-7.2-6.17-12h4.31c0.49 3.64 2.11 6.65 4.86 9.03 2.74 2.38 5.95 3.57 9.63 3.57 4.1 0 7.57-1.42 10.42-4.28C67.28 61.57 68.7 58.1 68.7 54s-1.42-7.57-4.28-10.42C61.57 40.72 58.1 39.3 54 39.3c-2.42 0-4.67 0.56-6.77 1.68-2.1 1.12-3.87 2.66-5.31 4.62h5.78v4.2H35.1V37.2h4.2v4.93c1.78-2.24 3.96-3.97 6.54-5.19 2.57-1.23 5.29-1.84 8.16-1.84 2.63 0 5.08 0.5 7.38 1.49 2.29 1 4.29 2.35 5.98 4.05 1.7 1.69 3.05 3.69 4.05 5.98 0.99 2.3 1.49 4.75 1.49 7.38s-0.5 5.08-1.49 7.38c-1 2.29-2.35 4.29-4.05 5.98-1.69 1.7-3.69 3.05-5.98 4.05-2.3 0.99-4.75 1.49-7.38 1.49zm5.88-10.08l-7.98-7.98V43.5h4.2v9.66l6.72 6.72-2.94 2.94z" android:fillColor="#FFFFFF"/>
|
||||
</vector>
|
||||
@@ -8,9 +8,10 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true">
|
||||
android:fitsSystemWindows="@bool/fitsSystemWindows">
|
||||
<include layout="@layout/toolbar"/>
|
||||
<SeekBar
|
||||
android:id="@+id/zoom"
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
<FrameLayout
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true">
|
||||
android:fitsSystemWindows="@bool/fitsSystemWindows">
|
||||
<include layout="@layout/toolbar"/>
|
||||
</FrameLayout>
|
||||
<include layout="@layout/navbar"/>
|
||||
</merge>
|
||||
|
||||
@@ -9,9 +9,10 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true">
|
||||
android:fitsSystemWindows="@bool/fitsSystemWindows">
|
||||
<include layout="@layout/toolbar"/>
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
style="@style/FAB"
|
||||
@@ -19,4 +20,5 @@
|
||||
android:contentDescription="@string/scan_code"
|
||||
android:src="@drawable/ic_action_next"/>
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
<include layout="@layout/navbar"/>
|
||||
</merge>
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<ScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true">
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
<!-- The FrameLayout is necessary because this LinearLayout
|
||||
should have a padding which would be reset by the window
|
||||
insets from `fitsSystemWindows`. -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="bottom"
|
||||
android:padding="24dp"
|
||||
android:orientation="vertical"
|
||||
tools:ignore="UselessParent">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:drawableTop="@drawable/logo"
|
||||
android:drawablePadding="16dp"
|
||||
android:gravity="center"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:text="@string/welcome"/>
|
||||
|
||||
<LinearLayout
|
||||
style="@style/WelcomeButton"
|
||||
android:id="@+id/expert"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
style="@style/WelcomeButtonText"
|
||||
android:text="@string/mode_expert"/>
|
||||
<TextView
|
||||
style="@style/WelcomeButtonSubText"
|
||||
android:text="@string/mode_expert_description"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
style="@style/WelcomeButton"
|
||||
android:id="@+id/simple"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
style="@style/WelcomeButtonText"
|
||||
android:text="@string/mode_simple"/>
|
||||
<TextView
|
||||
style="@style/WelcomeButtonSubText"
|
||||
android:text="@string/mode_simple_description"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
</ScrollView>
|
||||
@@ -0,0 +1,22 @@
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:text="@string/find_code_with_content"/>
|
||||
<EditText
|
||||
android:id="@+id/term"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textShortMessage"
|
||||
android:imeOptions="actionDone"
|
||||
android:hint="@string/find_code_hint"
|
||||
android:maxLines="1"
|
||||
android:maxLength="1024"
|
||||
android:importantForAutofill="no"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,23 @@
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
<TextView
|
||||
android:id="@+id/size_display"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
<SeekBar
|
||||
style="@style/Widget.AppCompat.SeekBar.Discrete"
|
||||
android:id="@+id/size_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:progress="2"
|
||||
android:max="4"/>
|
||||
</LinearLayout>
|
||||
@@ -13,6 +13,17 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
<EditText
|
||||
android:id="@+id/label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:gravity="start|top"
|
||||
android:hint="@string/label"
|
||||
android:imeOptions="flagNoExtractUi"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="text"
|
||||
tools:ignore="UnusedAttribute"/>
|
||||
<EditText
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -13,58 +13,6 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/format"/>
|
||||
<Spinner
|
||||
android:id="@+id/format"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
<TextView
|
||||
android:id="@+id/error_correction_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/error_correction_level"/>
|
||||
<Spinner
|
||||
android:id="@+id/error_correction_level"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
<TextView
|
||||
android:id="@+id/colors_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/colors"/>
|
||||
<Spinner
|
||||
android:id="@+id/colors"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:entries="@array/colors"/>
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/size"/>
|
||||
<TextView
|
||||
android:id="@+id/size_display"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:gravity="center"/>
|
||||
<SeekBar
|
||||
style="@style/Widget.AppCompat.SeekBar.Discrete"
|
||||
android:id="@+id/size_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:progress="4"
|
||||
android:max="7"/>
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -75,7 +23,6 @@
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:gravity="start|top"
|
||||
android:typeface="monospace"
|
||||
android:imeOptions="flagNoExtractUi"
|
||||
@@ -88,6 +35,42 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/unescape"/>
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/format"/>
|
||||
<Spinner
|
||||
style="@style/Spinner"
|
||||
android:id="@+id/format"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
<TextView
|
||||
android:id="@+id/error_correction_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/error_correction_level"/>
|
||||
<Spinner
|
||||
style="@style/Spinner"
|
||||
android:id="@+id/error_correction_level"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
<TextView
|
||||
android:id="@+id/colors_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/colors"/>
|
||||
<Spinner
|
||||
style="@style/Spinner"
|
||||
android:id="@+id/colors"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:entries="@array/colors"/>
|
||||
<android.support.v7.widget.SwitchCompat
|
||||
android:visibility="gone"
|
||||
android:id="@+id/add_quiet_zone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/add_quiet_zone"/>
|
||||
</LinearLayout>
|
||||
</de.markusfisch.android.binaryeye.widget.ConfinedScrollView>
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<!-- Navigation bar background from SDK35+ -->
|
||||
<View
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/navbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:visibility="gone"/>
|
||||
@@ -13,7 +13,7 @@
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/pick_file"
|
||||
android:title="@string/pick_file"
|
||||
android:title="@string/pick_image_file"
|
||||
android:icon="@drawable/ic_action_pick_file"
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
@@ -32,6 +32,11 @@
|
||||
android:title="@string/restrict_format"
|
||||
android:icon="@drawable/ic_action_restrict_format"
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/find_code"
|
||||
android:title="@string/find_code"
|
||||
android:icon="@drawable/ic_action_search"
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/preferences"
|
||||
android:title="@string/preferences"
|
||||
|
||||
@@ -1,14 +1,30 @@
|
||||
<menu
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:material="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/add_to_history"
|
||||
android:title="@string/add_to_history"
|
||||
android:icon="@drawable/ic_action_history_add"
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/copy_to_clipboard"
|
||||
android:title="@string/copy_to_clipboard"
|
||||
android:icon="@drawable/ic_action_copy"
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/copy_as_deeplink"
|
||||
android:title="@string/copy_as_deeplink"
|
||||
android:icon="@drawable/ic_action_link"
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/export_to_file"
|
||||
android:title="@string/export_to_file"
|
||||
android:icon="@drawable/ic_action_save"
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/brighten_screen"
|
||||
android:title="@string/brighten_screen"
|
||||
android:icon="@drawable/ic_action_bright"
|
||||
android:checkable="true"
|
||||
material:showAsAction="ifRoom"/>
|
||||
</menu>
|
||||
|
||||
@@ -18,11 +18,21 @@
|
||||
android:title="@string/copy_to_clipboard"
|
||||
android:icon="@drawable/ic_action_copy"
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/copy_as_deeplink"
|
||||
android:title="@string/copy_as_deeplink"
|
||||
android:icon="@drawable/ic_action_link"
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/share"
|
||||
android:title="@string/share"
|
||||
android:icon="@drawable/ic_action_share"
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/share_file"
|
||||
android:title="@string/share_file"
|
||||
android:icon="@drawable/ic_action_pick_file"
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/create"
|
||||
android:title="@string/compose_barcode"
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<menu
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:material="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/pick_file"
|
||||
android:title="@string/pick_file"
|
||||
android:icon="@drawable/ic_action_pick_file"
|
||||
material:showAsAction="ifRoom"/>
|
||||
</menu>
|
||||
@@ -1,23 +1,25 @@
|
||||
<menu
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:material="http://schemas.android.com/apk/res-auto">
|
||||
<group android:id="@+id/scans_available"
|
||||
android:visible="false">
|
||||
<item
|
||||
android:id="@+id/search"
|
||||
android:icon="@drawable/ic_action_search"
|
||||
android:title="@string/search_scan"
|
||||
material:actionViewClass="android.support.v7.widget.SearchView"
|
||||
material:showAsAction="collapseActionView|always"/>
|
||||
<item
|
||||
android:id="@+id/export_history"
|
||||
android:icon="@drawable/ic_action_export"
|
||||
android:title="@string/export_to_file"
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/clear"
|
||||
android:icon="@drawable/ic_action_remove"
|
||||
android:title="@string/clear_history"
|
||||
material:showAsAction="ifRoom"/>
|
||||
</group>
|
||||
<item
|
||||
android:id="@+id/search"
|
||||
android:icon="@drawable/ic_action_search"
|
||||
android:title="@string/search_scan"
|
||||
material:actionViewClass="android.support.v7.widget.SearchView"
|
||||
material:showAsAction="collapseActionView|always"/>
|
||||
<item
|
||||
android:id="@+id/export_history"
|
||||
android:icon="@drawable/ic_action_export"
|
||||
android:title="@string/export_to_file"
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/share_as_file"
|
||||
android:icon="@drawable/ic_action_share"
|
||||
android:title="@string/share_file"
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/clear"
|
||||
android:icon="@drawable/ic_action_remove"
|
||||
android:title="@string/clear_history"
|
||||
material:showAsAction="ifRoom"/>
|
||||
</menu>
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
android:icon="@drawable/ic_action_copy"
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/edit_scan"
|
||||
android:title="@string/edit_scan"
|
||||
android:id="@+id/edit_label"
|
||||
android:title="@string/edit_label"
|
||||
android:icon="@drawable/ic_action_edit"
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="welcome">مرحبًا!</string>
|
||||
<string name="mode_expert">متقدم</string>
|
||||
<string name="mode_expert_description">أود التحقق من المحتوى المقروء وفتحه يدويًا.</string>
|
||||
<string name="mode_simple">بسيط</string>
|
||||
<string name="mode_simple_description">أريد فقط قراءة الباركود وفتحه فورًا.</string>
|
||||
<string name="camera_error">يتعذر الوصول إلى الكاميرا، يرجى المحاولة مرة أخرى</string>
|
||||
<string name="scan_code">مسح الرمز</string>
|
||||
<string name="compose_barcode">إنشاء رمز Bardcode</string>
|
||||
<string name="decode_barcode">فك رمز Barcode</string>
|
||||
<string name="content">المحتوى</string>
|
||||
<string name="binary_data">(بيانات ثنائية)</string>
|
||||
<plurals name="barcode_info">
|
||||
<item quantity="zero">%2$d حروف، %1$s</item>
|
||||
<item quantity="one">حرف واحد، %1$s</item>
|
||||
<item quantity="two">حرفين، %1$s</item>
|
||||
<item quantity="few">%2$d حروف، %1$s</item>
|
||||
<item quantity="many">%2$d حرف، %1$s</item>
|
||||
<item quantity="other">%2$d حرف، %1$s</item>
|
||||
</plurals>
|
||||
<string name="error_correction_level">مستوى تصحيح الأخطاء</string>
|
||||
<string name="error_correction_level_l" formatted="false">منخفض (~7% تصحيح)</string>
|
||||
<string name="error_correction_level_m" formatted="false">متوسط (~15% تصحيح)</string>
|
||||
<string name="error_correction_level_q" formatted="false">ربع (~25% تصحيح)</string>
|
||||
<string name="error_correction_level_h" formatted="false">مرتفع (~30% تصحيح)</string>
|
||||
<string name="label">العنوان</string>
|
||||
<string name="colors">المقدمة والخلفية</string>
|
||||
<string name="colors_black_on_white">أسود على خلفية بيضاء</string>
|
||||
<string name="colors_white_on_black">أبيض على خلفية سوداء</string>
|
||||
<string name="colors_black_on_transparent">أسود على خلفية شفافة</string>
|
||||
<string name="colors_white_on_transparent">أبيض على خلفية شفافة</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" tools:ignore="PluralsCandidate">%1$s (%2$d وحدات)</string>
|
||||
<string name="qr_data_mask">قناع البيانات</string>
|
||||
<string name="toggle_flash">تشغيل/إيقاف الوميض</string>
|
||||
<string name="error_flash">خطأ في تشغيل الوميض</string>
|
||||
<string name="share">مشاركة</string>
|
||||
<string name="share_file">مشاركة كملف</string>
|
||||
<string name="share_as">مشاركة كـ؟</string>
|
||||
<string name="copy_to_clipboard">نسخ إلى الحافظة</string>
|
||||
<string name="copy_as_deeplink">نسخ كرابط عميق</string>
|
||||
<string name="copied_to_clipboard">تم النسخ إلى الحافظة</string>
|
||||
<string name="copy_password">نسخ كلمة المرور إلى الحافظة</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_width_by_height">الحجم بالبكسل: %1$d×%2$d</string>
|
||||
<string name="size_no_magnification">بدون تكبير</string>
|
||||
<string name="add_quiet_zone">إضافة منطقة هادئة</string>
|
||||
<string name="input_content_here">أدخل المحتوى هنا</string>
|
||||
<string name="unescape">توسيع رموز الهروب (escape sequences)</string>
|
||||
<string name="encode">ترميز</string>
|
||||
<string name="error_no_content">المحتوى مفقود</string>
|
||||
<string name="error_encoding_barcode">لا يمكن إنشاء رمز Barcode</string>
|
||||
<string name="view_barcode">عرض رمز 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="find_code">العثور على الرمز</string>
|
||||
<string name="find_code_with_content">ما المحتوى؟</string>
|
||||
<string name="find_code_hint">أدخل مصطلحًا</string>
|
||||
<string name="scan_format">مسح %s</string>
|
||||
<string name="quarter_second">ربع ثانية</string>
|
||||
<string name="half_second">نصف ثانية</string>
|
||||
<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">تنسيقات Barcode</string>
|
||||
<string name="zoom_by_swiping">مرر لأعلى أو لأسفل للتكبير/التصغير</string>
|
||||
<string name="zoom_by_swiping_summary">مرر لأعلى أو لأسفل على واجهة الكاميرا للتحكم في التكبير/التصغير لها.</string>
|
||||
<string name="auto_rotate">التعرف على رموز Barcode العمودية أحادية الأبعاد</string>
|
||||
<string name="auto_rotate_summary">تدوير إطار الكاميرا تلقائيًا للتعرف على رموز Barcode العمودية أحادية الأبعاد. بناءً على الجهاز المستخدم، قد يؤثر ذلك على الأداء.</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="beep">صوت صفير عند الكشف</string>
|
||||
<string name="beep_summary">قم بتمكين هذا إذا كنت تريد أن يصدر جهازك صوتًا عند التعرف على رمز.</string>
|
||||
<string name="beep_tone">نغمة الصفير</string>
|
||||
<string name="tone_cdma_confirm">تأكيد CDMA</string>
|
||||
<string name="tone_sup_confirm">تأكيد الإشراف على الاتصال</string>
|
||||
<string name="tone_sup_radio_ack">إقرار مسار الراديو الإشرافي على الاتصال</string>
|
||||
<string name="tone_prop_ack">إقرار إيجابي</string>
|
||||
<string name="tone_prop_beep">صوت صفير عام</string>
|
||||
<string name="tone_prop_beep2">صوت صفير مزدوج عام</string>
|
||||
<string name="use_history">حفظ تاريخ المسح</string>
|
||||
<string name="use_history_summary">حفظ الرموز المتعرف عليها على جهازك.</string>
|
||||
<string name="ignore_duplicates">تجاهل النسخ المكررة</string>
|
||||
<string name="ignore_consecutive_duplicates">النسخ المكررة المتتالية</string>
|
||||
<string name="ignore_any_duplicates">أي نسخ مكررة</string>
|
||||
<string name="accept_duplicates">قبول النسخ المكررة</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">عرض بيانات إضافية حول رمز Barcode الممسوح.</string>
|
||||
<string name="show_hex_dump">عرض التفريغ سداسي عشري</string>
|
||||
<string name="show_hex_dump_summary">عرض تفريغ سداسي عشري للمحتويات الممسوحة.</string>
|
||||
<string name="show_checksum">عرض الـ checksum</string>
|
||||
<string name="no_checksum">الـ checksum غير متوفر</string>
|
||||
<string name="show_recreation">إظهار رمز Barcode المعاد إنشاؤه</string>
|
||||
<string name="show_recreation_summary">إعادة إنشاء وإظهار رمز Barcode من المحتوى الذي تمت قراءته إن أمكن.</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">فتح البيانات المجهولة باستخدام رابط</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="send_scan_bluetooth">إرسال عمليات المسح عبر بلوتوث</string>
|
||||
<string name="send_scan_bluetooth_summary">تمكين إرسال عمليات المسح إلى مضيف بلوتوث معين.</string>
|
||||
<string name="send_scan_bluetooth_host">المضيف</string>
|
||||
<string name="no_bluetooth_hosts_paired">لا يوجد اقتران مع مضيفين</string>
|
||||
<string name="bluetooth_connect_fail">تعذر الاتصال بجهاز بلوتوث.</string>
|
||||
<string name="bluetooth_send_fail">تعذر الإرسال إلى جهاز بلوتوث.</string>
|
||||
<string name="bluetooth_send_success">نجح الإرسال إلى جهاز بلوتوث.</string>
|
||||
<string name="generation_category">التوليد</string>
|
||||
<string name="brighten_screen">شاشة ساطعة</string>
|
||||
<string name="brighten_screen_summary">تعيين مستوى السطوع إلى الحد الأقصى لتحسين مسح الرمز من الشاشة.</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_label">تعديل العنوان</string>
|
||||
<string name="remove_scan">حذف عملية المسح</string>
|
||||
<string name="enter_name">أدخل اسمًا لوصف عملية المسح</string>
|
||||
<string name="enter_name_hint">اسم عملية المسح</string>
|
||||
<string name="no_barcode_found">لا يوجد أي رمز Barcode</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">الاتصال بشبكة Wi-Fi</string>
|
||||
<string name="wifi_config_failed">تعذر تهيئة شبكة Wi-Fi</string>
|
||||
<string name="wifi_added">تمت إضافة شبكة Wi-Fi</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="add_to_history">إضافة إلى التاريخ</string>
|
||||
<string name="added_to_history">تمت الإضافة إلى التاريخ</string>
|
||||
<string name="export_to_file">تصدير إلى ملف</string>
|
||||
<string name="export_as">تصدير كـ؟</string>
|
||||
<string name="export_csv_comma">ملف CSV مع فواصل</string>
|
||||
<string name="export_csv_semicolon">ملف CSV مع فواصل منقوطة</string>
|
||||
<string name="export_json">ملف JSON</string>
|
||||
<string name="export_database">قاعدة بيانات SQLite</string>
|
||||
<string name="saved_in_downloads">تم الحفظ في مجلد التنزيلات</string>
|
||||
<string name="rotate_image_cw">تدوير الصورة باتجاه عقارب الساعة</string>
|
||||
<string name="toggle_free_rotation">تبديل التدوير الحر</string>
|
||||
<string name="pick_file">اختر ملف</string>
|
||||
<string name="pick_image_file">اختر صورة</string>
|
||||
<string name="pick_code_to_scan">اختر رمزًا لمسحه</string>
|
||||
<string name="shortcut_decode">مسح رمز Barcode</string>
|
||||
<string name="shortcut_encode">إنشاء رمز Barcode</string>
|
||||
<string name="shortcut_history">التاريخ</string>
|
||||
<string name="shortcut_preferences">الإعدادات</string>
|
||||
<string name="background_request_failed">فشل الطلب للعمل في الخلفية</string>
|
||||
<string name="entry_type">النوع</string>
|
||||
<string name="wifi_network">شبكة Wi-Fi</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">طريقة Phase 2</string>
|
||||
<string name="network_suggestions">الشبكات المقترحة</string>
|
||||
<string name="remove_suggestion">إزالة الاقتراحات</string>
|
||||
<string name="email_to">المستلم</string>
|
||||
<string name="email_subject">الموضوع</string>
|
||||
<string name="email_body">النص</string>
|
||||
<string name="scheme">المخطط</string>
|
||||
<string name="host">المضيف</string>
|
||||
<string name="query">استعلام</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,224 @@
|
||||
<resources>
|
||||
<string name="welcome">Добре дошли!</string>
|
||||
<string name="mode_expert">Разширен</string>
|
||||
<string name="mode_expert_description">Искам да проверя прочетеното съдържание и да го отворя ръчно.</string>
|
||||
<string name="mode_simple">Опростен</string>
|
||||
<string name="mode_simple_description">Искам просто да сканирам баркодове и да ги отварям веднага.</string>
|
||||
<string name="camera_error">Няма достъп до камерата, моля, опитайте отново</string>
|
||||
<string name="scan_code">Сканирайте код</string>
|
||||
<string name="compose_barcode">Създаване на баркод</string>
|
||||
<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">Ниско (~7% корекция)</string>
|
||||
<string name="error_correction_level_m" formatted="false">Средно (~15% корекция)</string>
|
||||
<string name="error_correction_level_q" formatted="false">Четвъртично (~25% корекция)</string>
|
||||
<string name="error_correction_level_h" formatted="false">Високо (~30% корекция)</string>
|
||||
<string name="label">Етикет</string>
|
||||
<string name="colors">Преден и заден план</string>
|
||||
<string name="colors_black_on_white">Черно на бяло</string>
|
||||
<string name="colors_white_on_black">Бяло на черно</string>
|
||||
<string name="colors_black_on_transparent">Черно на прозрачно</string>
|
||||
<string name="colors_white_on_transparent">Бяло на прозрачно</string>
|
||||
<string name="sequence_size">Размер на последователността</string>
|
||||
<string name="sequence_index">Индекс на последователността</string>
|
||||
<string name="sequence_id">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$s (%2$d модули)</string>
|
||||
<string name="qr_data_mask">Маска за данни</string>
|
||||
<string name="toggle_flash">Превключване на светкавицата</string>
|
||||
<string name="error_flash">Грешка при превключването на светкавицата</string>
|
||||
<string name="share">Сподели</string>
|
||||
<string name="share_file">Споделяне като файл</string>
|
||||
<string name="share_as">Сподели като?</string>
|
||||
<string name="copy_to_clipboard">Копиране в клипборда</string>
|
||||
<string name="copy_as_deeplink">Копиране като дийп линк</string>
|
||||
<string name="copied_to_clipboard">Копирано в клипборда</string>
|
||||
<string name="copy_password">Копиране на паролата в клипборда</string>
|
||||
<string name="open_url">Отворете URL(Линк)</string>
|
||||
<string name="cannot_resolve_action">Няма приложение което може да отвори това</string>
|
||||
<string name="pick_search_engine">Изберете търсачка</string>
|
||||
<string name="format">Формат</string>
|
||||
<string name="size_width_by_height">Размер в пиксели: %1$d×%2$d</string>
|
||||
<string name="size_no_magnification">Без увеличение</string>
|
||||
<string name="add_quiet_zone">Добавяне на тиха зона</string>
|
||||
<string name="input_content_here">Въведете съдържание тук</string>
|
||||
<string name="unescape">Разширете последователности за извеждане</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="find_code">Намерете кода</string>
|
||||
<string name="find_code_with_content">Какво съдържание?</string>
|
||||
<string name="find_code_hint">Въведете термин</string>
|
||||
<string name="scan_format">Сканиране %s</string>
|
||||
<string name="quarter_second">Червърт секунда</string>
|
||||
<string name="half_second">Половин секунда</string>
|
||||
<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">Разпознаване на вертикални 1D баркодове</string>
|
||||
<string name="auto_rotate_summary">Автоматично завъртете рамката на камерата, за разпознаване на вертикални 1D баркодове. В зависимост от устройството това може да повлияе на скоростта.</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="beep">Звуков сигнал при сканиране</string>
|
||||
<string name="beep_summary">Активирайте това, ако искате вашето устройство да издава звуков сигнал при разпознаване на код.</string>
|
||||
<string name="beep_tone">Звуков сигнал</string>
|
||||
<string name="tone_cdma_confirm">CDMA потвърждение</string>
|
||||
<string name="tone_sup_confirm">Потвърждение на надзорния разговор</string>
|
||||
<string name="tone_sup_radio_ack">Потвърждение на контролния радиопът на повикване</string>
|
||||
<string name="tone_prop_ack">Положително признание</string>
|
||||
<string name="tone_prop_beep">Общ звуков сигнал</string>
|
||||
<string name="tone_prop_beep2">Общ двоен звуков сигнал</string>
|
||||
<string name="use_history">Хронология на сканирането</string>
|
||||
<string name="use_history_summary">Запазва разпознатите кодове на вашето устройство.</string>
|
||||
<string name="ignore_duplicates">Игнорирайте дубликати</string>
|
||||
<string name="ignore_consecutive_duplicates">Игнориране на дубликатите</string>
|
||||
<string name="ignore_any_duplicates">Ignore any duplicates</string>
|
||||
<string name="accept_duplicates">Приемане на дубликати</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="show_checksum">Покажи контролна сума</string>
|
||||
<string name="no_checksum">Без контролна сума</string>
|
||||
<string name="show_recreation">Показване на пресъздаден баркод</string>
|
||||
<string name="show_recreation_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">Включване на препращането на сканиранията към даден 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">Отваряне във външен браузър</string>
|
||||
<string name="test_url">Тествайте URL(Линка)</string>
|
||||
<string name="send_scan_bluetooth">Препращане на сканиранията с Bluetooth</string>
|
||||
<string name="send_scan_bluetooth_summary">Активиране на препращане на сканирания към даден Bluetooth хост.</string>
|
||||
<string name="send_scan_bluetooth_host">Хост</string>
|
||||
<string name="no_bluetooth_hosts_paired">Няма сдвоени хостове</string>
|
||||
<string name="bluetooth_connect_fail">Неуспешно свързване с Bluetooth устройство.</string>
|
||||
<string name="bluetooth_send_fail">Неуспешно изпращане до Bluetooth устройство.</string>
|
||||
<string name="bluetooth_send_success">Успешно Bluetooth изпращане.</string>
|
||||
<string name="generation_category">Поколение</string>
|
||||
<string name="brighten_screen">Подсвети екрана</string>
|
||||
<string name="brighten_screen_summary">Задайте яркостта на максимум, за да подобрите сканирането на баркод от екрана.</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_label">Редактиране на етикет</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">Свържи се към Wi-Fi</string>
|
||||
<string name="wifi_config_failed">Неуспешно конфигуриране на Wi-Fi</string>
|
||||
<string name="wifi_added">Wi-Fi успешно добавен</string>
|
||||
<string name="sms_send">Изпрати SMS</string>
|
||||
<string name="sms_error">Неуспешно изпращане на SMS</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="add_to_history">Добави към историята</string>
|
||||
<string name="added_to_history">Добавено към историята</string>
|
||||
<string name="export_to_file">Извеждане в файл</string>
|
||||
<string name="export_as">Извеждане като?</string>
|
||||
<string name="export_csv_comma">CSV файл със запетаи</string>
|
||||
<string name="export_csv_semicolon">CSV файл с точки и запетаи</string>
|
||||
<string name="export_json">JSON файл</string>
|
||||
<string name="export_database">SQLite база данни</string>
|
||||
<string name="saved_in_downloads">Запазено в изтеглянията</string>
|
||||
<string name="rotate_image_cw">Завъртете изображението по часовниковата стрелка</string>
|
||||
<string name="toggle_free_rotation">Превключване на свободното въртене</string>
|
||||
<string name="pick_file">Изберете файл</string>
|
||||
<string name="pick_image_file">Изберете снимка</string>
|
||||
<string name="pick_code_to_scan">Изберете код за сканиране</string>
|
||||
<string name="shortcut_decode">Сканирай баркод</string>
|
||||
<string name="shortcut_encode">Създай баркод</string>
|
||||
<string name="shortcut_history">История</string>
|
||||
<string name="shortcut_preferences">Настройки</string>
|
||||
<string name="background_request_failed">Неуспешна заявка в фонов режим</string>
|
||||
<string name="entry_type">Тип</string>
|
||||
<string name="wifi_network">Wi-Fi мрежа</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">Фаза 2 метод</string>
|
||||
<string name="network_suggestions">Предложения за интернет мрежа</string>
|
||||
<string name="remove_suggestion">Премахване на предложението</string>
|
||||
<string name="email_to">Получател</string>
|
||||
<string name="email_subject">Тема</string>
|
||||
<string name="email_body">Съдържание</string>
|
||||
<string name="scheme">Схема</string>
|
||||
<string name="host">Домакин</string>
|
||||
<string name="query">Заявка</string>
|
||||
</resources>
|
||||
@@ -1,4 +1,9 @@
|
||||
<resources>
|
||||
<string name="welcome">স্বাগতম!</string>
|
||||
<string name="mode_expert">উন্নত</string>
|
||||
<string name="mode_expert_description">আমি পড়া বিষয়বস্তু যাচাই করতে চাই এবং এটি নিজে খুলতে চাই।</string>
|
||||
<string name="mode_simple">সহজ</string>
|
||||
<string name="mode_simple_description">আমি কেবল বারকোড স্ক্যান করতে চাই এবং তা সঙ্গে সঙ্গে খুলতে চাই।</string>
|
||||
<string name="camera_error">ক্যামেরা পাওয়া যাচ্ছে না, পরে চেষ্টা করো</string>
|
||||
<string name="scan_code">সংকেতের পাঠোদ্ধার</string>
|
||||
<string name="compose_barcode">বারকোড বানাও</string>
|
||||
@@ -14,36 +19,40 @@
|
||||
<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="colors">Fore and background</string>
|
||||
<string name="colors_black_on_white">Black on white</string>
|
||||
<string name="colors_white_on_black">White on black</string>
|
||||
<string name="colors_black_on_transparent">Black on transparent</string>
|
||||
<string name="colors_white_on_transparent">White on transparent</string>
|
||||
<string name="sequence_size">Sequence size</string>
|
||||
<string name="sequence_index">Sequence index</string>
|
||||
<string name="sequence_id">Sequence ID</string>
|
||||
<string name="label">লেবেল</string>
|
||||
<string name="colors">পূর্ববর্তী এবং সৃজনাধীন</string>
|
||||
<string name="colors_black_on_white">সাদা উপরে কালো</string>
|
||||
<string name="colors_white_on_black">কালো উপর সাদা</string>
|
||||
<string name="colors_black_on_transparent">সাদা সহজ পারদার উপর</string>
|
||||
<string name="colors_white_on_transparent">পারদার উপর সাদা</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">ইউপিসি ইএএন বর্ধিত</string>
|
||||
<string name="barcode_version_number">Version</string>
|
||||
<string name="barcode_version_number">সংস্করণ</string>
|
||||
<string name="qr_version_and_modules">%1$s (%2$d modules)</string>
|
||||
<string name="qr_data_mask">ডেটা মাস্ক</string>
|
||||
<string name="toggle_flash">আলো</string>
|
||||
<string name="error_flash">Error toggling flash</string>
|
||||
<string name="error_flash">ফ্ল্যাশ টগল করার সময় ত্রুটি</string>
|
||||
<string name="share">ভাগ করো</string>
|
||||
<string name="share_file">ফাইল হিসেবে ভাগাভাগি</string>
|
||||
<string name="share_as">ভাগ করার মাধ্যম</string>
|
||||
<string name="copy_to_clipboard">ক্লিপবোর্ডে অনুলিপি</string>
|
||||
<string name="copy_as_deeplink">ডিপলিংক হিসেবে কপি করুন</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="size_width_by_height">পিক্সেলে আকা:%1$d×%2$d</string>
|
||||
<string name="size_no_magnification">কোনো বৃদ্ধি নেই</string>
|
||||
<string name="add_quiet_zone">শান্তি জোন যোগ করুন</string>
|
||||
<string name="input_content_here">এখানে তথ্য দাও</string>
|
||||
<string name="unescape">Expand escape sequences</string>
|
||||
<string name="unescape">এস্কেপ সিকোয়েন্স প্রসারিত করুন</string>
|
||||
<string name="encode">সংকেতকরণ</string>
|
||||
<string name="error_no_content">তথ্য নেই</string>
|
||||
<string name="error_encoding_barcode">বারকোড বানানো যায়নি</string>
|
||||
@@ -52,15 +61,18 @@
|
||||
<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="find_code">কোড খুঁজুন</string>
|
||||
<string name="find_code_with_content"> কী বিষয়টি? </string>
|
||||
<string name="find_code_hint">একটি শব্দ লিখুন</string>
|
||||
<string name="scan_format">স্ক্যান %s</string>
|
||||
<string name="quarter_second">এক চতুর্থ সেকেন্ড</string>
|
||||
<string name="half_second">আধা সেকেন্ড</string>
|
||||
<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>
|
||||
@@ -70,7 +82,7 @@
|
||||
<string name="follow_system_settings">ব্যবস্থার পছন্দসমূহ অনুসরণ করো</string>
|
||||
<string name="show_crop_handle">ছাটার সীমা দেখাও</string>
|
||||
<string name="show_crop_handle_summary">পরিবর্তনযোগ্য জায়গায়ই শুধু খুঁজো।</string>
|
||||
<string name="barcode_formats">Barcode formats</string>
|
||||
<string name="barcode_formats">বারকোড বিন্যাস</string>
|
||||
<string name="zoom_by_swiping">ওপরে/নিচে টানো বড়ো করার জন্য</string>
|
||||
<string name="zoom_by_swiping_summary">ক্যামেরার ছোটো-বড় নিয়ন্ত্রণের জন্য উপরে-নিচে টানো।</string>
|
||||
<string name="auto_rotate">খাড়া ১ডি বারকোড সনাক্ত করো</string>
|
||||
@@ -81,19 +93,21 @@
|
||||
<string name="show_toast_in_bulk_mode_summary">ধারাবাহিকভাবে তথ্য দেখানোর সময় মুহূর্তের জন্য তথ্য দেখাও।</string>
|
||||
<string name="vibrate">চিনলে কাঁপো</string>
|
||||
<string name="vibrate_summary">এটা সক্ষম রাখলে সংকেত চিনার সাথে সাথে কাঁপবে।</string>
|
||||
<string name="beep">Beep on detection</string>
|
||||
<string name="beep_summary">Enable this if you want your device to beep when a code is recognized.</string>
|
||||
<string name="beep_tone">Beep tone</string>
|
||||
<string name="tone_cdma_confirm">CDMA confirm</string>
|
||||
<string name="tone_sup_confirm">Call supervisory confirm</string>
|
||||
<string name="tone_sup_radio_ack">Call supervisory radio path acknowledgment</string>
|
||||
<string name="tone_prop_ack">Positive acknowledgment</string>
|
||||
<string name="tone_prop_beep">General beep</string>
|
||||
<string name="tone_prop_beep2">General double beep</string>
|
||||
<string name="beep">প্রকোপে বিপ দিন</string>
|
||||
<string name="beep_summary">যদি আপনি চান যে আপনার ডিভাইসটি কোড প্রাপ্ত হলে বিপ শব্দ করে।</string>
|
||||
<string name="beep_tone">বিপ টোন</string>
|
||||
<string name="tone_cdma_confirm">সিডিএমএ নিশ্চিত করুন</string>
|
||||
<string name="tone_sup_confirm">কল সুপারভাইজরী কনফার্ম</string>
|
||||
<string name="tone_sup_radio_ack">কল সুপারভাইজরি রেডিও পাথ স্বীকৃতি</string>
|
||||
<string name="tone_prop_ack">ধন্যবাদ</string>
|
||||
<string name="tone_prop_beep">সাধারণ বিপ</string>
|
||||
<string name="tone_prop_beep2">সাধারণ ডাবল বিপ</string>
|
||||
<string name="use_history">ইতিহাস সংরক্ষণ করো।</string>
|
||||
<string name="use_history_summary">সনাক্তকৃত সংকেত যন্ত্রে সংরক্ষণ করো।</string>
|
||||
<string name="ignore_duplicates">নকল উপেক্ষা করুন</string>
|
||||
<string name="ignore_consecutive_duplicates">দ্বিত্ব উপেক্ষা করো</string>
|
||||
<string name="ignore_consecutive_duplicates_summary">দুইটা ইতিহাস সংরক্ষণ করবে না।</string>
|
||||
<string name="ignore_any_duplicates">Ignore any duplicates</string>
|
||||
<string name="accept_duplicates">ডুপ্লিকেট গ্রহণ করুন</string>
|
||||
<string name="open_immediately">সাথে সাথে খুলো</string>
|
||||
<string name="open_immediately_summary">না দেখে সাথে সাথে খুলো। সক্রিয় থাকলে ক্ষতিকারক তথ্য খুলতে পারে।</string>
|
||||
<string name="copy_immediately">ক্লিপবোর্ডে রাখো</string>
|
||||
@@ -102,20 +116,22 @@
|
||||
<string name="show_meta_data_summary">সনাক্তকৃত বারকোড নিয়ে অতিরিক্ত তথ্য দেখাও।</string>
|
||||
<string name="show_hex_dump">ষড়(hex) সংকেত দেখাও</string>
|
||||
<string name="show_hex_dump_summary">পড়া তথ্যের ষড় সংকেত দেখাও</string>
|
||||
<string name="show_recreation">Show recreated barcode</string>
|
||||
<string name="show_recreation_summary">Recreate and show barcode from read content if possible.</string>
|
||||
<string name="show_checksum">সামগ্রিক চেকসাম দেখান</string>
|
||||
<string name="no_checksum">কোনো চেকসাম নেই</string>
|
||||
<string name="show_recreation">পুনরায় তৈরি বারকোড দেখান</string>
|
||||
<string name="show_recreation_summary">যদি সম্ভব হয়, পঠিত বিষয় থেকে পুনরায় সৃষ্টি করুন এবং বারকোড দেখান।</string>
|
||||
<string name="close_automatically">অনুলিপি/ভাগ করার পরে পিছনে যাও</string>
|
||||
<string name="close_automatically_summary">তথ্য অনুলিপি/ভাগ করার পরে স্বয়ংক্রিয়ভাবে পিছনে যাও।</string>
|
||||
<string name="default_search_engine">Open unknown data in</string>
|
||||
<string name="always_ask">Always ask</string>
|
||||
<string name="default_search_engine">অজানা ডেটা খোলুন</string>
|
||||
<string name="always_ask">সবসময় জিজ্ঞাসা করুন</string>
|
||||
<string name="open_with_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="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">Forward scans</string>
|
||||
<string name="send_scan_active">পুনরায় স্ক্যান পাঠান</string>
|
||||
<string name="send_scan_active_summary">Enable to forward scans to a the given URL</string>
|
||||
<string name="send_scan_url">প্রতিটি পাঠোদ্ধার ইউআরএল-এ পাঠাও</string>
|
||||
<string name="send_scan_type">প্রতি পাঠোদ্ধারের জন্য অনুরোধ</string>
|
||||
@@ -123,21 +139,24 @@
|
||||
<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">পরীক্ষার জন্য ইউআরএল</string>
|
||||
<string name="send_scan_bluetooth">Forward scans with Bluetooth</string>
|
||||
<string name="send_scan_bluetooth">ব্লুটুথ দিয়ে স্ক্যান ফরোয়ার্ড করুন</string>
|
||||
<string name="send_scan_bluetooth_summary">Enable to forwards scans to a given Bluetooth host.</string>
|
||||
<string name="send_scan_bluetooth_host">Host</string>
|
||||
<string name="no_bluetooth_hosts_paired">No Hosts Paired</string>
|
||||
<string name="send_scan_bluetooth_host">হোস্ট</string>
|
||||
<string name="no_bluetooth_hosts_paired">কোন হোস্ট পেয়ার করা হয়নি</string>
|
||||
<string name="bluetooth_connect_fail">Could not connect to bluetooth device.</string>
|
||||
<string name="bluetooth_send_fail">Could not send to bluetooth device.</string>
|
||||
<string name="bluetooth_send_success">Successful bluetooth send.</string>
|
||||
<string name="generation_category">প্রজন্ম</string>
|
||||
<string name="brighten_screen">স্ক্রিন উজ্জ্বল করুন</string>
|
||||
<string name="brighten_screen_summary">স্ক্রিন থেকে বারকোড স্ক্যান করার জন্য উজ্জ্বলতা সর্বোচ্চ করুন।</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="edit_label">চিহ্ন সম্পাদনা করো</string>
|
||||
<string name="remove_scan">পাঠোদ্ধার মুছো</string>
|
||||
<string name="enter_name">পাঠোদ্ধার ব্যাখ্যা করতে একটা নাম দাও</string>
|
||||
<string name="enter_name_hint">পাঠোদ্ধারের নাম</string>
|
||||
@@ -165,6 +184,8 @@
|
||||
<string name="otpauth_add">২এফএ যোগ করো</string>
|
||||
<string name="search_web">ওয়েবে খুঁজো</string>
|
||||
<string name="search_scan">আগেরগুলোতে খুঁজো</string>
|
||||
<string name="add_to_history">ইতিহাসে যোগ করুন</string>
|
||||
<string name="added_to_history">ইতিহাসে যোগ করা হয়েছে</string>
|
||||
<string name="export_to_file">নথিতে রপ্তানি করো</string>
|
||||
<string name="export_as">রপ্তানির ধরণ?</string>
|
||||
<string name="export_csv_comma">কমাসহ সিএসভি(.csv) নথি</string>
|
||||
@@ -173,23 +194,31 @@
|
||||
<string name="export_database">এসকিউলাইট তথ্যভিত্তি</string>
|
||||
<string name="saved_in_downloads">ডাউনলোডে সংরক্ষিত</string>
|
||||
<string name="rotate_image_cw">ছবি ঘড়ির দিকে ঘুরাও</string>
|
||||
<string name="toggle_free_rotation">Toggle free rotation</string>
|
||||
<string name="pick_file">ছবি নাও</string>
|
||||
<string name="toggle_free_rotation">মুক্ত ঘূর্ণন টগল করুন</string>
|
||||
<string name="pick_file">একটি ফাইল চয়ন করুন</string>
|
||||
<string name="pick_image_file">ছবি নাও</string>
|
||||
<string name="pick_code_to_scan">খুঁজার জন্য কোড নাও</string>
|
||||
<string name="shortcut_decode">বারকোড খুজো</string>
|
||||
<string name="shortcut_encode">বারকোড বানাও</string>
|
||||
<string name="shortcut_history">ইতিহাস</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="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="network_suggestions">Network suggestions</string>
|
||||
<string name="remove_suggestion">Remove suggestion</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>
|
||||
<string name="network_suggestions">নেটওয়ার্ক সুপারিশ</string>
|
||||
<string name="remove_suggestion">প্রস্তাব সরান</string>
|
||||
<string name="email_to">প্রাপক</string>
|
||||
<string name="email_subject">বিষয়</string>
|
||||
<string name="email_body">বার্তা</string>
|
||||
<string name="scheme">ব্যবস্থা</string>
|
||||
<string name="host">হোস্ট</string>
|
||||
<string name="query">অনুসন্ধান</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="welcome">Vítejte!</string>
|
||||
<string name="mode_expert">Pokročilý</string>
|
||||
<string name="mode_expert_description">Chci zkontrolovat načtený obsah a otevřít jej ručně.</string>
|
||||
<string name="mode_simple">Jednoduchý</string>
|
||||
<string name="mode_simple_description">Chci jen skenovat čárové kódy a okamžitě je otevírat.</string>
|
||||
<string name="camera_error">Nepodařilo se otevřít fotoaparát, prosím zkuste to znovu</string>
|
||||
<string name="scan_code">Naskenovat kód</string>
|
||||
<string name="compose_barcode">Vytvořit čárový kód</string>
|
||||
@@ -12,10 +18,11 @@
|
||||
<item quantity="other">%2$d znaků, %1$s</item>
|
||||
</plurals>
|
||||
<string name="error_correction_level">Stupeň korekce chyb</string>
|
||||
<string name="error_correction_level_l" formatted="false">Nízký (~7% korekce)</string>
|
||||
<string name="error_correction_level_m" formatted="false">Střední (~15% korekce)</string>
|
||||
<string name="error_correction_level_q" formatted="false">Čtvrtinový (~25% korekce)</string>
|
||||
<string name="error_correction_level_h" formatted="false">Vysoký (~30% korekce)</string>
|
||||
<string name="error_correction_level_l" formatted="false">Nízký (~7% korekce)</string>
|
||||
<string name="error_correction_level_m" formatted="false">Střední (~15% korekce)</string>
|
||||
<string name="error_correction_level_q" formatted="false">Čtvrtinový (~25% korekce)</string>
|
||||
<string name="error_correction_level_h" formatted="false">Vysoký (~30% korekce)</string>
|
||||
<string name="label">Štítek</string>
|
||||
<string name="colors">Popředí a pozadí</string>
|
||||
<string name="colors_black_on_white">Černá na bílé</string>
|
||||
<string name="colors_white_on_black">Bílá na černé</string>
|
||||
@@ -30,20 +37,23 @@
|
||||
<string name="gtin_add_on">rozšíření UPC EAN</string>
|
||||
<string name="barcode_version_number">Verze</string>
|
||||
<string name="qr_version_and_modules">%1$s (%2$d modulů)</string>
|
||||
<string name="qr_data_mask">Maska dat</string>
|
||||
<string name="toggle_flash">Přepnout blesk</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="share_file">Sdílet jako soubor</string>
|
||||
<string name="share_as">Sdílet jako?</string>
|
||||
<string name="copy_to_clipboard">Zkopírovat do schránky</string>
|
||||
<string name="copy_as_deeplink">Kopírovat jako deeplink</string>
|
||||
<string name="copied_to_clipboard">Zkopírováno do schránky</string>
|
||||
<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 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>
|
||||
<string name="width_by_height">%1$d×%2$d</string>
|
||||
<string name="size_width_by_height">Velikost v pixelech: %1$d×%2$d</string>
|
||||
<string name="size_no_magnification">Žádné zvětšení</string>
|
||||
<string name="add_quiet_zone">Přidat tichou zónu</string>
|
||||
<string name="input_content_here">Sem vložte obsah</string>
|
||||
<string name="unescape">Rozšířit escape sekvence</string>
|
||||
<string name="encode">ZAKÓDOVAT</string>
|
||||
@@ -57,6 +67,9 @@
|
||||
<string name="bulk_mode_delay">Prodleva mezi skeny</string>
|
||||
<string name="restrict_format">Omezit na formát</string>
|
||||
<string name="remove_restriction">Zrušit omezení formátu</string>
|
||||
<string name="find_code">Najít kód</string>
|
||||
<string name="find_code_with_content">Co obsah?</string>
|
||||
<string name="find_code_hint">Zadejte termín</string>
|
||||
<string name="scan_format">Naskenovat %s</string>
|
||||
<string name="quarter_second">Čtvrt sekundy</string>
|
||||
<string name="half_second">Půl sekundy</string>
|
||||
@@ -87,15 +100,17 @@
|
||||
<string name="beep_summary">Povolte, pokud chcete pípnutí při rozpoznání kódu.</string>
|
||||
<string name="beep_tone">Tón pípnutí</string>
|
||||
<string name="tone_cdma_confirm">Potvrzení CDMA</string>
|
||||
<string name="tone_sup_confirm">Call supervisory confirm</string>
|
||||
<string name="tone_sup_radio_ack">Call supervisory radio path acknowledgment</string>
|
||||
<string name="tone_sup_confirm">Potvrzení dohledu volání</string>
|
||||
<string name="tone_sup_radio_ack">Hovor na dohledové rádiové cestě potvrzení</string>
|
||||
<string name="tone_prop_ack">Kladné potvrzení příjmu</string>
|
||||
<string name="tone_prop_beep">Obecné pípnutí</string>
|
||||
<string name="tone_prop_beep2">Dvojité obecné pípnutí</string>
|
||||
<string name="use_history">Ukládat historii skenování</string>
|
||||
<string name="use_history_summary">Rozpoznané kódy se uloží do zařízení.</string>
|
||||
<string name="ignore_duplicates">Ignorovat duplicity</string>
|
||||
<string name="ignore_consecutive_duplicates">Ignorovat duplikáty</string>
|
||||
<string name="ignore_consecutive_duplicates_summary">Další skeny stejného kódu se nebudou ukládat.</string>
|
||||
<string name="ignore_any_duplicates">Ignore any duplicates</string>
|
||||
<string name="accept_duplicates">Přijmout duplicity</string>
|
||||
<string name="open_immediately">Otevírat okamžitě</string>
|
||||
<string name="open_immediately_summary">Přeskočí kontrolu a otevře obsah okamžitě. Může otevřít škodlivý obsah.</string>
|
||||
<string name="copy_immediately">Vložit do schránky</string>
|
||||
@@ -104,6 +119,8 @@
|
||||
<string name="show_meta_data_summary">Zobrazí další data o naskenovaném čárovém kódu.</string>
|
||||
<string name="show_hex_dump">Zobrazit hex dump</string>
|
||||
<string name="show_hex_dump_summary">Zobrazí hex dump naskenovaného obsahu.</string>
|
||||
<string name="show_checksum">Zobrazit kontrolní součet</string>
|
||||
<string name="no_checksum">Žádný kontrolní součet</string>
|
||||
<string name="show_recreation">Zobrazit zrekonstruovaný kód</string>
|
||||
<string name="show_recreation_summary">Zrekonstruovat a zobrazit čárový kód z přečteného obrazu, pokud je to možné.</string>
|
||||
<string name="close_automatically">Vrátit se po zkopírování/sdílení</string>
|
||||
@@ -115,14 +132,14 @@
|
||||
<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="clear_network_suggestions_nothing_to_remove">Nic ke smazání</string>
|
||||
<string name="send_category">Předává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_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 požadavku 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_get_query_string">GET s kompletním řetězcem dotazu</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">Otevřít v prohlížeči</string>
|
||||
@@ -134,12 +151,15 @@
|
||||
<string name="bluetooth_connect_fail">Nelze se připojit k zařízení Bluetooth.</string>
|
||||
<string name="bluetooth_send_fail">Nelze odesílat na zařízení Bluetooth.</string>
|
||||
<string name="bluetooth_send_success">Odesílání přes Bluetooth se zdařilo.</string>
|
||||
<string name="generation_category">Generace</string>
|
||||
<string name="brighten_screen">Zvýšit jas obrazovky</string>
|
||||
<string name="brighten_screen_summary">Nastavte jas na maximum pro zlepšení skenování čárového kódu ze obrazovky.</string>
|
||||
<string name="really_remove_scan">Opravdu odstranit sken?</string>
|
||||
<string name="really_remove_all_scans">Opravdu odstranit všechny skeny?</string>
|
||||
<string name="really_remove_selected_scans">Opravdu odstranit zvolené skeny?</string>
|
||||
<string name="clear_history">Vymazat historii</string>
|
||||
<string name="copy_scan">Zkopírovat sken</string>
|
||||
<string name="edit_scan">Upravit popisek</string>
|
||||
<string name="edit_label">Upravit popisek</string>
|
||||
<string name="remove_scan">Odstranit sken</string>
|
||||
<string name="enter_name">Napište název popisující sken</string>
|
||||
<string name="enter_name_hint">Název skenu</string>
|
||||
@@ -147,7 +167,7 @@
|
||||
<string name="pick_list_separator">Jak mají být odděleny položky?</string>
|
||||
<string name="separator_line_break">Nový řádek</string>
|
||||
<string name="separator_ruler">Svislice</string>
|
||||
<string name="save_as_file_name">Uložit jako do složky Stahování</string>
|
||||
<string name="save_as_file_name">Uložit do složky Stahování?</string>
|
||||
<string name="file_name">Název souboru</string>
|
||||
<string name="error_saving_file">Soubor se nepodařilo uložit</string>
|
||||
<string name="error_file_exists">Soubor již existuje</string>
|
||||
@@ -167,8 +187,10 @@
|
||||
<string name="otpauth_add">Přidat 2FA</string>
|
||||
<string name="search_web">Vyhledat na webu</string>
|
||||
<string name="search_scan">Vyhledat sken</string>
|
||||
<string name="add_to_history">Přidat do historie</string>
|
||||
<string name="added_to_history">Přidáno do historie</string>
|
||||
<string name="export_to_file">Exportovat do souboru</string>
|
||||
<string name="export_as">Exportovat jako</string>
|
||||
<string name="export_as">Exportovat jako?</string>
|
||||
<string name="export_csv_comma">CSV oddělené čárkami</string>
|
||||
<string name="export_csv_semicolon">CSV oddělené středníky</string>
|
||||
<string name="export_json">soubor JSON</string>
|
||||
@@ -176,10 +198,12 @@
|
||||
<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="toggle_free_rotation">Přepnout zámek natočení</string>
|
||||
<string name="pick_file">Vybrat obrázek</string>
|
||||
<string name="pick_file">Vybrat soubor</string>
|
||||
<string name="pick_image_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_history">Historie</string>
|
||||
<string name="shortcut_preferences">Nastavení</string>
|
||||
<string name="background_request_failed">Požadavek na pozadí selhal</string>
|
||||
<string name="entry_type">Typ</string>
|
||||
@@ -194,4 +218,10 @@
|
||||
<string name="wifi_phase2">Ověření Phase 2</string>
|
||||
<string name="network_suggestions">Návrhy sítí</string>
|
||||
<string name="remove_suggestion">Odebrat návrh</string>
|
||||
<string name="email_to">Příjemce</string>
|
||||
<string name="email_subject">Předmět</string>
|
||||
<string name="email_body">Tělo zprávy</string>
|
||||
<string name="scheme">Schéma</string>
|
||||
<string name="host">Host</string>
|
||||
<string name="query">Dotaz</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
<resources>
|
||||
<string name="welcome">Velkommen!</string>
|
||||
<string name="mode_expert">Avanceret</string>
|
||||
<string name="mode_expert_description">Jeg vil kontrollere det læste indhold og åbne det manuelt.</string>
|
||||
<string name="mode_simple">Simpel</string>
|
||||
<string name="mode_simple_description">Jeg vil bare scanne stregkoder og få dem åbnet med det samme.</string>
|
||||
<string name="camera_error">Kan ikke få adgang til kamera, prøv igen</string>
|
||||
<string name="scan_code">Scan kode</string>
|
||||
<string name="compose_barcode">Opret stregkode</string>
|
||||
@@ -14,6 +19,7 @@
|
||||
<string name="error_correction_level_m" formatted="false">Medium (~15% korrigering)</string>
|
||||
<string name="error_correction_level_q" formatted="false">Kvartil (~25% korrigering)</string>
|
||||
<string name="error_correction_level_h" formatted="false">Høj (~30% korrigering)</string>
|
||||
<string name="label">Mærke</string>
|
||||
<string name="colors">For- og baggrund</string>
|
||||
<string name="colors_black_on_white">Sort på hvidt</string>
|
||||
<string name="colors_white_on_black">Hvid på sort</string>
|
||||
@@ -28,22 +34,25 @@
|
||||
<string name="gtin_add_on">UPC EAN udvidelse</string>
|
||||
<string name="barcode_version_number">Version</string>
|
||||
<string name="qr_version_and_modules">%1$s (%2$d moduler)</string>
|
||||
<string name="qr_data_mask">Data mask</string>
|
||||
<string name="toggle_flash">Slå blitz til/fra</string>
|
||||
<string name="error_flash">Der opstod en fejl ved at slå blitz til/fra</string>
|
||||
<string name="share">Del</string>
|
||||
<string name="share_file">Del som fil</string>
|
||||
<string name="share_as">Del som?</string>
|
||||
<string name="copy_to_clipboard">Kopier til udklipsholder</string>
|
||||
<string name="copy_as_deeplink">Kopiér som deeplink</string>
|
||||
<string name="copied_to_clipboard">Kopieret til udklipsholder</string>
|
||||
<string name="copy_password">Kopier adgangskode til udklipsholderen</string>
|
||||
<string name="copied_password_to_clipboard">Adgangskode kopieret til udklipsholderen</string>
|
||||
<string name="open_url">Åbn URL</string>
|
||||
<string name="cannot_resolve_action">Inge applikation kan åbne det</string>
|
||||
<string name="pick_search_engine">Vælg en søgemaskine</string>
|
||||
<string name="format">Format</string>
|
||||
<string name="size">Størrelse i pixels</string>
|
||||
<string name="width_by_height">%1$d×%2$d</string>
|
||||
<string name="size_width_by_height">Størrelse i pixels: %1$d×%2$d</string>
|
||||
<string name="size_no_magnification">Ingen forstørrelse</string>
|
||||
<string name="add_quiet_zone">Tilføj stille zone</string>
|
||||
<string name="input_content_here">Indtast indhold her</string>
|
||||
<string name="unescape">Expand escape sequences</string>
|
||||
<string name="unescape">Udvid escape-sekvenser</string>
|
||||
<string name="encode">INDKOD</string>
|
||||
<string name="error_no_content">Manglende indhold</string>
|
||||
<string name="error_encoding_barcode">Stregkode kan ikke genereres</string>
|
||||
@@ -55,6 +64,9 @@
|
||||
<string name="bulk_mode_delay">Forsinkelse mellem kontinuerlige scanninger</string>
|
||||
<string name="restrict_format">Begræns format</string>
|
||||
<string name="remove_restriction">Fjern begrænsningen</string>
|
||||
<string name="find_code">Find kode</string>
|
||||
<string name="find_code_with_content">Hvad indhold?</string>
|
||||
<string name="find_code_hint">Indtast en term</string>
|
||||
<string name="scan_format">Scan %s</string>
|
||||
<string name="quarter_second">Et kvart sekund</string>
|
||||
<string name="half_second">Et halvt sekund</string>
|
||||
@@ -85,15 +97,17 @@
|
||||
<string name="beep_summary">Aktiver dette, hvis du vil have din enhed til at bippe, når en kode genkendes.</string>
|
||||
<string name="beep_tone">Bip tone</string>
|
||||
<string name="tone_cdma_confirm">CDMA bekræftelse</string>
|
||||
<string name="tone_sup_confirm">Call supervisory confirm</string>
|
||||
<string name="tone_sup_radio_ack">Call supervisory radio path acknowledgment</string>
|
||||
<string name="tone_sup_confirm">Opkald tilsyn bekræft</string>
|
||||
<string name="tone_sup_radio_ack">Opkald tilsynsradio sti anerkendelse</string>
|
||||
<string name="tone_prop_ack">Positiv anerkendelse</string>
|
||||
<string name="tone_prop_beep">Generelt bip</string>
|
||||
<string name="tone_prop_beep2">Generelt dobbelt bip</string>
|
||||
<string name="use_history">Gem scanningshistorik</string>
|
||||
<string name="use_history_summary">Gem genkendte koder på din enhed.</string>
|
||||
<string name="ignore_duplicates">Ignorer duplikater</string>
|
||||
<string name="ignore_consecutive_duplicates">Ignorer dubletter</string>
|
||||
<string name="ignore_consecutive_duplicates_summary">Gem ikke fortløbende dubletter i scanningshistorikken.</string>
|
||||
<string name="ignore_any_duplicates">Ignore any duplicates</string>
|
||||
<string name="accept_duplicates">Accept duplicates</string>
|
||||
<string name="open_immediately">Åbn straks</string>
|
||||
<string name="open_immediately_summary">Spring inspektion over og åbn indholdet med det samme. Kan åbne skadeligt indhold, hvis det er aktiveret.</string>
|
||||
<string name="copy_immediately">Læg i udklipsholderen</string>
|
||||
@@ -102,6 +116,8 @@
|
||||
<string name="show_meta_data_summary">Vis yderligere data om den scannede stregkode.</string>
|
||||
<string name="show_hex_dump">Vis hex-dump</string>
|
||||
<string name="show_hex_dump_summary">Vis et hex-dump af det scannede indhold.</string>
|
||||
<string name="show_checksum">Vis kontrolsum</string>
|
||||
<string name="no_checksum">Ingen kontrolsum</string>
|
||||
<string name="show_recreation">Vis genskabt stregkode</string>
|
||||
<string name="show_recreation_summary">Genskab og vis stregkode fra læst indhold, hvis det er muligt.</string>
|
||||
<string name="close_automatically">Gå tilbage efter Kopier/Del</string>
|
||||
@@ -125,19 +141,22 @@
|
||||
<string name="send_type_post_json">POST application/json</string>
|
||||
<string name="send_type_external_browser">Åbn i ekstern browser</string>
|
||||
<string name="test_url">Test URL</string>
|
||||
<string name="send_scan_bluetooth">Forward scans with Bluetooth</string>
|
||||
<string name="send_scan_bluetooth">Send scanninger med Bluetooth</string>
|
||||
<string name="send_scan_bluetooth_summary">Enable to forwards scans to a given Bluetooth host.</string>
|
||||
<string name="send_scan_bluetooth_host">Host</string>
|
||||
<string name="no_bluetooth_hosts_paired">No Hosts Paired</string>
|
||||
<string name="send_scan_bluetooth_host">Vært</string>
|
||||
<string name="no_bluetooth_hosts_paired">Ingen værter parret</string>
|
||||
<string name="bluetooth_connect_fail">Could not connect to bluetooth device.</string>
|
||||
<string name="bluetooth_send_fail">Could not send to bluetooth device.</string>
|
||||
<string name="bluetooth_send_success">Successful bluetooth send.</string>
|
||||
<string name="generation_category">Generation</string>
|
||||
<string name="brighten_screen">Skærm lysere</string>
|
||||
<string name="brighten_screen_summary">Indstil lysstyrken til maksimum for at forbedre scanning af en stregkode fra skærmen.</string>
|
||||
<string name="really_remove_scan">Vil du virkelig fjerne scanningen?</string>
|
||||
<string name="really_remove_all_scans">Vil du virkelig fjerne alle scanninger?</string>
|
||||
<string name="really_remove_selected_scans">Vil du virkelig fjerne valgte scanninger?</string>
|
||||
<string name="clear_history">Ryd historik</string>
|
||||
<string name="copy_scan">Kopier scanning</string>
|
||||
<string name="edit_scan">Rediger etiket</string>
|
||||
<string name="edit_label">Rediger etiket</string>
|
||||
<string name="remove_scan">Fjern scanning</string>
|
||||
<string name="enter_name">Indtast et navn for at beskrive scanningen</string>
|
||||
<string name="enter_name_hint">Navn på scanningen</string>
|
||||
@@ -165,19 +184,23 @@
|
||||
<string name="otpauth_add">Tilføj 2FA</string>
|
||||
<string name="search_web">Søg på nettet</string>
|
||||
<string name="search_scan">Søg scanning</string>
|
||||
<string name="add_to_history">Tilføj til historik</string>
|
||||
<string name="added_to_history">Tilføjet til historik</string>
|
||||
<string name="export_to_file">Eksporter til fil</string>
|
||||
<string name="export_as">Eksporter som?</string>
|
||||
<string name="export_csv_comma">CSV-fil med kommaer</string>
|
||||
<string name="export_csv_semicolon">CSV-fil med semikoloner</string>
|
||||
<string name="export_json">JSON fil</string>
|
||||
<string name="export_database">SQLite database</string>
|
||||
<string name="export_database">SQLite-database</string>
|
||||
<string name="saved_in_downloads">Gemt i downloads</string>
|
||||
<string name="rotate_image_cw">Roter billedet med uret</string>
|
||||
<string name="toggle_free_rotation">Slå fri rotation til/fra</string>
|
||||
<string name="pick_file">Vælg billedfil</string>
|
||||
<string name="pick_file">Vælg en fil</string>
|
||||
<string name="pick_image_file">Vælg billedfil</string>
|
||||
<string name="pick_code_to_scan">Vælg kode til at scanne</string>
|
||||
<string name="shortcut_decode">Scan en stregkode</string>
|
||||
<string name="shortcut_encode">Opret en stregkode</string>
|
||||
<string name="shortcut_history">Historik</string>
|
||||
<string name="shortcut_preferences">Indstillinger</string>
|
||||
<string name="background_request_failed">Baggrundsanmodning mislykkedes</string>
|
||||
<string name="entry_type">Type</string>
|
||||
@@ -192,4 +215,10 @@
|
||||
<string name="wifi_phase2">Fase 2 metode</string>
|
||||
<string name="network_suggestions">Netværksforslag</string>
|
||||
<string name="remove_suggestion">Fjern forslag</string>
|
||||
<string name="email_to">Modtager</string>
|
||||
<string name="email_subject">Emne</string>
|
||||
<string name="email_body">Brødtekst</string>
|
||||
<string name="scheme">Ordning</string>
|
||||
<string name="host">Vært</string>
|
||||
<string name="query">Forespørgsel</string>
|
||||
</resources>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<string-array name="search_engines_names">
|
||||
<item>@string/always_ask</item>
|
||||
<item>Google</item>
|
||||
<item>Brave Search</item>
|
||||
<item>DuckDuckGo</item>
|
||||
<item>Qwant</item>
|
||||
<item>Bing</item>
|
||||
@@ -16,6 +17,7 @@
|
||||
<string-array name="search_engines_values">
|
||||
<item></item>
|
||||
<item>https://www.google.de/search?q=</item>
|
||||
<item>https://search.brave.com/search?q=</item>
|
||||
<item>https://duckduckgo.com/?q=</item>
|
||||
<item>https://www.qwant.com/?q=</item>
|
||||
<item>https://bing.com/?q=</item>
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
<resources>
|
||||
<string name="welcome">Willkommen!</string>
|
||||
<string name="mode_expert">Erweitert</string>
|
||||
<string name="mode_expert_description">Ich möchte den gelesenen Inhalt überprüfen und manuell öffnen.</string>
|
||||
<string name="mode_simple">Einfach</string>
|
||||
<string name="mode_simple_description">Ich möchte nur Barcodes scannen und sie sofort öffnen.</string>
|
||||
<string name="camera_error">Auf die Kamera kann nicht zugegriffen werden, bitte versuche es nochmal</string>
|
||||
<string name="scan_code">Code scannen</string>
|
||||
<string name="compose_barcode">Barcode erstellen</string>
|
||||
@@ -14,34 +19,38 @@
|
||||
<string name="error_correction_level_m" formatted="false">Medium (~15% wiederherstellbar)</string>
|
||||
<string name="error_correction_level_q" formatted="false">Quartile (~25% wiederherstellbar)</string>
|
||||
<string name="error_correction_level_h" formatted="false">High (~30% wiederherstellbar)</string>
|
||||
<string name="label">Bezeichnung</string>
|
||||
<string name="colors">Vor und Hintergrund</string>
|
||||
<string name="colors_black_on_white">Schwarz auf Weiß</string>
|
||||
<string name="colors_white_on_black">Weiß auf Schwarz</string>
|
||||
<string name="colors_black_on_transparent">Schwarz auf Transparent</string>
|
||||
<string name="colors_white_on_transparent">Weiß auf Transparent</string>
|
||||
<string name="sequence_size">Sequence size</string>
|
||||
<string name="sequence_index">Sequence index</string>
|
||||
<string name="sequence_id">Sequence ID</string>
|
||||
<string name="sequence_size">Sequenzgröße</string>
|
||||
<string name="sequence_index">Sequenzindex</string>
|
||||
<string name="sequence_id">Sequenz-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$s (%2$d Module)</string>
|
||||
<string name="qr_data_mask">Maske</string>
|
||||
<string name="toggle_flash">Licht an/ausschalten</string>
|
||||
<string name="error_flash">Fehler beim ein/ausschalten des Lichts</string>
|
||||
<string name="share">Teilen</string>
|
||||
<string name="share_file">Als Datei teilen</string>
|
||||
<string name="share_as">Teilen als?</string>
|
||||
<string name="copy_to_clipboard">In die Zwischenablage kopieren</string>
|
||||
<string name="copy_as_deeplink">Als Deeplink kopieren</string>
|
||||
<string name="copied_to_clipboard">Inhalt wurde in die Zwischenablage kopiert</string>
|
||||
<string name="copy_password">Passwort in die Zwischenablage kopieren</string>
|
||||
<string name="copied_password_to_clipboard">Passwort wurde in die Zwischenablage kopiert</string>
|
||||
<string name="open_url">URL öffnen</string>
|
||||
<string name="cannot_resolve_action">Keine App kann das öffnen</string>
|
||||
<string name="pick_search_engine">Suchmaschine auswählen</string>
|
||||
<string name="format">Format</string>
|
||||
<string name="size">Größe in Pixel</string>
|
||||
<string name="width_by_height">%1$d×%2$d</string>
|
||||
<string name="size_width_by_height">Größe in Pixel: %1$d×%2$d</string>
|
||||
<string name="size_no_magnification">Keine Vergrößerung</string>
|
||||
<string name="add_quiet_zone">Ruhezone hinzufügen</string>
|
||||
<string name="input_content_here">Gib den Inhalt hier ein</string>
|
||||
<string name="unescape">Escape-Sequenzen interpretieren</string>
|
||||
<string name="encode">KODIEREN</string>
|
||||
@@ -55,6 +64,9 @@
|
||||
<string name="bulk_mode_delay">Pause zwischen fortlaufenden Scans</string>
|
||||
<string name="restrict_format">Format eingrenzen</string>
|
||||
<string name="remove_restriction">Einschränkung entfernen</string>
|
||||
<string name="find_code">Barcode finden</string>
|
||||
<string name="find_code_with_content">Mit welchem Inhalt?</string>
|
||||
<string name="find_code_hint">Begriff eingeben</string>
|
||||
<string name="scan_format">%s scannen</string>
|
||||
<string name="quarter_second">Eine Viertelsekunde</string>
|
||||
<string name="half_second">Eine halbe Sekunde</string>
|
||||
@@ -84,16 +96,18 @@
|
||||
<string name="beep">Signalton bei Erkennung</string>
|
||||
<string name="beep_summary">Bei Erkennung eines Codes soll ein Signalton abgespielt werden.</string>
|
||||
<string name="beep_tone">Signalton</string>
|
||||
<string name="tone_cdma_confirm">CDMA confirm</string>
|
||||
<string name="tone_sup_confirm">Call supervisory confirm</string>
|
||||
<string name="tone_sup_radio_ack">Call supervisory radio path acknowledgment</string>
|
||||
<string name="tone_prop_ack">Positive acknowledgment</string>
|
||||
<string name="tone_prop_beep">General beep</string>
|
||||
<string name="tone_prop_beep2">General double beep</string>
|
||||
<string name="tone_cdma_confirm">CDMA bestätigen</string>
|
||||
<string name="tone_sup_confirm">Anrufaufsicht bestätigen</string>
|
||||
<string name="tone_sup_radio_ack">Anrufüberwachungsfunkpfadbestätigung</string>
|
||||
<string name="tone_prop_ack">Positive Bestätigung</string>
|
||||
<string name="tone_prop_beep">Allgemeines Piepen</string>
|
||||
<string name="tone_prop_beep2">Allgemeiner Doppelton</string>
|
||||
<string name="use_history">Codes speichern</string>
|
||||
<string name="use_history_summary">Eingelesene Barcodes auf dem Gerät speichern.</string>
|
||||
<string name="ignore_consecutive_duplicates">Duplikate ignorieren</string>
|
||||
<string name="ignore_consecutive_duplicates_summary">Aufeinanderfolgende Duplikate werden nicht in der Scan-Historie gespeichert.</string>
|
||||
<string name="ignore_duplicates">Ignoriere Duplikate</string>
|
||||
<string name="ignore_consecutive_duplicates">Aufeinanderfolgende Duplikate</string>
|
||||
<string name="ignore_any_duplicates">Jedes Duplicat</string>
|
||||
<string name="accept_duplicates">Akzeptiere Duplikate</string>
|
||||
<string name="open_immediately">Inhalte sofort öffnen</string>
|
||||
<string name="open_immediately_summary">Inspektion überspringen und Inhalt sofort öffnen. Kann schädliche Inhalte öffnen, wenn aktiviert.</string>
|
||||
<string name="copy_immediately">Automatisch in Zwischenablage</string>
|
||||
@@ -102,6 +116,8 @@
|
||||
<string name="show_meta_data_summary">Zusätzliche Daten über den eingelesenen Barcode anzeigen.</string>
|
||||
<string name="show_hex_dump">Hex Dump anzeigen</string>
|
||||
<string name="show_hex_dump_summary">Zeigt einen Hexdump des eingelesenen Inhalts an.</string>
|
||||
<string name="show_checksum">Prüfsumme anzeigen</string>
|
||||
<string name="no_checksum">Keine Prüfsumme anzeigen</string>
|
||||
<string name="show_recreation">Wiederhergestellten Barcode anzeigen</string>
|
||||
<string name="show_recreation_summary">Barcode aus dem gelesenen Inhalt wiederherstellen und anzeigen, wenn möglich.</string>
|
||||
<string name="close_automatically">Automatisch zurückkehren</string>
|
||||
@@ -132,12 +148,15 @@
|
||||
<string name="bluetooth_connect_fail">Es kann leider keine Verbindung zum Bluetooth-Gerät hergestellt werden</string>
|
||||
<string name="bluetooth_send_fail">Senden zum Bluetooth-Gerät fehlgeschlagen</string>
|
||||
<string name="bluetooth_send_success">Erfolgreich zum Bluetooth-Gerät gesendet</string>
|
||||
<string name="generation_category">Generation</string>
|
||||
<string name="brighten_screen">Bildschirm aufhellen</string>
|
||||
<string name="brighten_screen_summary">Helligkeit auf Maximum stellen um das Scannen eines Barcodes vom Bildschirm zu verbessern.</string>
|
||||
<string name="really_remove_scan">Code wirklich entfernen?</string>
|
||||
<string name="really_remove_all_scans">Alle Codes entfernen?</string>
|
||||
<string name="really_remove_selected_scans">Alle ausgewählten Codes entfernen?</string>
|
||||
<string name="clear_history">Gespeicherte Codes löschen</string>
|
||||
<string name="copy_scan">Code kopieren</string>
|
||||
<string name="edit_scan">Bezeichnung ändern</string>
|
||||
<string name="edit_label">Bezeichnung ändern</string>
|
||||
<string name="remove_scan">Code löschen</string>
|
||||
<string name="enter_name">Geben Sie eine Bezeichnung für den Code ein</string>
|
||||
<string name="enter_name_hint">Bezeichung des Codes</string>
|
||||
@@ -165,6 +184,8 @@
|
||||
<string name="otpauth_add">2FA hinzufügen</string>
|
||||
<string name="search_web">Suche im Internet</string>
|
||||
<string name="search_scan">Code suchen</string>
|
||||
<string name="add_to_history">Zu gespeicherten Codes hinzufügen</string>
|
||||
<string name="added_to_history">Zu den gespeicherten Codes hinzugefügt</string>
|
||||
<string name="export_to_file">In Datei exportieren</string>
|
||||
<string name="export_as">Exportieren als?</string>
|
||||
<string name="export_csv_comma">CSV Datei mit Kommas</string>
|
||||
@@ -174,10 +195,12 @@
|
||||
<string name="saved_in_downloads">In den Downloads gespeichert</string>
|
||||
<string name="rotate_image_cw">Bild im Uhrzeigersinn rotieren</string>
|
||||
<string name="toggle_free_rotation">Freie Rotation ein/ausschalten</string>
|
||||
<string name="pick_file">Bilddatei auswählen</string>
|
||||
<string name="pick_file">Datei auswählen</string>
|
||||
<string name="pick_image_file">Bilddatei auswählen</string>
|
||||
<string name="pick_code_to_scan">Code im Bild auswählen</string>
|
||||
<string name="shortcut_decode">Barcode lesen</string>
|
||||
<string name="shortcut_encode">Barcode erstellen</string>
|
||||
<string name="shortcut_history">Verlauf</string>
|
||||
<string name="shortcut_preferences">Einstellungen</string>
|
||||
<string name="background_request_failed">Hintergrundanfrage fehlgeschlagen</string>
|
||||
<string name="entry_type">Typ</string>
|
||||
@@ -192,4 +215,10 @@
|
||||
<string name="wifi_phase2">Phase 2 Methode</string>
|
||||
<string name="network_suggestions">WiFi Netzwerkvorschläge</string>
|
||||
<string name="remove_suggestion">Vorschlag entfernen</string>
|
||||
<string name="email_to">Empfänger</string>
|
||||
<string name="email_subject">Betreff</string>
|
||||
<string name="email_body">Nachricht</string>
|
||||
<string name="scheme">Schema</string>
|
||||
<string name="host">Host</string>
|
||||
<string name="query">Query</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
<resources>
|
||||
<string name="welcome">¡Bienvenido!</string>
|
||||
<string name="mode_expert">Avanzado</string>
|
||||
<string name="mode_expert_description">Quiero verificar el contenido leído y abrirlo manualmente.</string>
|
||||
<string name="mode_simple">Sencillo</string>
|
||||
<string name="mode_simple_description">Solo quiero escanear códigos de barras y abrirlos de inmediato.</string>
|
||||
<string name="camera_error">No se puede acceder a la cámara, por favor, inténtelo de nuevo</string>
|
||||
<string name="scan_code">Escanear código de barras</string>
|
||||
<string name="compose_barcode">Componer código de barras</string>
|
||||
<string name="scan_code">Escanear código</string>
|
||||
<string name="compose_barcode">Componer código</string>
|
||||
<string name="decode_barcode">Decodificar código de barras</string>
|
||||
<string name="content">Contenido</string>
|
||||
<string name="binary_data">(datos binarios)</string>
|
||||
@@ -14,47 +19,54 @@
|
||||
<string name="error_correction_level_m" formatted="false">Medio (~15% de correción)</string>
|
||||
<string name="error_correction_level_q" formatted="false">Cuartil (~25% de correción)</string>
|
||||
<string name="error_correction_level_h" formatted="false">Alto (~30% de correción)</string>
|
||||
<string name="colors">Fore and background</string>
|
||||
<string name="colors_black_on_white">Black on white</string>
|
||||
<string name="colors_white_on_black">White on black</string>
|
||||
<string name="colors_black_on_transparent">Black on transparent</string>
|
||||
<string name="colors_white_on_transparent">White on transparent</string>
|
||||
<string name="sequence_size">Sequence size</string>
|
||||
<string name="sequence_index">Sequence index</string>
|
||||
<string name="sequence_id">Sequence ID</string>
|
||||
<string name="label">Etiqueta</string>
|
||||
<string name="colors">Color frontal y fondo</string>
|
||||
<string name="colors_black_on_white">Negro sobre blanco</string>
|
||||
<string name="colors_white_on_black">Blanco sobre negro</string>
|
||||
<string name="colors_black_on_transparent">Negro sobre transparente</string>
|
||||
<string name="colors_white_on_transparent">Blanco sobre transparente</string>
|
||||
<string name="sequence_size">Tamaño secuencia</string>
|
||||
<string name="sequence_index">Índice secuencia</string>
|
||||
<string name="sequence_id">ID secuencia</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$s (%2$d modules)</string>
|
||||
<string name="barcode_version_number">Versión</string>
|
||||
<string name="qr_version_and_modules">%1$s (%2$d modulos)</string>
|
||||
<string name="qr_data_mask">Máscara de datos</string>
|
||||
<string name="toggle_flash">Activar el flash</string>
|
||||
<string name="error_flash">Error toggling flash</string>
|
||||
<string name="error_flash">Error activando flash</string>
|
||||
<string name="share">Compartir</string>
|
||||
<string name="share_file">Compartir como archivo</string>
|
||||
<string name="share_as">¿Compartir como?</string>
|
||||
<string name="copy_to_clipboard">Copiar al portapapeles</string>
|
||||
<string name="copy_as_deeplink">Copiar como deeplink</string>
|
||||
<string name="copied_to_clipboard">Copiado al portapapeles</string>
|
||||
<string name="copy_password">Copiar contraseña al portapapeles</string>
|
||||
<string name="copied_password_to_clipboard">Contraseña copiada al portapapeles</string>
|
||||
<string name="open_url">Abrir URL</string>
|
||||
<string name="cannot_resolve_action">Ninguna aplicación puede abrir eso</string>
|
||||
<string name="pick_search_engine">Elija un motor de búsqueda</string>
|
||||
<string name="pick_search_engine">Seleccionar motor de búsqueda</string>
|
||||
<string name="format">Formato</string>
|
||||
<string name="size">Tamaño en píxeles</string>
|
||||
<string name="width_by_height">%1$d×%2$d</string>
|
||||
<string name="size_width_by_height">Tamaño en píxeles: %1$d×%2$d</string>
|
||||
<string name="size_no_magnification">Sin aumento</string>
|
||||
<string name="add_quiet_zone">Agregar zona tranquila</string>
|
||||
<string name="input_content_here">Introduzca el contenido aquí</string>
|
||||
<string name="unescape">Expand escape sequences</string>
|
||||
<string name="unescape">Expandir secuencia de escape</string>
|
||||
<string name="encode">CODIFICAR</string>
|
||||
<string name="error_no_content">Contenido no disponible</string>
|
||||
<string name="error_encoding_barcode">No se pudo generar el código de barras</string>
|
||||
<string name="view_barcode">Ver código de barras</string>
|
||||
<string name="info">Información</string>
|
||||
<string name="switch_camera">Cambiar cámara</string>
|
||||
<string name="bulk_mode">Escanear constantemente</string>
|
||||
<string name="bulk_mode_summary">Siempre iniciar escaneando constantemente</string>
|
||||
<string name="bulk_mode">Escaneo continuo</string>
|
||||
<string name="bulk_mode_summary">Iniciar siempre en modo de escaneo continuo</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="find_code">Encontrar código</string>
|
||||
<string name="find_code_with_content">¿Qué contenido?</string>
|
||||
<string name="find_code_hint">Introduzca un término</string>
|
||||
<string name="scan_format">Escanear %s</string>
|
||||
<string name="quarter_second">Un cuarto de segundo</string>
|
||||
<string name="half_second">Medio segundo</string>
|
||||
@@ -68,55 +80,59 @@
|
||||
<string name="locale_category">Idioma</string>
|
||||
<string name="custom_locale">Idioma personalizado</string>
|
||||
<string name="follow_system_settings">Seguir los ajustes del sistema</string>
|
||||
<string name="show_crop_handle">Mostrar el delimitador de recorte</string>
|
||||
<string name="show_crop_handle_summary">Restringir el escaneo a una región personalizable.</string>
|
||||
<string name="show_crop_handle">Mostrar delimitador de recorte</string>
|
||||
<string name="show_crop_handle_summary">Permite personalizar la región a escanear.</string>
|
||||
<string name="barcode_formats">Formatos de códigos de barras</string>
|
||||
<string name="zoom_by_swiping">Deslice hacia arriba/abajo para ampliar</string>
|
||||
<string name="zoom_by_swiping_summary">Deslice su dedo hacia arriba o abajo en la pantalla para controlar el aumento de la cámara.</string>
|
||||
<string name="auto_rotate">Reconocer los códigos de barras 1D verticalmente</string>
|
||||
<string name="auto_rotate_summary">Rotar automáticamente el marco de la cámara para reconocer los códigos de barras 1D verticales. Dependiendo del dispositivo, esto puede afectar al rendimiento.</string>
|
||||
<string name="zoom_by_swiping">Ampliar deslizando arriba/abajo</string>
|
||||
<string name="zoom_by_swiping_summary">Deslice el dedo hacia arriba o abajo sobre la pantalla para controlar el zoom de la cámara.</string>
|
||||
<string name="auto_rotate">Reconocer códigos 1D verticales</string>
|
||||
<string name="auto_rotate_summary">Rota automáticamente el marco de la cámara para reconocer los códigos de barras 1D verticales. Dependiendo del dispositivo, podría afectar al rendimiento.</string>
|
||||
<string name="try_harder">Optimizar la precisión del lector</string>
|
||||
<string name="try_harder_summary">Active esta opción para códigos muy difíciles de leer. Esto afectará al rendimiento.</string>
|
||||
<string name="show_toast_in_bulk_mode">Mostrar datos en el modo constante</string>
|
||||
<string name="show_toast_in_bulk_mode_summary">Mostrar brevemente los datos escaneados al escanear constantemente. </string>
|
||||
<string name="vibrate">Vibrar al detectar</string>
|
||||
<string name="vibrate_summary">Active esta opción si desea que su dispositivo vibre cuando se reconozca un código.</string>
|
||||
<string name="beep">Beep on detection</string>
|
||||
<string name="beep_summary">Enable this if you want your device to beep when a code is recognized.</string>
|
||||
<string name="beep_tone">Beep tone</string>
|
||||
<string name="tone_cdma_confirm">CDMA confirm</string>
|
||||
<string name="tone_sup_confirm">Call supervisory confirm</string>
|
||||
<string name="tone_sup_radio_ack">Call supervisory radio path acknowledgment</string>
|
||||
<string name="tone_prop_ack">Positive acknowledgment</string>
|
||||
<string name="tone_prop_beep">General beep</string>
|
||||
<string name="tone_prop_beep2">General double beep</string>
|
||||
<string name="vibrate">Vibración al detectar</string>
|
||||
<string name="vibrate_summary">El dispositivo vibra cuando se reconoce un código.</string>
|
||||
<string name="beep">Sonido al detectar</string>
|
||||
<string name="beep_summary">El dispositivo emite un sonido cuando se reconoce un código.</string>
|
||||
<string name="beep_tone">Sonido</string>
|
||||
<string name="tone_cdma_confirm">Confirmar CDMA</string>
|
||||
<string name="tone_sup_confirm">Confirmar llamada de supervisión</string>
|
||||
<string name="tone_sup_radio_ack">Reconocer ruta de radio de llamada de supervisión</string>
|
||||
<string name="tone_prop_ack">Reconocimiento positivo</string>
|
||||
<string name="tone_prop_beep">Sonido general</string>
|
||||
<string name="tone_prop_beep2">Sonido doble general</string>
|
||||
<string name="use_history">Guardar historial de escaneo</string>
|
||||
<string name="use_history_summary">Guardar códigos reconocidos en su dispositivo.</string>
|
||||
<string name="use_history_summary">Guarda los códigos reconocidos en su dispositivo.</string>
|
||||
<string name="ignore_duplicates">Ignorar duplicados</string>
|
||||
<string name="ignore_consecutive_duplicates">Ignorar duplicados</string>
|
||||
<string name="ignore_consecutive_duplicates_summary">No guarde duplicados consecutivos en el historial de escaneo.</string>
|
||||
<string name="ignore_any_duplicates">Ignore any duplicates</string>
|
||||
<string name="accept_duplicates">Aceptar duplicados</string>
|
||||
<string name="open_immediately">Abrir inmediatamente</string>
|
||||
<string name="open_immediately_summary">Omite la inspección y abre el contenido inmediatamente. Puede abrir contenido dañino si está activado.</string>
|
||||
<string name="copy_immediately">Poner en el portapapeles</string>
|
||||
<string name="copy_immediately_summary">Copiar contenido al portapapeles automáticamente. Ten en cuenta que otras aplicaciones pueden husmear en tu portapapeles en segundo plano.</string>
|
||||
<string name="open_immediately_summary">Omite la inspección y abre el contenido inmediatamente. Podría abrir contenido dañino si está activado.</string>
|
||||
<string name="copy_immediately">Copiar en el portapapeles</string>
|
||||
<string name="copy_immediately_summary">Copia automáticamente el contenido al portapapeles. Ten en cuenta que otras aplicaciones pueden husmear en tu portapapeles en segundo plano.</string>
|
||||
<string name="show_meta_data">Mostrar metadatos</string>
|
||||
<string name="show_meta_data_summary">Mostrar datos adicionales del código escaneado.</string>
|
||||
<string name="show_hex_dump">Mostrar dump hexadecimal</string>
|
||||
<string name="show_hex_dump_summary">Mostrar un dump hexadecimal del contenido escaneado.</string>
|
||||
<string name="show_recreation">Show recreated barcode</string>
|
||||
<string name="show_recreation_summary">Recreate and show barcode from read content if possible.</string>
|
||||
<string name="close_automatically">Retroceder luego de Copiar/Compartir</string>
|
||||
<string name="close_automatically_summary">Regresar automáticamente a la pantalla de escaneo luego de copiar o compartir el contenido leído.</string>
|
||||
<string name="show_meta_data_summary">Muestra datos adicionales del código escaneado.</string>
|
||||
<string name="show_hex_dump">Mostrar volcado hexadecimal</string>
|
||||
<string name="show_hex_dump_summary">Muestra un volcado hexadecimal del contenido escaneado.</string>
|
||||
<string name="show_checksum">Mostrar suma de comprobación</string>
|
||||
<string name="no_checksum">Sin suma de comprobación</string>
|
||||
<string name="show_recreation">Mostrar reconstrucción del código</string>
|
||||
<string name="show_recreation_summary">Reconstruye y muestra el código de barras desde el contenido leído si es posible.</string>
|
||||
<string name="close_automatically">Retroceder tras Copiar/Compartir</string>
|
||||
<string name="close_automatically_summary">Retrocede automáticamente a la pantalla de escaneo después de copiar o compartir el contenido leído.</string>
|
||||
<string name="default_search_engine">Abrir datos desconocidos en</string>
|
||||
<string name="always_ask">Preguntar siempre</string>
|
||||
<string name="open_with_url">Abrir datos desconocidos con la URL</string>
|
||||
<string name="clear_network_suggestions">Borrar las sugerencias de red</string>
|
||||
<string name="clear_network_suggestions_summary">Elimina todas las sugerencias de red previamente proporcionadas por esta aplicación.</string>
|
||||
<string name="open_with_url">Abrir datos desconocidos en la URL</string>
|
||||
<string name="clear_network_suggestions">Eliminar sugerencias de red</string>
|
||||
<string name="clear_network_suggestions_summary">Elimina todas las sugerencias de red proporcionadas por esta aplicación.</string>
|
||||
<string name="really_remove_all_networks">¿Realmente eliminar todas las redes?</string>
|
||||
<string name="clear_network_suggestions_success">Sugerencias de red eliminadas</string>
|
||||
<string name="clear_network_suggestions_nothing_to_remove">Nada que eliminar</string>
|
||||
<string name="send_category">Reenvío</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">Enviar escaneos</string>
|
||||
<string name="send_scan_active_summary">Habilita el envío de escaneos a una URL</string>
|
||||
<string name="send_scan_url">Enviar todo escaneo a la URL</string>
|
||||
<string name="send_scan_type">Tipo de solicitud para cada escaneo</string>
|
||||
<string name="send_type_get_add_content">GET y solo añadir contenido</string>
|
||||
@@ -125,20 +141,23 @@
|
||||
<string name="send_type_post_json">POST application/json</string>
|
||||
<string name="send_type_external_browser">Abrir en el navegador</string>
|
||||
<string name="test_url">URL de prueba</string>
|
||||
<string name="send_scan_bluetooth">Forward scans with Bluetooth</string>
|
||||
<string name="send_scan_bluetooth_summary">Enable to forwards scans to a given Bluetooth host.</string>
|
||||
<string name="send_scan_bluetooth_host">Host</string>
|
||||
<string name="no_bluetooth_hosts_paired">No Hosts Paired</string>
|
||||
<string name="bluetooth_connect_fail">Could not connect to bluetooth device.</string>
|
||||
<string name="bluetooth_send_fail">Could not send to bluetooth device.</string>
|
||||
<string name="bluetooth_send_success">Successful bluetooth send.</string>
|
||||
<string name="really_remove_scan">¿Realmente desea borrar el escaneo seleccionado?</string>
|
||||
<string name="really_remove_all_scans">¿Realmente desea borrar todo lo escaneado?</string>
|
||||
<string name="really_remove_selected_scans">¿Realmente desea borrar los escaneos seleccionados?</string>
|
||||
<string name="clear_history">Borrar el historial</string>
|
||||
<string name="send_scan_bluetooth">Enviar escaneos con Bluetooth</string>
|
||||
<string name="send_scan_bluetooth_summary">Habilita el envío de escaneos a un host Bluetooth.</string>
|
||||
<string name="send_scan_bluetooth_host">Anfitrión</string>
|
||||
<string name="no_bluetooth_hosts_paired">Ningún host emparejado</string>
|
||||
<string name="bluetooth_connect_fail">No se pudo conectar a un dispositivo bluetooth.</string>
|
||||
<string name="bluetooth_send_fail">No se pudo enviar a un dispositivo bluetooth.</string>
|
||||
<string name="bluetooth_send_success">Envío bluetooth completado.</string>
|
||||
<string name="generation_category">Generación</string>
|
||||
<string name="brighten_screen">Aumentar el brillo de la pantalla</string>
|
||||
<string name="brighten_screen_summary">Suba el brillo al máximo para mejorar la lectura del código de barras desde la pantalla.</string>
|
||||
<string name="really_remove_scan">¿Eliminar el escaneo seleccionado?</string>
|
||||
<string name="really_remove_all_scans">¿Eliminar todos los escaneos?</string>
|
||||
<string name="really_remove_selected_scans">¿Eliminar los escaneos seleccionados?</string>
|
||||
<string name="clear_history">Eliminar el historial</string>
|
||||
<string name="copy_scan">Copiar escaneo</string>
|
||||
<string name="edit_scan">Editar etiqueta</string>
|
||||
<string name="remove_scan">Borrar escaneo</string>
|
||||
<string name="edit_label">Editar etiqueta</string>
|
||||
<string name="remove_scan">Eliminar escaneo</string>
|
||||
<string name="enter_name">Introduzca un nombre para describir el escaneo</string>
|
||||
<string name="enter_name_hint">Nombre del escaneo</string>
|
||||
<string name="no_barcode_found">No se encontró ningún código de barras</string>
|
||||
@@ -153,7 +172,7 @@
|
||||
<string name="wifi_config_failed">No se pudo configurar el Wifi</string>
|
||||
<string name="wifi_added">Wifi añadido</string>
|
||||
<string name="sms_send">Enviar SMS</string>
|
||||
<string name="sms_error">No se puedo enviar SMS</string>
|
||||
<string name="sms_error">No se pudo enviar SMS</string>
|
||||
<string name="tel_dial">Marcar número</string>
|
||||
<string name="tel_error">No se pudo marcar el número</string>
|
||||
<string name="mail_send">Enviar correo</string>
|
||||
@@ -165,6 +184,8 @@
|
||||
<string name="otpauth_add">Añadir 2FA</string>
|
||||
<string name="search_web">Buscar en la web</string>
|
||||
<string name="search_scan">Buscar escaneo</string>
|
||||
<string name="add_to_history">Añadir al historial</string>
|
||||
<string name="added_to_history">Añadido al historial</string>
|
||||
<string name="export_to_file">Exportar a archivo</string>
|
||||
<string name="export_as">¿Exportar como?</string>
|
||||
<string name="export_csv_comma">Archivo CSV con comas</string>
|
||||
@@ -173,15 +194,17 @@
|
||||
<string name="export_database">Base de datos SQLite</string>
|
||||
<string name="saved_in_downloads">Guardado en Descargas</string>
|
||||
<string name="rotate_image_cw">Rotar la imagen en sentido horario</string>
|
||||
<string name="toggle_free_rotation">Toggle free rotation</string>
|
||||
<string name="pick_file">Seleccionar imagen</string>
|
||||
<string name="toggle_free_rotation">Activar rotación libre</string>
|
||||
<string name="pick_file">Seleccionar archivo</string>
|
||||
<string name="pick_image_file">Seleccionar imagen</string>
|
||||
<string name="pick_code_to_scan">Seleccione el código a escanear</string>
|
||||
<string name="shortcut_decode">Escanear un código</string>
|
||||
<string name="shortcut_encode">Crear un código</string>
|
||||
<string name="shortcut_history">Historial</string>
|
||||
<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">Red wi-fi</string>
|
||||
<string name="entry_type">Tipo</string>
|
||||
<string name="wifi_network">Red WiFi</string>
|
||||
<string name="wifi_ssid">Nombre</string>
|
||||
<string name="wifi_type">Tipo de autenticación</string>
|
||||
<string name="wifi_password">Contraseña</string>
|
||||
@@ -189,7 +212,13 @@
|
||||
<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>
|
||||
<string name="network_suggestions">Network suggestions</string>
|
||||
<string name="remove_suggestion">Remove suggestion</string>
|
||||
<string name="wifi_phase2">Método Phase 2</string>
|
||||
<string name="network_suggestions">Sugerencias de red</string>
|
||||
<string name="remove_suggestion">Eliminar sugerencia</string>
|
||||
<string name="email_to">Destinatario</string>
|
||||
<string name="email_subject">Asunto</string>
|
||||
<string name="email_body">Cuerpo</string>
|
||||
<string name="scheme">Esquema</string>
|
||||
<string name="host">Anfitrión</string>
|
||||
<string name="query">Consulta</string>
|
||||
</resources>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<string-array name="search_engines_names">
|
||||
<item>@string/always_ask</item>
|
||||
<item>گوگل</item>
|
||||
<item>Brave Search</item>
|
||||
<item>داکداکگو</item>
|
||||
<item>Qwant</item>
|
||||
<item>بینگ</item>
|
||||
@@ -16,6 +17,7 @@
|
||||
<string-array name="search_engines_values">
|
||||
<item></item>
|
||||
<item>https://www.google.com/search?q=</item>
|
||||
<item>https://search.brave.com/search?q=</item>
|
||||
<item>https://duckduckgo.com/?q=</item>
|
||||
<item>https://www.qwant.com/?q=</item>
|
||||
<item>https://bing.com/?q=</item>
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
<resources>
|
||||
<string name="welcome">خوش آمدید!</string>
|
||||
<string name="mode_expert">پیشرفته</string>
|
||||
<string name="mode_expert_description">میخواهم محتوای خوانده شده را بررسی کرده و به صورت دستی باز کنم.</string>
|
||||
<string name="mode_simple">ساده</string>
|
||||
<string name="mode_simple_description">فقط میخواهم بارکدها را اسکن کنم و فوراً باز شوند.</string>
|
||||
<string name="camera_error">نمیتوان به دوربین دسترسی داشت، لطفاً دوباره تلاش کنید</string>
|
||||
<string name="scan_code">پویش کد</string>
|
||||
<string name="compose_barcode">ایجاد بارکد</string>
|
||||
@@ -14,36 +19,40 @@
|
||||
<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="colors">Fore and background</string>
|
||||
<string name="colors_black_on_white">Black on white</string>
|
||||
<string name="colors_white_on_black">White on black</string>
|
||||
<string name="colors_black_on_transparent">Black on transparent</string>
|
||||
<string name="colors_white_on_transparent">White on transparent</string>
|
||||
<string name="sequence_size">Sequence size</string>
|
||||
<string name="sequence_index">Sequence index</string>
|
||||
<string name="sequence_id">Sequence ID</string>
|
||||
<string name="label">برچسب</string>
|
||||
<string name="colors">پس زمینه و پیش زمینه</string>
|
||||
<string name="colors_black_on_white">سیاه روی سفید</string>
|
||||
<string name="colors_white_on_black">سفید روی سیاه</string>
|
||||
<string name="colors_black_on_transparent">سیاه روی شفاف</string>
|
||||
<string name="colors_white_on_transparent">سفید روی شفاف</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 extension</string>
|
||||
<string name="barcode_version_number">Version</string>
|
||||
<string name="gtin_add_on">تمدید UPC EAN</string>
|
||||
<string name="barcode_version_number">نسخه</string>
|
||||
<string name="qr_version_and_modules">%1$s (%2$d modules)</string>
|
||||
<string name="qr_data_mask">ماسک داده</string>
|
||||
<string name="toggle_flash">روشن/خاموش کردن چراغ</string>
|
||||
<string name="error_flash">خطا هنگام روشن/خاموش کردن چراغ</string>
|
||||
<string name="share">همرسانی</string>
|
||||
<string name="share_file">اشتراک گذاری به عنوان فایل</string>
|
||||
<string name="share_as">همرسانی به عنوان؟</string>
|
||||
<string name="copy_to_clipboard">رونوشت به تخهگیره</string>
|
||||
<string name="copy_as_deeplink">کپی به صورت پیوند عمیق</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="size_width_by_height">اندازه به پیکسل: %1$d×%2$d</string>
|
||||
<string name="size_no_magnification">بدون بزرگنمایی</string>
|
||||
<string name="add_quiet_zone">اضافه کردن حوزه ساکت</string>
|
||||
<string name="input_content_here">محتویات ورودی را اینجا بنویسید</string>
|
||||
<string name="unescape">Expand escape sequences</string>
|
||||
<string name="unescape">توسیع دنبالههای فراری</string>
|
||||
<string name="encode">رمزگذاری</string>
|
||||
<string name="error_no_content">محتویاتی نیست</string>
|
||||
<string name="error_encoding_barcode">بارکد نمیتواند ایجاد شود</string>
|
||||
@@ -55,6 +64,9 @@
|
||||
<string name="bulk_mode_delay">تأخیر بین پویشهای مداوم</string>
|
||||
<string name="restrict_format">محدود کردن قالب</string>
|
||||
<string name="remove_restriction">برداشتن محدودیت</string>
|
||||
<string name="find_code">کد را پیدا کنید</string>
|
||||
<string name="find_code_with_content">چه محتوا؟</string>
|
||||
<string name="find_code_hint">یک عبارت وارد کنید</string>
|
||||
<string name="scan_format">پویش %s</string>
|
||||
<string name="quarter_second">یکچهارم ثانیه</string>
|
||||
<string name="half_second">نیم ثانیه</string>
|
||||
@@ -81,19 +93,21 @@
|
||||
<string name="show_toast_in_bulk_mode_summary">هنگام پویش مداوم، دادههای پویش شده را به طور خلاصه نشان میدهید.</string>
|
||||
<string name="vibrate">لرزش هنگام تشخیص</string>
|
||||
<string name="vibrate_summary">اگر میخواهید دستگاهتان هنگام شناسایی کد بلرزد، این را فعال کنید.</string>
|
||||
<string name="beep">Beep on detection</string>
|
||||
<string name="beep_summary">Enable this if you want your device to beep when a code is recognized.</string>
|
||||
<string name="beep_tone">Beep tone</string>
|
||||
<string name="tone_cdma_confirm">CDMA confirm</string>
|
||||
<string name="tone_sup_confirm">Call supervisory confirm</string>
|
||||
<string name="tone_sup_radio_ack">Call supervisory radio path acknowledgment</string>
|
||||
<string name="tone_prop_ack">Positive acknowledgment</string>
|
||||
<string name="tone_prop_beep">General beep</string>
|
||||
<string name="tone_prop_beep2">General double beep</string>
|
||||
<string name="beep">صدای بیپ در شناسایی</string>
|
||||
<string name="beep_summary">اگر میخواهید دستگاه شما هنگام شناسایی یک کد صدا کند، این را فعال کنید.</string>
|
||||
<string name="beep_tone">صدای بیپ</string>
|
||||
<string name="tone_cdma_confirm">تأیید CDMA</string>
|
||||
<string name="tone_sup_confirm">تماس نظارتی تأیید کنید</string>
|
||||
<string name="tone_sup_radio_ack">تائید مسیر رادیویی نظارت تماس</string>
|
||||
<string name="tone_prop_ack">تایید مثبت</string>
|
||||
<string name="tone_prop_beep">صدای عمومی</string>
|
||||
<string name="tone_prop_beep2">صدای دو بیپ عمومی</string>
|
||||
<string name="use_history">ذخیرهٔ تاریخچهٔ پویش</string>
|
||||
<string name="use_history_summary">کدهای شناسایی شده را در همین دستگاه ذخیره میکند.</string>
|
||||
<string name="ignore_duplicates">نادیده گرفتن تکراری ها</string>
|
||||
<string name="ignore_consecutive_duplicates">نادیده گرفتن موارد تکراری</string>
|
||||
<string name="ignore_consecutive_duplicates_summary">موارد تکراری متوالی را در تاریخچهٔ پویش ذخیره نمیکند.</string>
|
||||
<string name="ignore_any_duplicates">Ignore any duplicates</string>
|
||||
<string name="accept_duplicates">تکراریها را پذیرا باش</string>
|
||||
<string name="open_immediately">گشودن بلافاصله</string>
|
||||
<string name="open_immediately_summary">بازرسی را رها کنید و محتویات را فورا باز کنید. در صورت فعال بودن ممکن است محتوای مضر را باز کند.</string>
|
||||
<string name="copy_immediately">قرار دادن در تختهگیره</string>
|
||||
@@ -102,8 +116,10 @@
|
||||
<string name="show_meta_data_summary">نمایش دادههای اضافی در مورد بارکد پویش شده.</string>
|
||||
<string name="show_hex_dump">نمایش کد هگز استخراج شده</string>
|
||||
<string name="show_hex_dump_summary">نمایش کد هگز استخراج شده از محتویات پویش شده.</string>
|
||||
<string name="show_recreation">Show recreated barcode</string>
|
||||
<string name="show_recreation_summary">Recreate and show barcode from read content if possible.</string>
|
||||
<string name="show_checksum">نمایش مجموعه کنترلی</string>
|
||||
<string name="no_checksum">بدون چکسام</string>
|
||||
<string name="show_recreation">نمایش بارکد بازسازی شده</string>
|
||||
<string name="show_recreation_summary">دوباره ایجاد کرده و بارکد را از محتوای خوانده شده نشان دهید اگر امکان دارد.</string>
|
||||
<string name="close_automatically">بازگشت پس از رونوشت یا همرسانی</string>
|
||||
<string name="close_automatically_summary">پس از رونوشت یا همرسانی مطالب خوانده شده، به طور خودکار به صفحهٔ پویش بازگردید.</string>
|
||||
<string name="default_search_engine">گشودن دادهٔ نامشخص در</string>
|
||||
@@ -121,23 +137,26 @@
|
||||
<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_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="send_scan_bluetooth">Forward scans with Bluetooth</string>
|
||||
<string name="send_scan_bluetooth">انتقال اسکن ها با بلوتوث</string>
|
||||
<string name="send_scan_bluetooth_summary">Enable to forwards scans to a given Bluetooth host.</string>
|
||||
<string name="send_scan_bluetooth_host">Host</string>
|
||||
<string name="no_bluetooth_hosts_paired">No Hosts Paired</string>
|
||||
<string name="send_scan_bluetooth_host">میزبان</string>
|
||||
<string name="no_bluetooth_hosts_paired">میزبان های جفت نشده</string>
|
||||
<string name="bluetooth_connect_fail">Could not connect to bluetooth device.</string>
|
||||
<string name="bluetooth_send_fail">Could not send to bluetooth device.</string>
|
||||
<string name="bluetooth_send_success">Successful bluetooth send.</string>
|
||||
<string name="generation_category">نسل</string>
|
||||
<string name="brighten_screen">روشن کردن صفحه نمایش</string>
|
||||
<string name="brighten_screen_summary">تنظیم روشنایی به حداکثر برای افزایش اسکن کردن بارکد از صفحه نمایش.</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="edit_label">ویرایش عنوان</string>
|
||||
<string name="remove_scan">برداشتن پویش</string>
|
||||
<string name="enter_name">یک نام برای توضیح پویش وارد کنید</string>
|
||||
<string name="enter_name_hint">نام پویش</string>
|
||||
@@ -165,6 +184,8 @@
|
||||
<string name="otpauth_add">افزودن احراز هویت چندعاملی(2FA)</string>
|
||||
<string name="search_web">جستوجوی وب</string>
|
||||
<string name="search_scan">جستوجوی پویش</string>
|
||||
<string name="add_to_history">اضافه به تاریخچه</string>
|
||||
<string name="added_to_history">اضافه شد به تاریخچه</string>
|
||||
<string name="export_to_file">برونریزی به پرونده</string>
|
||||
<string name="export_as">برونریزی به عنوان؟</string>
|
||||
<string name="export_csv_comma">پروندهٔ سیاسوی با ویرگول</string>
|
||||
@@ -173,11 +194,13 @@
|
||||
<string name="export_database">پایگاهدادهٔ اسکیولایت</string>
|
||||
<string name="saved_in_downloads">در بارگیریها ذخیره شد</string>
|
||||
<string name="rotate_image_cw">تصویر را در جهت ساعتگرد بچرخانید</string>
|
||||
<string name="toggle_free_rotation">Toggle free rotation</string>
|
||||
<string name="pick_file">پروندهٔ تصویری را برگزینید</string>
|
||||
<string name="toggle_free_rotation">تغییر چرخش آزاد</string>
|
||||
<string name="pick_file">یک فایل را انتخاب کنید</string>
|
||||
<string name="pick_image_file">پروندهٔ تصویری را برگزینید</string>
|
||||
<string name="pick_code_to_scan">کدی را برای پویش برگزینید</string>
|
||||
<string name="shortcut_decode">پویش یک بارکد</string>
|
||||
<string name="shortcut_encode">ایجاد یک بارکد</string>
|
||||
<string name="shortcut_history">تاریخچه</string>
|
||||
<string name="shortcut_preferences">تنظیمات</string>
|
||||
<string name="background_request_failed">درخواست پسزمینه انجام نشد</string>
|
||||
<string name="entry_type">گونه</string>
|
||||
@@ -190,6 +213,12 @@
|
||||
<string name="wifi_anonymous_identity">هویت ناشناس</string>
|
||||
<string name="wifi_identity">هویت</string>
|
||||
<string name="wifi_phase2">روش دو مرحلهای</string>
|
||||
<string name="network_suggestions">Network suggestions</string>
|
||||
<string name="remove_suggestion">Remove suggestion</string>
|
||||
<string name="network_suggestions">پیشنهادهای شبکه</string>
|
||||
<string name="remove_suggestion">حذف پیشنهاد</string>
|
||||
<string name="email_to">گیرنده</string>
|
||||
<string name="email_subject">موضوع</string>
|
||||
<string name="email_body">متن</string>
|
||||
<string name="scheme">برنامه</string>
|
||||
<string name="host">میزبان</string>
|
||||
<string name="query">پرس و جو</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
<resources>
|
||||
<string name="welcome">Bienvenue !</string>
|
||||
<string name="mode_expert">Avancé</string>
|
||||
<string name="mode_expert_description">Je souhaite vérifier le contenu lu et l\'ouvrir manuellement.</string>
|
||||
<string name="mode_simple">Simple</string>
|
||||
<string name="mode_simple_description">Je veux simplement scanner les codes-barres et les ouvrir immédiatement.</string>
|
||||
<string name="camera_error">Impossible d\'accéder à la caméra, veuillez réessayer</string>
|
||||
<string name="scan_code">Scanner le code</string>
|
||||
<string name="scan_code">Scanner un code</string>
|
||||
<string name="compose_barcode">Créer un code-barres</string>
|
||||
<string name="decode_barcode">Décoder le code-barres</string>
|
||||
<string name="content">Contenu</string>
|
||||
<string name="binary_data">(données binaires)</string>
|
||||
<plurals name="barcode_info">
|
||||
<item quantity="one">%2$d caractère, %1$s</item>
|
||||
<item quantity="many">%2$d caractères, %1$s</item>
|
||||
<item quantity="other">%2$d caractères, %1$s</item>
|
||||
</plurals>
|
||||
<string name="error_correction_level">Niveau de correction d\'erreur</string>
|
||||
@@ -15,6 +19,7 @@
|
||||
<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="label">Étiquette</string>
|
||||
<string name="colors">Premier plan et arrière-plan</string>
|
||||
<string name="colors_black_on_white">Noir sur blanc</string>
|
||||
<string name="colors_white_on_black">Blanc sur noir</string>
|
||||
@@ -29,34 +34,40 @@
|
||||
<string name="gtin_add_on">Extension UPC EAN</string>
|
||||
<string name="barcode_version_number">Version</string>
|
||||
<string name="qr_version_and_modules">%1$s (%2$d modules)</string>
|
||||
<string name="toggle_flash">Activer le flash</string>
|
||||
<string name="error_flash">Erreur de basculement du flash</string>
|
||||
<string name="qr_data_mask">Masquage</string>
|
||||
<string name="toggle_flash">Activer/Désactiver le flash</string>
|
||||
<string name="error_flash">Erreur de changement d\'état du flash</string>
|
||||
<string name="share">Partager</string>
|
||||
<string name="share_as">Partager comme ?</string>
|
||||
<string name="share_file">Partager en tant que fichier</string>
|
||||
<string name="share_as">Partager en tant que ?</string>
|
||||
<string name="copy_to_clipboard">Copier dans le presse-papiers</string>
|
||||
<string name="copy_as_deeplink">Copier sous forme de lien profond</string>
|
||||
<string name="copied_to_clipboard">Copié dans le presse-papiers</string>
|
||||
<string name="copy_password">Copier le mot de passe dans le presse-papiers</string>
|
||||
<string name="copied_password_to_clipboard">Mot de passe copié dans le presse-papiers</string>
|
||||
<string name="open_url">Ouvrir l\'url</string>
|
||||
<string name="open_url">Ouvrir l\'URL</string>
|
||||
<string name="cannot_resolve_action">Aucune application ne peut ouvrir cela</string>
|
||||
<string name="pick_search_engine">Choisir un moteur de recherche</string>
|
||||
<string name="format">Format</string>
|
||||
<string name="size">Taille en pixels</string>
|
||||
<string name="width_by_height">%1$d×%2$d</string>
|
||||
<string name="input_content_here">Votre contenu ici</string>
|
||||
<string name="unescape">Expand escape sequences</string>
|
||||
<string name="size_width_by_height">Taille en pixels : %1$d×%2$d</string>
|
||||
<string name="size_no_magnification">Pas de grossissement</string>
|
||||
<string name="add_quiet_zone">Ajouter une zone blanche</string>
|
||||
<string name="input_content_here">Insérer le contenu ici</string>
|
||||
<string name="unescape">Étendre les caractères d\'échappement</string>
|
||||
<string name="encode">ENCODER</string>
|
||||
<string name="error_no_content">Contenu manquant</string>
|
||||
<string name="error_encoding_barcode">Le code-barres ne peut pas être généré</string>
|
||||
<string name="view_barcode">Voir le code barre</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="switch_camera">Changer de camera</string>
|
||||
<string name="bulk_mode">Scan continu</string>
|
||||
<string name="bulk_mode_summary">Démarre toujours en scan continu.</string>
|
||||
<string name="view_barcode">Voir le code-barres</string>
|
||||
<string name="info">Informations</string>
|
||||
<string name="switch_camera">Changer de caméra</string>
|
||||
<string name="bulk_mode">Scanner en continu</string>
|
||||
<string name="bulk_mode_summary">Toujours démarrer avec le scan en continu.</string>
|
||||
<string name="bulk_mode_delay">Délai entre les scans continus</string>
|
||||
<string name="restrict_format">Restreindre le format</string>
|
||||
<string name="remove_restriction">Supprimer la restriction</string>
|
||||
<string name="scan_format">Scan %s</string>
|
||||
<string name="find_code">Trouver un code</string>
|
||||
<string name="find_code_with_content">Quel contenu ?</string>
|
||||
<string name="find_code_hint">Entrer un terme</string>
|
||||
<string name="scan_format">Numériser %s</string>
|
||||
<string name="quarter_second">Un quart de seconde</string>
|
||||
<string name="half_second">Une demi-seconde</string>
|
||||
<string name="a_second">Une seconde</string>
|
||||
@@ -64,60 +75,64 @@
|
||||
<string name="five_seconds">Cinq secondes</string>
|
||||
<string name="history">Historique</string>
|
||||
<string name="preferences">Préférences</string>
|
||||
<string name="scan_category">Scan</string>
|
||||
<string name="scan_category">Numériser</string>
|
||||
<string name="content_category">Contenu</string>
|
||||
<string name="locale_category">Langue</string>
|
||||
<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">Restreint le scan à une région personnalisée.</string>
|
||||
<string name="show_crop_handle">Afficher le contrôle du cadrage</string>
|
||||
<string name="show_crop_handle_summary">Restreindre le scan à une zone 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="zoom_by_swiping_summary">Balayer vers le haut ou vers le bas de l\'écran pour contrôler le niveau de zoom de la caméra.</string>
|
||||
<string name="auto_rotate">Reconnaître les codes-barres 1D verticaux</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="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="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="try_harder_summary">Activer cette option pour les codes très difficiles à lire. Les performances seront affectées.</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">Fait vibrer l\'appareil lorsqu\'un code est reconnu.</string>
|
||||
<string name="beep">Beep on detection</string>
|
||||
<string name="beep_summary">Enable this if you want your device to beep when a code is recognized.</string>
|
||||
<string name="beep_tone">Beep tone</string>
|
||||
<string name="tone_cdma_confirm">CDMA confirm</string>
|
||||
<string name="tone_sup_confirm">Call supervisory confirm</string>
|
||||
<string name="tone_sup_radio_ack">Call supervisory radio path acknowledgment</string>
|
||||
<string name="tone_prop_ack">Positive acknowledgment</string>
|
||||
<string name="tone_prop_beep">General beep</string>
|
||||
<string name="tone_prop_beep2">General double beep</string>
|
||||
<string name="vibrate_summary">Faire vibrer l\'appareil lorsqu\'un code est détecté.</string>
|
||||
<string name="beep">Biper à la détection</string>
|
||||
<string name="beep_summary">Émettre un son lorsqu\'un code est détecté.</string>
|
||||
<string name="beep_tone">Tonalité du signal sonore</string>
|
||||
<string name="tone_cdma_confirm">Confirmation CDMA</string>
|
||||
<string name="tone_sup_confirm">Confirmation de la supervision de l\'appel</string>
|
||||
<string name="tone_sup_radio_ack">Accusé de réception du chemin radio de supervision d\'appel</string>
|
||||
<string name="tone_prop_ack">Accusé de réception positif</string>
|
||||
<string name="tone_prop_beep">Bip simple</string>
|
||||
<string name="tone_prop_beep2">Bip double</string>
|
||||
<string name="use_history">Sauvegarder l\'historique des scans</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 sauvegarde pas les doublons dans l\'historique des scans.</string>
|
||||
<string name="use_history_summary">Sauvegarder les codes reconnus sur votre appareil.</string>
|
||||
<string name="ignore_duplicates">Ignorer les doublons</string>
|
||||
<string name="ignore_consecutive_duplicates">Ignorer les doublons consécutifs</string>
|
||||
<string name="ignore_any_duplicates">Ignorer tous les doublons</string>
|
||||
<string name="accept_duplicates">Autoriser les doublons</string>
|
||||
<string name="open_immediately">Ouvrir immédiatement</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">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">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 le dump hexadécimal du contenu scanné.</string>
|
||||
<string name="open_immediately_summary">Ignorer l\'inspection et ouvrir le contenu immédiatement. Si cette option est activée, cela peut ouvrir un contenu nocif.</string>
|
||||
<string name="copy_immediately">Copier dans le presse-papiers</string>
|
||||
<string name="copy_immediately_summary">Copier automatiquement les contenus scannés dans le presse-papiers. Veuillez noter que d\'autres applications peuvent lire dans votre presse-papiers en arrière-plan.</string>
|
||||
<string name="show_meta_data">Afficher les métadonnées</string>
|
||||
<string name="show_meta_data_summary">Afficher des informations supplémentaires sur le code-barres scanné.</string>
|
||||
<string name="show_hex_dump">Afficher le dump hexadécimal</string>
|
||||
<string name="show_hex_dump_summary">Afficher le dump hexadécimal du contenu scanné.</string>
|
||||
<string name="show_checksum">Afficher la somme de contrôle</string>
|
||||
<string name="no_checksum">Pas de somme de contrôle</string>
|
||||
<string name="show_recreation">Afficher le code-barres recréé</string>
|
||||
<string name="show_recreation_summary">Recrée et affiche, si possible, le code-barres à partir du contenu lu.</string>
|
||||
<string name="show_recreation_summary">Recréer et afficher, si possible, le code-barres à partir 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="close_automatically_summary">Revenir automatiquement sur l\'écran de scan après avoir copié ou partagé le contenu scanné.</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 l’URL</string>
|
||||
<string name="open_with_url">Ouvrir les données inconnues avec l\'URL</string>
|
||||
<string name="clear_network_suggestions">Supprimer les suggestions de réseau</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_summary">Supprimer toutes les suggestions de réseau précédemment fournies par cette application.</string>
|
||||
<string name="really_remove_all_networks">Vraiment supprimer tous les réseaux ?</string>
|
||||
<string name="clear_network_suggestions_success">Suggestion(s) de réseau(x) supprimée(s)</string>
|
||||
<string name="clear_network_suggestions_nothing_to_remove">Rien à supprimer</string>
|
||||
<string name="send_category">Transfert</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_active_summary">Transférer 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>
|
||||
@@ -126,19 +141,22 @@
|
||||
<string name="send_type_post_json">POST application/json</string>
|
||||
<string name="send_type_external_browser">Ouvrir dans un navigateur externe</string>
|
||||
<string name="test_url">Tester l\'URL</string>
|
||||
<string name="send_scan_bluetooth">Forward scans with Bluetooth</string>
|
||||
<string name="send_scan_bluetooth_summary">Enable to forwards scans to a given Bluetooth host.</string>
|
||||
<string name="send_scan_bluetooth_host">Host</string>
|
||||
<string name="no_bluetooth_hosts_paired">No Hosts Paired</string>
|
||||
<string name="bluetooth_connect_fail">Could not connect to bluetooth device.</string>
|
||||
<string name="bluetooth_send_fail">Could not send to bluetooth device.</string>
|
||||
<string name="bluetooth_send_success">Successful bluetooth send.</string>
|
||||
<string name="send_scan_bluetooth">Partager les scans par Bluetooth</string>
|
||||
<string name="send_scan_bluetooth_summary">Partager les scans à un hôte Bluetooth donné.</string>
|
||||
<string name="send_scan_bluetooth_host">Hôte</string>
|
||||
<string name="no_bluetooth_hosts_paired">Aucun hôte appairé</string>
|
||||
<string name="bluetooth_connect_fail">Impossible de se connecter à l\'appareil Bluetooth.</string>
|
||||
<string name="bluetooth_send_fail">Impossible d\'envoyer à l\'appareil Bluetooth.</string>
|
||||
<string name="bluetooth_send_success">Envoi Bluetooth réussi.</string>
|
||||
<string name="generation_category">Génération</string>
|
||||
<string name="brighten_screen">Augmenter la luminosité de l\'écran</string>
|
||||
<string name="brighten_screen_summary">Régler la luminosité au maximum pour améliorer la lecture d\'un code-barres à l\'écran.</string>
|
||||
<string name="really_remove_scan">Supprimer le scan ?</string>
|
||||
<string name="really_remove_all_scans">Supprimer tous les scans ?</string>
|
||||
<string name="really_remove_selected_scans">Supprimer les scans sélectionnés ?</string>
|
||||
<string name="clear_history">Effacer l\'historique</string>
|
||||
<string name="clear_history">Supprimer l\'historique</string>
|
||||
<string name="copy_scan">Copier le scan</string>
|
||||
<string name="edit_scan">Éditer l\'étiquette</string>
|
||||
<string name="edit_label">Modifier l\'étiquette</string>
|
||||
<string name="remove_scan">Supprimer le scan</string>
|
||||
<string name="enter_name">Saisir un nom décrivant le scan</string>
|
||||
<string name="enter_name_hint">Nom du scan</string>
|
||||
@@ -150,9 +168,9 @@
|
||||
<string name="file_name">Nom du fichier</string>
|
||||
<string name="error_saving_file">Impossible de sauvegarder le fichier</string>
|
||||
<string name="error_file_exists">Fichier déjà existant</string>
|
||||
<string name="connect_to_wifi">Connecter au WiFi</string>
|
||||
<string name="wifi_config_failed">Impossible de configurer le WiFi</string>
|
||||
<string name="wifi_added">WiFi ajouté</string>
|
||||
<string name="connect_to_wifi">Connecter au Wi-Fi</string>
|
||||
<string name="wifi_config_failed">Impossible de configurer le Wi-Fi</string>
|
||||
<string name="wifi_added">Wi-Fi ajouté</string>
|
||||
<string name="sms_send">Envoyer SMS</string>
|
||||
<string name="sms_error">Impossible d\'envoyer le SMS</string>
|
||||
<string name="tel_dial">Numéro de téléphone</string>
|
||||
@@ -163,22 +181,26 @@
|
||||
<string name="vcard_failed">Impossible d\'ajouter aux contacts</string>
|
||||
<string name="vevent_add">Ajouter à l\'agenda</string>
|
||||
<string name="vevent_failed">Impossible d\'ajouter à l\'agenda</string>
|
||||
<string name="otpauth_add">Ajouter 2FA</string>
|
||||
<string name="otpauth_add">Ajouter l\'authentification à deux facteurs</string>
|
||||
<string name="search_web">Rechercher sur internet</string>
|
||||
<string name="search_scan">Rechercher un scan</string>
|
||||
<string name="export_to_file">Exporter comme fichier</string>
|
||||
<string name="export_as">Exporter comme ?</string>
|
||||
<string name="add_to_history">Ajouter à l\'historique</string>
|
||||
<string name="added_to_history">Ajouté à l\'historique</string>
|
||||
<string name="export_to_file">Exporter en tant que fichier</string>
|
||||
<string name="export_as">Exporter en tant que ?</string>
|
||||
<string name="export_csv_comma">CSV avec virgules</string>
|
||||
<string name="export_csv_semicolon">CSV avec points-virgules</string>
|
||||
<string name="export_json">Exporter comme JSON</string>
|
||||
<string name="export_json">Exporter en tant que JSON</string>
|
||||
<string name="export_database">Exporter la base de données SQLite</string>
|
||||
<string name="saved_in_downloads">Sauvegardé dans les Téléchargements</string>
|
||||
<string name="rotate_image_cw">Tourner l\'image dans le sens horaire</string>
|
||||
<string name="toggle_free_rotation">Activer la rotation libre</string>
|
||||
<string name="pick_file">Choisir un fichier image</string>
|
||||
<string name="pick_code_to_scan">Choisissez le code à scanner</string>
|
||||
<string name="pick_file">Choisir un fichier</string>
|
||||
<string name="pick_image_file">Choisir un fichier image</string>
|
||||
<string name="pick_code_to_scan">Choisir le code à scanner</string>
|
||||
<string name="shortcut_decode">Scanner un code-barres</string>
|
||||
<string name="shortcut_encode">Créer un code-barres</string>
|
||||
<string name="shortcut_history">Historique</string>
|
||||
<string name="shortcut_preferences">Réglages</string>
|
||||
<string name="background_request_failed">Échec de la requête d\'arrière-plan</string>
|
||||
<string name="entry_type">Type</string>
|
||||
@@ -191,6 +213,12 @@
|
||||
<string name="wifi_anonymous_identity">Identité anonyme</string>
|
||||
<string name="wifi_identity">Identité</string>
|
||||
<string name="wifi_phase2">Méthode de la phase 2</string>
|
||||
<string name="network_suggestions">Network suggestions</string>
|
||||
<string name="remove_suggestion">Remove suggestion</string>
|
||||
<string name="network_suggestions">Suggestions de réseaux</string>
|
||||
<string name="remove_suggestion">Supprimer les suggestions</string>
|
||||
<string name="email_to">Destinataire</string>
|
||||
<string name="email_subject">Sujet</string>
|
||||
<string name="email_body">Corps du message</string>
|
||||
<string name="scheme">Schéma</string>
|
||||
<string name="host">Hôte</string>
|
||||
<string name="query">Requête</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
<resources>
|
||||
<string name="welcome">Üdvözöljük!</string>
|
||||
<string name="mode_expert">Haladó</string>
|
||||
<string name="mode_expert_description">Szeretném ellenőrizni az olvasott tartalmat és kézzel megnyitni.</string>
|
||||
<string name="mode_simple">Egyszerű</string>
|
||||
<string name="mode_simple_description">Csak be akarom olvasni a vonalkódokat, és azonnal megnyitni őket.</string>
|
||||
<string name="camera_error">Nem lehet hozzáférni a kamerához, próbálja meg újra</string>
|
||||
<string name="scan_code">Kód beolvasása</string>
|
||||
<string name="compose_barcode">Vonalkód létrehozása</string>
|
||||
@@ -14,36 +19,40 @@
|
||||
<string name="error_correction_level_m" formatted="false">Közepes (~15% javítás)</string>
|
||||
<string name="error_correction_level_q" formatted="false">Kvartilis (~25% javítás)</string>
|
||||
<string name="error_correction_level_h" formatted="false">Magas (~30% javítás)</string>
|
||||
<string name="colors">Fore and background</string>
|
||||
<string name="colors_black_on_white">Black on white</string>
|
||||
<string name="colors_white_on_black">White on black</string>
|
||||
<string name="colors_black_on_transparent">Black on transparent</string>
|
||||
<string name="colors_white_on_transparent">White on transparent</string>
|
||||
<string name="sequence_size">Sequence size</string>
|
||||
<string name="sequence_index">Sequence index</string>
|
||||
<string name="sequence_id">Sequence ID</string>
|
||||
<string name="label">Címke</string>
|
||||
<string name="colors">Háttér és előtér</string>
|
||||
<string name="colors_black_on_white">Fekete fehér háttéren</string>
|
||||
<string name="colors_white_on_black">Fehér fekete hátteren</string>
|
||||
<string name="colors_black_on_transparent">Fekete átlátszó háttéren</string>
|
||||
<string name="colors_white_on_transparent">Fehér átlátszó hátteren</string>
|
||||
<string name="sequence_size">Sorozat mérete</string>
|
||||
<string name="sequence_index">Sorrend index</string>
|
||||
<string name="sequence_id">Sorozat azonosító</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="barcode_version_number">Verzió</string>
|
||||
<string name="qr_version_and_modules">%1$s (%2$d modules)</string>
|
||||
<string name="qr_data_mask">Adatmaszk</string>
|
||||
<string name="toggle_flash">Vaku be- vagy kikapcsolása</string>
|
||||
<string name="error_flash">Hiba a vaku be- vagy kikapcsolásakor</string>
|
||||
<string name="share">Megosztás</string>
|
||||
<string name="share_file">Fájlként 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>
|
||||
<string name="copy_as_deeplink">Másolás mélyhivatkozásként</string>
|
||||
<string name="copied_to_clipboard">Vágólapra helyezés</string>
|
||||
<string name="copy_password">Jelszó másolása a vágólapra</string>
|
||||
<string name="copied_password_to_clipboard">Jelszó kimásolva a vágólapra</string>
|
||||
<string name="open_url">URL megnyitása</string>
|
||||
<string name="cannot_resolve_action">Nincs alkalmazás, amely meg tudná nyitni azt</string>
|
||||
<string name="pick_search_engine">Válasszon keresőmotort</string>
|
||||
<string name="format">Formátum</string>
|
||||
<string name="size">Méret képpontban</string>
|
||||
<string name="width_by_height">%1$d×%2$d</string>
|
||||
<string name="size_width_by_height">Méret képpontban: %1$d×%2$d</string>
|
||||
<string name="size_no_magnification">Nincs nagyítás</string>
|
||||
<string name="add_quiet_zone">Nyugodt övezet hozzáadása</string>
|
||||
<string name="input_content_here">Itt adja meg a tartalmat</string>
|
||||
<string name="unescape">Expand escape sequences</string>
|
||||
<string name="unescape">Kibontja a menekülési sorokat</string>
|
||||
<string name="encode">KÓDOLÁS</string>
|
||||
<string name="error_no_content">Hiányzó tartalom</string>
|
||||
<string name="error_encoding_barcode">A vonalkódot nem lehet előállítani</string>
|
||||
@@ -55,6 +64,9 @@
|
||||
<string name="bulk_mode_delay">Folyamatos beolvasások közti késleltetés</string>
|
||||
<string name="restrict_format">Formátum korlátozása</string>
|
||||
<string name="remove_restriction">Korlátozás eltávolítása</string>
|
||||
<string name="find_code">Kód keresése</string>
|
||||
<string name="find_code_with_content">Mi a tartalom?</string>
|
||||
<string name="find_code_hint">Írjon be egy kifejezést</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>
|
||||
@@ -81,19 +93,21 @@
|
||||
<string name="show_toast_in_bulk_mode_summary">A beolvasott adatok gyors megjelenítése folyamatos beolvasáskor.</string>
|
||||
<string name="vibrate">Rezgetés felismeréskor</string>
|
||||
<string name="vibrate_summary">Akkor engedélyezze ezt, ha rezgetni szeretné az eszközét egy kód felismerésekor.</string>
|
||||
<string name="beep">Beep on detection</string>
|
||||
<string name="beep_summary">Enable this if you want your device to beep when a code is recognized.</string>
|
||||
<string name="beep_tone">Beep tone</string>
|
||||
<string name="tone_cdma_confirm">CDMA confirm</string>
|
||||
<string name="tone_sup_confirm">Call supervisory confirm</string>
|
||||
<string name="tone_sup_radio_ack">Call supervisory radio path acknowledgment</string>
|
||||
<string name="tone_prop_ack">Positive acknowledgment</string>
|
||||
<string name="tone_prop_beep">General beep</string>
|
||||
<string name="tone_prop_beep2">General double beep</string>
|
||||
<string name="beep">Jelzés hanggal</string>
|
||||
<string name="beep_summary">Engedélyezze ezt, ha azt szeretné, hogy eszköze pittyegjen, amikor egy kód felismerésre kerül.</string>
|
||||
<string name="beep_tone">Hangjelzés</string>
|
||||
<string name="tone_cdma_confirm">CDMA megerősítés</string>
|
||||
<string name="tone_sup_confirm">Hívás felügyeleti megerősítés</string>
|
||||
<string name="tone_sup_radio_ack">Hívás felügyeleti rádió útvonal megerősítés</string>
|
||||
<string name="tone_prop_ack">Pozitív visszaigazolás</string>
|
||||
<string name="tone_prop_beep">Általános csipogás</string>
|
||||
<string name="tone_prop_beep2">Általános dupla sípolás</string>
|
||||
<string name="use_history">Beolvasási előzmények mentése</string>
|
||||
<string name="use_history_summary">Felismert kódok mentése a saját eszközén.</string>
|
||||
<string name="ignore_duplicates">Ismétlések figyelmen kívül hagyása</string>
|
||||
<string name="ignore_consecutive_duplicates">Ne mentsen egymást követő azonos adatokat</string>
|
||||
<string name="ignore_consecutive_duplicates_summary">Ne mentse az egymást követő azonos adatokat a beolvasási előzményekbe.</string>
|
||||
<string name="ignore_any_duplicates">Ignore any duplicates</string>
|
||||
<string name="accept_duplicates">Ismétlődések elfogadása</string>
|
||||
<string name="open_immediately">Vizsgálat kihagyása és a tartalom azonnali megnyitása</string>
|
||||
<string name="open_immediately_summary">Vizsgálat kihagyása és a tartalom azonnali megnyitása. Ártalmas tartalmat is megnyithat, ha engedélyezve van.</string>
|
||||
<string name="copy_immediately">Tartalom automatikus másolása a vágólapra</string>
|
||||
@@ -102,8 +116,10 @@
|
||||
<string name="show_meta_data_summary">További adatok megjelenítése a beolvasott vonalkódokról.</string>
|
||||
<string name="show_hex_dump">Hexakiírás megjelenítése</string>
|
||||
<string name="show_hex_dump_summary">A beolvasott tartalom hexakiírásának megjelenítése.</string>
|
||||
<string name="show_recreation">Show recreated barcode</string>
|
||||
<string name="show_recreation_summary">Recreate and show barcode from read content if possible.</string>
|
||||
<string name="show_checksum">Mutassa meg az ellenőrző összeget</string>
|
||||
<string name="no_checksum">Nincs ellenőrző összeg</string>
|
||||
<string name="show_recreation">Mutasd az újrateremtett vonalkódot</string>
|
||||
<string name="show_recreation_summary">Ha lehetséges, újra létrehoz és mutasson vonalkódot az olvasott tartalomból.</string>
|
||||
<string name="close_automatically">Ugrás vissza másolás vagy megosztás után</string>
|
||||
<string name="close_automatically_summary">Automatikus visszatérés a beolvasási képernyőre a beolvasott tartalom másolása vagy megosztása után.</string>
|
||||
<string name="default_search_engine">Ismeretlen adatok megnyitása ebben</string>
|
||||
@@ -125,19 +141,22 @@
|
||||
<string name="send_type_post_json">POST application/json</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="send_scan_bluetooth">Forward scans with Bluetooth</string>
|
||||
<string name="send_scan_bluetooth">Bluetooth segítségével továbbítás</string>
|
||||
<string name="send_scan_bluetooth_summary">Enable to forwards scans to a given Bluetooth host.</string>
|
||||
<string name="send_scan_bluetooth_host">Host</string>
|
||||
<string name="no_bluetooth_hosts_paired">No Hosts Paired</string>
|
||||
<string name="no_bluetooth_hosts_paired">Nincsenek párosított hosztok</string>
|
||||
<string name="bluetooth_connect_fail">Could not connect to bluetooth device.</string>
|
||||
<string name="bluetooth_send_fail">Could not send to bluetooth device.</string>
|
||||
<string name="bluetooth_send_success">Successful bluetooth send.</string>
|
||||
<string name="generation_category">Generáció</string>
|
||||
<string name="brighten_screen">Képernyő világosítása</string>
|
||||
<string name="brighten_screen_summary">Állítsa a fényerőt maximumra a vonalkód olvasásának érdekében a képernyőről.</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>
|
||||
<string name="clear_history">Előzmények törlése</string>
|
||||
<string name="copy_scan">Beolvasás másolása</string>
|
||||
<string name="edit_scan">Címke szerkesztése</string>
|
||||
<string name="edit_label">Címke szerkesztése</string>
|
||||
<string name="remove_scan">Beolvasás eltávolítása</string>
|
||||
<string name="enter_name">Adjon meg egy nevet a beolvasás leírásához</string>
|
||||
<string name="enter_name_hint">A beolvasás neve</string>
|
||||
@@ -165,6 +184,8 @@
|
||||
<string name="otpauth_add">Kétlépcsős hitelesítés hozzáadása</string>
|
||||
<string name="search_web">Keresés a weben</string>
|
||||
<string name="search_scan">Beolvasás keresése</string>
|
||||
<string name="add_to_history">Hozzáadás a történethez</string>
|
||||
<string name="added_to_history">Hozzáadva a történethez</string>
|
||||
<string name="export_to_file">Exportálás fájlba</string>
|
||||
<string name="export_as">Exportálja másként?</string>
|
||||
<string name="export_csv_comma">CSV-fájl vesszővel</string>
|
||||
@@ -173,11 +194,13 @@
|
||||
<string name="export_database">SQLite adatbázis</string>
|
||||
<string name="saved_in_downloads">Elmentve a letöltésekbe</string>
|
||||
<string name="rotate_image_cw">Kép forgatása jobbra</string>
|
||||
<string name="toggle_free_rotation">Toggle free rotation</string>
|
||||
<string name="pick_file">Képfájl kiválasztása</string>
|
||||
<string name="toggle_free_rotation">Szabad forgatás váltása</string>
|
||||
<string name="pick_file">Válasszon egy fájlt</string>
|
||||
<string name="pick_image_file">Képfájl kiválasztása</string>
|
||||
<string name="pick_code_to_scan">Kód kiválasztása a beolvasáshoz</string>
|
||||
<string name="shortcut_decode">Vonalkód beolvasása</string>
|
||||
<string name="shortcut_encode">Vonalkód létrehozása</string>
|
||||
<string name="shortcut_history">Előzmények</string>
|
||||
<string name="shortcut_preferences">Beállítások</string>
|
||||
<string name="background_request_failed">A háttérkérés sikertelen</string>
|
||||
<string name="entry_type">Típus</string>
|
||||
@@ -190,6 +213,12 @@
|
||||
<string name="wifi_anonymous_identity">Névtelen személyazonosság</string>
|
||||
<string name="wifi_identity">Személyazonosság</string>
|
||||
<string name="wifi_phase2">Phase 2 módszer</string>
|
||||
<string name="network_suggestions">Network suggestions</string>
|
||||
<string name="remove_suggestion">Remove suggestion</string>
|
||||
<string name="network_suggestions">Hálózati javaslatok</string>
|
||||
<string name="remove_suggestion">Javaslat eltávolítása</string>
|
||||
<string name="email_to">Címzett</string>
|
||||
<string name="email_subject">Tárgy</string>
|
||||
<string name="email_body">Törzs</string>
|
||||
<string name="scheme">Terv</string>
|
||||
<string name="host">Házigazda</string>
|
||||
<string name="query">Lekérdezés</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
<resources>
|
||||
<string name="welcome">Selamat datang!</string>
|
||||
<string name="mode_expert">Lanjutan</string>
|
||||
<string name="mode_expert_description">Saya ingin memeriksa konten yang dibaca dan membukanya secara manual.</string>
|
||||
<string name="mode_simple">Sederhana</string>
|
||||
<string name="mode_simple_description">Saya hanya ingin memindai kode batang dan langsung membukanya.</string>
|
||||
<string name="camera_error">Tidak bisa mengakses kamera, silakan coba lagi</string>
|
||||
<string name="scan_code">Pindai kode</string>
|
||||
<string name="compose_barcode">Buat barcode</string>
|
||||
@@ -13,6 +18,7 @@
|
||||
<string name="error_correction_level_m" formatted="false">Medium (~15% koreksi)</string>
|
||||
<string name="error_correction_level_q" formatted="false">Kuartil (~25% koreksi)</string>
|
||||
<string name="error_correction_level_h" formatted="false">Tinggi (~30% koreksi)</string>
|
||||
<string name="label">Etiqueta</string>
|
||||
<string name="colors">Muka dan latar belakang</string>
|
||||
<string name="colors_black_on_white">Hitam di atas putih</string>
|
||||
<string name="colors_white_on_black">Putih di atas hitam</string>
|
||||
@@ -27,33 +33,39 @@
|
||||
<string name="gtin_add_on">Ekstensi UPC EAN</string>
|
||||
<string name="barcode_version_number">Versi</string>
|
||||
<string name="qr_version_and_modules">%1$s (%2$d modul)</string>
|
||||
<string name="qr_data_mask">Maschera dati</string>
|
||||
<string name="toggle_flash">Nyala/matikan blitz</string>
|
||||
<string name="error_flash">Galat nyala/matikan blitz</string>
|
||||
<string name="share">Bagikan</string>
|
||||
<string name="share_file">Dijeli kao datoteku</string>
|
||||
<string name="share_as">Bagikan sebagai?</string>
|
||||
<string name="copy_to_clipboard">Salin ke clipboard</string>
|
||||
<string name="copy_as_deeplink">Salin sebagai tautan langsung</string>
|
||||
<string name="copied_to_clipboard">Disalin ke clipboard</string>
|
||||
<string name="copy_password">Salin sandi ke clipboard</string>
|
||||
<string name="copied_password_to_clipboard">Sandi disalin ke clipboard</string>
|
||||
<string name="open_url">Buka url</string>
|
||||
<string name="cannot_resolve_action">Tidak ada aplikasi yang bisa membukanya</string>
|
||||
<string name="pick_search_engine">Pilih mesin pencari</string>
|
||||
<string name="format">Format</string>
|
||||
<string name="size">Ukuran dalam piksel</string>
|
||||
<string name="width_by_height">%1$d×%2$d</string>
|
||||
<string name="size_width_by_height">Ukuran dalam piksel: %1$d×%2$d</string>
|
||||
<string name="size_no_magnification">無放大</string>
|
||||
<string name="add_quiet_zone">Dodaj cichą strefę</string>
|
||||
<string name="input_content_here">Masukkan konten di sini</string>
|
||||
<string name="unescape">Expand escape sequences</string>
|
||||
<string name="unescape">Escapar secuencias de escape</string>
|
||||
<string name="encode">ENKODE</string>
|
||||
<string name="error_no_content">Tidak ada konten</string>
|
||||
<string name="error_encoding_barcode">Barcode tidak bisa dibuat</string>
|
||||
<string name="view_barcode">Lihat barcode</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="info">Informazioni</string>
|
||||
<string name="switch_camera">Ganti kamera</string>
|
||||
<string name="bulk_mode">Pindai tanpa henti</string>
|
||||
<string name="bulk_mode_summary">Selalu mulai pemindaian tanpa henti.</string>
|
||||
<string name="bulk_mode_delay">Tundaan antara pemindaian tanpa henti</string>
|
||||
<string name="restrict_format">Batasi format</string>
|
||||
<string name="remove_restriction">Hapus pembatasan</string>
|
||||
<string name="find_code">Trouver le code</string>
|
||||
<string name="find_code_with_content">Quel contenu?</string>
|
||||
<string name="find_code_hint">Ingresa un término</string>
|
||||
<string name="scan_format">Pindai %s</string>
|
||||
<string name="quarter_second">Seperempat detik</string>
|
||||
<string name="half_second">Setengah detik</string>
|
||||
@@ -83,16 +95,18 @@
|
||||
<string name="beep">Beep on detection</string>
|
||||
<string name="beep_summary">Enable this if you want your device to beep when a code is recognized.</string>
|
||||
<string name="beep_tone">Beep tone</string>
|
||||
<string name="tone_cdma_confirm">CDMA confirm</string>
|
||||
<string name="tone_sup_confirm">Call supervisory confirm</string>
|
||||
<string name="tone_sup_radio_ack">Call supervisory radio path acknowledgment</string>
|
||||
<string name="tone_cdma_confirm">CDMA 확인</string>
|
||||
<string name="tone_sup_confirm">تأكيد الإشراف على المكالمة</string>
|
||||
<string name="tone_sup_radio_ack">Llamada de supervisión de la ruta de radio de confirmación</string>
|
||||
<string name="tone_prop_ack">Positive acknowledgment</string>
|
||||
<string name="tone_prop_beep">General beep</string>
|
||||
<string name="tone_prop_beep2">General double beep</string>
|
||||
<string name="tone_prop_beep">Generale segnale acustico</string>
|
||||
<string name="tone_prop_beep2">Generelle Doppelton</string>
|
||||
<string name="use_history">Simpan riwayat pemindaian</string>
|
||||
<string name="use_history_summary">Simpan kode yang berhasil dipindai pada perangkat anda.</string>
|
||||
<string name="ignore_duplicates">Ignore duplicates</string>
|
||||
<string name="ignore_consecutive_duplicates">Abaikan duplikat</string>
|
||||
<string name="ignore_consecutive_duplicates_summary">Jangan simpan duplikat yang berturutan di dalam riwayat pemindaian.</string>
|
||||
<string name="ignore_any_duplicates">Ignore any duplicates</string>
|
||||
<string name="accept_duplicates">Accept duplicates</string>
|
||||
<string name="open_immediately">Buka segera</string>
|
||||
<string name="open_immediately_summary">Lewati inspeksi dan segera buka konten. Mungkin membuka konten berbahaya jika diaktifkan.</string>
|
||||
<string name="copy_immediately">Simpan ke dalam clipboard</string>
|
||||
@@ -101,6 +115,8 @@
|
||||
<string name="show_meta_data_summary">Tampilkan data tambahan tentang barcode yang dipindai.</string>
|
||||
<string name="show_hex_dump">Tampilkan hex dump</string>
|
||||
<string name="show_hex_dump_summary">Tampilkan hex dump dari konten hasil pemindaian.</string>
|
||||
<string name="show_checksum">Mostrar suma de verificación</string>
|
||||
<string name="no_checksum">Ingen kontrollsiffer</string>
|
||||
<string name="show_recreation">Tampilkan barcode yang dibuat ulang</string>
|
||||
<string name="show_recreation_summary">Buat ulang dan tampilkan barcode dari isi konten jika memungkinkan.</string>
|
||||
<string name="close_automatically">Kembali setelah Salin/Berbagi</string>
|
||||
@@ -124,19 +140,22 @@
|
||||
<string name="send_type_post_json">POST application/json</string>
|
||||
<string name="send_type_external_browser">Buka di peramban eksternal</string>
|
||||
<string name="test_url">URL Tes</string>
|
||||
<string name="send_scan_bluetooth">Forward scans with Bluetooth</string>
|
||||
<string name="send_scan_bluetooth">Reenviar exploraciones con Bluetooth</string>
|
||||
<string name="send_scan_bluetooth_summary">Enable to forwards scans to a given Bluetooth host.</string>
|
||||
<string name="send_scan_bluetooth_host">Host</string>
|
||||
<string name="send_scan_bluetooth_host">مضيف</string>
|
||||
<string name="no_bluetooth_hosts_paired">No Hosts Paired</string>
|
||||
<string name="bluetooth_connect_fail">Could not connect to bluetooth device.</string>
|
||||
<string name="bluetooth_send_fail">Could not send to bluetooth device.</string>
|
||||
<string name="bluetooth_send_success">Successful bluetooth send.</string>
|
||||
<string name="generation_category">Generasi</string>
|
||||
<string name="brighten_screen">明るさを強調する</string>
|
||||
<string name="brighten_screen_summary">Set brightness to maximum to enhance scanning a barcode from the screen.</string>
|
||||
<string name="really_remove_scan">Yakin menghapus pindaian?</string>
|
||||
<string name="really_remove_all_scans">Yakin menghapus semua pindaian?</string>
|
||||
<string name="really_remove_selected_scans">Yakin menghapus pindaian yang dipilih?</string>
|
||||
<string name="clear_history">Hapus riwayat</string>
|
||||
<string name="copy_scan">Salin pindaian</string>
|
||||
<string name="edit_scan">Sunting label</string>
|
||||
<string name="edit_label">Sunting label</string>
|
||||
<string name="remove_scan">Buang pindaian</string>
|
||||
<string name="enter_name">Masukkan keterangan pindaian</string>
|
||||
<string name="enter_name_hint">Nama pindaian</string>
|
||||
@@ -164,6 +183,8 @@
|
||||
<string name="otpauth_add">Tambahkan 2FA</string>
|
||||
<string name="search_web">Cari secara daring</string>
|
||||
<string name="search_scan">Cari pindaian</string>
|
||||
<string name="add_to_history">Aggiungi alla cronologia</string>
|
||||
<string name="added_to_history">Dodano do historii</string>
|
||||
<string name="export_to_file">Ekspor ke berkas</string>
|
||||
<string name="export_as">Ekspor sebagai?</string>
|
||||
<string name="export_csv_comma">CSV dengan koma</string>
|
||||
@@ -173,10 +194,12 @@
|
||||
<string name="saved_in_downloads">Simpan di dalam unduhan</string>
|
||||
<string name="rotate_image_cw">Putar gambar searah jarum jam</string>
|
||||
<string name="toggle_free_rotation">Nyala/matikan rotasi bebas</string>
|
||||
<string name="pick_file">Pilih berkas gambar</string>
|
||||
<string name="pick_file">Vælg en fil</string>
|
||||
<string name="pick_image_file">Pilih berkas gambar</string>
|
||||
<string name="pick_code_to_scan">Pilih kode untuk dipindai</string>
|
||||
<string name="shortcut_decode">Pindai barcode</string>
|
||||
<string name="shortcut_encode">Buat barcode</string>
|
||||
<string name="shortcut_history">Istoric</string>
|
||||
<string name="shortcut_preferences">Pengaturan</string>
|
||||
<string name="background_request_failed">Permintaan latar belakang gagal</string>
|
||||
<string name="entry_type">Tipe</string>
|
||||
@@ -189,6 +212,12 @@
|
||||
<string name="wifi_anonymous_identity">Identitas anonim</string>
|
||||
<string name="wifi_identity">Identitas</string>
|
||||
<string name="wifi_phase2">Metode fase 2</string>
|
||||
<string name="network_suggestions">Network suggestions</string>
|
||||
<string name="remove_suggestion">Remove suggestion</string>
|
||||
<string name="network_suggestions">Suggerimenti di rete</string>
|
||||
<string name="remove_suggestion">Elimina sugerencia</string>
|
||||
<string name="email_to">Penerima</string>
|
||||
<string name="email_subject">Subjek</string>
|
||||
<string name="email_body">Isi</string>
|
||||
<string name="scheme">Schema</string>
|
||||
<string name="host">Anfitrión</string>
|
||||
<string name="query">Consulta</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
<resources>
|
||||
<string name="welcome">Benvenuto!</string>
|
||||
<string name="mode_expert">Avanzato</string>
|
||||
<string name="mode_expert_description">Voglio controllare il contenuto letto e aprirlo manualmente.</string>
|
||||
<string name="mode_simple">Semplice</string>
|
||||
<string name="mode_simple_description">Voglio solo scansionare codici a barre e aprirli immediatamente.</string>
|
||||
<string name="camera_error">Impossibile accedere alla fotocamera. Riprova</string>
|
||||
<string name="scan_code">Scansiona codice</string>
|
||||
<string name="scan_code">Scansione codice</string>
|
||||
<string name="compose_barcode">Crea codice a barre</string>
|
||||
<string name="decode_barcode">Decodifica codice a barre</string>
|
||||
<string name="content">Contenuto</string>
|
||||
@@ -14,6 +19,7 @@
|
||||
<string name="error_correction_level_m" formatted="false">Medio (correzione ~15%)</string>
|
||||
<string name="error_correction_level_q" formatted="false">Quartile (correzione ~25%)</string>
|
||||
<string name="error_correction_level_h" formatted="false">Alto (correzione ~30%)</string>
|
||||
<string name="label">Etichetta</string>
|
||||
<string name="colors">Colore primo piano/sfondo</string>
|
||||
<string name="colors_black_on_white">Nero su bianco</string>
|
||||
<string name="colors_white_on_black">Bianco su nero</string>
|
||||
@@ -28,33 +34,39 @@
|
||||
<string name="gtin_add_on">Estensione UPC EAN</string>
|
||||
<string name="barcode_version_number">Versione</string>
|
||||
<string name="qr_version_and_modules">%1$s (%2$d moduli)</string>
|
||||
<string name="qr_data_mask">Maschera dati</string>
|
||||
<string name="toggle_flash">Attiva/disattiva flash</string>
|
||||
<string name="error_flash">Errore durante l\'attivazione/disattivazione del flash</string>
|
||||
<string name="error_flash">Errore durante attivazione/disattivazione flash</string>
|
||||
<string name="share">Condividi</string>
|
||||
<string name="share_file">Condividi come file</string>
|
||||
<string name="share_as">Condividi come?</string>
|
||||
<string name="copy_to_clipboard">Copia negli Appunti</string>
|
||||
<string name="copy_as_deeplink">Copia come deeplink</string>
|
||||
<string name="copied_to_clipboard">Copiato negli Appunti</string>
|
||||
<string name="copy_password">Copia password negli Appunti</string>
|
||||
<string name="copied_password_to_clipboard">Password copiata negli Appunti</string>
|
||||
<string name="open_url">Apri URL</string>
|
||||
<string name="cannot_resolve_action">Nessuna app può aprirlo</string>
|
||||
<string name="pick_search_engine">Scegli motore di ricerca</string>
|
||||
<string name="format">Formato</string>
|
||||
<string name="size">Dimensioni (pixel)</string>
|
||||
<string name="width_by_height">%1$d×%2$d</string>
|
||||
<string name="size_width_by_height">Dimensioni (pixel): %1$d×%2$d</string>
|
||||
<string name="size_no_magnification">Nessun ingrandimento</string>
|
||||
<string name="add_quiet_zone">Aggiungi zona tranquilla</string>
|
||||
<string name="input_content_here">Inserisci qui il contenuto</string>
|
||||
<string name="unescape">Espandi sequenze escape</string>
|
||||
<string name="encode">CODIFICA</string>
|
||||
<string name="error_no_content">Contenuto mancante</string>
|
||||
<string name="error_encoding_barcode">Il codice a barre non può essere generato</string>
|
||||
<string name="view_barcode">Vedi codice a barre</string>
|
||||
<string name="view_barcode">Visualizza codice a barre</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="switch_camera">Cambia fotocamera</string>
|
||||
<string name="bulk_mode">Scansione continua</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="remove_restriction">Rimuovi limitazione</string>
|
||||
<string name="find_code">Trova codice</string>
|
||||
<string name="find_code_with_content">Quale contenuto?</string>
|
||||
<string name="find_code_hint">Inserisci un termine</string>
|
||||
<string name="scan_format">Scansiona %s</string>
|
||||
<string name="quarter_second">Un quarto di secondo</string>
|
||||
<string name="half_second">Mezzo secondo</string>
|
||||
@@ -62,25 +74,25 @@
|
||||
<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="preferences">Impostazioni</string>
|
||||
<string name="scan_category">Scansione</string>
|
||||
<string name="content_category">Contenuto</string>
|
||||
<string name="locale_category">Lingua</string>
|
||||
<string name="custom_locale">Lingua personalizzata</string>
|
||||
<string name="follow_system_settings">Usa le impostazioni di sistema</string>
|
||||
<string name="follow_system_settings">Usa impostazioni di sistema</string>
|
||||
<string name="show_crop_handle">Visualizza riquadro limite</string>
|
||||
<string name="show_crop_handle_summary">Limita la scansione ad una regione personalizzata.</string>
|
||||
<string name="show_crop_handle_summary">Limita scansione ad una regione personalizzata.</string>
|
||||
<string name="barcode_formats">Formati 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 codici a barre verticali</string>
|
||||
<string name="auto_rotate_summary">Ruota automaticamente la fotocamera per riconoscere i codici a barre verticali. In base al dispositivo, potrebbe ridurre le prestazioni.</string>
|
||||
<string name="try_harder">Ottimizza il lettore per accuratezza</string>
|
||||
<string name="try_harder">Ottimizza 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">Visualizza i dati in modalità continua</string>
|
||||
<string name="show_toast_in_bulk_mode">Visualizza dati in modalità continua</string>
|
||||
<string name="show_toast_in_bulk_mode_summary">Durante la scansione continua visualizza brevemente i dati scansionati.</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="vibrate">Vibra al rilevamento</string>
|
||||
<string name="vibrate_summary">Attiva questa opzione se vuoi che il dispositivo vibri quando viene riconosciuto un codice.</string>
|
||||
<string name="beep">Beep al rilevamento</string>
|
||||
<string name="beep_summary">Abilita questa opzione se vuoi che il dispositivo emetta un beep quando viene riconosciuto un codice.</string>
|
||||
<string name="beep_tone">Tono beep</string>
|
||||
@@ -91,22 +103,26 @@
|
||||
<string name="tone_prop_beep">Beep generale</string>
|
||||
<string name="tone_prop_beep2">Doppio beep generale</string>
|
||||
<string name="use_history">Salva cronologia scansioni</string>
|
||||
<string name="use_history_summary">Salva i codici riconosciuti nel dispositivo.</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="use_history_summary">Salva nel dispositivo codici riconosciuti.</string>
|
||||
<string name="ignore_duplicates">Ignora duplicati</string>
|
||||
<string name="ignore_consecutive_duplicates">Duplicati consecutivi</string>
|
||||
<string name="ignore_any_duplicates">Qualsiasi duplicato</string>
|
||||
<string name="accept_duplicates">Accetta duplicati</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">Visualizza metadati</string>
|
||||
<string name="show_meta_data_summary">Visualizza dati aggiuntivi sul codice a barre scansionato.</string>
|
||||
<string name="show_meta_data_summary">Visualizza dati aggiuntivi codice a barre scansionato.</string>
|
||||
<string name="show_hex_dump">Visualizza dump esadecimale</string>
|
||||
<string name="show_hex_dump_summary">Visualizza un dump esadecimale dei contenuti scansionati.</string>
|
||||
<string name="show_recreation">Visualizza il codice a barre ricreato</string>
|
||||
<string name="show_recreation_summary">Ricrea e visualizza, se possibile, il codice a barre dal contenuto letto.</string>
|
||||
<string name="show_hex_dump_summary">Visualizza dump esadecimale contenuti scansionati.</string>
|
||||
<string name="show_checksum">Visualizza checksum</string>
|
||||
<string name="no_checksum">Nessuna checksum</string>
|
||||
<string name="show_recreation">Visualizza codice a barre ricreato</string>
|
||||
<string name="show_recreation_summary">Ricrea e visualizza, se possibile, il codice a barre, dal contenuto letto.</string>
|
||||
<string name="close_automatically">Vai indietro dopo la copia/condivisione</string>
|
||||
<string name="close_automatically_summary">Dopo aver copiato o condiviso i contenuti letti torna automaticamente alla schermata di scansione.</string>
|
||||
<string name="default_search_engine">Apri dati sconosciuti in</string>
|
||||
<string name="default_search_engine">Apri dati sconosciuti con</string>
|
||||
<string name="always_ask">Chiedi sempre</string>
|
||||
<string name="open_with_url">Apri dati sconosciuti con URL</string>
|
||||
<string name="clear_network_suggestions">Cancella suggerimenti di rete</string>
|
||||
@@ -119,8 +135,8 @@
|
||||
<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">OTTIENI e aggiungi il contenuto</string>
|
||||
<string name="send_type_get_query_string">OTTIENI con stringa completa della richiesta</string>
|
||||
<string name="send_type_get_add_content">GET e aggiungi contenuto</string>
|
||||
<string name="send_type_get_query_string">GET con stringa completa 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">Apri nel browser esterno</string>
|
||||
@@ -132,12 +148,15 @@
|
||||
<string name="bluetooth_connect_fail">Impossibile connettersi al dispositivo Bluetooth.</string>
|
||||
<string name="bluetooth_send_fail">Impossibile inviare al dispositivo Bluetooth.</string>
|
||||
<string name="bluetooth_send_success">Invio via Bluetooth riuscito.</string>
|
||||
<string name="generation_category">Generazione</string>
|
||||
<string name="brighten_screen">Schiarisci schermo</string>
|
||||
<string name="brighten_screen_summary">Imposta luminosità al massimo per migliorare la scansione di un codice a barre dallo schermo.</string>
|
||||
<string name="really_remove_scan">Vuoi rimuovere la scansione?</string>
|
||||
<string name="really_remove_all_scans">Vuoi rimuovere tutte le scansioni?</string>
|
||||
<string name="really_remove_selected_scans">Vuoi rimuovere le scansioni selezionate?</string>
|
||||
<string name="clear_history">Azzera cronologia</string>
|
||||
<string name="copy_scan">Copia scansione</string>
|
||||
<string name="edit_scan">Modifica etichetta</string>
|
||||
<string name="edit_label">Modifica etichetta</string>
|
||||
<string name="remove_scan">Rimuovi scansione</string>
|
||||
<string name="enter_name">Inserisci un nome per descrivere la scansione</string>
|
||||
<string name="enter_name_hint">Nome scansione</string>
|
||||
@@ -145,7 +164,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">Salva come file nei Download?</string>
|
||||
<string name="save_as_file_name">Salva come file nei download?</string>
|
||||
<string name="file_name">Nome file</string>
|
||||
<string name="error_saving_file">Impossibile salvare il file</string>
|
||||
<string name="error_file_exists">Il file esiste già</string>
|
||||
@@ -165,8 +184,10 @@
|
||||
<string name="otpauth_add">Aggiungi 2FA</string>
|
||||
<string name="search_web">Cerca nel web</string>
|
||||
<string name="search_scan">Cerca scansione</string>
|
||||
<string name="export_to_file">Esporta su file</string>
|
||||
<string name="export_as">Come esportare?</string>
|
||||
<string name="add_to_history">Aggiungi alla cronologia</string>
|
||||
<string name="added_to_history">Aggiunto alla cronologia</string>
|
||||
<string name="export_to_file">Esporta in un file</string>
|
||||
<string name="export_as">Esportare come?</string>
|
||||
<string name="export_csv_comma">CSV con virgole</string>
|
||||
<string name="export_csv_semicolon">CSV con punti e virgola</string>
|
||||
<string name="export_json">File JSON</string>
|
||||
@@ -174,10 +195,12 @@
|
||||
<string name="saved_in_downloads">Salvato nei download</string>
|
||||
<string name="rotate_image_cw">Ruota immagine in senso orario</string>
|
||||
<string name="toggle_free_rotation">Abilita/disabilita rotazione libera</string>
|
||||
<string name="pick_file">Scegli file immagine</string>
|
||||
<string name="pick_code_to_scan">Scegli codice da scansionare</string>
|
||||
<string name="shortcut_decode">Scansiona un codice a barre</string>
|
||||
<string name="shortcut_encode">Crea un codice a barre</string>
|
||||
<string name="pick_file">Seleziona file</string>
|
||||
<string name="pick_image_file">Apri file immagine</string>
|
||||
<string name="pick_code_to_scan">Seleziona codice da scansionare</string>
|
||||
<string name="shortcut_decode">Scansiona codice a barre</string>
|
||||
<string name="shortcut_encode">Crea codice a barre</string>
|
||||
<string name="shortcut_history">Cronologia</string>
|
||||
<string name="shortcut_preferences">Impostazioni</string>
|
||||
<string name="background_request_failed">Richiesta in secondo piano fallita</string>
|
||||
<string name="entry_type">Tipo</string>
|
||||
@@ -192,4 +215,10 @@
|
||||
<string name="wifi_phase2">Metodo fase 2</string>
|
||||
<string name="network_suggestions">Suggerimenti rete</string>
|
||||
<string name="remove_suggestion">Elimina suggerimento</string>
|
||||
<string name="email_to">Destinatario</string>
|
||||
<string name="email_subject">Oggetto</string>
|
||||
<string name="email_body">Corpo</string>
|
||||
<string name="scheme">Schema</string>
|
||||
<string name="host">Host</string>
|
||||
<string name="query">Query</string>
|
||||
</resources>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user