diff options
| author | Danilo M. <danix@danix.xyz> | 2026-11-03 08:59:12 +0100 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-11-03 08:59:12 +0100 |
| commit | 7bdcb041de95c8d8d8d14f1dbd34a3380b326871 (patch) | |
| tree | 42c1c3799f821ecc796fd88708b494237573583a | |
| parent | 32511cf9a3970727df982ac2509696e534a027e7 (diff) | |
| download | HardenedPass-7bdcb041de95c8d8d8d14f1dbd34a3380b326871.tar.gz HardenedPass-7bdcb041de95c8d8d8d14f1dbd34a3380b326871.zip | |
refactor(app): remove the legacy editor, generators and folder picker
The Compose editor, generator sheet, OTP dialog and folder picker
replace PasswordCreationActivity, the generator and manual OTP dialog
fragments, SelectFolderActivity with its fragment and adapter, their
layouts and menus, and upstream's unused EditPasswordScreen with the
PasswordField it used. flowbinding and swiperefreshlayout had no other
users. BasePGPActivity keeps what autofill decryption still needs.
Also drops two stale SshKey baseline entries, the snackbar FAB anchor
whose view is gone, and resources only the deleted files used.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
39 files changed, 9 insertions, 1958 deletions
diff --git a/app/build.gradle.kts b/app/build.gradle.kts index cc10d21e..5f7a8ee8 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -73,7 +73,6 @@ dependencies { implementation(libs.androidx.preference) implementation(libs.androidx.recyclerview) implementation(libs.androidx.recyclerviewSelection) - implementation(libs.androidx.swiperefreshlayout) implementation(libs.compose.ui.tooling) implementation(libs.dagger.hilt.android) @@ -86,7 +85,6 @@ dependencies { implementation(libs.aps.zxingAndroidEmbedded) implementation(libs.thirdparty.fastscroll) - implementation(libs.thirdparty.flowbinding.android) implementation(libs.thirdparty.jgit) { exclude(group = "org.apache.httpcomponents", module = "httpclient") } diff --git a/app/lint-baseline.xml b/app/lint-baseline.xml index 20c4683f..a19d0ad9 100644 --- a/app/lint-baseline.xml +++ b/app/lint-baseline.xml @@ -36,29 +36,7 @@ errorLine2=" ~~~~~~~~~~~~~~"> <location file="src/main/java/app/passwordstore/util/git/sshj/SshKey.kt" - line="238" - column="17"/> - </issue> - - <issue - id="RawDispatchersUse" - message="Use SlackDispatchers." - errorLine1=" withContext(Dispatchers.IO) {" - errorLine2=" ~~~~~~~~~~~~~~"> - <location - file="src/main/java/app/passwordstore/util/git/sshj/SshKey.kt" - line="247" - column="17"/> - </issue> - - <issue - id="RawDispatchersUse" - message="Use SlackDispatchers." - errorLine1=" withContext(Dispatchers.IO) {" - errorLine2=" ~~~~~~~~~~~~~~"> - <location - file="src/main/java/app/passwordstore/util/git/sshj/SshKey.kt" - line="259" + line="249" column="17"/> </issue> @@ -197,26 +175,4 @@ column="4"/> </issue> - <issue - id="SetTextI18n" - message="Number formatting does not take into account locale settings. Consider using `String.format` instead." - errorLine1=" binding.passwordLengthText.setText(prefs.getInt(DICEWARE_LENGTH, 5).toString())" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="src/main/java/app/passwordstore/ui/dialogs/DicewarePasswordGeneratorDialogFragment.kt" - line="46" - column="40"/> - </issue> - - <issue - id="SetTextI18n" - message="Number formatting does not take into account locale settings. Consider using `String.format` instead." - errorLine1=" binding.lengthNumber.setText(prefs.getInt(PreferenceKeys.LENGTH, 20).toString())" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="src/main/java/app/passwordstore/ui/dialogs/PasswordGeneratorDialogFragment.kt" - line="54" - column="34"/> - </issue> - </issues> diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b4859c0e..0429b2ac 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -95,12 +95,6 @@ android:label="@string/action_settings" android:parentActivityName=".ui.main.MainActivity" /> - <activity - android:name=".ui.crypto.PasswordCreationActivity" - android:exported="false" - android:label="@string/new_password_title" - android:windowSoftInputMode="adjustResize" /> - <service android:name=".util.services.ClipboardService" android:exported="false" @@ -124,9 +118,6 @@ </service> <activity - android:name=".ui.folderselect.SelectFolderActivity" - android:exported="false" /> - <activity android:name=".ui.sshkeygen.SshKeyImportActivity" android:exported="false" android:theme="@style/NoBackgroundThemeM3" diff --git a/app/src/main/java/app/passwordstore/injection/pwgen/DicewareModule.kt b/app/src/main/java/app/passwordstore/injection/pwgen/DicewareModule.kt index 8572e3b8..c9c4e928 100644 --- a/app/src/main/java/app/passwordstore/injection/pwgen/DicewareModule.kt +++ b/app/src/main/java/app/passwordstore/injection/pwgen/DicewareModule.kt @@ -13,7 +13,6 @@ import app.passwordstore.passgen.diceware.RandomIntGenerator import dagger.Module import dagger.Provides import dagger.hilt.InstallIn -import dagger.hilt.android.components.FragmentComponent import dagger.hilt.android.components.ViewModelComponent import dagger.hilt.android.qualifiers.ApplicationContext import java.io.InputStream @@ -21,7 +20,7 @@ import java.security.SecureRandom import javax.inject.Qualifier @Module -@InstallIn(FragmentComponent::class, ViewModelComponent::class) +@InstallIn(ViewModelComponent::class) object DicewareModule { @Provides diff --git a/app/src/main/java/app/passwordstore/ui/adapters/PasswordItemRecyclerAdapter.kt b/app/src/main/java/app/passwordstore/ui/adapters/PasswordItemRecyclerAdapter.kt deleted file mode 100644 index 54be64d6..00000000 --- a/app/src/main/java/app/passwordstore/ui/adapters/PasswordItemRecyclerAdapter.kt +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ -package app.passwordstore.ui.adapters - -import android.text.SpannableString -import android.text.style.RelativeSizeSpan -import android.view.MotionEvent -import android.view.View -import androidx.appcompat.widget.AppCompatImageView -import androidx.appcompat.widget.AppCompatTextView -import androidx.recyclerview.selection.ItemDetailsLookup -import androidx.recyclerview.selection.Selection -import androidx.recyclerview.widget.RecyclerView -import app.passwordstore.R -import app.passwordstore.data.password.PasswordItem -import app.passwordstore.util.coroutines.DispatcherProvider -import app.passwordstore.util.viewmodel.SearchableRepositoryAdapter -import app.passwordstore.util.viewmodel.stableId -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.withContext - -open class PasswordItemRecyclerAdapter( - coroutineScope: CoroutineScope, - dispatcherProvider: DispatcherProvider, -) : - SearchableRepositoryAdapter<PasswordItemRecyclerAdapter.PasswordItemViewHolder>( - R.layout.password_row_layout, - ::PasswordItemViewHolder, - coroutineScope, - dispatcherProvider, - PasswordItemViewHolder::bind, - ) { - - fun makeSelectable(recyclerView: RecyclerView) { - makeSelectable(recyclerView, ::PasswordItemDetailsLookup) - } - - override fun onItemClicked( - listener: (holder: PasswordItemViewHolder, item: PasswordItem) -> Unit - ): PasswordItemRecyclerAdapter { - return super.onItemClicked(listener) as PasswordItemRecyclerAdapter - } - - override fun onSelectionChanged( - listener: (selection: Selection<String>) -> Unit - ): PasswordItemRecyclerAdapter { - return super.onSelectionChanged(listener) as PasswordItemRecyclerAdapter - } - - class PasswordItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { - - private val name: AppCompatTextView = itemView.findViewById(R.id.label) - private val childCount: AppCompatTextView = itemView.findViewById(R.id.child_count) - private val folderIndicator: AppCompatImageView = itemView.findViewById(R.id.folder_indicator) - var itemDetails: ItemDetailsLookup.ItemDetails<String>? = null - - suspend fun bind(item: PasswordItem, dispatcherProvider: DispatcherProvider) { - val parentPath = item.fullPathToParent.replace("(^/)|(/$)".toRegex(), "") - val source = - if (parentPath.isNotEmpty()) { - "$parentPath\n$item" - } else { - "$item" - } - val spannable = SpannableString(source) - spannable.setSpan(RelativeSizeSpan(0.7f), 0, parentPath.length, 0) - name.text = spannable - if (item.type == PasswordItem.TYPE_CATEGORY) { - folderIndicator.visibility = View.VISIBLE - val count = - withContext(dispatcherProvider.io()) { - item.file.listFiles { path -> path.isDirectory || path.extension == "gpg" }?.size ?: 0 - } - childCount.visibility = if (count > 0) View.VISIBLE else View.GONE - childCount.text = "$count" - } else { - childCount.visibility = View.GONE - folderIndicator.visibility = View.GONE - } - itemDetails = - object : ItemDetailsLookup.ItemDetails<String>() { - override fun getPosition() = absoluteAdapterPosition - - override fun getSelectionKey() = item.stableId - } - } - } - - class PasswordItemDetailsLookup(private val recyclerView: RecyclerView) : - ItemDetailsLookup<String>() { - - override fun getItemDetails(event: MotionEvent): ItemDetails<String>? { - val view = recyclerView.findChildViewUnder(event.x, event.y) ?: return null - return (recyclerView.getChildViewHolder(view) as PasswordItemViewHolder).itemDetails - } - } -} diff --git a/app/src/main/java/app/passwordstore/ui/crypto/BasePGPActivity.kt b/app/src/main/java/app/passwordstore/ui/crypto/BasePGPActivity.kt index ba49a62c..ccd3bf08 100644 --- a/app/src/main/java/app/passwordstore/ui/crypto/BasePGPActivity.kt +++ b/app/src/main/java/app/passwordstore/ui/crypto/BasePGPActivity.kt @@ -29,7 +29,6 @@ import app.passwordstore.util.extensions.clipboard import app.passwordstore.util.extensions.getString import app.passwordstore.util.extensions.sharedPrefs import app.passwordstore.util.extensions.snackbar -import app.passwordstore.util.extensions.unsafeLazy import app.passwordstore.util.services.ClipboardService import app.passwordstore.util.settings.Constants import app.passwordstore.util.settings.PreferenceKeys @@ -44,12 +43,6 @@ import kotlinx.coroutines.withContext @AndroidEntryPoint open class BasePGPActivity : AppCompatActivity() { - /** Full path to the repository */ - val repoPath by unsafeLazy { intent.getStringExtra("REPO_PATH")!! } - - /** Full path to the password file being worked on */ - val fullPath by unsafeLazy { intent.getStringExtra("FILE_PATH")!! } - /** Action to invoke if [keyImportAction] succeeds. */ private var onKeyImport: (() -> Unit)? = null private val keyImportAction = @@ -103,7 +96,6 @@ open class BasePGPActivity : AppCompatActivity() { companion object { const val EXTRA_FILE_PATH = "FILE_PATH" - const val EXTRA_REPO_PATH = "REPO_PATH" /** Gets the relative path to the repository */ fun getRelativePath(fullPath: String, repositoryPath: String): String = diff --git a/app/src/main/java/app/passwordstore/ui/crypto/EditPasswordScreen.kt b/app/src/main/java/app/passwordstore/ui/crypto/EditPasswordScreen.kt deleted file mode 100644 index d8610307..00000000 --- a/app/src/main/java/app/passwordstore/ui/crypto/EditPasswordScreen.kt +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package app.passwordstore.ui.crypto - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.material3.TextField -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import app.passwordstore.R -import app.passwordstore.data.passfile.PasswordEntry -import app.passwordstore.ui.APSAppBar -import app.passwordstore.ui.compose.PasswordField -import app.passwordstore.ui.compose.theme.APSTheme -import app.passwordstore.ui.compose.theme.SpacingLarge -import app.passwordstore.ui.compose.theme.SpacingMedium -import app.passwordstore.util.time.UserClock -import app.passwordstore.util.totp.UriTotpFinder - -/** Composable to show allow editing an existing [PasswordEntry]. */ -@Composable -fun EditPasswordScreen( - entryName: String, - entry: PasswordEntry, - onNavigateUp: () -> Unit, - @Suppress("UNUSED_PARAMETER") onSave: (PasswordEntry) -> Unit, - modifier: Modifier = Modifier, -) { - Scaffold( - topBar = { - APSAppBar( - title = entryName, - navigationIcon = painterResource(R.drawable.ic_arrow_back_black_24dp), - onNavigationIconClick = onNavigateUp, - backgroundColor = MaterialTheme.colorScheme.surface, - ) - } - ) { paddingValues -> - Box(modifier = modifier.padding(paddingValues)) { - Column( - modifier = - Modifier.padding(vertical = SpacingMedium, horizontal = SpacingLarge).fillMaxSize() - ) { - if (entry.password != null) { - PasswordField( - value = entry.password!!, - label = stringResource(R.string.password), - initialVisibility = false, - modifier = Modifier.padding(bottom = SpacingMedium).fillMaxWidth(), - ) - } - ExtraContent(entry = entry) - } - } - } -} - -@Composable -private fun ExtraContent(entry: PasswordEntry, modifier: Modifier = Modifier) { - TextField( - value = entry.extraContentString, - onValueChange = {}, - label = { Text("Extra content") }, - modifier = modifier.fillMaxWidth(), - ) -} - -@Preview -@Composable -private fun EditPasswordScreenPreview() { - APSTheme { - EditPasswordScreen( - entryName = "Test Entry", - entry = createTestEntry(), - onNavigateUp = {}, - onSave = {}, - ) - } -} - -private fun createTestEntry() = - PasswordEntry( - UserClock(), - UriTotpFinder(), - """ - |My Password - |otpauth://totp/ACME%20Co:john@example.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30 - |login: msfjarvis - |URL: example.com - """ - .trimMargin() - .encodeToByteArray(), - ) diff --git a/app/src/main/java/app/passwordstore/ui/crypto/PasswordCreationActivity.kt b/app/src/main/java/app/passwordstore/ui/crypto/PasswordCreationActivity.kt deleted file mode 100644 index 7652c98e..00000000 --- a/app/src/main/java/app/passwordstore/ui/crypto/PasswordCreationActivity.kt +++ /dev/null @@ -1,510 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package app.passwordstore.ui.crypto - -import android.content.Context -import android.content.Intent -import android.content.pm.PackageManager -import android.graphics.Bitmap -import android.graphics.ImageDecoder -import android.os.Build -import android.os.Bundle -import android.provider.MediaStore -import android.text.InputType -import android.view.Menu -import android.view.MenuItem -import android.view.View -import androidx.activity.result.contract.ActivityResultContracts -import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult -import androidx.core.content.edit -import androidx.core.view.isVisible -import androidx.core.widget.doAfterTextChanged -import androidx.lifecycle.lifecycleScope -import app.passwordstore.R -import app.passwordstore.data.passfile.PasswordEntry -import app.passwordstore.databinding.PasswordCreationActivityBinding -import app.passwordstore.ui.dialogs.DicewarePasswordGeneratorDialogFragment -import app.passwordstore.ui.dialogs.OtpImportDialogFragment -import app.passwordstore.ui.dialogs.PasswordGeneratorDialogFragment -import app.passwordstore.util.autofill.AutofillPreferences -import app.passwordstore.util.extensions.asLog -import app.passwordstore.util.extensions.base64 -import app.passwordstore.util.extensions.commitChange -import app.passwordstore.util.extensions.getString -import app.passwordstore.util.extensions.isInsideRepository -import app.passwordstore.util.extensions.snackbar -import app.passwordstore.util.extensions.unsafeLazy -import app.passwordstore.util.extensions.viewBinding -import app.passwordstore.util.settings.DirectoryStructure -import app.passwordstore.util.settings.PreferenceKeys -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 com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.google.zxing.BinaryBitmap -import com.google.zxing.LuminanceSource -import com.google.zxing.RGBLuminanceSource -import com.google.zxing.common.HybridBinarizer -import com.google.zxing.integration.android.IntentIntegrator -import com.google.zxing.integration.android.IntentIntegrator.QR_CODE -import com.google.zxing.qrcode.QRCodeReader -import dagger.hilt.android.AndroidEntryPoint -import java.io.ByteArrayOutputStream -import java.io.IOException -import java.nio.file.Paths -import javax.inject.Inject -import kotlin.io.path.absolutePathString -import kotlin.io.path.createDirectories -import kotlin.io.path.deleteIfExists -import kotlin.io.path.exists -import kotlin.io.path.isSameFileAs -import kotlin.io.path.nameWithoutExtension -import kotlin.io.path.pathString -import kotlin.io.path.relativeTo -import kotlin.io.path.writeBytes -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import logcat.LogPriority.ERROR -import logcat.asLog -import logcat.logcat - -@AndroidEntryPoint -class PasswordCreationActivity : BasePGPActivity() { - - private val binding by viewBinding(PasswordCreationActivityBinding::inflate) - @Inject lateinit var passwordEntryFactory: PasswordEntry.Factory - - private val suggestedName by unsafeLazy { intent.getStringExtra(EXTRA_FILE_NAME) } - private val suggestedUsername by unsafeLazy { intent.getStringExtra(EXTRA_USERNAME) } - private val suggestedPass by unsafeLazy { intent.getStringExtra(EXTRA_PASSWORD) } - private val suggestedExtra by unsafeLazy { intent.getStringExtra(EXTRA_EXTRA_CONTENT) } - private val shouldGeneratePassword by unsafeLazy { - intent.getBooleanExtra(EXTRA_GENERATE_PASSWORD, false) - } - private val editing by unsafeLazy { intent.getBooleanExtra(EXTRA_EDITING, false) } - private var oldCategory: String? = null - private var copy: Boolean = false - - private val otpImportAction = - registerForActivityResult(StartActivityForResult()) { result -> - if (result.resultCode == RESULT_OK) { - binding.otpImportButton.isVisible = false - val intentResult = IntentIntegrator.parseActivityResult(RESULT_OK, result.data) - val contents = "${intentResult.contents}\n" - val currentExtras = binding.extraContent.text.toString() - if (currentExtras.isNotEmpty() && currentExtras.last() != '\n') - binding.extraContent.append("\n$contents") - else binding.extraContent.append(contents) - snackbar(message = getString(R.string.otp_import_success)) - } else { - snackbar(message = getString(R.string.otp_import_failure_generic)) - } - } - - private val imageImportAction = - registerForActivityResult(ActivityResultContracts.GetContent()) { imageUri -> - if (imageUri == null) { - snackbar(message = getString(R.string.otp_import_failure_no_selection)) - return@registerForActivityResult - } - val bitmap = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - ImageDecoder.decodeBitmap(ImageDecoder.createSource(contentResolver, imageUri)) - .copy(Bitmap.Config.ARGB_8888, true) - } else { - @Suppress("DEPRECATION") MediaStore.Images.Media.getBitmap(contentResolver, imageUri) - } - val intArray = IntArray(bitmap.width * bitmap.height) - // copy pixel data from the Bitmap into the 'intArray' array - bitmap.getPixels(intArray, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height) - val source: LuminanceSource = RGBLuminanceSource(bitmap.width, bitmap.height, intArray) - val binaryBitmap = BinaryBitmap(HybridBinarizer(source)) - - 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 - } - .onFailure { snackbar(message = getString(R.string.otp_import_failure_generic)) } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - supportActionBar?.setDisplayHomeAsUpEnabled(true) - title = - if (editing) getString(R.string.edit_password) else getString(R.string.new_password_title) - with(binding) { - setContentView(root) - generatePassword.setOnClickListener { generatePassword() } - otpImportButton.setOnClickListener { - supportFragmentManager.setFragmentResultListener( - OTP_RESULT_REQUEST_KEY, - this@PasswordCreationActivity, - ) { requestKey, bundle -> - if (requestKey == OTP_RESULT_REQUEST_KEY) { - val contents = bundle.getString(RESULT) - val currentExtras = binding.extraContent.text.toString() - if (currentExtras.isNotEmpty() && currentExtras.last() != '\n') - binding.extraContent.append("\n$contents") - else binding.extraContent.append(contents) - } - } - val hasCamera = packageManager?.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY) == true - if (hasCamera) { - val items = - arrayOf( - getString(R.string.otp_import_qr_code), - getString(R.string.otp_import_from_file), - getString(R.string.otp_import_manual_entry), - ) - MaterialAlertDialogBuilder(this@PasswordCreationActivity) - .setItems(items) { _, index -> - when (index) { - 0 -> - otpImportAction.launch( - IntentIntegrator(this@PasswordCreationActivity) - .setOrientationLocked(false) - .setBeepEnabled(false) - .setDesiredBarcodeFormats(QR_CODE) - .createScanIntent() - ) - 1 -> imageImportAction.launch("image/*") - 2 -> OtpImportDialogFragment().show(supportFragmentManager, "OtpImport") - } - } - .show() - } else { - OtpImportDialogFragment().show(supportFragmentManager, "OtpImport") - } - } - - directoryInputLayout.apply { - if (suggestedName != null || suggestedPass != null || shouldGeneratePassword) { - isEnabled = true - } else { - setBackgroundColor(getColor(android.R.color.transparent)) - } - val path = getRelativePath(fullPath, repoPath) - // Keep empty path field visible if it is editable. - if (path.isEmpty() && !isEnabled) visibility = View.GONE - else { - directory.setText(path) - oldCategory = path - } - } - if (suggestedName != null) { - filename.setText(suggestedName) - } else { - filename.requestFocus() - } - - if ( - AutofillPreferences.directoryStructure(this@PasswordCreationActivity) == - DirectoryStructure.EncryptedUsername || suggestedUsername != null - ) { - usernameInputLayout.visibility = View.VISIBLE - if (suggestedUsername != null) username.setText(suggestedUsername) - else if (suggestedName != null) username.requestFocus() - } - - // Allow the user to quickly switch between storing the username as the filename or - // in the encrypted extras. This only makes sense if the directory structure is - // FileBased. - if ( - suggestedName == null && - AutofillPreferences.directoryStructure(this@PasswordCreationActivity) == - DirectoryStructure.FileBased - ) { - encryptUsername.apply { - visibility = View.VISIBLE - setOnClickListener { - if (isChecked) { - // User wants to enable username encryption, so we use the filename - // as username and insert it into the username input field. - val login = filename.text.toString() - filename.text?.clear() - username.setText(login) - usernameInputLayout.apply { visibility = View.VISIBLE } - } else { - // User wants to disable username encryption, so we take the username - // from the username text field and insert it into the filename input field. - val login = username.text.toString() - username.text?.clear() - filename.setText(login) - usernameInputLayout.apply { visibility = View.GONE } - } - } - } - } - suggestedPass?.let { - password.setText(it) - password.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD - } - suggestedExtra?.let { extraContent.setText(it) } - if (shouldGeneratePassword) { - generatePassword() - password.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD - } - } - listOf(binding.filename, binding.username, binding.extraContent).forEach { - it.doAfterTextChanged { updateViewState() } - } - updateViewState() - } - - override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.pgp_handler_new_password, menu) - return true - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - android.R.id.home -> { - setResult(RESULT_CANCELED) - onBackPressedDispatcher.onBackPressed() - } - R.id.save_password -> { - copy = false - requireKeysExist { encrypt() } - } - R.id.save_and_copy_password -> { - copy = true - requireKeysExist { encrypt() } - } - else -> return super.onOptionsItemSelected(item) - } - return true - } - - private fun generatePassword() { - supportFragmentManager.setFragmentResultListener(PASSWORD_RESULT_REQUEST_KEY, this) { - requestKey, - bundle -> - if (requestKey == PASSWORD_RESULT_REQUEST_KEY) { - binding.password.setText(bundle.getString(RESULT)) - } - } - when (settings.getString(PreferenceKeys.PREF_KEY_PWGEN_TYPE) ?: KEY_PWGEN_TYPE_DICEWARE) { - KEY_PWGEN_TYPE_CLASSIC -> - PasswordGeneratorDialogFragment().show(supportFragmentManager, "generator") - KEY_PWGEN_TYPE_DICEWARE -> - DicewarePasswordGeneratorDialogFragment().show(supportFragmentManager, "generator") - } - } - - private fun updateViewState() = - with(binding) { - encryptUsername.apply { - if (visibility != View.VISIBLE) return@apply - val hasUsernameInFileName = filename.text.toString().isNotBlank() - val usernameIsEncrypted = username.text.toString().isNotEmpty() - isEnabled = hasUsernameInFileName xor usernameIsEncrypted - isChecked = usernameIsEncrypted - } - // Use PasswordEntry to parse extras for OTP - val entry = - passwordEntryFactory.create("PLACEHOLDER\n${extraContent.text}".encodeToByteArray()) - otpImportButton.isVisible = !entry.hasTotp() - } - - /** Encrypts the password and the extra content */ - private fun encrypt() { - with(binding) { - val oldName = suggestedName - val editName = filename.text.toString().trim() - var editUsername = username.text.toString() - val editPass = password.text.toString() - val editExtra = extraContent.text.toString() - - if (editName.isEmpty()) { - snackbar(message = resources.getString(R.string.file_toast_text)) - return@with - } else if (editName.contains('/')) { - snackbar(message = resources.getString(R.string.invalid_filename_text)) - return@with - } - - if (!editUsername.isEmpty()) { - editUsername = "\nusername: $editUsername" - } - - if (editPass.isEmpty() && editExtra.isEmpty()) { - snackbar(message = resources.getString(R.string.empty_toast_text)) - return@with - } - - if (copy) { - copyPasswordToClipboard(editPass) - } - - // pass enters the key ID into `.gpg-id`. - val gpgIdentifiers = getPGPIdentifiers(directory.text.toString()) ?: return@with - val content = "$editPass$editUsername\n$editExtra" - val path = - when { - // If we allowed the user to edit the relative path, we have to consider it here - // instead of fullPath. - directoryInputLayout.isEnabled -> { - val editRelativePath = directory.text.toString().trim() - if (editRelativePath.isEmpty()) { - snackbar(message = resources.getString(R.string.path_toast_text)) - return - } - val passwordDirectory = Paths.get(repoPath, editRelativePath.trim('/')) - passwordDirectory.createDirectories() - if (!passwordDirectory.exists()) { - snackbar( - message = - "Failed to create directory ${passwordDirectory.relativeTo(Paths.get(repoPath)).pathString}" - ) - return - } - - "${passwordDirectory.pathString}/$editName.gpg" - } - else -> "$fullPath/$editName.gpg" - } - - lifecycleScope.launch(dispatcherProvider.main()) { - runCatching { - val result = - withContext(dispatcherProvider.io()) { - val outputStream = ByteArrayOutputStream() - repository.encrypt(gpgIdentifiers, content.byteInputStream(), outputStream) - } - .getOrElse { e -> - logcat(ERROR) { e.asLog("Failed to encrypt password file") } - MaterialAlertDialogBuilder(this@PasswordCreationActivity) - .setTitle(R.string.password_creation_file_fail_title) - .setMessage( - getString(R.string.password_creation_encrypt_fail_message, e.message) - ) - .setPositiveButton(android.R.string.ok, null) - .show() - return@runCatching - } - val passwordFile = Paths.get(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 != passwordFile.nameWithoutExtension)) && - passwordFile.exists() - ) { - snackbar(message = getString(R.string.password_creation_duplicate_error)) - return@runCatching - } - - if (!passwordFile.toFile().isInsideRepository()) { - snackbar(message = getString(R.string.message_error_destination_outside_repo)) - return@runCatching - } - - withContext(dispatcherProvider.io()) { passwordFile.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('/')}/$suggestedName.gpg".base64() - val timestamp = preference.getString(oldFilePathHash) - if (timestamp != null) { - preference.edit { - remove(oldFilePathHash) - putString(passwordFile.absolutePathString().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(passwordFile.toFile()) - returnIntent.putExtra(RETURN_EXTRA_USERNAME, username) - } - - if ( - directoryInputLayout.isVisible && - directoryInputLayout.isEnabled && - oldName != editName - ) { - val oldPath = Paths.get(repoPath, oldCategory?.trim('/') ?: "", "$oldName.gpg") - if (!oldPath.isSameFileAs(passwordFile) && !oldPath.deleteIfExists()) { - setResult(RESULT_CANCELED) - MaterialAlertDialogBuilder(this@PasswordCreationActivity) - .setTitle(R.string.password_creation_file_fail_title) - .setMessage( - getString(R.string.password_creation_file_delete_fail_message, oldName) - ) - .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") } - setResult(RESULT_CANCELED) - MaterialAlertDialogBuilder(this@PasswordCreationActivity) - .setTitle(getString(R.string.password_creation_file_fail_title)) - .setMessage(getString(R.string.password_creation_file_write_fail_message)) - .setCancelable(false) - .setPositiveButton(android.R.string.ok) { _, _ -> finish() } - .show() - } else { - logcat(ERROR) { e.asLog() } - } - } - } - } - } - - companion object { - - private const val KEY_PWGEN_TYPE_CLASSIC = "classic" - private const val KEY_PWGEN_TYPE_DICEWARE = "diceware" - const val PASSWORD_RESULT_REQUEST_KEY = "PASSWORD_GENERATOR" - const val OTP_RESULT_REQUEST_KEY = "OTP_IMPORT" - const val RESULT = "RESULT" - const val RETURN_EXTRA_CREATED_FILE = "CREATED_FILE" - const val RETURN_EXTRA_NAME = "NAME" - const val RETURN_EXTRA_LONG_NAME = "LONG_NAME" - const val RETURN_EXTRA_USERNAME = "USERNAME" - const val RETURN_EXTRA_PASSWORD = "PASSWORD" - const val EXTRA_FILE_NAME = "FILENAME" - const val EXTRA_USERNAME = "USERNAME" - const val EXTRA_PASSWORD = "PASSWORD" - const val EXTRA_EXTRA_CONTENT = "EXTRA_CONTENT" - const val EXTRA_GENERATE_PASSWORD = "GENERATE_PASSWORD" - const val EXTRA_EDITING = "EDITING" - } -} diff --git a/app/src/main/java/app/passwordstore/ui/dialogs/DicewarePasswordGeneratorDialogFragment.kt b/app/src/main/java/app/passwordstore/ui/dialogs/DicewarePasswordGeneratorDialogFragment.kt deleted file mode 100644 index 2605e0ef..00000000 --- a/app/src/main/java/app/passwordstore/ui/dialogs/DicewarePasswordGeneratorDialogFragment.kt +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package app.passwordstore.ui.dialogs - -import android.app.AlertDialog -import android.app.Dialog -import android.content.SharedPreferences -import android.graphics.Typeface -import android.os.Bundle -import androidx.core.content.edit -import androidx.core.os.bundleOf -import androidx.fragment.app.DialogFragment -import androidx.fragment.app.setFragmentResult -import androidx.lifecycle.lifecycleScope -import app.passwordstore.R -import app.passwordstore.databinding.FragmentPwgenDicewareBinding -import app.passwordstore.injection.prefs.PasswordGeneratorPreferences -import app.passwordstore.passgen.diceware.DicewarePassphraseGenerator -import app.passwordstore.ui.crypto.PasswordCreationActivity -import app.passwordstore.util.extensions.getString -import app.passwordstore.util.settings.PreferenceKeys.DICEWARE_LENGTH -import app.passwordstore.util.settings.PreferenceKeys.DICEWARE_SEPARATOR -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject -import kotlinx.coroutines.flow.merge -import kotlinx.coroutines.launch -import reactivecircus.flowbinding.android.widget.afterTextChanges - -@AndroidEntryPoint -class DicewarePasswordGeneratorDialogFragment : DialogFragment() { - - @Inject lateinit var dicewareGenerator: DicewarePassphraseGenerator - @Inject @PasswordGeneratorPreferences lateinit var prefs: SharedPreferences - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val builder = MaterialAlertDialogBuilder(requireContext()) - - val binding = FragmentPwgenDicewareBinding.inflate(layoutInflater) - builder.setView(binding.root) - - binding.passwordSeparatorText.setText(prefs.getString(DICEWARE_SEPARATOR) ?: "-") - binding.passwordLengthText.setText(prefs.getInt(DICEWARE_LENGTH, 5).toString()) - binding.passwordText.typeface = Typeface.MONOSPACE - - lifecycleScope.launch { - merge( - binding.passwordLengthText.afterTextChanges(), - binding.passwordSeparatorText.afterTextChanges(), - ) - .collect { _ -> generatePassword(binding) } - } - return builder - .run { - setTitle(R.string.pwgen_title) - setPositiveButton(R.string.dialog_ok) { _, _ -> - setFragmentResult( - PasswordCreationActivity.PASSWORD_RESULT_REQUEST_KEY, - bundleOf(PasswordCreationActivity.RESULT to "${binding.passwordText.text}"), - ) - } - setNeutralButton(R.string.dialog_cancel) { _, _ -> } - setNegativeButton(R.string.pwgen_generate, null) - create() - } - .apply { - setOnShowListener { - generatePassword(binding) - getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener { generatePassword(binding) } - } - } - } - - private fun generatePassword(binding: FragmentPwgenDicewareBinding) { - val length = binding.passwordLengthText.text?.toString()?.toIntOrNull() ?: 5 - val separator = binding.passwordSeparatorText.text?.toString()?.getOrNull(0) ?: '-' - setPreferences(length, separator) - binding.passwordText.text = dicewareGenerator.generatePassphrase(length, separator) - } - - private fun setPreferences(length: Int, separator: Char) { - prefs.edit { - putInt(DICEWARE_LENGTH, length) - putString(DICEWARE_SEPARATOR, separator.toString()) - } - } -} diff --git a/app/src/main/java/app/passwordstore/ui/dialogs/OtpImportDialogFragment.kt b/app/src/main/java/app/passwordstore/ui/dialogs/OtpImportDialogFragment.kt deleted file mode 100644 index 1a75175b..00000000 --- a/app/src/main/java/app/passwordstore/ui/dialogs/OtpImportDialogFragment.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package app.passwordstore.ui.dialogs - -import android.app.Dialog -import android.net.Uri -import android.os.Bundle -import android.view.WindowManager -import androidx.core.os.bundleOf -import androidx.fragment.app.DialogFragment -import androidx.fragment.app.setFragmentResult -import app.passwordstore.databinding.FragmentManualOtpEntryBinding -import app.passwordstore.ui.crypto.PasswordCreationActivity -import com.google.android.material.dialog.MaterialAlertDialogBuilder - -class OtpImportDialogFragment : DialogFragment() { - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val builder = MaterialAlertDialogBuilder(requireContext()) - val binding = FragmentManualOtpEntryBinding.inflate(layoutInflater) - builder.setView(binding.root) - builder.setPositiveButton(android.R.string.ok) { _, _ -> - setFragmentResult( - PasswordCreationActivity.OTP_RESULT_REQUEST_KEY, - bundleOf(PasswordCreationActivity.RESULT to getTOTPUri(binding)), - ) - } - val dialog = builder.create() - dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE) - return dialog - } - - private fun getTOTPUri(binding: FragmentManualOtpEntryBinding): String { - val secret = binding.secret.text.toString() - val account = binding.account.text.toString() - if (secret.isBlank()) return "" - val builder = Uri.Builder() - builder.scheme("otpauth") - builder.authority("totp") - builder.appendQueryParameter("secret", secret) - if (account.isNotBlank()) builder.appendQueryParameter("issuer", account) - return builder.build().toString() - } -} diff --git a/app/src/main/java/app/passwordstore/ui/dialogs/PasswordGeneratorDialogFragment.kt b/app/src/main/java/app/passwordstore/ui/dialogs/PasswordGeneratorDialogFragment.kt deleted file mode 100644 index d4a2372b..00000000 --- a/app/src/main/java/app/passwordstore/ui/dialogs/PasswordGeneratorDialogFragment.kt +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ -package app.passwordstore.ui.dialogs - -import android.app.AlertDialog -import android.app.Dialog -import android.content.Context -import android.graphics.Typeface -import android.os.Bundle -import android.widget.CheckBox -import android.widget.EditText -import android.widget.Toast -import androidx.annotation.IdRes -import androidx.appcompat.widget.AppCompatTextView -import androidx.core.content.edit -import androidx.core.os.bundleOf -import androidx.fragment.app.DialogFragment -import androidx.fragment.app.setFragmentResult -import androidx.lifecycle.lifecycleScope -import app.passwordstore.R -import app.passwordstore.databinding.FragmentPwgenBinding -import app.passwordstore.passgen.random.MaxIterationsExceededException -import app.passwordstore.passgen.random.NoCharactersIncludedException -import app.passwordstore.passgen.random.PasswordGenerator -import app.passwordstore.passgen.random.PasswordLengthTooShortException -import app.passwordstore.passgen.random.PasswordOption -import app.passwordstore.ui.crypto.PasswordCreationActivity -import app.passwordstore.util.settings.PreferenceKeys -import com.github.michaelbull.result.getOrElse -import com.github.michaelbull.result.runCatching -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import kotlinx.coroutines.flow.merge -import kotlinx.coroutines.launch -import reactivecircus.flowbinding.android.widget.afterTextChanges -import reactivecircus.flowbinding.android.widget.checkedChanges - -class PasswordGeneratorDialogFragment : DialogFragment() { - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val prefs = requireContext().getSharedPreferences("PasswordGenerator", Context.MODE_PRIVATE) - val builder = MaterialAlertDialogBuilder(requireContext()) - - val binding = FragmentPwgenBinding.inflate(layoutInflater) - builder.setView(binding.root) - - binding.numerals.isChecked = !prefs.getBoolean(PasswordOption.NoDigits.key, false) - binding.symbols.isChecked = prefs.getBoolean(PasswordOption.AtLeastOneSymbol.key, false) - binding.uppercase.isChecked = !prefs.getBoolean(PasswordOption.NoUppercaseLetters.key, false) - binding.lowercase.isChecked = !prefs.getBoolean(PasswordOption.NoLowercaseLetters.key, false) - binding.ambiguous.isChecked = !prefs.getBoolean(PasswordOption.NoAmbiguousCharacters.key, false) - binding.pronounceable.isChecked = !prefs.getBoolean(PasswordOption.FullyRandom.key, true) - binding.lengthNumber.setText(prefs.getInt(PreferenceKeys.LENGTH, 20).toString()) - binding.passwordText.typeface = Typeface.MONOSPACE - - lifecycleScope.launch { - merge( - binding.numerals.checkedChanges().skipInitialValue(), - binding.symbols.checkedChanges().skipInitialValue(), - binding.uppercase.checkedChanges().skipInitialValue(), - binding.lowercase.checkedChanges().skipInitialValue(), - binding.ambiguous.checkedChanges().skipInitialValue(), - binding.pronounceable.checkedChanges().skipInitialValue(), - binding.lengthNumber.afterTextChanges().skipInitialValue(), - ) - .collect { generate(binding.passwordText) } - } - - return builder - .run { - setTitle(R.string.pwgen_title) - setPositiveButton(R.string.dialog_ok) { _, _ -> - setFragmentResult( - PasswordCreationActivity.PASSWORD_RESULT_REQUEST_KEY, - bundleOf(PasswordCreationActivity.RESULT to "${binding.passwordText.text}"), - ) - } - setNeutralButton(R.string.dialog_cancel) { _, _ -> } - setNegativeButton(R.string.pwgen_generate, null) - create() - } - .apply { - setOnShowListener { - generate(binding.passwordText) - getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener { - generate(binding.passwordText) - } - } - } - } - - private fun generate(passwordField: AppCompatTextView) { - val passwordOptions = getSelectedOptions() - 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() - "" - } - } - - private fun isChecked(@IdRes id: Int): Boolean { - return requireDialog().findViewById<CheckBox>(id).isChecked - } - - private fun getSelectedOptions(): List<PasswordOption> { - return listOfNotNull( - PasswordOption.NoDigits.takeIf { !isChecked(R.id.numerals) }, - PasswordOption.AtLeastOneSymbol.takeIf { isChecked(R.id.symbols) }, - PasswordOption.NoUppercaseLetters.takeIf { !isChecked(R.id.uppercase) }, - PasswordOption.NoAmbiguousCharacters.takeIf { !isChecked(R.id.ambiguous) }, - PasswordOption.FullyRandom.takeIf { !isChecked(R.id.pronounceable) }, - PasswordOption.NoLowercaseLetters.takeIf { !isChecked(R.id.lowercase) }, - ) - } - - private fun getLength(): Int { - val lengthText = requireDialog().findViewById<EditText>(R.id.lengthNumber).text.toString() - return lengthText.toIntOrNull()?.takeIf { it >= 0 } ?: PasswordGenerator.DEFAULT_LENGTH - } - - /** - * Enables the [PasswordOption]s in [options] and sets [targetLength] as the length for generated - * passwords. - */ - private fun setPrefs(ctx: Context, options: List<PasswordOption>, targetLength: Int): Boolean { - ctx.getSharedPreferences("PasswordGenerator", Context.MODE_PRIVATE).edit { - for (possibleOption in PasswordOption.entries) { - putBoolean(possibleOption.key, possibleOption in options) - } - putInt("length", targetLength) - } - return true - } -} diff --git a/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderActivity.kt b/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderActivity.kt deleted file mode 100644 index 2139d7d2..00000000 --- a/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderActivity.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ -package app.passwordstore.ui.folderselect - -import android.os.Bundle -import android.view.Menu -import android.view.MenuItem -import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.FragmentManager -import androidx.fragment.app.commit -import app.passwordstore.R -import dagger.hilt.android.AndroidEntryPoint - -@AndroidEntryPoint -class SelectFolderActivity : AppCompatActivity(R.layout.select_folder_layout) { - - private lateinit var passwordList: SelectFolderFragment - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - passwordList = SelectFolderFragment() - supportActionBar?.show() - supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) - supportFragmentManager.commit { replace(R.id.pgp_handler_linearlayout, passwordList) } - } - - override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.pgp_handler_select_folder, menu) - return true - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - android.R.id.home -> { - setResult(RESULT_CANCELED) - onBackPressedDispatcher.onBackPressed() - } - R.id.crypto_select -> selectFolder() - else -> return super.onOptionsItemSelected(item) - } - return true - } - - private fun selectFolder() { - intent.putExtra("SELECTED_FOLDER_PATH", passwordList.currentDir.absolutePath) - setResult(RESULT_OK, intent) - finish() - } -} diff --git a/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderFragment.kt b/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderFragment.kt deleted file mode 100644 index acf4b56d..00000000 --- a/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderFragment.kt +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ -package app.passwordstore.ui.folderselect - -import android.content.Context -import android.os.Bundle -import android.view.View -import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.flowWithLifecycle -import androidx.lifecycle.lifecycleScope -import androidx.recyclerview.widget.LinearLayoutManager -import app.passwordstore.R -import app.passwordstore.data.password.PasswordItem -import app.passwordstore.data.repo.PasswordRepository -import app.passwordstore.databinding.PasswordRecyclerViewBinding -import app.passwordstore.ui.adapters.PasswordItemRecyclerAdapter -import app.passwordstore.util.coroutines.DispatcherProvider -import app.passwordstore.util.extensions.viewBinding -import app.passwordstore.util.viewmodel.ListMode -import app.passwordstore.util.viewmodel.SearchableRepositoryViewModel -import com.github.michaelbull.result.onFailure -import com.github.michaelbull.result.runCatching -import dagger.hilt.android.AndroidEntryPoint -import java.io.File -import javax.inject.Inject -import kotlinx.coroutines.launch -import me.zhanghai.android.fastscroll.FastScrollerBuilder - -@AndroidEntryPoint -class SelectFolderFragment : Fragment(R.layout.password_recycler_view) { - - @Inject lateinit var dispatcherProvider: DispatcherProvider - private val binding by viewBinding(PasswordRecyclerViewBinding::bind) - private lateinit var recyclerAdapter: PasswordItemRecyclerAdapter - private lateinit var listener: OnFragmentInteractionListener - - private val model: SearchableRepositoryViewModel by activityViewModels() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - binding.fab.hide() - recyclerAdapter = - PasswordItemRecyclerAdapter(lifecycleScope, dispatcherProvider).onItemClicked { _, item -> - listener.onFragmentInteraction(item) - } - binding.passRecycler.apply { - layoutManager = LinearLayoutManager(requireContext()) - itemAnimator = null - adapter = recyclerAdapter - } - - FastScrollerBuilder(binding.passRecycler).build() - registerForContextMenu(binding.passRecycler) - - model.navigateTo( - PasswordRepository.getRepositoryDirectory(), - listMode = ListMode.DirectoriesOnly, - pushPreviousLocation = false, - ) - lifecycleScope.launch { - model.searchResult.flowWithLifecycle(lifecycle).collect { result -> - recyclerAdapter.submitList(result.passwordItems) - } - } - } - - 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) - } - } - } - } - .onFailure { - throw ClassCastException("$context must implement OnFragmentInteractionListener") - } - } - - val currentDir: File - get() = model.currentDir.value - - interface OnFragmentInteractionListener { - - fun onFragmentInteraction(item: PasswordItem) - } -} diff --git a/app/src/main/java/app/passwordstore/util/extensions/AndroidExtensions.kt b/app/src/main/java/app/passwordstore/util/extensions/AndroidExtensions.kt index a13484c6..bad55de4 100644 --- a/app/src/main/java/app/passwordstore/util/extensions/AndroidExtensions.kt +++ b/app/src/main/java/app/passwordstore/util/extensions/AndroidExtensions.kt @@ -95,18 +95,13 @@ fun FragmentActivity.isPermissionGranted(permission: String): Boolean { return ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED } -/** - * Show a [Snackbar] in a [FragmentActivity] and correctly anchor it to a - * [com.google.android.material.floatingactionbutton.FloatingActionButton] if one exists in the - * [view] - */ +/** Show a [Snackbar] in a [FragmentActivity]. */ fun FragmentActivity.snackbar( view: View = findViewById(android.R.id.content), message: String, length: Int = Snackbar.LENGTH_SHORT, ): Snackbar { val snackbar = Snackbar.make(view, message, length) - snackbar.anchorView = findViewById(R.id.fab) snackbar.show() return snackbar } diff --git a/app/src/main/res/drawable/ic_done_24dp.xml b/app/src/main/res/drawable/ic_done_24dp.xml deleted file mode 100644 index f8b6cef6..00000000 --- a/app/src/main/res/drawable/ic_done_24dp.xml +++ /dev/null @@ -1,15 +0,0 @@ -<!-- - ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - ~ SPDX-License-Identifier: GPL-3.0-only - --> - -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:tint="?attr/colorControlNormal" - android:viewportWidth="24" - android:viewportHeight="24"> - <path - android:fillColor="#FFFFFF" - android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z" /> -</vector> diff --git a/app/src/main/res/drawable/ic_save_24dp.xml b/app/src/main/res/drawable/ic_save_24dp.xml deleted file mode 100644 index 8a4276c4..00000000 --- a/app/src/main/res/drawable/ic_save_24dp.xml +++ /dev/null @@ -1,15 +0,0 @@ -<!-- - ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - ~ SPDX-License-Identifier: GPL-3.0-only - --> - -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:tint="?attr/colorControlNormal" - android:viewportWidth="24" - android:viewportHeight="24"> - <path - android:fillColor="#FFFFFF" - android:pathData="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM19,19L5,19L5,5h11.17L19,7.83L19,19zM12,12c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3zM6,6h9v4L6,10z" /> -</vector> diff --git a/app/src/main/res/drawable/ic_save_copy_24dp.xml b/app/src/main/res/drawable/ic_save_copy_24dp.xml deleted file mode 100644 index 06419aac..00000000 --- a/app/src/main/res/drawable/ic_save_copy_24dp.xml +++ /dev/null @@ -1,15 +0,0 @@ -<!-- - ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - ~ SPDX-License-Identifier: GPL-3.0-only - --> - -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:tint="?attr/colorControlNormal" - android:viewportWidth="24" - android:viewportHeight="24"> - <path - android:fillColor="#FFFFFF" - android:pathData="M19,5L7,5A2,2 0,0 0,5 7L5,21a2,2 0,0 0,2 2L21,23a2,2 0,0 0,2 -2L23,9ZM21,21L7,21L7,7L18.17,7L21,9.83ZM14,14a3,3 0,1 0,3 3A3,3 0,0 0,14 14ZM8,8h9v4L8,12ZM15,1L3,1A2,2 0,0 0,1 3L1,17L3,17L3,3L15,3Z" /> -</vector> diff --git a/app/src/main/res/layout/fragment_manual_otp_entry.xml b/app/src/main/res/layout/fragment_manual_otp_entry.xml deleted file mode 100644 index 7d7ea832..00000000 --- a/app/src/main/res/layout/fragment_manual_otp_entry.xml +++ /dev/null @@ -1,53 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?><!-- - ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - ~ SPDX-License-Identifier: GPL-3.0-only - --> - -<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/secret_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:hint="@string/otp_import_manual_hint_secret" - android:paddingStart="16dp" - android:paddingTop="16dp" - android:paddingEnd="16dp" - app:hintEnabled="true" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/secret" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:inputType="textPassword" /> - - <requestFocus /> - - </com.google.android.material.textfield.TextInputLayout> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/account_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:hint="@string/otp_import_manual_hint_account" - android:paddingStart="16dp" - android:paddingTop="16dp" - android:paddingEnd="16dp" - android:paddingBottom="16dp" - app:hintEnabled="true" - app:layout_constraintTop_toBottomOf="@id/secret_layout"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/account" - android:layout_width="match_parent" - android:layout_height="match_parent" /> - - </com.google.android.material.textfield.TextInputLayout> - -</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/fragment_pwgen.xml b/app/src/main/res/layout/fragment_pwgen.xml deleted file mode 100644 index 02eadff5..00000000 --- a/app/src/main/res/layout/fragment_pwgen.xml +++ /dev/null @@ -1,115 +0,0 @@ -<!-- - ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - ~ SPDX-License-Identifier: GPL-3.0-only - --> - -<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:paddingLeft="24dp" - android:paddingTop="20dp" - android:paddingRight="24dp" - android:paddingBottom="20dp" - tools:context=".MainActivityFragment"> - - <androidx.appcompat.widget.AppCompatTextView - android:id="@+id/passwordText" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginBottom="8dp" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textIsSelectable="true" /> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="match_parent" - android:baselineAligned="false" - android:orientation="horizontal" - android:weightSum="2"> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_weight="1" - android:orientation="vertical"> - - <androidx.appcompat.widget.AppCompatTextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginBottom="8dp" - android:text="@string/pwgen_include" - android:textAppearance="?android:attr/textAppearanceSmall" /> - - <CheckBox - android:id="@+id/numerals" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/pwgen_numerals" /> - - - <CheckBox - android:id="@+id/symbols" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/pwgen_symbols" /> - - <CheckBox - android:id="@+id/uppercase" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/pwgen_uppercase" /> - - <CheckBox - android:id="@+id/lowercase" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/pwgen_lowercase" /> - - </LinearLayout> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_weight="1" - android:orientation="vertical"> - - <androidx.appcompat.widget.AppCompatTextView - android:id="@+id/length" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginBottom="8dp" - android:labelFor="@id/lengthNumber" - android:text="@string/pwgen_length" - android:textAppearance="?android:attr/textAppearanceSmall" /> - - <EditText - android:id="@+id/lengthNumber" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginBottom="8dp" - android:autofillHints="notApplicable" - android:ems="10" - android:inputType="number" /> - - <CheckBox - android:id="@+id/pronounceable" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/pwgen_pronounceable" /> - - <CheckBox - android:id="@+id/ambiguous" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/pwgen_ambiguous" /> - - </LinearLayout> - </LinearLayout> - </LinearLayout> -</ScrollView> diff --git a/app/src/main/res/layout/fragment_pwgen_diceware.xml b/app/src/main/res/layout/fragment_pwgen_diceware.xml deleted file mode 100644 index b8627670..00000000 --- a/app/src/main/res/layout/fragment_pwgen_diceware.xml +++ /dev/null @@ -1,73 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?><!-- - ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - ~ SPDX-License-Identifier: GPL-3.0-only - --> - -<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:paddingLeft="24dp" - android:paddingTop="20dp" - android:paddingRight="24dp" - android:paddingBottom="20dp"> - - <androidx.appcompat.widget.AppCompatTextView - android:id="@+id/password_text" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceMedium" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/password_length" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:layout_marginEnd="4dp" - android:hint="@string/pwgen_length" - android:labelFor="@id/password_length_text" - app:hintAnimationEnabled="true" - app:hintEnabled="true" - app:layout_constraintEnd_toStartOf="@id/password_separator" - app:layout_constraintHorizontal_weight="0.5" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/password_text"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/password_length_text" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:inputType="number" - android:maxLength="2" /> - </com.google.android.material.textfield.TextInputLayout> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/password_separator" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginStart="4dp" - android:layout_marginTop="8dp" - android:hint="@string/pwgen_separator" - android:labelFor="@id/password_separator_text" - app:hintAnimationEnabled="true" - app:hintEnabled="true" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_weight="0.5" - app:layout_constraintStart_toEndOf="@id/password_length" - app:layout_constraintTop_toBottomOf="@id/password_text"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/password_separator_text" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:autofillHints="" - android:importantForAutofill="no" - android:inputType="text" - android:maxLength="1" /> - </com.google.android.material.textfield.TextInputLayout> - -</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/password_creation_activity.xml b/app/src/main/res/layout/password_creation_activity.xml deleted file mode 100644 index c0692753..00000000 --- a/app/src/main/res/layout/password_creation_activity.xml +++ /dev/null @@ -1,147 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?><!-- - ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - ~ SPDX-License-Identifier: GPL-3.0-only - --> - - -<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:fillViewport="false" - tools:context="app.passwordstore.ui.crypto.PasswordCreationActivity"> - - <androidx.constraintlayout.widget.ConstraintLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:padding="@dimen/activity_horizontal_margin"> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/directory_input_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="center_vertical" - android:layout_margin="8dp" - android:enabled="false" - android:hint="@string/directory_hint" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/directory" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:imeOptions="actionNext" - android:inputType="textNoSuggestions" - android:nextFocusForward="@id/password" - tools:text="CATEGORY HERE" /> - </com.google.android.material.textfield.TextInputLayout> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/name_input_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="center_vertical" - android:layout_margin="8dp" - android:hint="@string/crypto_name_hint" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/directory_input_layout"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/filename" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:imeOptions="actionNext" - android:inputType="textNoSuggestions" - android:nextFocusForward="@id/username" /> - </com.google.android.material.textfield.TextInputLayout> - - <com.google.android.material.textfield.TextInputLayout - android:visibility="gone" - tools:visibility="visible" - android:id="@+id/username_input_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_margin="8dp" - android:hint="@string/crypto_username_hint" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/name_input_layout"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/username" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:imeOptions="actionNext" - android:inputType="textNoSuggestions" - android:nextFocusForward="@id/password" /> - </com.google.android.material.textfield.TextInputLayout> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/password_input_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_margin="8dp" - android:hint="@string/crypto_pass_label" - app:endIconMode="password_toggle" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/username_input_layout"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/password" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:imeOptions="actionDone" - android:inputType="textVisiblePassword" /> - </com.google.android.material.textfield.TextInputLayout> - - <com.google.android.material.button.MaterialButton - android:id="@+id/generate_password" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_margin="8dp" - android:text="@string/pwd_generate_button" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@id/password_input_layout" /> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/extra_input_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_margin="8dp" - android:hint="@string/crypto_extra_label" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/generate_password"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/extra_content" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:inputType="textMultiLine|textVisiblePassword" /> - - </com.google.android.material.textfield.TextInputLayout> - - <com.google.android.material.button.MaterialButton - android:id="@+id/otp_import_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_margin="8dp" - android:text="@string/add_otp" - app:icon="@drawable/ic_qr_code_scanner" - app:iconTint="?attr/colorOnSecondary" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@id/extra_input_layout" /> - - <com.google.android.material.switchmaterial.SwitchMaterial - android:id="@+id/encrypt_username" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_margin="8dp" - android:text="@string/crypto_encrypt_username_label" - android:visibility="gone" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/otp_import_button" - tools:visibility="visible" /> - </androidx.constraintlayout.widget.ConstraintLayout> -</ScrollView> diff --git a/app/src/main/res/layout/password_recycler_view.xml b/app/src/main/res/layout/password_recycler_view.xml deleted file mode 100644 index c2b5114f..00000000 --- a/app/src/main/res/layout/password_recycler_view.xml +++ /dev/null @@ -1,38 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?><!-- - ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - ~ SPDX-License-Identifier: GPL-3.0-only - --> - -<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:clipChildren="false" - android:clipToPadding="false" - android:orientation="vertical"> - - <androidx.swiperefreshlayout.widget.SwipeRefreshLayout - android:id="@+id/swipe_refresher" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <me.zhanghai.android.fastscroll.FixOnItemTouchListenerRecyclerView - android:id="@+id/pass_recycler" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:scrollbars="none" - tools:itemCount="20" - tools:listitem="@layout/password_row_layout" /> - </androidx.swiperefreshlayout.widget.SwipeRefreshLayout> - - <com.google.android.material.floatingactionbutton.FloatingActionButton - android:id="@+id/fab" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentEnd="true" - android:layout_alignParentBottom="true" - android:layout_gravity="bottom|end" - android:layout_margin="@dimen/fab_compat_margin" - android:contentDescription="@string/password_list_fab_content_description" - android:src="@drawable/ic_add_48dp" /> -</androidx.coordinatorlayout.widget.CoordinatorLayout> diff --git a/app/src/main/res/layout/password_row_layout.xml b/app/src/main/res/layout/password_row_layout.xml deleted file mode 100644 index 5c042839..00000000 --- a/app/src/main/res/layout/password_row_layout.xml +++ /dev/null @@ -1,50 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?><!-- - ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - ~ SPDX-License-Identifier: GPL-3.0-only - --> - -<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="@drawable/password_row_background" - android:paddingTop="12dp" - android:paddingBottom="12dp"> - - <androidx.appcompat.widget.AppCompatTextView - android:id="@+id/label" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_gravity="bottom" - android:layout_marginStart="@dimen/activity_horizontal_margin" - android:textSize="18sp" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@id/child_count" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" - tools:text="FILE_NAME" /> - - <androidx.appcompat.widget.AppCompatTextView - android:id="@+id/child_count" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="8dp" - android:textSize="18sp" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@id/folder_indicator" - app:layout_constraintTop_toTopOf="parent" - tools:text="12" /> - - <androidx.appcompat.widget.AppCompatImageView - android:id="@+id/folder_indicator" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="@dimen/activity_horizontal_margin" - android:contentDescription="@null" - android:src="@drawable/ic_keyboard_arrow_right_24dp" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" /> - -</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/select_folder_layout.xml b/app/src/main/res/layout/select_folder_layout.xml deleted file mode 100644 index 78b03579..00000000 --- a/app/src/main/res/layout/select_folder_layout.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?><!-- - ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - ~ SPDX-License-Identifier: GPL-3.0-only - --> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical"> - - <LinearLayout - android:id="@+id/pgp_handler_linearlayout" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" /> -</LinearLayout> diff --git a/app/src/main/res/menu/pgp_handler_new_password.xml b/app/src/main/res/menu/pgp_handler_new_password.xml deleted file mode 100644 index 6c61fb55..00000000 --- a/app/src/main/res/menu/pgp_handler_new_password.xml +++ /dev/null @@ -1,20 +0,0 @@ -<!-- - ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - ~ SPDX-License-Identifier: GPL-3.0-only - --> - -<menu xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:pwstore="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" - tools:context=".ui.crypto.PgpActivity"> - <item - android:id="@+id/save_password" - android:icon="@drawable/ic_save_24dp" - android:title="@string/crypto_save" - pwstore:showAsAction="ifRoom" /> - <item - android:id="@+id/save_and_copy_password" - android:icon="@drawable/ic_save_copy_24dp" - android:title="@string/crypto_save_and_copy" - pwstore:showAsAction="ifRoom" /> -</menu> diff --git a/app/src/main/res/menu/pgp_handler_select_folder.xml b/app/src/main/res/menu/pgp_handler_select_folder.xml deleted file mode 100644 index d9505814..00000000 --- a/app/src/main/res/menu/pgp_handler_select_folder.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?><!-- - ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - ~ SPDX-License-Identifier: GPL-3.0-only - --> - -<menu xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:pwstore="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" - tools:context="app.passwordstore.SelectFolderActivity"> - <item - android:id="@+id/crypto_select" - android:icon="@drawable/ic_done_24dp" - android:title="@string/crypto_select" - pwstore:showAsAction="ifRoom" /> -</menu> diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index d13ab19f..a9fa4b00 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -39,7 +39,6 @@ <!-- PGPHandler --> <string name="clipboard_copied_text">In die Zwischenablage kopiert</string> <string name="file_toast_text">Bitte setzen Sie einen Dateinamen.</string> - <string name="path_toast_text">Bitte setzen Sie einen Pfad.</string> <string name="empty_toast_text">Sie können kein leeres Passwort oder leere Extra-Angaben setzen.</string> <!-- Git Async Task --> <string name="jgit_error_dialog_title">Es ist ein Fehler während der Git-Operation aufgetreten.</string> @@ -63,7 +62,6 @@ <string name="crypto_name_hint">Name</string> <string name="crypto_pass_label">Passwort</string> <string name="crypto_extra_label">Zusätzliche Angaben</string> - <string name="crypto_encrypt_username_label">Benutzername verschlüsseln</string> <string name="crypto_select">Auswählen</string> <string name="crypto_save">Speichern</string> <string name="crypto_save_and_copy">Speichern und kopieren</string> @@ -125,7 +123,6 @@ <!-- PasswordGenerator fragment --> <string name="pwgen_title">Passwort erstellen</string> <string name="pwgen_generate">Erstellen</string> - <string name="pwgen_include">Einschließen</string> <string name="pwgen_length">Länge</string> <string name="pwgen_numerals">Nummern</string> <string name="pwgen_symbols">Symbole</string> @@ -173,7 +170,6 @@ <string name="git_log">Commit-Log anzeigen</string> <string name="show_password_pref_title">Zeige das Passwort.</string> <string name="show_password_pref_summary">Soll das entschlüsselte Passwort sichtbar sein? Dies deaktiviert nicht das Kopieren.</string> - <string name="pwd_generate_button">Erstellen</string> <string name="send_plaintext_password_to">Passwort unverschlüsselt senden an…</string> <string name="app_icon_hint">App-Icon</string> <!-- Oreo Autofill --> @@ -207,7 +203,6 @@ <string name="oreo_autofill_enable_dialog_installed_browsers">Autofill-Unterstützung mit installierten Browsern:</string> <!-- Autofill --> <string name="ssh_key_does_not_exist">Der private SSH-Schlüssel konnte nicht geöffnet werden. Bitte überprüfen Sie, ob die Datei existiert.</string> - <string name="new_password_title">Neues Passwort</string> <string name="clear_saved_passphrase_ssh">Gespeicherte Passphrase für lokalen SSH-Schlüssel löschen</string> <string name="clear_saved_passphrase_https">Gespeichertes HTTPS-Passwort löschen</string> <string name="git_operation_remember_passphrase">Schlüsselpasswort merken</string> @@ -276,7 +271,6 @@ <string name="invalid_gpg_id">.gpg-id wurde gefunden, enthält jedoch ungültige Schlüssel-ID, Fingerabdruck oder Benutzer-ID.</string> <string name="short_gpg_id">.gpg-id wurde gefunden, enthält jedoch eine kurze Hex-ID, die nicht unterstützt wird.</string> <string name="invalid_filename_text">Dateiname darf kein \'/\' enthalten; Verzeichnis oben setzen</string> - <string name="directory_hint">Ordner</string> <!-- GitException messages --> <string name="git_unknown_error">Unbekannter Fehler</string> <string name="git_pull_rebase_fail_error">Pull fehlgeschlagen. Sie befinden sich in einem abgelösten Head. Speichern Sie die Änderungen in \"Einstellungen > Lokale Git-Konfiguration & Dienstprogramme\" auf das Remote-Repository in einem neuen Branch und lösen Sie den Konflikt auf Ihrem Computer auf.</string> @@ -319,11 +313,9 @@ <string name="clear_saved_host_key_success">Host-Schlüssel erfolgreich gelöscht!</string> <string name="otp_import_qr_code">QR-Code scannen</string> <string name="otp_import_from_file">Wählen Sie ein Bild.</string> - <string name="otp_import_manual_entry">Manuell eingeben</string> <string name="otp_import_manual_hint_secret">Schlüssel</string> <string name="otp_import_manual_hint_account">Benutzer-Konto</string> <string name="place_shortcut_on_home_screen">Verknüpfung auf dem Home-Screen erstellen</string> - <string name="password_list_fab_content_description">Neues Passwort oder neuen Ordner erstellen</string> <string name="pgp_key_import_failed">Fehler beim Import des PGP-Schlüssels</string> <string name="pgp_key_import_failed_replace_message">Ein bestehender Schlüssel mit dieser ID wurde gefunden. Möchten Sie ihn ersetzen?</string> <string name="pgp_key_import_succeeded">PGP-Schlüssel erfolgreich importiert</string> diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 0cfb345c..d41048f8 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -38,7 +38,6 @@ <!-- PGPHandler --> <string name="clipboard_copied_text">Copié dans le presse-papiers</string> <string name="file_toast_text">Veuillez fournir un nom de fichier</string> - <string name="path_toast_text">Veuillez fournir un chemin d\'accès au fichier</string> <string name="empty_toast_text">Vous ne pouvez pas utiliser un mot de passe vide ou des données supplémentaires vide</string> <!-- Git Async Task --> <string name="jgit_error_dialog_title">Une erreur c\'est produite lors d\'une opération Git</string> @@ -62,7 +61,6 @@ <string name="crypto_name_hint">Nom</string> <string name="crypto_pass_label">Mot de passe</string> <string name="crypto_extra_label">Extra</string> - <string name="crypto_encrypt_username_label">Chiffrer le nom d\'utilisateur</string> <string name="crypto_select">Sélectionner</string> <string name="crypto_save">Sauvegarder</string> <string name="crypto_save_and_copy">Enregistrer et copier</string> @@ -113,7 +111,6 @@ <!-- PasswordGenerator fragment --> <string name="pwgen_title">Générer un mot de passe</string> <string name="pwgen_generate">Générer</string> - <string name="pwgen_include">Inclure</string> <string name="pwgen_length">Longueur</string> <string name="pwgen_numerals">Chiffres</string> <string name="pwgen_symbols">Symboles</string> @@ -161,7 +158,6 @@ <string name="git_log">Afficher l\'historique des commits</string> <string name="show_password_pref_title">Montrer le mot de passe</string> <string name="show_password_pref_summary">Contrôle la visibilité des mots de passe une fois déchifrées, ceci n\'empêche pas de copier le mot de passe</string> - <string name="pwd_generate_button">Générer</string> <string name="send_plaintext_password_to">Envoyer le mot de passe en clair via…</string> <string name="app_icon_hint">Icône de l\'application</string> <!-- Oreo Autofill --> @@ -195,7 +191,6 @@ <string name="oreo_autofill_enable_dialog_installed_browsers">Prise en charge du remplissage automatique avec les navigateurs installés:</string> <!-- Autofill --> <string name="ssh_key_does_not_exist">Impossible d\'ouvrir la clef ssh, merci de vérifier que le ficher existe</string> - <string name="new_password_title">Nouveau mot de passe</string> <string name="clear_saved_passphrase_ssh">Effacer le mot de passe enregistrée pour la clé SSH locale</string> <string name="clear_saved_passphrase_https">Effacer le mot de passe HTTPS enregistré</string> <string name="git_operation_remember_passphrase">Se rappeler de la phrase secrète dans la configuration de l\'application (peu sûr)</string> @@ -261,7 +256,6 @@ <string name="otp_import_failure_generic">Impossible d\'importer la configuration TOTP</string> <string name="exporting_passwords">Exportation des mots de passe…</string> <string name="invalid_filename_text">Le nom du fichier ne doit pas contenir \'/\', définir le répertoire parent</string> - <string name="directory_hint">Dossier</string> <!-- GitException messages --> <string name="git_unknown_error">Erreur inconnue</string> <string name="git_pull_rebase_fail_error">Tirer a échoué, vous êtes dans un \'head\' détaché. En utilisant \"settings > git utils\", enregistrez vos modifications sur le dépôt distant dans une nouvelle branche et résolvez le conflit sur votre ordinateur.</string> @@ -300,10 +294,8 @@ <string name="clear_saved_host_key_success">La clé d\'hôte a été effacée avec succès!</string> <string name="otp_import_qr_code">Scanner le code QR</string> <string name="otp_import_from_file">Choisissez une image</string> - <string name="otp_import_manual_entry">Saisir manuellement</string> <string name="otp_import_manual_hint_secret">Secret</string> <string name="otp_import_manual_hint_account">Compte</string> - <string name="password_list_fab_content_description">Créer un nouveau mot de passe ou dossier</string> <string name="pgp_key_import_failed">Impossible d\'importer la clé PGP</string> <string name="pgp_key_import_succeeded">Clé PGP importée avec succès</string> <string name="pgp_key_import_succeeded_message">La clé d\'identification de la clé importée est donné ci-dessous, veuillez la vérifier pour qu\'elle soit correcte :\n%1$s</string> diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 589208ab..3fb321e7 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -39,7 +39,6 @@ <!-- PGPHandler --> <string name="clipboard_copied_text">Copiado ao portapapeis</string> <string name="file_toast_text">Debes proporcionar un nome de ficheiro</string> - <string name="path_toast_text">Por favor indica a ruta ao ficheiro</string> <string name="empty_toast_text">Non podes deixar baleiro o contrasinal ou o contido extra</string> <!-- Git Async Task --> <string name="jgit_error_dialog_title">Algo fallou na operación de Git</string> @@ -63,7 +62,6 @@ <string name="crypto_name_hint">Nome</string> <string name="crypto_pass_label">Contrasinal</string> <string name="crypto_extra_label">Contido extra</string> - <string name="crypto_encrypt_username_label">Cifrar identificador</string> <string name="crypto_select">Seleccionar</string> <string name="crypto_save">Gardar</string> <string name="crypto_save_and_copy">Gardar e copiar</string> @@ -126,7 +124,6 @@ <!-- PasswordGenerator fragment --> <string name="pwgen_title">Crear contrasinal</string> <string name="pwgen_generate">Crear</string> - <string name="pwgen_include">Incluír</string> <string name="pwgen_length">Lonxitude</string> <string name="pwgen_numerals">Números</string> <string name="pwgen_symbols">Símbolos</string> @@ -174,7 +171,6 @@ <string name="git_log">Mostrar rexistro dos cambios</string> <string name="show_password_pref_title">Mostrar contrasinal</string> <string name="show_password_pref_summary">Controlar a visibilidade do contrasinal unha vez descifrado, esto non desactiva o copiado do contrasinal</string> - <string name="pwd_generate_button">Crear</string> <string name="send_plaintext_password_to">Enviar contrasinal como texto plano usando…</string> <string name="app_icon_hint">Icona da app</string> <!-- Oreo Autofill --> @@ -209,7 +205,6 @@ a app desde unha fonte de confianza, como a Play Store, Amazon Appstore, F-Droid <string name="oreo_autofill_enable_dialog_installed_browsers">Soporte de autocompletado cos navegadores:</string> <!-- Autofill --> <string name="ssh_key_does_not_exist">Non se pode abrir a chave privada ssh, comproba que existe tal ficheiro</string> - <string name="new_password_title">Novo contrasinal</string> <string name="clear_saved_passphrase_ssh">Baleirar a frase de paso gardada para a chave SSH local</string> <string name="clear_saved_passphrase_https">Baleirar contrasinal HTTPS gardado</string> <string name="git_operation_remember_passphrase">Lembrar frase de paso da chave</string> @@ -278,7 +273,6 @@ a app desde unha fonte de confianza, como a Play Store, Amazon Appstore, F-Droid <string name="invalid_gpg_id">Atopouse .gpg-id, pero contén un ID de chave, impresión dixital ou ID de usuaria non válidos</string> <string name="short_gpg_id">Atopouse .gpg-id, pero contén un ID hex curto, ao que non damos soporte</string> <string name="invalid_filename_text">O nome do ficheiro non pode conter \'/\', establece un directorio superior</string> - <string name="directory_hint">Directorio</string> <!-- GitException messages --> <string name="git_unknown_error">Fallo descoñecido</string> <string name="git_pull_rebase_fail_error">Fallou a acción pull, estás nun head diferente. Utiliza \"axustes > utilidades git\", garda os cambios no remoto nunha nova rama e resolve o conflicto nun ordenador.</string> @@ -321,11 +315,9 @@ a app desde unha fonte de confianza, como a Play Store, Amazon Appstore, F-Droid <string name="clear_saved_host_key_success">Eliminouse correctamente a chave gardada do host!</string> <string name="otp_import_qr_code">Escanear código QR</string> <string name="otp_import_from_file">Elixe unha imaxe</string> - <string name="otp_import_manual_entry">Escribir manualmente</string> <string name="otp_import_manual_hint_secret">Segredo</string> <string name="otp_import_manual_hint_account">Conta</string> <string name="place_shortcut_on_home_screen">Crear atallo na pantalla de inicio</string> - <string name="password_list_fab_content_description">Crear novo contrasinal ou cartafol</string> <string name="pgp_key_import_failed">Fallou a importación da chave PGP</string> <string name="pgp_key_import_failed_replace_message">Atopouse unha chave existente con este ID, queres substituíla?</string> <string name="pgp_key_import_succeeded">Chave PGP importada correctamente</string> diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 91cd28aa..c14efa33 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -38,7 +38,6 @@ <!-- PGPHandler --> <string name="clipboard_copied_text">Copiato negli appunti</string> <string name="file_toast_text">Inserisci un nome per il file</string> - <string name="path_toast_text">Inserisci il percorso del file</string> <string name="empty_toast_text">Non puoi usare una password vuota o dei contenuti extra vuoti</string> <!-- Git Async Task --> <string name="jgit_error_dialog_title">Si è verificato un errore durante un\'operazione Git</string> @@ -62,7 +61,6 @@ <string name="crypto_name_hint">Nome</string> <string name="crypto_pass_label">Password</string> <string name="crypto_extra_label">Contenuto extra</string> - <string name="crypto_encrypt_username_label">Crittografa nome utente</string> <string name="crypto_select">Seleziona</string> <string name="crypto_save">Salva</string> <string name="crypto_save_and_copy">Salva e Copia</string> @@ -108,7 +106,6 @@ <!-- PasswordGenerator fragment --> <string name="pwgen_title">Genera Password</string> <string name="pwgen_generate">Genera</string> - <string name="pwgen_include">Includi</string> <string name="pwgen_length">Lunghezza</string> <string name="pwgen_numerals">Numeri</string> <string name="pwgen_symbols">Simboli</string> @@ -156,7 +153,6 @@ <string name="git_log">Mostra registro dei commit</string> <string name="show_password_pref_title">Mostra la password</string> <string name="show_password_pref_summary">Controlla la visibilità delle password una volta decrittografate. Questo non disabilita la copia negli appunti.</string> - <string name="pwd_generate_button">Genera</string> <string name="send_plaintext_password_to">Invia password come testo semplice usando…</string> <string name="app_icon_hint">Icona app</string> <!-- Oreo Autofill --> @@ -190,7 +186,6 @@ <string name="oreo_autofill_enable_dialog_installed_browsers">Supporto alla compilazione automatica nei browser installati:</string> <!-- Autofill --> <string name="ssh_key_does_not_exist">Impossibile aprire la chiave privata SSH, controlla che il file esista</string> - <string name="new_password_title">Nuova password</string> <string name="clear_saved_passphrase_ssh">Cancella la frase segreta salvata per la chiave SSH locale</string> <string name="clear_saved_passphrase_https">Cancella la password HTTPS salvata</string> <string name="git_operation_remember_passphrase">Ricorda la frase segreta della chiave</string> @@ -256,7 +251,6 @@ <string name="otp_import_failure_generic">Impossibile importare la configurazione di TOTP</string> <string name="exporting_passwords">Esportazione delle password…</string> <string name="invalid_filename_text">Il nome del file non deve contenere \'/\', imposta la directory qui sopra</string> - <string name="directory_hint">Directory</string> <!-- GitException messages --> <string name="git_unknown_error">Errore sconosciuto</string> <string name="git_pull_rebase_fail_error">Pull non riuscito, sei in detached HEAD. Da \"Impostazioni > Utilità Git\" esegui il push delle modifiche sul remote in un nuovo branch e risolvi il conflitto dal computer.</string> @@ -293,7 +287,6 @@ <string name="clear_saved_host_key">Cancella la chiave host salvata</string> <string name="clear_saved_host_key_success">Chiave host cancellata con successo!</string> <string name="otp_import_from_file">Scegli un\'immagine</string> - <string name="password_list_fab_content_description">Crea nuova password o cartella</string> <string name="git_commit_gpg_id">Inizializza gli ID GPG in %1$s.</string> <string name="crypto_username_hint">Nome utente</string> <string name="otp_label_format">OTP (scade tra %ds)</string> @@ -327,7 +320,6 @@ <string name="git_scheme_disallowed_message">Il protocollo git di git-daemon non cifra il trasporto e non è adatto a operazioni sicure.</string> <string name="activity_proxy_label">Impostazioni proxy</string> <string name="otp_import_qr_code">Scansiona codice QR</string> - <string name="otp_import_manual_entry">Inserisci manualmente</string> <string name="otp_import_manual_hint_secret">Segreto</string> <string name="otp_import_manual_hint_account">Account</string> <string name="place_shortcut_on_home_screen">Aggiungi scorciatoia alla schermata Home</string> diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml index 7f63ad8b..3efbed68 100644 --- a/app/src/main/res/values-ko-rKR/strings.xml +++ b/app/src/main/res/values-ko-rKR/strings.xml @@ -37,7 +37,6 @@ <!-- PGPHandler --> <string name="clipboard_copied_text">클립보드로 복사됨</string> <string name="file_toast_text">파일 이름이 필요합니다</string> - <string name="path_toast_text">파일 경로가 필요합니다</string> <string name="empty_toast_text">비어있는 비밀번호나 추가 정보를 사용할 수 없습니다</string> <!-- Git Async Task --> <string name="jgit_error_dialog_title">Git 동작 중 문제가 발생하였습니다</string> @@ -61,7 +60,6 @@ <string name="crypto_name_hint">이름</string> <string name="crypto_pass_label">비밀번호</string> <string name="crypto_extra_label">추가 내용</string> - <string name="crypto_encrypt_username_label">암호화 계정 이름</string> <string name="crypto_select">선택</string> <string name="crypto_save">저장</string> <string name="crypto_save_and_copy">저장 및 복사</string> @@ -121,7 +119,6 @@ <!-- PasswordGenerator fragment --> <string name="pwgen_title">암호 생성</string> <string name="pwgen_generate">생성</string> - <string name="pwgen_include">포함</string> <string name="pwgen_length">길이</string> <string name="pwgen_numerals">숫자만</string> <string name="pwgen_symbols">특수문자</string> @@ -169,7 +166,6 @@ <string name="git_log">커밋 로그 보기</string> <string name="show_password_pref_title">비밀번호 표시하기</string> <string name="show_password_pref_summary">암호가 해독된 후 비밀번호의 공개 여부를 제어합니다. 그렇다고 해서 클립보드 복사가 비활성화되지는 않습니다.</string> - <string name="pwd_generate_button">생성</string> <string name="send_plaintext_password_to">비밀번호를 일반 텍스트로 보내는 중…</string> <string name="app_icon_hint">앱 아이콘</string> <!-- Oreo Autofill --> @@ -203,7 +199,6 @@ <string name="oreo_autofill_enable_dialog_installed_browsers">자동 완성 지원 하는 설치된 브라우저:</string> <!-- Autofill --> <string name="ssh_key_does_not_exist">SSH 개인 키를 열 수 없습니다. 파일이 존재하는지 확인하세요</string> - <string name="new_password_title">새 암호</string> <string name="clear_saved_passphrase_ssh">로컬 SSH 키의 저장된 비밀번호 지우기</string> <string name="clear_saved_passphrase_https">저장된 HTTPS 비밀번호 지우기</string> <string name="git_operation_remember_passphrase">키 암호 기억하기</string> @@ -274,7 +269,6 @@ username@example.com:…</string> <string name="invalid_gpg_id">.gpg-id를 찾았지만 잘못된 키 ID, 지문 또는 사용자 ID가 포함되어 있습니다</string> <string name="short_gpg_id">.gpg-id를 찾았지만, 지원되지 않는 짧은 16진수 ID가 포함되어 있습니다.</string> <string name="invalid_filename_text">파일 이름에 \'/\'가 포함되어서는 안됩니다. 위의 디렉터리를 설정하세요</string> - <string name="directory_hint">디렉토리</string> <!-- GitException messages --> <string name="git_unknown_error">알 수 없는 오류</string> <string name="git_pull_rebase_fail_error">풀이 실패하여 head가 분리된 상태입니다. \"설정 > git 기능\"을 사용하여 원격 저장소에 변경 내용을 새 브랜치에 저장하고 컴퓨터에서 충돌을 해결하세요.</string> @@ -317,11 +311,9 @@ username@example.com:…</string> <string name="clear_saved_host_key_success">저장된 호스트 키를 성공적으로 지웠습니다!</string> <string name="otp_import_qr_code">QR코드 스캔</string> <string name="otp_import_from_file">이미지 선택</string> - <string name="otp_import_manual_entry">직접 입력</string> <string name="otp_import_manual_hint_secret">비밀</string> <string name="otp_import_manual_hint_account">계정</string> <string name="place_shortcut_on_home_screen">홈 화면에 추가</string> - <string name="password_list_fab_content_description">새 비밀번호 또는 폴더 만들기</string> <string name="pgp_key_import_failed">PGP 키를 가져오지 못했습니다</string> <string name="pgp_key_import_failed_replace_message">이 ID를 가진 기존 키가 발견되었습니다. 교체하시겠습니까?</string> <string name="pgp_key_import_succeeded">PGP 키를 성공적으로 가져왔습니다</string> diff --git a/app/src/main/res/values-pl-rPL/strings.xml b/app/src/main/res/values-pl-rPL/strings.xml index a2068e65..65f14ce5 100644 --- a/app/src/main/res/values-pl-rPL/strings.xml +++ b/app/src/main/res/values-pl-rPL/strings.xml @@ -37,7 +37,6 @@ <!-- PGPHandler --> <string name="clipboard_copied_text">Skopiowano do schowka</string> <string name="file_toast_text">Podaj nazwę pliku</string> - <string name="path_toast_text">Podaj ścieżkę do pliku</string> <string name="empty_toast_text">Nie możesz użyć pustego hasła lub pustej dodatkowej zawartości</string> <!-- Git Async Task --> <string name="jgit_error_dialog_title">Wystąpił błąd podczas operacji Git</string> @@ -61,7 +60,6 @@ <string name="crypto_name_hint">Nazwa</string> <string name="crypto_pass_label">Hasło</string> <string name="crypto_extra_label">Treść dodatkowa</string> - <string name="crypto_encrypt_username_label">Szyfruj nazwę użytkownika</string> <string name="crypto_select">Wybierz</string> <string name="crypto_save">Zapisz</string> <string name="crypto_save_and_copy">Zapisz i skopiuj</string> @@ -122,7 +120,6 @@ <!-- PasswordGenerator fragment --> <string name="pwgen_title">Generuj hasło</string> <string name="pwgen_generate">Wygeneruj</string> - <string name="pwgen_include">Załącz</string> <string name="pwgen_length">Długość</string> <string name="pwgen_numerals">Cyfry</string> <string name="pwgen_symbols">Symbole</string> @@ -170,7 +167,6 @@ <string name="git_log">Pokaż dziennik commitów</string> <string name="show_password_pref_title">Pokaż hasło</string> <string name="show_password_pref_summary">Kontroluj widoczność haseł po odszyfrowaniu. To nie wyłącza kopiowania do schowka.</string> - <string name="pwd_generate_button">Generuj</string> <string name="send_plaintext_password_to">Wyślij niezaszyfrowane hasło, używając…</string> <string name="app_icon_hint">Ikona aplikacji</string> <!-- Oreo Autofill --> @@ -202,7 +198,6 @@ <string name="oreo_autofill_enable_dialog_installed_browsers">Obsługa autouzupełniania w zainstalowanych przeglądarkach:</string> <!-- Autofill --> <string name="ssh_key_does_not_exist">Nie można otworzyć klucza prywatnego ssh, sprawdź czy plik istnieje</string> - <string name="new_password_title">Nowe hasło</string> <string name="clear_saved_passphrase_ssh">Wyczyść zapisane hasło dla lokalnego klucza SSH</string> <string name="clear_saved_passphrase_https">Wyczyść zapisane hasło HTTPS</string> <string name="git_operation_remember_passphrase">Zapamiętaj hasło klucza</string> @@ -268,7 +263,6 @@ <string name="invalid_gpg_id">Znaleziono .gpg-id, ale zawiera nieprawidłowy identyfikator klucza, odcisk palca lub identyfikator użytkownika</string> <string name="short_gpg_id">Znaleziono .gpg-id, ale zawarty w nim skrócony identyfikator nie jest obsługiwany</string> <string name="invalid_filename_text">Nazwa pliku nie może zawierać \'/\', ustaw katalog powyżej</string> - <string name="directory_hint">Katalog</string> <!-- GitException messages --> <string name="git_unknown_error">Nieznany błąd</string> <string name="git_pull_rebase_fail_error">Zaciąganie zmian nie powiodło się, nie jesteś na żadnej gałęzi (detached head). Używając \"Ustawienia > narzędzia Git\", wyślij lokalne zmiany do zdalnego repozytorium na nowej gałęzi i rozwiąż konflikt na komputerze.</string> @@ -309,11 +303,9 @@ <string name="clear_saved_host_key_success">Pomyślnie wyczyszczono zapisany klucz hosta!</string> <string name="otp_import_qr_code">Skanuj kod QR</string> <string name="otp_import_from_file">Wybierz obraz</string> - <string name="otp_import_manual_entry">Wprowadź ręcznie</string> <string name="otp_import_manual_hint_secret">Sekret</string> <string name="otp_import_manual_hint_account">Konto</string> <string name="place_shortcut_on_home_screen">Utwórz skrót na pulpicie</string> - <string name="password_list_fab_content_description">Utwórz nowe hasło lub folder</string> <string name="pgp_key_import_failed">Nie udało się zaimportować klucza PGP</string> <string name="pgp_key_import_failed_replace_message">Znaleziono istniejący klucz z tym identyfikatorem, czy chcesz go zastąpić?</string> <string name="pgp_key_import_succeeded">Pomyślnie zaimportowano klucz PGP</string> diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 909fd33e..86ebfb24 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -39,7 +39,6 @@ <!-- PGPHandler --> <string name="clipboard_copied_text">Copiado para a área de transferência</string> <string name="file_toast_text">Por favor, informe um nome de arquivo</string> - <string name="path_toast_text">Por favor, forneça o caminho do arquivo</string> <string name="empty_toast_text">Você não pode usar uma senha vazia ou conteúdo extra vazio</string> <!-- Git Async Task --> <string name="jgit_error_dialog_title">Ocorreu um erro durante uma operação do Git</string> @@ -63,7 +62,6 @@ <string name="crypto_name_hint">Nome</string> <string name="crypto_pass_label">Senha</string> <string name="crypto_extra_label">Conteúdo extra</string> - <string name="crypto_encrypt_username_label">Criptografar nome de usuário</string> <string name="crypto_select">Selecionar</string> <string name="crypto_save">Salvar</string> <string name="crypto_save_and_copy">Salvar e Copiar</string> @@ -125,7 +123,6 @@ <!-- PasswordGenerator fragment --> <string name="pwgen_title">Gerar Senha</string> <string name="pwgen_generate">Gerar</string> - <string name="pwgen_include">Incluir</string> <string name="pwgen_length">Tamanho</string> <string name="pwgen_numerals">Numerais</string> <string name="pwgen_symbols">Símbolos</string> @@ -173,7 +170,6 @@ <string name="git_log">Mostrar log de commit</string> <string name="show_password_pref_title">Mostrar a senha</string> <string name="show_password_pref_summary">Controle a visibilidade das senhas quando descriptografadas. Isso não desativa a cópia para a área de transferência.</string> - <string name="pwd_generate_button">Gerar</string> <string name="send_plaintext_password_to">Enviar senha como texto simples usando…</string> <string name="app_icon_hint">Ícone do aplicativo</string> <!-- Oreo Autofill --> @@ -207,7 +203,6 @@ <string name="oreo_autofill_enable_dialog_installed_browsers">Suporte ao preenchimento automático com navegadores instalados:</string> <!-- Autofill --> <string name="ssh_key_does_not_exist">Não foi possível abrir a chave privada ssh, por favor verifique se o arquivo existe</string> - <string name="new_password_title">Nova senha</string> <string name="clear_saved_passphrase_ssh">Limpar a frase secreta salva para chave SSH local</string> <string name="clear_saved_passphrase_https">Limpar senha HTTPS salva</string> <string name="git_operation_remember_passphrase">Lembrar senha da chave</string> @@ -276,7 +271,6 @@ <string name="invalid_gpg_id">Encontrado .gpg-id, mas contém um ID de chave, fingerprint ou ID de usuário inválidos</string> <string name="short_gpg_id">Encontrado .gpg-id, mas contém uma ID hexadecimal curta, que não é suportada</string> <string name="invalid_filename_text">Nome do arquivo não deve conter \'/\', defina o diretório acima</string> - <string name="directory_hint">Diretório</string> <!-- GitException messages --> <string name="git_unknown_error">Erro desconhecido</string> <string name="git_pull_rebase_fail_error">O pull falhou, você está em uma Head avulsa. Usando \"configurações > utils\" do git, salve suas alterações no remoto em uma nova branch e resolva o conflito no seu computador.</string> @@ -319,11 +313,9 @@ <string name="clear_saved_host_key_success">Chave de host limpa com sucesso!</string> <string name="otp_import_qr_code">Escanear QR code</string> <string name="otp_import_from_file">Selecione uma imagem</string> - <string name="otp_import_manual_entry">Inserir manualmente</string> <string name="otp_import_manual_hint_secret">Segredo</string> <string name="otp_import_manual_hint_account">Conta</string> <string name="place_shortcut_on_home_screen">Criar atalho na tela inicial</string> - <string name="password_list_fab_content_description">Criar nova senha ou pasta</string> <string name="pgp_key_import_failed">Falha ao importar a chave PGP</string> <string name="pgp_key_import_failed_replace_message">Uma chave existente com esse ID foi encontrada, você deseja substituí-lo?</string> <string name="pgp_key_import_succeeded">Chave PGP importada com sucesso</string> diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 2ef59d70..8873b34f 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -43,7 +43,6 @@ <!-- PGPHandler --> <string name="clipboard_copied_text">Скопировано в буфер обмена</string> <string name="file_toast_text">Пожалуйста, укажите имя файла</string> - <string name="path_toast_text">Пожалуйста, задайте путь к файлу</string> <string name="empty_toast_text">Вы не можете использовать пустой пароль или пустое поле информации</string> <!-- Git Async Task --> <string name="jgit_error_dialog_title">Произошла ошибка выполнения операции Git</string> @@ -67,7 +66,6 @@ <string name="crypto_name_hint">название</string> <string name="crypto_pass_label">Пароль</string> <string name="crypto_extra_label">Дополнительно</string> - <string name="crypto_encrypt_username_label">Зашифровать имя пользователя</string> <string name="crypto_select">Выбрать</string> <string name="crypto_save">Сохранить</string> <string name="crypto_save_and_copy">Сохранить и скопировать</string> @@ -118,7 +116,6 @@ <!-- PasswordGenerator fragment --> <string name="pwgen_title">Сгенерировать пароль</string> <string name="pwgen_generate">Сгенерировать</string> - <string name="pwgen_include">Включая</string> <string name="pwgen_length">Длина</string> <string name="pwgen_numerals">Цифры</string> <string name="pwgen_symbols">Символы</string> @@ -166,7 +163,6 @@ <string name="git_log">Показать журнал изменений</string> <string name="show_password_pref_title">Показывать пароли</string> <string name="show_password_pref_summary">Видимость расшифрованных паролей, не влияет на копирование в буфер.</string> - <string name="pwd_generate_button">Сгенерировать</string> <string name="send_plaintext_password_to">Поделиться паролем в открытом виде с помощью</string> <string name="app_icon_hint">Иконка приложения</string> <!-- Oreo Autofill --> @@ -200,7 +196,6 @@ <string name="oreo_autofill_enable_dialog_installed_browsers">Поддержка автозаполнения установленными браузерами:</string> <!-- Autofill --> <string name="ssh_key_does_not_exist">Невозможно открыть приватный ключ ssh, пожалуйста проверьте, что файл существует</string> - <string name="new_password_title">Новый пароль</string> <string name="clear_saved_passphrase_ssh">Очистить сохраненную кодовую фразу для локального SSH ключа</string> <string name="clear_saved_passphrase_https">Удалить сохраненный пароль HTTPS</string> <string name="git_operation_remember_passphrase">Запомнить кодовую фразу</string> @@ -268,7 +263,6 @@ <string name="exporting_passwords">Экспорт паролей…</string> <string name="invalid_gpg_id">Найден .gpg-id, но он содержит неверный ID ключа, fingerprint или ID пользователя</string> <string name="invalid_filename_text">Имя файла не должно содержать \'/\', укажите директорию выше</string> - <string name="directory_hint">Директория</string> <!-- GitException messages --> <string name="git_unknown_error">Неизвестная ошибка</string> <string name="git_pull_rebase_fail_error">Не удалось получить изменения, вы находитесь в состоянии \"оторванной головы\". Используйте \"настройки > утилиты git\", сохраните ваши изменения в новую удаленную ветку и разрешите конфликты на своем компьютере.</string> @@ -311,10 +305,8 @@ <string name="clear_saved_host_key_success">Сохраненный ключ хоста был успешно удален!</string> <string name="otp_import_qr_code">Отсканировать QR-код</string> <string name="otp_import_from_file">Выберите изображение</string> - <string name="otp_import_manual_entry">Ввести вручную</string> <string name="otp_import_manual_hint_account">Учетная запись</string> <string name="place_shortcut_on_home_screen">Разместить ярлык на рабочем столе</string> - <string name="password_list_fab_content_description">Создать новый пароль или папку</string> <string name="pgp_key_import_failed">Не удалось импортировать PGP-ключ</string> <string name="pgp_key_import_succeeded">PGP ключ успешно импортирован</string> </resources> diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 55fbeefa..746b74d2 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -38,7 +38,6 @@ <!-- PGPHandler --> <string name="clipboard_copied_text">复制到剪切板</string> <string name="file_toast_text">请提供文件名</string> - <string name="path_toast_text">请提供文件路径</string> <string name="empty_toast_text">您不能使用空密码或空额外内容</string> <!-- Git Async Task --> <string name="jgit_error_dialog_title">Git操作过程中发生错误</string> @@ -62,7 +61,6 @@ <string name="crypto_name_hint">名称</string> <string name="crypto_pass_label">密码</string> <string name="crypto_extra_label">附加内容</string> - <string name="crypto_encrypt_username_label">加密用户名</string> <string name="crypto_select">选择</string> <string name="crypto_save">保存</string> <string name="crypto_save_and_copy">保存并复制</string> @@ -121,7 +119,6 @@ <!-- PasswordGenerator fragment --> <string name="pwgen_title">生成密码</string> <string name="pwgen_generate">生成</string> - <string name="pwgen_include">包含</string> <string name="pwgen_length">长度</string> <string name="pwgen_numerals">数字</string> <string name="pwgen_symbols">标识</string> @@ -174,7 +171,6 @@ <string name="git_log">显示提交日志</string> <string name="show_password_pref_title">显示密码</string> <string name="show_password_pref_summary">控制密码解密后的可见性,这不会禁用复制到剪贴板</string> - <string name="pwd_generate_button">生成</string> <string name="send_plaintext_password_to">使用明文发送密码…</string> <string name="app_icon_hint">应用图标</string> <!-- Oreo Autofill --> @@ -209,7 +205,6 @@ <string name="oreo_autofill_enable_dialog_installed_browsers">已安装支持自动填充的浏览器:</string> <!-- Autofill --> <string name="ssh_key_does_not_exist">无法打开SSH私钥,请检查文件是否存在</string> - <string name="new_password_title">新密码</string> <string name="clear_saved_passphrase_ssh">清除本地已保存的SSH密钥密码</string> <string name="clear_saved_passphrase_https">清除保存的HTTPS密码</string> <string name="git_operation_remember_passphrase">记住密钥密码</string> @@ -278,7 +273,6 @@ personal.com</string> <string name="invalid_gpg_id">找到了 .gpg-id,但它包含无效的密钥ID、指纹或用户ID</string> <string name="short_gpg_id">找到 .gpg-id 但它包含一个不受支持的短十六进制ID。</string> <string name="invalid_filename_text">文件名不能包含\'/\'</string> - <string name="directory_hint">目录</string> <!-- GitException messages --> <string name="git_unknown_error">未知错误</string> <string name="git_pull_merge_fail_error">合并失败,你处于冲突状态。</string> @@ -324,11 +318,9 @@ GPG <string name="clear_saved_host_key_success">已成功清除已保存的主机密钥!</string> <string name="otp_import_qr_code">扫描二维码</string> <string name="otp_import_from_file">选择图像</string> - <string name="otp_import_manual_entry">手动输入</string> <string name="otp_import_manual_hint_secret">安全码 (Secret)</string> <string name="otp_import_manual_hint_account">账户</string> <string name="place_shortcut_on_home_screen">在主屏幕上放置快捷方式</string> - <string name="password_list_fab_content_description">创建新密码或文件夹</string> <string name="pgp_key_import_failed">导入 PGP 密钥失败</string> <string name="pgp_key_import_failed_replace_message">发现了 ID 相同的现有密钥,您要替换它吗?</string> <string name="pgp_key_import_succeeded">成功导入 PGP 密钥</string> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 34b11662..239cb219 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -47,7 +47,6 @@ <!-- PGPHandler --> <string name="clipboard_copied_text">Copied to clipboard</string> <string name="file_toast_text">Please provide a file name</string> - <string name="path_toast_text">Please provide a file path</string> <string name="empty_toast_text">You cannot use an empty password or empty extra content</string> <!-- Git Async Task --> @@ -79,7 +78,6 @@ <string name="crypto_username_hint">Username</string> <string name="crypto_pass_label">Password</string> <string name="crypto_extra_label">Extra content</string> - <string name="crypto_encrypt_username_label">Encrypt username</string> <string name="crypto_select">Select</string> <string name="crypto_save">Save</string> <string name="crypto_save_and_copy">Save and Copy</string> @@ -148,7 +146,6 @@ <!-- PasswordGenerator fragment --> <string name="pwgen_title">Generate Password</string> <string name="pwgen_generate">Generate</string> - <string name="pwgen_include">Include</string> <string name="pwgen_length">Length</string> <string name="pwgen_numerals">Numerals</string> <string name="pwgen_symbols">Symbols</string> @@ -201,7 +198,6 @@ <string name="git_log">Show commit log</string> <string name="show_password_pref_title">Show the password</string> <string name="show_password_pref_summary">Control the visibility of the passwords once decrypted. This does not disable copying to clipboard.</string> - <string name="pwd_generate_button">Generate</string> <string name="send_plaintext_password_to">Send password as plaintext using…</string> <string name="app_icon_hint">App icon</string> @@ -239,7 +235,6 @@ <!-- Autofill --> <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="clear_saved_passphrase_ssh">Clear saved passphrase for local SSH key</string> <string name="clear_saved_passphrase_https">Clear saved HTTPS password</string> <string name="git_operation_remember_passphrase">Remember key passphrase</string> @@ -311,7 +306,6 @@ <string name="invalid_gpg_id">Found .gpg-id, but it contains an invalid key ID, fingerprint or user ID</string> <string name="short_gpg_id">Found .gpg-id, but it contains a short hex ID, which is not supported</string> <string name="invalid_filename_text">File name must not contain \'/\', set directory above</string> - <string name="directory_hint">Directory</string> <!-- GitException messages --> <string name="git_unknown_error">Unknown error</string> @@ -360,11 +354,9 @@ <string name="clear_saved_host_key_success">Successfully cleared saved host key!</string> <string name="otp_import_qr_code">Scan QR code</string> <string name="otp_import_from_file">Choose an image</string> - <string name="otp_import_manual_entry">Enter manually</string> <string name="otp_import_manual_hint_secret">Secret</string> <string name="otp_import_manual_hint_account">Account</string> <string name="place_shortcut_on_home_screen">Place shortcut on home screen</string> - <string name="password_list_fab_content_description">Create new password or folder</string> <string name="pgp_key_import_failed">Failed to import PGP key</string> <string name="pgp_key_import_failed_replace_message">An existing key with this ID was found, do you want to replace it?</string> <string name="pgp_key_import_succeeded">Successfully imported PGP key</string> diff --git a/docs/superpowers/specs/2026-11-02-compose-ui-overhaul-design.md b/docs/superpowers/specs/2026-11-02-compose-ui-overhaul-design.md index 75a599fa..7eab1692 100644 --- a/docs/superpowers/specs/2026-11-02-compose-ui-overhaul-design.md +++ b/docs/superpowers/specs/2026-11-02-compose-ui-overhaul-design.md @@ -46,7 +46,10 @@ the maintainer's phone stays on 0.1.4 until then. - Password with reveal and a generator button; the generator is a bottom sheet with random and diceware tabs. - Username field, free-text extra content (pass format kept as text, nothing hidden). -- "OTP from QR" and "OTP manual" actions. +- "OTP from QR" (camera or image) and "OTP manual" actions. +- "Save and copy" in the overflow. Saving returns to the list; closing returns to where the + editor was opened. The folder picker is a dialog, shared with the list's move action. The + autofill save flow (`AutofillSaveActivity`) hosts the same editor. ## Architecture @@ -94,7 +97,8 @@ needs it. ## Security - `FLAG_SECURE` is set on the window while the entry view or editor destination is shown and - cleared when leaving it, so secrets stay out of screenshots and recents. + cleared when leaving it, so secrets stay out of screenshots and recents. It is counted, so + moving between the two never clears it. - `MainActivity` is not exported. Routes come only from `LaunchActivity` after its checks; an entry route is still validated with `isFileInRepo`. diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9d8f4111..13d866e4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,6 @@ agp = "9.5.0" androidxActivity = "1.9.2" bouncycastle = "1.86" coroutines = "1.9.0" -flowbinding = "1.2.0" hilt = "2.60.1" kotlin = "2.4.21" kotlinResult = "2.0.0" @@ -33,7 +32,6 @@ androidx-material = "com.google.android.material:material:1.12.0" androidx-preference = "androidx.preference:preference:1.2.1" androidx-recyclerview = "androidx.recyclerview:recyclerview:1.4.0" androidx-recyclerviewSelection = "androidx.recyclerview:recyclerview-selection:1.2.0" -androidx-swiperefreshlayout = "androidx.swiperefreshlayout:swiperefreshlayout:1.2.0" aps-sublimeFuzzy = "com.github.android-password-store:sublime-fuzzy:2.3.4" aps-zxingAndroidEmbedded = "com.github.android-password-store:zxing-android-embedded:4.2.1" build-agp = { module = "com.android.tools.build:gradle", version.ref = "agp" } @@ -77,7 +75,6 @@ thirdparty-bouncycastle-bcutil = { module = "org.bouncycastle:bcutil-jdk18on", v thirdparty-commons_codec = "commons-codec:commons-codec:1.14" thirdparty-compose-lints = "com.slack.lint.compose:compose-lint-checks:1.4.1" thirdparty-fastscroll = "me.zhanghai.android.fastscroll:library:1.3.0" -thirdparty-flowbinding-android = { module = "io.github.reactivecircus.flowbinding:flowbinding-android", version.ref = "flowbinding" } # JGit upgrades also raise its minimum Java runtime requirements that we cannot satisfy on Android # noinspection GradleDependency thirdparty-jgit = "org.eclipse.jgit:org.eclipse.jgit:5.13.5.202508271544-r" diff --git a/ui/compose/src/main/kotlin/app/passwordstore/ui/compose/PasswordField.kt b/ui/compose/src/main/kotlin/app/passwordstore/ui/compose/PasswordField.kt deleted file mode 100644 index 066a839c..00000000 --- a/ui/compose/src/main/kotlin/app/passwordstore/ui/compose/PasswordField.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package app.passwordstore.ui.compose - -import androidx.annotation.StringRes -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.Text -import androidx.compose.material3.TextField -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalClipboardManager -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.input.PasswordVisualTransformation -import androidx.compose.ui.text.input.VisualTransformation - -@Composable -public fun PasswordField( - value: String, - label: String, - initialVisibility: Boolean, - modifier: Modifier = Modifier, - readOnly: Boolean = false, -) { - var visible by remember { mutableStateOf(initialVisibility) } - TextField( - value = value, - onValueChange = {}, - readOnly = readOnly, - label = { Text(label) }, - visualTransformation = - if (visible) VisualTransformation.None else PasswordVisualTransformation(), - trailingIcon = { - ToggleButton( - visible = visible, - contentDescription = "Toggle password visibility", - onButtonClick = { visible = !visible }, - ) - }, - modifier = modifier, - ) -} - -@Composable -private fun ToggleButton( - visible: Boolean, - contentDescription: String, - onButtonClick: () -> Unit, - modifier: Modifier = Modifier, -) { - IconButton(onClick = onButtonClick, modifier = modifier) { - val icon = - if (visible) painterResource(id = R.drawable.baseline_visibility_off_24) - else painterResource(id = R.drawable.baseline_visibility_24) - Icon(painter = icon, contentDescription = contentDescription) - } -} - -@Composable -public fun CopyButton( - textToCopy: String, - @StringRes buttonLabelRes: Int, - modifier: Modifier = Modifier, -) { - val clipboard = LocalClipboardManager.current - IconButton(onClick = { clipboard.setText(AnnotatedString(textToCopy)) }, modifier = modifier) { - Icon( - painter = painterResource(R.drawable.ic_content_copy), - contentDescription = stringResource(buttonLabelRes), - ) - } -} |
