Ping activity ok

This commit is contained in:
Jérémy Munsch 2023-07-11 16:20:14 +02:00
parent ebbbf468a5
commit cfd7b2fe9f
No known key found for this signature in database
GPG key ID: 18361AF3BFEC0FEC
11 changed files with 613 additions and 11 deletions

View file

@ -17,6 +17,12 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".ui.ldc.KioskPingActivity"
android:exported="false" />
<activity
android:name=".ui.ldc.KioskDiagnoticActivity"
android:exported="false" />
<activity
android:name=".ui.ldc.KioskParameterActivity"
android:exported="false" />

View file

@ -0,0 +1,69 @@
package com.gaurav.avnc.ui.ldc
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Button
import androidx.preference.PreferenceManager
import com.gaurav.avnc.R
import com.gaurav.avnc.ui.ldc.lib.ApplicationInfo
class KioskDiagnoticActivity : AppCompatActivity(), View.OnClickListener {
// * View Elements
private lateinit var btnPing: Button
private lateinit var btnReboot: Button
private lateinit var btnRefresh: Button
private lateinit var btnGoBack: Button
// * AppInfo
lateinit var appInfo: ApplicationInfo
private set
/**
* On Create
*/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_kiosk_diagnotic)
btnPing = findViewById<Button>(R.id.btnPing)
btnReboot = findViewById<Button>(R.id.btnReboot)
btnRefresh = findViewById<Button>(R.id.btnRefresh)
btnGoBack = findViewById<Button>(R.id.btnGoBack)
// * EVENTS
btnPing.setOnClickListener(this)
btnReboot.setOnClickListener(this)
btnRefresh.setOnClickListener(this)
btnGoBack.setOnClickListener(this)
// * Setup AppInfo
appInfo = ApplicationInfo(PreferenceManager.getDefaultSharedPreferences(applicationContext))
}
/**
* Event on Activity Resume
*/
override fun onResume() {
super.onResume()
// * AppInfo Refresh
appInfo.refreshConfigProperties()
appInfo.retrieveDeviceNetworkInfo(applicationContext)
}
// * Action UI events
override fun onClick(v: View) {
when (v.id) {
R.id.btnPing -> {
startActivity(Intent(this, KioskPingActivity::class.java))
return
}
R.id.btnGoBack -> {
finish()
return
}
}
}
}

View file

@ -22,6 +22,7 @@ class KioskParameterActivity : AppCompatActivity(), View.OnClickListener {
// * View Elements
private lateinit var btnValidateConfig: Button
private lateinit var btnGoBack: Button
private lateinit var btnDiagnostic: Button
private lateinit var editTextServerUrl: EditText
private lateinit var editTextIp: EditText
private lateinit var editTextPort: EditText
@ -38,11 +39,13 @@ class KioskParameterActivity : AppCompatActivity(), View.OnClickListener {
btnValidateConfig = findViewById<Button>(R.id.btnValidateConfig)
btnGoBack = findViewById<Button>(R.id.btnGoBack)
btnDiagnostic = findViewById<Button>(R.id.btnDiagnostic)
editTextServerUrl = findViewById<EditText>(R.id.editTextServerUrl)
editTextIp = findViewById<EditText>(R.id.editTextIp)
editTextPort = findViewById<EditText>(R.id.editTextPort)
// * EVENTS
btnDiagnostic.setOnClickListener(this)
btnValidateConfig.setOnClickListener(this)
btnGoBack.setOnClickListener(this)
@ -77,10 +80,18 @@ class KioskParameterActivity : AppCompatActivity(), View.OnClickListener {
// * Action UI events
override fun onClick(v: View) {
when (v.id) {
R.id.btnValidateConfig -> validateAndSaveConfig()
R.id.btnValidateConfig -> {
validateAndSaveConfig()
return
}
R.id.btnGoBack -> {
finish()
navigateUpTo(Intent(baseContext, KioskActivity::class.java))
return
}
R.id.btnDiagnostic -> {
startActivity(Intent(this, KioskDiagnoticActivity::class.java))
return
}
}
}

View file

@ -83,7 +83,6 @@ class KioskParameterPasswordActivity : AppCompatActivity(), View.OnClickListener
}
R.id.btnGoBack -> {
finish()
navigateUpTo(Intent(baseContext, KioskActivity::class.java))
return
}
}
@ -94,8 +93,7 @@ class KioskParameterPasswordActivity : AppCompatActivity(), View.OnClickListener
* Simple validation pour next intent, pas besoin de haute sécurité
*/
private fun validatePassword() {
println(editTextPassword.text.toString())
println(this.appInfo.configPassword)
editTextPassword.clearFocus()
if(editTextPassword.text.toString() != this.appInfo.configPassword) {
toastMessage(this,"Mot de passe invalide")
runOnUiThread { editTextPassword.setTextColor(Color.RED) }

View file

@ -0,0 +1,276 @@
package com.gaurav.avnc.ui.ldc
import android.annotation.SuppressLint
import android.app.Activity
import android.graphics.Color
import android.os.AsyncTask
import android.os.Bundle
import android.os.StrictMode
import android.util.Patterns
import android.view.View
import android.webkit.URLUtil
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.graphics.toColorInt
import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager
import com.gaurav.avnc.R
import com.gaurav.avnc.model.ServerProfile
import com.gaurav.avnc.ui.ldc.lib.ApplicationInfo
import com.gaurav.avnc.ui.ldc.lib.exceptions.InvalidIpException
import com.gaurav.avnc.ui.ldc.lib.exceptions.PingFailedException
import com.google.common.net.InetAddresses
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.Call
import okhttp3.Callback
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.apache.commons.csv.CSVFormat
import org.apache.commons.csv.CSVParser
import org.apache.commons.csv.CSVRecord
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.io.Reader
import java.net.Inet4Address
import java.net.Inet6Address
import java.net.InetAddress
import java.net.UnknownHostException
import java.util.regex.Matcher
import java.util.regex.Pattern
import kotlin.math.roundToInt
import kotlin.system.exitProcess
class KioskPingActivity : AppCompatActivity(), View.OnClickListener {
// * View Elements
private lateinit var editTextPingUrl: EditText
private lateinit var btnPing: Button
private lateinit var btnGoBack: Button
private lateinit var textResult: TextView
// * AppInfo
private lateinit var appInfo: ApplicationInfo
/**
* On Create
*/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
StrictMode.setThreadPolicy(policy)
setContentView(R.layout.activity_kiosk_ping)
editTextPingUrl = findViewById<EditText>(R.id.editTextPingUrl)
btnPing = findViewById<Button>(R.id.btnPing)
btnGoBack = findViewById<Button>(R.id.btnGoBack)
textResult = findViewById<TextView>(R.id.textResult)
// * EVENTS
btnPing.setOnClickListener(this)
btnGoBack.setOnClickListener(this)
// * Setup AppInfo
appInfo = ApplicationInfo(PreferenceManager.getDefaultSharedPreferences(applicationContext))
// * Init text result
textResult.visibility = View.INVISIBLE
}
/**
* Event on Activity Resume
*/
override fun onResume() {
super.onResume()
// * AppInfo Refresh
appInfo.refreshConfigProperties()
appInfo.retrieveDeviceNetworkInfo(applicationContext)
editTextPingUrl.setText(appInfo.gateway.hostAddress)
}
// * Action UI events
override fun onClick(v: View) {
when (v.id) {
R.id.btnPing -> {
ping()
return
}
R.id.btnGoBack -> {
finish()
return
}
}
}
/**
* Ping action
*/
private fun ping() {
// * Prepare UI
editTextPingUrl.clearFocus()
textResult.text = "En cours.."
textResult.setTextColor(Color.BLACK)
textResult.visibility = View.VISIBLE
// * Disable ping BTN
btnPing.isEnabled = false
btnPing.isClickable = false
btnPing.setBackgroundColor(Color.GRAY)
// * Disable textBox
editTextPingUrl.isEnabled = false
editTextPingUrl.isClickable = false
val elementToPing = editTextPingUrl.text.toString()
lifecycleScope.executeAsyncTask(onPreExecute = {}, doInBackground = {
Thread {
try {
lateinit var inetToPing: InetAddress
// ? Is IP
if (isValidIpAddress(elementToPing)) {
// Resolve
inetToPing = InetAddress.getByName(elementToPing)
} else {
// * Is FQDN
var host = elementToPing.toHttpUrlOrNull()?.host
// Resolve
inetToPing = InetAddress.getByName(if(host !== null) host else elementToPing)
}
// * Ping ping
val avg = pingHost(inetToPing)
toastMessage(this, "FQDN AVG $avg ms")
runOnUiThread {
textResult.text = "Accessible ${inetToPing.hostAddress}"
textResult.setTextColor(Color.GREEN)
}
return@Thread
} catch(e: PingFailedException) {
runOnUiThread {
textResult.text = "Echec du ping !!"
textResult.setTextColor(Color.RED)
}
} catch(e: InvalidIpException) {
runOnUiThread {
textResult.text = "Echec du ping ip invalide"
textResult.setTextColor(Color.RED)
}
} catch(e: SecurityException) {
toastMessage(this, "Erreur : ${e.message} - ${e.cause?.message}")
runOnUiThread {
textResult.text = "Echec du ping erreur de sécurité"
textResult.setTextColor(Color.RED)
}
} catch(e: UnknownHostException) {
toastMessage(this, "Echec du ping hote inconnu")
runOnUiThread {
textResult.text = "Echec du ping !!"
textResult.setTextColor(Color.RED)
}
} catch(e: IOException) {
toastMessage(this, "Echec du ping erreur de connection")
runOnUiThread {
textResult.text = "Echec du ping !!"
textResult.setTextColor(Color.RED)
}
}
}.run()
}, onPostExecute = {
// * Enable ping BTN
btnPing.isEnabled = true
btnPing.isClickable = true
btnPing.setBackgroundColor("#D4021D".toColorInt())
// * Enable textBox
editTextPingUrl.isEnabled = true
editTextPingUrl.isClickable = true
})
}
/**
* Ping host and return average time in ms
* @throws PingFailedException In case of failure
* @throws InvalidIpException If inetAddress is not a valid IPv4 or IPv6
*/
private fun pingHost(inetToPing: InetAddress): Int {
// ? Is Valid IP
if (!isValidIpAddress(inetToPing.hostAddress)) {
throw InvalidIpException()
}
inetToPing.isReachable(600)
var avg = -1
lateinit var process: Process
if (inetToPing is Inet6Address) {
process = Runtime.getRuntime().exec("/system/bin/ping6 -c 1 ${inetToPing.hostAddress}")
} else if (inetToPing is Inet4Address) {
process = Runtime.getRuntime().exec("/system/bin/ping -c 1 ${inetToPing.hostAddress}")
} else {
throw InvalidIpException()
}
BufferedReader(InputStreamReader(process.inputStream)).forEachLine {
// it: "PING 194.194.194.194 (194.194.194.194) 56(84) bytes of data."
// it: "rtt min/avg/max/mdev = 326.137/326.137/326.137/0.000 ms"
if (it.contains("rtt ")) {
avg = Regex("\\d+\\.\\d+").findAll(it).toList()[1].value.toFloat().roundToInt()
}
}
if (avg === -1) {
toastMessage(this, "IP erreur de connection")
throw PingFailedException()
}
return avg
}
/**
* Test if ip Address is valid
*/
private fun isValidIpAddress(str: String): Boolean {
if (str.isEmpty())
{
return false
}
// Regex for IPV4 and IPV6
return InetAddresses.isInetAddress(str)
}
/**
* Display toast message
*/
private fun toastMessage(activity: Activity, message: String) {
activity.runOnUiThread(Runnable {
Toast.makeText(
activity.applicationContext,
message,
Toast.LENGTH_LONG
).show()
})
}
fun <R> CoroutineScope.executeAsyncTask(
onPreExecute: () -> Unit,
doInBackground: () -> R,
onPostExecute: (R) -> Unit
) = launch {
onPreExecute()
val result = withContext(Dispatchers.IO) { // runs in background thread without blocking the Main Thread
doInBackground()
}
onPostExecute(result)
}
}

View file

@ -5,6 +5,7 @@ import android.app.Activity
import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import android.util.Patterns
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -24,6 +25,7 @@ import com.gaurav.avnc.ui.ldc.events.AppSwitchedToForegroundEvent
import com.gaurav.avnc.ui.ldc.events.NetworkStateChangedEvent
import com.gaurav.avnc.ui.vnc.startVncActivity
import com.gaurav.avnc.viewmodel.HomeViewModel
import com.google.common.net.InetAddresses
import okhttp3.Call
import okhttp3.Callback
import okhttp3.OkHttpClient
@ -332,11 +334,7 @@ class KioskHomeFragment : Fragment(), View.OnClickListener {
return false
}
// Regex for IPV4 and IPV6
val regex: String =
"^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\\.(?!\$)|\$)){4}$"
val p: Pattern = Pattern.compile(regex)
val m: Matcher = p.matcher(str)
return m.matches()
return InetAddresses.isInetAddress(str)
}
/**

View file

@ -0,0 +1,4 @@
package com.gaurav.avnc.ui.ldc.lib.exceptions
class InvalidIpException: RuntimeException() {
}

View file

@ -0,0 +1,4 @@
package com.gaurav.avnc.ui.ldc.lib.exceptions
class PingFailedException: RuntimeException() {
}

View file

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.ldc.KioskDiagnoticActivity">
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Diagnostique"
android:textColor="#000000"
android:textSize="48sp"
android:textStyle="bold" />
</LinearLayout>
<ImageView
android:id="@+id/imageView"
android:layout_width="141dp"
android:layout_height="132dp"
android:layout_marginTop="52dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linearLayout"
app:srcCompat="@drawable/logo_ldc" />
<LinearLayout
android:id="@+id/btnLineLinearLayout"
style="@style/width_match_parent_max_200"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="280dp"
android:orientation="horizontal"
android:layout_marginTop="5dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView">
<Button
android:id="@+id/btnPing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="5dp"
android:text="Ping"
android:backgroundTint="#D4021D"
android:width="120dp"/>
<Button
android:id="@+id/btnReboot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="1"
android:backgroundTint="#D4021D"
android:text="Reboot"
android:width="120dp"/>
</LinearLayout>
<LinearLayout
android:id="@+id/btnLine2LinearLayout"
style="@style/width_match_parent_max_200"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="280dp"
android:orientation="horizontal"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btnLineLinearLayout">
<Button
android:id="@+id/btnRefresh"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="5dp"
android:text="Rafraichir"
android:backgroundTint="#D4021D"
android:width="120dp"/>
<Button
android:id="@+id/btnGoBack"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="1"
android:backgroundTint="#D4021D"
android:text="Retour"
android:width="120dp"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -39,7 +39,7 @@
android:id="@+id/configBloc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="52dp"
android:layout_marginTop="22dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:orientation="vertical"
@ -119,7 +119,7 @@
style="@style/width_match_parent_max_200"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:layout_marginTop="12dp"
android:orientation="horizontal">
<Button
android:id="@+id/btnValidateConfig"
@ -137,7 +137,25 @@
android:layout_margin="5dp"
android:text="Retour"
android:backgroundTint="#D4021D"/>
</LinearLayout>
<LinearLayout
style="@style/width_match_parent_max_200"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="22dp"
android:orientation="horizontal">
<Button
android:id="@+id/btnDiagnostic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="5dp"
android:text="Diagnostique"
android:backgroundTint="#D4021D"/>
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,111 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.ldc.KioskPingActivity">
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Diagnostique"
android:textColor="#000000"
android:textSize="48sp"
android:textStyle="bold" />
</LinearLayout>
<ImageView
android:id="@+id/imageView"
android:layout_width="141dp"
android:layout_height="132dp"
android:layout_marginTop="52dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linearLayout"
app:srcCompat="@drawable/logo_ldc" />
<LinearLayout
android:id="@+id/configBloc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="52dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:minWidth="240dp"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="FQDN / Adresse Ip"
android:textAlignment="center"
android:textColor="#000000"
android:textSize="20sp"
android:textStyle="bold" />
<EditText
android:id="@+id/editTextPingUrl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="30dp"
android:ems="10"
android:inputType="textUri" />
<TextView
android:id="@+id/textResult"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxWidth="200dp"
android:layout_marginRight="20dp"
android:layout_marginLeft="20dp"
android:maxLines="2"
android:text="Succès !"
android:textAlignment="center"
android:textColor="#000000"
android:textSize="15sp"
android:textStyle="bold"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="parent" />
<LinearLayout
style="@style/width_match_parent_max_200"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:orientation="horizontal">
<Button
android:id="@+id/btnPing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="5dp"
android:text="Ping"
android:backgroundTint="#D4021D"/>
<Button
android:id="@+id/btnGoBack"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="5dp"
android:text="Retour"
android:backgroundTint="#D4021D"/>
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>