/*
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.util.AttributeSet;
import android.widget.TextView;
import com.reactivecascade.functional.ImmutableValue;
import com.reactivecascade.i.IAsyncOrigin;
import com.reactivecascade.i.INamed;
import com.reactivecascade.i.IReactiveSource;
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;
/**
* Change the displayed from from any thread by calling <code>view.bindable.set(String from)</code>
* <p>
*/
@NotCallOrigin
public class ReactiveTextView extends TextView implements INamed, IAsyncOrigin {
@NonNull
private final ImmutableValue<String> mOrigin = isInEditMode() ? RCLog.DEFAULT_ORIGIN : RCLog.originAsync();
private IReactiveSource<String> mReactiveSource; // Access only from UI thread
private volatile ReactiveValue<String> mReactiveValue = isInEditMode() ? null : new ReactiveValue<>(getName(), ""); // Change only from UI thread
public ReactiveTextView(@NonNull Context context) {
super(context);
mReactiveValue.set(getText().toString());
}
public ReactiveTextView(@NonNull Context context,
@NonNull AttributeSet attrs) {
super(context, attrs);
mReactiveValue.set(getText().toString());
}
public ReactiveTextView(@NonNull Context context,
@NonNull AttributeSet attrs,
@StyleRes int defStyle) {
super(context, attrs, defStyle);
mReactiveValue.set(getText().toString());
}
@TargetApi(21)
public ReactiveTextView(@NonNull Context context,
@NonNull AttributeSet attrs,
@AttrRes int defStyleAttr,
@StyleRes int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mReactiveValue.set(getText().toString());
}
@NonNull
public String getName() {
return "ReactiveEditText" + getId();
}
@Override // View
@UiThread
public void onDetachedFromWindow() {
if (mReactiveSource != null) {
mReactiveValue.unsubscribeSource("onDetachedFromWindow", mReactiveSource);
mReactiveSource = null;
}
super.onDetachedFromWindow();
}
@Override // View
@UiThread
public void onAttachedToWindow() {
onDetachedFromWindow();
subscribe();
super.onAttachedToWindow();
}
private void subscribe() {
mReactiveSource = mReactiveValue.subscribe(UI, this::setText);
}
/**
* Change the view model used to set the display
*
* @param reactiveValue the new view model
* @param fire push the current from of the view model to the screen after this action completes on the UI thread
*/
public void setReactiveValue(@NonNull ReactiveValue<String> reactiveValue,
boolean fire) {
AssertUtil.assertNotNull(mOrigin);
final String s = "setReactiveValue(" + reactiveValue.getName() + ")";
UI.execute(() -> {
if (mReactiveSource != null) {
reactiveValue.unsubscribeSource(s, mReactiveSource);
}
RCLog.v(this, s);
this.mReactiveValue = reactiveValue;
subscribe();
if (fire) {
reactiveValue.fire();
}
});
}
@NonNull
public ReactiveValue<String> getReactiveValue() {
return this.mReactiveValue;
}
@NonNull
@Override
public ImmutableValue<String> getOrigin() {
return mOrigin;
}
}