Compare commits

...
65 Commits
Author SHA1 Message Date
Markus Fisch 694ebf83ed Advance version number to 1.9.0 2019-06-16 19:20:51 +02:00
Markus Fisch 78bb03fabf Try Google when opening content as URL fails
Better than showing an error and doing nothing.
2019-06-16 19:10:34 +02:00
Markus Fisch 902215f064 Show generic error message if Exception is empty
Because Zxing throws empty Exceptions for some barcodes.
2019-06-16 18:58:18 +02:00
Markus Fisch eaf38729bd Advance version number to 1.8.1 2019-05-26 17:04:54 +02:00
Markus Fisch bdd89ca539 Add contribution guidelines 2019-05-26 16:33:11 +02:00
Markus Fisch 39ecd31455 Advance version number to 1.8.0 2019-05-26 16:15:52 +02:00
Markus Fisch c96a2cf3ec Assume binary data if content is empty 2019-05-26 16:08:52 +02:00
Markus Fisch ea9e29b98c Support for saving binary data into external file
Since sharing binary data doesn't make much sense.
2019-05-26 15:57:55 +02:00
Markus Fisch 96e1ef5a52 Save binary data as blob
Required to restore it correctly.
2019-05-26 15:55:02 +02:00
Markus Fisch ed4b0a75dd Remove horizontal orientation line from scanner
Looks cleaner without that boring line.
2019-05-24 10:18:29 +02:00
Markus Fisch 37354338cb Increase button height for API < 21
48dp is the recommended touch height.
2019-05-21 10:48:27 +02:00
Markus Fisch 3cd2eecc22 Advance version number to 1.7.3 2019-05-20 20:17:04 +02:00
Markus Fisch 0e40d3f7c3 Use idiomatic Kotlin syntax 2019-05-20 20:12:18 +02:00
Markus Fisch c8fa01116d Hide binary data and text-only actions
Show binary data in hex dump only.
2019-05-20 20:10:42 +02:00
Markus Fisch 04539b4c37 Remove RenderScript fallback
Isn't needed anymore now that RenderScript works on Lineage 16.0.
2019-05-20 20:09:25 +02:00
Markus Fisch b9e8f4cfca Invoke forceCompat() to fix crashes on Lineage
For some reason invoking RenderScript.forceCompat() makes
RenderScript finally work on Lineage 16.0. Without that,
RenderScript will crash on (at least a Samsung Galaxy S5
running) Lineage 16.0 as soon as it's initialized.
Seems to be a device/system bug.

The solution was found by https://github.com/nonnymoose
Thanks a lot! :D

For more details about this please see:
https://github.com/markusfisch/BinaryEye/issues/21
2019-05-20 10:57:38 +02:00
Markus Fisch ad06c2bf9e Update build tools 2019-05-17 21:03:35 +02:00
Markus Fisch 2c7ec74b1b Add rs_fp_relaxed to rotator RenderScript
The docs recommend using rs_fp_relaxed because it "may be
beneficial on some architectures due to additional optimizations
only available with relaxed precision (such as SIMD CPU
instructions)."
2019-05-15 22:10:31 +02:00
Markus Fisch b34f29c95f Catch RSRuntimeException earlier
Turns out ScriptC_rotator() can raise a RSRuntimeException
so instantiating the Preprocessor object needs to be
guarded in the try/catch block.
2019-05-15 22:05:56 +02:00
Markus Fisch e11518c3f5 Use metadata byte segments for hex dump
Some barcodes can contain binary data (e.g. QR code in 8bit mode).
But Result.getText() returns an already translated string.
2019-05-15 13:41:27 +02:00
Markus Fisch 57c4fad307 Advance version number to 1.7.2 2019-05-14 19:27:43 +02:00
Markus Fisch 856b8e37e1 Add signing config to release target 2019-05-14 19:27:27 +02:00
Markus Fisch 6b8dd8e864 Add a fallback for RenderScript
Since RenderScript fails on some devices/ROMs for unknown
reasons (e.g. Samsung Galaxy S5 with Lineage OS 16.0).

I have confirmed it's not this app by trying the official
Google sample for RenderScript which crashed too.
So I believe it's a system issue.

Now, instead of crashing, the app provides a fallback for
YUV conversion and image rotation. Of course, it's much
slower than the RenderScript implementation but still
better than crashing the app.
2019-05-14 19:14:59 +02:00
Markus Fisch c993cd53f1 Add italian translation 2019-05-12 21:23:32 +02:00
Markus Fisch 934983f668 Add shadow to Google Play icon 2019-05-09 23:02:13 +02:00
Markus Fisch ccf425faad Add new Google Play icon
According to new icon specification.
2019-05-09 22:34:45 +02:00
Markus Fisch a244763109 Move signing configuration into app/build.gradle
And add a rule to build an app bundle.
2019-05-09 20:38:36 +02:00
Markus Fisch 7ab712e040 Advance version number to 1.7.1 2019-05-08 11:09:25 +02:00
Markus Fisch cb7219b290 Update to RenderScript API 24 to build 64bit libs
Building against RenderScript API 18 doesn't produce
librs.rotator.so for arm64-v8a and x86_64 architectures.
2019-05-08 10:58:55 +02:00
Markus Fisch 7f4ba50ee4 Update gradle, build tools and remove Error Prone
I liked Error Prone. But the old version is incompatible
with Gradle 5 now and using a newer one requires an amount
of work I'm not willing to invest.
2019-05-07 17:05:50 +02:00
Markus Fisch b1f00f0592 Remove unused member variable 2019-05-07 10:51:54 +02:00
Markus Fisch 80f8f908d2 Advance version number to 1.7.0 2019-05-01 13:44:51 +02:00
Markus Fisch 506308ece9 Remove now unused remove_scan string resource 2019-05-01 13:04:08 +02:00
Markus Fisch baecded9d5 Pick list separator on sharing history listing
Depending on the use case, it may be better to have
a dividing line or not.
2019-05-01 12:31:54 +02:00
Markus Fisch ceefee397f Remove extra decoding thread
Unnecessary since CameraView 1.8.2 because Camera.PreviewCallback
is called from a separate thread already.
2019-05-01 12:11:18 +02:00
Markus Fisch e45804be31 Update URL to F-Droid badge 2019-04-30 10:08:54 +02:00
Balázs ÚrandMarkus Fisch edef1acbfb Update Hungarian translation (#28) 2019-04-24 22:08:34 +02:00
Markus Fisch 742939182c Advance version number to 1.6.0 2019-04-24 17:52:46 +02:00
Markus Fisch a21bfcdea6 Share history listing as plain text
Feature request.
2019-04-24 17:36:05 +02:00
Markus Fisch bd281e449b Add missing barcode_info plural for french 2019-04-24 16:49:49 +02:00
Markus Fisch 0e1464a15f Add separator line between history items
Makes it easier to separate list items.
2019-04-24 16:48:47 +02:00
Markus Fisch 8a6441ddf3 Add content description for share FABs
Improve accessibility.
2019-04-24 16:47:48 +02:00
Markus Fisch 94874c0a9e Add content description for flash FAB
Improve accessibility.
2019-04-24 16:46:38 +02:00
Markus Fisch b9a6410959 Advance version number to 1.5.1 2019-04-12 20:46:26 +02:00
Markus Fisch 512f5e00dd Show content bytes instead of chars in hex dump 2019-04-12 20:42:21 +02:00
Markus Fisch 91c2364958 Advance version number to 1.5.0 2019-04-10 21:53:24 +02:00
Markus Fisch 1e3f1c553c Show a hex dump of decoded contents
And update character count when contents are edited too.
2019-04-10 21:48:33 +02:00
Markus Fisch 80c65a48a0 Make when() return the fragment
Instead of assigning a help variable.
2019-04-10 10:30:46 +02:00
Markus Fisch 4c04ffb7db Move string literal to the right side
Left-over from Java "".equals() pattern.
2019-04-10 10:30:16 +02:00
Markus Fisch cc3949fa71 Fix evaluating sent text from share menu 2019-04-10 10:29:55 +02:00
Markus Fisch 904a0ecbdd Update CameraView library to latest version 2019-04-08 20:56:29 +02:00
Balázs ÚrandMarkus Fisch 9155dd026d Update Hungarian translation 2019-03-26 09:53:39 +01:00
grenagitandMarkus Fisch 7286a06985 Add French translation 2019-03-22 15:25:34 +01:00
Markus Fisch a71552a406 Advance version number to 1.4.3 2019-03-21 10:27:09 +01:00
Markus Fisch 5857e8c394 Remove toStartOf/toLeftOf from time view
Circular dependency because "format" already specifies
toEndOf/toRightOf.
2019-03-21 10:20:49 +01:00
Markus Fisch 429af04714 Advance version number to 1.4.2 2019-03-18 22:22:11 +01:00
Markus Fisch f134987572 Remove unused system bar methods 2019-03-18 22:17:51 +01:00
Markus Fisch d8a9043f94 Put findViewById() on one line
Because Android Studio can't stand the line break.
2019-03-18 22:15:07 +01:00
Markus Fisch 3782fa8360 Make sure time and format won't overlap in row 2019-03-18 22:14:40 +01:00
Markus Fisch 023e32f796 Use a plural resource for barcode length 2019-03-18 22:09:41 +01:00
Markus Fisch 82f3abdc86 Use string resource for width by height string 2019-03-18 22:01:40 +01:00
Markus Fisch 1345d31a6d Use property access syntax where possible
And clean up other syntax deviations.
2019-03-18 21:57:05 +01:00
Markus Fisch c56fa39c15 Show barcode type and content length
After a barcode was decoded.
2019-03-18 20:31:08 +01:00
Markus Fisch 7c8997ae9a Update Error Prone version 2019-03-18 20:21:23 +01:00
Markus Fisch 584343c4e9 Add main sources to Gradle source sets
Otherwise Android Studio won't find them.
2019-03-18 20:19:31 +01:00
48 changed files with 1329 additions and 450 deletions
+46
View File
@@ -1,5 +1,51 @@
# Change Log
## 1.9.0
* Try Google when opening content as URL fails
* Show generic error message if barcode generation fails
## 1.8.1
* Distribute APK instead of App Bundle
## 1.8.0
* Save binary data into external file
* Fix storage of binary data
* Remove horizontal orientation line from scanner
## 1.7.3
* Fix crashes on Lineage and remove RenderScript fallback
* Improve handling of binary data
## 1.7.2
* Add a fallback for RenderScript
* Add italian translation
## 1.7.1
* Fix support for 64 bit architectures
## 1.7.0
* Pick list separator on sharing history listing
* Update Hungarian translation
## 1.6.0
* Share history listing as plain text
* Add separator line between history items
* Add content description for floating action buttons
## 1.5.1
* Fix generating hex dump
## 1.5.0
* Show a hex dump of decoded contents
* Fix processing text from share menu
* Add French translation
## 1.4.3
* Fix broken history listing
## 1.4.2
* Show barcode type and content length after decoding
## 1.4.1
* Toggle availability of torch mode button according to camera features
+20
View File
@@ -0,0 +1,20 @@
# Contribution Guidelines
Please try to keep things in good shape and comply to what's there.
This project follows Android [best practices][android_best_practices]
so please have a look if you've never heard of them.
The code is formatted according to Android Studio's standard, with the
exception of indent being tabs instead of spaces.
Use the feature branch workflow to add new features and make sure
to squash when merging into master:
$ git merge cool_feature --squash
Then write a [good commit messages][commit_messages] to keep the history
meaningful and useful. One feature, one commit.
[android_best_practices]: https://developer.android.com/distribute/best-practices/develop/
[commit_messages]: https://juffalow.com/other/write-good-git-commit-message
+5 -7
View File
@@ -1,5 +1,4 @@
PACKAGE = de.markusfisch.android.binaryeye
APK = app/build/outputs/apk/debug/app-debug.apk
all: debug install start
@@ -7,11 +6,10 @@ debug:
./gradlew assembleDebug
release: lint
@./gradlew assembleRelease \
-Pandroid.injected.signing.store.file=$(ANDROID_KEYFILE) \
-Pandroid.injected.signing.store.password=$(ANDROID_STORE_PASSWORD) \
-Pandroid.injected.signing.key.alias=$(ANDROID_KEY_ALIAS) \
-Pandroid.injected.signing.key.password=$(ANDROID_KEY_PASSWORD)
./gradlew assembleRelease
bundle: lint
./gradlew bundleRelease
lint:
./gradlew lintDebug
@@ -26,7 +24,7 @@ test:
./gradlew cAT
install:
adb $(TARGET) install -r $(APK)
adb $(TARGET) install -r app/build/outputs/apk/debug/app-debug.apk
start:
adb $(TARGET) shell 'am start -n \
+1 -1
View File
@@ -9,7 +9,7 @@ Unlike many others it works in portrait and landscape orientation, can read
inverted codes, uses Material Design and doesn't do anything else but decoding
a barcode.
<a href="https://f-droid.org/en/packages/de.markusfisch.android.binaryeye/"><img alt="Get it on F-Droid" src="https://f-droid.org/badge/get-it-on.png" height="80"/></a> <a href="https://play.google.com/store/apps/details?id=de.markusfisch.android.binaryeye"><img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png" height="80"/></a>
<a href="https://f-droid.org/en/packages/de.markusfisch.android.binaryeye/"><img alt="Get it on F-Droid" src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="80"/></a> <a href="https://play.google.com/store/apps/details?id=de.markusfisch.android.binaryeye"><img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png" height="80"/></a>
And it's written in [Kotlin][kotlin] because I wanted to explore that a
bit more.
+17 -12
View File
@@ -1,10 +1,5 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'net.ltgt.errorprone'
configurations.errorprone {
resolutionStrategy.force 'com.google.errorprone:error_prone_core:2.0.19'
}
android {
compileSdkVersion sdk_version
@@ -14,18 +9,29 @@ android {
minSdkVersion 9
targetSdkVersion sdk_version
versionCode 16
versionName '1.4.1'
versionCode 28
versionName '1.9.0'
// it's recommended to set this value to the lowest API level
// able to provide all the functionality
renderscriptTargetApi 18
renderscriptTargetApi 24
renderscriptSupportModeEnabled true
testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
}
signingConfigs {
config {
keyAlias System.getenv('ANDROID_KEY_ALIAS')
keyPassword System.getenv('ANDROID_KEY_PASSWORD')
storePassword System.getenv('ANDROID_STORE_PASSWORD')
def filePath = System.getenv('ANDROID_KEYFILE')
storeFile filePath ? file(filePath) : null
}
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
androidTest.java.srcDirs += 'src/androidTest/kotlin'
}
@@ -35,11 +41,10 @@ android {
}
release {
proguardFiles \
getDefaultProguardFile('proguard-android.txt'), \
'proguard-rules.pro'
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
minifyEnabled true
shrinkResources true
signingConfig signingConfigs.config
}
}
@@ -65,5 +70,5 @@ dependencies {
implementation "com.android.support:appcompat-v7:$support_version"
implementation "com.android.support:design:$support_version"
implementation 'com.google.zxing:core:3.3.3'
implementation 'com.github.markusfisch:CameraView:1.8.2'
implementation 'com.github.markusfisch:CameraView:1.8.3'
}
@@ -1,10 +1,12 @@
package de.markusfisch.android.binaryeye.activity
import com.google.zxing.Result
import com.google.zxing.ResultMetadataType
import de.markusfisch.android.cameraview.widget.CameraView
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.rs.Preprocessor
@@ -21,8 +23,6 @@ import android.os.Bundle
import android.os.Parcelable
import android.os.Vibrator
import android.provider.MediaStore
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import android.view.Menu
@@ -38,31 +38,18 @@ import java.io.IOException
class CameraActivity : AppCompatActivity() {
private val zxing = Zxing()
private val decodingRunnable = Runnable {
while (!Thread.currentThread().isInterrupted()) {
val result = decodeFrame()
if (result != null) {
cameraView.post { found(result) }
break
}
}
}
private lateinit var vibrator: Vibrator
private lateinit var cameraView: CameraView
private lateinit var zoomBar: SeekBar
private lateinit var flashFab: View
private var decodingThread: Thread? = null
private var preprocessor: Preprocessor? = null
private var frameData: ByteArray? = null
private var frameWidth: Int = 0
private var frameHeight: Int = 0
private var frameOrientation: Int = 0
private var invert = false
private var flash = false
private var returnResult = false
private var frontFacing = false
private var fallbackBuffer: IntArray? = null
override fun onRequestPermissionsResult(
requestCode: Int,
@@ -70,8 +57,9 @@ class CameraActivity : AppCompatActivity() {
grantResults: IntArray
) {
when (requestCode) {
REQUEST_CAMERA -> if (grantResults.size > 0 &&
grantResults[0] != PackageManager.PERMISSION_GRANTED) {
REQUEST_CAMERA -> if (grantResults.isNotEmpty() &&
grantResults[0] != PackageManager.PERMISSION_GRANTED
) {
Toast.makeText(
this,
R.string.no_camera_no_fun,
@@ -94,14 +82,14 @@ class CameraActivity : AppCompatActivity() {
cameraView = findViewById(R.id.camera_view) as CameraView
zoomBar = findViewById(R.id.zoom) as SeekBar
flashFab = findViewById(R.id.flash)
flashFab.setOnClickListener { _ -> toggleTorchMode() }
flashFab.setOnClickListener { toggleTorchMode() }
initCameraView()
initZoomBar()
restoreZoom()
if (intent?.action == Intent.ACTION_SEND) {
if ("text/plain" == intent.type) {
if (intent.type == "text/plain") {
handleSendText(intent)
} else if (intent.type?.startsWith("image/") == true) {
handleSendImage(intent)
@@ -112,16 +100,15 @@ class CameraActivity : AppCompatActivity() {
override fun onDestroy() {
super.onDestroy()
preprocessor?.destroy()
fallbackBuffer = null
saveZoom()
}
override fun onResume() {
super.onResume()
System.gc()
returnResult = "com.google.zxing.client.android.SCAN".equals(
intent.action
)
if (hasCameraPermission()) {
returnResult = "com.google.zxing.client.android.SCAN" == intent.action
if (hasCameraPermission(this, REQUEST_CAMERA)) {
openCamera()
}
}
@@ -144,7 +131,6 @@ class CameraActivity : AppCompatActivity() {
}
private fun closeCamera() {
cancelDecoding()
cameraView.close()
}
@@ -204,18 +190,15 @@ class CameraActivity : AppCompatActivity() {
}
private fun handleSendText(intent: Intent) {
var text = intent.getStringExtra(Intent.EXTRA_TEXT)
if (text?.isEmpty() == true) {
val text = intent.getStringExtra(Intent.EXTRA_TEXT)
if (text?.isEmpty() == false) {
startActivity(MainActivity.getEncodeIntent(this, text, true))
finish()
}
}
private fun handleSendImage(intent: Intent) {
var uri = intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri
if (uri == null) {
return
}
val uri = intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri ?: return
val bitmap = try {
MediaStore.Images.Media.getBitmap(contentResolver, uri)
} catch (e: IOException) {
@@ -229,7 +212,7 @@ class CameraActivity : AppCompatActivity() {
).show()
return
}
val result = zxing.decode(downsizeIfBigger(bitmap, 1024))
val result = zxing.decodePositiveNegative(downsizeIfBigger(bitmap, 1024))
if (result != null) {
showResult(result)
finish()
@@ -243,21 +226,6 @@ class CameraActivity : AppCompatActivity() {
finish()
}
private fun hasCameraPermission(): Boolean {
val permission = android.Manifest.permission.CAMERA
if (ContextCompat.checkSelfPermission(this, permission) !=
PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this, arrayOf(permission),
REQUEST_CAMERA
)
return false
}
return true
}
private fun initCameraView() {
cameraView.setUseOrientationListener(true)
cameraView.setTapToFocus()
@@ -265,21 +233,21 @@ class CameraActivity : AppCompatActivity() {
override fun onConfigureParameters(
parameters: Camera.Parameters
) {
if (parameters.isZoomSupported()) {
val max = parameters.getMaxZoom()
if (parameters.isZoomSupported) {
val max = parameters.maxZoom
if (zoomBar.max != max) {
zoomBar.max = max
zoomBar.progress = max / 10
saveZoom()
}
parameters.setZoom(zoomBar.progress)
parameters.zoom = zoomBar.progress
} else {
zoomBar.visibility = View.GONE
}
val sceneModes = parameters.supportedSceneModes
sceneModes?.let {
for (mode in sceneModes) {
if (mode.equals(Camera.Parameters.SCENE_MODE_BARCODE)) {
if (mode == Camera.Parameters.SCENE_MODE_BARCODE) {
parameters.sceneMode = mode
break
}
@@ -299,18 +267,33 @@ class CameraActivity : AppCompatActivity() {
}
override fun onCameraReady(camera: Camera) {
frameWidth = cameraView.frameWidth
frameHeight = cameraView.frameHeight
frameOrientation = cameraView.frameOrientation
camera.setPreviewCallback { data, _ -> frameData = data }
val frameWidth = cameraView.frameWidth
val frameHeight = cameraView.frameHeight
val frameOrientation = cameraView.frameOrientation
var decoding = true
camera.setPreviewCallback { frameData, _ ->
if (decoding) {
val result = decodeFrame(
frameData,
frameWidth,
frameHeight,
frameOrientation
)
result?.let {
cameraView.post {
vibrator.vibrate(100)
showResult(result)
}
decoding = false
}
}
}
}
override fun onPreviewStarted(camera: Camera) {
startDecoding()
}
override fun onCameraStopping(camera: Camera) {
cancelDecoding()
camera.setPreviewCallback(null)
}
})
@@ -334,12 +317,12 @@ class CameraActivity : AppCompatActivity() {
}
private fun setZoom(zoom: Int) {
val camera: Camera? = cameraView.getCamera()
val camera: Camera? = cameraView.camera
camera?.let {
try {
val params = camera.getParameters()
params.setZoom(zoom)
camera.setParameters(params)
val params = camera.parameters
params.zoom = zoom
camera.parameters = params
} catch (e: RuntimeException) {
// ignore; there's nothing we can do
}
@@ -370,18 +353,16 @@ class CameraActivity : AppCompatActivity() {
}
private fun toggleTorchMode() {
val camera = cameraView.getCamera()
val parameters = camera?.getParameters()
parameters?.setFlashMode(
if (flash) {
Camera.Parameters.FLASH_MODE_OFF
} else {
Camera.Parameters.FLASH_MODE_TORCH
}
)
val camera = cameraView.camera
val parameters = camera?.parameters
parameters?.flashMode = if (flash) {
Camera.Parameters.FLASH_MODE_OFF
} else {
Camera.Parameters.FLASH_MODE_TORCH
}
parameters?.let {
try {
camera.setParameters(parameters)
camera.parameters = parameters
flash = flash xor true
} catch (e: RuntimeException) {
// ignore; there's nothing we can do
@@ -389,59 +370,39 @@ class CameraActivity : AppCompatActivity() {
}
}
private fun startDecoding() {
frameData = null
decodingThread = Thread(decodingRunnable)
decodingThread?.start()
}
private fun cancelDecoding() {
if (decodingThread != null) {
decodingThread?.interrupt()
try {
decodingThread?.join()
} catch (e: InterruptedException) {
// parent thread was interrupted
}
decodingThread = null
}
}
private fun decodeFrame(): Result? {
var fd = frameData
fd ?: return null
if (preprocessor == null) {
preprocessor = Preprocessor(
this,
frameWidth,
frameHeight,
frameOrientation
)
}
var pp = preprocessor
pp ?: return null
pp.process(fd)
private fun decodeFrame(
frameData: ByteArray?,
frameWidth: Int,
frameHeight: Int,
frameOrientation: Int
): Result? {
frameData ?: return null
invert = invert xor true
val pp = preprocessor ?: Preprocessor(
this,
frameWidth,
frameHeight,
frameOrientation
)
pp.process(frameData)
preprocessor = pp
return zxing.decode(
fd,
frameData,
pp.outWidth,
pp.outHeight,
invert
)
}
private fun found(result: Result) {
cancelDecoding()
vibrator.vibrate(100)
showResult(result)
}
private fun showResult(result: Result) {
val rawBytes = getRawBytes(result)
if (prefs.useHistory) {
GlobalScope.launch {
db.insertScan(
System.currentTimeMillis(),
result.text,
rawBytes,
result.barcodeFormat.toString()
)
}
@@ -459,7 +420,8 @@ class CameraActivity : AppCompatActivity() {
MainActivity.getDecodeIntent(
this,
result.text,
result.barcodeFormat
result.barcodeFormat,
rawBytes
)
)
}
@@ -478,12 +440,12 @@ fun downsizeIfBigger(bitmap: Bitmap, max: Int): Bitmap {
if (bitmap.width > bitmap.height) {
height = Math.round(
width.toFloat() / bitmap.width.toFloat() *
bitmap.height.toFloat()
bitmap.height.toFloat()
)
} else {
width = Math.round(
height.toFloat() / bitmap.height.toFloat() *
bitmap.width.toFloat()
bitmap.width.toFloat()
)
}
Bitmap.createScaledBitmap(bitmap, width, height, true)
@@ -491,3 +453,14 @@ fun downsizeIfBigger(bitmap: Bitmap, max: Int): Bitmap {
bitmap
}
}
fun getRawBytes(result: Result): ByteArray? {
val metadata = result.resultMetadata ?: return null
val segments = metadata[ResultMetadataType.BYTE_SEGMENTS] ?: return null
var bytes = ByteArray(0)
@Suppress("UNCHECKED_CAST")
for (seg in segments as Iterable<ByteArray>) {
bytes += seg
}
return bytes
}
@@ -12,7 +12,6 @@ import de.markusfisch.android.binaryeye.R
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
@@ -36,32 +35,29 @@ class MainActivity : AppCompatActivity() {
supportActionBar?.setDisplayHomeAsUpEnabled(true)
if (state == null) {
var fragment: Fragment
if (intent?.hasExtra(HISTORY) == true) {
fragment = HistoryFragment()
} else if (intent?.hasExtra(ENCODE) == true) {
fragment = EncodeFragment.newInstance(
setFragment(supportFragmentManager, when {
intent?.hasExtra(HISTORY) == true -> HistoryFragment()
intent?.hasExtra(ENCODE) == true -> EncodeFragment.newInstance(
intent.getStringExtra(ENCODE)
)
} else if (intent?.hasExtra(DECODE) == true) {
fragment = DecodeFragment.newInstance(
intent.getStringExtra(DECODE),
intent?.hasExtra(DECODED_TEXT) == true -> DecodeFragment.newInstance(
intent.getStringExtra(DECODED_TEXT),
intent.getSerializableExtra(
DECODE_FORMAT
) as BarcodeFormat
DECODED_FORMAT
) as BarcodeFormat,
intent.getByteArrayExtra(DECODED_RAW)
)
} else {
fragment = DecodeFragment()
}
setFragment(supportFragmentManager, fragment)
else -> DecodeFragment()
})
}
}
companion object {
private const val HISTORY = "history"
private const val ENCODE = "encode"
private const val DECODE = "decode"
private const val DECODE_FORMAT = "decode_format"
private const val DECODED_TEXT = "decoded_text"
private const val DECODED_FORMAT = "decoded_format"
private const val DECODED_RAW = "decoded_raw"
fun getHistoryIntent(context: Context): Intent {
val intent = Intent(context, MainActivity::class.java)
@@ -78,9 +74,9 @@ class MainActivity : AppCompatActivity() {
intent.putExtra(ENCODE, text)
if (isExternal) {
intent.addFlags(
android.content.Intent.FLAG_ACTIVITY_NO_HISTORY or
android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK or
android.content.Intent.FLAG_ACTIVITY_NEW_TASK
Intent.FLAG_ACTIVITY_NO_HISTORY or
Intent.FLAG_ACTIVITY_CLEAR_TASK or
Intent.FLAG_ACTIVITY_NEW_TASK
)
}
return intent
@@ -89,11 +85,15 @@ class MainActivity : AppCompatActivity() {
fun getDecodeIntent(
context: Context,
text: String,
format: BarcodeFormat
format: BarcodeFormat,
raw: ByteArray? = null
): Intent {
val intent = Intent(context, MainActivity::class.java)
intent.putExtra(DECODE, text)
intent.putExtra(DECODE_FORMAT, format)
intent.putExtra(DECODED_TEXT, text)
intent.putExtra(DECODED_FORMAT, format)
if (raw != null) {
intent.putExtra(DECODED_RAW, raw)
}
return intent
}
}
@@ -33,8 +33,13 @@ class ScansAdapter(context: Context, cursor: Cursor) :
cursor: Cursor
) {
val holder = getViewHolder(view)
val content = cursor.getString(contentIndex)
holder.timeView.text = cursor.getString(timeIndex)
holder.contentView.text = cursor.getString(contentIndex)
holder.contentView.text = if (content.isEmpty()) {
context.getString(R.string.binary_data)
} else {
content
}
holder.formatView.text = cursor.getString(formatIndex)
}
@@ -4,6 +4,7 @@ import de.markusfisch.android.binaryeye.data.Database
import de.markusfisch.android.binaryeye.preference.Preferences
import android.app.Application
import android.support.v8.renderscript.RenderScript
val db = Database()
val prefs = Preferences()
@@ -11,6 +12,11 @@ val prefs = Preferences()
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()
db.open(this)
prefs.init(this)
}
@@ -2,6 +2,7 @@ package de.markusfisch.android.binaryeye.app
import de.markusfisch.android.binaryeye.R
import android.annotation.SuppressLint
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager
import android.support.v4.app.FragmentTransaction
@@ -14,6 +15,7 @@ fun addFragment(fm: FragmentManager?, fragment: Fragment) {
fm?.let { getTransaction(fm, fragment).addToBackStack(null).commit() }
}
@SuppressLint("CommitTransaction")
private fun getTransaction(
fm: FragmentManager,
fragment: Fragment
@@ -0,0 +1,7 @@
package de.markusfisch.android.binaryeye.app
import java.util.regex.Pattern
private val nonPrintable = Pattern.compile("[\\x00-\\x08\\x0e-\\x1f]")
fun hasNonPrintableCharacters(s: String) = nonPrintable.matcher(s).find()
@@ -0,0 +1,38 @@
package de.markusfisch.android.binaryeye.app
import android.app.Activity
import android.content.pm.PackageManager
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import android.Manifest
fun hasCameraPermission(activity: Activity, requestCode: Int): Boolean {
return hasPermission(activity, Manifest.permission.CAMERA, requestCode)
}
fun hasWritePermission(activity: Activity, requestCode: Int = 1): Boolean {
return hasPermission(
activity,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
requestCode
)
}
fun hasPermission(
activity: Activity,
permission: String,
requestCode: Int
): Boolean {
return if (ContextCompat.checkSelfPermission(activity, permission) !=
PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
activity,
arrayOf(permission),
requestCode
)
false
} else {
true
}
}
@@ -0,0 +1,42 @@
package de.markusfisch.android.binaryeye.app
import de.markusfisch.android.binaryeye.BuildConfig
import android.content.Context
import android.content.Intent
import android.os.Build
import android.support.v4.content.FileProvider
import android.net.Uri
import java.io.File
fun shareText(context: Context, text: String, type: String = "text/plain") {
val intent = Intent(Intent.ACTION_SEND)
intent.putExtra(Intent.EXTRA_TEXT, text)
intent.type = type
context.startActivity(intent)
}
fun shareUri(context: Context, uri: Uri, type: String) {
val intent = Intent(Intent.ACTION_SEND)
intent.putExtra(Intent.EXTRA_STREAM, uri)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.type = type
context.startActivity(intent)
}
fun shareFile(context: Context, file: File, type: String) {
shareUri(context, getUriForFile(context, file), type)
}
fun getUriForFile(context: Context, file: File): Uri {
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
Uri.fromFile(file)
} else {
FileProvider.getUriForFile(
context,
BuildConfig.APPLICATION_ID + ".provider",
file
)
}
}
@@ -2,14 +2,10 @@ package de.markusfisch.android.binaryeye.app
import de.markusfisch.android.binaryeye.R
import android.content.Context
import android.content.res.Configuration
import android.content.res.Resources
import android.graphics.Point
import android.os.Build
import android.support.v4.content.ContextCompat
import android.support.v7.app.AppCompatActivity
import android.util.TypedValue
import android.view.View
import android.view.Window
@@ -37,63 +33,16 @@ fun setSystemBarColor(window: Window, color: Int): Boolean {
window.navigationBarColor = color
window.decorView.systemUiVisibility =
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
return true
}
fun getStatusAndToolBarHeight(context: Context): Int {
return getStatusBarHeight(context.resources) + getToolBarHeight(context)
}
fun getStatusBarHeight(res: Resources): Int {
return getIdentifierDimen(res, "status_bar_height")
}
fun getToolBarHeight(context: Context): Int {
val tv = TypedValue()
return if (context.theme.resolveAttribute(
android.R.attr.actionBarSize,
tv,
true
))
TypedValue.complexToDimensionPixelSize(
tv.data,
context.resources.displayMetrics
)
else
0
}
fun getNavigationBarSize(res: Resources): Point {
val size = Point(0, 0)
if (!getIdentifierBoolean(res, "config_showNavigationBar")) {
return size
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
val conf = res.getConfiguration()
if (conf.orientation == Configuration.ORIENTATION_LANDSCAPE &&
// according to https://developer.android.com/training/multiscreen/screensizes.html#TaskUseSWQuali
// only a screen < 600 dp is considered to be a phone
// and can move its navigation bar to the side
conf.smallestScreenWidthDp < 600) {
size.x = getIdentifierDimen(
res,
"navigation_bar_height_landscape"
)
return size
}
}
size.y = getIdentifierDimen(res, "navigation_bar_height")
return size
}
private fun getIdentifierBoolean(res: Resources, name: String): Boolean {
val id = res.getIdentifier(name, "bool", "android")
return id > 0 && res.getBoolean(id)
}
private fun getIdentifierDimen(res: Resources, name: String): Int {
val id = res.getIdentifier(name, "dimen", "android")
return if (id > 0) res.getDimensionPixelSize(id) else 0
@@ -1,7 +1,10 @@
package de.markusfisch.android.binaryeye.data
import de.markusfisch.android.binaryeye.app.hasNonPrintableCharacters
import android.content.ContentValues
import android.content.Context
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.text.format.DateFormat
@@ -13,7 +16,7 @@ class Database {
db = OpenHelper(context).writableDatabase
}
fun getScans() = db.rawQuery(
fun getScans(): Cursor? = db.rawQuery(
"""SELECT
$SCANS_ID,
$SCANS_DATETIME,
@@ -24,17 +27,23 @@ class Database {
""", null
)
fun getScan(id: Long) = db.rawQuery(
fun getScan(id: Long): Cursor? = db.rawQuery(
"""SELECT
$SCANS_DATETIME,
$SCANS_CONTENT,
$SCANS_RAW,
$SCANS_FORMAT
FROM $SCANS
WHERE $SCANS_ID = ?
""", arrayOf("$id")
)
fun insertScan(timestamp: Long, name: String, code: String): Long {
fun insertScan(
timestamp: Long,
content: String,
raw: ByteArray?,
format: String
): Long {
val cv = ContentValues()
cv.put(
SCANS_DATETIME, DateFormat.format(
@@ -42,8 +51,13 @@ class Database {
timestamp
).toString()
)
cv.put(SCANS_CONTENT, name)
cv.put(SCANS_FORMAT, code)
if (hasNonPrintableCharacters(content) && raw != null) {
cv.put(SCANS_CONTENT, "")
cv.put(SCANS_RAW, raw)
} else {
cv.put(SCANS_CONTENT, content)
}
cv.put(SCANS_FORMAT, format)
return db.insert(SCANS, null, cv)
}
@@ -56,7 +70,7 @@ class Database {
}
private class OpenHelper(context: Context) :
SQLiteOpenHelper(context, "history.db", null, 1) {
SQLiteOpenHelper(context, "history.db", null, 2) {
override fun onCreate(db: SQLiteDatabase) {
createScans(db)
}
@@ -66,6 +80,9 @@ class Database {
oldVersion: Int,
newVersion: Int
) {
if (oldVersion < 2) {
addRawColumn(db)
}
}
}
@@ -74,6 +91,7 @@ class Database {
const val SCANS_ID = "_id"
const val SCANS_DATETIME = "_datetime"
const val SCANS_CONTENT = "content"
const val SCANS_RAW = "raw"
const val SCANS_FORMAT = "format"
private fun createScans(db: SQLiteDatabase) {
@@ -83,9 +101,14 @@ class Database {
$SCANS_ID INTEGER PRIMARY KEY AUTOINCREMENT,
$SCANS_DATETIME DATETIME NOT NULL,
$SCANS_CONTENT TEXT NOT NULL,
$SCANS_RAW BLOB,
$SCANS_FORMAT TEXT NOT NULL
)"""
)
}
private fun addRawColumn(db: SQLiteDatabase) {
db.execSQL("ALTER TABLE $SCANS ADD COLUMN $SCANS_RAW BLOB")
}
}
}
@@ -2,17 +2,13 @@ package de.markusfisch.android.binaryeye.fragment
import com.google.zxing.BarcodeFormat
import de.markusfisch.android.binaryeye.app.shareFile
import de.markusfisch.android.binaryeye.zxing.Zxing
import de.markusfisch.android.binaryeye.BuildConfig
import de.markusfisch.android.binaryeye.R
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.graphics.Bitmap
import android.support.v4.app.Fragment
import android.support.v4.content.FileProvider
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -41,30 +37,38 @@ class BarcodeFragment : Fragment() {
false
)
val args = getArguments()
val args = arguments
args?.let {
val size = args.getInt(SIZE)
val bitmap: Bitmap?
try {
bitmap = Zxing.encodeAsBitmap(
args.getString(CONTENT),
args.getSerializable(FORMAT) as BarcodeFormat,
size,
size
)
} catch (e: Exception) {
Toast.makeText(
activity,
e.message,
Toast.LENGTH_SHORT
).show()
fragmentManager.popBackStack()
return null
}
view.findViewById<ImageView>(R.id.barcode).setImageBitmap(bitmap)
view.findViewById<View>(R.id.share).setOnClickListener { _ ->
bitmap?.let {
share(bitmap)
val content = args.getString(CONTENT)
val format = args.getSerializable(FORMAT) as BarcodeFormat?
if (content != null && format != null) {
val size = args.getInt(SIZE)
val bitmap: Bitmap?
try {
bitmap = Zxing.encodeAsBitmap(
content,
format,
size,
size
)
} catch (e: Exception) {
var message = e.message
if (message == null || message.isEmpty()) {
message = getString(R.string.error_encoding_barcode)
}
Toast.makeText(
activity,
message,
Toast.LENGTH_SHORT
).show()
fragmentManager.popBackStack()
return null
}
view.findViewById<ImageView>(R.id.barcode).setImageBitmap(bitmap)
view.findViewById<View>(R.id.share).setOnClickListener {
bitmap?.let {
share(bitmap)
}
}
}
}
@@ -77,40 +81,24 @@ class BarcodeFragment : Fragment() {
val file = saveBitmap(bitmap)
GlobalScope.launch(Main) {
file?.let {
val intent = Intent(Intent.ACTION_SEND)
intent.putExtra(Intent.EXTRA_STREAM, getUriForFile(file))
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.setType("image/png")
startActivity(intent)
shareFile(context, file, "image/png")
}
}
}
}
private fun saveBitmap(bitmap: Bitmap): File? {
try {
return try {
val file = File(
context.getExternalCacheDir(),
context.externalCacheDir,
"shared_barcode.png"
)
val fos = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos)
fos.close()
return file
file
} catch (e: IOException) {
return null
}
}
private fun getUriForFile(file: File): Uri {
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
Uri.fromFile(file)
} else {
FileProvider.getUriForFile(
context,
BuildConfig.APPLICATION_ID + ".provider",
file
)
null
}
}
@@ -129,7 +117,7 @@ class BarcodeFragment : Fragment() {
args.putSerializable(FORMAT, format)
args.putInt(SIZE, size)
val fragment = BarcodeFragment()
fragment.setArguments(args)
fragment.arguments = args
return fragment
}
}
@@ -3,14 +3,22 @@ package de.markusfisch.android.binaryeye.fragment
import com.google.zxing.BarcodeFormat
import de.markusfisch.android.binaryeye.app.addFragment
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.R
import android.app.AlertDialog
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
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
@@ -18,12 +26,22 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import java.io.File
import java.io.IOException
import java.net.URLEncoder
class DecodeFragment : Fragment() {
private lateinit var contentView: EditText
private lateinit var formatView: TextView
private lateinit var hexView: TextView
private lateinit var format: BarcodeFormat
private var isBinary = false
override fun onCreate(state: Bundle?) {
super.onCreate(state)
setHasOptionsMenu(true)
@@ -43,20 +61,73 @@ class DecodeFragment : Fragment() {
)
val content = arguments?.getString(CONTENT) ?: ""
isBinary = hasNonPrintableCharacters(content) or content.isEmpty()
val raw = arguments?.getByteArray(RAW) ?: content.toByteArray()
format = arguments?.getSerializable(FORMAT) as BarcodeFormat? ?: BarcodeFormat.QR_CODE
contentView = view.findViewById<EditText>(R.id.content)
contentView.setText(content)
contentView = view.findViewById(R.id.content) as EditText
val shareFab = view.findViewById<View>(R.id.share)
view.findViewById<View>(R.id.share).setOnClickListener { _ ->
share(getContent())
if (!isBinary) {
contentView.setText(content)
contentView.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
updateFormatAndHex(getContent().toByteArray())
}
override fun beforeTextChanged(
s: CharSequence?,
start: Int,
count: Int,
after: Int
) {
}
override fun onTextChanged(
s: CharSequence?,
start: Int,
before: Int,
count: Int
) {
}
})
shareFab.setOnClickListener { v ->
shareText(v.context, getContent())
}
} else {
contentView.setText(R.string.binary_data)
contentView.isEnabled = false
(shareFab as ImageView).setImageResource(R.drawable.ic_action_save)
shareFab.setOnClickListener {
askForFileNameAndSave(raw)
}
}
formatView = view.findViewById(R.id.format)
hexView = view.findViewById(R.id.hex)
updateFormatAndHex(raw)
return view
}
private fun updateFormatAndHex(bytes: ByteArray) {
formatView.text = resources.getQuantityString(
R.plurals.barcode_info,
bytes.size,
format.toString(),
bytes.size
)
hexView.text = hexDump(bytes, 33)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.fragment_decode, menu)
if (isBinary) {
menu.findItem(R.id.copy_to_clipboard).isVisible = false
menu.findItem(R.id.open_url).isVisible = false
menu.findItem(R.id.create).isVisible = false
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
@@ -81,7 +152,7 @@ class DecodeFragment : Fragment() {
}
private fun getContent(): String {
return contentView.getText().toString()
return contentView.text.toString()
}
private fun copyToClipboard(text: String) {
@@ -90,7 +161,7 @@ class DecodeFragment : Fragment() {
val cm = activity.getSystemService(
Context.CLIPBOARD_SERVICE
) as ClipboardManager
cm.setText(text)
cm.text = text
Toast.makeText(
activity,
R.string.put_into_clipboard,
@@ -98,16 +169,25 @@ class DecodeFragment : Fragment() {
).show()
}
private fun openUrl(url: String) {
private fun openUrl(url: String, googleIfNoUrl: Boolean = true) {
if (activity == null || url.isEmpty()) {
return
}
val intent = Intent(
Intent.ACTION_VIEW,
Uri.parse(url).normalizeScheme()
)
if (intent.resolveActivity(activity.getPackageManager()) != null) {
var uri = Uri.parse(url)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
uri = uri.normalizeScheme()
}
val intent = Intent(Intent.ACTION_VIEW, uri)
if (intent.resolveActivity(activity.packageManager) != null) {
startActivity(intent)
} else if (googleIfNoUrl) {
openUrl(
"https://www.google.com/search?q=" + URLEncoder.encode(
url,
"utf-8"
),
false
)
} else {
Toast.makeText(
activity,
@@ -117,24 +197,105 @@ class DecodeFragment : Fragment() {
}
}
private fun share(text: String) {
val intent = Intent(Intent.ACTION_SEND)
intent.putExtra(Intent.EXTRA_TEXT, text)
intent.setType("text/plain")
startActivity(intent)
private fun askForFileNameAndSave(raw: ByteArray) {
val ac = activity
ac ?: return
val view = ac.layoutInflater.inflate(R.layout.dialog_save_file, null)
val editText = view.findViewById(R.id.file_name) as EditText
AlertDialog.Builder(ac)
.setView(view)
.setPositiveButton(android.R.string.ok) { _, _ ->
if (hasWritePermission(ac)) {
val messageId = saveByteArray(
editText.text.toString(),
raw
)
if (messageId > 0) {
Toast.makeText(ac, messageId, Toast.LENGTH_SHORT).show()
}
}
}
.show()
}
companion object {
private const val CONTENT = "content"
private const val FORMAT = "format"
private const val RAW = "raw"
fun newInstance(content: String, format: BarcodeFormat): Fragment {
fun newInstance(
content: String,
format: BarcodeFormat,
raw: ByteArray? = null
): Fragment {
val args = Bundle()
args.putString(CONTENT, content)
args.putSerializable(FORMAT, format)
if (raw != null) {
args.putByteArray(RAW, raw)
}
val fragment = DecodeFragment()
fragment.arguments = args
return fragment
}
}
}
private fun hexDump(bytes: ByteArray, charsPerLine: Int): String {
if (charsPerLine < 4 || bytes.isEmpty()) {
return ""
}
val dump = StringBuilder()
val hex = StringBuilder()
val ascii = StringBuilder()
val itemsPerLine = (charsPerLine - 1) / 4
val len = bytes.size
var i = 0
while (true) {
val ord = bytes[i]
hex.append(String.format("%02X ", ord))
ascii.append(if (ord > 31) ord.toChar() else " ")
++i
val posInLine = i % itemsPerLine
val atEnd = i >= len
var atLineEnd = posInLine == 0
if (atEnd && !atLineEnd) {
for (j in posInLine until itemsPerLine) {
hex.append(" ")
}
atLineEnd = true
}
if (atLineEnd) {
dump.append(hex.toString())
dump.append(" ")
dump.append(ascii.toString())
dump.append("\n")
hex.setLength(0)
ascii.setLength(0)
}
if (atEnd) {
break
}
}
return dump.toString()
}
fun saveByteArray(name: String, raw: ByteArray): Int {
return try {
val file = File(
Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS
),
name
)
if (file.exists()) {
return R.string.error_file_exists
}
file.writeBytes(raw)
0
} catch (e: IOException) {
R.string.error_saving_binary_data
}
}
@@ -50,22 +50,20 @@ class EncodeFragment : Fragment() {
false
)
formatView = view.findViewById<Spinner>(R.id.format)
formatView.setAdapter(
ArrayAdapter<String>(
activity,
android.R.layout.simple_list_item_1,
writers.map { it -> it.name }
)
formatView = view.findViewById(R.id.format)
formatView.adapter = ArrayAdapter<String>(
activity,
android.R.layout.simple_list_item_1,
writers.map { it.name }
)
sizeView = view.findViewById<TextView>(R.id.size_display)
sizeBarView = view.findViewById<SeekBar>(R.id.size_bar)
sizeView = view.findViewById(R.id.size_display)
sizeBarView = view.findViewById(R.id.size_bar)
initSizeBar()
val contentView = view.findViewById<EditText>(R.id.content)
val args = getArguments()
val args = arguments
args?.let {
contentView.setText(args.getString(CONTENT))
formatView.setSelection(
@@ -76,9 +74,9 @@ class EncodeFragment : Fragment() {
}
view.findViewById<View>(R.id.encode).setOnClickListener { v ->
val format = writers.get(formatView.getSelectedItemPosition())
var size = getSize(sizeBarView.getProgress())
val content = contentView.getText().toString()
val format = writers[formatView.selectedItemPosition]
val size = getSize(sizeBarView.progress)
val content = contentView.text.toString()
if (content.isEmpty()) {
Toast.makeText(
v.context,
@@ -101,7 +99,7 @@ class EncodeFragment : Fragment() {
}
private fun initSizeBar() {
updateSize(sizeBarView.getProgress())
updateSize(sizeBarView.progress)
sizeBarView.setOnSeekBarChangeListener(
object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(
@@ -120,7 +118,7 @@ class EncodeFragment : Fragment() {
private fun updateSize(power: Int) {
val size = getSize(power)
sizeView.setText("${size}x$size")
sizeView.text = getString(R.string.width_by_height, size, size)
}
private fun getSize(power: Int) = 128 * (power + 1)
@@ -130,7 +128,7 @@ class EncodeFragment : Fragment() {
Context.INPUT_METHOD_SERVICE
) as InputMethodManager?
im?.let {
im.hideSoftInputFromWindow(view.getWindowToken(), 0)
im.hideSoftInputFromWindow(view.windowToken, 0)
}
}
@@ -146,7 +144,7 @@ class EncodeFragment : Fragment() {
args.putString(CONTENT, content)
args.putSerializable(FORMAT, format)
val fragment = EncodeFragment()
fragment.setArguments(args)
fragment.arguments = args
return fragment
}
}
@@ -3,11 +3,11 @@ package de.markusfisch.android.binaryeye.fragment
import com.google.zxing.BarcodeFormat
import de.markusfisch.android.binaryeye.adapter.ScansAdapter
import de.markusfisch.android.binaryeye.app.db
import de.markusfisch.android.binaryeye.app.addFragment
import de.markusfisch.android.binaryeye.app.db
import de.markusfisch.android.binaryeye.app.prefs
import de.markusfisch.android.binaryeye.app.shareText
import de.markusfisch.android.binaryeye.data.Database
import de.markusfisch.android.binaryeye.fragment.DecodeFragment
import de.markusfisch.android.binaryeye.R
import android.app.AlertDialog
@@ -16,6 +16,9 @@ import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.SwitchCompat
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.ListView
@@ -27,6 +30,12 @@ import kotlinx.coroutines.launch
class HistoryFragment : Fragment() {
private lateinit var listView: ListView
private lateinit var fab: View
private lateinit var progressView: View
override fun onCreate(state: Bundle?) {
super.onCreate(state)
setHasOptionsMenu(true)
}
override fun onCreateView(
inflater: LayoutInflater,
@@ -56,11 +65,13 @@ class HistoryFragment : Fragment() {
true
}
fab = view.findViewById(R.id.clear)
fab = view.findViewById(R.id.share)
fab.setOnClickListener { v ->
askToRemoveAllScans(v.context)
pickListSeparatorAndShare(v.context)
}
progressView = view.findViewById(R.id.progress_view)
return view
}
@@ -74,6 +85,20 @@ class HistoryFragment : Fragment() {
update(context)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.fragment_history, menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.clear -> {
askToRemoveAllScans(context)
true
}
else -> super.onOptionsItemSelected(item)
}
}
private fun initHistorySwitch(switchView: SwitchCompat) {
switchView.setOnCheckedChangeListener { _, isChecked ->
prefs.useHistory = isChecked
@@ -87,17 +112,19 @@ class HistoryFragment : Fragment() {
GlobalScope.launch {
val cursor = db.getScans()
GlobalScope.launch(Main) {
fab.visibility = if (cursor != null && cursor.getCount() > 0) {
fab.visibility = if (cursor != null && cursor.count > 0) {
View.VISIBLE
} else {
View.GONE
}
if (listView.adapter == null) {
listView.adapter = ScansAdapter(context, cursor)
} else {
val adapter = listView.adapter as ScansAdapter
adapter.changeCursor(cursor)
adapter.notifyDataSetChanged()
cursor?.let {
if (listView.adapter == null) {
listView.adapter = ScansAdapter(context, cursor)
} else {
val adapter = listView.adapter as ScansAdapter
adapter.changeCursor(cursor)
adapter.notifyDataSetChanged()
}
}
}
}
@@ -107,13 +134,15 @@ class HistoryFragment : Fragment() {
val cursor = db.getScan(id)
if (cursor != null && cursor.moveToFirst()) {
val contentIndex = cursor.getColumnIndex(Database.SCANS_CONTENT)
val rawIndex = cursor.getColumnIndex(Database.SCANS_RAW)
val formatIndex = cursor.getColumnIndex(Database.SCANS_FORMAT)
try {
addFragment(
fragmentManager,
DecodeFragment.newInstance(
cursor.getString(contentIndex),
BarcodeFormat.valueOf(cursor.getString(formatIndex))
BarcodeFormat.valueOf(cursor.getString(formatIndex)),
cursor.getBlob(rawIndex)
)
)
} catch (e: IllegalArgumentException) {
@@ -146,4 +175,46 @@ class HistoryFragment : Fragment() {
}
.show()
}
private fun pickListSeparatorAndShare(context: Context) {
val separators = context.resources.getStringArray(
R.array.list_separators_values
)
AlertDialog.Builder(context)
.setTitle(R.string.pick_list_separator)
.setItems(R.array.list_separators_names) { _, which ->
shareScans(separators[which])
}
.show()
}
private fun shareScans(separator: String) {
if (progressView.visibility == View.VISIBLE) {
return
}
progressView.visibility = View.VISIBLE
GlobalScope.launch {
val cursor = db.getScans()
cursor?.let {
val sb = StringBuilder()
if (cursor.moveToFirst()) {
val contentIndex = cursor.getColumnIndex(
Database.SCANS_CONTENT
)
do {
sb.append(cursor.getString(contentIndex))
sb.append(separator)
} while (cursor.moveToNext())
}
cursor.close()
val text = sb.toString()
if (text.isNotEmpty()) {
GlobalScope.launch(Main) {
progressView.visibility = View.GONE
shareText(context, text)
}
}
}
}
}
}
@@ -13,7 +13,7 @@ class Preprocessor(
context: Context,
width: Int,
height: Int,
val orientation: Int
private val orientation: Int
) {
var outWidth = 0
var outHeight = 0
@@ -42,7 +42,7 @@ class Preprocessor(
Allocation.USAGE_SCRIPT
)
var f = .75f
val f = .75f
var w = Math.round(width * f)
var h = Math.round(height * f)
@@ -50,7 +50,7 @@ class Zxing {
)
val hints = EnumMap<DecodeHintType, Any>(DecodeHintType::class.java)
hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats)
hints[DecodeHintType.POSSIBLE_FORMATS] = decodeFormats
multiFormatReader.setHints(hints)
}
@@ -71,6 +71,42 @@ class Zxing {
height,
false
)
return decodeLuminanceSource(source, invert)
}
fun decodePositiveNegative(bitmap: Bitmap): Result? {
val result = decode(bitmap, false)
if (result != null) {
return result
}
return decode(bitmap, true)
}
fun decode(bitmap: Bitmap, invert: Boolean = false): Result? {
val pixels = IntArray(bitmap.width * bitmap.height)
return decode(pixels, bitmap, invert)
}
fun decode(
pixels: IntArray,
bitmap: Bitmap,
invert: Boolean = false
): Result? {
val width = bitmap.width
val height = bitmap.height
if (bitmap.config != Bitmap.Config.ARGB_8888) {
bitmap.copy(Bitmap.Config.ARGB_8888, true)
} else {
bitmap
}.getPixels(pixels, 0, width, 0, 0, width, height)
val source = RGBLuminanceSource(width, height, pixels)
return decodeLuminanceSource(source, invert)
}
private fun decodeLuminanceSource(
source: LuminanceSource,
invert: Boolean
): Result? {
return decodeLuminanceSource(
if (invert) {
source.invert()
@@ -80,23 +116,6 @@ class Zxing {
)
}
fun decode(bitmap: Bitmap): Result? {
val width = bitmap.getWidth()
val height = bitmap.getHeight()
val pixels = IntArray(width * height)
if (bitmap.getConfig() != Bitmap.Config.ARGB_8888) {
bitmap.copy(Bitmap.Config.ARGB_8888, true)
} else {
bitmap
}.getPixels(pixels, 0, width, 0, 0, width, height)
val source = RGBLuminanceSource(width, height, pixels)
val result = decodeLuminanceSource(source)
if (result != null) {
return result
}
return decodeLuminanceSource(source.invert())
}
private fun decodeLuminanceSource(source: LuminanceSource): Result? {
val bitmap = BinaryBitmap(HybridBinarizer(source))
return try {
@@ -119,19 +138,23 @@ class Zxing {
height: Int
): Bitmap? {
val result = MultiFormatWriter().encode(
text, format,
width, height, null
text,
format,
width,
height,
null
)
val w = result.getWidth()
val h = result.getHeight()
val w = result.width
val h = result.height
val pixels = IntArray(w * h)
var offset = 0
for (y in 0..h - 1) {
for (x in 0..w - 1) {
pixels[offset + x] = if (result.get(x, y))
for (y in 0 until h) {
for (x in 0 until w) {
pixels[offset + x] = if (result.get(x, y)) {
BLACK
else
} else {
WHITE
}
}
offset += w
}
@@ -1,7 +0,0 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="line"
android:gravity="center">
<stroke
android:width="1dp"
android:color="@color/accent"/>
</shape>
@@ -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="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z"/>
</vector>
+2 -2
View File
@@ -10,8 +10,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:fitsSystemWindows="true"
android:background="@drawable/background_scanner">
android:fitsSystemWindows="true">
<include layout="@layout/toolbar"/>
<SeekBar
android:id="@+id/zoom"
@@ -33,6 +32,7 @@
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>
@@ -0,0 +1,18 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="@string/save_as_file_name"/>
<EditText
android:id="@+id/file_name"
android:inputType="text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/file_name" />
</LinearLayout>
+1 -1
View File
@@ -5,7 +5,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/barcode"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -19,6 +18,7 @@
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>
+44 -9
View File
@@ -4,16 +4,50 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/content"
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:gravity="start|top"
android:imeOptions="flagNoExtractUi"
android:inputType="textMultiLine"
android:typeface="monospace"
android:hint="@string/content"/>
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
tools:targetApi="o"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:gravity="start|top"
android:imeOptions="flagNoExtractUi"
android:inputType="textMultiLine"
android:typeface="monospace"
android:hint="@string/content"
android:importantForAutofill="no"/>
<TextView
android:id="@+id/format"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
android:gravity="end|top"
android:textSize="14sp"/>
<TextView
android:id="@+id/hex"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/format"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
android:typeface="monospace"
android:textSize="12sp"/>
</RelativeLayout>
</ScrollView>
<android.support.design.widget.FloatingActionButton
tools:ignore="UnusedAttribute"
android:id="@+id/share"
@@ -23,6 +57,7 @@
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>
+3 -1
View File
@@ -43,6 +43,7 @@
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"
@@ -51,7 +52,8 @@
android:typeface="monospace"
android:imeOptions="flagNoExtractUi"
android:inputType="textMultiLine"
android:hint="@string/input_content_here"/>
android:hint="@string/input_content_here"
android:importantForAutofill="no"/>
<Button
style="@style/Button"
android:id="@+id/encode"
+5 -3
View File
@@ -9,7 +9,7 @@
android:choiceMode="singleChoice"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@android:color/transparent"
android:divider="@color/separator"
android:dividerHeight="1px"/>
<android.support.v7.widget.SwitchCompat
android:visibility="gone"
@@ -22,13 +22,15 @@
<android.support.design.widget.FloatingActionButton
tools:ignore="UnusedAttribute"
android:visibility="gone"
android:id="@+id/clear"
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:src="@drawable/ic_action_clear"
android:contentDescription="@string/share"
android:src="@drawable/ic_action_share"
app:elevation="4dp"/>
<include layout="@layout/progress_view"/>
</RelativeLayout>
+16
View File
@@ -0,0 +1,16 @@
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/progress_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/background_progress"
android:clickable="true"
android:focusable="true"
android:visibility="gone">
<ProgressBar
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"/>
</FrameLayout>
+2
View File
@@ -15,6 +15,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="end"
android:layout_toRightOf="@+id/time"
android:layout_toEndOf="@+id/time"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"/>
@@ -2,8 +2,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:material="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/remove_scan"
android:title="@string/remove_scan"
android:icon="@drawable/ic_action_remove"
android:id="@+id/clear"
android:title="@string/clear_history"
android:icon="@drawable/ic_action_clear"
material:showAsAction="ifRoom"/>
</menu>
+17 -1
View File
@@ -5,22 +5,38 @@
<string name="compose_barcode">Barcode erstellen</string>
<string name="decode_barcode">Barcode dekodieren</string>
<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>
</plurals>
<string name="toggle_flash">Licht an/ausschalten</string>
<string name="share">Teilen</string>
<string name="copy_to_clipboard">In die Zwischenablage kopieren</string>
<string name="put_into_clipboard">In die Zwischenablage legen</string>
<string name="open_url">URL öffnen</string>
<string name="cannot_resolve_action">Keine App kann das öffnen</string>
<string name="format">Format</string>
<string name="size">Größe in Pixeln</string>
<string name="width_by_height">%1$d×%2$d</string>
<string name="input_content_here">Gib den Inhalt hier ein</string>
<string name="encode">KODIEREN</string>
<string name="error_no_content">Fehlender Inhalt</string>
<string name="error_encoding_barcode">Barcode kann nicht generiert werden</string>
<string name="view_barcode">Barcode ansehen</string>
<string name="info">Info</string>
<string name="switch_camera">Kamera wechseln</string>
<string name="history">Gespeicherte Codes</string>
<string name="use_history">Codes speichern</string>
<string name="remove_scan">Code entfernen</string>
<string name="really_remove_scan">Code wirklich entfernen?</string>
<string name="really_remove_all_scans">Alle Codes entfernen?</string>
<string name="clear_history">Gespeicherte Codes löschen</string>
<string name="no_barcode_found">Kein Barcode gefunden</string>
<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="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>
</resources>
+42
View File
@@ -0,0 +1,42 @@
<resources>
<string name="no_camera_no_fun">Cette application n\'a aucun sens sans accès possible à la caméra. Au revoir.</string>
<string name="camera_error">Impossible d\'accéder à la caméra, veuillez réessayer</string>
<string name="scan_code">Scanner le code</string>
<string name="compose_barcode">Créer un code-barres</string>
<string name="decode_barcode">Décoder le code-barres</string>
<string name="content">Contenu</string>
<string name="binary_data">(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>
</plurals>
<string name="toggle_flash">Toggle flash</string>
<string name="share">Share</string>
<string name="copy_to_clipboard">Copier dans le presse-papier</string>
<string name="put_into_clipboard">Mis dans le presse-papiers</string>
<string name="open_url">Ouvrir l\'url</string>
<string name="cannot_resolve_action">Aucune application ne peut ouvrir ça</string>
<string name="format">Format</string>
<string name="size">Taille en pixels</string>
<string name="width_by_height">%1$d×%2$d</string>
<string name="input_content_here">Entrez votre contenu ici</string>
<string name="encode">ENCODER</string>
<string name="error_no_content">Contenu manquant</string>
<string name="error_encoding_barcode">Barcode cannot be generated</string>
<string name="view_barcode">Voir le code barre</string>
<string name="info">Info</string>
<string name="switch_camera">Changer de camera</string>
<string name="history">Historique</string>
<string name="use_history">Sauvegarder l\'histoire des scans</string>
<string name="really_remove_scan">Supprimer le scan ?</string>
<string name="really_remove_all_scans">Supprimer tous les scans ?</string>
<string name="clear_history">Clear history</string>
<string name="no_barcode_found">Aucun code-barres trouvé</string>
<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="file_name">File name</string>
<string name="error_saving_binary_data">Cannot save file</string>
<string name="error_file_exists">File already exists</string>
</resources>
+23 -7
View File
@@ -3,24 +3,40 @@
<string name="camera_error">Nem lehet hozzáférni a kamerához, próbálja meg újra</string>
<string name="scan_code">Kód beolvasása</string>
<string name="compose_barcode">Vonalkód létrehozása</string>
<string name="decode_barcode">Decode barcode</string>
<string name="decode_barcode">Vonalkód visszafejtése</string>
<string name="content">Tartalom</string>
<string name="binary_data">(binary data)</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>
</plurals>
<string name="toggle_flash">Vaku be- vagy kikapcsolása</string>
<string name="share">Megosztás</string>
<string name="copy_to_clipboard">Másolás a vágólapra</string>
<string name="put_into_clipboard">Vágólapra helyezés</string>
<string name="open_url">URL megnyitása</string>
<string name="cannot_resolve_action">Nincs alkalmazás, amely meg tudná nyitni azt</string>
<string name="format">Formátum</string>
<string name="size">Méret képpontban</string>
<string name="width_by_height">%1$d×%2$d</string>
<string name="input_content_here">Itt adja meg a tartalmat</string>
<string name="encode">KÓDOLÁS</string>
<string name="error_no_content">Hiányzó tartalom</string>
<string name="error_encoding_barcode">Barcode cannot be generated</string>
<string name="view_barcode">Vonalkód megtekintése</string>
<string name="info">Információ</string>
<string name="switch_camera">Kamera átkapcsolása</string>
<string name="history">History</string>
<string name="use_history">Save scan history</string>
<string name="remove_scan">Remove scan</string>
<string name="really_remove_scan">Really remove scan?</string>
<string name="really_remove_all_scans">Really remove all scans?</string>
<string name="no_barcode_found">No barcode found</string>
<string name="history">Előzmények</string>
<string name="use_history">Beolvasási előzmények mentése</string>
<string name="really_remove_scan">Valóban eltávolítja a beolvasást?</string>
<string name="really_remove_all_scans">Valóban eltávolítja az összes beolvasását?</string>
<string name="clear_history">Előzmények törlése</string>
<string name="no_barcode_found">Nem található vonalkód</string>
<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="file_name">File name</string>
<string name="error_saving_binary_data">Cannot save file</string>
<string name="error_file_exists">File already exists</string>
</resources>
+42
View File
@@ -0,0 +1,42 @@
<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="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">(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>
</plurals>
<string name="toggle_flash">Flash</string>
<string name="share">Condividi</string>
<string name="copy_to_clipboard">Copia negli appunti</string>
<string name="put_into_clipboard">Metti negli appunti</string>
<string name="open_url">Apri URL</string>
<string name="cannot_resolve_action">Nessuna app può aprirlo</string>
<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="encode">CODIFICA</string>
<string name="error_no_content">Contenuto mancante</string>
<string name="error_encoding_barcode">Barcode cannot be generated</string>
<string name="view_barcode">Vedi codice a barre</string>
<string name="info">Info</string>
<string name="switch_camera">Cambia camera</string>
<string name="history">Cronologia</string>
<string name="use_history">Salva cronologia delle scansioni</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="separator_line_break">Interruzione di linea</string>
<string name="separator_ruler">Righello</string>
<string name="save_as_file_name">Save 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>
</resources>
+17 -1
View File
@@ -5,22 +5,38 @@
<string name="compose_barcode">Scan een barcode</string>
<string name="decode_barcode">Decode barcode</string>
<string name="content">Inhoud</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>
</plurals>
<string name="toggle_flash">Toggle flash</string>
<string name="share">Share</string>
<string name="copy_to_clipboard">Kopiëren naar klembord</string>
<string name="put_into_clipboard">Kopieer de inhoud naar het klembord</string>
<string name="open_url">URL openen</string>
<string name="cannot_resolve_action">Er is geen app aangetroffen die dit kan openen</string>
<string name="format">Formaat</string>
<string name="size">Grootte, in pixels</string>
<string name="width_by_height">%1$d×%2$d</string>
<string name="input_content_here">Voer hier de inhoud in</string>
<string name="encode">CODEREN</string>
<string name="error_no_content">Inhoud ontbreekt</string>
<string name="error_encoding_barcode">Barcode cannot be generated</string>
<string name="view_barcode">Barcode bekijken</string>
<string name="info">Informatie</string>
<string name="switch_camera">Camera wisselen</string>
<string name="history">History</string>
<string name="use_history">Save scan history</string>
<string name="remove_scan">Remove scan</string>
<string name="really_remove_scan">Really remove scan?</string>
<string name="really_remove_all_scans">Really remove all scans?</string>
<string name="clear_history">Clear history</string>
<string name="no_barcode_found">No barcode found</string>
<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="file_name">File name</string>
<string name="error_saving_binary_data">Cannot save file</string>
<string name="error_file_exists">File already exists</string>
</resources>
+2
View File
@@ -6,5 +6,7 @@
<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>
</resources>
@@ -0,0 +1,10 @@
<resources>
<string-array name="list_separators_names">
<item>@string/separator_line_break</item>
<item>@string/separator_ruler</item>
</string-array>
<string-array name="list_separators_values">
<item>\n</item>
<item>\n---\n</item>
</string-array>
</resources>
+17 -1
View File
@@ -5,22 +5,38 @@
<string name="compose_barcode">Compose barcode</string>
<string name="decode_barcode">Decode barcode</string>
<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>
</plurals>
<string name="toggle_flash">Toggle flash</string>
<string name="share">Share</string>
<string name="copy_to_clipboard">Copy to clipboard</string>
<string name="put_into_clipboard">Put into clipboard</string>
<string name="open_url">Open url</string>
<string name="cannot_resolve_action">No application can open that</string>
<string name="format">Format</string>
<string name="size">Size in pixels</string>
<string name="width_by_height">%1$d×%2$d</string>
<string name="input_content_here">Input content here</string>
<string name="encode">ENCODE</string>
<string name="error_no_content">Missing content</string>
<string name="error_encoding_barcode">Barcode cannot be generated</string>
<string name="view_barcode">View barcode</string>
<string name="info">Info</string>
<string name="switch_camera">Switch camera</string>
<string name="history">History</string>
<string name="use_history">Save scan history</string>
<string name="remove_scan">Remove scan</string>
<string name="really_remove_scan">Really remove scan?</string>
<string name="really_remove_all_scans">Really remove all scans?</string>
<string name="clear_history">Clear history</string>
<string name="no_barcode_found">No barcode found</string>
<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="file_name">File name</string>
<string name="error_saving_binary_data">Cannot save file</string>
<string name="error_file_exists">File already exists</string>
</resources>
+1 -1
View File
@@ -12,7 +12,7 @@
name="Button"
parent="@android:style/Widget.Button">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">36dp</item>
<item name="android:layout_height">48dp</item>
<item name="android:layout_gravity">center_horizontal</item>
<item name="android:background">@drawable/button_raised</item>
<item name="android:textColor">@color/button_text</item>
+1
View File
@@ -1,5 +1,6 @@
#pragma version(1)
#pragma rs java_package_name(de.markusfisch.android.binaryeye.renderscript)
#pragma rs_fp_relaxed
rs_allocation inImage;
int inWidth;
+4 -9
View File
@@ -1,22 +1,17 @@
buildscript {
ext.kotlin_version = '1.3.21'
ext.tools_version = '3.3.2'
ext.kotlin_version = '1.3.31'
ext.tools_version = '3.4.1'
ext.build_tools_version = '28.0.3'
ext.sdk_version = 28
ext.support_version = '25.3.1'
repositories {
jcenter()
google()
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:$tools_version"
classpath 'net.ltgt.gradle:gradle-errorprone-plugin:0.0.10'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
@@ -27,8 +22,8 @@ plugins {
allprojects {
repositories {
jcenter()
google()
jcenter()
maven { url 'https://jitpack.io' }
}
Binary file not shown.
+2 -2
View File
@@ -1,6 +1,6 @@
#Wed Apr 10 15:27:10 PDT 2013
#Tue May 07 14:37:45 CEST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
Vendored
+59 -51
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/usr/bin/env sh
##############################################################################
##
@@ -6,47 +6,6 @@
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
@@ -61,9 +20,49 @@ while [ -h "$PRG" ] ; do
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -90,7 +89,7 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -114,6 +113,7 @@ fi
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
@@ -154,11 +154,19 @@ if $cygwin ; then
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
APP_ARGS=$(save "$@")
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
Vendored
+4 -10
View File
@@ -8,14 +8,14 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
@@ -46,10 +46,9 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
@@ -60,11 +59,6 @@ set _SKIP=2
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
+293
View File
@@ -0,0 +1,293 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="512"
height="512"
viewBox="0 0 512 512"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="google_play_icon.svg"
inkscape:export-filename="/Users/markus/app_icon.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.80761719"
inkscape:cx="-59.743651"
inkscape:cy="256"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:showpageshadow="false"
inkscape:window-width="1680"
inkscape:window-height="1028"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
showguides="false" />
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-540.36218)">
<rect
style="opacity:1;fill:#b6d46f;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4145"
width="512"
height="512"
x="0"
y="540.36218"
rx="0"
ry="0" />
<path
inkscape:connector-curvature="0"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:0.1254902;fill-rule:nonzero;stroke:none;stroke-width:8.60725021;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 259.47183,657.46384 c -36.63695,0 -73.27309,13.80545 -101.20422,41.42278 -55.86225,55.2346 -55.68941,144.72925 0,200.13856 l 193.69605,192.72152 202.57936,4.825 L 554.16705,891.40531 360.67609,698.88662 C 332.83155,671.18195 296.10884,657.46389 259.47188,657.46384 Z"
id="path5462"
sodipodi:nodetypes="ssscccss" />
<circle
r="141.46466"
cy="799.18176"
cx="258.80164"
id="circle5467"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="circle5451"
cx="254.77118"
cy="803.87579"
r="77.382751" />
<circle
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="circle5455"
cx="307.03757"
cy="747.37183"
r="50.853714" />
<flowRoot
xml:space="preserve"
id="flowRoot3776"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
transform="matrix(5.4385115,0,0,5.4385115,-615.98708,-4631.8746)"><flowRegion
id="flowRegion3778"><rect
id="rect3780"
width="29"
height="71"
x="52"
y="60" /></flowRegion><flowPara
id="flowPara3782" /></flowRoot> <text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:217.54046631px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
x="24816.699"
y="13685.234"
id="text5084"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan5086"
x="24816.699"
y="13685.234" /></text>
<flowRoot
xml:space="preserve"
id="flowRoot3776-6"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
transform="matrix(5.4385115,0,0,5.4385115,2280.8097,-4663.4521)"><flowRegion
id="flowRegion3778-7"><rect
id="rect3780-2"
width="29"
height="71"
x="52"
y="60" /></flowRegion><flowPara
id="flowPara3782-0" /></flowRoot> <text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:217.54046631px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
x="27713.496"
y="13653.656"
id="text5084-0"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan5086-1"
x="27713.496"
y="13653.656" /></text>
<flowRoot
xml:space="preserve"
id="flowRoot3776-8"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
transform="matrix(5.4385115,0,0,5.4385115,2962.7932,-4497.8264)"><flowRegion
id="flowRegion3778-4"><rect
id="rect3780-5"
width="29"
height="71"
x="52"
y="60" /></flowRegion><flowPara
id="flowPara3782-2" /></flowRoot> <text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:217.54046631px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
x="28395.479"
y="13819.282"
id="text5084-3"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan5086-18"
x="28395.479"
y="13819.282" /></text>
<flowRoot
xml:space="preserve"
id="flowRoot3776-2"
style="font-style:normal;font-weight:normal;font-size:4.31339216px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
transform="matrix(5.4385115,0,0,5.4385115,-615.98708,-4631.8746)"><flowRegion
id="flowRegion3778-41"><rect
id="rect3780-54"
width="3.1272094"
height="7.6562715"
x="32.001694"
y="919.38837" /></flowRegion><flowPara
id="flowPara3782-28" /></flowRoot> <text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:23.45843315px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
x="2270.0874"
y="2308.2637"
id="text5084-7"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan5086-8"
x="2270.0874"
y="2308.2637" /></text>
<flowRoot
xml:space="preserve"
id="flowRoot3776-6-6"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
transform="matrix(0.83313297,0,0,0.83313363,76.628678,129.4161)"><flowRegion
id="flowRegion3778-9"><rect
id="rect3780-6"
width="29"
height="71"
x="52"
y="60" /></flowRegion><flowPara
id="flowPara3782-8" /></flowRoot> <text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:33.32533264px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
x="3972.6985"
y="2935.4377"
id="text5084-3-4"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan5086-9"
x="3972.6985"
y="2935.4377" /></text>
<flowRoot
xml:space="preserve"
id="flowRoot3776-0"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
transform="matrix(0.83313297,0,0,0.83313363,-535.58967,134.25349)"><flowRegion
id="flowRegion3778-6"><rect
id="rect3780-9"
width="29"
height="71"
x="52"
y="60" /></flowRegion><flowPara
id="flowPara3782-3" /></flowRoot> <text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:33.32533264px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
x="3360.4797"
y="2940.2754"
id="text5084-9"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan5086-96"
x="3360.4797"
y="2940.2754" /></text>
<flowRoot
xml:space="preserve"
id="flowRoot5144"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
transform="matrix(0.90032116,0,0,0.90032187,-704.04381,822.02136)"><flowRegion
id="flowRegion5146"><rect
id="rect5148"
width="29"
height="71"
x="52"
y="60" /></flowRegion><flowPara
id="flowPara5150" /></flowRoot> <text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:36.01285934px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
x="3506.2246"
y="3854.3357"
id="text5152"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan5154"
x="3506.2246"
y="3854.3357" /></text>
<flowRoot
xml:space="preserve"
id="flowRoot5388"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
transform="matrix(1.1287608,0,0,1.1287617,-518.09387,856.33891)"><flowRegion
id="flowRegion5390"><rect
id="rect5392"
width="29"
height="71"
x="52"
y="60" /></flowRegion><flowPara
id="flowPara5394" /></flowRoot> <text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:45.15045166px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
x="4760.4517"
y="4658.0464"
id="text5396"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan5398"
x="4760.4517"
y="4658.0464" /></text>
<flowRoot
xml:space="preserve"
id="flowRoot3776-2-9"
style="font-style:normal;font-weight:normal;font-size:33.32533264px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"><flowRegion
id="flowRegion3778-7-9"><rect
id="rect3780-0"
width="24.160856"
height="59.152489"
x="920.29907"
y="14.729361" /></flowRegion><flowPara
id="flowPara3782-5" /></flowRoot> <text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:33.32533264px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
x="4773.0464"
y="2770.7634"
id="text5084-7-9"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan5086-0"
x="4773.0464"
y="2770.7634" /></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB