Jetpack Compose WebView ImePadding

322 Views Asked by At

I have a component which has a webview that uses quill.js to create a rich text editor .the component itself part of another view which has multiple elements which places this view to the bottom when I tap the webview which in turn makes the quill editor editable.Ideally due adjustResize the view should scroll up as i am also using imePadding() but for webview it doesn't work here is my editor

@SuppressLint("SetJavaScriptEnabled", "ClickableViewAccessibility")
@Composable
fun UpdateEditor(
    htmlString: String,
    modifier: Modifier = Modifier,
    onTextChangeCallback: (Int, String) -> Unit,
    enableButton: (Boolean) -> Unit,
    projectId: MutableInt,
    date: MutableState<Date>,
    hideKeyboardTrigger: Boolean,
    update: MutableState<Update?>
) {
    val context = LocalContext.current
    val webView = remember {
        WebView(context).apply {
            settings.apply {
                javaScriptEnabled = true
                domStorageEnabled = true
                defaultTextEncodingName = "UTF-8";
            }
            addJavascriptInterface(
                DailyUpdateScriptInterface(
                    onTextChangeCallback,
                    projectId,
                    enableButton
                ),
                "Editor"
            )
            isVerticalScrollBarEnabled = false
            isHorizontalScrollBarEnabled = false
            overScrollMode = WebView.OVER_SCROLL_ALWAYS
            viewTreeObserver.addOnGlobalLayoutListener {
                if (!isKeyboardShown(rootView)) {
                    evaluateJavascript("document.activeElement.blur();", null)
                }
            }
            webViewClient = object : WebViewClient() {
                override fun onPageFinished(view: WebView?, url: String?) {
                    super.onPageFinished(view, url)
                    // Handle page loading finished
                    val placeholder = "Add Update ..."
                    val jsScript = """
        javascript:(function() {
            window.setQuillPlaceholder('$placeholder');
            quill.setSelection(selection);
        })()
        """
                    view?.evaluateJavascript(jsScript, null)
                }
            }

        }
    }

    Box(modifier.imePadding()) {
        AndroidView(
            factory = { webView },
        ) {
            // Check if the WebView is loading the URL for the first time
            if (it.url == null) {
                it.loadUrl("file:///android_asset/editor.html")
            }
            it.setOnTouchListener { v, event ->
                // Handle touch events to adjust the viewport or other necessary actions
                false
            }
        }
    }
    LaunchedEffect(htmlString) {
        val jsScript = """
                        javascript:(function() { 
                        window.setQuillContent('${htmlString.replace("\n", "<br>")}'); 
                        }
                        )()
                    """
        webView.evaluateJavascript(jsScript, null)
    }
}

here is my html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
    <link href="https://cdn.quilljs.com/1.1.6/quill.snow.css" rel="stylesheet">
    <link href='https://fonts.googleapis.com/css?family=Inter' rel='stylesheet'>
    <script src="https://cdn.quilljs.com/1.1.6/quill.js"></script>
    <style>
        body {
            font-family: 'Inter';
            font-size: 14px;
            margin: 0;
            padding: 0;
        }

        #toolbar {
            position: sticky;
            top: 0;
            display: flex;
            flex-wrap: wrap;
            width: 100%;
            background: #ffffff;
            z-index: 100;
            border: none;
        }

        #editor-container {
            overflow-y: scroll;
            box-sizing: border-box; /* Include padding and border in total width/height */
        }

        #editor {
            height: 100%;
            border: none;
        }

        .quill .ql-snow {
            border: none;
        }

        .quill .ql-editor {
            line-height: unset;
            height: 100%;
            border: none;
            font-family: inherit;
            background-color: none;
        }

        .quill .ql-editor ul,
        .quill .ql-editor ol {
            padding-left: 0;
        }

        .quill .ql-tooltip {
            margin-left: 140px;
            z-index: 2;
        }

        .ql-cursor {
            color: #FF5C8AFF !important;
        }

        .editor-text h1,
        .editor-text h2,
        .editor-text h3,
        .editor-text p,
        .editor-text blockquote {
            margin-block-start: 6px;
            margin-block-end: 6px;
            margin-inline-start: 0;
            margin-inline-end: 0;
        }

        .editor-text ul,
        .editor-text ol {
            padding-inline-start: 12px;
        }

        .editor-text ul li,
        .editor-text ol li {
            word-break: break-word;
            margin: 6px 0;
            font-size: 14px;
        }

        .editor-text p {
            white-space: pre-line;
            font-size: 14px;
            word-break: break-word;
            overflow-y: scroll;
        }

        /* Example media query for screens smaller than 600px */
        @media (max-width: 600px) {
            #editor-container {
                padding: 0; /* Adjust or remove padding for smaller screens */
            }
        }

        /* Dark mode styles */
        @media (prefers-color-scheme: dark) {
            body {
                background: #333333;
                color: white;
                font-family: 'Inter';
                font-size: 14px;
                margin: 0;
                padding: 0;
            }
            #toolbar {
                position: sticky;
                top: 0;
                display: flex;
                flex-wrap: wrap;
                width: 100%;
                background: #333333;
                z-index: 100;
                border: none;
            }
            .ql-toolbar button svg {
                filter: invert(1);
            }
            .ql-snow .ql-picker.ql-header .ql-picker-label,
            .ql-snow .ql-picker.ql-header .ql-picker-item {
                color: #ffffff;
            }

            /* Styling the dropdown background color (optional) */
            .ql-snow .ql-picker.ql-header .ql-picker-options {
                background-color: #1E1E1E; /* or any other suitable dark color */
            }

            #editor {
                color: #ffffff;
            }
            .ql-editor.ql-blank::before {
                color: #CCCCCC;  /* Choose the color that fits the standard mode best */
            }
        }

    </style>
</head>

<body>
<div id="container">
    <div id="toolbar">
        <!-- Add buttons for the features you want -->
        <select class="ql-header">
            <option value="1">Heading 1</option>
            <option value="2">Heading 2</option>
            <option value="3">Heading 3</option>
            <option value="">Normal</option>
        </select>
        <button class="ql-bold">Bold</button>
        <button class="ql-italic">Italic</button>
        <button class="ql-link">Link</button>
        <button class="ql-list" value="ordered">Ordered List</button>
        <button class="ql-list" value="bullet">Unordered List</button>
    </div>
    <div id="editor-container">
        <div id="editor"></div>
    </div>
</div>
<!-- Create the toolbar container -->
<script>

      var quill = new Quill('#editor', {
        modules: {
          toolbar: '#toolbar'
        },
        placeholder: 'Enter Update...',
        theme: 'snow' // or 'bubble',
      });
      // New function to set the editor content

      let selection = quill.getSelection();
      window.setQuillContent = function(content) {
        var contentWithoutLineBreaks = content.replace(/<br>/g, '');
        var delta = quill.clipboard.convert(content);
        quill.setContents(delta, 'silent');
        quill.setSelection(selection);
      }
      window.getQuillContent = function() {
        return quill.root.innerHTML;
      }
      quill.on('text-change', function(delta, oldDelta, source) {
        if (source == 'user') {
          var htmlContent = quill.root.innerHTML;
          // Replace AndroidFunction with your actual Android function for passing data to Android.
          Editor.onTextChange(htmlContent);
        }
      });

      function setQuillPlaceholder(placeholderText) {
        quill.container.dataset.placeholder = placeholderText;
        // Force Quill to recognize the change
        if (quill.root.dataset.placeholder !== undefined) {
            quill.root.dataset.placeholder = placeholderText;
        }
     }



      document.getElementById('editor').addEventListener('click', function() {
            quill.focus();
      });
      function setContainerHeight() {
        var toolbarHeight = 40; // replace this with actual toolbar height if dynamic
        var viewportHeight = window.innerHeight;
        var editorContainer = document.getElementById('editor-container');
        editorContainer.style.height = (viewportHeight - toolbarHeight - adjustment) + 'px';
      }
      setContainerHeight(); // initial set
      window.addEventListener('resize', setContainerHeight);

</script>
</body>
</html>

I have tried using

     val rootView = (context as? Activity)?.window?.decorView?.findViewById\<View\>.    (android.R.id.content)

    LaunchedEffect(rootView) {
        val listener = ViewTreeObserver.OnGlobalLayoutListener {
            val rect = Rect()
            rootView?.getWindowVisibleDisplayFrame(rect)
            val screenHeight = rootView?.height ?: 0
            val keypadHeight = screenHeight - rect.bottom
    
            if (keypadHeight > screenHeight * 0.15) {
                // Keyboard is opened
                listState.animateScrollToItem(0)
            } else {
                // Keyboard is closed
            }
        }
    
        rootView?.viewTreeObserver?.addOnGlobalLayoutListener(listener)
    
        onDispose {
            rootView?.viewTreeObserver?.removeOnGlobalLayoutListener(listener)
        }
    }

I have added imePadding to every parent view as well as tried using

UpdateEditor(
htmlString = htmlString.value,
modifier = Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState()),
onTextChangeCallback = onTextChangeCallback,
projectId = mutableProjectId,
enableButton = { enableButton = true },
date = date,
hideKeyboardTrigger = hideKeyboardTrigger,
update = mutableUpdate
)
0

There are 0 best solutions below