Android์—์„œ WebView๋ฅผ ํ†ตํ•ด Web๊ณผ ์†Œํ†ตํ•˜๊ธฐ

soojin
  • #WebView
  • #Android
  • #Video
  • #FullScreen

๋“ค์–ด๊ฐ€๋ฉฐ

์•ˆ๋…•ํ•˜์„ธ์š” ๋…ธ๋จธ์Šค์—์„œ ์•ˆ๋“œ๋กœ์ด๋“œ ๊ฐœ๋ฐœ์„ ํ•˜๊ณ  ์žˆ๋Š” ์–‘์ˆ˜์ง„์ž…๋‹ˆ๋‹ค. ์ด๋ฒˆ์—๋Š” WebView์™€ ๊ด€๋ จ๋œ ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ•˜๋ฉด์„œ ํ•™์Šตํ–ˆ๋˜ ๋‚ด์šฉ์„ ๊ณต์œ ํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

Android์—์„œ WebView ๋™์˜์ƒ ์ „์ฒดํ™”๋ฉด์ด ์•ˆ๋ผ์š”!

WebView๋กœ ๊ตฌ์„ฑ๋œ ํ™”๋ฉด์—์„œ ๋™์˜์ƒ ์ „์ฒดํ™”๋ฉด ๋ฒ„ํŠผ์ด ๋น„ํ™œ์„ฑํ™”๋˜์–ด ๋™์˜์ƒ์„ ์ „์ฒดํ™”๋ฉด์œผ๋กœ ๋ณผ ์ˆ˜ ์—†๋Š” ์ด์Šˆ๊ฐ€ ๋ฐœ๊ฒฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์•„๋ฟ”์‹ธ.. ios๋Š” OS์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ „์ฒดํ™”๋ฉด ๊ธฐ๋Šฅ์„ ์ œ๊ณต์„ ํ•ด์ฃผ์–ด์„œ ์ •์ƒ ์ž‘๋™๋˜๊ณ  ์žˆ์—ˆ์ง€๋งŒ Android๋Š” ๋ณ„๋„์˜ ๊ตฌํ˜„์„ ํ•ด์ฃผ์–ด์•ผ ํ™œ์„ฑํ™”๋˜๋Š” ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

Android Webview์—์„œ ๋™์˜์ƒ ์ „์ฒด๋ณด๊ธฐ ํ™œ์„ฑํ™”

WebChromeClient์˜ onShowCustomView()์™€ onHideCustomView() ๋ฅผ overrideํ•˜์—ฌ ์ „์ฒดํ™”๋ฉด ์ง„์ž…/ํ•ด์ œ ์‹œ์˜ ๋™์ž‘์„ ๊ตฌํ˜„ํ•ด ์ฃผ๋ฉด ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค.


    override fun onShowCustomView(view: View?, callback: CustomViewCallback?) {
        super.onShowCustomView(view, callback)
        // ์ „์ฒดํ™”๋ฉด view๋ฅผ ํ™”๋ฉด์— ๋ณด์—ฌ์ฃผ๋Š” ๋กœ์ง
    }
    
    
    override fun onHideCustomView() {
        super.onHideCustomView()
        // ์ „์ฒดํ™”๋ฉด view๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ๋กœ์ง
    }

  • onShowCustomView()์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜์–ด์˜ค๋Š” view๊ฐ€ ์ „์ฒดํ™”๋ฉด์œผ๋กœ ํ‘œ์‹œํ•  View ์ž…๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ..

์‹คํ–‰ํ•ด๋ณด์•˜๋”๋‹ˆ ์ „์ฒดํ™”๋ฉด ๊ธฐ๋Šฅ ์ž์ฒด๋Š” ๋™์ž‘ํ–ˆ์ง€๋งŒ, ๋™์˜์ƒ ๋น„์œจ์— ๊ด€๊ณ„์—†์ด ์„ธ๋กœ๋กœ๋งŒ ํ‘œ์‹œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์•ˆ๋“œ๋กœ์ด๋“œ ๋„ค์ดํ‹ฐ๋ธŒ์—์„œ ํ•  ์ˆ˜ ์žˆ๋Š” ์ด๋Ÿฐ์ €๋Ÿฐ ๋ฐฉ๋ฒ•์„ ์‹œ๋„ํ•ด ๋ณด์•˜์ง€๋งŒ ๊ฒฐ๊ตญ ์—…๋กœ๋“œ ์‹œ ์„œ๋ฒ„์— ์˜์ƒ ํฌ๊ธฐ๋ฅผ ์ €์žฅํ•˜์—ฌ ํ™œ์šฉํ•˜๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ํ•ด๊ฒฐํ•˜๋„๋ก ์ด์•ผ๊ธฐ๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ชจ๋ฐ”์ผํŒ€ ํŒ€์žฅ๋‹˜์€ ํ•œ๋งˆ๋””๋ฅผ ๋‚จ๊ธฐ๊ณ  ์„œ๋‘˜๋Ÿฌ ํšŒ์˜์‹ค๋กœ ๋– ๋‚˜์…จ์Šต๋‹ˆ๋‹ค.

โ€œํ”„๋ก ํŠธ ํŒ€์—๊ฒŒ ์˜์ƒ ํฌ๊ธฐ ์ „๋‹ฌํ•˜๋Š”๊ฑฐ ๋งŒ๋“ค์–ด ๋‹ฌ๋ผ๊ณ  ์š”์ฒญํ•ด์ฃผ์„ธ์š”~โ€

.

.

.

๊ทธ.. ๊ทธ๊ฒŒ ๋ญ์ฃ ?

WebView ๊ฐœ๋ฐœ ๊ฒฝํ—˜์ด ์—†๋Š” ์ž…์‚ฌ 3๊ฐœ์›” ์ฐจ ์‹ ์ž…์ด์—ˆ๋˜ ์ €๋Š” ๊ทธ๋งŒ ํ˜ผ๋ž€์— ๋น ์ง€๊ณ  ๋ง์•˜์Šต๋‹ˆ๋‹คโ€ฆ

  • ์„œ๋ฒ„์— ์ €์žฅ๋˜์–ด ์žˆ์„ ์˜์ƒ ํฌ๊ธฐ๋ฅผ ์™œ ํ”„๋ก ํŠธํŒ€์—๊ฒŒ ์ „๋‹ฌํ•ด๋‹ฌ๋ผ๊ณ  ์š”์ฒญํ•ด์•ผ ํ•˜๋Š” ๊ฑฐ์ง€????
  • ํฐ์—์„œ ๋ฌด์Šจ ์˜์ƒ์„ ๋ณด๋Š”์ง€ ์–ด๋–ป๊ฒŒ ์•Œ๊ณ  ์˜์ƒ ํฌ๊ธฐ ์ „๋‹ฌ์ด ๋˜๋Š” ๊ฑฐ์ง€???
  • ์• ์ดˆ์— ๋ญ˜ ์–ด๋–ค ํ˜•์‹์œผ๋กœ ๋งŒ๋“ค์–ด๋‹ฌ๋ผ๊ณ  ์š”์ฒญํ•ด์•ผ ํ•˜์ง€???

๋ธŒ๋ฆฟ์ง€ ์ธํ„ฐํŽ˜์ด์Šค

๋งˆ์นจ ํ”„๋ก ํŠธํŒ€ ์ˆ˜๋ฏผ๋‹˜์˜ ์›น๋ทฐ(WebView) ๊ฐœ๋ฐœ ์ ์šฉ๊ธฐ ๊ธฐ์ˆ ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŠธ์—์„œ ๊ด€๋ จ ๋‚ด์šฉ์„ ๋‹ค๋ฃจ๊ณ  ์žˆ์–ด์„œ ์ฝ์–ด ๋ณด์•˜๊ณ , ์›น๊ณผ ์•ฑ๊ฐ„์˜ ๋ธŒ๋ฆฟ์ง€ ์ธํ„ฐํŽ˜์ด์Šค์˜ ์กด์žฌ๋ฅผ ์•Œ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

JavaScript๋กœ Android ์ฝ”๋“œ ์‹คํ–‰์‹œํ‚ค๊ธฐ

JavaScript ์‚ฌ์šฉ ํ™œ์„ฑํ™”

webView.settings.javaScriptEnabled = true
  • WebView์˜ WebSettings๋ฅผ ํ†ตํ•ด JavaScript ์‚ฌ์šฉ์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • JavaScript๊ฐ€ Android ์•ฑ์„ ์ œ์–ดํ•˜๋Š” ๊ฒƒ์€ ์œ ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋ณด์•ˆ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ธฐ๋ณธ์ ์œผ๋กœ ๋น„ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
    • ex) WebView์— ํ‘œ์‹œ๋˜๋Š” ํŽ˜์ด์ง€์— ์•…์˜์ ์ธ JavaScript ์ฝ”๋“œ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ
  • ์•ˆ๋“œ๋กœ์ด๋“œ ๊ณต์‹ ๋ฌธ์„œ์—์„œ๋„ ์ง์ ‘ ์ž‘์„ฑํ•œ JavaScript ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‹คํ–‰์‹œํ‚ค๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ๋ฉด ๋ธŒ๋ผ์šฐ์ € ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ๋งํฌ๋ฅผ ์—ด๋„๋ก ๊ถŒ์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋…ธ๋จธ์Šค ํ”„๋ก ํŠธํŒ€ ํŒ€์›๋ถ„๋“ค์€ ๋‹น์—ฐํžˆ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์˜ต์…˜์„ ํ—ˆ์šฉํ•ด ์ค๋‹ˆ๋‹ค.

์ธํ„ฐํŽ˜์ด์Šค ์„ค๊ณ„

internal class FrommAppJsInterface(private val viewModel: InAppBrowserViewModel) {

    @JavascriptInterface
    fun openBrowser(url: String) {
        viewModel.action(InAppBrowserAction.OpenBrowser(url))
    }

    @JavascriptInterface
    fun openInAppBrowser(url: String) {
        viewModel.action(InAppBrowserAction.OpenInAppBrowser(url))
    }

    @JavascriptInterface
    fun closeInAppBrowser() {
        viewModel.action(InAppBrowserAction.CloseInAppBrowser)
    }
}
  • @JavascriptInterface ์–ด๋…ธํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ•จ์œผ๋กœ์จ WebView์˜ JavaScript ์ฝ”๋“œ๊ฐ€ FrommAppJsInterface ๊ฐ์ฒด์˜ ๋ฉ”์„œ๋“œ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

WebView์— ๋ธŒ๋ฆฟ์ง€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ž‘์„ฑํ•œ ํด๋ž˜์Šค ๊ฐ์ฒด ์ฃผ์ž…

webView.addJavascriptInterface(FrommAppJsInterface(inAppBrowserViewModel), "Fromm")
  • addJavascriptInterface๋ฅผ ํ†ตํ•ด ๊ฐ์ฒด๋ฅผ WebView์— ์ฃผ์ž…ํ•˜์—ฌ JavaScript์—์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  • ์ฃผ์ž…๋œ ๊ฐ์ฒด๋Š” ์›นํŽ˜์ด์ง€์˜ ๋ชจ๋“  ํ”„๋ ˆ์ž„์—์„œ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
    android({ url }) {
        window.Fromm.openBrowser(url);
    },
  • ์œ„ ์ฝ”๋“œ๋Š” Web ์ฝ”๋“œ์˜ ์ผ๋ถ€์ž…๋‹ˆ๋‹ค. WebView์—์„œ ์œ„ ์ปค๋งจ๋“œ๊ฐ€ ์‹คํ–‰๋˜๋ฉด ์ด์— ์ƒ์‘ํ•˜๋Š” ์•„๋ž˜์˜ ๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
  • openBrowser()์˜ ๊ฒฝ์šฐ์—๋Š” ์ธ์ž๋กœ ๋“ค์–ด์˜ค๋Š” url์„ ๋ธŒ๋ผ์šฐ์ €๋กœ ์—ฌ๋Š” ๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜๊ฒ ์Šต๋‹ˆ๋‹ค.
    @JavascriptInterface
    fun openBrowser(url: String) {
        viewModel.action(InAppBrowserAction.OpenBrowser(url))
    }

Android์—์„œ JavaScript ์ฝ”๋“œ ์‹คํ–‰์‹œํ‚ค๊ธฐ

๋ฐ˜๋Œ€๋กœ ์•ˆ๋“œ๋กœ์ด๋“œ์—์„œ webview.evaluateJavascript() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด Web์˜ JavaScript ์ฝ”๋“œ๋ฅผ ์‹คํ–‰์‹œํ‚ฌ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

webView.evaluateJavascript("javascript:nativeBack()", null)
  • ํ˜„์žฌ ๋กœ๋“œ๋œ ์›น ํŽ˜์ด์ง€์—์„œ JavaScript ์ฝ”๋“œ๋ฅผ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    • resultCallback์„ ๋“ฑ๋กํ•˜๋ฉด ์‹คํ–‰ ๊ฒฐ๊ณผ๊ฐ€ ์ฝœ๋ฐฑ์œผ๋กœ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์‹œ ์ฝ”๋“œ๋Š” Android์—์„œ ์‹œ์Šคํ…œ ๋„ค๋น„๊ฒŒ์ด์…˜์˜ ๋ฐฑ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ JavaScript์˜ nativeBack() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.
  • ์•ˆ๋“œ๋กœ์ด๋“œ์—์„œ ํ˜ธ์ถœํ•  ๋•Œ์—๋Š” WebView์™€์˜ ํ˜ธํ™˜์„ฑ์„ ์œ„ํ•ด ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด์Šˆ ํ•ด๊ฒฐ

  • ๋™์˜์ƒ ์ „์ฒด๋ณด๊ธฐ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ์ „์ฒดํ™”๋ฉด์— ์ง„์ž…ํ•  ๋•Œ evaluateJavascript์œผ๋กœ ์›น์˜ getVideoDimension() ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ฝœ๋ฐฑ์œผ๋กœ ์˜์ƒ ํฌ๊ธฐ๋ฅผ ๋ฐ›์•„์˜ค๋„๋ก ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

Web์˜ getVideoDimension() ์ฝ”๋“œ ์ผ๋ถ€

    window.getVideoDimension = function () {
        return `${video.width}x${video.height}`;
    }

Android

 fun isVideoLandscape(onResult: (Boolean) -> Unit) {
        webView.evaluateJavascript("javascript:getVideoDimension()") { dimension ->
            try {
                val (width, height) = dimension.replace("\"", "").split("x").map { it.toIntOrNull() ?: 0 }
                onResult(width > height)
            } catch (e: Throwable) {
                onResult(false)
            }
        }
    }
  • getVideoDimension()์˜ ์ฝœ๋ฐฑ์œผ๋กœ ์–ป์€ ์˜์ƒ ์‚ฌ์ด์ฆˆ โ€œwidthxheightโ€๋ฅผ ๊ฐ€๊ณตํ•˜์—ฌ ์˜์ƒ์˜ ๋ฐฉํ–ฅ์„ ํŒ๋‹จํ•ฉ๋‹ˆ๋‹ค.
  • ์„œ๋ฒ„์— ์˜์ƒ ํฌ๊ธฐ๊ฐ€ ์ €์žฅ๋˜์–ด ์žˆ์ง€ ์•Š๋Š” ๋“ฑ ์œ ํšจํ•˜์ง€ ์•Š์€ ๊ฐ’์ด ์˜จ๋‹ค๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ ์„ธ๋กœ๋กœ ๋ณด์—ฌ์ฃผ๋„๋ก ์ฒ˜๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.
    is InAppBrowserEffect.Web.EnterFullScreen -> {
        webViewContainer.isVideoLandscape { isLandscape ->
            if (isLandscape) {
                (context as Activity).requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
            }
        }
    }
  • ์ „์ฒดํ™”๋ฉด ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ, ์•ž์„œ ๊ตฌํ˜„ํ•œ isVideoLandscape()๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ๊ฐ€๋กœ ๋ฐฉํ–ฅ์˜ ์˜์ƒ์ผ ๊ฒฝ์šฐ ๊ฐ•์ œ๋กœ Activity์˜ ๋ฐฉํ–ฅ์„ ๋ฐ”๊พธ์–ด ์ฃผ์–ด ์˜์ƒ์ด ํ™”๋ฉด์— ๊ฝ‰~ ์ฐฐ ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

๋งˆ์น˜๋ฉฐ

๋น„๊ต์  ๊ฐ„๋‹จํ•œ ์ด์Šˆ์˜€์ง€๋งŒ WebView ๊ฐœ๋ฐœ ๊ฒฝํ—˜์ด ์—†๋Š” ์‹ ์ž… Android ๊ฐœ๋ฐœ์ž์ธ ์ €์—๊ฒŒ ๊ท€์ค‘ํ•œ ํ•™์Šต์˜ ๊ธฐํšŒ์˜€์Šต๋‹ˆ๋‹ค. ์ „ํ˜€ ๋ชฐ๋ž๋˜ WebView์— ์กฐ๊ธˆ ์ต์ˆ™ํ•ด์งˆ ์ˆ˜ ์žˆ์—ˆ๊ณ , ํ”„๋ก ํŠธ ํŒ€๊ณผ ๋ฐฑ์—”๋“œ ํŒ€๊ณผ์˜ ํ˜‘์—…๋„ ๊ฒฝํ—˜ํ•ด ๋ณผ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋˜ Web๊ณผ Android ๊ฐ„ ํ†ต์‹ ์ด ํ•„์š”ํ•˜๊ฒŒ ๋˜๋ฉด ์šฉ๋„์— ๋งž๊ฒŒ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์„ค๊ณ„ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค~

โ† ๋ชฉ๋ก์œผ๋กœ ๋Œ์•„๊ฐ€๊ธฐ

Art Changes Life

๋…ธ๋จธ์Šค์™€ ํ•จ๊ป˜ ์—”ํ„ฐํ…Œํฌ ์‚ฐ์—…์„ ํ˜์‹ ํ•ด๋‚˜๊ฐˆ ๋ฉค๋ฒ„๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.

์ฑ„์šฉ ์ค‘์ธ ๊ณต๊ณ  ๋ณด๊ธฐ