Compare commits

...
76 Commits
Author SHA1 Message Date
Markus Fisch 6999f0a863 Advance version number to 1.22.2 2020-03-11 21:52:33 +01:00
Markus Fisch 6574a5d651 Fix getting window insets for Lollipop and above
Don't use the static `windowInsets` when `setWindowInsetListener()`
is called because this is just for pre-Lollipop devices where
`ViewCompat.setOnApplyWindowInsetsListener(view)` *never* fires.
2020-03-11 21:42:55 +01:00
Markus Fisch 1639e73738 Advance version number to 1.22.1 2020-03-10 19:32:06 +01:00
Markus Fisch 68fa67f54f Save non-printable content as raw data 2020-03-09 17:05:42 +01:00
Markus Fisch 326c34b6c1 Clean up some style issues 2020-03-09 17:04:31 +01:00
Markus Fisch a43dca3644 Advance version number to 1.22.0 2020-03-04 21:51:06 +01:00
Markus Fisch 168ccf7d10 Remove using a special tool version for Catalina
But keep using a special version for `buildToolsVersion` since
RenderScript requires a minimum of 29.0.3 on Catalina what
doesn't work on Linux yet.
2020-03-02 13:59:23 +01:00
Markus Fisch a9630b4d53 Optimize imports 2020-02-28 21:58:59 +01:00
Markus Fisch 4a138bd425 Highlight detected barcodes in camera view too
Although it's just visible before the result screen
takes over.
2020-02-28 21:48:54 +01:00
Markus Fisch 339ff59270 Update build tools 2020-02-27 22:22:21 +01:00
yzqzssandGitHub d208ec12bc Add bing and yandex search engines 2020-02-23 14:06:54 +01:00
yzqzssandGitHub 893ddefa71 Update Chinese Translation 2020-02-22 19:35:36 +01:00
Markus Fisch 5d56365e79 Skip forceCompat() when building on Catalina
Calling RenderScript.forceCompat() on a build that was made with the
new build tools (which are required to make RenderScript build again
on macOS Catalina), results in a RSRuntimeException (Loading of ScriptC
script failed).

RenderScript.forceCompat() was introduced to fix RenderScript for
Lineage 16.0. Maybe this is no longer required with those new build
tools?

Yes, this means this setup is producing platform independent builds
that have different features depending on what platform they were
built on. And yes, that's as bad as it sounds.

So, for the time being, it's probably better to _NOT_ use Catalina
to make release builds.
2020-02-20 15:08:52 +01:00
Markus Fisch 3237467503 Check for macOS Catalina and use appropriate tools
Since RenderScript can no longer be compiled on macOS Catalina
because Catalina cannot run 32bit binaries and Google failed
to provide a 64bit compiler in time. Now, as if that wasn't
bad enough, the new build tools that provide a 64bit compiler
don't work on any other system than macOS unfortunately.

For details on the issue see:
https://issuetracker.google.com/issues/142590626
2020-02-20 15:08:06 +01:00
Markus Fisch 346529469c Revert "Update build tools for macOS Catalina"
This reverts commit cf4020c613.

Those new build tools don't work on other systems than macOS
Catalina and even there produce broken builds :(

Also, it looks like the only solution is to remove
`RenderScript.forceCompat()` what's probably no longer
required to support Lineage 16.0 with those new build tools.
Confirmation pending.
2020-02-18 16:23:39 +01:00
zmniandGitHub 9f4e420dbb Update Indonesian translation 2020-02-17 15:09:33 +01:00
Markus Fisch cf4020c613 Update build tools for macOS Catalina
To make RenderScript compile again on Catalina.

For details see:
https://issuetracker.google.com/issues/142590626
2020-02-17 10:43:10 +01:00
Markus Fisch 93edde7ed1 Specify full backup rules
"true" isn't a valid value for android:fullBackupContent.
2020-02-17 10:36:19 +01:00
unbranchedandGitHub 362e462c2a Update Italian translation 2020-02-09 18:51:08 +01:00
Markus Fisch a7241bc0fb Fix typo in remark about commit messages 2020-02-08 12:43:57 +01:00
Markus Fisch f26046f34b Add links for all supported barcode formats
And put them into a list.
2020-02-08 12:39:44 +01:00
zmniandMarkus Fisch a70813ea5c Update Indonesian translation 2020-01-12 13:46:32 +01:00
Markus Fisch 3f251fdb66 Advance version number to 1.21.0 2019-12-29 17:03:28 +01:00
Markus Fisch b76cf329dd Ignore PickActivity for Firebase App Indexing
Because Lint/Firebase can't handle the VIEW intent filter
without a http URL. What doesn't make any sense here.
2019-12-29 17:00:18 +01:00
Markus Fisch e0ad177a62 Improve loading big images
Load a scaled down bitmap instead of trying to load the
whole image and then scaling it down. If the image is too
large, it cannot be loaded completely in the first place.
2019-12-29 13:57:35 +01:00
Markus Fisch 1223bd3679 Remove file scheme for VIEW intent
Because newer Androids use the "content://" scheme instead of the
"file://" scheme. So remove this restriction.
2019-12-29 12:44:33 +01:00
Markus Fisch b4e24fa328 Save and restore history list state
If we're going back to the list. Makes it far easier to
look through a long list.
2019-12-25 18:14:08 +01:00
Markus Fisch 0368ab5f68 Advance version number to 1.20.1 2019-12-22 13:50:05 +01:00
Markus Fisch c44ee8f063 Directly invoke insets listener below Lollipop
Because ViewCompat.setOnApplyWindowInsetsListener() will never
fire on devices below Lollipop.
2019-12-22 13:45:33 +01:00
Markus Fisch 3bfa6345a0 Update description in README 2019-12-21 18:31:56 +01:00
Markus Fisch 77d87ba995 Advance version number to 1.20.0 2019-12-21 18:13:48 +01:00
Markus Fisch a2ac68028f Put all FABs in a CoordinatorLayout 2019-12-21 17:49:56 +01:00
Markus Fisch aeb945f145 Make system bars a bit less transparent
So the foreground text can be seen better.
2019-12-21 17:49:28 +01:00
Markus Fisch aadde397ea Color nav bar when list is as high as the view too
Because there's now a list footer, the navigation bar at
the bottom of the screen must be translucent as soon as
the last item touches the end of the screen.
2019-12-21 17:49:19 +01:00
Markus Fisch 063fea913e Add additional next action item to encode fragment
Because android:windowSoftInputMode="adjustResize" doesn't go
well with android:windowFullscreen="true" and the latter one
is needed to draw below the system bars.
2019-12-21 17:26:07 +01:00
Markus Fisch 9d57889b81 Fix returning result for SCAN intents again
Actually use the flag to return the result and it's
working like it should.
2019-12-21 17:01:07 +01:00
Markus Fisch 233b69623f Revert "Fix returning result for SCAN intents"
SCAN intents are still processed in CameraActivity - not in
PickActivity.

This reverts commit 7416037762.
2019-12-21 16:59:42 +01:00
Markus Fisch c7d6e60efb Scroll content below tool and system bars 2019-12-21 16:11:23 +01:00
Markus Fisch 63688a9994 Refactor format to resolve variable shadowing 2019-12-20 10:53:07 +01:00
Markus Fisch dc74e33b8b Remove unnecessary <merge/> tag 2019-12-19 16:45:04 +01:00
Markus Fisch 81f2620a69 Add a style for secondary text
To better distinguish between content and secondary
information.
2019-12-19 16:44:29 +01:00
Markus Fisch 1c95550f5d Remove bottom margin after content on decode
Looks better with content length and format on one line
below it.
2019-12-19 16:43:19 +01:00
Markus Fisch 37a821818d Put character count and format on one line 2019-12-19 16:42:40 +01:00
Markus Fisch 29e0bdfbc3 Increase font size of barcode contents in history 2019-12-17 14:30:52 +01:00
Markus Fisch d4bbcef011 Fix transfering barcode format from history 2019-12-17 14:29:44 +01:00
Markus Fisch 81fc41ea81 Tag all activities as resizable 2019-12-17 14:11:42 +01:00
Markus Fisch 8cadf42c86 Convert encode button to a FAB
And make sure it's always reachable and above the
soft keyboard.
2019-12-17 14:09:10 +01:00
Markus Fisch e29ec2cc40 Fix some style issues
Apply auto format.
2019-12-17 13:57:54 +01:00
Markus Fisch 05952e7f7f Use explict locale when converting to lower case 2019-12-17 13:57:17 +01:00
Markus Fisch 0e8abb4607 Use a common style for all FABs 2019-12-17 13:51:22 +01:00
Markus Fisch cede9186a2 Use just 0 instead of an elaborate variable 2019-12-17 13:35:22 +01:00
Markus Fisch c1c9fa050b Fix casting context to AppCompatActivity
On Android 4.x, since support library 23.3.0, View.getContext()
returns a TintContextWrapper object instead of an Activity.
2019-12-17 13:30:27 +01:00
Markus Fisch 76d86288cc Preset file name to save with format and content
Saves typing it out again when saving a barcode as a file.
2019-12-17 11:34:42 +01:00
Markus Fisch c0902f8830 Update build tools 2019-12-17 11:11:22 +01:00
Markus Fisch 1d3546d676 Update Kotlin version 2019-12-09 19:48:50 +01:00
Balázs ÚrandMarkus Fisch 58baab6ffd Update Hungarian translation 2019-12-09 19:46:28 +01:00
zmniandMarkus Fisch 3bafa60d59 Update Indonesian translation 2019-11-28 13:25:57 +01:00
Markus Fisch 825062ba78 Advance version number to 1.19.0 2019-11-24 17:32:23 +01:00
Markus Fisch d128e3cc24 Use property access syntax
And remove explicit types.
2019-11-24 17:21:46 +01:00
Markus Fisch 83619834a8 Remember last selected barcode format
When encoding a new barcode.

Makes it easier to use the same format multiple times.
2019-11-24 17:11:54 +01:00
Markus Fisch cb16220ebb Update Kotlin and SDK version to 29 2019-11-22 10:55:20 +01:00
Markus Fisch 7f3af23773 Allow scaling a new barcode to half its size
Apparently some barcodes aren't created with enough "quiet space"
inside the generated image so scanning them from a screen can be
a little tough.
2019-11-19 21:52:20 +01:00
Markus Fisch 9898a14dd3 Advance version number to 1.18.0 2019-11-16 15:42:19 +01:00
Markus Fisch 85fc475118 Highlight codes in pick activity 2019-11-16 15:31:44 +01:00
Markus Fisch 987e8531e6 Add a setting to open contents without inspection 2019-11-16 14:46:25 +01:00
Markus Fisch 4d9a537bef Mention Downloads in Save-as-file dialog
To give the user an idea where the file will get saved.
2019-11-16 14:28:24 +01:00
Markus Fisch 05de15488e Fix cropping rotated images 2019-11-16 14:19:18 +01:00
Markus Fisch 721a3ee801 Improve visability of crop border 2019-11-16 14:16:33 +01:00
Markus Fisch bca000c21b Improve title of PickActivity 2019-11-16 13:20:20 +01:00
Markus Fisch 80afd0c5b7 Remove up navigation from PickActivity
There's no up - just back.
2019-11-16 13:10:09 +01:00
Markus Fisch 7416037762 Fix returning result for SCAN intents
Set parameter for `com.google.zxing.client.android.SCAN`
intents so the result is returned instead of displayed.
2019-11-16 13:00:57 +01:00
Markus Fisch 325aa00bf8 Make open URL default action on decode fragment
I think most users would rather want to open an URL from a
QR code (or something like that) than sharing the contents
of a barcode to another app.
2019-11-14 16:14:27 +01:00
Markus Fisch 40c2cc9b60 Improve handling of nullable types
And update a few other Kotlin improvements along the way.
2019-11-14 13:32:12 +01:00
Markus Fisch 1bf16a2fb7 Set explicit locale for case conversions
To make Lint happy. Shouldn't make a difference.
2019-11-14 13:31:12 +01:00
Markus Fisch 2687ce031a Add gradle.properties to repository
Since SonarQube is removed, this file can be part of the
repository again.
2019-11-14 12:58:43 +01:00
Markus Fisch 3213169ea1 Move PickActivity to the right directory
Misplaced by mistake.
2019-11-14 12:53:27 +01:00
68 changed files with 1115 additions and 532 deletions
-1
View File
@@ -4,6 +4,5 @@
.idea
build
local.properties
gradle.properties
infer-out
*.iml
+43
View File
@@ -1,5 +1,48 @@
# Change Log
# 1.22.2
* Fix getting window insets for Android Lollipop and above
# 1.22.1
* Ensure non-printable content is saved as raw data
# 1.22.0
* Highlight detected barcodes in camera view
* Add bing and yandex search engines
* Update Chinese Translation
* Update Italian translation
* Update Indonesian translation
# 1.21.0
* Save and restore history list state
* Improve scanning from large images
* Fix opening image files by "Open with"
# 1.20.1
* Fix window insets below Android Lollipop
# 1.20.0
* Scroll content below tool and system bars
* Preset file name to save with format and content
* Add encode menu item to make encoding easier when keyboard is visible
* Increase font size of barcode contents in history
* Update Hungarian translation
* Update Indonesian translation
* Fix encoding for old Androids
* Fix returning result for ZXing's SCAN intent again
* Fix transfering barcode format from history
# 1.19.0
* Allow scaling a new barcode to half its size
* Remember last selected barcode format
# 1.18.0
* Make open URL default action after decoding a barcode
* Add a setting to open contents without inspection
* Highlight codes for picking in shared/loaded images
* Improve visability of crop border
* Fix returning result for ZXing's SCAN intent
# 1.17.1
* Fix navigating back without choosing an image
+1 -1
View File
@@ -13,7 +13,7 @@ to squash when merging into master:
$ git merge cool_feature --squash
Then write a [good commit messages][commit_messages] to keep the history
Then write a [good commit message][commit_messages] to keep the history
meaningful and useful. One feature, one commit.
[android_best_practices]: https://developer.android.com/distribute/best-practices/develop/
+59 -7
View File
@@ -2,15 +2,51 @@
Yet another barcode scanner for Android. As if there weren't [enough][play].
This one is totally free, open source and does use the [ZXing][zxing] barcode
scanning library.
This one is free, without any ads and open source.
Unlike many others, it works in portrait and landscape orientation, can read
inverted codes, comes in Material Design and doesn't do much else but decoding
a barcode.
Works in portrait and landscape orientation, can read inverted codes,
comes in Material Design and can also generate barcodes.
And it's written in [Kotlin][kotlin] because I wanted to explore that a
bit more.
Binary Eye uses the [ZXing][zxing] ("Zebra Crossing") barcode scanning
library.
## Supported Barcode Formats
### Read
ZXing can _read_ the following barcode formats:
* [AZTEC][aztec]
* [CODABAR][codabar]
* [CODE 39][code_39]
* [CODE 93][code_93]
* [CODE 128][code_128]
* [DATA MATRIX][data_matrix]
* [EAN 8][ean_8]
* [EAN 13][ean_13]
* [ITF][itf]
* [MAXICODE][maxicode]
* [PDF417][pdf417]
* [QR CODE][qr_code]
* [RSS 14][rss]
* [RSS EXPANDED][rss]
* [UPC A][upc_a]
* [UPC E][upc_e]
* [UPC EAN EXTENSION][upc_ean]
### Generate
ZXing can _generate_ the following barcode formats:
* [AZTEC][aztec]
* [CODABAR][codabar]
* [CODE 39][code_39]
* [CODE 128][code_128]
* [DATA MATRIX][data_matrix]
* [EAN 8][ean_8]
* [EAN 13][ean_13]
* [ITF][itf]
* [PDF 417][pdf417]
* [QR CODE][qr_code]
* [UPC A][upc_a]
## Screenshots
@@ -31,3 +67,19 @@ bit more.
[play]: https://play.google.com/store/search?q=barcode%20scanner&c=apps
[zxing]: https://github.com/zxing/zxing
[kotlin]: http://kotlinlang.org/
[aztec]: https://en.wikipedia.org/wiki/Aztec_Code
[codabar]: https://en.wikipedia.org/wiki/Codabar
[code_39]: https://en.wikipedia.org/wiki/Code_39
[code_93]: https://en.wikipedia.org/wiki/Code_93
[code_128]: https://en.wikipedia.org/wiki/Code_128
[data_matrix]: https://en.wikipedia.org/wiki/Data_Matrix
[ean_8]: https://en.wikipedia.org/wiki/EAN-8
[ean_13]: https://en.wikipedia.org/wiki/International_Article_Number
[itf]: https://en.wikipedia.org/wiki/Interleaved_2_of_5
[maxicode]: https://en.wikipedia.org/wiki/MaxiCode
[pdf417]: https://en.wikipedia.org/wiki/PDF417
[qr_code]: https://en.wikipedia.org/wiki/QR_code
[rss]: https://en.wikipedia.org/wiki/GS1_DataBar
[upc_a]: https://en.wikipedia.org/wiki/Universal_Product_Code
[upc_e]: https://en.wikipedia.org/wiki/Universal_Product_Code#UPC-E
[upc_ean]: https://en.wikipedia.org/wiki/Universal_Product_Code#EAN-13
+7 -5
View File
@@ -9,8 +9,8 @@ android {
minSdkVersion 9
targetSdkVersion sdk_version
versionCode 45
versionName '1.17.1'
versionCode 54
versionName '1.22.2'
// it's recommended to set this value to the lowest API level
// able to provide all the functionality
@@ -18,6 +18,8 @@ android {
renderscriptSupportModeEnabled true
testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
buildConfigField 'boolean', 'IS_CATALINA', is_catalina.toString()
}
signingConfigs {
@@ -58,10 +60,10 @@ android {
}
dependencies {
androidTestImplementation ('com.android.support.test:runner:1.0.2') {
androidTestImplementation('com.android.support.test:runner:1.0.2') {
exclude module: 'support-annotations'
}
androidTestImplementation ('com.android.support.test:rules:1.0.2') {
androidTestImplementation('com.android.support.test:rules:1.0.2') {
exclude module: 'support-annotations'
}
testImplementation 'junit:junit:4.12'
@@ -74,5 +76,5 @@ dependencies {
// https://github.com/zxing/zxing/issues/1170
implementation 'com.google.zxing:core:3.3.3'
implementation 'com.github.markusfisch:CameraView:1.8.4'
implementation 'com.github.markusfisch:ScalingImageView:1.1.4'
implementation 'com.github.markusfisch:ScalingImageView:1.2.0'
}
+2 -2
View File
@@ -3,10 +3,10 @@
<!-- Lint can't understand Kotlin sources and reports all resources
as unused -->
<issue id="UnusedResources">
<ignore path="src/main/res/"/>
<ignore path="src/main/res/" />
</issue>
<!-- Ignore RenderScript output -->
<issue id="UnusedResources">
<ignore path="*/res/rs/debug/raw/bc*"/>
<ignore path="*/res/rs/debug/raw/bc*" />
</issue>
</lint>
@@ -21,22 +21,21 @@ class PreprocessorTest {
val pattern = Pattern.compile(
"[0-9]+-([0-9]+)x([0-9]+)-([0-9]+)deg.yuv"
)
for (file in assets.list("yuv")) {
val files = assets.list("yuv") ?: return
for (file in files) {
val m = pattern.matcher(file)
if (!m.find() || m.groupCount() < 3) {
continue
}
val frameWidth = m.group(1).toInt()
val frameHeight = m.group(2).toInt()
val frameOrientation = m.group(3).toInt()
val frameWidth = m.group(1) ?: return
val frameHeight = m.group(2) ?: return
val frameOrientation = m.group(3) ?: return
val frameData = assets.open("yuv/$file").readBytes()
val preprocessor = Preprocessor(
InstrumentationRegistry.getTargetContext(),
frameWidth,
frameHeight,
frameOrientation
frameWidth.toInt(),
frameHeight.toInt(),
frameOrientation.toInt()
)
preprocessor.process(frameData)
val result = zxing.decode(
+5 -3
View File
@@ -16,8 +16,9 @@
tools:ignore="UnusedAttribute"
android:name=".app.BinaryEyeApp"
android:allowBackup="true"
android:fullBackupContent="true"
android:fullBackupContent="@xml/backup_rules"
android:supportsRtl="true"
android:resizeableActivity="true"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:theme="@style/AppTheme">
@@ -54,7 +55,8 @@
</activity>
<activity
android:name=".activity.PickActivity"
android:label="@string/scan_code">
android:label="@string/pick_code_to_scan"
tools:ignore="AppLinkUrlError">
<intent-filter android:label="@string/decode_barcode">
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
@@ -63,7 +65,7 @@
<intent-filter android:label="@string/decode_barcode">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="image/*" android:scheme="file"/>
<data android:mimeType="image/*"/>
</intent-filter>
</activity>
<activity
@@ -1,5 +1,7 @@
package de.markusfisch.android.binaryeye.actions.vtype
import java.util.*
object VTypeParser {
private const val BACKSLASH_R_LEGACY =
"(?:\u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029])"
@@ -10,13 +12,13 @@ object VTypeParser {
private val propertyRegex = """^(.+?):([\s\S]*?)$""".toRegex(RegexOption.MULTILINE)
fun parseVType(data: String): String? =
vTypeRegex.matchEntire(data)?.groupValues?.get(1)?.toUpperCase()
vTypeRegex.matchEntire(data)?.groupValues?.get(1)?.toUpperCase(Locale.US)
fun parseMap(data: String): Map<String, List<VTypeProperty>> = propertyRegex.findAll(data).map {
it.groupValues[1].split(';').let { typeAndInfo ->
typeAndInfo[0] to VTypeProperty(typeAndInfo.drop(1), it.groupValues[2])
}
}.groupBy({ it.first.toUpperCase() }) { it.second }
}.groupBy({ it.first.toUpperCase(Locale.US) }) { it.second }
}
data class VTypeProperty(
@@ -8,6 +8,8 @@ import android.provider.ContactsContract
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.actions.IntentAction
import de.markusfisch.android.binaryeye.actions.vtype.VTypeParser
import java.util.*
import kotlin.collections.ArrayList
object VCardAction : IntentAction() {
override val iconResId: Int
@@ -46,7 +48,10 @@ object VCardAction : IntentAction() {
putExtra(extraPhoneType, phoneType)
} ?: putExtra(extraPhoneType, it)
}
putExtra(extraPhone, phoneProperty.value.toLowerCase().removePrefix("tel:"))
putExtra(
extraPhone,
phoneProperty.value.toLowerCase(Locale.US).removePrefix("tel:")
)
}
info["EMAIL"]?.forEachIndexed { index, mailProperty ->
val (extraMailType, extraMail) = when (index) {
@@ -135,7 +140,7 @@ object VCardAction : IntentAction() {
} ?: this
private val String.mailType: Int?
get() = when (this.toUpperCase()) {
get() = when (this.toUpperCase(Locale.US)) {
"HOME" -> ContactsContract.CommonDataKinds.Email.TYPE_HOME
"WORK" -> ContactsContract.CommonDataKinds.Email.TYPE_WORK
"OTHER" -> ContactsContract.CommonDataKinds.Email.TYPE_OTHER
@@ -144,7 +149,7 @@ object VCardAction : IntentAction() {
}
private val String.phoneType: Int?
get() = when (this.toUpperCase()) {
get() = when (this.toUpperCase(Locale.US)) {
"HOME" -> ContactsContract.CommonDataKinds.Phone.TYPE_HOME
"MOBILE", "CELL" -> ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE
"WORK" -> ContactsContract.CommonDataKinds.Phone.TYPE_WORK
@@ -169,7 +174,7 @@ object VCardAction : IntentAction() {
}
private val String.addressType: Int?
get() = when (this.toUpperCase()) {
get() = when (this.toUpperCase(Locale.US)) {
"HOME" -> ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME
"WORK" -> ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK
"OTHER" -> ContactsContract.CommonDataKinds.StructuredPostal.TYPE_OTHER
@@ -10,7 +10,7 @@ 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.Date
import java.util.*
object VEventAction : IntentAction() {
override val iconResId: Int
@@ -3,6 +3,7 @@ package de.markusfisch.android.binaryeye.actions.wifi
import android.net.wifi.WifiConfiguration
import android.net.wifi.WifiEnterpriseConfig
import android.os.Build
import java.util.*
/**
* Normal:
@@ -47,7 +48,7 @@ object WifiConfigurationFactory {
string
)?.groupValues?.get(1)?.let { pairs ->
pairRegex.findAll(pairs).map { pair ->
pair.groupValues[1].toUpperCase() to pair.groupValues[2]
pair.groupValues[1].toUpperCase(Locale.US) to pair.groupValues[2]
}.toMap()
}
}
@@ -22,7 +22,10 @@ import de.markusfisch.android.binaryeye.app.db
import de.markusfisch.android.binaryeye.app.hasCameraPermission
import de.markusfisch.android.binaryeye.app.initSystemBars
import de.markusfisch.android.binaryeye.app.prefs
import de.markusfisch.android.binaryeye.graphics.Mapping
import de.markusfisch.android.binaryeye.graphics.frameToView
import de.markusfisch.android.binaryeye.rs.Preprocessor
import de.markusfisch.android.binaryeye.widget.DetectorView
import de.markusfisch.android.binaryeye.zxing.Zxing
import de.markusfisch.android.cameraview.widget.CameraView
import kotlinx.coroutines.GlobalScope
@@ -33,6 +36,7 @@ class CameraActivity : AppCompatActivity() {
private lateinit var vibrator: Vibrator
private lateinit var cameraView: CameraView
private lateinit var detectorView: DetectorView
private lateinit var zoomBar: SeekBar
private lateinit var flashFab: View
@@ -83,12 +87,13 @@ class CameraActivity : AppCompatActivity() {
super.onCreate(state)
setContentView(R.layout.activity_camera)
vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
initSystemBars(this)
setSupportActionBar(findViewById(R.id.toolbar) as Toolbar)
vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
cameraView = findViewById(R.id.camera_view) as CameraView
detectorView = findViewById(R.id.detector_view) as DetectorView
zoomBar = findViewById(R.id.zoom) as SeekBar
flashFab = findViewById(R.id.flash)
flashFab.setOnClickListener { toggleTorchMode() }
@@ -264,6 +269,7 @@ class CameraActivity : AppCompatActivity() {
val frameWidth = cameraView.frameWidth
val frameHeight = cameraView.frameHeight
val frameOrientation = cameraView.frameOrientation
var mapping: Mapping? = null
var decoding = true
camera.setPreviewCallback { frameData, _ ->
if (decoding) {
@@ -274,9 +280,25 @@ class CameraActivity : AppCompatActivity() {
frameOrientation
)
result?.let {
preprocessor?.let {
mapping = mapping ?: frameToView(
it.outWidth,
it.outHeight,
cameraView.previewRect
)
}
cameraView.post {
mapping?.let {
detectorView.mark(
it.map(result.resultPoints)
)
}
vibrator.vibrate(100)
showResult(this@CameraActivity, result)
showResult(
this@CameraActivity,
result,
returnResult
)
}
decoding = false
}
@@ -8,9 +8,10 @@ import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import com.google.zxing.BarcodeFormat
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.app.colorSystemAndToolBars
import de.markusfisch.android.binaryeye.app.initSystemBars
import de.markusfisch.android.binaryeye.app.setFragment
import de.markusfisch.android.binaryeye.app.setSystemAndToolBarTransparency
import de.markusfisch.android.binaryeye.app.setupInsets
import de.markusfisch.android.binaryeye.fragment.DecodeFragment
import de.markusfisch.android.binaryeye.fragment.EncodeFragment
import de.markusfisch.android.binaryeye.fragment.HistoryFragment
@@ -30,14 +31,16 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(state: Bundle?) {
super.onCreate(state)
setContentView(R.layout.activity_main)
initSystemBars(this)
setSupportActionBar(findViewById(R.id.toolbar) as Toolbar)
initSystemBars(this)
val toolbar = findViewById(R.id.toolbar) as Toolbar
setupInsets(findViewById(android.R.id.content), toolbar)
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportFragmentManager.addOnBackStackChangedListener({
setSystemAndToolBarTransparency(this@MainActivity)
})
supportFragmentManager.addOnBackStackChangedListener {
colorSystemAndToolBars(this@MainActivity)
}
if (state == null) {
setFragment(
@@ -48,11 +51,11 @@ class MainActivity : AppCompatActivity() {
HistoryFragment()
intent?.hasExtra(ENCODE) == true ->
EncodeFragment.newInstance(
intent.getStringExtra(ENCODE)
intent.getStringExtra(ENCODE) ?: ""
)
intent?.hasExtra(DECODED_TEXT) == true ->
DecodeFragment.newInstance(
intent.getStringExtra(DECODED_TEXT),
intent.getStringExtra(DECODED_TEXT) ?: "",
intent.getSerializableExtra(
DECODED_FORMAT
) as BarcodeFormat,
@@ -1,41 +1,51 @@
package de.markusfisch.android.binaryeye.activity
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Point
import android.graphics.Rect
import android.net.Uri
import android.os.Bundle
import android.os.Parcelable
import android.provider.MediaStore
import android.os.Vibrator
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import android.view.Menu
import android.view.MenuItem
import android.widget.Toast
import com.google.zxing.Result
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.app.colorSystemAndToolBars
import de.markusfisch.android.binaryeye.app.initSystemBars
import de.markusfisch.android.binaryeye.app.setSystemAndToolBarTransparency
import de.markusfisch.android.binaryeye.app.setWindowInsetListener
import de.markusfisch.android.binaryeye.app.setupInsets
import de.markusfisch.android.binaryeye.graphics.crop
import de.markusfisch.android.binaryeye.graphics.downsizeIfBigger
import de.markusfisch.android.binaryeye.graphics.loadImageUri
import de.markusfisch.android.binaryeye.graphics.mapResult
import de.markusfisch.android.binaryeye.widget.CropImageView
import de.markusfisch.android.binaryeye.zxing.Zxing
import java.io.IOException
class PickActivity : AppCompatActivity() {
private val zxing = Zxing()
private lateinit var vibrator: Vibrator
private lateinit var cropImageView: CropImageView
override fun onCreate(state: Bundle?) {
super.onCreate(state)
setContentView(R.layout.activity_pick)
vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
initSystemBars(this)
val toolbar = findViewById(R.id.toolbar) as Toolbar
setupInsets(findViewById(android.R.id.content), toolbar)
setSupportActionBar(toolbar)
setSupportActionBar(findViewById(R.id.toolbar) as Toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportFragmentManager.addOnBackStackChangedListener({
setSystemAndToolBarTransparency(this@PickActivity)
})
supportFragmentManager.addOnBackStackChangedListener {
colorSystemAndToolBars(this@PickActivity)
}
val bitmap = if (
intent?.action == Intent.ACTION_SEND &&
@@ -61,18 +71,41 @@ class PickActivity : AppCompatActivity() {
return
}
cropImageView = findViewById(R.id.image) as CropImageView
cropImageView.setImageBitmap(bitmap)
findViewById(R.id.scan).setOnClickListener {
val detail = crop(
val scannedRect = Rect()
val scanWithinBounds: () -> Result? = {
var result: Result? = null
crop(
bitmap,
cropImageView.normalizedRectInBounds,
cropImageView.imageRotation
)
detail?.let {
scanImage(detail)
)?.also {
scannedRect.set(0, 0, it.width, it.height)
result = zxing.decodePositiveNegative(it)
}
result
}
cropImageView = findViewById(R.id.image) as CropImageView
cropImageView.setImageBitmap(bitmap)
cropImageView.onScan = {
var points: List<Point>? = null
scanWithinBounds()?.let {
points = mapResult(
scannedRect.width(),
scannedRect.height(),
cropImageView.getBoundsRect(),
it
)
vibrator.vibrate(100)
}
points
}
setWindowInsetListener { insets ->
cropImageView.windowInsets.set(insets)
}
findViewById(R.id.scan).setOnClickListener {
scanImage(scanWithinBounds())
}
}
@@ -99,25 +132,15 @@ class PickActivity : AppCompatActivity() {
val uri = intent.getParcelableExtra<Parcelable>(
Intent.EXTRA_STREAM
) as? Uri ?: return null
return loadImageUri(uri)
return loadImageUri(contentResolver, uri)
}
private fun loadImageToOpen(intent: Intent): Bitmap? {
val uri = intent.data ?: return null
return loadImageUri(uri)
return loadImageUri(contentResolver, uri)
}
private fun loadImageUri(uri: Uri): Bitmap? = try {
downsizeIfBigger(
MediaStore.Images.Media.getBitmap(contentResolver, uri),
1024
)
} catch (e: IOException) {
null
}
private fun scanImage(bitmap: Bitmap) {
val result = zxing.decodePositiveNegative(bitmap)
private fun scanImage(result: Result?) {
if (result != null) {
showResult(this, result)
finish()
@@ -20,11 +20,9 @@ class ScansAdapter(context: Context, cursor: Cursor) :
context: Context,
cursor: Cursor,
parent: ViewGroup
): View {
return LayoutInflater.from(parent.context).inflate(
R.layout.row_scan, parent, false
)
}
) = LayoutInflater.from(parent.context).inflate(
R.layout.row_scan, parent, false
)
override fun bindView(
view: View,
@@ -2,6 +2,7 @@ package de.markusfisch.android.binaryeye.app
import android.app.Application
import android.support.v8.renderscript.RenderScript
import de.markusfisch.android.binaryeye.BuildConfig
import de.markusfisch.android.binaryeye.preference.Preferences
import de.markusfisch.android.binaryeye.repository.DatabaseRepository
@@ -12,9 +13,19 @@ class BinaryEyeApp : Application() {
override fun onCreate() {
super.onCreate()
// required to make RenderScript work for Lineage 16.0
// possibly because of a system/device bug
RenderScript.forceCompat()
// Since RenderScript can no longer be compiled on macOS Catalina
// because Catalina cannot run 32bit binaries and Google failed
// to provide a 64bit compiler in time. Now, as if that wasn't
// bad enough, `RenderScript.forceCompat()` makes any build compiled
// with the new build tools that provide a 64bit compiler crash
// so we need to exclude this if we're building on Catalina.
// Maybe `RenderScript.forceCompat()` isn't required anymore for
// those new builds but I don't have a device to test this.
if (!BuildConfig.IS_CATALINA) {
// required to make RenderScript work for Lineage 16.0
// possibly because of a system/device bug
RenderScript.forceCompat()
}
db.open(this)
prefs.init(this)
@@ -1,17 +1,18 @@
package de.markusfisch.android.binaryeye.app
import android.content.Context
import android.graphics.drawable.ColorDrawable
import android.content.ContextWrapper
import android.graphics.Rect
import android.graphics.drawable.ColorDrawable
import android.os.Build
import android.support.v4.content.ContextCompat
import android.support.v4.view.ViewCompat
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import android.view.View
import android.widget.AbsListView
import de.markusfisch.android.binaryeye.R
val windowInsets = Rect()
val systemBarScrollListener = object : AbsListView.OnScrollListener {
override fun onScroll(
view: AbsListView,
@@ -25,8 +26,8 @@ val systemBarScrollListener = object : AbsListView.OnScrollListener {
val scrolled = firstVisibleItem > 0 ||
(totalItemCount > 0 && view.getChildAt(0).top < 0)
val scrollable = if (scrolled) true else totalItemCount > 0 &&
view.getChildAt(view.lastVisiblePosition).bottom > view.height
setSystemAndToolBarTransparency(view.context, scrolled, scrollable)
view.getChildAt(view.lastVisiblePosition).bottom >= view.height
colorSystemAndToolBars(view.context, scrolled, scrollable)
}
}
@@ -37,16 +38,25 @@ val systemBarScrollListener = object : AbsListView.OnScrollListener {
}
}
fun setSystemAndToolBarTransparency(
fun initSystemBars(activity: AppCompatActivity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.window.decorView.systemUiVisibility =
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
}
colorSystemAndToolBars(activity)
}
fun colorSystemAndToolBars(
context: Context,
scrolled: Boolean = false,
scrollable: Boolean = false
) {
val opaqueColor = ContextCompat.getColor(context, R.color.primary)
val transparentColor = 0x00000000
val topColor = if (scrolled) opaqueColor else transparentColor
val bottomColor = if (scrolled || scrollable) opaqueColor else transparentColor
val activity = context as AppCompatActivity
val translucentColor = getTranslucentPrimaryColor(context)
val topColor = if (scrolled) translucentColor else 0
val bottomColor = if (scrolled || scrollable) translucentColor else 0
val activity = getAppCompatActivity(context) ?: return
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val window = activity.window
window.statusBarColor = topColor
@@ -55,29 +65,54 @@ fun setSystemAndToolBarTransparency(
activity.supportActionBar?.setBackgroundDrawable(ColorDrawable(topColor))
}
fun initSystemBars(activity: AppCompatActivity) {
activity.findViewById(R.id.main_layout)?.also { view ->
setWindowInsetListener(view)
private fun getTranslucentPrimaryColor(context: Context) = ContextCompat.getColor(
context,
R.color.primary
) and 0xffffff or 0xd8000000.toInt()
private fun getAppCompatActivity(context: Context): AppCompatActivity? {
var ctx = context
while (ctx is ContextWrapper) {
if (ctx is AppCompatActivity) {
return ctx
}
ctx = ctx.baseContext ?: break
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.window.decorView.systemUiVisibility =
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
}
setSystemAndToolBarTransparency(activity)
return null
}
private fun setWindowInsetListener(view: View) {
ViewCompat.setOnApplyWindowInsetsListener(view) { _, insets ->
if (insets.hasSystemWindowInsets()) {
val left = insets.systemWindowInsetLeft
val top = insets.systemWindowInsetTop
val right = insets.systemWindowInsetRight
val bottom = insets.systemWindowInsetBottom
view.setPadding(left, top, right, bottom)
windowInsets.set(left, top, right, bottom)
}
insets.consumeSystemWindowInsets()
private var windowInsetsListener: ((insets: Rect) -> Unit)? = null
// invoke this from Fragment.onCreateView() *or* Activity.onCreate()
fun setWindowInsetListener(listener: (insets: Rect) -> Unit) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
listener(windowInsets)
} else {
windowInsetsListener = listener
}
}
private val windowInsets = Rect()
// invoke this from Activity.onCreate() to setup listening for insets
fun setupInsets(view: View, toolbar: Toolbar) {
val toolBarHeight = toolbar.layoutParams.height
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
// save insets so setWindowInsetListener() can return them
// when it's called from Fragment.onCreateView() later on
windowInsets.set(0, toolBarHeight, 0, 0)
windowInsetsListener?.also { it(windowInsets) }
return
}
ViewCompat.setOnApplyWindowInsetsListener(view) { _, insets ->
// runs *after* layout and thus *after* Fragment.onCreateView()
if (insets.hasSystemWindowInsets()) {
windowInsets.set(
insets.systemWindowInsetLeft,
insets.systemWindowInsetTop + toolBarHeight,
insets.systemWindowInsetRight,
insets.systemWindowInsetBottom
)
windowInsetsListener?.also { it(windowInsets) }
}
insets
}
}
@@ -59,9 +59,9 @@ class Database {
timestamp
).toString()
)
if (hasNonPrintableCharacters(content) && raw != null) {
if (hasNonPrintableCharacters(content)) {
cv.put(SCANS_CONTENT, "")
cv.put(SCANS_RAW, raw)
cv.put(SCANS_RAW, if (raw != null) raw else content.toByteArray())
} else {
cv.put(SCANS_CONTENT, content)
}
@@ -2,12 +2,7 @@ package de.markusfisch.android.binaryeye.data.csv
import de.markusfisch.android.binaryeye.app.addDelimiter
import de.markusfisch.android.binaryeye.app.asFlowWith
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flattenConcat
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.*
fun <T> csvBuilder(config: CSVBuilder<T>.() -> Unit): CSVBuilder<T> {
return CSVBuilder<T>().apply(config)
@@ -1,24 +1,22 @@
package de.markusfisch.android.binaryeye.fragment
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.graphics.Bitmap
import android.os.Bundle
import android.os.Environment
import android.support.v4.app.Fragment
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.view.*
import android.widget.EditText
import android.widget.ImageView
import android.widget.Toast
import com.google.zxing.BarcodeFormat
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.app.addSuffixIfNotGiven
import de.markusfisch.android.binaryeye.app.hasWritePermission
import de.markusfisch.android.binaryeye.app.setWindowInsetListener
import de.markusfisch.android.binaryeye.app.shareFile
import de.markusfisch.android.binaryeye.view.setPadding
import de.markusfisch.android.binaryeye.widget.ConfinedScalingImageView
import de.markusfisch.android.binaryeye.zxing.Zxing
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.GlobalScope
@@ -26,9 +24,12 @@ import kotlinx.coroutines.launch
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.util.*
class BarcodeFragment : Fragment() {
private var barcode: Bitmap? = null
private var content: String = ""
private var format: BarcodeFormat? = null
override fun onCreate(state: Bundle?) {
super.onCreate(state)
@@ -40,7 +41,7 @@ class BarcodeFragment : Fragment() {
container: ViewGroup?,
state: Bundle?
): View? {
activity.setTitle(R.string.view_barcode)
activity?.setTitle(R.string.view_barcode)
val view = inflater.inflate(
R.layout.fragment_barcode,
@@ -63,7 +64,18 @@ class BarcodeFragment : Fragment() {
fragmentManager.popBackStack()
return null
}
view.findViewById<ImageView>(R.id.barcode).setImageBitmap(barcode)
this.content = content
this.format = format
val imageView = view.findViewById<ConfinedScalingImageView>(
R.id.barcode
)
imageView.setImageBitmap(barcode)
imageView.post {
// make sure to invoke this after ScalingImageView.onLayout()
imageView.minWidth /= 2f
}
view.findViewById<View>(R.id.share).setOnClickListener {
val bitmap = barcode
bitmap?.let {
@@ -71,6 +83,11 @@ class BarcodeFragment : Fragment() {
}
}
setWindowInsetListener { insets ->
(view.findViewById(R.id.inset_layout) as View).setPadding(insets)
imageView.insets.set(insets)
}
return view
}
@@ -88,10 +105,13 @@ class BarcodeFragment : Fragment() {
}
}
// dialogs do not have a parent view
@SuppressLint("InflateParams")
private fun askForFileNameAndSave() {
val ac = activity ?: return
val view = ac.layoutInflater.inflate(R.layout.dialog_save_file, null)
val editText = view.findViewById<EditText>(R.id.file_name)
editText.setText(encodeFileName("${format.toString()}_$content"))
AlertDialog.Builder(ac)
.setView(view)
.setPositiveButton(android.R.string.ok) { _, _ ->
@@ -112,7 +132,8 @@ class BarcodeFragment : Fragment() {
}
private fun saveAsFile(bitmap: Bitmap, path: String) {
if (!hasWritePermission(activity)) {
val ac = activity ?: return
if (!hasWritePermission(ac)) {
return
}
GlobalScope.launch {
@@ -126,18 +147,15 @@ class BarcodeFragment : Fragment() {
)
)
GlobalScope.launch(Main) {
val ac = activity
ac?.let {
Toast.makeText(
ac,
if (success) {
R.string.saved_in_downloads
} else {
R.string.error_saving_binary_data
},
Toast.LENGTH_LONG
).show()
}
Toast.makeText(
ac,
if (success) {
R.string.saved_in_downloads
} else {
R.string.error_saving_binary_data
},
Toast.LENGTH_LONG
).show()
}
}
}
@@ -197,3 +215,7 @@ private fun saveBitmap(bitmap: Bitmap, file: File): Boolean {
false
}
}
private val fileNameCharacters = "[^A-Za-z0-9]".toRegex()
private fun encodeFileName(name: String): String =
fileNameCharacters.replace(name, "_").take(16).trim('_').toLowerCase(Locale.getDefault())
@@ -1,30 +1,22 @@
package de.markusfisch.android.binaryeye.fragment
import android.content.Context
import android.os.Build
import android.os.Bundle
import android.support.design.widget.FloatingActionButton
import android.support.v4.app.Fragment
import android.text.ClipboardManager
import android.text.Editable
import android.text.TextWatcher
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.view.*
import android.widget.EditText
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import com.google.zxing.BarcodeFormat
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.actions.ActionRegistry
import de.markusfisch.android.binaryeye.app.addFragment
import de.markusfisch.android.binaryeye.app.askForFileName
import de.markusfisch.android.binaryeye.app.hasNonPrintableCharacters
import de.markusfisch.android.binaryeye.app.hasWritePermission
import de.markusfisch.android.binaryeye.app.shareText
import de.markusfisch.android.binaryeye.app.writeToFile
import de.markusfisch.android.binaryeye.app.*
import de.markusfisch.android.binaryeye.view.setPadding
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -36,7 +28,7 @@ class DecodeFragment : Fragment() {
private lateinit var formatView: TextView
private lateinit var hexView: TextView
private lateinit var format: BarcodeFormat
private lateinit var actionMenuItem: MenuItem
private lateinit var fab: FloatingActionButton
private var action = ActionRegistry.DEFAULT_ACTION
private var isBinary = false
@@ -56,7 +48,7 @@ class DecodeFragment : Fragment() {
container: ViewGroup?,
state: Bundle?
): View {
activity.setTitle(R.string.content)
activity?.setTitle(R.string.content)
val view = inflater.inflate(
R.layout.fragment_decode,
@@ -70,7 +62,7 @@ class DecodeFragment : Fragment() {
format = arguments?.getSerializable(FORMAT) as BarcodeFormat? ?: BarcodeFormat.QR_CODE
contentView = view.findViewById(R.id.content)
val shareFab = view.findViewById<ImageView>(R.id.share)
fab = view.findViewById(R.id.open)
if (!isBinary) {
contentView.setText(inputContent)
@@ -95,14 +87,17 @@ class DecodeFragment : Fragment() {
) {
}
})
shareFab.setOnClickListener { v ->
shareText(v.context, content)
fab.setOnClickListener {
executeAction(content.toByteArray())
}
if (prefs.openImmediately) {
executeAction(content.toByteArray())
}
} else {
contentView.setText(R.string.binary_data)
contentView.isEnabled = false
shareFab.setImageResource(R.drawable.ic_action_save)
shareFab.setOnClickListener {
fab.setImageResource(R.drawable.ic_action_save)
fab.setOnClickListener {
askForFileNameAndSave(raw)
}
}
@@ -112,6 +107,11 @@ class DecodeFragment : Fragment() {
updateViewsAndAction(raw)
setWindowInsetListener { insets ->
(view.findViewById(R.id.inset_layout) as View).setPadding(insets)
(view.findViewById(R.id.scroll_view) as View).setPadding(insets)
}
return view
}
@@ -127,20 +127,27 @@ class DecodeFragment : Fragment() {
bytes.size
)
hexView.text = hexDump(bytes, 33)
if (::actionMenuItem.isInitialized && prevAction !== action) {
actionMenuItem.setIcon(action.iconResId)
actionMenuItem.setTitle(action.titleResId)
if (prevAction !== action) {
fab.setImageResource(action.iconResId)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
fab.setOnLongClickListener { v ->
Toast.makeText(
v.context,
action.titleResId,
Toast.LENGTH_SHORT
).show()
true
}
} else {
fab.tooltipText = getString(action.titleResId)
}
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.fragment_decode, menu)
actionMenuItem = menu.findItem(R.id.open_url)
actionMenuItem.setIcon(action.iconResId)
actionMenuItem.setTitle(action.titleResId)
if (isBinary) {
menu.findItem(R.id.copy_to_clipboard).isVisible = false
menu.findItem(R.id.open_url).isVisible = false
menu.findItem(R.id.create).isVisible = false
}
}
@@ -151,8 +158,8 @@ class DecodeFragment : Fragment() {
copyToClipboard(content)
true
}
R.id.open_url -> {
executeAction(content.toByteArray())
R.id.share -> {
context?.also { shareText(it, content) }
true
}
R.id.create -> {
@@ -3,19 +3,22 @@ package de.markusfisch.android.binaryeye.fragment
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.view.*
import android.view.inputmethod.InputMethodManager
import android.widget.*
import com.google.zxing.BarcodeFormat
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.app.addFragment
import de.markusfisch.android.binaryeye.app.prefs
import de.markusfisch.android.binaryeye.app.setWindowInsetListener
import de.markusfisch.android.binaryeye.view.setPadding
class EncodeFragment : Fragment() {
private lateinit var formatView: Spinner
private lateinit var sizeView: TextView
private lateinit var sizeBarView: SeekBar
private lateinit var contentView: EditText
private val writers = arrayListOf(
BarcodeFormat.AZTEC,
BarcodeFormat.CODABAR,
@@ -30,12 +33,18 @@ class EncodeFragment : Fragment() {
BarcodeFormat.UPC_A
)
override fun onCreate(state: Bundle?) {
super.onCreate(state)
setHasOptionsMenu(true)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
state: Bundle?
): View {
activity.setTitle(R.string.compose_barcode)
): View? {
val ac = activity ?: return null
ac.setTitle(R.string.compose_barcode)
val view = inflater.inflate(
R.layout.fragment_encode,
@@ -45,7 +54,7 @@ class EncodeFragment : Fragment() {
formatView = view.findViewById(R.id.format)
formatView.adapter = ArrayAdapter<String>(
activity,
ac,
android.R.layout.simple_list_item_1,
writers.map { it.name }
)
@@ -54,40 +63,72 @@ class EncodeFragment : Fragment() {
sizeBarView = view.findViewById(R.id.size_bar)
initSizeBar()
val contentView = view.findViewById<EditText>(R.id.content)
contentView = view.findViewById<EditText>(R.id.content)
val args = arguments
args?.let {
contentView.setText(args.getString(CONTENT))
formatView.setSelection(
writers.indexOf(
args.getSerializable(FORMAT) as BarcodeFormat?
)
)
args?.also {
contentView.setText(it.getString(CONTENT))
}
view.findViewById<View>(R.id.encode).setOnClickListener { v ->
val format = writers[formatView.selectedItemPosition]
val size = getSize(sizeBarView.progress)
val content = contentView.text.toString()
if (content.isEmpty()) {
Toast.makeText(
v.context,
R.string.error_no_content,
Toast.LENGTH_SHORT
).show()
} else {
hideSoftKeyboard(contentView)
addFragment(
fragmentManager,
BarcodeFragment.newInstance(content, format, size)
)
val barcodeFormat = args?.getSerializable(FORMAT) as BarcodeFormat?
if (barcodeFormat != null) {
formatView.setSelection(writers.indexOf(barcodeFormat))
} else if (state == null) {
formatView.post {
formatView.setSelection(prefs.indexOfLastSelectedFormat)
}
}
view.findViewById<View>(R.id.encode).setOnClickListener { v ->
encode()
}
setWindowInsetListener { insets ->
(view.findViewById(R.id.inset_layout) as View).setPadding(insets)
(view.findViewById(R.id.scroll_view) as View).setPadding(insets)
}
return view
}
override fun onPause() {
super.onPause()
prefs.indexOfLastSelectedFormat = formatView.selectedItemPosition
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.fragment_encode, menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.next -> {
encode()
true
}
else -> super.onOptionsItemSelected(item)
}
}
private fun encode() {
val format = writers[formatView.selectedItemPosition]
val size = getSize(sizeBarView.progress)
val content = contentView.text.toString()
if (content.isEmpty()) {
Toast.makeText(
context,
R.string.error_no_content,
Toast.LENGTH_SHORT
).show()
} else {
hideSoftKeyboard(contentView)
addFragment(
fragmentManager,
BarcodeFragment.newInstance(content, format, size)
)
}
}
private fun initSizeBar() {
updateSize(sizeBarView.progress)
sizeBarView.setOnSeekBarChangeListener(
@@ -128,11 +169,13 @@ class EncodeFragment : Fragment() {
fun newInstance(
content: String,
format: BarcodeFormat = BarcodeFormat.AZTEC
format: BarcodeFormat? = null
): Fragment {
val args = Bundle()
args.putString(CONTENT, content)
args.putSerializable(FORMAT, format)
format?.let {
args.putSerializable(FORMAT, format)
}
val fragment = EncodeFragment()
fragment.arguments = args
return fragment
@@ -3,40 +3,25 @@ package de.markusfisch.android.binaryeye.fragment
import android.app.AlertDialog
import android.content.Context
import android.os.Bundle
import android.os.Parcelable
import android.support.annotation.WorkerThread
import android.support.v4.app.Fragment
import android.support.v7.widget.SwitchCompat
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.view.*
import android.widget.AbsListView
import android.widget.ListView
import android.widget.Toast
import com.google.zxing.BarcodeFormat
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.adapter.ScansAdapter
import de.markusfisch.android.binaryeye.app.addFragment
import de.markusfisch.android.binaryeye.app.alertDialog
import de.markusfisch.android.binaryeye.app.askForFileName
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.app.shareText
import de.markusfisch.android.binaryeye.app.systemBarScrollListener
import de.markusfisch.android.binaryeye.app.useVisibility
import de.markusfisch.android.binaryeye.app.writeToFile
import de.markusfisch.android.binaryeye.app.*
import de.markusfisch.android.binaryeye.data.csv.csvBuilder
import de.markusfisch.android.binaryeye.repository.DatabaseRepository
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import de.markusfisch.android.binaryeye.view.setPadding
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class HistoryFragment : Fragment() {
private lateinit var listView: ListView
@@ -46,6 +31,9 @@ class HistoryFragment : Fragment() {
private val parentJob = Job()
private val scope = CoroutineScope(Dispatchers.IO + parentJob)
private var scansAdapter: ScansAdapter? = null
private var listViewState: Parcelable? = null
override fun onCreate(state: Bundle?) {
super.onCreate(state)
setHasOptionsMenu(true)
@@ -56,7 +44,7 @@ class HistoryFragment : Fragment() {
container: ViewGroup?,
state: Bundle?
): View {
activity.setTitle(R.string.history)
activity?.setTitle(R.string.history)
val view = inflater.inflate(
R.layout.fragment_history,
@@ -69,7 +57,17 @@ class HistoryFragment : Fragment() {
) as SwitchCompat
initHistorySwitch(useHistorySwitch)
val headView = View(context)
val footView = View(context)
setWindowInsetListener { insets ->
(view.findViewById(R.id.inset_layout) as View).setPadding(insets)
headView.layoutParams = AbsListView.LayoutParams(0, insets.top)
footView.layoutParams = AbsListView.LayoutParams(0, insets.bottom)
}
listView = view.findViewById(R.id.scans)
listView.addHeaderView(headView, null, false)
listView.addFooterView(footView, null, false)
listView.emptyView = useHistorySwitch
listView.setOnItemClickListener { _, _, _, id ->
showScan(id)
@@ -92,7 +90,7 @@ class HistoryFragment : Fragment() {
override fun onDestroy() {
super.onDestroy()
(listView.adapter as ScansAdapter?)?.changeCursor(null)
scansAdapter?.changeCursor(null)
parentJob.cancel()
}
@@ -101,6 +99,11 @@ class HistoryFragment : Fragment() {
update(context)
}
override fun onPause() {
super.onPause()
listViewState = listView.onSaveInstanceState()
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.fragment_history, menu)
}
@@ -138,12 +141,12 @@ class HistoryFragment : Fragment() {
View.GONE
}
cursor?.let {
if (listView.adapter == null) {
listView.adapter = ScansAdapter(context, cursor)
} else {
val adapter = listView.adapter as ScansAdapter
adapter.changeCursor(cursor)
adapter.notifyDataSetChanged()
// close previous cursor
scansAdapter?.also { it.changeCursor(null) }
scansAdapter = ScansAdapter(context, it)
listView.adapter = scansAdapter
listViewState?.also {
listView.onRestoreInstanceState(it)
}
}
}
@@ -9,8 +9,11 @@ import android.view.ViewGroup
import android.widget.EditText
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.app.prefs
import de.markusfisch.android.binaryeye.app.setWindowInsetListener
import de.markusfisch.android.binaryeye.view.setPadding
class PreferencesFragment : Fragment() {
private lateinit var openImmediatelySwitch: SwitchCompat
private lateinit var useHistorySwitch: SwitchCompat
private lateinit var openWithUrlInput: EditText
@@ -19,7 +22,7 @@ class PreferencesFragment : Fragment() {
container: ViewGroup?,
state: Bundle?
): View? {
activity.setTitle(R.string.preferences)
activity?.setTitle(R.string.preferences)
val view = inflater.inflate(
R.layout.fragment_preferences,
@@ -27,20 +30,30 @@ class PreferencesFragment : Fragment() {
false
)
useHistorySwitch = view.findViewById<SwitchCompat>(R.id.use_history)
openImmediatelySwitch = view.findViewById(R.id.open_immediately)
if (prefs.openImmediately) {
openImmediatelySwitch.toggle()
}
useHistorySwitch = view.findViewById(R.id.use_history)
if (prefs.useHistory) {
useHistorySwitch.toggle()
}
openWithUrlInput = view.findViewById<EditText>(R.id.open_with_url)
openWithUrlInput = view.findViewById(R.id.open_with_url)
openWithUrlInput.setText(prefs.openWithUrl)
setWindowInsetListener { insets ->
(view.findViewById(R.id.scroll_view) as View).setPadding(insets)
}
return view
}
override fun onPause() {
super.onPause()
prefs.useHistory = useHistorySwitch.isChecked()
prefs.openImmediately = openImmediatelySwitch.isChecked
prefs.useHistory = useHistorySwitch.isChecked
prefs.openWithUrl = openWithUrlInput.text.toString()
}
}
@@ -1,35 +1,60 @@
package de.markusfisch.android.binaryeye.graphics
import android.content.ContentResolver
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.graphics.RectF
import kotlin.math.max
import android.net.Uri
import java.io.IOException
import kotlin.math.roundToInt
fun downsizeIfBigger(bitmap: Bitmap, maxSize: Int): Bitmap = if (
max(bitmap.width, bitmap.height) > maxSize
) {
val srcWidth = bitmap.width
val srcHeight = bitmap.height
var newWidth = maxSize
var newHeight = maxSize
if (srcWidth > srcHeight) {
newHeight = (newWidth.toFloat() / srcWidth.toFloat() *
srcHeight.toFloat()).roundToInt()
} else {
newWidth = (newHeight.toFloat() / srcHeight.toFloat() *
srcWidth.toFloat()).roundToInt()
fun loadImageUri(cr: ContentResolver, uri: Uri): Bitmap? = try {
val options = BitmapFactory.Options()
cr.openInputStream(uri)?.use {
options.inJustDecodeBounds = true
BitmapFactory.decodeStream(it, null, options)
options.inSampleSize = calculateInSampleSize(
options.outWidth,
options.outHeight,
1024,
1024
)
options.inJustDecodeBounds = false
}
Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true)
} else {
bitmap
cr.openInputStream(uri)?.use {
BitmapFactory.decodeStream(it, null, options)
}
} catch (e: IOException) {
null
}
private fun calculateInSampleSize(
width: Int,
height: Int,
reqWidth: Int,
reqHeight: Int
): Int {
var inSampleSize = 1
if (height > reqHeight || width > reqWidth) {
val halfHeight = height / 2
val halfWidth = width / 2
while (
halfHeight / inSampleSize >= reqHeight ||
halfWidth / inSampleSize >= reqWidth
) {
inSampleSize *= 2
}
}
return inSampleSize
}
fun crop(bitmap: Bitmap, rect: RectF, rotation: Float) = try {
val w = bitmap.width
val h = bitmap.height
val erected = erect(bitmap, rotation)
val w = erected.width
val h = erected.height
Bitmap.createBitmap(
erect(bitmap, rotation),
erected,
(rect.left * w).roundToInt(),
(rect.top * h).roundToInt(),
(rect.width() * w).roundToInt(),
@@ -0,0 +1,66 @@
package de.markusfisch.android.binaryeye.graphics
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Point
import android.graphics.Rect
import android.support.v4.content.ContextCompat
import com.google.zxing.Result
import com.google.zxing.ResultPoint
import de.markusfisch.android.binaryeye.R
import kotlin.math.roundToInt
class Candidates(context: Context) {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val radius = 8f * context.resources.displayMetrics.density
init {
paint.color = ContextCompat.getColor(
context,
R.color.candidate
)
paint.style = Paint.Style.FILL
}
fun draw(canvas: Canvas, points: List<Point>) {
for (point in points) {
canvas.drawCircle(
point.x.toFloat(),
point.y.toFloat(),
radius,
paint
)
}
}
}
fun mapResult(
width: Int,
height: Int,
viewRect: Rect,
result: Result
): List<Point> = frameToView(width, height, viewRect).map(result.resultPoints)
fun frameToView(width: Int, height: Int, viewRect: Rect) = Mapping(
viewRect.width().toFloat() / width.toFloat(),
viewRect.height().toFloat() / height.toFloat(),
viewRect.left,
viewRect.top
)
data class Mapping(
val ratioX: Float,
val ratioY: Float,
val offsetX: Int,
val offsetY: Int
) {
fun map(points: Array<ResultPoint>): List<Point> {
return points.map {
Point(
(it.x * ratioX).roundToInt() + offsetX,
(it.y * ratioY).roundToInt() + offsetY
)
}
}
}
@@ -7,6 +7,11 @@ import android.preference.PreferenceManager
class Preferences {
lateinit var preferences: SharedPreferences
var openImmediately = false
set(value) {
setBoolean(OPEN_IMMEDIATELY, value)
field = value
}
var useHistory = false
set(value) {
setBoolean(USE_HISTORY, value)
@@ -17,6 +22,11 @@ class Preferences {
setString(OPEN_WITH_URL, value)
field = value
}
var indexOfLastSelectedFormat: Int = 0
set(value) {
setInt(INDEX_OF_LAST_SELECTED_FORMAT, value)
field = value
}
fun init(context: Context) {
preferences = PreferenceManager.getDefaultSharedPreferences(context)
@@ -24,8 +34,18 @@ class Preferences {
}
fun update() {
openImmediately = preferences.getBoolean(
OPEN_IMMEDIATELY,
openImmediately
)
useHistory = preferences.getBoolean(USE_HISTORY, useHistory)
openWithUrl = preferences.getString(OPEN_WITH_URL, openWithUrl)
indexOfLastSelectedFormat = preferences.getInt(
INDEX_OF_LAST_SELECTED_FORMAT,
indexOfLastSelectedFormat
)
preferences.getString(OPEN_WITH_URL, openWithUrl)?.also {
openWithUrl = it
}
}
private fun setBoolean(label: String, value: Boolean) {
@@ -40,8 +60,16 @@ class Preferences {
editor.apply()
}
private fun setInt(label: String, value: Int) {
val editor = preferences.edit()
editor.putInt(label, value)
editor.apply()
}
companion object {
const val OPEN_IMMEDIATELY = "open_immediately"
const val USE_HISTORY = "use_history"
const val OPEN_WITH_URL = "open_with_url"
const val INDEX_OF_LAST_SELECTED_FORMAT = "index_of_last_selected_format"
}
}
@@ -102,7 +102,8 @@ class DatabaseRepository {
return true
}
// Needed to be overwritten manually, as ByteArray is an array and this isn't handled well by Kotlin
// Needs to be overwritten manually, as ByteArray is an array and this
// isn't handled well by Kotlin
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + timestamp.hashCode()
@@ -118,7 +119,10 @@ private val Cursor.asIterable: Iterable<Cursor>
get() = object : Iterable<Cursor> {
override fun iterator(): Iterator<Cursor> = object : Iterator<Cursor> {
override fun hasNext(): Boolean = position + 1 < count
override fun next(): Cursor =
if (moveToNext()) this@asIterable else throw IllegalStateException("You can't access more items, then there are")
override fun next(): Cursor = if (moveToNext()) {
this@asIterable
} else {
throw IllegalStateException("You can't access more items, then there are")
}
}
}
@@ -3,6 +3,7 @@ package de.markusfisch.android.binaryeye.rs
import android.content.Context
import android.support.v8.renderscript.*
import de.markusfisch.android.binaryeye.renderscript.ScriptC_rotator
import kotlin.math.roundToInt
class Preprocessor(
context: Context,
@@ -38,8 +39,8 @@ class Preprocessor(
)
val f = .75f
var w = Math.round(width * f)
var h = Math.round(height * f)
var w = (width * f).roundToInt()
var h = (height * f).roundToInt()
resizedType = Type.createXY(
rs,
@@ -0,0 +1,13 @@
package de.markusfisch.android.binaryeye.view
import android.graphics.Rect
import android.view.View
fun View.setPadding(rect: Rect) {
this.setPadding(
rect.left,
rect.top,
rect.right,
rect.bottom
)
}
@@ -2,11 +2,14 @@ package de.markusfisch.android.binaryeye.widget
import android.content.Context
import android.graphics.Canvas
import android.graphics.Rect
import android.util.AttributeSet
import de.markusfisch.android.binaryeye.app.setSystemAndToolBarTransparency
import de.markusfisch.android.binaryeye.app.colorSystemAndToolBars
import de.markusfisch.android.scalingimageview.widget.ScalingImageView
open class ConfinedScalingImageView : ScalingImageView {
val insets = Rect()
constructor(context: Context, attrs: AttributeSet, defStyle: Int) :
super(context, attrs, defStyle)
@@ -15,6 +18,24 @@ open class ConfinedScalingImageView : ScalingImageView {
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
setSystemAndToolBarTransparency(context, !bounds.contains(mappedRect))
colorSystemAndToolBars(context, !bounds.contains(mappedRect))
}
override fun layoutImage(
changed: Boolean,
left: Int,
top: Int,
right: Int,
bottom: Int
) {
if (changed) {
setBounds(
(left + insets.left).toFloat(),
(top + insets.top).toFloat(),
(right - insets.right).toFloat(),
(bottom - insets.bottom).toFloat()
)
}
centerRemap()
}
}
@@ -3,7 +3,7 @@ package de.markusfisch.android.binaryeye.widget
import android.content.Context
import android.util.AttributeSet
import android.widget.ScrollView
import de.markusfisch.android.binaryeye.app.setSystemAndToolBarTransparency
import de.markusfisch.android.binaryeye.app.colorSystemAndToolBars
class ConfinedScrollView : ScrollView {
private var scrollable = false
@@ -28,9 +28,9 @@ class ConfinedScrollView : ScrollView {
post {
getChildAt(0)?.also { child ->
scrollable = height < child.height + paddingTop + paddingBottom
setSystemAndToolBarTransparency(
colorSystemAndToolBars(
context,
false,
scrollY > 0,
scrollable
)
}
@@ -40,6 +40,6 @@ class ConfinedScrollView : ScrollView {
override fun onScrollChanged(x: Int, y: Int, oldx: Int, oldy: Int) {
super.onScrollChanged(x, y, oldx, oldy)
setSystemAndToolBarTransparency(context, y > 0, scrollable)
colorSystemAndToolBars(context, y > 0, scrollable)
}
}
@@ -1,22 +1,38 @@
package de.markusfisch.android.binaryeye.widget
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.*
import android.support.v4.content.ContextCompat
import android.util.AttributeSet
import de.markusfisch.android.binaryeye.R
import de.markusfisch.android.binaryeye.app.windowInsets
import de.markusfisch.android.binaryeye.graphics.Candidates
import kotlin.math.roundToInt
class CropImageView(context: Context, attr: AttributeSet) :
ConfinedScalingImageView(context, attr) {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val padding: Int = (context.resources.displayMetrics.density * 24f).roundToInt()
val windowInsets = Rect()
var onScan: (() -> List<Point>?)? = null
private val candidates = Candidates(context)
private val boundsPaint = Paint(Paint.ANTI_ALIAS_FLAG)
private val lastMappedRect = RectF()
private val dp = context.resources.displayMetrics.density
private val padding: Int = (dp * 24f).roundToInt()
private val onScanRunnable = Runnable {
onScan?.invoke()?.also {
candidatePoints = it
invalidate()
}
}
private var candidatePoints: List<Point>? = null
init {
paint.color = ContextCompat.getColor(context, R.color.crop_bound)
paint.style = Paint.Style.STROKE
boundsPaint.color = ContextCompat.getColor(context, R.color.crop_bound)
boundsPaint.style = Paint.Style.STROKE
boundsPaint.strokeWidth = dp * 2f
boundsPaint.pathEffect = DashPathEffect(floatArrayOf(dp * 10f, dp * 10f), 0f)
scaleType = ScaleType.CENTER_CROP
}
@@ -36,6 +52,13 @@ class CropImageView(context: Context, attr: AttributeSet) :
)
}
fun getBoundsRect() = Rect(
bounds.left.roundToInt(),
bounds.top.roundToInt(),
bounds.right.roundToInt(),
bounds.bottom.roundToInt()
)
private fun setBoundsWithPadding(
left: Int,
top: Int,
@@ -61,6 +84,16 @@ class CropImageView(context: Context, attr: AttributeSet) :
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawRect(bounds, paint)
canvas.drawRect(bounds, boundsPaint)
candidatePoints?.let {
candidates.draw(canvas, it)
}
candidatePoints = null
val mr = mappedRect ?: return
if (mr != lastMappedRect) {
removeCallbacks(onScanRunnable)
postDelayed(onScanRunnable, 500)
lastMappedRect.set(mr)
}
}
}
@@ -0,0 +1,36 @@
package de.markusfisch.android.binaryeye.widget
import android.content.Context
import android.graphics.Canvas
import android.graphics.Point
import android.graphics.PorterDuff
import android.util.AttributeSet
import android.view.View
import de.markusfisch.android.binaryeye.graphics.Candidates
class DetectorView : View {
private val candidates = Candidates(context)
private var marks: List<Point>? = null
constructor(context: Context, attrs: AttributeSet) :
super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) :
super(context, attrs, defStyleAttr)
fun mark(points: List<Point>) {
marks = points
invalidate()
postDelayed({
marks = null
invalidate()
}, 500)
}
override fun onDraw(canvas: Canvas) {
canvas.drawColor(0, PorterDuff.Mode.CLEAR)
marks?.let {
candidates.draw(canvas, it)
}
}
}
@@ -6,7 +6,7 @@ import com.google.zxing.common.HybridBinarizer
import java.util.*
class Zxing {
private val multiFormatReader: MultiFormatReader = MultiFormatReader()
private val multiFormatReader = MultiFormatReader()
init {
val decodeFormats = EnumSet.noneOf<BarcodeFormat>(
@@ -14,7 +14,7 @@ class Zxing {
)
decodeFormats.addAll(
EnumSet.copyOf(
Arrays.asList(
listOf(
BarcodeFormat.AZTEC,
BarcodeFormat.CODABAR,
BarcodeFormat.CODE_39,
@@ -63,10 +63,11 @@ class Zxing {
fun decodePositiveNegative(bitmap: Bitmap): Result? {
val result = decode(bitmap, false)
if (result != null) {
return result
return if (result != null) {
result
} else {
decode(bitmap, true)
}
return decode(bitmap, true)
}
fun decode(bitmap: Bitmap, invert: Boolean = false): Result? {
@@ -86,8 +87,10 @@ class Zxing {
} else {
bitmap
}.getPixels(pixels, 0, width, 0, 0, width, height)
val source = RGBLuminanceSource(width, height, pixels)
return decodeLuminanceSource(source, invert)
return decodeLuminanceSource(
RGBLuminanceSource(width, height, pixels),
invert
)
}
private fun decodeLuminanceSource(
@@ -0,0 +1,5 @@
<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="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z"/>
</vector>
+10 -18
View File
@@ -1,12 +1,13 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<de.markusfisch.android.cameraview.widget.CameraView
android:id="@+id/camera_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<RelativeLayout
android:id="@+id/main_layout"
<de.markusfisch.android.binaryeye.widget.DetectorView
android:id="@+id/detector_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
@@ -15,24 +16,15 @@
android:id="@+id/zoom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom|start"
android:layout_marginBottom="8dp"
android:layout_marginRight="80dp"
android:layout_marginEnd="80dp"
android:padding="16dp"/>
<android.support.design.widget.FloatingActionButton
tools:ignore="UnusedAttribute"
style="@style/FAB"
android:id="@+id/flash"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_margin="20dp"
android:contentDescription="@string/toggle_flash"
android:src="@drawable/ic_action_flash"
app:elevation="4dp"/>
</RelativeLayout>
android:src="@drawable/ic_action_flash"/>
</android.support.design.widget.CoordinatorLayout>
</merge>
+6 -8
View File
@@ -1,14 +1,12 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:id="@+id/main_layout"
<android.support.design.widget.CoordinatorLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:fitsSystemWindows="true">
<include layout="@layout/toolbar"/>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
</FrameLayout>
</merge>
+13 -21
View File
@@ -1,26 +1,18 @@
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<de.markusfisch.android.binaryeye.widget.CropImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/view_barcode"/>
<include layout="@layout/toolbar"/>
<android.support.design.widget.FloatingActionButton
tools:ignore="UnusedAttribute"
android:id="@+id/scan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_margin="20dp"
android:contentDescription="@string/scan_code"
android:src="@drawable/ic_action_scan"
app:elevation="4dp"/>
</RelativeLayout>
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<include layout="@layout/toolbar"/>
<android.support.design.widget.FloatingActionButton
style="@style/FAB"
android:id="@+id/scan"
android:contentDescription="@string/scan_code"
android:src="@drawable/ic_action_scan"/>
</android.support.design.widget.CoordinatorLayout>
</merge>
+12 -16
View File
@@ -1,7 +1,5 @@
<RelativeLayout
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<de.markusfisch.android.binaryeye.widget.ConfinedScalingImageView
@@ -9,16 +7,14 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/view_barcode"/>
<android.support.design.widget.FloatingActionButton
tools:ignore="UnusedAttribute"
android:id="@+id/share"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_margin="20dp"
android:contentDescription="@string/share"
android:src="@drawable/ic_action_share"
app:elevation="4dp"/>
</RelativeLayout>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/inset_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.FloatingActionButton
style="@style/FAB"
android:id="@+id/share"
android:contentDescription="@string/share"
android:src="@drawable/ic_action_share"/>
</android.support.design.widget.CoordinatorLayout>
</FrameLayout>
+21 -21
View File
@@ -1,21 +1,25 @@
<RelativeLayout
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<de.markusfisch.android.binaryeye.widget.ConfinedScrollView
android:id="@+id/scroll_view"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:layout_marginTop="16dp"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:gravity="start|top"
android:hint="@string/content"
android:imeOptions="flagNoExtractUi"
@@ -23,8 +27,8 @@
android:inputType="textMultiLine"
android:typeface="monospace"
tools:ignore="UnusedAttribute"/>
<TextView
style="@style/SecondaryText"
android:id="@+id/format"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -32,8 +36,7 @@
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
android:gravity="end|top"
android:textSize="14sp" />
android:gravity="end|top" />
<TextView
android:id="@+id/hex"
android:layout_width="match_parent"
@@ -47,19 +50,16 @@
android:typeface="monospace"
android:textSize="12sp"
tools:text="54 65 73 74 20 51 52 20 Test QR\n43 6F 64 65 Code"/>
</RelativeLayout>
</de.markusfisch.android.binaryeye.widget.ConfinedScrollView>
<android.support.design.widget.FloatingActionButton
tools:ignore="UnusedAttribute"
android:id="@+id/share"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_margin="20dp"
android:contentDescription="@string/share"
android:src="@drawable/ic_action_share"
app:elevation="4dp"/>
</RelativeLayout>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/inset_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.FloatingActionButton
style="@style/FAB"
android:id="@+id/open"
android:contentDescription="@string/open_url"
android:src="@drawable/ic_action_open"/>
</android.support.design.widget.CoordinatorLayout>
</FrameLayout>
+66 -56
View File
@@ -1,64 +1,74 @@
<de.markusfisch.android.binaryeye.widget.ConfinedScrollView
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
<de.markusfisch.android.binaryeye.widget.ConfinedScrollView
android:id="@+id/scroll_view"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="@string/format"/>
<Spinner
android:id="@+id/format"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="@string/size"/>
<TextView
android:id="@+id/size_display"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:gravity="center"/>
<SeekBar
style="@style/Widget.AppCompat.SeekBar.Discrete"
android:id="@+id/size_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:progress="4"
android:max="7"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="@string/content"/>
<EditText
tools:targetApi="o"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:gravity="start|top"
android:typeface="monospace"
android:imeOptions="flagNoExtractUi"
android:inputType="textMultiLine"
android:hint="@string/input_content_here"
android:importantForAutofill="no"/>
<Button
style="@style/Button"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="@string/format"/>
<Spinner
android:id="@+id/format"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="@string/size"/>
<TextView
android:id="@+id/size_display"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:gravity="center"/>
<SeekBar
style="@style/Widget.AppCompat.SeekBar.Discrete"
android:id="@+id/size_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:progress="4"
android:max="7"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="@string/content"/>
<EditText
tools:targetApi="o"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:gravity="start|top"
android:typeface="monospace"
android:imeOptions="flagNoExtractUi"
android:inputType="textMultiLine"
android:hint="@string/input_content_here"
android:importantForAutofill="no"/>
</LinearLayout>
</de.markusfisch.android.binaryeye.widget.ConfinedScrollView>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/inset_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.FloatingActionButton
style="@style/FAB"
android:id="@+id/encode"
android:layout_width="match_parent"
android:text="@string/encode"/>
</LinearLayout>
</de.markusfisch.android.binaryeye.widget.ConfinedScrollView>
android:contentDescription="@string/encode"
android:src="@drawable/ic_action_next"/>
</android.support.design.widget.CoordinatorLayout>
</FrameLayout>
+20 -24
View File
@@ -1,7 +1,5 @@
<RelativeLayout
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
@@ -11,26 +9,24 @@
android:layout_height="match_parent"
android:divider="@color/separator"
android:dividerHeight="1px"/>
<android.support.v7.widget.SwitchCompat
android:visibility="gone"
android:id="@+id/use_history"
<android.support.design.widget.CoordinatorLayout
android:id="@+id/inset_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:padding="16dp"
android:text="@string/use_history"/>
<android.support.design.widget.FloatingActionButton
tools:ignore="UnusedAttribute"
android:visibility="gone"
android:id="@+id/share"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_margin="20dp"
android:contentDescription="@string/share"
android:src="@drawable/ic_action_share"
app:elevation="4dp"/>
android:layout_height="match_parent">
<android.support.v7.widget.SwitchCompat
android:visibility="gone"
android:id="@+id/use_history"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:padding="16dp"
android:text="@string/use_history"/>
<android.support.design.widget.FloatingActionButton
style="@style/FAB"
android:visibility="gone"
android:id="@+id/share"
android:contentDescription="@string/share"
android:src="@drawable/ic_action_share"/>
</android.support.design.widget.CoordinatorLayout>
<include layout="@layout/progress_view"/>
</RelativeLayout>
</FrameLayout>
@@ -1,6 +1,8 @@
<de.markusfisch.android.binaryeye.widget.ConfinedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/scroll_view"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
@@ -8,6 +10,12 @@
android:layout_height="match_parent"
android:padding="16dp"
android:orientation="vertical">
<android.support.v7.widget.SwitchCompat
android:id="@+id/open_immediately"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:text="@string/open_immediately"/>
<android.support.v7.widget.SwitchCompat
android:id="@+id/use_history"
android:layout_width="match_parent"
+4
View File
@@ -4,6 +4,7 @@
android:layout_height="match_parent"
android:padding="16dp">
<TextView
style="@style/SecondaryText"
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -11,6 +12,7 @@
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"/>
<TextView
style="@style/SecondaryText"
android:id="@+id/format"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -26,5 +28,7 @@
android:layout_height="wrap_content"
android:layout_below="@+id/time"
android:layout_marginTop="4dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorPrimary"
android:typeface="monospace"/>
</RelativeLayout>
+3 -3
View File
@@ -7,9 +7,9 @@
android:icon="@drawable/ic_action_copy"
material:showAsAction="ifRoom"/>
<item
android:id="@+id/open_url"
android:title="@string/open_url"
android:icon="@drawable/ic_action_open"
android:id="@+id/share"
android:title="@string/share"
android:icon="@drawable/ic_action_share"
material:showAsAction="ifRoom"/>
<item
android:id="@+id/create"
@@ -0,0 +1,9 @@
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:material="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/next"
android:title="@string/encode"
android:icon="@drawable/ic_action_next"
material:showAsAction="ifRoom"/>
</menu>
+5 -3
View File
@@ -7,8 +7,8 @@
<string name="content">Inhalt</string>
<string name="binary_data">(Binäre Daten)</string>
<plurals name="barcode_info">
<item quantity="one">%1$s\n%2$d Zeichen</item>
<item quantity="other">%1$s\n%2$d Zeichen</item>
<item quantity="one">%2$d Zeichen, %1$s</item>
<item quantity="other">%2$d Zeichen, %1$s</item>
</plurals>
<string name="toggle_flash">Licht an/ausschalten</string>
<string name="share">Teilen</string>
@@ -29,6 +29,7 @@
<string name="switch_camera">Kamera wechseln</string>
<string name="history">Gespeicherte Codes</string>
<string name="preferences">Einstellungen</string>
<string name="open_immediately">Inspektion überspringen und Inhalte sofort öffnen</string>
<string name="use_history">Codes speichern</string>
<string name="open_with_url">URL zum Öffnen unbekannter Daten</string>
<string name="really_remove_scan">Code wirklich entfernen?</string>
@@ -38,7 +39,7 @@
<string name="pick_list_separator">Wie sollen die Einträge getrennt werden?</string>
<string name="separator_line_break">Zeilenumbruch</string>
<string name="separator_ruler">Trennstrich</string>
<string name="save_as_file_name">Speichern als?</string>
<string name="save_as_file_name">Speichern in Downloads als?</string>
<string name="file_name">Dateiname</string>
<string name="error_saving_binary_data">Datei konnte nicht gespeichert werden</string>
<string name="error_file_exists">Datei existiert bereits</string>
@@ -68,4 +69,5 @@
<string name="saved_in_downloads">In den Downloads gespeichert</string>
<string name="rotate_image_cw">Bild im Uhrzeigersinn rotieren</string>
<string name="pick_file">Bilddatei auswählen</string>
<string name="pick_code_to_scan">Code im Bild auswählen</string>
</resources>
+5 -3
View File
@@ -7,8 +7,8 @@
<string name="content">Contenu</string>
<string name="binary_data">(binary data)</string>
<plurals name="barcode_info">
<item quantity="one">%1$s\n%2$d character</item>
<item quantity="other">%1$s\n%2$d characters</item>
<item quantity="one">%2$d characters, %1$s</item>
<item quantity="other">%2$d characters, %1$s</item>
</plurals>
<string name="toggle_flash">Toggle flash</string>
<string name="share">Share</string>
@@ -29,6 +29,7 @@
<string name="switch_camera">Changer de camera</string>
<string name="history">Historique</string>
<string name="preferences">Preferences</string>
<string name="open_immediately">Skip inspection and open contents immediately</string>
<string name="use_history">Sauvegarder l\'histoire des scans</string>
<string name="open_with_url">Open unknown data with URL</string>
<string name="really_remove_scan">Supprimer le scan ?</string>
@@ -38,7 +39,7 @@
<string name="pick_list_separator">How to separate list items?</string>
<string name="separator_line_break">Line break</string>
<string name="separator_ruler">Ruler</string>
<string name="save_as_file_name">Save as file?</string>
<string name="save_as_file_name">Save in Downloads as file?</string>
<string name="file_name">File name</string>
<string name="error_saving_binary_data">Cannot save file</string>
<string name="error_file_exists">File already exists</string>
@@ -68,4 +69,5 @@
<string name="saved_in_downloads">Saved in downloads</string>
<string name="rotate_image_cw">Rotate image clockwise</string>
<string name="pick_file">Pick image file</string>
<string name="pick_code_to_scan">Pick code to scan</string>
</resources>
+6 -4
View File
@@ -7,8 +7,8 @@
<string name="content">Tartalom</string>
<string name="binary_data">(bináris adat)</string>
<plurals name="barcode_info">
<item quantity="one">%1$s\n%2$d karakter</item>
<item quantity="other">%1$s\n%2$d karakter</item>
<item quantity="one">%2$d karakter, %1$s</item>
<item quantity="other">%2$d karakter, %1$s</item>
</plurals>
<string name="toggle_flash">Vaku be- vagy kikapcsolása</string>
<string name="share">Megosztás</string>
@@ -29,6 +29,7 @@
<string name="switch_camera">Kamera átkapcsolása</string>
<string name="history">Előzmények</string>
<string name="preferences">Beállítások</string>
<string name="open_immediately">Vizsgálat kihagyása és a tartalom azonnali megnyitása</string>
<string name="use_history">Beolvasási előzmények mentése</string>
<string name="open_with_url">Ismeretlen adat megnyitása URL-lel</string>
<string name="really_remove_scan">Valóban eltávolítja a beolvasást?</string>
@@ -66,6 +67,7 @@
<string name="no">Nem</string>
<string name="csv_allow_binary_message">Valószínűleg ezt nem szeretné engedélyezni, mivel tönkre fogja tenni a fájlformátumot, és a szövegszerkesztők talán nem fogják tudni többé olvasni.</string>
<string name="saved_in_downloads">Elmentve a letöltésekbe</string>
<string name="rotate_image_cw">Rotate image clockwise</string>
<string name="pick_file">Pick image file</string>
<string name="rotate_image_cw">Kép forgatása jobbra</string>
<string name="pick_file">Képfájl kiválasztása</string>
<string name="pick_code_to_scan">Kód kiválasztása a beolvasáshoz</string>
</resources>
+8 -6
View File
@@ -5,10 +5,10 @@
<string name="compose_barcode">Buat barcode</string>
<string name="decode_barcode">Dekode barcode</string>
<string name="content">Konten</string>
<string name="binary_data">(data binari)</string>
<string name="binary_data">(data biner)</string>
<plurals name="barcode_info">
<item quantity="one">%1$s\n%2$d karakter</item>
<item quantity="other">%1$s\n%2$d karakter</item>
<item quantity="one">%1$s, %2$d karakter</item>
<item quantity="other">%1$s, %2$d karakter</item>
</plurals>
<string name="toggle_flash">Nyala/Matikan blitz</string>
<string name="share">Bagikan</string>
@@ -29,6 +29,7 @@
<string name="switch_camera">Ganti kamera</string>
<string name="history">Riwayat</string>
<string name="preferences">Preferensi</string>
<string name="open_immediately">Lewati inspeksi dan segera buka konten</string>
<string name="use_history">Simpan riwayat pemindaian</string>
<string name="open_with_url">Buka data tak dikenal dengan URL</string>
<string name="really_remove_scan">Yakin menghapus pemindaian?</string>
@@ -62,10 +63,11 @@
<string name="csv_delimiter">Karakter pemisah apa yang harus digunakan (Berkas-CSV)?</string>
<string name="delimiter_comma">Koma</string>
<string name="delimiter_semicolon">Titik koma</string>
<string name="csv_allow_binary">Izinkan data binari di dalam berkas keluaran?</string>
<string name="csv_allow_binary">Izinkan data biner di dalam berkas keluaran?</string>
<string name="no">Tidak</string>
<string name="csv_allow_binary_message">Anda sebaiknya tidak mengizinkannya, karena akan merusak format berkas dan kemungkinan tidak bisa lagi dibaca oleh aplikasi penyunting teks.</string>
<string name="saved_in_downloads">Simpan di dalam unduhan</string>
<string name="rotate_image_cw">Rotate image clockwise</string>
<string name="pick_file">Pick image file</string>
<string name="rotate_image_cw">Putar gambar searah jarum jam</string>
<string name="pick_file">Pilih berkas gambar</string>
<string name="pick_code_to_scan">Pilih kode untuk dipindai</string>
</resources>
+19 -17
View File
@@ -1,14 +1,14 @@
<resources>
<string name="no_camera_no_fun">Quest\'app non ha alcuna utilità senza accesso alla camera. Addio.</string>
<string name="camera_error">Impossibile accedere alla camera. Riprovare</string>
<string name="scan_code">Codice</string>
<string name="no_camera_no_fun">Quest\'app non ha alcun senso senza l\'accesso alla fotocamera. Ciao.</string>
<string name="camera_error">Impossibile accedere alla fotocamera, riprova</string>
<string name="scan_code">Scansiona 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>
<string name="binary_data">(dati binari)</string>
<plurals name="barcode_info">
<item quantity="one">%1$s\n%2$d carattere</item>
<item quantity="other">%1$s\n%2$d caratteri</item>
<item quantity="one">%2$d carattere, %1$s</item>
<item quantity="other">%2$d caratteri, %1$s</item>
</plurals>
<string name="toggle_flash">Flash</string>
<string name="share">Condividi</string>
@@ -20,43 +20,44 @@
<string name="format">Formato</string>
<string name="size">Dimensioni in pixel</string>
<string name="width_by_height">%1$d×%2$d</string>
<string name="input_content_here">Contenuto input qui</string>
<string name="input_content_here">Inserisci il contenuto qui</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="info">Info</string>
<string name="switch_camera">Cambia camera</string>
<string name="switch_camera">Cambia fotocamera</string>
<string name="history">Cronologia</string>
<string name="preferences">Preferences</string>
<string name="preferences">Preferenze</string>
<string name="open_immediately">Salta l\'analisi e apri i contenuti direttamente</string>
<string name="use_history">Salva cronologia delle scansioni</string>
<string name="open_with_url">Apri dati sconosciuti con URL</string>
<string name="really_remove_scan">Vuoi veramente rimuovere la scansione?</string>
<string name="really_remove_all_scans">Vuoi veramente rimuovere tutte le scansioni?</string>
<string name="clear_history">Pulisci cronologia</string>
<string name="no_barcode_found">Nessun codice a barre trovato</string>
<string name="pick_list_separator">Come separare elementi di una lista?</string>
<string name="pick_list_separator">Come separare elementi della lista?</string>
<string name="separator_line_break">Interruzione di linea</string>
<string name="separator_ruler">Righello</string>
<string name="save_as_file_name">Salvare come file?</string>
<string name="file_name">Nome del file</string>
<string name="error_saving_binary_data">Impossibile salvare il file</string>
<string name="error_file_exists">Il File esiste già</string>
<string name="connect_to_wifi">Connecti al WiFi</string>
<string name="error_file_exists">Il file esiste già</string>
<string name="connect_to_wifi">Connetti al WiFi</string>
<string name="wifi_config_failed">Impossibile configurare il WiFi</string>
<string name="wifi_added">WiFi aggiunto</string>
<string name="sms_send">Invia SMS</string>
<string name="sms_error">Impossibile inviare SMS</string>
<string name="tel_dial">Componi numero</string>
<string name="tel_error">Impossibile comporre il numero</string>
<string name="mail_send">Invia mail</string>
<string name="mail_error">Impossibile inviare la mail</string>
<string name="mail_send">Invia email</string>
<string name="mail_error">Impossibile inviare l\'email</string>
<string name="vcard_add">Aggiungi ai contatti</string>
<string name="vcard_failed">Impossibile aggiungere ai contatti</string>
<string name="vevent_add">Aggiungi al calendario</string>
<string name="vevent_failed">Impossibile aggiungere al calendario</string>
<string name="otpauth_add">Aggiungi 2FA</string>
<string name="otpauth_error">Impossibile aggiungre il 2FA</string>
<string name="otpauth_error">Impossibile aggiungre la 2FA</string>
<string name="search_web">Cerca nel web</string>
<string name="export_to_file">Esporta su file</string>
<string name="csv_delimiter">Quale delimitatore usare (File-CSV)?</string>
@@ -64,8 +65,9 @@
<string name="delimiter_semicolon">Punto e virgola</string>
<string name="csv_allow_binary">Permetti dati binari nel file di output?</string>
<string name="no">No</string>
<string name="csv_allow_binary_message">È consigliato non permetterlo, perché rompe il formato del file e probabilmente lo renderà non più leggibile dagli editor di testo.</string>
<string name="csv_allow_binary_message">È consigliato non permetterlo, perché rovina il formato del file e probabilmente lo renderà non più leggibile dagli editor di testo.</string>
<string name="saved_in_downloads">Salvato nei download</string>
<string name="rotate_image_cw">Rotate image clockwise</string>
<string name="pick_file">Pick image file</string>
<string name="rotate_image_cw">Ruota immagine in senso orario</string>
<string name="pick_file">Scegli file immagine</string>
<string name="pick_code_to_scan">Scegli codice da scansionare</string>
</resources>
+4 -2
View File
@@ -7,8 +7,8 @@
<string name="content">Inhoud</string>
<string name="binary_data">(binaire data)</string>
<plurals name="barcode_info">
<item quantity="one">%1$s\n%2$d character</item>
<item quantity="other">%1$s\n%2$d characters</item>
<item quantity="one">%2$d character, %1$s</item>
<item quantity="other">%2$d characters, %1$s</item>
</plurals>
<string name="toggle_flash">Flitslicht schakelen</string>
<string name="share">Delen</string>
@@ -29,6 +29,7 @@
<string name="switch_camera">Camera wisselen</string>
<string name="history">Geschiedenis</string>
<string name="preferences">Voorkeuren</string>
<string name="open_immediately">Skip inspection and open contents immediately</string>
<string name="use_history">Scan-geschiedenis behouden</string>
<string name="really_remove_scan">Scan echt verwijderen?</string>
<string name="really_remove_all_scans">Echt alle scans verwijderen?</string>
@@ -68,4 +69,5 @@
<string name="vevent_failed">Kon niet aan kalender toevoegen</string>
<string name="rotate_image_cw">Rotate image clockwise</string>
<string name="pick_file">Pick image file</string>
<string name="pick_code_to_scan">Pick code to scan</string>
</resources>
+4 -2
View File
@@ -7,8 +7,8 @@
<string name="content">Conteúdo</string>
<string name="binary_data">(binários)</string>
<plurals name="barcode_info">
<item quantity="one">%1$s\n%2$d cacractere</item>
<item quantity="other">%1$s\n%2$d caracteres</item>
<item quantity="one">%2$d caractere, %1$s</item>
<item quantity="other">%2$d caracteres, %1$s</item>
</plurals>
<string name="toggle_flash">Ativar lanterna</string>
<string name="share">Compartilhar</string>
@@ -29,6 +29,7 @@
<string name="switch_camera">Trocar câmera</string>
<string name="history">Histórico</string>
<string name="preferences">Preferências</string>
<string name="open_immediately">Skip inspection and open contents immediately</string>
<string name="use_history">Salvar histórico de escaneamento</string>
<string name="open_with_url">Abrir dados desconhecidos com URL</string>
<string name="really_remove_scan">Realmente deseja remover o escaneamento?</string>
@@ -68,4 +69,5 @@
<string name="saved_in_downloads">Saved in downloads</string>
<string name="rotate_image_cw">Rotate image clockwise</string>
<string name="pick_file">Pick image file</string>
<string name="pick_code_to_scan">Pick code to scan</string>
</resources>
@@ -25,6 +25,7 @@
<string name="switch_camera">Переключить камеру</string>
<string name="history">История</string>
<string name="preferences">Настройки</string>
<string name="open_immediately">Skip inspection and open contents immediately</string>
<string name="use_history">Сохранить историю сканирования</string>
<string name="open_with_url">Открыть неизвестные данные с помощью URL</string>
<string name="really_remove_scan">Действительно удалить сканирование?</string>
@@ -64,4 +65,5 @@
<string name="saved_in_downloads">Saved in downloads</string>
<string name="rotate_image_cw">Rotate image clockwise</string>
<string name="pick_file">Pick image file</string>
<string name="pick_code_to_scan">Pick code to scan</string>
</resources>
+21 -19
View File
@@ -1,21 +1,21 @@
<resources>
<string name="no_camera_no_fun">没有相机权限,这个软件有啥意义。再见!</string>
<string name="no_camera_no_fun">没有给我相机权限,扫个锤子。再见!</string>
<string name="camera_error">无法访问相机,请重试。</string>
<string name="scan_code">Scan code</string>
<string name="scan_code">扫描</string>
<string name="compose_barcode">制作条形码</string>
<string name="decode_barcode">解码条形码</string>
<string name="content">内容</string>
<string name="binary_data">(二进制数据)</string>
<plurals name="barcode_info">
<item quantity="one">%1$s\n%2$d 字符</item>
<item quantity="other">%1$s\n%2$d 字符</item>
<item quantity="one">%2$d 字符, %1$s</item>
<item quantity="other">%2$d 字符, %1$s</item>
</plurals>
<string name="toggle_flash">切换闪光灯</string>
<string name="toggle_flash">开关闪光灯</string>
<string name="share">分享</string>
<string name="copy_to_clipboard">复制到剪切板</string>
<string name="put_into_clipboard">复制到剪切板</string>
<string name="open_url">打开链接</string>
<string name="cannot_resolve_action">没有应用程序可以打开它</string>
<string name="cannot_resolve_action">没有相关APP可以打开它</string>
<string name="pick_search_engine">选择一个搜索引擎</string>
<string name="format">编码格式</string>
<string name="size">图片尺寸</string>
@@ -29,6 +29,7 @@
<string name="switch_camera">切换摄像头</string>
<string name="history">历史</string>
<string name="preferences">首选项</string>
<string name="open_immediately">跳过内容展示并立即打开</string>
<string name="use_history">保存扫描历史</string>
<string name="open_with_url">使用URL打开未知数据</string>
<string name="really_remove_scan">确定删除记录?</string>
@@ -55,17 +56,18 @@
<string name="vcard_failed">无法添加到通讯录</string>
<string name="vevent_add">添加到日历</string>
<string name="vevent_failed">无法添加到日历</string>
<string name="otpauth_add">Add 2FA</string>
<string name="otpauth_error">Could not add 2FA</string>
<string name="search_web">Search the web</string>
<string name="export_to_file">Export to file</string>
<string name="csv_delimiter">Which delimiter should be used (CSV-File)?</string>
<string name="delimiter_comma">Comma</string>
<string name="delimiter_semicolon">Semicolon</string>
<string name="csv_allow_binary">Allow binary data in output file?</string>
<string name="no">No</string>
<string name="csv_allow_binary_message">You probably don\'t want to allow that, as it will break the file format and maybe isn\'t readable by text editors anymore.</string>
<string name="saved_in_downloads">Saved in downloads</string>
<string name="rotate_image_cw">Rotate image clockwise</string>
<string name="pick_file">Pick image file</string>
<string name="otpauth_add">添加两步验证</string>
<string name="otpauth_error">无法添加两步验证</string>
<string name="search_web">在互联网中搜索</string>
<string name="export_to_file">导出为CSV文件</string>
<string name="csv_delimiter">使用哪个分隔符 (CSV-文件)?</string>
<string name="delimiter_comma">逗号</string>
<string name="delimiter_semicolon">分号</string>
<string name="csv_allow_binary">允许输出文件中的二进制数据?</string>
<string name="no"></string>
<string name="csv_allow_binary_message">您可能不想这样做,因为它会破坏文件格式,并且可能无法再由文本编辑器读取。</string>
<string name="saved_in_downloads">已保存到下载目录</string>
<string name="rotate_image_cw">旋转图片</string>
<string name="pick_file">选择图片文件</string>
<string name="pick_code_to_scan">选择扫描的编码</string>
</resources>
+4 -2
View File
@@ -7,8 +7,8 @@
<string name="content">内容</string>
<string name="binary_data">(2進制碼)</string>
<plurals name="barcode_info">
<item quantity="one">%1$s\n%2$d 個字符</item>
<item quantity="other">%1$s\n%2$d 個字符</item>
<item quantity="one">%2$d 個字符, %1$s</item>
<item quantity="other">%2$d 個字符, %1$s</item>
</plurals>
<string name="toggle_flash">閃光燈</string>
<string name="share">分享</string>
@@ -28,6 +28,7 @@
<string name="info">資訊</string>
<string name="switch_camera">切換相機</string>
<string name="history">歷史</string>
<string name="open_immediately">Skip inspection and open contents immediately</string>
<string name="use_history">儲存掃描歷史</string>
<string name="really_remove_scan">真的要移除掃描記錄?</string>
<string name="really_remove_all_scans">真的要移除所有掃描記錄?</string>
@@ -68,4 +69,5 @@
<string name="saved_in_downloads">Saved in downloads</string>
<string name="rotate_image_cw">Rotate image clockwise</string>
<string name="pick_file">Pick image file</string>
<string name="pick_code_to_scan">Pick code to scan</string>
</resources>
+2 -2
View File
@@ -1,11 +1,11 @@
<resources>
<color name="primary">#222</color>
<color name="primary_dark">#111</color>
<color name="accent">#b6d46f</color>
<color name="accent_dark">#a6c45f</color>
<color name="background_color">#111</color>
<color name="background_progress">#8000</color>
<color name="button_text">#fff</color>
<color name="separator">#333</color>
<color name="crop_bound">#8fff</color>
<color name="crop_bound">#fff</color>
<color name="candidate">#80b6d46f</color>
</resources>
@@ -3,6 +3,8 @@
<item>Google</item>
<item>DuckDuckGo</item>
<item>Qwant</item>
<item>Bing</item>
<item>Yandex</item>
<item>OpenFoodFacts.org</item>
<item>OpenBeautyFacts.org</item>
<item>OpenPetFoodFacts.org</item>
@@ -11,6 +13,8 @@
<item>https://www.google.com/search?q=</item>
<item>https://duckduckgo.com/?q=</item>
<item>https://www.qwant.com/?q=</item>
<item>https://bing.com/?q=</item>
<item>https://yandex.com/?q=</item>
<item>https://world.openfoodfacts.org/cgi/search.pl?search_terms=</item>
<item>https://world.openbeautyfacts.org/cgi/search.pl?search_terms=</item>
<item>https://world.openpetfoodfacts.org/cgi/search.pl?search_terms=</item>
+5 -3
View File
@@ -7,8 +7,8 @@
<string name="content">Content</string>
<string name="binary_data">(binary data)</string>
<plurals name="barcode_info">
<item quantity="one">%1$s\n%2$d character</item>
<item quantity="other">%1$s\n%2$d characters</item>
<item quantity="one">%2$d character, %1$s</item>
<item quantity="other">%2$d characters, %1$s</item>
</plurals>
<string name="toggle_flash">Toggle flash</string>
<string name="share">Share</string>
@@ -29,6 +29,7 @@
<string name="switch_camera">Switch camera</string>
<string name="history">History</string>
<string name="preferences">Preferences</string>
<string name="open_immediately">Skip inspection and open contents immediately</string>
<string name="use_history">Save scan history</string>
<string name="open_with_url">Open unknown data with URL</string>
<string name="really_remove_scan">Really remove scan?</string>
@@ -38,7 +39,7 @@
<string name="pick_list_separator">How to separate list items?</string>
<string name="separator_line_break">Line break</string>
<string name="separator_ruler">Ruler</string>
<string name="save_as_file_name">Save as file?</string>
<string name="save_as_file_name">Save as in Downloads file?</string>
<string name="file_name">File name</string>
<string name="error_saving_binary_data">Cannot save file</string>
<string name="error_file_exists">File already exists</string>
@@ -68,4 +69,5 @@
<string name="saved_in_downloads">Saved in downloads</string>
<string name="rotate_image_cw">Rotate image clockwise</string>
<string name="pick_file">Pick image file</string>
<string name="pick_code_to_scan">Pick code to scan</string>
</resources>
+16 -1
View File
@@ -1,13 +1,19 @@
<resources>
<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primary_dark</item>
<item name="colorPrimaryDark">@android:color/transparent</item>
<item name="colorAccent">@color/accent</item>
<item name="android:windowBackground">@drawable/background_window</item>
</style>
<style name="SplashTheme" parent="@style/AppTheme">
<item name="android:windowBackground">@drawable/background_splash</item>
</style>
<style
name="SecondaryText"
parent="@android:style/Widget.TextView">
<item name="android:textSize">12sp</item>
<item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
<style
name="Button"
parent="@android:style/Widget.Button">
@@ -21,4 +27,13 @@
<item name="android:paddingRight">@dimen/button_padding</item>
<item name="android:maxLines">1</item>
</style>
<style
name="FAB"
parent="Widget.Design.FloatingActionButton">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_margin">20dp</item>
<item name="android:layout_gravity">bottom|end</item>
<item name="elevation">4dp</item>
</style>
</resources>
+3
View File
@@ -0,0 +1,3 @@
<full-backup-content>
<include domain="root" path="."/>
</full-backup-content>
@@ -7,7 +7,10 @@ fun simpleFail(message: String = "Unknown reason, but failed"): Nothing {
throw IllegalStateException("You should never have reached this point in the code, but anyways: $message")
}
inline fun <reified E : Throwable> assertThrows(message: String = "Unknown reason, but didn't failed even though should fail", block: () -> Unit) {
inline fun <reified E : Throwable> assertThrows(
message: String = "Unknown reason, but didn't failed even though should fail",
block: () -> Unit
) {
try {
block()
fail(message)
@@ -1,10 +1,7 @@
package de.markusfisch.android.binaryeye.actions.wifi
import de.markusfisch.android.binaryeye.simpleFail
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertFalse
import junit.framework.TestCase.assertNull
import junit.framework.TestCase.assertTrue
import junit.framework.TestCase.*
import org.junit.Test
class WifiConfigurationFactoryTest {
@@ -86,8 +83,8 @@ class WifiConfigurationFactoryTest {
private fun simpleDataAccessor(wifiString: String): WifiConfigurationFactory.SimpleDataAccessor {
val map = WifiConfigurationFactory.parseMap(wifiString)
?: simpleFail("parsing map of valid string fails ($wifiString)")
?: simpleFail("parsing map of valid string fails ($wifiString)")
return WifiConfigurationFactory.SimpleDataAccessor.of(map)
?: simpleFail("could not create SimpleDataAccessor of (potentially) valid map ($map of $wifiString)")
?: simpleFail("could not create SimpleDataAccessor of (potentially) valid map ($map of $wifiString)")
}
}
+22 -5
View File
@@ -1,9 +1,26 @@
buildscript {
ext.kotlin_version = '1.3.50'
ext.tools_version = '3.5.2'
ext.build_tools_version = '28.0.3'
ext.sdk_version = 28
ext.support_version = '25.3.1'
// Since RenderScript can no longer be compiled on macOS Catalina
// because Catalina cannot run 32bit binaries and Google failed
// to provide a 64bit compiler in time. Now, as if that wasn't
// bad enough, the new build tools that provide a 64bit compiler
// don't work on any other system than macOS unfortunately.
ext.is_catalina = {
if (System.properties['os.name'] == 'Mac OS X') {
def numbers = System.properties['os.version'].split('\\.')
if (numbers.length > 1) {
return numbers[1].toInteger() >= 15
}
}
return false
}()
ext {
kotlin_version = '1.3.61'
tools_version = '3.6.1'
build_tools_version = ext.is_catalina ? '29.0.3' : '28.0.3'
sdk_version = 29
support_version = '25.3.1'
}
repositories {
google()
+2
View File
@@ -0,0 +1,2 @@
org.gradle.jvmargs=-Xmx1536m
kotlin.code.style=official
+1 -1
View File
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip