diff --git a/pretixscan/app/build.gradle b/pretixscan/app/build.gradle
index 3fc18809..49e358e6 100644
--- a/pretixscan/app/build.gradle
+++ b/pretixscan/app/build.gradle
@@ -129,7 +129,6 @@ dependencies {
implementation 'com.louiscad.splitties:splitties-toast:3.0.0'
implementation 'com.github.traex.rippleeffect:library:1.3'
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
- implementation 'com.andrognito.pinlockview:pinlockview:2.1.0'
implementation 'com.github.kizitonwose:CalendarView:1.0.4'
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KeyboardButtonView.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KeyboardButtonView.kt
new file mode 100644
index 00000000..77e0642c
--- /dev/null
+++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KeyboardButtonView.kt
@@ -0,0 +1,88 @@
+package eu.pretix.pretixscan.droid.ui
+
+import android.content.Context
+import android.os.Build
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.MotionEvent
+import android.view.View
+import android.widget.ImageView
+import android.widget.RelativeLayout
+import android.widget.TextView
+import eu.pretix.pretixscan.droid.R
+
+
+// (c) 2015 OrangeGangsters
+// MIT License
+// https://github.com/omadahealth/LolliPin/blob/0c523dfb7e9ee5dfcf37ad047cbb970ccd8794fb/lib/src/main/java/com/github/omadahealth/lollipin/lib/views/KeyboardButtonView.java
+
+interface KeyboardButtonClickedListener {
+
+}
+
+class KeyboardButtonView @JvmOverloads constructor(private val mContext: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : RelativeLayout(mContext, attrs, defStyleAttr) {
+
+ private var mKeyboardButtonClickedListener: KeyboardButtonClickedListener? = null
+ private var mRippleView: View? = null
+
+ init {
+ initializeView(attrs, defStyleAttr)
+ }
+
+ private fun initializeView(attrs: AttributeSet?, defStyleAttr: Int) {
+ if (attrs != null && !isInEditMode) {
+ val attributes = mContext.getTheme().obtainStyledAttributes(attrs, R.styleable.KeyboardButtonView,
+ defStyleAttr, 0)
+ val text = attributes.getString(R.styleable.KeyboardButtonView_lp_keyboard_button_text)
+ val image = attributes.getDrawable(R.styleable.KeyboardButtonView_lp_keyboard_button_image)
+ val rippleEnabled = attributes.getBoolean(R.styleable.KeyboardButtonView_lp_keyboard_button_ripple_enabled, true)
+
+ attributes.recycle()
+
+ val inflater = mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
+ val view = inflater.inflate(R.layout.view_keyboard_button, this) as KeyboardButtonView
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ view.focusable = NOT_FOCUSABLE
+ }
+
+ if (text != null) {
+ val textView = view.findViewById(R.id.keyboard_button_textview) as TextView
+ textView?.setText(text)
+ }
+ if (image != null) {
+ val imageView = view.findViewById(R.id.keyboard_button_imageview) as ImageView
+ if (imageView != null) {
+ imageView!!.setImageDrawable(image)
+ imageView!!.setVisibility(View.VISIBLE)
+ }
+ }
+
+ mRippleView = view.findViewById(R.id.pin_code_keyboard_button_ripple) as View
+ //mRippleView!!.setRippleAnimationListener(this)
+ if (mRippleView != null) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ mRippleView!!.focusable = NOT_FOCUSABLE
+ }
+ if (!rippleEnabled) {
+ mRippleView!!.setVisibility(View.INVISIBLE)
+ }
+ }
+ }
+ }
+
+ fun setOnRippleAnimationEndListener(keyboardButtonClickedListener: KeyboardButtonClickedListener) {
+ mKeyboardButtonClickedListener = keyboardButtonClickedListener
+ }
+
+ fun onRippleAnimationEnd() {
+ if (mKeyboardButtonClickedListener != null) {
+ //mKeyboardButtonClickedListener!!.onRippleAnimationEnd()
+ }
+ }
+
+ override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
+ onTouchEvent(event)
+ return false
+ }
+}
diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt
index a57352a8..e8cb62bc 100644
--- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt
+++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt
@@ -42,9 +42,6 @@ import androidx.databinding.DataBindingUtil
import androidx.databinding.ObservableField
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.LinearLayoutManager
-import com.andrognito.pinlockview.IndicatorDots
-import com.andrognito.pinlockview.PinLockListener
-import com.andrognito.pinlockview.PinLockView
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.module.SimpleModule
@@ -167,6 +164,8 @@ class MainActivity : AppCompatActivity(), ReloadableActivity, ZXingScannerView.R
private var syncMessage = ""
+ private var pendingPinAction: ((pin: String) -> Unit)? = null
+
companion object {
const val PERMISSIONS_REQUEST_CAMERA = 1337
const val PERMISSIONS_REQUEST_WRITE_STORAGE = 1338
@@ -484,6 +483,14 @@ class MainActivity : AppCompatActivity(), ReloadableActivity, ZXingScannerView.R
binding.recyclerViewSearch.layoutManager = LinearLayoutManager(this)
binding.recyclerViewSearch.addItemDecoration(androidx.recyclerview.widget.DividerItemDecoration(binding.recyclerViewSearch.context, androidx.recyclerview.widget.DividerItemDecoration.VERTICAL))
+
+ supportFragmentManager.setFragmentResultListener(PinDialog.RESULT_PIN, this) { _, bundle ->
+ val pin = bundle.getString(PinDialog.RESULT_PIN)
+ if (pin != null && conf.verifyPin(pin)) {
+ (supportFragmentManager.findFragmentByTag(PinDialog.TAG) as? PinDialog)?.dismiss()
+ pendingPinAction?.let { it(pin) }
+ }
+ }
}
private fun eventButtonText(): String {
@@ -1298,35 +1305,8 @@ class MainActivity : AppCompatActivity(), ReloadableActivity, ZXingScannerView.R
valid("")
return
}
- val view = layoutInflater.inflate(R.layout.dialog_pin, null)
- val dialog = AlertDialog.Builder(this)
- .setView(view)
- .create()
- dialog.setOnShowListener {
- val mPinLockListener: PinLockListener = object : PinLockListener {
- override fun onComplete(pin: String) {
- this.onPinChange(pin.length, pin)
- }
-
- override fun onEmpty() {
- }
-
- override fun onPinChange(pinLength: Int, intermediatePin: String) {
- if (conf.verifyPin(intermediatePin)) {
- dialog.dismiss()
- valid(intermediatePin)
- }
- }
- }
-
- val lockView = view.findViewById(R.id.pin_lock_view) as PinLockView
- lockView.pinLength = conf.getPinLength()
- lockView.setPinLockListener(mPinLockListener)
- val idots = view.findViewById(R.id.indicator_dots) as IndicatorDots
- idots.pinLength = conf.getPinLength()
- lockView.attachIndicatorDots(idots);
- }
- dialog.show()
+ pendingPinAction = valid
+ PinDialog().show(supportFragmentManager)
}
fun startWithPIN(intent: Intent, key: String, resultCode: Int? = null, bundle: Bundle? = null) {
diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/PinDialog.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/PinDialog.kt
new file mode 100644
index 00000000..5a070822
--- /dev/null
+++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/PinDialog.kt
@@ -0,0 +1,97 @@
+package eu.pretix.pretixscan.droid.ui
+
+import android.app.Dialog
+import android.content.DialogInterface
+import android.os.Bundle
+import android.view.KeyEvent
+import android.view.View
+import androidx.core.os.bundleOf
+import androidx.databinding.ObservableField
+import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.FragmentManager
+import androidx.fragment.app.setFragmentResult
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import eu.pretix.pretixscan.droid.databinding.DialogPinBinding
+import java.util.Collections
+
+
+class PINInputDataHolder() {
+ val input = ObservableField("")
+ val text = ObservableField("")
+}
+
+class PinDialog : DialogFragment() {
+ companion object {
+ const val TAG = "PinDialogFragment"
+ const val RESULT_PIN = "pin"
+ const val RESULT_DISMISS = "dismiss"
+ }
+
+ val data = PINInputDataHolder()
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ data.input.set("")
+
+ val binding = DialogPinBinding.inflate(layoutInflater)
+ binding.data = data
+
+ binding.keyboardButtonView0.setOnClickListener { pushDigit("0") }
+ binding.keyboardButtonView00.visibility = View.INVISIBLE
+ binding.keyboardButtonView1.setOnClickListener { pushDigit("1") }
+ binding.keyboardButtonView2.setOnClickListener { pushDigit("2") }
+ binding.keyboardButtonView3.setOnClickListener { pushDigit("3") }
+ binding.keyboardButtonView4.setOnClickListener { pushDigit("4") }
+ binding.keyboardButtonView5.setOnClickListener { pushDigit("5") }
+ binding.keyboardButtonView6.setOnClickListener { pushDigit("6") }
+ binding.keyboardButtonView7.setOnClickListener { pushDigit("7") }
+ binding.keyboardButtonView8.setOnClickListener { pushDigit("8") }
+ binding.keyboardButtonView9.setOnClickListener { pushDigit("9") }
+ binding.keyboardButtonViewBackspace.setOnClickListener { pushBackspace() }
+
+ return MaterialAlertDialogBuilder(requireContext())
+ .setView(binding.root)
+ .setOnKeyListener { _, keyCode, event ->
+ if (event.action != KeyEvent.ACTION_DOWN) return@setOnKeyListener false
+
+ if (event.displayLabel.toString().matches(Regex("^[0-9]$"))) {
+ pushDigit(event.displayLabel.toString())
+ return@setOnKeyListener true
+ }
+ if (keyCode == KeyEvent.KEYCODE_DEL) {
+ pushBackspace()
+ return@setOnKeyListener true
+ }
+ if (keyCode == KeyEvent.KEYCODE_ESCAPE) {
+ dismiss()
+ return@setOnKeyListener true
+ }
+
+ return@setOnKeyListener false
+ }
+ .create()
+ }
+
+ protected fun pushBackspace() {
+ val current = data.input.get()!!
+ if (current.isNotBlank()) {
+ data.input.set(current.substring(0, current.length - 1))
+ }
+ data.text.set(Collections.nCopies(data.input.get()!!.length, "*").joinToString(""))
+ }
+
+ protected fun pushDigit(digit: String) {
+ val current = data.input.get()!!
+ data.input.set(current + digit)
+ data.text.set(Collections.nCopies(data.input.get()!!.length, "*").joinToString(""))
+ setFragmentResult(RESULT_PIN, bundleOf(RESULT_PIN to data.input.get()!!))
+ }
+
+ override fun onDismiss(dialog: DialogInterface) {
+ setFragmentResult(RESULT_DISMISS, bundleOf())
+ super.onDismiss(dialog)
+ }
+
+ fun show(manager: FragmentManager) {
+ super.show(manager, TAG)
+ }
+}
\ No newline at end of file
diff --git a/pretixscan/app/src/main/res/drawable/ic_backspace_black_24dp.xml b/pretixscan/app/src/main/res/drawable/ic_backspace_black_24dp.xml
new file mode 100644
index 00000000..35b3af30
--- /dev/null
+++ b/pretixscan/app/src/main/res/drawable/ic_backspace_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/pretixscan/app/src/main/res/layout/dialog_pin.xml b/pretixscan/app/src/main/res/layout/dialog_pin.xml
index 2f01ad43..8216d3c9 100644
--- a/pretixscan/app/src/main/res/layout/dialog_pin.xml
+++ b/pretixscan/app/src/main/res/layout/dialog_pin.xml
@@ -1,32 +1,197 @@
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+ android:minWidth="@dimen/dialog_minwidth">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pretixscan/app/src/main/res/layout/view_keyboard_button.xml b/pretixscan/app/src/main/res/layout/view_keyboard_button.xml
new file mode 100644
index 00000000..6191a8ab
--- /dev/null
+++ b/pretixscan/app/src/main/res/layout/view_keyboard_button.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pretixscan/app/src/main/res/raw/about.html b/pretixscan/app/src/main/res/raw/about.html
index f58e6d04..4a02bd2f 100644
--- a/pretixscan/app/src/main/res/raw/about.html
+++ b/pretixscan/app/src/main/res/raw/about.html
@@ -19,6 +19,7 @@
Raven by Sentry – BSD 3-clause
libpretixsync by Raphael Michel – Apache License 2.0
libpretixprint by Raphael Michel – Apache License 2.0
+LolliPin (c) OrangeGangsters – MIT License
Barcodescanner Integration by Dushyanth Maguluru – Apache License 2.0
QLCipher by Zetetic LLC – BSD 3-clause
GPL license
diff --git a/pretixscan/app/src/main/res/values-de/strings.xml b/pretixscan/app/src/main/res/values-de/strings.xml
index 5dbe09b7..b347e245 100644
--- a/pretixscan/app/src/main/res/values-de/strings.xml
+++ b/pretixscan/app/src/main/res/values-de/strings.xml
@@ -154,6 +154,7 @@
Statistiken sperren
Veranstaltungswechsel sperren
Moduswechsel sperren
+ PIN eingeben
Ticketkontrolle
Synchronisierung
Benutzeroberfläche
diff --git a/pretixscan/app/src/main/res/values/attrs.xml b/pretixscan/app/src/main/res/values/attrs.xml
new file mode 100644
index 00000000..2dcbe121
--- /dev/null
+++ b/pretixscan/app/src/main/res/values/attrs.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pretixscan/app/src/main/res/values/strings.xml b/pretixscan/app/src/main/res/values/strings.xml
index ba404396..2dbc7d6a 100644
--- a/pretixscan/app/src/main/res/values/strings.xml
+++ b/pretixscan/app/src/main/res/values/strings.xml
@@ -167,6 +167,7 @@
Protect statistics
Protect event switch
Protect mode switch
+ Enter PIN
currently attending
COVID-19 Check
Mark ticket automatically as verified