Add discovered items to the listView.

This commit is contained in:
Mark Nellemann 2023-08-07 18:04:56 +02:00
parent 48019f23bb
commit f041684909
8 changed files with 129 additions and 197 deletions

View file

@ -69,7 +69,7 @@ Add the platform-tools to you PATH to use 'adb' command:
export PATH="$PATH:/home/mark/Android/Sdk/platform-tools/" export PATH="$PATH:/home/mark/Android/Sdk/platform-tools/"
``` ```
Run the 'adb devices' and 'adb usb' to verify your Android device is in development mode and allows connections. Run the 'adb devices' and 'adb usb' to verify your Android networkService is in development mode and allows connections.
### MacOS ### MacOS

View file

@ -1,44 +1,41 @@
package biz.nellemann.mdexpl; package biz.nellemann.mdexpl;
import biz.nellemann.mdexpl.model.Device; import biz.nellemann.mdexpl.model.NetworkService;
import biz.nellemann.mdexpl.model.Devices;
import com.gluonhq.charm.glisten.control.CharmListCell; import com.gluonhq.charm.glisten.control.CharmListCell;
import com.gluonhq.charm.glisten.control.ListTile; import com.gluonhq.charm.glisten.control.ListTile;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
public class DeviceCell extends CharmListCell<Device> { public class NetworkServiceCell extends CharmListCell<NetworkService> {
private final ListTile tile; private final ListTile tile;
private final ImageView imageView; private final ImageView imageView;
public DeviceCell() { public NetworkServiceCell() {
this.tile = new ListTile(); this.tile = new ListTile();
imageView = new ImageView(); imageView = new ImageView();
imageView.setFitHeight(15); imageView.setFitHeight(15);
imageView.setFitWidth(25); imageView.setFitWidth(25);
tile.setPrimaryGraphic(imageView); tile.setPrimaryGraphic(imageView);
tile.setOnMouseClicked(e -> { System.out.println("Selected -> " + itemProperty().get().getName() ); });
setText(null); setText(null);
} }
@Override @Override
public void updateItem(Device item, boolean empty) { public void updateItem(NetworkService item, boolean empty) {
super.updateItem(item, empty); super.updateItem(item, empty);
if (item != null && !empty) { if (item != null && !empty) {
tile.textProperty().setAll(item.getName() + " (" + item.getAbbr() + ")", tile.textProperty().setAll(item.getName() + " (" + item.getType() + ")",
"Capital: " + item.getCapital() + "App: " + item.getApp(), "URL: " + item.getUrl()
", Population (M): " + String.format("%.2f", item.getPopulation() / 1_000_000d),
"Area (km" + "\u00B2" + "): " + item.getArea() +
", Density (pop/km" + "\u00B2" + "): " + String.format("%.1f", item.getDensity())
); );
final Image image = Devices.getImage(item.getFlag()); //final Image image = Devices.getImage(item.getFlag());
if (image != null) { /*if (image != null) {
imageView.setImage(image); imageView.setImage(image);
} }*/
setGraphic(tile); setGraphic(tile);
} else { } else {
setGraphic(null); setGraphic(null);
} }
} }
} }

View file

@ -1,24 +1,27 @@
package biz.nellemann.mdexpl; package biz.nellemann.mdexpl;
import biz.nellemann.mdexpl.model.NetworkService;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.jmdns.ServiceEvent; import javax.jmdns.ServiceEvent;
import javax.jmdns.ServiceInfo; import javax.jmdns.ServiceInfo;
import javax.jmdns.ServiceListener; import javax.jmdns.ServiceListener;
import java.util.HashMap;
public class NetworkServiceListener implements ServiceListener { public class NetworkServiceListener implements ServiceListener {
private static final Logger log = LoggerFactory.getLogger(NetworkServiceListener.class); private static final Logger log = LoggerFactory.getLogger(NetworkServiceListener.class);
private final HashMap<String, String> onlineList = new HashMap<>();
private final String serviceType; private final String serviceType;
private final ObservableList<NetworkService> observableList;
public NetworkServiceListener(String type) { public NetworkServiceListener(String type, ObservableList<NetworkService> observableList) {
log.info("NetworkServiceListener() - type: {}", type); log.info("NetworkServiceListener() - type: {}", type);
this.serviceType = type; this.serviceType = type;
this.observableList = observableList;
} }
@Override @Override
@ -30,8 +33,11 @@ public class NetworkServiceListener implements ServiceListener {
ServiceInfo serviceInfo = event.getInfo(); ServiceInfo serviceInfo = event.getInfo();
if (serviceInfo != null) { if (serviceInfo != null) {
String name = serviceInfo.getName(); String name = serviceInfo.getName();
onlineList.remove(name); log.info("serviceRemoved() - Service: " + name);
log.info("serviceRemoved() - Removed service: " + name); NetworkService oldNetworkService = new NetworkService(name, serviceType, serviceInfo.getApplication(), serviceInfo.getURLs()[0]);
Platform.runLater(() -> {
observableList.remove(oldNetworkService);
});
} }
} }
@ -42,9 +48,13 @@ public class NetworkServiceListener implements ServiceListener {
String url = serviceInfo.getURLs()[0]; String url = serviceInfo.getURLs()[0];
String name = serviceInfo.getName(); String name = serviceInfo.getName();
String app = serviceInfo.getApplication(); String app = serviceInfo.getApplication();
log.debug(serviceInfo.toString()); log.info("serviceResolved() - Service: {} - {} with url {}", app, name, url);
onlineList.put(name, url); NetworkService newNetworkService = new NetworkService(name, serviceType, app, url);
log.info("serviceResolved() - Found {}: {} with url {}", app, name, url); Platform.runLater(() -> {
if(!observableList.contains(newNetworkService)) {
observableList.add(newNetworkService);
}
});
} }
} }

View file

@ -1,87 +0,0 @@
package biz.nellemann.mdexpl.model;
public class Device {
private String name;
private String abbr;
private String capital;
private int population;
private int area; /* km^2 */
private String flag;
public Device(String name, String abbr, String capital, int population, int area, String flag) {
this.name = name;
this.abbr = abbr;
this.capital = capital;
this.population = population;
this.area = area;
this.flag = flag;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAbbr() {
return abbr;
}
public void setAbbr(String abbr) {
this.abbr = abbr;
}
public String getCapital() {
return capital;
}
public void setCapital(String capital) {
this.capital = capital;
}
public int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
public int getArea() {
return area;
}
public void setArea(int area) {
this.area = area;
}
public String getFlag() {
return flag;
}
public void setFlag(String flag) {
this.flag = flag;
}
/**
* Population density
* @return population density (pop. per km^2)
*/
public double getDensity() {
if (area > 0) {
return (double) population / (double) area;
}
return 0;
}
@Override
public String toString() {
return name + " (" + abbr + "), capital=" + capital + ", population=" + population + ", area=" + area;
}
}

View file

@ -1,65 +0,0 @@
package biz.nellemann.mdexpl.model;
import com.gluonhq.attach.cache.Cache;
import com.gluonhq.attach.cache.CacheService;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.image.Image;
public class Devices {
private static final Cache<String, Image> CACHE;
static {
CACHE = CacheService.create()
.map(cache -> cache.<String, Image>getCache("images"))
.orElseThrow(() -> new RuntimeException("No CacheService available"));
}
private static final String URL_PATH = "https://upload.wikimedia.org/wikipedia/commons/thumb/";
public static ObservableList<Device> statesList = FXCollections.observableArrayList(
new Device("Alabama", "AL", "Montgomery", 4903185, 135767, URL_PATH + "5/5c/Flag_of_Alabama.svg/23px-Flag_of_Alabama.svg.png"),
new Device("Alaska", "AK", "Juneau", 731545, 1723337, URL_PATH + "e/e6/Flag_of_Alaska.svg/21px-Flag_of_Alaska.svg.png"),
new Device("Arizona", "AZ", "Phoenix", 7278717, 295233, URL_PATH + "9/9d/Flag_of_Arizona.svg/23px-Flag_of_Arizona.svg.png"),
new Device("Arkansas", "AR", "Little Rock", 3017804, 137733, URL_PATH + "9/9d/Flag_of_Arkansas.svg/23px-Flag_of_Arkansas.svg.png"),
new Device("California", "CA", "Sacramento", 39512223, 423968, URL_PATH + "0/01/Flag_of_California.svg/23px-Flag_of_California.svg.png"),
new Device("Colorado", "CO", "Denver", 5758736, 269602, URL_PATH + "4/46/Flag_of_Colorado.svg/23px-Flag_of_Colorado.svg.png"),
new Device("Connecticut", "CT", "Hartford", 3565287, 14356, URL_PATH + "9/96/Flag_of_Connecticut.svg/20px-Flag_of_Connecticut.svg.png"),
new Device("Delaware", "DE", "Dover", 973764, 6446, URL_PATH + "c/c6/Flag_of_Delaware.svg/23px-Flag_of_Delaware.svg.png"),
new Device("Florida", "FL", "Tallahassee", 21477737, 170312, URL_PATH + "f/f7/Flag_of_Florida.svg/23px-Flag_of_Florida.svg.png"),
new Device("Georgia", "GA", "Atlanta", 10617423, 153910, URL_PATH + "5/54/Flag_of_Georgia_%28U.S._state%29.svg/23px-Flag_of_Georgia_%28U.S._state%29.svg.png"),
new Device("Hawaii", "HI", "Honolulu", 1415872, 28314, URL_PATH + "e/ef/Flag_of_Hawaii.svg/23px-Flag_of_Hawaii.svg.png"),
new Device("Idaho", "ID", "Boise", 1787065, 216443, URL_PATH + "a/a4/Flag_of_Idaho.svg/19px-Flag_of_Idaho.svg.png"),
new Device("Illinois", "IL", "Springfield", 12671821, 149997, URL_PATH + "0/01/Flag_of_Illinois.svg/23px-Flag_of_Illinois.svg.png")
);
public static Image getUSFlag() {
return getImage(URL_PATH + "a/a4/Flag_of_the_United_States.svg/320px-Flag_of_the_United_States.svg.png");
}
/**
* This method will always return the required image.
* It will cache the image and return from cache if still there.
* @param image: A valid url to retrieve the image
* @return an Image
*/
public static Image getImage(String image) {
if (image == null || image.isEmpty()) {
return null;
}
Image cachedImage = CACHE.get(image);
if (cachedImage == null) {
cachedImage = new Image(image, true);
cachedImage.errorProperty().addListener((obs, ov, nv) -> {
if (nv) {
CACHE.remove(image);
}
});
CACHE.put(image, cachedImage);
}
return cachedImage;
}
}

View file

@ -0,0 +1,66 @@
package biz.nellemann.mdexpl.model;
public class NetworkService {
private String name;
private String type;
private String app;
private String url;
public NetworkService(String name, String type, String app, String url) {
this.name = name;
this.type = type;
this.app = app;
this.url = url;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getApp() {
return app;
}
public void setApp(String app) {
this.app = app;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
@Override
public String toString() {
return name + " (" + type + "), app=" + app + ", url=" + url;
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof NetworkService networkService)) {
return false;
}
return networkService.name.equals(name) && networkService.type.equals(type) && networkService.url.equals(url);
}
}

View file

@ -1,16 +1,15 @@
package biz.nellemann.mdexpl.service; package biz.nellemann.mdexpl.service;
import biz.nellemann.mdexpl.NetworkServiceListener; import biz.nellemann.mdexpl.NetworkServiceListener;
import biz.nellemann.mdexpl.model.NetworkService;
import javafx.collections.ObservableList;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.inject.Singleton; import javax.inject.Singleton;
import javax.jmdns.JmDNS; import javax.jmdns.JmDNS;
import javax.jmdns.ServiceInfo;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -20,29 +19,39 @@ public class DiscoveryService {
private final static Logger log = LoggerFactory.getLogger(DiscoveryService.class); private final static Logger log = LoggerFactory.getLogger(DiscoveryService.class);
// http://www.dns-sd.org/serviceTypes.html private ObservableList<NetworkService> observableList;
private JmDNS jmdns;
// From: http://www.dns-sd.org/serviceTypes.html + some guesses
private final List<String> services = Arrays.asList( private final List<String> services = Arrays.asList(
"http", "https", "upnp", "ssh", "sip", "rdp", "ntp", "rdp", "rtsp", "http", "https", "upnp", "ssh", "sip", "rdp", "ntp", "rdp", "rtsp",
"ntp", "smb", "nfs", "llrp", "ftp", "ep", "daap", "ipp", "ipps", "ntp", "smb", "nfs", "llrp", "ftp", "ep", "daap", "ipp", "ipps",
"googlecast", "appletv", "appletv-itunes", "smartenergy", "skype", "bittorrent", "googlecast", "sonos", "airplay", "smartenergy", "skype", "bittorrent"
"sonos", "airplay"
); );
@PostConstruct @PostConstruct
public void initialize() { public void initialize() {
log.info("initialize()"); log.info("initialize()");
try { try {
JmDNS jmdns = JmDNS.create(null, "mdnsExplorer"); jmdns = JmDNS.create(null, "mdnsExplorer");
services.forEach(service -> {
String serviceType = String.format("_%s._%s.local.", service, "tcp");
NetworkServiceListener networkServiceListener = new NetworkServiceListener(serviceType);
jmdns.addServiceListener(serviceType, networkServiceListener);
});
} catch (IOException e) { } catch (IOException e) {
log.error("initialize() - {}", e.getMessage()); log.error("initialize() - {}", e.getMessage());
} }
} }
public void register() {
}
public void setObservableList(ObservableList<NetworkService> list) {
this.observableList = list;
services.forEach(service -> {
String serviceType = String.format("_%s._%s.local.", service, "tcp");
NetworkServiceListener networkServiceListener = new NetworkServiceListener(serviceType, observableList);
jmdns.addServiceListener(serviceType, networkServiceListener);
});
}
} }

View file

@ -1,20 +1,20 @@
package biz.nellemann.mdexpl.view; package biz.nellemann.mdexpl.view;
import biz.nellemann.mdexpl.NetworkServiceCell;
import biz.nellemann.mdexpl.model.NetworkService;
import biz.nellemann.mdexpl.model.MainModel; import biz.nellemann.mdexpl.model.MainModel;
import biz.nellemann.mdexpl.service.DiscoveryService; import biz.nellemann.mdexpl.service.DiscoveryService;
import com.gluonhq.charm.glisten.application.AppManager; import com.gluonhq.charm.glisten.application.AppManager;
import com.gluonhq.charm.glisten.control.AppBar; import com.gluonhq.charm.glisten.control.AppBar;
import com.gluonhq.charm.glisten.control.CharmListView; import com.gluonhq.charm.glisten.control.CharmListView;
import com.gluonhq.charm.glisten.control.Icon;
import com.gluonhq.charm.glisten.control.LifecycleEvent; import com.gluonhq.charm.glisten.control.LifecycleEvent;
import com.gluonhq.charm.glisten.mvc.View; import com.gluonhq.charm.glisten.mvc.View;
import com.gluonhq.charm.glisten.visual.MaterialDesignIcon; import com.gluonhq.charm.glisten.visual.MaterialDesignIcon;
import javafx.beans.property.DoubleProperty; import javafx.collections.FXCollections;
import javafx.beans.property.SimpleDoubleProperty; import javafx.collections.ObservableList;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.geometry.Orientation; import javafx.scene.input.MouseEvent;
import javafx.scene.control.ScrollPane; import javafx.scene.input.TouchEvent;
import javafx.scene.image.ImageView;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -41,6 +41,8 @@ public class MainPresenter {
@FXML @FXML
private CharmListView charmListView; private CharmListView charmListView;
private ObservableList<NetworkService> devicesList = FXCollections.observableArrayList();
@FXML @FXML
public void initialize() { public void initialize() {
@ -56,19 +58,19 @@ public class MainPresenter {
appManager.getDrawer().open())); appManager.getDrawer().open()));
appBar.setTitleText("mDNS Explorer"); appBar.setTitleText("mDNS Explorer");
//appBar.getActionItems().add(progressIndicator);
} }
}); });
discoveryService.setObservableList(devicesList);
charmListView.setItems(devicesList);
charmListView.setCellFactory(p -> new NetworkServiceCell());
} }
@FXML @FXML
protected void onButtonRefresh() { protected void onButtonRefresh() {
log.info("onButtonRefresh()"); log.info("onButtonRefresh()");
} }