Misc cleanups to build and extension functions (#1108)

This commit is contained in:
Harsh Shandilya 2020-09-18 18:14:52 +05:30 committed by GitHub
parent 9d63b11391
commit bad8e2b404
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 221 additions and 172 deletions

View file

@ -141,9 +141,6 @@ dependencies {
androidTestImplementation(Dependencies.Testing.kotlin_test_junit) androidTestImplementation(Dependencies.Testing.kotlin_test_junit)
androidTestImplementation(Dependencies.Testing.AndroidX.runner) androidTestImplementation(Dependencies.Testing.AndroidX.runner)
androidTestImplementation(Dependencies.Testing.AndroidX.rules) 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.junit)
testImplementation(Dependencies.Testing.kotlin_test_junit) testImplementation(Dependencies.Testing.kotlin_test_junit)

View file

@ -54,7 +54,7 @@ import com.zeapo.pwdstore.utils.BiometricAuthenticator
import com.zeapo.pwdstore.utils.PasswordRepository import com.zeapo.pwdstore.utils.PasswordRepository
import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.PreferenceKeys
import com.zeapo.pwdstore.utils.autofillManager 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.getString
import com.zeapo.pwdstore.utils.sharedPrefs import com.zeapo.pwdstore.utils.sharedPrefs
import java.io.File import java.io.File
@ -81,7 +81,7 @@ class UserPreference : AppCompatActivity() {
prefsActivity = requireActivity() as UserPreference prefsActivity = requireActivity() as UserPreference
val context = requireContext() val context = requireContext()
sharedPreferences = preferenceManager.sharedPreferences sharedPreferences = preferenceManager.sharedPreferences
encryptedPreferences = requireActivity().getEncryptedPrefs("git_operation") encryptedPreferences = requireActivity().getEncryptedGitPrefs()
addPreferencesFromResource(R.xml.preference) addPreferencesFromResource(R.xml.preference)

View file

@ -21,7 +21,7 @@ import com.zeapo.pwdstore.git.operation.PushOperation
import com.zeapo.pwdstore.git.operation.ResetToRemoteOperation import com.zeapo.pwdstore.git.operation.ResetToRemoteOperation
import com.zeapo.pwdstore.git.operation.SyncOperation import com.zeapo.pwdstore.git.operation.SyncOperation
import com.zeapo.pwdstore.utils.PreferenceKeys 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 com.zeapo.pwdstore.utils.sharedPrefs
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -86,7 +86,7 @@ abstract class BaseGitActivity : AppCompatActivity() {
suspend fun promptOnErrorHandler(err: Throwable, onPromptDone: () -> Unit = {}) { suspend fun promptOnErrorHandler(err: Throwable, onPromptDone: () -> Unit = {}) {
val error = rootCauseException(err) val error = rootCauseException(err)
if (!isExplicitlyUserInitiatedError(error)) { if (!isExplicitlyUserInitiatedError(error)) {
getEncryptedPrefs("git_operation").edit { getEncryptedGitPrefs().edit {
remove(PreferenceKeys.HTTPS_PASSWORD) remove(PreferenceKeys.HTTPS_PASSWORD)
} }
sharedPrefs.edit { remove(PreferenceKeys.SSH_OPENKEYSTORE_KEYID) } sharedPrefs.edit { remove(PreferenceKeys.SSH_OPENKEYSTORE_KEYID) }

View file

@ -10,7 +10,7 @@ import com.github.michaelbull.result.runCatching
import com.zeapo.pwdstore.Application import com.zeapo.pwdstore.Application
import com.zeapo.pwdstore.utils.PasswordRepository import com.zeapo.pwdstore.utils.PasswordRepository
import com.zeapo.pwdstore.utils.PreferenceKeys 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.getString
import com.zeapo.pwdstore.utils.sharedPrefs import com.zeapo.pwdstore.utils.sharedPrefs
import java.io.File import java.io.File
@ -53,7 +53,7 @@ object GitSettings {
private const val DEFAULT_BRANCH = "master" private const val DEFAULT_BRANCH = "master"
private val settings by lazy { Application.instance.sharedPrefs } 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 var authMode
get() = AuthMode.fromString(settings.getString(PreferenceKeys.GIT_REMOTE_AUTH)) get() = AuthMode.fromString(settings.getString(PreferenceKeys.GIT_REMOTE_AUTH))

View file

@ -14,7 +14,7 @@ import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.git.config.AuthMode import com.zeapo.pwdstore.git.config.AuthMode
import com.zeapo.pwdstore.git.sshj.InteractivePasswordFinder import com.zeapo.pwdstore.git.sshj.InteractivePasswordFinder
import com.zeapo.pwdstore.utils.PreferenceKeys 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 com.zeapo.pwdstore.utils.requestInputFocusOnView
import kotlin.coroutines.Continuation import kotlin.coroutines.Continuation
import kotlin.coroutines.resume import kotlin.coroutines.resume
@ -25,7 +25,7 @@ class CredentialFinder(
) : InteractivePasswordFinder() { ) : InteractivePasswordFinder() {
override fun askForPassword(cont: Continuation<String?>, isRetry: Boolean) { override fun askForPassword(cont: Continuation<String?>, isRetry: Boolean) {
val gitOperationPrefs = callingActivity.getEncryptedPrefs("git_operation") val gitOperationPrefs = callingActivity.getEncryptedGitPrefs()
val credentialPref: String val credentialPref: String
@StringRes val messageRes: Int @StringRes val messageRes: Int
@StringRes val hintRes: Int @StringRes val hintRes: Int

View file

@ -23,7 +23,7 @@ import com.github.michaelbull.result.runCatching
import com.zeapo.pwdstore.Application import com.zeapo.pwdstore.Application
import com.zeapo.pwdstore.R import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.utils.PreferenceKeys 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.getString
import com.zeapo.pwdstore.utils.sharedPrefs import com.zeapo.pwdstore.utils.sharedPrefs
import java.io.File import java.io.File
@ -169,7 +169,7 @@ object SshKey {
if (publicKeyFile.isFile) { if (publicKeyFile.isFile) {
publicKeyFile.delete() publicKeyFile.delete()
} }
context.getEncryptedPrefs("git_operation").edit { context.getEncryptedGitPrefs().edit {
remove(PreferenceKeys.SSH_KEY_LOCAL_PASSPHRASE) remove(PreferenceKeys.SSH_KEY_LOCAL_PASSPHRASE)
} }
type = null type = null

View file

@ -20,7 +20,7 @@ import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.databinding.ActivitySshKeygenBinding import com.zeapo.pwdstore.databinding.ActivitySshKeygenBinding
import com.zeapo.pwdstore.git.sshj.SshKey import com.zeapo.pwdstore.git.sshj.SshKey
import com.zeapo.pwdstore.utils.BiometricAuthenticator 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.keyguardManager
import com.zeapo.pwdstore.utils.viewBinding import com.zeapo.pwdstore.utils.viewBinding
import kotlin.coroutines.resume import kotlin.coroutines.resume
@ -128,7 +128,7 @@ class SshKeyGenActivity : AppCompatActivity() {
keyGenType.generateKey(requireAuthentication) keyGenType.generateKey(requireAuthentication)
} }
} }
getEncryptedPrefs("git_operation").edit { getEncryptedGitPrefs().edit {
remove("ssh_key_local_passphrase") remove("ssh_key_local_passphrase")
} }
binding.generate.apply { binding.generate.apply {

View 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)
}

View file

@ -4,74 +4,33 @@
*/ */
package com.zeapo.pwdstore.utils 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.getOrElse
import com.github.michaelbull.result.runCatching 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 com.zeapo.pwdstore.utils.PasswordRepository.Companion.getRepositoryDirectory
import java.io.File import java.io.File
import java.util.Date import java.util.Date
import org.eclipse.jgit.lib.ObjectId import org.eclipse.jgit.lib.ObjectId
import org.eclipse.jgit.revwalk.RevCommit import org.eclipse.jgit.revwalk.RevCommit
/**
* The default OpenPGP provider for the app
*/
const val OPENPGP_PROVIDER = "org.sufficientlysecure.keychain" const val OPENPGP_PROVIDER = "org.sufficientlysecure.keychain"
/**
* Clears the given [flag] from the value of this [Int]
*/
fun Int.clearFlag(flag: Int): Int { fun Int.clearFlag(flag: Int): Int {
return this and flag.inv() return this and flag.inv()
} }
/**
* Checks if this [Int] contains the given [flag]
*/
infix fun Int.hasFlag(flag: Int): Boolean { infix fun Int.hasFlag(flag: Int): Boolean {
return this and flag == flag 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]. * 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 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 * Checks if this [File] is in the password repository directory as given
* view whose id is [id]. Solution based on a StackOverflow * by [getRepositoryDirectory]
* 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()
}
}
}
val Context.autofillManager: AutofillManager?
@RequiresApi(Build.VERSION_CODES.O)
get() = getSystemService()
val Context.keyguardManager: KeyguardManager
get() = getSystemService()!!
fun File.isInsideRepository(): Boolean { fun File.isInsideRepository(): Boolean {
return canonicalPath.contains(getRepositoryDirectory().canonicalPath) 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. * Unique SHA-1 hash of this commit as hexadecimal string.
* *
* @see RevCommit.id * @see RevCommit.getId
*/ */
val RevCommit.hash: String val RevCommit.hash: String
get() = ObjectId.toString(id) get() = ObjectId.toString(id)
@ -189,3 +80,11 @@ val RevCommit.time: Date
val epochMilliseconds = epochSeconds * 1000 val epochMilliseconds = epochSeconds * 1000
return Date(epochMilliseconds) 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()
}

View file

@ -1,34 +1,29 @@
package com.zeapo.pwdstore.utils package com.zeapo.pwdstore.utils
import android.content.pm.PackageManager
import android.view.View
import androidx.annotation.IdRes import androidx.annotation.IdRes
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.fragment.app.commit import androidx.fragment.app.commit
import com.zeapo.pwdstore.R import com.zeapo.pwdstore.R
/**
* Check if [permission] is granted to the app. Aliases to [isPermissionGranted] internally.
*/
fun Fragment.isPermissionGranted(permission: String): Boolean { 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 Fragment.finish() = requireActivity().finish()
fun FragmentManager.performTransaction(destinationFragment: Fragment, @IdRes containerViewId: Int = android.R.id.content) { /**
this.commit { * Perform a [commit] on this [FragmentManager] with custom animations and adding the [destinationFragment]
beginTransaction() * to the fragment backstack
setCustomAnimations( */
R.animator.slide_in_left,
R.animator.slide_out_left,
R.animator.slide_in_right,
R.animator.slide_out_right)
replace(containerViewId, destinationFragment)
}
}
fun FragmentManager.performTransactionWithBackStack(destinationFragment: Fragment, @IdRes containerViewId: Int = android.R.id.content) { fun FragmentManager.performTransactionWithBackStack(destinationFragment: Fragment, @IdRes containerViewId: Int = android.R.id.content) {
this.commit { commit {
beginTransaction() beginTransaction()
addToBackStack(destinationFragment.tag) addToBackStack(destinationFragment.tag)
setCustomAnimations( setCustomAnimations(
@ -39,14 +34,3 @@ fun FragmentManager.performTransactionWithBackStack(destinationFragment: Fragmen
replace(containerViewId, destinationFragment) 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)
}
}

View file

@ -83,9 +83,6 @@ object Dependencies {
const val runner = "androidx.test:runner:1.3.0" const val runner = "androidx.test:runner:1.3.0"
const val rules = "androidx.test:rules: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"
} }
} }
} }