Misc cleanups to build and extension functions (#1108)
This commit is contained in:
parent
9d63b11391
commit
bad8e2b404
11 changed files with 221 additions and 172 deletions
|
@ -141,9 +141,6 @@ dependencies {
|
|||
androidTestImplementation(Dependencies.Testing.kotlin_test_junit)
|
||||
androidTestImplementation(Dependencies.Testing.AndroidX.runner)
|
||||
androidTestImplementation(Dependencies.Testing.AndroidX.rules)
|
||||
androidTestImplementation(Dependencies.Testing.AndroidX.junit)
|
||||
androidTestImplementation(Dependencies.Testing.AndroidX.espresso_core)
|
||||
androidTestImplementation(Dependencies.Testing.AndroidX.espresso_intents)
|
||||
|
||||
testImplementation(Dependencies.Testing.junit)
|
||||
testImplementation(Dependencies.Testing.kotlin_test_junit)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
172
app/src/main/java/com/zeapo/pwdstore/utils/AndroidExtensions.kt
Normal file
172
app/src/main/java/com/zeapo/pwdstore/utils/AndroidExtensions.kt
Normal file
|
@ -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)
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,9 +83,6 @@ object Dependencies {
|
|||
|
||||
const val runner = "androidx.test:runner:1.3.0"
|
||||
const val rules = "androidx.test:rules:1.3.0"
|
||||
const val junit = "androidx.test.ext:junit:1.1.2"
|
||||
const val espresso_core = "androidx.test.espresso:espresso-core:3.3.0"
|
||||
const val espresso_intents = "androidx.test.espresso:espresso-intents:3.3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue