-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Parity signer/retrieve signature (#377)
* Extract scanning into base class * Scan screen draft * Prevent expiration dialog from showing twice * Share expiration dialog logic between show and scan screens * Fix expiration presenting * Return mortality period * Code style * Fix qr code width on long devices * Fix - scan scree breaks dapp brower webview * Center scan labels horizontally
- Loading branch information
Showing
34 changed files
with
875 additions
and
231 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,39 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<navigation xmlns:android="http://schemas.android.com/apk/res/android" | ||
xmlns:app="http://schemas.android.com/apk/res-auto" | ||
android:id="@+id/sign_parity_signer_graph" | ||
xmlns:tools="http://schemas.android.com/tools" | ||
android:id="@+id/sign_parity_signer_graph" | ||
app:startDestination="@id/showSignParitySignerFragment"> | ||
|
||
<action | ||
android:id="@+id/action_finish_parity_signer_flow" | ||
app:enterAnim="@anim/fragment_close_enter" | ||
app:exitAnim="@anim/fragment_close_exit" | ||
app:popEnterAnim="@anim/fragment_open_enter" | ||
app:popExitAnim="@anim/fragment_open_exit" | ||
app:popUpTo="@id/sign_parity_signer_graph" | ||
app:popUpToInclusive="true" /> | ||
|
||
<fragment | ||
app:useAdd="true" | ||
android:id="@+id/showSignParitySignerFragment" | ||
tools:layout="@layout/fragment_sign_parity_signer_show" | ||
android:name="io.novafoundation.nova.feature_account_impl.presentation.paritySigner.sign.show.ShowSignParitySignerFragment" | ||
android:label="FinishImportParitySignerFragment" /> | ||
android:label="FinishImportParitySignerFragment" | ||
app:useAdd="true" | ||
tools:layout="@layout/fragment_sign_parity_signer_show"> | ||
|
||
<action | ||
android:id="@+id/action_showSignParitySignerFragment_to_scanSignParitySignerFragment" | ||
app:destination="@id/scanSignParitySignerFragment" | ||
app:enterAnim="@anim/fragment_open_enter" | ||
app:exitAnim="@anim/fragment_open_exit" | ||
app:popEnterAnim="@anim/fragment_close_enter" | ||
app:popExitAnim="@anim/fragment_close_exit" /> | ||
</fragment> | ||
|
||
<fragment | ||
app:useAdd="true" | ||
android:id="@+id/scanSignParitySignerFragment" | ||
android:name="io.novafoundation.nova.feature_account_impl.presentation.paritySigner.sign.scan.ScanSignParitySignerFragment" | ||
android:label="ScanSignParitySignerFragment" | ||
tools:layout="@layout/fragment_sign_parity_signer_scan" /> | ||
</navigation> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
61 changes: 61 additions & 0 deletions
61
common/src/main/java/io/novafoundation/nova/common/presentation/scan/ScanQrFragment.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package io.novafoundation.nova.common.presentation.scan | ||
|
||
import android.view.WindowManager | ||
import androidx.annotation.CallSuper | ||
import io.novafoundation.nova.common.base.BaseFragment | ||
import io.novafoundation.nova.common.utils.permissions.setupPermissionAsker | ||
|
||
abstract class ScanQrFragment<V : ScanQrViewModel> : BaseFragment<V>() { | ||
|
||
abstract val scanView: ScanView | ||
|
||
@CallSuper | ||
override fun initViews() { | ||
startScanning() | ||
} | ||
|
||
@CallSuper | ||
override fun subscribe(viewModel: V) { | ||
viewModel.scanningAvailable.observe { scanningAvailable -> | ||
if (scanningAvailable) { | ||
scanView.resume() | ||
} else { | ||
scanView.pause() | ||
} | ||
} | ||
|
||
viewModel.resetScanningEvent.observeEvent { | ||
startScanning() | ||
} | ||
|
||
setupPermissionAsker(viewModel) | ||
} | ||
|
||
override fun onStart() { | ||
super.onStart() | ||
|
||
viewModel.onStart() | ||
} | ||
|
||
override fun onResume() { | ||
super.onResume() | ||
|
||
if (viewModel.scanningAvailable.value) { | ||
scanView.resume() | ||
} | ||
|
||
requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) | ||
} | ||
|
||
override fun onPause() { | ||
super.onPause() | ||
|
||
scanView.pause() | ||
|
||
requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) | ||
} | ||
|
||
private fun startScanning() { | ||
scanView.startDecoding { viewModel.onScanned(it) } | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
common/src/main/java/io/novafoundation/nova/common/presentation/scan/ScanQrViewModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package io.novafoundation.nova.common.presentation.scan | ||
|
||
import android.Manifest | ||
import androidx.lifecycle.LiveData | ||
import androidx.lifecycle.MutableLiveData | ||
import io.novafoundation.nova.common.base.BaseViewModel | ||
import io.novafoundation.nova.common.utils.Event | ||
import io.novafoundation.nova.common.utils.permissions.PermissionsAsker | ||
import io.novafoundation.nova.common.utils.sendEvent | ||
import kotlinx.coroutines.flow.MutableStateFlow | ||
import kotlinx.coroutines.launch | ||
|
||
abstract class ScanQrViewModel( | ||
private val permissionsAsker: PermissionsAsker.Presentation, | ||
) : BaseViewModel(), PermissionsAsker by permissionsAsker { | ||
|
||
val scanningAvailable = MutableStateFlow(false) | ||
|
||
private val _resetScanningEvent = MutableLiveData<Event<Unit>>() | ||
val resetScanningEvent: LiveData<Event<Unit>> = _resetScanningEvent | ||
|
||
protected abstract suspend fun scanned(result: String) | ||
|
||
fun onScanned(result: String) { | ||
launch { | ||
scanned(result) | ||
} | ||
} | ||
|
||
fun onStart() { | ||
requirePermissions() | ||
} | ||
|
||
protected fun resetScanning() { | ||
_resetScanningEvent.sendEvent() | ||
} | ||
|
||
private fun requirePermissions() = launch { | ||
val granted = permissionsAsker.requirePermissionsOrExit(Manifest.permission.CAMERA) | ||
|
||
scanningAvailable.value = granted | ||
} | ||
} |
140 changes: 140 additions & 0 deletions
140
common/src/main/java/io/novafoundation/nova/common/presentation/scan/ScanView.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
package io.novafoundation.nova.common.presentation.scan | ||
|
||
import android.content.Context | ||
import android.graphics.Rect | ||
import android.util.AttributeSet | ||
import android.view.Gravity | ||
import android.view.View | ||
import android.view.ViewGroup | ||
import android.widget.FrameLayout | ||
import android.widget.TextView | ||
import com.google.zxing.BarcodeFormat | ||
import com.google.zxing.ResultPoint | ||
import com.journeyapps.barcodescanner.BarcodeCallback | ||
import com.journeyapps.barcodescanner.BarcodeResult | ||
import com.journeyapps.barcodescanner.DefaultDecoderFactory | ||
import io.novafoundation.nova.common.R | ||
import io.novafoundation.nova.common.utils.WithContextExtensions | ||
import io.novafoundation.nova.common.utils.makeVisible | ||
import io.novafoundation.nova.common.utils.useAttributes | ||
import kotlinx.android.synthetic.main.view_scan.view.viewScanScanner | ||
import kotlinx.android.synthetic.main.view_scan.view.viewScanSubtitle | ||
import kotlinx.android.synthetic.main.view_scan.view.viewScanTitle | ||
import kotlinx.android.synthetic.main.view_scan.view.viewScanViewFinder | ||
|
||
class ScanView @JvmOverloads constructor( | ||
context: Context, | ||
attrs: AttributeSet? = null, | ||
defStyleAttr: Int = 0 | ||
) : FrameLayout(context, attrs, defStyleAttr), WithContextExtensions by WithContextExtensions(context) { | ||
|
||
init { | ||
View.inflate(context, R.layout.view_scan, this) | ||
|
||
setupDecoder() | ||
|
||
viewScanViewFinder.setCameraPreview(viewScanScanner) | ||
|
||
viewScanViewFinder.onFinderRectChanges { | ||
positionLabels(it) | ||
} | ||
|
||
attrs?.let(::applyAttributes) | ||
} | ||
|
||
val subtitle: TextView | ||
get() = viewScanSubtitle | ||
|
||
fun resume() { | ||
viewScanScanner.resume() | ||
} | ||
|
||
fun pause() { | ||
viewScanScanner.pause() | ||
} | ||
|
||
inline fun startDecoding(crossinline onScanned: (String) -> Unit) { | ||
viewScanScanner.decodeSingle(object : BarcodeCallback { | ||
override fun barcodeResult(result: BarcodeResult) { | ||
onScanned(result.toString()) | ||
} | ||
|
||
override fun possibleResultPoints(resultPoints: MutableList<ResultPoint>?) {} | ||
}) | ||
} | ||
|
||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { | ||
super.onLayout(changed, left, top, right, bottom) | ||
|
||
if (!changed) return | ||
|
||
viewScanViewFinder.framingRect?.let { | ||
positionLabels(it) | ||
} | ||
} | ||
|
||
fun setTitle(title: String) { | ||
viewScanTitle.text = title | ||
} | ||
|
||
fun setSubtitle(subtitle: String) { | ||
viewScanSubtitle.text = subtitle | ||
} | ||
|
||
private fun setupDecoder() { | ||
val charSet = null | ||
val formats = listOf(BarcodeFormat.QR_CODE) | ||
val hints = null | ||
val inverted = false | ||
|
||
viewScanScanner.decoderFactory = DefaultDecoderFactory( | ||
formats, | ||
hints, | ||
charSet, | ||
inverted | ||
) | ||
} | ||
|
||
private fun positionLabels(finderRect: Rect) { | ||
viewScanTitle.doIfHasText { positionTitle(finderRect) } | ||
viewScanSubtitle.doIfHasText { positionSubTitle(finderRect) } | ||
} | ||
|
||
private inline fun TextView.doIfHasText(action: () -> Unit) { | ||
if (text.isNotEmpty()) action() | ||
} | ||
|
||
private fun positionTitle(finderRect: Rect) { | ||
val rectTop = finderRect.top | ||
|
||
// how much finderRect offsets from center of the screen + half of textView height since it is originally centered itself | ||
val requiredBottomMargin = height / 2 - rectTop + viewScanTitle.height / 2 | ||
|
||
viewScanTitle.layoutParams = LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply { | ||
gravity = Gravity.CENTER | ||
setMargins(16.dp, 0, 16.dp, requiredBottomMargin + 24.dp) | ||
} | ||
viewScanTitle.makeVisible() | ||
} | ||
|
||
private fun positionSubTitle(finderRect: Rect) { | ||
val rectBottom = finderRect.bottom | ||
|
||
// how much finderRect offsets from center of the screen + half of textView height since it is originally centered itself | ||
val requiredTopMargin = rectBottom - height / 2 + viewScanSubtitle.height / 2 | ||
|
||
viewScanSubtitle.layoutParams = LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply { | ||
gravity = Gravity.CENTER | ||
setMargins(16.dp, requiredTopMargin + 24.dp, 16.dp, 0) | ||
} | ||
viewScanSubtitle.makeVisible() | ||
} | ||
|
||
private fun applyAttributes(attrs: AttributeSet) = context.useAttributes(attrs, R.styleable.ScanView) { typedArray -> | ||
val title = typedArray.getString(R.styleable.ScanView_title) | ||
title?.let(::setTitle) | ||
|
||
val subTitle = typedArray.getString(R.styleable.ScanView_subTitle) | ||
subTitle?.let(::setSubtitle) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.