TunnelEditorFragment: add hooks for biometric auth
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
parent
2337fe37be
commit
d44a83faaa
@ -6,6 +6,8 @@ package com.wireguard.android.databinding
|
|||||||
|
|
||||||
import android.text.InputFilter
|
import android.text.InputFilter
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.EditText
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.databinding.BindingAdapter
|
import androidx.databinding.BindingAdapter
|
||||||
@ -13,6 +15,7 @@ import androidx.databinding.DataBindingUtil
|
|||||||
import androidx.databinding.ObservableList
|
import androidx.databinding.ObservableList
|
||||||
import androidx.databinding.ViewDataBinding
|
import androidx.databinding.ViewDataBinding
|
||||||
import androidx.databinding.adapters.ListenerUtil
|
import androidx.databinding.adapters.ListenerUtil
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.wireguard.android.BR
|
import com.wireguard.android.BR
|
||||||
@ -41,10 +44,10 @@ object BindingAdapters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@BindingAdapter("items", "layout")
|
@BindingAdapter("items", "layout", "fragment")
|
||||||
fun <E> setItems(view: LinearLayout,
|
fun <E> setItems(view: LinearLayout,
|
||||||
oldList: ObservableList<E>?, oldLayoutId: Int,
|
oldList: ObservableList<E>?, oldLayoutId: Int, @Suppress("UNUSED_PARAMETER") oldFragment: Fragment?,
|
||||||
newList: ObservableList<E>?, newLayoutId: Int) {
|
newList: ObservableList<E>?, newLayoutId: Int, newFragment: Fragment?) {
|
||||||
if (oldList === newList && oldLayoutId == newLayoutId)
|
if (oldList === newList && oldLayoutId == newLayoutId)
|
||||||
return
|
return
|
||||||
var listener: ItemChangeListener<E>? = ListenerUtil.getListener(view, R.id.item_change_listener)
|
var listener: ItemChangeListener<E>? = ListenerUtil.getListener(view, R.id.item_change_listener)
|
||||||
@ -59,7 +62,7 @@ object BindingAdapters {
|
|||||||
if (newList == null || newLayoutId == 0)
|
if (newList == null || newLayoutId == 0)
|
||||||
return
|
return
|
||||||
if (listener == null) {
|
if (listener == null) {
|
||||||
listener = ItemChangeListener(view, newLayoutId)
|
listener = ItemChangeListener(view, newLayoutId, newFragment)
|
||||||
ListenerUtil.trackListener(view, listener, R.id.item_change_listener)
|
ListenerUtil.trackListener(view, listener, R.id.item_change_listener)
|
||||||
}
|
}
|
||||||
// Either the list changed, or this is an entirely new listener because the layout changed.
|
// Either the list changed, or this is an entirely new listener because the layout changed.
|
||||||
@ -123,6 +126,13 @@ object BindingAdapters {
|
|||||||
view.setOnBeforeCheckedChangeListener(listener)
|
view.setOnBeforeCheckedChangeListener(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@BindingAdapter("onFocusChange")
|
||||||
|
fun setOnFocusChange(view: EditText,
|
||||||
|
listener: View.OnFocusChangeListener?) {
|
||||||
|
view.setOnFocusChangeListener(listener)
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@BindingAdapter("android:text")
|
@BindingAdapter("android:text")
|
||||||
fun setText(view: TextView, text: Optional<*>) {
|
fun setText(view: TextView, text: Optional<*>) {
|
||||||
|
@ -10,13 +10,14 @@ import android.view.ViewGroup
|
|||||||
import androidx.databinding.DataBindingUtil
|
import androidx.databinding.DataBindingUtil
|
||||||
import androidx.databinding.ObservableList
|
import androidx.databinding.ObservableList
|
||||||
import androidx.databinding.ViewDataBinding
|
import androidx.databinding.ViewDataBinding
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
import com.wireguard.android.BR
|
import com.wireguard.android.BR
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class for binding an ObservableList to the children of a ViewGroup.
|
* Helper class for binding an ObservableList to the children of a ViewGroup.
|
||||||
*/
|
*/
|
||||||
internal class ItemChangeListener<T>(private val container: ViewGroup, private val layoutId: Int) {
|
internal class ItemChangeListener<T>(private val container: ViewGroup, private val layoutId: Int, private val fragment: Fragment?) {
|
||||||
private val callback = OnListChangedCallback(this)
|
private val callback = OnListChangedCallback(this)
|
||||||
private val layoutInflater: LayoutInflater = LayoutInflater.from(container.context)
|
private val layoutInflater: LayoutInflater = LayoutInflater.from(container.context)
|
||||||
private var list: ObservableList<T>? = null
|
private var list: ObservableList<T>? = null
|
||||||
@ -29,6 +30,7 @@ internal class ItemChangeListener<T>(private val container: ViewGroup, private v
|
|||||||
require(list != null) { "Trying to get a view while list is still null" }
|
require(list != null) { "Trying to get a view while list is still null" }
|
||||||
binding!!.setVariable(BR.collection, list)
|
binding!!.setVariable(BR.collection, list)
|
||||||
binding.setVariable(BR.item, list!![position])
|
binding.setVariable(BR.item, list!![position])
|
||||||
|
binding.setVariable(BR.fragment, fragment)
|
||||||
binding.executePendingBindings()
|
binding.executePendingBindings()
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ package com.wireguard.android.fragment
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.text.InputType
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
@ -15,9 +16,9 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import android.widget.EditText
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
|
||||||
import com.wireguard.android.Application
|
import com.wireguard.android.Application
|
||||||
import com.wireguard.android.R
|
import com.wireguard.android.R
|
||||||
import com.wireguard.android.backend.Tunnel
|
import com.wireguard.android.backend.Tunnel
|
||||||
@ -34,6 +35,7 @@ import com.wireguard.config.Config
|
|||||||
* Fragment for editing a WireGuard configuration.
|
* Fragment for editing a WireGuard configuration.
|
||||||
*/
|
*/
|
||||||
class TunnelEditorFragment : BaseFragment(), AppExclusionListener {
|
class TunnelEditorFragment : BaseFragment(), AppExclusionListener {
|
||||||
|
private var haveShownKeys = false
|
||||||
private var binding: TunnelEditorFragmentBinding? = null
|
private var binding: TunnelEditorFragmentBinding? = null
|
||||||
private var tunnel: ObservableTunnel? = null
|
private var tunnel: ObservableTunnel? = null
|
||||||
private fun onConfigLoaded(config: Config) {
|
private fun onConfigLoaded(config: Config) {
|
||||||
@ -76,7 +78,6 @@ class TunnelEditorFragment : BaseFragment(), AppExclusionListener {
|
|||||||
setUpScrollingContent(mainContainer, null)
|
setUpScrollingContent(mainContainer, null)
|
||||||
privateKeyTextLayout.setEndIconOnClickListener { config?.`interface`?.generateKeyPair() }
|
privateKeyTextLayout.setEndIconOnClickListener { config?.`interface`?.generateKeyPair() }
|
||||||
}
|
}
|
||||||
requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
|
||||||
return binding?.root
|
return binding?.root
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,6 +227,23 @@ class TunnelEditorFragment : BaseFragment(), AppExclusionListener {
|
|||||||
super.onViewStateRestored(savedInstanceState)
|
super.onViewStateRestored(savedInstanceState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onKeyClick(view: View) = onKeyFocusChange(view, true)
|
||||||
|
|
||||||
|
fun onKeyFocusChange(view: View, isFocused: Boolean) {
|
||||||
|
if (!isFocused) return
|
||||||
|
val edit = view as? EditText ?: return
|
||||||
|
if (!haveShownKeys && edit.text.isNotEmpty()) {
|
||||||
|
if (true /* TODO: do biometric auth prompt */) {
|
||||||
|
haveShownKeys = true
|
||||||
|
} else {
|
||||||
|
/* Unauthorized, so return and don't change visibility. */
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||||
|
edit.inputType = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val KEY_LOCAL_CONFIG = "local_config"
|
private const val KEY_LOCAL_CONFIG = "local_config"
|
||||||
private const val KEY_ORIGINAL_NAME = "original_name"
|
private const val KEY_ORIGINAL_NAME = "original_name"
|
||||||
|
@ -102,9 +102,11 @@
|
|||||||
android:id="@+id/private_key_text"
|
android:id="@+id/private_key_text"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:inputType="textNoSuggestions|textVisiblePassword"
|
android:inputType="textNoSuggestions|textPassword"
|
||||||
|
android:onClick="@{fragment::onKeyClick}"
|
||||||
android:text="@={config.interface.privateKey}"
|
android:text="@={config.interface.privateKey}"
|
||||||
app:filter="@{KeyInputFilter.newInstance()}" />
|
app:filter="@{KeyInputFilter.newInstance()}"
|
||||||
|
app:onFocusChange="@{fragment::onKeyFocusChange}" />
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
@ -234,6 +236,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:divider="@null"
|
android:divider="@null"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
|
app:fragment="@{fragment}"
|
||||||
app:items="@{config.peers}"
|
app:items="@{config.peers}"
|
||||||
app:layout="@{@layout/tunnel_editor_peer}"
|
app:layout="@{@layout/tunnel_editor_peer}"
|
||||||
tools:ignore="UselessLeaf" />
|
tools:ignore="UselessLeaf" />
|
||||||
|
@ -15,6 +15,10 @@
|
|||||||
<variable
|
<variable
|
||||||
name="item"
|
name="item"
|
||||||
type="com.wireguard.android.viewmodel.PeerProxy" />
|
type="com.wireguard.android.viewmodel.PeerProxy" />
|
||||||
|
|
||||||
|
<variable
|
||||||
|
name="fragment"
|
||||||
|
type="com.wireguard.android.fragment.TunnelEditorFragment" />
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
@ -91,8 +95,11 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="@string/hint_optional"
|
android:hint="@string/hint_optional"
|
||||||
android:inputType="textNoSuggestions|textVisiblePassword"
|
android:inputType="textNoSuggestions|textPassword"
|
||||||
android:text="@={item.preSharedKey}" />
|
android:onClick="@{fragment::onKeyClick}"
|
||||||
|
android:text="@={item.preSharedKey}"
|
||||||
|
app:filter="@{KeyInputFilter.newInstance()}"
|
||||||
|
app:onFocusChange="@{fragment::onKeyFocusChange}" />
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
Loading…
Reference in New Issue
Block a user