Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not work on android #371

Open
davidgvf opened this issue Nov 25, 2024 · 10 comments
Open

Not work on android #371

davidgvf opened this issue Nov 25, 2024 · 10 comments

Comments

@davidgvf
Copy link

When you swipe to make the signature, it only draws a dot

RN version 0.76.1
react-native-signature-canvas version 4.7.2

const SharedSignatureCanvas = (props: SharedSignatureCanvasProps) => {
    const ref = useRef<SignatureViewRef>(null);

    const [isReady, setIsReady] = useState(false);

    useEffect(() => {
        const preloadTimeout = setTimeout(() => setIsReady(true), 500);

        return () => clearTimeout(preloadTimeout);
    }, []);

    const handleSignature = (signatureParam: string) => {
        props.handleSignature(signatureParam);
    };

    const readSignature = () => {
        if (ref.current) {
            ref.current.readSignature();
        }
    };

    const imgWidth = '100%';
    const imgHeight = '100%';
    const style = `
    .m-signature-pad {
        border: 0px #fff;
        position: absolute;
        width: ${Dimensions.get('window').width}px;
        margin: auto;
        top: 0;
        background-color: #fff;
        box-shadow: 0 1px 4px rgba(0, 0, 0, 0.27), 0 0 40px rgba(0, 0, 0, 0.08) inset;
        overflow: hidden;
    }
    .m-signature-pad--body {
        border: none;
    }
    .m-signature-pad--footer {
        display: none;
        margin: 0px;
        padding: 0px;
    }
    body, html {
        width: ${imgWidth}px;
        height: ${imgHeight}px;
    }
`;

    if (!isReady) {
        return (
            <WebView
                source={{ html: '<html lang="es"><body></body></html>' }}
                style={{ width: 0, height: 0 }} // Invisible
            />
        );
    }

    return (
        <View style={{ flex: 1, minHeight: 250 }}>
            <SignatureScreen
                ref={ref}
                key={'ex-1-sign'}
                onOK={handleSignature}
                webStyle={style}
                onEnd={readSignature}
                style={{ borderColor: '#0066BD', borderWidth: 1 }}
            />
        </View>
    );
};
@brunossaless
Copy link

news?

@hayder-nexgen
Copy link

any update?

@davidgvf
Copy link
Author

Not wroked for me because my signature canva was wrap into touchablewithoutfeedback

@remacr
Copy link

remacr commented Feb 6, 2025

I was running into the same issue using RN 0.76.5 with the new arch enabled. The problem also relies on[email protected], which is still not ready to run on the new architecture. If you check the LogCat of your app when you start drawing into the component, you should see the error pointing to the WebView not using the main thread to post the messages. Therefore, the internal commands (BEGIN, END ....etc) can't be read in the RN layer.

java.lang.Throwable: A WebView method was called on thread 'JavaBridge'. All WebView methods must be called on the same thread. (Expected Looper Looper (main, tid 1) {37fd6bf} called on Looper (JavaBridge, tid 563) {952c249}, FYI main Looper is Looper (main, tid 1) {37fd6bf})
                                                                                                    	at android.webkit.WebView.checkThread(WebView.java:2350)
                                                                                                    	at android.webkit.WebView.getUrl(WebView.java:1427)
                                                                                                    	at com.reactnativecommunity.webview.RNCWebView$RNCWebViewBridge.postMessage(RNCWebView.java:458)
                                                                                                    	at org.chromium.base.SystemMessageHandler.nativeDoRunLoopOnce(Native Method)
                                                                                                    	at org.chromium.base.SystemMessageHandler.handleMessage(SystemMessageHandler.java:9)
                                                                                                    	at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                                                    	at android.os.Looper.loop(Looper.java:154)

Here is the solution, first add a safe check to react-native-signature-canvas/h5/js/app.js to only call window.ReactNativeWebView.postMessage when it exists:

I created the following diff with npx patch-package react-native-signature-canvas

react-native-signature-canvas+4.7.2.patch

diff --git a/node_modules/react-native-signature-canvas/h5/js/app.js b/node_modules/react-native-signature-canvas/h5/js/app.js
index e4690cd..8f16939 100755
--- a/node_modules/react-native-signature-canvas/h5/js/app.js
+++ b/node_modules/react-native-signature-canvas/h5/js/app.js
@@ -22,12 +22,22 @@ export default `
         imgData && signaturePad.fromData(imgData);
     }
     
+    function safePostMessage(data) {
+        setTimeout(() => {
+            if (window.ReactNativeWebView && window.ReactNativeWebView.postMessage) {
+                window.ReactNativeWebView.postMessage(data);
+            } else if (window.postMessage) {
+                window.postMessage(data);
+            }
+        }, 0)
+    }
+
     window.onresize = resizeCanvas;
     resizeCanvas();
     
     signaturePad = new SignaturePad(canvas, {
-        onBegin: () => window.ReactNativeWebView.postMessage("BEGIN"),
-        onEnd: () => window.ReactNativeWebView.postMessage("END"),
+        onBegin: () => safePostMessage("BEGIN"),
+        onEnd: () => safePostMessage("END"),
         penColor: '<%penColor%>',
         backgroundColor: '<%backgroundColor%>',
         dotSize: <%dotSize%>,
@@ -37,50 +47,50 @@ export default `
 
     function clearSignature () {
         signaturePad.clear();
-        window.ReactNativeWebView.postMessage("CLEAR");
+        safePostMessage("CLEAR");
     }
     
     function undo() {
         signaturePad.undo();
-        window.ReactNativeWebView.postMessage("UNDO");
+        safePostMessage("UNDO");
     }
     
     function redo() {
         signaturePad.redo();
-        window.ReactNativeWebView.postMessage("REDO");
+        safePostMessage("REDO");
       }
 
     function changePenColor(color) {
         signaturePad.penColor = color;
-        window.ReactNativeWebView.postMessage("CHANGE_PEN");
+        safePostMessage("CHANGE_PEN");
     }
 
     function changePenSize(minW, maxW) {
       signaturePad.minWidth = minW;
       signaturePad.maxWidth = maxW;
-      window.ReactNativeWebView.postMessage("CHANGE_PEN_SIZE");
+      safePostMessage("CHANGE_PEN_SIZE");
     }
     
     function getData () {
         var data = signaturePad.toData();
-        window.ReactNativeWebView.postMessage(JSON.stringify(data));
+        safePostMessage(JSON.stringify(data));
     }
 
     function draw() {
       signaturePad.draw();
-      window.ReactNativeWebView.postMessage("DRAW");
+      safePostMessage("DRAW");
     }
 
     function erase() {
       signaturePad.erase();
-      window.ReactNativeWebView.postMessage("ERASE");
+      safePostMessage("ERASE");
     }
 
     function cropWhitespace(url) {
         var myImage = new Image();
         myImage.crossOrigin = "Anonymous";
         myImage.onload = function(){
-            window.ReactNativeWebView.postMessage(removeImageBlanks(myImage)); //Will return cropped image data
+            safePostMessage(removeImageBlanks(myImage)); //Will return cropped image data
         }
         myImage.src = url;
 
@@ -170,10 +180,10 @@ export default `
 
     function readSignature()  {
         if (signaturePad.isEmpty()) {
-            window.ReactNativeWebView.postMessage("EMPTY");
+            safePostMessage("EMPTY");
         } else {
             var url = signaturePad.toDataURL('<%imageType%>');
-            trimWhitespace? cropWhitespace(url): window.ReactNativeWebView.postMessage(url);
+            trimWhitespace ? cropWhitespace(url) : safePostMessage(url);
             if (autoClear) signaturePad.clear();
         }
     }
diff --git a/node_modules/react-native-signature-canvas/index.js b/node_modules/react-native-signature-canvas/index.js
index 7f9f0e8..12ab8af 100644
--- a/node_modules/react-native-signature-canvas/index.js
+++ b/node_modules/react-native-signature-canvas/index.js
@@ -50,20 +50,20 @@ const SignatureView = forwardRef(
       minWidth = 0.5,
       maxWidth = 2.5,
       nestedScrollEnabled = false,
-      showsVerticalScrollIndicator= true,
-      onOK = () => {},
-      onEmpty = () => {},
-      onClear = () => {},
-      onUndo = () => {},
-      onRedo = () => {},
-      onDraw = () => {},
-      onErase = () => {},
-      onGetData = () => {},
-      onChangePenColor = () => {},
-      onChangePenSize = () => {},
-      onBegin = () => {},
-      onEnd = () => {},
-      onLoadEnd = () => {},
+      showsVerticalScrollIndicator = true,
+      onOK = () => { },
+      onEmpty = () => { },
+      onClear = () => { },
+      onUndo = () => { },
+      onRedo = () => { },
+      onDraw = () => { },
+      onErase = () => { },
+      onGetData = () => { },
+      onChangePenColor = () => { },
+      onChangePenSize = () => { },
+      onBegin = () => { },
+      onEnd = () => { },
+      onLoadEnd = () => { },
       overlayHeight = 0,
       overlayWidth = 0,
       overlaySrc = null,
@@ -258,13 +258,22 @@ const SignatureView = forwardRef(
       console.warn("WebView error: ", nativeEvent);
 
     const handleLoadEnd = () => {
-        setLoading(false);
-        onLoadEnd();
+      setLoading(false);
+      onLoadEnd();
     }
 
+    // Patch to force reload the webview when using the new arch when the component has already been mount before.
+    // It solves the infinite loading indicator issue.
+    const [key, setKey] = useState(new Date().getTime());
+
+    useEffect(() => {
+      setKey(new Date().getTime());
+    }, []);
+
     return (
       <View style={[styles.webBg, style]}>
         <WebView
+          key={`signature-screen-${key}`}
           bounces={false}
           style={[webviewContainerStyle]}
           scrollEnabled={scrollable}

Lastly, patch the react-native-webview making sure it runs in the main thread by using the View post method. I created the following diff with npx patch-package react-native-webview

react-native-webview+13.13.2.patch

diff --git a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebView.java b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebView.java
index 31cf298..af8b2a3 100644
--- a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebView.java
+++ b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebView.java
@@ -449,7 +455,7 @@ public class RNCWebView extends WebView implements LifecycleEventListener {
         @JavascriptInterface
         public void postMessage(String message) {
             if (mWebView.getMessagingEnabled()) {
-                mWebView.onMessage(message, mWebView.getUrl());
+                post(() -> mWebView.onMessage(message, mWebView.getUrl()));
             } else {
                 FLog.w(TAG, "ReactNativeWebView.postMessage method was called but messaging is disabled. Pass an onMessage handler to the WebView.");
             }

@yossularko
Copy link

I was running into the same issue using RN 0.76.5 with the new arch enabled. The problem also relies on[email protected], which is still not ready to run on the new architecture. If you check the LogCat of your app when you start drawing into the component, you should see the error pointing to the WebView not using the main thread to post the messages. Therefore, the internal commands (BEGIN, END ....etc) can't be read in the RN layer.

java.lang.Throwable: A WebView method was called on thread 'JavaBridge'. All WebView methods must be called on the same thread. (Expected Looper Looper (main, tid 1) {37fd6bf} called on Looper (JavaBridge, tid 563) {952c249}, FYI main Looper is Looper (main, tid 1) {37fd6bf})
                                                                                                    	at android.webkit.WebView.checkThread(WebView.java:2350)
                                                                                                    	at android.webkit.WebView.getUrl(WebView.java:1427)
                                                                                                    	at com.reactnativecommunity.webview.RNCWebView$RNCWebViewBridge.postMessage(RNCWebView.java:458)
                                                                                                    	at org.chromium.base.SystemMessageHandler.nativeDoRunLoopOnce(Native Method)
                                                                                                    	at org.chromium.base.SystemMessageHandler.handleMessage(SystemMessageHandler.java:9)
                                                                                                    	at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                                                    	at android.os.Looper.loop(Looper.java:154)

Here is the solution, first add a safe check to react-native-signature-canvas/h5/js/app.js to only call window.ReactNativeWebView.postMessage when it exists:

I created the following diff with npx patch-package react-native-signature-canvas

react-native-signature-canvas+4.7.2.patch

diff --git a/node_modules/react-native-signature-canvas/h5/js/app.js b/node_modules/react-native-signature-canvas/h5/js/app.js
index e4690cd..8f16939 100755
--- a/node_modules/react-native-signature-canvas/h5/js/app.js
+++ b/node_modules/react-native-signature-canvas/h5/js/app.js
@@ -22,12 +22,22 @@ export default `
imgData && signaturePad.fromData(imgData);
}

  • function safePostMessage(data) {

  •    setTimeout(() => {
    
  •        if (window.ReactNativeWebView && window.ReactNativeWebView.postMessage) {
    
  •            window.ReactNativeWebView.postMessage(data);
    
  •        } else if (window.postMessage) {
    
  •            window.postMessage(data);
    
  •        }
    
  •    }, 0)
    
  • }

  • window.onresize = resizeCanvas;
    resizeCanvas();

    signaturePad = new SignaturePad(canvas, {

  •    onBegin: () => window.ReactNativeWebView.postMessage("BEGIN"),
    
  •    onEnd: () => window.ReactNativeWebView.postMessage("END"),
    
  •    onBegin: () => safePostMessage("BEGIN"),
    
  •    onEnd: () => safePostMessage("END"),
       penColor: '<%penColor%>',
       backgroundColor: '<%backgroundColor%>',
       dotSize: <%dotSize%>,
    

@@ -37,50 +47,50 @@ export default `

 function clearSignature () {
     signaturePad.clear();
  •    window.ReactNativeWebView.postMessage("CLEAR");
    
  •    safePostMessage("CLEAR");
    

    }

    function undo() {
    signaturePad.undo();

  •    window.ReactNativeWebView.postMessage("UNDO");
    
  •    safePostMessage("UNDO");
    

    }

    function redo() {
    signaturePad.redo();

  •    window.ReactNativeWebView.postMessage("REDO");
    
  •    safePostMessage("REDO");
     }
    

    function changePenColor(color) {
    signaturePad.penColor = color;

  •    window.ReactNativeWebView.postMessage("CHANGE_PEN");
    
  •    safePostMessage("CHANGE_PEN");
    

    }

    function changePenSize(minW, maxW) {
    signaturePad.minWidth = minW;
    signaturePad.maxWidth = maxW;

  •  window.ReactNativeWebView.postMessage("CHANGE_PEN_SIZE");
    
  •  safePostMessage("CHANGE_PEN_SIZE");
    

    }

    function getData () {
    var data = signaturePad.toData();

  •    window.ReactNativeWebView.postMessage(JSON.stringify(data));
    
  •    safePostMessage(JSON.stringify(data));
    

    }

    function draw() {
    signaturePad.draw();

  •  window.ReactNativeWebView.postMessage("DRAW");
    
  •  safePostMessage("DRAW");
    

    }

    function erase() {
    signaturePad.erase();

  •  window.ReactNativeWebView.postMessage("ERASE");
    
  •  safePostMessage("ERASE");
    

    }

    function cropWhitespace(url) {
    var myImage = new Image();
    myImage.crossOrigin = "Anonymous";
    myImage.onload = function(){

  •        window.ReactNativeWebView.postMessage(removeImageBlanks(myImage)); //Will return cropped image data
    
  •        safePostMessage(removeImageBlanks(myImage)); //Will return cropped image data
       }
       myImage.src = url;
    

@@ -170,10 +180,10 @@ export default `

 function readSignature()  {
     if (signaturePad.isEmpty()) {
  •        window.ReactNativeWebView.postMessage("EMPTY");
    
  •        safePostMessage("EMPTY");
       } else {
           var url = signaturePad.toDataURL('<%imageType%>');
    
  •        trimWhitespace? cropWhitespace(url): window.ReactNativeWebView.postMessage(url);
    
  •        trimWhitespace ? cropWhitespace(url) : safePostMessage(url);
           if (autoClear) signaturePad.clear();
       }
    
    }
    diff --git a/node_modules/react-native-signature-canvas/index.js b/node_modules/react-native-signature-canvas/index.js
    index 7f9f0e8..12ab8af 100644
    --- a/node_modules/react-native-signature-canvas/index.js
    +++ b/node_modules/react-native-signature-canvas/index.js
    @@ -50,20 +50,20 @@ const SignatureView = forwardRef(
    minWidth = 0.5,
    maxWidth = 2.5,
    nestedScrollEnabled = false,
  •  showsVerticalScrollIndicator= true,
    
  •  onOK = () => {},
    
  •  onEmpty = () => {},
    
  •  onClear = () => {},
    
  •  onUndo = () => {},
    
  •  onRedo = () => {},
    
  •  onDraw = () => {},
    
  •  onErase = () => {},
    
  •  onGetData = () => {},
    
  •  onChangePenColor = () => {},
    
  •  onChangePenSize = () => {},
    
  •  onBegin = () => {},
    
  •  onEnd = () => {},
    
  •  onLoadEnd = () => {},
    
  •  showsVerticalScrollIndicator = true,
    
  •  onOK = () => { },
    
  •  onEmpty = () => { },
    
  •  onClear = () => { },
    
  •  onUndo = () => { },
    
  •  onRedo = () => { },
    
  •  onDraw = () => { },
    
  •  onErase = () => { },
    
  •  onGetData = () => { },
    
  •  onChangePenColor = () => { },
    
  •  onChangePenSize = () => { },
    
  •  onBegin = () => { },
    
  •  onEnd = () => { },
    
  •  onLoadEnd = () => { },
     overlayHeight = 0,
     overlayWidth = 0,
     overlaySrc = null,
    

@@ -258,13 +258,22 @@ const SignatureView = forwardRef(
console.warn("WebView error: ", nativeEvent);

 const handleLoadEnd = () => {
  •    setLoading(false);
    
  •    onLoadEnd();
    
  •  setLoading(false);
    
  •  onLoadEnd();
    

    }

  • // Patch to force reload the webview when using the new arch when the component has already been mount before.

  • // It solves the infinite loading indicator issue.

  • const [key, setKey] = useState(new Date().getTime());

  • useEffect(() => {

  •  setKey(new Date().getTime());
    
  • }, []);

  • return (
    <View style={[styles.webBg, style]}>
    <WebView

  •      key={`signature-screen-${key}`}
         bounces={false}
         style={[webviewContainerStyle]}
         scrollEnabled={scrollable}
    

Lastly, patch the react-native-webview making sure it runs in the main thread by using the View post method. I created the following diff with npx patch-package react-native-webview

react-native-webview+13.13.2.patch

diff --git a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebView.java b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebView.java
index 31cf298..af8b2a3 100644
--- a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebView.java
+++ b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebView.java
@@ -449,7 +455,7 @@ public class RNCWebView extends WebView implements LifecycleEventListener {
@JavascriptInterface
public void postMessage(String message) {
if (mWebView.getMessagingEnabled()) {

  •            mWebView.onMessage(message, mWebView.getUrl());
    
  •            post(() -> mWebView.onMessage(message, mWebView.getUrl()));
           } else {
               FLog.w(TAG, "ReactNativeWebView.postMessage method was called but messaging is disabled. Pass an onMessage handler to the WebView.");
           }
    

I'm using RN 0.76.7 and react-native-webview 13.13.2 works perfectly without applying this patch. (tested on android only)

@next6leo
Copy link

next6leo commented Mar 7, 2025

same not working, previously working

@davidgvf
Copy link
Author

davidgvf commented Mar 7, 2025

@next6leo a mi se soluciono actualizando la lib de react native web view, ya que subieron una version con un error

@next6leo
Copy link

next6leo commented Mar 7, 2025

@davidgvf what webview version?

@davidgvf
Copy link
Author

davidgvf commented Mar 7, 2025

@davidgvf what webview version?

With this version for me its fix

"react-native-webview": "13.13.2"

@next6leo
Copy link

next6leo commented Mar 7, 2025

thanks @davidgvf i try install see

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants