Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e02420d446 | ||
|
|
e63fe0d601 | ||
|
|
0803fdcac8 | ||
|
|
e64dd2e972 | ||
|
|
418edd34a5 | ||
|
|
af2a1dc940 | ||
|
|
e0c420c29f | ||
|
|
0ba41ca863 | ||
|
|
ed1826679e | ||
|
|
bd75b5b71d | ||
|
|
bbdf724bbb | ||
|
|
0ace08c8b4 | ||
|
|
41164c31f1 | ||
|
|
9f3288e7a8 | ||
|
|
a61fc63fa6 | ||
|
|
26a19a9657 | ||
|
|
a0d157551a | ||
|
|
53fe8b3689 | ||
|
|
0da9e34e55 | ||
|
|
db276372b7 | ||
|
|
c148635c26 | ||
|
|
5c99309125 | ||
|
|
2443eb066a | ||
|
|
d7281266e4 | ||
|
|
e8c676ab8c | ||
|
|
6c1353596d | ||
|
|
ff14130c91 | ||
|
|
e0147df76c | ||
|
|
cbc2b6b8db | ||
|
|
bc727ab45f | ||
|
|
9e8153e8f6 | ||
|
|
b9a5687c5a | ||
|
|
cbc0ff435f |
@@ -0,0 +1,18 @@
|
||||
language: android
|
||||
android:
|
||||
components:
|
||||
- platform-tools
|
||||
- build-tools-26.0.0
|
||||
- android-26
|
||||
- extra-android-m2repository
|
||||
addons:
|
||||
sonarcloud:
|
||||
organization: "markusfisch-github"
|
||||
token:
|
||||
secure: "mTpn1JNKn/PzEFGTRvkmSfCj0c6nX8n/ef2IO0xPSFyveuM+cpNka5d4/nT1QxEqq1sLUj5HMHEShdk6XlER5Oro+DqekTSw8ONv8P24IZGjiM5noLJZ7uqv/73tM0SevROl3Ldee6AzyS7PzcextQms5UC8uyCgI2jbYwz3hpayaWPquBGRnlw7xGuYwCjJS2cMrhz8sEYRn3Egk3no1YJlxCdbIVaqtVmLweqBzLp176FWB5Svg8N3r+UzaZ3fhQn6LhlHPuObPTrCoVjy6W9TxdUsHfW3F61Zei9h1IEsCC0qsalY0OZC8ML5F2DTDp0spKHnPlXR4Q4hF9XIngk2vPVMtfJhu3JJRojjrxhtfOUZO/VS0AMpueZ5QiL1ZbHziKk9ztuRsB+OIoehWnBTq/r/igrWfqxfsq2VURfAYFldRnobBlT/svNkHBjHfZl9EqKprvwmmvw15ZD0twgsskweDn3GssHC5MGTH6+ujZI19t/SLbDMY2KVPNCrG9sHEXSqKT8wWxqGigUaIbVytD2SqGWep0qsB86FQLAXOmgr3+0upTx3B8YhMG5GGu/R45pJBwzXZbTqSx0goaxP+BBEkzcjQHNl0RvpF44m/FbPMYUBuNyHHEoEZC5G1raOV9jtI8G6whQP3OaT8lO2Aqbp1W/lTSEnCDcKYlU="
|
||||
script:
|
||||
- bash gradlew build
|
||||
- bash gradlew sonarqube
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
@@ -1,7 +1,21 @@
|
||||
# Change Log
|
||||
|
||||
## 1.3.0
|
||||
* Support tap to focus
|
||||
* Save chose zoom level permanently
|
||||
* Update ZXing to latest version (3.3.2)
|
||||
|
||||
## 1.2.0
|
||||
* Added a camera zoom slider
|
||||
* Added an unobtrusive wallpaper background
|
||||
* Replaced lock marks with just a horizontal line
|
||||
* Put toggling torch mode into Floating Action Button
|
||||
|
||||
## 1.1.1
|
||||
* Brought back inverting every second frame
|
||||
|
||||
## 1.1.0
|
||||
* Create barcodes now too
|
||||
* Create barcodes
|
||||
* Streamlined video pre-processing
|
||||
* Decoded content is editable now
|
||||
* Fixed position of lock marks on tablets
|
||||
|
||||
@@ -16,6 +16,9 @@ release: lint
|
||||
lint:
|
||||
./gradlew lintDebug
|
||||
|
||||
sonarqube:
|
||||
./gradlew sonarqube
|
||||
|
||||
infer: clean
|
||||
infer -- ./gradlew assembleDebug
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Binary Eye
|
||||
|
||||
<a href="https://travis-ci.org/markusfisch/BinaryEye"><img src="https://api.travis-ci.org/markusfisch/BinaryEye.svg" alt="Travis-ci"/></a>
|
||||
|
||||
Yet another barcode scanner for Android. As if there weren't [enough][play].
|
||||
|
||||
This one is totally free, open source and does use the [ZXing][zxing] barcode
|
||||
|
||||
@@ -14,8 +14,8 @@ android {
|
||||
minSdkVersion 9
|
||||
targetSdkVersion sdk_version
|
||||
|
||||
versionCode 3
|
||||
versionName '1.1.0'
|
||||
versionCode 6
|
||||
versionName '1.3.0'
|
||||
|
||||
renderscriptTargetApi 24
|
||||
renderscriptSupportModeEnabled true
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
tools:ignore="UnusedAttribute"
|
||||
android:allowBackup="true"
|
||||
android:fullBackupContent="true"
|
||||
android:supportsRtl="true"
|
||||
android:label="@string/app_name"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:theme="@style/AppTheme">
|
||||
@@ -30,7 +31,7 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.MainActivity"
|
||||
android:name=".activity.CameraActivity"
|
||||
android:label="@string/scan_code">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND"/>
|
||||
@@ -47,5 +48,8 @@
|
||||
android:scheme="http"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.MainActivity"
|
||||
android:label="@string/app_name"/>
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -0,0 +1,351 @@
|
||||
package de.markusfisch.android.binaryeye.activity
|
||||
|
||||
import com.google.zxing.Result
|
||||
|
||||
import de.markusfisch.android.cameraview.widget.CameraView
|
||||
|
||||
import de.markusfisch.android.binaryeye.app.initSystemBars
|
||||
import de.markusfisch.android.binaryeye.rs.YuvToGray
|
||||
import de.markusfisch.android.binaryeye.zxing.Zxing
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.SharedPreferences
|
||||
import android.hardware.Camera
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Vibrator
|
||||
import android.preference.PreferenceManager
|
||||
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
|
||||
import android.view.MenuItem
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.widget.SeekBar
|
||||
import android.widget.Toast
|
||||
|
||||
class CameraActivity : AppCompatActivity() {
|
||||
companion object {
|
||||
private val REQUEST_CAMERA = 1
|
||||
private val ZOOM_MAX = "zoom_max"
|
||||
private val ZOOM_LEVEL = "zoom_level"
|
||||
}
|
||||
|
||||
private val zxing = Zxing()
|
||||
private val decodingRunnable = Runnable {
|
||||
while (decoding) {
|
||||
val result = decodeFrame()
|
||||
if (result != null) {
|
||||
cameraView.post { found(result) }
|
||||
decoding = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var preferences: SharedPreferences
|
||||
private lateinit var vibrator: Vibrator
|
||||
private lateinit var cameraView: CameraView
|
||||
private lateinit var zoomBar: SeekBar
|
||||
private lateinit var yuvToGray: YuvToGray
|
||||
|
||||
private var decoding = false
|
||||
private var decodingThread: Thread? = null
|
||||
private var frameData: ByteArray? = null
|
||||
private var frameWidth: Int = 0
|
||||
private var frameHeight: Int = 0
|
||||
private var frameOrientation: Int = 0
|
||||
private var odd = false
|
||||
private var flash = false
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<String>,
|
||||
grantResults: IntArray) {
|
||||
when (requestCode) {
|
||||
REQUEST_CAMERA -> if (grantResults.size > 0 &&
|
||||
grantResults[0] != PackageManager.PERMISSION_GRANTED) {
|
||||
Toast.makeText(this, R.string.no_camera_no_fun,
|
||||
Toast.LENGTH_SHORT).show()
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(state: Bundle?) {
|
||||
super.onCreate(state)
|
||||
setContentView(R.layout.activity_camera)
|
||||
|
||||
initSystemBars(this)
|
||||
setSupportActionBar(findViewById(R.id.toolbar) as Toolbar)
|
||||
|
||||
preferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
||||
yuvToGray = YuvToGray(this)
|
||||
|
||||
cameraView = findViewById(R.id.camera_view) as CameraView
|
||||
zoomBar = findViewById(R.id.zoom) as SeekBar
|
||||
|
||||
initCameraView()
|
||||
setTapToFocus()
|
||||
initZoomBar()
|
||||
restoreZoom()
|
||||
initFlashFab(findViewById(R.id.flash))
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
yuvToGray.destroy()
|
||||
saveZoom()
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
handleSendText(intent)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
System.gc()
|
||||
if (hasCameraPermission()) {
|
||||
cameraView.openAsync(CameraView.findCameraId(
|
||||
Camera.CameraInfo.CAMERA_FACING_BACK))
|
||||
startDecoding()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
cancelDecoding()
|
||||
cameraView.close()
|
||||
}
|
||||
|
||||
override fun onRestoreInstanceState(savedState: Bundle) {
|
||||
super.onRestoreInstanceState(savedState)
|
||||
zoomBar.max = savedState.getInt(ZOOM_MAX)
|
||||
zoomBar.progress = savedState.getInt(ZOOM_LEVEL)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
outState.putInt(ZOOM_MAX, zoomBar.max)
|
||||
outState.putInt(ZOOM_LEVEL, zoomBar.progress)
|
||||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.activity_camera, menu)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.create -> {
|
||||
startActivity(MainActivity.getEncodeIntent(this))
|
||||
true
|
||||
}
|
||||
R.id.info -> {
|
||||
openReadme()
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
private fun openReadme() {
|
||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(
|
||||
"https://github.com/markusfisch/BinaryEye/blob/master/README.md")))
|
||||
}
|
||||
|
||||
private fun handleSendText(intent: Intent) {
|
||||
if (!Intent.ACTION_SEND.equals(intent.getAction()) ||
|
||||
!"text/plain".equals(intent.getType())) {
|
||||
return
|
||||
}
|
||||
|
||||
var text = intent.getStringExtra(Intent.EXTRA_TEXT)
|
||||
if (text == null || text.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
// consume this intent
|
||||
intent.setAction(null)
|
||||
|
||||
startActivity(MainActivity.getEncodeIntent(this, text))
|
||||
}
|
||||
|
||||
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.setOnCameraListener(object : CameraView.OnCameraListener {
|
||||
override fun onConfigureParameters(
|
||||
parameters: Camera.Parameters) {
|
||||
if (parameters.isZoomSupported()) {
|
||||
val max = parameters.getMaxZoom()
|
||||
if (zoomBar.max != max) {
|
||||
zoomBar.max = max
|
||||
zoomBar.progress = max / 3
|
||||
saveZoom()
|
||||
}
|
||||
parameters.setZoom(zoomBar.progress)
|
||||
} else {
|
||||
zoomBar.visibility = View.GONE
|
||||
}
|
||||
for (mode in parameters.getSupportedSceneModes()) {
|
||||
if (mode.equals(Camera.Parameters.SCENE_MODE_BARCODE)) {
|
||||
parameters.setSceneMode(mode)
|
||||
break
|
||||
}
|
||||
}
|
||||
CameraView.setAutoFocus(parameters)
|
||||
}
|
||||
|
||||
override fun onCameraError() {
|
||||
Toast.makeText(this@CameraActivity, R.string.camera_error,
|
||||
Toast.LENGTH_SHORT).show()
|
||||
finish()
|
||||
}
|
||||
|
||||
override fun onCameraStarted(camera: Camera) {
|
||||
frameWidth = cameraView.frameWidth
|
||||
frameHeight = cameraView.frameHeight
|
||||
frameOrientation = cameraView.frameOrientation
|
||||
camera.setPreviewCallback { data, _ -> frameData = data }
|
||||
}
|
||||
|
||||
override fun onCameraStopping(camera: Camera) {
|
||||
camera.setPreviewCallback(null)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setTapToFocus() {
|
||||
val runnable = Runnable {
|
||||
cameraView.setFocusArea(null)
|
||||
}
|
||||
cameraView.setOnTouchListener({ v: View, event: MotionEvent ->
|
||||
val camera = cameraView.getCamera()
|
||||
camera?.cancelAutoFocus()
|
||||
cameraView.setFocusArea(cameraView.calculateFocusRect(
|
||||
event.getX(), event.getY(), 100))
|
||||
camera?.autoFocus({ _: Boolean, _: Camera ->
|
||||
cameraView.removeCallbacks(runnable)
|
||||
cameraView.postDelayed(runnable, 3000)
|
||||
})
|
||||
v.performClick()
|
||||
true
|
||||
})
|
||||
}
|
||||
|
||||
private fun initZoomBar() {
|
||||
zoomBar.setOnSeekBarChangeListener(object :
|
||||
SeekBar.OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int,
|
||||
fromUser: Boolean) {
|
||||
setZoom(progress)
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setZoom(zoom: Int) {
|
||||
val camera: Camera? = cameraView.getCamera()
|
||||
camera?.let {
|
||||
try {
|
||||
val params = camera.getParameters()
|
||||
params.setZoom(zoom)
|
||||
camera.setParameters(params)
|
||||
} catch (e: RuntimeException) {
|
||||
// ignore; there's nothing we can do
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveZoom() {
|
||||
val editor = preferences.edit()
|
||||
editor.putInt(ZOOM_MAX, zoomBar.max)
|
||||
editor.putInt(ZOOM_LEVEL, zoomBar.progress)
|
||||
editor.apply()
|
||||
}
|
||||
|
||||
private fun restoreZoom() {
|
||||
zoomBar.max = preferences.getInt(ZOOM_MAX, zoomBar.max)
|
||||
zoomBar.progress = preferences.getInt(ZOOM_LEVEL, zoomBar.progress)
|
||||
}
|
||||
|
||||
private fun initFlashFab(fab: View) {
|
||||
if (!packageManager.hasSystemFeature(
|
||||
PackageManager.FEATURE_CAMERA_FLASH)) {
|
||||
fab.visibility = View.GONE
|
||||
} else {
|
||||
fab.setOnClickListener { _ ->
|
||||
toggleTorchMode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
flash = flash xor true
|
||||
parameters?.let { camera.setParameters(parameters) }
|
||||
}
|
||||
|
||||
private fun startDecoding() {
|
||||
frameData = null
|
||||
decoding = true
|
||||
decodingThread = Thread(decodingRunnable)
|
||||
decodingThread?.start()
|
||||
}
|
||||
|
||||
private fun cancelDecoding() {
|
||||
decoding = false
|
||||
if (decodingThread != null) {
|
||||
var retry = 100
|
||||
while (retry-- > 0) {
|
||||
try {
|
||||
decodingThread?.join()
|
||||
break
|
||||
} catch (e: InterruptedException) {
|
||||
decodingThread?.interrupt()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun decodeFrame(): Result? {
|
||||
frameData ?: return null
|
||||
return zxing.decodeBitmap(yuvToGray.convert(
|
||||
frameData!!, frameWidth, frameHeight, frameOrientation))
|
||||
}
|
||||
|
||||
private fun found(result: Result) {
|
||||
cancelDecoding()
|
||||
vibrator.vibrate(100)
|
||||
|
||||
startActivity(MainActivity.getDecodeIntent(this, result.text,
|
||||
result.getBarcodeFormat()))
|
||||
}
|
||||
}
|
||||
@@ -1,43 +1,38 @@
|
||||
package de.markusfisch.android.binaryeye.activity
|
||||
|
||||
import de.markusfisch.android.cameraview.widget.CameraView
|
||||
import com.google.zxing.BarcodeFormat
|
||||
|
||||
import de.markusfisch.android.binaryeye.app.addFragment
|
||||
import de.markusfisch.android.binaryeye.app.initSystemBars
|
||||
import de.markusfisch.android.binaryeye.app.setFragment
|
||||
import de.markusfisch.android.binaryeye.fragment.CameraFragment
|
||||
import de.markusfisch.android.binaryeye.fragment.DecodeFragment
|
||||
import de.markusfisch.android.binaryeye.fragment.EncodeFragment
|
||||
import de.markusfisch.android.binaryeye.view.SystemBarMetrics
|
||||
import de.markusfisch.android.binaryeye.widget.LockOnView
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.ActivityCompat
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.support.v4.app.Fragment
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.widget.Toolbar
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import android.widget.Toast
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
lateinit var cameraView: CameraView
|
||||
lateinit var lockOnView: LockOnView
|
||||
companion object {
|
||||
private val ENCODE = "encode"
|
||||
private val DECODE = "decode"
|
||||
private val DECODE_FORMAT = "decode_format"
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<String>,
|
||||
grantResults: IntArray) {
|
||||
when (requestCode) {
|
||||
REQUEST_CAMERA -> if (grantResults.size > 0 &&
|
||||
grantResults[0] != PackageManager.PERMISSION_GRANTED) {
|
||||
Toast.makeText(this,
|
||||
R.string.no_camera_no_fun,
|
||||
Toast.LENGTH_SHORT).show()
|
||||
finish()
|
||||
}
|
||||
fun getEncodeIntent(context: Context, text: String? = ""): Intent {
|
||||
val intent = Intent(context, MainActivity::class.java)
|
||||
intent.putExtra(ENCODE, text)
|
||||
return intent
|
||||
}
|
||||
|
||||
fun getDecodeIntent(context: Context, text: String,
|
||||
format: BarcodeFormat): Intent {
|
||||
val intent = Intent(context, MainActivity::class.java)
|
||||
intent.putExtra(DECODE, text)
|
||||
intent.putExtra(DECODE_FORMAT, format)
|
||||
return intent
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,90 +51,24 @@ class MainActivity : AppCompatActivity() {
|
||||
setContentView(R.layout.activity_main)
|
||||
initSystemBars(this)
|
||||
|
||||
cameraView = findViewById(R.id.camera_view) as CameraView
|
||||
lockOnView = findViewById(R.id.lock_on) as LockOnView
|
||||
|
||||
setSupportActionBar(findViewById(R.id.toolbar) as Toolbar)
|
||||
setUpListener()
|
||||
checkPermissions()
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
|
||||
if (state == null) {
|
||||
setFragment(supportFragmentManager, CameraFragment())
|
||||
handleSendText(intent)
|
||||
}
|
||||
}
|
||||
|
||||
override protected fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
handleSendText(intent)
|
||||
}
|
||||
|
||||
private fun checkPermissions() {
|
||||
val permission = android.Manifest.permission.CAMERA
|
||||
|
||||
if (ContextCompat.checkSelfPermission(this, permission) !=
|
||||
PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(this, arrayOf(permission),
|
||||
REQUEST_CAMERA)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpListener() {
|
||||
supportFragmentManager.addOnBackStackChangedListener { canBack() }
|
||||
canBack()
|
||||
}
|
||||
|
||||
private fun canBack() {
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(
|
||||
supportFragmentManager.backStackEntryCount > 0)
|
||||
}
|
||||
|
||||
private fun handleSendText(intent: Intent) {
|
||||
val type = intent.getType()
|
||||
var text = intent.getStringExtra(Intent.EXTRA_TEXT)
|
||||
if (!Intent.ACTION_SEND.equals(intent.getAction()) ||
|
||||
!"text/plain".equals(type) ||
|
||||
text.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
// consume this intent
|
||||
intent.setAction(null)
|
||||
|
||||
addFragment(supportFragmentManager,
|
||||
EncodeFragment.newInstance(text))
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val REQUEST_CAMERA = 1
|
||||
|
||||
private fun initSystemBars(activity: AppCompatActivity?) {
|
||||
val view = activity?.findViewById(R.id.main_layout)
|
||||
if (view != null && setSystemBarColor(activity.window,
|
||||
ContextCompat.getColor(activity,
|
||||
R.color.primary_dark_translucent))) {
|
||||
view.setPadding(
|
||||
0,
|
||||
SystemBarMetrics.getStatusBarHeight(
|
||||
activity.resources),
|
||||
0,
|
||||
0)
|
||||
val intent = getIntent()
|
||||
var fragment: Fragment
|
||||
if (intent != null && intent.hasExtra(ENCODE)) {
|
||||
fragment = EncodeFragment.newInstance(
|
||||
intent.getStringExtra(ENCODE))
|
||||
} else if (intent != null && intent.hasExtra(DECODE)) {
|
||||
fragment = DecodeFragment.newInstance(
|
||||
intent.getStringExtra(DECODE),
|
||||
intent.getSerializableExtra(
|
||||
DECODE_FORMAT) as BarcodeFormat)
|
||||
} else {
|
||||
fragment = DecodeFragment()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setSystemBarColor(window: Window, color: Int): Boolean {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
return false
|
||||
}
|
||||
|
||||
window.statusBarColor = color
|
||||
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
|
||||
|
||||
return true
|
||||
setFragment(supportFragmentManager, fragment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ class SplashActivity : AppCompatActivity() {
|
||||
// because that would happen after the app is fully
|
||||
// initialized what is too late
|
||||
|
||||
startActivity(Intent(applicationContext, MainActivity::class.java))
|
||||
startActivity(Intent(applicationContext, CameraActivity::class.java))
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package de.markusfisch.android.binaryeye.app
|
||||
|
||||
import de.markusfisch.android.binaryeye.view.SystemBarMetrics
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
|
||||
import android.os.Build
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
|
||||
fun initSystemBars(activity: AppCompatActivity?) {
|
||||
val view = activity?.findViewById(R.id.main_layout)
|
||||
if (view != null && setSystemBarColor(activity.window,
|
||||
ContextCompat.getColor(activity,
|
||||
R.color.primary_dark_translucent))) {
|
||||
view.setPadding(
|
||||
0,
|
||||
SystemBarMetrics.getStatusBarHeight(
|
||||
activity.resources),
|
||||
0,
|
||||
0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setSystemBarColor(window: Window, color: Int): Boolean {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
return false
|
||||
}
|
||||
|
||||
window.statusBarColor = color
|
||||
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
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,242 +0,0 @@
|
||||
package de.markusfisch.android.binaryeye.fragment
|
||||
|
||||
import com.google.zxing.Result
|
||||
import com.google.zxing.ResultPoint
|
||||
|
||||
import de.markusfisch.android.cameraview.widget.CameraView
|
||||
|
||||
import de.markusfisch.android.binaryeye.activity.MainActivity
|
||||
import de.markusfisch.android.binaryeye.app.addFragment
|
||||
import de.markusfisch.android.binaryeye.rs.YuvToGray
|
||||
import de.markusfisch.android.binaryeye.widget.LockOnView
|
||||
import de.markusfisch.android.binaryeye.zxing.Zxing
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Rect
|
||||
import android.hardware.Camera
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Vibrator
|
||||
import android.support.v4.app.Fragment
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
||||
class CameraFragment : Fragment() {
|
||||
private val zxing = Zxing()
|
||||
private val decodingRunnable = Runnable {
|
||||
while (decoding) {
|
||||
val result = decodeFrame()
|
||||
if (result != null) {
|
||||
cameraView.post { found(result) }
|
||||
decoding = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var vibrator: Vibrator
|
||||
private lateinit var cameraView: CameraView
|
||||
private lateinit var lockOnView: LockOnView
|
||||
private lateinit var yuvToGray: YuvToGray
|
||||
|
||||
private var decoding = false
|
||||
private var decodingThread: Thread? = null
|
||||
private var frameData: ByteArray? = null
|
||||
private var frameWidth: Int = 0
|
||||
private var frameHeight: Int = 0
|
||||
private var frameOrientation: Int = 0
|
||||
private var odd = false
|
||||
private var flash = false
|
||||
|
||||
override fun onCreate(state: Bundle?) {
|
||||
super.onCreate(state)
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
state: Bundle?): View? {
|
||||
activity.setTitle(R.string.scan_code)
|
||||
vibrator = activity.getSystemService(
|
||||
Context.VIBRATOR_SERVICE) as Vibrator
|
||||
|
||||
yuvToGray = YuvToGray(context)
|
||||
|
||||
cameraView = (activity as MainActivity).cameraView
|
||||
cameraView.setOnCameraListener(object : CameraView.OnCameraListener {
|
||||
override fun onConfigureParameters(
|
||||
parameters: Camera.Parameters) {}
|
||||
|
||||
override fun onCameraStarted(camera: Camera) {
|
||||
frameWidth = cameraView.frameWidth
|
||||
frameHeight = cameraView.frameHeight
|
||||
frameOrientation = cameraView.frameOrientation
|
||||
camera.setPreviewCallback { data, _ -> frameData = data }
|
||||
}
|
||||
|
||||
override fun onCameraStopping(camera: Camera) {
|
||||
camera.setPreviewCallback(null)
|
||||
}
|
||||
})
|
||||
|
||||
lockOnView = (activity as MainActivity).lockOnView
|
||||
|
||||
val view = inflater.inflate(
|
||||
R.layout.fragment_camera,
|
||||
container,
|
||||
false)
|
||||
|
||||
view.findViewById<View>(R.id.create).setOnClickListener { _ ->
|
||||
addFragment(fragmentManager, EncodeFragment())
|
||||
}
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
yuvToGray.destroy()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
System.gc()
|
||||
lockOnView.visibility = View.VISIBLE
|
||||
cameraView.visibility = View.VISIBLE
|
||||
cameraView.openAsync(CameraView.findCameraId(
|
||||
Camera.CameraInfo.CAMERA_FACING_BACK))
|
||||
startDecoding()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
cancelDecoding()
|
||||
cameraView.close()
|
||||
cameraView.visibility = View.GONE
|
||||
lockOnView.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.fragment_camera, menu)
|
||||
if (!context.packageManager.hasSystemFeature(
|
||||
PackageManager.FEATURE_CAMERA_FLASH)) {
|
||||
menu.findItem(R.id.flash).setVisible(false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.flash -> {
|
||||
toggleTorchMode()
|
||||
true
|
||||
}
|
||||
R.id.info -> {
|
||||
openReadme()
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
flash = flash xor true
|
||||
parameters?.let { camera.setParameters(parameters) }
|
||||
}
|
||||
|
||||
private fun openReadme() {
|
||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(
|
||||
"https://github.com/markusfisch/BinaryEye/blob/master/README.md")))
|
||||
}
|
||||
|
||||
private fun startDecoding() {
|
||||
activity ?: return
|
||||
|
||||
frameData = null
|
||||
decoding = true
|
||||
decodingThread = Thread(decodingRunnable)
|
||||
decodingThread?.start()
|
||||
}
|
||||
|
||||
private fun cancelDecoding() {
|
||||
decoding = false
|
||||
if (decodingThread != null) {
|
||||
var retry = 100
|
||||
while (retry-- > 0) {
|
||||
try {
|
||||
decodingThread?.join()
|
||||
break
|
||||
} catch (e: InterruptedException) {
|
||||
// try again
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun decodeFrame(): Result? {
|
||||
frameData ?: return null
|
||||
return zxing.decodeBitmap(yuvToGray.convert(
|
||||
frameData!!, frameWidth, frameHeight, frameOrientation))
|
||||
}
|
||||
|
||||
private fun found(result: Result) {
|
||||
cancelDecoding()
|
||||
vibrator.vibrate(100)
|
||||
lockOnView.lock(getRelativeBounds(result.resultPoints), 250)
|
||||
lockOnView.postDelayed({
|
||||
addFragment(fragmentManager, DecodeFragment.newInstance(
|
||||
result.text,
|
||||
result.getBarcodeFormat()))
|
||||
}, 500)
|
||||
}
|
||||
|
||||
private fun getRelativeBounds(resultPoints: Array<ResultPoint>): Rect {
|
||||
val fw: Float
|
||||
val fh: Float
|
||||
if (frameOrientation == 90 || frameOrientation == 270) {
|
||||
fw = frameHeight.toFloat()
|
||||
fh = frameWidth.toFloat()
|
||||
} else {
|
||||
fw = frameWidth.toFloat()
|
||||
fh = frameHeight.toFloat()
|
||||
}
|
||||
val viewRect = cameraView.previewRect
|
||||
val xf = fw / viewRect.width()
|
||||
val yf = fh / viewRect.height()
|
||||
val bounds = getBounds(resultPoints)
|
||||
bounds.set(
|
||||
Math.round(bounds.left / xf),
|
||||
Math.round(bounds.top / yf),
|
||||
Math.round(bounds.right / xf),
|
||||
Math.round(bounds.bottom / yf))
|
||||
bounds.offset(viewRect.left, viewRect.top)
|
||||
return bounds
|
||||
}
|
||||
|
||||
private fun getBounds(resultPoints: Array<ResultPoint>): Rect {
|
||||
val bounds = Rect(0xffffff, 0xffffff, 0, 0)
|
||||
for (resultPoint in resultPoints) {
|
||||
val x = Math.round(resultPoint.x)
|
||||
val y = Math.round(resultPoint.y)
|
||||
bounds.set(
|
||||
Math.min(bounds.left, x),
|
||||
Math.min(bounds.top, y),
|
||||
Math.max(bounds.right, x),
|
||||
Math.max(bounds.bottom, y))
|
||||
}
|
||||
return bounds
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ class YuvToGray(context: Context) {
|
||||
private var rgbaAlloc: Allocation? = null
|
||||
private var destAlloc: Allocation? = null
|
||||
private var dest: Bitmap? = null
|
||||
private var odd = true
|
||||
|
||||
fun destroy() {
|
||||
yuvType?.destroy()
|
||||
@@ -76,7 +77,14 @@ class YuvToGray(context: Context) {
|
||||
// width/height are uint_32 but Kotlin wants toLong()
|
||||
yuv2grayScript._inWidth = width.toLong()
|
||||
yuv2grayScript._inHeight = height.toLong()
|
||||
yuv2grayScript.forEach_yuv2gray(rgbaAlloc)
|
||||
|
||||
// invert every second frame to also read inverted barcodes
|
||||
if (odd) {
|
||||
yuv2grayScript.forEach_yuv2gray(rgbaAlloc)
|
||||
} else {
|
||||
yuv2grayScript.forEach_yuv2inverted(rgbaAlloc)
|
||||
}
|
||||
odd = odd xor true
|
||||
|
||||
rotatorScript._inImage = rgbaAlloc
|
||||
rotatorScript._inWidth = width
|
||||
|
||||
@@ -1,262 +0,0 @@
|
||||
package de.markusfisch.android.binaryeye.widget
|
||||
|
||||
import de.markusfisch.android.binaryeye.view.SystemBarMetrics
|
||||
import de.markusfisch.android.binaryeye.R
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Matrix
|
||||
import android.graphics.Paint
|
||||
import android.graphics.PixelFormat
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.graphics.Rect
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.util.AttributeSet
|
||||
import android.view.SurfaceView
|
||||
import android.view.SurfaceHolder
|
||||
|
||||
class LockOnView : SurfaceView {
|
||||
private val markerPaint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
private val restRect = Rect()
|
||||
private val startRect = Rect()
|
||||
private val endRect = Rect()
|
||||
private val currentRect = Rect()
|
||||
private val animationRunnable = Runnable {
|
||||
setAnimationStart()
|
||||
while (running) {
|
||||
val now = System.currentTimeMillis() - animationStart
|
||||
if (now > animationDuration) {
|
||||
break
|
||||
}
|
||||
animateCurrentRect(now)
|
||||
lockCanvasAndDraw()
|
||||
}
|
||||
lockCanvasAndDraw()
|
||||
running = false
|
||||
}
|
||||
|
||||
@Volatile private var running = false
|
||||
|
||||
private lateinit var markerTopLeft: Bitmap
|
||||
private lateinit var markerTopRight: Bitmap
|
||||
private lateinit var markerBottomLeft: Bitmap
|
||||
private lateinit var markerBottomRight: Bitmap
|
||||
|
||||
private var markerSize: Int = 0
|
||||
private var minSize: Int = 0
|
||||
private var thread: Thread? = null
|
||||
private var animationStart: Long = 0
|
||||
private var animationDuration: Long = 0
|
||||
|
||||
init {
|
||||
loadMarkers(context.resources)
|
||||
initSurfaceHolder(context)
|
||||
setZOrderOnTop(true)
|
||||
}
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet) :
|
||||
super(context, attrs) {
|
||||
}
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) :
|
||||
super(context, attrs, defStyleAttr) {
|
||||
}
|
||||
|
||||
fun lock(rect: Rect, ms: Int) {
|
||||
startRect.set(currentRect)
|
||||
if (running) {
|
||||
tintMarkers()
|
||||
fitRect(rect)
|
||||
endRect.set(rect)
|
||||
setAnimationStart()
|
||||
} else {
|
||||
startAnimation(rect, ms)
|
||||
}
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
untintMarkers()
|
||||
startRect.set(restRect)
|
||||
currentRect.set(startRect)
|
||||
lockCanvasAndDraw()
|
||||
}
|
||||
|
||||
private fun loadMarkers(res: Resources) {
|
||||
val dp = res.displayMetrics.density
|
||||
markerTopLeft = BitmapFactory.decodeResource(res, R.drawable.marker)
|
||||
val width = markerTopLeft.width
|
||||
val height = markerTopLeft.height
|
||||
markerSize = Math.max(width, height)
|
||||
minSize = markerSize * 2 + Math.round(dp * 8f)
|
||||
val matrix = Matrix()
|
||||
matrix.setScale(-1f, 1f)
|
||||
markerTopRight = Bitmap.createBitmap(markerTopLeft,
|
||||
0, 0, width, height, matrix, false)
|
||||
matrix.setScale(1f, -1f)
|
||||
markerBottomLeft = Bitmap.createBitmap(markerTopLeft,
|
||||
0, 0, width, height, matrix, false)
|
||||
matrix.setScale(-1f, -1f)
|
||||
markerBottomRight = Bitmap.createBitmap(markerTopLeft,
|
||||
0, 0, width, height, matrix, false)
|
||||
}
|
||||
|
||||
private fun initSurfaceHolder(context: Context) {
|
||||
holder.setFormat(PixelFormat.TRANSPARENT)
|
||||
holder.addCallback(object : SurfaceHolder.Callback {
|
||||
override fun surfaceChanged(
|
||||
holder: SurfaceHolder,
|
||||
format: Int,
|
||||
width: Int,
|
||||
height: Int) {
|
||||
initRestRect(context, width, height)
|
||||
reset()
|
||||
}
|
||||
|
||||
override fun surfaceCreated(holder: SurfaceHolder) {}
|
||||
|
||||
override fun surfaceDestroyed(holder: SurfaceHolder) {
|
||||
cancelAnimation()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun cancelAnimation() {
|
||||
running = false
|
||||
if (thread != null) {
|
||||
var retry = 100
|
||||
while (retry-- > 0) {
|
||||
try {
|
||||
thread?.join()
|
||||
retry = 0
|
||||
} catch (e: InterruptedException) {
|
||||
// try again
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun startAnimation(rect: Rect, ms: Int) {
|
||||
if (running) {
|
||||
return
|
||||
}
|
||||
if (rect == restRect) {
|
||||
untintMarkers()
|
||||
} else {
|
||||
tintMarkers()
|
||||
}
|
||||
running = true
|
||||
fitRect(rect)
|
||||
endRect.set(rect)
|
||||
animationDuration = ms.toLong()
|
||||
thread = Thread(animationRunnable)
|
||||
thread?.start()
|
||||
}
|
||||
|
||||
private fun initRestRect(context: Context, width: Int, height: Int) {
|
||||
val res = context.resources
|
||||
val dp = res.displayMetrics.density
|
||||
val padding = Math.round(16f * dp)
|
||||
var left = padding
|
||||
var top = padding + if (width > height)
|
||||
SystemBarMetrics.getStatusAndToolBarHeight(context)
|
||||
else
|
||||
0
|
||||
val navBar = SystemBarMetrics.getNavigationBarSize(res)
|
||||
val paddingLeftRight = padding * 2 + navBar.x
|
||||
val paddingTopBottom = top + padding + navBar.y
|
||||
val innerWidth = width - paddingLeftRight
|
||||
val innerHeight = height - paddingTopBottom
|
||||
val size = Math.min(innerWidth, innerHeight)
|
||||
left += (innerWidth - size) / 2
|
||||
top += (innerHeight - size) / 2
|
||||
restRect.set(left, top, left + size, top + size)
|
||||
}
|
||||
|
||||
private fun fitRect(rect: Rect) {
|
||||
val width = rect.width()
|
||||
val height = rect.height()
|
||||
if (width < minSize) {
|
||||
val pad = (minSize - width) / 2
|
||||
rect.left -= pad
|
||||
rect.right += pad
|
||||
}
|
||||
if (height < minSize) {
|
||||
val pad = (minSize - height) / 2
|
||||
rect.top -= pad
|
||||
rect.bottom += pad
|
||||
}
|
||||
}
|
||||
|
||||
private fun setAnimationStart() {
|
||||
animationStart = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
private fun animateCurrentRect(now: Long) {
|
||||
currentRect.set(
|
||||
linear(now,
|
||||
startRect.left,
|
||||
endRect.left - startRect.left,
|
||||
animationDuration),
|
||||
linear(now,
|
||||
startRect.top,
|
||||
endRect.top - startRect.top,
|
||||
animationDuration),
|
||||
linear(now,
|
||||
startRect.right,
|
||||
endRect.right - startRect.right,
|
||||
animationDuration),
|
||||
linear(now,
|
||||
startRect.bottom,
|
||||
endRect.bottom - startRect.bottom,
|
||||
animationDuration))
|
||||
}
|
||||
|
||||
private fun linear(
|
||||
time: Long,
|
||||
begin: Int,
|
||||
change: Int,
|
||||
duration: Long): Int = Math.round(
|
||||
change.toFloat() * time / duration + begin)
|
||||
|
||||
private fun lockCanvasAndDraw() {
|
||||
val canvas = holder.lockCanvas()
|
||||
if (canvas != null) {
|
||||
drawMarkers(canvas, currentRect)
|
||||
holder.unlockCanvasAndPost(canvas)
|
||||
}
|
||||
}
|
||||
|
||||
private fun untintMarkers() {
|
||||
markerPaint.colorFilter = null
|
||||
}
|
||||
|
||||
private fun tintMarkers() {
|
||||
markerPaint.colorFilter = PorterDuffColorFilter(
|
||||
ContextCompat.getColor(context, R.color.accent),
|
||||
PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
|
||||
private fun drawMarkers(canvas: Canvas, rect: Rect) {
|
||||
canvas.drawColor(0, PorterDuff.Mode.CLEAR)
|
||||
canvas.drawBitmap(markerTopLeft,
|
||||
rect.left.toFloat(),
|
||||
rect.top.toFloat(),
|
||||
markerPaint)
|
||||
canvas.drawBitmap(markerTopRight,
|
||||
(rect.right - markerSize).toFloat(),
|
||||
rect.top.toFloat(),
|
||||
markerPaint)
|
||||
canvas.drawBitmap(markerBottomLeft,
|
||||
rect.left.toFloat(),
|
||||
(rect.bottom - markerSize).toFloat(),
|
||||
markerPaint)
|
||||
canvas.drawBitmap(markerBottomRight,
|
||||
(rect.right - markerSize).toFloat(),
|
||||
(rect.bottom - markerSize).toFloat(),
|
||||
markerPaint)
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 499 B |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 315 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 355 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 597 B |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 799 B |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 32 KiB |
@@ -0,0 +1,7 @@
|
||||
<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,8 @@
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@color/background_color"/>
|
||||
<item>
|
||||
<bitmap
|
||||
android:gravity="top|right"
|
||||
android:src="@drawable/wallpaper"/>
|
||||
</item>
|
||||
</layer-list>
|
||||
@@ -0,0 +1,39 @@
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<de.markusfisch.android.cameraview.widget.CameraView
|
||||
android:id="@+id/camera_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
<RelativeLayout
|
||||
android:id="@+id/main_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:fitsSystemWindows="true"
|
||||
android:background="@drawable/background_scanner">
|
||||
<include layout="@layout/toolbar"/>
|
||||
<SeekBar
|
||||
android:id="@+id/zoom"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginRight="80dp"
|
||||
android:layout_marginEnd="80dp"
|
||||
android:padding="16dp"/>
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
tools:ignore="UnusedAttribute"
|
||||
android:id="@+id/flash"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_margin="20dp"
|
||||
android:src="@drawable/ic_action_flash"
|
||||
app:elevation="4dp"/>
|
||||
</RelativeLayout>
|
||||
</merge>
|
||||
@@ -1,14 +1,4 @@
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<de.markusfisch.android.cameraview.widget.CameraView
|
||||
android:id="@+id/camera_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"/>
|
||||
<de.markusfisch.android.binaryeye.widget.LockOnView
|
||||
android:id="@+id/lock_on"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"/>
|
||||
<LinearLayout
|
||||
android:id="@+id/main_layout"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
tools:ignore="UnusedAttribute"
|
||||
android:id="@+id/create"
|
||||
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_create"
|
||||
app:elevation="4dp"/>
|
||||
</RelativeLayout>
|
||||
@@ -7,8 +7,8 @@
|
||||
android:icon="@drawable/ic_action_info"
|
||||
material:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/flash"
|
||||
android:title="@string/flash"
|
||||
android:icon="@drawable/ic_action_flash"
|
||||
android:id="@+id/create"
|
||||
android:title="@string/compose_barcode"
|
||||
android:icon="@drawable/ic_action_create"
|
||||
material:showAsAction="ifRoom"/>
|
||||
</menu>
|
||||
@@ -5,5 +5,6 @@
|
||||
<color name="primary_dark_translucent">#8111</color>
|
||||
<color name="accent">#26e1cf</color>
|
||||
<color name="accent_dark">#16d1bf</color>
|
||||
<color name="background_color">#111</color>
|
||||
<color name="button_text">#fff</color>
|
||||
</resources>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<resources>
|
||||
<string name="no_camera_no_fun">This app does not make any sense without camera access. Good bye.</string>
|
||||
<string name="scan_code">Scan a code</string>
|
||||
<string name="flash">Toggle flash</string>
|
||||
<string name="camera_error">Can\'t access camera, please try again</string>
|
||||
<string name="scan_code">Scan code</string>
|
||||
<string name="compose_barcode">Compose barcode</string>
|
||||
<string name="content">Content</string>
|
||||
<string name="copy_to_clipboard">Copy to clipboard</string>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<item name="colorPrimary">@color/primary</item>
|
||||
<item name="colorPrimaryDark">@color/primary_dark</item>
|
||||
<item name="colorAccent">@color/accent</item>
|
||||
<item name="android:windowBackground">@drawable/background_window</item>
|
||||
</style>
|
||||
<style name="SplashTheme" parent="@style/AppTheme">
|
||||
<item name="android:windowBackground">@drawable/background_splash</item>
|
||||
|
||||
@@ -15,3 +15,13 @@ uchar4 RS_KERNEL yuv2gray(uint32_t x, uint32_t y) {
|
||||
out.a = 255;
|
||||
return out;
|
||||
}
|
||||
|
||||
uchar4 RS_KERNEL yuv2inverted(uint32_t x, uint32_t y) {
|
||||
uchar Y = 255 - rsGetElementAt_uchar(inYUV, x, y);
|
||||
uchar4 out;
|
||||
out.r = Y;
|
||||
out.g = Y;
|
||||
out.b = Y;
|
||||
out.a = 255;
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.1.4-2'
|
||||
ext.kotlin_version = '1.1.51'
|
||||
ext.tools_version = '2.3.3'
|
||||
ext.build_tools_version = '26.0.0'
|
||||
ext.sdk_version = 26
|
||||
@@ -20,6 +20,10 @@ buildscript {
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'org.sonarqube' version '2.5'
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
jcenter()
|
||||
|
||||
@@ -18,8 +18,8 @@ android {
|
||||
minSdkVersion 9
|
||||
targetSdkVersion sdk_version
|
||||
|
||||
versionCode 2
|
||||
versionName '1.0.1'
|
||||
versionCode 5
|
||||
versionName '1.3.0'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,11 +15,13 @@ import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CameraView extends FrameLayout {
|
||||
public interface OnCameraListener {
|
||||
void onConfigureParameters(Camera.Parameters parameters);
|
||||
void onCameraError();
|
||||
void onCameraStarted(Camera camera);
|
||||
void onCameraStopping(Camera camera);
|
||||
}
|
||||
@@ -56,18 +58,45 @@ public class CameraView extends FrameLayout {
|
||||
.getSystemService(Context.WINDOW_SERVICE))
|
||||
.getDefaultDisplay()
|
||||
.getRotation()) {
|
||||
default:
|
||||
case Surface.ROTATION_0:
|
||||
return 0;
|
||||
case Surface.ROTATION_90:
|
||||
return 90;
|
||||
case Surface.ROTATION_180:
|
||||
return 180;
|
||||
case Surface.ROTATION_270:
|
||||
return 270;
|
||||
case Surface.ROTATION_0:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
public static boolean setAutoFocus(Camera.Parameters parameters) {
|
||||
// best for taking pictures, API >= ICE_CREAM_SANDWICH
|
||||
String continuousPicture =
|
||||
Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE;
|
||||
// less aggressive than CONTINUOUS_PICTURE, API >= GINGERBREAD
|
||||
String continuousVideo =
|
||||
Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO;
|
||||
// last resort
|
||||
String autoFocus = Camera.Parameters.FOCUS_MODE_AUTO;
|
||||
|
||||
// prefer feature detection instead of checking BUILD.VERSION
|
||||
List<String> focusModes = parameters.getSupportedFocusModes();
|
||||
|
||||
if (focusModes.contains(continuousPicture)) {
|
||||
parameters.setFocusMode(continuousPicture);
|
||||
} else if (focusModes.contains(continuousVideo)) {
|
||||
parameters.setFocusMode(continuousVideo);
|
||||
} else if (focusModes.contains(autoFocus)) {
|
||||
parameters.setFocusMode(autoFocus);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public CameraView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
@@ -87,12 +116,12 @@ public class CameraView extends FrameLayout {
|
||||
new AsyncTask<Void, Void, Camera>() {
|
||||
@Override
|
||||
protected Camera doInBackground(Void... nothings) {
|
||||
if (camera != null) {
|
||||
if (CameraView.this.camera != null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
// open() may take a while so it shouldn't be
|
||||
// run on the main thread according to the docs
|
||||
// invoked on the main thread according to the docs
|
||||
return Camera.open(cameraId);
|
||||
} catch (RuntimeException e) {
|
||||
return null;
|
||||
@@ -102,6 +131,10 @@ public class CameraView extends FrameLayout {
|
||||
@Override
|
||||
protected void onPostExecute(Camera camera) {
|
||||
if (camera == null) {
|
||||
if (onCameraListener != null &&
|
||||
CameraView.this.camera == null) {
|
||||
onCameraListener.onCameraError();
|
||||
}
|
||||
return;
|
||||
}
|
||||
CameraView.this.camera = camera;
|
||||
@@ -151,6 +184,43 @@ public class CameraView extends FrameLayout {
|
||||
return frameOrientation;
|
||||
}
|
||||
|
||||
public Rect calculateFocusRect(float x, float y, int radius) {
|
||||
int cx = Math.round(2000f / viewWidth * x - 1000f);
|
||||
int cy = Math.round(2000f / viewHeight * y - 1000f);
|
||||
return new Rect(
|
||||
Math.max(-1000, cx - radius),
|
||||
Math.max(-1000, cy - radius),
|
||||
Math.min(1000, cx + radius),
|
||||
Math.min(1000, cy + radius));
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
public boolean setFocusArea(Rect area) {
|
||||
if (camera == null || Build.VERSION.SDK_INT <
|
||||
Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Camera.Parameters parameters = camera.getParameters();
|
||||
if (parameters.getMaxNumFocusAreas() > 0) {
|
||||
if (area != null) {
|
||||
List<Camera.Area> focusAreas =
|
||||
new ArrayList<Camera.Area>();
|
||||
focusAreas.add(new Camera.Area(area, 1000));
|
||||
parameters.setFocusAreas(focusAreas);
|
||||
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
|
||||
} else {
|
||||
parameters.setFocusAreas(null);
|
||||
CameraView.setAutoFocus(parameters);
|
||||
}
|
||||
}
|
||||
camera.setParameters(parameters);
|
||||
return true;
|
||||
} catch (RuntimeException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(
|
||||
boolean changed,
|
||||
@@ -175,14 +245,21 @@ public class CameraView extends FrameLayout {
|
||||
private void setCameraParameters(Context context) {
|
||||
boolean transpose = frameOrientation == 90 || frameOrientation == 270;
|
||||
|
||||
Camera.Parameters parameters = camera.getParameters();
|
||||
parameters.setRotation(frameOrientation);
|
||||
setPreviewSize(parameters, transpose);
|
||||
setAutoFocus(parameters);
|
||||
if (onCameraListener != null) {
|
||||
onCameraListener.onConfigureParameters(parameters);
|
||||
try {
|
||||
Camera.Parameters parameters = camera.getParameters();
|
||||
parameters.setRotation(frameOrientation);
|
||||
setPreviewSize(parameters, transpose);
|
||||
if (onCameraListener != null) {
|
||||
onCameraListener.onConfigureParameters(parameters);
|
||||
}
|
||||
camera.setParameters(parameters);
|
||||
} catch (RuntimeException e) {
|
||||
if (onCameraListener != null) {
|
||||
onCameraListener.onCameraError();
|
||||
}
|
||||
return;
|
||||
}
|
||||
camera.setParameters(parameters);
|
||||
|
||||
camera.setDisplayOrientation(frameOrientation);
|
||||
|
||||
int childWidth;
|
||||
@@ -216,9 +293,11 @@ public class CameraView extends FrameLayout {
|
||||
parameters.getSupportedPreviewSizes(),
|
||||
frameWidth,
|
||||
frameHeight);
|
||||
frameWidth = size.width;
|
||||
frameHeight = size.height;
|
||||
parameters.setPreviewSize(frameWidth, frameHeight);
|
||||
if (size != null) {
|
||||
frameWidth = size.width;
|
||||
frameHeight = size.height;
|
||||
parameters.setPreviewSize(frameWidth, frameHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private static Camera.Size findBestPreviewSize(
|
||||
@@ -233,7 +312,7 @@ public class CameraView extends FrameLayout {
|
||||
Camera.Size bestSizeAspect = null;
|
||||
|
||||
for (Camera.Size size : sizes) {
|
||||
double diff = Math.abs(size.height - height) +
|
||||
double diff = (double) Math.abs(size.height - height) +
|
||||
Math.abs(size.width - width);
|
||||
|
||||
if (diff < minDiff) {
|
||||
@@ -253,29 +332,6 @@ public class CameraView extends FrameLayout {
|
||||
return bestSizeAspect != null ? bestSizeAspect : bestSize;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
private static void setAutoFocus(Camera.Parameters parameters) {
|
||||
// best for taking pictures, API >= ICE_CREAM_SANDWICH
|
||||
String continuousPicture =
|
||||
Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE;
|
||||
// less aggressive than CONTINUOUS_PICTURE, API >= GINGERBREAD
|
||||
String continuousVideo =
|
||||
Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO;
|
||||
// last resort
|
||||
String autoFocus = Camera.Parameters.FOCUS_MODE_AUTO;
|
||||
|
||||
// prefer feature detection instead of checking BUILD.VERSION
|
||||
List<String> focusModes = parameters.getSupportedFocusModes();
|
||||
|
||||
if (focusModes.contains(continuousPicture)) {
|
||||
parameters.setFocusMode(continuousPicture);
|
||||
} else if (focusModes.contains(continuousVideo)) {
|
||||
parameters.setFocusMode(continuousVideo);
|
||||
} else if (focusModes.contains(autoFocus)) {
|
||||
parameters.setFocusMode(autoFocus);
|
||||
}
|
||||
}
|
||||
|
||||
private void addSurfaceView(
|
||||
Context context,
|
||||
int surfaceWidth,
|
||||
@@ -289,6 +345,7 @@ public class CameraView extends FrameLayout {
|
||||
holder.addCallback(new SurfaceHolder.Callback() {
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
// wait until the surface has dimensions
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<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"
|
||||
fill="#000000"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="marker.svg">
|
||||
<metadata
|
||||
id="metadata12">
|
||||
<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></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs10" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1215"
|
||||
inkscape:window-height="643"
|
||||
id="namedview8"
|
||||
showgrid="false"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:zoom="9.8333333"
|
||||
inkscape:cx="12"
|
||||
inkscape:cy="12"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg2" />
|
||||
<path
|
||||
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"
|
||||
d="M 6.4576853,2.0235976 C 4.0190093,2.0235564 2.0237288,4.0188781 2.0237288,6.4575542 l 0,15.5188478 4.4339565,0 0,-15.5188478 15.5185857,0 0,-4.4336942 z"
|
||||
id="rect4146"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="sscccccs" />
|
||||
<path
|
||||
d="M0 0h24v24H0z"
|
||||
fill="none"
|
||||
id="path4" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.0 KiB |
@@ -102,10 +102,6 @@ type converter &>/dev/null || find_converter || {
|
||||
exit 1
|
||||
}
|
||||
|
||||
update app/src/main/res/drawable << EOF
|
||||
svg/marker.svg 48
|
||||
EOF
|
||||
|
||||
update app/src/debug/res/mipmap << EOF
|
||||
svg/debug/ic_launcher.svg 48
|
||||
EOF
|
||||
@@ -113,3 +109,7 @@ EOF
|
||||
update app/src/main/res/mipmap << EOF
|
||||
svg/ic_launcher.svg 48
|
||||
EOF
|
||||
|
||||
update app/src/main/res/drawable << EOF
|
||||
svg/wallpaper.svg 384
|
||||
EOF
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
<?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="192"
|
||||
height="192"
|
||||
viewBox="0 0 192 192"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="wallpaper.svg"
|
||||
inkscape:export-filename="/Users/markus/app_icon.png"
|
||||
inkscape:export-xdpi="240"
|
||||
inkscape:export-ydpi="240">
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.0833333"
|
||||
inkscape:cx="96"
|
||||
inkscape:cy="96"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:window-width="1680"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="1"
|
||||
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,-860.36218)">
|
||||
<circle
|
||||
r="88"
|
||||
cy="882.36218"
|
||||
cx="150"
|
||||
id="circle4151"
|
||||
style="fill:#1b1b1b;fill-opacity:1;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill:#111111;fill-opacity:1;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 151.2832,816.81726 a 64.837967,64.837967 0 0 0 -64.837888,64.83789 64.837967,64.837967 0 0 0 64.837888,64.83789 64.837967,64.837967 0 0 0 64.83789,-64.83789 64.837967,64.837967 0 0 0 -64.83789,-64.83789 z m -1.8457,31.52148 a 35.467094,35.467094 0 0 1 2.66016,0.13282 23.307953,23.307953 0 0 0 -2.01368,9.4375 23.307953,23.307953 0 0 0 23.3086,23.30664 23.307953,23.307953 0 0 0 11.07031,-2.82032 35.467094,35.467094 0 0 1 0.44141,5.41016 35.467094,35.467094 0 0 1 -35.4668,35.4668 35.467094,35.467094 0 0 1 -35.46875,-35.4668 35.467094,35.467094 0 0 1 35.46875,-35.4668 z"
|
||||
id="circle4143"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |