aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java')
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/UserPreference.kt4
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/BaseGitActivity.kt4
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/config/GitSettings.kt4
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/operation/CredentialFinder.kt4
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/sshj/SshKey.kt4
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/sshkeygen/SshKeyGenActivity.kt4
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/utils/AndroidExtensions.kt172
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt151
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/utils/FragmentExtensions.kt40
9 files changed, 221 insertions, 166 deletions
diff --git a/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt b/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt
index 7aeec148..62ce8b46 100644
--- a/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt
@@ -54,7 +54,7 @@ import com.zeapo.pwdstore.utils.BiometricAuthenticator
import com.zeapo.pwdstore.utils.PasswordRepository
import com.zeapo.pwdstore.utils.PreferenceKeys
import com.zeapo.pwdstore.utils.autofillManager
-import com.zeapo.pwdstore.utils.getEncryptedPrefs
+import com.zeapo.pwdstore.utils.getEncryptedGitPrefs
import com.zeapo.pwdstore.utils.getString
import com.zeapo.pwdstore.utils.sharedPrefs
import java.io.File
@@ -81,7 +81,7 @@ class UserPreference : AppCompatActivity() {
prefsActivity = requireActivity() as UserPreference
val context = requireContext()
sharedPreferences = preferenceManager.sharedPreferences
- encryptedPreferences = requireActivity().getEncryptedPrefs("git_operation")
+ encryptedPreferences = requireActivity().getEncryptedGitPrefs()
addPreferencesFromResource(R.xml.preference)
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/BaseGitActivity.kt b/app/src/main/java/com/zeapo/pwdstore/git/BaseGitActivity.kt
index 6a975f6c..b0aed087 100644
--- a/app/src/main/java/com/zeapo/pwdstore/git/BaseGitActivity.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/git/BaseGitActivity.kt
@@ -21,7 +21,7 @@ import com.zeapo.pwdstore.git.operation.PushOperation
import com.zeapo.pwdstore.git.operation.ResetToRemoteOperation
import com.zeapo.pwdstore.git.operation.SyncOperation
import com.zeapo.pwdstore.utils.PreferenceKeys
-import com.zeapo.pwdstore.utils.getEncryptedPrefs
+import com.zeapo.pwdstore.utils.getEncryptedGitPrefs
import com.zeapo.pwdstore.utils.sharedPrefs
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@@ -86,7 +86,7 @@ abstract class BaseGitActivity : AppCompatActivity() {
suspend fun promptOnErrorHandler(err: Throwable, onPromptDone: () -> Unit = {}) {
val error = rootCauseException(err)
if (!isExplicitlyUserInitiatedError(error)) {
- getEncryptedPrefs("git_operation").edit {
+ getEncryptedGitPrefs().edit {
remove(PreferenceKeys.HTTPS_PASSWORD)
}
sharedPrefs.edit { remove(PreferenceKeys.SSH_OPENKEYSTORE_KEYID) }
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/config/GitSettings.kt b/app/src/main/java/com/zeapo/pwdstore/git/config/GitSettings.kt
index f4cbc04f..e43deaf4 100644
--- a/app/src/main/java/com/zeapo/pwdstore/git/config/GitSettings.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/git/config/GitSettings.kt
@@ -10,7 +10,7 @@ import com.github.michaelbull.result.runCatching
import com.zeapo.pwdstore.Application
import com.zeapo.pwdstore.utils.PasswordRepository
import com.zeapo.pwdstore.utils.PreferenceKeys
-import com.zeapo.pwdstore.utils.getEncryptedPrefs
+import com.zeapo.pwdstore.utils.getEncryptedGitPrefs
import com.zeapo.pwdstore.utils.getString
import com.zeapo.pwdstore.utils.sharedPrefs
import java.io.File
@@ -53,7 +53,7 @@ object GitSettings {
private const val DEFAULT_BRANCH = "master"
private val settings by lazy { Application.instance.sharedPrefs }
- private val encryptedSettings by lazy { Application.instance.getEncryptedPrefs("git_operation") }
+ private val encryptedSettings by lazy { Application.instance.getEncryptedGitPrefs() }
var authMode
get() = AuthMode.fromString(settings.getString(PreferenceKeys.GIT_REMOTE_AUTH))
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/operation/CredentialFinder.kt b/app/src/main/java/com/zeapo/pwdstore/git/operation/CredentialFinder.kt
index 742361c0..c34af18a 100644
--- a/app/src/main/java/com/zeapo/pwdstore/git/operation/CredentialFinder.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/git/operation/CredentialFinder.kt
@@ -14,7 +14,7 @@ import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.git.config.AuthMode
import com.zeapo.pwdstore.git.sshj.InteractivePasswordFinder
import com.zeapo.pwdstore.utils.PreferenceKeys
-import com.zeapo.pwdstore.utils.getEncryptedPrefs
+import com.zeapo.pwdstore.utils.getEncryptedGitPrefs
import com.zeapo.pwdstore.utils.requestInputFocusOnView
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
@@ -25,7 +25,7 @@ class CredentialFinder(
) : InteractivePasswordFinder() {
override fun askForPassword(cont: Continuation<String?>, isRetry: Boolean) {
- val gitOperationPrefs = callingActivity.getEncryptedPrefs("git_operation")
+ val gitOperationPrefs = callingActivity.getEncryptedGitPrefs()
val credentialPref: String
@StringRes val messageRes: Int
@StringRes val hintRes: Int
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/sshj/SshKey.kt b/app/src/main/java/com/zeapo/pwdstore/git/sshj/SshKey.kt
index ce09f1a9..73072f7d 100644
--- a/app/src/main/java/com/zeapo/pwdstore/git/sshj/SshKey.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/git/sshj/SshKey.kt
@@ -23,7 +23,7 @@ import com.github.michaelbull.result.runCatching
import com.zeapo.pwdstore.Application
import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.utils.PreferenceKeys
-import com.zeapo.pwdstore.utils.getEncryptedPrefs
+import com.zeapo.pwdstore.utils.getEncryptedGitPrefs
import com.zeapo.pwdstore.utils.getString
import com.zeapo.pwdstore.utils.sharedPrefs
import java.io.File
@@ -169,7 +169,7 @@ object SshKey {
if (publicKeyFile.isFile) {
publicKeyFile.delete()
}
- context.getEncryptedPrefs("git_operation").edit {
+ context.getEncryptedGitPrefs().edit {
remove(PreferenceKeys.SSH_KEY_LOCAL_PASSPHRASE)
}
type = null
diff --git a/app/src/main/java/com/zeapo/pwdstore/sshkeygen/SshKeyGenActivity.kt b/app/src/main/java/com/zeapo/pwdstore/sshkeygen/SshKeyGenActivity.kt
index c7277455..d2fdab61 100644
--- a/app/src/main/java/com/zeapo/pwdstore/sshkeygen/SshKeyGenActivity.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/sshkeygen/SshKeyGenActivity.kt
@@ -20,7 +20,7 @@ import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.databinding.ActivitySshKeygenBinding
import com.zeapo.pwdstore.git.sshj.SshKey
import com.zeapo.pwdstore.utils.BiometricAuthenticator
-import com.zeapo.pwdstore.utils.getEncryptedPrefs
+import com.zeapo.pwdstore.utils.getEncryptedGitPrefs
import com.zeapo.pwdstore.utils.keyguardManager
import com.zeapo.pwdstore.utils.viewBinding
import kotlin.coroutines.resume
@@ -128,7 +128,7 @@ class SshKeyGenActivity : AppCompatActivity() {
keyGenType.generateKey(requireAuthentication)
}
}
- getEncryptedPrefs("git_operation").edit {
+ getEncryptedGitPrefs().edit {
remove("ssh_key_local_passphrase")
}
binding.generate.apply {
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/AndroidExtensions.kt b/app/src/main/java/com/zeapo/pwdstore/utils/AndroidExtensions.kt
new file mode 100644
index 00000000..3ee1820d
--- /dev/null
+++ b/app/src/main/java/com/zeapo/pwdstore/utils/AndroidExtensions.kt
@@ -0,0 +1,172 @@
+/*
+ * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package com.zeapo.pwdstore.utils
+
+import android.app.KeyguardManager
+import android.content.ClipboardManager
+import android.content.Context
+import android.content.SharedPreferences
+import android.content.pm.PackageManager
+import android.os.Build
+import android.util.Base64
+import android.util.TypedValue
+import android.view.View
+import android.view.autofill.AutofillManager
+import android.view.inputmethod.InputMethodManager
+import androidx.annotation.IdRes
+import androidx.annotation.RequiresApi
+import androidx.appcompat.app.AlertDialog
+import androidx.core.content.ContextCompat
+import androidx.core.content.getSystemService
+import androidx.fragment.app.FragmentActivity
+import androidx.preference.PreferenceManager
+import androidx.security.crypto.EncryptedSharedPreferences
+import androidx.security.crypto.MasterKey
+import com.github.ajalt.timberkt.d
+import com.github.michaelbull.result.Ok
+import com.github.michaelbull.result.Result
+import com.google.android.material.snackbar.Snackbar
+import com.zeapo.pwdstore.R
+import com.zeapo.pwdstore.git.operation.GitOperation
+
+/**
+ * Extension function for [AlertDialog] that requests focus for the
+ * view whose id is [id]. Solution based on a StackOverflow
+ * answer: https://stackoverflow.com/a/13056259/297261
+ */
+fun <T : View> AlertDialog.requestInputFocusOnView(@IdRes id: Int) {
+ setOnShowListener {
+ findViewById<T>(id)?.apply {
+ setOnFocusChangeListener { v, _ ->
+ v.post {
+ context.getSystemService<InputMethodManager>()
+ ?.showSoftInput(v, InputMethodManager.SHOW_IMPLICIT)
+ }
+ }
+ requestFocus()
+ }
+ }
+}
+
+/**
+ * Get an instance of [AutofillManager]. Only
+ * available on Android Oreo and above
+ */
+val Context.autofillManager: AutofillManager?
+ @RequiresApi(Build.VERSION_CODES.O)
+ get() = getSystemService()
+
+/**
+ * Get an instance of [ClipboardManager]
+ */
+val Context.clipboard
+ get() = getSystemService<ClipboardManager>()
+
+/**
+ * Wrapper for [getEncryptedPrefs] to avoid open-coding the file name at
+ * each call site
+ */
+fun Context.getEncryptedGitPrefs() = getEncryptedPrefs("git_operation")
+
+/**
+ * Get an instance of [EncryptedSharedPreferences] with the given [fileName]
+ */
+private fun Context.getEncryptedPrefs(fileName: String): SharedPreferences {
+ val masterKeyAlias = MasterKey.Builder(applicationContext)
+ .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
+ .build()
+ return EncryptedSharedPreferences.create(
+ applicationContext,
+ fileName,
+ masterKeyAlias,
+ EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
+ EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
+ )
+}
+
+/**
+ * Get an instance of [KeyguardManager]
+ */
+val Context.keyguardManager: KeyguardManager
+ get() = getSystemService()!!
+
+/**
+ * Get the default [SharedPreferences] instance
+ */
+val Context.sharedPrefs: SharedPreferences
+ get() = PreferenceManager.getDefaultSharedPreferences(applicationContext)
+
+
+/**
+ * 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]
+ */
+suspend fun FragmentActivity.commitChange(
+ message: String,
+): Result<Unit, Throwable> {
+ if (!PasswordRepository.isGitRepo()) {
+ return Ok(Unit)
+ }
+ return object : GitOperation(this@commitChange) {
+ override val commands = arrayOf(
+ // Stage all files
+ git.add().addFilepattern("."),
+ // Populate the changed files count
+ git.status(),
+ // Commit everything! If anything changed, that is.
+ git.commit().setAll(true).setMessage(message),
+ )
+
+ override fun preExecute(): Boolean {
+ d { "Committing with message: '$message'" }
+ return true
+ }
+ }.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] and correctly
+ * anchor it to a [com.google.android.material.floatingactionbutton.FloatingActionButton]
+ * if one exists in the [view]
+ */
+fun FragmentActivity.snackbar(
+ view: View = findViewById(android.R.id.content),
+ message: String,
+ length: Int = Snackbar.LENGTH_SHORT,
+): Snackbar {
+ val snackbar = Snackbar.make(view, message, length)
+ snackbar.anchorView = findViewById(R.id.fab)
+ snackbar.show()
+ return snackbar
+}
+
+/**
+ * Simplifies the common `getString(key, null) ?: defaultValue` case slightly
+ */
+fun SharedPreferences.getString(key: String): String? = getString(key, null)
+
+/**
+ * Convert this [String] to its [Base64] representation
+ */
+fun String.base64(): String {
+ return Base64.encodeToString(encodeToByteArray(), Base64.NO_WRAP)
+}
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt b/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt
index 2a11f56a..10a29cf0 100644
--- a/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt
@@ -4,74 +4,33 @@
*/
package com.zeapo.pwdstore.utils
-import android.app.KeyguardManager
-import android.content.ClipboardManager
-import android.content.Context
-import android.content.SharedPreferences
-import android.content.pm.PackageManager
-import android.os.Build
-import android.util.Base64
-import android.util.TypedValue
-import android.view.View
-import android.view.autofill.AutofillManager
-import android.view.inputmethod.InputMethodManager
-import androidx.annotation.IdRes
-import androidx.annotation.RequiresApi
-import androidx.appcompat.app.AlertDialog
-import androidx.core.content.ContextCompat
-import androidx.core.content.getSystemService
-import androidx.fragment.app.FragmentActivity
-import androidx.preference.PreferenceManager
-import androidx.security.crypto.EncryptedSharedPreferences
-import androidx.security.crypto.MasterKey
-import com.github.ajalt.timberkt.d
-import com.github.michaelbull.result.Ok
-import com.github.michaelbull.result.Result
import com.github.michaelbull.result.getOrElse
import com.github.michaelbull.result.runCatching
-import com.google.android.material.snackbar.Snackbar
-import com.zeapo.pwdstore.R
-import com.zeapo.pwdstore.git.operation.GitOperation
import com.zeapo.pwdstore.utils.PasswordRepository.Companion.getRepositoryDirectory
import java.io.File
import java.util.Date
import org.eclipse.jgit.lib.ObjectId
import org.eclipse.jgit.revwalk.RevCommit
+/**
+ * The default OpenPGP provider for the app
+ */
const val OPENPGP_PROVIDER = "org.sufficientlysecure.keychain"
+/**
+ * Clears the given [flag] from the value of this [Int]
+ */
fun Int.clearFlag(flag: Int): Int {
return this and flag.inv()
}
+/**
+ * Checks if this [Int] contains the given [flag]
+ */
infix fun Int.hasFlag(flag: Int): Boolean {
return this and flag == flag
}
-fun String.splitLines(): Array<String> {
- return split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
-}
-
-fun String.base64(): String {
- return Base64.encodeToString(encodeToByteArray(), Base64.NO_WRAP)
-}
-
-val Context.clipboard
- get() = getSystemService<ClipboardManager>()
-
-fun FragmentActivity.snackbar(
- view: View = findViewById(android.R.id.content),
- message: String,
- length: Int = Snackbar.LENGTH_SHORT,
-): Snackbar {
- val snackbar = Snackbar.make(view, message, length)
- snackbar.anchorView = findViewById(R.id.fab)
- snackbar.show()
- return snackbar
-}
-
-fun File.listFilesRecursively() = walkTopDown().filter { !it.isDirectory }.toList()
-
/**
* Checks whether this [File] is a directory that contains [other].
*/
@@ -89,91 +48,23 @@ fun File.contains(other: File): Boolean {
return relativePath.path == other.name
}
-fun Context.resolveAttribute(attr: Int): Int {
- val typedValue = TypedValue()
- this.theme.resolveAttribute(attr, typedValue, true)
- return typedValue.data
-}
-
-fun Context.getEncryptedPrefs(fileName: String): SharedPreferences {
- val masterKeyAlias = MasterKey.Builder(applicationContext)
- .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
- .build()
- return EncryptedSharedPreferences.create(
- applicationContext,
- fileName,
- masterKeyAlias,
- EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
- EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
- )
-}
-
-val Context.sharedPrefs: SharedPreferences
- get() = PreferenceManager.getDefaultSharedPreferences(applicationContext)
-
-fun SharedPreferences.getString(key: String): String? = getString(key, null)
-
-suspend fun FragmentActivity.commitChange(
- message: String,
-): Result<Unit, Throwable> {
- if (!PasswordRepository.isGitRepo()) {
- return Ok(Unit)
- }
- return object : GitOperation(this@commitChange) {
- override val commands = arrayOf(
- // Stage all files
- git.add().addFilepattern("."),
- // Populate the changed files count
- git.status(),
- // Commit everything! If anything changed, that is.
- git.commit().setAll(true).setMessage(message),
- )
-
- override fun preExecute(): Boolean {
- d { "Committing with message: '$message'" }
- return true
- }
- }.execute()
-}
-
-fun FragmentActivity.isPermissionGranted(permission: String): Boolean {
- return ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
-}
-
/**
- * Extension function for [AlertDialog] that requests focus for the
- * view whose id is [id]. Solution based on a StackOverflow
- * answer: https://stackoverflow.com/a/13056259/297261
+ * Checks if this [File] is in the password repository directory as given
+ * by [getRepositoryDirectory]
*/
-fun <T : View> AlertDialog.requestInputFocusOnView(@IdRes id: Int) {
- setOnShowListener {
- findViewById<T>(id)?.apply {
- setOnFocusChangeListener { v, _ ->
- v.post {
- context.getSystemService<InputMethodManager>()
- ?.showSoftInput(v, InputMethodManager.SHOW_IMPLICIT)
- }
- }
- requestFocus()
- }
- }
-}
-
-val Context.autofillManager: AutofillManager?
- @RequiresApi(Build.VERSION_CODES.O)
- get() = getSystemService()
-
-val Context.keyguardManager: KeyguardManager
- get() = getSystemService()!!
-
fun File.isInsideRepository(): Boolean {
return canonicalPath.contains(getRepositoryDirectory().canonicalPath)
}
/**
+ * Recursively lists the files in this [File], skipping any directories it encounters.
+ */
+fun File.listFilesRecursively() = walkTopDown().filter { !it.isDirectory }.toList()
+
+/**
* Unique SHA-1 hash of this commit as hexadecimal string.
*
- * @see RevCommit.id
+ * @see RevCommit.getId
*/
val RevCommit.hash: String
get() = ObjectId.toString(id)
@@ -189,3 +80,11 @@ val RevCommit.time: Date
val epochMilliseconds = epochSeconds * 1000
return Date(epochMilliseconds)
}
+
+/**
+ * Splits this [String] into an [Array] of [String]s, split on the UNIX LF line ending
+ * and stripped of any empty lines.
+ */
+fun String.splitLines(): Array<String> {
+ return split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+}
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/FragmentExtensions.kt b/app/src/main/java/com/zeapo/pwdstore/utils/FragmentExtensions.kt
index 01103d19..251f5259 100644
--- a/app/src/main/java/com/zeapo/pwdstore/utils/FragmentExtensions.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/utils/FragmentExtensions.kt
@@ -1,34 +1,29 @@
package com.zeapo.pwdstore.utils
-import android.content.pm.PackageManager
-import android.view.View
import androidx.annotation.IdRes
-import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.commit
import com.zeapo.pwdstore.R
+/**
+ * Check if [permission] is granted to the app. Aliases to [isPermissionGranted] internally.
+ */
fun Fragment.isPermissionGranted(permission: String): Boolean {
- return ContextCompat.checkSelfPermission(requireActivity(), permission) == PackageManager.PERMISSION_GRANTED
+ return requireActivity().isPermissionGranted(permission)
}
+/**
+ * Calls `finish()` on the enclosing [androidx.fragment.app.FragmentActivity]
+ */
fun Fragment.finish() = requireActivity().finish()
-fun FragmentManager.performTransaction(destinationFragment: Fragment, @IdRes containerViewId: Int = android.R.id.content) {
- this.commit {
- beginTransaction()
- setCustomAnimations(
- R.animator.slide_in_left,
- R.animator.slide_out_left,
- R.animator.slide_in_right,
- R.animator.slide_out_right)
- replace(containerViewId, destinationFragment)
- }
-}
-
+/**
+ * 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) {
- this.commit {
+ commit {
beginTransaction()
addToBackStack(destinationFragment.tag)
setCustomAnimations(
@@ -39,14 +34,3 @@ fun FragmentManager.performTransactionWithBackStack(destinationFragment: Fragmen
replace(containerViewId, destinationFragment)
}
}
-
-fun FragmentManager.performSharedElementTransaction(destinationFragment: Fragment, views: List<View>, @IdRes containerViewId: Int = android.R.id.content) {
- this.commit {
- beginTransaction()
- for (view in views) {
- addSharedElement(view, view.transitionName)
- }
- addToBackStack(destinationFragment.tag)
- replace(containerViewId, destinationFragment)
- }
-}