In order to enable the Jane book, nuggets, CSDN, public number of the article display into the night mode, the need for webview to do the relevant adaptation. The principle is actually relatively simple, as long as the page load when the relevant CSS style to do the replacement. The actual implementation varies from site to site, so here’s how it works for each site.

The project address

Github.com/iamyours/Wa…

Jane’s book

reader-night-mode

Jane book website is a night mode, so it is relatively easy to implement. But by default, when a brief article is loaded with a WebView, it shows the daytime mode effect. Open the Chrome debugger and then switch to night mode in the browser. We can see that when night mode is used, the body has a reader-night-mode class style added to it.

Guess Jane’s night mode is related to this class style, then we can pass

WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG)
Copy the code

To debug the WebView, type Chrome ://inspect into your Chrome browser and debug the Web page. If we open a short text and use the debugger to replace the body style with reader-night-mode, we will see that the current text is in night mode.

Expand the text, go to navigation, go to advertising

In order to make the reading experience better, we directly expand the full text when opening the article, while removing navigation, advertising and other elements irrelevant to the content of the article, we first test through the debugger.

Regular replacement CSS

Through debugging, we found that the CSS styles corresponding to these effects are under the head tag of the current HTML page, not in the form of CSS files. So an HTML string is generated using an OkHttp request for the address of the article, and the associated CSS is replaced with a re. Start by creating a Wget utility class for turning a web page into a string, noting that the request header is fixed to a mobile device.

object Wget {
    fun get(url: String): String {
        val client = OkHttpClient.Builder()
            .build()
        val request = Request.Builder()
            .url(url)
            .header(
                "user-agent"."Mozilla / 5.0 (Linux; The Android 8.0. Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3887.7 Mobile Safari/537.36"
            )
            .build()
        val response = client.newCall(request).execute()
        returnresponse.body()? .string() ? :""}}Copy the code

Then create a JianShuWebClient that ADAPTS to simple CSS. Those styles written under the head tag are found to be written under

class JianShuWebClient:WebViewClient(){ override fun shouldInterceptRequest(view: WebView? , url: String?) : WebResourceResponse? { val urlStr = url ? :""
        if (urlStr.startsWith("https://www.jianshu.com/p/")) { val response = Wget.get(url ? :"") val res = darkBody(replaceCss(response, view!! .context)) val input = ByteArrayInputStream(res.toByteArray())return WebResourceResponse("text/html"."utf-8", input)
        }
        return super.shouldInterceptRequest(view, url)
    }

    private val rex = "(
    private val bodyRex = "
    private fun darkBody(res: String): String {
        val pattern = Pattern.compile(bodyRex)
        val m = pattern.matcher(res)
        return if (m.find()) {
            val s = "<body class=\"reader-night-mode normal-size\""
            res.replace(bodyRex.toRegex(), s)
        } else res
    }

    private fun replaceCss(res: String, context: Context): String {
        val pattern = Pattern.compile(rex)
        val m = pattern.matcher(res)
        return if (m.find()) {
            val css = StringUtil.getString(context.assets.open("jianshu/jianshu.css"))
            val sb = StringBuilder()
            sb.append(m.group(1))
            sb.append(css)
            sb.append(m.group(3))
            val res = res.replace(rex.toRegex(), sb.toString())
            Log.e("test"."$res")
            res
        } else {
            res
        }
    }
}
Copy the code

The effect

The Denver nuggets

Primary CSS file replacement

The Nuggets site doesn’t have night mode (it does on Android), so it’s a bit more difficult to adapt to than a simple book. Unlike the simple book, the style of the nuggets article is introduced outside of the CSS file, so there is no need for the OkHttp request to convert strings. We simply find the corresponding file and replace it in the shouldInterceptRequest method

override fun shouldInterceptRequest(view: WebView? , url:String?).
            : WebResourceResponse? {
        Log.i("Nuggets"."url:$url")
        valurlStr = url ? :""
        if (urlStr.startsWith("https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-assets/v3/static/css/0~tplv-t2oaga2asx-image.image")
            && urlStr.endsWith(".css")) {valstream = view!! .context.assets.open("juejin/css/juejin.css")
            return WebResourceResponse("text/css"."utf-8", stream)
        }

        return super.shouldInterceptRequest(view, url)
    }
Copy the code

It can be seen from the plug-in that the front end of digging gold is written by VUE. The compiled CSS will have the information of [data-V-XXX], and the XXX number will be higher each time it is updated. We need to remove the information of [data]. We added the night mode style to juejin. CSS using the night mode style.

.article-area{padding:0 8px; background:#3f3f3f; color:#969696; }// blockquote{background:#555; border-left:3px solid #222; margin:0px; padding:5px 16px; } / / reference code {color: # c7254e; border-radius:4px; background-color:#282828; padding:2px 4px; font-size:12px; }// code.hljs {display: block; padding: 5px; color: #abb2bf; background: #282c34; border-radius:4px; font-size:12px; }.hljs-comment,.hljs-quote {// font-style: italic } ... There are many more, see the projectCopy the code

Specific things to look out for are background colors, text colors, code backgrounds, colors, references, tables, etc.

The picture problem, the avatar problem

The image of the gold digging article is lazy loading, using a replacement CSS, found that the image inside is not displayed. So inject the image display script when the page is loaded as follows

 val script = """ javascript:(function(){ var arr = document.getElementsByClassName("lazyload"); for(var i=0; i
webview.loadUrl(script)
Copy the code

Avatars get user data through the interface and then modify it through javascript.

  private var head = ""
    private var username = ""

    private fun loadUser() { val client = OkHttpClient.Builder().build() val req = Request.Builder().url(detailApi).build() val call = client.newCall(req) call.enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { } override fun onResponse(call: Call, response: Response) { val res = response.body()? .string() ? :"{}"
                val obj =
                    Gson().fromJson<JsonObject>(res, JsonObject::class.java)
                obj?.getAsJsonObject("d")
                    ?.getAsJsonObject("user")? .run { head = get("avatarLarge").asString
                        username = get("username").asString}}})} private fun getDetailApi(postId: String): String {// Avatar is not loaded, manual callreturn "https://post-storage-api-ms.juejin" +
                ".im/v1/getDetailData? src=web&type=entry&postId=$postId"
    }

    fun loadUserScript() {
            val script = """ javascript:(function(){ document.getElementsByClassName("author-info-block")[0].children[0].children[0].style.backgroundImage = "url('$head')"; document.getElementsByClassName("username")[0].innerHTML="$username"; }) (); """.trimIndent()
            webView.loadUrl(script)
        }

Copy the code

The effect

The same ADAPTATION of CSDN is also similar, and is completed by replacing CSS files, so I will not talk about specific adaptation here.

Wechat official account

The style of the wechat official account article is also placed inside the current HTML, just like the simple book. Regular expressions are different

val rex = "(<style>)([\\S ]*)(</style>)"
Copy the code

This means matching the style tag and containing characters or Spaces (newlines do not count).

Important Mandatory replacement

Some text labels in wechat public numbers (such as P tags) have their own style, which cannot be replaced by the regular. However, CSS3 has one! Important increases the priority and forces the attributes of the associated tag (even if it has a style style).

Of course, you should not abuse important, otherwise some styles (such as code) that you do not want to change will be changed, so CSS selectors must match exactly! Important.