diff options
11 files changed, 14 insertions, 276 deletions
diff --git a/app/src/main/java/app/passwordstore/data/password/PasswordItem.kt b/app/src/main/java/app/passwordstore/data/password/PasswordItem.kt index 765e886d..999ad050 100644 --- a/app/src/main/java/app/passwordstore/data/password/PasswordItem.kt +++ b/app/src/main/java/app/passwordstore/data/password/PasswordItem.kt @@ -10,13 +10,8 @@ import app.passwordstore.ui.crypto.BasePGPActivity import app.passwordstore.ui.main.LaunchActivity import java.io.File -data class PasswordItem( - val name: String, - val parent: PasswordItem? = null, - val type: Char, - val file: File, - val rootDir: File, -) : Comparable<PasswordItem> { +data class PasswordItem(val name: String, val type: Char, val file: File, val rootDir: File) : + Comparable<PasswordItem> { val fullPathToParent = file.absolutePath.replace(rootDir.absolutePath, "").replace(file.name, "") @@ -41,7 +36,6 @@ data class PasswordItem( /** Creates an [Intent] to launch this [PasswordItem] through the authentication process. */ fun createAuthEnabledIntent(context: Context): Intent { val intent = Intent(context, LaunchActivity::class.java) - intent.putExtra("NAME", toString()) intent.putExtra("FILE_PATH", file.absolutePath) intent.action = LaunchActivity.ACTION_DECRYPT_PASS return intent @@ -52,24 +46,12 @@ data class PasswordItem( const val TYPE_CATEGORY = 'c' const val TYPE_PASSWORD = 'p' - @JvmStatic - fun newCategory(name: String, file: File, parent: PasswordItem, rootDir: File): PasswordItem { - return PasswordItem(name, parent, TYPE_CATEGORY, file, rootDir) - } - - @JvmStatic fun newCategory(name: String, file: File, rootDir: File): PasswordItem { - return PasswordItem(name, null, TYPE_CATEGORY, file, rootDir) - } - - @JvmStatic - fun newPassword(name: String, file: File, parent: PasswordItem, rootDir: File): PasswordItem { - return PasswordItem(name, parent, TYPE_PASSWORD, file, rootDir) + return PasswordItem(name, TYPE_CATEGORY, file, rootDir) } - @JvmStatic fun newPassword(name: String, file: File, rootDir: File): PasswordItem { - return PasswordItem(name, null, TYPE_PASSWORD, file, rootDir) + return PasswordItem(name, TYPE_PASSWORD, file, rootDir) } } } diff --git a/app/src/main/java/app/passwordstore/ui/list/ListDestination.kt b/app/src/main/java/app/passwordstore/ui/list/ListDestination.kt index adae3649..a9228484 100644 --- a/app/src/main/java/app/passwordstore/ui/list/ListDestination.kt +++ b/app/src/main/java/app/passwordstore/ui/list/ListDestination.kt @@ -58,10 +58,9 @@ private enum class ListDialog { /** * The list at [path] (relative to the store root): wires [ListScreen] to the [viewModel], the - * folder picker, the legacy activities and the store operations. [onOpenFolder] takes a path - * relative to the root, [onOpenEntry] an entry file; [onNewEntry] opens the editor in this folder, - * [onClone] the git server screen for a clone, [onSettings] the settings; [onNoStore] runs when the - * store is gone. + * folder picker and the store operations. [onOpenFolder] takes a path relative to the root, + * [onOpenEntry] an entry file; [onNewEntry] opens the editor in this folder, [onClone] the git + * server screen for a clone, [onSettings] the settings; [onNoStore] runs when the store is gone. */ @Composable fun ListDestination( @@ -89,7 +88,7 @@ fun ListDestination( // Declared before the collection below on purpose: effects start in order, so the ViewModel // already points at this folder when the first rows are built (no flash of the store root). LaunchedEffect(path, query) { - viewModel.navigateTo(dir, pushPreviousLocation = false) + viewModel.navigateTo(dir) if (query.isNotBlank()) viewModel.search(query.trim()) } val rows by viewModel.rows.collectAsStateWithLifecycle(ListRows()) @@ -100,7 +99,7 @@ fun ListDestination( if (path.isNotEmpty() && !dir.isDirectory) onOpenFolder("") else viewModel.forceRefresh() } - // Every return to this folder (back from a child, or from a legacy activity) re-reads it. + // Every return to this folder (back from a child, or from another activity) re-reads it. LifecycleResumeEffect(Unit) { if (activity.checkStore()) refresh() else onNoStore() resumes++ 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 bad55de4..6528334d 100644 --- a/app/src/main/java/app/passwordstore/util/extensions/AndroidExtensions.kt +++ b/app/src/main/java/app/passwordstore/util/extensions/AndroidExtensions.kt @@ -8,7 +8,6 @@ package app.passwordstore.util.extensions import android.app.KeyguardManager import android.content.ClipboardManager import android.content.Context -import android.content.Intent import android.content.SharedPreferences import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo @@ -17,12 +16,8 @@ import android.content.pm.PackageManager.ApplicationInfoFlags import android.content.pm.PackageManager.PackageInfoFlags import android.os.Build import android.util.Base64 -import android.util.TypedValue import android.view.View import android.view.autofill.AutofillManager -import androidx.activity.ComponentActivity -import androidx.compose.ui.Modifier -import androidx.core.content.ContextCompat import androidx.core.content.getSystemService import androidx.fragment.app.FragmentActivity import app.passwordstore.BuildConfig @@ -56,13 +51,6 @@ val Context.keyguardManager: KeyguardManager val Context.sharedPrefs: SharedPreferences get() = getSharedPreferences("${BuildConfig.APPLICATION_ID}_preferences", 0) -/** Resolve [attr] from the [Context]'s theme */ -fun Context.resolveAttribute(attr: Int): Int { - val typedValue = TypedValue() - this.theme.resolveAttribute(attr, typedValue, true) - return typedValue.data -} - /** * Commit changes to the store from a [FragmentActivity] using a custom implementation of * [GitOperation] @@ -90,11 +78,6 @@ suspend fun FragmentActivity.commitChange(message: String): Result<Unit, Throwab .execute() } -/** Check if [permission] has been granted to the app. */ -fun FragmentActivity.isPermissionGranted(permission: String): Boolean { - return ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED -} - /** Show a [Snackbar] in a [FragmentActivity]. */ fun FragmentActivity.snackbar( view: View = findViewById(android.R.id.content), @@ -106,11 +89,6 @@ fun FragmentActivity.snackbar( return snackbar } -/** Launch an activity denoted by [clazz]. */ -fun <T : ComponentActivity> ComponentActivity.launchActivity(clazz: Class<T>) { - startActivity(Intent(this, clazz).setAction(Intent.ACTION_VIEW)) -} - /** Simplifies the common `getString(key, null) ?: defaultValue` case slightly */ fun SharedPreferences.getString(key: String): String? = getString(key, null) @@ -134,12 +112,3 @@ fun PackageManager.getApplicationInfoCompat(packageName: String, flags: Int): Ap getApplicationInfo(packageName, flags) } } - -/** Allows conditionally applying the given [modifier] if [isEnabled] is `true`. */ -fun Modifier.conditional(isEnabled: Boolean, modifier: Modifier.() -> Modifier): Modifier { - return if (isEnabled) { - then(modifier()) - } else { - this - } -} diff --git a/app/src/main/java/app/passwordstore/util/extensions/FragmentExtensions.kt b/app/src/main/java/app/passwordstore/util/extensions/FragmentExtensions.kt deleted file mode 100644 index 2dd3af0c..00000000 --- a/app/src/main/java/app/passwordstore/util/extensions/FragmentExtensions.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package app.passwordstore.util.extensions - -import androidx.annotation.IdRes -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager -import androidx.fragment.app.commit -import app.passwordstore.R - -/** Check if [permission] is granted to the app. Aliases to [isPermissionGranted] internally. */ -fun Fragment.isPermissionGranted(permission: String): Boolean { - return requireActivity().isPermissionGranted(permission) -} - -/** Calls `finish()` on the enclosing [androidx.fragment.app.FragmentActivity] */ -fun Fragment.finish() = requireActivity().finish() - -/** - * Perform a [commit] on this [FragmentManager] with custom animations and adding the - * [destinationFragment] to the fragment backstack - */ -fun FragmentManager.performTransactionWithBackStack( - destinationFragment: Fragment, - @IdRes containerViewId: Int = android.R.id.content, -) { - commit { - addToBackStack(destinationFragment.tag) - setCustomAnimations( - R.animator.slide_in_left, - R.animator.slide_out_left, - R.animator.slide_in_right, - R.animator.slide_out_right, - ) - replace(containerViewId, destinationFragment) - } -} diff --git a/app/src/main/java/app/passwordstore/util/extensions/FragmentViewBindingDelegate.kt b/app/src/main/java/app/passwordstore/util/extensions/FragmentViewBindingDelegate.kt deleted file mode 100644 index 0050f2e5..00000000 --- a/app/src/main/java/app/passwordstore/util/extensions/FragmentViewBindingDelegate.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package app.passwordstore.util.extensions - -import android.view.LayoutInflater -import android.view.View -import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.Fragment -import androidx.lifecycle.DefaultLifecycleObserver -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleOwner -import androidx.viewbinding.ViewBinding -import kotlin.properties.ReadOnlyProperty -import kotlin.reflect.KProperty - -/** - * Imported from - * https://medium.com/@Zhuinden/simple-one-liner-viewbinding-in-fragments-and-activities-with-kotlin-961430c6c07c - */ -class FragmentViewBindingDelegate<T : ViewBinding>( - val fragment: Fragment, - val viewBindingFactory: (View) -> T, -) : ReadOnlyProperty<Fragment, T> { - - private var binding: T? = null - - init { - fragment.lifecycle.addObserver( - object : DefaultLifecycleObserver { - override fun onCreate(owner: LifecycleOwner) { - fragment.viewLifecycleOwnerLiveData.observe(fragment) { viewLifecycleOwner -> - viewLifecycleOwner.lifecycle.addObserver( - object : DefaultLifecycleObserver { - override fun onDestroy(owner: LifecycleOwner) { - binding = null - } - } - ) - } - } - } - ) - } - - override fun getValue(thisRef: Fragment, property: KProperty<*>): T { - val binding = binding - if (binding != null) { - return binding - } - - val lifecycle = fragment.viewLifecycleOwner.lifecycle - if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) { - throw IllegalStateException( - "Should not attempt to get bindings when Fragment views are destroyed." - ) - } - - return viewBindingFactory(thisRef.requireView()).also { this.binding = it } - } -} - -fun <T : ViewBinding> Fragment.viewBinding(viewBindingFactory: (View) -> T) = - FragmentViewBindingDelegate(this, viewBindingFactory) - -inline fun <T : ViewBinding> AppCompatActivity.viewBinding( - crossinline bindingInflater: (LayoutInflater) -> T -) = unsafeLazy { bindingInflater.invoke(layoutInflater) } diff --git a/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt b/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt index 25ea2cf7..5f35644d 100644 --- a/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt +++ b/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt @@ -6,7 +6,6 @@ package app.passwordstore.util.viewmodel import android.app.Application import android.content.SharedPreferences -import android.os.Parcelable import androidx.annotation.VisibleForTesting import androidx.lifecycle.AndroidViewModel import app.passwordstore.data.password.PasswordItem @@ -68,9 +67,6 @@ private fun PasswordItem.Companion.makeComparator( ) } -val PasswordItem.stableId: String - get() = file.absolutePath - enum class FilterMode { NoFilter, StrictDomain, @@ -85,7 +81,6 @@ enum class SearchMode { enum class ListMode { FilesOnly, - DirectoriesOnly, AllEntries, } @@ -171,7 +166,7 @@ constructor( ) ) - data class SearchResult(val passwordItems: List<PasswordItem>, val isFiltered: Boolean) + data class SearchResult(val passwordItems: List<PasswordItem>) val searchResult = searchActionFlow @@ -187,8 +182,6 @@ constructor( when (searchAction.listMode) { ListMode.FilesOnly -> listResultFlow.filter { it.isFile }.flowOn(dispatcherProvider.io()) - ListMode.DirectoriesOnly -> - listResultFlow.filter { it.isDirectory }.flowOn(dispatcherProvider.io()) ListMode.AllEntries -> listResultFlow } val passwordList = @@ -244,7 +237,7 @@ constructor( .map { it.second } } } - SearchResult(passwordList, isFiltered = searchAction.filterMode != FilterMode.NoFilter) + SearchResult(passwordList) } .flowOn(dispatcherProvider.io()) @@ -288,55 +281,22 @@ constructor( .map { result -> listRows(result.passwordItems.map { it.file }, currentDir.value) } .flowOn(dispatcherProvider.io()) - data class NavigationStackEntry(val dir: File, val recyclerViewState: Parcelable?) - - private val navigationStack = ArrayDeque<NavigationStackEntry>() - - fun navigateTo( - newDirectory: File = root, - listMode: ListMode = ListMode.AllEntries, - recyclerViewState: Parcelable? = null, - pushPreviousLocation: Boolean = true, - ) { + /** Lists [newDirectory]; the Compose list keeps the back stack (one route per folder). */ + fun navigateTo(newDirectory: File) { if (!newDirectory.exists()) return require(newDirectory.isDirectory) { "Can only navigate to a directory" } - if (pushPreviousLocation) { - navigationStack.addFirst(NavigationStackEntry(_currentDir.value, recyclerViewState)) - } searchActionFlow.update { makeSearchAction( filter = "", baseDirectory = newDirectory, filterMode = FilterMode.NoFilter, searchMode = SearchMode.InCurrentDirectoryOnly, - listMode = listMode, + listMode = ListMode.AllEntries, ) } _currentDir.update { newDirectory } } - val canNavigateBack - get() = navigationStack.isNotEmpty() - - /** - * Navigate back to the last location on the [navigationStack] and restore a cached scroll - * position if possible. - * - * Returns the old RecyclerView's LinearLayoutManager state as a [Parcelable] if it was cached. - */ - fun navigateBack(): Parcelable? { - if (!canNavigateBack) return null - val (oldDir, oldRecyclerViewState) = navigationStack.removeFirst() - navigateTo(oldDir, pushPreviousLocation = false) - return oldRecyclerViewState - } - - fun reset() { - navigationStack.clear() - forceUpdateOnNextSearchAction() - navigateTo(pushPreviousLocation = false) - } - fun search( filter: String, baseDirectory: File? = null, diff --git a/app/src/main/res/animator/slide_in_left.xml b/app/src/main/res/animator/slide_in_left.xml deleted file mode 100644 index 355aeefb..00000000 --- a/app/src/main/res/animator/slide_in_left.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 - --> - -<set xmlns:android="http://schemas.android.com/apk/res/android"> - - <objectAnimator - android:duration="250" - android:propertyName="x" - android:valueFrom="1000" - android:valueTo="0" - android:valueType="floatType" /> - -</set> diff --git a/app/src/main/res/animator/slide_in_right.xml b/app/src/main/res/animator/slide_in_right.xml deleted file mode 100644 index 685bda4d..00000000 --- a/app/src/main/res/animator/slide_in_right.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 - --> - -<set xmlns:android="http://schemas.android.com/apk/res/android"> - - <objectAnimator - android:duration="250" - android:propertyName="x" - android:valueFrom="-1000" - android:valueTo="0" - android:valueType="floatType" /> - -</set> diff --git a/app/src/main/res/animator/slide_out_left.xml b/app/src/main/res/animator/slide_out_left.xml deleted file mode 100644 index 06c3f076..00000000 --- a/app/src/main/res/animator/slide_out_left.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 - --> - -<set xmlns:android="http://schemas.android.com/apk/res/android"> - - <objectAnimator - android:duration="250" - android:propertyName="x" - android:valueFrom="0" - android:valueTo="-1000" - android:valueType="floatType" /> - -</set> diff --git a/app/src/main/res/animator/slide_out_right.xml b/app/src/main/res/animator/slide_out_right.xml deleted file mode 100644 index e5c61bdc..00000000 --- a/app/src/main/res/animator/slide_out_right.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 - --> - -<set xmlns:android="http://schemas.android.com/apk/res/android"> - - <objectAnimator - android:duration="250" - android:propertyName="x" - android:valueFrom="0" - android:valueTo="1000" - android:valueType="floatType" /> - -</set> diff --git a/sentry.properties b/sentry.properties deleted file mode 100644 index 47a8fadc..00000000 --- a/sentry.properties +++ /dev/null @@ -1,2 +0,0 @@ -defaults.project=password-store-google-play -defaults.org=password-store |
