initial work on pgp activity
This commit is contained in:
parent
902cbd78b0
commit
c3eb4f0a41
7 changed files with 555 additions and 13 deletions
|
@ -68,6 +68,7 @@ dependencies {
|
||||||
compile 'org.apache.commons:commons-io:1.3.2'
|
compile 'org.apache.commons:commons-io:1.3.2'
|
||||||
compile 'com.jayway.android.robotium:robotium-solo:5.3.1'
|
compile 'com.jayway.android.robotium:robotium-solo:5.3.1'
|
||||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
|
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
|
||||||
|
compile 'com.android.support.constraint:constraint-layout:1.0.2'
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|
|
@ -84,6 +84,8 @@
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
</application>
|
<activity android:name=".crypto.PgpActivity"
|
||||||
|
android:parentActivityName=".PasswordStore"/>
|
||||||
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
@ -30,6 +30,7 @@ import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.zeapo.pwdstore.crypto.PgpActivity;
|
||||||
import com.zeapo.pwdstore.crypto.PgpHandler;
|
import com.zeapo.pwdstore.crypto.PgpHandler;
|
||||||
import com.zeapo.pwdstore.git.GitActivity;
|
import com.zeapo.pwdstore.git.GitActivity;
|
||||||
import com.zeapo.pwdstore.git.GitAsyncTask;
|
import com.zeapo.pwdstore.git.GitAsyncTask;
|
||||||
|
@ -395,10 +396,11 @@ public class PasswordStore extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void decryptPassword(PasswordItem item) {
|
public void decryptPassword(PasswordItem item) {
|
||||||
Intent intent = new Intent(this, PgpHandler.class);
|
Intent intent = new Intent(this, PgpActivity.class);
|
||||||
intent.putExtra("NAME", item.toString());
|
intent.putExtra("NAME", item.toString());
|
||||||
intent.putExtra("FILE_PATH", item.getFile().getAbsolutePath());
|
intent.putExtra("FILE_PATH", item.getFile().getAbsolutePath());
|
||||||
intent.putExtra("Operation", "DECRYPT");
|
intent.putExtra("REPO_PATH", PasswordRepository.getRepositoryDirectory(getApplicationContext()).getAbsolutePath());
|
||||||
|
intent.putExtra("OPERATION", "DECRYPT");
|
||||||
|
|
||||||
// Adds shortcut
|
// Adds shortcut
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||||
|
@ -459,9 +461,10 @@ public class PasswordStore extends AppCompatActivity {
|
||||||
File currentDir = getCurrentDir();
|
File currentDir = getCurrentDir();
|
||||||
Log.i("PWDSTR", "Adding file to : " + currentDir.getAbsolutePath());
|
Log.i("PWDSTR", "Adding file to : " + currentDir.getAbsolutePath());
|
||||||
|
|
||||||
Intent intent = new Intent(this, PgpHandler.class);
|
Intent intent = new Intent(this, PgpActivity.class);
|
||||||
intent.putExtra("FILE_PATH", getCurrentDir().getAbsolutePath());
|
intent.putExtra("PARENT_PATH", getCurrentDir().getAbsolutePath());
|
||||||
intent.putExtra("Operation", "ENCRYPT");
|
intent.putExtra("REPO_PATH", PasswordRepository.getRepositoryDirectory(getApplicationContext()).getAbsolutePath());
|
||||||
|
intent.putExtra("OPERATION", "ENCRYPT");
|
||||||
startActivityForResult(intent, PgpHandler.REQUEST_CODE_ENCRYPT);
|
startActivityForResult(intent, PgpHandler.REQUEST_CODE_ENCRYPT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -620,6 +623,7 @@ public class PasswordStore extends AppCompatActivity {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO move this to an async task
|
||||||
for (String string : data.getStringArrayListExtra("Files")) {
|
for (String string : data.getStringArrayListExtra("Files")) {
|
||||||
File source = new File(string);
|
File source = new File(string);
|
||||||
if (!source.exists()) {
|
if (!source.exists()) {
|
||||||
|
|
533
app/src/main/java/com/zeapo/pwdstore/crypto/PgpActivity.kt
Normal file
533
app/src/main/java/com/zeapo/pwdstore/crypto/PgpActivity.kt
Normal file
|
@ -0,0 +1,533 @@
|
||||||
|
package com.zeapo.pwdstore.crypto
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.*
|
||||||
|
import android.graphics.Typeface
|
||||||
|
import android.os.AsyncTask
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.SystemClock
|
||||||
|
import android.preference.PreferenceManager
|
||||||
|
import android.support.v7.app.AppCompatActivity
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.text.method.PasswordTransformationMethod
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.*
|
||||||
|
import android.widget.*
|
||||||
|
import com.zeapo.pwdstore.PasswordEntry
|
||||||
|
import com.zeapo.pwdstore.R
|
||||||
|
import com.zeapo.pwdstore.UserPreference
|
||||||
|
import com.zeapo.pwdstore.pwgenDialogFragment
|
||||||
|
import kotlinx.android.synthetic.main.decrypt_layout.*
|
||||||
|
import kotlinx.android.synthetic.main.encrypt_layout.*
|
||||||
|
import org.apache.commons.io.FileUtils
|
||||||
|
import org.openintents.openpgp.IOpenPgpService2
|
||||||
|
import org.openintents.openpgp.OpenPgpError
|
||||||
|
import org.openintents.openpgp.util.OpenPgpApi
|
||||||
|
import org.openintents.openpgp.util.OpenPgpApi.*
|
||||||
|
import org.openintents.openpgp.util.OpenPgpServiceConnection
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
|
class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
|
||||||
|
val clipboard: ClipboardManager by lazy {
|
||||||
|
getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
|
}
|
||||||
|
var passwordEntry: PasswordEntry? = null
|
||||||
|
|
||||||
|
val name: String by lazy { intent.getStringExtra("NAME") }
|
||||||
|
val repoPath: String by lazy { intent.getStringExtra("REPO_PATH") }
|
||||||
|
val path: String by lazy { intent.getStringExtra("FILE_PATH") }
|
||||||
|
val parentPath: String by lazy {
|
||||||
|
// when encrypting we pass "PARENT_PATH" as we do not have a file
|
||||||
|
if (operation == "ENCRYPT") intent.getStringExtra("PARENT_PATH")
|
||||||
|
else File(path).parentFile.absolutePath
|
||||||
|
}
|
||||||
|
val cat: String by lazy { parentPath.replace(repoPath, "") }
|
||||||
|
val operation: String by lazy { intent.getStringExtra("OPERATION") }
|
||||||
|
|
||||||
|
val settings: SharedPreferences by lazy {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
}
|
||||||
|
val keyIDs: MutableSet<String> by lazy {
|
||||||
|
settings.getStringSet("openpgp_key_ids_set", emptySet())
|
||||||
|
}
|
||||||
|
var mServiceConnection: OpenPgpServiceConnection? = null
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE)
|
||||||
|
|
||||||
|
// some persistence
|
||||||
|
val providerPackageName = settings.getString("openpgp_provider_list", "")
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(providerPackageName)) {
|
||||||
|
Toast.makeText(this, this.resources.getString(R.string.provider_toast_text), Toast.LENGTH_LONG).show()
|
||||||
|
val intent = Intent(this, UserPreference::class.java)
|
||||||
|
startActivityForResult(intent, OPEN_PGP_BOUND)
|
||||||
|
} else {
|
||||||
|
// bind to service
|
||||||
|
mServiceConnection = OpenPgpServiceConnection(this, providerPackageName, this)
|
||||||
|
mServiceConnection?.bindToService()
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (operation) {
|
||||||
|
"DECRYPT" -> {
|
||||||
|
setContentView(R.layout.decrypt_layout)
|
||||||
|
crypto_password_category_decrypt.text = "$cat/"
|
||||||
|
crypto_password_file.text = name
|
||||||
|
}
|
||||||
|
"ENCRYPT" -> {
|
||||||
|
setContentView(R.layout.encrypt_layout)
|
||||||
|
title = getString(R.string.new_password_title)
|
||||||
|
crypto_password_category.text = "$cat/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
mServiceConnection?.unbindFromService()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||||
|
// Inflate the menu; this adds items to the action bar if it is present.
|
||||||
|
// Do not use the value `operation` in this case as it is not valid when editing
|
||||||
|
val menuId = when (intent.getStringExtra("OPERATION")) {
|
||||||
|
"ENCRYPT" -> R.menu.pgp_handler_new_password
|
||||||
|
"DECRYPT" -> R.menu.pgp_handler
|
||||||
|
else -> R.menu.pgp_handler
|
||||||
|
}
|
||||||
|
|
||||||
|
menuInflater.inflate(menuId, menu)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
||||||
|
when (item?.itemId) {
|
||||||
|
android.R.id.home -> {
|
||||||
|
setResult(RESULT_CANCELED)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
R.id.copy_password -> copyPasswordToClipBoard()
|
||||||
|
R.id.share_password_as_plaintext -> shareAsPlaintext()
|
||||||
|
R.id.edit_password -> editPassword()
|
||||||
|
R.id.crypto_confirm_add -> encrypt()
|
||||||
|
R.id.crypto_cancel_add -> setResult(RESULT_CANCELED)
|
||||||
|
else -> return super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleClick(view: View) {
|
||||||
|
when (view.id) {
|
||||||
|
R.id.generate_password -> {
|
||||||
|
val df = pwgenDialogFragment()
|
||||||
|
df.show(fragmentManager, "generator")
|
||||||
|
Log.wtf(TAG, "This should not happen.... PgpHandler.java#handleClick(View) default reached.")
|
||||||
|
}
|
||||||
|
else -> Log.wtf(TAG, "This should not happen.... PgpHandler.java#handleClick(View) default reached.")
|
||||||
|
}// should not happen
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun showToast(message: String) {
|
||||||
|
runOnUiThread({ Toast.makeText(this, message, Toast.LENGTH_SHORT).show() })
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleError(error: OpenPgpError) {
|
||||||
|
showToast("Error from OpenKeyChain : " + error.message)
|
||||||
|
Log.e(TAG, "onError getErrorId:" + error.errorId)
|
||||||
|
Log.e(TAG, "onError getMessage:" + error.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decryptAndVerify(): Unit {
|
||||||
|
val data = Intent()
|
||||||
|
data.action = ACTION_DECRYPT_VERIFY
|
||||||
|
|
||||||
|
val iStream = FileUtils.openInputStream(File(path))
|
||||||
|
val oStream = ByteArrayOutputStream()
|
||||||
|
|
||||||
|
val api = OpenPgpApi(this, mServiceConnection?.service)
|
||||||
|
|
||||||
|
api.executeApiAsync(data, iStream, oStream, { result: Intent? ->
|
||||||
|
when (result?.getIntExtra(RESULT_CODE, RESULT_CODE_ERROR)) {
|
||||||
|
RESULT_CODE_SUCCESS -> {
|
||||||
|
try {
|
||||||
|
val showPassword = settings.getBoolean("show_password", true)
|
||||||
|
val showExtraContent = settings.getBoolean("show_extra_content", true)
|
||||||
|
|
||||||
|
crypto_container_decrypt.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
val monoTypeface = Typeface.createFromAsset(assets, "fonts/sourcecodepro.ttf")
|
||||||
|
val entry = PasswordEntry(oStream)
|
||||||
|
|
||||||
|
passwordEntry = entry
|
||||||
|
|
||||||
|
crypto_password_show.typeface = monoTypeface
|
||||||
|
crypto_password_show.text = entry.password
|
||||||
|
|
||||||
|
crypto_password_toggle_show.visibility = if (showPassword) View.GONE else View.VISIBLE
|
||||||
|
crypto_password_show.transformationMethod = if (showPassword) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
HoldToShowPasswordTransformation(
|
||||||
|
crypto_password_toggle_show,
|
||||||
|
Runnable { crypto_password_show.text = entry.password }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.hasExtraContent()) {
|
||||||
|
crypto_extra_show_layout.visibility = if (showExtraContent) View.VISIBLE else View.GONE
|
||||||
|
|
||||||
|
crypto_extra_show.typeface = monoTypeface
|
||||||
|
crypto_extra_show.text = entry.extraContent
|
||||||
|
|
||||||
|
if (entry.hasUsername()) {
|
||||||
|
crypto_username_show.visibility = View.VISIBLE
|
||||||
|
crypto_username_show_label.visibility = View.VISIBLE
|
||||||
|
crypto_copy_username.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
crypto_copy_username.setOnClickListener { copyUsernameToClipBoard(entry.username) }
|
||||||
|
crypto_username_show.typeface = monoTypeface
|
||||||
|
crypto_username_show.text = entry.username
|
||||||
|
} else {
|
||||||
|
crypto_username_show.visibility = View.GONE
|
||||||
|
crypto_username_show_label.visibility = View.GONE
|
||||||
|
crypto_copy_username.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.getBoolean("copy_on_decrypt", true)) {
|
||||||
|
copyPasswordToClipBoard()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "An Exception occurred", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RESULT_CODE_USER_INTERACTION_REQUIRED -> {
|
||||||
|
Log.i(TAG, "RESULT_CODE_USER_INTERACTION_REQUIRED")
|
||||||
|
|
||||||
|
val pi: PendingIntent = result.getParcelableExtra(RESULT_INTENT)
|
||||||
|
try {
|
||||||
|
this@PgpActivity.startIntentSenderFromChild(
|
||||||
|
this@PgpActivity, pi.intentSender, REQUEST_DECRYPT,
|
||||||
|
null, 0, 0, 0)
|
||||||
|
} catch (e: IntentSender.SendIntentException) {
|
||||||
|
Log.e(TAG, "SendIntentException", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RESULT_CODE_ERROR -> {
|
||||||
|
// TODO show what kind of error it is
|
||||||
|
/* For example:
|
||||||
|
* No suitable key found -> no key in OpenKeyChain
|
||||||
|
*
|
||||||
|
* Check in open-pgp-lib how their definitions and error code
|
||||||
|
*/
|
||||||
|
val error: OpenPgpError = result.getParcelableExtra(RESULT_ERROR)
|
||||||
|
handleError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts the password and the extra content
|
||||||
|
*/
|
||||||
|
private fun encrypt(): Unit {
|
||||||
|
val name = crypto_password_file_edit.text.toString()
|
||||||
|
val pass = crypto_password_edit.text.toString()
|
||||||
|
val extra = crypto_extra_edit.text.toString()
|
||||||
|
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
showToast(resources.getString(R.string.file_toast_text))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pass.isEmpty() && extra.isEmpty()) {
|
||||||
|
showToast(resources.getString(R.string.empty_toast_text))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val data = Intent()
|
||||||
|
data.action = OpenPgpApi.ACTION_ENCRYPT
|
||||||
|
|
||||||
|
// EXTRA_KEY_IDS requires long[]
|
||||||
|
val longKeys = keyIDs.map { it.toLong() }
|
||||||
|
data.putExtra(OpenPgpApi.EXTRA_KEY_IDS, longKeys.toLongArray())
|
||||||
|
data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true)
|
||||||
|
|
||||||
|
// TODO Check if we could use PasswordEntry to generate the file
|
||||||
|
val iStream = ByteArrayInputStream("$pass\n$extra".toByteArray(Charset.forName("UTF-8")))
|
||||||
|
val oStream = ByteArrayOutputStream()
|
||||||
|
|
||||||
|
val api = OpenPgpApi(this, mServiceConnection?.service)
|
||||||
|
val path = "$parentPath/$name.gpg"
|
||||||
|
|
||||||
|
api.executeApiAsync(data, iStream, oStream, { result: Intent? ->
|
||||||
|
when (result?.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
|
||||||
|
OpenPgpApi.RESULT_CODE_SUCCESS -> {
|
||||||
|
try {
|
||||||
|
// TODO This might fail, we should check that the write is successful
|
||||||
|
val outputStream = FileUtils.openOutputStream(File(path))
|
||||||
|
outputStream.write(oStream.toByteArray())
|
||||||
|
outputStream.close()
|
||||||
|
|
||||||
|
val returnIntent = Intent()
|
||||||
|
returnIntent.putExtra("CREATED_FILE", path)
|
||||||
|
returnIntent.putExtra("NAME", name)
|
||||||
|
|
||||||
|
// if coming from decrypt screen->edit button
|
||||||
|
if (intent.getBooleanExtra("fromDecrypt", false)) {
|
||||||
|
data.putExtra("needCommit", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
setResult(RESULT_OK, returnIntent)
|
||||||
|
finish()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "An Exception occurred", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OpenPgpApi.RESULT_CODE_ERROR -> {
|
||||||
|
// TODO show what kind of error it is
|
||||||
|
/* For example:
|
||||||
|
* No suitable key found -> no key in OpenKeyChain
|
||||||
|
*
|
||||||
|
* Check in open-pgp-lib how their definitions and error code
|
||||||
|
*/
|
||||||
|
val error: OpenPgpError = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR)
|
||||||
|
handleError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens EncryptActivity with the information for this file to be edited
|
||||||
|
*/
|
||||||
|
fun editPassword() {
|
||||||
|
setContentView(R.layout.encrypt_layout)
|
||||||
|
|
||||||
|
title = getString(R.string.edit_password_title)
|
||||||
|
|
||||||
|
val monoTypeface = Typeface.createFromAsset(assets, "fonts/sourcecodepro.ttf")
|
||||||
|
crypto_password_edit.setText(passwordEntry?.password)
|
||||||
|
crypto_password_edit.typeface = monoTypeface
|
||||||
|
crypto_extra_edit.setText(passwordEntry?.extraContent)
|
||||||
|
crypto_extra_edit.typeface = monoTypeface
|
||||||
|
|
||||||
|
crypto_password_category.text = "$cat/"
|
||||||
|
crypto_password_file_edit.setText(name)
|
||||||
|
crypto_password_file_edit.isEnabled = false
|
||||||
|
|
||||||
|
delayTask?.skip = true
|
||||||
|
|
||||||
|
val data = Intent(this, PgpActivity::class.java)
|
||||||
|
data.putExtra("OPERATION", "ENCRYPT")
|
||||||
|
data.putExtra("fromDecrypt", true)
|
||||||
|
intent = data
|
||||||
|
invalidateOptionsMenu()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(e: Exception?) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The action to take when the PGP service is bound
|
||||||
|
*/
|
||||||
|
override fun onBound(service: IOpenPgpService2?) {
|
||||||
|
if (operation == "DECRYPT") decryptAndVerify()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
Log.d(TAG, "onActivityResult resultCode: " + resultCode)
|
||||||
|
|
||||||
|
if (data == null) {
|
||||||
|
setResult(Activity.RESULT_CANCELED, null)
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// try again after user interaction
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
when (requestCode) {
|
||||||
|
REQUEST_DECRYPT -> decryptAndVerify()
|
||||||
|
else -> {
|
||||||
|
setResult(Activity.RESULT_OK)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (resultCode == Activity.RESULT_CANCELED) {
|
||||||
|
setResult(Activity.RESULT_CANCELED, data)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class HoldToShowPasswordTransformation constructor(button: Button, private val onToggle: Runnable) :
|
||||||
|
PasswordTransformationMethod(), View.OnTouchListener {
|
||||||
|
private var shown = false
|
||||||
|
|
||||||
|
init {
|
||||||
|
button.setOnTouchListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTransformation(charSequence: CharSequence, view: View): CharSequence {
|
||||||
|
return if (shown) charSequence else super.getTransformation("12345", view)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTouch(view: View, motionEvent: MotionEvent): Boolean {
|
||||||
|
when (motionEvent.action) {
|
||||||
|
MotionEvent.ACTION_DOWN -> {
|
||||||
|
shown = true
|
||||||
|
onToggle.run()
|
||||||
|
}
|
||||||
|
MotionEvent.ACTION_UP -> {
|
||||||
|
shown = false
|
||||||
|
onToggle.run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun copyPasswordToClipBoard() {
|
||||||
|
|
||||||
|
if (findViewById(R.id.crypto_password_show) == null)
|
||||||
|
return
|
||||||
|
|
||||||
|
setTimer()
|
||||||
|
|
||||||
|
val clip = ClipData.newPlainText("pgp_handler_result_pm", passwordEntry?.password)
|
||||||
|
clipboard.primaryClip = clip
|
||||||
|
|
||||||
|
var clearAfter = 45
|
||||||
|
try {
|
||||||
|
clearAfter = Integer.parseInt(settings.getString("general_show_time", "45"))
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
|
// ignore and keep default
|
||||||
|
}
|
||||||
|
|
||||||
|
showToast(this.resources.getString(R.string.clipboard_password_toast_text, clearAfter))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun copyUsernameToClipBoard(username: String) {
|
||||||
|
val clip = ClipData.newPlainText("pgp_handler_result_pm", username)
|
||||||
|
clipboard.primaryClip = clip
|
||||||
|
showToast(resources.getString(R.string.clipboard_username_toast_text))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun shareAsPlaintext() {
|
||||||
|
if (findViewById(R.id.share_password_as_plaintext) == null)
|
||||||
|
return
|
||||||
|
|
||||||
|
val sendIntent = Intent()
|
||||||
|
sendIntent.action = Intent.ACTION_SEND
|
||||||
|
sendIntent.putExtra(Intent.EXTRA_TEXT, passwordEntry?.password)
|
||||||
|
sendIntent.type = "text/plain"
|
||||||
|
startActivity(Intent.createChooser(sendIntent, resources.getText(R.string.send_plaintext_password_to)))//Always show a picker to give the user a chance to cancel
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTimer() {
|
||||||
|
delayTask?.skip = true
|
||||||
|
|
||||||
|
// launch a new one
|
||||||
|
delayTask = DelayShow(this)
|
||||||
|
delayTask?.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class DelayShow(val activity: PgpActivity) : AsyncTask<Void, Int, Boolean>() {
|
||||||
|
internal val pb: ProgressBar by lazy { pbLoading }
|
||||||
|
internal var skip = false
|
||||||
|
internal var showTime: Int = 0
|
||||||
|
|
||||||
|
val settings: SharedPreferences by lazy {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPreExecute() {
|
||||||
|
try {
|
||||||
|
showTime = Integer.parseInt(settings.getString("general_show_time", "45"))
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
|
showTime = 45
|
||||||
|
}
|
||||||
|
|
||||||
|
val container = findViewById(R.id.crypto_container_decrypt) as LinearLayout
|
||||||
|
container.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
val extraText = findViewById(R.id.crypto_extra_show) as TextView
|
||||||
|
|
||||||
|
if (extraText.text.isNotEmpty())
|
||||||
|
findViewById(R.id.crypto_extra_show_layout).visibility = View.VISIBLE
|
||||||
|
|
||||||
|
if (showTime == 0) {
|
||||||
|
// treat 0 as forever, and the user must exit and/or clear clipboard on their own
|
||||||
|
cancel(true)
|
||||||
|
} else {
|
||||||
|
this.pb.max = showTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun doInBackground(vararg params: Void): Boolean? {
|
||||||
|
var current = 0
|
||||||
|
while (current < showTime) {
|
||||||
|
SystemClock.sleep(1000)
|
||||||
|
current++
|
||||||
|
publishProgress(current)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPostExecute(b: Boolean?) {
|
||||||
|
if (skip) return
|
||||||
|
|
||||||
|
// only clear the clipboard if we automatically copied the password to it
|
||||||
|
if (settings.getBoolean("copy_on_decrypt", true)) {
|
||||||
|
Log.d("DELAY_SHOW", "Clearing the clipboard")
|
||||||
|
val clip = ClipData.newPlainText("pgp_handler_result_pm", "")
|
||||||
|
clipboard.primaryClip = clip
|
||||||
|
if (settings.getBoolean("clear_clipboard_20x", false)) {
|
||||||
|
val handler = Handler()
|
||||||
|
for (i in 0..18) {
|
||||||
|
val count = i.toString()
|
||||||
|
handler.postDelayed({ clipboard.primaryClip = ClipData.newPlainText(count, count) }, (i * 500).toLong())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crypto_password_show != null) {
|
||||||
|
passwordEntry = null
|
||||||
|
// clear password; if decrypt changed to encrypt layout via edit button, no need
|
||||||
|
crypto_password_show.text = ""
|
||||||
|
crypto_extra_show.text = ""
|
||||||
|
crypto_extra_show_layout.visibility = View.INVISIBLE
|
||||||
|
crypto_container_decrypt.visibility = View.INVISIBLE
|
||||||
|
setResult(Activity.RESULT_CANCELED)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onProgressUpdate(vararg values: Int?) {
|
||||||
|
this.pb.progress = values[0] ?: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val OPEN_PGP_BOUND = 101
|
||||||
|
val REQUEST_EDIT = 201
|
||||||
|
val REQUEST_DECRYPT = 202
|
||||||
|
|
||||||
|
val TAG = "PgpActivity"
|
||||||
|
|
||||||
|
private var delayTask: DelayShow? = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -201,7 +201,7 @@ public class PgpHandler extends AppCompatActivity implements OpenPgpServiceConne
|
||||||
|| findViewById(R.id.crypto_container).getVisibility() != View.VISIBLE)
|
|| findViewById(R.id.crypto_container).getVisibility() != View.VISIBLE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CharSequence category = ((TextView) findViewById(R.id.crypto_password_category)).getText();
|
CharSequence category = ((TextView) findViewById(R.id.crypto_password_category_decrypt)).getText();
|
||||||
CharSequence file = ((TextView) findViewById(R.id.crypto_password_file)).getText();
|
CharSequence file = ((TextView) findViewById(R.id.crypto_password_file)).getText();
|
||||||
CharSequence password = ((TextView) findViewById(R.id.crypto_password_show)).getText();
|
CharSequence password = ((TextView) findViewById(R.id.crypto_password_show)).getText();
|
||||||
CharSequence extra = ((TextView) findViewById(R.id.crypto_extra_show)).getText();
|
CharSequence extra = ((TextView) findViewById(R.id.crypto_extra_show)).getText();
|
||||||
|
@ -211,7 +211,7 @@ public class PgpHandler extends AppCompatActivity implements OpenPgpServiceConne
|
||||||
((EditText) findViewById(R.id.crypto_password_edit)).setTypeface(monoTypeface);
|
((EditText) findViewById(R.id.crypto_password_edit)).setTypeface(monoTypeface);
|
||||||
((EditText) findViewById(R.id.crypto_extra_edit)).setTypeface(monoTypeface);
|
((EditText) findViewById(R.id.crypto_extra_edit)).setTypeface(monoTypeface);
|
||||||
|
|
||||||
((TextView) findViewById(R.id.crypto_password_category)).setText(category);
|
((TextView) findViewById(R.id.crypto_password_category_decrypt)).setText(category);
|
||||||
((EditText) findViewById(R.id.crypto_password_file_edit)).setText(file);
|
((EditText) findViewById(R.id.crypto_password_file_edit)).setText(file);
|
||||||
((EditText) findViewById(R.id.crypto_password_edit)).setText(password);
|
((EditText) findViewById(R.id.crypto_password_edit)).setText(password);
|
||||||
((EditText) findViewById(R.id.crypto_extra_edit)).setText(extra);
|
((EditText) findViewById(R.id.crypto_extra_edit)).setText(extra);
|
||||||
|
@ -748,7 +748,7 @@ public class PgpHandler extends AppCompatActivity implements OpenPgpServiceConne
|
||||||
.replace(PasswordRepository.getRepositoryDirectory(getApplicationContext()).getAbsolutePath(), "");
|
.replace(PasswordRepository.getRepositoryDirectory(getApplicationContext()).getAbsolutePath(), "");
|
||||||
String cat = new File(path).getParentFile().getName();
|
String cat = new File(path).getParentFile().getName();
|
||||||
|
|
||||||
((TextView) findViewById(R.id.crypto_password_category)).setText(cat + "/");
|
((TextView) findViewById(R.id.crypto_password_category_decrypt)).setText(cat + "/");
|
||||||
decryptAndVerify(new Intent());
|
decryptAndVerify(new Intent());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -760,7 +760,7 @@ public class PgpHandler extends AppCompatActivity implements OpenPgpServiceConne
|
||||||
String cat = extra.getString("FILE_PATH");
|
String cat = extra.getString("FILE_PATH");
|
||||||
cat = cat.replace(PasswordRepository.getRepositoryDirectory(getApplicationContext()).getAbsolutePath(), "");
|
cat = cat.replace(PasswordRepository.getRepositoryDirectory(getApplicationContext()).getAbsolutePath(), "");
|
||||||
cat = cat + "/";
|
cat = cat + "/";
|
||||||
((TextView) findViewById(R.id.crypto_password_category)).setText(cat);
|
((TextView) findViewById(R.id.crypto_password_category_decrypt)).setText(cat);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "GET_KEY_ID":
|
case "GET_KEY_ID":
|
||||||
|
@ -778,7 +778,7 @@ public class PgpHandler extends AppCompatActivity implements OpenPgpServiceConne
|
||||||
String cat = new File(extra.getString("FILE_PATH").replace(PasswordRepository.getRepositoryDirectory(getApplicationContext()).getAbsolutePath(), ""))
|
String cat = new File(extra.getString("FILE_PATH").replace(PasswordRepository.getRepositoryDirectory(getApplicationContext()).getAbsolutePath(), ""))
|
||||||
.getParentFile().getName();
|
.getParentFile().getName();
|
||||||
|
|
||||||
((TextView) findViewById(R.id.crypto_password_category)).setText(cat + "/");
|
((TextView) findViewById(R.id.crypto_password_category_decrypt)).setText(cat + "/");
|
||||||
edit(new Intent());
|
edit(new Intent());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/crypto_password_category"
|
android:id="@+id/crypto_password_category_decrypt"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
|
@ -54,7 +54,7 @@
|
||||||
tools:ignore="ContentDescription" />
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/crypto_container"
|
android:id="@+id/crypto_container_decrypt"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -199,6 +199,8 @@
|
||||||
<string name="autofill_paste_username">Paste username?\n\n%s</string>
|
<string name="autofill_paste_username">Paste username?\n\n%s</string>
|
||||||
<string name="autofill_toast_username">Select an editable field to past the username.\nUsername is available for %d seconds.</string>
|
<string name="autofill_toast_username">Select an editable field to past the username.\nUsername is available for %d seconds.</string>
|
||||||
<string name="ssh_key_does_not_exist">Unable to open the ssh private key, please check that the file exists</string>
|
<string name="ssh_key_does_not_exist">Unable to open the ssh private key, please check that the file exists</string>
|
||||||
|
<string name="new_password_title">New password</string>
|
||||||
|
<string name="edit_password_title">Editing</string>
|
||||||
<string name="autofill_ins_1_hint">Screenshot of accessibility services</string>
|
<string name="autofill_ins_1_hint">Screenshot of accessibility services</string>
|
||||||
<string name="autofill_ins_2_hint">Screenshot of toggle in accessibility services</string>
|
<string name="autofill_ins_2_hint">Screenshot of toggle in accessibility services</string>
|
||||||
<string name="autofill_ins_3_hint">Screenshot of autofill service in action</string>
|
<string name="autofill_ins_3_hint">Screenshot of autofill service in action</string>
|
||||||
|
|
Loading…
Reference in a new issue