Mobile Apps 2023W Lecture 21

From Soma-notes

Notes

March 29
--------

So I'm trying to figure out how to implement the back button properly.

Currently, the back button gets triggered but doesn't work right, .goBack() isn't doing what we expected
 - hypothesis: state changes are updating history too often, getting copies of current page and so can't keep track of last page properly

Two choices:
 - integrate more tightly with webview history so we can better manipulate and interact with it
 - ignore webview history, implement it all on our own and just push current page to the webview widget as needed

Code

package carleton.comp2601.webviewcompose

import android.os.Bundle
import android.util.Log
import android.view.ViewGroup
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.viewinterop.AndroidView

class MainActivity : ComponentActivity() {

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

// Creating a composable
// function to display Top Bar
@Composable
fun MainContent() {
    var curURL by remember { mutableStateOf("https://news.ycombinator.com") }
    var navigate by remember { mutableStateOf("") }

    Scaffold(
//        topBar = { TopAppBar(title = {Text("COMP 2601")})},
        topBar = {
            Row {
                ClickableText(
                    AnnotatedString(text = "Back"),
                    onClick = {
                        navigate = "back"
                        Log.d("webviewCompose", "navigate = back")
                    }
                )
                URLBar(curURL = curURL, updateURL = { curURL = it })
            }
        },
        content = { MyContent(curURL = curURL, updateURL = {
            curURL = it
            Log.d("webviewCompose", "updating URL to $curURL")
                                                           },
            navigate = navigate, resetNavigate = { navigate = ""}) }
    )
}

@Composable
fun URLBar(curURL: String, updateURL: (String) -> Unit) {
    var newURL = ""

    TextField(
        value = curURL,
        //onFocusChange = { updateURL(newURL) },
        //onValueChange = { updatedURL: String -> newURL = updatedURL},
        onValueChange = updateURL,
        label = { Text("Enter URL here") }
    )
}

@Composable
fun MyContent(curURL: String, updateURL: (String) -> Unit,
              navigate: String, resetNavigate: () -> Unit){

    class ObservableWebViewClient : WebViewClient() {

        override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
            if (url != null) {
                updateURL(url)
            }
            return false
        }
    }

    fun logWebViewHistory(view: WebView) {
        val history = view.copyBackForwardList()

        val current = history.currentIndex

        Log.d("webviewCompose", "Current history item: $current")

        Log.d("webviewCompose", "Webview BackForwardList contents:")

        for (i in 0..history.size -1) {
            val item = history.getItemAtIndex(i)

            if (item != null) {
                val u = item.originalUrl
                Log.d("webviewCompose", "  $i: $u")
            }
        }
    }

    // Adding a WebView inside AndroidView
    // with layout as full screen
    AndroidView(factory = {
        WebView(it).apply {
//            layoutParams = ViewGroup.LayoutParams(
//                ViewGroup.LayoutParams.MATCH_PARENT,
//                ViewGroup.LayoutParams.MATCH_PARENT
//            )
            webViewClient = ObservableWebViewClient()
            loadUrl(curURL)
        }
    }, update = {
        if (navigate == "back") {
            Log.d("webviewCompose", "Navigating back")
            val currentURL = it.url
            Log.d("webviewCompose", "currentURL = $currentURL")
            it.goBack()
            val prevURL = it.url
            Log.d("webviewCompose", "prevURL = $prevURL")

            if (currentURL == prevURL) {
                Log.d("webviewCompose", "current and prev are the SAME!")
            }
            if (prevURL != null) {
                Log.d("webviewCompose", "updating URL after back button")
                updateURL(prevURL)
            }
            resetNavigate()

            logWebViewHistory(it)
        } else {
            Log.d("webviewCompose", "loading URL $curURL")
            it.loadUrl(curURL)
            logWebViewHistory(it)
        }
    })
}

// For displaying preview in
// the Android Studio IDE emulator
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    MainContent()
}