on develop help sign in
WIP: Initial Android support
6ac1193Ryan Trinkle
reflex-dom/cbits/MainWidget.cnew file+26
@@ -0,0 +1,26 @@
1+#include <jni.h>
2+#include <assert.h>
3+#include <android/log.h>
4+#include <HaskellActivity.h>
5+
6+void Reflex_Dom_Android_MainWidget_start(jobject activity, const char *url) {
7+ assert(activity);
8+ assert(url);
9+
10+ JNIEnv *env;
11+ jint attachResult = (*HaskellActivity_jvm)->AttachCurrentThread(HaskellActivity_jvm, &env, NULL);
12+ assert(attachResult == JNI_OK);
13+
14+ jclass cls = (*env)->FindClass(env, "org/reflexfrp/reflexdom/MainWidget");
15+ assert(cls);
16+ jmethodID startMainWidget = (*env)->GetStaticMethodID(env, cls, "startMainWidget", "(Landroid/app/Activity;Ljava/lang/String;)V");
17+ assert(startMainWidget);
18+
19+ jstring jurl = (*env)->NewStringUTF(env, url);
20+ assert(jurl);
21+ (*env)->CallStaticVoidMethod(env, cls, startMainWidget, activity, jurl);
22+ if((*env)->ExceptionOccurred(env)) {
23+ __android_log_write(ANDROID_LOG_DEBUG, "MainWidget", "startMainWidget exception");
24+ (*env)->ExceptionDescribe(env);
25+ }
26+}
reflex-dom/default.nix+8−3
@@ -1,18 +1,23 @@
1{ mkDerivation, base, bytestring, jsaddle-webkit2gtk, jsaddle-wkwebview, reflex
1{ mkDerivation, base, bytestring, jsaddle-webkit2gtk, jsaddle-wkwebview, reflex
2−, reflex-dom-core, stdenv, text, ghc
2+, reflex-dom-core, stdenv, text, ghc, hostPlatform, jsaddle-clib, android-activity ? null
3}:
3}:
4+let isAndroid = hostPlatform.libc == "bionic";
4−mkDerivation {
5+in mkDerivation {
5 pname = "reflex-dom";
6 pname = "reflex-dom";
6 version = "0.4";
7 version = "0.4";
7 src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
8 src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
8 libraryHaskellDepends = [
9 libraryHaskellDepends = [
9 base bytestring reflex reflex-dom-core text
10 base bytestring reflex reflex-dom-core text
10 ] ++ (if ghc.isGhcjs or false then [
11 ] ++ (if ghc.isGhcjs or false then [
11− ] else if stdenv.isDarwin then [
12+ ] else if hostPlatform.isDarwin then [
12 jsaddle-wkwebview
13 jsaddle-wkwebview
14+ ] else if isAndroid then [
15+ jsaddle-clib
16+ android-activity
13 ] else [
17 ] else [
14 jsaddle-webkit2gtk
18 jsaddle-webkit2gtk
15 ]);
19 ]);
20+ configureFlags = if isAndroid then [ "-fandroid" ] else [];
16 description = "Functional Reactive Web Apps with Reflex";
21 description = "Functional Reactive Web Apps with Reflex";
17 license = stdenv.lib.licenses.bsd3;
22 license = stdenv.lib.licenses.bsd3;
18}
23}
reflex-dom/java/org/reflexfrp/reflexdom/MainWidget.javanew file+33
@@ -0,0 +1,33 @@
1+package org.reflexfrp.reflexdom;
2+
3+import android.app.Activity;
4+import android.content.Context;
5+import android.view.ViewGroup.LayoutParams;
6+import android.view.Window;
7+import android.webkit.CookieManager;
8+import android.webkit.WebSettings;
9+import android.webkit.WebView;
10+import java.util.concurrent.SynchronousQueue;
11+
12+
13+public class MainWidget {
14+ private static void startMainWidget(Activity a, String url) {
15+ CookieManager.setAcceptFileSchemeCookies(true); //TODO: Can we do this just for our own WebView?
16+
17+ // Remove title and notification bars
18+ a.requestWindowFeature(Window.FEATURE_NO_TITLE);
19+
20+ final WebView wv = new WebView(a);
21+ wv.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
22+ a.setContentView(wv);
23+ WebSettings ws = wv.getSettings();
24+ ws.setJavaScriptEnabled(true);
25+ ws.setAllowFileAccessFromFileURLs(true);
26+ ws.setAllowUniversalAccessFromFileURLs(true);
27+ wv.setWebContentsDebuggingEnabled(true);
28+ // allow video to play without user interaction
29+ wv.getSettings().setMediaPlaybackRequiresUserGesture(false);
30+
31+ wv.loadUrl(url);
32+ }
33+}
reflex-dom/reflex-dom.cabal+17−2
@@ -15,6 +15,7 @@ extra-source-files: src/Reflex/Dom/Xhr/Foreign.hs
15 src/Reflex/Dom/WebSocket/Foreign.hs
15 src/Reflex/Dom/WebSocket/Foreign.hs
16 src/Reflex/Dom/Xhr/ResponseType.hs
16 src/Reflex/Dom/Xhr/ResponseType.hs
17 src/Reflex/Dom/Xhr/Exception.hs
17 src/Reflex/Dom/Xhr/Exception.hs
18+ java/org/reflexfrp/reflexdom/MainWidget.java
18
19
19flag webkit2gtk
20flag webkit2gtk
20 description: Use WebKit2 version of WebKitGTK.
21 description: Use WebKit2 version of WebKitGTK.
@@ -35,8 +36,21 @@ flag build-examples
35 default: False
36 default: False
36 manual: True
37 manual: True
37
38
39+flag android
40+ description: Build an Android app
41+ default: False
42+ manual: True
43+
38library
44library
39 hs-source-dirs: src
45 hs-source-dirs: src
46+ if flag(android)
47+ hs-source-dirs: src-android
48+ build-depends:
49+ android-activity == 0.1.*,
50+ data-default == 0.7.*,
51+ jsaddle-clib
52+ c-sources: cbits/MainWidget.c
53+ cpp-options: -DANDROID
40 build-depends:
54 build-depends:
41 base >= 4.7 && < 4.11,
55 base >= 4.7 && < 4.11,
42 bytestring == 0.10.*,
56 bytestring == 0.10.*,
@@ -52,8 +66,9 @@ library
52 build-depends:
66 build-depends:
53 jsaddle-webkit2gtk
67 jsaddle-webkit2gtk
54 else
68 else
69+ if !flag(android)
55− build-depends:
70+ build-depends:
56− jsaddle-webkitgtk
71+ jsaddle-webkitgtk
57
72
58 exposed-modules:
73 exposed-modules:
59 Reflex.Dom
74 Reflex.Dom
reflex-dom/src-android/Reflex/Dom/Android/MainWidget.hsnew file+13
@@ -0,0 +1,13 @@
1+{-# LANGUAGE ForeignFunctionInterface #-}
2+module Reflex.Dom.Android.MainWidget
3+ ( startMainWidget
4+ ) where
5+
6+import Android.HaskellActivity
7+import Data.ByteString
8+import Foreign.C.String
9+
10+startMainWidget :: HaskellActivity -> ByteString -> IO ()
11+startMainWidget a url = useAsCString url $ startMainWidget_ a
12+
13+foreign import ccall safe "Reflex_Dom_Android_MainWidget_start" startMainWidget_ :: HaskellActivity -> CString -> IO ()
reflex-dom/src/Reflex/Dom/Internal.hs+14
@@ -21,6 +21,20 @@ run :: a -> a
21run = id
21run = id
22#elif defined(MIN_VERSION_jsaddle_wkwebview)
22#elif defined(MIN_VERSION_jsaddle_wkwebview)
23import Language.Javascript.JSaddle.WKWebView (run)
23import Language.Javascript.JSaddle.WKWebView (run)
24+#elif defined(ANDROID)
25+import Android.HaskellActivity
26+import Control.Monad
27+import Control.Concurrent
28+import Data.Default
29+import Data.String
30+import Reflex.Dom.Android.MainWidget
31+run _ = do
32+ continueWithCallbacks $ traceActivityCallbacks $ def
33+ { _activityCallbacks_onCreate = \_ -> do
34+ a <- getHaskellActivity
35+ startMainWidget a $ fromString "data:,Hello%2C%20World!" -- "file:///android_asset/index.html"
36+ }
37+ forever $ threadDelay 1000000000
24#else
38#else
25import Language.Javascript.JSaddle.WebKitGTK (run)
39import Language.Javascript.JSaddle.WebKitGTK (run)
26#endif
40#endif