I have a test that checks to see if a dialog is present or not.
#Test
fun dismissedWhenClicked() {
//dimiss dialog
onView(withText(R.string.simple)).inRoot(isDialog()).perform(click())
//check dialog
onView(isRoot()).inRoot(isDialog()).check(matches(not(isDisplayed())))
}
above is my best guess, but fails because Matcher 'is dialog' did not match any of the following roots
i have found 3 questions on here that address it but none seem to solve it.
Espresso check if no dialog is displayed - the comment works but it also passes when there is a dialog
Check the dialog is visible - Espresso - this doesn't check, instead it will just fail gracefully, i think.
espresso: Assert a Dialog is not shown - seems to have no answer.
I have solved this with a custom matcher modified slightly from here
#Test
fun dismissedWhenClicked() {
onView(withText(R.string.simple)).inRoot(isDialog()).perform(click())
onView(withId(R.id.fragment_layout)).inRoot(Utils.ActivityMatcher()).check(matches(isDisplayed()))
}
class ActivityMatcher : TypeSafeMatcher<Root>() {
override fun describeTo(description: Description) {
description.appendText("is activity")
}
public override fun matchesSafely(root: Root): Boolean {
val type: Int = root.windowLayoutParams.get().type
if (type == WindowManager.LayoutParams.TYPE_BASE_APPLICATION) {
val windowToken: IBinder = root.decorView.windowToken
val appToken: IBinder = root.decorView.applicationWindowToken
if (windowToken === appToken) {
//means this window isn't contained by any other windows.
return true
}
}
return false
}
}
I am building an app where there is a settings screen which I build with Andreoid Jetpack Preference library. I want to travel from the preference screen into a more detailed fragment in order to personalize email. But I don know how to handle the click on the preference, I have managed to import the method provided from the library but I dont know how to implement it as there is no information available. I have the function onPreferenceClick but i dont know how to build its logic. It shall return false when it is not clicked and true when it is.
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
if (onPreferenceClick(preferenceScreen.getPreference(1))) {
findNavController().navigate(R.id.editMailFragment)
}
override fun onPreferenceClick(preference: Preference?): Boolean {
preference?.setOnPreferenceClickListener {
}
}
return
}
If you using PreferenceFragmentCompat, you can override onCreatePreferences to handle click on preference:
#Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.your_preference);
Preference preferenceMap = findPreference("your_preference_key");
preferenceMap.setOnPreferenceClickListener(
new Preference.OnPreferenceClickListener() {
#Override
public boolean onPreferenceClick(Preference arg0) {
findNavController().navigate(R.id.editMailFragment)
return true;
}
});
}
I have solved it, here is the code if someone has the same trouble.
val showValueListener = Preference.OnPreferenceClickListener {
findNavController().navigate(R.id.editMailFragment)
true
}
findPreference<Preference>("email")?.onPreferenceClickListener = showValueListener
I've pretty excited by Kotlin compiler features and by by in particular - it saves time generating gelegating code:
https://kotlinlang.org/docs/reference/delegation.html
But i want delegate to be nullable and delegating code to check if it's null first and return if it is:
interface Base {
val message: String
fun print()
}
class BaseImpl(val x: Int?) : Base {
override val message = "BaseImpl: x = $x"
override fun print() { println(message) }
}
class Derived(b: Base?) : Base by b {
// This property is not accessed from b's implementation of `print`
override val message = "Message of Derived"
}
fun main() {
val b = BaseImpl(10)
val derived = Derived(b)
derived.print()
println(derived.message)
}
When compiling ^ i'm getting Type mismatch: inferred type is Base? but Base was expected.
Is it still possible with Kotlin?
To be more detailed i'd like Kotlin compiler to generate forwarding calls to wrapped impl (extWebChromeClient) in https://developer.android.com/reference/android/webkit/WebChromeClient like follows:
private WebChromeClient intWebChromeClient = new WebChromeClient()
{
#Override
public void onReceivedTitle(WebView view, String title)
{
if (extWebChromeClient != null)
{
extWebChromeClient.onReceivedTitle(view, title);
}
}
...
You can make this yourself using dynamic proxies, though I wouldn't really recommend it. Note that for non-void methods there's no way to require overriding them. The below implementation just throws exceptions for them unconditionally, but you could still call them for non-null x.
inline fun <reified T : Any> nullableProxy(x: T?): T {
val handler = InvocationHandler { _, method, args ->
if (method.returnType == Void.TYPE) {
if (x != null) {
method.invoke(x, *(args ?: arrayOf()))
}
} else
throw UnsupportedOperationException("Non-void method")
}
return Proxy.newProxyInstance(
T::class.java.classLoader,
arrayOf(T::class.java),
handler) as T
}
class Derived(b: Base?) : Base by nullableProxy(b)
This also won't perform as well as implementing methods directly would.
When I copy and paste Code B (Java Code) into Android Studio 3.1.2, I choose to convert to Kotlin code automatically.
So I get the shown Code A in Kotlin, but with the following error. Why?
Why is that error occurring when Android Studio converts the Java code into Kotlin code automatically?
BTW, Code B (Java code) works well.
Error
java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter state
at ui.DialogChangePassword.showDialog(DialogChangePassword.kt)
Code A (Kotlin Code)
class DialogChangePassword(context: Context, attrs: AttributeSet) : DialogPreference(context, attrs) {
private var mView: View? = null
init {
dialogLayoutResource = R.layout.item_custom_password_dialog
}
override fun onCreateDialogView(): View? {
// TODO Auto-generated method stub
mView = super.onCreateDialogView()
return mView
}
override fun onDialogClosed(positiveResult: Boolean) {
super.onDialogClosed(positiveResult)
}
override fun showDialog(state: Bundle) {
// Call show on default first so we can
// override the handlers
super.showDialog(state)
val d = dialog as AlertDialog
d.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
if (IsInputOKAndSavePassword()) {
d.dismiss()
}
}
}
private fun IsInputOKAndSavePassword(): Boolean {
return true
}
}
Code B (Java Code)
public class DialogChangePassword extends DialogPreference {
private View mView;
public DialogChangePassword(Context context, AttributeSet attrs) {
super(context, attrs);
setDialogLayoutResource(R.layout.item_custom_password_dialog);
}
#Override
protected View onCreateDialogView() {
// TODO Auto-generated method stub
mView = super.onCreateDialogView();
return mView;
}
#Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
}
#Override
protected void showDialog(Bundle state) {
// Call show on default first so we can
// override the handlers
super.showDialog(state);
final AlertDialog d = (AlertDialog) getDialog();
d.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
if (IsInputOKAndSavePassword()){
d.dismiss();
}
}
});
}
private boolean IsInputOKAndSavePassword(){
boolean result=true;
return result;
}
}
Kotlin treats null as a first class citizen by distinguishing nullable types (e.g. Bundle?) vs non-nullable types (e.g. Bundle).
as you know, a Bundle instance can be null depending on what phase of the component's lifecycle you're currently in. for example, the first time an Activity instance is created, onCreate() is called with a null Bundle because there is no state to be restored. if that same Activity is re-created due to a configuration change, however, onCreate() can be called with a non-null Bundle instance (in which you may have stored some data to help recreate the associated screen).
given as much, the showDialog signature should be written in such a way that allows for the possibility that state could possibly be null, like so:
override fun showDialog(state: Bundle?) {
...
}
hope that helps!
I am trying to use the done button of the soft keyboard to activate a method via databinding. Just like onClick. Is there a way to do that?
example:
<EditText
android:id="#+id/preSignUpPg2EnterPhone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
onOkInSoftKeyboard="#{(v) -> viewModel.someMethod()}"
/>
onOkInSoftKeyboard doesn't exists... Is there something to create this behavior?
Thanks!
I won't claim to be an expert in onEditorAction() or soft keyboard. That said, assuming you use the solution to the stack overflow question Firoz Memon suggested, you can make it happen. Even if there is another solution that works better, this can give you an idea on how to add your own event handlers.
You'd need a binding adapter that takes some kind of handler. Let's assume you have an empty listener like this:
public class OnOkInSoftKeyboardListener {
void onOkInSoftKeyboard();
}
Then you need a BindingAdapter:
#BindingAdapter("onOkInSoftKeyboard") // I like it to match the listener method name
public static void setOnOkInSoftKeyboardListener(TextView view,
final OnOkInSoftKeyboardListener listener) {
if (listener == null) {
view.setOnEditorActionListener(null);
} else {
view.setOnEditorActionListener(new OnEditorActionListener() {
#Override
public void onEditorAction(TextView v, int actionId, KeyEvent event) {
// ... solution to receiving event
if (somethingOrOther) {
listener.onOkInSoftKeyboard();
}
}
});
}
}
Using Kotlin, kapt produces:
e: [kapt] An exception occurred: android.databinding.tool.util.LoggedErrorException: Found data binding errors.
****/ data binding error ****msg:Listener class kotlin.jvm.functions.Function1 with method invoke did not match signature of any method viewModel::signIn
(because viewModel::signIn is of type KFunction1) so we can't use a method reference. However, if we create a variable within the viewModel that is explicit about the type, then we can pass that variable as the binding's param. (or just use a class)
Bindings.kt:
#BindingAdapter("onEditorEnterAction")
fun EditText.onEditorEnterAction(f: Function1<String, Unit>?) {
if (f == null) setOnEditorActionListener(null)
else setOnEditorActionListener { v, actionId, event ->
val imeAction = when (actionId) {
EditorInfo.IME_ACTION_DONE,
EditorInfo.IME_ACTION_SEND,
EditorInfo.IME_ACTION_GO -> true
else -> false
}
val keydownEvent = event?.keyCode == KeyEvent.KEYCODE_ENTER
&& event.action == KeyEvent.ACTION_DOWN
if (imeAction or keydownEvent)
true.also { f(v.editableText.toString()) }
else false
}
}
MyViewModel.kt:
fun signIn(password: String) {
Toast.makeText(context, password, Toast.LENGTH_SHORT).show()
}
val signIn: Function1<String, Unit> = this::signIn
layout.xml:
<EditText
android:id="#+id/password"
app:onEditorEnterAction="#{viewModel.signIn}"
android:imeOptions="actionDone|actionSend|actionGo"
android:singleLine="true"/>
Just as i was looking at this myself, here a simpler version where the function is directly called from the data binding:
In your ViewModel use this function:
public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {
return false; // if you want the default action of the actionNext or so on
return true; // if you want to intercept
}
And in the layout:
android:onEditorAction="#{(view,actionId,event) -> viewModel.onEditorAction(view,actionId,event)}"
Kotlin, without writing custom binding adapter
In Layout,
<EditText
...
android:onEditorAction="#{(view, actionId, event) -> viewModel.onDoneClicked(view, actionId, event)}" />
ViewModel
fun onDoneClicked(view: View, actionId: Int, event: KeyEvent?): Boolean {
if(actionId == EditorInfo.IME_ACTION_DONE) {
// handle here
return true
}
return false
}
Note: event can be null, so make KeyEvent nullable by putting ? there.
You can directly call login method what inside ViewModel by implementing setOnEditorActionListener to the Edittext, taking reference from binging class
loginFragmentBinding.etPassword.setOnEditorActionListener(TextView.OnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
loginViewModel.login()
return#OnEditorActionListener true
}
false
})
Android framework already have this implemented. Take a look at TextViewBindingAdapter
You'll see those attributes, the documentation kind of glosses over what this means, but in a nutshell:
attribute = when this attribute appears in a layout file
type = then look for the implementation in this class
method = of a method with this name in the class defined in type
For more on this take a look at this blog post.