2019-10-24 18:29:41 +02:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
2020-01-14 01:18:41 +01:00
|
|
|
package org.mozilla.fenix.settings.logins
|
2019-10-24 18:29:41 +02:00
|
|
|
|
2020-02-06 00:33:11 +01:00
|
|
|
import android.content.Context
|
2019-10-24 18:29:41 +02:00
|
|
|
import android.os.Bundle
|
|
|
|
import android.text.InputType
|
2020-02-07 17:58:30 +01:00
|
|
|
import android.view.Menu
|
|
|
|
import android.view.MenuInflater
|
|
|
|
import android.view.MenuItem
|
2019-10-24 18:29:41 +02:00
|
|
|
import android.view.View
|
2019-11-15 22:50:35 +01:00
|
|
|
import android.view.WindowManager
|
2020-02-06 00:33:11 +01:00
|
|
|
import androidx.annotation.StringRes
|
|
|
|
import androidx.appcompat.content.res.AppCompatResources.getDrawable
|
2019-10-24 18:29:41 +02:00
|
|
|
import androidx.fragment.app.Fragment
|
2020-02-07 17:58:30 +01:00
|
|
|
import androidx.lifecycle.lifecycleScope
|
2019-10-30 01:14:12 +01:00
|
|
|
import androidx.navigation.fragment.findNavController
|
2020-02-06 00:33:11 +01:00
|
|
|
import androidx.navigation.fragment.navArgs
|
2019-10-24 18:29:41 +02:00
|
|
|
import com.google.android.material.snackbar.Snackbar
|
|
|
|
import kotlinx.android.synthetic.main.fragment_saved_login_site_info.*
|
2020-02-07 17:58:30 +01:00
|
|
|
import kotlinx.coroutines.CancellationException
|
|
|
|
import kotlinx.coroutines.Deferred
|
|
|
|
import kotlinx.coroutines.Dispatchers.IO
|
|
|
|
import kotlinx.coroutines.Dispatchers.Main
|
|
|
|
import kotlinx.coroutines.async
|
|
|
|
import kotlinx.coroutines.launch
|
|
|
|
import kotlinx.coroutines.withContext
|
2019-10-24 18:29:41 +02:00
|
|
|
import org.mozilla.fenix.R
|
|
|
|
import org.mozilla.fenix.components.FenixSnackbar
|
2019-11-13 00:55:36 +01:00
|
|
|
import org.mozilla.fenix.components.metrics.Event
|
2019-10-24 18:29:41 +02:00
|
|
|
import org.mozilla.fenix.ext.components
|
2019-11-25 21:36:47 +01:00
|
|
|
import org.mozilla.fenix.ext.showToolbar
|
2019-10-24 18:29:41 +02:00
|
|
|
|
2020-02-06 00:33:11 +01:00
|
|
|
/**
|
|
|
|
* Displays saved login information for a single website.
|
|
|
|
*/
|
2019-10-24 18:29:41 +02:00
|
|
|
class SavedLoginSiteInfoFragment : Fragment(R.layout.fragment_saved_login_site_info) {
|
|
|
|
|
2020-02-06 00:33:11 +01:00
|
|
|
private val args by navArgs<SavedLoginSiteInfoFragmentArgs>()
|
2019-10-24 18:29:41 +02:00
|
|
|
|
2020-02-07 17:58:30 +01:00
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
|
super.onCreate(savedInstanceState)
|
|
|
|
setHasOptionsMenu(true)
|
|
|
|
}
|
|
|
|
|
2019-10-30 01:14:12 +01:00
|
|
|
override fun onPause() {
|
|
|
|
// If we pause this fragment, we want to pop users back to reauth
|
2019-11-15 01:12:52 +01:00
|
|
|
if (findNavController().currentDestination?.id != R.id.savedLoginsFragment) {
|
2019-11-15 22:50:35 +01:00
|
|
|
activity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
2019-11-15 01:12:52 +01:00
|
|
|
findNavController().popBackStack(R.id.loginsFragment, false)
|
|
|
|
}
|
2019-10-30 01:14:12 +01:00
|
|
|
super.onPause()
|
|
|
|
}
|
|
|
|
|
2019-10-24 18:29:41 +02:00
|
|
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
|
|
super.onViewCreated(view, savedInstanceState)
|
|
|
|
|
2020-02-06 00:33:11 +01:00
|
|
|
siteInfoText.text = args.savedLoginItem.url
|
|
|
|
copySiteItem.setOnClickListener(
|
|
|
|
CopyButtonListener(args.savedLoginItem.url, R.string.logins_site_copied)
|
|
|
|
)
|
2019-10-24 18:29:41 +02:00
|
|
|
|
2020-02-06 00:33:11 +01:00
|
|
|
usernameInfoText.text = args.savedLoginItem.userName
|
|
|
|
copyUsernameItem.setOnClickListener(
|
|
|
|
CopyButtonListener(args.savedLoginItem.userName, R.string.logins_username_copied)
|
|
|
|
)
|
2019-10-24 18:29:41 +02:00
|
|
|
|
|
|
|
passwordInfoText.inputType =
|
|
|
|
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
|
2020-02-06 00:33:11 +01:00
|
|
|
passwordInfoText.text = args.savedLoginItem.password
|
2019-10-24 18:29:41 +02:00
|
|
|
revealPasswordItem.setOnClickListener {
|
2020-02-06 00:33:11 +01:00
|
|
|
togglePasswordReveal(it.context)
|
2019-10-24 18:29:41 +02:00
|
|
|
}
|
2020-02-06 00:33:11 +01:00
|
|
|
copyPasswordItem.setOnClickListener(
|
|
|
|
CopyButtonListener(args.savedLoginItem.password, R.string.logins_password_copied)
|
|
|
|
)
|
2019-10-24 18:29:41 +02:00
|
|
|
}
|
|
|
|
|
2020-02-07 17:58:30 +01:00
|
|
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
|
|
|
inflater.inflate(R.menu.login_edit, menu)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
|
|
|
|
R.id.delete_login_button -> {
|
|
|
|
deleteLogin()
|
|
|
|
true
|
|
|
|
}
|
|
|
|
else -> false
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun deleteLogin() {
|
|
|
|
var deleteLoginJob: Deferred<Boolean>? = null
|
|
|
|
val deleteJob = lifecycleScope.launch(IO) {
|
|
|
|
deleteLoginJob = async {
|
|
|
|
requireContext().components.core.syncablePasswordsStorage.withUnlocked {
|
|
|
|
it.delete(args.savedLoginItem.id).await()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
deleteLoginJob?.await()
|
|
|
|
withContext(Main) {
|
|
|
|
findNavController().popBackStack(R.id.savedLoginsFragment, false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
deleteJob.invokeOnCompletion {
|
|
|
|
if (it is CancellationException) {
|
|
|
|
deleteLoginJob?.cancel()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-06 00:33:11 +01:00
|
|
|
private fun togglePasswordReveal(context: Context) {
|
2019-10-24 18:29:41 +02:00
|
|
|
if (passwordInfoText.inputType == InputType.TYPE_TEXT_VARIATION_PASSWORD or InputType.TYPE_CLASS_TEXT) {
|
2020-02-06 00:33:11 +01:00
|
|
|
context.components.analytics.metrics.track(Event.ViewLoginPassword)
|
2019-10-24 18:29:41 +02:00
|
|
|
passwordInfoText.inputType = InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
|
2020-02-07 17:58:30 +01:00
|
|
|
revealPasswordItem.setImageDrawable(
|
|
|
|
getDrawable(
|
|
|
|
context,
|
|
|
|
R.drawable.mozac_ic_password_hide
|
|
|
|
)
|
|
|
|
)
|
|
|
|
revealPasswordItem.contentDescription =
|
|
|
|
context.getString(R.string.saved_login_hide_password)
|
2019-10-24 18:29:41 +02:00
|
|
|
} else {
|
2020-02-07 17:58:30 +01:00
|
|
|
passwordInfoText.inputType =
|
|
|
|
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
|
|
|
|
revealPasswordItem.setImageDrawable(
|
|
|
|
getDrawable(
|
|
|
|
context,
|
|
|
|
R.drawable.mozac_ic_password_reveal
|
|
|
|
)
|
|
|
|
)
|
|
|
|
revealPasswordItem.contentDescription =
|
|
|
|
context.getString(R.string.saved_login_reveal_password)
|
2019-10-24 18:29:41 +02:00
|
|
|
}
|
|
|
|
// For the new type to take effect you need to reset the text
|
2020-02-06 00:33:11 +01:00
|
|
|
passwordInfoText.text = args.savedLoginItem.password
|
2019-10-24 18:29:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun onResume() {
|
|
|
|
super.onResume()
|
2019-11-15 22:50:35 +01:00
|
|
|
activity?.window?.setFlags(
|
|
|
|
WindowManager.LayoutParams.FLAG_SECURE,
|
|
|
|
WindowManager.LayoutParams.FLAG_SECURE
|
|
|
|
)
|
2020-02-06 00:33:11 +01:00
|
|
|
showToolbar(args.savedLoginItem.url)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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))
|
|
|
|
view.context.components.analytics.metrics.track(Event.CopyLogin)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun showCopiedSnackbar(copiedItem: String) {
|
|
|
|
view?.let {
|
|
|
|
FenixSnackbar.make(it, Snackbar.LENGTH_SHORT).setText(copiedItem).show()
|
|
|
|
}
|
|
|
|
}
|
2019-10-24 18:29:41 +02:00
|
|
|
}
|
|
|
|
}
|