How to check whether it is possible to open url in Kotlin - android

In swift there is a wonderful thing
if UIApplication.shared.canOpenURL(myUrl) {
// url can be opened
UIApplication.shared.open(myUrl) { success in
if !success {
//...
}
}
} else {
// and cannot
}
Is there an analogue in Kotlin?

Going off the documentation for canOpenURL(), it doesn't check if the URL is available, only if there's an app available that can handle its scheme.
On Android, the URL has to be wrapped in an Intent to be able to open it. You can then check if an app is available for the Intent by using the PackageManager.
val intent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse(url)
}
if (intent.resolveActivity(packageManager) != null) {
// url can be opened with startActivity(intent) or requireContext().startActivity(intent)
} else {
// ...
}
If this function is in a Fragment rather than Activity, prefix packageManager with requireContext()..
Edit:
You can check if it's possible to connect to the URL using a function like this (adapted from here):
suspend fun canConnect(url: String): Boolean = withContext(Dispatchers.IO) {
// We want to check the current URL
HttpURLConnection.setFollowRedirects(false)
val httpURLConnection = (URL(url).openConnection() as HttpURLConnection)
// We don't need to get data
httpURLConnection.requestMethod = "HEAD"
// Some websites don't like programmatic access so pretend to be a browser
httpURLConnection.setRequestProperty(
"User-Agent",
"Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 (.NET CLR 3.5.30729)"
)
// We only accept response code 200
return#withContext try {
httpURLConnection.responseCode == HttpURLConnection.HTTP_OK
} catch (e: IOException) {
false
} catch (e: UnknownHostException){
false
}
}
It has to be done asynchronously since you're making a connection, or else you risk an Application Not Responding error. So I made it a suspend function that you can call from a coroutine.

You can check if a URL is valid or not using patterns. See the sample function:
fun isValidUrl(url: String): Boolean {
val p = Patterns.WEB_URL
val m = p.matcher(url)
return m.matches()
}
Once the URL is validated, you can verify whether the device is able to connect to the URL or not using below method:
fun isAPIAvailable(c: Context, url:String): Boolean {
return try {
val ipAddr: InetAddress = InetAddress.getByName(url)
ipAddr.hostAddress != ""
} catch (e: Exception) {
false
}
}

Add isValidUrl():
fun String.isValidUrl(): Boolean = Patterns.WEB_URL.matcher(this).matches()
&& URLUtil.isValidUrl(url)
Then check:
val url = "www.myWebpage.com"
if (!url.isValidUrl()) {
// url can be opened
}else{
// and cannot
}

Related

WalletConnectV1 kotlin JSON_RPC method doesn't show pop up on metamask

I am a beginner in android application development(Kotlin) and recently I was handover a project on NFT which involves walletConnect integration & for that I am using the walletConnectV1 library.
Fetching the public key and Connecting with metamask was not so hard but I am struggling when it comes to signing methods.
if anyone can help me with, how to sign messages and transactions or what I was doing wrong all this time that would really help me.
Thank you
Connect Button Click Listener
screen_main_connect_button.setOnClickListener {
try {
ExampleApplication.resetSession()
ExampleApplication.session.addCallback(this)
val i = Intent(Intent.ACTION_VIEW, Uri.parse(ExampleApplication.config.toWCUri()))
startActivity(i)
} catch (e: ActivityNotFoundException) {
// open play store
} catch (e: Exception) {
//handle exceptions
}
}
Response after the session was approved
private fun sessionApproved() {
uiScope.launch {
val account = session.approvedAccounts()?.get(0)?:""
screen_main_status.text = "Connected: $account"
screen_main_connect_button.visibility = View.GONE
screen_main_disconnect_button.visibility = View.VISIBLE
screen_main_tx_button.visibility = View.VISIBLE
val job = async {
personalSign(
"Sign this message of mine to this address",
account) {
Log.d(TAG, "sessionApproved: ${it.result}")
}
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("wc:")
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
}
}
}
private fun personalSign(
message: String,
address: String,
response: (Session.MethodCall.Response) -> Unit
) {
val id = System.currentTimeMillis()
val messageParam = if (message.hasHexPrefix()) message else message.toHex()
session.performMethodCall(
Session.MethodCall.Custom(
id, "personal_sign", listOf(messageParam, address)
)
) { response(it) }
}

Host endpoints inside Android application

TLDR: I am looking for a way to expose a POST endpoint from an Android app. Is this possible? If so, how?
Long story:
I want to receive data from Arduino devices on my android app. So I want a way to get this data through Wi-Fi (that may be a wrong assumption) but without internet connection. My current idea is to post the data from the Arduino to the smartphone over Wi-Fi.
The thing I don't know, and I didn't find answer yet is: Can I get data send to my hotspot Wi-Fi inside my app?
To host endpoints inside your Android application you will need a sever to serve those endpoints. You can use the NanoHttpD for this.
Check this question to check how to use NanoHttpD in Android.
Thank you to both #CommonsWare and #Taranmeet Singh for helping to find my solution.
I build a small http server on my phone that can be reach through http call from my laptop.
Two points were not so obvious for me :
the tecno
the host ip
For the first point, you can use :
NanoHttpD
AsuncHttpServer ex :
https://github.com/andreivisan/AndroidAsyncHttpServer/blob/master/app/src/main/java/server/http/android/MainActivity.java
Sun http :
https://medium.com/hacktive-devs/creating-a-local-http-server-on-android-49831fbad9ca
Ktor :
https://diamantidis.github.io/2019/11/10/running-an-http-server-on-an-android-app
I choose the last option because :
It used natively kotlin
It is build and maintained by JetBrains, the other library were less maintained
It is really light (10 lines to make it works)
To use Ktor you need to add this in your app gradle :
defaultConfig {
...
multiDexEnabled true
}
For the second point : by default you are bind and localhost, but you can change that :
embeddedServer(Netty, host = "192.168.43.1", port = 8080)
This ip is the default one for Android (it seems), you can also Utils method to get it :
How to get IP address of the device from code?
fun getIPAddress(useIPv4: Boolean): String {
try {
val interfaces: List<NetworkInterface> =
Collections.list(NetworkInterface.getNetworkInterfaces())
for (intf in interfaces) {
val addrs: List<InetAddress> = Collections.list(intf.inetAddresses)
for (addr in addrs) {
if (!addr.isLoopbackAddress) {
val sAddr = addr.hostAddress
//boolean isIPv4 = InetAddressUtils.isIPv4Address(sAddr);
val isIPv4 = sAddr.indexOf(':') < 0
if (useIPv4) {
if (isIPv4) return sAddr
} else {
if (!isIPv4) {
val delim = sAddr.indexOf('%') // drop ip6 zone suffix
return if (delim < 0) sAddr.toUpperCase(Locale.ROOT) else sAddr.substring(
0,
delim
).toUpperCase(Locale.ROOT)
}
}
}
}
}
} catch (ignored: Exception) {
} // for now eat exceptions
return ""
}
This work for me, hope it will help others.
Final code look like this:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
embeddedServer(Netty, host = getIPAddress(true), port = 8080) {
install(ContentNegotiation) {
gson {}
}
routing {
get("/") {
call.respond(mapOf("message" to "Hello , this ktor server"))
}
}
}.start(wait = false)
}
/**
* Get IP address from first non-localhost interface
*
* #param useIPv4 true=return ipv4, false=return ipv6
* #return address or empty string
*/
private fun getIPAddress(useIPv4: Boolean): String {
try {
val interfaces: List<NetworkInterface> =
Collections.list(NetworkInterface.getNetworkInterfaces())
for (intf in interfaces) {
val addrs: List<InetAddress> = Collections.list(intf.inetAddresses)
for (addr in addrs) {
if (!addr.isLoopbackAddress) {
val sAddr = addr.hostAddress
//boolean isIPv4 = InetAddressUtils.isIPv4Address(sAddr);
val isIPv4 = sAddr.indexOf(':') < 0
if (useIPv4) {
if (isIPv4) return sAddr
} else {
if (!isIPv4) {
val delim = sAddr.indexOf('%') // drop ip6 zone suffix
return if (delim < 0) sAddr.toUpperCase(Locale.ROOT) else sAddr.substring(
0,
delim
).toUpperCase(Locale.ROOT)
}
}
}
}
}
} catch (ignored: Exception) {
} // for now eat exceptions
return ""
}}

Twitter Streaming API HTTP 420

I want to consume twitter streaming api in android.
I've used kotlin coroutines and retrofit.
Somehow in the third request i get an HTTP 420 ERROR (Enhance your calm)
I cannot understand why this happens. I am using kotlin coroutines.
Here's my code:
fun getStreamData(str: String) {
Log.d("debug", "Fetching data..")
coroutineScope.launch {
withContext(Dispatchers.Main) {
//Display loading animation in UI
_status.value = DataApiStatus.LOADING
}
try {
val listResult = ApiService().api!!.getTweetList(str).await()
while (!listResult.source().exhausted()) {
val reader = JsonReader(InputStreamReader(listResult.byteStream()))
// https://stackoverflow.com/questions/11484353/gson-throws-malformedjsonexception
reader.setLenient(true);
val gson = GsonBuilder().create()
val j = gson.fromJson<JsonObject>(reader, JsonObject::class.java)
Log.d("debug", "JSON: " + j.toString())
if (j.get("text") != null && j.getAsJsonObject("user").get("profile_image_url_https") != null && j.getAsJsonObject("user").get("name") != null){
val t = gson.fromJson<Tweet>(j, Tweet::class.java)
withContext(Dispatchers.Main) {
_status.value = DataApiStatus.DONE
// https://stackoverflow.com/questions/47941537/notify-observer-when-item-is-added-to-list-of-livedata
tweetsList.add(t)
_tweetsList.value = tweetsList
}
}
}
}
catch (e : JsonSyntaxException) {
Log.e("error", "JsonSyntaxException ${e.message}");
}
catch (e: Exception) {
Log.e("error", "ERROR ${e.message}")
}
}
}
This function is responsible to search the stream accordingly to str string which is a parameter.
Also, when the search parameter changes i cancel the current job and relaunch a new one with the actual search parameter.
fun cancelJob(){
Log.d("debug", "Cancelling current Job!")
coroutineScope.coroutineContext.cancelChildren()
}
What am i doing wrong? In the third request i get an HTTP 420 ERROR.
Here's the full code:
https://github.com/maiamiguel/RHO-Challenge
The 420 Enhance Your Calm status code is an unofficial extension by Twitter. Twitter used this to tell HTTP clients that they were being rate limited. Rate limiting means putting restrictions on the total number of requests a client may do within a time period.

Ping website URL before loading it in WebView in Android Kotlin

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+.
My solution below is trying to do two things:
Using AsyncTask "ping" a website and then
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+.
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
}
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)

Dropbox V2 throwing "SHARED_LINK_NOT_FOUND" when trying to get shared link url

When picking files from the dropbox (V2), I am trying to get the shared link url using this below code.
internal inner class GetDropboxSharableURLTask(file_path: String, pathLower: String) : AsyncTask<String, Void, Void>() {
private var pDialog: ProgressDialog? = null
private var dropbox_url: String? = file_path
private var db_pathLower: String? = pathLower
override fun onPreExecute() {
super.onPreExecute()
pDialog = ProgressDialog(context)
pDialog!!.setCancelable(false)
pDialog!!.setMessage(context?.resources?.getString(R.string.please_wait))
pDialog!!.show()
}
override fun doInBackground(vararg urls: String): Void? {
try {
val shareLink = DropboxClientFactory.getClient().sharing().getSharedLinkMetadata(db_pathLower)
dropbox_url = getShareURL(shareLink.url)!!.replaceFirst("https://www".toRegex(), "https://dl")
return null
} catch (e: Exception) {
e.printStackTrace()
return null
}
}
fun getShareURL(strURL: String?): String? {
var conn: URLConnection? = null
var redirectedUrl: String? = null
try {
val inputURL = URL(strURL)
conn = inputURL.openConnection()
conn!!.connect()
val `is` = conn.getInputStream()
println("Redirected URL: " + conn.url)
redirectedUrl = conn.url.toString()
`is`.close()
} catch (e: MalformedURLException) {
Log.d("", "Please input a valid URL")
} catch (ioe: IOException) {
Log.d("", "Can not connect to the URL")
}
return redirectedUrl
}
override fun onPostExecute(feed: Void?) {
// TODO: check this.exception
// TODO: do something with the feed
pDialog!!.hide()
Log.d("url", dropbox_url)
val intent = Intent()
intent.putExtra(Constant.KEY_DROPBOX_PICKER, dropbox_url)
context?.setResult(Activity.RESULT_OK, intent)
context?.finish()
}
}
I am getting the error as below -
com.dropbox.core.v2.sharing.SharedLinkErrorException: Exception in
2/sharing/get_shared_link_metadata: SHARED_LINK_NOT_FOUND
This above error is thrown when calling getSharedLinkMetadata(db_pathLower)
Though, earlier, I was using the similar code in dropbox V1, but as soon as I switched to newer version of the dropbox API, i.e, dropbox V2, I started getting this error when trying to get the actual sharing url of dropbox, (the one which must contain the file extension as well).
The expected url should be like this - https://dl.dropbox.com/s/hpe1gjfb4aqsv3e/property_1523965639_img.jpeg?dl=0
but, what I am getting is something like https://dl.dropboxusercontent.com/apitl/1/AAD62_lYN5n_cYyRQWhg_rmXGJPFzqF5m8OJp‌​Nt_SIIxG7bVvmO6X5d1pKg7uulM1vEBWx_X9PZ9i3vFy-jb3eBC-M_q3YCWRmPrdAwpQ7kqSFGCIPrZaH‌​NC44YRjwXGXYTbnqMO1hPhKb-G5matDzTABUQOssB-LIN4qWoJmPnuhNgzpL9FO4ibet4uBPoef_SLZLj‌​upsOV9PKYUhtPxY_NY7HjymZSHsQh67m4HoBN4YgEAPot0KMAsV1eE3WCjK0XbD1YfGqdsVI9H40KUQ_9‌​R-nmAouoqdbA37G5CXjQKYPC8cENTvN2pjHKwCnHvgI just because of that error.
Please, let me the know the thing, which I need to modify in order to get the dropbox sharing url (along with file extension), and to avoid the above error, as soon as the file is selected / clicked.
Finally, resolved with the help of this, as listSharedLinksBuilder finally worked for me.
It got resolved by modifying the code to -
val sharedLinksResults = DropboxClientFactory.getClient().sharing().listSharedLinksBuilder().withPath(db_pathLower).withDirectOnly(true).start()
var url = ""
if (sharedLinksResults.links.isEmpty()) {
val shareLink = DropboxClientFactory.getClient().sharing().createSharedLinkWithSettings(db_pathLower) //.getClient().sharing().getSharedLinkMetadata(db_pathLower)
url = shareLink.url
} else {
url = sharedLinksResults.links[0].url
}
dropbox_url = getShareURL(url)!!.replaceFirst("https://www".toRegex(), "https://dl")

Categories

Resources