From d44a83faaaf48080b8ab9ed6f1e32d98ac0579bc Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Sun, 29 Mar 2020 19:34:27 -0600 Subject: TunnelEditorFragment: add hooks for biometric auth Signed-off-by: Jason A. Donenfeld --- .../android/databinding/BindingAdapters.kt | 18 ++++++++++++++---- .../android/databinding/ItemChangeListener.kt | 4 +++- .../android/fragment/TunnelEditorFragment.kt | 22 ++++++++++++++++++++-- ui/src/main/res/layout/tunnel_editor_fragment.xml | 7 +++++-- ui/src/main/res/layout/tunnel_editor_peer.xml | 11 +++++++++-- 5 files changed, 51 insertions(+), 11 deletions(-) (limited to 'ui/src/main') diff --git a/ui/src/main/java/com/wireguard/android/databinding/BindingAdapters.kt b/ui/src/main/java/com/wireguard/android/databinding/BindingAdapters.kt index 3460e96e..213338f1 100644 --- a/ui/src/main/java/com/wireguard/android/databinding/BindingAdapters.kt +++ b/ui/src/main/java/com/wireguard/android/databinding/BindingAdapters.kt @@ -6,6 +6,8 @@ package com.wireguard.android.databinding import android.text.InputFilter import android.view.LayoutInflater +import android.view.View +import android.widget.EditText import android.widget.LinearLayout import android.widget.TextView import androidx.databinding.BindingAdapter @@ -13,6 +15,7 @@ import androidx.databinding.DataBindingUtil import androidx.databinding.ObservableList import androidx.databinding.ViewDataBinding import androidx.databinding.adapters.ListenerUtil +import androidx.fragment.app.Fragment import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.wireguard.android.BR @@ -41,10 +44,10 @@ object BindingAdapters { } @JvmStatic - @BindingAdapter("items", "layout") + @BindingAdapter("items", "layout", "fragment") fun setItems(view: LinearLayout, - oldList: ObservableList?, oldLayoutId: Int, - newList: ObservableList?, newLayoutId: Int) { + oldList: ObservableList?, oldLayoutId: Int, @Suppress("UNUSED_PARAMETER") oldFragment: Fragment?, + newList: ObservableList?, newLayoutId: Int, newFragment: Fragment?) { if (oldList === newList && oldLayoutId == newLayoutId) return var listener: ItemChangeListener? = ListenerUtil.getListener(view, R.id.item_change_listener) @@ -59,7 +62,7 @@ object BindingAdapters { if (newList == null || newLayoutId == 0) return if (listener == null) { - listener = ItemChangeListener(view, newLayoutId) + listener = ItemChangeListener(view, newLayoutId, newFragment) ListenerUtil.trackListener(view, listener, R.id.item_change_listener) } // Either the list changed, or this is an entirely new listener because the layout changed. @@ -123,6 +126,13 @@ object BindingAdapters { view.setOnBeforeCheckedChangeListener(listener) } + @JvmStatic + @BindingAdapter("onFocusChange") + fun setOnFocusChange(view: EditText, + listener: View.OnFocusChangeListener?) { + view.setOnFocusChangeListener(listener) + } + @JvmStatic @BindingAdapter("android:text") fun setText(view: TextView, text: Optional<*>) { diff --git a/ui/src/main/java/com/wireguard/android/databinding/ItemChangeListener.kt b/ui/src/main/java/com/wireguard/android/databinding/ItemChangeListener.kt index 131f3877..29784a75 100644 --- a/ui/src/main/java/com/wireguard/android/databinding/ItemChangeListener.kt +++ b/ui/src/main/java/com/wireguard/android/databinding/ItemChangeListener.kt @@ -10,13 +10,14 @@ import android.view.ViewGroup import androidx.databinding.DataBindingUtil import androidx.databinding.ObservableList import androidx.databinding.ViewDataBinding +import androidx.fragment.app.Fragment import com.wireguard.android.BR import java.lang.ref.WeakReference /** * Helper class for binding an ObservableList to the children of a ViewGroup. */ -internal class ItemChangeListener(private val container: ViewGroup, private val layoutId: Int) { +internal class ItemChangeListener(private val container: ViewGroup, private val layoutId: Int, private val fragment: Fragment?) { private val callback = OnListChangedCallback(this) private val layoutInflater: LayoutInflater = LayoutInflater.from(container.context) private var list: ObservableList? = null @@ -29,6 +30,7 @@ internal class ItemChangeListener(private val container: ViewGroup, private v require(list != null) { "Trying to get a view while list is still null" } binding!!.setVariable(BR.collection, list) binding.setVariable(BR.item, list!![position]) + binding.setVariable(BR.fragment, fragment) binding.executePendingBindings() return binding.root } diff --git a/ui/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.kt b/ui/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.kt index ff5bb42b..9ac2473c 100644 --- a/ui/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.kt +++ b/ui/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.kt @@ -6,6 +6,7 @@ package com.wireguard.android.fragment import android.content.Context import android.os.Bundle +import android.text.InputType import android.util.Log import android.view.LayoutInflater import android.view.Menu @@ -15,9 +16,9 @@ import android.view.View import android.view.ViewGroup import android.view.WindowManager import android.view.inputmethod.InputMethodManager +import android.widget.EditText import android.widget.Toast import com.google.android.material.snackbar.Snackbar -import com.google.android.material.textfield.TextInputLayout import com.wireguard.android.Application import com.wireguard.android.R import com.wireguard.android.backend.Tunnel @@ -34,6 +35,7 @@ import com.wireguard.config.Config * Fragment for editing a WireGuard configuration. */ class TunnelEditorFragment : BaseFragment(), AppExclusionListener { + private var haveShownKeys = false private var binding: TunnelEditorFragmentBinding? = null private var tunnel: ObservableTunnel? = null private fun onConfigLoaded(config: Config) { @@ -76,7 +78,6 @@ class TunnelEditorFragment : BaseFragment(), AppExclusionListener { setUpScrollingContent(mainContainer, null) privateKeyTextLayout.setEndIconOnClickListener { config?.`interface`?.generateKeyPair() } } - requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) return binding?.root } @@ -226,6 +227,23 @@ class TunnelEditorFragment : BaseFragment(), AppExclusionListener { 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 { private const val KEY_LOCAL_CONFIG = "local_config" private const val KEY_ORIGINAL_NAME = "original_name" diff --git a/ui/src/main/res/layout/tunnel_editor_fragment.xml b/ui/src/main/res/layout/tunnel_editor_fragment.xml index d87e1efb..d5724c11 100644 --- a/ui/src/main/res/layout/tunnel_editor_fragment.xml +++ b/ui/src/main/res/layout/tunnel_editor_fragment.xml @@ -102,9 +102,11 @@ android:id="@+id/private_key_text" android:layout_width="match_parent" android:layout_height="wrap_content" - android:inputType="textNoSuggestions|textVisiblePassword" + android:inputType="textNoSuggestions|textPassword" + android:onClick="@{fragment::onKeyClick}" android:text="@={config.interface.privateKey}" - app:filter="@{KeyInputFilter.newInstance()}" /> + app:filter="@{KeyInputFilter.newInstance()}" + app:onFocusChange="@{fragment::onKeyFocusChange}" /> diff --git a/ui/src/main/res/layout/tunnel_editor_peer.xml b/ui/src/main/res/layout/tunnel_editor_peer.xml index 2f3b0689..f00a6d26 100644 --- a/ui/src/main/res/layout/tunnel_editor_peer.xml +++ b/ui/src/main/res/layout/tunnel_editor_peer.xml @@ -15,6 +15,10 @@ + + + android:inputType="textNoSuggestions|textPassword" + android:onClick="@{fragment::onKeyClick}" + android:text="@={item.preSharedKey}" + app:filter="@{KeyInputFilter.newInstance()}" + app:onFocusChange="@{fragment::onKeyFocusChange}" />