From 5bd747fac279c86f5a6153237d5a14daa2cbd0b6 Mon Sep 17 00:00:00 2001 From: Maximilian Richt Date: Tue, 14 Nov 2023 14:21:26 +0100 Subject: [PATCH 1/2] Replace com.andrognito.pinlockview (jcenter only dependency) with custom implementation --- pretixscan/app/build.gradle | 1 - .../pretixscan/droid/ui/KeyboardButtonView.kt | 88 +++++++ .../pretixscan/droid/ui/MainActivity.kt | 44 +--- .../pretix/pretixscan/droid/ui/PinDialog.kt | 97 ++++++++ .../res/drawable/ic_backspace_black_24dp.xml | 9 + .../app/src/main/res/layout/dialog_pin.xml | 223 +++++++++++++++--- .../main/res/layout/view_keyboard_button.xml | 29 +++ pretixscan/app/src/main/res/raw/about.html | 1 + .../app/src/main/res/values-de/strings.xml | 1 + pretixscan/app/src/main/res/values/attrs.xml | 12 + .../app/src/main/res/values/strings.xml | 1 + 11 files changed, 444 insertions(+), 62 deletions(-) create mode 100644 pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KeyboardButtonView.kt create mode 100644 pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/PinDialog.kt create mode 100644 pretixscan/app/src/main/res/drawable/ic_backspace_black_24dp.xml create mode 100644 pretixscan/app/src/main/res/layout/view_keyboard_button.xml create mode 100644 pretixscan/app/src/main/res/values/attrs.xml 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..e3bf558e --- /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(context!!) + .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 From f69fe15ef0ef7f40e43e3e6e7e08259f1bb28f4c Mon Sep 17 00:00:00 2001 From: Maximilian Richt Date: Tue, 14 Nov 2023 14:31:38 +0100 Subject: [PATCH 2/2] Fix lint issue by using requireContext() --- .../src/main/java/eu/pretix/pretixscan/droid/ui/PinDialog.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index e3bf558e..5a070822 100644 --- 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 @@ -48,7 +48,7 @@ class PinDialog : DialogFragment() { binding.keyboardButtonView9.setOnClickListener { pushDigit("9") } binding.keyboardButtonViewBackspace.setOnClickListener { pushBackspace() } - return MaterialAlertDialogBuilder(context!!) + return MaterialAlertDialogBuilder(requireContext()) .setView(binding.root) .setOnKeyListener { _, keyCode, event -> if (event.action != KeyEvent.ACTION_DOWN) return@setOnKeyListener false