mirror of
https://github.com/fork-maintainers/iceraven-browser
synced 2024-11-17 15:26:23 +00:00
[fenix] For https://github.com/mozilla-mobile/fenix/issues/25588, hide password in android 13 clipboard preview (https://github.com/mozilla-mobile/fenix/pull/28719)
* For https://github.com/mozilla-mobile/fenix/issues/25588, hide password in android 13 clipboard preview This patch hide passwords copied from "Saved logins" in android 13 clipboard preview Closes https://github.com/mozilla-mobile/fenix/issues/25588 * fix: show a toast for android 12 and lower * For mozilla-mobilehttps://github.com/mozilla-mobile/fenix/issues/25588, hide password in android 13 clipboard preview * fix: move metrics to new listeners * fix: actually move metrics to new listeners --------- Co-authored-by: cschanaj <cschanaj@users.noreply.github.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
parent
30ee8f1f81
commit
71349f9688
@ -26,6 +26,7 @@ import org.mozilla.fenix.settings.logins.LoginsFragmentStore
|
||||
import org.mozilla.fenix.settings.logins.fragment.AddLoginFragmentDirections
|
||||
import org.mozilla.fenix.settings.logins.fragment.EditLoginFragmentDirections
|
||||
import org.mozilla.fenix.settings.logins.mapToSavedLogin
|
||||
import org.mozilla.fenix.utils.ClipboardHandler
|
||||
|
||||
/**
|
||||
* Controller for all saved logins interactions with the password storage component
|
||||
@ -37,6 +38,7 @@ open class SavedLoginsStorageController(
|
||||
private val navController: NavController,
|
||||
private val loginsFragmentStore: LoginsFragmentStore,
|
||||
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO,
|
||||
private val clipboardHandler: ClipboardHandler,
|
||||
) {
|
||||
|
||||
fun delete(loginId: String) {
|
||||
@ -252,4 +254,22 @@ open class SavedLoginsStorageController(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy login username to clipboard
|
||||
* @param loginId id of the login entry to copy username from
|
||||
*/
|
||||
fun copyUsername(loginId: String) = lifecycleScope.launch {
|
||||
val login = passwordsStorage.get(loginId)
|
||||
clipboardHandler.text = login?.username
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy login password to clipboard
|
||||
* @param loginId id of the login entry to copy password from
|
||||
*/
|
||||
fun copyPassword(loginId: String) = lifecycleScope.launch {
|
||||
val login = passwordsStorage.get(loginId)
|
||||
clipboardHandler.sensitiveText = login?.password
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,7 @@ class AddLoginFragment : Fragment(R.layout.fragment_add_login), MenuProvider {
|
||||
lifecycleScope = lifecycleScope,
|
||||
navController = findNavController(),
|
||||
loginsFragmentStore = loginsFragmentStore,
|
||||
clipboardHandler = requireContext().components.clipboardHandler,
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -82,6 +82,7 @@ class EditLoginFragment : Fragment(R.layout.fragment_edit_login), MenuProvider {
|
||||
lifecycleScope = lifecycleScope,
|
||||
navController = findNavController(),
|
||||
loginsFragmentStore = loginsFragmentStore,
|
||||
clipboardHandler = requireContext().components.clipboardHandler,
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
package org.mozilla.fenix.settings.logins.fragment
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import android.view.LayoutInflater
|
||||
@ -13,7 +14,6 @@ import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.lifecycle.Lifecycle
|
||||
@ -89,8 +89,10 @@ class LoginDetailFragment : SecureFragment(R.layout.fragment_login_detail), Menu
|
||||
lifecycleScope = lifecycleScope,
|
||||
navController = findNavController(),
|
||||
loginsFragmentStore = savedLoginsStore,
|
||||
clipboardHandler = requireContext().components.clipboardHandler,
|
||||
),
|
||||
)
|
||||
|
||||
interactor.onFetchLoginList(args.savedLoginId)
|
||||
|
||||
consumeFrom(savedLoginsStore) {
|
||||
@ -145,14 +147,18 @@ class LoginDetailFragment : SecureFragment(R.layout.fragment_login_detail), Menu
|
||||
}
|
||||
|
||||
binding.usernameText.text = login?.username
|
||||
binding.copyUsername.setOnClickListener(
|
||||
CopyButtonListener(login?.username, R.string.logins_username_copied),
|
||||
)
|
||||
binding.copyUsername.setOnClickListener {
|
||||
interactor.onCopyUsername(args.savedLoginId)
|
||||
showCopiedSnackbar(view = it, copiedItem = it.context.getString(R.string.logins_username_copied))
|
||||
Logins.copyLogin.record(NoExtras())
|
||||
}
|
||||
|
||||
binding.passwordText.text = login?.password
|
||||
binding.copyPassword.setOnClickListener(
|
||||
CopyButtonListener(login?.password, R.string.logins_password_copied),
|
||||
)
|
||||
binding.copyPassword.setOnClickListener {
|
||||
interactor.onCopyPassword(args.savedLoginId)
|
||||
showCopiedSnackbar(view = it, copiedItem = it.context.getString(R.string.logins_password_copied))
|
||||
Logins.copyLogin.record(NoExtras())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateMenu(menu: Menu, inflater: MenuInflater) {
|
||||
@ -172,6 +178,17 @@ class LoginDetailFragment : SecureFragment(R.layout.fragment_login_detail), Menu
|
||||
else -> false
|
||||
}
|
||||
|
||||
private fun showCopiedSnackbar(view: View, copiedItem: String) {
|
||||
// Only show a toast for Android 12 and lower.
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) {
|
||||
FenixSnackbar.make(
|
||||
view,
|
||||
duration = Snackbar.LENGTH_SHORT,
|
||||
isDisplayedWithBrowserToolbar = false,
|
||||
).setText(copiedItem).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateToBrowser(address: String) {
|
||||
(activity as HomeActivity).openToBrowserAndLoad(
|
||||
address,
|
||||
@ -206,33 +223,6 @@ class LoginDetailFragment : SecureFragment(R.layout.fragment_login_detail), Menu
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Click listener for a textview's copy button.
|
||||
* @param value Value to be copied
|
||||
* @param snackbarText Text to display in snackbar after copying.
|
||||
*/
|
||||
private inner class CopyButtonListener(
|
||||
private val value: String?,
|
||||
@StringRes private val snackbarText: Int,
|
||||
) : View.OnClickListener {
|
||||
override fun onClick(view: View) {
|
||||
val clipboard = view.context.components.clipboardHandler
|
||||
clipboard.text = value
|
||||
showCopiedSnackbar(view.context.getString(snackbarText))
|
||||
Logins.copyLogin.record(NoExtras())
|
||||
}
|
||||
|
||||
private fun showCopiedSnackbar(copiedItem: String) {
|
||||
view?.let {
|
||||
FenixSnackbar.make(
|
||||
view = it,
|
||||
duration = Snackbar.LENGTH_SHORT,
|
||||
isDisplayedWithBrowserToolbar = false,
|
||||
).setText(copiedItem).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
|
@ -88,6 +88,7 @@ class SavedLoginsFragment : SecureFragment(), MenuProvider {
|
||||
lifecycleScope = viewLifecycleOwner.lifecycleScope,
|
||||
navController = findNavController(),
|
||||
loginsFragmentStore = savedLoginsStore,
|
||||
clipboardHandler = requireContext().components.clipboardHandler,
|
||||
)
|
||||
|
||||
savedLoginsInteractor =
|
||||
|
@ -21,4 +21,20 @@ class LoginDetailInteractor(
|
||||
fun onDeleteLogin(loginId: String) {
|
||||
savedLoginsController.delete(loginId)
|
||||
}
|
||||
|
||||
/**
|
||||
* for the copy username button
|
||||
* @param loginId id of the login entry to copy username from
|
||||
*/
|
||||
fun onCopyUsername(loginId: String) {
|
||||
savedLoginsController.copyUsername(loginId)
|
||||
}
|
||||
|
||||
/**
|
||||
* for the copy password button
|
||||
* @param loginId id of the login entry to copy password from
|
||||
*/
|
||||
fun onCopyPassword(loginId: String) {
|
||||
savedLoginsController.copyPassword(loginId)
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.PersistableBundle
|
||||
import android.view.textclassifier.TextClassifier
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.core.content.getSystemService
|
||||
@ -47,7 +48,39 @@ class ClipboardHandler(val context: Context) {
|
||||
return null
|
||||
}
|
||||
set(value) {
|
||||
clipboard.setPrimaryClip(ClipData.newPlainText("Text", value))
|
||||
val clipData = ClipData.newPlainText("Text", value)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
clipData.apply {
|
||||
description.extras = PersistableBundle().apply {
|
||||
putBoolean("android.content.extra.IS_SENSITIVE", false)
|
||||
}
|
||||
}
|
||||
}
|
||||
clipboard.setPrimaryClip(clipData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides access to the sensitive content of the clipboard, be aware this is a sensitive
|
||||
* API as from Android 12 and above, accessing it will trigger a notification letting the user
|
||||
* know the app has accessed the clipboard, make sure when you call this API that users are
|
||||
* completely aware that we are accessing the clipboard.
|
||||
* See for more details https://github.com/mozilla-mobile/fenix/issues/22271.
|
||||
*
|
||||
*/
|
||||
var sensitiveText: String?
|
||||
get() {
|
||||
return text
|
||||
}
|
||||
set(value) {
|
||||
val clipData = ClipData.newPlainText("Text", value)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
clipData.apply {
|
||||
description.extras = PersistableBundle().apply {
|
||||
putBoolean("android.content.extra.IS_SENSITIVE", true)
|
||||
}
|
||||
}
|
||||
}
|
||||
clipboard.setPrimaryClip(clipData)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,6 +26,7 @@ import org.mozilla.fenix.ext.directionsEq
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import org.mozilla.fenix.settings.logins.controller.SavedLoginsStorageController
|
||||
import org.mozilla.fenix.settings.logins.fragment.EditLoginFragmentDirections
|
||||
import org.mozilla.fenix.utils.ClipboardHandler
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class SavedLoginsStorageControllerTest {
|
||||
@ -38,6 +39,7 @@ class SavedLoginsStorageControllerTest {
|
||||
private lateinit var controller: SavedLoginsStorageController
|
||||
private val navController: NavController = mockk(relaxed = true)
|
||||
private val loginsFragmentStore: LoginsFragmentStore = mockk(relaxed = true)
|
||||
private val clipboardHandler: ClipboardHandler = mockk(relaxed = true)
|
||||
private val loginMock: Login = mockk(relaxed = true)
|
||||
|
||||
@Before
|
||||
@ -54,6 +56,7 @@ class SavedLoginsStorageControllerTest {
|
||||
navController = navController,
|
||||
loginsFragmentStore = loginsFragmentStore,
|
||||
ioDispatcher = ioDispatcher,
|
||||
clipboardHandler = clipboardHandler,
|
||||
)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user