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: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">
|
||||||
|
|||||||
|
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
|
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
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 = ""
|
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 {
|
||||||
|
|||||||
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: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"
|
||||||
|
|||||||
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" />
|
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"
|
||||||
|
|||||||
@@ -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>
|
||||||
Reference in New Issue
Block a user