all: reformat with ktfmt 0.36

This commit is contained in:
Harsh Shandilya 2022-04-25 10:47:02 +05:30
parent 11720e9542
commit aaf6ceb8ec
No known key found for this signature in database
GPG key ID: 366D7BBAD1031E80
34 changed files with 637 additions and 614 deletions

View file

@ -42,10 +42,11 @@ object PasswordRepository {
private fun initializeRepository(repositoryDir: File) {
val builder = FileRepositoryBuilder()
repository =
runCatching { builder.setGitDir(repositoryDir).build() }.getOrElse { e ->
e.printStackTrace()
null
}
runCatching { builder.setGitDir(repositoryDir).build() }
.getOrElse { e ->
e.printStackTrace()
null
}
}
fun createRepository(repositoryDir: File) {
@ -61,40 +62,40 @@ object PasswordRepository {
if (!remotes.contains(name)) {
runCatching {
val uri = URIish(url)
val refSpec = RefSpec("+refs/head/*:refs/remotes/$name/*")
val uri = URIish(url)
val refSpec = RefSpec("+refs/head/*:refs/remotes/$name/*")
val remoteConfig = RemoteConfig(storedConfig, name)
remoteConfig.addFetchRefSpec(refSpec)
remoteConfig.addPushRefSpec(refSpec)
remoteConfig.addURI(uri)
remoteConfig.addPushURI(uri)
val remoteConfig = RemoteConfig(storedConfig, name)
remoteConfig.addFetchRefSpec(refSpec)
remoteConfig.addPushRefSpec(refSpec)
remoteConfig.addURI(uri)
remoteConfig.addPushURI(uri)
remoteConfig.update(storedConfig)
remoteConfig.update(storedConfig)
storedConfig.save()
}
storedConfig.save()
}
.onFailure { e -> e.printStackTrace() }
} else if (replace) {
runCatching {
val uri = URIish(url)
val uri = URIish(url)
val remoteConfig = RemoteConfig(storedConfig, name)
// remove the first and eventually the only uri
if (remoteConfig.urIs.size > 0) {
remoteConfig.removeURI(remoteConfig.urIs[0])
val remoteConfig = RemoteConfig(storedConfig, name)
// remove the first and eventually the only uri
if (remoteConfig.urIs.size > 0) {
remoteConfig.removeURI(remoteConfig.urIs[0])
}
if (remoteConfig.pushURIs.size > 0) {
remoteConfig.removePushURI(remoteConfig.pushURIs[0])
}
remoteConfig.addURI(uri)
remoteConfig.addPushURI(uri)
remoteConfig.update(storedConfig)
storedConfig.save()
}
if (remoteConfig.pushURIs.size > 0) {
remoteConfig.removePushURI(remoteConfig.pushURIs[0])
}
remoteConfig.addURI(uri)
remoteConfig.addPushURI(uri)
remoteConfig.update(storedConfig)
storedConfig.save()
}
.onFailure { e -> e.printStackTrace() }
}
}

View file

@ -200,18 +200,18 @@ class AutofillDecryptActivity : AppCompatActivity() {
) {
OpenPgpApi.RESULT_CODE_SUCCESS -> {
runCatching {
val entry =
withContext(Dispatchers.IO) {
@Suppress("BlockingMethodInNonBlockingContext")
passwordEntryFactory.create(decryptedOutput.toByteArray())
}
AutofillPreferences.credentialsFromStoreEntry(
this,
file,
entry,
directoryStructure
)
}
val entry =
withContext(Dispatchers.IO) {
@Suppress("BlockingMethodInNonBlockingContext")
passwordEntryFactory.create(decryptedOutput.toByteArray())
}
AutofillPreferences.credentialsFromStoreEntry(
this,
file,
entry,
directoryStructure
)
}
.getOrElse { e ->
logcat(ERROR) { e.asLog("Failed to parse password entry") }
return null
@ -221,17 +221,17 @@ class AutofillDecryptActivity : AppCompatActivity() {
val pendingIntent: PendingIntent =
result.getParcelableExtra(OpenPgpApi.RESULT_INTENT)!!
runCatching {
val intentToResume =
withContext(Dispatchers.Main) {
suspendCoroutine<Intent> { cont ->
continueAfterUserInteraction = cont
decryptInteractionRequiredAction.launch(
IntentSenderRequest.Builder(pendingIntent.intentSender).build()
)
val intentToResume =
withContext(Dispatchers.Main) {
suspendCoroutine<Intent> { cont ->
continueAfterUserInteraction = cont
decryptInteractionRequiredAction.launch(
IntentSenderRequest.Builder(pendingIntent.intentSender).build()
)
}
}
}
decryptCredential(file, intentToResume)
}
decryptCredential(file, intentToResume)
}
.getOrElse { e ->
logcat(ERROR) {
e.asLog("OpenPgpApi ACTION_DECRYPT_VERIFY failed with user interaction")

View file

@ -150,25 +150,25 @@ class AutofillDecryptActivityV2 : AppCompatActivity() {
}
.onSuccess { encryptedInput ->
runCatching {
withContext(Dispatchers.IO) {
val outputStream = ByteArrayOutputStream()
repository.decrypt(
password,
encryptedInput,
outputStream,
)
outputStream
withContext(Dispatchers.IO) {
val outputStream = ByteArrayOutputStream()
repository.decrypt(
password,
encryptedInput,
outputStream,
)
outputStream
}
}
}
.onFailure { e ->
logcat(ERROR) { e.asLog("Decryption failed") }
return null
}
.onSuccess { result ->
return runCatching {
val entry = passwordEntryFactory.create(result.toByteArray())
AutofillPreferences.credentialsFromStoreEntry(this, file, entry, directoryStructure)
}
val entry = passwordEntryFactory.create(result.toByteArray())
AutofillPreferences.credentialsFromStoreEntry(this, file, entry, directoryStructure)
}
.getOrElse { e ->
logcat(ERROR) { e.asLog("Failed to parse password entry") }
return null

View file

@ -149,31 +149,31 @@ class AutofillFilterView : AppCompatActivity() {
::PasswordViewHolder,
lifecycleScope,
) { item ->
val file = item.file.relativeTo(item.rootDir)
val pathToIdentifier = directoryStructure.getPathToIdentifierFor(file)
val identifier = directoryStructure.getIdentifierFor(file)
val accountPart = directoryStructure.getAccountPartFor(file)
check(identifier != null || accountPart != null) {
"At least one of identifier and accountPart should always be non-null"
}
title.text =
if (identifier != null) {
buildSpannedString {
if (pathToIdentifier != null) append("$pathToIdentifier/")
bold { underline { append(identifier) } }
val file = item.file.relativeTo(item.rootDir)
val pathToIdentifier = directoryStructure.getPathToIdentifierFor(file)
val identifier = directoryStructure.getIdentifierFor(file)
val accountPart = directoryStructure.getAccountPartFor(file)
check(identifier != null || accountPart != null) {
"At least one of identifier and accountPart should always be non-null"
}
title.text =
if (identifier != null) {
buildSpannedString {
if (pathToIdentifier != null) append("$pathToIdentifier/")
bold { underline { append(identifier) } }
}
} else {
accountPart
}
subtitle.apply {
if (identifier != null && accountPart != null) {
text = accountPart
visibility = View.VISIBLE
} else {
visibility = View.GONE
}
} else {
accountPart
}
subtitle.apply {
if (identifier != null && accountPart != null) {
text = accountPart
visibility = View.VISIBLE
} else {
visibility = View.GONE
}
}
}
.onItemClicked { _, item -> decryptAndFill(item) }
layoutManager = LinearLayoutManager(context)
}

View file

@ -105,27 +105,28 @@ class AutofillPublisherChangedActivity : AppCompatActivity() {
private fun showPackageInfo() {
runCatching {
with(binding) {
val packageInfo = packageManager.getPackageInfo(appPackage, PackageManager.GET_META_DATA)
val installTime = DateUtils.getRelativeTimeSpanString(packageInfo.firstInstallTime)
warningAppInstallDate.text =
getString(R.string.oreo_autofill_warning_publisher_install_time, installTime)
val appInfo = packageManager.getApplicationInfo(appPackage, PackageManager.GET_META_DATA)
warningAppName.text =
getString(
R.string.oreo_autofill_warning_publisher_app_name,
packageManager.getApplicationLabel(appInfo)
)
with(binding) {
val packageInfo = packageManager.getPackageInfo(appPackage, PackageManager.GET_META_DATA)
val installTime = DateUtils.getRelativeTimeSpanString(packageInfo.firstInstallTime)
warningAppInstallDate.text =
getString(R.string.oreo_autofill_warning_publisher_install_time, installTime)
val appInfo = packageManager.getApplicationInfo(appPackage, PackageManager.GET_META_DATA)
warningAppName.text =
getString(
R.string.oreo_autofill_warning_publisher_app_name,
packageManager.getApplicationLabel(appInfo)
)
val currentHash = computeCertificatesHash(this@AutofillPublisherChangedActivity, appPackage)
warningAppAdvancedInfo.text =
getString(
R.string.oreo_autofill_warning_publisher_advanced_info_template,
appPackage,
currentHash
)
val currentHash =
computeCertificatesHash(this@AutofillPublisherChangedActivity, appPackage)
warningAppAdvancedInfo.text =
getString(
R.string.oreo_autofill_warning_publisher_advanced_info_template,
appPackage,
currentHash
)
}
}
}
.onFailure { e ->
logcat(ERROR) { e.asLog("Failed to retrieve package info for $appPackage") }
finish()

View file

@ -178,42 +178,42 @@ class DecryptActivity : BasePgpActivity(), OpenPgpServiceConnection.OnBound {
OpenPgpApi.RESULT_CODE_SUCCESS -> {
startAutoDismissTimer()
runCatching {
val showPassword = settings.getBoolean(PreferenceKeys.SHOW_PASSWORD, true)
val entry = passwordEntryFactory.create(outputStream.toByteArray())
val showPassword = settings.getBoolean(PreferenceKeys.SHOW_PASSWORD, true)
val entry = passwordEntryFactory.create(outputStream.toByteArray())
if (settings.getBoolean(PreferenceKeys.COPY_ON_DECRYPT, false)) {
copyPasswordToClipboard(entry.password)
if (settings.getBoolean(PreferenceKeys.COPY_ON_DECRYPT, false)) {
copyPasswordToClipboard(entry.password)
}
passwordEntry = entry
invalidateOptionsMenu()
val items = arrayListOf<FieldItem>()
if (!entry.password.isNullOrBlank()) {
items.add(FieldItem.createPasswordField(entry.password!!))
}
if (entry.hasTotp()) {
items.add(FieldItem.createOtpField(entry.totp.first()))
}
if (!entry.username.isNullOrBlank()) {
items.add(FieldItem.createUsernameField(entry.username!!))
}
entry.extraContent.forEach { (key, value) ->
items.add(FieldItem(key, value, FieldItem.ActionType.COPY))
}
val adapter =
FieldItemAdapter(items, showPassword) { text -> copyTextToClipboard(text) }
binding.recyclerView.adapter = adapter
binding.recyclerView.itemAnimator = null
if (entry.hasTotp()) {
entry.totp.onEach(adapter::updateOTPCode).launchIn(lifecycleScope)
}
}
passwordEntry = entry
invalidateOptionsMenu()
val items = arrayListOf<FieldItem>()
if (!entry.password.isNullOrBlank()) {
items.add(FieldItem.createPasswordField(entry.password!!))
}
if (entry.hasTotp()) {
items.add(FieldItem.createOtpField(entry.totp.first()))
}
if (!entry.username.isNullOrBlank()) {
items.add(FieldItem.createUsernameField(entry.username!!))
}
entry.extraContent.forEach { (key, value) ->
items.add(FieldItem(key, value, FieldItem.ActionType.COPY))
}
val adapter =
FieldItemAdapter(items, showPassword) { text -> copyTextToClipboard(text) }
binding.recyclerView.adapter = adapter
binding.recyclerView.itemAnimator = null
if (entry.hasTotp()) {
entry.totp.onEach(adapter::updateOTPCode).launchIn(lifecycleScope)
}
}
.onFailure { e -> logcat(ERROR) { e.asLog() } }
}
OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED -> {

View file

@ -56,15 +56,15 @@ class GetKeyIdsActivity : BasePgpActivity() {
when (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
OpenPgpApi.RESULT_CODE_SUCCESS -> {
runCatching {
val ids =
result.getLongArrayExtra(OpenPgpApi.RESULT_KEY_IDS)?.map {
OpenPgpUtils.convertKeyIdToHex(it)
}
?: emptyList()
val keyResult = Intent().putExtra(OpenPgpApi.EXTRA_KEY_IDS, ids.toTypedArray())
setResult(RESULT_OK, keyResult)
finish()
}
val ids =
result.getLongArrayExtra(OpenPgpApi.RESULT_KEY_IDS)?.map {
OpenPgpUtils.convertKeyIdToHex(it)
}
?: emptyList()
val keyResult = Intent().putExtra(OpenPgpApi.EXTRA_KEY_IDS, ids.toTypedArray())
setResult(RESULT_OK, keyResult)
finish()
}
.onFailure { e -> logcat(ERROR) { e.asLog() } }
}
OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED -> {

View file

@ -143,15 +143,15 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
val reader = QRCodeReader()
runCatching {
val result = reader.decode(binaryBitmap)
val text = result.text
val currentExtras = binding.extraContent.text.toString()
if (currentExtras.isNotEmpty() && currentExtras.last() != '\n')
binding.extraContent.append("\n$text")
else binding.extraContent.append(text)
snackbar(message = getString(R.string.otp_import_success))
binding.otpImportButton.isVisible = false
}
val result = reader.decode(binaryBitmap)
val text = result.text
val currentExtras = binding.extraContent.text.toString()
if (currentExtras.isNotEmpty() && currentExtras.last() != '\n')
binding.extraContent.append("\n$text")
else binding.extraContent.append(text)
snackbar(message = getString(R.string.otp_import_success))
binding.otpImportButton.isVisible = false
}
.onFailure { snackbar(message = getString(R.string.otp_import_failure)) }
}
@ -411,22 +411,25 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
File(repoRoot, directory.text.toString()).findTillRoot(".gpg-id", repoRoot)
?: File(repoRoot, ".gpg-id").apply { createNewFile() }
val gpgIdentifiers =
gpgIdentifierFile.readLines().filter { it.isNotBlank() }.map { line ->
GpgIdentifier.fromString(line)
?: run {
// The line being empty means this is most likely an empty `.gpg-id`
// file
// we created. Skip the validation so we can make the user add a real
// ID.
if (line.isEmpty()) return@run
if (line.removePrefix("0x").matches("[a-fA-F0-9]{8}".toRegex())) {
snackbar(message = resources.getString(R.string.short_key_ids_unsupported))
} else {
snackbar(message = resources.getString(R.string.invalid_gpg_id))
gpgIdentifierFile
.readLines()
.filter { it.isNotBlank() }
.map { line ->
GpgIdentifier.fromString(line)
?: run {
// The line being empty means this is most likely an empty `.gpg-id`
// file
// we created. Skip the validation so we can make the user add a real
// ID.
if (line.isEmpty()) return@run
if (line.removePrefix("0x").matches("[a-fA-F0-9]{8}".toRegex())) {
snackbar(message = resources.getString(R.string.short_key_ids_unsupported))
} else {
snackbar(message = resources.getString(R.string.invalid_gpg_id))
}
return@with
}
return@with
}
}
}
if (gpgIdentifiers.isEmpty()) {
gpgKeySelectAction.launch(
Intent(this@PasswordCreationActivity, GetKeyIdsActivity::class.java)
@ -480,86 +483,92 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
when (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
OpenPgpApi.RESULT_CODE_SUCCESS -> {
runCatching {
val file = File(path)
// If we're not editing, this file should not already exist!
// Additionally, if we were editing and the incoming and outgoing
// filenames differ, it means we renamed. Ensure that the target
// doesn't already exist to prevent an accidental overwrite.
if ((!editing || (editing && suggestedName != file.nameWithoutExtension)) &&
file.exists()
) {
snackbar(message = getString(R.string.password_creation_duplicate_error))
return@runCatching
}
if (!file.isInsideRepository()) {
snackbar(message = getString(R.string.message_error_destination_outside_repo))
return@runCatching
}
withContext(Dispatchers.IO) {
file.outputStream().use { it.write(outputStream.toByteArray()) }
}
// associate the new password name with the last name's timestamp in
// history
val preference = getSharedPreferences("recent_password_history", Context.MODE_PRIVATE)
val oldFilePathHash = "$repoPath/${oldCategory?.trim('/')}/$oldFileName.gpg".base64()
val timestamp = preference.getString(oldFilePathHash)
if (timestamp != null) {
preference.edit {
remove(oldFilePathHash)
putString(file.absolutePath.base64(), timestamp)
}
}
val returnIntent = Intent()
returnIntent.putExtra(RETURN_EXTRA_CREATED_FILE, path)
returnIntent.putExtra(RETURN_EXTRA_NAME, editName)
returnIntent.putExtra(
RETURN_EXTRA_LONG_NAME,
getLongName(fullPath, repoPath, editName)
)
if (shouldGeneratePassword) {
val directoryStructure = AutofillPreferences.directoryStructure(applicationContext)
val entry = passwordEntryFactory.create(content.encodeToByteArray())
returnIntent.putExtra(RETURN_EXTRA_PASSWORD, entry.password)
val username = entry.username ?: directoryStructure.getUsernameFor(file)
returnIntent.putExtra(RETURN_EXTRA_USERNAME, username)
}
if (directoryInputLayout.isVisible &&
directoryInputLayout.isEnabled &&
oldFileName != null
) {
val oldFile = File("$repoPath/${oldCategory?.trim('/')}/$oldFileName.gpg")
if (oldFile.path != file.path && !oldFile.delete()) {
setResult(RESULT_CANCELED)
MaterialAlertDialogBuilder(this@PasswordCreationActivity)
.setTitle(R.string.password_creation_file_fail_title)
.setMessage(
getString(R.string.password_creation_file_delete_fail_message, oldFileName)
)
.setCancelable(false)
.setPositiveButton(android.R.string.ok) { _, _ -> finish() }
.show()
val file = File(path)
// If we're not editing, this file should not already exist!
// Additionally, if we were editing and the incoming and outgoing
// filenames differ, it means we renamed. Ensure that the target
// doesn't already exist to prevent an accidental overwrite.
if ((!editing || (editing && suggestedName != file.nameWithoutExtension)) &&
file.exists()
) {
snackbar(message = getString(R.string.password_creation_duplicate_error))
return@runCatching
}
}
val commitMessageRes =
if (editing) R.string.git_commit_edit_text else R.string.git_commit_add_text
lifecycleScope.launch {
commitChange(
resources.getString(commitMessageRes, getLongName(fullPath, repoPath, editName))
)
.onSuccess {
setResult(RESULT_OK, returnIntent)
finish()
if (!file.isInsideRepository()) {
snackbar(message = getString(R.string.message_error_destination_outside_repo))
return@runCatching
}
withContext(Dispatchers.IO) {
file.outputStream().use { it.write(outputStream.toByteArray()) }
}
// associate the new password name with the last name's timestamp in
// history
val preference =
getSharedPreferences("recent_password_history", Context.MODE_PRIVATE)
val oldFilePathHash =
"$repoPath/${oldCategory?.trim('/')}/$oldFileName.gpg".base64()
val timestamp = preference.getString(oldFilePathHash)
if (timestamp != null) {
preference.edit {
remove(oldFilePathHash)
putString(file.absolutePath.base64(), timestamp)
}
}
val returnIntent = Intent()
returnIntent.putExtra(RETURN_EXTRA_CREATED_FILE, path)
returnIntent.putExtra(RETURN_EXTRA_NAME, editName)
returnIntent.putExtra(
RETURN_EXTRA_LONG_NAME,
getLongName(fullPath, repoPath, editName)
)
if (shouldGeneratePassword) {
val directoryStructure =
AutofillPreferences.directoryStructure(applicationContext)
val entry = passwordEntryFactory.create(content.encodeToByteArray())
returnIntent.putExtra(RETURN_EXTRA_PASSWORD, entry.password)
val username = entry.username ?: directoryStructure.getUsernameFor(file)
returnIntent.putExtra(RETURN_EXTRA_USERNAME, username)
}
if (directoryInputLayout.isVisible &&
directoryInputLayout.isEnabled &&
oldFileName != null
) {
val oldFile = File("$repoPath/${oldCategory?.trim('/')}/$oldFileName.gpg")
if (oldFile.path != file.path && !oldFile.delete()) {
setResult(RESULT_CANCELED)
MaterialAlertDialogBuilder(this@PasswordCreationActivity)
.setTitle(R.string.password_creation_file_fail_title)
.setMessage(
getString(R.string.password_creation_file_delete_fail_message, oldFileName)
)
.setCancelable(false)
.setPositiveButton(android.R.string.ok) { _, _ -> finish() }
.show()
return@runCatching
}
}
val commitMessageRes =
if (editing) R.string.git_commit_edit_text else R.string.git_commit_add_text
lifecycleScope.launch {
commitChange(
resources.getString(
commitMessageRes,
getLongName(fullPath, repoPath, editName)
)
)
.onSuccess {
setResult(RESULT_OK, returnIntent)
finish()
}
}
}
}
.onFailure { e ->
if (e is IOException) {
logcat(ERROR) { e.asLog("Failed to write password file") }

View file

@ -119,15 +119,15 @@ class PasswordCreationActivityV2 : BasePgpActivity() {
val reader = QRCodeReader()
runCatching {
val result = reader.decode(binaryBitmap)
val text = result.text
val currentExtras = binding.extraContent.text.toString()
if (currentExtras.isNotEmpty() && currentExtras.last() != '\n')
binding.extraContent.append("\n$text")
else binding.extraContent.append(text)
snackbar(message = getString(R.string.otp_import_success))
binding.otpImportButton.isVisible = false
}
val result = reader.decode(binaryBitmap)
val text = result.text
val currentExtras = binding.extraContent.text.toString()
if (currentExtras.isNotEmpty() && currentExtras.last() != '\n')
binding.extraContent.append("\n$text")
else binding.extraContent.append(text)
snackbar(message = getString(R.string.otp_import_success))
binding.otpImportButton.isVisible = false
}
.onFailure { snackbar(message = getString(R.string.otp_import_failure)) }
}
@ -357,86 +357,87 @@ class PasswordCreationActivityV2 : BasePgpActivity() {
lifecycleScope.launch(Dispatchers.Main) {
runCatching {
val result =
withContext(Dispatchers.IO) {
val outputStream = ByteArrayOutputStream()
repository.encrypt(content.byteInputStream(), outputStream)
outputStream
}
val file = File(path)
// If we're not editing, this file should not already exist!
// Additionally, if we were editing and the incoming and outgoing
// filenames differ, it means we renamed. Ensure that the target
// doesn't already exist to prevent an accidental overwrite.
if ((!editing || (editing && suggestedName != file.nameWithoutExtension)) && file.exists()
) {
snackbar(message = getString(R.string.password_creation_duplicate_error))
return@runCatching
}
if (!file.isInsideRepository()) {
snackbar(message = getString(R.string.message_error_destination_outside_repo))
return@runCatching
}
withContext(Dispatchers.IO) { file.writeBytes(result.toByteArray()) }
// associate the new password name with the last name's timestamp in
// history
val preference = getSharedPreferences("recent_password_history", Context.MODE_PRIVATE)
val oldFilePathHash = "$repoPath/${oldCategory?.trim('/')}/$oldFileName.gpg".base64()
val timestamp = preference.getString(oldFilePathHash)
if (timestamp != null) {
preference.edit {
remove(oldFilePathHash)
putString(file.absolutePath.base64(), timestamp)
}
}
val returnIntent = Intent()
returnIntent.putExtra(RETURN_EXTRA_CREATED_FILE, path)
returnIntent.putExtra(RETURN_EXTRA_NAME, editName)
returnIntent.putExtra(RETURN_EXTRA_LONG_NAME, getLongName(fullPath, repoPath, editName))
if (shouldGeneratePassword) {
val directoryStructure = AutofillPreferences.directoryStructure(applicationContext)
val entry = passwordEntryFactory.create(content.encodeToByteArray())
returnIntent.putExtra(RETURN_EXTRA_PASSWORD, entry.password)
val username = entry.username ?: directoryStructure.getUsernameFor(file)
returnIntent.putExtra(RETURN_EXTRA_USERNAME, username)
}
if (directoryInputLayout.isVisible &&
directoryInputLayout.isEnabled &&
oldFileName != null
) {
val oldFile = File("$repoPath/${oldCategory?.trim('/')}/$oldFileName.gpg")
if (oldFile.path != file.path && !oldFile.delete()) {
setResult(RESULT_CANCELED)
MaterialAlertDialogBuilder(this@PasswordCreationActivityV2)
.setTitle(R.string.password_creation_file_fail_title)
.setMessage(
getString(R.string.password_creation_file_delete_fail_message, oldFileName)
)
.setCancelable(false)
.setPositiveButton(android.R.string.ok) { _, _ -> finish() }
.show()
val result =
withContext(Dispatchers.IO) {
val outputStream = ByteArrayOutputStream()
repository.encrypt(content.byteInputStream(), outputStream)
outputStream
}
val file = File(path)
// If we're not editing, this file should not already exist!
// Additionally, if we were editing and the incoming and outgoing
// filenames differ, it means we renamed. Ensure that the target
// doesn't already exist to prevent an accidental overwrite.
if ((!editing || (editing && suggestedName != file.nameWithoutExtension)) &&
file.exists()
) {
snackbar(message = getString(R.string.password_creation_duplicate_error))
return@runCatching
}
}
val commitMessageRes =
if (editing) R.string.git_commit_edit_text else R.string.git_commit_add_text
lifecycleScope.launch {
commitChange(
resources.getString(commitMessageRes, getLongName(fullPath, repoPath, editName))
)
.onSuccess {
setResult(RESULT_OK, returnIntent)
finish()
if (!file.isInsideRepository()) {
snackbar(message = getString(R.string.message_error_destination_outside_repo))
return@runCatching
}
withContext(Dispatchers.IO) { file.writeBytes(result.toByteArray()) }
// associate the new password name with the last name's timestamp in
// history
val preference = getSharedPreferences("recent_password_history", Context.MODE_PRIVATE)
val oldFilePathHash = "$repoPath/${oldCategory?.trim('/')}/$oldFileName.gpg".base64()
val timestamp = preference.getString(oldFilePathHash)
if (timestamp != null) {
preference.edit {
remove(oldFilePathHash)
putString(file.absolutePath.base64(), timestamp)
}
}
val returnIntent = Intent()
returnIntent.putExtra(RETURN_EXTRA_CREATED_FILE, path)
returnIntent.putExtra(RETURN_EXTRA_NAME, editName)
returnIntent.putExtra(RETURN_EXTRA_LONG_NAME, getLongName(fullPath, repoPath, editName))
if (shouldGeneratePassword) {
val directoryStructure = AutofillPreferences.directoryStructure(applicationContext)
val entry = passwordEntryFactory.create(content.encodeToByteArray())
returnIntent.putExtra(RETURN_EXTRA_PASSWORD, entry.password)
val username = entry.username ?: directoryStructure.getUsernameFor(file)
returnIntent.putExtra(RETURN_EXTRA_USERNAME, username)
}
if (directoryInputLayout.isVisible &&
directoryInputLayout.isEnabled &&
oldFileName != null
) {
val oldFile = File("$repoPath/${oldCategory?.trim('/')}/$oldFileName.gpg")
if (oldFile.path != file.path && !oldFile.delete()) {
setResult(RESULT_CANCELED)
MaterialAlertDialogBuilder(this@PasswordCreationActivityV2)
.setTitle(R.string.password_creation_file_fail_title)
.setMessage(
getString(R.string.password_creation_file_delete_fail_message, oldFileName)
)
.setCancelable(false)
.setPositiveButton(android.R.string.ok) { _, _ -> finish() }
.show()
return@runCatching
}
}
val commitMessageRes =
if (editing) R.string.git_commit_edit_text else R.string.git_commit_add_text
lifecycleScope.launch {
commitChange(
resources.getString(commitMessageRes, getLongName(fullPath, repoPath, editName))
)
.onSuccess {
setResult(RESULT_OK, returnIntent)
finish()
}
}
}
}
.onFailure { e ->
if (e is IOException) {
logcat(ERROR) { e.asLog("Failed to write password file") }

View file

@ -48,8 +48,7 @@ class PasswordGeneratorDialogFragment : DialogFragment() {
val monoTypeface = Typeface.createFromAsset(callingActivity.assets, "fonts/sourcecodepro.ttf")
val prefs =
requireActivity()
.applicationContext
.getSharedPreferences("PasswordGenerator", Context.MODE_PRIVATE)
.applicationContext.getSharedPreferences("PasswordGenerator", Context.MODE_PRIVATE)
builder.setView(binding.root)
@ -102,21 +101,21 @@ class PasswordGeneratorDialogFragment : DialogFragment() {
val passwordLength = getLength()
setPrefs(requireContext(), passwordOptions, passwordLength)
passwordField.text =
runCatching { PasswordGenerator.generate(passwordOptions, passwordLength) }.getOrElse {
exception ->
val errorText =
when (exception) {
is MaxIterationsExceededException ->
requireContext().getString(R.string.pwgen_max_iterations_exceeded)
is NoCharactersIncludedException ->
requireContext().getString(R.string.pwgen_no_chars_error)
is PasswordLengthTooShortException ->
requireContext().getString(R.string.pwgen_length_too_short_error)
else -> requireContext().getString(R.string.pwgen_some_error_occurred)
}
Toast.makeText(requireActivity(), errorText, Toast.LENGTH_SHORT).show()
""
}
runCatching { PasswordGenerator.generate(passwordOptions, passwordLength) }
.getOrElse { exception ->
val errorText =
when (exception) {
is MaxIterationsExceededException ->
requireContext().getString(R.string.pwgen_max_iterations_exceeded)
is NoCharactersIncludedException ->
requireContext().getString(R.string.pwgen_no_chars_error)
is PasswordLengthTooShortException ->
requireContext().getString(R.string.pwgen_length_too_short_error)
else -> requireContext().getString(R.string.pwgen_some_error_occurred)
}
Toast.makeText(requireActivity(), errorText, Toast.LENGTH_SHORT).show()
""
}
}
private fun isChecked(@IdRes id: Int): Boolean {

View file

@ -59,18 +59,17 @@ class SelectFolderFragment : Fragment(R.layout.password_recycler_view) {
override fun onAttach(context: Context) {
super.onAttach(context)
runCatching {
listener =
object : OnFragmentInteractionListener {
override fun onFragmentInteraction(item: PasswordItem) {
if (item.type == PasswordItem.TYPE_CATEGORY) {
model.navigateTo(item.file, listMode = ListMode.DirectoriesOnly)
(requireActivity() as AppCompatActivity).supportActionBar?.setDisplayHomeAsUpEnabled(
true
)
listener =
object : OnFragmentInteractionListener {
override fun onFragmentInteraction(item: PasswordItem) {
if (item.type == PasswordItem.TYPE_CATEGORY) {
model.navigateTo(item.file, listMode = ListMode.DirectoriesOnly)
(requireActivity() as AppCompatActivity)
.supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
}
}
}
}
}
.onFailure {
throw ClassCastException("$context must implement OnFragmentInteractionListener")
}

View file

@ -88,9 +88,8 @@ class GitConfigActivity : BaseGitActivity() {
binding.gitAbortRebase.alpha = if (needsAbort) 1.0f else 0.5f
}
binding.gitLog.setOnClickListener {
runCatching { startActivity(Intent(this, GitLogActivity::class.java)) }.onFailure { ex ->
logcat(ERROR) { "Failed to start GitLogActivity\n${ex}" }
}
runCatching { startActivity(Intent(this, GitLogActivity::class.java)) }
.onFailure { ex -> logcat(ERROR) { "Failed to start GitLogActivity\n${ex}" } }
}
binding.gitAbortRebase.setOnClickListener {
lifecycleScope.launch {
@ -142,16 +141,16 @@ class GitConfigActivity : BaseGitActivity() {
*/
private fun headStatusMsg(repo: Repository): String {
return runCatching {
val headRef = repo.getRef(Constants.HEAD)
if (headRef.isSymbolic) {
val branchName = headRef.target.name
val shortBranchName = Repository.shortenRefName(branchName)
getString(R.string.git_head_on_branch, shortBranchName)
} else {
val commitHash = headRef.objectId.abbreviate(8).name()
getString(R.string.git_head_detached, commitHash)
val headRef = repo.getRef(Constants.HEAD)
if (headRef.isSymbolic) {
val branchName = headRef.target.name
val shortBranchName = Repository.shortenRefName(branchName)
getString(R.string.git_head_on_branch, shortBranchName)
} else {
val commitHash = headRef.objectId.abbreviate(8).name()
getString(R.string.git_head_detached, commitHash)
}
}
}
.getOrElse { ex ->
logcat(ERROR) { "Error getting HEAD reference\n${ex}" }
getString(R.string.git_head_missing)

View file

@ -240,24 +240,24 @@ class GitServerConfigActivity : BaseGitActivity() {
.setCancelable(false)
.setPositiveButton(R.string.dialog_delete) { dialog, _ ->
runCatching {
lifecycleScope.launch {
val snackbar =
snackbar(
message = getString(R.string.delete_directory_progress_text),
length = Snackbar.LENGTH_INDEFINITE
)
withContext(Dispatchers.IO) { localDir.deleteRecursively() }
snackbar.dismiss()
launchGitOperation(GitOp.CLONE)
.fold(
success = {
setResult(RESULT_OK)
finish()
},
failure = { err -> promptOnErrorHandler(err) { finish() } }
)
lifecycleScope.launch {
val snackbar =
snackbar(
message = getString(R.string.delete_directory_progress_text),
length = Snackbar.LENGTH_INDEFINITE
)
withContext(Dispatchers.IO) { localDir.deleteRecursively() }
snackbar.dismiss()
launchGitOperation(GitOp.CLONE)
.fold(
success = {
setResult(RESULT_OK)
finish()
},
failure = { err -> promptOnErrorHandler(err) { finish() } }
)
}
}
}
.onFailure { e ->
e.printStackTrace()
MaterialAlertDialogBuilder(this).setMessage(e.message).show()
@ -268,11 +268,11 @@ class GitServerConfigActivity : BaseGitActivity() {
.show()
} else {
runCatching {
// Silently delete & replace the lone .git folder if it exists
if (localDir.exists() && localDirFiles.size == 1 && localDirFiles[0].name == ".git") {
localDir.deleteRecursively()
// Silently delete & replace the lone .git folder if it exists
if (localDir.exists() && localDirFiles.size == 1 && localDirFiles[0].name == ".git") {
localDir.deleteRecursively()
}
}
}
.onFailure { e ->
logcat(ERROR) { e.asLog() }
MaterialAlertDialogBuilder(this).setMessage(e.message).show()

View file

@ -55,13 +55,13 @@ class CloneFragment : Fragment(R.layout.fragment_clone) {
private fun createRepository() {
val localDir = PasswordRepository.getRepositoryDirectory()
runCatching {
check(localDir.exists() || localDir.mkdir()) { "Failed to create directory!" }
PasswordRepository.createRepository(localDir)
if (!PasswordRepository.isInitialized) {
PasswordRepository.initialize()
check(localDir.exists() || localDir.mkdir()) { "Failed to create directory!" }
PasswordRepository.createRepository(localDir)
if (!PasswordRepository.isInitialized) {
PasswordRepository.initialize()
}
parentFragmentManager.performTransactionWithBackStack(KeySelectionFragment.newInstance())
}
parentFragmentManager.performTransactionWithBackStack(KeySelectionFragment.newInstance())
}
.onFailure { e ->
logcat(ERROR) { e.asLog() }
if (!localDir.delete()) {

View file

@ -293,32 +293,32 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
override fun onAttach(context: Context) {
super.onAttach(context)
runCatching {
listener =
object : OnFragmentInteractionListener {
override fun onFragmentInteraction(item: PasswordItem) {
if (settings.getString(PreferenceKeys.SORT_ORDER) ==
PasswordSortOrder.RECENTLY_USED.name
) {
// save the time when password was used
val preferences =
context.getSharedPreferences("recent_password_history", Context.MODE_PRIVATE)
preferences.edit {
putString(item.file.absolutePath.base64(), System.currentTimeMillis().toString())
listener =
object : OnFragmentInteractionListener {
override fun onFragmentInteraction(item: PasswordItem) {
if (settings.getString(PreferenceKeys.SORT_ORDER) ==
PasswordSortOrder.RECENTLY_USED.name
) {
// save the time when password was used
val preferences =
context.getSharedPreferences("recent_password_history", Context.MODE_PRIVATE)
preferences.edit {
putString(item.file.absolutePath.base64(), System.currentTimeMillis().toString())
}
}
}
if (item.type == PasswordItem.TYPE_CATEGORY) {
navigateTo(item.file)
} else {
if (requireArguments().getBoolean("matchWith", false)) {
requireStore().matchPasswordWithApp(item)
if (item.type == PasswordItem.TYPE_CATEGORY) {
navigateTo(item.file)
} else {
requireStore().decryptPassword(item)
if (requireArguments().getBoolean("matchWith", false)) {
requireStore().matchPasswordWithApp(item)
} else {
requireStore().decryptPassword(item)
}
}
}
}
}
}
}
.onFailure {
throw ClassCastException("$context must implement OnFragmentInteractionListener")
}

View file

@ -292,9 +292,8 @@ class PasswordStore : BaseGitActivity() {
.setPositiveButton(resources.getString(R.string.dialog_ok), null)
when (id) {
R.id.user_pref -> {
runCatching { startActivity(Intent(this, SettingsActivity::class.java)) }.onFailure { e ->
e.printStackTrace()
}
runCatching { startActivity(Intent(this, SettingsActivity::class.java)) }
.onFailure { e -> e.printStackTrace() }
}
R.id.git_push -> {
if (!PasswordRepository.isInitialized) {
@ -618,9 +617,7 @@ class PasswordStore : BaseGitActivity() {
fun matchPasswordWithApp(item: PasswordItem) {
val path =
item
.file
.absolutePath
item.file.absolutePath
.replace(PasswordRepository.getRepositoryDirectory().toString() + "/", "")
.replace(".gpg", "")
val data = Intent()

View file

@ -42,9 +42,10 @@ class ProxySelectorActivity : AppCompatActivity() {
with(binding) {
proxyHost.setText(proxyPrefs.getString(PreferenceKeys.PROXY_HOST))
proxyUser.setText(proxyPrefs.getString(PreferenceKeys.PROXY_USERNAME))
proxyPrefs.getInt(PreferenceKeys.PROXY_PORT, -1).takeIf { it != -1 }?.let {
proxyPort.setText("$it")
}
proxyPrefs
.getInt(PreferenceKeys.PROXY_PORT, -1)
.takeIf { it != -1 }
?.let { proxyPort.setText("$it") }
proxyPassword.setText(proxyPrefs.getString(PreferenceKeys.PROXY_PASSWORD))
save.setOnClickListener { saveSettings() }
proxyHost.doOnTextChanged { text, _, _, _ ->
@ -70,18 +71,22 @@ class ProxySelectorActivity : AppCompatActivity() {
private fun saveSettings() {
proxyPrefs.edit {
binding.proxyHost.text?.toString()?.takeIf { it.isNotEmpty() }.let {
gitSettings.proxyHost = it
}
binding.proxyUser.text?.toString()?.takeIf { it.isNotEmpty() }.let {
gitSettings.proxyUsername = it
}
binding.proxyPort.text?.toString()?.takeIf { it.isNotEmpty() }?.let {
gitSettings.proxyPort = it.toInt()
}
binding.proxyPassword.text?.toString()?.takeIf { it.isNotEmpty() }.let {
gitSettings.proxyPassword = it
}
binding.proxyHost.text
?.toString()
?.takeIf { it.isNotEmpty() }
.let { gitSettings.proxyHost = it }
binding.proxyUser.text
?.toString()
?.takeIf { it.isNotEmpty() }
.let { gitSettings.proxyUsername = it }
binding.proxyPort.text
?.toString()
?.takeIf { it.isNotEmpty() }
?.let { gitSettings.proxyPort = it.toInt() }
binding.proxyPassword.text
?.toString()
?.takeIf { it.isNotEmpty() }
.let { gitSettings.proxyPassword = it }
}
proxyUtils.setDefaultProxy()
Handler(Looper.getMainLooper()).postDelayed(500) { finish() }

View file

@ -161,9 +161,9 @@ class RepositorySettings(private val activity: FragmentActivity) : SettingsProvi
.setCancelable(false)
.setPositiveButton(R.string.dialog_delete) { dialogInterface, _ ->
runCatching {
PasswordRepository.getRepositoryDirectory().deleteRecursively()
PasswordRepository.closeRepository()
}
PasswordRepository.getRepositoryDirectory().deleteRecursively()
PasswordRepository.closeRepository()
}
.onFailure { it.message?.let { message -> activity.snackbar(message = message) } }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {

View file

@ -25,16 +25,16 @@ class SshKeyImportActivity : AppCompatActivity() {
return@registerForActivityResult
}
runCatching {
SshKey.import(uri)
Toast.makeText(
this,
resources.getString(R.string.ssh_key_success_dialog_title),
Toast.LENGTH_LONG
)
.show()
setResult(RESULT_OK)
finish()
}
SshKey.import(uri)
Toast.makeText(
this,
resources.getString(R.string.ssh_key_success_dialog_title),
Toast.LENGTH_LONG
)
.show()
setResult(RESULT_OK)
finish()
}
.onFailure { e ->
MaterialAlertDialogBuilder(this)
.setTitle(resources.getString(R.string.ssh_key_error_dialog_title))

View file

@ -107,11 +107,13 @@ class AutofillMatcher {
val matchedFiles =
matchPreferences.getStringSet(matchesKey(formOrigin), emptySet())!!.map { File(it) }
return Ok(
matchedFiles.filter { it.exists() }.also { validFiles ->
matchPreferences.edit {
putStringSet(matchesKey(formOrigin), validFiles.map { it.absolutePath }.toSet())
matchedFiles
.filter { it.exists() }
.also { validFiles ->
matchPreferences.edit {
putStringSet(matchesKey(formOrigin), validFiles.map { it.absolutePath }.toSet())
}
}
}
)
}

View file

@ -34,9 +34,10 @@ fun File.contains(other: File): Boolean {
if (!isDirectory) return false
if (!other.exists()) return false
val relativePath =
runCatching { other.relativeTo(this) }.getOrElse {
return false
}
runCatching { other.relativeTo(this) }
.getOrElse {
return false
}
// Direct containment is equivalent to the relative path being equal to the filename.
return relativePath.path == other.name
}

View file

@ -51,76 +51,76 @@ class GitCommandExecutor(
// Count the number of uncommitted files
var nbChanges = 0
return runCatching {
for (command in operation.commands) {
when (command) {
is StatusCommand -> {
val res = withContext(Dispatchers.IO) { command.call() }
nbChanges = res.uncommittedChanges.size
}
is CommitCommand -> {
// the previous status will eventually be used to avoid a commit
if (nbChanges > 0) {
withContext(Dispatchers.IO) {
val name = gitSettings.authorName.ifEmpty { "root" }
val email = gitSettings.authorEmail.ifEmpty { "localhost" }
val identity = PersonIdent(name, email)
command.setAuthor(identity).setCommitter(identity).call()
}
for (command in operation.commands) {
when (command) {
is StatusCommand -> {
val res = withContext(Dispatchers.IO) { command.call() }
nbChanges = res.uncommittedChanges.size
}
}
is PullCommand -> {
val result = withContext(Dispatchers.IO) { command.call() }
if (result.rebaseResult != null) {
if (!result.rebaseResult.status.isSuccessful) {
throw PullException.PullRebaseFailed
}
} else if (result.mergeResult != null) {
if (!result.mergeResult.mergeStatus.isSuccessful) {
throw PullException.PullMergeFailed
}
}
}
is PushCommand -> {
val results = withContext(Dispatchers.IO) { command.call() }
for (result in results) {
// Code imported (modified) from Gerrit PushOp, license Apache v2
for (rru in result.remoteUpdates) {
when (rru.status) {
RemoteRefUpdate.Status.REJECTED_NONFASTFORWARD ->
throw PushException.NonFastForward
RemoteRefUpdate.Status.REJECTED_NODELETE,
RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED,
RemoteRefUpdate.Status.NON_EXISTING,
RemoteRefUpdate.Status.NOT_ATTEMPTED, ->
throw PushException.Generic(rru.status.name)
RemoteRefUpdate.Status.REJECTED_OTHER_REASON -> {
throw if ("non-fast-forward" == rru.message) {
PushException.RemoteRejected
} else {
PushException.Generic(rru.message)
}
}
RemoteRefUpdate.Status.UP_TO_DATE -> {
withContext(Dispatchers.Main) {
Toast.makeText(
activity.applicationContext,
activity.applicationContext.getString(R.string.git_push_up_to_date),
Toast.LENGTH_SHORT
)
.show()
}
}
else -> {}
is CommitCommand -> {
// the previous status will eventually be used to avoid a commit
if (nbChanges > 0) {
withContext(Dispatchers.IO) {
val name = gitSettings.authorName.ifEmpty { "root" }
val email = gitSettings.authorEmail.ifEmpty { "localhost" }
val identity = PersonIdent(name, email)
command.setAuthor(identity).setCommitter(identity).call()
}
}
}
}
else -> {
withContext(Dispatchers.IO) { command.call() }
is PullCommand -> {
val result = withContext(Dispatchers.IO) { command.call() }
if (result.rebaseResult != null) {
if (!result.rebaseResult.status.isSuccessful) {
throw PullException.PullRebaseFailed
}
} else if (result.mergeResult != null) {
if (!result.mergeResult.mergeStatus.isSuccessful) {
throw PullException.PullMergeFailed
}
}
}
is PushCommand -> {
val results = withContext(Dispatchers.IO) { command.call() }
for (result in results) {
// Code imported (modified) from Gerrit PushOp, license Apache v2
for (rru in result.remoteUpdates) {
when (rru.status) {
RemoteRefUpdate.Status.REJECTED_NONFASTFORWARD ->
throw PushException.NonFastForward
RemoteRefUpdate.Status.REJECTED_NODELETE,
RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED,
RemoteRefUpdate.Status.NON_EXISTING,
RemoteRefUpdate.Status.NOT_ATTEMPTED, ->
throw PushException.Generic(rru.status.name)
RemoteRefUpdate.Status.REJECTED_OTHER_REASON -> {
throw if ("non-fast-forward" == rru.message) {
PushException.RemoteRejected
} else {
PushException.Generic(rru.message)
}
}
RemoteRefUpdate.Status.UP_TO_DATE -> {
withContext(Dispatchers.Main) {
Toast.makeText(
activity.applicationContext,
activity.applicationContext.getString(R.string.git_push_up_to_date),
Toast.LENGTH_SHORT
)
.show()
}
}
else -> {}
}
}
}
}
else -> {
withContext(Dispatchers.IO) { command.call() }
}
}
}
}
}
.also { snackbar.dismiss() }
}

View file

@ -25,10 +25,11 @@ private fun commits(): Iterable<RevCommit> {
logcat(TAG, ERROR) { "Could not access git repository" }
return listOf()
}
return runCatching { Git(repo).log().call() }.getOrElse { e ->
logcat(TAG, ERROR) { e.asLog("Failed to obtain git commits") }
listOf()
}
return runCatching { Git(repo).log().call() }
.getOrElse { e ->
logcat(TAG, ERROR) { e.asLog("Failed to obtain git commits") }
listOf()
}
}
/**

View file

@ -109,14 +109,14 @@ abstract class GitOperation(protected val callingActivity: FragmentActivity) {
private fun getSshKey(make: Boolean) {
runCatching {
val intent =
if (make) {
Intent(callingActivity.applicationContext, SshKeyGenActivity::class.java)
} else {
Intent(callingActivity.applicationContext, SshKeyImportActivity::class.java)
}
callingActivity.startActivity(intent)
}
val intent =
if (make) {
Intent(callingActivity.applicationContext, SshKeyGenActivity::class.java)
} else {
Intent(callingActivity.applicationContext, SshKeyImportActivity::class.java)
}
callingActivity.startActivity(intent)
}
.onFailure { e -> logcat(ERROR) { e.asLog() } }
}

View file

@ -84,19 +84,19 @@ object SshKey {
val mustAuthenticate: Boolean
get() {
return runCatching {
if (type !in listOf(Type.KeystoreNative, Type.KeystoreWrappedEd25519)) return false
when (val key = androidKeystore.getKey(KEYSTORE_ALIAS, null)) {
is PrivateKey -> {
val factory = KeyFactory.getInstance(key.algorithm, PROVIDER_ANDROID_KEY_STORE)
return factory.getKeySpec(key, KeyInfo::class.java).isUserAuthenticationRequired
if (type !in listOf(Type.KeystoreNative, Type.KeystoreWrappedEd25519)) return false
when (val key = androidKeystore.getKey(KEYSTORE_ALIAS, null)) {
is PrivateKey -> {
val factory = KeyFactory.getInstance(key.algorithm, PROVIDER_ANDROID_KEY_STORE)
return factory.getKeySpec(key, KeyInfo::class.java).isUserAuthenticationRequired
}
is SecretKey -> {
val factory = SecretKeyFactory.getInstance(key.algorithm, PROVIDER_ANDROID_KEY_STORE)
(factory.getKeySpec(key, KeyInfo::class.java) as KeyInfo).isUserAuthenticationRequired
}
else -> throw IllegalStateException("SSH key does not exist in Keystore")
}
is SecretKey -> {
val factory = SecretKeyFactory.getInstance(key.algorithm, PROVIDER_ANDROID_KEY_STORE)
(factory.getKeySpec(key, KeyInfo::class.java) as KeyInfo).isUserAuthenticationRequired
}
else -> throw IllegalStateException("SSH key does not exist in Keystore")
}
}
.getOrElse { error ->
// It is fine to swallow the exception here since it will reappear when the key
// is
@ -316,19 +316,24 @@ object SshKey {
private object KeystoreNativeKeyProvider : KeyProvider {
override fun getPublic(): PublicKey =
runCatching { androidKeystore.sshPublicKey!! }.getOrElse { error ->
logcat { error.asLog() }
throw IOException("Failed to get public key '$KEYSTORE_ALIAS' from Android Keystore", error)
}
runCatching { androidKeystore.sshPublicKey!! }
.getOrElse { error ->
logcat { error.asLog() }
throw IOException(
"Failed to get public key '$KEYSTORE_ALIAS' from Android Keystore",
error
)
}
override fun getPrivate(): PrivateKey =
runCatching { androidKeystore.sshPrivateKey!! }.getOrElse { error ->
logcat { error.asLog() }
throw IOException(
"Failed to access private key '$KEYSTORE_ALIAS' from Android Keystore",
error
)
}
runCatching { androidKeystore.sshPrivateKey!! }
.getOrElse { error ->
logcat { error.asLog() }
throw IOException(
"Failed to access private key '$KEYSTORE_ALIAS' from Android Keystore",
error
)
}
override fun getType(): KeyType = KeyType.fromKey(public)
}
@ -336,22 +341,23 @@ object SshKey {
private object KeystoreWrappedEd25519KeyProvider : KeyProvider {
override fun getPublic(): PublicKey =
runCatching { parseSshPublicKey(sshPublicKey!!)!! }.getOrElse { error ->
logcat { error.asLog() }
throw IOException("Failed to get the public key for wrapped ed25519 key", error)
}
runCatching { parseSshPublicKey(sshPublicKey!!)!! }
.getOrElse { error ->
logcat { error.asLog() }
throw IOException("Failed to get the public key for wrapped ed25519 key", error)
}
override fun getPrivate(): PrivateKey =
runCatching {
// The current MasterKey API does not allow getting a reference to an existing one
// without specifying the KeySpec for a new one. However, the value for passed here
// for `requireAuthentication` is not used as the key already exists at this point.
val encryptedPrivateKeyFile = runBlocking { getOrCreateWrappedPrivateKeyFile(false) }
val rawPrivateKey = encryptedPrivateKeyFile.openFileInput().use { it.readBytes() }
EdDSAPrivateKey(
EdDSAPrivateKeySpec(rawPrivateKey, EdDSANamedCurveTable.ED_25519_CURVE_SPEC)
)
}
// The current MasterKey API does not allow getting a reference to an existing one
// without specifying the KeySpec for a new one. However, the value for passed here
// for `requireAuthentication` is not used as the key already exists at this point.
val encryptedPrivateKeyFile = runBlocking { getOrCreateWrappedPrivateKeyFile(false) }
val rawPrivateKey = encryptedPrivateKeyFile.openFileInput().use { it.readBytes() }
EdDSAPrivateKey(
EdDSAPrivateKeySpec(rawPrivateKey, EdDSANamedCurveTable.ED_25519_CURVE_SPEC)
)
}
.getOrElse { error ->
logcat { error.asLog() }
throw IOException("Failed to unwrap wrapped ed25519 key", error)

View file

@ -93,9 +93,8 @@ private fun makeTofuHostKeyVerifier(hostKeyFile: File): HostKeyVerifier {
return object : HostKeyVerifier {
override fun verify(hostname: String?, port: Int, key: PublicKey?): Boolean {
val digest =
runCatching { SecurityUtils.getMessageDigest("SHA-256") }.getOrElse { e ->
throw SSHRuntimeException(e)
}
runCatching { SecurityUtils.getMessageDigest("SHA-256") }
.getOrElse { e -> throw SSHRuntimeException(e) }
digest.update(PlainBuffer().putPublicKey(key).compactData)
val digestData = digest.digest()
val hostKeyEntry = "SHA256:${Base64.encodeToString(digestData, Base64.NO_WRAP)}"

View file

@ -140,9 +140,10 @@ constructor(
newBranch: String
): UpdateConnectionSettingsResult {
val parsedUrl =
runCatching { URIish(newUrl) }.getOrElse {
return UpdateConnectionSettingsResult.FailedToParseUrl
}
runCatching { URIish(newUrl) }
.getOrElse {
return UpdateConnectionSettingsResult.FailedToParseUrl
}
val newProtocol =
when (parsedUrl.scheme) {
in listOf("http", "https") -> Protocol.Https

View file

@ -229,9 +229,8 @@ class SearchableRepositoryViewModel(application: Application) : AndroidViewModel
.filter { it.first > 0 }
.toList()
.sortedWith(
compareByDescending<Pair<Int, PasswordItem>> { it.first }.thenBy(itemComparator) {
it.second
}
compareByDescending<Pair<Int, PasswordItem>> { it.first }
.thenBy(itemComparator) { it.second }
)
.map { it.second }
}

View file

@ -130,8 +130,8 @@ class AutofillSmsActivity : AppCompatActivity() {
private suspend fun waitForSms() {
val smsClient = SmsCodeRetriever.getAutofillClient(this@AutofillSmsActivity)
runCatching {
withContext(Dispatchers.IO) { smsClient.startSmsCodeRetriever().suspendableAwait() }
}
withContext(Dispatchers.IO) { smsClient.startSmsCodeRetriever().suspendableAwait() }
}
.onFailure { e ->
if (e is ResolvableApiException) {
e.startResolutionForResult(this@AutofillSmsActivity, 1)

View file

@ -105,31 +105,34 @@ internal class SingleFieldMatcher(
}
override fun match(fields: List<FormField>, alreadyMatched: List<FormField>): List<FormField>? {
return fields.minus(alreadyMatched).filter { take(it, alreadyMatched) }.let { contestants ->
when (contestants.size) {
1 -> return@let listOf(contestants.single())
0 -> return@let null
}
var current = contestants
for ((i, tieBreaker) in tieBreakers.withIndex()) {
// Successively filter matched fields via tie breakers...
val new = current.filter { tieBreaker(it, alreadyMatched) }
// skipping those tie breakers that are not satisfied for any remaining field...
if (new.isEmpty()) {
logcat { "Tie breaker #${i + 1}: Didn't match any field; skipping" }
continue
return fields
.minus(alreadyMatched)
.filter { take(it, alreadyMatched) }
.let { contestants ->
when (contestants.size) {
1 -> return@let listOf(contestants.single())
0 -> return@let null
}
// and return if the available options have been narrowed to a single field.
if (new.size == 1) {
logcat { "Tie breaker #${i + 1}: Success" }
var current = contestants
for ((i, tieBreaker) in tieBreakers.withIndex()) {
// Successively filter matched fields via tie breakers...
val new = current.filter { tieBreaker(it, alreadyMatched) }
// skipping those tie breakers that are not satisfied for any remaining field...
if (new.isEmpty()) {
logcat { "Tie breaker #${i + 1}: Didn't match any field; skipping" }
continue
}
// and return if the available options have been narrowed to a single field.
if (new.size == 1) {
logcat { "Tie breaker #${i + 1}: Success" }
current = new
break
}
logcat { "Tie breaker #${i + 1}: Matched ${new.size} fields; continuing" }
current = new
break
}
logcat { "Tie breaker #${i + 1}: Matched ${new.size} fields; continuing" }
current = new
listOf(current.singleOrNull() ?: return null)
}
listOf(current.singleOrNull() ?: return null)
}
}
}

View file

@ -251,8 +251,7 @@ public fun getInstalledBrowsersWithAutofillSupportLevel(
.map { it to getBrowserAutofillSupportLevel(context, it.activityInfo.packageName) }
.filter { it.first.isDefault || it.second != BrowserAutofillSupportLevel.None }
.map {
context
.packageManager
context.packageManager
.getApplicationLabel(it.first.activityInfo.applicationInfo)
.toString() to it.second
}

View file

@ -182,9 +182,10 @@ internal class FormField(
// Basic type detection for HTML fields
private val htmlTag = node.htmlInfo?.tag
private val htmlAttributes: Map<String, String> =
node.htmlInfo?.attributes?.filter { it.first != null && it.second != null }?.associate {
Pair(it.first.lowercase(Locale.US), it.second.lowercase(Locale.US))
}
node.htmlInfo
?.attributes
?.filter { it.first != null && it.second != null }
?.associate { Pair(it.first.lowercase(Locale.US), it.second.lowercase(Locale.US)) }
?: emptyMap()
private val htmlAttributesDebug = htmlAttributes.entries.joinToString { "${it.key}=${it.value}" }
private val htmlInputType = htmlAttributes["type"]

View file

@ -32,9 +32,9 @@ afterEvaluate {
}
}
// disable kapt tasks for unit tests
tasks.matching { it.name.startsWith("kapt") && it.name.endsWith("UnitTestKotlin") }.configureEach {
enabled = false
}
tasks
.matching { it.name.startsWith("kapt") && it.name.endsWith("UnitTestKotlin") }
.configureEach { enabled = false }
fun Project.hasDaggerCompilerDependency(): Boolean {
return configurations.any {

View file

@ -79,17 +79,17 @@ constructor(
when (id) {
is GpgIdentifier.KeyId -> {
val keyIdMatch =
keys.map { key -> key to tryGetId(key) }.firstOrNull { (_, keyId) ->
keyId?.id == id.id
}
keys
.map { key -> key to tryGetId(key) }
.firstOrNull { (_, keyId) -> keyId?.id == id.id }
keyIdMatch?.first
}
is GpgIdentifier.UserId -> {
val selector = SelectUserId.byEmail(id.email)
val userIdMatch =
keys.map { key -> key to tryParseKeyring(key) }.firstOrNull { (_, keyRing) ->
selector.firstMatch(keyRing) != null
}
keys
.map { key -> key to tryParseKeyring(key) }
.firstOrNull { (_, keyRing) -> selector.firstMatch(keyRing) != null }
userIdMatch?.first
}
}