Compare commits
37 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f12e5a8f6 | ||
|
|
53896d2865 | ||
|
|
478f6334ec | ||
|
|
708bba6f59 | ||
|
|
ce5cc01ba5 | ||
|
|
8fd5b838bb | ||
|
|
1329f78a8a | ||
|
|
10234ccfa8 | ||
|
|
e6897c42ed | ||
|
|
b859623950 | ||
|
|
d2ee0352cc | ||
|
|
ec25150632 | ||
|
|
dceddbec3c | ||
|
|
d70f1e46ab | ||
|
|
dd5b421d34 | ||
|
|
76b80c7f7d | ||
|
|
97143ccf7a | ||
|
|
edb556f2a3 | ||
|
|
85a003c42d | ||
|
|
8027dbd574 | ||
|
|
5eead612ee | ||
|
|
af8d8e2570 | ||
|
|
df62f17c7a | ||
|
|
66a697904a | ||
|
|
2a77e0ab62 | ||
|
|
9037ce9bfb | ||
|
|
e5ae57a657 | ||
|
|
cfd7b2fe9f | ||
|
|
ebbbf468a5 | ||
|
|
d750be9ac8 | ||
|
|
d4b64d5ae6 | ||
|
|
1085cacc35 | ||
|
|
fd99d655a0 | ||
|
|
fd16bea84f | ||
|
|
deeef24a25 | ||
|
|
a8c216e8f5 | ||
|
|
5abd9bb5c4 |
5
.idea/codeStyles/Project.xml
generated
|
|
@ -4,6 +4,9 @@
|
|||
<JavaCodeStyleSettings>
|
||||
<option name="JD_P_AT_EMPTY_LINES" value="false" />
|
||||
</JavaCodeStyleSettings>
|
||||
<JetCodeStyleSettings>
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<codeStyleSettings language="ObjectiveC">
|
||||
<option name="RIGHT_MARGIN" value="120" />
|
||||
<option name="SOFT_MARGINS" value="80,100" />
|
||||
|
|
@ -121,9 +124,9 @@
|
|||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
<option name="RIGHT_MARGIN" value="120" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
|
||||
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" />
|
||||
<option name="CLASS_ANNOTATION_WRAP" value="1" />
|
||||
<option name="WRAP_ON_TYPING" value="0" />
|
||||
</codeStyleSettings>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,12 @@ android {
|
|||
compileSdkVersion 30
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.gaurav.avnc"
|
||||
minSdkVersion 21
|
||||
applicationId "com.ldc.kiosk"
|
||||
minSdkVersion 30
|
||||
targetSdkVersion 30
|
||||
versionCode 14
|
||||
versionName "1.9.2"
|
||||
//versionName "1.9.2"
|
||||
versionName "1.0.0"
|
||||
|
||||
javaCompileOptions {
|
||||
annotationProcessorOptions {
|
||||
|
|
@ -53,14 +54,34 @@ android {
|
|||
buildFeatures {
|
||||
dataBinding true
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
buildTypes {
|
||||
debug {
|
||||
buildConfigField "java.util.Date", "BUILD_TIME", "new java.util.Date(" + System.currentTimeMillis() + "L)"
|
||||
}
|
||||
release {
|
||||
buildConfigField "java.util.Date", "BUILD_TIME", "new java.util.Date(" + System.currentTimeMillis() + "L)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
pickFirst 'META-INF/ASL-2.0.txt'
|
||||
pickFirst 'META-INF/LGPL-3.0.txt'
|
||||
pickFirst 'draftv4/schema'
|
||||
pickFirst 'draftv3/schema'
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path file('CMakeLists.txt')
|
||||
|
|
@ -79,18 +100,36 @@ android {
|
|||
|
||||
dependencies {
|
||||
implementation "androidx.core:core-ktx:1.5.0"
|
||||
implementation "androidx.activity:activity-ktx:1.2.3"
|
||||
implementation "androidx.fragment:fragment-ktx:1.3.4"
|
||||
implementation 'androidx.activity:activity-ktx:1.3.1'
|
||||
implementation "androidx.fragment:fragment-ktx:1.4.1"
|
||||
implementation "androidx.appcompat:appcompat:1.3.0"
|
||||
implementation "androidx.dynamicanimation:dynamicanimation:1.0.0"
|
||||
implementation "androidx.biometric:biometric-ktx:1.2.0-alpha04"
|
||||
implementation "androidx.preference:preference:1.1.1"
|
||||
implementation "org.greenrobot:eventbus:3.3.1"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
|
||||
implementation "androidx.lifecycle:lifecycle-process:2.3.1"
|
||||
implementation "androidx.lifecycle:lifecycle-common:2.3.1"
|
||||
implementation "androidx.recyclerview:recyclerview:1.2.0"
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
|
||||
implementation "androidx.drawerlayout:drawerlayout:1.1.1"
|
||||
implementation "androidx.room:room-runtime:2.3.0"
|
||||
implementation "androidx.room:room-ktx:2.3.0"
|
||||
implementation files('libs/com.serialIntCaster-1.1.0.jar')
|
||||
|
||||
// Life-cycle
|
||||
implementation 'androidx.lifecycle:lifecycle-process:2.5.1'
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.1"
|
||||
|
||||
// Json
|
||||
implementation "com.fasterxml.jackson.core:jackson-databind:2.9.8"
|
||||
implementation "com.fasterxml.jackson.core:jackson-core:2.9.8"
|
||||
implementation "com.fasterxml.jackson.core:jackson-annotations:2.9.8"
|
||||
implementation "com.github.fge:jackson-coreutils:1.8"
|
||||
implementation "com.github.fge:json-schema-core:1.2.5"
|
||||
implementation "com.github.fge:json-schema-validator:2.2.6"
|
||||
|
||||
kapt "androidx.room:room-compiler:2.3.0"
|
||||
|
||||
implementation "com.google.android.material:material:1.3.0"
|
||||
|
|
@ -108,4 +147,7 @@ dependencies {
|
|||
androidTestImplementation "androidx.test:rules:1.4.0"
|
||||
androidTestImplementation "androidx.test.ext:junit-ktx:1.1.3"
|
||||
androidTestImplementation "io.mockk:mockk-android:1.12.0"
|
||||
|
||||
implementation("com.squareup.okhttp3:okhttp:4.10.0")
|
||||
implementation("org.apache.commons:commons-csv:1.5")
|
||||
}
|
||||
BIN
app/libs/com.serialIntCaster-1.1.0-javadoc.jar
Normal file
BIN
app/libs/com.serialIntCaster-1.1.0.jar
Normal file
BIN
app/src/ci/ic_launcher-playstore.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
5
app/src/ci/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
5
app/src/ci/res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
BIN
app/src/ci/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
app/src/ci/res/mipmap-hdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
app/src/ci/res/mipmap-hdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
app/src/ci/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
app/src/ci/res/mipmap-mdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
app/src/ci/res/mipmap-mdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/ci/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
app/src/ci/res/mipmap-xhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
app/src/ci/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
app/src/ci/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
app/src/ci/res/mipmap-xxhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
app/src/ci/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
app/src/ci/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
app/src/ci/res/mipmap-xxxhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
app/src/ci/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
4
app/src/ci/res/values/ic_launcher_background.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FFFFFF</color>
|
||||
</resources>
|
||||
|
|
@ -1,16 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Gaurav Ujjwal.
|
||||
~
|
||||
~ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
~
|
||||
~ See COPYING.txt for more details.
|
||||
-->
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.gaurav.avnc">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.REBOOT" />
|
||||
|
||||
<application
|
||||
android:name=".App"
|
||||
|
|
@ -18,25 +17,69 @@
|
|||
android:fullBackupContent="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
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.KioskDiagnosticActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.ldc.KioskParameterActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.ldc.KioskParameterPasswordActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.ldc.KioskActivity"
|
||||
android:exported="true">
|
||||
<meta-data
|
||||
android:name="android.app.lib_name"
|
||||
android:value="" />
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.home.SplashActivity"
|
||||
android:theme="@style/SplashTheme" />
|
||||
|
||||
<activity-alias
|
||||
android:name=".StartupActivity"
|
||||
android:exported="true"
|
||||
android:targetActivity=".ui.home.SplashActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- <intent-filter> -->
|
||||
<!-- <action android:name="android.intent.action.MAIN" /> -->
|
||||
|
||||
|
||||
<!-- <category android:name="android.intent.category.LAUNCHER" /> -->
|
||||
<!-- </intent-filter> -->
|
||||
</activity-alias>
|
||||
|
||||
<activity android:name=".ui.home.HomeActivity" />
|
||||
<activity
|
||||
android:name=".ui.home.UrlBarActivity"
|
||||
android:parentActivityName=".ui.home.HomeActivity"
|
||||
android:windowSoftInputMode="stateVisible" />
|
||||
<activity
|
||||
android:name=".ui.vnc.VncActivity"
|
||||
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
|
||||
android:resizeableActivity="true"
|
||||
android:supportsPictureInPicture="true"
|
||||
tools:ignore="UnusedAttribute" />
|
||||
<activity android:name=".ui.vnc.UriReceiverActivity" />
|
||||
|
||||
<activity-alias
|
||||
android:name=".UriReceiverActivity"
|
||||
android:exported="true"
|
||||
android:targetActivity=".ui.vnc.UriReceiverActivity">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
|
|
@ -47,28 +90,7 @@
|
|||
</intent-filter>
|
||||
</activity-alias>
|
||||
|
||||
<activity
|
||||
android:name=".ui.home.SplashActivity"
|
||||
android:theme="@style/SplashTheme" />
|
||||
|
||||
<activity android:name=".ui.home.HomeActivity" />
|
||||
|
||||
<activity
|
||||
android:name=".ui.home.UrlBarActivity"
|
||||
android:parentActivityName=".ui.home.HomeActivity"
|
||||
android:windowSoftInputMode="stateVisible" />
|
||||
|
||||
<activity
|
||||
android:name=".ui.vnc.VncActivity"
|
||||
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
|
||||
android:resizeableActivity="true"
|
||||
android:supportsPictureInPicture="true"
|
||||
tools:ignore="UnusedAttribute" />
|
||||
|
||||
<activity android:name=".ui.vnc.UriReceiverActivity" />
|
||||
|
||||
<activity android:name=".ui.prefs.PrefsActivity" />
|
||||
|
||||
<activity android:name=".ui.about.AboutActivity" />
|
||||
</application>
|
||||
|
||||
|
|
|
|||
14
app/src/main/assets/security.schema.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "Configuration",
|
||||
"description": "Kiosk app configuration",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"password": {
|
||||
"type": "string",
|
||||
"description": "Kiosk config password"
|
||||
}
|
||||
},
|
||||
"required": ["password"]
|
||||
}
|
||||
BIN
app/src/main/ic_launcher-playstore.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
|
|
@ -24,11 +24,13 @@ class App : Application() {
|
|||
}
|
||||
|
||||
private fun updateNightMode(theme: String) {
|
||||
val nightMode = when (theme) {
|
||||
var nightMode = when (theme) {
|
||||
"light" -> AppCompatDelegate.MODE_NIGHT_NO
|
||||
"dark" -> AppCompatDelegate.MODE_NIGHT_YES
|
||||
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
}
|
||||
// * Force Light mode
|
||||
nightMode = AppCompatDelegate.MODE_NIGHT_NO;
|
||||
AppCompatDelegate.setDefaultNightMode(nightMode)
|
||||
}
|
||||
}
|
||||
195
app/src/main/java/com/gaurav/avnc/ui/ldc/KioskActivity.kt
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
package com.gaurav.avnc.ui.ldc
|
||||
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleObserver
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.OnLifecycleEvent
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.gaurav.avnc.R
|
||||
import com.gaurav.avnc.ui.ldc.events.AppSwitchedToForegroundEvent
|
||||
import com.gaurav.avnc.ui.ldc.events.NetworkStateChangedEvent
|
||||
import com.gaurav.avnc.ui.ldc.lib.ApplicationInfo
|
||||
import com.gaurav.avnc.ui.ldc.lib.KioskViewPagerAdapter
|
||||
import com.gaurav.avnc.ui.ldc.lib.NetworkObserver
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import okhttp3.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
||||
|
||||
class KioskActivity : AppCompatActivity(), LifecycleObserver {
|
||||
|
||||
// * Network
|
||||
private val networkObserver = NetworkObserver(this, lifecycle)
|
||||
init { lifecycle.addObserver(networkObserver) }
|
||||
|
||||
// * Tab Elements
|
||||
private lateinit var tabLayout: TabLayout
|
||||
private lateinit var viewPager: ViewPager2
|
||||
private lateinit var kioskViewPagerAdapter: KioskViewPagerAdapter
|
||||
|
||||
// * AppInfo
|
||||
lateinit var appInfo: ApplicationInfo
|
||||
private set
|
||||
|
||||
// * Permissions
|
||||
private lateinit var permissionLauncher: ActivityResultLauncher<Array<String>>
|
||||
private var isInternetPermissionGranted = false
|
||||
private var isNetworkStatePermissionGranted = false
|
||||
private var isWifiStatePermissionGranted = false
|
||||
|
||||
/**
|
||||
* On Create
|
||||
*/
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
|
||||
setContentView(R.layout.activity_kiosk)
|
||||
|
||||
// * Setup AppInfo
|
||||
appInfo = ApplicationInfo(getDefaultSharedPreferences(applicationContext))
|
||||
|
||||
// * Request Permissions
|
||||
permissionLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
|
||||
isInternetPermissionGranted = permissions[android.Manifest.permission.INTERNET] ?: isInternetPermissionGranted
|
||||
isNetworkStatePermissionGranted = permissions[android.Manifest.permission.ACCESS_NETWORK_STATE] ?: isNetworkStatePermissionGranted
|
||||
isWifiStatePermissionGranted = permissions[android.Manifest.permission.ACCESS_WIFI_STATE] ?: isWifiStatePermissionGranted
|
||||
}
|
||||
requestPermission()
|
||||
|
||||
// * Init tabs
|
||||
tabLayout = findViewById(R.id.kiosk_tab_layout)
|
||||
kioskViewPagerAdapter = KioskViewPagerAdapter(this)
|
||||
viewPager = findViewById(R.id.kiosk_view_pager)
|
||||
viewPager.adapter = kioskViewPagerAdapter
|
||||
|
||||
// * Tab Selection
|
||||
tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
|
||||
override fun onTabSelected(tab: TabLayout.Tab) {
|
||||
viewPager.currentItem = tab.position
|
||||
}
|
||||
override fun onTabReselected(tab: TabLayout.Tab) {
|
||||
viewPager.currentItem = tab.position
|
||||
}
|
||||
override fun onTabUnselected(tab: TabLayout.Tab) {}
|
||||
})
|
||||
viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
super.onPageSelected(position)
|
||||
viewPager.currentItem = position
|
||||
tabLayout.selectTab(tabLayout.getTabAt(position))
|
||||
}
|
||||
})
|
||||
|
||||
// * Register event bus
|
||||
EventBus.getDefault().register(this)
|
||||
|
||||
// Check NetWork Connection
|
||||
listenToNetworkConnectionChanges()
|
||||
}
|
||||
|
||||
/**
|
||||
* Event on Activity Resume
|
||||
*/
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
// * AppInfo Refresh
|
||||
appInfo.refreshConfigProperties()
|
||||
appInfo.retrieveDeviceNetworkInfo(applicationContext)
|
||||
}
|
||||
|
||||
/**
|
||||
* Event on App Foreground
|
||||
*/
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_START)
|
||||
fun onForegroundStart() {
|
||||
EventBus.getDefault().post(AppSwitchedToForegroundEvent())
|
||||
}
|
||||
|
||||
/**
|
||||
* Event on App Kill
|
||||
*/
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
// * Register event bus
|
||||
EventBus.getDefault().unregister(this)
|
||||
ProcessLifecycleOwner.get().lifecycle.removeObserver(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* On network message received
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onMessageEvent(event: NetworkStateChangedEvent?) {
|
||||
appInfo.networkConnected = networkObserver.isConnected
|
||||
appInfo.retrieveDeviceNetworkInfo(applicationContext)
|
||||
Log.d("network", "network state changed on kiosk activity")
|
||||
// runOnUiThread {
|
||||
// Toast.makeText(applicationContext, "Network changed", Toast.LENGTH_SHORT).show();
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Observe Network connection
|
||||
*/
|
||||
private fun listenToNetworkConnectionChanges() {
|
||||
networkObserver.networkAvailableStateFlow.asLiveData().observe(this, Observer {
|
||||
appInfo.networkConnected = networkObserver.isConnected
|
||||
appInfo.retrieveDeviceNetworkInfo(applicationContext)
|
||||
Log.d("network", "network state changed on kiosk activity")
|
||||
// runOnUiThread {
|
||||
// Toast.makeText(applicationContext, "Network changed", Toast.LENGTH_SHORT).show();
|
||||
// }
|
||||
EventBus.getDefault().post(NetworkStateChangedEvent())
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Request application permissions
|
||||
*/
|
||||
private fun requestPermission() {
|
||||
isInternetPermissionGranted = ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
android.Manifest.permission.INTERNET
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
isNetworkStatePermissionGranted = ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
android.Manifest.permission.ACCESS_NETWORK_STATE
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
isWifiStatePermissionGranted = ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
android.Manifest.permission.ACCESS_WIFI_STATE
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
val permissionRequest: MutableList<String> = ArrayList()
|
||||
|
||||
if (!isInternetPermissionGranted) {
|
||||
permissionRequest.add(android.Manifest.permission.INTERNET)
|
||||
}
|
||||
|
||||
if (!isNetworkStatePermissionGranted) {
|
||||
permissionRequest.add(android.Manifest.permission.ACCESS_NETWORK_STATE)
|
||||
}
|
||||
|
||||
if (!isWifiStatePermissionGranted) {
|
||||
permissionRequest.add(android.Manifest.permission.ACCESS_WIFI_STATE)
|
||||
}
|
||||
|
||||
if (permissionRequest.isNotEmpty()){
|
||||
permissionLauncher.launch(permissionRequest.toTypedArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,433 @@
|
|||
package com.gaurav.avnc.ui.ldc
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.location.LocationManager
|
||||
import android.net.wifi.WifiManager
|
||||
import android.os.Bundle
|
||||
import android.os.PowerManager
|
||||
import android.provider.Settings
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TableLayout
|
||||
import android.widget.TableRow
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.gaurav.avnc.R
|
||||
import com.gaurav.avnc.ui.ldc.lib.ApplicationInfo
|
||||
import com.gaurav.avnc.ui.ldc.lib.WifiFrequencies
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
|
||||
class KioskDiagnosticActivity : AppCompatActivity(), View.OnClickListener {
|
||||
|
||||
// * Permissions
|
||||
private lateinit var permissionLauncher: ActivityResultLauncher<Array<String>>
|
||||
private var isInternetPermissionGranted = false
|
||||
private var isNetworkStatePermissionGranted = false
|
||||
private var isWifiStatePermissionGranted = false
|
||||
private var isWifiStateChangePermissionGranted = false
|
||||
private var isAccessFineLocationGranted = false
|
||||
private var isAccessCoarseLocationGranted = false
|
||||
|
||||
// * View Elements
|
||||
private lateinit var btnPing: Button
|
||||
private lateinit var btnReboot: Button
|
||||
private lateinit var btnRefresh: Button
|
||||
private lateinit var btnGoBack: Button
|
||||
private lateinit var tableLayoutWifiRows: TableLayout
|
||||
private lateinit var progressBarWifiStrengthPercent: ProgressBar
|
||||
private lateinit var textViewWifiStrengthPercent: TextView
|
||||
|
||||
// * AppInfo
|
||||
private lateinit var appInfo: ApplicationInfo
|
||||
|
||||
// * WifiManager
|
||||
private var wifiManagerListening = false
|
||||
private lateinit var mWifiManager: WifiManager
|
||||
private lateinit var mWifiScanReceiver: BroadcastReceiver
|
||||
|
||||
/**
|
||||
* On Create
|
||||
*/
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_kiosk_diagnostic)
|
||||
|
||||
// * Request Permissions
|
||||
permissionLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
|
||||
isInternetPermissionGranted = permissions[Manifest.permission.INTERNET] ?: isInternetPermissionGranted
|
||||
isNetworkStatePermissionGranted = permissions[Manifest.permission.ACCESS_NETWORK_STATE] ?: isNetworkStatePermissionGranted
|
||||
isWifiStatePermissionGranted = permissions[Manifest.permission.ACCESS_WIFI_STATE] ?: isWifiStatePermissionGranted
|
||||
isWifiStateChangePermissionGranted = permissions[Manifest.permission.CHANGE_WIFI_STATE] ?: isWifiStateChangePermissionGranted
|
||||
isAccessFineLocationGranted = permissions[Manifest.permission.ACCESS_FINE_LOCATION] ?: isAccessFineLocationGranted
|
||||
isAccessCoarseLocationGranted = permissions[Manifest.permission.ACCESS_COARSE_LOCATION] ?: isAccessCoarseLocationGranted
|
||||
}
|
||||
|
||||
btnPing = findViewById<Button>(R.id.btnPing)
|
||||
btnReboot = findViewById<Button>(R.id.btnReboot)
|
||||
btnRefresh = findViewById<Button>(R.id.btnRefresh)
|
||||
btnGoBack = findViewById<Button>(R.id.btnGoBack)
|
||||
tableLayoutWifiRows = findViewById<TableLayout>(R.id.tableLayoutWifiRows)
|
||||
progressBarWifiStrengthPercent = findViewById<ProgressBar>(R.id.progressBarWifiStrengthPercent)
|
||||
textViewWifiStrengthPercent = findViewById<TextView>(R.id.textViewWifiStrengthPercent)
|
||||
|
||||
// * EVENTS
|
||||
btnPing.setOnClickListener(this)
|
||||
btnReboot.setOnClickListener(this)
|
||||
btnRefresh.setOnClickListener(this)
|
||||
btnGoBack.setOnClickListener(this)
|
||||
|
||||
// * Setup AppInfo
|
||||
appInfo = ApplicationInfo(PreferenceManager.getDefaultSharedPreferences(applicationContext))
|
||||
|
||||
// * Wont be able to => Hide
|
||||
btnReboot.visibility = View.INVISIBLE
|
||||
resetWifiTableView()
|
||||
requestPermission()
|
||||
}
|
||||
|
||||
/**
|
||||
* Event on Activity Resume
|
||||
*/
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
// * AppInfo Refresh
|
||||
appInfo.refreshConfigProperties()
|
||||
appInfo.retrieveDeviceNetworkInfo(applicationContext)
|
||||
}
|
||||
|
||||
/**
|
||||
* Event On Request permission result
|
||||
*/
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
permissions.forEachIndexed { index, permission ->
|
||||
when(permission) {
|
||||
Manifest.permission.INTERNET -> isInternetPermissionGranted = grantResults[index] == PackageManager.PERMISSION_GRANTED
|
||||
Manifest.permission.ACCESS_NETWORK_STATE -> isNetworkStatePermissionGranted = grantResults[index] == PackageManager.PERMISSION_GRANTED
|
||||
Manifest.permission.ACCESS_WIFI_STATE -> isWifiStatePermissionGranted = grantResults[index] == PackageManager.PERMISSION_GRANTED
|
||||
Manifest.permission.CHANGE_WIFI_STATE -> isWifiStateChangePermissionGranted = grantResults[index] == PackageManager.PERMISSION_GRANTED
|
||||
Manifest.permission.ACCESS_FINE_LOCATION -> isAccessFineLocationGranted = grantResults[index] == PackageManager.PERMISSION_GRANTED
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION -> isAccessCoarseLocationGranted = grantResults[index] == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// * Action UI events
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.btnPing -> {
|
||||
startActivity(Intent(this, KioskPingActivity::class.java))
|
||||
return
|
||||
}
|
||||
R.id.btnReboot -> {
|
||||
startReboot()
|
||||
return
|
||||
}
|
||||
R.id.btnRefresh -> {
|
||||
requestPermission()
|
||||
|
||||
mWifiManager = getSystemService(Context.WIFI_SERVICE) as WifiManager
|
||||
if(mWifiManager.wifiState === WifiManager.WIFI_STATE_DISABLED) {
|
||||
resetWifiTableView()
|
||||
toastMessage(this, "Wifi désactivé")
|
||||
return
|
||||
}
|
||||
if (!checkPermissionsAreGranted()) {
|
||||
toastMessage(this, "Permissions manquantes")
|
||||
return
|
||||
}
|
||||
if (!canGetLocation()) {
|
||||
showSettingsAlert()
|
||||
return
|
||||
}
|
||||
|
||||
initWifiScanner()
|
||||
|
||||
if(wifiManagerListening) {
|
||||
toastMessage(this, "Rafraichissement des réseaux WiFi")
|
||||
mWifiManager.startScan()
|
||||
}
|
||||
return
|
||||
}
|
||||
R.id.btnGoBack -> {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Wifi Scanner
|
||||
*/
|
||||
private fun initWifiScanner() {
|
||||
val activity = this;
|
||||
if(!checkPermissionsAreGranted()) {
|
||||
toastMessage(activity, "Permissions manquantes pour scanner les réseaux WiFI")
|
||||
return
|
||||
}
|
||||
if (wifiManagerListening) {
|
||||
return
|
||||
}
|
||||
// * WIFI scan init
|
||||
mWifiManager = getSystemService(Context.WIFI_SERVICE) as WifiManager
|
||||
mWifiScanReceiver = object : BroadcastReceiver () {
|
||||
override fun onReceive(c:Context, intent: Intent) {
|
||||
// * Scan changed
|
||||
resetWifiTableView()
|
||||
|
||||
mWifiManager.scanResults.forEach{
|
||||
/** From -100 to 0
|
||||
* https://stackoverflow.com/questions/15797920/how-to-convert-wifi-signal-strength-from-quality-percent-to-rssi-dbm/15798024#15798024
|
||||
*/
|
||||
var precisePercent = if (it.level <= -100) 0 else if(it.level >= -50) 100 else 2 * (it.level + 100);
|
||||
val row = LayoutInflater.from(this@KioskDiagnosticActivity)
|
||||
.inflate(R.layout.activity_kiosk_diagnostic_wifi_row, null) as TableRow
|
||||
val networkName = if(it.SSID.length === 0) it.BSSID else it.SSID
|
||||
(row.findViewById<View>(R.id.tableCellSSID) as TextView).text = networkName.substring(0, min(networkName.length, 18))
|
||||
(row.findViewById<View>(R.id.tableCellSignal) as TextView).text = it.level.toString()
|
||||
(row.findViewById<View>(R.id.tableCellSignalPercent) as TextView).text = "$precisePercent %"
|
||||
(row.findViewById<View>(R.id.tableCellSignalFreq) as TextView).text = getWifiType(it.frequency)
|
||||
tableLayoutWifiRows.addView(row)
|
||||
}
|
||||
|
||||
// * Update Actual Wifi strength
|
||||
val wifiInfo = mWifiManager.connectionInfo
|
||||
val precisePercent = if (wifiInfo.rssi <= -100) 0 else if(wifiInfo.rssi >= -50) 100 else 2 * (wifiInfo.rssi + 100)
|
||||
|
||||
progressBarWifiStrengthPercent.max = 100
|
||||
progressBarWifiStrengthPercent.min = 0
|
||||
progressBarWifiStrengthPercent.progress = precisePercent
|
||||
textViewWifiStrengthPercent.text = "$precisePercent %"
|
||||
}
|
||||
}
|
||||
registerReceiver(mWifiScanReceiver, IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION))
|
||||
registerReceiver(mWifiScanReceiver, IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION))
|
||||
registerReceiver(mWifiScanReceiver, IntentFilter(WifiManager.EXTRA_RESULTS_UPDATED))
|
||||
/**
|
||||
* The WifiManager.startScan() usage is limited to: - Each foreground app is restricted to 4 scans every 2 minutes
|
||||
* All background apps combined are restricted to one scan every 30 minutes.
|
||||
* https://stackoverflow.com/questions/49178307/startscan-in-wifimanager-deprecated-in-android-p
|
||||
*/
|
||||
mWifiManager.startScan()
|
||||
wifiManagerListening = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset Wifi table View
|
||||
*/
|
||||
private fun resetWifiTableView() {
|
||||
val childCount = tableLayoutWifiRows.childCount
|
||||
if(childCount > 0) {
|
||||
tableLayoutWifiRows.removeViews(0, max(0, childCount))
|
||||
}
|
||||
// * Append Table head
|
||||
val row: TableRow = if (wifiManagerListening) {
|
||||
LayoutInflater.from(this@KioskDiagnosticActivity)
|
||||
.inflate(R.layout.activity_kiosk_diagnostic_wifi_header, null) as TableRow
|
||||
} else {
|
||||
LayoutInflater.from(this@KioskDiagnosticActivity)
|
||||
.inflate(R.layout.activity_kiosk_diagnostic_wifi_press_refresh_row, null) as TableRow
|
||||
}
|
||||
tableLayoutWifiRows.addView(row)
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to reboot device
|
||||
*/
|
||||
private fun startReboot()
|
||||
{
|
||||
try {
|
||||
val pm: PowerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||
pm.reboot(null)
|
||||
} catch(e: UnsupportedOperationException) {
|
||||
toastMessage(this ,"Erreur (${e.message}) : ${e.cause?.message}")
|
||||
} catch (e: SecurityException) {
|
||||
toastMessage(this ,"Erreur (${e.message}) : ${e.cause?.message}")
|
||||
} finally {
|
||||
toastMessage(this ,"Droits insufisants")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show settings for location
|
||||
* To allow user ot enable this
|
||||
*/
|
||||
private fun showSettingsAlert() {
|
||||
val alertDialog: AlertDialog.Builder = AlertDialog.Builder(this)
|
||||
|
||||
// Setting Dialog Title
|
||||
alertDialog.setTitle("Attention !")
|
||||
|
||||
// Setting Dialog Message
|
||||
alertDialog.setMessage("Pour scanner les réseaux wifi, la localisation doit être activée.")
|
||||
|
||||
// On pressing Settings button
|
||||
alertDialog.setPositiveButton(
|
||||
"Accéder à la configuration",
|
||||
DialogInterface.OnClickListener { _, _ ->
|
||||
startActivity(Intent(
|
||||
Settings.ACTION_LOCATION_SOURCE_SETTINGS
|
||||
))
|
||||
})
|
||||
alertDialog.show()
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we can get location
|
||||
* It has to be enabled to access wifi networks
|
||||
*/
|
||||
private fun canGetLocation(): Boolean {
|
||||
var gpsEnabled = false
|
||||
var networkEnabled = false
|
||||
var passiveLocationEnabled = false
|
||||
val lm: LocationManager = getSystemService(LOCATION_SERVICE) as LocationManager
|
||||
|
||||
// exceptions will be thrown if provider is not permitted.
|
||||
try {
|
||||
gpsEnabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER)
|
||||
} catch (ex: Exception) {
|
||||
toastMessage(this, "Erreur " + ex.message)
|
||||
}
|
||||
try {
|
||||
passiveLocationEnabled = lm.isProviderEnabled(LocationManager.PASSIVE_PROVIDER)
|
||||
} catch (ex: Exception) {
|
||||
toastMessage(this, "Erreur " + ex.message)
|
||||
}
|
||||
try {
|
||||
networkEnabled = lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
|
||||
} catch (ex: Exception) {
|
||||
toastMessage(this, "Erreur " + ex.message)
|
||||
}
|
||||
return gpsEnabled || networkEnabled || passiveLocationEnabled
|
||||
}
|
||||
|
||||
/**
|
||||
* Display toast message
|
||||
*/
|
||||
private fun toastMessage(activity: Activity, message: String) {
|
||||
activity.runOnUiThread {
|
||||
Toast.makeText(
|
||||
activity.applicationContext,
|
||||
message,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all permissions are granted
|
||||
*/
|
||||
private fun checkPermissionsAreGranted(): Boolean
|
||||
{
|
||||
isInternetPermissionGranted = ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
Manifest.permission.INTERNET
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
isNetworkStatePermissionGranted = ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
Manifest.permission.ACCESS_NETWORK_STATE
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
isWifiStatePermissionGranted = ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
Manifest.permission.ACCESS_WIFI_STATE
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
isWifiStateChangePermissionGranted = ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
Manifest.permission.CHANGE_WIFI_STATE
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
isAccessFineLocationGranted = ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
Manifest.permission.ACCESS_FINE_LOCATION
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
isAccessCoarseLocationGranted = ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
return isInternetPermissionGranted && isNetworkStatePermissionGranted &&
|
||||
isWifiStatePermissionGranted && isWifiStateChangePermissionGranted &&
|
||||
isAccessFineLocationGranted && isAccessCoarseLocationGranted
|
||||
}
|
||||
|
||||
/**
|
||||
* Request application permissions
|
||||
*/
|
||||
private fun requestPermission() {
|
||||
if (checkPermissionsAreGranted()) {
|
||||
return
|
||||
}
|
||||
val permissionRequest: MutableList<String> = ArrayList()
|
||||
|
||||
if (!isInternetPermissionGranted) {
|
||||
permissionRequest.add(Manifest.permission.INTERNET)
|
||||
}
|
||||
|
||||
if (!isNetworkStatePermissionGranted) {
|
||||
permissionRequest.add(Manifest.permission.ACCESS_NETWORK_STATE)
|
||||
}
|
||||
|
||||
if (!isWifiStatePermissionGranted) {
|
||||
permissionRequest.add(Manifest.permission.ACCESS_WIFI_STATE)
|
||||
}
|
||||
|
||||
if (!isWifiStateChangePermissionGranted) {
|
||||
permissionRequest.add(Manifest.permission.CHANGE_WIFI_STATE)
|
||||
}
|
||||
|
||||
if (!isAccessFineLocationGranted) {
|
||||
permissionRequest.add(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
}
|
||||
|
||||
if (!isAccessCoarseLocationGranted) {
|
||||
permissionRequest.add(Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||
}
|
||||
|
||||
ActivityCompat.requestPermissions(this@KioskDiagnosticActivity, permissionRequest.toTypedArray(), 100)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wifi network type based on frequency
|
||||
*/
|
||||
private fun getWifiType(frequency: Int): String {
|
||||
|
||||
if (WifiFrequencies.AH_LOW.freq.rangeTo(WifiFrequencies.AH_HIGH.freq).contains(frequency)) {
|
||||
return "860/900 MHz"
|
||||
}
|
||||
if (WifiFrequencies.BGNAX_LOW.freq.rangeTo(WifiFrequencies.BGNAX_HIGH.freq).contains(frequency)) {
|
||||
return "2.4Ghz"
|
||||
}
|
||||
if (WifiFrequencies.Y_LOW.freq.rangeTo(WifiFrequencies.Y_HIGH.freq).contains(frequency)) {
|
||||
return "3.65 GHz (US)"
|
||||
}
|
||||
if (WifiFrequencies.J_LOW.freq.rangeTo(WifiFrequencies.J_HIGH.freq).contains(frequency)) {
|
||||
return "4.9–5.0 GHz (J)"
|
||||
}
|
||||
if (WifiFrequencies.AHNACAX_LOW.freq.rangeTo(WifiFrequencies.AHNACAX_HIGH.freq).contains(frequency)) {
|
||||
return "5Ghz"
|
||||
}
|
||||
return "$frequency N/a"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
package com.gaurav.avnc.ui.ldc
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import android.widget.Toast
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
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.InvalidUriException
|
||||
import com.gaurav.avnc.viewmodel.HomeViewModel
|
||||
import java.net.InetAddress
|
||||
import java.net.UnknownHostException
|
||||
|
||||
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
|
||||
|
||||
// * AppInfo
|
||||
private lateinit var appInfo: ApplicationInfo
|
||||
|
||||
/**
|
||||
* On Create
|
||||
*/
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_kiosk_parameter)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
// * Setup AppInfo
|
||||
appInfo = ApplicationInfo(getDefaultSharedPreferences(applicationContext))
|
||||
|
||||
// * Assign inputs
|
||||
editTextServerUrl.setText(appInfo.configServerUri.toString())
|
||||
editTextIp.setText(appInfo.vncArgIp.hostAddress)
|
||||
editTextPort.setText(appInfo.vncArgPort.toString())
|
||||
}
|
||||
|
||||
/**
|
||||
* Event on Activity Resume
|
||||
*/
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
// * AppInfo Refresh
|
||||
appInfo.refreshConfigProperties()
|
||||
appInfo.retrieveDeviceNetworkInfo(applicationContext)
|
||||
}
|
||||
|
||||
/**
|
||||
* On Back pressed
|
||||
*/
|
||||
override fun onBackPressed() {
|
||||
navigateUpTo(Intent(baseContext, KioskActivity::class.java))
|
||||
finish()
|
||||
}
|
||||
|
||||
// * Action UI events
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.btnValidateConfig -> {
|
||||
validateAndSaveConfig()
|
||||
return
|
||||
}
|
||||
R.id.btnGoBack -> {
|
||||
finish()
|
||||
navigateUpTo(Intent(baseContext, KioskActivity::class.java))
|
||||
return
|
||||
}
|
||||
R.id.btnDiagnostic -> {
|
||||
startActivity(Intent(this, KioskDiagnosticActivity::class.java))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateAndSaveConfig() {
|
||||
try {
|
||||
appInfo.setConfigUri(Uri.parse(editTextServerUrl.text.toString()))
|
||||
} catch(e: InvalidUriException) {
|
||||
toastMessage(this, "L'url du fichier est invalide")
|
||||
}
|
||||
try {
|
||||
appInfo.setVncIpPort(
|
||||
InetAddress.getByName(editTextIp.text.toString()),
|
||||
editTextPort.text.toString().toInt()
|
||||
)
|
||||
} catch(e: NumberFormatException) {
|
||||
toastMessage(this, "Le port n'est pas valide")
|
||||
} catch(e: UnknownHostException) {
|
||||
toastMessage(this, "L'adresse ip n'est pas valide")
|
||||
}
|
||||
Thread {
|
||||
val viewModel by viewModels<HomeViewModel>()
|
||||
var profile = appInfo.loadProfile(viewModel)
|
||||
if (profile == null) {
|
||||
profile = ServerProfile()
|
||||
profile.host = appInfo.vncArgIp.hostAddress
|
||||
profile.port = appInfo.vncArgPort
|
||||
viewModel.insertProfile(profile)
|
||||
} else {
|
||||
profile.host = appInfo.vncArgIp.hostAddress
|
||||
profile.port = appInfo.vncArgPort
|
||||
viewModel.updateProfile(profile)
|
||||
}
|
||||
toastMessage(this, "Enregistré")
|
||||
}.start()
|
||||
}
|
||||
|
||||
/**
|
||||
* Display toast message
|
||||
*/
|
||||
private fun toastMessage(activity: Activity, message: String) {
|
||||
activity.runOnUiThread(Runnable {
|
||||
Toast.makeText(
|
||||
activity.applicationContext,
|
||||
message,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
package com.gaurav.avnc.ui.ldc
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import android.widget.TextView.OnEditorActionListener
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import com.gaurav.avnc.R
|
||||
import com.gaurav.avnc.ui.ldc.lib.ApplicationInfo
|
||||
|
||||
|
||||
class KioskParameterPasswordActivity : AppCompatActivity(), View.OnClickListener {
|
||||
|
||||
// * View Elements
|
||||
private lateinit var editTextPassword: EditText
|
||||
private lateinit var btnSubmitPassword: 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_parameter_password)
|
||||
|
||||
editTextPassword = findViewById<EditText>(R.id.editTextPassword)
|
||||
btnSubmitPassword = findViewById<Button>(R.id.btnSubmitPassword)
|
||||
btnGoBack = findViewById<Button>(R.id.btnGoBack)
|
||||
|
||||
// * EVENTS
|
||||
editTextPassword.setOnClickListener(this)
|
||||
btnSubmitPassword.setOnClickListener(this)
|
||||
btnGoBack.setOnClickListener(this)
|
||||
editTextPassword.setOnEditorActionListener(OnEditorActionListener { _, actionId, _ ->
|
||||
var handled = false
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.hideSoftInputFromWindow(editTextPassword.windowToken, 0)
|
||||
handled = true
|
||||
}
|
||||
handled
|
||||
})
|
||||
|
||||
// * Setup AppInfo
|
||||
appInfo = ApplicationInfo(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.btnSubmitPassword -> {
|
||||
validatePassword()
|
||||
return
|
||||
}
|
||||
R.id.btnGoBack -> {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate password and go to next activity
|
||||
* Simple validation pour next intent, pas besoin de haute sécurité
|
||||
*/
|
||||
private fun validatePassword() {
|
||||
editTextPassword.clearFocus()
|
||||
if(editTextPassword.text.toString() != this.appInfo.configPassword) {
|
||||
toastMessage(this,"Mot de passe invalide")
|
||||
runOnUiThread { editTextPassword.setTextColor(Color.RED) }
|
||||
return
|
||||
}
|
||||
runOnUiThread {editTextPassword.setTextColor(Color.BLACK) }
|
||||
startActivity(Intent(this, KioskParameterActivity::class.java))
|
||||
}
|
||||
/**
|
||||
* Display toast message
|
||||
*/
|
||||
private fun toastMessage(activity: Activity, message: String) {
|
||||
activity.runOnUiThread(Runnable {
|
||||
Toast.makeText(
|
||||
activity.applicationContext,
|
||||
message,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
})
|
||||
}
|
||||
}
|
||||
259
app/src/main/java/com/gaurav/avnc/ui/ldc/KioskPingActivity.kt
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
package com.gaurav.avnc.ui.ldc
|
||||
|
||||
import android.app.Activity
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.os.StrictMode
|
||||
import android.view.View
|
||||
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.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.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import java.io.BufferedReader
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
import java.net.Inet4Address
|
||||
import java.net.Inet6Address
|
||||
import java.net.InetAddress
|
||||
import java.net.UnknownHostException
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.gaurav.avnc.ui.ldc.events
|
||||
|
||||
/**
|
||||
* The network connection state has changed
|
||||
*/
|
||||
class AppSwitchedToForegroundEvent()
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.gaurav.avnc.ui.ldc.events
|
||||
|
||||
/**
|
||||
* The network connection state has changed
|
||||
*/
|
||||
class NetworkStateChangedEvent()
|
||||
|
|
@ -0,0 +1,476 @@
|
|||
package com.gaurav.avnc.ui.ldc.fragments
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.graphics.toColorInt
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.fasterxml.jackson.core.JsonProcessingException
|
||||
import com.fasterxml.jackson.core.type.TypeReference
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.gaurav.avnc.R
|
||||
import com.gaurav.avnc.model.ServerProfile
|
||||
import com.gaurav.avnc.ui.ldc.KioskActivity
|
||||
import com.gaurav.avnc.ui.ldc.KioskParameterPasswordActivity
|
||||
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.github.fge.jsonschema.main.JsonSchema
|
||||
import com.github.fge.jsonschema.main.JsonSchemaFactory
|
||||
import com.google.common.net.InetAddresses
|
||||
import okhttp3.Call
|
||||
import okhttp3.Callback
|
||||
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 org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import java.io.BufferedReader
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.Reader
|
||||
import java.net.InetAddress
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
|
||||
|
||||
/**
|
||||
* HOME TAB
|
||||
*/
|
||||
class KioskHomeFragment : Fragment(), View.OnClickListener {
|
||||
|
||||
// * View Elements
|
||||
private lateinit var txtMacAddress: TextView
|
||||
private lateinit var txtIPAddress: TextView
|
||||
private lateinit var txtVNCArgs: TextView
|
||||
private lateinit var btnLoadConfig: Button
|
||||
private lateinit var btnVNC: Button
|
||||
private lateinit var btnParameters: Button
|
||||
private lateinit var progressBar: ProgressBar
|
||||
|
||||
// * EVENTS
|
||||
|
||||
/**
|
||||
* On Start
|
||||
*/
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
}
|
||||
|
||||
/**
|
||||
* On Stop
|
||||
*/
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
/**
|
||||
* On Create
|
||||
*/
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// * Register event bus
|
||||
EventBus.getDefault().register(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* On Destroy
|
||||
*/
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
|
||||
// * Register event bus
|
||||
EventBus.getDefault().unregister(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* On Create view
|
||||
*/
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
// Inflate the layout for this fragment
|
||||
val view:View = inflater.inflate(R.layout.fragment_kiosk_home, container, false)
|
||||
|
||||
txtIPAddress = view.findViewById<TextView>(R.id.txtIPAddress)
|
||||
txtMacAddress = view.findViewById<TextView>(R.id.txtMacAddress)
|
||||
txtVNCArgs = view.findViewById<TextView>(R.id.txtVNCArgs)
|
||||
btnLoadConfig = view.findViewById<Button>(R.id.btnLoadConfig)
|
||||
btnVNC = view.findViewById<Button>(R.id.btnVNC)
|
||||
btnParameters = view.findViewById<Button>(R.id.btnParameters)
|
||||
progressBar = view.findViewById<ProgressBar>(R.id.progressBar)
|
||||
|
||||
// * EVENTS
|
||||
btnLoadConfig.setOnClickListener(this)
|
||||
btnVNC.setOnClickListener(this)
|
||||
btnParameters.setOnClickListener(this)
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event on Activity Resume
|
||||
*/
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
val activity = (this.activity as KioskActivity)
|
||||
// * AppInfo Refresh
|
||||
activity.appInfo.refreshConfigProperties()
|
||||
activity.appInfo.retrieveDeviceNetworkInfo(activity.applicationContext)
|
||||
showNetworkInfo(activity.appInfo.networkConnected)
|
||||
showVNCArgs()
|
||||
}
|
||||
|
||||
/**
|
||||
* On App Background
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onMessageEvent(event: AppSwitchedToForegroundEvent?) {
|
||||
val activity = (activity as KioskActivity)
|
||||
showNetworkInfo(activity.appInfo.networkConnected)
|
||||
}
|
||||
|
||||
/**
|
||||
* On network message received
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onMessageEvent(event: NetworkStateChangedEvent?) {
|
||||
val activity = (activity as KioskActivity)
|
||||
showNetworkInfo(activity.appInfo.networkConnected)
|
||||
}
|
||||
|
||||
// * Action UI events
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.btnVNC -> btnStartVncSessionClick(v, this)
|
||||
R.id.btnLoadConfig -> btnLoadConfigClick(v, this)
|
||||
R.id.btnParameters -> {
|
||||
startActivity(Intent(this.context, KioskParameterPasswordActivity::class.java))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action on click button "Load config"
|
||||
*/
|
||||
private fun btnLoadConfigClick(view:View, fragment: KioskHomeFragment) {
|
||||
val activity = (fragment.activity as KioskActivity)
|
||||
manageButtons(false)
|
||||
// Check we are connected to a network
|
||||
if (!activity.appInfo.networkConnected) {
|
||||
toastMessage(activity, "Vous devez être connecté à un réseau")
|
||||
}
|
||||
manageButtons(true)
|
||||
// Download Config File
|
||||
loadDistantConfiguration(fragment)
|
||||
// Download Password config
|
||||
fetchConfigPassword(fragment)
|
||||
}
|
||||
|
||||
/**
|
||||
* Action on click button "Load config"
|
||||
*/
|
||||
private fun btnStartVncSessionClick(view:View, fragment: KioskHomeFragment) {
|
||||
val activity = (fragment.activity as KioskActivity)
|
||||
val viewModel by viewModels<HomeViewModel>()
|
||||
Thread {
|
||||
if (!activity.appInfo.networkConnected) {
|
||||
toastMessage(activity, "Vous devez être connecté à un réseau")
|
||||
return@Thread
|
||||
}
|
||||
val profile = activity.appInfo.loadProfile(viewModel)
|
||||
if (profile == null) {
|
||||
toastMessage(activity, "La configuration est manquante")
|
||||
} else {
|
||||
startVncActivity(activity, profile)
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
// * PRIVATE METHODS
|
||||
|
||||
/**
|
||||
* Load config csv from server
|
||||
*/
|
||||
private fun loadDistantConfiguration(fragment: KioskHomeFragment) {
|
||||
val activity = (fragment.activity as KioskActivity)
|
||||
val viewModel by viewModels<HomeViewModel>()
|
||||
val client = OkHttpClient()
|
||||
val request = Request.Builder()
|
||||
.url(activity.appInfo.configServerUri.toString())
|
||||
.build()
|
||||
|
||||
val callback = object : Callback {
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
toastMessage(activity, "Erreur : ${e.message} - ${e.cause?.message}")
|
||||
manageButtons(true)
|
||||
return
|
||||
//e.printStackTrace()
|
||||
}
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onResponse(call: Call, response: Response) {
|
||||
response.use {
|
||||
if (!response.isSuccessful) {
|
||||
toastMessage(activity, "Erreur (${response.code}) : ${response.message}")
|
||||
manageButtons(true)
|
||||
return
|
||||
}
|
||||
val res: Reader = response.body!!.charStream();
|
||||
val bufferedReader: BufferedReader = BufferedReader(res);
|
||||
|
||||
val parser = CSVParser.parse(bufferedReader, CSVFormat.DEFAULT.withDelimiter(';'));
|
||||
for (csvRecord: CSVRecord in parser) {
|
||||
val identifier = csvRecord.get(0);
|
||||
// Si le premier caractère est un dieze la ligne est commentée donc on passe
|
||||
if (identifier[0] == '#' || identifier.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
// On test si l´Identifiant TID correspond à celle du Device
|
||||
if (identifier.length == 11 && identifier.equals(activity.appInfo.tidId)) {
|
||||
val ip = csvRecord.get(3);
|
||||
val port = csvRecord.get(4);
|
||||
if (ip!!.isEmpty() || !isValidIpAddress(ip)) {
|
||||
toastMessage(activity, "Adresse IP invalide")
|
||||
manageButtons(true)
|
||||
return
|
||||
}
|
||||
if (port!!.isEmpty() || !isValidPort(port)) {
|
||||
toastMessage(activity, "Port invalide")
|
||||
manageButtons(true)
|
||||
return
|
||||
}
|
||||
var profile = activity.appInfo.loadProfile(viewModel)
|
||||
if (profile == null) {
|
||||
profile = ServerProfile()
|
||||
profile.host = InetAddress.getByName(ip).hostAddress
|
||||
profile.port = port.toInt()
|
||||
viewModel.insertProfile(profile)
|
||||
} else {
|
||||
profile.host = InetAddress.getByName(ip).hostAddress
|
||||
profile.port = port.toInt()
|
||||
viewModel.updateProfile(profile)
|
||||
}
|
||||
// * AppInfo Refresh
|
||||
activity.appInfo.setVncIpPort(InetAddress.getByName(ip), port.toInt())
|
||||
activity.runOnUiThread {
|
||||
fragment.txtVNCArgs?.text = "Arguments VNC : ${profile.host}:${profile.port}"
|
||||
}
|
||||
toastMessage(activity, "Configuration chargée")
|
||||
manageButtons(true)
|
||||
return
|
||||
}
|
||||
}
|
||||
toastMessage(activity, "Configuration non trouvée")
|
||||
manageButtons(true)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
client.newCall(request).enqueue(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to fetch config password from server
|
||||
*/
|
||||
private fun fetchConfigPassword(fragment: KioskHomeFragment) {
|
||||
val activity = (fragment.activity as KioskActivity)
|
||||
val client = OkHttpClient()
|
||||
val request = Request.Builder()
|
||||
.url(activity.appInfo.appConfigUri.toString())
|
||||
.build()
|
||||
|
||||
val callback = object : Callback {
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
toastMessage(activity, "Erreur : ${e.message} - ${e.cause?.message}")
|
||||
return
|
||||
}
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onResponse(call: Call, response: Response) {
|
||||
response.use {
|
||||
if (!response.isSuccessful) {
|
||||
toastMessage(activity ,"Erreur (${response.code}) : ${response.message}")
|
||||
return
|
||||
}
|
||||
val bodyResponse = response.body?.string()
|
||||
|
||||
if (bodyResponse == null) {
|
||||
toastMessage(activity, "Erreur réponse vide du serveur")
|
||||
return
|
||||
}
|
||||
|
||||
if(!isJSONValid(activity, bodyResponse)) {
|
||||
toastMessage(activity, "Erreur réponse invalide du serveur")
|
||||
} else {
|
||||
saveJSONPassword(activity, bodyResponse)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
client.newCall(request).enqueue(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate Json parameter schema
|
||||
*/
|
||||
private fun isJSONValid(activity: KioskActivity, jsonString: String): Boolean {
|
||||
// * Validate JSON
|
||||
try {
|
||||
JSONObject(jsonString)
|
||||
} catch (ex: JSONException) {
|
||||
try {
|
||||
JSONArray(jsonString)
|
||||
} catch (ex1: JSONException) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// * Validate JSON Schema
|
||||
return try {
|
||||
val factory: JsonSchemaFactory = JsonSchemaFactory.byDefault()
|
||||
val inputStream: InputStream = activity.assets.open("security.schema.json");
|
||||
val schemaString = inputStream.bufferedReader().use{it.readText()}
|
||||
|
||||
val m = ObjectMapper()
|
||||
val jsonSchema: JsonSchema = factory.getJsonSchema(m.readTree(schemaString))
|
||||
|
||||
jsonSchema.validate(m.readTree(jsonString)).isSuccess
|
||||
} catch(e: JsonProcessingException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save Json password
|
||||
*/
|
||||
private fun saveJSONPassword(activity: KioskActivity, jsonString: String) {
|
||||
val mapper = ObjectMapper()
|
||||
val configNode = mapper.readValue<Map<String, String>>(jsonString, object : TypeReference<Map<String, String>>() {})
|
||||
activity.appInfo.setPassword(configNode["password"]!!)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Display network info
|
||||
*/
|
||||
@SuppressLint("HardwareIds")
|
||||
private fun showNetworkInfo(isConnected: Boolean) {
|
||||
val activity = (activity as KioskActivity)
|
||||
val ip = activity.appInfo.ip
|
||||
val identifier = activity.appInfo.tidId
|
||||
if (activity.appInfo.networkConnected) {
|
||||
txtIPAddress?.text = "Adresse IP: ${ip?.hostAddress}"
|
||||
txtMacAddress?.text = "Identifiant TID: $identifier"
|
||||
} else {
|
||||
txtIPAddress?.text = "Adresse IP: -"
|
||||
txtMacAddress?.text = "Identifiant TID: $identifier"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display VNCArgs
|
||||
*/
|
||||
private fun showVNCArgs() {
|
||||
val activity = (activity as KioskActivity)
|
||||
val ipVNC = activity.appInfo.vncArgIp.hostAddress
|
||||
val portVNC = activity.appInfo.vncArgPort
|
||||
//println("IPIP" + ipVNC)
|
||||
txtVNCArgs?.text = "Arguments VNC : $ipVNC:$portVNC"
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage interface buttons state
|
||||
*/
|
||||
private fun manageButtons(enable: Boolean) {
|
||||
val activity = (activity as KioskActivity)
|
||||
activity.runOnUiThread {
|
||||
if (enable) {
|
||||
btnLoadConfig?.isEnabled = true
|
||||
btnLoadConfig?.isClickable = true
|
||||
btnLoadConfig?.setBackgroundColor("#D4021D".toColorInt())
|
||||
|
||||
btnVNC?.isEnabled = true
|
||||
btnVNC?.isClickable = true
|
||||
btnVNC?.setBackgroundColor("#D4021D".toColorInt())
|
||||
|
||||
progressBar?.isVisible = false
|
||||
} else {
|
||||
btnLoadConfig?.isEnabled = false
|
||||
btnLoadConfig?.isClickable = false
|
||||
btnLoadConfig?.setBackgroundColor(Color.GRAY)
|
||||
|
||||
btnVNC?.isEnabled = false
|
||||
btnVNC?.isClickable = false
|
||||
btnVNC?.setBackgroundColor(Color.GRAY)
|
||||
|
||||
progressBar?.isVisible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if port is valid
|
||||
*/
|
||||
private fun isValidPort(str: String): Boolean {
|
||||
if (str.isEmpty())
|
||||
{
|
||||
return false
|
||||
}
|
||||
try {
|
||||
str.toInt()
|
||||
} catch (e: NumberFormatException) {
|
||||
return false
|
||||
}
|
||||
val regex: String =
|
||||
"^((6553[0-5])|(655[0-2][0-9])|(65[0-4][0-9]{2})|(6[0-4][0-9]{3})|([1-5][0-9]{4})|([0-5]{0,5})|([0-9]{1,4}))\$"
|
||||
val p: Pattern = Pattern.compile(regex)
|
||||
val m: Matcher = p.matcher(str)
|
||||
return m.matches()
|
||||
}
|
||||
|
||||
/**
|
||||
* Display toast message
|
||||
*/
|
||||
private fun toastMessage(activity: Activity, message: String) {
|
||||
activity.runOnUiThread(Runnable {
|
||||
Toast.makeText(
|
||||
activity.applicationContext,
|
||||
message,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
package com.gaurav.avnc.ui.ldc.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.GridLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.children
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.gaurav.avnc.R
|
||||
import com.gaurav.avnc.ui.ldc.KioskActivity
|
||||
import com.gaurav.avnc.ui.ldc.events.AppSwitchedToForegroundEvent
|
||||
import com.gaurav.avnc.ui.ldc.events.NetworkStateChangedEvent
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
||||
/**
|
||||
* INFORMATION'S TAB
|
||||
*/
|
||||
class KioskInformationFragment : Fragment(), View.OnClickListener {
|
||||
|
||||
// * View Elements
|
||||
private lateinit var infoGridLayout: GridLayout
|
||||
private lateinit var btnReloadInfo: Button
|
||||
|
||||
/**
|
||||
* On Start
|
||||
*/
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
}
|
||||
|
||||
/**
|
||||
* On Stop
|
||||
*/
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
/**
|
||||
* On Create
|
||||
*/
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// * Register event bus
|
||||
EventBus.getDefault().register(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* On Destroy
|
||||
*/
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
|
||||
// * Register event bus
|
||||
EventBus.getDefault().unregister(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* On Create view
|
||||
*/
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
// Inflate the layout for this fragment
|
||||
val view:View = inflater.inflate(R.layout.fragment_kiosk_information, container, false)
|
||||
|
||||
infoGridLayout = view.findViewById<GridLayout>(R.id.infoGridLayout)
|
||||
btnReloadInfo = view.findViewById<Button>(R.id.btnReloadInfo)
|
||||
|
||||
btnReloadInfo.setOnClickListener(this)
|
||||
btnReloadInfo.visibility = View.INVISIBLE
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event on Activity Resume
|
||||
*/
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
val activity = (this.activity as KioskActivity)
|
||||
// * AppInfo Refresh
|
||||
activity.appInfo.refreshConfigProperties()
|
||||
activity.appInfo.retrieveDeviceNetworkInfo(activity.applicationContext)
|
||||
displayTextInfo()
|
||||
}
|
||||
|
||||
// * Action UI events
|
||||
override fun onClick(v: View) {
|
||||
Log.d("click", "click")
|
||||
when (v.id) {
|
||||
R.id.btnReloadInfo -> reloadNetWorkInfo(v, this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On App Background
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onMessageEvent(event: AppSwitchedToForegroundEvent?) {
|
||||
displayTextInfo()
|
||||
}
|
||||
|
||||
/**
|
||||
* On network message received
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onMessageEvent(event: NetworkStateChangedEvent?) {
|
||||
Log.d("network", "network state changed on kiosk fragment")
|
||||
displayTextInfo()
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Text Info
|
||||
*/
|
||||
private fun displayTextInfo() {
|
||||
// * Load actual network configuration
|
||||
val activity = (activity as KioskActivity)
|
||||
val cells = infoGridLayout.children as Sequence<TextView>
|
||||
|
||||
val ip = activity.appInfo.ip
|
||||
val gateway = activity.appInfo.gateway
|
||||
|
||||
val info:Map<String, String> = mapOf(
|
||||
"Identifiant TID" to activity.appInfo.tidId,
|
||||
"Adresse IP" to if(ip !== null) ip.hostAddress else "-",
|
||||
"Gateway" to if(gateway !== null) gateway.hostAddress else "-",
|
||||
"Serveur de Config" to activity.appInfo.configServerUri.toString(),
|
||||
"Système" to activity.appInfo.systemVERSION,
|
||||
"Kiosk" to activity.appInfo.appVERSION,
|
||||
"Dernière MàJ" to activity.appInfo.buildTimestamp.toLocaleString(),
|
||||
)
|
||||
|
||||
info.onEachIndexed(fun (index, infoLine) {
|
||||
(cells.elementAt(index * 2) as TextView).text = infoLine.key
|
||||
(cells.elementAt(index * 2 + 1) as TextView).text = infoLine.value
|
||||
})
|
||||
}
|
||||
|
||||
private fun reloadNetWorkInfo (view: View, fragment: KioskInformationFragment) {
|
||||
EventBus.getDefault().post(NetworkStateChangedEvent())
|
||||
val activity = (fragment.activity as KioskActivity)
|
||||
activity.appInfo.retrieveDeviceNetworkInfo(activity.applicationContext)
|
||||
displayTextInfo()
|
||||
EventBus.getDefault().post(NetworkStateChangedEvent())
|
||||
}
|
||||
}
|
||||
297
app/src/main/java/com/gaurav/avnc/ui/ldc/lib/ApplicationInfo.kt
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
package com.gaurav.avnc.ui.ldc.lib
|
||||
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.webkit.URLUtil
|
||||
import com.gaurav.avnc.BuildConfig
|
||||
import com.gaurav.avnc.model.ServerProfile
|
||||
import com.gaurav.avnc.ui.ldc.lib.exceptions.InvalidUriException
|
||||
import com.gaurav.avnc.ui.ldc.lib.exceptions.PasswordValidationException
|
||||
import com.gaurav.avnc.viewmodel.HomeViewModel
|
||||
import com.serialIntCaster.SerialCaster
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import java.net.InetAddress
|
||||
import java.util.Date
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
|
||||
/**
|
||||
* This class stores application Info used for app execution
|
||||
* and info display
|
||||
*/
|
||||
class ApplicationInfo(private var preferences: SharedPreferences) {
|
||||
|
||||
// * Static Elements (Readonly as val)
|
||||
/** Android config filename */
|
||||
private val appConfigFile = "security.json"
|
||||
/** The unique TID identifier generated with SerialIntCaster */
|
||||
val tidId: String = if(BuildConfig.DEBUG) "2T63SSH2D8F" else fetchIdentifier()
|
||||
/** Android OS version */
|
||||
val systemVERSION: String = "Android " + Build.VERSION.RELEASE
|
||||
/** App version */
|
||||
val appVERSION: String = BuildConfig.VERSION_NAME
|
||||
/** App build timestamp generated using build.gradle */
|
||||
val buildTimestamp: Date = BuildConfig.BUILD_TIME
|
||||
|
||||
/**
|
||||
* Shared preferences items for dynamic update
|
||||
*/
|
||||
private val sharedPreferencesFields = mapOf<String,Set<String>>(
|
||||
"Int" to setOf<String>("vncArgPort"),
|
||||
"InetAddress" to setOf<String>("vncArgIp"),
|
||||
"String" to setOf<String>("configPassword", "configServerFqdn", "configServerPath", "appConfigPath"),
|
||||
"Uri" to setOf<String>("configServerUri", "appConfigUri")
|
||||
)
|
||||
|
||||
// * Dynamic elements
|
||||
/** Is the device actually connected to a network */
|
||||
var networkConnected: Boolean = false
|
||||
/** The device network ip that is used */
|
||||
var ip: InetAddress? = null
|
||||
private set
|
||||
/** The device network gateway that is used */
|
||||
var gateway: InetAddress = InetAddress.getByName("127.0.0.1")
|
||||
private set
|
||||
|
||||
// * Dynamic elements stored prefs
|
||||
/** The VNC ip fetched from server */
|
||||
var vncArgIp: InetAddress = InetAddress.getByName("127.0.0.1")
|
||||
private set
|
||||
/** The VNC port fetched from server */
|
||||
var vncArgPort: Int = 5900
|
||||
private set
|
||||
/** The default password for configuration */
|
||||
var configPassword: String = "5XtpqEz!A5"
|
||||
private set
|
||||
/** The server FQDN from where config should be fetched */
|
||||
private var configServerFqdn: String = "http://localtid"
|
||||
|
||||
/** VNC config path */
|
||||
private var configServerPath: String = if(BuildConfig.DEBUG) "/conf/AppCepa.conf" else "/conf/KioskTIDServer.conf"
|
||||
/** Android config path */
|
||||
private var appConfigPath: String = if(BuildConfig.DEBUG) "/conf/$appConfigFile" else "/conf/$appConfigFile"
|
||||
|
||||
/** VNC config URI */
|
||||
var configServerUri: Uri = Uri.parse(configServerFqdn + configServerPath)
|
||||
/** Android config URI */
|
||||
var appConfigUri: Uri = Uri.parse(configServerFqdn + appConfigPath)
|
||||
|
||||
init {
|
||||
refreshConfigProperties()
|
||||
SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, s ->
|
||||
sharedPreferencesFields.forEach(fun (key, items) {
|
||||
if (items.indexOf(s) != -1) {
|
||||
when (key) {
|
||||
"Int" -> initProp(s, sharedPreferences.getString(s,
|
||||
readInstanceProperty<Int>(this, s).toString()
|
||||
)!!)
|
||||
"InetAddress" -> initProp(s, InetAddress.getByName(sharedPreferences.getString(s,
|
||||
readInstanceProperty<InetAddress>(this, s).hostAddress
|
||||
)))
|
||||
"String" -> initProp(s, sharedPreferences.getString(s,
|
||||
readInstanceProperty<String>(this, s)
|
||||
)!!)
|
||||
"Uri" -> initProp(s, Uri.parse(sharedPreferences.getString(s,
|
||||
readInstanceProperty<Uri>(this, s).toString()
|
||||
)))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/** Init prop Integer */
|
||||
private fun initProp(prop: String, value: Int): Int {
|
||||
val preferenceValue = preferences.getInt(prop, value)
|
||||
return if(preferenceValue !== null) preferenceValue else value
|
||||
}
|
||||
/** Init prop String */
|
||||
private fun initProp(prop: String, value: String): String {
|
||||
val preferenceValue = preferences.getString(prop, value)
|
||||
return if(preferenceValue !== null) preferenceValue else value
|
||||
}
|
||||
/** Init prop Uri */
|
||||
private fun initProp(prop: String, value: Uri): Uri {
|
||||
val preferenceValue = Uri.parse(preferences.getString(prop, value.toString()))
|
||||
return if(preferenceValue !== null) preferenceValue else value
|
||||
}
|
||||
/** Init prop Inet */
|
||||
private fun initProp(prop: String, value: InetAddress): InetAddress {
|
||||
val preferenceValue = InetAddress.getByName(preferences.getString(prop, value.hostAddress))
|
||||
return if(preferenceValue !== null) preferenceValue else value
|
||||
}
|
||||
|
||||
/** Set prop Integer */
|
||||
private fun setProp(prop: String, value: Int) {
|
||||
preferences.edit().putInt(prop, value).commit()
|
||||
}
|
||||
/** Set prop String */
|
||||
private fun setProp(prop: String, value: String) {
|
||||
preferences.edit().putString(prop, value).commit()
|
||||
}
|
||||
/** Set prop Uri */
|
||||
private fun setProp(prop: String, value: Uri) {
|
||||
preferences.edit().putString(prop, value.toString()).commit()
|
||||
}
|
||||
/** Set prop Inet */
|
||||
private fun setProp(prop: String, value: InetAddress) {
|
||||
preferences.edit().putString(prop, value.hostAddress).commit()
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh info or init from sharedPreferences
|
||||
*/
|
||||
fun refreshConfigProperties() {
|
||||
vncArgIp = initProp("vncArgIp", vncArgIp)
|
||||
vncArgPort = initProp("vncArgPort", vncArgPort)
|
||||
configPassword = initProp("configPassword", configPassword)
|
||||
configServerFqdn = initProp("configServerFqdn", configServerFqdn)
|
||||
configServerPath = initProp("configServerPath", configServerPath)
|
||||
appConfigPath = initProp("appConfigPath", appConfigPath)
|
||||
configServerUri = initProp("configServerUri", configServerUri)
|
||||
appConfigUri = initProp("appConfigUri", appConfigUri)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate identifier using SerialIntCaster or fetch from preferences
|
||||
*/
|
||||
private fun fetchIdentifier(): String {
|
||||
val identifierPref: String? = preferences.getString("TID_IDENTIFIER", "NOT_SET")
|
||||
var identifier = if(identifierPref === null) "NOT_SET" else identifierPref
|
||||
if (identifier == "NOT_SET")
|
||||
{
|
||||
identifier = SerialCaster.encode(
|
||||
System.currentTimeMillis(),
|
||||
564646546765768,
|
||||
11,
|
||||
"ABCDEFGHJKLMNPQRSTUVWXYZ23456789".toCharArray()
|
||||
)
|
||||
preferences.edit().putString("TID_IDENTIFIER", identifier).commit()
|
||||
}
|
||||
return identifier
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Device ip in app info using application context
|
||||
*/
|
||||
@SuppressLint("HardwareIds")
|
||||
fun retrieveDeviceNetworkInfo(applicationContext: Context) {
|
||||
val connectivityManager = applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
|
||||
ip = InetAddress.getByName("127.0.0.1")
|
||||
gateway = InetAddress.getByName("127.0.0.1")
|
||||
val linkProperties = connectivityManager.getLinkProperties(connectivityManager.activeNetwork)
|
||||
ip = linkProperties?.linkAddresses?.first {
|
||||
!it.address.isLoopbackAddress && it.address.isSiteLocalAddress
|
||||
}?.address
|
||||
val fetchedGateway = linkProperties?.routes?.first {
|
||||
val inetGateway = it.gateway
|
||||
inetGateway !== null && !inetGateway.isLoopbackAddress && inetGateway.isSiteLocalAddress
|
||||
}?.gateway
|
||||
if(fetchedGateway !== null) {
|
||||
gateway = fetchedGateway
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load VNC profile (blocked on the first slot)
|
||||
*/
|
||||
fun loadProfile(viewModel: HomeViewModel): ServerProfile? {
|
||||
val profiles = viewModel.getAllProfiles()
|
||||
val profile = profiles.find { it.ID == 1.toLong() }
|
||||
if (profile != null) {
|
||||
return profile
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Set config Uri
|
||||
* @throws InvalidUriException If the url is not valid or the file pointing at is not a .conf
|
||||
*/
|
||||
fun setConfigUri(newConfigUri: Uri) {
|
||||
val uriInput = newConfigUri.toString()
|
||||
if (uriInput.toHttpUrlOrNull() == null || !URLUtil.isValidUrl(uriInput)) {
|
||||
throw InvalidUriException()
|
||||
}
|
||||
var newConfigPath = newConfigUri.path
|
||||
newConfigPath = if (newConfigPath !== null) newConfigPath else
|
||||
(if(BuildConfig.DEBUG) "/AppCepa.conf" else "/conf/KioskTIDServer.conf")
|
||||
|
||||
val pathSegments: List<String> = Uri.parse(newConfigPath).pathSegments
|
||||
val fileName = pathSegments.last()
|
||||
if (fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length) != "conf") {
|
||||
throw InvalidUriException()
|
||||
}
|
||||
|
||||
// * Save config
|
||||
configServerFqdn = Uri.parse("${newConfigUri.scheme}://${newConfigUri.host}").toString()
|
||||
setProp("configServerFqdn", configServerFqdn)
|
||||
|
||||
//println(configServerFqdn)
|
||||
|
||||
configServerPath = Uri.parse(newConfigPath).toString()
|
||||
setProp("configServerPath", configServerPath)
|
||||
|
||||
//println(configServerPath)
|
||||
|
||||
/** VNC config URI */
|
||||
configServerUri = Uri.parse(configServerFqdn + configServerPath)
|
||||
setProp("configServerUri", configServerUri)
|
||||
|
||||
//println(configServerUri)
|
||||
|
||||
/** Android config URI */
|
||||
appConfigPath = newConfigPath.substring(0, newConfigPath.indexOf(fileName)) + appConfigFile
|
||||
setProp("appConfigPath", appConfigPath)
|
||||
|
||||
//println(appConfigPath)
|
||||
|
||||
appConfigUri = Uri.parse(configServerFqdn + appConfigPath)
|
||||
setProp("appConfigUri", appConfigUri)
|
||||
|
||||
//println(appConfigUri)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set VNC ip port
|
||||
*/
|
||||
fun setVncIpPort(newVncArgIp: InetAddress, newVncArgPort: Int) {
|
||||
vncArgIp = newVncArgIp
|
||||
setProp("vncArgIp", vncArgIp)
|
||||
vncArgPort = newVncArgPort
|
||||
setProp("vncArgPort", vncArgPort)
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign settings password
|
||||
*/
|
||||
fun setPassword(password: String) {
|
||||
// * Must contain lower, upper and number min 8 max 30
|
||||
val regex: String = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?!.* ).{8,30}$"
|
||||
val p: Pattern = Pattern.compile(regex)
|
||||
val m: Matcher = p.matcher(password)
|
||||
if (!m.matches()) {
|
||||
throw PasswordValidationException()
|
||||
}
|
||||
configPassword = password
|
||||
setProp("configPassword", configPassword)
|
||||
}
|
||||
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <R> readInstanceProperty(instance: Any, propertyName: String): R {
|
||||
val property = instance::class.members
|
||||
// don't cast here to <Any, R>, it would succeed silently
|
||||
.first { it.name == propertyName } as KProperty1<Any, *>
|
||||
// force a invalid cast exception if incorrect type here
|
||||
return property.get(instance) as R
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.gaurav.avnc.ui.ldc.lib
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import com.gaurav.avnc.ui.ldc.KioskActivity
|
||||
import com.gaurav.avnc.ui.ldc.fragments.KioskHomeFragment
|
||||
import com.gaurav.avnc.ui.ldc.fragments.KioskInformationFragment
|
||||
|
||||
class KioskViewPagerAdapter(kiosk_activity: KioskActivity) : FragmentStateAdapter(kiosk_activity) {
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return 2
|
||||
}
|
||||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return when(position) {
|
||||
0 -> KioskHomeFragment()
|
||||
1 -> KioskInformationFragment()
|
||||
else -> throw Error("Fragment not Handled $position")
|
||||
}
|
||||
}
|
||||
}
|
||||
136
app/src/main/java/com/gaurav/avnc/ui/ldc/lib/NetworkObserver.kt
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
package com.gaurav.avnc.ui.ldc.lib
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Context.CONNECTIVITY_SERVICE
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
|
||||
import android.net.NetworkRequest
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.gaurav.avnc.ui.ldc.events.NetworkStateChangedEvent
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
/**
|
||||
* This class is in charge of listening to the state of the network connection and notifying the
|
||||
* activity if the state of the connection changes.
|
||||
* */
|
||||
class NetworkObserver constructor(
|
||||
private val context: Context,
|
||||
private val lifecycle: Lifecycle
|
||||
) : DefaultLifecycleObserver {
|
||||
|
||||
private lateinit var networkCallback: ConnectivityManager.NetworkCallback
|
||||
private var connectivityManager: ConnectivityManager? = null
|
||||
private val validNetworks = HashSet<Network>()
|
||||
|
||||
private lateinit var job: Job
|
||||
private lateinit var coroutineScope: CoroutineScope
|
||||
|
||||
// State Holder: Indicating either the network is available or not-available
|
||||
private val _networkAvailableStateFlow: MutableStateFlow<NetworkState> = MutableStateFlow(NetworkState.Available)
|
||||
|
||||
val networkAvailableStateFlow
|
||||
get() = _networkAvailableStateFlow
|
||||
|
||||
// ---> This variable can be accessed anytime to get the current state of the network
|
||||
val isConnected: Boolean
|
||||
get() = _isConnected.get()
|
||||
|
||||
private val _isConnected = AtomicBoolean(false)
|
||||
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
super.onCreate(owner)
|
||||
init()
|
||||
}
|
||||
|
||||
override fun onStart(owner: LifecycleOwner) {
|
||||
super.onStart(owner)
|
||||
registerNetworkCallback()
|
||||
checkValidNetworks()
|
||||
}
|
||||
|
||||
override fun onStop(owner: LifecycleOwner) {
|
||||
super.onStop(owner)
|
||||
unregisterNetworkCallback()
|
||||
}
|
||||
|
||||
private fun init() {
|
||||
// Initialize the connectivity manager instance
|
||||
connectivityManager = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
}
|
||||
|
||||
private fun registerNetworkCallback() {
|
||||
if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
|
||||
// Observing the network should happen only when the life-cycle is in started state
|
||||
initCoroutine()
|
||||
initNetworkMonitoring()
|
||||
}
|
||||
}
|
||||
|
||||
private fun unregisterNetworkCallback() {
|
||||
validNetworks.clear()
|
||||
connectivityManager?.unregisterNetworkCallback(networkCallback)
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
/**
|
||||
* Co-Routine used to monitor the connectivity
|
||||
*/
|
||||
private fun initCoroutine() {
|
||||
// Create a job instance
|
||||
job = Job()
|
||||
// Provide a co-routine scope
|
||||
coroutineScope = CoroutineScope(Dispatchers.Default + job)
|
||||
}
|
||||
|
||||
private fun initNetworkMonitoring() {
|
||||
networkCallback = createNetworkCallback()
|
||||
|
||||
val networkRequest = NetworkRequest.Builder()
|
||||
.addCapability(NET_CAPABILITY_INTERNET)
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
|
||||
.build()
|
||||
connectivityManager?.registerNetworkCallback(networkRequest, networkCallback)
|
||||
}
|
||||
|
||||
private fun createNetworkCallback() = object : ConnectivityManager.NetworkCallback() {
|
||||
override fun onAvailable(network: Network) {
|
||||
Log.d("network", "Has new network $network")
|
||||
validNetworks.add(network)
|
||||
checkValidNetworks()
|
||||
EventBus.getDefault().post(NetworkStateChangedEvent())
|
||||
}
|
||||
|
||||
override fun onLost(network: Network) {
|
||||
Log.d("network", "Remove network $network")
|
||||
validNetworks.remove(network)
|
||||
checkValidNetworks()
|
||||
EventBus.getDefault().post(NetworkStateChangedEvent())
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkValidNetworks() {
|
||||
coroutineScope.launch {
|
||||
_networkAvailableStateFlow.emit(
|
||||
if (validNetworks.size > 0){
|
||||
_isConnected.set(true)
|
||||
NetworkState.Available
|
||||
} else {
|
||||
_isConnected.set(false)
|
||||
NetworkState.UnAvailable
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.gaurav.avnc.ui.ldc.lib
|
||||
|
||||
enum class NetworkState {
|
||||
UnAvailable, Available
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.gaurav.avnc.ui.ldc.lib
|
||||
|
||||
/**
|
||||
* Wifi Channels
|
||||
* https://en.wikipedia.org/wiki/List_of_WLAN_channels
|
||||
*/
|
||||
enum class WifiFrequencies() {
|
||||
/** 802.11ah LOW (860/900 MHz) */
|
||||
AH_LOW(755),
|
||||
/** 802.11ah HIGH (860/900 MHz) */
|
||||
AH_HIGH(755),
|
||||
/** 802.11b/g/n/ax LOW (2.4Ghz) */
|
||||
BGNAX_LOW(2400),
|
||||
/** 802.11b/g/n/ax HIGH (2.4Ghz) */
|
||||
BGNAX_HIGH(2500),
|
||||
/** 802.11y LOW (3.65 GHz) */
|
||||
Y_LOW(3650),
|
||||
/** 802.11y HIGH (3.65 GHz) */
|
||||
Y_HIGH(3700),
|
||||
/** 802.11j LOW (4.9–5.0 GHz) */
|
||||
J_LOW(4900),
|
||||
/** 802.11j HIGH (4.9–5.0 GHz) */
|
||||
J_HIGH(5100),
|
||||
/** 802.11a/h/n/ac/axLOW (5Ghz) */
|
||||
AHNACAX_LOW(5100),
|
||||
/** 802.11a/h/n/ac/ax HIGH (5Ghz) */
|
||||
AHNACAX_HIGH(5900);
|
||||
|
||||
var freq: Int = 0;
|
||||
|
||||
// custom constructors
|
||||
constructor(freq: Int) : this() {
|
||||
this.freq = freq
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package com.gaurav.avnc.ui.ldc.lib.exceptions
|
||||
|
||||
class InvalidIpException: RuntimeException() {
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package com.gaurav.avnc.ui.ldc.lib.exceptions
|
||||
|
||||
class InvalidUriException: RuntimeException() {
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package com.gaurav.avnc.ui.ldc.lib.exceptions
|
||||
|
||||
class PasswordValidationException: RuntimeException() {
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package com.gaurav.avnc.ui.ldc.lib.exceptions
|
||||
|
||||
class PingFailedException: RuntimeException() {
|
||||
}
|
||||
|
|
@ -90,6 +90,10 @@ class HomeViewModel(app: Application) : BaseViewModel(app) {
|
|||
}
|
||||
}
|
||||
|
||||
fun getAllProfiles(): List<ServerProfile> {
|
||||
return serverProfileDao.getList()
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
* Profile editing/creating
|
||||
|
|
|
|||
12
app/src/main/res/drawable/bg_rect_no_bottom.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:insetBottom="-2dp">
|
||||
|
||||
<shape android:shape="rectangle">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#000"/>
|
||||
<solid android:color="@android:color/transparent" />
|
||||
</shape>
|
||||
|
||||
</inset>
|
||||
12
app/src/main/res/drawable/bg_rect_no_bottom_no_left.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:insetLeft="-2dp" android:insetBottom="-2dp">
|
||||
|
||||
<shape android:shape="rectangle">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#000" />
|
||||
<solid android:color="@android:color/transparent" />
|
||||
</shape>
|
||||
|
||||
</inset>
|
||||
12
app/src/main/res/drawable/bg_rect_no_bottom_no_top.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:insetTop="-2dp" android:insetBottom="-2dp">
|
||||
|
||||
<shape android:shape="rectangle">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#000" />
|
||||
<solid android:color="@android:color/transparent" />
|
||||
</shape>
|
||||
|
||||
</inset>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:insetTop="-2dp" android:insetBottom="-2dp" android:insetLeft="-2dp">
|
||||
|
||||
<shape android:shape="rectangle">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#000" />
|
||||
<solid android:color="@android:color/transparent" />
|
||||
</shape>
|
||||
|
||||
</inset>
|
||||
12
app/src/main/res/drawable/bg_rect_no_top.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:insetTop="-2dp">
|
||||
|
||||
<shape android:shape="rectangle">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#000"/>
|
||||
<solid android:color="@android:color/transparent" />
|
||||
</shape>
|
||||
|
||||
</inset>
|
||||
13
app/src/main/res/drawable/bg_rect_no_top_no_left.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:insetLeft="-2dp"
|
||||
android:insetTop="-2dp">
|
||||
|
||||
<shape android:shape="rectangle">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#000" />
|
||||
<solid android:color="@android:color/transparent" />
|
||||
</shape>
|
||||
|
||||
</inset>
|
||||
BIN
app/src/main/res/drawable/logo_ldc.jpg
Executable file
|
After Width: | Height: | Size: 40 KiB |
36
app/src/main/res/layout/activity_kiosk.xml
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout 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.KioskActivity">
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/kiosk_tab_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
app:tabIconTint="#D4021D"
|
||||
app:tabIndicatorColor="#D4021D"
|
||||
app:tabTextColor="?android:attr/textColorSecondaryInverseNoDisable">
|
||||
|
||||
<com.google.android.material.tabs.TabItem
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Accueil" />
|
||||
|
||||
<com.google.android.material.tabs.TabItem
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Informations" />
|
||||
|
||||
</com.google.android.material.tabs.TabLayout>
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/kiosk_view_pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_above="@id/kiosk_tab_layout" />
|
||||
|
||||
</RelativeLayout>
|
||||
169
app/src/main/res/layout/activity_kiosk_diagnostic.xml
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
<?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.KioskDiagnosticActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
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>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/btnLineLinearLayout"
|
||||
style="@style/width_match_parent_max_200"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginTop="64dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:minWidth="280dp"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/linearLayout">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnPing"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:layout_weight="1"
|
||||
android:width="120dp"
|
||||
android:backgroundTint="#D4021D"
|
||||
android:text="Ping" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnReboot"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:layout_weight="1"
|
||||
android:width="120dp"
|
||||
android:backgroundTint="#D4021D"
|
||||
android:text="Reboot" />
|
||||
</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/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"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnRefresh"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:layout_weight="1"
|
||||
android:width="120dp"
|
||||
android:backgroundTint="#D4021D"
|
||||
android:text="Rafraichir" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/wifiStrengthLinearLayout"
|
||||
style="@style/width_match_parent_max_200"
|
||||
android:layout_width="match_parent"
|
||||
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/btnLine2LinearLayout">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Signal"
|
||||
android:textColor="#000000"
|
||||
android:textSize="18dp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBarWifiStrengthPercent"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:padding="4dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewWifiStrengthPercent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="0%"
|
||||
android:textColor="#000000"
|
||||
android:textSize="18dp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginTop="5dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/wifiStrengthLinearLayout">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TableLayout
|
||||
android:id="@+id/tableLayoutWifiRows"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="5dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tableCellMissingPermissions"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="81dp"
|
||||
android:padding="2.5dp"
|
||||
android:text="Chargement.." />
|
||||
|
||||
</TableLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TableRow xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/tableRowWifiHeader"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="2.5dp">
|
||||
|
||||
<TextView
|
||||
android:padding="2.5dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Réseau"/>
|
||||
<TextView
|
||||
android:padding="2.5dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Freq"/>
|
||||
<TextView
|
||||
android:padding="2.5dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="dBm"/>
|
||||
<TextView
|
||||
android:padding="2.5dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Signal"/>
|
||||
</TableRow>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TableRow xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="2.5dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tableCellMissingPermissions"
|
||||
android:padding="2.5dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Utilisez le bouton rafraichir"/>
|
||||
</TableRow>
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TableRow xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="2.5dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tableCellSSID"
|
||||
android:padding="2.5dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""/>
|
||||
<TextView
|
||||
android:id="@+id/tableCellSignalFreq"
|
||||
android:padding="2.5dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textAlignment="center"/>
|
||||
<TextView
|
||||
android:id="@+id/tableCellSignal"
|
||||
android:padding="2.5dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textAlignment="center"/>
|
||||
<TextView
|
||||
android:id="@+id/tableCellSignalPercent"
|
||||
android:padding="2.5dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textAlignment="center"/>
|
||||
</TableRow>
|
||||
154
app/src/main/res/layout/activity_kiosk_parameter.xml
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
<?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.KioskParameterActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
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="Paramètres"
|
||||
android:textColor="#000000"
|
||||
android:textSize="48sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/configBloc"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginTop="64dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/linearLayout">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Url du serveur de configuration"
|
||||
android:textAlignment="center"
|
||||
android:textColor="#000000"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editTextServerUrl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:ems="10"
|
||||
android:inputType="textUri" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:text="Arguments VNC (IP:Port)"
|
||||
android:textAlignment="center"
|
||||
android:textColor="#000000"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<LinearLayout
|
||||
style="@style/width_match_parent_max_200"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editTextIp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:ems="10"
|
||||
android:inputType="text|numberSigned" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=":"
|
||||
android:textAlignment="center"
|
||||
android:textColor="#000000"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editTextPort"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:ems="10"
|
||||
android:inputType="text|numberSigned" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
style="@style/width_match_parent_max_200"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<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" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnValidateConfig"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:layout_weight="1"
|
||||
android:backgroundTint="#D4021D"
|
||||
android:text="Ok" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
style="@style/width_match_parent_max_200"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnDiagnostic"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:layout_weight="1"
|
||||
android:backgroundTint="#D4021D"
|
||||
android:text="Diagnostique" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
<?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.KioskParameterPasswordActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
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="Paramètres"
|
||||
android:textColor="#000000"
|
||||
android:textSize="48sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/passwordBloc"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginTop="64dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/linearLayout">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Mot de passe requis"
|
||||
android:textAlignment="center"
|
||||
android:textColor="#000000"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editTextPassword"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:ems="10"
|
||||
android:inputType="textPassword"
|
||||
android:text="" />
|
||||
|
||||
<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/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" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnSubmitPassword"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:layout_weight="1"
|
||||
android:backgroundTint="#D4021D"
|
||||
android:text="Ok" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
103
app/src/main/res/layout/activity_kiosk_ping.xml
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
<?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="10dp"
|
||||
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>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/configBloc"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginTop="64dp"
|
||||
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/linearLayout">
|
||||
|
||||
<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_marginTop="30dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:ems="10"
|
||||
android:inputType="textUri" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textResult"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:maxWidth="200dp"
|
||||
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/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" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnPing"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:layout_weight="1"
|
||||
android:backgroundTint="#D4021D"
|
||||
android:text="Ping" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
android:layout_height="80dp"
|
||||
android:layout_gravity="center"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@mipmap/ic_launcher_round" />
|
||||
android:src="@mipmap/ic_launcher" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
|
|
|
|||
112
app/src/main/res/layout/fragment_kiosk_home.xml
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
<?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.fragments.KioskHomeFragment">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnLoadConfig"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:backgroundTint="#D4021D"
|
||||
android:text="Charger la configuration"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/linearLayout" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnVNC"
|
||||
android:layout_width="285dp"
|
||||
android:layout_height="78dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:backgroundTint="#D4021D"
|
||||
android:onClick="btnStartVncSession"
|
||||
android:text="Commencer"
|
||||
android:textSize="34sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/btnLoadConfig" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnParameters"
|
||||
android:layout_width="215dp"
|
||||
android:layout_height="65dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:backgroundTint="#D4021D"
|
||||
android:text="Paramètres"
|
||||
android:textSize="24sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/btnVNC" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="124dp"
|
||||
android:layout_height="135dp"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/logo_ldc" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageView">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtMacAddress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Identifiant TID : -"
|
||||
android:textColor="#000000"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtIPAddress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Adresse IP : -"
|
||||
android:textColor="#000000"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtVNCArgs"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Arguments VNC : -"
|
||||
android:textColor="#000000"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="@android:style/Widget.DeviceDefault.Light.ProgressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.498"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/btnParameters" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
239
app/src/main/res/layout/fragment_kiosk_information.xml
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
<?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.fragments.KioskInformationFragment">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
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="Informations"
|
||||
android:textColor="#000000"
|
||||
android:textSize="48sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/infoLinearLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/btnReloadInfo">
|
||||
|
||||
<GridLayout
|
||||
android:id="@+id/infoGridLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:columnCount="2"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnSpan="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_gravity="fill"
|
||||
android:background="@drawable/bg_rect_no_bottom"
|
||||
android:padding="10dp"
|
||||
android:text="TEXT"
|
||||
android:textColor="#000000"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnSpan="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_gravity="fill"
|
||||
android:background="@drawable/bg_rect_no_bottom_no_left"
|
||||
android:padding="10dp"
|
||||
android:text="TEXT"
|
||||
android:textColor="#000000"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnSpan="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_gravity="fill"
|
||||
android:background="@drawable/bg_rect_no_bottom_no_top"
|
||||
android:padding="10dp"
|
||||
android:text="TEXT"
|
||||
android:textColor="#000000"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnSpan="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_gravity="fill"
|
||||
android:background="@drawable/bg_rect_no_bottom_no_top_no_left"
|
||||
android:padding="10dp"
|
||||
android:text="TEXT"
|
||||
android:textColor="#000000"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnSpan="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_gravity="fill"
|
||||
android:background="@drawable/bg_rect_no_bottom_no_top"
|
||||
android:padding="10dp"
|
||||
android:text="TEXT"
|
||||
android:textColor="#000000"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnSpan="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_gravity="fill"
|
||||
android:background="@drawable/bg_rect_no_bottom_no_top_no_left"
|
||||
android:padding="10dp"
|
||||
android:text="TEXT"
|
||||
android:textColor="#000000"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnSpan="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_gravity="fill"
|
||||
android:background="@drawable/bg_rect_no_bottom_no_top"
|
||||
android:padding="10dp"
|
||||
android:text="TEXT"
|
||||
android:textColor="#000000"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnSpan="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_gravity="fill"
|
||||
android:background="@drawable/bg_rect_no_bottom_no_top_no_left"
|
||||
android:maxEms="15"
|
||||
android:maxLines="4"
|
||||
android:padding="10dp"
|
||||
android:singleLine="false"
|
||||
android:text="TEXT"
|
||||
android:textColor="#000000"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnSpan="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_gravity="fill"
|
||||
android:background="@drawable/bg_rect_no_bottom_no_top"
|
||||
android:padding="10dp"
|
||||
android:text="TEXT"
|
||||
android:textColor="#000000"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnSpan="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_gravity="fill"
|
||||
android:background="@drawable/bg_rect_no_bottom_no_top_no_left"
|
||||
android:padding="10dp"
|
||||
android:text="TEXT"
|
||||
android:textColor="#000000"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnSpan="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_gravity="fill"
|
||||
android:background="@drawable/bg_rect_no_bottom_no_top"
|
||||
android:padding="10dp"
|
||||
android:text="TEXT"
|
||||
android:textColor="#000000"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnSpan="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_gravity="fill"
|
||||
android:background="@drawable/bg_rect_no_bottom_no_top_no_left"
|
||||
android:padding="10dp"
|
||||
android:text="TEXT"
|
||||
android:textColor="#000000"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnSpan="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_gravity="fill"
|
||||
android:background="@drawable/bg_rect_no_top"
|
||||
android:padding="10dp"
|
||||
android:text="TEXT"
|
||||
android:textColor="#000000"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnSpan="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_gravity="fill"
|
||||
android:background="@drawable/bg_rect_no_top_no_left"
|
||||
android:padding="10dp"
|
||||
android:text="TEXT"
|
||||
android:textColor="#000000"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
</GridLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnReloadInfo"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:backgroundTint="#D4021D"
|
||||
android:foreground="@drawable/ic_refresh"
|
||||
android:foregroundTint="@color/design_default_color_background"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/linearLayout" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -1,12 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Gaurav Ujjwal.
|
||||
~
|
||||
~ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
~
|
||||
~ See COPYING.txt for more details.
|
||||
-->
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
|
@ -1,12 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2020 Gaurav Ujjwal.
|
||||
~
|
||||
~ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
~
|
||||
~ See COPYING.txt for more details.
|
||||
-->
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.6 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.7 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 3.6 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.3 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 10 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 19 KiB |
4
app/src/main/res/values/ic_launcher_background.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FFFFFF</color>
|
||||
</resources>
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
-->
|
||||
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">AVNC</string>
|
||||
<string name="app_name" translatable="false">Kiosk LDC</string>
|
||||
|
||||
<string name="title_add_server_profile">Add server</string>
|
||||
<string name="title_edit_server_profile">Edit server</string>
|
||||
|
|
@ -184,4 +184,6 @@
|
|||
<string name="pref_experimental_toolbar_swipe_close_summary">During a remote session, you can swipe to close currently open Toolbar</string>
|
||||
<string name="pref_experimental_immersive_mode">Full immersive mode</string>
|
||||
<string name="pref_experimental_immersive_mode_summary">Hides all system bars in fullscreen mode</string>
|
||||
<!-- TODO: Remove or change this placeholder text -->
|
||||
<string name="hello_blank_fragment">Hello blank fragment</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -101,4 +101,8 @@
|
|||
<style name="VirtualKey.Toggle">
|
||||
<item name="android:background">@drawable/bg_toggle_button</item>
|
||||
</style>
|
||||
|
||||
<style name="width_match_parent_max_200">
|
||||
<item name="android:layout_width">200dp</item>
|
||||
</style>
|
||||
</resources>
|
||||
6
app/src/main/res/xml/network_security_config.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<domain-config cleartextTrafficPermitted="true">
|
||||
<domain includeSubdomains="true">localtid</domain>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
buildscript {
|
||||
ext.kotlin_version = '1.4.31'
|
||||
//ext.kotlin_version = '1.4.31'
|
||||
ext.kotlin_version = '1.6.0'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
include ':app'
|
||||
rootProject.name = "AVNC"
|
||||
rootProject.name = "AVNC"
|
||||
|
|
|
|||