0

I having problems with Webview Jetpack Compose iFrame video full screen. I want to use this function in my coding but I dont know how to implement it to my coding.I new in android development. Need help.

This is my coding.

MainActivity.kt

package com.example.runningman

import android.os.Bundle
import android.view.View
import android.widget.FrameLayout
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.example.runningman.ui.theme.RunningManTheme
import com.google.accompanist.web.AccompanistWebChromeClient
import com.google.accompanist.web.rememberWebViewNavigator
import com.google.accompanist.web.rememberWebViewState
import com.google.accompanist.web.WebView
import com.google.accompanist.web.rememberWebViewStateWithHTMLData

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            // Calling the composable function
            // to display element and its contents
            MyContent(MainActivity())
        }
    }
}

@Composable
fun MyContent(activity: ComponentActivity) {
    val frameVideo = "<html><body><iframe width=\"300\" height=\"320\" src=\"https://www.youtube.com/embed/gTzSw_xVCWI\" title=\"Alec Benjamin - Must Have Been The Wind (Lyrics)\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen></iframe></body></html>"
    val state = rememberWebViewState(url = frameVideo)
    val navigator = rememberWebViewNavigator()
    val chromeClient = remember { CustomChromeClient(activity) }


    WebView(
        state = rememberWebViewStateWithHTMLData(data = frameVideo),
        modifier = Modifier.fillMaxSize(),
        navigator = navigator,
        chromeClient = chromeClient, //This is what concerns us for fullscreen mode
        onCreated = { webView ->
            webView.settings.javaScriptEnabled=true
            webView.loadData(frameVideo, "text/html", "utf-8")
            //configuring the webview settings here such as enabling javascript
        }
    )
}

class CustomChromeClient(val activity: ComponentActivity): AccompanistWebChromeClient() {

    var customView: View? = null

    override fun onHideCustomView() {
        (activity.window.decorView as FrameLayout).removeView(this.customView)
        this.customView = null

    }

    override fun onShowCustomView(paramView: View, paramCustomViewCallback: CustomViewCallback) {
        if (this.customView != null) {
            onHideCustomView()
            return
        }
        this.customView = paramView
        (activity.window.decorView as FrameLayout).addView(this.customView, FrameLayout.LayoutParams(-1, -1))
    }

}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:hardwareAccelerated="true"
        android:configChanges="orientation|screenSize"
        android:usesCleartextTraffic="true"
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.RunningMan"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.RunningMan">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

logcat

2023-08-29 22:25:13.610 17532-18686 Codec2Client            com.example.runningman               D  setOutputSurface -- failed to set consumer usage (6/BAD_INDEX)
2023-08-29 22:25:13.610 17532-18686 Codec2Client            com.example.runningman               D  setOutputSurface -- generation=17952769 consumer usage=0x900
2023-08-29 22:25:13.635 17532-18686 Codec2Client            com.example.runningman               D  Surface configure completed
2023-08-29 22:25:13.636 17532-18686 DMABUFHEAPS             com.example.runningman               I  Using DMA-BUF heap named: system
2023-08-29 22:25:13.693 17532-17532 System.err              com.example.runningman               W  java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.Window.getDecorView()' on a null object reference
2023-08-29 22:25:13.698 17532-17532 System.err              com.example.runningman               W      at com.example.runningman.CustomChromeClient.onShowCustomView(MainActivity.kt:71)
2023-08-29 22:25:13.699 17532-17532 System.err              com.example.runningman               W      at Qb.enterFullscreenModeForTab(chromium-TrichromeWebViewGoogle6432.apk-stable-567263637:155)
2023-08-29 22:25:13.699 17532-17532 System.err              com.example.runningman               W      at android.os.MessageQueue.nativePollOnce(Native Method)
2023-08-29 22:25:13.699 17532-17532 System.err              com.example.runningman               W      at android.os.MessageQueue.next(MessageQueue.java:335)
2023-08-29 22:25:13.699 17532-17532 System.err              com.example.runningman               W      at android.os.Looper.loopOnce(Looper.java:162)
2023-08-29 22:25:13.700 17532-17532 System.err              com.example.runningman               W      at android.os.Looper.loop(Looper.java:294)
2023-08-29 22:25:13.700 17532-17532 System.err              com.example.runningman               W      at android.app.ActivityThread.main(ActivityThread.java:8177)
2023-08-29 22:25:13.700 17532-17532 System.err              com.example.runningman               W      at java.lang.reflect.Method.invoke(Native Method)
2023-08-29 22:25:13.701 17532-17532 System.err              com.example.runningman               W      at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
2023-08-29 22:25:13.701 17532-17532 System.err              com.example.runningman               W      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
2023-08-29 22:25:13.715 17532-17532 chromium                com.example.runningman               A  [FATAL:jni_android.cc(289)] Please include Java exception stack in crash report
2023-08-29 22:25:14.812 17532-17532 libc                    com.example.runningman               A  Fatal signal 5 (SIGTRAP), code 128 (SI_KERNEL), fault addr 0x0 in tid 17532 (mple.runningman), pid 17532 (mple.runningman)
---------------------------- PROCESS STARTED (18735) for package com.example.runningman ----------------------------
2023-08-29 22:25:15.696 18733-18733 DEBUG                   pid-18733                            A  Cmdline: com.example.runningman
2023-08-29 22:25:15.696 18733-18733 DEBUG                   pid-18733                            A  pid: 17532, tid: 17532, name: mple.runningman  >>> com.example.runningman <<<
---------------------------- PROCESS ENDED (17532) for package com.example.runningman ----------------------------

I expected this full screen function are implemented in my coding

SyahmiSam
  • 5
  • 2

1 Answers1

0

First of all, if you're intending to use a WebView inside Jetpack Compose, it's much better to use this special library which is basically an optimized wrapper for WebViews in Jetpack Compose. Here's the link Google Accompanists WebView Compose

The usage is very simple:

1- Instead of using wrapping a standard WebView inside an AndroidView using factory, the library will do it for you, so do this instead:



@Composable
fun MyContent(activity: ComponentActivity) {
  val state = rememberWebViewState(url = "www.google.com")
  val navigator = rememberWebViewNavigator()
  val chromeClient = remember { CustomChromeClient(activity) }


  WebView(
    state = state,
    modifier = Modifier.fillMaxSize(),
    navigator = navigator,
    chromeClient = chromeClient, //This is what concerns us for fullscreen mode
    onCreated = { webView -> 
        //configuring the webview settings here such as enabling javascript
    }
  )
}

2- Now to answer your question and solve the problem, in order to make the webview go to fullscreen mode, you should implement a custom WebChromeClientand override some of its methods to control what happens when the view goes to fullscreen mode. In our case, I created a new class called CustomChromeClient which extends AccompanistWebChromeClient (which is basically an optimized WebChromeClient for Jetpack Compose), then I overrode the methods related to creating custom views, this is how it's done:

class CustomChromeClient(val activity: ComponentActivity): AccompanistWebChromeClient() {

    var customView: View? = null

    override fun onHideCustomView() {
        (activity.window.decorView as FrameLayout).removeView(this.customView)
        this.customView = null

    }

    override fun onShowCustomView(paramView: View, paramCustomViewCallback: CustomViewCallback) {
        if (this.customView != null) {
            onHideCustomView()
            return
        }
        this.customView = paramView
        (activity.window.decorView as FrameLayout).addView(this.customView, FrameLayout.LayoutParams(-1, -1))
    }

}

In principle: When you try to launch something from WebView into fullscreen mode (like a video on some website), it's not the WebView itself that goes into fullscreen mode, but actually, it shows that content in another webview which is passed as a parameter in those override methods as you can see, so we need to always keep a reference to it. So, when our main WebView gives us a new WebView which will be our fullscreen WebView, we can actually add it as a direct child to the whole FrameLayout for the activity (yes, every activity is basically a FrameLayout with stuff inside it), and voilà, you can show things that cover the whole screen using this trick, not just full screen webviews, but a lot of other stuff!

And that's it ! By using a custom chrome client and overriding those methods to control how fullscreen is executed, we make a WebView that supports fullscreen in Jetpack Compose.

yuroyami
  • 814
  • 6
  • 24
  • I try to follow your code given but I receive [error](https://prnt.sc/8E4K0mg3-xoi) – SyahmiSam Aug 25 '23 at 13:47
  • @SyahmiSam I modified the code to adapt it to your way of doing things, you should at least pass "activity" to your composable because we need it inside our WebChromeClient... I also removed the explicit "WebViewClient" parameter from the webview because we're not using a custom webviewclient, so the accompanist webview will use the default implementation for it. Try the modified code now – yuroyami Aug 25 '23 at 19:29
  • New code given, there is no errors but I still can't run app. Can you help me, what is the [problem](https://prnt.sc/I6A5qlLrOwQQ) – SyahmiSam Aug 27 '23 at 13:28
  • @SyahmiSam Bro, post the logcat crash stacktrace so we could identify the problem. There is nothing wrong with the code. – yuroyami Aug 27 '23 at 16:50
  • Now I can run my app but when I try to full screen for youtube video it goes crush. seems like have error on full screen function. [logcat](https://prnt.sc/rZRo7Tao-qkr) – SyahmiSam Aug 28 '23 at 05:38
  • @SyahmiSam Post your entire activity's jetpack compose code (edit your main post), as I said before the code is correct and it works just fine on my end. You're just calling it wrong – yuroyami Aug 29 '23 at 10:35
  • I have edit my main post. Hopefully you can solve my problem. – SyahmiSam Aug 29 '23 at 14:28
  • @SyahmiSam Why are you passing another instance of `MainActivity` as a parameter for your composable ? Passing `MainActivity()` means you're creating a new copy of your activity and passing it, and creating a new activity like that means you're creating an empty shell that is linked to nothing not even a window, so what you should be doing is passing your CURRENT main activity instance... to do that: you should pass the activity using "this": `MyContent(this@MainActivity)` . This way, it should work. – yuroyami Aug 29 '23 at 19:32
  • 1
    Now work. I appreciate it. – SyahmiSam Aug 30 '23 at 01:56