diff options
| author | Danilo M. <danix@danix.xyz> | 2026-11-02 21:39:54 +0100 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-11-02 21:39:54 +0100 |
| commit | bcd2d061288004020cc437694dbf4218abc52c89 (patch) | |
| tree | 7ee8561ba881d6a93f4629e2a30a0d3c68235c2b /app/src/main | |
| parent | 815f18752f99fb8cc7f57401eafaef20d9c59ca5 (diff) | |
| download | HardenedPass-bcd2d061288004020cc437694dbf4218abc52c89.tar.gz HardenedPass-bcd2d061288004020cc437694dbf4218abc52c89.zip | |
refactor(app): remove the legacy PasswordStore list
The Compose list replaces it. The autofill inline suggestion's
attribution intent now opens LaunchActivity, behind the biometric gate.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'app/src/main')
30 files changed, 10 insertions, 1570 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4840ca1f..9bfb3b6a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -34,11 +34,6 @@ tools:targetApi="s"> <activity - android:name=".ui.passwords.PasswordStore" - android:configChanges="orientation|screenSize" - android:exported="false" /> - - <activity android:name=".ui.onboarding.activity.OnboardingActivity" android:configChanges="orientation|screenSize" android:exported="false" /> diff --git a/app/src/main/java/app/passwordstore/ui/dialogs/FolderCreationDialogFragment.kt b/app/src/main/java/app/passwordstore/ui/dialogs/FolderCreationDialogFragment.kt deleted file mode 100644 index 669e126e..00000000 --- a/app/src/main/java/app/passwordstore/ui/dialogs/FolderCreationDialogFragment.kt +++ /dev/null @@ -1,77 +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.os.Bundle -import android.view.WindowManager -import androidx.appcompat.app.AlertDialog -import androidx.core.os.bundleOf -import androidx.fragment.app.DialogFragment -import app.passwordstore.R -import app.passwordstore.ui.passwords.PasswordStore -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.google.android.material.textfield.TextInputEditText -import com.google.android.material.textfield.TextInputLayout -import java.io.File - -class FolderCreationDialogFragment : DialogFragment() { - - private lateinit var newFolder: File - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val alertDialogBuilder = MaterialAlertDialogBuilder(requireContext()) - alertDialogBuilder.setTitle(R.string.title_create_folder) - alertDialogBuilder.setView(R.layout.folder_dialog_fragment) - alertDialogBuilder.setPositiveButton(getString(R.string.button_create), null) - alertDialogBuilder.setNegativeButton(getString(android.R.string.cancel)) { _, _ -> dismiss() } - val dialog = alertDialogBuilder.create() - dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE) - dialog.setOnShowListener { - dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { - createDirectory(requireArguments().getString(CURRENT_DIR_EXTRA)!!) - } - } - return dialog - } - - private fun createDirectory(currentDir: String) { - val dialog = requireDialog() - val folderNameView = dialog.findViewById<TextInputEditText>(R.id.folder_name_text) - val folderNameViewContainer = dialog.findViewById<TextInputLayout>(R.id.folder_name_container) - newFolder = File("$currentDir/${folderNameView.text}") - folderNameViewContainer.error = - when { - newFolder.isFile -> getString(R.string.folder_creation_err_file_exists) - newFolder.isDirectory -> getString(R.string.folder_creation_err_folder_exists) - else -> null - } - if (folderNameViewContainer.error != null) return - newFolder.mkdirs() - (requireActivity() as PasswordStore).refreshPasswordList(newFolder) - // TODO(msfjarvis): Restore this functionality - /* - if (dialog.findViewById<MaterialCheckBox>(R.id.set_gpg_key).isChecked) { - keySelectAction.launch(Intent(requireContext(), GetKeyIdsActivity::class.java)) - return - } else { - dismiss() - } - */ - dismiss() - } - - companion object { - - private const val CURRENT_DIR_EXTRA = "CURRENT_DIRECTORY" - - fun newInstance(startingDirectory: String): FolderCreationDialogFragment { - val extras = bundleOf(CURRENT_DIR_EXTRA to startingDirectory) - val fragment = FolderCreationDialogFragment() - fragment.arguments = extras - return fragment - } - } -} diff --git a/app/src/main/java/app/passwordstore/ui/dialogs/ItemCreationBottomSheet.kt b/app/src/main/java/app/passwordstore/ui/dialogs/ItemCreationBottomSheet.kt deleted file mode 100644 index 0304b16c..00000000 --- a/app/src/main/java/app/passwordstore/ui/dialogs/ItemCreationBottomSheet.kt +++ /dev/null @@ -1,77 +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.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.view.ViewTreeObserver -import android.widget.FrameLayout -import androidx.core.os.bundleOf -import androidx.fragment.app.setFragmentResult -import app.passwordstore.R -import app.passwordstore.ui.passwords.PasswordFragment.Companion.ACTION_FOLDER -import app.passwordstore.ui.passwords.PasswordFragment.Companion.ACTION_KEY -import app.passwordstore.ui.passwords.PasswordFragment.Companion.ACTION_PASSWORD -import app.passwordstore.ui.passwords.PasswordFragment.Companion.ITEM_CREATION_REQUEST_KEY -import com.google.android.material.bottomsheet.BottomSheetBehavior -import com.google.android.material.bottomsheet.BottomSheetDialog -import com.google.android.material.bottomsheet.BottomSheetDialogFragment - -class ItemCreationBottomSheet : BottomSheetDialogFragment() { - - private var behavior: BottomSheetBehavior<FrameLayout>? = null - private val bottomSheetCallback = - object : BottomSheetBehavior.BottomSheetCallback() { - override fun onSlide(bottomSheet: View, slideOffset: Float) {} - - override fun onStateChanged(bottomSheet: View, newState: Int) { - if (newState == BottomSheetBehavior.STATE_COLLAPSED) { - dismiss() - } - } - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle?, - ): View? { - if (savedInstanceState != null) dismiss() - return inflater.inflate(R.layout.item_create_sheet, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - view.viewTreeObserver.addOnGlobalLayoutListener( - object : ViewTreeObserver.OnGlobalLayoutListener { - override fun onGlobalLayout() { - view.viewTreeObserver.removeOnGlobalLayoutListener(this) - val dialog = dialog as BottomSheetDialog? ?: return - behavior = dialog.behavior - behavior?.apply { - state = BottomSheetBehavior.STATE_EXPANDED - peekHeight = 0 - addBottomSheetCallback(bottomSheetCallback) - } - dialog.findViewById<View>(R.id.create_folder)?.setOnClickListener { - setFragmentResult(ITEM_CREATION_REQUEST_KEY, bundleOf(ACTION_KEY to ACTION_FOLDER)) - dismiss() - } - dialog.findViewById<View>(R.id.create_password)?.setOnClickListener { - setFragmentResult(ITEM_CREATION_REQUEST_KEY, bundleOf(ACTION_KEY to ACTION_PASSWORD)) - dismiss() - } - } - } - ) - } - - override fun dismiss() { - super.dismiss() - behavior?.removeBottomSheetCallback(bottomSheetCallback) - } -} diff --git a/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderActivity.kt b/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderActivity.kt index cfeb6206..2139d7d2 100644 --- a/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderActivity.kt +++ b/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderActivity.kt @@ -11,9 +11,6 @@ import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.FragmentManager import androidx.fragment.app.commit import app.passwordstore.R -import app.passwordstore.data.repo.PasswordRepository -import app.passwordstore.ui.passwords.PASSWORD_FRAGMENT_TAG -import app.passwordstore.ui.passwords.PasswordStore import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint @@ -23,23 +20,10 @@ class SelectFolderActivity : AppCompatActivity(R.layout.select_folder_layout) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - passwordList = SelectFolderFragment() - val args = Bundle() - args.putString( - PasswordStore.REQUEST_ARG_PATH, - PasswordRepository.getRepositoryDirectory().absolutePath, - ) - - passwordList.arguments = args - supportActionBar?.show() - supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) - - supportFragmentManager.commit { - replace(R.id.pgp_handler_linearlayout, passwordList, PASSWORD_FRAGMENT_TAG) - } + supportFragmentManager.commit { replace(R.id.pgp_handler_linearlayout, passwordList) } } override fun onCreateOptionsMenu(menu: Menu): Boolean { diff --git a/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderFragment.kt b/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderFragment.kt index 3cf12fdd..acf4b56d 100644 --- a/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderFragment.kt +++ b/app/src/main/java/app/passwordstore/ui/folderselect/SelectFolderFragment.kt @@ -15,9 +15,9 @@ 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.ui.passwords.PasswordStore import app.passwordstore.util.coroutines.DispatcherProvider import app.passwordstore.util.extensions.viewBinding import app.passwordstore.util.viewmodel.ListMode @@ -56,11 +56,11 @@ class SelectFolderFragment : Fragment(R.layout.password_recycler_view) { FastScrollerBuilder(binding.passRecycler).build() registerForContextMenu(binding.passRecycler) - val path = - requireNotNull(requireArguments().getString(PasswordStore.REQUEST_ARG_PATH)) { - "Cannot navigate if ${PasswordStore.REQUEST_ARG_PATH} is not provided" - } - model.navigateTo(File(path), listMode = ListMode.DirectoriesOnly, pushPreviousLocation = false) + model.navigateTo( + PasswordRepository.getRepositoryDirectory(), + listMode = ListMode.DirectoriesOnly, + pushPreviousLocation = false, + ) lifecycleScope.launch { model.searchResult.flowWithLifecycle(lifecycle).collect { result -> recyclerAdapter.submitList(result.passwordItems) diff --git a/app/src/main/java/app/passwordstore/ui/passwords/PasswordFragment.kt b/app/src/main/java/app/passwordstore/ui/passwords/PasswordFragment.kt deleted file mode 100644 index 917bb91c..00000000 --- a/app/src/main/java/app/passwordstore/ui/passwords/PasswordFragment.kt +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ -package app.passwordstore.ui.passwords - -import android.content.Context -import android.content.SharedPreferences -import android.os.Bundle -import android.os.Parcelable -import android.view.Menu -import android.view.MenuItem -import android.view.View -import android.view.animation.Animation -import android.view.animation.AnimationUtils -import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult -import androidx.appcompat.view.ActionMode -import androidx.core.content.edit -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.flowWithLifecycle -import androidx.lifecycle.lifecycleScope -import androidx.recyclerview.widget.DividerItemDecoration -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.injection.prefs.SettingsPreferences -import app.passwordstore.ui.adapters.PasswordItemRecyclerAdapter -import app.passwordstore.ui.dialogs.BasicBottomSheet -import app.passwordstore.ui.dialogs.ItemCreationBottomSheet -import app.passwordstore.ui.git.base.BaseGitActivity -import app.passwordstore.ui.git.config.GitServerConfigActivity -import app.passwordstore.ui.util.OnOffItemAnimator -import app.passwordstore.util.coroutines.DispatcherProvider -import app.passwordstore.util.extensions.base64 -import app.passwordstore.util.extensions.getString -import app.passwordstore.util.extensions.sharedPrefs -import app.passwordstore.util.extensions.viewBinding -import app.passwordstore.util.settings.AuthMode -import app.passwordstore.util.settings.GitSettings -import app.passwordstore.util.settings.PasswordSortOrder -import app.passwordstore.util.settings.PreferenceKeys -import app.passwordstore.util.shortcuts.ShortcutHandler -import app.passwordstore.util.viewmodel.SearchableRepositoryViewModel -import com.github.michaelbull.result.fold -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 PasswordFragment : Fragment(R.layout.password_recycler_view) { - - @Inject lateinit var gitSettings: GitSettings - @Inject lateinit var shortcutHandler: ShortcutHandler - @Inject lateinit var dispatcherProvider: DispatcherProvider - @Inject @SettingsPreferences lateinit var prefs: SharedPreferences - private lateinit var recyclerAdapter: PasswordItemRecyclerAdapter - private lateinit var listener: OnFragmentInteractionListener - private lateinit var settings: SharedPreferences - - private var recyclerViewStateToRestore: Parcelable? = null - private var actionMode: ActionMode? = null - private var scrollTarget: File? = null - - private val model: SearchableRepositoryViewModel by activityViewModels() - private val binding by viewBinding(PasswordRecyclerViewBinding::bind) - private val swipeResult = - registerForActivityResult(StartActivityForResult()) { - binding.swipeRefresher.isRefreshing = false - requireStore().refreshPasswordList() - } - - val currentDir: File - get() = model.currentDir.value - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - settings = requireContext().sharedPrefs - initializePasswordList() - binding.fab.setOnClickListener { - ItemCreationBottomSheet().show(childFragmentManager, "BOTTOM_SHEET") - } - childFragmentManager.setFragmentResultListener(ITEM_CREATION_REQUEST_KEY, viewLifecycleOwner) { - _, - bundle -> - when (bundle.getString(ACTION_KEY)) { - ACTION_FOLDER -> requireStore().createFolder() - ACTION_PASSWORD -> requireStore().createPassword() - } - } - } - - private fun initializePasswordList() { - val gitDir = File(PasswordRepository.getRepositoryDirectory(), ".git") - val hasGitDir = - gitDir.exists() && gitDir.isDirectory && (gitDir.listFiles()?.isNotEmpty() == true) - binding.swipeRefresher.setOnRefreshListener { - if (!hasGitDir) { - requireStore().refreshPasswordList() - binding.swipeRefresher.isRefreshing = false - } else if (!PasswordRepository.isGitRepo()) { - BasicBottomSheet.Builder(requireContext()) - .setMessageRes(R.string.clone_git_repo) - .setPositiveButtonClickListener(getString(R.string.clone_button)) { - swipeResult.launch(GitServerConfigActivity.createCloneIntent(requireContext())) - } - .build() - .show(requireActivity().supportFragmentManager, "NOT_A_GIT_REPO") - binding.swipeRefresher.isRefreshing = false - } else { - // When authentication is set to AuthMode.None then the only git operation we can - // run is a pull, so automatically fallback to that. - val operationId = - when (gitSettings.authMode) { - AuthMode.None -> BaseGitActivity.GitOp.PULL - else -> BaseGitActivity.GitOp.SYNC - } - requireStore().apply { - lifecycleScope.launch { - launchGitOperation(operationId) - .fold( - success = { - binding.swipeRefresher.isRefreshing = false - refreshPasswordList() - }, - failure = { err -> - promptOnErrorHandler(err) { binding.swipeRefresher.isRefreshing = false } - }, - ) - } - } - } - } - - recyclerAdapter = - PasswordItemRecyclerAdapter(lifecycleScope, dispatcherProvider) - .onItemClicked { _, item -> listener.onFragmentInteraction(item) } - .onSelectionChanged { selection -> - // In order to not interfere with drag selection, we disable the - // SwipeRefreshLayout - // once an item is selected. - binding.swipeRefresher.isEnabled = selection.isEmpty - - if (actionMode == null) - actionMode = - requireStore().startSupportActionMode(actionModeCallback) ?: return@onSelectionChanged - - if (!selection.isEmpty) { - actionMode!!.title = - resources.getQuantityString( - R.plurals.delete_title, - selection.size(), - selection.size(), - ) - actionMode!!.invalidate() - } else { - actionMode!!.finish() - } - } - val recyclerView = binding.passRecycler - recyclerView.apply { - addItemDecoration(DividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL)) - layoutManager = LinearLayoutManager(requireContext()) - itemAnimator = OnOffItemAnimator() - adapter = recyclerAdapter - } - - FastScrollerBuilder(recyclerView).build() - recyclerAdapter.makeSelectable(recyclerView) - registerForContextMenu(recyclerView) - - val path = - requireNotNull(requireArguments().getString(PasswordStore.REQUEST_ARG_PATH)) { - "Cannot navigate if ${PasswordStore.REQUEST_ARG_PATH} is not provided" - } - model.navigateTo(File(path), pushPreviousLocation = false) - lifecycleScope.launch { - model.searchResult.flowWithLifecycle(lifecycle).collect { result -> - // Only run animations when the new list is filtered, i.e., the user submitted a search, - // and not on folder navigation since the latter leads to too many removal animations. - (recyclerView.itemAnimator as OnOffItemAnimator).isEnabled = result.isFiltered - recyclerAdapter.submitList(result.passwordItems) { - when { - result.isFiltered -> { - // When the result is filtered, we always scroll to the top since that is - // where the best fuzzy match appears. - recyclerView.scrollToPosition(0) - } - scrollTarget != null -> { - scrollTarget?.let { - recyclerView.scrollToPosition(recyclerAdapter.getPositionForFile(it)) - } - scrollTarget = null - } - else -> { - // When the result is not filtered and there is a saved scroll position for - // it, we try to restore it. - recyclerViewStateToRestore?.let { - recyclerView.layoutManager!!.onRestoreInstanceState(it) - } - recyclerViewStateToRestore = null - } - } - } - } - } - } - - private val actionModeCallback = - object : ActionMode.Callback { - // Called when the action mode is created; startActionMode() was called - override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { - // Inflate a menu resource providing context menu items - mode.menuInflater.inflate(R.menu.context_pass, menu) - // hide the fab - animateFab(false) - return true - } - - // Called each time the action mode is shown. Always called after onCreateActionMode, - // but may be called multiple times if the mode is invalidated. - override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { - val selectedItems = recyclerAdapter.getSelectedItems() - menu.findItem(R.id.menu_edit_password).isVisible = - selectedItems.all { it.type == PasswordItem.TYPE_CATEGORY } - menu.findItem(R.id.menu_pin_password).isVisible = - selectedItems.size == 1 && selectedItems[0].type == PasswordItem.TYPE_PASSWORD - return true - } - - // Called when the user selects a contextual menu item - override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { - return when (item.itemId) { - R.id.menu_delete_password -> { - requireStore().deletePasswords(recyclerAdapter.getSelectedItems()) - // Action picked, so close the CAB - mode.finish() - true - } - R.id.menu_move_password -> { - requireStore().movePasswords(recyclerAdapter.getSelectedItems()) - false - } - R.id.menu_edit_password -> { - requireStore().renameCategory(recyclerAdapter.getSelectedItems()) - mode.finish() - false - } - R.id.menu_pin_password -> { - val passwordItem = recyclerAdapter.getSelectedItems()[0] - shortcutHandler.addPinnedShortcut( - passwordItem, - passwordItem.createAuthEnabledIntent(requireContext()), - ) - false - } - else -> false - } - } - - // Called when the user exits the action mode - override fun onDestroyActionMode(mode: ActionMode) { - recyclerAdapter.requireSelectionTracker().clearSelection() - actionMode = null - // show the fab - animateFab(true) - } - - private fun animateFab(show: Boolean) = - with(binding.fab) { - val animation = - AnimationUtils.loadAnimation(context, if (show) R.anim.scale_up else R.anim.scale_down) - animation.setAnimationListener( - object : Animation.AnimationListener { - override fun onAnimationRepeat(animation: Animation?) {} - - override fun onAnimationEnd(animation: Animation?) { - if (!show) visibility = View.GONE - } - - override fun onAnimationStart(animation: Animation?) { - if (show) visibility = View.VISIBLE - } - } - ) - animate() - .rotationBy(if (show) -90f else 90f) - .setStartDelay(if (show) 100 else 0) - .setDuration(100) - .start() - startAnimation(animation) - } - } - - override fun onResume() { - super.onResume() - binding.swipeRefresher.isEnabled = !prefs.getBoolean(PreferenceKeys.DISABLE_SYNC_ACTION, false) - } - - override fun onAttach(context: Context) { - super.onAttach(context) - runCatching { - listener = - object : OnFragmentInteractionListener { - override fun onFragmentInteraction(item: PasswordItem) { - if ( - settings.getString(PreferenceKeys.SORT_ORDER) == - PasswordSortOrder.RECENTLY_USED.name - ) { - // save the time when password was used - val preferences = - context.getSharedPreferences("recent_password_history", Context.MODE_PRIVATE) - preferences.edit { - putString(item.file.absolutePath.base64(), System.currentTimeMillis().toString()) - } - } - - if (item.type == PasswordItem.TYPE_CATEGORY) { - navigateTo(item.file) - } else { - if (requireArguments().getBoolean("matchWith", false)) { - requireStore().matchPasswordWithApp(item) - } else { - requireStore().decryptPassword(item) - } - } - } - } - } - .onFailure { - throw ClassCastException("$context must implement OnFragmentInteractionListener") - } - } - - private fun requireStore() = requireActivity() as PasswordStore - - /** Returns true if the back press was handled by the [Fragment]. */ - fun onBackPressedInActivity(): Boolean { - if (!model.canNavigateBack) return false - // The RecyclerView state is restored when the asynchronous update operation on the - // adapter is completed. - recyclerViewStateToRestore = model.navigateBack() - if (!model.canNavigateBack) requireStore().supportActionBar?.setDisplayHomeAsUpEnabled(false) - return true - } - - fun dismissActionMode() { - actionMode?.finish() - } - - companion object { - - const val ITEM_CREATION_REQUEST_KEY = "creation_key" - const val ACTION_KEY = "action" - const val ACTION_FOLDER = "folder" - const val ACTION_PASSWORD = "password" - - fun newInstance(args: Bundle): PasswordFragment { - val fragment = PasswordFragment() - fragment.arguments = args - return fragment - } - } - - fun navigateTo(file: File) { - requireStore().clearSearch() - model.navigateTo( - file, - recyclerViewState = binding.passRecycler.layoutManager!!.onSaveInstanceState(), - ) - requireStore().supportActionBar?.setDisplayHomeAsUpEnabled(true) - } - - fun scrollToOnNextRefresh(file: File) { - scrollTarget = file - } - - interface OnFragmentInteractionListener { - - fun onFragmentInteraction(item: PasswordItem) - } -} diff --git a/app/src/main/java/app/passwordstore/ui/passwords/PasswordStore.kt b/app/src/main/java/app/passwordstore/ui/passwords/PasswordStore.kt deleted file mode 100644 index 059b398f..00000000 --- a/app/src/main/java/app/passwordstore/ui/passwords/PasswordStore.kt +++ /dev/null @@ -1,621 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ -package app.passwordstore.ui.passwords - -import android.content.ComponentName -import android.content.Context -import android.content.Intent -import android.os.Bundle -import android.view.KeyEvent -import android.view.Menu -import android.view.MenuItem -import android.view.MenuItem.OnActionExpandListener -import android.view.WindowManager -import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult -import androidx.activity.viewModels -import androidx.appcompat.widget.SearchView -import androidx.appcompat.widget.SearchView.OnQueryTextListener -import androidx.core.content.edit -import androidx.fragment.app.FragmentManager -import androidx.fragment.app.commit -import androidx.lifecycle.flowWithLifecycle -import androidx.lifecycle.lifecycleScope -import app.passwordstore.R -import app.passwordstore.data.password.PasswordItem -import app.passwordstore.data.repo.PasswordRepository -import app.passwordstore.ui.crypto.BasePGPActivity -import app.passwordstore.ui.crypto.BasePGPActivity.Companion.getLongName -import app.passwordstore.ui.crypto.DecryptActivity -import app.passwordstore.ui.crypto.PasswordCreationActivity -import app.passwordstore.ui.dialogs.FolderCreationDialogFragment -import app.passwordstore.ui.folderselect.SelectFolderActivity -import app.passwordstore.ui.git.base.BaseGitActivity -import app.passwordstore.ui.onboarding.activity.OnboardingActivity -import app.passwordstore.ui.settings.SettingsActivity -import app.passwordstore.util.autofill.AutofillMatcher -import app.passwordstore.util.extensions.base64 -import app.passwordstore.util.extensions.commitChange -import app.passwordstore.util.extensions.contains -import app.passwordstore.util.extensions.getString -import app.passwordstore.util.extensions.isInsideRepository -import app.passwordstore.util.extensions.launchActivity -import app.passwordstore.util.extensions.listFilesRecursively -import app.passwordstore.util.extensions.sharedPrefs -import app.passwordstore.util.settings.AuthMode -import app.passwordstore.util.settings.PreferenceKeys -import app.passwordstore.util.shortcuts.ShortcutHandler -import app.passwordstore.util.viewmodel.SearchableRepositoryViewModel -import com.github.michaelbull.result.fold -import com.github.michaelbull.result.onFailure -import com.github.michaelbull.result.runCatching -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.google.android.material.textfield.TextInputEditText -import dagger.hilt.android.AndroidEntryPoint -import java.io.File -import java.lang.Character.UnicodeBlock -import javax.inject.Inject -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import logcat.LogPriority.ERROR -import logcat.LogPriority.INFO -import logcat.logcat - -const val PASSWORD_FRAGMENT_TAG = "PasswordsList" - -@AndroidEntryPoint -class PasswordStore : BaseGitActivity() { - - @Inject lateinit var shortcutHandler: ShortcutHandler - private lateinit var searchItem: MenuItem - private val settings by lazy { sharedPrefs } - - private val model: SearchableRepositoryViewModel by viewModels() - - private val listRefreshAction = - registerForActivityResult(StartActivityForResult()) { result -> - if (result.resultCode == RESULT_OK) { - refreshPasswordList() - } - } - - private val passwordMoveAction = - registerForActivityResult(StartActivityForResult()) { result -> - val intentData = result.data ?: return@registerForActivityResult - val filesToMove = - requireNotNull(intentData.getStringArrayExtra("Files")) { - "'Files' intent extra must be set" - } - val target = - File( - requireNotNull(intentData.getStringExtra("SELECTED_FOLDER_PATH")) { - "'SELECTED_FOLDER_PATH' intent extra must be set" - } - ) - val repositoryPath = PasswordRepository.getRepositoryDirectory().absolutePath - if (!target.isDirectory) { - logcat(ERROR) { "Tried moving passwords to a non-existing folder." } - return@registerForActivityResult - } - - logcat { "Moving passwords to ${intentData.getStringExtra("SELECTED_FOLDER_PATH")}" } - logcat { filesToMove.joinToString(", ") } - - lifecycleScope.launch(dispatcherProvider.io()) { - for (file in filesToMove) { - val source = File(file) - if (!source.exists()) { - logcat(ERROR) { "Tried moving something that appears non-existent." } - continue - } - val destinationFile = File(target.absolutePath + "/" + source.name) - val basename = source.nameWithoutExtension - val sourceLongName = - getLongName( - requireNotNull(source.parent) { "$file has no parent" }, - repositoryPath, - basename, - ) - val destinationLongName = getLongName(target.absolutePath, repositoryPath, basename) - if (destinationFile.exists()) { - logcat(ERROR) { "Trying to move a file that already exists." } - withContext(dispatcherProvider.main()) { - MaterialAlertDialogBuilder(this@PasswordStore) - .setTitle(resources.getString(R.string.password_exists_title)) - .setMessage( - resources.getString( - R.string.password_exists_message, - destinationLongName, - sourceLongName, - ) - ) - .setPositiveButton(R.string.dialog_ok) { _, _ -> - launch(dispatcherProvider.io()) { moveFile(source, destinationFile) } - } - .setNegativeButton(R.string.dialog_cancel, null) - .show() - } - } else { - launch(dispatcherProvider.io()) { moveFile(source, destinationFile) } - } - } - when (filesToMove.size) { - 1 -> { - val source = File(filesToMove[0]) - val basename = source.nameWithoutExtension - val sourceLongName = - getLongName( - requireNotNull(source.parent) { "$basename has no parent" }, - repositoryPath, - basename, - ) - val destinationLongName = getLongName(target.absolutePath, repositoryPath, basename) - withContext(dispatcherProvider.main()) { - commitChange( - resources.getString( - R.string.git_commit_move_text, - sourceLongName, - destinationLongName, - ) - ) - } - } - else -> { - val repoDir = PasswordRepository.getRepositoryDirectory().absolutePath - val relativePath = getRelativePath("${target.absolutePath}/", repoDir) - withContext(dispatcherProvider.main()) { - commitChange( - resources.getString(R.string.git_commit_move_multiple_text, relativePath) - ) - } - } - } - } - refreshPasswordList() - getPasswordFragment()?.dismissActionMode() - } - - override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { - // open search view on search key, or Ctr+F - if ( - (keyCode == KeyEvent.KEYCODE_SEARCH || - keyCode == KeyEvent.KEYCODE_F && event.isCtrlPressed) && !searchItem.isActionViewExpanded - ) { - searchItem.expandActionView() - return true - } - - // open search view on any printable character and query for it - val c = event.unicodeChar.toChar() - val printable = isPrintable(c) - if (printable && !searchItem.isActionViewExpanded) { - searchItem.expandActionView() - (searchItem.actionView as SearchView).setQuery(c.toString(), true) - return true - } - return super.onKeyDown(keyCode, event) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_pwdstore) - - lifecycleScope.launch { - model.currentDir.flowWithLifecycle(lifecycle).collect { dir -> - val basePath = PasswordRepository.getRepositoryDirectory().absoluteFile - supportActionBar?.apply { - if (dir != basePath) title = dir.name else setTitle(R.string.app_name) - } - } - } - } - - override fun onStart() { - super.onStart() - refreshPasswordList() - } - - override fun onResume() { - super.onResume() - checkLocalRepository() - if (settings.getBoolean(PreferenceKeys.SEARCH_ON_START, false) && ::searchItem.isInitialized) { - if (!searchItem.isActionViewExpanded) { - searchItem.expandActionView() - } - } - } - - override fun onCreateOptionsMenu(menu: Menu): Boolean { - val menuRes = - when { - gitSettings.authMode == AuthMode.None -> R.menu.main_menu_no_auth - PasswordRepository.isGitRepo() -> R.menu.main_menu_git - else -> R.menu.main_menu_non_git - } - menuInflater.inflate(menuRes, menu) - return super.onCreateOptionsMenu(menu) - } - - override fun onPrepareOptionsMenu(menu: Menu): Boolean { - // Invalidation forces onCreateOptionsMenu to be called again. This is cheap and quick so - // we can get by without any noticeable difference in performance. - invalidateOptionsMenu() - searchItem = menu.findItem(R.id.action_search) - val searchView = searchItem.actionView as SearchView - searchView.setOnQueryTextListener( - object : OnQueryTextListener { - override fun onQueryTextSubmit(s: String): Boolean { - searchView.clearFocus() - return true - } - - override fun onQueryTextChange(s: String): Boolean { - val filter = s.trim() - // List the contents of the current directory if the user enters a blank - // search term. - if (filter.isEmpty()) - model.navigateTo(newDirectory = model.currentDir.value, pushPreviousLocation = false) - else model.search(filter) - return true - } - } - ) - - // When using the support library, the setOnActionExpandListener() method is - // static and accepts the MenuItem object as an argument - searchItem.setOnActionExpandListener( - object : OnActionExpandListener { - override fun onMenuItemActionCollapse(item: MenuItem): Boolean { - refreshPasswordList() - return true - } - - override fun onMenuItemActionExpand(item: MenuItem): Boolean { - return true - } - } - ) - if (settings.getBoolean(PreferenceKeys.SEARCH_ON_START, false)) { - searchItem.expandActionView() - } - return super.onPrepareOptionsMenu(menu) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - val id = item.itemId - val initBefore = - MaterialAlertDialogBuilder(this) - .setMessage(resources.getString(R.string.creation_dialog_text)) - .setPositiveButton(resources.getString(R.string.dialog_ok), null) - when (id) { - R.id.user_pref -> { - runCatching { launchActivity(SettingsActivity::class.java) } - .onFailure { e -> e.printStackTrace() } - } - R.id.git_push -> { - if (!PasswordRepository.isInitialized) { - initBefore.show() - } else { - runGitOperation(GitOp.PUSH) - } - } - R.id.git_pull -> { - if (!PasswordRepository.isInitialized) { - initBefore.show() - } else { - runGitOperation(GitOp.PULL) - } - } - R.id.git_sync -> { - if (!PasswordRepository.isInitialized) { - initBefore.show() - } else { - runGitOperation(GitOp.SYNC) - } - } - R.id.refresh -> refreshPasswordList() - android.R.id.home -> { - @Suppress("DEPRECATION") onBackPressed() - } - else -> return super.onOptionsItemSelected(item) - } - return true - } - - @Deprecated("Deprecated in Java") - @Suppress("DEPRECATION") - override fun onBackPressed() { - if (getPasswordFragment()?.onBackPressedInActivity() != true) super.onBackPressed() - } - - private fun getPasswordFragment(): PasswordFragment? { - return supportFragmentManager.findFragmentByTag(PASSWORD_FRAGMENT_TAG) as? PasswordFragment - } - - fun clearSearch() { - if (searchItem.isActionViewExpanded) searchItem.collapseActionView() - } - - private fun runGitOperation(operation: GitOp) = - lifecycleScope.launch { - launchGitOperation(operation) - .fold(success = { refreshPasswordList() }, failure = { promptOnErrorHandler(it) }) - } - - private fun checkLocalRepository() { - PasswordRepository.initialize() - checkLocalRepository(PasswordRepository.getRepositoryDirectory()) - } - - private fun checkLocalRepository(localDir: File?) { - if (localDir != null && settings.getBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, false)) { - // do not push the fragment if we already have it - if ( - getPasswordFragment() == null || settings.getBoolean(PreferenceKeys.REPO_CHANGED, false) - ) { - settings.edit { putBoolean(PreferenceKeys.REPO_CHANGED, false) } - val args = Bundle() - args.putString(REQUEST_ARG_PATH, PasswordRepository.getRepositoryDirectory().absolutePath) - - // if the activity was started from the autofill settings, the - // intent is to match a clicked pwd with app. pass this to fragment - if (intent.getBooleanExtra("matchWith", false)) { - args.putBoolean("matchWith", true) - } - supportActionBar?.apply { - show() - setDisplayHomeAsUpEnabled(false) - } - supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) - supportFragmentManager.commit { - replace(R.id.main_layout, PasswordFragment.newInstance(args), PASSWORD_FRAGMENT_TAG) - } - } - } else { - launchActivity(OnboardingActivity::class.java) - } - } - - private fun getRelativePath(fullPath: String, repositoryPath: String): String { - return fullPath.replace(repositoryPath, "").replace("/+".toRegex(), "/") - } - - fun decryptPassword(item: PasswordItem) { - val authDecryptIntent = item.createAuthEnabledIntent(this) - val decryptIntent = - Intent(authDecryptIntent).setComponent(ComponentName(this, DecryptActivity::class.java)) - - startActivity(decryptIntent) - - // Adds shortcut - shortcutHandler.addDynamicShortcut(item, authDecryptIntent) - } - - private fun validateState(): Boolean { - if (!PasswordRepository.isInitialized) { - MaterialAlertDialogBuilder(this) - .setMessage(resources.getString(R.string.creation_dialog_text)) - .setPositiveButton(resources.getString(R.string.dialog_ok), null) - .show() - return false - } - return true - } - - fun createPassword() { - if (!validateState()) return - val currentDir = currentDir - logcat(INFO) { "Adding file to : ${currentDir.absolutePath}" } - val intent = Intent(this, PasswordCreationActivity::class.java) - intent.putExtra(BasePGPActivity.EXTRA_FILE_PATH, currentDir.absolutePath) - intent.putExtra( - BasePGPActivity.EXTRA_REPO_PATH, - PasswordRepository.getRepositoryDirectory().absolutePath, - ) - listRefreshAction.launch(intent) - } - - fun createFolder() { - if (!validateState()) return - FolderCreationDialogFragment.newInstance(currentDir.path).show(supportFragmentManager, null) - } - - fun deletePasswords(selectedItems: List<PasswordItem>) { - var size = 0 - selectedItems.forEach { - if (it.file.isFile) size++ else size += it.file.listFilesRecursively().size - } - if (size == 0) { - selectedItems.map { item -> item.file.deleteRecursively() } - refreshPasswordList() - return - } - MaterialAlertDialogBuilder(this) - .setMessage(resources.getQuantityString(R.plurals.delete_dialog_text, size, size)) - .setPositiveButton(resources.getString(R.string.dialog_yes)) { _, _ -> - val filesToDelete = arrayListOf<File>() - selectedItems.forEach { item -> - if (item.file.isDirectory) filesToDelete.addAll(item.file.listFilesRecursively()) - else filesToDelete.add(item.file) - } - selectedItems.map { item -> item.file.deleteRecursively() } - refreshPasswordList() - AutofillMatcher.updateMatches(applicationContext, delete = filesToDelete) - val fmt = - selectedItems.joinToString(separator = ", ") { item -> - item.file.toRelativeString(PasswordRepository.getRepositoryDirectory()) - } - lifecycleScope.launch { - commitChange(resources.getString(R.string.git_commit_remove_text, fmt)) - } - } - .setNegativeButton(resources.getString(R.string.dialog_no), null) - .show() - } - - fun movePasswords(values: List<PasswordItem>) { - val intent = Intent(this, SelectFolderActivity::class.java) - val fileLocations = values.map { it.file.absolutePath }.toTypedArray() - intent.putExtra("Files", fileLocations) - passwordMoveAction.launch(intent) - } - - enum class CategoryRenameError(val resource: Int) { - None(0), - EmptyField(R.string.message_category_error_empty_field), - CategoryExists(R.string.message_category_error_category_exists), - DestinationOutsideRepo(R.string.message_error_destination_outside_repo), - } - - /** - * Prompt the user with a new category name to assign, if the new category forms/leads a path - * (i.e. contains "/"), intermediate directories will be created and new category will be placed - * inside. - * - * @param oldCategory The category to change its name - * @param error Determines whether to show an error to the user in the alert dialog, this error - * may be due to the new category the user entered already exists or the field was empty or the - * destination path is outside the repository - * @see [CategoryRenameError] - * @see [isInsideRepository] - */ - private fun renameCategory( - oldCategory: PasswordItem, - error: CategoryRenameError = CategoryRenameError.None, - ) { - val view = layoutInflater.inflate(R.layout.folder_dialog_fragment, null) - val newCategoryEditText = view.findViewById<TextInputEditText>(R.id.folder_name_text) - - if (error != CategoryRenameError.None) { - newCategoryEditText.error = getString(error.resource) - } - - val dialog = - MaterialAlertDialogBuilder(this) - .setTitle(R.string.title_rename_folder) - .setView(view) - .setMessage(getString(R.string.message_rename_folder, oldCategory.name)) - .setPositiveButton(R.string.dialog_ok) { _, _ -> - val newCategory = File("${oldCategory.file.parent}/${newCategoryEditText.text}") - when { - newCategoryEditText.text.isNullOrBlank() -> - renameCategory(oldCategory, CategoryRenameError.EmptyField) - newCategory.exists() -> renameCategory(oldCategory, CategoryRenameError.CategoryExists) - !newCategory.isInsideRepository() -> - renameCategory(oldCategory, CategoryRenameError.DestinationOutsideRepo) - else -> - lifecycleScope.launch(dispatcherProvider.io()) { - moveFile(oldCategory.file, newCategory) - - // associate the new category with the last category's timestamp in - // history - val preference = - getSharedPreferences("recent_password_history", Context.MODE_PRIVATE) - val timestamp = preference.getString(oldCategory.file.absolutePath.base64()) - if (timestamp != null) { - preference.edit { - remove(oldCategory.file.absolutePath.base64()) - putString(newCategory.absolutePath.base64(), timestamp) - } - } - - withContext(dispatcherProvider.main()) { - commitChange( - resources.getString( - R.string.git_commit_move_text, - oldCategory.name, - newCategory.name, - ) - ) - } - } - } - } - .setNegativeButton(R.string.dialog_skip, null) - .create() - - dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE) - dialog.show() - } - - fun renameCategory(categories: List<PasswordItem>) { - for (oldCategory in categories) { - renameCategory(oldCategory) - } - } - - /** - * Refreshes the password list by re-executing the last navigation or search action, preserving - * the navigation stack and scroll position. If the current directory no longer exists, navigation - * is reset to the repository root. If the optional [target] argument is provided, it will be - * entered if it is a directory or scrolled into view if it is a file (both inside the current - * directory). - */ - fun refreshPasswordList(target: File? = null) { - val plist = getPasswordFragment() - if (target?.isDirectory == true && model.currentDir.value.contains(target)) { - plist?.navigateTo(target) - } else if (target?.isFile == true && model.currentDir.value.contains(target)) { - // Creating new passwords is handled by an activity, so we will refresh in onStart. - plist?.scrollToOnNextRefresh(target) - } else if (model.currentDir.value.isDirectory) { - model.forceRefresh() - } else { - model.reset() - supportActionBar?.setDisplayHomeAsUpEnabled(false) - } - } - - private val currentDir: File - get() = getPasswordFragment()?.currentDir ?: PasswordRepository.getRepositoryDirectory() - - private suspend fun moveFile(source: File, destinationFile: File) { - val sourceDestinationMap = - if (source.isDirectory) { - destinationFile.mkdirs() - // Recursively list all files (not directories) below `source`, then - // obtain the corresponding target file by resolving the relative path - // starting at the destination folder. - source.listFilesRecursively().associateWith { - destinationFile.resolve(it.relativeTo(source)) - } - } else { - mapOf(source to destinationFile) - } - if (!source.renameTo(destinationFile)) { - logcat(ERROR) { "Something went wrong while moving $source to $destinationFile." } - withContext(dispatcherProvider.main()) { - MaterialAlertDialogBuilder(this@PasswordStore) - .setTitle(R.string.password_move_error_title) - .setMessage(getString(R.string.password_move_error_message, source, destinationFile)) - .setCancelable(true) - .setPositiveButton(android.R.string.ok, null) - .show() - } - } else { - AutofillMatcher.updateMatches(this, sourceDestinationMap) - } - } - - fun matchPasswordWithApp(item: PasswordItem) { - val path = - item.file.absolutePath - .replace(PasswordRepository.getRepositoryDirectory().toString() + "/", "") - .replace(".gpg", "") - val data = Intent() - data.putExtra("path", path) - setResult(RESULT_OK, data) - finish() - } - - companion object { - - const val REQUEST_ARG_PATH = "PATH" - - private fun isPrintable(c: Char): Boolean { - val block = UnicodeBlock.of(c) - return (!Character.isISOControl(c) && block != null && block !== UnicodeBlock.SPECIALS) - } - } -} diff --git a/app/src/main/java/app/passwordstore/ui/util/OnOffItemAnimator.kt b/app/src/main/java/app/passwordstore/ui/util/OnOffItemAnimator.kt deleted file mode 100644 index cd29f626..00000000 --- a/app/src/main/java/app/passwordstore/ui/util/OnOffItemAnimator.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ -package app.passwordstore.ui.util - -import androidx.recyclerview.widget.DefaultItemAnimator -import androidx.recyclerview.widget.RecyclerView - -class OnOffItemAnimator : DefaultItemAnimator() { - - var isEnabled: Boolean = true - set(value) { - // Defer update until no animation is running anymore. - isRunning { field = value } - } - - private fun dontAnimate(viewHolder: RecyclerView.ViewHolder): Boolean { - dispatchAnimationFinished(viewHolder) - return false - } - - override fun animateAppearance( - viewHolder: RecyclerView.ViewHolder, - preLayoutInfo: ItemHolderInfo?, - postLayoutInfo: ItemHolderInfo, - ): Boolean { - return if (isEnabled) { - super.animateAppearance(viewHolder, preLayoutInfo, postLayoutInfo) - } else { - dontAnimate(viewHolder) - } - } - - override fun animateChange( - oldHolder: RecyclerView.ViewHolder, - newHolder: RecyclerView.ViewHolder, - preInfo: ItemHolderInfo, - postInfo: ItemHolderInfo, - ): Boolean { - return if (isEnabled) { - super.animateChange(oldHolder, newHolder, preInfo, postInfo) - } else { - dontAnimate(oldHolder) - } - } - - override fun animateDisappearance( - viewHolder: RecyclerView.ViewHolder, - preLayoutInfo: ItemHolderInfo, - postLayoutInfo: ItemHolderInfo?, - ): Boolean { - return if (isEnabled) { - super.animateDisappearance(viewHolder, preLayoutInfo, postLayoutInfo) - } else { - dontAnimate(viewHolder) - } - } - - override fun animatePersistence( - viewHolder: RecyclerView.ViewHolder, - preInfo: ItemHolderInfo, - postInfo: ItemHolderInfo, - ): Boolean { - return if (isEnabled) { - super.animatePersistence(viewHolder, preInfo, postInfo) - } else { - dontAnimate(viewHolder) - } - } -} diff --git a/app/src/main/java/app/passwordstore/util/autofill/AutofillViewUtils.kt b/app/src/main/java/app/passwordstore/util/autofill/AutofillViewUtils.kt index ebe4bc7d..59fd5cca 100644 --- a/app/src/main/java/app/passwordstore/util/autofill/AutofillViewUtils.kt +++ b/app/src/main/java/app/passwordstore/util/autofill/AutofillViewUtils.kt @@ -20,7 +20,7 @@ import androidx.autofill.inline.UiVersions import androidx.autofill.inline.v1.InlineSuggestionUi import app.passwordstore.R import app.passwordstore.data.repo.PasswordRepository -import app.passwordstore.ui.passwords.PasswordStore +import app.passwordstore.ui.main.LaunchActivity import java.io.File data class DatasetMetadata(val title: String, val subtitle: String?, @DrawableRes val iconRes: Int) @@ -55,7 +55,7 @@ fun makeInlinePresentation( PendingIntent.getActivity( context, 0, - Intent(context, PasswordStore::class.java), + Intent(context, LaunchActivity::class.java), if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { PendingIntent.FLAG_MUTABLE } else { diff --git a/app/src/main/res/anim/scale_down.xml b/app/src/main/res/anim/scale_down.xml deleted file mode 100644 index cbb4cb97..00000000 --- a/app/src/main/res/anim/scale_down.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 - --> - -<set xmlns:android="http://schemas.android.com/apk/res/android"> - <scale - android:duration="300" - android:fromXScale="1.0" - android:fromYScale="1.0" - android:interpolator="@android:anim/linear_interpolator" - android:pivotX="50%" - android:pivotY="50%" - android:toXScale="0" - android:toYScale="0" /> -</set> diff --git a/app/src/main/res/anim/scale_up.xml b/app/src/main/res/anim/scale_up.xml deleted file mode 100644 index 1e149e38..00000000 --- a/app/src/main/res/anim/scale_up.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 - --> - -<set xmlns:android="http://schemas.android.com/apk/res/android"> - <scale - android:duration="300" - android:fromXScale="0" - android:fromYScale="0" - android:interpolator="@android:anim/linear_interpolator" - android:pivotX="50%" - android:pivotY="50%" - android:toXScale="1.0" - android:toYScale="1.0" /> -</set> diff --git a/app/src/main/res/layout/activity_pwdstore.xml b/app/src/main/res/layout/activity_pwdstore.xml deleted file mode 100644 index 295efd9c..00000000 --- a/app/src/main/res/layout/activity_pwdstore.xml +++ /dev/null @@ -1,19 +0,0 @@ -<!-- - ~ 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" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - tools:context=".PasswordStore"> - - <LinearLayout - android:id="@+id/main_layout" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" /> - -</LinearLayout> diff --git a/app/src/main/res/layout/folder_dialog_fragment.xml b/app/src/main/res/layout/folder_dialog_fragment.xml deleted file mode 100644 index 7860c9b4..00000000 --- a/app/src/main/res/layout/folder_dialog_fragment.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.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" - android:orientation="vertical" - android:padding="16dp"> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/folder_name_container" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:hint="@string/crypto_name_hint" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/folder_name_text" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:inputType="textNoSuggestions|textVisiblePassword" /> - <requestFocus /> - </com.google.android.material.textfield.TextInputLayout> - - <!-- TODO(msfjarvis): Restore this functionality --> - <com.google.android.material.checkbox.MaterialCheckBox - android:id="@+id/set_gpg_key" - android:layout_width="0dp" - android:visibility="gone" - android:layout_height="wrap_content" - android:text="@string/new_folder_set_gpg_key" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@id/folder_name_container" /> -</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/item_create_sheet.xml b/app/src/main/res/layout/item_create_sheet.xml deleted file mode 100644 index 10882a00..00000000 --- a/app/src/main/res/layout/item_create_sheet.xml +++ /dev/null @@ -1,42 +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.button.MaterialButton - android:id="@+id/create_folder" - style="@style/Widget.MaterialComponents.Button.TextButton" - android:layout_width="match_parent" - android:layout_height="@dimen/bottom_sheet_item_height" - android:layout_margin="@dimen/normal_margin" - android:text="@string/bottom_sheet_create_new_folder" - android:textAlignment="viewStart" - android:textColor="?attr/colorOnSurface" - app:icon="@drawable/ic_action_new_folder" - app:iconPadding="@dimen/normal_margin" - app:iconTint="?attr/colorSecondary" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" - app:rippleColor="?attr/colorSecondary" /> - - <com.google.android.material.button.MaterialButton - android:id="@+id/create_password" - style="@style/Widget.MaterialComponents.Button.TextButton" - android:layout_width="match_parent" - android:layout_height="@dimen/bottom_sheet_item_height" - android:layout_margin="@dimen/normal_margin" - android:text="@string/bottom_sheet_create_new_password" - android:textAlignment="viewStart" - android:textColor="?attr/colorOnSurface" - app:icon="@drawable/ic_action_new_password" - app:iconPadding="@dimen/normal_margin" - app:iconTint="?attr/colorSecondary" - app:layout_constraintTop_toBottomOf="@id/create_folder" - app:rippleColor="?attr/colorSecondary" /> - -</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/password_recycler_view.xml b/app/src/main/res/layout/password_recycler_view.xml index 49b7ff82..c2b5114f 100644 --- a/app/src/main/res/layout/password_recycler_view.xml +++ b/app/src/main/res/layout/password_recycler_view.xml @@ -9,8 +9,7 @@ android:layout_height="match_parent" android:clipChildren="false" android:clipToPadding="false" - android:orientation="vertical" - tools:context="app.passwordstore.ui.passwords.PasswordFragment"> + android:orientation="vertical"> <androidx.swiperefreshlayout.widget.SwipeRefreshLayout android:id="@+id/swipe_refresher" diff --git a/app/src/main/res/menu/context_pass.xml b/app/src/main/res/menu/context_pass.xml deleted file mode 100644 index 608d7eef..00000000 --- a/app/src/main/res/menu/context_pass.xml +++ /dev/null @@ -1,34 +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:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" - tools:context=".pwdstore"> - - <item - android:id="@+id/menu_move_password" - android:icon="@drawable/ic_move_folder_24dp" - android:title="@string/move" - app:showAsAction="ifRoom" /> - - <item - android:id="@+id/menu_delete_password" - android:icon="@drawable/ic_delete_24dp" - android:title="@string/delete" - app:showAsAction="ifRoom" /> - - <item - android:id="@+id/menu_edit_password" - android:icon="@drawable/ic_edit_24dp" - android:title="@string/edit" - app:showAsAction="ifRoom" /> - - <item - android:id="@+id/menu_pin_password" - android:icon="@drawable/ic_push_pin_24dp" - android:title="@string/place_shortcut_on_home_screen" - app:showAsAction="ifRoom" /> -</menu> diff --git a/app/src/main/res/menu/main_menu_git.xml b/app/src/main/res/menu/main_menu_git.xml deleted file mode 100644 index 1b64c083..00000000 --- a/app/src/main/res/menu/main_menu_git.xml +++ /dev/null @@ -1,36 +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:app="http://schemas.android.com/apk/res-auto"> - - <item - android:id="@+id/action_search" - android:icon="@drawable/ic_search_24dp" - android:title="@string/action_search" - app:actionViewClass="androidx.appcompat.widget.SearchView" - app:showAsAction="always|collapseActionView" /> - - <item - android:id="@+id/git_sync" - android:title="@string/git_sync" /> - - <item - android:id="@+id/git_pull" - android:title="@string/git_pull" /> - - <item - android:id="@+id/git_push" - android:title="@string/git_push" /> - - <item - android:id="@+id/refresh" - android:title="@string/refresh_list" /> - - <item - android:id="@+id/user_pref" - android:orderInCategory="100" - android:title="@string/action_settings" /> -</menu> diff --git a/app/src/main/res/menu/main_menu_no_auth.xml b/app/src/main/res/menu/main_menu_no_auth.xml deleted file mode 100644 index 46f19e49..00000000 --- a/app/src/main/res/menu/main_menu_no_auth.xml +++ /dev/null @@ -1,28 +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:app="http://schemas.android.com/apk/res-auto"> - - <item - android:id="@+id/action_search" - android:icon="@drawable/ic_search_24dp" - android:title="@string/action_search" - app:actionViewClass="androidx.appcompat.widget.SearchView" - app:showAsAction="always|collapseActionView" /> - - <item - android:id="@+id/git_pull" - android:title="@string/git_pull" /> - - <item - android:id="@+id/refresh" - android:title="@string/refresh_list" /> - - <item - android:id="@+id/user_pref" - android:orderInCategory="100" - android:title="@string/action_settings" /> -</menu> diff --git a/app/src/main/res/menu/main_menu_non_git.xml b/app/src/main/res/menu/main_menu_non_git.xml deleted file mode 100644 index 5a7cf00b..00000000 --- a/app/src/main/res/menu/main_menu_non_git.xml +++ /dev/null @@ -1,24 +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:app="http://schemas.android.com/apk/res-auto"> - - <item - android:id="@+id/action_search" - android:icon="@drawable/ic_search_24dp" - android:title="@string/action_search" - app:actionViewClass="androidx.appcompat.widget.SearchView" - app:showAsAction="always|collapseActionView" /> - - <item - android:id="@+id/refresh" - android:title="@string/refresh_list" /> - - <item - android:id="@+id/user_pref" - android:orderInCategory="100" - android:title="@string/action_settings" /> -</menu> diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 7499fe5e..d13ab19f 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -24,7 +24,6 @@ </plurals> <string name="delete_directory_progress_text">Wird gelöscht</string> <string name="move">Verschieben</string> - <string name="edit">Bearbeiten</string> <string name="delete">Löschen</string> <string name="password_exists_title">Passwort existiert bereits!</string> <string name="password_exists_message">Dies wird %1$s mit %2$s überschreiben.</string> @@ -167,7 +166,6 @@ <string name="dialog_yes">Ja</string> <string name="dialog_no">Nein</string> <string name="dialog_cancel">Abbruch</string> - <string name="dialog_skip">Überspringen</string> <string name="git_sync">Synchronisiere Repository</string> <string name="git_pull">Git Pull</string> <string name="git_push">Git Push</string> @@ -176,7 +174,6 @@ <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="refresh_list">Liste aktualisieren</string> <string name="send_plaintext_password_to">Passwort unverschlüsselt senden an…</string> <string name="app_icon_hint">App-Icon</string> <!-- Oreo Autofill --> @@ -234,7 +231,6 @@ <string name="title_create_folder">Ordner erstellen</string> <string name="title_rename_folder">Ordner umbenennen</string> <string name="message_category_error_empty_field">Der Name der Kategorie darf nicht leer sein.</string> - <string name="message_category_error_category_exists">Der Kategoriename existiert bereits.</string> <string name="message_error_destination_outside_repo">Ziel muss innerhalb des Repositorys sein.</string> <string name="message_rename_folder">Ziel für %1$s angeben</string> <string name="button_create">Erstellen</string> @@ -281,7 +277,6 @@ <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> - <string name="new_folder_set_gpg_key">PGP-Schlüssel für Ordner festlegen</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> diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 6c8ac709..0cfb345c 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -24,7 +24,6 @@ </plurals> <string name="delete_directory_progress_text">Suppression…</string> <string name="move">Déplacer</string> - <string name="edit">Éditer</string> <string name="delete">Supprimer</string> <string name="password_exists_title">Le mot de passe existe!</string> <string name="password_exists_message">Cela écrasera %1$s avec %2$s.</string> @@ -155,7 +154,6 @@ <string name="dialog_yes">Oui</string> <string name="dialog_no">Non</string> <string name="dialog_cancel">Annuler</string> - <string name="dialog_skip">Ignorer</string> <string name="git_sync">Synchronisation du dépôt</string> <string name="git_pull">Importer du serveur</string> <string name="git_push">Pousser sur le serveur</string> @@ -164,7 +162,6 @@ <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="refresh_list">Rafraichir la liste</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 --> @@ -222,7 +219,6 @@ <string name="title_create_folder">Créer un dossier</string> <string name="title_rename_folder">Renommer le dossier</string> <string name="message_category_error_empty_field">Le nom de la catégorie ne peut pas être vide</string> - <string name="message_category_error_category_exists">Une catégorie du même nom existe déjà</string> <string name="message_error_destination_outside_repo">La destination doit être à l\'intérieur du dépôt</string> <string name="message_rename_folder">Entrez le chemin pour %1$s</string> <string name="button_create">Créer</string> @@ -266,7 +262,6 @@ <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> - <string name="new_folder_set_gpg_key">Définir la clé GPG pour le 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> diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 4ca39712..589208ab 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -24,7 +24,6 @@ </plurals> <string name="delete_directory_progress_text">Eliminando…</string> <string name="move">Mover</string> - <string name="edit">Editar</string> <string name="delete">Eliminar</string> <string name="password_exists_title">O contrasinal xa existe!</string> <string name="password_exists_message">Vas sobrescribir %1$s con %2$s.</string> @@ -168,7 +167,6 @@ <string name="dialog_yes">Si</string> <string name="dialog_no">Non</string> <string name="dialog_cancel">Cancelar</string> - <string name="dialog_skip">Omitir</string> <string name="git_sync">Sincronizar repositorio</string> <string name="git_pull">Traer desde remoto</string> <string name="git_push">Enviar a remoto</string> @@ -177,7 +175,6 @@ <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="refresh_list">Actualizar lista</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 --> @@ -236,7 +233,6 @@ a app desde unha fonte de confianza, como a Play Store, Amazon Appstore, F-Droid <string name="title_create_folder">Crear cartafol</string> <string name="title_rename_folder">Renomear cartafol</string> <string name="message_category_error_empty_field">O nome da categoría non pode estar baleiro</string> - <string name="message_category_error_category_exists">Xa existe o nome da categoría</string> <string name="message_error_destination_outside_repo">O destino debe estar dentro do repositorio</string> <string name="message_rename_folder">Escribe o destino para %1$s</string> <string name="button_create">Crear</string> @@ -283,7 +279,6 @@ a app desde unha fonte de confianza, como a Play Store, Amazon Appstore, F-Droid <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> - <string name="new_folder_set_gpg_key">Establece chave GPG para o 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> diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 95893b17..5ed24efb 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -24,7 +24,6 @@ </plurals> <string name="delete_directory_progress_text">Eliminazione…</string> <string name="move">Sposta</string> - <string name="edit">Modifica</string> <string name="delete">Elimina</string> <string name="password_exists_title">La password esiste già!</string> <string name="password_exists_message">Questo sovrascriverà %1$s con %2$s.</string> @@ -150,7 +149,6 @@ <string name="dialog_yes">Sì</string> <string name="dialog_no">No</string> <string name="dialog_cancel">Annulla</string> - <string name="dialog_skip">Salta</string> <string name="git_sync">Sincronizza repository</string> <string name="git_pull">Pull dal remote</string> <string name="git_push">Push sul remote</string> @@ -159,7 +157,6 @@ <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="refresh_list">Aggiorna elenco</string> <string name="send_plaintext_password_to">Invia password come testo semplice usando…</string> <string name="app_icon_hint">Icona app</string> <!-- Oreo Autofill --> @@ -217,7 +214,6 @@ <string name="title_create_folder">Crea cartella</string> <string name="title_rename_folder">Rinomina cartella</string> <string name="message_category_error_empty_field">Il nome della categoria non può essere vuoto</string> - <string name="message_category_error_category_exists">Il nome della categoria esiste già</string> <string name="message_error_destination_outside_repo">La destinazione deve essere nel repository</string> <string name="message_rename_folder">Inserisci la destinazione per %1$s</string> <string name="button_create">Crea</string> @@ -261,7 +257,6 @@ <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> - <string name="new_folder_set_gpg_key">Imposta la chiave GPG per la 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> diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml index 432b6fbe..7f63ad8b 100644 --- a/app/src/main/res/values-ko-rKR/strings.xml +++ b/app/src/main/res/values-ko-rKR/strings.xml @@ -22,7 +22,6 @@ </plurals> <string name="delete_directory_progress_text">삭제중…</string> <string name="move">이동</string> - <string name="edit">수정</string> <string name="delete">삭제</string> <string name="password_exists_title">이미 추가된 비밀번호입니다!</string> <string name="password_exists_message">%1$s 내용이 %2$s 로 덮어씌워집니다.</string> @@ -163,7 +162,6 @@ <string name="dialog_yes">예</string> <string name="dialog_no">아니오</string> <string name="dialog_cancel">취소</string> - <string name="dialog_skip">건너뛰기</string> <string name="git_sync">저장소 동기화</string> <string name="git_pull">원격에서 pull</string> <string name="git_push">원격으로 푸시</string> @@ -172,7 +170,6 @@ <string name="show_password_pref_title">비밀번호 표시하기</string> <string name="show_password_pref_summary">암호가 해독된 후 비밀번호의 공개 여부를 제어합니다. 그렇다고 해서 클립보드 복사가 비활성화되지는 않습니다.</string> <string name="pwd_generate_button">생성</string> - <string name="refresh_list">목록 새로 고침</string> <string name="send_plaintext_password_to">비밀번호를 일반 텍스트로 보내는 중…</string> <string name="app_icon_hint">앱 아이콘</string> <!-- Oreo Autofill --> @@ -230,7 +227,6 @@ <string name="title_create_folder">폴더 생성</string> <string name="title_rename_folder">폴더 이름 변경</string> <string name="message_category_error_empty_field">카테고리 이름은 비워 둘 수 없습니다</string> - <string name="message_category_error_category_exists">카테고리가 이미 있습니다</string> <string name="message_error_destination_outside_repo">대상은 리포지토리 내에 있어야 합니다</string> <string name="message_rename_folder">%1$s의 목적지 입력</string> <string name="button_create">생성</string> @@ -279,7 +275,6 @@ username@example.com:…</string> <string name="short_gpg_id">.gpg-id를 찾았지만, 지원되지 않는 짧은 16진수 ID가 포함되어 있습니다.</string> <string name="invalid_filename_text">파일 이름에 \'/\'가 포함되어서는 안됩니다. 위의 디렉터리를 설정하세요</string> <string name="directory_hint">디렉토리</string> - <string name="new_folder_set_gpg_key">디렉토리의 GPG 키 설정</string> <!-- GitException messages --> <string name="git_unknown_error">알 수 없는 오류</string> <string name="git_pull_rebase_fail_error">풀이 실패하여 head가 분리된 상태입니다. \"설정 > git 기능\"을 사용하여 원격 저장소에 변경 내용을 새 브랜치에 저장하고 컴퓨터에서 충돌을 해결하세요.</string> diff --git a/app/src/main/res/values-pl-rPL/strings.xml b/app/src/main/res/values-pl-rPL/strings.xml index a915f67d..a2068e65 100644 --- a/app/src/main/res/values-pl-rPL/strings.xml +++ b/app/src/main/res/values-pl-rPL/strings.xml @@ -23,7 +23,6 @@ <string name="creation_dialog_text">Zanim dodasz hasło lub rozpoczniesz synchronizację, stwórz albo sklonuj poniżej nowe repozytorium.</string> <string name="delete_directory_progress_text">Usuwanie…</string> <string name="move">Przenieś</string> - <string name="edit">Edytuj</string> <string name="delete">Usuń</string> <string name="password_exists_title">Hasło już istnieje!</string> <string name="password_exists_message">%1$s zostanie nadpisane przez %2$s.</string> @@ -164,7 +163,6 @@ <string name="dialog_yes">Tak</string> <string name="dialog_no">Nie</string> <string name="dialog_cancel">Anuluj</string> - <string name="dialog_skip">Pomiń</string> <string name="git_sync">Synchronizuj repozytorium</string> <string name="git_pull">Pobierz ze zdalnego repozytorium</string> <string name="git_push">Wypchnij do zdalnego repozytorium</string> @@ -173,7 +171,6 @@ <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="refresh_list">Odśwież listę</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 --> @@ -229,7 +226,6 @@ <string name="title_create_folder">Utwórz katalog</string> <string name="title_rename_folder">Zmień nazwę katalogu</string> <string name="message_category_error_empty_field">Nazwa kategorii nie może być pusta</string> - <string name="message_category_error_category_exists">Kategoria już istnieje</string> <string name="message_error_destination_outside_repo">Miejsce docelowe musi znajdować się w repozytorium</string> <string name="message_rename_folder">Wprowadź miejsce docelowe dla %1$s</string> <string name="button_create">Utwórz</string> @@ -273,7 +269,6 @@ <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> - <string name="new_folder_set_gpg_key">Ustaw klucz GPG dla katalogu</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> diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 1d95286d..909fd33e 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -24,7 +24,6 @@ </plurals> <string name="delete_directory_progress_text">Excluindo…</string> <string name="move">Mover</string> - <string name="edit">Editar</string> <string name="delete">Excluir</string> <string name="password_exists_title">Senha já existe!</string> <string name="password_exists_message">Isso irá sobrescrever %1$s com %2$s.</string> @@ -167,7 +166,6 @@ <string name="dialog_yes">Sim</string> <string name="dialog_no">Não</string> <string name="dialog_cancel">Cancelar</string> - <string name="dialog_skip">Pular</string> <string name="git_sync">Sincronizar repositório</string> <string name="git_pull">Pull do remoto</string> <string name="git_push">Push do remoto</string> @@ -176,7 +174,6 @@ <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="refresh_list">Atualizar lista</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 --> @@ -234,7 +231,6 @@ <string name="title_create_folder">Criar pasta</string> <string name="title_rename_folder">Renomear pasta</string> <string name="message_category_error_empty_field">O nome da categoria não pode ser vazio</string> - <string name="message_category_error_category_exists">Nome da categoria já existe</string> <string name="message_error_destination_outside_repo">O destino deve estar dentro do repositório</string> <string name="message_rename_folder">Insira o destino para %1$s</string> <string name="button_create">Criar</string> @@ -281,7 +277,6 @@ <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> - <string name="new_folder_set_gpg_key">Definir chave GPG para 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> diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index c10bdcd8..2ef59d70 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -28,7 +28,6 @@ </plurals> <string name="delete_directory_progress_text">Удаляется…</string> <string name="move">Переместить</string> - <string name="edit">Редактировать</string> <string name="delete">Удалить</string> <string name="password_exists_title">Пароль уже существует!</string> <string name="password_exists_message">Это перезапишет %1$s на %2$s</string> @@ -160,7 +159,6 @@ <string name="dialog_yes">Да</string> <string name="dialog_no">Нет</string> <string name="dialog_cancel">Отмена</string> - <string name="dialog_skip">Пропустить</string> <string name="git_sync">Синхронизировать репозиторий</string> <string name="git_pull">Пулл с удаленного сервера</string> <string name="git_push">Пуш на удаленный сервер</string> @@ -169,7 +167,6 @@ <string name="show_password_pref_title">Показывать пароли</string> <string name="show_password_pref_summary">Видимость расшифрованных паролей, не влияет на копирование в буфер.</string> <string name="pwd_generate_button">Сгенерировать</string> - <string name="refresh_list">Обновить список</string> <string name="send_plaintext_password_to">Поделиться паролем в открытом виде с помощью</string> <string name="app_icon_hint">Иконка приложения</string> <!-- Oreo Autofill --> @@ -227,7 +224,6 @@ <string name="title_create_folder">Создать папку</string> <string name="title_rename_folder">Переименовать папку</string> <string name="message_category_error_empty_field">Название категории не может быть пустым</string> - <string name="message_category_error_category_exists">Название категории уже существует</string> <string name="message_error_destination_outside_repo">Путь должен указывать на область внутри репозитория</string> <string name="message_rename_folder">Введите адрес назначения для %1$s</string> <string name="button_create">Создать</string> @@ -273,7 +269,6 @@ <string name="invalid_gpg_id">Найден .gpg-id, но он содержит неверный ID ключа, fingerprint или ID пользователя</string> <string name="invalid_filename_text">Имя файла не должно содержать \'/\', укажите директорию выше</string> <string name="directory_hint">Директория</string> - <string name="new_folder_set_gpg_key">Установить GPG ключ для каталога</string> <!-- GitException messages --> <string name="git_unknown_error">Неизвестная ошибка</string> <string name="git_pull_rebase_fail_error">Не удалось получить изменения, вы находитесь в состоянии \"оторванной головы\". Используйте \"настройки > утилиты git\", сохраните ваши изменения в новую удаленную ветку и разрешите конфликты на своем компьютере.</string> diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index b20749b3..55fbeefa 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -23,7 +23,6 @@ </plurals> <string name="delete_directory_progress_text">正在删除…</string> <string name="move">更多</string> - <string name="edit">编辑</string> <string name="delete">删除</string> <string name="password_exists_title">密码已经存在!</string> <string name="password_exists_message">这将用%2$s覆盖%1$s</string> @@ -168,7 +167,6 @@ <string name="dialog_yes">同意</string> <string name="dialog_no">否定</string> <string name="dialog_cancel">取消</string> - <string name="dialog_skip">跳过</string> <string name="git_sync">同步存储库</string> <string name="git_pull">拉取远程数据</string> <string name="git_push">推送数据到远程</string> @@ -177,7 +175,6 @@ <string name="show_password_pref_title">显示密码</string> <string name="show_password_pref_summary">控制密码解密后的可见性,这不会禁用复制到剪贴板</string> <string name="pwd_generate_button">生成</string> - <string name="refresh_list">刷新列表</string> <string name="send_plaintext_password_to">使用明文发送密码…</string> <string name="app_icon_hint">应用图标</string> <!-- Oreo Autofill --> @@ -235,7 +232,6 @@ <string name="title_create_folder">清除目录</string> <string name="title_rename_folder">重命名目录</string> <string name="message_category_error_empty_field">类别名称不能为空</string> - <string name="message_category_error_category_exists">类别名称已存在</string> <string name="message_error_destination_outside_repo">目标必须在存储库中</string> <string name="message_rename_folder">输入%1$s的目标</string> <string name="button_create">创建</string> @@ -283,7 +279,6 @@ personal.com</string> <string name="short_gpg_id">找到 .gpg-id 但它包含一个不受支持的短十六进制ID。</string> <string name="invalid_filename_text">文件名不能包含\'/\'</string> <string name="directory_hint">目录</string> - <string name="new_folder_set_gpg_key">为目录设置GPG密钥</string> <!-- GitException messages --> <string name="git_unknown_error">未知错误</string> <string name="git_pull_merge_fail_error">合并失败,你处于冲突状态。</string> diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index d6939e63..e5d7ff94 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -9,7 +9,6 @@ <dimen name="activity_vertical_margin">16dp</dimen> <dimen name="fab_compat_margin">16dp</dimen> <dimen name="normal_margin">8dp</dimen> - <dimen name="bottom_sheet_item_height">56dp</dimen> <dimen name="onboarding_icon_margin_top">100dp</dimen> <dimen name="onboarding_button_margin_top">140dp</dimen> <dimen name="onboarding_desc_margin_top">48dp</dimen> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b280e9f2..9e402f6b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -30,7 +30,6 @@ </plurals> <string name="delete_directory_progress_text">Deleting…</string> <string name="move">Move</string> - <string name="edit">Edit</string> <string name="delete">Delete</string> <string name="password_exists_title">Password already exists!</string> <string name="password_exists_message">This will overwrite %1$s with %2$s.</string> @@ -195,7 +194,6 @@ <string name="dialog_yes">Yes</string> <string name="dialog_no">No</string> <string name="dialog_cancel">Cancel</string> - <string name="dialog_skip">Skip</string> <string name="git_sync">Synchronize repository</string> <string name="git_pull">Pull from remote</string> <string name="git_push">Push to remote</string> @@ -204,7 +202,6 @@ <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="refresh_list">Refresh list</string> <string name="send_plaintext_password_to">Send password as plaintext using…</string> <string name="app_icon_hint">App icon</string> @@ -267,7 +264,6 @@ <string name="title_create_folder">Create folder</string> <string name="title_rename_folder">Rename folder</string> <string name="message_category_error_empty_field">Category name can\'t be empty</string> - <string name="message_category_error_category_exists">Category name already exists</string> <string name="message_error_destination_outside_repo">Destination must be within the repository</string> <string name="message_rename_folder">Enter destination for %1$s</string> <string name="button_create">Create</string> @@ -316,7 +312,6 @@ <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> - <string name="new_folder_set_gpg_key">Set GPG key for directory</string> <!-- GitException messages --> <string name="git_unknown_error">Unknown error</string> |
