diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 858d190..54bf586 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -12,11 +12,13 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
+
+
+ android:windowSoftInputMode="adjustPan" />
diff --git a/app/src/main/res/drawable/dummy_qr_code.png b/app/src/main/assets/dummy_qr_code.png
similarity index 100%
rename from app/src/main/res/drawable/dummy_qr_code.png
rename to app/src/main/assets/dummy_qr_code.png
diff --git a/app/src/main/assets/print_background.png b/app/src/main/assets/print_background.png
new file mode 100644
index 0000000..53625c2
Binary files /dev/null and b/app/src/main/assets/print_background.png differ
diff --git a/app/src/main/res/font/robotobold.ttf b/app/src/main/assets/robotobold.ttf
similarity index 100%
rename from app/src/main/res/font/robotobold.ttf
rename to app/src/main/assets/robotobold.ttf
diff --git a/app/src/main/res/font/robotoregular.ttf b/app/src/main/assets/robotoregular.ttf
similarity index 100%
rename from app/src/main/res/font/robotoregular.ttf
rename to app/src/main/assets/robotoregular.ttf
diff --git a/app/src/main/res/font/robotothin.ttf b/app/src/main/assets/robotothin.ttf
similarity index 100%
rename from app/src/main/res/font/robotothin.ttf
rename to app/src/main/assets/robotothin.ttf
diff --git a/app/src/main/java/com/github/mondei1/offpass/CreateActivity.kt b/app/src/main/java/com/github/mondei1/offpass/CreateActivity.kt
index bbd4c8d..7064c9f 100644
--- a/app/src/main/java/com/github/mondei1/offpass/CreateActivity.kt
+++ b/app/src/main/java/com/github/mondei1/offpass/CreateActivity.kt
@@ -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
diff --git a/app/src/main/java/com/github/mondei1/offpass/CryptoOperations.kt b/app/src/main/java/com/github/mondei1/offpass/CryptoOperations.kt
index 628e85f..d22bad6 100644
--- a/app/src/main/java/com/github/mondei1/offpass/CryptoOperations.kt
+++ b/app/src/main/java/com/github/mondei1/offpass/CryptoOperations.kt
@@ -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)
diff --git a/app/src/main/java/com/github/mondei1/offpass/MainActivity.kt b/app/src/main/java/com/github/mondei1/offpass/MainActivity.kt
index 0b615ef..ddb733c 100644
--- a/app/src/main/java/com/github/mondei1/offpass/MainActivity.kt
+++ b/app/src/main/java/com/github/mondei1/offpass/MainActivity.kt
@@ -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()
}
}
diff --git a/app/src/main/java/com/github/mondei1/offpass/PassphraseActivity.kt b/app/src/main/java/com/github/mondei1/offpass/PassphraseActivity.kt
new file mode 100644
index 0000000..4c8e093
--- /dev/null
+++ b/app/src/main/java/com/github/mondei1/offpass/PassphraseActivity.kt
@@ -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)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/github/mondei1/offpass/QRSchema.kt b/app/src/main/java/com/github/mondei1/offpass/QRSchema.kt
index 5391a06..3ed8ef4 100644
--- a/app/src/main/java/com/github/mondei1/offpass/QRSchema.kt
+++ b/app/src/main/java/com/github/mondei1/offpass/QRSchema.kt
@@ -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 {
diff --git a/app/src/main/java/com/github/mondei1/offpass/ViewActivity.kt b/app/src/main/java/com/github/mondei1/offpass/ViewActivity.kt
new file mode 100644
index 0000000..ec3c1f6
--- /dev/null
+++ b/app/src/main/java/com/github/mondei1/offpass/ViewActivity.kt
@@ -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
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_create.xml b/app/src/main/res/layout/activity_create.xml
index 1183075..be6d949 100644
--- a/app/src/main/res/layout/activity_create.xml
+++ b/app/src/main/res/layout/activity_create.xml
@@ -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">
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_view.xml b/app/src/main/res/layout/activity_view.xml
new file mode 100644
index 0000000..a0a6ab5
--- /dev/null
+++ b/app/src/main/res/layout/activity_view.xml
@@ -0,0 +1,227 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/create_bar.xml b/app/src/main/res/layout/create_bar.xml
index 619e358..753d49c 100644
--- a/app/src/main/res/layout/create_bar.xml
+++ b/app/src/main/res/layout/create_bar.xml
@@ -49,7 +49,7 @@
app:layout_constraintTop_toTopOf="parent" />
@@ -71,9 +70,10 @@
Password hint
$HINT
-

-
-
+

+
+

+