diff options
| author | Harsh Shandilya <me@msfjarvis.dev> | 2022-07-15 00:53:48 +0530 |
|---|---|---|
| committer | Harsh Shandilya <me@msfjarvis.dev> | 2022-07-15 01:13:47 +0530 |
| commit | 549ee790d3e52bc62565ddf92e6a556e98b5195e (patch) | |
| tree | d5758e5eb80093704e683c8da926838e18182588 /app/src/main/java/dev/msfjarvis/aps/ui/autofill | |
| parent | 010c6e227c9cc27f4d01bc912311f977b2aeb3a7 (diff) | |
| download | HardenedPass-549ee790d3e52bc62565ddf92e6a556e98b5195e.tar.gz HardenedPass-549ee790d3e52bc62565ddf92e6a556e98b5195e.zip | |
all: re-do package structure yet again
Diffstat (limited to 'app/src/main/java/dev/msfjarvis/aps/ui/autofill')
6 files changed, 0 insertions, 1014 deletions
diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/autofill/AutofillDecryptActivity.kt b/app/src/main/java/dev/msfjarvis/aps/ui/autofill/AutofillDecryptActivity.kt deleted file mode 100644 index b5052049..00000000 --- a/app/src/main/java/dev/msfjarvis/aps/ui/autofill/AutofillDecryptActivity.kt +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ -package dev.msfjarvis.aps.ui.autofill - -import android.app.PendingIntent -import android.content.Context -import android.content.Intent -import android.content.IntentSender -import android.os.Build -import android.os.Bundle -import android.view.autofill.AutofillManager -import android.widget.Toast -import androidx.activity.result.IntentSenderRequest -import androidx.activity.result.contract.ActivityResultContracts.StartIntentSenderForResult -import androidx.annotation.RequiresApi -import androidx.appcompat.app.AppCompatActivity -import androidx.lifecycle.lifecycleScope -import com.github.androidpasswordstore.autofillparser.AutofillAction -import com.github.androidpasswordstore.autofillparser.Credentials -import com.github.michaelbull.result.getOrElse -import com.github.michaelbull.result.onFailure -import com.github.michaelbull.result.onSuccess -import com.github.michaelbull.result.runCatching -import dagger.hilt.android.AndroidEntryPoint -import dev.msfjarvis.aps.data.passfile.PasswordEntry -import dev.msfjarvis.aps.util.autofill.AutofillPreferences -import dev.msfjarvis.aps.util.autofill.AutofillResponseBuilder -import dev.msfjarvis.aps.util.autofill.DirectoryStructure -import dev.msfjarvis.aps.util.extensions.OPENPGP_PROVIDER -import dev.msfjarvis.aps.util.extensions.asLog -import java.io.ByteArrayOutputStream -import java.io.File -import java.io.InputStream -import java.io.OutputStream -import javax.inject.Inject -import kotlin.coroutines.Continuation -import kotlin.coroutines.resume -import kotlin.coroutines.resumeWithException -import kotlin.coroutines.suspendCoroutine -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import logcat.LogPriority.ERROR -import logcat.logcat -import me.msfjarvis.openpgpktx.util.OpenPgpApi -import me.msfjarvis.openpgpktx.util.OpenPgpServiceConnection -import org.openintents.openpgp.IOpenPgpService2 -import org.openintents.openpgp.OpenPgpError - -@RequiresApi(26) -@AndroidEntryPoint -class AutofillDecryptActivity : AppCompatActivity() { - - companion object { - - private const val EXTRA_FILE_PATH = "dev.msfjarvis.aps.autofill.oreo.EXTRA_FILE_PATH" - private const val EXTRA_SEARCH_ACTION = "dev.msfjarvis.aps.autofill.oreo.EXTRA_SEARCH_ACTION" - - private var decryptFileRequestCode = 1 - - fun makeDecryptFileIntent(file: File, forwardedExtras: Bundle, context: Context): Intent { - return Intent(context, AutofillDecryptActivity::class.java).apply { - putExtras(forwardedExtras) - putExtra(EXTRA_SEARCH_ACTION, true) - putExtra(EXTRA_FILE_PATH, file.absolutePath) - } - } - - fun makeDecryptFileIntentSender(file: File, context: Context): IntentSender { - val intent = - Intent(context, AutofillDecryptActivity::class.java).apply { - putExtra(EXTRA_SEARCH_ACTION, false) - putExtra(EXTRA_FILE_PATH, file.absolutePath) - } - return PendingIntent.getActivity( - context, - decryptFileRequestCode++, - intent, - if (Build.VERSION.SDK_INT >= 31) { - PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE - } else { - PendingIntent.FLAG_CANCEL_CURRENT - }, - ) - .intentSender - } - } - - @Inject lateinit var passwordEntryFactory: PasswordEntry.Factory - - private val decryptInteractionRequiredAction = - registerForActivityResult(StartIntentSenderForResult()) { result -> - if (continueAfterUserInteraction != null) { - val data = result.data - if (result.resultCode == RESULT_OK && data != null) { - continueAfterUserInteraction?.resume(data) - } else { - continueAfterUserInteraction?.resumeWithException( - Exception("OpenPgpApi ACTION_DECRYPT_VERIFY failed to continue after user interaction") - ) - } - continueAfterUserInteraction = null - } - } - - private var continueAfterUserInteraction: Continuation<Intent>? = null - private lateinit var directoryStructure: DirectoryStructure - - override fun onStart() { - super.onStart() - val filePath = - intent?.getStringExtra(EXTRA_FILE_PATH) - ?: run { - logcat(ERROR) { "AutofillDecryptActivity started without EXTRA_FILE_PATH" } - finish() - return - } - val clientState = - intent?.getBundleExtra(AutofillManager.EXTRA_CLIENT_STATE) - ?: run { - logcat(ERROR) { "AutofillDecryptActivity started without EXTRA_CLIENT_STATE" } - finish() - return - } - val isSearchAction = intent?.getBooleanExtra(EXTRA_SEARCH_ACTION, true)!! - val action = if (isSearchAction) AutofillAction.Search else AutofillAction.Match - directoryStructure = AutofillPreferences.directoryStructure(this) - logcat { action.toString() } - lifecycleScope.launch { - val credentials = decryptCredential(File(filePath)) - if (credentials == null) { - setResult(RESULT_CANCELED) - } else { - val fillInDataset = - AutofillResponseBuilder.makeFillInDataset( - this@AutofillDecryptActivity, - credentials, - clientState, - action - ) - withContext(Dispatchers.Main) { - setResult( - RESULT_OK, - Intent().apply { putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, fillInDataset) } - ) - } - } - withContext(Dispatchers.Main) { finish() } - } - } - - private suspend fun executeOpenPgpApi( - data: Intent, - input: InputStream, - output: OutputStream - ): Intent { - var openPgpServiceConnection: OpenPgpServiceConnection? = null - val openPgpService = - suspendCoroutine<IOpenPgpService2> { cont -> - openPgpServiceConnection = - OpenPgpServiceConnection( - this, - OPENPGP_PROVIDER, - object : OpenPgpServiceConnection.OnBound { - override fun onBound(service: IOpenPgpService2) { - cont.resume(service) - } - - override fun onError(e: Exception) { - cont.resumeWithException(e) - } - } - ) - .also { it.bindToService() } - } - return OpenPgpApi(this, openPgpService).executeApi(data, input, output).also { - openPgpServiceConnection?.unbindFromService() - } - } - - private suspend fun decryptCredential(file: File, resumeIntent: Intent? = null): Credentials? { - val command = resumeIntent ?: Intent().apply { action = OpenPgpApi.ACTION_DECRYPT_VERIFY } - runCatching { file.inputStream() } - .onFailure { e -> - logcat(ERROR) { e.asLog("File to decrypt not found") } - return null - } - .onSuccess { encryptedInput -> - val decryptedOutput = ByteArrayOutputStream() - runCatching { executeOpenPgpApi(command, encryptedInput, decryptedOutput) } - .onFailure { e -> - logcat(ERROR) { e.asLog("OpenPgpApi ACTION_DECRYPT_VERIFY failed") } - return null - } - .onSuccess { result -> - return when ( - val resultCode = - result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR) - ) { - OpenPgpApi.RESULT_CODE_SUCCESS -> { - runCatching { - 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 - } - } - OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED -> { - 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() - ) - } - } - decryptCredential(file, intentToResume) - } - .getOrElse { e -> - logcat(ERROR) { - e.asLog("OpenPgpApi ACTION_DECRYPT_VERIFY failed with user interaction") - } - return null - } - } - OpenPgpApi.RESULT_CODE_ERROR -> { - val error = result.getParcelableExtra<OpenPgpError>(OpenPgpApi.RESULT_ERROR) - if (error != null) { - withContext(Dispatchers.Main) { - Toast.makeText( - applicationContext, - "Error from OpenKeyChain: ${error.message}", - Toast.LENGTH_LONG - ) - .show() - } - logcat(ERROR) { - "OpenPgpApi ACTION_DECRYPT_VERIFY failed (${error.errorId}): ${error.message}" - } - } - null - } - else -> { - logcat(ERROR) { "Unrecognized OpenPgpApi result: $resultCode" } - null - } - } - } - } - return null - } -} diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/autofill/AutofillDecryptActivityV2.kt b/app/src/main/java/dev/msfjarvis/aps/ui/autofill/AutofillDecryptActivityV2.kt deleted file mode 100644 index 4fd8f026..00000000 --- a/app/src/main/java/dev/msfjarvis/aps/ui/autofill/AutofillDecryptActivityV2.kt +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ -package dev.msfjarvis.aps.ui.autofill - -import android.app.PendingIntent -import android.content.Context -import android.content.Intent -import android.content.IntentSender -import android.os.Build -import android.os.Bundle -import android.view.autofill.AutofillManager -import androidx.annotation.RequiresApi -import androidx.appcompat.app.AppCompatActivity -import androidx.lifecycle.lifecycleScope -import com.github.androidpasswordstore.autofillparser.AutofillAction -import com.github.androidpasswordstore.autofillparser.Credentials -import com.github.michaelbull.result.getOrElse -import com.github.michaelbull.result.onFailure -import com.github.michaelbull.result.onSuccess -import com.github.michaelbull.result.runCatching -import dagger.hilt.android.AndroidEntryPoint -import dev.msfjarvis.aps.data.crypto.CryptoRepository -import dev.msfjarvis.aps.data.passfile.PasswordEntry -import dev.msfjarvis.aps.ui.crypto.PasswordDialog -import dev.msfjarvis.aps.util.autofill.AutofillPreferences -import dev.msfjarvis.aps.util.autofill.AutofillResponseBuilder -import dev.msfjarvis.aps.util.autofill.DirectoryStructure -import dev.msfjarvis.aps.util.extensions.asLog -import java.io.ByteArrayOutputStream -import java.io.File -import javax.inject.Inject -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import logcat.LogPriority.ERROR -import logcat.logcat - -@RequiresApi(26) -@AndroidEntryPoint -class AutofillDecryptActivityV2 : AppCompatActivity() { - - companion object { - - private const val EXTRA_FILE_PATH = "dev.msfjarvis.aps.autofill.oreo.EXTRA_FILE_PATH" - private const val EXTRA_SEARCH_ACTION = "dev.msfjarvis.aps.autofill.oreo.EXTRA_SEARCH_ACTION" - - private var decryptFileRequestCode = 1 - - fun makeDecryptFileIntent(file: File, forwardedExtras: Bundle, context: Context): Intent { - return Intent(context, AutofillDecryptActivityV2::class.java).apply { - putExtras(forwardedExtras) - putExtra(EXTRA_SEARCH_ACTION, true) - putExtra(EXTRA_FILE_PATH, file.absolutePath) - } - } - - fun makeDecryptFileIntentSender(file: File, context: Context): IntentSender { - val intent = - Intent(context, AutofillDecryptActivityV2::class.java).apply { - putExtra(EXTRA_SEARCH_ACTION, false) - putExtra(EXTRA_FILE_PATH, file.absolutePath) - } - return PendingIntent.getActivity( - context, - decryptFileRequestCode++, - intent, - if (Build.VERSION.SDK_INT >= 31) { - PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE - } else { - PendingIntent.FLAG_CANCEL_CURRENT - }, - ) - .intentSender - } - } - - @Inject lateinit var passwordEntryFactory: PasswordEntry.Factory - @Inject lateinit var repository: CryptoRepository - - private lateinit var directoryStructure: DirectoryStructure - - override fun onStart() { - super.onStart() - val filePath = - intent?.getStringExtra(EXTRA_FILE_PATH) - ?: run { - logcat(ERROR) { "AutofillDecryptActivityV2 started without EXTRA_FILE_PATH" } - finish() - return - } - val clientState = - intent?.getBundleExtra(AutofillManager.EXTRA_CLIENT_STATE) - ?: run { - logcat(ERROR) { "AutofillDecryptActivityV2 started without EXTRA_CLIENT_STATE" } - finish() - return - } - val isSearchAction = intent?.getBooleanExtra(EXTRA_SEARCH_ACTION, true)!! - val action = if (isSearchAction) AutofillAction.Search else AutofillAction.Match - directoryStructure = AutofillPreferences.directoryStructure(this) - logcat { action.toString() } - val dialog = PasswordDialog() - lifecycleScope.launch { - withContext(Dispatchers.Main) { - dialog.password.collectLatest { value -> - if (value != null) { - decrypt(File(filePath), clientState, action, value) - } - } - } - } - dialog.show(supportFragmentManager, "PASSWORD_DIALOG") - } - - private suspend fun decrypt( - filePath: File, - clientState: Bundle, - action: AutofillAction, - password: String, - ) { - val credentials = decryptCredential(filePath, password) - if (credentials == null) { - setResult(RESULT_CANCELED) - } else { - val fillInDataset = - AutofillResponseBuilder.makeFillInDataset( - this@AutofillDecryptActivityV2, - credentials, - clientState, - action - ) - withContext(Dispatchers.Main) { - setResult( - RESULT_OK, - Intent().apply { putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, fillInDataset) } - ) - } - } - withContext(Dispatchers.Main) { finish() } - } - - private suspend fun decryptCredential(file: File, password: String): Credentials? { - runCatching { file.readBytes().inputStream() } - .onFailure { e -> - logcat(ERROR) { e.asLog("File to decrypt not found") } - return null - } - .onSuccess { encryptedInput -> - runCatching { - 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) - } - .getOrElse { e -> - logcat(ERROR) { e.asLog("Failed to parse password entry") } - return null - } - } - } - return null - } -} diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/autofill/AutofillFilterView.kt b/app/src/main/java/dev/msfjarvis/aps/ui/autofill/AutofillFilterView.kt deleted file mode 100644 index 89f1a733..00000000 --- a/app/src/main/java/dev/msfjarvis/aps/ui/autofill/AutofillFilterView.kt +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ -package dev.msfjarvis.aps.ui.autofill - -import android.annotation.TargetApi -import android.app.PendingIntent -import android.content.Context -import android.content.Intent -import android.content.IntentSender -import android.os.Build -import android.os.Bundle -import android.view.View -import android.view.autofill.AutofillManager -import android.widget.TextView -import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult -import androidx.activity.viewModels -import androidx.appcompat.app.AppCompatActivity -import androidx.core.text.bold -import androidx.core.text.buildSpannedString -import androidx.core.text.underline -import androidx.core.widget.addTextChangedListener -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.lifecycleScope -import androidx.recyclerview.widget.LinearLayoutManager -import com.github.androidpasswordstore.autofillparser.FormOrigin -import dagger.hilt.android.AndroidEntryPoint -import dev.msfjarvis.aps.R -import dev.msfjarvis.aps.data.password.PasswordItem -import dev.msfjarvis.aps.databinding.ActivityOreoAutofillFilterBinding -import dev.msfjarvis.aps.util.autofill.AutofillMatcher -import dev.msfjarvis.aps.util.autofill.AutofillPreferences -import dev.msfjarvis.aps.util.autofill.DirectoryStructure -import dev.msfjarvis.aps.util.extensions.viewBinding -import dev.msfjarvis.aps.util.features.Feature -import dev.msfjarvis.aps.util.features.Features -import dev.msfjarvis.aps.util.viewmodel.FilterMode -import dev.msfjarvis.aps.util.viewmodel.ListMode -import dev.msfjarvis.aps.util.viewmodel.SearchMode -import dev.msfjarvis.aps.util.viewmodel.SearchableRepositoryAdapter -import dev.msfjarvis.aps.util.viewmodel.SearchableRepositoryViewModel -import javax.inject.Inject -import logcat.LogPriority.ERROR -import logcat.logcat - -@TargetApi(26) -@AndroidEntryPoint -class AutofillFilterView : AppCompatActivity() { - - companion object { - - private const val HEIGHT_PERCENTAGE = 0.9 - private const val WIDTH_PERCENTAGE = 0.75 - - private const val EXTRA_FORM_ORIGIN_WEB = - "dev.msfjarvis.aps.autofill.oreo.ui.EXTRA_FORM_ORIGIN_WEB" - private const val EXTRA_FORM_ORIGIN_APP = - "dev.msfjarvis.aps.autofill.oreo.ui.EXTRA_FORM_ORIGIN_APP" - private var matchAndDecryptFileRequestCode = 1 - - fun makeMatchAndDecryptFileIntentSender( - context: Context, - formOrigin: FormOrigin - ): IntentSender { - val intent = - Intent(context, AutofillFilterView::class.java).apply { - when (formOrigin) { - is FormOrigin.Web -> putExtra(EXTRA_FORM_ORIGIN_WEB, formOrigin.identifier) - is FormOrigin.App -> putExtra(EXTRA_FORM_ORIGIN_APP, formOrigin.identifier) - } - } - return PendingIntent.getActivity( - context, - matchAndDecryptFileRequestCode++, - intent, - if (Build.VERSION.SDK_INT >= 31) { - PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE - } else { - PendingIntent.FLAG_CANCEL_CURRENT - }, - ) - .intentSender - } - } - - @Inject lateinit var features: Features - private lateinit var formOrigin: FormOrigin - private lateinit var directoryStructure: DirectoryStructure - private val binding by viewBinding(ActivityOreoAutofillFilterBinding::inflate) - - private val model: SearchableRepositoryViewModel by viewModels { - ViewModelProvider.AndroidViewModelFactory(application) - } - - private val decryptAction = - registerForActivityResult(StartActivityForResult()) { result -> - if (result.resultCode == RESULT_OK) { - setResult(RESULT_OK, result.data) - } - finish() - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(binding.root) - setFinishOnTouchOutside(true) - - val params = window.attributes - params.height = (HEIGHT_PERCENTAGE * resources.displayMetrics.heightPixels).toInt() - params.width = (WIDTH_PERCENTAGE * resources.displayMetrics.widthPixels).toInt() - window.attributes = params - - if (intent?.hasExtra(AutofillManager.EXTRA_CLIENT_STATE) != true) { - logcat(ERROR) { "AutofillFilterActivity started without EXTRA_CLIENT_STATE" } - finish() - return - } - formOrigin = - when { - intent?.hasExtra(EXTRA_FORM_ORIGIN_WEB) == true -> { - FormOrigin.Web(intent!!.getStringExtra(EXTRA_FORM_ORIGIN_WEB)!!) - } - intent?.hasExtra(EXTRA_FORM_ORIGIN_APP) == true -> { - FormOrigin.App(intent!!.getStringExtra(EXTRA_FORM_ORIGIN_APP)!!) - } - else -> { - logcat(ERROR) { - "AutofillFilterActivity started without EXTRA_FORM_ORIGIN_WEB or EXTRA_FORM_ORIGIN_APP" - } - finish() - return - } - } - directoryStructure = AutofillPreferences.directoryStructure(this) - - supportActionBar?.hide() - bindUI() - updateSearch() - setResult(RESULT_CANCELED) - } - - private fun bindUI() { - with(binding) { - rvPassword.apply { - adapter = - SearchableRepositoryAdapter( - R.layout.oreo_autofill_filter_row, - ::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) } } - } - } 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) - } - search.apply { - val initialSearch = formOrigin.getPrettyIdentifier(applicationContext, untrusted = false) - setText(initialSearch, TextView.BufferType.EDITABLE) - addTextChangedListener { updateSearch() } - } - origin.text = buildSpannedString { - append(getString(R.string.oreo_autofill_select_and_fill_into)) - append("\n") - bold { append(formOrigin.getPrettyIdentifier(applicationContext, untrusted = true)) } - } - strictDomainSearch.apply { - visibility = if (formOrigin is FormOrigin.Web) View.VISIBLE else View.GONE - isChecked = formOrigin is FormOrigin.Web - setOnCheckedChangeListener { _, _ -> updateSearch() } - } - shouldMatch.text = - getString( - R.string.oreo_autofill_match_with, - formOrigin.getPrettyIdentifier(applicationContext) - ) - model.searchResult.observe(this@AutofillFilterView) { result -> - val list = result.passwordItems - (rvPassword.adapter as SearchableRepositoryAdapter).submitList(list) { - rvPassword.scrollToPosition(0) - } - // Switch RecyclerView out for a "no results" message if the new list is empty and - // the message is not yet shown (and vice versa). - if ( - (list.isEmpty() && rvPasswordSwitcher.nextView.id == rvPasswordEmpty.id) || - (list.isNotEmpty() && rvPasswordSwitcher.nextView.id == rvPassword.id) - ) { - rvPasswordSwitcher.showNext() - } - } - } - } - - private fun updateSearch() { - model.search( - binding.search.text.toString().trim(), - filterMode = - if (binding.strictDomainSearch.isChecked) FilterMode.StrictDomain else FilterMode.Fuzzy, - searchMode = SearchMode.RecursivelyInSubdirectories, - listMode = ListMode.FilesOnly - ) - } - - private fun decryptAndFill(item: PasswordItem) { - if (binding.shouldClear.isChecked) - AutofillMatcher.clearMatchesFor(applicationContext, formOrigin) - if (binding.shouldMatch.isChecked) - AutofillMatcher.addMatchFor(applicationContext, formOrigin, item.file) - // intent?.extras? is checked to be non-null in onCreate - decryptAction.launch( - if (features.isEnabled(Feature.EnablePGPainlessBackend)) { - AutofillDecryptActivityV2.makeDecryptFileIntent(item.file, intent!!.extras!!, this) - } else { - AutofillDecryptActivity.makeDecryptFileIntent(item.file, intent!!.extras!!, this) - } - ) - } -} diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/autofill/AutofillPublisherChangedActivity.kt b/app/src/main/java/dev/msfjarvis/aps/ui/autofill/AutofillPublisherChangedActivity.kt deleted file mode 100644 index 85911815..00000000 --- a/app/src/main/java/dev/msfjarvis/aps/ui/autofill/AutofillPublisherChangedActivity.kt +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ -package dev.msfjarvis.aps.ui.autofill - -import android.annotation.TargetApi -import android.app.PendingIntent -import android.content.Context -import android.content.Intent -import android.content.IntentSender -import android.content.pm.PackageManager -import android.os.Build -import android.os.Bundle -import android.service.autofill.FillResponse -import android.text.format.DateUtils -import android.view.View -import android.view.autofill.AutofillManager -import androidx.appcompat.app.AppCompatActivity -import com.github.androidpasswordstore.autofillparser.FormOrigin -import com.github.androidpasswordstore.autofillparser.computeCertificatesHash -import com.github.michaelbull.result.onFailure -import com.github.michaelbull.result.runCatching -import dev.msfjarvis.aps.R -import dev.msfjarvis.aps.databinding.ActivityOreoAutofillPublisherChangedBinding -import dev.msfjarvis.aps.util.autofill.AutofillMatcher -import dev.msfjarvis.aps.util.autofill.AutofillPublisherChangedException -import dev.msfjarvis.aps.util.extensions.asLog -import dev.msfjarvis.aps.util.extensions.viewBinding -import logcat.LogPriority.ERROR -import logcat.logcat - -@TargetApi(26) -class AutofillPublisherChangedActivity : AppCompatActivity() { - - companion object { - - private const val EXTRA_APP_PACKAGE = "dev.msfjarvis.aps.autofill.oreo.ui.EXTRA_APP_PACKAGE" - private const val EXTRA_FILL_RESPONSE_AFTER_RESET = - "dev.msfjarvis.aps.autofill.oreo.ui.EXTRA_FILL_RESPONSE_AFTER_RESET" - private var publisherChangedRequestCode = 1 - - fun makePublisherChangedIntentSender( - context: Context, - publisherChangedException: AutofillPublisherChangedException, - fillResponseAfterReset: FillResponse?, - ): IntentSender { - val intent = - Intent(context, AutofillPublisherChangedActivity::class.java).apply { - putExtra(EXTRA_APP_PACKAGE, publisherChangedException.formOrigin.identifier) - putExtra(EXTRA_FILL_RESPONSE_AFTER_RESET, fillResponseAfterReset) - } - return PendingIntent.getActivity( - context, - publisherChangedRequestCode++, - intent, - if (Build.VERSION.SDK_INT >= 31) { - PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE - } else { - PendingIntent.FLAG_CANCEL_CURRENT - }, - ) - .intentSender - } - } - - private lateinit var appPackage: String - private val binding by viewBinding(ActivityOreoAutofillPublisherChangedBinding::inflate) - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(binding.root) - setFinishOnTouchOutside(true) - - appPackage = - intent.getStringExtra(EXTRA_APP_PACKAGE) - ?: run { - logcat(ERROR) { "AutofillPublisherChangedActivity started without EXTRA_PACKAGE_NAME" } - finish() - return - } - supportActionBar?.hide() - showPackageInfo() - with(binding) { - okButton.setOnClickListener { finish() } - advancedButton.setOnClickListener { - advancedButton.visibility = View.GONE - warningAppAdvancedInfo.visibility = View.VISIBLE - resetButton.visibility = View.VISIBLE - } - resetButton.setOnClickListener { - AutofillMatcher.clearMatchesFor( - this@AutofillPublisherChangedActivity, - FormOrigin.App(appPackage) - ) - val fillResponse = intent.getParcelableExtra<FillResponse>(EXTRA_FILL_RESPONSE_AFTER_RESET) - setResult( - RESULT_OK, - Intent().apply { putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, fillResponse) } - ) - finish() - } - } - } - - 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) - ) - - 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() - } - } -} diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/autofill/AutofillSaveActivity.kt b/app/src/main/java/dev/msfjarvis/aps/ui/autofill/AutofillSaveActivity.kt deleted file mode 100644 index a963836e..00000000 --- a/app/src/main/java/dev/msfjarvis/aps/ui/autofill/AutofillSaveActivity.kt +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ -package dev.msfjarvis.aps.ui.autofill - -import android.app.PendingIntent -import android.content.Context -import android.content.Intent -import android.content.IntentSender -import android.os.Bundle -import android.view.autofill.AutofillManager -import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult -import androidx.annotation.RequiresApi -import androidx.appcompat.app.AppCompatActivity -import androidx.core.os.bundleOf -import com.github.androidpasswordstore.autofillparser.AutofillAction -import com.github.androidpasswordstore.autofillparser.Credentials -import com.github.androidpasswordstore.autofillparser.FormOrigin -import dagger.hilt.android.AndroidEntryPoint -import dev.msfjarvis.aps.data.repo.PasswordRepository -import dev.msfjarvis.aps.ui.crypto.PasswordCreationActivity -import dev.msfjarvis.aps.ui.crypto.PasswordCreationActivityV2 -import dev.msfjarvis.aps.util.autofill.AutofillMatcher -import dev.msfjarvis.aps.util.autofill.AutofillPreferences -import dev.msfjarvis.aps.util.autofill.AutofillResponseBuilder -import dev.msfjarvis.aps.util.extensions.unsafeLazy -import dev.msfjarvis.aps.util.features.Feature -import dev.msfjarvis.aps.util.features.Features -import java.io.File -import javax.inject.Inject -import logcat.LogPriority.ERROR -import logcat.logcat - -@RequiresApi(26) -@AndroidEntryPoint -class AutofillSaveActivity : AppCompatActivity() { - - companion object { - - private const val EXTRA_FOLDER_NAME = "dev.msfjarvis.aps.autofill.oreo.ui.EXTRA_FOLDER_NAME" - private const val EXTRA_PASSWORD = "dev.msfjarvis.aps.autofill.oreo.ui.EXTRA_PASSWORD" - private const val EXTRA_NAME = "dev.msfjarvis.aps.autofill.oreo.ui.EXTRA_NAME" - private const val EXTRA_SHOULD_MATCH_APP = - "dev.msfjarvis.aps.autofill.oreo.ui.EXTRA_SHOULD_MATCH_APP" - private const val EXTRA_SHOULD_MATCH_WEB = - "dev.msfjarvis.aps.autofill.oreo.ui.EXTRA_SHOULD_MATCH_WEB" - private const val EXTRA_GENERATE_PASSWORD = - "dev.msfjarvis.aps.autofill.oreo.ui.EXTRA_GENERATE_PASSWORD" - - private var saveRequestCode = 1 - - fun makeSaveIntentSender( - context: Context, - credentials: Credentials?, - formOrigin: FormOrigin - ): IntentSender { - val identifier = formOrigin.getPrettyIdentifier(context, untrusted = false) - // Prevent directory traversals - val sanitizedIdentifier = - identifier.replace('\\', '_').replace('/', '_').trimStart('.').takeUnless { it.isBlank() } - ?: formOrigin.identifier - val directoryStructure = AutofillPreferences.directoryStructure(context) - val folderName = - directoryStructure.getSaveFolderName( - sanitizedIdentifier = sanitizedIdentifier, - username = credentials?.username - ) - val fileName = - directoryStructure.getSaveFileName( - username = credentials?.username, - identifier = identifier - ) - val intent = - Intent(context, AutofillSaveActivity::class.java).apply { - putExtras( - bundleOf( - EXTRA_FOLDER_NAME to folderName, - EXTRA_NAME to fileName, - EXTRA_PASSWORD to credentials?.password, - EXTRA_SHOULD_MATCH_APP to - formOrigin.identifier.takeIf { formOrigin is FormOrigin.App }, - EXTRA_SHOULD_MATCH_WEB to - formOrigin.identifier.takeIf { formOrigin is FormOrigin.Web }, - EXTRA_GENERATE_PASSWORD to (credentials == null) - ) - ) - } - return PendingIntent.getActivity( - context, - saveRequestCode++, - intent, - PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE - ) - .intentSender - } - } - - private val formOrigin by unsafeLazy { - val shouldMatchApp: String? = intent.getStringExtra(EXTRA_SHOULD_MATCH_APP) - val shouldMatchWeb: String? = intent.getStringExtra(EXTRA_SHOULD_MATCH_WEB) - if (shouldMatchApp != null && shouldMatchWeb == null) { - FormOrigin.App(shouldMatchApp) - } else if (shouldMatchApp == null && shouldMatchWeb != null) { - FormOrigin.Web(shouldMatchWeb) - } else { - null - } - } - - @Inject lateinit var features: Features - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - val repo = PasswordRepository.getRepositoryDirectory() - val creationActivity = - if (features.isEnabled(Feature.EnablePGPainlessBackend)) - PasswordCreationActivityV2::class.java - else PasswordCreationActivity::class.java - val saveIntent = - Intent(this, creationActivity).apply { - putExtras( - bundleOf( - "REPO_PATH" to repo.absolutePath, - "FILE_PATH" to repo.resolve(intent.getStringExtra(EXTRA_FOLDER_NAME)!!).absolutePath, - PasswordCreationActivity.EXTRA_FILE_NAME to intent.getStringExtra(EXTRA_NAME), - PasswordCreationActivity.EXTRA_PASSWORD to intent.getStringExtra(EXTRA_PASSWORD), - PasswordCreationActivity.EXTRA_GENERATE_PASSWORD to - intent.getBooleanExtra(EXTRA_GENERATE_PASSWORD, false) - ) - ) - } - registerForActivityResult(StartActivityForResult()) { result -> - val data = result.data - if (result.resultCode == RESULT_OK && data != null) { - val createdPath = data.getStringExtra("CREATED_FILE")!! - formOrigin?.let { AutofillMatcher.addMatchFor(this, it, File(createdPath)) } - val password = data.getStringExtra("PASSWORD") - val resultIntent = - if (password != null) { - // Password was generated and should be filled into a form. - val username = data.getStringExtra("USERNAME") - val clientState = - intent?.getBundleExtra(AutofillManager.EXTRA_CLIENT_STATE) - ?: run { - logcat(ERROR) { "AutofillDecryptActivity started without EXTRA_CLIENT_STATE" } - finish() - return@registerForActivityResult - } - val credentials = Credentials(username, password, null) - val fillInDataset = - AutofillResponseBuilder.makeFillInDataset( - this, - credentials, - clientState, - AutofillAction.Generate - ) - Intent().apply { - putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, fillInDataset) - } - } else { - // Password was extracted from a form, there is nothing to fill. - Intent() - } - setResult(RESULT_OK, resultIntent) - } else { - setResult(RESULT_CANCELED) - } - finish() - } - .launch(saveIntent) - } -} diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/autofill/PasswordViewHolder.kt b/app/src/main/java/dev/msfjarvis/aps/ui/autofill/PasswordViewHolder.kt deleted file mode 100644 index eacd49c3..00000000 --- a/app/src/main/java/dev/msfjarvis/aps/ui/autofill/PasswordViewHolder.kt +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ -package dev.msfjarvis.aps.ui.autofill - -import android.view.View -import android.widget.TextView -import androidx.recyclerview.widget.RecyclerView -import dev.msfjarvis.aps.R - -class PasswordViewHolder(view: View) : RecyclerView.ViewHolder(view) { - - val title: TextView = itemView.findViewById(R.id.title) - val subtitle: TextView = itemView.findViewById(R.id.subtitle) -} |
