QR-Codes can now be displayed after scanning.
- 2FA also supported
This commit is contained in:
@@ -12,11 +12,13 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity android:name=".ViewActivity"></activity>
|
||||
<activity android:name=".PassphraseActivity" />
|
||||
<activity
|
||||
android:name=".CreateActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
|
||||
android:windowSoftInputMode="adjustPan"
|
||||
android:screenOrientation="portrait" />
|
||||
android:windowSoftInputMode="adjustPan" />
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:screenOrientation="portrait">
|
||||
|
||||
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
BIN
app/src/main/assets/print_background.png
Normal file
BIN
app/src/main/assets/print_background.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
@@ -1,48 +1,97 @@
|
||||
package com.github.mondei1.offpass
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.graphics.ColorFilter
|
||||
import android.net.Uri
|
||||
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.view.View
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import android.widget.Toast
|
||||
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.IntentResult
|
||||
import com.journeyapps.barcodescanner.BarcodeEncoder
|
||||
import dev.turingcomplete.kotlinonetimepassword.GoogleAuthenticator
|
||||
import kotlinx.android.synthetic.main.activity_create.*
|
||||
import kotlinx.coroutines.*
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.util.*
|
||||
|
||||
class CreateActivity : AppCompatActivity() {
|
||||
private var fragment_title: TextInput? = null
|
||||
private var fragment_username: TextInput? = null
|
||||
private var schema: QRSchema? = null
|
||||
private var qrCodeBitmap: Bitmap? = 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?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
//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!!.parse(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!!.parse(this)
|
||||
|
||||
//this.schema!!.build(arrayOf("website_url", "2fa", "What's your favorite series"), "123")
|
||||
//this.schema!!.decrypt(this.schema!!.raw, "123")
|
||||
this.schema!!.build(arrayOf("website_url", "2fa", "What's your favorite series"), "123")
|
||||
this.schema!!.decrypt(this.schema!!.raw, "123")
|
||||
|
||||
setSupportActionBar(findViewById(R.id.toolbar))
|
||||
|
||||
@@ -51,6 +100,20 @@ class CreateActivity : AppCompatActivity() {
|
||||
Log.i("CREATE", "Back got clicked!")
|
||||
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
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ class CryptoOperations {
|
||||
|
||||
// Performing actual crypto operation
|
||||
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
|
||||
val byteBuffer: ByteBuffer = ByteBuffer.allocate(ciphered.size)
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.github.mondei1.offpass
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.HandlerThread
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@@ -61,8 +60,11 @@ class MainActivity : AppCompatActivity() {
|
||||
if (result.contents == null) {
|
||||
Toast.makeText(this, "Cancelled", Toast.LENGTH_LONG).show()
|
||||
} else {
|
||||
Log.i("SCANNER", "Scanned: " + result.contents)
|
||||
Toast.makeText(this, "Scanned: " + result.contents, Toast.LENGTH_LONG).show()
|
||||
// TODO: Check if scanned QR-Code is an OffPass one.
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ class QRSchema {
|
||||
var decrypted_raw: String = ""
|
||||
|
||||
// Parsed content
|
||||
lateinit var session_key: String
|
||||
var session_key: String = ""
|
||||
lateinit var title: String
|
||||
lateinit var username: String
|
||||
lateinit var password: String
|
||||
@@ -100,23 +100,23 @@ class QRSchema {
|
||||
var key = fields[i].substring(1, closingBracket)
|
||||
var value = fields[i].substring(closingBracket+1, fields[i].length)
|
||||
|
||||
// Check if key and or value are compressed
|
||||
if (key.startsWith("$")) {
|
||||
runBlocking {
|
||||
key = resolve(key, context)
|
||||
}
|
||||
}
|
||||
if (value.startsWith("$")) {
|
||||
runBlocking {
|
||||
value = resolve(value, context)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
if (key.startsWith("$")) {
|
||||
runBlocking {
|
||||
key = resolve(key, context)
|
||||
}
|
||||
}
|
||||
if (value.startsWith("$")) {
|
||||
runBlocking {
|
||||
value = resolve(value, context)
|
||||
}
|
||||
}
|
||||
|
||||
custom.put(key, value)
|
||||
}
|
||||
} else {
|
||||
|
||||
77
app/src/main/java/com/github/mondei1/offpass/ViewActivity.kt
Normal file
77
app/src/main/java/com/github/mondei1/offpass/ViewActivity.kt
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,19 +14,19 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/colorPrimaryDark"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:title="Create">
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/textView4"
|
||||
android:id="@+id/print_button"
|
||||
style="@style/Widget.AppCompat.Button.Colored"
|
||||
android:layout_width="86dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_margin="0dp"
|
||||
android:layout_marginStart="64dp"
|
||||
android:backgroundTint="#F8F8F8"
|
||||
android:padding="0dp"
|
||||
android:src="@drawable/printer"
|
||||
|
||||
65
app/src/main/res/layout/activity_passphrase.xml
Normal file
65
app/src/main/res/layout/activity_passphrase.xml
Normal 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>
|
||||
227
app/src/main/res/layout/activity_view.xml
Normal file
227
app/src/main/res/layout/activity_view.xml
Normal 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>
|
||||
@@ -49,7 +49,7 @@
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView4"
|
||||
android:id="@+id/print_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
|
||||
@@ -6,19 +6,25 @@
|
||||
</head>
|
||||
<body>
|
||||
<style>
|
||||
@media print {
|
||||
body {
|
||||
max-height: 100vh;
|
||||
page-break-after: always;
|
||||
}
|
||||
}
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
src: url(font/robotolight.ttf);
|
||||
src: url(../font/robotolight.ttf);
|
||||
font-weight: lighter;
|
||||
}
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
src: url(font/robotoregular.ttf);
|
||||
src: url(../font/robotoregular.ttf);
|
||||
font-weight: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
src: url(font/robotobold.ttf);
|
||||
src: url(../font/robotobold.ttf);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@@ -26,6 +32,12 @@
|
||||
text-align: center;
|
||||
font-family: Roboto;
|
||||
}
|
||||
#content {
|
||||
overflow: hidden;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
border: 3px rgba(0, 0, 0, 0) solid;
|
||||
}
|
||||
h1 {
|
||||
margin-top: 8rem;
|
||||
font-size: 36pt;
|
||||
@@ -39,29 +51,16 @@
|
||||
}
|
||||
img {
|
||||
margin-top: 5rem;
|
||||
height: 400px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
/* Background stripes */
|
||||
#red {
|
||||
position: absolute;
|
||||
bottom: -350px;
|
||||
#bg img {
|
||||
position: fixed;
|
||||
bottom: 10px;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
width: 340px;
|
||||
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);
|
||||
width: 100vw;
|
||||
height: auto;
|
||||
}
|
||||
</style>
|
||||
<div id="content">
|
||||
@@ -71,9 +70,10 @@
|
||||
<p id="hint_title">Password hint</p>
|
||||
<p>$HINT</p>
|
||||
|
||||
<img src="drawable/dummy_qr_code.png">
|
||||
<div id="red"></div>
|
||||
<div id="black"></div>
|
||||
<img src="data:image/png;base64, $QRCODE">
|
||||
<div id="bg">
|
||||
<img src="print_background.png">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user