QR-Codes can now be displayed after scanning.

- 2FA also supported
This commit is contained in:
2020-07-09 16:18:49 +02:00
parent a6a9d95042
commit 8865e9a394
17 changed files with 532 additions and 62 deletions

View File

@@ -12,11 +12,13 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme"> android:theme="@style/AppTheme">
<activity android:name=".ViewActivity"></activity>
<activity android:name=".PassphraseActivity" />
<activity <activity
android:name=".CreateActivity" android:name=".CreateActivity"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Light.NoActionBar" android:theme="@style/Theme.AppCompat.Light.NoActionBar"
android:windowSoftInputMode="adjustPan" android:windowSoftInputMode="adjustPan" />
android:screenOrientation="portrait" />
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:screenOrientation="portrait"> android:screenOrientation="portrait">

View File

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -1,48 +1,97 @@
package com.github.mondei1.offpass package com.github.mondei1.offpass
import android.animation.ObjectAnimator
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Bitmap
import android.graphics.Color import android.graphics.Color
import android.graphics.ColorFilter
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.print.PrintAttributes
import android.print.PrintJob
import android.print.PrintManager
import android.util.Base64
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.view.animation.LinearInterpolator import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible import com.google.zxing.BarcodeFormat
import com.google.zxing.integration.android.IntentIntegrator import com.google.zxing.integration.android.IntentIntegrator
import com.google.zxing.integration.android.IntentResult import com.google.zxing.integration.android.IntentResult
import com.journeyapps.barcodescanner.BarcodeEncoder
import dev.turingcomplete.kotlinonetimepassword.GoogleAuthenticator import dev.turingcomplete.kotlinonetimepassword.GoogleAuthenticator
import kotlinx.android.synthetic.main.activity_create.* import kotlinx.android.synthetic.main.activity_create.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import java.text.DateFormat import java.io.ByteArrayOutputStream
import java.text.SimpleDateFormat
import java.util.* import java.util.*
class CreateActivity : AppCompatActivity() { class CreateActivity : AppCompatActivity() {
private var fragment_title: TextInput? = null
private var fragment_username: TextInput? = null
private var schema: QRSchema? = null private var schema: QRSchema? = null
private var qrCodeBitmap: Bitmap? = null
private var fa_coroutine: Job? = null private var fa_coroutine: Job? = null
private var mWebView: WebView? = null
private var printJob: PrintJob? = null
fun doPrint() {
val webView = WebView(this)
webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, requesst: WebResourceRequest) = false
override fun onPageFinished(view: WebView?, url: String?) {
Log.i("Create Activity", "Template page finished loading")
createWebPrintJob(webView)
mWebView = null
}
}
var htmlDocument = String(this.resources.openRawResource(
this.resources.getIdentifier("print", "raw", this.packageName)
).readBytes()).replace("\n", "")
// Prepare html document
var byteArrayOutputStream = ByteArrayOutputStream()
this.qrCodeBitmap?.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream)
htmlDocument = htmlDocument.replace("\$DATE", Date().time.toString())
.replace("\$HINT", "Not configurable yet!")
.replace("\$QRCODE", Base64.encodeToString(byteArrayOutputStream.toByteArray(), Base64.NO_WRAP))
Log.i("Create Activity", htmlDocument)
webView.loadDataWithBaseURL("file:///android_asset/", htmlDocument, "text/HTML", "UTF-8", null)
mWebView = webView
}
fun createWebPrintJob(webView: WebView) {
(this.getSystemService(Context.PRINT_SERVICE) as? PrintManager)?.let { printManager ->
val jobName = "Offpass Document"
val printAdapter = webView.createPrintDocumentAdapter(jobName)
printManager.print(
jobName,
printAdapter,
PrintAttributes.Builder().build()
).also { printJob ->
this.printJob = printJob
}
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
//this.schema = QRSchema(this) this.schema = QRSchema(this)
//this.schema!!.decrypted_raw = "%JtuB4O9M42%Gitea|Nicolas|542superGoOD_pW&|klier.nicolas@protonmail.com|\$ul|(\$vb)\$O4|()What's your favorite series%Rick and morty|(2fa)otpauth://totp/OffPass%20Test?secret=d34gfkki5dkd5knifysrpgndd5xb2c7eddwki7ya4pvoisfa5c3ko5pv&issuer=Nicolas%20Klier" this.schema!!.decrypted_raw = "%JtuB4O9M42%Gitea|Nicolas|542superGoOD_pW&|klier.nicolas@protonmail.com|\$ul|(\$vb)\$O4|()What's your favorite series%Rick and morty|(2fa)otpauth://totp/OffPass%20Test?secret=d34gfkki5dkd5knifysrpgndd5xb2c7eddwki7ya4pvoisfa5c3ko5pv&issuer=Nicolas%20Klier"
//this.schema!!.parse(this) this.schema!!.parse(this)
//this.schema!!.build(arrayOf("website_url", "2fa", "What's your favorite series"), "123") this.schema!!.build(arrayOf("website_url", "2fa", "What's your favorite series"), "123")
//this.schema!!.decrypt(this.schema!!.raw, "123") this.schema!!.decrypt(this.schema!!.raw, "123")
setSupportActionBar(findViewById(R.id.toolbar)) setSupportActionBar(findViewById(R.id.toolbar))
@@ -51,6 +100,20 @@ class CreateActivity : AppCompatActivity() {
Log.i("CREATE", "Back got clicked!") Log.i("CREATE", "Back got clicked!")
finish() finish()
} }
print_button.setOnClickListener {
// Set up printing
this.schema = QRSchema(this)
this.schema!!.title = title_input.text.toString()
this.schema!!.username = username_input.text.toString()
this.schema!!.password = password_input.text.toString()
this.schema!!.email = email_input.text.toString()
this.schema!!.website_url = url_input.text.toString()
this.schema!!.build(arrayOf(), "123")
val barcodeEncoder: BarcodeEncoder = BarcodeEncoder()
this.qrCodeBitmap = barcodeEncoder.encodeBitmap(this.schema!!.raw, BarcodeFormat.QR_CODE, 400, 400)
doPrint()
}
fa_input.isFocusable = false fa_input.isFocusable = false

View File

@@ -117,7 +117,7 @@ class CryptoOperations {
// Performing actual crypto operation // Performing actual crypto operation
cipher.init(Cipher.DECRYPT_MODE, keySpec, IvParameterSpec(iv)) cipher.init(Cipher.DECRYPT_MODE, keySpec, IvParameterSpec(iv))
val ciphered = cipher.doFinal(encrypted_raw) val ciphered = cipher.doFinal(encrypted_raw) // TODO: Catch error if decryption goes wrong
// Concat everything into one byte array // Concat everything into one byte array
val byteBuffer: ByteBuffer = ByteBuffer.allocate(ciphered.size) val byteBuffer: ByteBuffer = ByteBuffer.allocate(ciphered.size)

View File

@@ -2,7 +2,6 @@ package com.github.mondei1.offpass
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.os.HandlerThread
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@@ -61,8 +60,11 @@ class MainActivity : AppCompatActivity() {
if (result.contents == null) { if (result.contents == null) {
Toast.makeText(this, "Cancelled", Toast.LENGTH_LONG).show() Toast.makeText(this, "Cancelled", Toast.LENGTH_LONG).show()
} else { } else {
Log.i("SCANNER", "Scanned: " + result.contents) // TODO: Check if scanned QR-Code is an OffPass one.
Toast.makeText(this, "Scanned: " + result.contents, Toast.LENGTH_LONG).show() val intent: Intent = Intent(this, PassphraseActivity::class.java)
intent.putExtra("raw", result.contents)
startActivity(intent)
//Toast.makeText(this, "Scanned: " + result.contents, Toast.LENGTH_LONG).show()
} }
} }

View File

@@ -0,0 +1,34 @@
package com.github.mondei1.offpass
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.Editable
import kotlinx.android.synthetic.main.activity_passphrase.*
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.runBlocking
class PassphraseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_passphrase)
val raw: String = intent.getStringExtra("raw")!!
val qrSchema: QRSchema = QRSchema(this)
back.setOnClickListener {
finish()
}
decrypt_button.setOnClickListener {
decrypt_button.text = "Decrypting ..."
runBlocking {
qrSchema.decrypt(raw, passphrase_input.text.toString())
}
val intent: Intent = Intent(this, ViewActivity::class.java)
intent.putExtra("decrypted_raw",qrSchema.decrypted_raw)
startActivity(intent)
}
}
}

View File

@@ -18,7 +18,7 @@ class QRSchema {
var decrypted_raw: String = "" var decrypted_raw: String = ""
// Parsed content // Parsed content
lateinit var session_key: String var session_key: String = ""
lateinit var title: String lateinit var title: String
lateinit var username: String lateinit var username: String
lateinit var password: String lateinit var password: String
@@ -100,6 +100,11 @@ class QRSchema {
var key = fields[i].substring(1, closingBracket) var key = fields[i].substring(1, closingBracket)
var value = fields[i].substring(closingBracket+1, fields[i].length) var value = fields[i].substring(closingBracket+1, fields[i].length)
// We got a security question/awnser
if (key == "") {
val qa = value.split("%")
question_awnser.put(qa[0], qa[1]);
} else {
// Check if key and or value are compressed // Check if key and or value are compressed
if (key.startsWith("$")) { if (key.startsWith("$")) {
runBlocking { runBlocking {
@@ -112,11 +117,6 @@ class QRSchema {
} }
} }
// We got a security question/awnser
if (key == "") {
val qa = value.split("%")
question_awnser.put(qa[0], qa[1]);
} else {
custom.put(key, value) custom.put(key, value)
} }
} else { } else {

View File

@@ -0,0 +1,77 @@
package com.github.mondei1.offpass
import android.R.attr.key
import android.annotation.SuppressLint
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.Color
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import dev.turingcomplete.kotlinonetimepassword.GoogleAuthenticator
import kotlinx.android.synthetic.main.activity_view.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class ViewActivity : AppCompatActivity() {
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view)
val qrSchema: QRSchema = QRSchema(this)
back.setOnClickListener {
val i = Intent(this, MainActivity::class.java)
i.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
startActivity(i)
}
qrSchema.decrypted_raw = intent.getStringExtra("decrypted_raw")!!
qrSchema.parse(this)
title_label.text = qrSchema.title
username_input.setText(qrSchema.username, TextView.BufferType.EDITABLE)
password_input.setText(qrSchema.password, TextView.BufferType.EDITABLE)
email_input.setText(qrSchema.email, TextView.BufferType.EDITABLE)
url_input.setText(qrSchema.website_url, TextView.BufferType.EDITABLE)
if (qrSchema.custom.containsKey("2fa")) {
val fa_uri = Uri.parse(qrSchema.custom.get("2fa"))
val fa_generator = GoogleAuthenticator(base32secret = fa_uri.getQueryParameter("secret").toString())
fa_label.text = "${fa_uri.path?.replace("/", "")} (${fa_uri.getQueryParameter("issuer")})"
fa_progress.visibility = View.VISIBLE
GlobalScope.launch(Dispatchers.Main) {
while (true) {
fa_input.setText(fa_generator.generate())
var seconds_remaining = CryptoOperations().getRemainingTOTPTime()
Log.i("2FA Generator", "Remaining: $seconds_remaining")
if (seconds_remaining == 0) seconds_remaining = 30
// Change color
if (seconds_remaining >= 15) {
fa_progress.progressTintList = ColorStateList.valueOf(Color.GREEN)
} else if (seconds_remaining < 15 && seconds_remaining >= 8) {
fa_progress.progressTintList = ColorStateList.valueOf(Color.YELLOW)
} else {
fa_progress.progressTintList = ColorStateList.valueOf(Color.RED)
}
fa_progress.progress = seconds_remaining
delay(1000)
}
}
} else {
fa_layout.visibility = View.GONE
}
}
}

View File

@@ -14,19 +14,19 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/colorPrimaryDark" android:background="@color/colorPrimaryDark"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent">
app:title="Create">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<ImageButton <ImageButton
android:id="@+id/textView4" android:id="@+id/print_button"
style="@style/Widget.AppCompat.Button.Colored" style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="86dp" android:layout_width="86dp"
android:layout_height="50dp" android:layout_height="50dp"
android:layout_margin="0dp" android:layout_margin="0dp"
android:layout_marginStart="64dp"
android:backgroundTint="#F8F8F8" android:backgroundTint="#F8F8F8"
android:padding="0dp" android:padding="0dp"
android:src="@drawable/printer" android:src="@drawable/printer"

View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimaryDark"
tools:context=".PassphraseActivity">
<EditText
android:id="@+id/passphrase_input"
android:layout_width="234dp"
android:layout_height="wrap_content"
android:layout_marginStart="88dp"
android:layout_marginEnd="89dp"
android:backgroundTint="#757575"
android:ems="10"
android:hint="Passphrase"
android:includeFontPadding="false"
android:inputType="textPassword"
android:letterSpacing="0.2"
android:paddingBottom="15dp"
android:textAlignment="center"
android:textColor="@color/colorPrimary"
android:textColorHint="#757575"
android:textSize="20dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.513" />
<Button
android:id="@+id/decrypt_button"
style="@style/Widget.AppCompat.Button"
android:layout_width="152dp"
android:layout_height="59dp"
android:layout_marginStart="161dp"
android:layout_marginTop="72dp"
android:layout_marginEnd="162dp"
android:background="#00FFFFFF"
android:backgroundTint="#001C1C1C"
android:hapticFeedbackEnabled="false"
android:text="Decrypt"
android:textColor="@color/colorPrimary"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/passphrase_input" />
<Button
android:id="@+id/back"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="163dp"
android:layout_marginEnd="160dp"
android:layout_marginBottom="30dp"
android:fontFamily="sans-serif-light"
android:text="Back"
android:textColor="#AAAAAA"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,227 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
style="@style/AppTheme"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary"
tools:context=".ViewActivity">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimaryDark"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:titleMargin="0dp">
</androidx.appcompat.widget.Toolbar>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="horizontal">
<ImageButton
android:id="@+id/back"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="56dp"
android:layout_height="50dp"
android:rotation="180"
android:src="@drawable/baseline_arrow_forward_white_36"
android:text="" />
<TextView
android:id="@+id/title_label"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:fontFamily="sans-serif-black"
android:paddingTop="8dp"
android:text="TITLE"
android:textAlignment="viewStart"
android:textColor="@color/colorPrimary"
android:textSize="24sp" />
</LinearLayout>
<ScrollView
android:id="@+id/scrollView2"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/fa_layout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/url_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="20dp"
android:text="Login URL"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/email_input" />
<TextView
android:id="@+id/email_input"
style="@style/Widget.AppCompat.AutoCompleteTextView"
android:layout_width="354dp"
android:layout_height="48dp"
android:layout_marginStart="16dp"
android:background="@color/colorPrimary"
android:ems="10"
android:fontFamily="sans-serif-black"
android:singleLine="true"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/email_label" />
<TextView
android:id="@+id/username_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="20dp"
android:text="Username"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/password_input"
android:layout_width="288dp"
android:layout_height="68dp"
android:layout_marginStart="16dp"
android:background="@color/colorPrimary"
android:ems="10"
android:fontFamily="sans-serif-black"
android:inputType="textPassword"
android:singleLine="true"
android:textSize="28sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/password_label" />
<TextView
android:id="@+id/username_input"
style="@style/Widget.AppCompat.AutoCompleteTextView"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_marginStart="16dp"
android:background="@color/colorPrimary"
android:ems="10"
android:fontFamily="sans-serif-black"
android:singleLine="true"
android:textSize="24sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/username_label" />
<TextView
android:id="@+id/password_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="20dp"
android:text="Password"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/username_input" />
<TextView
android:id="@+id/url_input"
style="@style/Widget.AppCompat.AutoCompleteTextView"
android:layout_width="354dp"
android:layout_height="48dp"
android:layout_marginStart="15dp"
android:background="@color/colorPrimary"
android:ems="10"
android:fontFamily="sans-serif-black"
android:singleLine="true"
android:textSize="24sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/url_label" />
<TextView
android:id="@+id/email_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="20dp"
android:text="E-Mail"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/password_input" />
<ImageButton
android:id="@+id/password_hide"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="60dp"
android:background="@color/colorPrimary"
app:layout_constraintBottom_toBottomOf="@+id/password_input"
app:layout_constraintStart_toEndOf="@+id/password_input"
app:layout_constraintTop_toTopOf="@+id/password_input"
app:layout_constraintVertical_bias="0.437"
app:srcCompat="@drawable/baseline_visibility_black_36" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/fa_layout"
android:layout_width="match_parent"
android:layout_height="120dp"
android:background="@color/colorPrimary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@id/scrollView2">
<TextView
android:id="@+id/fa_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="Two factor authentication"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/fa_input"
style="@style/Widget.AppCompat.AutoCompleteTextView"
android:layout_width="354dp"
android:layout_height="48dp"
android:layout_marginStart="16dp"
android:background="@color/colorPrimary"
android:editable="false"
android:ems="10"
android:fontFamily="sans-serif-black"
android:hint="TAP TO SCAN"
android:inputType="none|textEmailAddress"
android:singleLine="true"
android:textSize="24sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/fa_label" />
<ProgressBar
android:id="@+id/fa_progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="379dp"
android:layout_height="15dp"
android:layout_marginStart="16dp"
android:layout_marginTop="-10dp"
android:layout_marginEnd="16dp"
android:max="30"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/fa_input" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -49,7 +49,7 @@
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<TextView <TextView
android:id="@+id/textView4" android:id="@+id/print_button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"

View File

@@ -6,19 +6,25 @@
</head> </head>
<body> <body>
<style> <style>
@media print {
body {
max-height: 100vh;
page-break-after: always;
}
}
@font-face { @font-face {
font-family: Roboto; font-family: Roboto;
src: url(font/robotolight.ttf); src: url(../font/robotolight.ttf);
font-weight: lighter; font-weight: lighter;
} }
@font-face { @font-face {
font-family: Roboto; font-family: Roboto;
src: url(font/robotoregular.ttf); src: url(../font/robotoregular.ttf);
font-weight: normal; font-weight: normal;
} }
@font-face { @font-face {
font-family: Roboto; font-family: Roboto;
src: url(font/robotobold.ttf); src: url(../font/robotobold.ttf);
font-weight: bold; font-weight: bold;
} }
@@ -26,6 +32,12 @@
text-align: center; text-align: center;
font-family: Roboto; font-family: Roboto;
} }
#content {
overflow: hidden;
height: 100vh;
width: 100vw;
border: 3px rgba(0, 0, 0, 0) solid;
}
h1 { h1 {
margin-top: 8rem; margin-top: 8rem;
font-size: 36pt; font-size: 36pt;
@@ -39,29 +51,16 @@
} }
img { img {
margin-top: 5rem; margin-top: 5rem;
height: 400px; height: 300px;
} }
/* Background stripes */ /* Background stripes */
#red { #bg img {
position: absolute; position: fixed;
bottom: -350px; bottom: 10px;
left: 0; left: 0;
z-index: -1; width: 100vw;
width: 340px; height: auto;
height: 815px;
background-color: #FF2A2A;
transform: rotate(130deg);
}
#black {
position: absolute;
bottom: -320px;
right: 0;
z-index: -1;
width: 380px;
height: 815px;
background-color: #1C1C1C;
transform: rotate(-130deg);
} }
</style> </style>
<div id="content"> <div id="content">
@@ -71,9 +70,10 @@
<p id="hint_title">Password hint</p> <p id="hint_title">Password hint</p>
<p>$HINT</p> <p>$HINT</p>
<img src="drawable/dummy_qr_code.png"> <img src="data:image/png;base64, $QRCODE">
<div id="red"></div> <div id="bg">
<div id="black"></div> <img src="print_background.png">
</div>
</div> </div>
</body> </body>
</html> </html>