package com.gaiagps.iburn.js;
import android.content.Context;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.subjects.PublishSubject;
import timber.log.Timber;
/**
* Created by dbro on 8/6/15.
*/
public class JSEvaluator {
private static final PublishSubject jsEvaluatorSubject = PublishSubject.create();
private static final AtomicBoolean initializedJsEvaluator = new AtomicBoolean();
private static JSEvaluator jsEvaluator;
private WebView webView;
private Evaluator evaluator;
// <editor-fold desc="Private API">
public static Observable<JSEvaluator> getInstance(Context context) {
return getInstance("", context);
}
public static Observable<JSEvaluator> getInstance(String url, Context context) {
if (!initializedJsEvaluator.getAndSet(true)) {
// We need to start initializing the singleton
new JSEvaluator(url, new WebView(context), evaluator -> jsEvaluatorSubject.onNext(evaluator));
} else if (jsEvaluator != null) {
// We've already initialized the singleton
return Observable.just(jsEvaluator);
}
// Another caller started initialization, await its result
return jsEvaluatorSubject.first();
}
private JSEvaluator(String url, WebView webView, JSEvaluatorCallback callback) {
this.webView = webView;
this.evaluator = new LegacyEvaluator(webView);
// Not sure I want to have two different javascript pipelines
// especially bc there seems to be some inconsistent behavior:
// https://github.com/evgenyneu/js-evaluator-for-android/issues/4
// this.evaluator = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT ?
// new KitKatEvaluator() : new LegacyEvaluator(webView);
setupWebView(url, callback);
}
private void setupWebView(String url, JSEvaluatorCallback callback) {
webView.setWillNotDraw(true);
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
Timber.d("onPageFinished");
Observable.just(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(ignored -> {
Timber.d("Notifying callback");
jsEvaluator = JSEvaluator.this;
callback.onReady(JSEvaluator.this);
});
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
Timber.e("Webview error " + errorCode + " desc " + description);
}
});
Timber.d("Loading html");
webView.loadUrl(url);
}
// </editor-fold desc="Private API">
// <editor-fold desc="Public API">
public void reverseGeocode(final double latitude, final double longitude, Evaluator.EvaluatorCallback callback) {
evaluator.evaluate(webView,
String.format("return window.coder.reverse(%f, %f);", latitude, longitude),
callback);
}
public void forwardGeocode(final String playaAddress, Evaluator.EvaluatorCallback callback) {
evaluator.evaluate(webView,
String.format("return window.coder.forwardAsString(\"%s\");", playaAddress),
callback);
}
// </editor-fold desc="Public API">
public interface JSEvaluatorCallback {
void onReady(JSEvaluator evaluator);
}
public interface Evaluator {
interface EvaluatorCallback {
void onResult(String result);
}
void evaluate(WebView webView, String script, EvaluatorCallback callback);
}
}