Compare commits

...
254 Commits
Author SHA1 Message Date
Markus Fisch 694c854650 Advance version number to 1.68.1 2026-02-21 21:50:51 +01:00
Markus Fisch b1cd12ec77 Validate frame data before giving it to ZXing
And refactor preview callback to use an early exit for clarity.

According to Vitals, `frameData` can apparently be `null` on
some devices sometimes.
2026-02-21 21:40:38 +01:00
Markus Fisch 0c27b15025 Check if fragment is still added
Before setting brightness, which may happen later because
it's called from the queue.
2026-02-21 21:20:31 +01:00
Markus Fisch 5de950bdbf Update tools version 2026-02-21 19:14:54 +01:00
Markus Fisch ae9e304ef6 Handle bad decoded intent parcelables safely
Guard reading the `decoded` Scan extra in MainActivity against
framework unparcel exceptions. Some external or malformed intents can
trigger a NullPointerException in Parcel before app-level checks run.

Return null when unparceling fails so the app falls back to the default
DecodeFragment path instead of crashing on launch.
2026-02-21 19:13:47 +01:00
Dominik StadlerandMarkus Fisch c4cc14554b Clear search-term on empty input, not on "Cancel"
Clearing search-term on cancel and not on empty input is
unexpected. "Cancel" usually does not change the state.

Rather clear the searchTerm on empty input and do not
handle the "Cancel"-button at all.
2026-02-20 13:52:50 +01:00
Dominik StadlerandMarkus Fisch d6afccf562 Apply some IDE suggestions 2026-02-20 13:52:50 +01:00
Markus Fisch a90bdad117 Update ZXingCpp
And remove the now missing `returnCodabarStartEnd` feature.
2026-02-13 19:05:35 +01:00
solokotandGitHub 52d4f082f2 Update Russian translation
Small Improvements
2026-02-12 19:45:38 +01:00
Markus Fisch 1c63f5b3ac Advance version number to 1.68.0 2026-02-07 20:18:43 +01:00
Markus Fisch 21985b4533 Remove deprecated flags from gradle.properties
Stuff only added because Google deemed it necessary.
Now the Google propeller caps have apparently changed
their minds. Don't care.
2026-02-07 20:15:41 +01:00
bovirusandGitHub 012ac644aa Italian language update 2026-02-07 12:21:26 +01:00
Markus Fisch 19f19739fd Fix up navigation for setting fragments 2026-02-06 17:42:18 +01:00
Markus Fisch f01f9fcd91 Add a dedicated fragement for managing profiles
And the missing option to remove profiles.
2026-02-06 17:42:18 +01:00
Markus Fisch 4177b248c3 Clean up AutomatedActionsFragment 2026-02-06 17:42:18 +01:00
Markus Fisch e4182a265b Add a ViewHolder to ActionsAdapter 2026-02-06 17:42:18 +01:00
Markus Fisch 98df5a447c Move adding an action into its own function
For readability.
2026-02-06 17:42:18 +01:00
Markus Fisch a0a1405359 Allow alphanumeric profile names only
Since a profile name is also used as a preference name,
it cannot contain path separators or anything like that.
So keep names alphanumeric for robustness.
2026-02-06 17:42:18 +01:00
bovirusandGitHub 76922c421c Italian language update 2026-02-06 17:41:42 +01:00
Markus Fisch cac9dd16da Add automated actions
To automatically execute actions when certain barcodes
are scanned.
2026-02-04 22:40:39 +01:00
Markus Fisch a1f9ba1056 Update gradle wrapper 2026-02-04 21:45:58 +01:00
Markus Fisch 0b67c272b6 Update robolectric to latest version 2026-02-03 19:16:54 +01:00
Markus Fisch 9b8be5305d Update tools and gradle wrapper to Gradle 9
And remove the now unnecessary kotlin-android plugin.
2026-02-03 18:15:33 +01:00
Markus Fisch a026ade13b Remove own toHexString() implementation
In favor of the new Kotlin API.
2026-01-26 20:31:07 +01:00
Markus Fisch b1771bc5ea Advance version number to 1.67.1 2026-01-14 23:19:07 +01:00
Markus Fisch 5199460669 Open file:// Uris as documents from Android N+
Because file:// can't be used with Scoped Storage anymore.
2026-01-14 23:16:05 +01:00
Markus Fisch fd040e1660 Remove lateinit from PickActivity members
Because `onCreate()` may return before the `lateinit`
members have been initialized, which would raise a
`kotlin.UninitializedPropertyAccessException`.
2026-01-13 14:53:27 +01:00
Markus Fisch 2e1528364e Fix auto closing of DecodeFragment
Some actions require the `Activity` object to stay alive, e.g.
`OpenOrSearchAction` or `WifiAction` may show a dialog first and
only then may trigger `startActivity`.

Before this commit, with the preferences `openImmediately` or
`closeAutomatically` enabled, the app crashed for content that
would show a dialog through `DecodeFragment.executeAction()`.
2026-01-13 21:20:35 +01:00
KamaleiZestriandGitHub d21af142bb Catch all exceptions for socket.connect()
`socket.connect()` can throw `IOException`s, too.

Occurs with Windows when the bluetooth service gets killed and
restarted in the background.
2026-01-13 09:52:50 +01:00
Markus Fisch 8395e135f9 Update Kotlin version 2025-12-11 20:04:25 +01:00
Markus Fisch c0caa0ce44 Update tools version 2025-12-11 20:03:10 +01:00
Markus Fisch 96927ef9b2 Add line breaks in README 2025-11-14 19:11:18 +01:00
TheRedCyclopsandGitHub 84d9a3b55a Remove double "at" in README 2025-11-14 11:14:34 +01:00
Markus Fisch ffe8e4c5ba Advance version number to 1.67.0 2025-10-10 20:06:03 +02:00
Markus Fisch bc7c5dca97 Fix restoring crop and zoom on profile changes 2025-10-09 20:45:52 +02:00
Markus Fisch d9737f4df1 Store crop handle position per view size
So we have a crop handle configuration for each view size.
2025-10-05 22:42:00 +02:00
Markus Fisch 0e8147713a Use default preferences for welcome screen marker
Since the welcome screen is intended to be shown for the first
start only.
2025-10-05 22:39:54 +02:00
Markus Fisch 3d425a097a Update robolectric version 2025-09-30 23:24:26 +02:00
Markus Fisch c26765cc63 Update Kotlin version 2025-09-30 23:22:15 +02:00
Markus Fisch fb6dc8ba55 Update kotlinx coroutines library 2025-09-30 23:21:54 +02:00
Markus Fisch 70f4d166a0 Support having multiple preference profiles
Requested in #558.
2025-09-30 23:20:30 +02:00
Markus Fisch 6c136ce719 Support regex when searching for a barcode 2025-09-30 16:36:14 +02:00
Markus Fisch c6dee80c0a Quote search term and remove comma
Between search term and barcode restriction.

Cleaner and less irritating.
2025-09-29 22:24:54 +02:00
Markus Fisch 1f43dd4827 Refactor scan_format to scan_for
Since this label is used for barcode formats and search terms.
2025-09-29 22:19:56 +02:00
Markus Fisch a366157977 Fix setting title with format and search term 2025-09-29 22:18:30 +02:00
Markus Fisch 6bb72672a3 Show a message for unmatching barcodes
So it's clear that the app recognized the barcode, but it's
not the one we're looking for.
2025-09-29 22:12:26 +02:00
Markus Fisch 04774527a0 Fix string capitalization for consistency 2025-09-29 21:36:44 +02:00
Markus Fisch 6f3e644aa8 Update tools version 2025-09-23 21:48:02 +02:00
Markus Fisch 5e3176b703 Update ZXingC++ for 16 KB ELF alignment
Because starting November 1st, 2025, all new apps and updates to
existing apps submitted to Google Play and targeting Android 15+
devices must support 16 KB page sizes on 64-bit devices.
2025-09-23 21:38:46 +02:00
zicaandGitHub 0f0aaff284 Improve zh-tw and ja translations 2025-09-12 18:39:29 +02:00
Markus Fisch f0d09e81b4 Advance version number to 1.66.2 2025-08-26 21:16:08 +02:00
Markus Fisch 771258cb5c Update tools version 2025-08-18 20:33:59 +02:00
Markus Fisch 9482086666 Reformat code 2025-08-18 20:33:21 +02:00
CertainBotandGitHub bcad50c3e8 Update Spanish translation
Few corrections over the AI translations.
2025-08-08 09:45:07 +02:00
Markus Fisch dbd055699e Remove adding ?content= to forward URL
When no other query was given.

This was meant to make it clear that the scanned content will
just be added to the given URL. But it's too restrictive as
there can be URLs without query strings.
2025-08-04 21:52:05 +02:00
Markus Fisch d67f8273d6 Update to target SDK 36 2025-07-30 21:03:48 +02:00
Markus Fisch 649be0ceb6 Update Kotlin version 2025-07-30 20:59:22 +02:00
Markus Fisch f5eb5dd8d2 Update gradle wrapper 2025-07-30 20:52:46 +02:00
Markus Fisch eb805dad2f Update tools version and gradle wrapper 2025-06-11 17:44:40 +02:00
Markus Fisch 202bb32b29 Advance version number to 1.66.1 2025-05-28 18:33:03 +02:00
Markus Fisch 50b8a4d329 Add missing curly braces
We always use curly braces around here.

When in doubt, google for "Apple's SSL Bug".
2025-05-28 18:27:26 +02:00
Markus Fisch fa15478fb4 Remove unused parameter from Bluetooth send method 2025-05-28 18:26:51 +02:00
Markus Fisch 20c2a5a8aa Catch Bluetooth SecurityExceptions explicitly
These are thrown when the user denies access to Bluetooth,
and `$ ./gradlew lintDebug` complains rightly about this.
2025-05-28 18:25:10 +02:00
Markus Fisch 00b95678ed Remove now unnecessary cast 2025-05-19 21:31:39 +02:00
Markus Fisch 5df3517563 Use an underscore for unused exception parameters
To get rid of the warnings.
2025-05-19 21:29:02 +02:00
Markus Fisch fd6946d2c2 Fix checking scan.id instead of fragment id 2025-05-19 21:05:22 +02:00
Markus Fisch c74d229e1f Update tools and gradle wrapper 2025-05-19 21:04:33 +02:00
Unknownman820andGitHub fa99a19db5 Update chinese translation 2025-05-07 19:07:44 +02:00
Markus Fisch b083fcf213 Show elements of invalid Wifi configurations too
So users can still access the elements even when the configuration
is invalid.
2025-05-07 18:47:34 +02:00
Markus Fisch 46930ddea6 Unescape Wifi elements in parseMap()
Instead of unescaping elements selectively in `SimpleDataAccessor`.

This is much simpler and this way the `Map` `parseMap()` returns has
always unescaped values. This `Map` is also layed out in the
`DecodeFragment`, which still displayed the escaped values before
this commit.
2025-05-07 18:47:34 +02:00
Markus Fisch 7bef77b96e Add test for unescaping emojis in Wifi credentials
Because of #544.
2025-05-07 18:47:34 +02:00
NathanandGitHub c5f4c088b4 Update french locale
Correction of automatic translations.
2025-05-01 21:58:36 +02:00
Yurt PageandGitHub 73f7210e18 Update translations from Toolate 2025-04-25 23:50:20 +02:00
Markus Fisch 469429e663 Advance version number to 1.66.0 2025-04-25 23:49:16 +02:00
Markus Fisch 1a7ac59740 Refactor BinaryEyeDeeplink to just Deeplink 2025-04-25 12:54:50 +02:00
Markus Fisch c13c59e728 Refactor IDLParser/ElementMap to use camel case
And match these names with other symbols in this code base
that don't use all caps for abbreviations like `Url` or
`UrlDialogFragment` and so on.
2025-04-25 12:51:30 +02:00
Markus Fisch bc17e25717 Reformat with Android Studio 2025-04-25 12:48:51 +02:00
Yurt PageandGitHub 5017bd4c8d fastlane: simplify short description
And add "QR code" to make it easier to find.
2025-04-25 12:43:58 +02:00
Anton SerebryakovandGitHub a44a913eca Support deep links to encode content via web links
To open barcode://encode via a HTTP deep link.

Also adds actions to copy the deep link to the clipboard.
2025-04-25 12:42:42 +02:00
Yurt PageandGitHub 007e83e243 fastlane: merge summary.txt into short_description.txt
summary.txt was used by Tripple-T but the default Fastlane file name is short_description.txt. It supports the summary.txt too but we have a duplication problem and some languages has both.
So I removed the duplicated summary.txt and renamed existing summary.txt into short_description.txt.
2025-04-23 21:43:47 +02:00
Markus Fisch de8f97c246 Parse International Driver Licenses
Usually from PDF417 barcodes.
2025-04-06 15:10:36 +02:00
Markus Fisch 1287591e53 Clean up Bluetooth instructions
Use proper bold syntax instead of making things headlines that
aren't headlines. Markdown is not source code for HTML but a human
readable document format. So semantics are important.

Also removes detailed install instructions for the companion apps.
These should be in the README of the corresponding application.
2025-04-03 21:44:40 +02:00
Markus Fisch 8c17238572 Only show clipboard toast before Android 13
Because there is a clipboard popup from Android 13 on.
2025-04-03 09:17:12 +02:00
Curious, aren't we?andGitHub 5dbb36e6a9 Add Bluetooth howto to the README 2025-04-02 23:33:14 +02:00
Markus Fisch ca7dfb3a33 Add new WiFi's via Settings intent
From API level 30, Android R, on.

This has the advantage that Binary Eye doesn't take ownership of
the newly added WiFi. See #482
2025-03-30 19:53:12 +02:00
Markus Fisch 419b7d5765 Give share file its own icon
So that we can clearly distinguish this action from the share
action when all actions are layed out next to each other.
2025-03-16 21:20:23 +01:00
Markus Fisch fa38866669 Update missing string resources
With a little help from the OpenAI API ;)
2025-03-27 14:19:44 +01:00
bovirusandGitHub 19ca521f59 Italian language update 2025-03-16 11:43:37 +01:00
Markus Fisch be657d86cf Show parsed data for MatMsgAction and WebAction 2025-03-14 12:27:06 +01:00
Markus Fisch 5199a4a526 Remove log messages
From debugging.
2025-03-14 12:27:06 +01:00
Markus Fisch ff48a543c9 Show parsed VType items in data view 2025-03-14 12:27:06 +01:00
Markus Fisch 2eadd01a87 Use apply scope function initialize valueView
Removes repetitions.
2025-03-14 12:27:06 +01:00
Markus Fisch dd2abe7d89 Copy password as every other content
And remove the corresponding special message.

It's probably clear enough with the generic message.
2025-03-14 12:27:06 +01:00
Markus Fisch 8e05caac70 Add click listener to copy data items
Into the clipboard for easy access.

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

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

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

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

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

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

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

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

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

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

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

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

This reverts commit 12554e63e2.
2024-12-05 19:52:56 +01:00
Markus Fisch 5dc5bed281 Fix indent in Portuguese (Brazil) translation 2024-12-01 17:58:36 +01:00
Too Late (bot)andGitHub 2a13561962 Update Portuguese (Brazil) translation 2024-12-01 17:57:46 +01:00
Markus Fisch 1476f41cda Advance version number to 1.63.13 2024-11-24 19:37:04 +01:00
Markus Fisch 0da1ffbe02 Use JavaVersion.VERSION_1_8 for compatibility
Instead of just the version literal. Using the literal breaks
when the host has a higher Java version installed.
2024-11-19 20:08:34 +01:00
Markus Fisch 980679b6a3 Avoid UTF-8 conversion for binary input data
Otherwise the data is modified and doesn't match the desired input.

Still, it's probably better to introduce another CheckBox to explicity
control charset conversion.
2024-11-17 16:50:02 +01:00
Markus Fisch 28cbb55299 Use early returns when handling escape sequences
Improves readability.
2024-11-17 16:47:23 +01:00
Markus Fisch 1a4e7d100a Improve readability of expanding escape sequences 2024-11-17 16:46:23 +01:00
Markus Fisch 265e43b439 Fix argument order for assertEquals()
In EscapeTest.
2024-11-17 16:44:56 +01:00
NathanandGitHub 03a58c3412 Update French translation 2024-11-13 21:37:17 +01:00
Markus Fisch 327cad48f0 Update coroutines library 2024-11-06 21:50:30 +01:00
Markus Fisch 8e64fe2f56 Update Kotlin version 2024-11-06 21:50:14 +01:00
Markus Fisch 368d4dbb66 Fix typo in german translation 2024-10-18 19:39:38 +02:00
Manuel CovasandGitHub c8c3c094db Fix chronological order of scans taken less than 1 second apart 2024-10-17 16:36:24 +02:00
Wiktor PerskawiecandGitHub aa4aefe35f Fix typos in README and PRIVACY 2024-10-17 16:34:34 +02:00
AhmedandGitHub 536b3d77c1 Update arabian zero plural translation 2024-10-02 16:14:54 +02:00
Markus Fisch bbf2a80981 Add missing plurals for arabic
To silence lint from complaining that these are missing.
2024-10-02 15:39:33 +02:00
AhmedandGitHub ae99214d6c Update plurals in arabic translation 2024-10-02 15:36:54 +02:00
AhmedandGitHub 73b5a0969c Add arabic translation 2024-10-02 12:42:07 +02:00
Markus Fisch bf5dc96ac9 Advance version number to 1.63.12 2024-09-26 21:30:48 +02:00
Markus Fisch 301d9c2513 Fix argument list of recreation in onClickListener 2024-09-26 21:24:49 +02:00
Markus Fisch 0a11611b8f Advance version number to 1.63.11 2024-09-21 20:20:24 +02:00
Markus Fisch 249e310e43 Simplify asking for a size when sharing/exporting 2024-09-20 12:10:06 +02:00
Sergiy StuparandGitHub 6279c5731a Update Ukrainian translation 2024-09-20 12:09:46 +02:00
bovirusandGitHub df98637951 Update Italian language 2024-09-18 12:09:08 +02:00
solokotandGitHub 08b3558e46 Update Russian translation 2024-09-18 12:08:45 +02:00
Markus Fisch 648ef6fdf8 Update Kotlin coroutines library 2024-09-17 22:38:04 +02:00
Markus Fisch 807f075a51 Update Kotlin version 2024-09-17 22:37:08 +02:00
Markus Fisch 27789beeed Simplify ellipsis regex 2024-09-17 22:31:42 +02:00
Markus Fisch 97bee27827 Replace View casts for generic denotation 2024-09-17 22:29:02 +02:00
Markus Fisch 3fee49301e Only ask for barcode size when exporting/sharing
Because this parameter is just relevant when exporting/sharing
a raster image format.
2024-09-17 22:28:37 +02:00
Markus Fisch 8a6073291b Improve size slider for barcode generation
Reduce size options to popular powers of two.

And add the option to generate a barcode in the smallest
possible size of one pixel per module.
2024-09-17 20:54:07 +02:00
Markus Fisch 47151de4a4 Fix indent in Portuguese (Brazil) translation 2024-09-06 16:19:41 +02:00
Too Late (bot)andGitHub 248c4edca1 Update Portuguese (Brazil) translation 2024-09-06 16:18:21 +02:00
Markus Fisch 2c7386d536 Fix indent in Chinese translation 2024-08-17 18:12:32 +02:00
Too Late (bot)andGitHub e19cad4c87 Update Chinese translation 2024-08-17 18:09:47 +02:00
ZahnstocherandGitHub 585e0f3372 Update parcel tracking URL 2024-07-25 12:04:37 +02:00
Markus Fisch 2b7f611774 Fix showing tooltip on action FAB 2024-07-24 20:21:50 +02:00
Markus Fisch 05b2a0d9c9 Support hyphens in the second level domain
To correctly recognize URLs as WebAction.
2024-07-24 20:08:57 +02:00
Markus Fisch e7287eb088 Update tools version and gradle wrapper 2024-07-24 19:41:43 +02:00
NathanandGitHub 4b45ad3fe7 Update French translation
Added translations for checksum calculation (1.63.8) and screen brightening (1.63.10) features.
2024-07-13 19:58:04 +02:00
Markus Fisch 449d8fdbbd Advance version number to 1.63.10 2024-06-30 19:41:13 +02:00
Markus Fisch f44cff9912 Use type in getParcelableExtra() on Tiramisu+
And ignore the deprecation warning for earlier Android versions.

Also use a scope function to remove the local variable in the
parent context.
2024-06-30 19:06:02 +02:00
ThetaDevandGitHub 24730dfde3 Allow file input from share menu
Updates the handleSendText method to support both types of intents.

Also extends the accepted file types to text/* to also accept VCards
and other text-based files.
2024-06-30 18:54:20 +02:00
Sergiy StuparandGitHub be6539d41f Update Ukrainian translation 2024-06-16 13:41:14 +02:00
bovirusandGitHub 60dd40554e Update Italian language 2024-06-12 09:05:24 +02:00
solokotandGitHub 837119b6ec Update Russian translation 2024-06-12 09:04:57 +02:00
Markus Fisch abe11b82e5 Fix indent in Polish translation 2024-07-11 12:49:33 +02:00
Markus Fisch 4fc1e36d04 Add a menu item to toggle screen brightness
So this setting can be enabled/disabled where it's required, too.
2024-06-11 14:22:22 +02:00
Markus Fisch 54aa15e1be Set maximum brightness when showing a barcode
To make scanning from screen easier.
2024-06-11 14:22:18 +02:00
Too Late (bot)andGitHub 876d8fc7b3 Update Polish translation 2024-06-10 20:31:07 +02:00
Markus Fisch 7bdcdaaa5d Advance version number to 1.63.9 2024-06-02 18:15:53 +02:00
Markus Fisch fd44728e89 Ignore "URL:" before URLs in barcode content
Apparently some people think it's necessary to add an "URL:" prefix
before an actual URL, so let's cut that rubbish.
2024-06-02 15:24:22 +02:00
Markus Fisch 8242520a0b Export/share generated barcode as JPG
On request.

Generally, using a lossless format like PNG makes more sense.
2024-06-02 14:00:27 +02:00
Markus Fisch 2e7e9a942b Fix indent in new translations 2024-06-02 13:51:43 +02:00
Too Late (bot)andGitHub dcc9d5b3ff Update Brazilian translation 2024-06-02 11:54:14 +02:00
Markus Fisch 607aab854c Update build tools 2024-05-27 19:39:46 +02:00
Markus Fisch cc2b2f076b Fix exporting/sharing multiple history items
By their ID.

`?` is only one argument so a list of numbers becomes just one ID,
which cannot work, of course.
2024-05-27 19:37:43 +02:00
Too Late (bot)andGitHub 90e9723de5 Update Chinese translation 2024-05-13 10:38:54 +02:00
Sergiy StuparandGitHub ab6787e60b Update Ukrainian translation 2024-05-12 10:40:14 +02:00
solokotandGitHub 698686a45a Update Russian translation 2024-05-11 15:35:51 +02:00
Markus Fisch 95bdaa1252 Reformat imports with Android Studio
Seems like wildcard imports are back in fashion.
2024-05-11 14:13:39 +02:00
Markus Fisch f0c02e5156 Reformat with Android Studio 2024-05-11 14:13:20 +02:00
Markus Fisch a8c50308d0 Add Ko-fi for donations 2024-05-11 13:20:31 +02:00
bovirusandGitHub 6b359d4c65 Update Italian language 2024-05-10 21:08:07 +02:00
Markus Fisch 863caed387 Advance version number to 1.63.8 2024-05-10 20:56:25 +02:00
Markus Fisch e4f17b9bdf Add an option to show a checksum
For the decoded content. On request of #452.
2024-05-10 20:37:32 +02:00
Markus Fisch c2071d8d81 Treat LF/CR/Tab as printable characters
When encoding "something\nlike\tthüs". This string should be encoded
as text rather than binary, of course.
2024-05-08 19:49:19 +02:00
Markus Fisch 4758c2d337 Update tools and gradle wrapper 2024-05-07 21:57:36 +02:00
Markus Fisch 91336fe15c Encode as ByteArray if text contains non-printable
To support escape sequences containing non-printable characters.
2024-05-07 21:54:57 +02:00
Markus Fisch 2c437f3771 Add Liberapay to README 2024-04-27 15:58:37 +02:00
bovirusandGitHub cb082b20fe Update Italian language 2024-04-21 21:27:57 +02:00
Markus Fisch bce3630686 Advance version number to 1.63.7 2024-04-17 21:48:31 +02:00
Markus Fisch 4feb4d066c Update ZXingCpp for Aztec Runes 2024-04-17 21:24:30 +02:00
Markus Fisch e7d84cd916 Update tools version 2024-04-17 21:22:53 +02:00
Giovanni DonisiandGitHub 84e2917694 Update Italian translation 2024-04-14 13:58:07 +02:00
Markus Fisch b87464ec40 Advance version number to 1.63.6 2024-04-13 14:38:27 +02:00
Markus Fisch 269e0f41a6 Correct spelling mistakes in Spanish translation
Following the recommendations from the linter.
2024-04-13 14:32:27 +02:00
NathanandGitHub aefbc4a296 Update French translation 2024-04-13 14:26:19 +02:00
Sergiy StuparandGitHub c64984dc6a Update Ukrainian translation 2024-03-31 12:49:47 +02:00
Markus Fisch 24c7ad0ba4 Return Codabar start/stop codes
As requested in #445.

The start/stop codes determine if a barcode is complete or if there
is a continuation code to scan.
2024-03-30 12:46:15 +01:00
Markus Fisch 643a935852 Move buildConfig from properties into build.gradle
Apparently setting 'android.defaults.buildfeatures.buildconfig=true'
is deprecated and will be removed in version 9.0 of the Android Gradle
plugin.
2024-03-30 12:46:15 +01:00
solokotandGitHub 4fcb836b53 Update Russian translation 2024-03-26 12:12:30 +01:00
Markus Fisch 48ed5f1a2c Add history shortcut 2024-03-25 21:36:53 +01:00
199 changed files with 6764 additions and 1896 deletions
+111
View File
@@ -1,5 +1,116 @@
# Change Log
## 1.68.1
* Improve UI of search barcode dialog
* Stability improvements
* Update ZXingCpp
* Update Russian translation
## 1.68.0
* Add automated actions
* Improve managing profiles
## 1.67.1
* Fix opening file:// Uris
* Fix auto-return/immediate opening
* Improve Bluetooth stability
## 1.67.0
* Add preference profiles
* Support regex when searching for a barcode
* Improve feedback when searching for a barcode
* Update translations
## 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
+5
View File
@@ -16,5 +16,10 @@ to squash when merging into master:
Then write a [good commit message][commit_messages] to keep the history
meaningful and useful. One feature, one commit.
## Translations
For translations use [Toolate][toolate].
[android_best_practices]: https://developer.android.com/distribute/best-practices/develop/
[commit_messages]: https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#_commit_guidelines
[toolate]: https://toolate.othing.xyz/projects/binaryeye/
+20
View File
@@ -28,6 +28,26 @@ 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 \
+1 -1
View File
@@ -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]
+57 -9
View File
@@ -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.
@@ -17,7 +17,15 @@ 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>
style="height: 60px !important;width: 217px !important;"/></a>&nbsp;<a
href="https://liberapay.com/markusfisch/" target="_blank"><img
src="https://liberapay.com/assets/widgets/donate.svg"
alt="Liberapay"
style="height: 60px !important"/></a>&nbsp;<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
@@ -75,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
@@ -96,10 +104,32 @@ 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
[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
@@ -147,6 +177,21 @@ class YourActivity : AppCompatActivity() {
}
```
## 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/
@@ -169,6 +214,9 @@ class YourActivity : AppCompatActivity() {
[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
+13 -9
View File
@@ -1,5 +1,4 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
android {
namespace "de.markusfisch.android.binaryeye"
@@ -9,8 +8,8 @@ android {
minSdk 9
targetSdk sdk_version
versionCode 133
versionName '1.63.5'
versionCode 155
versionName '1.68.1'
}
signingConfigs {
@@ -35,13 +34,17 @@ android {
}
release {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
minifyEnabled true
shrinkResources true
signingConfig signingConfigs.release
}
}
buildFeatures {
buildConfig true
}
bundle {
language {
// To make the app bundle contain all language resources
@@ -54,17 +57,17 @@ android {
}
}
// Required since Gradle 8.
compileOptions {
// Required for Gradle 8.
sourceCompatibility = 17
targetCompatibility = 17
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
dependencies {
// Kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2"
// Support
implementation "com.android.support:appcompat-v7:$support_version"
@@ -75,8 +78,9 @@ dependencies {
// External
implementation 'com.github.markusfisch:CameraView:1.10.0'
implementation 'com.github.markusfisch:ScalingImageView:1.4.1'
implementation 'com.github.markusfisch:zxing-cpp:v2.2.0.4'
implementation 'com.github.markusfisch:zxing-cpp:v3.0.1.0'
// Testing
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.robolectric:robolectric:4.16.1'
}
+34 -4
View File
@@ -19,7 +19,8 @@
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-feature android:name="android.hardware.camera"/>
<uses-feature android:name="android.hardware.camera.autofocus"/>
<uses-feature android:name="android.hardware.camera.autofocus"
android:required="false" />
<uses-sdk tools:overrideLibrary="android.support.v14.preference"/>
<queries>
<intent>
@@ -62,7 +63,7 @@
<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"/>
@@ -75,6 +76,15 @@
<data android:host="details" android:scheme="market"
android:pathSuffix="id=com.xiaomi.scanner"/>
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:host="markusfisch.de"/>
<data android:pathPrefix="/BinaryEye"/>
<data android:scheme="http"/>
<data android:scheme="https"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
@@ -99,14 +109,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>
@@ -6,15 +6,17 @@ import de.markusfisch.android.binaryeye.content.execShareIntent
import de.markusfisch.android.binaryeye.content.openUrl
import de.markusfisch.android.binaryeye.widget.toast
interface IAction {
val iconResId: Int
val titleResId: Int
abstract class Action {
abstract val iconResId: Int
abstract val titleResId: Int
fun canExecuteOn(data: ByteArray): Boolean
suspend fun execute(context: Context, data: ByteArray)
var fired: Boolean = false
abstract fun canExecuteOn(data: ByteArray): Boolean
abstract suspend fun execute(context: Context, data: ByteArray)
}
abstract class IntentAction : IAction {
abstract class IntentAction : Action() {
abstract val errorMsg: Int
final override suspend fun execute(context: Context, data: ByteArray) {
@@ -22,7 +24,7 @@ abstract class IntentAction : IAction {
if (intent == null) {
context.toast(errorMsg)
} else {
context.execShareIntent(intent)
fired = context.execShareIntent(intent)
}
}
@@ -32,7 +34,7 @@ abstract class IntentAction : IAction {
): Intent?
}
abstract class SchemeAction : IAction {
abstract class SchemeAction : Action() {
abstract val scheme: String
open val buildRegex: Boolean = false
@@ -50,6 +52,6 @@ abstract class SchemeAction : IAction {
}
final override suspend fun execute(context: Context, data: ByteArray) {
context.openUrl(String(data))
fired = context.openUrl(String(data))
}
}
@@ -12,9 +12,9 @@ import de.markusfisch.android.binaryeye.actions.web.WebAction
import de.markusfisch.android.binaryeye.actions.wifi.WifiAction
object ActionRegistry {
val DEFAULT_ACTION: IAction = OpenOrSearchAction
val DEFAULT_ACTION: Action = OpenOrSearchAction
private val REGISTRY: Set<IAction> = setOf(
private val REGISTRY: Set<Action> = setOf(
MailAction,
MatMsgAction,
OtpauthAction,
@@ -28,7 +28,7 @@ object ActionRegistry {
WebAction
)
fun getAction(data: ByteArray): IAction = REGISTRY.find {
fun getAction(data: ByteArray): Action = REGISTRY.find {
it.canExecuteOn(data)
} ?: DEFAULT_ACTION
}
@@ -2,62 +2,65 @@ package de.markusfisch.android.binaryeye.actions.search
import android.content.Context
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.actions.IAction
import de.markusfisch.android.binaryeye.actions.Action
import de.markusfisch.android.binaryeye.app.alertDialog
import de.markusfisch.android.binaryeye.app.prefs
import de.markusfisch.android.binaryeye.content.openUrl
import de.markusfisch.android.binaryeye.net.urlEncode
object OpenOrSearchAction : IAction {
object OpenOrSearchAction : Action() {
override val iconResId: Int = R.drawable.ic_action_search
override val titleResId: Int = R.string.search_web
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)
fired = openUrlOrSearch(context, String(data))
}
}
private suspend fun openUrlOrSearch(
context: Context,
url: String,
search: Boolean = true
): Boolean {
if (context.openUrl(url, silent = true)) {
return true
}
if (search) {
return prependSearchUrl(context, url)?.let {
openUrlOrSearch(context, it, false)
} ?: false
}
return 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())
}
}
}
@@ -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
}
@@ -2,12 +2,12 @@ package de.markusfisch.android.binaryeye.actions.web
import android.content.Context
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.actions.IAction
import de.markusfisch.android.binaryeye.actions.Action
import de.markusfisch.android.binaryeye.content.openUrl
object WebAction : IAction {
object WebAction : Action() {
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,9 +18,12 @@ 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}"
}
context.openUrl(url)
fired = context.openUrl(url)
}
}
@@ -2,31 +2,39 @@ package de.markusfisch.android.binaryeye.actions.wifi
import android.content.Context
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.actions.IAction
import de.markusfisch.android.binaryeye.actions.Action
import de.markusfisch.android.binaryeye.widget.toast
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
object WifiAction : IAction {
object WifiAction : Action() {
override val iconResId = R.drawable.ic_action_wifi
override val titleResId = R.string.connect_to_wifi
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 message = WifiConnector.parse(String(data))?.let {
WifiConnector.addNetwork(context, it)
} ?: R.string.wifi_config_failed
withContext(Dispatchers.Main) {
context.toast(message)
override suspend fun execute(context: Context, data: ByteArray) {
withContext(Dispatchers.IO) {
fired = WifiConnector.parse(String(data))?.let {
WifiConnector.addNetwork(context, it)
} ?: false
withContext(Dispatchers.Main) {
context.toast(
if (fired) {
R.string.wifi_added
} else {
R.string.wifi_config_failed
}
)
}
}
}
}
@@ -1,14 +1,15 @@
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,15 +36,9 @@ 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 try {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
// WifiConfiguration is deprecated in Android Q but
@@ -54,7 +49,7 @@ object WifiConnector {
} else {
WifiNetworkSuggestion.Builder().apply(parsedData)
}
} catch (e: IllegalArgumentException) {
} 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
@@ -62,24 +57,26 @@ object WifiConnector {
}
}
fun addNetwork(context: Context, config: Any): Int {
fun addNetwork(context: Context, config: Any): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
config is WifiNetworkSuggestion.Builder
) {
context.suggestNetworkFromBuilder(config)
return true
}
val wifiManager = context.applicationContext.getSystemService(
Context.WIFI_SERVICE
) as WifiManager
// WifiConfiguration is deprecated in Android Q.
@Suppress("DEPRECATION")
return if ((config is WifiConfiguration &&
wifiManager.enableWifi() &&
wifiManager.removeOldNetwork(config) &&
wifiManager.enableNewNetwork(config)) ||
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
config is WifiNetworkSuggestion.Builder &&
wifiManager.addNetworkFromBuilder(config))
) {
R.string.wifi_added
} else {
R.string.wifi_config_failed
}
return (config is WifiConfiguration &&
wifiManager.enableWifi() &&
wifiManager.removeOldNetwork(config) &&
wifiManager.enableNewNetwork(config)) ||
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
config is WifiNetworkSuggestion.Builder &&
wifiManager.addNetworkFromBuilder(config))
}
internal fun parseMap(input: String): Map<String, String>? {
@@ -93,7 +90,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()
}
}
@@ -103,17 +100,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) {
@@ -201,7 +198,7 @@ object WifiConnector {
})
}
}
} catch (e: IllegalArgumentException) {
} catch (_: IllegalArgumentException) {
// Don't accept passphrases that aren't ASCII encodeable.
return null
}
@@ -313,6 +310,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()
@@ -366,7 +378,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,14 +10,15 @@ 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
@@ -26,22 +28,23 @@ 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.automation.runAutomatedActions
import de.markusfisch.android.binaryeye.bluetooth.sendBluetoothAsync
import de.markusfisch.android.binaryeye.content.copyToClipboard
import de.markusfisch.android.binaryeye.content.execShareIntent
import de.markusfisch.android.binaryeye.content.openUrl
import de.markusfisch.android.binaryeye.content.toHexString
import de.markusfisch.android.binaryeye.database.toScan
import de.markusfisch.android.binaryeye.graphics.FrameMetrics
import de.markusfisch.android.binaryeye.graphics.mapPosition
import de.markusfisch.android.binaryeye.graphics.setFrameRoi
import de.markusfisch.android.binaryeye.graphics.setFrameToView
import de.markusfisch.android.binaryeye.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
@@ -52,6 +55,9 @@ import de.markusfisch.android.zxingcpp.ZxingCpp.BarcodeFormat
import de.markusfisch.android.zxingcpp.ZxingCpp.Binarizer
import de.markusfisch.android.zxingcpp.ZxingCpp.ReaderOptions
import de.markusfisch.android.zxingcpp.ZxingCpp.Result
import de.markusfisch.android.zxingcpp.ZxingCpp.TextMode
import java.io.FileInputStream
import java.util.Scanner
import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToInt
@@ -65,6 +71,8 @@ class CameraActivity : AppCompatActivity() {
private lateinit var zoomBar: SeekBar
private lateinit var flashFab: FloatingActionButton
private var currentProfile = prefs.profile
private var shouldStoreSettings = true
private var formatsToRead = setOf<BarcodeFormat>()
private var frameMetrics = FrameMetrics()
private var decoding = true
@@ -74,6 +82,7 @@ class CameraActivity : AppCompatActivity() {
private var frontFacing = false
private var bulkMode = prefs.bulkMode
private var restrictFormat: String? = null
private var searchTerm: Regex? = null
private var ignoreNext: String? = null
private var fallbackBuffer: IntArray? = null
private var requestCameraPermission = true
@@ -99,7 +108,7 @@ class CameraActivity : AppCompatActivity() {
) {
when (requestCode) {
PICK_FILE_RESULT_CODE -> {
if (resultCode == Activity.RESULT_OK && resultData != null) {
if (resultCode == RESULT_OK && resultData != null) {
val pick = Intent(this, PickActivity::class.java)
pick.action = Intent.ACTION_VIEW
pick.setDataAndType(resultData.data, "image/*")
@@ -122,8 +131,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
@@ -135,7 +143,7 @@ class CameraActivity : AppCompatActivity() {
initDetectorView()
if (intent?.action == Intent.ACTION_SEND &&
intent.type == "text/plain"
intent.type?.startsWith("text/") == true
) {
handleSendText(intent)
}
@@ -144,20 +152,20 @@ class CameraActivity : AppCompatActivity() {
override fun onDestroy() {
super.onDestroy()
fallbackBuffer = null
saveZoom()
detectorView.saveCropHandlePos()
releaseToneGenerators()
}
override fun onResume() {
super.onResume()
System.gc()
updateHints()
if (prefs.bulkMode && bulkMode != prefs.bulkMode) {
bulkMode = prefs.bulkMode
invalidateOptionsMenu()
ignoreNext = null
if (firstStart()) {
startActivity(Intent(this, WelcomeActivity::class.java))
return
}
loadPreferences()
if (refreshIfProfileChanged()) {
return
}
System.gc()
setReturnTarget(intent)
// Avoid asking multiple times when the user has denied access
// for this session. Otherwise ActivityCompat.requestPermissions()
@@ -168,18 +176,66 @@ class CameraActivity : AppCompatActivity() {
requestCameraPermission = false
}
private fun updateHints() {
private fun Context.firstStart(): Boolean {
val welcomeShownName = "welcome_shown"
if (prefs.defaultPreferences.getBoolean(welcomeShownName, false)) {
return false
}
prefs.defaultPreferences.edit().putBoolean(welcomeShownName, true).apply()
val packageInfo = packageManager.getPackageInfo(packageName, 0)
val installedSince = System.currentTimeMillis() -
packageInfo.firstInstallTime
return installedSince < 86400000
}
private fun refreshIfProfileChanged(): Boolean {
if (currentProfile == prefs.profile) {
return false
}
shouldStoreSettings = false
zoomBar.max = 0 // Reset zoom when there are no preferences yet.
currentProfile = prefs.profile
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
recreate()
} else {
finish()
startActivity(Intent(this, CameraActivity::class.java))
}
return true
}
private fun loadPreferences() {
detectorView.updateCropHandlePos()
updateHintsAndTitle()
if (prefs.bulkMode && bulkMode != prefs.bulkMode) {
bulkMode = prefs.bulkMode
invalidateOptionsMenu()
ignoreNext = null
}
}
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_for,
listOfNotNull(
searchTerm?.let { "\"$it\"" },
restrictFormat?.prettifyFormatName(),
).joinToString(" ")
)
} else {
setTitle(R.string.scan_code)
}
}
private fun setReturnTarget(intent: Intent?) {
@@ -188,7 +244,7 @@ class CameraActivity : AppCompatActivity() {
returnResult = true
}
intent?.dataString?.isReturnUrl() == true -> {
intent?.dataString?.let(::isScanDeeplink) == true -> {
finishAfterShowingResult = true
returnUrlTemplate = intent.data?.getQueryParameter("ret")
}
@@ -211,12 +267,21 @@ class CameraActivity : AppCompatActivity() {
override fun onPause() {
super.onPause()
closeCamera()
if (shouldStoreSettings) {
storeSettings()
}
shouldStoreSettings = true
}
private fun closeCamera() {
cameraView.close()
}
private fun storeSettings() {
storeZoomBarSettings()
detectorView.storeCropHandlePos()
}
override fun onRestoreInstanceState(savedState: Bundle) {
super.onRestoreInstanceState(savedState)
zoomBar.max = savedState.getInt(ZOOM_MAX)
@@ -224,6 +289,7 @@ class CameraActivity : AppCompatActivity() {
frontFacing = savedState.getBoolean(FRONT_FACING)
bulkMode = savedState.getBoolean(BULK_MODE)
restrictFormat = savedState.getString(RESTRICT_FORMAT)
searchTerm = savedState.getString(SEARCH_TERM)?.toRegex()
}
override fun onSaveInstanceState(outState: Bundle) {
@@ -232,6 +298,7 @@ class CameraActivity : AppCompatActivity() {
outState.putBoolean(FRONT_FACING, frontFacing)
outState.putBoolean(BULK_MODE, bulkMode)
outState.putString(RESTRICT_FORMAT, restrictFormat)
outState.putString(SEARCH_TERM, searchTerm?.toString())
super.onSaveInstanceState(outState)
}
@@ -247,6 +314,10 @@ class CameraActivity : AppCompatActivity() {
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.activity_camera, menu)
menu.findItem(R.id.bulk_mode).isChecked = bulkMode
menu.findItem(R.id.profile).title = getString(
R.string.current_profile,
prefs.profile ?: getString(R.string.profile_default)
)
return true
}
@@ -292,6 +363,11 @@ class CameraActivity : AppCompatActivity() {
true
}
R.id.find_code -> {
askForCode()
true
}
R.id.preferences -> {
startActivity(MainActivity.getPreferencesIntent(this))
true
@@ -302,6 +378,11 @@ class CameraActivity : AppCompatActivity() {
true
}
R.id.profile -> {
pickProfile()
true
}
else -> super.onOptionsItemSelected(item)
}
}
@@ -331,12 +412,38 @@ 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.toString())
}
AlertDialog.Builder(this)
.setView(view)
.setPositiveButton(android.R.string.ok) { _, _ ->
val term = editText.text.toString().trim()
if (!term.isEmpty()) {
searchTerm = term.toRegex()
} else {
searchTerm = null
}
ignoreNext = null
updateTitle()
}
.setNegativeButton(android.R.string.cancel) { _, _ ->
// do not change anything on cancel
}
.show()
}
private fun openReadme() {
val intent = Intent(
Intent.ACTION_VIEW,
@@ -345,11 +452,57 @@ class CameraActivity : AppCompatActivity() {
execShareIntent(intent)
}
private fun pickProfile() {
val profiles = arrayOf(
getString(R.string.profile_default),
) + prefs.profiles
AlertDialog.Builder(this)
.setTitle(R.string.profile)
.setItems(profiles) { _, which ->
val newProfile = when (which) {
0 -> null
else -> profiles[which]
}
if (currentProfile != newProfile) {
storeSettings()
prefs.load(this, newProfile)
loadPreferences()
refreshIfProfileChanged()
}
}
.show()
}
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()
}
}
}
@@ -376,7 +529,8 @@ class CameraActivity : AppCompatActivity() {
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)
@@ -406,11 +560,12 @@ class CameraActivity : AppCompatActivity() {
parameters: Camera.Parameters
) {
zoomBar.visibility = if (parameters.isZoomSupported) {
restoreZoomBarSettings()
val max = parameters.maxZoom
if (zoomBar.max != max) {
zoomBar.max = max
zoomBar.progress = max / 10
saveZoom()
storeZoomBarSettings()
}
parameters.zoom = zoomBar.progress
View.VISIBLE
@@ -449,36 +604,53 @@ class CameraActivity : AppCompatActivity() {
tryRotate = prefs.autoRotate,
tryInvert = true,
tryDownscale = true,
maxNumberOfSymbols = 1
maxNumberOfSymbols = 1,
textMode = TextMode.PLAIN
)
var useLocalAverage = false
camera.setPreviewCallback { frameData, _ ->
if (decoding) {
useLocalAverage = useLocalAverage xor true
ZxingCpp.readByteArray(
frameData,
frameMetrics.width,
frameRoi.left, frameRoi.top,
frameRoi.width(), frameRoi.height(),
frameMetrics.orientation,
options.apply {
// By default, ZXing uses LOCAL_AVERAGE, but
// this does not work well with inverted
// barcodes on low-contrast backgrounds.
binarizer = if (useLocalAverage) {
Binarizer.LOCAL_AVERAGE
} else {
Binarizer.GLOBAL_HISTOGRAM
}
formats = formatsToRead
}
)?.let { results ->
val result = results.first()
if (result.text != ignoreNext) {
postResult(result)
decoding = false
if (!decoding ||
frameData == null ||
frameData.isEmpty()
) {
return@setPreviewCallback
}
useLocalAverage = useLocalAverage xor true
ZxingCpp.readByteArray(
frameData,
frameMetrics.width,
frameRoi.left, frameRoi.top,
frameRoi.width(), frameRoi.height(),
frameMetrics.orientation,
options.apply {
// By default, ZXing uses LOCAL_AVERAGE, but
// this does not work well with inverted
// barcodes on low-contrast backgrounds.
binarizer = if (useLocalAverage) {
Binarizer.LOCAL_AVERAGE
} else {
Binarizer.GLOBAL_HISTOGRAM
}
formats = formatsToRead
}
)?.let { results ->
val result = results.first()
val text = result.text
if (text == ignoreNext) {
return@let
}
val term = searchTerm
if (term != null &&
!text.matches(term) &&
!text.contains(term)
) {
ignoreNext = text
errorFeedback()
toast(R.string.does_not_match_search_term)
return@let
}
postResult(result)
decoding = false
}
}
}
@@ -507,7 +679,6 @@ class CameraActivity : AppCompatActivity() {
override fun onStopTrackingTouch(seekBar: SeekBar) {}
})
restoreZoom()
}
@Suppress("DEPRECATION")
@@ -516,19 +687,19 @@ class CameraActivity : AppCompatActivity() {
val params = parameters
params.zoom = zoom
parameters = params
} catch (e: RuntimeException) {
} catch (_: RuntimeException) {
// Ignore. There's nothing we can do.
}
}
private fun saveZoom() {
private fun storeZoomBarSettings() {
val editor = prefs.preferences.edit()
editor.putInt(ZOOM_MAX, zoomBar.max)
editor.putInt(ZOOM_LEVEL, zoomBar.progress)
editor.apply()
}
private fun restoreZoom() {
private fun restoreZoomBarSettings() {
zoomBar.max = prefs.preferences.getInt(ZOOM_MAX, zoomBar.max)
zoomBar.progress = prefs.preferences.getInt(
ZOOM_LEVEL,
@@ -545,12 +716,21 @@ class CameraActivity : AppCompatActivity() {
updateFrameRoiAndMappingMatrix()
}
detectorView.setPaddingFromWindowInsets()
detectorView.restoreCropHandlePos()
detectorView.cropHandleName = "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
@@ -608,7 +788,7 @@ class CameraActivity : AppCompatActivity() {
}
when {
returnResult -> {
setResult(Activity.RESULT_OK, getReturnIntent(result))
setResult(RESULT_OK, getReturnIntent(result))
finish()
}
@@ -651,6 +831,7 @@ class CameraActivity : AppCompatActivity() {
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"
}
}
@@ -672,7 +853,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
}
@@ -714,6 +895,9 @@ fun Activity.showResult(
)
}
}
if (runAutomatedActions(scan)) {
return
}
if (!bulkMode) {
startActivity(
MainActivity.getDecodeIntent(this, scan)
@@ -731,12 +915,6 @@ 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())
@@ -3,11 +3,11 @@ package de.markusfisch.android.binaryeye.activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.app.PERMISSION_LOCATION
import de.markusfisch.android.binaryeye.app.PERMISSION_WRITE
@@ -20,9 +20,9 @@ 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(
@@ -31,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()
@@ -59,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 {
@@ -85,6 +83,17 @@ 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)
)
@@ -117,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
@@ -138,11 +148,15 @@ 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)
private fun Intent.getScanExtra(name: String): Scan? = try {
if (
Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU
) {
@Suppress("DEPRECATION")
getParcelableExtra(name)
} else {
getParcelableExtra(name, Scan::class.java)
}
} catch (_: Throwable) {
null
}
@@ -10,7 +10,6 @@ import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import android.view.Menu
import android.view.MenuItem
import de.markusfisch.android.binaryeye.R
@@ -22,8 +21,7 @@ import de.markusfisch.android.binaryeye.graphics.loadImageUri
import de.markusfisch.android.binaryeye.graphics.mapPosition
import de.markusfisch.android.binaryeye.media.releaseToneGenerators
import de.markusfisch.android.binaryeye.view.colorSystemAndToolBars
import de.markusfisch.android.binaryeye.view.initSystemBars
import de.markusfisch.android.binaryeye.view.recordToolbarHeight
import de.markusfisch.android.binaryeye.view.initBars
import de.markusfisch.android.binaryeye.view.scanFeedback
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
import de.markusfisch.android.binaryeye.widget.CropImageView
@@ -55,10 +53,9 @@ class PickActivity : AppCompatActivity() {
formats = prefs.barcodeFormats.toFormatSet()
)
private lateinit var cropImageView: CropImageView
private lateinit var detectorView: DetectorView
private lateinit var freeRotationItem: MenuItem
private var cropImageView: CropImageView? = null
private var detectorView: DetectorView? = null
private var freeRotationItem: MenuItem? = null
private var result: Result? = null
override fun attachBaseContext(base: Context?) {
@@ -74,11 +71,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)
}
@@ -87,23 +80,34 @@ class PickActivity : AppCompatActivity() {
if (bitmap == null) {
applicationContext.toast(R.string.error_no_content)
finish()
// This is why cropImageView and detectorView aren't lateinit.
// If they were, a kotlin.UninitializedPropertyAccessException
// would happen here.
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()
cropHandleName = "picker_crop_handle"
}
detectorView.setPaddingFromWindowInsets()
detectorView.restoreCropHandlePos()
findViewById(R.id.scan).setOnClickListener {
showResult()
@@ -125,17 +129,19 @@ class PickActivity : AppCompatActivity() {
}
private fun scanWithinBounds(bitmap: Bitmap) {
val dv = detectorView ?: return
val civ = cropImageView ?: return
val viewRoi = Rect(
if (detectorView.roi.width() < 1) {
cropImageView.getBoundsRect()
if (dv.roi.width() < 1) {
civ.getBoundsRect()
} else {
detectorView.roi
dv.roi
}
)
val mappedRect = RectF(cropImageView.mappedRect)
val imageRotation = cropImageView.imageRotation
val pivotX = cropImageView.pivotX
val pivotY = cropImageView.pivotY
val mappedRect = RectF(civ.mappedRect)
val imageRotation = civ.imageRotation
val pivotX = civ.pivotX
val pivotY = civ.pivotY
scope.launch {
val cropped = bitmap.crop(
getNormalizedRoi(mappedRect, viewRoi),
@@ -166,10 +172,10 @@ class PickActivity : AppCompatActivity() {
}
result = it
scanFeedback()
detectorView.update(
dv.update(
matrix.mapPosition(
it.position,
detectorView.coordinates
dv.coordinates
)
)
}
@@ -199,7 +205,7 @@ class PickActivity : AppCompatActivity() {
override fun onDestroy() {
super.onDestroy()
detectorView.saveCropHandlePos()
detectorView?.storeCropHandlePos()
parentJob.cancel()
releaseToneGenerators()
}
@@ -221,8 +227,8 @@ class PickActivity : AppCompatActivity() {
R.id.toggle_free -> {
prefs.freeRotation = prefs.freeRotation xor true
cropImageView.freeRotation = prefs.freeRotation
freeRotationItem.updateFreeRotationIcon()
cropImageView?.freeRotation = prefs.freeRotation
freeRotationItem?.updateFreeRotationIcon()
true
}
@@ -241,7 +247,8 @@ class PickActivity : AppCompatActivity() {
}
private fun rotateClockwise() {
cropImageView.imageRotation = cropImageView.imageRotation + 90 % 360
val civ = cropImageView ?: return
civ.imageRotation = (civ.imageRotation + 90) % 360
}
private fun loadSentImage(intent: Intent): Bitmap? {
@@ -0,0 +1,32 @@
package de.markusfisch.android.binaryeye.activity
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.app.prefs
class WelcomeActivity : AppCompatActivity() {
override fun onCreate(state: Bundle?) {
super.onCreate(state)
setContentView(R.layout.activity_welcome)
findViewById(R.id.expert).setOnClickListener {
finish()
}
findViewById(R.id.simple).setOnClickListener {
prefs.apply {
showCropHandle = false
zoomBySwiping = false
beep = true
copyImmediately = true
openImmediately = true
showMetaData = false
showHexDump = false
defaultSearchUrl = "https://www.google.com/search?q="
openWithUrl = defaultSearchUrl
expandEscapeSequences = false
brightenScreen = true
}
finish()
}
}
}
@@ -20,7 +20,7 @@ class ScansAdapter(context: Context, cursor: Cursor) :
private val idIndex = cursor.getColumnIndex(Database.SCANS_ID)
private val timeIndex = cursor.getColumnIndex(Database.SCANS_DATETIME)
private val nameIndex = cursor.getColumnIndex(Database.SCANS_NAME)
private val contentIndex = cursor.getColumnIndex(Database.SCANS_CONTENT)
private val textIndex = cursor.getColumnIndex(Database.SCANS_TEXT)
private val formatIndex = cursor.getColumnIndex(Database.SCANS_FORMAT)
private val selections = mutableMapOf<Long, Int>()
private val selectedColor = ContextCompat.getColor(
@@ -74,7 +74,7 @@ class ScansAdapter(context: Context, cursor: Cursor) :
fun getContent(
position: Int
) = (getItem(position) as Cursor?)?.getString(contentIndex)
) = (getItem(position) as Cursor?)?.getString(textIndex)
override fun newView(
context: Context,
@@ -92,7 +92,7 @@ class ScansAdapter(context: Context, cursor: Cursor) :
getViewHolder(view).apply {
timeView.text = formatDateTime(cursor.getString(timeIndex))
val name = cursor.getString(nameIndex)
val content = cursor.getString(contentIndex)
val text = cursor.getString(textIndex)
var icon = 0
contentView.text = when {
name?.isNotEmpty() == true -> {
@@ -100,16 +100,16 @@ class ScansAdapter(context: Context, cursor: Cursor) :
name
}
content.isNullOrEmpty() -> context.getString(
text.isNullOrEmpty() -> context.getString(
R.string.binary_data
)
else -> content
else -> text
}
contentView.setCompoundDrawablesWithIntrinsicBounds(
icon, 0, 0, 0
)
formatView.text = prettifyFormatName(cursor.getString(formatIndex))
formatView.text = cursor.getString(formatIndex).prettifyFormatName()
}
val selected = selections[cursor.getLong(idIndex)] != null
view.post {
@@ -139,7 +139,7 @@ class ScansAdapter(context: Context, cursor: Cursor) :
)
}
fun prettifyFormatName(format: String) = format.replace("_", " ")
fun String.prettifyFormatName() = replace("_", " ")
private fun formatDateTime(rfc: String): String {
try {
@@ -149,7 +149,7 @@ private fun formatDateTime(rfc: String): String {
DateFormat.SHORT
).format(it)
}
} catch (e: ParseException) {
} catch (_: ParseException) {
// Ignore and fall through.
}
return rfc
@@ -0,0 +1,105 @@
package de.markusfisch.android.binaryeye.automation
import de.markusfisch.android.binaryeye.database.Scan
import de.markusfisch.android.binaryeye.net.urlEncode
import org.json.JSONArray
import org.json.JSONObject
import java.util.Locale
data class AutomatedAction(
val pattern: String,
val type: Type,
val urlTemplate: String? = null,
val intentUriTemplate: String? = null
) {
enum class Type {
Intent, CustomIntent
}
fun matches(content: String): Boolean = try {
Regex(pattern).matches(content)
} catch (_: Exception) {
false
}
fun toJson(): JSONObject = JSONObject().apply {
put(KEY_PATTERN, pattern)
put(KEY_TYPE, type.name.lowercase(Locale.ROOT))
put(KEY_URL_TEMPLATE, urlTemplate ?: "")
put(KEY_INTENT_URI_TEMPLATE, intentUriTemplate ?: "")
}
companion object {
private const val KEY_PATTERN = "pattern"
private const val KEY_TYPE = "type"
private const val KEY_URL_TEMPLATE = "url_template"
private const val KEY_INTENT_URI_TEMPLATE = "intent_uri_template"
fun fromJson(obj: JSONObject): AutomatedAction? {
val pattern = obj.optString(KEY_PATTERN, "").trim()
if (pattern.isEmpty()) {
return null
}
val type = when (obj.optString(KEY_TYPE, "")) {
"custom_intent" -> Type.CustomIntent
"intent" -> Type.Intent
"html" -> return null
else -> Type.Intent
}
val urlTemplate = obj.optString(KEY_URL_TEMPLATE, "").trim()
val intentUriTemplate = obj.optString(
KEY_INTENT_URI_TEMPLATE,
""
).trim()
return AutomatedAction(
pattern = pattern,
type = type,
urlTemplate = urlTemplate.ifEmpty { null },
intentUriTemplate = intentUriTemplate.ifEmpty { null }
)
}
fun fromJsonArray(json: String): MutableList<AutomatedAction> {
val actions = ArrayList<AutomatedAction>()
val array = try {
JSONArray(json)
} catch (_: Exception) {
JSONArray()
}
for (i in 0 until array.length()) {
val obj = array.optJSONObject(i) ?: continue
fromJson(obj)?.let { actions.add(it) }
}
return actions
}
fun toJsonArray(actions: List<AutomatedAction>): String {
val array = JSONArray()
actions.forEach { array.put(it.toJson()) }
return array.toString()
}
}
}
fun String.buildUrl(scan: Scan): String {
return buildTemplate(scan, encodeResult = true)
}
fun String.buildTemplate(scan: Scan, encodeResult: Boolean): String {
val text = scan.text
val raw = scan.raw ?: text.toByteArray()
val result = if (encodeResult) {
text.urlEncode()
} else {
text
}
val format = if (encodeResult) {
scan.format.name.urlEncode()
} else {
scan.format.name
}
return this
.replace("{RESULT}", result)
.replace("{RESULT_BYTES}", raw.toHexString())
.replace("{FORMAT}", format)
}
@@ -0,0 +1,59 @@
package de.markusfisch.android.binaryeye.automation
import android.content.Context
import android.content.Intent
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.app.prefs
import de.markusfisch.android.binaryeye.content.execShareIntent
import de.markusfisch.android.binaryeye.content.openUrl
import de.markusfisch.android.binaryeye.database.Scan
import de.markusfisch.android.binaryeye.widget.toast
fun Context.runAutomatedActions(scan: Scan): Boolean {
val content = scan.text
if (content.isEmpty()) {
return false
}
val actions = prefs.automatedActions
if (actions.isEmpty()) {
return false
}
for (action in actions) {
if (action.matches(content)) {
return when (action.type) {
AutomatedAction.Type.Intent -> {
val url = action.urlTemplate?.buildUrl(scan)
if (url.isNullOrEmpty()) {
toast(R.string.automated_action_invalid_url)
false
} else {
openUrl(url)
}
}
AutomatedAction.Type.CustomIntent ->
execCustomIntent(action, scan)
}
}
}
return false
}
private fun Context.execCustomIntent(
action: AutomatedAction,
scan: Scan
): Boolean {
val template = action.intentUriTemplate
if (template.isNullOrEmpty()) {
toast(R.string.automated_action_intent_uri_required)
return false
}
val intentUri = template.buildTemplate(scan, encodeResult = false)
val intent = try {
Intent.parseUri(intentUri, Intent.URI_INTENT_SCHEME)
} catch (_: Exception) {
toast(R.string.automated_action_intent_uri_invalid)
return false
}
return execShareIntent(intent)
}
@@ -21,10 +21,10 @@ fun Scan.sendBluetoothAsync(
val connected = if (isConnected) {
true
} else {
connect(host,true)
connect(host, true)
}
val sent = if (connected) {
send(content,host,true)
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
@@ -71,39 +70,41 @@ private fun connect(deviceName: String, onceMore: Boolean): Boolean = try {
writer = socket?.outputStream?.writer()
isConnected = true
true
} catch (e: Exception) {
if (onceMore)
} catch (_: Exception) {
if (onceMore) {
connect(deviceName, false)
else
} else {
close()
}
false
}
private fun send(message: String, host: String, onceMore: Boolean): 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()
if (connect(host, false))
send(message, host, false)
else
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.
}
@@ -113,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
}
@@ -1,3 +0,0 @@
package de.markusfisch.android.binaryeye.content
fun ByteArray.toHexString() = joinToString("") { "%02X".format(it) }
@@ -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
}
}
@@ -33,12 +33,20 @@ fun Context.startIntent(intent: Intent): Boolean = try {
// necessary.
startActivity(intent)
true
} catch (e: ActivityNotFoundException) {
} catch (_: ActivityNotFoundException) {
false
}
fun Context.openUrl(url: String, silent: Boolean = false): Boolean {
val intent = Intent(Intent.ACTION_VIEW, url.parseAndNormalizeUri())
return openUri(url.parseAndNormalizeUri(), silent)
}
fun Context.openUri(uri: Uri, silent: Boolean = false): Boolean {
val intent = Intent(Intent.ACTION_VIEW, uri).apply {
if (uri.scheme == "content") {
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
}
return if (silent) {
startIntent(intent)
} else {
@@ -91,7 +99,7 @@ fun <T> Context.shareAsFile(content: T, fileName: String) {
FileOutputStream(file).use {
it.write(bytes)
}
} catch (e: IOException) {
} catch (_: IOException) {
toast(R.string.error_saving_file)
return
}
@@ -2,7 +2,6 @@ package de.markusfisch.android.binaryeye.database
import android.content.Context
import android.database.Cursor
import de.markusfisch.android.binaryeye.content.toHexString
import de.markusfisch.android.binaryeye.io.writeExternalFile
import java.io.ByteArrayOutputStream
import java.io.OutputStream
@@ -33,7 +32,7 @@ private fun exportCsv(
Database.SCANS_DATETIME,
Database.SCANS_FORMAT,
Database.SCANS_NAME,
Database.SCANS_CONTENT,
Database.SCANS_TEXT,
Database.SCANS_ERROR_CORRECTION_LEVEL,
Database.SCANS_VERSION,
Database.SCANS_SEQUENCE_SIZE,
@@ -47,7 +46,7 @@ private fun exportCsv(
val indices = columns.map {
cursor.getColumnIndex(it)
}
val contentIndex = cursor.getColumnIndex(Database.SCANS_CONTENT)
val contentIndex = cursor.getColumnIndex(Database.SCANS_TEXT)
val rawIndex = cursor.getColumnIndex(Database.SCANS_RAW)
outputStream.write(
columns.joinToString(
@@ -1,6 +1,7 @@
package de.markusfisch.android.binaryeye.database
import android.database.Cursor
import de.markusfisch.android.zxingcpp.ZxingCpp
// Overwrite Kotlin's ".use" function for Cursor because Cursor cannot
// be cast to Closeable below API level 16. This should be removed when
@@ -30,3 +31,18 @@ fun Cursor.getBlob(name: String): ByteArray? {
val idx = getColumnIndex(name)
return if (idx < 0) null else getBlob(idx)
}
fun Cursor.getBitMatrix(
widthName: String,
heightName: String,
dataName: String
): ZxingCpp.BitMatrix? {
val width = getInt(widthName)
val height = getInt(heightName)
val data = getBlob(dataName)
return if (width > 0 && height > 0 && data != null) {
ZxingCpp.BitMatrix(width, height, data)
} else {
null
}
}
@@ -16,12 +16,12 @@ class Database {
db = OpenHelper(context).writableDatabase
}
fun getScans(query: String? = null): Cursor? = db.rawQuery(
fun getScans(query: String? = null): Cursor = db.rawQuery(
"""SELECT
$SCANS_ID,
$SCANS_DATETIME,
$SCANS_NAME,
$SCANS_CONTENT,
$SCANS_TEXT,
$SCANS_FORMAT
FROM $SCANS
${getWhereClause(query)}
@@ -29,12 +29,12 @@ class Database {
""".trimMargin(), getWhereArguments(query)
)
fun getScansDetailed(query: String? = null): Cursor? = db.rawQuery(
fun getScansDetailed(query: String? = null): Cursor = db.rawQuery(
"""SELECT
$SCANS_ID,
$SCANS_DATETIME,
$SCANS_NAME,
$SCANS_CONTENT,
$SCANS_TEXT,
$SCANS_RAW,
$SCANS_FORMAT,
$SCANS_ERROR_CORRECTION_LEVEL,
@@ -56,7 +56,7 @@ class Database {
query: String?,
prefix: String = "WHERE"
) = if (query?.isNotEmpty() == true) {
"""$prefix $SCANS_CONTENT LIKE ?
"""$prefix $SCANS_TEXT LIKE ?
OR $SCANS_NAME LIKE ?"""
} else {
""
@@ -71,12 +71,12 @@ class Database {
null
}
fun getScansDetailed(ids: LongArray): Cursor? = db.rawQuery(
fun getScansDetailed(ids: LongArray): Cursor = db.rawQuery(
"""SELECT
$SCANS_ID,
$SCANS_DATETIME,
$SCANS_NAME,
$SCANS_CONTENT,
$SCANS_TEXT,
$SCANS_RAW,
$SCANS_FORMAT,
$SCANS_ERROR_CORRECTION_LEVEL,
@@ -89,9 +89,9 @@ class Database {
$SCANS_GTIN_PRICE,
$SCANS_GTIN_ISSUE_NUMBER
FROM $SCANS
WHERE $SCANS_ID IN (?)
WHERE $SCANS_ID IN (${ids.joinToString(",")})
ORDER BY $SCANS_DATETIME DESC
""".trimMargin(), arrayOf(ids.joinToString(","))
""".trimMargin(), null
)
fun getScan(id: Long): Scan? = db.rawQuery(
@@ -99,11 +99,15 @@ class Database {
$SCANS_ID,
$SCANS_DATETIME,
$SCANS_NAME,
$SCANS_CONTENT,
$SCANS_TEXT,
$SCANS_RAW,
$SCANS_FORMAT,
$SCANS_ERROR_CORRECTION_LEVEL,
$SCANS_VERSION,
$SCANS_DATA_MASK,
$SCANS_SYMBOL_WIDTH,
$SCANS_SYMBOL_HEIGHT,
$SCANS_SYMBOL_DATA,
$SCANS_SEQUENCE_SIZE,
$SCANS_SEQUENCE_INDEX,
$SCANS_SEQUENCE_ID,
@@ -114,14 +118,20 @@ class Database {
FROM $SCANS
WHERE $SCANS_ID = ?
""".trimMargin(), arrayOf("$id")
)?.use {
).use {
if (it.moveToFirst()) {
Scan(
it.getString(SCANS_CONTENT),
it.getString(SCANS_TEXT),
it.getBlob(SCANS_RAW),
BarcodeFormat.valueOf(it.getString(SCANS_FORMAT)),
it.getString(SCANS_ERROR_CORRECTION_LEVEL),
it.getString(SCANS_VERSION),
it.getInt(SCANS_DATA_MASK),
it.getBitMatrix(
SCANS_SYMBOL_WIDTH,
SCANS_SYMBOL_HEIGHT,
SCANS_SYMBOL_DATA
),
it.getInt(SCANS_SEQUENCE_SIZE),
it.getInt(SCANS_SEQUENCE_INDEX),
it.getString(SCANS_SEQUENCE_ID),
@@ -142,7 +152,7 @@ class Database {
when (prefs.ignoreDuplicates()) {
Preferences.Companion.IgnoreDuplicates.Consecutive -> {
val id = getIdOfLastScan(
scan.content,
scan.text,
scan.raw,
scan.format
)
@@ -152,7 +162,7 @@ class Database {
}
Preferences.Companion.IgnoreDuplicates.Any -> {
val id = getIdOfScanByContent(scan.content, scan.format)
val id = getIdOfScanByContent(scan.text, scan.format)
if (id > 0L) {
return id
}
@@ -165,7 +175,7 @@ class Database {
null,
ContentValues().apply {
put(SCANS_DATETIME, scan.dateTime)
put(SCANS_CONTENT, scan.content)
put(SCANS_TEXT, scan.text)
if (scan.raw != null) {
put(SCANS_RAW, scan.raw)
}
@@ -174,6 +184,12 @@ class Database {
put(SCANS_ERROR_CORRECTION_LEVEL, it)
}
put(SCANS_VERSION, scan.version)
put(SCANS_DATA_MASK, scan.dataMask)
if (scan.symbol != null) {
put(SCANS_SYMBOL_WIDTH, scan.symbol.width)
put(SCANS_SYMBOL_HEIGHT, scan.symbol.height)
put(SCANS_SYMBOL_DATA, scan.symbol.data)
}
put(SCANS_SEQUENCE_SIZE, scan.sequenceSize)
put(SCANS_SEQUENCE_INDEX, scan.sequenceIndex)
put(SCANS_SEQUENCE_ID, scan.sequenceId)
@@ -192,17 +208,17 @@ class Database {
): Long = db.rawQuery(
"""SELECT
$SCANS_ID,
$SCANS_CONTENT,
$SCANS_TEXT,
$SCANS_RAW,
$SCANS_FORMAT
FROM $SCANS
ORDER BY $SCANS_ID DESC
LIMIT 1
""".trimMargin(), null
)?.use {
).use {
if (it.count > 0 &&
it.moveToFirst() &&
it.getString(SCANS_CONTENT) == content &&
it.getString(SCANS_TEXT) == content &&
(raw == null || it.getBlob(SCANS_RAW)
?.contentEquals(raw) == true) &&
it.getString(SCANS_FORMAT) == format.name
@@ -211,26 +227,26 @@ class Database {
} else {
0L
}
} ?: 0L
}
fun getIdOfScanByContent(
private fun getIdOfScanByContent(
content: String,
format: BarcodeFormat
): Long = db.rawQuery(
"""SELECT
$SCANS_ID
FROM $SCANS
WHERE $SCANS_CONTENT = ?
WHERE $SCANS_TEXT = ?
AND $SCANS_FORMAT = ?
LIMIT 1
""".trimMargin(), arrayOf(content, format.name)
)?.use {
).use {
if (it.moveToFirst()) {
it.getLong(SCANS_ID)
} else {
0L
}
} ?: 0L
}
fun removeScan(id: Long) {
db.delete(SCANS, "$SCANS_ID = ?", arrayOf("$id"))
@@ -247,7 +263,7 @@ class Database {
}
private class OpenHelper(context: Context) :
SQLiteOpenHelper(context, FILE_NAME, null, 6) {
SQLiteOpenHelper(context, FILE_NAME, null, 8) {
override fun onCreate(db: SQLiteDatabase) {
db.createScans()
}
@@ -272,6 +288,12 @@ class Database {
if (oldVersion < 6) {
db.migrateToVersionString()
}
if (oldVersion < 7) {
db.addDataMaskColumn()
}
if (oldVersion < 8) {
db.addSymbolColumns()
}
}
}
@@ -281,12 +303,16 @@ class Database {
const val SCANS_ID = "_id"
const val SCANS_DATETIME = "_datetime"
const val SCANS_NAME = "name"
const val SCANS_CONTENT = "content"
const val SCANS_TEXT = "content"
const val SCANS_RAW = "raw"
const val SCANS_FORMAT = "format"
const val SCANS_ERROR_CORRECTION_LEVEL = "error_correction_level"
const val SCANS_VERSION_NUMBER = "version_number"
const val SCANS_VERSION = "version"
const val SCANS_DATA_MASK = "data_mask"
const val SCANS_SYMBOL_WIDTH = "symbol_width"
const val SCANS_SYMBOL_HEIGHT = "symbol_height"
const val SCANS_SYMBOL_DATA = "symbol_data"
const val SCANS_ISSUE_NUMBER = "issue_number"
const val SCANS_ORIENTATION = "orientation"
const val SCANS_OTHER_META_DATA = "other_meta_data"
@@ -309,11 +335,15 @@ class Database {
$SCANS_ID INTEGER PRIMARY KEY AUTOINCREMENT,
$SCANS_DATETIME DATETIME NOT NULL,
$SCANS_NAME TEXT,
$SCANS_CONTENT TEXT NOT NULL,
$SCANS_TEXT TEXT NOT NULL,
$SCANS_RAW BLOB,
$SCANS_FORMAT TEXT NOT NULL,
$SCANS_ERROR_CORRECTION_LEVEL TEXT,
$SCANS_VERSION TEXT,
$SCANS_DATA_MASK INTEGER,
$SCANS_SYMBOL_WIDTH INTEGER,
$SCANS_SYMBOL_HEIGHT INTEGER,
$SCANS_SYMBOL_DATA BLOB,
$SCANS_SEQUENCE_SIZE INTEGER,
$SCANS_SEQUENCE_INDEX INTEGER,
$SCANS_SEQUENCE_ID TEXT,
@@ -341,7 +371,7 @@ class Database {
}
private fun SQLiteDatabase.addNameColumn() {
execSQL("ALTER TABLE $SCANS ADD COLUMN $SCANS_NAME TEXT".trimMargin())
execSQL("ALTER TABLE $SCANS ADD COLUMN $SCANS_NAME TEXT")
}
private fun SQLiteDatabase.migrateToZxingCpp() {
@@ -365,5 +395,16 @@ class Database {
execSQL("ALTER TABLE $SCANS ADD $SCANS_VERSION TEXT")
execSQL("UPDATE $SCANS SET $SCANS_VERSION = $SCANS_VERSION_NUMBER")
}
private fun SQLiteDatabase.addDataMaskColumn() {
execSQL("ALTER TABLE $SCANS ADD $SCANS_DATA_MASK INTEGER")
execSQL("UPDATE $SCANS SET $SCANS_DATA_MASK = -1")
}
private fun SQLiteDatabase.addSymbolColumns() {
execSQL("ALTER TABLE $SCANS ADD $SCANS_SYMBOL_WIDTH INTEGER")
execSQL("ALTER TABLE $SCANS ADD $SCANS_SYMBOL_HEIGHT INTEGER")
execSQL("ALTER TABLE $SCANS ADD $SCANS_SYMBOL_DATA BLOB")
}
}
}
@@ -2,7 +2,6 @@ package de.markusfisch.android.binaryeye.database
import android.content.Context
import android.database.Cursor
import de.markusfisch.android.binaryeye.content.toHexString
import de.markusfisch.android.binaryeye.io.writeExternalFile
import org.json.JSONArray
import org.json.JSONException
@@ -25,7 +24,7 @@ fun Cursor.exportJson(): String? {
Database.SCANS_DATETIME,
Database.SCANS_FORMAT,
Database.SCANS_NAME,
Database.SCANS_CONTENT,
Database.SCANS_TEXT,
Database.SCANS_ERROR_CORRECTION_LEVEL,
Database.SCANS_VERSION,
Database.SCANS_SEQUENCE_SIZE,
@@ -39,7 +38,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 {
@@ -70,7 +69,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.Result
import java.util.Locale
data class Scan(
val content: String,
val text: String,
val raw: ByteArray?,
val format: BarcodeFormat,
val errorCorrectionLevel: String? = null,
val version: String? = null,
val dataMask: Int = -1,
val symbol: BitMatrix? = null,
val sequenceSize: Int = -1,
val sequenceIndex: Int = -1,
val sequenceId: String = "",
@@ -41,12 +43,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 &&
@@ -61,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()
@@ -77,11 +82,13 @@ data class Scan(
}
private constructor(parcel: Parcel) : this(
content = parcel.readString() ?: "",
text = parcel.readString() ?: "",
raw = parcel.readSizedByteArray(),
format = BarcodeFormat.valueOf(parcel.readString() ?: ""),
errorCorrectionLevel = parcel.readString(),
version = parcel.readString(),
dataMask = parcel.readInt(),
symbol = parcel.readBitMatrix(),
sequenceSize = parcel.readInt(),
sequenceIndex = parcel.readInt(),
sequenceId = parcel.readString() ?: "",
@@ -96,11 +103,13 @@ data class Scan(
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.apply {
writeString(content)
writeString(text)
writeSizedByteArray(raw)
writeString(format.name)
writeString(errorCorrectionLevel)
writeString(version)
writeInt(dataMask)
writeBitMatrix(symbol)
writeInt(sequenceSize)
writeInt(sequenceIndex)
writeString(sequenceId)
@@ -132,12 +141,14 @@ fun Result.toScan(): Scan {
ContentType.ISO15434,
ContentType.TEXT -> text
else -> ""
else -> "" // Show as binary.
},
rawBytes,
format,
ecLevel,
version,
dataMask,
symbol,
sequenceSize,
sequenceIndex,
sequenceId,
@@ -148,37 +159,6 @@ fun Result.toScan(): Scan {
)
}
data class Recreation(
val format: BarcodeFormat,
val ecLevel: Int,
val size: Int,
val margin: Int
) {
fun <T> encode(content: T): Bitmap? = try {
ZxingCpp.encodeAsBitmap(
content, format, size, size, margin, ecLevel
)
} catch (_: RuntimeException) {
null
}
}
fun Scan.toRecreation(
size: Int = 128,
margin: Int = -1
) = Recreation(
format,
when (errorCorrectionLevel) {
"L" -> 0
"M" -> 4
"Q" -> 6
"H" -> 8
else -> -1
},
size,
margin
)
private fun getDateTime(
time: Long = System.currentTimeMillis()
) = DateFormat.format(
@@ -190,7 +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
@@ -210,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)
}
@@ -0,0 +1,280 @@
package de.markusfisch.android.binaryeye.fragment
import android.app.AlertDialog
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.EditText
import android.widget.ListView
import android.widget.RadioGroup
import android.widget.TextView
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.app.prefs
import de.markusfisch.android.binaryeye.automation.AutomatedAction
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
import de.markusfisch.android.binaryeye.view.systemBarListViewScrollListener
class AutomatedActionsFragment : Fragment() {
private val actions = ArrayList<AutomatedAction>()
private lateinit var adapter: ActionsAdapter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
state: Bundle?
): View? {
val ac = activity ?: return null
ac.setTitle(R.string.automated_actions)
val view = inflater.inflate(
R.layout.fragment_automated_actions,
container,
false
)
actions.clear()
actions.addAll(prefs.automatedActions)
val listView = view.findViewById<ListView>(R.id.actions)
listView.emptyView = view.findViewById(R.id.no_actions)
adapter = ActionsAdapter(ac, actions)
listView.adapter = adapter
listView.setOnScrollListener(systemBarListViewScrollListener)
listView.setOnItemClickListener { _, _, position, _ ->
editAction(position)
}
listView.setOnItemLongClickListener { _, _, position, _ ->
confirmRemoveAction(position)
true
}
view.findViewById<View>(R.id.add).setOnClickListener {
editAction(null)
}
view.findViewById<View>(R.id.inset_layout).setPaddingFromWindowInsets()
listView.setPaddingFromWindowInsets()
return view
}
private fun editAction(position: Int?) {
val ac = activity ?: return
val action = position?.let { actions[it] }
val view = ac.layoutInflater.inflate(
R.layout.dialog_automated_action,
null
)
val regexView = view.findViewById<EditText>(R.id.regex)
val urlView = view.findViewById<EditText>(R.id.url_template)
val typeGroup = view.findViewById<RadioGroup>(R.id.action_type)
val urlSection = view.findViewById<View>(R.id.url_section)
val intentSection = view.findViewById<View>(R.id.intent_section)
val intentUriView = view.findViewById<EditText>(
R.id.intent_uri_template
)
if (action != null) {
regexView.setText(action.pattern)
when (action.type) {
AutomatedAction.Type.Intent -> {
urlView.setText(action.urlTemplate ?: "")
}
AutomatedAction.Type.CustomIntent -> {
intentUriView.setText(action.intentUriTemplate ?: "")
}
}
}
fun updateTypeSections(checkedId: Int) {
val isIntent = checkedId == R.id.action_type_intent
urlSection.visibility = if (isIntent) View.VISIBLE else View.GONE
intentSection.visibility = if (isIntent) View.GONE else View.VISIBLE
}
typeGroup.setOnCheckedChangeListener { _, checkedId ->
updateTypeSections(checkedId)
}
val initialTypeId = if (
action?.type == AutomatedAction.Type.CustomIntent
) {
R.id.action_type_custom_intent
} else {
R.id.action_type_intent
}
typeGroup.check(initialTypeId)
updateTypeSections(initialTypeId)
val dialog = AlertDialog.Builder(ac)
.setTitle(
if (action == null) {
R.string.automated_action_add
} else {
R.string.automated_action_edit
}
)
.setView(view)
.setPositiveButton(android.R.string.ok, null)
.setNegativeButton(android.R.string.cancel) { _, _ -> }
.create()
dialog.setOnShowListener {
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
addOrUpdateAction(
position,
regexView,
urlView,
typeGroup,
intentUriView,
dialog
)
}
}
dialog.show()
}
private fun addOrUpdateAction(
position: Int?,
regexView: EditText,
urlView: EditText,
typeGroup: RadioGroup,
intentUriView: EditText,
dialog: Dialog,
) {
val pattern = regexView.text.toString().trim()
if (pattern.isEmpty()) {
regexView.error = getString(R.string.automated_action_regex_required)
return
}
try {
Regex(pattern)
} catch (_: Exception) {
regexView.error = getString(R.string.automated_action_regex_invalid)
return
}
val isIntent = typeGroup.checkedRadioButtonId == R.id.action_type_intent
val newAction = if (isIntent) {
val urlTemplate = urlView.text.toString().trim()
if (urlTemplate.isEmpty()) {
urlView.error = getString(R.string.automated_action_url_required)
return
}
AutomatedAction(
pattern = pattern,
type = AutomatedAction.Type.Intent,
urlTemplate = urlTemplate
)
} else {
val intentUriTemplate = intentUriView.text.toString().trim()
if (intentUriTemplate.isEmpty()) {
intentUriView.error = getString(
R.string.automated_action_intent_uri_required
)
return
}
if (!intentUriTemplate.startsWith(
"intent:",
ignoreCase = true
)
) {
intentUriView.error = getString(
R.string.automated_action_intent_uri_invalid
)
return
}
AutomatedAction(
pattern = pattern,
type = AutomatedAction.Type.CustomIntent,
intentUriTemplate = intentUriTemplate
)
}
if (position == null) {
actions.add(newAction)
} else {
actions[position] = newAction
}
prefs.setAutomatedActions(actions)
adapter.notifyDataSetChanged()
dialog.dismiss()
}
private fun confirmRemoveAction(position: Int) {
val ac = activity ?: return
AlertDialog.Builder(ac)
.setMessage(R.string.automated_action_remove_confirm)
.setPositiveButton(android.R.string.ok) { _, _ ->
actions.removeAt(position)
prefs.setAutomatedActions(actions)
adapter.notifyDataSetChanged()
}
.setNegativeButton(android.R.string.cancel) { _, _ -> }
.show()
}
private class ActionsAdapter(
context: Context,
private val items: List<AutomatedAction>
) : ArrayAdapter<AutomatedAction>(
context,
android.R.layout.simple_list_item_2,
android.R.id.text1,
items
) {
override fun getView(
position: Int,
convertView: View?,
parent: ViewGroup
): View {
val view = super.getView(position, convertView, parent)
val action = items[position]
getViewHolder(view).apply {
title.text = action.pattern
subtitle.text = when (action.type) {
AutomatedAction.Type.Intent -> if (!action.urlTemplate.isNullOrEmpty()) {
context.getString(
R.string.automated_action_list_intent,
action.urlTemplate
)
} else {
context.getString(R.string.automated_action_list_intent_empty)
}
AutomatedAction.Type.CustomIntent ->
if (!action.intentUriTemplate.isNullOrEmpty()) {
context.getString(
R.string.automated_action_list_custom_intent,
action.intentUriTemplate
)
} else {
context.getString(
R.string.automated_action_list_custom_intent_empty
)
}
}
}
return view
}
private fun getViewHolder(
view: View
): ViewHolder = view.tag as ViewHolder? ?: ViewHolder(
view.findViewById(android.R.id.text1),
view.findViewById(android.R.id.text2),
).also {
view.tag = it
}
private data class ViewHolder(
val title: TextView,
val subtitle: TextView,
)
}
}
@@ -4,47 +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.database.toScan
import de.markusfisch.android.binaryeye.graphics.COLOR_BLACK
import de.markusfisch.android.binaryeye.graphics.COLOR_WHITE
import de.markusfisch.android.binaryeye.io.addSuffixIfNotGiven
import de.markusfisch.android.binaryeye.io.toSaveResult
import de.markusfisch.android.binaryeye.io.writeExternalFile
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.BarcodeFormat
import kotlinx.coroutines.*
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 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)
@@ -65,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(
@@ -84,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()
)
}
@@ -104,28 +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_TEXT) ?: getByteArray(CONTENT_RAW) ?: throw IllegalArgumentException(
"content cannot be null"
),
BarcodeFormat.valueOf(
private fun Bundle.toBarcode(): Barcode<*> {
val content =
getString(CONTENT_TEXT) ?: getByteArray(CONTENT_RAW) ?: throw IllegalArgumentException(
"content cannot be null"
)
val barcodeFormat = BarcodeFormat.valueOf(
getString(FORMAT) ?: throw IllegalArgumentException(
"format cannot be null"
)
),
getInt(SIZE),
getInt(MARGIN),
getInt(EC_LEVEL),
Colors.entries[getInt(COLORS)]
)
)
val colors = BarcodeColors.entries[getInt(COLORS)]
val data = getByteArray(BIT_MATRIX_DATA)
return if (data != null) {
BitMatrixBarcode(
ZxingCpp.BitMatrix(
getInt(BIT_MATRIX_WIDTH),
getInt(BIT_MATRIX_HEIGHT),
data
),
content,
barcodeFormat,
colors
)
} else {
ContentBarcode(
content,
barcodeFormat,
getInt(EC_LEVEL),
getInt(MARGIN),
colors
)
}
}
override fun onResume() {
super.onResume()
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()
@@ -135,28 +206,52 @@ 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)
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
}
@@ -165,6 +260,18 @@ class BarcodeFragment : Fragment() {
}
}
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
@@ -180,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
}
@@ -202,21 +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())
}
}
}
@@ -225,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,
@@ -232,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)
}
@@ -241,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)
}
@@ -271,34 +448,96 @@ class BarcodeFragment : Fragment() {
}
}
private fun brightenScreen() {
activity?.let {
if (!isAdded) {
return
}
currentBrightness = it.getScreenBrightness()
it.setScreenBrightness(1f)
brightenScreenItem.isChecked = true
}
}
private fun restoreScreenBrightness() {
if (currentBrightness > -1f && isAdded) {
activity?.setScreenBrightness(currentBrightness)
currentBrightness = -1f
brightenScreenItem.isChecked = false
}
}
companion object {
private const val BIT_MATRIX_WIDTH = "bit_matrix_width"
private const val BIT_MATRIX_HEIGHT = "bit_matrix_height"
private const val BIT_MATRIX_DATA = "bit_matrix_data"
private const val CONTENT_TEXT = "content_text"
private const val CONTENT_RAW = "content_raw"
private const val FORMAT = "format"
private const val SIZE = "size"
private const val MARGIN = "margin"
private const val EC_LEVEL = "ec_level"
private const val MARGIN = "margin"
private const val COLORS = "colors"
private const val MIME_PNG = "image/png"
private const val MIME_JPG = "image/jpeg"
private const val MIME_SVG = "image/svg+xmg"
private const val MIME_TXT = "text/plain"
fun newInstance(
barcode: Barcode<*>,
colors: Int = 0
): Fragment {
val args = Bundle()
when (barcode) {
is ContentBarcode -> args.putEcLevelMargin(
barcode.ecLevel,
barcode.margin
)
is BitMatrixBarcode -> {
args.putInt(BIT_MATRIX_WIDTH, barcode.bitMatrix.width)
args.putInt(BIT_MATRIX_HEIGHT, barcode.bitMatrix.height)
args.putByteArray(BIT_MATRIX_DATA, barcode.bitMatrix.data)
}
else -> throw IllegalArgumentException("unknown barcode")
}
args.putContentFormatColors(
barcode.content,
barcode.format,
colors
)
val fragment = BarcodeFragment()
fragment.arguments = args
return fragment
}
fun <T> newInstance(
content: T,
format: BarcodeFormat,
size: Int,
margin: Int,
ecLevel: Int = -1,
margin: Int = -1,
colors: Int = 0
): Fragment {
val args = Bundle()
args.putContentFormatColors(content, format, colors)
args.putEcLevelMargin(ecLevel, margin)
val fragment = BarcodeFragment()
fragment.arguments = args
return fragment
}
private fun <T> Bundle.putContentFormatColors(
content: T,
format: BarcodeFormat,
colors: Int
) {
when (content) {
is String -> {
args.putString(CONTENT_TEXT, content)
putString(CONTENT_TEXT, content)
}
is ByteArray -> {
args.putByteArray(CONTENT_RAW, content)
putByteArray(CONTENT_RAW, content)
}
else -> {
@@ -307,82 +546,40 @@ class BarcodeFragment : Fragment() {
)
}
}
args.putString(FORMAT, format.name)
args.putInt(SIZE, size)
args.putInt(MARGIN, margin)
args.putInt(EC_LEVEL, ecLevel)
args.putInt(COLORS, colors)
val fragment = BarcodeFragment()
fragment.arguments = args
return fragment
putString(FORMAT, format.name)
putInt(COLORS, colors)
}
private fun Bundle.putEcLevelMargin(
ecLevel: Int,
margin: Int
) {
putInt(EC_LEVEL, ecLevel)
putInt(MARGIN, margin)
}
}
}
private data class Barcode<T>(
val content: T,
val format: BarcodeFormat,
val size: Int,
val margin: Int,
val ecLevel: Int,
val colors: Colors
) {
private var _bitmap: Bitmap? = null
fun bitmap(): Bitmap {
val b = _bitmap ?: ZxingCpp.encodeAsBitmap(
content, format, size, size, margin, 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, margin, ecLevel
)
_svg = s
return s
}
private var _text: String? = null
fun text(): String {
val t = _text ?: ZxingCpp.encodeAsText(
content, format, margin, ecLevel,
inverted = colors == Colors.BLACK_ON_WHITE
)
_text = t
return t
private fun ImageView.setScale(scale: Float, x: Float, y: Float) {
imageMatrix = Matrix().apply {
setTranslate(-x, -y)
postScale(scale, scale)
postTranslate(x, y)
}
}
private enum class Colors {
BLACK_ON_WHITE,
WHITE_ON_BLACK,
BLACK_ON_TRANSPARENT,
WHITE_ON_TRANSPARENT;
fun foregroundColor(): Int = when (this) {
BLACK_ON_WHITE,
BLACK_ON_TRANSPARENT -> COLOR_BLACK
WHITE_ON_BLACK,
WHITE_ON_TRANSPARENT -> COLOR_WHITE
}
fun backgroundColor(): Int = when (this) {
BLACK_ON_WHITE -> COLOR_WHITE
WHITE_ON_BLACK -> COLOR_BLACK
BLACK_ON_TRANSPARENT,
WHITE_ON_TRANSPARENT -> 0
}
private fun ImageView.getScale(): Float {
val values = FloatArray(9)
imageMatrix.getValues(values)
return values[Matrix.MSCALE_X]
}
private fun Bitmap.saveAsPng(outputStream: OutputStream, quality: Int = 90) =
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, "_")
@@ -390,6 +587,11 @@ private fun encodeFileName(name: String): String = fileNameCharacters
.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
@@ -1,13 +1,21 @@
package de.markusfisch.android.binaryeye.fragment
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.DocumentsContract
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.text.style.TypefaceSpan
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
@@ -21,7 +29,12 @@ 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
@@ -31,24 +44,31 @@ 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.toHexString
import de.markusfisch.android.binaryeye.content.toBarcode
import de.markusfisch.android.binaryeye.content.toErrorCorrectionInt
import de.markusfisch.android.binaryeye.content.wipeLastShareFile
import de.markusfisch.android.binaryeye.database.Recreation
import de.markusfisch.android.binaryeye.database.Scan
import de.markusfisch.android.binaryeye.database.toRecreation
import de.markusfisch.android.binaryeye.io.askForFileName
import de.markusfisch.android.binaryeye.io.toSaveResult
import de.markusfisch.android.binaryeye.io.writeExternalFile
import de.markusfisch.android.binaryeye.net.createEncodeDeeplink
import de.markusfisch.android.binaryeye.view.hideSoftKeyboard
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
import de.markusfisch.android.binaryeye.widget.toast
import de.markusfisch.android.zxingcpp.ZxingCpp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.security.MessageDigest
import kotlin.math.roundToInt
class DecodeFragment : Fragment() {
@@ -61,10 +81,13 @@ class DecodeFragment : Fragment() {
private lateinit var recreationView: ImageView
private lateinit var labelView: EditText
private lateinit var fab: FloatingActionButton
private lateinit var scan: Scan
private lateinit var format: String
private val parentJob = Job()
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
@@ -74,9 +97,23 @@ 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 onActivityResult(
requestCode: Int,
resultCode: Int,
resultData: Intent?
) {
when (requestCode) {
OPEN_DOCUMENT -> {
if (resultCode == Activity.RESULT_OK) {
val uri = resultData?.data ?: return
activity?.openPickedFile(uri)
}
}
}
}
override fun onCreate(state: Bundle?) {
super.onCreate(state)
@@ -96,21 +133,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.content.isEmpty()
originalBytes = scan.raw ?: originalContent.toByteArray()
isBinary = scan.text.isEmpty()
originalBytes = scan.raw ?: scan.text.toByteArray()
format = scan.format.name
id = scan.id
recreationSize = (200f * dp).roundToInt()
contentView = view.findViewById(R.id.content)
formatView = view.findViewById(R.id.format)
@@ -122,19 +160,13 @@ class DecodeFragment : Fragment() {
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 (id > 0) {
if (scan.id > 0) {
scan.label?.let {
labelView.setText(it)
label = it
@@ -143,16 +175,18 @@ class DecodeFragment : Fragment() {
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 ?: "")) {
@@ -160,6 +194,9 @@ class DecodeFragment : Fragment() {
label = newLabel
}
}
if (action.fired) {
maybeBackOrFinish()
}
}
override fun onDestroy() {
@@ -168,7 +205,7 @@ class DecodeFragment : Fragment() {
wipeLastShareFile()
}
private fun initContentAndFab(originalContent: String) {
private fun initContentAndFab(justScanned: Boolean) {
if (isBinary) {
contentView.setText(String(originalBytes).foldNonAlNum())
contentView.isEnabled = false
@@ -177,7 +214,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() ?: "")
@@ -200,7 +237,8 @@ class DecodeFragment : Fragment() {
fab.setOnClickListener {
executeAction(content)
}
if (prefs.openImmediately) {
if (justScanned && prefs.openImmediately) {
closeAutomatically = true
executeAction(content)
}
}
@@ -213,17 +251,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)
@@ -238,36 +275,119 @@ class DecodeFragment : Fragment() {
formatView.text = resources.getQuantityString(
R.plurals.barcode_info,
bytes.size,
prettifyFormatName(format),
format.prettifyFormatName(),
bytes.size
)
hexView.showIf(prefs.showHexDump) { v ->
v.text = hexDump(bytes)
}
recreationView.showIf(prefs.showRecreation) { v ->
val r = recreation ?: return@showIf
val content = if (isBinary) bytes else text
v.setImageBitmap(r.encode(content))
v.setOnClickListener {
fragmentManager?.addFragment(
BarcodeFragment.newInstance(
content,
r.format,
r.size,
r.margin,
r.ecLevel
)
val unmodified: Boolean
val content = if (isBinary) {
if (bytes.isEmpty()) {
clearRecreation()
return@showIf
}
unmodified = bytes.contentEquals(originalBytes)
bytes
} else {
if (text.isEmpty()) {
clearRecreation()
return@showIf
}
unmodified = text == scan.text
text
}
val barcode = if (unmodified) {
scan.toBarcode()
} else {
ContentBarcode(
content,
scan.format,
scan.errorCorrectionLevel.toErrorCorrectionInt()
)
}
try {
v.setImageBitmap(barcode.bitmap(recreationSize))
v.setOnClickListener {
v.context.hideSoftKeyboard(contentView)
v.post {
fragmentManager?.addFragment(
BarcodeFragment.newInstance(barcode)
)
}
}
} catch (_: 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),
@@ -287,7 +407,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(),
@@ -313,11 +433,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 {
@@ -328,10 +457,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)
@@ -365,6 +502,12 @@ class DecodeFragment : Fragment() {
true
}
R.id.copy_as_deeplink -> {
copyToClipboard(deeplinkToCopy())
maybeBackOrFinish()
true
}
R.id.share -> {
context?.apply {
shareText(textOrHex())
@@ -397,7 +540,7 @@ class DecodeFragment : Fragment() {
}
R.id.remove -> {
db.removeScan(id)
db.removeScan(scan.id)
backOrFinish()
true
}
@@ -412,14 +555,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)
}
}
}
@@ -427,7 +572,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)
}
}
}
@@ -446,20 +594,51 @@ class DecodeFragment : Fragment() {
}
}
private fun executeAction(content: String) {
private fun executeAction(str: 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 (str.isEmpty() || openLocalDocument(str)) {
return
}
if (action is WifiAction &&
Build.VERSION.SDK_INT < Build.VERSION_CODES.Q &&
!ac.hasLocationPermission { executeAction(str) }
) {
return
}
scope.launch {
action.execute(ac, str.toByteArray())
}
}
private fun openLocalDocument(str: String): Boolean {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N ||
!str.startsWith(SCHEME_FILE)
) {
return false
}
// Needs to be disabled so we get the result.
closeAutomatically = false
// file:// can't be used with Scoped Storage anymore.
startActivityForResult(
Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val primary = str.substring(
SCHEME_FILE.length
).localDirName() ?: Environment.getExternalStorageDirectory().path
putExtra(
DocumentsContract.EXTRA_INITIAL_URI,
DocumentsContract.buildDocumentUri(
"com.android.externalstorage.documents",
"primary:$primary"
)
)
}
},
OPEN_DOCUMENT
)
return true
}
private fun askForFileNameAndSave(raw: ByteArray) {
@@ -488,6 +667,8 @@ class DecodeFragment : Fragment() {
companion object {
private const val SCAN = "scan"
private const val OPEN_DOCUMENT = 1
private const val SCHEME_FILE = "file://"
fun newInstance(scan: Scan): Fragment {
val args = Bundle()
@@ -512,7 +693,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, "")
@@ -608,7 +789,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.
@@ -643,3 +824,60 @@ 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
}
}
private fun String.localDirName(): String? = when {
endsWith("/") -> this
else -> File(this).parentFile?.absolutePath
}?.replaceFirst(Regex("^/storage/emulated/0/"), "")
?.replaceFirst(Regex("^/sdcard/"), "")
?.trim('/')
private fun Activity.openPickedFile(uri: Uri) {
startActivity(
Intent.createChooser(
Intent(Intent.ACTION_VIEW).apply {
setDataAndType(uri, contentResolver.getType(uri) ?: "*/*")
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
},
getString(R.string.open_url)
)
)
}
@@ -3,9 +3,9 @@ package de.markusfisch.android.binaryeye.fragment
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.SwitchCompat
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
@@ -16,20 +16,19 @@ import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.CheckBox
import android.widget.EditText
import android.widget.SeekBar
import android.widget.Spinner
import android.widget.TextView
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.adapter.prettifyFormatName
import de.markusfisch.android.binaryeye.app.addFragment
import de.markusfisch.android.binaryeye.app.prefs
import de.markusfisch.android.binaryeye.app.setFragment
import de.markusfisch.android.binaryeye.text.unescape
import de.markusfisch.android.binaryeye.view.hideSoftKeyboard
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
import de.markusfisch.android.binaryeye.widget.toast
import de.markusfisch.android.zxingcpp.ZxingCpp.BarcodeFormat
import java.io.InputStream
import kotlin.math.max
import kotlin.math.min
class EncodeFragment : Fragment() {
@@ -38,11 +37,7 @@ 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 marginLayout: View
private lateinit var marginView: TextView
private lateinit var marginBarView: SeekBar
private lateinit var addQuietZoneSwitch: SwitchCompat
private lateinit var contentView: EditText
private lateinit var unescapeCheckBox: CheckBox
@@ -60,7 +55,6 @@ class EncodeFragment : Fragment() {
BarcodeFormat.UPC_A
)
private var minMargin = 0
private var bytes: ByteArray? = null
override fun onActivityResult(
@@ -109,7 +103,7 @@ class EncodeFragment : Fragment() {
val formatAdapter = ArrayAdapter(
ac,
android.R.layout.simple_spinner_item,
formats.map { prettifyFormatName(it.name) }
formats.map { it.name.prettifyFormatName() }
)
formatAdapter.setDropDownViewResource(
android.R.layout.simple_spinner_dropdown_item
@@ -145,12 +139,13 @@ class EncodeFragment : Fragment() {
colorsLabel, colorsSpinner
)
when (format) {
BarcodeFormat.AZTEC -> setMarginBar(4, 4)
BarcodeFormat.DATA_MATRIX -> setMarginBar(1, 1)
BarcodeFormat.QR_CODE -> setMarginBar(4, 4)
BarcodeFormat.PDF_417 -> setMarginBar(0, 30)
BarcodeFormat.AZTEC,
BarcodeFormat.DATA_MATRIX,
BarcodeFormat.QR_CODE,
BarcodeFormat.PDF_417 -> true
else -> false
}.setVisibilityFor(marginLayout)
}.setVisibilityFor(addQuietZoneSwitch)
}
override fun onNothingSelected(parentView: AdapterView<*>?) {}
@@ -177,26 +172,22 @@ 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()
marginLayout = view.findViewById(R.id.margin)
marginView = view.findViewById(R.id.margin_display)
marginBarView = view.findViewById(R.id.margin_bar)
initMarginBar()
addQuietZoneSwitch = view.findViewById(R.id.add_quiet_zone)
addQuietZoneSwitch.isChecked = prefs.addQuietZone
contentView = view.findViewById(R.id.content)
unescapeCheckBox = view.findViewById(R.id.unescape)
unescapeCheckBox.isChecked = prefs.expandEscapeSequences
var complete = false
val args = arguments
args?.getString(CONTENT_TEXT)?.let {
contentView.setText(it)
complete = it.isNotEmpty()
}
args?.getByteArray(CONTENT_RAW)?.let {
bytes = it
complete = it.isNotEmpty()
setEncodeByteArray()
}
@@ -215,8 +206,17 @@ 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
}
@@ -248,113 +248,65 @@ class EncodeFragment : Fragment() {
super.onPause()
prefs.indexOfLastSelectedFormat = formatView.selectedItemPosition
prefs.expandEscapeSequences = unescapeCheckBox.isChecked
prefs.lastMargin = marginBarView.progress
prefs.addQuietZone = addQuietZoneSwitch.isChecked
}
private fun Context.encode() {
hideSoftKeyboard(contentView)
val content = if (bytes != null) {
bytes
} else {
var text = contentView.text.toString()
if (unescapeCheckBox.isChecked) {
try {
text = text.unescape()
} catch (e: IllegalArgumentException) {
toast(e.message ?: "Invalid escape sequence")
return
}
}
if (text.isEmpty()) {
toast(R.string.error_no_content)
return
}
text
val content = getContent() ?: return
contentView.post {
fragmentManager?.addFragment(newEncodeFragment(content))
}
val format = formats[formatView.selectedItemPosition]
fragmentManager?.addFragment(
BarcodeFragment.newInstance(
content,
format,
getSize(sizeBarView.progress),
when (format) {
BarcodeFormat.AZTEC,
BarcodeFormat.DATA_MATRIX,
BarcodeFormat.QR_CODE,
BarcodeFormat.PDF_417 -> marginBarView.progress
}
else -> -1
},
format.getErrorCorrectionLevel(ecSpinner.selectedItemPosition),
if (format.canBeInverted()) {
colorsSpinner.selectedItemPosition
} else 0
)
private fun newEncodeFragment(content: Any): Fragment {
val format = formats[formatView.selectedItemPosition]
return BarcodeFragment.newInstance(
content,
format,
format.getErrorCorrectionLevel(ecSpinner.selectedItemPosition),
when (format) {
BarcodeFormat.AZTEC,
BarcodeFormat.DATA_MATRIX,
BarcodeFormat.QR_CODE,
BarcodeFormat.PDF_417 -> if (
addQuietZoneSwitch.isChecked
) 1 else 0
else -> -1
},
if (format.canBeInverted()) {
colorsSpinner.selectedItemPosition
} else 0
)
}
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 updateSize(power: Int) {
val size = getSize(power)
sizeView.text = getString(R.string.size_width_by_height, size, size)
}
private fun initMarginBar() {
updateMargin(marginBarView.progress)
marginBarView.setOnSeekBarChangeListener(
object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(
seekBar: SeekBar,
progressValue: Int,
fromUser: Boolean
) {
if (progressValue >= minMargin) {
updateMargin(progressValue)
}
}
override fun onStartTrackingTouch(seekBar: SeekBar) {}
override fun onStopTrackingTouch(seekBar: SeekBar) {}
})
}
private fun updateMargin(margin: Int) {
marginView.text = getString(R.string.margin_size, margin)
}
private fun setMarginBar(minimum: Int, value: Int): Boolean {
minMargin = minimum
marginBarView.apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
min = minimum
}
progress = max(
minimum, if (prefs.lastMargin > -1) {
prefs.lastMargin
} else {
value
}
)
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
}
return true
}
private fun setEncodeByteArray() {
@@ -368,11 +320,13 @@ class EncodeFragment : Fragment() {
private const val CONTENT_TEXT = "content_text"
private const val CONTENT_RAW = "content_raw"
private const val FORMAT = "format"
private const val EXECUTE = "execute"
private const val PICK_FILE_RESULT_CODE = 1
fun <T> newInstance(
content: T? = null,
format: String? = null
format: String? = null,
execute: Boolean = false
): Fragment {
val args = Bundle()
content?.let {
@@ -383,6 +337,7 @@ class EncodeFragment : Fragment() {
"content must be a String or a ByteArray"
)
}
args.putBoolean(EXECUTE, execute)
}
format?.let { args.putString(FORMAT, it) }
val fragment = EncodeFragment()
@@ -422,7 +377,9 @@ private fun BarcodeFormat.canBeInverted() = when (this) {
else -> false
}
private fun String.toFormat(default: BarcodeFormat = BarcodeFormat.QR_CODE): BarcodeFormat = try {
private fun String.toFormat(
default: BarcodeFormat = BarcodeFormat.QR_CODE
): BarcodeFormat = try {
BarcodeFormat.valueOf(this)
} catch (_: IllegalArgumentException) {
default
@@ -446,8 +403,6 @@ 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)
@@ -15,22 +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.content.wipeLastShareFile
import de.markusfisch.android.binaryeye.database.*
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
@@ -49,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
@@ -142,7 +167,7 @@ class HistoryFragment : Fragment() {
useHistorySwitch = view.findViewById(
R.id.use_history
) as SwitchCompat
)
initHistorySwitch(useHistorySwitch)
listView = view.findViewById(R.id.scans)
@@ -171,7 +196,7 @@ class HistoryFragment : Fragment() {
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()
@@ -276,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
@@ -289,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)
@@ -320,7 +345,7 @@ class HistoryFragment : Fragment() {
closeActionMode()
try {
fragmentManager?.addFragment(DecodeFragment.newInstance(scan))
} catch (e: IllegalArgumentException) {
} catch (_: IllegalArgumentException) {
// Ignore, can never happen.
}
}
@@ -421,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)
@@ -454,14 +479,14 @@ class HistoryFragment : Fragment() {
db.getScansDetailed(selectedIds.toLongArray())
} else {
db.getScansDetailed(filter)
}?.use { cursor ->
}.use { cursor ->
val details = format.split(":")
when (details[0]) {
"text" -> Pair(cursor.exportText(details[1]), "txt")
"csv" -> Pair(cursor.exportCsv(details[1]), "csv")
else -> Pair(cursor.exportJson(), "json")
}
}?.let { (text, ext) ->
}.let { (text, ext) ->
text?.let {
withContext(Dispatchers.Main) {
if (asFile) {
@@ -481,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)
@@ -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 {
@@ -28,6 +28,8 @@ import de.markusfisch.android.binaryeye.view.systemBarRecyclerViewScrollListener
import de.markusfisch.android.binaryeye.widget.toast
class PreferencesFragment : PreferenceFragmentCompat() {
private var lastProfile: String? = null
private val changeListener = object : OnSharedPreferenceChangeListener {
override fun onSharedPreferenceChanged(
sharedPreferences: SharedPreferences?,
@@ -35,34 +37,78 @@ class PreferencesFragment : PreferenceFragmentCompat() {
) {
key ?: return
val preference = findPreference(key) ?: return
prefs.update()
when (preference.key) {
"custom_locale" -> activity?.restartApp()
"beep_tone_name" -> {
beepConfirm()
setSummary(preference)
}
"send_scan_bluetooth" -> {
if (prefs.sendScanBluetooth &&
activity?.hasBluetoothPermission() == false
) {
prefs.sendScanBluetooth = false
}
setSummary(preference)
}
else -> setSummary(preference)
if (preference.key != PROFILE) {
prefs.update()
}
when (preference.key) {
"custom_locale" -> {
activity?.restartApp()
return
}
"beep_tone_name" -> beepConfirm()
"send_scan_bluetooth" -> if (
prefs.sendScanBluetooth &&
activity?.hasBluetoothPermission() == false
) {
prefs.sendScanBluetooth = false
}
}
setSummary(preference)
}
}
override fun onCreatePreferences(state: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.preferences)
loadPreferences(rootKey)
}
private fun loadPreferences(rootKey: String? = null) {
preferenceScreen?.sharedPreferences
?.unregisterOnSharedPreferenceChangeListener(changeListener)
preferenceManager.apply {
sharedPreferencesName = prefs.profile ?: "${context.packageName}_preferences"
sharedPreferencesMode = Context.MODE_PRIVATE
}
// Refresh the preferences.
preferenceScreen = null
setPreferencesFromResource(R.xml.preferences, rootKey)
preferenceScreen.sharedPreferences
.registerOnSharedPreferenceChangeListener(changeListener)
setSummaries(preferenceScreen)
wireProfiles()
wireAutomatedActions()
setBluetoothResources()
wireClearNetworkPreferences()
}
private fun wireProfiles() {
findPreference(PROFILE).apply {
updateProfileSummary(this)
onPreferenceClickListener = Preference.OnPreferenceClickListener {
activity?.supportFragmentManager?.addFragment(
ProfilesFragment()
)
true
}
}
}
private fun wireAutomatedActions() {
findPreference(AUTOMATED_ACTIONS).apply {
updateAutomatedActionsSummary(this)
onPreferenceClickListener = Preference.OnPreferenceClickListener {
activity?.supportFragmentManager?.addFragment(
AutomatedActionsFragment()
)
true
}
}
}
private fun setBluetoothResources() {
if (prefs.sendScanBluetooth &&
activity?.hasBluetoothPermission() == true
@@ -79,7 +125,9 @@ class PreferencesFragment : PreferenceFragmentCompat() {
// From R+ we can query past network suggestions and
// make them editable.
setOnPreferenceClickListener {
fragmentManager.addFragment(NetworkSuggestionsFragment())
activity?.supportFragmentManager?.addFragment(
NetworkSuggestionsFragment()
)
true
}
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
@@ -124,13 +172,19 @@ class PreferencesFragment : PreferenceFragmentCompat() {
override fun onResume() {
super.onResume()
if (lastProfile != prefs.profile) {
loadPreferences()
}
activity?.setTitle(R.string.preferences)
findPreference(AUTOMATED_ACTIONS)?.let {
updateAutomatedActionsSummary(it)
}
findPreference(PROFILE)?.let {
updateProfileSummary(it)
}
listView.setPaddingFromWindowInsets()
listView.removeOnScrollListener(systemBarRecyclerViewScrollListener)
listView.addOnScrollListener(systemBarRecyclerViewScrollListener)
preferenceScreen.sharedPreferences
.registerOnSharedPreferenceChangeListener(changeListener)
setSummaries(preferenceScreen)
}
override fun onPause() {
@@ -165,9 +219,13 @@ class PreferencesFragment : PreferenceFragmentCompat() {
}
private fun setSummary(preference: Preference) {
if (preference.key == AUTOMATED_ACTIONS) {
updateAutomatedActionsSummary(preference)
return
}
when (preference) {
is UrlPreference -> {
preference.setSummary(preference.getUrl())
preference.summary = preference.getUrl()
}
is ListPreference -> {
@@ -175,11 +233,9 @@ class PreferencesFragment : PreferenceFragmentCompat() {
}
is MultiSelectListPreference -> {
preference.setSummary(
preference.values.joinToString(", ") {
it.replace(Regex("_"), " ")
}
)
preference.summary = preference.values.joinToString(", ") {
it.replace(Regex("_"), " ")
}
}
is PreferenceGroup -> {
@@ -187,6 +243,29 @@ class PreferencesFragment : PreferenceFragmentCompat() {
}
}
}
private fun updateAutomatedActionsSummary(preference: Preference) {
val count = prefs.automatedActions.size
preference.summary = if (count == 0) {
getString(R.string.automated_actions_none)
} else {
resources.getQuantityString(
R.plurals.automated_actions_count,
count,
count
)
}
}
private fun updateProfileSummary(preference: Preference) {
preference.summary = prefs.profile ?: getString(R.string.profile_default)
lastProfile = prefs.profile
}
companion object {
private const val PROFILE = "profile"
private const val AUTOMATED_ACTIONS = "automated_actions"
}
}
private fun Activity.restartApp() {
@@ -0,0 +1,179 @@
package de.markusfisch.android.binaryeye.fragment
import android.app.AlertDialog
import android.content.Context
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.EditText
import android.widget.ListView
import android.widget.TextView
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.app.prefs
import de.markusfisch.android.binaryeye.view.setPaddingFromWindowInsets
import de.markusfisch.android.binaryeye.view.systemBarListViewScrollListener
import de.markusfisch.android.binaryeye.widget.toast
class ProfilesFragment : Fragment() {
private val profiles = ArrayList<ProfileItem>()
private lateinit var adapter: ProfilesAdapter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
state: Bundle?
): View? {
val ac = activity ?: return null
ac.setTitle(R.string.profile)
val view = inflater.inflate(
R.layout.fragment_profiles,
container,
false
)
refreshProfiles()
val listView = view.findViewById<ListView>(R.id.profiles)
listView.emptyView = view.findViewById(R.id.no_profiles)
adapter = ProfilesAdapter(ac, profiles)
listView.adapter = adapter
listView.setOnScrollListener(systemBarListViewScrollListener)
listView.setOnItemClickListener { _, _, position, _ ->
selectProfile(position)
}
listView.setOnItemLongClickListener { _, _, position, _ ->
confirmRemoveProfile(position)
true
}
view.findViewById<View>(R.id.add).setOnClickListener {
addProfile()
}
view.findViewById<View>(R.id.inset_layout).setPaddingFromWindowInsets()
listView.setPaddingFromWindowInsets()
return view
}
private fun refreshProfiles() {
profiles.clear()
profiles.add(
ProfileItem(
null,
getString(R.string.profile_default)
)
)
prefs.profiles.forEach { name ->
profiles.add(ProfileItem(name, name))
}
if (this::adapter.isInitialized) {
adapter.notifyDataSetChanged()
}
}
private fun selectProfile(position: Int) {
val ac = activity ?: return
val profile = profiles[position].name
if (prefs.profile == profile) {
return
}
prefs.load(ac, profile)
adapter.notifyDataSetChanged()
}
// Dialogs do not have a parent view.
@Suppress("InflateParams")
private fun addProfile() {
val ac = activity ?: return
val view = ac.layoutInflater.inflate(
R.layout.dialog_profile_name, null
)
val editText = view.findViewById<EditText>(R.id.name)
AlertDialog.Builder(ac)
.setView(view)
.setPositiveButton(android.R.string.ok) { _, _ ->
val name = editText.text.toString().trim()
if (name.isNotEmpty() && prefs.addProfile(name)) {
prefs.load(ac, name)
refreshProfiles()
} else {
ac.toast(R.string.profile_invalid_name)
}
}
.show()
}
private fun confirmRemoveProfile(position: Int) {
val ac = activity ?: return
val profile = profiles[position]
val profileName = profile.name ?: return
AlertDialog.Builder(ac)
.setMessage(R.string.profile_remove_confirm)
.setPositiveButton(android.R.string.ok) { _, _ ->
val isCurrent = prefs.profile == profileName
prefs.removeProfile(profileName)
if (isCurrent) {
prefs.load(ac, null)
}
refreshProfiles()
}
.setNegativeButton(android.R.string.cancel) { _, _ -> }
.show()
}
private class ProfilesAdapter(
context: Context,
private val items: List<ProfileItem>
) : ArrayAdapter<ProfileItem>(
context,
android.R.layout.simple_list_item_2,
android.R.id.text1,
items
) {
override fun getView(
position: Int,
convertView: View?,
parent: ViewGroup
): View {
val view = super.getView(position, convertView, parent)
val item = items[position]
val isCurrent = item.name == prefs.profile
getViewHolder(view).apply {
title.text = item.label
if (isCurrent) {
subtitle.text = context.getString(R.string.profile_current)
subtitle.visibility = View.VISIBLE
} else {
subtitle.text = ""
subtitle.visibility = View.GONE
}
}
return view
}
private fun getViewHolder(
view: View
): ViewHolder = view.tag as ViewHolder? ?: ViewHolder(
view.findViewById(android.R.id.text1),
view.findViewById(android.R.id.text2),
).also {
view.tag = it
}
private data class ViewHolder(
val title: TextView,
val subtitle: TextView,
)
}
}
private data class ProfileItem(
val name: String?,
val label: String
)
@@ -80,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
}
@@ -10,7 +10,9 @@ data class FrameMetrics(
var width: Int = 0,
var height: Int = 0,
var orientation: Int = 0
)
) {
fun isValid() = width > 0 && height > 0
}
fun Rect.setFrameRoi(
frameMetrics: FrameMetrics,
@@ -5,11 +5,12 @@ import android.graphics.Canvas
fun Bitmap.fixTransparency(): Bitmap {
val result = analyzeTransparency()
return if (result.hasTransparentPixels) {
val cfg = config
return if (cfg != null && result.hasTransparentPixels) {
val copy = Bitmap.createBitmap(
width,
height,
config
cfg
)
Canvas(copy).apply {
drawColor(result.backgroundColor)
@@ -53,7 +53,7 @@ fun Context.writeExternalFile(
): Boolean = try {
openExternalOutputStream(fileName, mimeType).use { write(it) }
true
} catch (e: IOException) {
} catch (_: IOException) {
false
}
@@ -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()
}
@@ -1,6 +1,5 @@
package de.markusfisch.android.binaryeye.net
import de.markusfisch.android.binaryeye.content.toHexString
import de.markusfisch.android.binaryeye.database.Scan
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -36,7 +35,10 @@ private fun Scan.send(url: String, type: String): Response {
"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()
@@ -53,7 +55,7 @@ private fun Scan.send(url: String, type: String): Response {
}
else -> request(
url + content.urlEncode()
url + text.urlEncode()
)
}
}
@@ -67,7 +69,7 @@ private fun Scan.asJson() = JSONObject().apply {
}
private fun Scan.getMap(): Map<String, String> = mapOf(
"content" to content,
"content" to text,
"raw" to raw?.toHexString(),
"format" to format.name,
"errorCorrectionLevel" to errorCorrectionLevel,
@@ -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,46 @@
package de.markusfisch.android.binaryeye.preference
import android.content.SharedPreferences
import android.graphics.Point
import org.json.JSONObject
fun SharedPreferences.storeCropHandle(
name: String,
viewWidth: Int,
viewHeight: Int,
cropHandle: Point
) {
edit().putString(
cropHandleKey(name, viewWidth, viewHeight),
JSONObject().apply {
put("x", cropHandle.x)
put("y", cropHandle.y)
}.toString()
).apply()
}
fun SharedPreferences.restoreCropHandle(
name: String,
viewWidth: Int,
viewHeight: Int,
default: Point = Point(-2, -2) // -2 means set default roi.
): Point {
val s = getString(
cropHandleKey(name, viewWidth, viewHeight), null
) ?: return default
return try {
val json = JSONObject(s)
Point(
json.getInt("x"),
json.getInt("y")
)
} catch (_: Exception) {
return default
}
}
private fun cropHandleKey(
name: String,
viewWidth: Int,
viewHeight: Int
) = "${name}:${viewWidth}:${viewHeight}"
@@ -3,15 +3,28 @@ package de.markusfisch.android.binaryeye.preference
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import android.graphics.Point
import android.media.ToneGenerator
import android.os.Build
import android.preference.PreferenceManager
import android.support.annotation.RequiresApi
import de.markusfisch.android.binaryeye.automation.AutomatedAction
import de.markusfisch.android.binaryeye.automation.AutomatedAction.Companion.fromJsonArray
import de.markusfisch.android.binaryeye.automation.AutomatedAction.Companion.toJsonArray
import de.markusfisch.android.zxingcpp.ZxingCpp.BarcodeFormat
import org.json.JSONArray
class Preferences {
lateinit var defaultPreferences: SharedPreferences
lateinit var preferences: SharedPreferences
val profiles = ArrayList<String>()
var profile: String? = null
set(value) {
defaultPreferences.edit().putString(PROFILE, value).apply()
field = value
}
var barcodeFormats = setOf(
BarcodeFormat.AZTEC.name,
BarcodeFormat.CODABAR.name,
@@ -38,21 +51,6 @@ class Preferences {
apply(BARCODE_FORMATS, value)
field = value
}
var cropHandleX = -2 // -2 means set default roi.
set(value) {
apply(CROP_HANDLE_X, value)
field = value
}
var cropHandleY = -2
set(value) {
apply(CROP_HANDLE_Y, value)
field = value
}
var cropHandleOrientation = 0
set(value) {
apply(CROP_HANDLE_ORIENTATION, value)
field = value
}
var showCropHandle = true
set(value) {
apply(SHOW_CROP_HANDLE, value)
@@ -133,6 +131,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)
@@ -195,11 +198,6 @@ class Preferences {
apply(INDEX_OF_LAST_SELECTED_EC_LEVEL, value)
field = value
}
var lastMargin: Int = -1
set(value) {
apply(LAST_MARGIN, value)
field = value
}
var freeRotation = false
set(value) {
apply(FREE_ROTATION, value)
@@ -210,12 +208,83 @@ 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
}
var automatedActions = mutableListOf<AutomatedAction>()
private set
fun init(context: Context) {
preferences = PreferenceManager.getDefaultSharedPreferences(context)
// I'm not including a support library just to get the
// default preferences. Dependencies are a burden.
@Suppress("DEPRECATION")
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context)
loadProfiles()
load(context, defaultPreferences.getString(PROFILE, null))
}
fun load(context: Context, profileName: String?) {
preferences = if (profileName.isNullOrEmpty()) {
profile = null
defaultPreferences
} else {
profile = profileName
context.getSharedPreferences(
profileName,
Context.MODE_PRIVATE
)
}
update()
}
fun addProfile(name: String): Boolean {
if (!name.matches("^[a-zA-Z0-9_-]+$".toRegex()) ||
profiles.contains(name)
) {
return false
}
profiles.add(name)
saveProfiles()
return true
}
fun removeProfile(name: String): Boolean {
if (!profiles.remove(name)) {
return false
}
saveProfiles()
return true
}
fun saveProfiles() {
defaultPreferences.edit().putString(
PROFILES,
JSONArray(profiles).toString()
).apply()
}
fun loadProfiles() {
profiles.clear()
JSONArray(
defaultPreferences.getString(PROFILES, "[]")
).let {
profiles.addAll(Array(it.length()) { i -> it.getString(i) })
}
}
fun update() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
preferences.getStringSet(BARCODE_FORMATS, barcodeFormats)?.let {
@@ -227,12 +296,6 @@ class Preferences {
}
}
cropHandleX = preferences.getInt(CROP_HANDLE_X, cropHandleX)
cropHandleY = preferences.getInt(CROP_HANDLE_Y, cropHandleY)
cropHandleOrientation = preferences.getInt(
CROP_HANDLE_ORIENTATION,
cropHandleOrientation
)
showCropHandle = preferences.getBoolean(
SHOW_CROP_HANDLE,
showCropHandle
@@ -263,7 +326,10 @@ class Preferences {
ignoreDuplicatesName = "accept_duplicates"
}
preferences.getString(IGNORE_DUPLICATES_NAME, ignoreDuplicatesName)?.also {
preferences.getString(
IGNORE_DUPLICATES_NAME,
ignoreDuplicatesName
)?.also {
ignoreDuplicatesName = it
}
copyImmediately = preferences.getBoolean(
@@ -276,6 +342,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,
@@ -318,15 +387,28 @@ class Preferences {
INDEX_OF_LAST_SELECTED_EC_LEVEL,
indexOfLastSelectedEcLevel
)
lastMargin = preferences.getInt(
LAST_MARGIN,
lastMargin
)
freeRotation = preferences.getBoolean(FREE_ROTATION, freeRotation)
expandEscapeSequences = preferences.getBoolean(
EXPAND_ESCAPE_SEQUENCES,
expandEscapeSequences
)
addQuietZone = preferences.getBoolean(ADD_QUIET_ZONE, addQuietZone)
brightenScreen = preferences.getBoolean(
BRIGHTEN_SCREEN,
brightenScreen
)
previewScale = preferences.getFloat(
PREVIEW_SCALE,
previewScale
)
automatedActions = fromJsonArray(
preferences.getString(AUTOMATED_ACTIONS, "[]") ?: "[]"
)
}
fun setAutomatedActions(actions: List<AutomatedAction>) {
automatedActions = actions.toMutableList()
apply(AUTOMATED_ACTIONS, toJsonArray(actions))
}
private fun addFormatsOnUpdate(
@@ -342,6 +424,19 @@ class Preferences {
}
}
fun restoreCropHandle(
name: String,
viewWidth: Int,
viewHeight: Int
) = preferences.restoreCropHandle(name, viewWidth, viewHeight)
fun storeCropHandle(
name: String,
viewWidth: Int,
viewHeight: Int,
cropHandle: Point
) = preferences.storeCropHandle(name, viewWidth, viewHeight, cropHandle)
fun beepTone() = when (beepToneName) {
"tone_cdma_confirm" -> ToneGenerator.TONE_CDMA_CONFIRM
"tone_sup_radio_ack" -> ToneGenerator.TONE_SUP_RADIO_ACK
@@ -360,9 +455,6 @@ class Preferences {
private fun put(label: String, value: Boolean) =
preferences.edit().putBoolean(label, value)
private fun put(label: String, value: String) =
preferences.edit().putString(label, value)
private fun apply(label: String, value: Boolean) {
put(label, value).apply()
}
@@ -375,6 +467,10 @@ class Preferences {
preferences.edit().putInt(label, value).apply()
}
private fun apply(label: String, value: Float) {
preferences.edit().putFloat(label, value).apply()
}
@SuppressLint("ApplySharedPref")
private fun commit(label: String, value: String) {
preferences.edit().putString(label, value).commit()
@@ -390,10 +486,9 @@ class Preferences {
Consecutive, Any, Never
}
private const val PROFILES = "profiles"
private const val PROFILE = "profile"
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"
@@ -410,6 +505,7 @@ class Preferences {
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"
@@ -422,8 +518,11 @@ class Preferences {
private const val CUSTOM_LOCALE = "custom_locale"
private const val INDEX_OF_LAST_SELECTED_FORMAT = "index_of_last_selected_format"
private const val INDEX_OF_LAST_SELECTED_EC_LEVEL = "index_of_last_selected_ec_level"
private const val LAST_MARGIN = "last_margin"
private const val FREE_ROTATION = "free_rotation"
private const val EXPAND_ESCAPE_SEQUENCES = "expand_escape_sequences"
private const val ADD_QUIET_ZONE = "add_quiet_zone"
private const val BRIGHTEN_SCREEN = "brighten_screen"
private const val PREVIEW_SCALE = "preview_scale"
private const val AUTOMATED_ACTIONS = "automated_actions"
}
}
@@ -70,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()
}
@@ -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() {
@@ -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,10 +2,13 @@ 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
import android.support.v4.content.ContextCompat
import android.util.AttributeSet
import android.view.MotionEvent
@@ -23,10 +26,10 @@ class DetectorView : View {
val coordinates = FloatArray(32)
val roi = Rect()
var cropHandleName: String? = null
var onRoiChange: (() -> Unit)? = null
var onRoiChanged: (() -> Unit)? = null
private val currentOrientation = resources.configuration.orientation
private val invalidateRunnable: Runnable = Runnable {
coordinatesLast = 0
invalidate()
@@ -42,7 +45,8 @@ class DetectorView : View {
)
private val handleXRadius = handleBitmap.width / 2
private val handleYRadius = handleBitmap.height / 2
private val handlePos = Point(-1, -1)
private val inactiveHandlePos = Point(-1, -1)
private val handlePos = Point(inactiveHandlePos)
private val handleHome = Point()
private val center = Point()
private val touchDown = Point()
@@ -52,6 +56,8 @@ class DetectorView : View {
private val fabHeight: Int
private val padding: Int
private var viewWidth = 0
private var viewHeight = 0
private var coordinatesLast = 0
private var handleGrabbed = false
private var handleActive = false
@@ -78,37 +84,38 @@ class DetectorView : View {
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) :
super(context, attrs, defStyleAttr)
fun saveCropHandlePos() {
val pos = getCropHandlePos()
prefs.cropHandleX = pos.x
prefs.cropHandleY = pos.y
prefs.cropHandleOrientation = currentOrientation
}
private fun getCropHandlePos() = if (handleActive) {
handlePos
} else {
Point(-1, -1)
}
fun restoreCropHandlePos() {
setCropHandlePos(
prefs.cropHandleX,
prefs.cropHandleY,
prefs.cropHandleOrientation
fun storeCropHandlePos() {
val name = cropHandleName ?: return
val pos = if (handleActive) {
handlePos
} else {
inactiveHandlePos
}
prefs.storeCropHandle(
name,
viewWidth,
viewHeight,
pos
)
}
private fun setCropHandlePos(x: Int, y: Int, orientation: Int) {
// Always set handlePos even if it's invalid because
// handlePos.x may be -2 which is a signal to set the
// default ROI.
if (orientation == currentOrientation) {
handlePos.set(x, y)
} else {
handlePos.set(y, x)
fun updateCropHandlePos() {
val name = cropHandleName ?: return
if (viewWidth < 1 || viewHeight < 1) {
return
}
val cropHandle = prefs.restoreCropHandle(name, viewWidth, viewHeight)
if (cropHandle.x == -2) {
setHandleToDefaultRoi()
} else {
handlePos.set(cropHandle.x, cropHandle.y)
handleActive = handlePos.x > -1
}
if (handleActive) {
updateClipRect()
} else {
reset()
}
handleActive = handlePos.x > -1
}
fun update(numberOfCoordinates: Int) {
@@ -118,31 +125,6 @@ class DetectorView : View {
postDelayed(invalidateRunnable, 500)
}
override fun onSaveInstanceState(): Parcelable? {
if (!handleActive) {
return super.onSaveInstanceState()
}
return SavedState(super.onSaveInstanceState()).apply {
savedHandlePos.set(getCropHandlePos())
savedOrientation = currentOrientation
}
}
override fun onRestoreInstanceState(state: Parcelable) {
super.onRestoreInstanceState(
if (state is SavedState) {
setCropHandlePos(
state.savedHandlePos.x,
state.savedHandlePos.y,
state.savedOrientation
)
state.superState
} else {
state
}
)
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent?): Boolean {
event ?: return super.onTouchEvent(event)
@@ -239,28 +221,27 @@ 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
viewWidth = right - left
viewHeight = bottom - top
center.set(
left + (width / 2),
top + (height / 2)
left + (viewWidth / 2),
top + (viewHeight / 2)
)
minY = padding * 2
maxY = height - minY
maxY = viewHeight - minY
handleHome.set(
width - handleXRadius - paddingRight - padding,
height - handleYRadius - paddingBottom - fabHeight
viewWidth - handleXRadius - paddingRight - padding,
viewHeight - handleYRadius - paddingBottom - fabHeight
)
if (handlePos.x == -2) {
setHandleToDefaultRoi()
}
if (handleActive) {
updateClipRect()
} else {
reset()
}
updateCropHandlePos()
}
override fun onDraw(canvas: Canvas) {
@@ -344,37 +325,6 @@ class DetectorView : View {
private fun clampX(x: Int) = min(center.x * 2, max(0, x))
private fun clampY(y: Int) = min(maxY, max(minY, y))
internal class SavedState : BaseSavedState {
val savedHandlePos = Point()
var savedOrientation = 0
constructor(superState: Parcelable?) : super(superState)
private constructor(parcel: Parcel) : super(parcel) {
savedHandlePos.set(
parcel.readInt(),
parcel.readInt()
)
savedOrientation = parcel.readInt()
}
override fun writeToParcel(out: Parcel, flags: Int) {
super.writeToParcel(out, flags)
out.writeInt(savedHandlePos.x)
out.writeInt(savedHandlePos.y)
out.writeInt(savedOrientation)
}
companion object {
@JvmField
val CREATOR = object : Parcelable.Creator<SavedState> {
override fun createFromParcel(source: Parcel) = SavedState(source)
override fun newArray(size: Int): Array<SavedState?> = arrayOfNulls(size)
}
}
}
}
private fun distSq(a: Point, b: Point): Int {
@@ -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>
@@ -1,3 +1,3 @@
<vector android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M14.54,18.49L14.54,16.51L17.51,16.51L17.51,13.54L19.49,13.54L19.49,16.51L22.46,16.51L22.46,18.49L19.508,18.508L19.49,21.46L17.51,21.46L17.492,18.508L14.54,18.49ZM13.35,14.75L11,12.4L11,7L13,7L13,11.6L14.4,13L13.35,14.75ZM3,10L3,4L5,4L5,6.35C5.85,5.283 6.888,4.458 8.113,3.875C9.338,3.292 10.633,3 12,3C14.5,3 16.625,3.875 18.375,5.625C20.125,7.375 21,9.5 21,12L19,12C19,10.05 18.321,8.396 16.963,7.037C15.604,5.679 13.95,5 12,5C10.85,5 9.775,5.267 8.775,5.8C7.775,6.333 6.933,7.067 6.25,8L9,8L9,10L3,10ZM12,21C9.7,21 7.696,20.237 5.988,18.712C4.279,17.187 3.3,15.283 3.05,13L5.1,13C5.333,14.733 6.104,16.167 7.413,17.3C8.721,18.433 10.25,19 12,19C12.183,19 12.354,18.996 12.513,18.987C12.671,18.979 12.833,18.958 13,18.925L13,20.95C12.833,20.967 12.671,20.979 12.513,20.987C12.354,20.996 12.183,21 12,21Z"/>
<path android:fillColor="#FF000000" android:pathData="M14.54 18.49v-1.98h2.97v-2.97h1.98v2.97h2.97v1.98l-2.952 0.018-0.018 2.952h-1.98l-0.018-2.952-2.952-0.018zm-1.19-3.74L11 12.4V7h2v4.6l1.4 1.4-1.05 1.75zM3 10V4h2v2.35c0.85-1.067 1.888-1.892 3.113-2.475C9.338 3.292 10.633 3 12 3c2.5 0 4.625 0.875 6.375 2.625S21 9.5 21 12h-2c0-1.95-0.679-3.604-2.037-4.963C15.604 5.679 13.95 5 12 5c-1.15 0-2.225 0.267-3.225 0.8S6.933 7.067 6.25 8H9v2H3zm9 11c-2.3 0-4.304-0.763-6.012-2.288C4.279 17.187 3.3 15.283 3.05 13H5.1c0.233 1.733 1.004 3.167 2.313 4.3C8.721 18.433 10.25 19 12 19c0.183 0 0.354-0.004 0.513-0.013 0.158-0.008 0.32-0.029 0.487-0.062v2.025c-0.167 0.017-0.329 0.029-0.487 0.037C12.354 20.996 12.183 21 12 21z"/>
</vector>
@@ -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:height="24dp" android:tint="#FFFFFF" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="#fff" android:pathData="M480,480Q414,480 367,433Q320,386 320,320Q320,254 367,207Q414,160 480,160Q546,160 593,207Q640,254 640,320Q640,386 593,433Q546,480 480,480ZM160,800L160,688Q160,654 177.5,625.5Q195,597 224,582Q286,551 350,535.5Q414,520 480,520Q546,520 610,535.5Q674,551 736,582Q765,597 782.5,625.5Q800,654 800,688L800,800L160,800ZM240,720L720,720L720,688Q720,677 714.5,668Q709,659 700,654Q646,627 591,613.5Q536,600 480,600Q424,600 369,613.5Q314,627 260,654Q251,659 245.5,668Q240,677 240,688L240,720ZM480,400Q513,400 536.5,376.5Q560,353 560,320Q560,287 536.5,263.5Q513,240 480,240Q447,240 423.5,263.5Q400,287 400,320Q400,353 423.5,376.5Q447,400 480,400ZM480,320Q480,320 480,320Q480,320 480,320Q480,320 480,320Q480,320 480,320Q480,320 480,320Q480,320 480,320Q480,320 480,320Q480,320 480,320ZM480,720L480,720Q480,720 480,720Q480,720 480,720Q480,720 480,720Q480,720 480,720Q480,720 480,720Q480,720 480,720Q480,720 480,720Q480,720 480,720L480,720Z"/>
</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>
+2 -1
View File
@@ -8,9 +8,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
android:fitsSystemWindows="@bool/fitsSystemWindows">
<include layout="@layout/toolbar"/>
<SeekBar
android:id="@+id/zoom"
+3 -1
View File
@@ -4,9 +4,11 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<FrameLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
android:fitsSystemWindows="@bool/fitsSystemWindows">
<include layout="@layout/toolbar"/>
</FrameLayout>
<include layout="@layout/navbar"/>
</merge>
+3 -1
View File
@@ -9,9 +9,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
android:fitsSystemWindows="@bool/fitsSystemWindows">
<include layout="@layout/toolbar"/>
<android.support.design.widget.FloatingActionButton
style="@style/FAB"
@@ -19,4 +20,5 @@
android:contentDescription="@string/scan_code"
android:src="@drawable/ic_action_next"/>
</android.support.design.widget.CoordinatorLayout>
<include layout="@layout/navbar"/>
</merge>
@@ -0,0 +1,57 @@
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<!-- The FrameLayout is necessary because this LinearLayout
should have a padding which would be reset by the window
insets from `fitsSystemWindows`. -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="bottom"
android:padding="24dp"
android:orientation="vertical"
tools:ignore="UselessParent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:drawableTop="@drawable/logo"
android:drawablePadding="16dp"
android:gravity="center"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="@string/welcome"/>
<LinearLayout
style="@style/WelcomeButton"
android:id="@+id/expert"
android:layout_marginBottom="16dp"
android:orientation="vertical">
<TextView
style="@style/WelcomeButtonText"
android:text="@string/mode_expert"/>
<TextView
style="@style/WelcomeButtonSubText"
android:text="@string/mode_expert_description"/>
</LinearLayout>
<LinearLayout
style="@style/WelcomeButton"
android:id="@+id/simple"
android:orientation="vertical">
<TextView
style="@style/WelcomeButtonText"
android:text="@string/mode_simple"/>
<TextView
style="@style/WelcomeButtonSubText"
android:text="@string/mode_simple_description"/>
</LinearLayout>
</LinearLayout>
</FrameLayout>
</ScrollView>
@@ -0,0 +1,76 @@
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
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:text="@string/automated_action_regex"/>
<EditText
android:id="@+id/regex"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/automated_action_regex_hint"
android:inputType="text"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="12dp"
android:text="@string/automated_action_type"/>
<RadioGroup
android:id="@+id/action_type"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioButton
android:id="@+id/action_type_intent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/automated_action_type_intent"/>
<RadioButton
android:id="@+id/action_type_custom_intent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/automated_action_type_custom_intent"/>
</RadioGroup>
<LinearLayout
android:id="@+id/url_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:text="@string/automated_action_url_template"/>
<EditText
android:id="@+id/url_template"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/automated_action_url_hint"
android:inputType="textUri"/>
</LinearLayout>
<LinearLayout
android:id="@+id/intent_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:text="@string/automated_action_intent_uri_template"/>
<EditText
android:id="@+id/intent_uri_template"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/automated_action_intent_uri_hint"
android:inputType="textUri"/>
</LinearLayout>
</LinearLayout>
</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,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/profile_enter_name"/>
<EditText
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textShortMessage|textAutoCorrect|textCapSentences"
android:imeOptions="actionDone"
android:hint="@string/profile_enter_name_hint"
android:maxLines="1"
android:maxLength="1024"
android:importantForAutofill="no"/>
</LinearLayout>
+23
View File
@@ -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>
@@ -0,0 +1,31 @@
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/actions"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:divider="@color/separator"
android:dividerHeight="1px"/>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/inset_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:visibility="gone"
android:id="@+id/no_actions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:padding="32dp"
android:text="@string/automated_actions_empty"/>
<android.support.design.widget.FloatingActionButton
style="@style/FAB"
android:id="@+id/add"
android:contentDescription="@string/automated_action_add"
android:src="@drawable/ic_action_create"/>
</android.support.design.widget.CoordinatorLayout>
</FrameLayout>
+4 -34
View File
@@ -65,42 +65,12 @@
android:id="@+id/colors"
android:layout_marginBottom="16dp"
android:entries="@array/colors"/>
<TextView
android:id="@+id/size_display"
<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: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="4"
android:max="7"/>
<LinearLayout
android:id="@+id/margin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:id="@+id/margin_display"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"/>
<SeekBar
style="@style/Widget.AppCompat.SeekBar.Discrete"
android:id="@+id/margin_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:max="32"/>
</LinearLayout>
android:text="@string/add_quiet_zone"/>
</LinearLayout>
</de.markusfisch.android.binaryeye.widget.ConfinedScrollView>
<android.support.design.widget.CoordinatorLayout
@@ -0,0 +1,31 @@
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/profiles"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:divider="@color/separator"
android:dividerHeight="1px"/>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/inset_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:visibility="gone"
android:id="@+id/no_profiles"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:padding="32dp"
android:text="@string/profile_empty"/>
<android.support.design.widget.FloatingActionButton
style="@style/FAB"
android:id="@+id/add"
android:contentDescription="@string/profile_add"
android:src="@drawable/ic_action_create"/>
</android.support.design.widget.CoordinatorLayout>
</FrameLayout>
+8
View File
@@ -0,0 +1,8 @@
<!-- Navigation bar background from SDK35+ -->
<View
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/navbar"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="bottom"
android:visibility="gone"/>
+10
View File
@@ -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"
@@ -42,4 +47,9 @@
android:title="@string/info"
android:icon="@drawable/ic_action_info"
material:showAsAction="ifRoom"/>
<item
android:id="@+id/profile"
android:title="@string/profile_default"
android:icon="@drawable/ic_action_profile"
material:showAsAction="ifRoom"/>
</menu>
@@ -11,9 +11,20 @@
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>
+6 -1
View File
@@ -18,6 +18,11 @@
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"
@@ -26,7 +31,7 @@
<item
android:id="@+id/share_file"
android:title="@string/share_file"
android:icon="@drawable/ic_action_share"
android:icon="@drawable/ic_action_pick_file"
material:showAsAction="ifRoom"/>
<item
android:id="@+id/create"
+277
View File
@@ -0,0 +1,277 @@
<?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="does_not_match_search_term">لا يطابق مصطلح البحث</string>
<string name="scan_for">مسح %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="profile_category">الملف الشخصي</string>
<string name="profile">الملف الشخصي</string>
<string name="profile_default">افتراضي</string>
<string name="profile_add">إضافة ملف شخصي جديد</string>
<string name="profile_enter_name">أدخل اسم الملف الشخصي</string>
<string name="profile_enter_name_hint">الاسم</string>
<string name="profile_invalid_name">اسم الملف الشخصي غير صالح</string>
<string name="profile_empty">لا توجد ملفات شخصية بعد</string>
<string name="profile_current">الملف الشخصي الحالي</string>
<string name="profile_remove_confirm">إزالة هذا الملف الشخصي؟</string>
<string name="current_profile">الملف الشخصي: %s</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="automated_actions">الإجراءات الآلية</string>
<string name="automated_actions_none">لا توجد إجراءات آلية</string>
<string name="automated_actions_empty">لا توجد إجراءات آلية بعد</string>
<plurals name="automated_actions_count">
<item quantity="zero">%d إجراء آلي</item>
<item quantity="one">%d إجراء آلي</item>
<item quantity="two">%d إجراءان آليان</item>
<item quantity="few">%d إجراءات آلية</item>
<item quantity="many">%d إجراءً آليًا</item>
<item quantity="other">%d إجراءات آلية</item>
</plurals>
<string name="automated_action_add">إضافة إجراء</string>
<string name="automated_action_edit">تعديل الإجراء</string>
<string name="automated_action_regex">تعبير نمطي</string>
<string name="automated_action_regex_hint">مثل ^https?://.*</string>
<string name="automated_action_regex_required">التعبير النمطي مطلوب</string>
<string name="automated_action_regex_invalid">تعبير نمطي غير صالح</string>
<string name="automated_action_type">نوع الإجراء</string>
<string name="automated_action_type_intent">فتح URL</string>
<string name="automated_action_url_template">قالب URL</string>
<string name="automated_action_url_hint">https://example.com/?q={RESULT}</string>
<string name="automated_action_url_required">قالب URL مطلوب</string>
<string name="automated_action_invalid_url">قالب URL غير صالح</string>
<string name="automated_action_remove_confirm">إزالة هذا الإجراء؟</string>
<string name="automated_action_list_intent">فتح URL: %s</string>
<string name="automated_action_list_intent_empty">فتح URL</string>
<string name="automated_action_webview_title">إجراء آلي</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>
<string name="automated_action_type_custom_intent">Intent مخصص</string>
<string name="automated_action_intent_uri_template">قالب عنوان Intent</string>
<string name="automated_action_intent_uri_hint">intent:#Intent;action=android.intent.action.SEND;type=text/plain;S.android.intent.extra.TEXT={RESULT};end</string>
<string name="automated_action_intent_uri_required">عنوان Intent مطلوب</string>
<string name="automated_action_intent_uri_invalid">عنوان Intent غير صالح</string>
<string name="automated_action_list_custom_intent">Intent مخصص: %s</string>
<string name="automated_action_list_custom_intent_empty">Intent مخصص</string>
</resources>
+75 -9
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">Добре дошли!</string>
<string name="mode_expert">Разширен</string>
<string name="mode_expert_description">Искам да проверя прочетеното съдържание и да го отворя ръчно.</string>
<string name="mode_simple">Опростен</string>
<string name="mode_simple_description">Искам просто да сканирам баркодове и да ги отварям веднага.</string>
<string name="camera_error">Няма достъп до камерата, моля, опитайте отново</string>
<string name="scan_code">Сканирайте код</string>
<string name="compose_barcode">Създаване на баркод</string>
@@ -14,7 +19,7 @@
<string name="error_correction_level_m" formatted="false">Средно (~15&#37; корекция)</string>
<string name="error_correction_level_q" formatted="false">Четвъртично (~25&#37; корекция)</string>
<string name="error_correction_level_h" formatted="false">Високо (~30&#37; корекция)</string>
<string name="label">Label</string>
<string name="label">Етикет</string>
<string name="colors">Преден и заден план</string>
<string name="colors_black_on_white">Черно на бяло</string>
<string name="colors_white_on_black">Бяло на черно</string>
@@ -29,21 +34,23 @@
<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">Share as file</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">Отворете 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="margin_size">Марж в пиксели: %1$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>
@@ -57,7 +64,11 @@
<string name="bulk_mode_delay">Забавяне между непрекъснатите сканирания</string>
<string name="restrict_format">Ограничаване на формат</string>
<string name="remove_restriction">Премахване на ограничението</string>
<string name="scan_format">Сканиране %s</string>
<string name="find_code">Намерете кода</string>
<string name="find_code_with_content">Какво съдържание?</string>
<string name="find_code_hint">Термин или регулярен израз</string>
<string name="does_not_match_search_term">Не съответства на търсения термин</string>
<string name="scan_for">Сканиране %s</string>
<string name="quarter_second">Червърт секунда</string>
<string name="half_second">Половин секунда</string>
<string name="a_second">Една секунда</string>
@@ -68,6 +79,17 @@
<string name="scan_category">Сканиране</string>
<string name="content_category">Съдържание</string>
<string name="locale_category">Език</string>
<string name="profile_category">Профил</string>
<string name="profile">Профил</string>
<string name="profile_default">По подразбиране</string>
<string name="profile_add">Добавяне на нов профил</string>
<string name="profile_enter_name">Въведете име на профила</string>
<string name="profile_enter_name_hint">Име</string>
<string name="profile_invalid_name">Невалидно име на профила</string>
<string name="profile_empty">Все още няма профили</string>
<string name="profile_current">Текущ профил</string>
<string name="profile_remove_confirm">Премахване на този профил?</string>
<string name="current_profile">Профил: %s</string>
<string name="custom_locale">Персонализиран език</string>
<string name="follow_system_settings">Следване на системните настройки</string>
<string name="show_crop_handle">Показване на ограничителя за изрязване</string>
@@ -94,10 +116,10 @@
<string name="tone_prop_beep2">Общ двоен звуков сигнал</string>
<string name="use_history">Хронология на сканирането</string>
<string name="use_history_summary">Запазва разпознатите кодове на вашето устройство.</string>
<string name="ignore_duplicates">Ignore duplicates</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">Accept duplicates</string>
<string name="accept_duplicates">Приемане на дубликати</string>
<string name="open_immediately">Отваряй внезапно</string>
<string name="open_immediately_summary">Пропуска проверката и внезапно отваря съдържанието. Може да отвори вредно съдържание, ако е активирано.</string>
<string name="copy_immediately">Поставяй в клипборда</string>
@@ -106,6 +128,8 @@
<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>
@@ -113,6 +137,30 @@
<string name="default_search_engine">Отворяй неизвестни данни в</string>
<string name="always_ask">Винаги питай</string>
<string name="open_with_url">Отворяне на неизвестни данни с URL</string>
<string name="automated_actions">Автоматизирани действия</string>
<string name="automated_actions_none">Няма автоматизирани действия</string>
<string name="automated_actions_empty">Все още няма автоматизирани действия</string>
<plurals name="automated_actions_count">
<item quantity="one">%d автоматизирано действие</item>
<item quantity="other">%d автоматизирани действия</item>
</plurals>
<string name="automated_action_add">Добавяне на действие</string>
<string name="automated_action_edit">Редактиране на действие</string>
<string name="automated_action_regex">Регулярен израз</string>
<string name="automated_action_regex_hint">напр. ^https?://.*</string>
<string name="automated_action_regex_required">Регулярен израз е задължителен</string>
<string name="automated_action_regex_invalid">Невалиден регулярен израз</string>
<string name="automated_action_type">Тип действие</string>
<string name="automated_action_type_intent">Отваряне на URL</string>
<string name="automated_action_url_template">Шаблон на URL</string>
<string name="automated_action_url_hint">https://example.com/?q={RESULT}</string>
<string name="automated_action_url_required">Шаблон на URL е задължителен</string>
<string name="automated_action_invalid_url">Невалиден шаблон на URL</string>
<string name="automated_action_remove_confirm">Да се премахне ли това действие?</string>
<string name="automated_action_list_intent">Отваряне на URL: %s</string>
<string name="automated_action_list_intent_empty">Отваряне на URL</string>
<string name="automated_action_webview_title">Автоматизирано действие</string>
<string name="clear_network_suggestions">Премахнете мрежовите предложения</string>
<string name="clear_network_suggestions_summary">Премахване на мрежовите предложения, които преди са били добавени с това приложение.</string>
<string name="really_remove_all_networks">Наистина ли искате да премахнете всички мрежи?</string>
@@ -136,6 +184,9 @@
<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>
@@ -169,8 +220,8 @@
<string name="otpauth_add">Добави 2FA</string>
<string name="search_web">Търси в мрежата</string>
<string name="search_scan">Търсещо сканиране</string>
<string name="add_to_history">Add to history</string>
<string name="added_to_history">Added to history</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>
@@ -185,6 +236,7 @@
<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>
@@ -199,4 +251,18 @@
<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>
<string name="automated_action_type_custom_intent">Персонализиран Intent</string>
<string name="automated_action_intent_uri_template">Шаблон за Intent URI</string>
<string name="automated_action_intent_uri_hint">intent:#Intent;action=android.intent.action.SEND;type=text/plain;S.android.intent.extra.TEXT={RESULT};end</string>
<string name="automated_action_intent_uri_required">Intent URI е задължителен</string>
<string name="automated_action_intent_uri_invalid">Невалиден Intent URI</string>
<string name="automated_action_list_custom_intent">Персонализиран Intent: %s</string>
<string name="automated_action_list_custom_intent_empty">Персонализиран Intent</string>
</resources>
+130 -64
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">স্বাগতম!</string>
<string name="mode_expert">উন্নত</string>
<string name="mode_expert_description">আমি পড়া বিষয়বস্তু যাচাই করতে চাই এবং এটি নিজে খুলতে চাই।</string>
<string name="mode_simple">সহজ</string>
<string name="mode_simple_description">আমি কেবল বারকোড স্ক্যান করতে চাই এবং তা সঙ্গে সঙ্গে খুলতে চাই।</string>
<string name="camera_error">ক্যামেরা পাওয়া যাচ্ছে না, পরে চেষ্টা করো</string>
<string name="scan_code">সংকেতের পাঠোদ্ধার</string>
<string name="compose_barcode">বারকোড বানাও</string>
@@ -14,38 +19,40 @@
<string name="error_correction_level_m" formatted="false">মধ্যম (~১৫&#37; ঠিককরণ)</string>
<string name="error_correction_level_q" formatted="false">বেশি (~২৫&#37; ঠিককরণ)</string>
<string name="error_correction_level_h" formatted="false">অনেক বেশি (~৩০&#37; ঠিককরণ)</string>
<string name="label">Label</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">Share as file</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_width_by_height">পিক্সেলে আকা:%1$d×%2$d</string>
<string name="margin_size">Margin in pixels: %1$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>
@@ -54,25 +61,40 @@
<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="does_not_match_search_term">অনুসন্ধান শর্তের সাথে মিলে না</string>
<string name="scan_for">স্ক্যান %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="profile_category">প্রোফাইল</string>
<string name="profile">প্রোফাইল</string>
<string name="profile_default">ডিফল্ট</string>
<string name="profile_add">নতুন প্রোফাইল যোগ করো</string>
<string name="profile_enter_name">প্রোফাইলের নাম লেখো</string>
<string name="profile_enter_name_hint">নাম</string>
<string name="profile_invalid_name">প্রোফাইলের নাম অবৈধ</string>
<string name="profile_empty">এখনও কোনো প্রোফাইল নেই</string>
<string name="profile_current">বর্তমান প্রোফাইল</string>
<string name="profile_remove_confirm">এই প্রোফাইলটি সরাবেন?</string>
<string name="current_profile">প্রোফাইল: %s</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 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>
@@ -83,21 +105,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">Ignore duplicates</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">Accept duplicates</string>
<string name="accept_duplicates">ডুপ্লিকেট গ্রহণ করুন</string>
<string name="open_immediately">সাথে সাথে খুলো</string>
<string name="open_immediately_summary">না দেখে সাথে সাথে খুলো। সক্রিয় থাকলে ক্ষতিকারক তথ্য খুলতে পারে।</string>
<string name="copy_immediately">ক্লিপবোর্ডে রাখো</string>
@@ -106,20 +128,46 @@
<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="automated_actions">স্বয়ংক্রিয় ক্রিয়াসমূহ</string>
<string name="automated_actions_none">কোনো স্বয়ংক্রিয় ক্রিয়া নেই</string>
<string name="automated_actions_empty">এখনো কোনো স্বয়ংক্রিয় ক্রিয়া নেই</string>
<plurals name="automated_actions_count">
<item quantity="one">%dটি স্বয়ংক্রিয় ক্রিয়া</item>
<item quantity="other">%dটি স্বয়ংক্রিয় ক্রিয়া</item>
</plurals>
<string name="automated_action_add">ক্রিয়া যোগ করুন</string>
<string name="automated_action_edit">ক্রিয়া সম্পাদনা</string>
<string name="automated_action_regex">রেগেক্স</string>
<string name="automated_action_regex_hint">উদাহরণ ^https?://.*</string>
<string name="automated_action_regex_required">রেগেক্স প্রয়োজন</string>
<string name="automated_action_regex_invalid">অবৈধ রেগেক্স</string>
<string name="automated_action_type">ক্রিয়ার ধরন</string>
<string name="automated_action_type_intent">URL খুলুন</string>
<string name="automated_action_url_template">URL টেমপ্লেট</string>
<string name="automated_action_url_hint">https://example.com/?q={RESULT}</string>
<string name="automated_action_url_required">URL টেমপ্লেট প্রয়োজন</string>
<string name="automated_action_invalid_url">অবৈধ URL টেমপ্লেট</string>
<string name="automated_action_remove_confirm">এই ক্রিয়াটি সরাবেন?</string>
<string name="automated_action_list_intent">URL খুলুন: %s</string>
<string name="automated_action_list_intent_empty">URL খুলুন</string>
<string name="automated_action_webview_title">স্বয়ংক্রিয় ক্রিয়া</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>
@@ -127,15 +175,18 @@
<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>
@@ -169,8 +220,8 @@
<string name="otpauth_add">২এফএ যোগ করো</string>
<string name="search_web">ওয়েবে খুঁজো</string>
<string name="search_scan">আগেরগুলোতে খুঁজো</string>
<string name="add_to_history">Add to history</string>
<string name="added_to_history">Added to history</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>
@@ -179,24 +230,39 @@
<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">Pick a 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>
<string name="automated_action_type_custom_intent">কাস্টম Intent</string>
<string name="automated_action_intent_uri_template">Intent URI টেমপ্লেট</string>
<string name="automated_action_intent_uri_hint">intent:#Intent;action=android.intent.action.SEND;type=text/plain;S.android.intent.extra.TEXT={RESULT};end</string>
<string name="automated_action_intent_uri_required">Intent URI প্রয়োজন</string>
<string name="automated_action_intent_uri_invalid">অবৈধ Intent URI</string>
<string name="automated_action_list_custom_intent">কাস্টম Intent: %s</string>
<string name="automated_action_list_custom_intent_empty">কাস্টম Intent</string>
</resources>
+88 -19
View File
@@ -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,11 +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&#37; korekce)</string>
<string name="error_correction_level_m" formatted="false">Střední (~15&#37; korekce)</string>
<string name="error_correction_level_q" formatted="false">Čtvrtinový (~25&#37; korekce)</string>
<string name="error_correction_level_h" formatted="false">Vysoký (~30&#37; korekce)</string>
<string name="label">Label</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>
@@ -31,21 +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_file">Sdílet jako soubor</string>
<string name="share_as">Sdílet jako</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_width_by_height">Velikost v pixelech: %1$d×%2$d</string>
<string name="margin_size">Okraje v pixelech: %1$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>
@@ -59,7 +67,11 @@
<string name="bulk_mode_delay">Prodleva mezi skeny</string>
<string name="restrict_format">Omezit na formát</string>
<string name="remove_restriction">Zrušit omezení formátu</string>
<string name="scan_format">Naskenovat %s</string>
<string name="find_code">Najít kód</string>
<string name="find_code_with_content">Co obsah?</string>
<string name="find_code_hint">Termín nebo regulární výraz</string>
<string name="does_not_match_search_term">Neodpovídá vyhledávacímu termínu</string>
<string name="scan_for">Naskenovat %s</string>
<string name="quarter_second">Čtvrt sekundy</string>
<string name="half_second">Půl sekundy</string>
<string name="a_second">Jedna sekunda</string>
@@ -70,6 +82,17 @@
<string name="scan_category">Skenování</string>
<string name="content_category">Obsah</string>
<string name="locale_category">Jazyk</string>
<string name="profile_category">Profil</string>
<string name="profile">Profil</string>
<string name="profile_default">Výchozí</string>
<string name="profile_add">Přidat nový profil</string>
<string name="profile_enter_name">Zadejte název profilu</string>
<string name="profile_enter_name_hint">Název</string>
<string name="profile_invalid_name">Neplatný název profilu</string>
<string name="profile_empty">Zatím žádné profily</string>
<string name="profile_current">Aktuální profil</string>
<string name="profile_remove_confirm">Odebrat tento profil?</string>
<string name="current_profile">Profil: %s</string>
<string name="custom_locale">Nastavit jazyk</string>
<string name="follow_system_settings">Podle systému</string>
<string name="show_crop_handle">Zobrazovat hrany výřezu</string>
@@ -89,17 +112,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">Ignore duplicates</string>
<string name="ignore_duplicates">Ignorovat duplicity</string>
<string name="ignore_consecutive_duplicates">Ignorovat duplikáty</string>
<string name="ignore_any_duplicates">Ignore any duplicates</string>
<string name="accept_duplicates">Accept 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>
@@ -108,6 +131,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,6 +140,32 @@
<string name="default_search_engine">Nerozpoznaná data otevřít v</string>
<string name="always_ask">Vždy se zeptat</string>
<string name="open_with_url">Otevřít nerozpoznaná data pomocí URL</string>
<string name="automated_actions">Automatizované akce</string>
<string name="automated_actions_none">Žádné automatizované akce</string>
<string name="automated_actions_empty">Zatím žádné automatizované akce</string>
<plurals name="automated_actions_count">
<item quantity="one">%d automatizovaná akce</item>
<item quantity="few">%d automatizované akce</item>
<item quantity="many">%d automatizovaných akcí</item>
<item quantity="other">%d automatizované akce</item>
</plurals>
<string name="automated_action_add">Přidat akci</string>
<string name="automated_action_edit">Upravit akci</string>
<string name="automated_action_regex">Regulární výraz</string>
<string name="automated_action_regex_hint">např. ^https?://.*</string>
<string name="automated_action_regex_required">Regulární výraz je povinný</string>
<string name="automated_action_regex_invalid">Neplatný regulární výraz</string>
<string name="automated_action_type">Typ akce</string>
<string name="automated_action_type_intent">Otevřít URL</string>
<string name="automated_action_url_template">Šablona URL</string>
<string name="automated_action_url_hint">https://example.com/?q={RESULT}</string>
<string name="automated_action_url_required">Šablona URL je povinná</string>
<string name="automated_action_invalid_url">Neplatná šablona URL</string>
<string name="automated_action_remove_confirm">Odstranit tuto akci?</string>
<string name="automated_action_list_intent">Otevřít URL: %s</string>
<string name="automated_action_list_intent_empty">Otevřít URL</string>
<string name="automated_action_webview_title">Automatizovaná akce</string>
<string name="clear_network_suggestions">Smazat navrhované sítě</string>
<string name="clear_network_suggestions_summary">Smazat všechny sítě, které byly navrženy touto aplikací.</string>
<string name="really_remove_all_networks">Opravdu smazat všechny sítě?</string>
@@ -122,11 +173,11 @@
<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>
@@ -138,6 +189,9 @@
<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>
@@ -151,7 +205,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>
@@ -171,10 +225,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">Add to history</string>
<string name="added_to_history">Added to history</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>
@@ -187,6 +241,7 @@
<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>
@@ -201,4 +256,18 @@
<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>
<string name="automated_action_type_custom_intent">Vlastní intent</string>
<string name="automated_action_intent_uri_template">Šablona URI intentu</string>
<string name="automated_action_intent_uri_hint">intent:#Intent;action=android.intent.action.SEND;type=text/plain;S.android.intent.extra.TEXT={RESULT};end</string>
<string name="automated_action_intent_uri_required">URI intentu je povinné</string>
<string name="automated_action_intent_uri_invalid">Neplatné URI intentu</string>
<string name="automated_action_list_custom_intent">Vlastní intent: %s</string>
<string name="automated_action_list_custom_intent_empty">Vlastní intent</string>
</resources>
+82 -16
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">Velkommen!</string>
<string name="mode_expert">Avanceret</string>
<string name="mode_expert_description">Jeg vil kontrollere det læste indhold og åbne det manuelt.</string>
<string name="mode_simple">Simpel</string>
<string name="mode_simple_description">Jeg vil bare scanne stregkoder og få dem åbnet med det samme.</string>
<string name="camera_error">Kan ikke få adgang til kamera, prøv igen</string>
<string name="scan_code">Scan kode</string>
<string name="compose_barcode">Opret stregkode</string>
@@ -14,7 +19,7 @@
<string name="error_correction_level_m" formatted="false">Medium (~15&#37; korrigering)</string>
<string name="error_correction_level_q" formatted="false">Kvartil (~25&#37; korrigering)</string>
<string name="error_correction_level_h" formatted="false">Høj (~30&#37; korrigering)</string>
<string name="label">Label</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>
@@ -29,23 +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">Share as file</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_width_by_height">Størrelse i pixels: %1$d×%2$d</string>
<string name="margin_size">Margin in pixels: %1$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>
@@ -57,7 +64,11 @@
<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="scan_format">Scan %s</string>
<string name="find_code">Find kode</string>
<string name="find_code_with_content">Hvad indhold?</string>
<string name="find_code_hint">Term eller regulært udtryk</string>
<string name="does_not_match_search_term">Stemmer ikke overens med søgeterm</string>
<string name="scan_for">Scan %s</string>
<string name="quarter_second">Et kvart sekund</string>
<string name="half_second">Et halvt sekund</string>
<string name="a_second">Et sekund</string>
@@ -68,6 +79,17 @@
<string name="scan_category">Scan</string>
<string name="content_category">Indhold</string>
<string name="locale_category">Sprog</string>
<string name="profile_category">Profil</string>
<string name="profile">Profil</string>
<string name="profile_default">Standard</string>
<string name="profile_add">Tilføj ny profil</string>
<string name="profile_enter_name">Indtast profilnavn</string>
<string name="profile_enter_name_hint">Navn</string>
<string name="profile_invalid_name">Ugyldigt profilnavn</string>
<string name="profile_empty">Ingen profiler endnu</string>
<string name="profile_current">Nuværende profil</string>
<string name="profile_remove_confirm">Fjern denne profil?</string>
<string name="current_profile">Profil: %s</string>
<string name="custom_locale">Brugerdefineret sprog</string>
<string name="follow_system_settings">Følg systemindstillingerne</string>
<string name="show_crop_handle">Vis beskæringsbegrænser</string>
@@ -87,14 +109,14 @@
<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">Ignore duplicates</string>
<string name="ignore_duplicates">Ignorer duplikater</string>
<string name="ignore_consecutive_duplicates">Ignorer dubletter</string>
<string name="ignore_any_duplicates">Ignore any duplicates</string>
<string name="accept_duplicates">Accept duplicates</string>
@@ -106,6 +128,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>
@@ -113,6 +137,30 @@
<string name="default_search_engine">Åbn ukendte data i</string>
<string name="always_ask">Spørg altid</string>
<string name="open_with_url">Åbn ukendte data med URL</string>
<string name="automated_actions">Automatiserede handlinger</string>
<string name="automated_actions_none">Ingen automatiserede handlinger</string>
<string name="automated_actions_empty">Ingen automatiserede handlinger endnu</string>
<plurals name="automated_actions_count">
<item quantity="one">%d automatiseret handling</item>
<item quantity="other">%d automatiserede handlinger</item>
</plurals>
<string name="automated_action_add">Tilføj handling</string>
<string name="automated_action_edit">Rediger handling</string>
<string name="automated_action_regex">Regex</string>
<string name="automated_action_regex_hint">f.eks. ^https?://.*</string>
<string name="automated_action_regex_required">Regex påkrævet</string>
<string name="automated_action_regex_invalid">Ugyldig regex</string>
<string name="automated_action_type">Handlingstype</string>
<string name="automated_action_type_intent">Åbn URL</string>
<string name="automated_action_url_template">URL-skabelon</string>
<string name="automated_action_url_hint">https://example.com/?q={RESULT}</string>
<string name="automated_action_url_required">URL-skabelon påkrævet</string>
<string name="automated_action_invalid_url">Ugyldig URL-skabelon</string>
<string name="automated_action_remove_confirm">Fjern denne handling?</string>
<string name="automated_action_list_intent">Åbn URL: %s</string>
<string name="automated_action_list_intent_empty">Åbn URL</string>
<string name="automated_action_webview_title">Automatiseret handling</string>
<string name="clear_network_suggestions">Fjern netværksforslag</string>
<string name="clear_network_suggestions_summary">Fjern netværksforslag, der tidligere blev tilføjet med denne app.</string>
<string name="really_remove_all_networks">Vil du virkelig fjerne alle netværk?</string>
@@ -129,13 +177,16 @@
<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>
@@ -169,22 +220,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">Add to history</string>
<string name="added_to_history">Added to history</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">Pick a file</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>
@@ -199,4 +251,18 @@
<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>
<string name="automated_action_type_custom_intent">Brugerdefineret intent</string>
<string name="automated_action_intent_uri_template">Intent-URI-skabelon</string>
<string name="automated_action_intent_uri_hint">intent:#Intent;action=android.intent.action.SEND;type=text/plain;S.android.intent.extra.TEXT={RESULT};end</string>
<string name="automated_action_intent_uri_required">Intent-URI påkrævet</string>
<string name="automated_action_intent_uri_invalid">Ugyldig Intent-URI</string>
<string name="automated_action_list_custom_intent">Brugerdefineret intent: %s</string>
<string name="automated_action_list_custom_intent_empty">Brugerdefineret intent</string>
</resources>
+78 -12
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">Willkommen!</string>
<string name="mode_expert">Erweitert</string>
<string name="mode_expert_description">Ich möchte den gelesenen Inhalt überprüfen und manuell öffnen.</string>
<string name="mode_simple">Einfach</string>
<string name="mode_simple_description">Ich möchte nur Barcodes scannen und sie sofort öffnen.</string>
<string name="camera_error">Auf die Kamera kann nicht zugegriffen werden, bitte versuche es nochmal</string>
<string name="scan_code">Code scannen</string>
<string name="compose_barcode">Barcode erstellen</string>
@@ -20,30 +25,32 @@
<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_width_by_height">Größe in Pixel: %1$d×%2$d</string>
<string name="margin_size">Randgröße in Pixel: %1$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>
@@ -57,7 +64,11 @@
<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="scan_format">%s scannen</string>
<string name="find_code">Barcode finden</string>
<string name="find_code_with_content">Mit welchem Inhalt?</string>
<string name="find_code_hint">Begriff oder regulärer Ausdruck</string>
<string name="does_not_match_search_term">Entspricht nicht dem Suchbegriff</string>
<string name="scan_for">%s scannen</string>
<string name="quarter_second">Eine Viertelsekunde</string>
<string name="half_second">Eine halbe Sekunde</string>
<string name="a_second">Eine Sekunde</string>
@@ -68,6 +79,17 @@
<string name="scan_category">Scannen</string>
<string name="content_category">Inhalt</string>
<string name="locale_category">Sprache</string>
<string name="profile_category">Profil</string>
<string name="profile">Profil</string>
<string name="profile_default">Standard</string>
<string name="profile_add">Neues Profil hinzufügen</string>
<string name="profile_enter_name">Profilnamen eingeben</string>
<string name="profile_enter_name_hint">Name</string>
<string name="profile_invalid_name">Ungültiger Profilname</string>
<string name="profile_empty">Noch keine Profile</string>
<string name="profile_current">Aktuelles Profil</string>
<string name="profile_remove_confirm">Dieses Profil entfernen?</string>
<string name="current_profile">Profil: %s</string>
<string name="custom_locale">Spracheinstellung</string>
<string name="follow_system_settings">Systemeinstellung übernehmen</string>
<string name="show_crop_handle">Ausschnitt verkleinern anzeigen</string>
@@ -86,12 +108,12 @@
<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_duplicates">Ignoriere Duplikate</string>
@@ -106,6 +128,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>
@@ -113,6 +137,30 @@
<string name="default_search_engine">Unbekannte Daten öffnen mit</string>
<string name="always_ask">Immer fragen</string>
<string name="open_with_url">URL zum Öffnen unbekannter Daten</string>
<string name="automated_actions">Automatisierte Aktionen</string>
<string name="automated_actions_none">Keine automatisierten Aktionen</string>
<string name="automated_actions_empty">Noch keine automatisierten Aktionen</string>
<plurals name="automated_actions_count">
<item quantity="one">%d automatisierte Aktion</item>
<item quantity="other">%d automatisierte Aktionen</item>
</plurals>
<string name="automated_action_add">Aktion hinzufügen</string>
<string name="automated_action_edit">Aktion bearbeiten</string>
<string name="automated_action_regex">Regex</string>
<string name="automated_action_regex_hint">z.B. ^https?://.*</string>
<string name="automated_action_regex_required">Regex erforderlich</string>
<string name="automated_action_regex_invalid">Ungültiger Regex</string>
<string name="automated_action_type">Aktionstyp</string>
<string name="automated_action_type_intent">URL öffnen</string>
<string name="automated_action_url_template">URL-Vorlage</string>
<string name="automated_action_url_hint">https://example.com/?q={RESULT}</string>
<string name="automated_action_url_required">URL-Vorlage erforderlich</string>
<string name="automated_action_invalid_url">Ungültige URL-Vorlage</string>
<string name="automated_action_remove_confirm">Diese Aktion entfernen?</string>
<string name="automated_action_list_intent">URL öffnen: %s</string>
<string name="automated_action_list_intent_empty">URL öffnen</string>
<string name="automated_action_webview_title">Automatisierte Aktion</string>
<string name="clear_network_suggestions">WiFi Netzwerkvorschläge entfernen</string>
<string name="clear_network_suggestions_summary">Betrifft nur Netzwerke, die zuvor mit dieser App bereitgestellt wurden.</string>
<string name="really_remove_all_networks">Alle Netzwerkvorschläge entfernen?</string>
@@ -136,6 +184,9 @@
<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>
@@ -185,6 +236,7 @@
<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>
@@ -199,4 +251,18 @@
<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>
<string name="automated_action_type_custom_intent">Benutzerdefinierter Intent</string>
<string name="automated_action_intent_uri_template">Intent-URI-Vorlage</string>
<string name="automated_action_intent_uri_hint">intent:#Intent;action=android.intent.action.SEND;type=text/plain;S.android.intent.extra.TEXT={RESULT};end</string>
<string name="automated_action_intent_uri_required">Intent-URI erforderlich</string>
<string name="automated_action_intent_uri_invalid">Ungültige Intent-URI</string>
<string name="automated_action_list_custom_intent">Benutzerdefinierter Intent: %s</string>
<string name="automated_action_list_custom_intent_empty">Benutzerdefinierter Intent</string>
</resources>
+78 -12
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">¡Bienvenido!</string>
<string name="mode_expert">Avanzado</string>
<string name="mode_expert_description">Quiero verificar el contenido leído y abrirlo manualmente.</string>
<string name="mode_simple">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</string>
<string name="compose_barcode">Componer código</string>
@@ -14,36 +19,38 @@
<string name="error_correction_level_m" formatted="false">Medio (~15&#37; de correción)</string>
<string name="error_correction_level_q" formatted="false">Cuartil (~25&#37; de correción)</string>
<string name="error_correction_level_h" formatted="false">Alto (~30&#37; de correción)</string>
<string name="label">Label</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">Indice 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="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 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">Seleccionar motor de búsqueda</string>
<string name="format">Formato</string>
<string name="size_width_by_height">Tamaño en píxeles: %1$d×%2$d</string>
<string name="margin_size">Margen en pixeles: %1$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">Expandir secuencia de escape</string>
<string name="encode">CODIFICAR</string>
@@ -53,11 +60,15 @@
<string name="info">Información</string>
<string name="switch_camera">Cambiar cámara</string>
<string name="bulk_mode">Escaneo continuo</string>
<string name="bulk_mode_summary">Iniciar siempre en modo de escaneo contínuo</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="scan_format">Escanear %s</string>
<string name="find_code">Encontrar código</string>
<string name="find_code_with_content">¿Qué contenido?</string>
<string name="find_code_hint">Término o expresión regular</string>
<string name="does_not_match_search_term">No coincide con el término de búsqueda</string>
<string name="scan_for">Escanear %s</string>
<string name="quarter_second">Un cuarto de segundo</string>
<string name="half_second">Medio segundo</string>
<string name="a_second">Un segundo</string>
@@ -68,6 +79,17 @@
<string name="scan_category">Escanear</string>
<string name="content_category">Contenido</string>
<string name="locale_category">Idioma</string>
<string name="profile_category">Perfil</string>
<string name="profile">Perfil</string>
<string name="profile_default">Predeterminado</string>
<string name="profile_add">Añadir nuevo perfil</string>
<string name="profile_enter_name">Introduce el nombre del perfil</string>
<string name="profile_enter_name_hint">Nombre</string>
<string name="profile_invalid_name">Nombre de perfil no válido</string>
<string name="profile_empty">Aún no hay perfiles</string>
<string name="profile_current">Perfil actual</string>
<string name="profile_remove_confirm">¿Eliminar este perfil?</string>
<string name="current_profile">Perfil: %s</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 delimitador de recorte</string>
@@ -94,10 +116,10 @@
<string name="tone_prop_beep2">Sonido doble general</string>
<string name="use_history">Guardar historial de escaneo</string>
<string name="use_history_summary">Guarda los códigos reconocidos en su dispositivo.</string>
<string name="ignore_duplicates">Ignore duplicates</string>
<string name="ignore_duplicates">Ignorar duplicados</string>
<string name="ignore_consecutive_duplicates">Ignorar duplicados</string>
<string name="ignore_any_duplicates">Ignore any duplicates</string>
<string name="accept_duplicates">Accept 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. Podría abrir contenido dañino si está activado.</string>
<string name="copy_immediately">Copiar en el portapapeles</string>
@@ -106,6 +128,8 @@
<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>
@@ -113,6 +137,30 @@
<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 en la URL</string>
<string name="automated_actions">Acciones automatizadas</string>
<string name="automated_actions_none">No hay acciones automatizadas</string>
<string name="automated_actions_empty">Aún no hay acciones automatizadas</string>
<plurals name="automated_actions_count">
<item quantity="one">%d acción automatizada</item>
<item quantity="other">%d acciones automatizadas</item>
</plurals>
<string name="automated_action_add">Añadir acción</string>
<string name="automated_action_edit">Editar acción</string>
<string name="automated_action_regex">Expresión regular</string>
<string name="automated_action_regex_hint">p. ej. ^https?://.*</string>
<string name="automated_action_regex_required">Se requiere expresión regular</string>
<string name="automated_action_regex_invalid">Expresión regular inválida</string>
<string name="automated_action_type">Tipo de acción</string>
<string name="automated_action_type_intent">Abrir URL</string>
<string name="automated_action_url_template">Plantilla de URL</string>
<string name="automated_action_url_hint">https://example.com/?q={RESULT}</string>
<string name="automated_action_url_required">Se requiere plantilla de URL</string>
<string name="automated_action_invalid_url">Plantilla de URL inválida</string>
<string name="automated_action_remove_confirm">¿Eliminar esta acción?</string>
<string name="automated_action_list_intent">Abrir URL: %s</string>
<string name="automated_action_list_intent_empty">Abrir URL</string>
<string name="automated_action_webview_title">Acción automatizada</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>
@@ -131,11 +179,14 @@
<string name="test_url">URL de prueba</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">Host</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>
@@ -169,8 +220,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">Add to history</string>
<string name="added_to_history">Added to history</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>
@@ -185,6 +236,7 @@
<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">Tipo</string>
@@ -199,4 +251,18 @@
<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>
<string name="automated_action_type_custom_intent">Intent personalizado</string>
<string name="automated_action_intent_uri_template">Plantilla de URI de Intent</string>
<string name="automated_action_intent_uri_hint">intent:#Intent;action=android.intent.action.SEND;type=text/plain;S.android.intent.extra.TEXT={RESULT};end</string>
<string name="automated_action_intent_uri_required">URI de Intent requerida</string>
<string name="automated_action_intent_uri_invalid">URI de Intent no válida</string>
<string name="automated_action_list_custom_intent">Intent personalizado: %s</string>
<string name="automated_action_list_custom_intent_empty">Intent personalizado</string>
</resources>
+105 -39
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">خوش آمدید!</string>
<string name="mode_expert">پیشرفته</string>
<string name="mode_expert_description">می‌خواهم محتوای خوانده شده را بررسی کرده و به صورت دستی باز کنم.</string>
<string name="mode_simple">ساده</string>
<string name="mode_simple_description">فقط می‌خواهم بارکدها را اسکن کنم و فوراً باز شوند.</string>
<string name="camera_error">نمی‌توان به دوربین دسترسی داشت، لطفاً دوباره تلاش کنید</string>
<string name="scan_code">پویش کد</string>
<string name="compose_barcode">ایجاد بارکد</string>
@@ -14,38 +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="label">Label</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">Share as file</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_width_by_height">اندازه به پیکسل: %1$d×%2$d</string>
<string name="margin_size">Margin in pixels: %1$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>
@@ -57,7 +64,11 @@
<string name="bulk_mode_delay">تأخیر بین پویش‌های مداوم</string>
<string name="restrict_format">محدود کردن قالب</string>
<string name="remove_restriction">برداشتن محدودیت</string>
<string name="scan_format">پویش %s</string>
<string name="find_code">کد را پیدا کنید</string>
<string name="find_code_with_content">چه محتوا؟</string>
<string name="find_code_hint">عبارت یا عبارت منظم</string>
<string name="does_not_match_search_term">با عبارت جستجو مطابقت ندارد</string>
<string name="scan_for">پویش %s</string>
<string name="quarter_second">یک‌چهارم ثانیه</string>
<string name="half_second">نیم ثانیه</string>
<string name="a_second">یک ثانیه</string>
@@ -68,6 +79,17 @@
<string name="scan_category">پویش</string>
<string name="content_category">محتویات</string>
<string name="locale_category">زبان</string>
<string name="profile_category">نمایه</string>
<string name="profile">نمایه</string>
<string name="profile_default">پیش‌فرض</string>
<string name="profile_add">افزودن نمایهٔ جدید</string>
<string name="profile_enter_name">نام نمایه را وارد کنید</string>
<string name="profile_enter_name_hint">نام</string>
<string name="profile_invalid_name">نام نمایه نامعتبر است</string>
<string name="profile_empty">هنوز هیچ نمایه‌ای نیست</string>
<string name="profile_current">نمایهٔ فعلی</string>
<string name="profile_remove_confirm">این نمایه حذف شود؟</string>
<string name="current_profile">نمایه: %s</string>
<string name="custom_locale">زبان سفارشی</string>
<string name="follow_system_settings">پیروی از زبان سامانه</string>
<string name="show_crop_handle">نمایش محدود کننده برش</string>
@@ -83,21 +105,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">Ignore duplicates</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">Accept duplicates</string>
<string name="accept_duplicates">تکراری‌ها را پذیرا باش</string>
<string name="open_immediately">گشودن بلافاصله</string>
<string name="open_immediately_summary">بازرسی را رها کنید و محتویات را فورا باز کنید. در صورت فعال بودن ممکن است محتوای مضر را باز کند.</string>
<string name="copy_immediately">قرار دادن در تخته‌گیره</string>
@@ -106,13 +128,39 @@
<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>
<string name="always_ask">همیشه بپرس</string>
<string name="open_with_url">داده‌های ناشناخته را با URL باز کنید</string>
<string name="automated_actions">اقدام‌های خودکار</string>
<string name="automated_actions_none">هیچ اقدام خودکاری وجود ندارد</string>
<string name="automated_actions_empty">هنوز اقدام خودکاری وجود ندارد</string>
<plurals name="automated_actions_count">
<item quantity="one">%d اقدام خودکار</item>
<item quantity="other">%d اقدام خودکار</item>
</plurals>
<string name="automated_action_add">افزودن اقدام</string>
<string name="automated_action_edit">ویرایش اقدام</string>
<string name="automated_action_regex">عبارت منظم</string>
<string name="automated_action_regex_hint">مثلاً ^https?://.*</string>
<string name="automated_action_regex_required">عبارت منظم لازم است</string>
<string name="automated_action_regex_invalid">عبارت منظم نامعتبر است</string>
<string name="automated_action_type">نوع اقدام</string>
<string name="automated_action_type_intent">باز کردن URL</string>
<string name="automated_action_url_template">قالب URL</string>
<string name="automated_action_url_hint">https://example.com/?q={RESULT}</string>
<string name="automated_action_url_required">قالب URL لازم است</string>
<string name="automated_action_invalid_url">قالب URL نامعتبر است</string>
<string name="automated_action_remove_confirm">این اقدام حذف شود؟</string>
<string name="automated_action_list_intent">باز کردن URL: %s</string>
<string name="automated_action_list_intent_empty">باز کردن URL</string>
<string name="automated_action_webview_title">اقدام خودکار</string>
<string name="clear_network_suggestions">پاک کردن پیشنهادات شبکه</string>
<string name="clear_network_suggestions_summary">همه پیشنهادات شبکه را که قبلاً توسط این برنامه ارائه شده است حذف کنید.</string>
<string name="really_remove_all_networks">واقعاً همه شبکه‌ها را حذف می‌کنید؟</string>
@@ -125,17 +173,20 @@
<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>
@@ -169,8 +220,8 @@
<string name="otpauth_add">افزودن احراز هویت چندعاملی(2FA)</string>
<string name="search_web">جست‌وجوی وب</string>
<string name="search_scan">جست‌وجوی پویش</string>
<string name="add_to_history">Add to history</string>
<string name="added_to_history">Added to history</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>
@@ -179,12 +230,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">Pick a 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>
@@ -197,6 +249,20 @@
<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>
<string name="automated_action_type_custom_intent">Intent سفارشی</string>
<string name="automated_action_intent_uri_template">الگوی URI Intent</string>
<string name="automated_action_intent_uri_hint">intent:#Intent;action=android.intent.action.SEND;type=text/plain;S.android.intent.extra.TEXT={RESULT};end</string>
<string name="automated_action_intent_uri_required">URI Intent لازم است</string>
<string name="automated_action_intent_uri_invalid">URI Intent نامعتبر</string>
<string name="automated_action_list_custom_intent">Intent سفارشی: %s</string>
<string name="automated_action_list_custom_intent_empty">Intent سفارشی</string>
</resources>
+85 -19
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">Bienvenue !</string>
<string name="mode_expert">Avancé</string>
<string name="mode_expert_description">Je souhaite vérifier le contenu lu et l\'ouvrir manuellement.</string>
<string name="mode_simple">Simple</string>
<string name="mode_simple_description">Je veux simplement scanner les codes-barres et les ouvrir immédiatement.</string>
<string name="camera_error">Impossible d\'accéder à la caméra, veuillez réessayer</string>
<string name="scan_code">Scanner un code</string>
<string name="compose_barcode">Créer un code-barres</string>
@@ -14,7 +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">Label</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,21 +34,23 @@
<string name="gtin_add_on">Extension UPC EAN</string>
<string name="barcode_version_number">Version</string>
<string name="qr_version_and_modules">%1$s (%2$d modules)</string>
<string name="qr_data_mask">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_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="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_width_by_height">Taille en pixels : %1$d×%2$d</string>
<string name="margin_size">Marge en pixels : %1$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>
@@ -57,7 +64,11 @@
<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">Terme ou expression régulière</string>
<string name="does_not_match_search_term">Ne correspond pas au terme de recherche</string>
<string name="scan_for">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>
@@ -65,16 +76,27 @@
<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="profile_category">Profil</string>
<string name="profile">Profil</string>
<string name="profile_default">Par défaut</string>
<string name="profile_add">Ajouter un nouveau profil</string>
<string name="profile_enter_name">Entrez le nom du profil</string>
<string name="profile_enter_name_hint">Nom</string>
<string name="profile_invalid_name">Nom de profil invalide</string>
<string name="profile_empty">Aucun profil pour le moment</string>
<string name="profile_current">Profil actuel</string>
<string name="profile_remove_confirm">Supprimer ce profil ?</string>
<string name="current_profile">Profil : %s</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 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">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="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">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>
@@ -86,18 +108,18 @@
<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">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">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">Sauvegarder les codes reconnus sur votre appareil.</string>
<string name="ignore_duplicates">Ignore duplicates</string>
<string name="ignore_consecutive_duplicates">Ignorer les doublons</string>
<string name="ignore_any_duplicates">Ignore any duplicates</string>
<string name="accept_duplicates">Accept duplicates</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">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>
@@ -106,13 +128,39 @@
<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é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">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 les données inconnues avec lURL</string>
<string name="open_with_url">Ouvrir les données inconnues avec l\'URL</string>
<string name="automated_actions">Actions automatiques</string>
<string name="automated_actions_none">Aucune action automatique</string>
<string name="automated_actions_empty">Pas encore dactions automatiques</string>
<plurals name="automated_actions_count">
<item quantity="one">%d action automatique</item>
<item quantity="other">%d actions automatiques</item>
</plurals>
<string name="automated_action_add">Ajouter une action</string>
<string name="automated_action_edit">Modifier laction</string>
<string name="automated_action_regex">Expression régulière</string>
<string name="automated_action_regex_hint">ex. ^https?://.*</string>
<string name="automated_action_regex_required">Expression régulière requise</string>
<string name="automated_action_regex_invalid">Expression régulière invalide</string>
<string name="automated_action_type">Type daction</string>
<string name="automated_action_type_intent">Ouvrir lURL</string>
<string name="automated_action_url_template">Modèle dURL</string>
<string name="automated_action_url_hint">https://example.com/?q={RESULT}</string>
<string name="automated_action_url_required">Modèle dURL requis</string>
<string name="automated_action_invalid_url">Modèle dURL invalide</string>
<string name="automated_action_remove_confirm">Supprimer cette action ?</string>
<string name="automated_action_list_intent">Ouvrir lURL : %s</string>
<string name="automated_action_list_intent_empty">Ouvrir lURL</string>
<string name="automated_action_webview_title">Action automatique</string>
<string name="clear_network_suggestions">Supprimer les suggestions de réseau</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>
@@ -136,6 +184,9 @@
<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>
@@ -169,8 +220,8 @@
<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="add_to_history">Add to history</string>
<string name="added_to_history">Added to history</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>
@@ -185,6 +236,7 @@
<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>
@@ -199,4 +251,18 @@
<string name="wifi_phase2">Méthode de la phase 2</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>
<string name="automated_action_type_custom_intent">Intent personnalisé</string>
<string name="automated_action_intent_uri_template">Modèle dURI dIntent</string>
<string name="automated_action_intent_uri_hint">intent:#Intent;action=android.intent.action.SEND;type=text/plain;S.android.intent.extra.TEXT={RESULT};end</string>
<string name="automated_action_intent_uri_required">URI dIntent requise</string>
<string name="automated_action_intent_uri_invalid">URI dIntent invalide</string>
<string name="automated_action_list_custom_intent">Intent personnalisé : %s</string>
<string name="automated_action_list_custom_intent_empty">Intent personnalisé</string>
</resources>
+102 -36
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">Üdvözöljük!</string>
<string name="mode_expert">Haladó</string>
<string name="mode_expert_description">Szeretném ellenőrizni az olvasott tartalmat és kézzel megnyitni.</string>
<string name="mode_simple">Egyszerű</string>
<string name="mode_simple_description">Csak be akarom olvasni a vonalkódokat, és azonnal megnyitni őket.</string>
<string name="camera_error">Nem lehet hozzáférni a kamerához, próbálja meg újra</string>
<string name="scan_code">Kód beolvasása</string>
<string name="compose_barcode">Vonalkód létrehozása</string>
@@ -14,38 +19,40 @@
<string name="error_correction_level_m" formatted="false">Közepes (~15&#37; javítás)</string>
<string name="error_correction_level_q" formatted="false">Kvartilis (~25&#37; javítás)</string>
<string name="error_correction_level_h" formatted="false">Magas (~30&#37; javítás)</string>
<string name="label">Label</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">Share as file</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_width_by_height">Méret képpontban: %1$d×%2$d</string>
<string name="margin_size">Margin in pixels: %1$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>
@@ -57,7 +64,11 @@
<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="scan_format">%s beolvasá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">Kifejezés vagy reguláris kifejezés</string>
<string name="does_not_match_search_term">Nem egyezik a keresőkifejezéssel</string>
<string name="scan_for">%s beolvasása</string>
<string name="quarter_second">Negyed másodperc</string>
<string name="half_second">Fél másodperc</string>
<string name="a_second">Egy másodperc</string>
@@ -68,6 +79,17 @@
<string name="scan_category">Beolvasás</string>
<string name="content_category">Tartalom</string>
<string name="locale_category">Nyelv</string>
<string name="profile_category">Profil</string>
<string name="profile">Profil</string>
<string name="profile_default">Alapértelmezett</string>
<string name="profile_add">Új profil hozzáadása</string>
<string name="profile_enter_name">Adja meg a profil nevét</string>
<string name="profile_enter_name_hint">Név</string>
<string name="profile_invalid_name">Érvénytelen profilnév</string>
<string name="profile_empty">Még nincs profil</string>
<string name="profile_current">Aktuális profil</string>
<string name="profile_remove_confirm">Eltávolítja ezt a profilt?</string>
<string name="current_profile">Profil: %s</string>
<string name="custom_locale">Egyéni nyelv</string>
<string name="follow_system_settings">Rendszerbeállítások követése</string>
<string name="show_crop_handle">Levágási korlátozó megjelenítése</string>
@@ -83,21 +105,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">Ignore duplicates</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_any_duplicates">Ignore any duplicates</string>
<string name="accept_duplicates">Accept 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>
@@ -106,13 +128,39 @@
<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>
<string name="always_ask">Mindig kérdezzen</string>
<string name="open_with_url">Ismeretlen adat megnyitása URL-lel</string>
<string name="automated_actions">Automatizált műveletek</string>
<string name="automated_actions_none">Nincsenek automatizált műveletek</string>
<string name="automated_actions_empty">Még nincsenek automatizált műveletek</string>
<plurals name="automated_actions_count">
<item quantity="one">%d automatizált művelet</item>
<item quantity="other">%d automatizált művelet</item>
</plurals>
<string name="automated_action_add">Művelet hozzáadása</string>
<string name="automated_action_edit">Művelet szerkesztése</string>
<string name="automated_action_regex">Reguláris kifejezés</string>
<string name="automated_action_regex_hint">pl. ^https?://.*</string>
<string name="automated_action_regex_required">A reguláris kifejezés kötelező</string>
<string name="automated_action_regex_invalid">Érvénytelen reguláris kifejezés</string>
<string name="automated_action_type">Művelet típusa</string>
<string name="automated_action_type_intent">URL megnyitása</string>
<string name="automated_action_url_template">URL-sablon</string>
<string name="automated_action_url_hint">https://example.com/?q={RESULT}</string>
<string name="automated_action_url_required">URL-sablon kötelező</string>
<string name="automated_action_invalid_url">Érvénytelen URL-sablon</string>
<string name="automated_action_remove_confirm">Törli ezt a műveletet?</string>
<string name="automated_action_list_intent">URL megnyitása: %s</string>
<string name="automated_action_list_intent_empty">URL megnyitása</string>
<string name="automated_action_webview_title">Automatizált művelet</string>
<string name="clear_network_suggestions">Hálózati javaslatok törlése</string>
<string name="clear_network_suggestions_summary">Az alkalmazás által korábban biztosított összes hálózati javaslat eltávolítása.</string>
<string name="really_remove_all_networks">Valóban eltávolítja az összes hálózatot?</string>
@@ -129,13 +177,16 @@
<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>
@@ -169,8 +220,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">Add to history</string>
<string name="added_to_history">Added to history</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>
@@ -179,12 +230,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">Pick a file</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>
@@ -197,6 +249,20 @@
<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>
<string name="automated_action_type_custom_intent">Egyéni intent</string>
<string name="automated_action_intent_uri_template">Intent URI-sablon</string>
<string name="automated_action_intent_uri_hint">intent:#Intent;action=android.intent.action.SEND;type=text/plain;S.android.intent.extra.TEXT={RESULT};end</string>
<string name="automated_action_intent_uri_required">Intent URI szükséges</string>
<string name="automated_action_intent_uri_invalid">Érvénytelen Intent URI</string>
<string name="automated_action_list_custom_intent">Egyéni intent: %s</string>
<string name="automated_action_list_custom_intent_empty">Egyéni intent</string>
</resources>
+85 -19
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">Selamat datang!</string>
<string name="mode_expert">Lanjutan</string>
<string name="mode_expert_description">Saya ingin memeriksa konten yang dibaca dan membukanya secara manual.</string>
<string name="mode_simple">Sederhana</string>
<string name="mode_simple_description">Saya hanya ingin memindai kode batang dan langsung membukanya.</string>
<string name="camera_error">Tidak bisa mengakses kamera, silakan coba lagi</string>
<string name="scan_code">Pindai kode</string>
<string name="compose_barcode">Buat barcode</string>
@@ -13,7 +18,7 @@
<string name="error_correction_level_m" formatted="false">Medium (~15&#37; koreksi)</string>
<string name="error_correction_level_q" formatted="false">Kuartil (~25&#37; koreksi)</string>
<string name="error_correction_level_h" formatted="false">Tinggi (~30&#37; koreksi)</string>
<string name="label">Label</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>
@@ -28,35 +33,41 @@
<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">Share as file</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_width_by_height">Ukuran dalam piksel: %1$d×%2$d</string>
<string name="margin_size">Margin in pixels: %1$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="scan_format">Pindai %s</string>
<string name="find_code">Trouver le code</string>
<string name="find_code_with_content">Quel contenu?</string>
<string name="find_code_hint">Istilah atau ekspresi reguler</string>
<string name="does_not_match_search_term">Tidak cocok dengan istilah pencarian</string>
<string name="scan_for">Pindai %s</string>
<string name="quarter_second">Seperempat detik</string>
<string name="half_second">Setengah detik</string>
<string name="a_second">Satu detik</string>
@@ -67,6 +78,17 @@
<string name="scan_category">Pemindaian</string>
<string name="content_category">Konten</string>
<string name="locale_category">Bahasa</string>
<string name="profile_category">Profil</string>
<string name="profile">Profil</string>
<string name="profile_default">Bawaan</string>
<string name="profile_add">Tambah profil baru</string>
<string name="profile_enter_name">Masukkan nama profil</string>
<string name="profile_enter_name_hint">Nama</string>
<string name="profile_invalid_name">Nama profil tidak valid</string>
<string name="profile_empty">Belum ada profil</string>
<string name="profile_current">Profil saat ini</string>
<string name="profile_remove_confirm">Hapus profil ini?</string>
<string name="current_profile">Profil: %s</string>
<string name="custom_locale">Bahasa lainnya</string>
<string name="follow_system_settings">Ikuti pengaturan sistem</string>
<string name="show_crop_handle">Tampilkan pembatas pemotongan</string>
@@ -85,12 +107,12 @@
<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>
@@ -105,6 +127,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>
@@ -112,6 +136,30 @@
<string name="default_search_engine">Buka data tak dikenal di</string>
<string name="always_ask">Selalu tanya</string>
<string name="open_with_url">Buka data tak dikenal dengan URL</string>
<string name="automated_actions">Tindakan otomatis</string>
<string name="automated_actions_none">Tidak ada tindakan otomatis</string>
<string name="automated_actions_empty">Belum ada tindakan otomatis</string>
<plurals name="automated_actions_count">
<item quantity="one">%d tindakan otomatis</item>
<item quantity="other">%d tindakan otomatis</item>
</plurals>
<string name="automated_action_add">Tambah tindakan</string>
<string name="automated_action_edit">Edit tindakan</string>
<string name="automated_action_regex">Regex</string>
<string name="automated_action_regex_hint">mis. ^https?://.*</string>
<string name="automated_action_regex_required">Regex wajib diisi</string>
<string name="automated_action_regex_invalid">Regex tidak valid</string>
<string name="automated_action_type">Jenis tindakan</string>
<string name="automated_action_type_intent">Buka URL</string>
<string name="automated_action_url_template">Templat URL</string>
<string name="automated_action_url_hint">https://example.com/?q={RESULT}</string>
<string name="automated_action_url_required">Templat URL wajib diisi</string>
<string name="automated_action_invalid_url">Templat URL tidak valid</string>
<string name="automated_action_remove_confirm">Hapus tindakan ini?</string>
<string name="automated_action_list_intent">Buka URL: %s</string>
<string name="automated_action_list_intent_empty">Buka URL</string>
<string name="automated_action_webview_title">Tindakan otomatis</string>
<string name="clear_network_suggestions">Hapus saran jaringan</string>
<string name="clear_network_suggestions_summary">Hapus semua saran jaringan yang sebelumnya disediakan oleh aplikasi ini.</string>
<string name="really_remove_all_networks">Yakin menghapus semua jaringan?</string>
@@ -128,13 +176,16 @@
<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>
@@ -168,8 +219,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">Add to history</string>
<string name="added_to_history">Added to history</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>
@@ -179,11 +230,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">Pick a file</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>
@@ -196,6 +248,20 @@
<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>
<string name="automated_action_type_custom_intent">Intent kustom</string>
<string name="automated_action_intent_uri_template">Templat URI Intent</string>
<string name="automated_action_intent_uri_hint">intent:#Intent;action=android.intent.action.SEND;type=text/plain;S.android.intent.extra.TEXT={RESULT};end</string>
<string name="automated_action_intent_uri_required">URI Intent wajib diisi</string>
<string name="automated_action_intent_uri_invalid">URI Intent tidak valid</string>
<string name="automated_action_list_custom_intent">Intent kustom: %s</string>
<string name="automated_action_list_custom_intent_empty">Intent kustom</string>
</resources>
+103 -39
View File
@@ -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,7 +19,7 @@
<string name="error_correction_level_m" formatted="false">Medio (correzione ~15&#37;)</string>
<string name="error_correction_level_q" formatted="false">Quartile (correzione ~25&#37;)</string>
<string name="error_correction_level_h" formatted="false">Alto (correzione ~30&#37;)</string>
<string name="label">Label</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>
@@ -29,60 +34,77 @@
<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">Share as file</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_width_by_height">Dimensioni (pixel): %1$d×%2$d</string>
<string name="margin_size">Margin in pixels: %1$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="scan_format">Scansiona %s</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">Termine o espressione regolare</string>
<string name="does_not_match_search_term">Non corrisponde al termine di ricerca</string>
<string name="scan_for">Scansiona %s</string>
<string name="quarter_second">Un quarto di secondo</string>
<string name="half_second">Mezzo secondo</string>
<string name="a_second">Un secondo</string>
<string name="two_seconds">Due secondi</string>
<string name="five_seconds">Cinque secondi</string>
<string name="history">Cronologia</string>
<string name="preferences">Preferenze</string>
<string name="preferences">Impostazioni</string>
<string name="scan_category">Scansione</string>
<string name="content_category">Contenuto</string>
<string name="locale_category">Lingua</string>
<string name="profile_category">Profilo</string>
<string name="profile">Profilo</string>
<string name="profile_default">Predefinito</string>
<string name="profile_add">Aggiungi nuovo profilo</string>
<string name="profile_enter_name">Inserisci nome profilo</string>
<string name="profile_enter_name_hint">Nome</string>
<string name="profile_invalid_name">Nome profilo non valido</string>
<string name="profile_empty">Nessun profilo per ora</string>
<string name="profile_current">Profilo attuale</string>
<string name="profile_remove_confirm">Vuoi rimuovere questo profilo?</string>
<string name="current_profile">Profilo: %s</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>
@@ -93,26 +115,58 @@
<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_duplicates">Ignore duplicates</string>
<string name="ignore_consecutive_duplicates">Ignora i duplicati</string>
<string name="ignore_any_duplicates">Ignore any duplicates</string>
<string name="accept_duplicates">Accept duplicates</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="automated_actions">Azioni automatizzate</string>
<string name="automated_actions_none">Nessuna azione automatizzata</string>
<string name="automated_actions_empty">Nessuna azione automatizzata ancora</string>
<plurals name="automated_actions_count">
<item quantity="one">%d azione automatizzata</item>
<item quantity="other">%d azioni automatizzate</item>
</plurals>
<string name="automated_action_add">Aggiungi azione</string>
<string name="automated_action_edit">Modifica azione</string>
<string name="automated_action_regex">Espressione regolare</string>
<string name="automated_action_regex_hint">es. ^https?://.*</string>
<string name="automated_action_regex_required">Richiesta espressione regolare</string>
<string name="automated_action_regex_invalid">Espressione regolare non valida</string>
<string name="automated_action_type">Tipo azione</string>
<string name="automated_action_type_intent">Apri URL</string>
<string name="automated_action_type_custom_intent">Intento personalizzato</string>
<string name="automated_action_url_template">Modello URL</string>
<string name="automated_action_url_hint">https://example.com/?q={RESULT}</string>
<string name="automated_action_url_required">Richiesto modello URL</string>
<string name="automated_action_intent_uri_template">Modello URI intento</string>
<string name="automated_action_intent_uri_hint">intento:#Intent;action=android.intent.action.SEND;type=text/plain;S.android.intent.extra.TEXT={RESULT};end</string>
<string name="automated_action_intent_uri_required">Richiesto URI intento</string>
<string name="automated_action_intent_uri_invalid">URI intento non valido</string>
<string name="automated_action_invalid_url">Modello URL non valido</string>
<string name="automated_action_remove_confirm">Vuoi rimuovere questa azione?</string>
<string name="automated_action_list_intent">Apri URL: %s</string>
<string name="automated_action_list_intent_empty">Apri URL</string>
<string name="automated_action_list_custom_intent">Intento personalizzato: %s</string>
<string name="automated_action_list_custom_intent_empty">Intento personalizzato</string>
<string name="automated_action_webview_title">Azione automatizzata</string>
<string name="clear_network_suggestions">Cancella suggerimenti di rete</string>
<string name="clear_network_suggestions_summary">Cancella tutti i suggerimenti di rete precedentemente forniti da questa app.</string>
<string name="really_remove_all_networks">Vuoi rimuovere tutte le reti?</string>
@@ -123,8 +177,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>
@@ -135,7 +189,10 @@
<string name="no_bluetooth_hosts_paired">Nessun host accoppiato</string>
<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="bluetooth_send_success">Invio via Bluetooth completato.</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>
@@ -149,7 +206,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>
@@ -169,10 +226,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="add_to_history">Add to history</string>
<string name="added_to_history">Added to history</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>
@@ -180,11 +237,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">Pick a file</string>
<string name="pick_image_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>
@@ -199,4 +257,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">Richiesta</string>
</resources>
+89 -23
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">ようこそ!</string>
<string name="mode_expert">上級</string>
<string name="mode_expert_description">読み取った内容を確認し、手動で開きたい。</string>
<string name="mode_simple">簡単</string>
<string name="mode_simple_description">バーコードをスキャンしてすぐに開きたい。</string>
<string name="camera_error">カメラに接続できませんでした。再試行してください。</string>
<string name="scan_code">バーコードをスキャン</string>
<string name="compose_barcode">バーコードを作成</string>
@@ -13,7 +18,7 @@
<string name="error_correction_level_m" formatted="false">中 (~15&#37; の正確性)</string>
<string name="error_correction_level_q" formatted="false">高 (~25&#37; の正確性)</string>
<string name="error_correction_level_h" formatted="false">最高 (~30&#37; の正確性)</string>
<string name="label">Label</string>
<string name="label">ラベル</string>
<string name="colors">背面と前面</string>
<string name="colors_black_on_white">白地に黒色</string>
<string name="colors_white_on_black">黒地に白色</string>
@@ -28,23 +33,25 @@
<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">Share as file</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">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="margin_size">Margin in pixels: %1$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>
@@ -56,7 +63,11 @@
<string name="bulk_mode_delay">連続スキャンの間隔</string>
<string name="restrict_format">フォーマットの制限</string>
<string name="remove_restriction">制限を解除</string>
<string name="scan_format">%sをスキャン</string>
<string name="find_code">コードを検索</string>
<string name="find_code_with_content">何の内容ですか?</string>
<string name="find_code_hint">検索ワードまたは正規表現</string>
<string name="does_not_match_search_term">検索ワードと一致しません</string>
<string name="scan_for">%sをスキャン</string>
<string name="quarter_second">0.25秒</string>
<string name="half_second">0.5秒</string>
<string name="a_second">1秒</string>
@@ -67,6 +78,17 @@
<string name="scan_category">スキャン</string>
<string name="content_category">コンテンツ</string>
<string name="locale_category">言語</string>
<string name="profile_category">プロファイル</string>
<string name="profile">プロファイル</string>
<string name="profile_default">デフォルト</string>
<string name="profile_add">新しいプロファイルを追加</string>
<string name="profile_enter_name">プロファイル名を入力</string>
<string name="profile_enter_name_hint">名前</string>
<string name="profile_invalid_name">無効なプロファイル名</string>
<string name="profile_empty">まだプロファイルはありません</string>
<string name="profile_current">現在のプロファイル</string>
<string name="profile_remove_confirm">このプロファイルを削除しますか?</string>
<string name="current_profile">プロファイル: %s</string>
<string name="custom_locale">言語の変更</string>
<string name="follow_system_settings">システム設定に従う</string>
<string name="show_crop_handle">トリミングツールを表示</string>
@@ -83,20 +105,20 @@
<string name="vibrate">認識時に振動する</string>
<string name="vibrate_summary">バーコードが認識されたときにバイブレーションを行います。</string>
<string name="beep">検出時にビープ音を鳴らす</string>
<string name="beep_summary">Enable this if you want your device to beep when a code is recognized.</string>
<string name="beep_summary">コードが認識されたときにデバイスがビープする場合は、これを有効にしてください。</string>
<string name="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_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">Ignore duplicates</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">Accept duplicates</string>
<string name="accept_duplicates">重複を許可</string>
<string name="open_immediately">すぐにコンテンツを開く</string>
<string name="open_immediately_summary">確認画面なしでコンテンツをすぐに開くようにします。有害なコンテンツを開いてしまうリスクがあります。</string>
<string name="copy_immediately">自動的にクリップボードにコピー</string>
@@ -105,6 +127,8 @@
<string name="show_meta_data_summary">スキャンされたバーコードの追加データを確認できるようにします。</string>
<string name="show_hex_dump">16進数を表示</string>
<string name="show_hex_dump_summary">スキャンされたコンテンツの16進数データを確認できるようにします。</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>
@@ -112,6 +136,30 @@
<string name="default_search_engine">未知のデータの表示方法</string>
<string name="always_ask">常に尋ねる</string>
<string name="open_with_url">未知のデータを次のURLから開く</string>
<string name="automated_actions">自動アクション</string>
<string name="automated_actions_none">自動アクションはありません</string>
<string name="automated_actions_empty">まだ自動アクションがありません</string>
<plurals name="automated_actions_count">
<item quantity="one">%d 件の自動アクション</item>
<item quantity="other">%d 件の自動アクション</item>
</plurals>
<string name="automated_action_add">アクションを追加</string>
<string name="automated_action_edit">アクションを編集</string>
<string name="automated_action_regex">正規表現</string>
<string name="automated_action_regex_hint">例: ^https?://.*</string>
<string name="automated_action_regex_required">正規表現が必要です</string>
<string name="automated_action_regex_invalid">正規表現が無効です</string>
<string name="automated_action_type">アクションの種類</string>
<string name="automated_action_type_intent">URL を開く</string>
<string name="automated_action_url_template">URLテンプレート</string>
<string name="automated_action_url_hint">https://example.com/?q={RESULT}</string>
<string name="automated_action_url_required">URLテンプレートが必要です</string>
<string name="automated_action_invalid_url">URLテンプレートが無効です</string>
<string name="automated_action_remove_confirm">このアクションを削除しますか?</string>
<string name="automated_action_list_intent">URLを開く: %s</string>
<string name="automated_action_list_intent_empty">URLを開く</string>
<string name="automated_action_webview_title">自動アクション</string>
<string name="clear_network_suggestions">ネットワークの提案を消去</string>
<string name="clear_network_suggestions_summary">このアプリのネットワークの提案をすべて削除します。</string>
<string name="really_remove_all_networks">ネットワークの提案を消去してもよろしいですか?</string>
@@ -128,13 +176,16 @@
<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">Forward scans with Bluetooth</string>
<string name="send_scan_bluetooth">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>
@@ -168,8 +219,8 @@
<string name="otpauth_add">二段階認証を追加</string>
<string name="search_web">ウェブで検索</string>
<string name="search_scan">スキャン結果を検索</string>
<string name="add_to_history">Add to history</string>
<string name="added_to_history">Added to history</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>
@@ -178,12 +229,13 @@
<string name="export_database">SQLiteデータベース</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">Pick a 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>
@@ -193,9 +245,23 @@
<string name="wifi_password">パスワード</string>
<string name="wifi_hidden">非表示のネットワーク</string>
<string name="wifi_eap">EAP方式</string>
<string name="wifi_anonymous_identity">Anonymous identity</string>
<string name="wifi_identity">Identity</string>
<string name="wifi_anonymous_identity">匿名ID</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>
<string name="automated_action_type_custom_intent">カスタム Intent</string>
<string name="automated_action_intent_uri_template">Intent URI テンプレート</string>
<string name="automated_action_intent_uri_hint">intent:#Intent;action=android.intent.action.SEND;type=text/plain;S.android.intent.extra.TEXT={RESULT};end</string>
<string name="automated_action_intent_uri_required">Intent URI が必要です</string>
<string name="automated_action_intent_uri_invalid">無効な Intent URI</string>
<string name="automated_action_list_custom_intent">カスタム Intent: %s</string>
<string name="automated_action_list_custom_intent_empty">カスタム Intent</string>
</resources>
+136 -70
View File
@@ -1,4 +1,9 @@
<resources>
<string name="welcome">მოგესალმებით!</string>
<string name="mode_expert">გაფართოებული</string>
<string name="mode_expert_description">მინდა გადავამოწმო წაკითხული შინაარსი და გავხსნა ხელით.</string>
<string name="mode_simple">მარტივი</string>
<string name="mode_simple_description">მხოლოდ მინდა დავასკანერო ბარკოდი და გავხსნა დაუყოვნებლივ.</string>
<string name="camera_error">კამერაზე წვდომის მოპოვება ვერ ხერხდება, გთხოვთ, კიდევ სცადეთ</string>
<string name="scan_code">შტრიხ-კოდის სკანირება</string>
<string name="compose_barcode">შტრიხ-კოდის შექმნა</string>
@@ -14,38 +19,40 @@
<string name="error_correction_level_m" formatted="false">Medium (~15&#37; correction)</string>
<string name="error_correction_level_q" formatted="false">Quartile (~25&#37; correction)</string>
<string name="error_correction_level_h" formatted="false">High (~30&#37; correction)</string>
<string name="label">Label</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">მიმდინარე ნამდვილი ID</string>
<string name="gtin_issue_number">გამოშვების ნომერი</string>
<string name="gtin_country">შესაძლო მწარმოებელი ქვეყანა</string>
<string name="gtin_price">დაახლოებითი ფასი</string>
<string name="gtin_add_on">UPC EAN გაფართოება</string>
<string name="barcode_version_number">Version</string>
<string name="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">Share as file</string>
<string name="share_as">Share as?</string>
<string name="share_file">გაზიარეთ ფაილის სახით</string>
<string name="share_as">გაზიარე როგორ?</string>
<string name="copy_to_clipboard">გაცვლის ბუფერში კოპირება</string>
<string name="copy_as_deeplink">კოპირება 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_width_by_height">ზომა პიქსელებში: %1$d×%2$d</string>
<string name="margin_size">Margin in pixels: %1$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>
@@ -53,51 +60,66 @@
<string name="info">ინფორმაცია</string>
<string name="switch_camera">კამერის გადართვა</string>
<string name="bulk_mode">სკანირება უწყვეტად</string>
<string name="bulk_mode_summary">Always start scanning continuously.</string>
<string name="bulk_mode_delay">Delay between continuous scans</string>
<string name="restrict_format">Restrict format</string>
<string name="remove_restriction">Remove restriction</string>
<string name="scan_format">Scan %s</string>
<string name="quarter_second">A quarter second</string>
<string name="half_second">Half a second</string>
<string name="a_second">One second</string>
<string name="two_seconds">Two seconds</string>
<string name="five_seconds">Five seconds</string>
<string name="bulk_mode_summary">ყოველთვის დაიწყეთ სკანირება უწყვეტად.</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="does_not_match_search_term">არ შეესაბამება ძიების ტერმინს</string>
<string name="scan_for">სკანირე %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">Language</string>
<string name="custom_locale">Custom language</string>
<string name="follow_system_settings">Follow system settings</string>
<string name="locale_category">ენა</string>
<string name="profile_category">პროფილი</string>
<string name="profile">პროფილი</string>
<string name="profile_default">ნაგულისხმევი</string>
<string name="profile_add">ახალი პროფილის დამატება</string>
<string name="profile_enter_name">შეიყვანეთ პროფილის სახელი</string>
<string name="profile_enter_name_hint">სახელი</string>
<string name="profile_invalid_name">პროფილის სახელი არასწორია</string>
<string name="profile_empty">პროფილები ჯერ არ არის</string>
<string name="profile_current">მიმდინარე პროფილი</string>
<string name="profile_remove_confirm">წაიშალოს ეს პროფილი?</string>
<string name="current_profile">პროფილი: %s</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 formats</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">Show data in continuous mode</string>
<string name="show_toast_in_bulk_mode_summary">Briefly show the scanned data when scanning continuously.</string>
<string name="show_toast_in_bulk_mode">მონაცემების ჩვენება მუდმივ რეჟიმში</string>
<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">Ignore duplicates</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">Accept duplicates</string>
<string name="accept_duplicates">დუპლიკატების მიღება</string>
<string name="open_immediately">შემოწმების გამოტოვება და მყისიერი გახსნა</string>
<string name="open_immediately_summary">შემოწმების გამოტოვება და მყისიერი გახსნა. შესაძლოა გამოიწვიოს არასასარგებლო კონტენტის ჩვენება.</string>
<string name="copy_immediately">გაცვლის ბუფერში ავტომატური კოპირება</string>
@@ -106,18 +128,44 @@
<string name="show_meta_data_summary">სკანირებულ შტრიხ-კოდებზე დამატებითი ინფორმაციის ჩვენება.</string>
<string name="show_hex_dump">თექვსმეტობითი dump-ის ჩვენება</string>
<string name="show_hex_dump_summary">სკანირებული შტრიხ-კოდის კონტენტის თექვსმეტობითი dump-ის ჩვენება.</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">Go back after Copy/Share</string>
<string name="close_automatically_summary">Automatically return to the scan screen after copying or sharing the read contents.</string>
<string name="default_search_engine">Open unknown data in</string>
<string name="always_ask">Always ask</string>
<string name="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">უცნობი მონაცემების ამ ბმულის საშუალებით გახსნა</string>
<string name="clear_network_suggestions">Remove network suggestions</string>
<string name="automated_actions">ავტომატური მოქმედებები</string>
<string name="automated_actions_none">ავტომატური მოქმედებები არ არის</string>
<string name="automated_actions_empty">ჯერ ავტომატური მოქმედებები არ არის</string>
<plurals name="automated_actions_count">
<item quantity="one">%d ავტომატური მოქმედება</item>
<item quantity="other">%d ავტომატური მოქმედება</item>
</plurals>
<string name="automated_action_add">მოქმედების დამატება</string>
<string name="automated_action_edit">მოქმედების რედაქტირება</string>
<string name="automated_action_regex">რეგულარული გამოსახულება</string>
<string name="automated_action_regex_hint">მაგ. ^https?://.*</string>
<string name="automated_action_regex_required">რეგულარული გამოსახულება აუცილებელია</string>
<string name="automated_action_regex_invalid">არასწორი რეგულარული გამოსახულება</string>
<string name="automated_action_type">მოქმედების ტიპი</string>
<string name="automated_action_type_intent">URL-ის გახსნა</string>
<string name="automated_action_url_template">URL შაბლონი</string>
<string name="automated_action_url_hint">https://example.com/?q={RESULT}</string>
<string name="automated_action_url_required">URL შაბლონი აუცილებელია</string>
<string name="automated_action_invalid_url">არასწორი URL შაბლონი</string>
<string name="automated_action_remove_confirm">ამ მოქმედების წაშლა?</string>
<string name="automated_action_list_intent">URL გახსნა: %s</string>
<string name="automated_action_list_intent_empty">URL გახსნა</string>
<string name="automated_action_webview_title">ავტომატური მოქმედება</string>
<string name="clear_network_suggestions">ქსელის შეთანხმებების წაშლა</string>
<string name="clear_network_suggestions_summary">Remove network suggestions that were previously added with this app.</string>
<string name="really_remove_all_networks">Really remove all networks?</string>
<string name="really_remove_all_networks">ნამდვილად ამოიღოს ყველა ქსელი?</string>
<string name="clear_network_suggestions_success">Network suggestions cleared</string>
<string name="clear_network_suggestions_nothing_to_remove">Nothing to remove</string>
<string name="clear_network_suggestions_nothing_to_remove">არაფერი წასაშლელი არ არის</string>
<string name="send_category">გადაგზავნა</string>
<string name="send_scan_active">Forward scans</string>
<string name="send_scan_active_summary">Enable to forward scans to a the given URL</string>
@@ -131,11 +179,14 @@
<string name="test_url">Test 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="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>
@@ -169,8 +220,8 @@
<string name="otpauth_add">დავამატოთ 2FA</string>
<string name="search_web">ქსელში მოძებნა</string>
<string name="search_scan">სკანირების ძიება</string>
<string name="add_to_history">Add to history</string>
<string name="added_to_history">Added to history</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>
@@ -179,24 +230,39 @@
<string name="export_database">SQLite მონაცემთა ბაზა</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">Pick a 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">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>
<string name="automated_action_type_custom_intent">კონფიგურირებადი Intent</string>
<string name="automated_action_intent_uri_template">Intent URI შაბლონი</string>
<string name="automated_action_intent_uri_hint">intent:#Intent;action=android.intent.action.SEND;type=text/plain;S.android.intent.extra.TEXT={RESULT};end</string>
<string name="automated_action_intent_uri_required">Intent URI აუცილებელია</string>
<string name="automated_action_intent_uri_invalid">არასწორი Intent URI</string>
<string name="automated_action_list_custom_intent">კონფიგურირებადი Intent: %s</string>
<string name="automated_action_list_custom_intent_empty">კონფიგურირებადი Intent</string>
</resources>

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