Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0b01d374 authored by Ricki Hirner's avatar Ricki Hirner
Browse files

Use ViewModel/data binding for TrustCertificateActivity

parent a24d07c5
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ buildscript {
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:3.3.1'
        classpath 'com.android.tools.build:gradle:3.3.2'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:${dokka_version}"
    }
@@ -33,6 +33,8 @@ android {
        targetSdkVersion 28
    }

    dataBinding.enabled = true

    lintOptions {
        disable 'MissingTranslation', 'ExtraTranslation'	// translations from Transifex are not always up to date
        disable "OnClick"     // doesn't recognize Kotlin onClick methods
@@ -48,10 +50,13 @@ dependencies {

    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.cardview:cardview:1.0.0'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
    implementation 'androidx.lifecycle:lifecycle-livedata:2.0.0'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.0.0'

    androidTestImplementation 'androidx.test:runner:1.1.1'
    androidTestImplementation 'androidx.test:rules:1.1.1'
    androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.12.1'
    androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.12.2'
    androidTestImplementation 'commons-io:commons-io:2.6'
    androidTestImplementation 'org.apache.commons:commons-lang3:3.8.1'

+79 −62
Original line number Diff line number Diff line
@@ -11,84 +11,48 @@ package at.bitfire.cert4android
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.CheckBox
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProviders
import at.bitfire.cert4android.databinding.ActivityTrustCertificateBinding
import java.io.ByteArrayInputStream
import java.security.MessageDigest
import java.security.cert.CertificateFactory
import java.security.cert.CertificateParsingException
import java.security.cert.X509Certificate
import java.security.spec.MGF1ParameterSpec.SHA1
import java.security.spec.MGF1ParameterSpec.SHA256
import java.text.DateFormat
import java.util.*
import java.util.logging.Level
import kotlin.concurrent.thread

class TrustCertificateActivity: AppCompatActivity() {

    companion object {
        const val EXTRA_CERTIFICATE = "certificate"

        val certFactory = CertificateFactory.getInstance("X.509")!!
    }

    private lateinit var model: Model

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_trust_certificate)
        showCertificate()
        model = ViewModelProviders.of(this).get(Model::class.java)
        model.processIntent(intent)

        val binding = DataBindingUtil.setContentView<ActivityTrustCertificateBinding>(this, R.layout.activity_trust_certificate)
        binding.lifecycleOwner = this
        binding.model = model
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        this.intent = intent
        showCertificate()
    }

    private fun showCertificate() {
        val raw = intent.getByteArrayExtra(EXTRA_CERTIFICATE)
        (certFactory.generateCertificate(ByteArrayInputStream(raw)) as X509Certificate?)?.let { cert ->
            val subject: String
            try {
                subject = if (cert.issuerAlternativeNames != null) {
                    val sb = StringBuilder()
                    for (altName in cert.subjectAlternativeNames.orEmpty()) {
                        val name = altName[1]
                        if (name is String)
                            sb.append("[").append(altName[0]).append("]").append(name).append(" ")
                    }
                    sb.toString()
                } else
                    cert.subjectDN.name

                var tv = findViewById<TextView>(R.id.issuedFor)
                tv.text = subject

                tv = findViewById(R.id.issuedBy)
                tv.text = cert.issuerDN.toString()

                val formatter = DateFormat.getDateInstance(DateFormat.LONG)
                tv = findViewById(R.id.validity_period)
                tv.text = getString(R.string.trust_certificate_validity_period_value,
                        formatter.format(cert.notBefore),
                        formatter.format(cert.notAfter))

                tv = findViewById(R.id.fingerprint_sha1)
                tv.text = fingerprint(cert, "SHA-1")
                tv = findViewById(R.id.fingerprint_sha256)
                tv.text = fingerprint(cert, "SHA-256")
            } catch(e: CertificateParsingException) {
                Constants.log.log(Level.WARNING, "Couldn't parse certificate", e)
            }
        model.processIntent(intent)
    }

        val btnAccept = findViewById<Button>(R.id.accept)
        val cb = findViewById<CheckBox>(R.id.fingerprint_ok)
        cb.setOnCheckedChangeListener { _, state -> btnAccept.isEnabled = state }
    }


    fun acceptCertificate(view: View) {
        sendDecision(true)
        finish()
@@ -110,7 +74,58 @@ class TrustCertificateActivity: AppCompatActivity() {
    }


    private fun fingerprint(cert: X509Certificate, algorithm: String) =
    class Model: ViewModel() {

        companion object {
            val certFactory = CertificateFactory.getInstance("X.509")!!
        }

        val issuedFor = MutableLiveData<String>()
        val issuedBy = MutableLiveData<String>()

        val validFrom = MutableLiveData<String>()
        val validTo = MutableLiveData<String>()

        val sha1 = MutableLiveData<String>()
        val sha256 = MutableLiveData<String>()

        val verifiedByUser = MutableLiveData<Boolean>()

        fun processIntent(intent: Intent?) {
            intent?.getByteArrayExtra(EXTRA_CERTIFICATE)?.let { raw ->
                thread {
                    val cert = certFactory.generateCertificate(ByteArrayInputStream(raw)) as? X509Certificate ?: return@thread

                    try {
                        val subject = if (cert.issuerAlternativeNames != null) {
                            val sb = StringBuilder()
                            for (altName in cert.subjectAlternativeNames.orEmpty()) {
                                val name = altName[1]
                                if (name is String)
                                    sb.append("[").append(altName[0]).append("]").append(name).append(" ")
                            }
                            sb.toString()
                        } else
                            cert.subjectDN.name
                        issuedFor.postValue(subject)

                        issuedBy.postValue(cert.issuerDN.toString())

                        val formatter = DateFormat.getDateInstance(DateFormat.LONG)
                        validFrom.postValue(formatter.format(cert.notBefore))
                        validTo.postValue(formatter.format(cert.notAfter))

                        sha1.postValue(fingerprint(cert, SHA1.digestAlgorithm))
                        sha256.postValue(fingerprint(cert, SHA256.digestAlgorithm))

                    } catch(e: CertificateParsingException) {
                        Constants.log.log(Level.WARNING, "Couldn't parse certificate", e)
                    }
                }
            }
        }

        fun fingerprint(cert: X509Certificate, algorithm: String) =
                try {
                    val md = MessageDigest.getInstance(algorithm)
                    "$algorithm: ${hexString(md.digest(cert.encoded))}"
@@ -118,9 +133,11 @@ class TrustCertificateActivity: AppCompatActivity() {
                    e.message ?: "Couldn't create message digest"
                }

    private fun hexString(data: ByteArray): String {
        fun hexString(data: ByteArray): String {
            val str = data.mapTo(LinkedList()) { String.format("%02x", it) }
            return str.joinToString(":")
        }

    }

}
 No newline at end of file
+121 −113
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
            xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <variable
            name="model"
            type="at.bitfire.cert4android.TrustCertificateActivity.Model"/>
    </data>

    <ScrollView
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:layout_margin="@dimen/activity_margin">
@@ -42,10 +49,10 @@
                        android:textStyle="bold"
                        android:text="@string/trust_certificate_issued_for"/>
                    <TextView
                    android:id="@+id/issuedFor"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginBottom="16dp"
                        android:text="@{model.issuedFor}"
                        tools:text="CN=example.com"/>

                    <TextView
@@ -54,10 +61,10 @@
                        android:textStyle="bold"
                        android:text="@string/trust_certificate_issued_by"/>
                    <TextView
                    android:id="@+id/issuedBy"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginBottom="16dp"
                        android:text="@{model.issuedBy}"
                        tools:text="CN=example.com"/>

                    <TextView
@@ -66,10 +73,10 @@
                        android:textStyle="bold"
                        android:text="@string/trust_certificate_validity_period"/>
                    <TextView
                    android:id="@+id/validity_period"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginBottom="16dp"
                        android:text="@{@string/trust_certificate_validity_period_value(model.validFrom, model.validTo)}"
                        tools:text="1.1.1000 – 2.2.2000"/>

                    <TextView
@@ -78,23 +85,25 @@
                        android:textStyle="bold"
                        android:text="@string/trust_certificate_fingerprints"/>
                    <TextView
                    android:id="@+id/fingerprint_sha1"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="4dp"
                        android:text="@{model.sha1}"
                        tools:text="SHA-1: abcdef"/>
                    <TextView
                    android:id="@+id/fingerprint_sha256"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="4dp"
                        android:layout_marginBottom="16dp"
                        android:text="@{model.sha256}"
                        tools:text="SHA-256: abcdef"/>

                    <CheckBox
                    android:id="@+id/fingerprint_ok"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:padding="8dp"
                        android:layout_marginBottom="8dp"
                        android:checked="@={model.verifiedByUser}"
                        android:text="@string/trust_certificate_fingerprint_verified"/>

                    <androidx.appcompat.widget.ButtonBarLayout
@@ -102,16 +111,14 @@
                        android:layout_height="wrap_content">

                        <Button
                        android:id="@+id/accept"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            style="@style/Widget.AppCompat.Button.Borderless.Colored"
                            android:text="@string/trust_certificate_accept"
                            android:onClick="acceptCertificate"
                        android:enabled="false"/>
                            android:enabled="@{model.verifiedByUser}"/>

                        <Button
                        android:id="@+id/reject"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            style="@style/Widget.AppCompat.Button.Borderless"
@@ -132,3 +139,4 @@

        </LinearLayout>
    </ScrollView>
</layout>