Added support for saved services
This commit is contained in:
parent
2817cc5f18
commit
d452e57bca
8 changed files with 426 additions and 33 deletions
|
@ -18,25 +18,32 @@ html, body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.services {
|
.services {
|
||||||
width: 50%;
|
width: 60%;
|
||||||
height: max-content;
|
height: max-content;
|
||||||
min-width: 15em;
|
min-width: 17em;
|
||||||
max-width: 22.5em;
|
max-width: 22.5em;
|
||||||
background: white;
|
background: white;
|
||||||
margin-top: 3em;
|
margin-top: 2em;
|
||||||
box-shadow: 1px 1px 2px 2px #bbb;
|
box-shadow: 1px 1px 2px 2px #bbb;
|
||||||
border-radius: 1.5em;
|
border-radius: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.services h3 {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.no-items {
|
.no-items {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 2em 1em;
|
padding: 2em 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.service {
|
.service {
|
||||||
|
width: 100%;
|
||||||
padding: 1em .5em;
|
padding: 1em .5em;
|
||||||
border-bottom: 1px solid #ccc;
|
border-bottom: 1px solid #ccc;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.service:first-child {
|
.service:first-child {
|
||||||
|
@ -51,6 +58,16 @@ html, body {
|
||||||
background: #bef6da;
|
background: #bef6da;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.service .remove {
|
||||||
|
position: absolute;
|
||||||
|
right: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service .remove img {
|
||||||
|
width: 1.2em;
|
||||||
|
height: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
@ -69,6 +86,7 @@ html, body {
|
||||||
opacity: .75;
|
opacity: .75;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Add host modal */
|
||||||
.add-modal-container {
|
.add-modal-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -128,6 +146,16 @@ html, body {
|
||||||
font-size: 1.05em;
|
font-size: 1.05em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.add-modal .buttons {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-modal .buttons .button {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
/* Splash screen */
|
/* Splash screen */
|
||||||
.splash {
|
.splash {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
|
|
BIN
app/src/main/assets/web/img/trash.png
Normal file
BIN
app/src/main/assets/web/img/trash.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
|
@ -25,18 +25,37 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="services">
|
<div class="services">
|
||||||
<div class="no-items" v-if="!Object.keys(services).length">
|
<div class="saved" v-if="services.saved?.length">
|
||||||
<div class="empty">No Platypush web services found on the network</div>
|
<h3>Saved services</h3>
|
||||||
|
|
||||||
|
<div class="service" v-for="(service, i) in services.saved" :key="i"
|
||||||
|
@click="onServiceClick(service)">
|
||||||
|
<span class="name" v-text="service.name" v-if="service.name"></span>
|
||||||
|
<span v-if="service.name"> on </span>
|
||||||
|
<span class="address" v-text="service.host"></span>:<span class="port" v-text="service.port"></span>
|
||||||
|
<span class="remove" @click="removeService(i, $event)">
|
||||||
|
<img src="img/trash.png" alt="Remove">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="service" v-for="(service, i) in services" :key="i" @click="onClick(service)">
|
<div class="scanned">
|
||||||
<span class="name" v-text="service.name" v-if="service.name"></span>
|
<h3>Scanned services</h3>
|
||||||
<span v-if="service.name"> on </span>
|
|
||||||
<span class="address" v-text="service.host"></span>:<span class="port" v-text="service.port"></span>
|
<div class="no-items" v-if="!services.scanned?.length">
|
||||||
|
<div class="empty">No Platypush web services found on the network</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="service" v-for="(service, i) in services.scanned" :key="i"
|
||||||
|
@click="onServiceClick(service)">
|
||||||
|
<span class="name" v-text="service.name" v-if="service.name"></span>
|
||||||
|
<span v-if="service.name"> on </span>
|
||||||
|
<span class="address" v-text="service.host"></span>:<span class="port" v-text="service.port"></span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="add-modal-container" @click="addModalVisible = false" v-if="addModalVisible">
|
<div class="add-modal-container" @click="addModal.visible = false" v-if="addModal.visible">
|
||||||
<div class="add-modal-background"></div>
|
<div class="add-modal-background"></div>
|
||||||
<div class="add-modal" @click="$event.stopPropagation()">
|
<div class="add-modal" @click="$event.stopPropagation()">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
|
@ -44,22 +63,36 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<form @submit.prevent="onConnect">
|
<form @submit.prevent="onServiceConnect">
|
||||||
<label>
|
<label>
|
||||||
<input type="text" placeholder="IP or hostname" v-model="addModalHost">
|
<input type="text" placeholder="IP or hostname" v-model="addModal.host">
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
<input type="number" placeholder="Port" v-model="addModalPort">
|
<input type="number" placeholder="Port" v-model="addModal.port">
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<input type="submit" value="Connect">
|
<label v-if="addModal.save">
|
||||||
|
<input type="text" placeholder="Name" v-model="addModal.name">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="buttons">
|
||||||
|
<div class="button">
|
||||||
|
<input type="submit" value="Connect">
|
||||||
|
</div>
|
||||||
|
<div class="button">
|
||||||
|
<label>
|
||||||
|
Remember this host
|
||||||
|
<input type="checkbox" v-model="addModal.save">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="add-btn" @click="addModalVisible = true"></div>
|
<div class="add-btn" @click="addModal.visible = true"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript" src="js/main.js"></script>
|
<script type="text/javascript" src="js/main.js"></script>
|
||||||
|
|
|
@ -4,47 +4,164 @@ new Vue({
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
splash: false,
|
splash: false,
|
||||||
services: {},
|
services: {
|
||||||
addModalVisible: false,
|
scanned: [],
|
||||||
addModalHost: undefined,
|
saved: [],
|
||||||
addModalPort: 8008,
|
},
|
||||||
|
addModal: {
|
||||||
|
visible: false,
|
||||||
|
host: undefined,
|
||||||
|
port: 8008,
|
||||||
|
name: undefined,
|
||||||
|
save: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
servicesByName: function() {
|
||||||
|
return Object.values(this.services).reduce((obj, services) =>
|
||||||
|
services.reduce((obj2, srv) => {
|
||||||
|
obj2[srv.name] = srv
|
||||||
|
return obj2
|
||||||
|
}, obj), {})
|
||||||
|
},
|
||||||
|
|
||||||
|
servicesByHostAndPort: function() {
|
||||||
|
return Object.values(this.services).reduce((obj, services) =>
|
||||||
|
services.reduce((obj2, srv) => {
|
||||||
|
obj2[`${srv.host}:${srv.port}`] = srv
|
||||||
|
return obj2
|
||||||
|
}, obj), {})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
refresh: function() {
|
refresh: function() {
|
||||||
this.services = {
|
this.services.scanned = [
|
||||||
...this.services,
|
...this.services.scanned,
|
||||||
...JSON.parse(app.pollServices()),
|
...JSON.parse(app.pollServices()).filter((srv) => !(srv.name in this.servicesByName)),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
onServiceClick: function(service) {
|
||||||
|
this.connect(service.host, service.port)
|
||||||
|
},
|
||||||
|
|
||||||
|
onServiceConnect: function() {
|
||||||
|
if (!this.addModal.host?.length) {
|
||||||
|
app.alert('Please specify a host name or IP address')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.addModal.save) {
|
||||||
|
if (!this.saveService(this.addModal.host, this.addModal.port, this.addModal.name || ''))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.connect(this.addModal.host, this.addModal.port)
|
||||||
|
},
|
||||||
|
|
||||||
|
saveService: function(host, port, name) {
|
||||||
|
name = this.addModal.name.trim()
|
||||||
|
if (!name.length) {
|
||||||
|
app.alert('Please specify a name')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let overwrite = false
|
||||||
|
if (name in this.servicesByName) {
|
||||||
|
if (!confirm(`A service named ${name} already exists. ` +
|
||||||
|
`Do you want to overwrite it?`))
|
||||||
|
return false
|
||||||
|
|
||||||
|
overwrite = true
|
||||||
|
} else if (`${host}:${port}` in this.servicesByHostAndPort) {
|
||||||
|
if (!confirm(`A service on ${host}:${port} already exists. ` +
|
||||||
|
`Do you want to overwrite it?`))
|
||||||
|
return false
|
||||||
|
|
||||||
|
overwrite = true
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
const rs = app.saveService(host, port, name, overwrite)
|
||||||
|
if (rs?.error)
|
||||||
|
throw rs.error
|
||||||
|
|
||||||
|
this.loadServices()
|
||||||
|
return true
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onClick: function(service) {
|
removeService: function(savedIndex, event) {
|
||||||
this.loadService(service.host, service.port)
|
event.stopPropagation()
|
||||||
|
const srv = this.services.saved[savedIndex]
|
||||||
|
if (!(srv && confirm('Are you sure that you want to remove this service?')))
|
||||||
|
return false
|
||||||
|
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
const rs = app.removeService(srv.host, srv.port, srv.name)
|
||||||
|
if (rs?.error)
|
||||||
|
throw rs.error
|
||||||
|
|
||||||
|
this.loadServices()
|
||||||
|
return true
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onConnect: function() {
|
connect: function(host, port) {
|
||||||
this.loadService(this.addModalHost, this.addModalPort)
|
|
||||||
},
|
|
||||||
|
|
||||||
loadService: function(host, port) {
|
|
||||||
this.loading = true
|
this.loading = true
|
||||||
app.stopServicesPoll()
|
app.stopServicesPoll()
|
||||||
window.location.href = `http://${host}:${port}/`
|
window.location.href = `http://${host}:${port}/`
|
||||||
},
|
},
|
||||||
|
|
||||||
splashScreen: function(duration) {
|
splashScreen: function(duration) {
|
||||||
var self = this
|
const self = this
|
||||||
this.splash = true
|
this.splash = true
|
||||||
window.setTimeout(() => {
|
setTimeout(() => {
|
||||||
self.splash = false
|
self.splash = false
|
||||||
}, duration)
|
}, duration)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
resetAddModal: function() {
|
||||||
|
this.addModal = {
|
||||||
|
host: undefined,
|
||||||
|
port: 8008,
|
||||||
|
name: undefined,
|
||||||
|
save: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
loadServices: function() {
|
||||||
|
this.services = {
|
||||||
|
...this.services,
|
||||||
|
...JSON.parse(app.loadServices())
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted: function() {
|
mounted: function() {
|
||||||
this.splashScreen(2500)
|
this.splashScreen(1500)
|
||||||
|
|
||||||
|
this.$watch(() => this.addModal.visible, (newValue) => {
|
||||||
|
if (!newValue)
|
||||||
|
this.resetAddModal()
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$watch(() => this.addModal.save, (newValue) => {
|
||||||
|
if (newValue)
|
||||||
|
this.addModal.name = this.addModal.host
|
||||||
|
})
|
||||||
|
|
||||||
|
this.loadServices()
|
||||||
app.startServicesPoll()
|
app.startServicesPoll()
|
||||||
window.setInterval(this.refresh, 500)
|
setInterval(this.refresh, 500)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
54
app/src/main/java/tech/platypush/app/JSON.kt
Normal file
54
app/src/main/java/tech/platypush/app/JSON.kt
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package tech.platypush.app
|
||||||
|
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
|
|
||||||
|
class JSON {
|
||||||
|
companion object {
|
||||||
|
private fun toMap(obj: JSONObject): Map<String, Any?> {
|
||||||
|
val ret = HashMap<String, Any?>()
|
||||||
|
val keys = obj.keys()
|
||||||
|
|
||||||
|
while (keys.hasNext()) {
|
||||||
|
val key = keys.next()
|
||||||
|
var value = obj[key]
|
||||||
|
|
||||||
|
if (value is JSONObject)
|
||||||
|
value = toMap(value)
|
||||||
|
else if (value is JSONArray)
|
||||||
|
value = toList(value)
|
||||||
|
|
||||||
|
ret[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toList(arr: JSONArray): List<Any?> {
|
||||||
|
val ret = LinkedList<Any?>()
|
||||||
|
for (i in 0 until arr.length()) {
|
||||||
|
var value = arr[i]
|
||||||
|
|
||||||
|
if (value is JSONObject)
|
||||||
|
value = toMap(value)
|
||||||
|
else if (value is JSONArray)
|
||||||
|
value = toList(value)
|
||||||
|
|
||||||
|
ret.add(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fun load(str: String): Map<String, Any?> {
|
||||||
|
return toMap(JSONObject(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dump(obj: Map<String, Any?>): String {
|
||||||
|
return JSONObject(obj).toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package tech.platypush.app
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.webkit.WebChromeClient
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import kotlinx.android.synthetic.main.activity_main.*
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
|
|
||||||
|
@ -12,6 +13,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
webview.webViewClient = WebView()
|
webview.webViewClient = WebView()
|
||||||
|
webview.webChromeClient = WebChromeClient()
|
||||||
webview.settings.javaScriptEnabled = true
|
webview.settings.javaScriptEnabled = true
|
||||||
webview.settings.javaScriptCanOpenWindowsAutomatically = true
|
webview.settings.javaScriptCanOpenWindowsAutomatically = true
|
||||||
webview.clearCache(true)
|
webview.clearCache(true)
|
||||||
|
|
|
@ -5,8 +5,12 @@ import android.content.Context
|
||||||
import android.net.nsd.NsdManager
|
import android.net.nsd.NsdManager
|
||||||
import android.net.nsd.NsdServiceInfo
|
import android.net.nsd.NsdServiceInfo
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileWriter
|
||||||
|
import java.lang.RuntimeException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
import kotlin.concurrent.withLock
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,6 +24,20 @@ data class Service(val host: String, val port: Int, val name: String?) {
|
||||||
"name" to name
|
"name" to name
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return JSON.dump(toMap())
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun load(obj: Map<String, Any?>): Service {
|
||||||
|
return Service(
|
||||||
|
host = obj["host"] as String,
|
||||||
|
port = obj["port"] as Int,
|
||||||
|
name = obj["name"] as String?
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -123,3 +141,109 @@ class Scanner(context: Context) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Services manager class
|
||||||
|
*/
|
||||||
|
class Manager(context: Context) {
|
||||||
|
private val servicesFile = File(context.filesDir, "services.json")
|
||||||
|
private val servicesFileLock = ReentrantLock()
|
||||||
|
private val emptyServices = mapOf<String, List<Service>>(
|
||||||
|
"saved" to emptyList(),
|
||||||
|
"lastConnected" to emptyList()
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun initServicesFile(reset: Boolean = false) {
|
||||||
|
if (reset || !servicesFile.exists())
|
||||||
|
saveServices(emptyServices)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadServices(): Map<String, List<Service>> {
|
||||||
|
initServicesFile()
|
||||||
|
var services: Map<String, List<Service>> = emptyServices
|
||||||
|
|
||||||
|
try {
|
||||||
|
services = deserializeServices(servicesFile.readText())
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error while parsing $servicesFile: resetting it", e)
|
||||||
|
initServicesFile(reset = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return services
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveServices(services: Map<String, List<Service>>) {
|
||||||
|
servicesFileLock.withLock {
|
||||||
|
FileWriter(servicesFile).use {
|
||||||
|
it.write(serializeServices(services))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveService(host: String, port: Int, name: String, overwrite: Boolean = false) {
|
||||||
|
val service = Service(host, port, name)
|
||||||
|
val services = this.loadServices().toMutableMap()
|
||||||
|
val savedServices = services["saved"]?.toMutableList() ?: ArrayList()
|
||||||
|
val dupIndex = getSavedServiceIndex(service, savedServices)
|
||||||
|
|
||||||
|
if (dupIndex != null && !overwrite)
|
||||||
|
throw DuplicateServiceException(service)
|
||||||
|
|
||||||
|
savedServices.add(service)
|
||||||
|
services["saved"] = savedServices
|
||||||
|
saveServices(services)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeService(host: String? = null, port: Int? = null, name: String? = null) {
|
||||||
|
val service = Service(host=host ?: "", port=port ?: 0, name=name)
|
||||||
|
val services = this.loadServices().toMutableMap()
|
||||||
|
val savedServices = ArrayList<Service>(services["saved"] ?: emptyList<Service>())
|
||||||
|
val srvIndex = getSavedServiceIndex(service, savedServices)
|
||||||
|
?: throw NoSuchServiceException(host, port, name)
|
||||||
|
|
||||||
|
savedServices.removeAt(srvIndex)
|
||||||
|
services["saved"] = savedServices
|
||||||
|
saveServices(services)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSavedServiceIndex(service: Service, services: List<Service>): Int? {
|
||||||
|
val matchingSrvIndexes = services.indices.filter {
|
||||||
|
(services[it].host == service.host && services[it].port == service.port) ||
|
||||||
|
services[it].name == service.name
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (matchingSrvIndexes.isNotEmpty()) matchingSrvIndexes[0] else null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun serializeServices(services: Map<String, List<Service>>): String {
|
||||||
|
val parsedServices = HashMap<String, List<Map<String, Any?>>>()
|
||||||
|
|
||||||
|
for (srvType in listOf("saved", "lastConnected")) {
|
||||||
|
val outputList = LinkedList<Map<String, Any?>>()
|
||||||
|
for (srv in services[srvType] ?: emptyList())
|
||||||
|
outputList.add(srv.toMap())
|
||||||
|
parsedServices[srvType] = outputList.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSON.dump(parsedServices)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deserializeServices(servicesJson: String): Map<String, List<Service>> {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val services = JSON.load(servicesJson) as Map<String, List<Map<String, Any?>>>
|
||||||
|
val parsedServices = HashMap<String, List<Service>>()
|
||||||
|
|
||||||
|
for (srvType in listOf("saved", "lastConnected")) {
|
||||||
|
val outputList = LinkedList<Service>()
|
||||||
|
for (srv in services[srvType] ?: emptyList())
|
||||||
|
outputList.add(Service.load(srv))
|
||||||
|
parsedServices[srvType] = outputList
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsedServices
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DuplicateServiceException(service: Service) : RuntimeException("Duplicate service: $service")
|
||||||
|
class NoSuchServiceException(host: String? = null, port: Int? = null, name: String? = null):
|
||||||
|
RuntimeException("No such host: host=$host, port=$port, name=$name")
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package tech.platypush.app
|
package tech.platypush.app
|
||||||
|
|
||||||
|
import android.content.ContentValues.TAG
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
import android.webkit.JavascriptInterface
|
import android.webkit.JavascriptInterface
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -8,6 +10,7 @@ import java.util.*
|
||||||
|
|
||||||
class WebAppInterface(context: Context) {
|
class WebAppInterface(context: Context) {
|
||||||
private val serviceScanner = Scanner(context)
|
private val serviceScanner = Scanner(context)
|
||||||
|
private val serviceManager = Manager(context)
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
|
@ -29,4 +32,36 @@ class WebAppInterface(context: Context) {
|
||||||
services.add(srv.toMap())
|
services.add(srv.toMap())
|
||||||
return JSONArray(services).toString()
|
return JSONArray(services).toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
@JavascriptInterface
|
||||||
|
fun loadServices(): String {
|
||||||
|
return serviceManager.serializeServices(serviceManager.loadServices())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
@JavascriptInterface
|
||||||
|
fun saveService(host: String, port: Int, name: String, overwrite: Boolean = false): String? {
|
||||||
|
try {
|
||||||
|
serviceManager.saveService(host, port, name, overwrite=overwrite)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, e.toString())
|
||||||
|
return JSON.dump(mapOf("error" to e.toString()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
@JavascriptInterface
|
||||||
|
fun removeService(host: String, port: Int, name: String): String? {
|
||||||
|
try {
|
||||||
|
serviceManager.removeService(host, port, name)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, e.toString())
|
||||||
|
return JSON.dump(mapOf("error" to e.toString()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue