Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>master
@@ -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 <E> setItems(view: LinearLayout, | |||
oldList: ObservableList<E>?, oldLayoutId: Int, | |||
newList: ObservableList<E>?, newLayoutId: Int) { | |||
oldList: ObservableList<E>?, oldLayoutId: Int, @Suppress("UNUSED_PARAMETER") oldFragment: Fragment?, | |||
newList: ObservableList<E>?, newLayoutId: Int, newFragment: Fragment?) { | |||
if (oldList === newList && oldLayoutId == newLayoutId) | |||
return | |||
var listener: ItemChangeListener<E>? = 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<*>) { | |||
@@ -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<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 layoutInflater: LayoutInflater = LayoutInflater.from(container.context) | |||
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" } | |||
binding!!.setVariable(BR.collection, list) | |||
binding.setVariable(BR.item, list!![position]) | |||
binding.setVariable(BR.fragment, fragment) | |||
binding.executePendingBindings() | |||
return binding.root | |||
} | |||
@@ -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" | |||
@@ -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}" /> | |||
</com.google.android.material.textfield.TextInputLayout> | |||
<com.google.android.material.textfield.TextInputLayout | |||
@@ -234,6 +236,7 @@ | |||
android:layout_height="wrap_content" | |||
android:divider="@null" | |||
android:orientation="vertical" | |||
app:fragment="@{fragment}" | |||
app:items="@{config.peers}" | |||
app:layout="@{@layout/tunnel_editor_peer}" | |||
tools:ignore="UselessLeaf" /> | |||
@@ -15,6 +15,10 @@ | |||
<variable | |||
name="item" | |||
type="com.wireguard.android.viewmodel.PeerProxy" /> | |||
<variable | |||
name="fragment" | |||
type="com.wireguard.android.fragment.TunnelEditorFragment" /> | |||
</data> | |||
<com.google.android.material.card.MaterialCardView | |||
@@ -91,8 +95,11 @@ | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" | |||
android:hint="@string/hint_optional" | |||
android:inputType="textNoSuggestions|textVisiblePassword" | |||
android:text="@={item.preSharedKey}" /> | |||
android:inputType="textNoSuggestions|textPassword" | |||
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 | |||