Proper SafetyNet UI databinding

This commit is contained in:
topjohnwu 2020-09-13 00:23:23 -07:00
parent e51a3dacb9
commit 60e1e07e87
5 changed files with 51 additions and 22 deletions

View File

@ -1,19 +1,23 @@
package com.topjohnwu.magisk.databinding package com.topjohnwu.magisk.databinding
import android.animation.ValueAnimator import android.animation.ValueAnimator
import android.content.res.ColorStateList
import android.graphics.Paint import android.graphics.Paint
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.util.TypedValue import android.util.TypedValue
import android.view.ContextThemeWrapper
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.* import android.widget.Button
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.cardview.widget.CardView import androidx.cardview.widget.CardView
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isInvisible import androidx.core.view.isInvisible
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.core.widget.ImageViewCompat
import androidx.databinding.BindingAdapter import androidx.databinding.BindingAdapter
import androidx.databinding.InverseBindingAdapter import androidx.databinding.InverseBindingAdapter
import androidx.databinding.InverseBindingListener import androidx.databinding.InverseBindingListener
@ -288,3 +292,8 @@ fun CardView.setCardBackgroundColorAttr(attr: Int) {
context.theme.resolveAttribute(attr, tv, true) context.theme.resolveAttribute(attr, tv, true)
setCardBackgroundColor(tv.data) setCardBackgroundColor(tv.data)
} }
@BindingAdapter("tint")
fun ImageView.setTint(color: Int) {
ImageViewCompat.setImageTintList(this, ColorStateList.valueOf(color))
}

View File

@ -1,5 +1,6 @@
package com.topjohnwu.magisk.di package com.topjohnwu.magisk.di
import android.view.ContextThemeWrapper
import com.topjohnwu.magisk.ui.MainViewModel import com.topjohnwu.magisk.ui.MainViewModel
import com.topjohnwu.magisk.ui.flash.FlashFragmentArgs import com.topjohnwu.magisk.ui.flash.FlashFragmentArgs
import com.topjohnwu.magisk.ui.flash.FlashViewModel import com.topjohnwu.magisk.ui.flash.FlashViewModel
@ -21,7 +22,7 @@ val viewModelModules = module {
viewModel { HomeViewModel(get()) } viewModel { HomeViewModel(get()) }
viewModel { LogViewModel(get()) } viewModel { LogViewModel(get()) }
viewModel { ModuleViewModel(get(), get(), get()) } viewModel { ModuleViewModel(get(), get(), get()) }
viewModel { SafetynetViewModel() } viewModel { (context: ContextThemeWrapper) -> SafetynetViewModel(context) }
viewModel { SettingsViewModel(get()) } viewModel { SettingsViewModel(get()) }
viewModel { SuperuserViewModel(get(), get()) } viewModel { SuperuserViewModel(get(), get()) }
viewModel { ThemeViewModel() } viewModel { ThemeViewModel() }

View File

@ -8,11 +8,14 @@ import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseUIFragment import com.topjohnwu.magisk.arch.BaseUIFragment
import com.topjohnwu.magisk.databinding.FragmentSafetynetMd2Binding import com.topjohnwu.magisk.databinding.FragmentSafetynetMd2Binding
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
class SafetynetFragment : BaseUIFragment<SafetynetViewModel, FragmentSafetynetMd2Binding>() { class SafetynetFragment : BaseUIFragment<SafetynetViewModel, FragmentSafetynetMd2Binding>() {
override val layoutRes = R.layout.fragment_safetynet_md2 override val layoutRes = R.layout.fragment_safetynet_md2
override val viewModel by viewModel<SafetynetViewModel>() override val viewModel by viewModel<SafetynetViewModel>() {
parametersOf(activity)
}
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()

View File

@ -1,23 +1,20 @@
package com.topjohnwu.magisk.ui.safetynet package com.topjohnwu.magisk.ui.safetynet
import android.util.TypedValue
import android.view.ContextThemeWrapper
import androidx.databinding.Bindable import androidx.databinding.Bindable
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseViewModel import com.topjohnwu.magisk.arch.BaseViewModel
import com.topjohnwu.magisk.ui.safetynet.SafetyNetState.*
import com.topjohnwu.magisk.utils.set import com.topjohnwu.magisk.utils.set
import org.json.JSONObject import org.json.JSONObject
enum class SafetyNetState {
LOADING, PASS, FAILED
}
data class SafetyNetResult( data class SafetyNetResult(
val response: JSONObject? = null, val response: JSONObject? = null,
val dismiss: Boolean = false val dismiss: Boolean = false
) )
class SafetynetViewModel : BaseViewModel() { class SafetynetViewModel(context: ContextThemeWrapper) : BaseViewModel() {
@get:Bindable @get:Bindable
var safetyNetTitle = R.string.empty var safetyNetTitle = R.string.empty
@ -36,23 +33,33 @@ class SafetynetViewModel : BaseViewModel() {
set(value) = set(value, field, { field = it }, BR.evalType) set(value) = set(value, field, { field = it }, BR.evalType)
@get:Bindable @get:Bindable
val isChecking get() = currentState == LOADING var isChecking = false
@get:Bindable set(value) = set(value, field, { field = it }, BR.checking)
val isFailed get() = currentState == FAILED
@get:Bindable
val isSuccess get() = currentState == PASS
private var currentState = LOADING @get:Bindable
set(value) = set(value, field, { field = it }, BR.checking, BR.failed, BR.success) var isSuccess = false
set(value) = set(value, field, { field = it }, BR.success, BR.textColor)
@get:Bindable
val textColor get() = if (isSuccess) colorOnPrimary else colorOnError
private val colorOnPrimary: Int
private val colorOnError: Int
init { init {
val tv = TypedValue()
context.theme.resolveAttribute(R.attr.colorOnPrimary, tv, true)
colorOnPrimary = tv.data
context.theme.resolveAttribute(R.attr.colorOnError, tv, true)
colorOnError = tv.data
cachedResult?.also { cachedResult?.also {
resolveResponse(SafetyNetResult(it)) resolveResponse(SafetyNetResult(it))
} ?: attest() } ?: attest()
} }
private fun attest() { private fun attest() {
currentState = LOADING isChecking = true
CheckSafetyNetEvent { CheckSafetyNetEvent {
resolveResponse(it) resolveResponse(it)
}.publish() }.publish()
@ -61,6 +68,8 @@ class SafetynetViewModel : BaseViewModel() {
fun reset() = attest() fun reset() = attest()
private fun resolveResponse(response: SafetyNetResult) { private fun resolveResponse(response: SafetyNetResult) {
isChecking = false
if (response.dismiss) { if (response.dismiss) {
back() back()
return return
@ -76,19 +85,19 @@ class SafetynetViewModel : BaseViewModel() {
ctsState = cts ctsState = cts
basicIntegrityState = basic basicIntegrityState = basic
evalType = if (eval.contains("HARDWARE")) "HARDWARE" else "BASIC" evalType = if (eval.contains("HARDWARE")) "HARDWARE" else "BASIC"
currentState = if (result) PASS else FAILED isSuccess = result
safetyNetTitle = safetyNetTitle =
if (result) R.string.safetynet_attest_success if (result) R.string.safetynet_attest_success
else R.string.safetynet_attest_failure else R.string.safetynet_attest_failure
}.onFailure { }.onFailure {
currentState = FAILED isSuccess = false
ctsState = false ctsState = false
basicIntegrityState = false basicIntegrityState = false
evalType = "N/A" evalType = "N/A"
safetyNetTitle = R.string.safetynet_res_invalid safetyNetTitle = R.string.safetynet_res_invalid
} }
} ?: { } ?: {
currentState = FAILED isSuccess = false
ctsState = false ctsState = false
basicIntegrityState = false basicIntegrityState = false
evalType = "N/A" evalType = "N/A"

View File

@ -80,6 +80,7 @@
android:gravity="center" android:gravity="center"
android:text="@{viewModel.safetyNetTitle}" android:text="@{viewModel.safetyNetTitle}"
android:textAppearance="@style/AppearanceFoundation.Display.OnPrimary" android:textAppearance="@style/AppearanceFoundation.Display.OnPrimary"
android:textColor="@{viewModel.textColor}"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintHorizontal_chainStyle="packed"
@ -93,7 +94,7 @@
android:layout_height="4dp" android:layout_height="4dp"
android:layout_marginTop="@dimen/l2" android:layout_marginTop="@dimen/l2"
app:srcCompat="@drawable/bg_divider_rounded_on_primary" app:srcCompat="@drawable/bg_divider_rounded_on_primary"
app:tint="?colorOnPrimary" app:tint="@{viewModel.textColor}"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/safetynet_title" /> app:layout_constraintTop_toBottomOf="@+id/safetynet_title" />
@ -116,6 +117,7 @@
android:text="basicIntegrity" android:text="basicIntegrity"
android:textAppearance="@style/AppearanceFoundation.Body.OnPrimary" android:textAppearance="@style/AppearanceFoundation.Body.OnPrimary"
android:textStyle="bold" android:textStyle="bold"
android:textColor="@{viewModel.textColor}"
android:layout_marginTop="@dimen/l2" android:layout_marginTop="@dimen/l2"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="@id/snet_barrier" app:layout_constraintEnd_toEndOf="@id/snet_barrier"
@ -131,6 +133,7 @@
android:text="ctsProfile" android:text="ctsProfile"
android:textAppearance="@style/AppearanceFoundation.Body.OnPrimary" android:textAppearance="@style/AppearanceFoundation.Body.OnPrimary"
android:textStyle="bold" android:textStyle="bold"
android:textColor="@{viewModel.textColor}"
android:layout_marginTop="@dimen/l2" android:layout_marginTop="@dimen/l2"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="@id/snet_barrier" app:layout_constraintEnd_toEndOf="@id/snet_barrier"
@ -149,6 +152,7 @@
isSelected="@{viewModel.basicIntegrityState}" isSelected="@{viewModel.basicIntegrityState}"
android:layout_gravity="center" android:layout_gravity="center"
app:srcCompat="@drawable/ic_check_circle_md2" app:srcCompat="@drawable/ic_check_circle_md2"
app:tint="@{viewModel.textColor}"
app:layout_constraintStart_toEndOf="@id/snet_barrier" app:layout_constraintStart_toEndOf="@id/snet_barrier"
app:layout_constraintTop_toTopOf="@id/basic_text" app:layout_constraintTop_toTopOf="@id/basic_text"
app:layout_constraintBottom_toBottomOf="@id/basic_text"/> app:layout_constraintBottom_toBottomOf="@id/basic_text"/>
@ -158,6 +162,7 @@
isSelected="@{viewModel.ctsState}" isSelected="@{viewModel.ctsState}"
android:layout_gravity="center" android:layout_gravity="center"
app:srcCompat="@drawable/ic_check_circle_md2" app:srcCompat="@drawable/ic_check_circle_md2"
app:tint="@{viewModel.textColor}"
app:layout_constraintStart_toEndOf="@id/snet_barrier" app:layout_constraintStart_toEndOf="@id/snet_barrier"
app:layout_constraintTop_toTopOf="@id/cts_text" app:layout_constraintTop_toTopOf="@id/cts_text"
app:layout_constraintBottom_toBottomOf="@id/cts_text"/> app:layout_constraintBottom_toBottomOf="@id/cts_text"/>
@ -181,6 +186,7 @@
android:text="evalType" android:text="evalType"
android:textAppearance="@style/AppearanceFoundation.Body.OnPrimary" android:textAppearance="@style/AppearanceFoundation.Body.OnPrimary"
android:textStyle="bold" android:textStyle="bold"
android:textColor="@{viewModel.textColor}"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
<TextView <TextView
@ -189,6 +195,7 @@
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:textAppearance="@style/AppearanceFoundation.Body.OnPrimary" android:textAppearance="@style/AppearanceFoundation.Body.OnPrimary"
android:textStyle="bold" android:textStyle="bold"
android:textColor="@{viewModel.textColor}"
android:layout_marginStart="@dimen/l1" android:layout_marginStart="@dimen/l1"
android:text="@{viewModel.evalType}" android:text="@{viewModel.evalType}"
tools:text="HARDWARE"/> tools:text="HARDWARE"/>