/*
This file is part of Reactive Cascade which is released under The MIT License.
See license.md , https://github.com/futurice/cascade and http://reactivecascade.com for details.
This is open source for the common good. Please contribute improvements by pull request or contact paulirotta@gmail.com
*/
package com.reactivecascade.reactive.ui;
import android.annotation.TargetApi;
import android.content.Context;
import android.support.annotation.AttrRes;
import android.support.annotation.NonNull;
import android.support.annotation.StyleRes;
import android.support.annotation.UiThread;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.widget.EditText;
import com.reactivecascade.functional.ImmutableValue;
import com.reactivecascade.i.IAsyncOrigin;
import com.reactivecascade.i.INamed;
import com.reactivecascade.i.NotCallOrigin;
import com.reactivecascade.reactive.ReactiveValue;
import com.reactivecascade.util.AssertUtil;
import com.reactivecascade.util.RCLog;
import static com.reactivecascade.Async.UI;
/**
* An {@link EditText} which can be manipulated and which in turn manipulates a supporting
* <code>{@link ReactiveValue}<String></code> which reflects the current on-screen from
* <p>
* Created by Paul Houghton on 12-03-2015.
*/
@NotCallOrigin
public class ReactiveEditText extends EditText implements INamed, IAsyncOrigin {
@NonNull
private final ImmutableValue<String> mOrigin = isInEditMode() ? RCLog.DEFAULT_ORIGIN : RCLog.originAsync();
public volatile ReactiveValue<String> mReactiveValue;
private TextWatcher mTextWatcher;
//TODO Constructors which support a string validator (example: trim whitespace or fix capitalization as you type)
public ReactiveEditText(@NonNull Context context) {
super(context);
mReactiveValue = new ReactiveValue<>(getName(), getText().toString());
}
public ReactiveEditText(
@NonNull Context context,
@NonNull ReactiveValue<String> reactiveValue) {
super(context);
this.mReactiveValue = reactiveValue;
}
public ReactiveEditText(
@NonNull Context context,
@NonNull AttributeSet attrs) {
super(context, attrs);
mReactiveValue = new ReactiveValue<>(getName(), getText().toString());
}
public ReactiveEditText(
@NonNull Context context,
@NonNull AttributeSet attrs,
@NonNull ReactiveValue<String> reactiveValue) {
super(context, attrs);
this.mReactiveValue = reactiveValue;
}
public ReactiveEditText(
@NonNull Context context,
@NonNull AttributeSet attrs,
@AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
mReactiveValue = new ReactiveValue<>(getName(), getText().toString());
}
public ReactiveEditText(
@NonNull Context context,
@NonNull AttributeSet attrs,
@AttrRes int defStyleAttr,
@NonNull ReactiveValue<String> reactiveValue) {
super(context, attrs, defStyleAttr);
this.mReactiveValue = reactiveValue;
}
@TargetApi(21)
public ReactiveEditText(
@NonNull String name,
@NonNull Context context,
@NonNull AttributeSet attrs,
@AttrRes int defStyleAttr,
@StyleRes int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mReactiveValue = new ReactiveValue<>(getName(), getText().toString());
}
@TargetApi(21)
public ReactiveEditText(
@NonNull String name,
@NonNull Context context,
@NonNull AttributeSet attrs,
@AttrRes int defStyleAttr,
@StyleRes int defStyleRes,
@NonNull ReactiveValue<String> reactiveValue) {
super(context, attrs, defStyleAttr, defStyleRes);
this.mReactiveValue = reactiveValue;
}
/**
* Set the reactive view model associated with this view
*
* @param reactiveValue
* @param fire
* @return
*/
@NonNull
public ReactiveValue<String> setReactiveValue(@NonNull ReactiveValue<String> reactiveValue,
final boolean fire) {
UI.run(() -> {
this.mReactiveValue = reactiveValue;
if (fire) {
reactiveValue.fire();
}
});
return reactiveValue;
}
@Override // INamed
@NonNull
public String getName() {
return "ReactiveEditText" + getId();
}
@Override // View
@UiThread
public void onAttachedToWindow() {
final String currentText = mReactiveValue.get();
setText(currentText);
RCLog.v(this, "onAttachedToWindow " + getName() + ", from=" + currentText);
mReactiveValue.subscribe(UI, this::setText);
if (mTextWatcher == null) {
mTextWatcher = new TextWatcher() {
@Override
public void beforeTextChanged(@NonNull final CharSequence s,
final int start,
final int count,
final int after) {
}
@Override
public void onTextChanged(@NonNull final CharSequence s,
final int start,
final int before,
final int count) {
}
@Override
public void afterTextChanged(@NonNull final Editable s) {
setSelection(s.length());
mReactiveValue.set(s.toString());
}
};
this.addTextChangedListener(mTextWatcher);
}
super.onAttachedToWindow();
}
@Override // View
@UiThread
public void onDetachedFromWindow() {
AssertUtil.assertNotNull(mOrigin);
RCLog.v(this, "onDetachedFromWindow " + getName() + ", current from=" + getText());
mReactiveValue.unsubscribeAll("onDetachedFromWindow");
super.onDetachedFromWindow();
}
@NonNull
@Override
public ImmutableValue<String> getOrigin() {
return mOrigin;
}
}