2

Before loading a website in WebView, I want to check the URL and make sure that it loads. If it does, show the WebView; if not, show another View with a message.

The idea is to check if the website can be loaded and depending on the response show either the "Website cannot be loaded" screen or show the WebView with URL loaded.

I have already checked if the connection is available, so no need to worry about that.

Need to support API 25+.

quietbits
  • 178
  • 1
  • 10
  • Your question was marked as [a duplicate of this](https://stackoverflow.com/questions/2786720/how-to-ping-a-url-in-an-android-service), so it would be better to post your answer there. In your answer it would be worth explaining how it relates to that question and how it solves it (code-only answers are usually not very useful). – halfer Nov 20 '18 at 22:07
  • 1
    Unfortunately, this post needs to be regarded as off-topic because it is an answer to another question, not a question in its own right. – halfer Nov 20 '18 at 22:08
  • Maybe I didn't explain my initial question correctly, but I think it's on topic, because there are 2 parts to my question: 1) ping and 2) use that answer in MainActivity to show WebView. I spent hours searching for that and didn't find anything that worked, so I had to piece it together. – quietbits Nov 20 '18 at 23:26
  • I have edited this to try to make it into more of a question. It is still not ideal, since if your original post was wrongly closed, that should be reopened, rather than a genuine duplicate being created. Since this has an answer, it may well survive - close voters are less likely to close material that is posted in a spirit of helpfulness. It would be ideal if you would cast a reopen vote on your earlier question, and if that is successful, perhaps you can move this there (and then delete this). – halfer Nov 21 '18 at 18:15
  • In the meantime, please accept your answer below, to indicate that you're happy with the solution. – halfer Nov 21 '18 at 18:16
  • Thanks, @halfer! I opted to delete my original post and will keep this one. It says that I can't accept my own answer here. – quietbits Nov 21 '18 at 18:31
  • You will be able to accept your own answer - there is just a delay on it. Please do accept it in the near future. – halfer Nov 21 '18 at 18:32
  • Since you've deleted the other one, that does tidy things somewhat. However, this question is now too sparse, and could do with some expansion. While I don't develop for Android, I would also advise you not to repost every time your original question is closed as duplicate - doing so is a downvote magnet. You appear to have gotten away with it this time. – halfer Nov 21 '18 at 18:36
  • Sorry about that, I'm new to this and thought that someone might stumble upon the same problem I was having. Since original question was marked as duplicate I thought I needed to create a new one explaining what was the different. – quietbits Nov 21 '18 at 18:50
  • You're right, the advice on the close reason is surprising to me (_This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question_). In general, questions that are closed should be edited, and then people may choose to reopen them after the edit. So, in this case, it seems that you did everything right. I will withdraw my close vote. – halfer Nov 21 '18 at 20:55
  • (If I get a chance I will ask a _Meta_ question about whether the advice is correct, but for now, let us assume it is. I apologise if I have given you some incorrect advice). – halfer Nov 21 '18 at 20:56
  • I appreciate your advice, @halfer! – quietbits Nov 21 '18 at 23:58

3 Answers3

1

My solution below is trying to do two things:

  1. Using AsyncTask "ping" a website and then

  2. By passing context from MainActivity, call a function to show WebView (using WeakReference here to achieve that)

class MainActivity : AppCompatActivity() {
private var websiteURL = "https://www.google.com"

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    // ...
    webView.webViewClient = WebViewClient()
}

// I called this function after checking that there is Internet connection
private fun connectionResponseFunction(): String {
    return if (isConnected) {
        // *** is connected
        // pass in "this" context from MainActivity
        val downloadData = CheckLink(this)
        downloadData.execute(websiteURL)
    } else {
      // *** not connected
    }
}

private fun showWebView() {
    webView.loadUrl(websiteURL)
}

companion object {
    class CheckLink internal constructor(context: MainActivity) : AsyncTask<String, Void, String>() {
      // Needed if you want to use webView or anything else from MainActivity
        private val activityReference: WeakReference<MainActivity> = WeakReference(context)

        override fun onPostExecute(result: String?) {
            super.onPostExecute(result)
            val activity = activityReference.get()
            if (activity == null || activity.isFinishing) return

            if (result == "success") {
                // URL loaded, show webView
                activity.showWebView()
            } else {
                // URL didn't load
            }
        }

        override fun doInBackground(vararg url: String?): String {
            val linkLoaded = loadLink(url[0])
            if (!linkLoaded) {
                return "failure"
            }
            return "success"
        }

        private fun loadLink(urlPath: String?): Boolean {
            try {
                val url = URL(urlPath)
                val connection: HttpURLConnection = url.openConnection() as HttpURLConnection
                connection.setRequestProperty("Connection", "close")
                connection.connectTimeout = 3000
                connection.connect()

                val response = connection.responseCode

                // 200 for success
                return if (response == 200) {
                    true
                } else {
                    false
                }
            } catch (e: Exception) {
              // Handle exceptions
                when (e) {
                    is MalformedURLException -> "loadLink: Invalid URL ${e.message}"
                    is IOException -> "loadLink: IO Exception reading data: ${e.message}"
                    is SecurityException -> { e.printStackTrace()
                        "loadLink: Security Exception. Needs permission? ${e.message}"
                    }
                    else -> "Unknown error: ${e.message}"
                }
            }
            return false  // Error
        }
    }
}
}

I'm quite new to Android and Kotlin, so I'm open to any suggestions to make it better.

I could not find any recent code that works for API 25+.

halfer
  • 19,824
  • 17
  • 99
  • 186
quietbits
  • 178
  • 1
  • 10
0

Another (shorter) alternative to AsyncTask would it be using Thread:

Thread {
    val result = isHostAvailable(BuildConfig.BASE_URL)
    runOnUiThread {
        if (result) {
            // do something ...
        }
        else showToast("no connection to server")
    }
}.start()

and

fun isHostAvailable(urlPath: String): Boolean {
    try {
        val url = URL(urlPath)
        val connection: HttpURLConnection = url.openConnection() as HttpURLConnection
        connection.setRequestProperty("Connection", "close")
        connection.connectTimeout = 3000
        connection.connect()

        return when (connection.responseCode) {
            200, 403 -> true
            else -> false
        }

    } catch (e: Exception) {
        when (e) {
            is MalformedURLException -> "loadLink: Invalid URL ${e.message}"
            is IOException -> "loadLink: IO Exception reading data: ${e.message}"
            is SecurityException -> {
                e.printStackTrace()
                "loadLink: Security Exception. Needs permission? ${e.message}"
            }
            else -> "Unknown error: ${e.message}"
        }
    }
    return false
}
BWappsAndmore
  • 491
  • 3
  • 17
0

Looks like ping doesn't work on emulators (How to Ping External IP from Java Android)...instead, try this on a real device, it'll work:

val process = Runtime.getRuntime().exec("/system/bin/ping -c 1 $SERVIDOR")
val pingResult = process .waitFor()
return pingResult == 0

By the way, the answer given by @quietbits is pretty obsolete...AsyncTask class was the way to go like 5 years ago...Since Android Studio supports kotlin, you should use coroutines!! the code line difference is huge...check this code labs for more info (https://codelabs.developers.google.com/codelabs/kotlin-coroutines/#0)

Ramiro G.M.
  • 357
  • 4
  • 7