Desktop builds. #1
2
.dockerignore
Normal file
2
.dockerignore
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.git
|
||||||
|
.gradle
|
29
.drone.yml
Normal file
29
.drone.yml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: default
|
||||||
|
type: docker
|
||||||
|
|
||||||
|
#platform:
|
||||||
|
# os: linux
|
||||||
|
# arch: amd64
|
||||||
|
|
||||||
|
|
||||||
|
steps:
|
||||||
|
|
||||||
|
# - name: test
|
||||||
|
# image: docker.io/bellsoft/liberica-openjdk-debian:17
|
||||||
|
# commands:
|
||||||
|
# - ./gradlew --quiet --no-daemon test
|
||||||
|
|
||||||
|
- name: jpackage
|
||||||
|
image: docker.io/debian:stable
|
||||||
|
environment:
|
||||||
|
AUTH_TOKEN: # Gitea access token ENV variable
|
||||||
|
from_secret: AUTH_TOKEN # Name of DroneCI secret exposed above
|
||||||
|
commands:
|
||||||
|
- apt-get update && apt-get install -y dpkg-dev rpm unzip zip curl openjdk-17-jdk
|
||||||
|
- ./gradlew --no-daemon clean build jpackage
|
||||||
|
- for file in build/jpackage/*.* ; do curl -s --user "$${AUTH_TOKEN}" --upload-file "$${file}" "https://git.data.coop/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${DRONE_TAG}/$(basename $file)" ; done
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
- tag
|
89
README.md
89
README.md
|
@ -1,96 +1,37 @@
|
||||||
# mDNS Explorer
|
# mDNS Explorer
|
||||||
|
|
||||||
View all multicastDNS devices.
|
View multicastDNS services on your local network.
|
||||||
|
|
||||||
|
![mDNS-Explorer](doc/mDNS-Explorer.png)
|
||||||
|
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
Java SDK version 17 (or later) is required.
|
Java SDK version 17 (or later) is required.
|
||||||
|
|
||||||
## Native Images
|
Information on how to build and package jftpd:
|
||||||
|
|
||||||
The native images are built with GraalVM.
|
|
||||||
Download the Gluon version of GraalVM from https://github.com/gluonhq/graal/releases/tag/gluon-22.1.0.1-Final and extract it locally.
|
|
||||||
|
|
||||||
Point the GRAALVM_HOME environment variable to the folder:
|
|
||||||
|
|
||||||
```export GRAALVM_HOME="/path/to/gluon-22.1.0.1```
|
|
||||||
|
|
||||||
With all requirements setup and on a supported platform, the general idea is to run:
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
./gradlew clean build
|
./gradlew build jpackage
|
||||||
./gradlew nativeBuild -Ptarget=android # or -Ptarget=ios
|
|
||||||
./gradlew nativeLink -Ptarget=android
|
|
||||||
./gradlew nativePackage -Ptarget=android
|
|
||||||
./gradlew nativeInstall -Ptarget=android
|
|
||||||
./gradlew nativeRun -Ptarget=android
|
|
||||||
```
|
```
|
||||||
|
|
||||||
or, in one go for Android:
|
### Windows
|
||||||
|
|
||||||
```shell
|
Download and install
|
||||||
./gradlew -Ptarget=android clean build nativeBuild nativeLink nativePackage nativeInstall nativeRun
|
- Microsoft .NET Framework 3.5
|
||||||
```
|
- Wix Toolset 3.11.2 (or later)
|
||||||
|
|
||||||
or, in one go for iOS:
|
|
||||||
```shell
|
|
||||||
./gradlew -Ptarget=ios clean build nativeBuild nativeLink nativePackage nativeInstall nativeRun
|
|
||||||
```
|
|
||||||
|
|
||||||
### Linux
|
### Linux
|
||||||
|
|
||||||
See https://docs.gluonhq.com/#prerequisites_linux
|
TODO
|
||||||
|
|
||||||
For nativeBuild on Linux, install the following dependencies.
|
docker build . -f docker/Dockerfile.rpm-txt
|
||||||
|
|
||||||
```shell
|
or
|
||||||
sudo apt install libgtk-3-dev libavformat-dev libavutil-dev libavcodec-dev libasound2-dev libpango1.0-dev libxtst-dev build-essential
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Building for Android (on Linux)
|
docker build . -f docker/Dockerfile.deb-txt
|
||||||
|
|
||||||
See https://docs.gluonhq.com/#prerequisites_android
|
|
||||||
|
|
||||||
Install Android Studio and use sdkmanager to install the following:
|
|
||||||
|
|
||||||
- Android SDK Platform 31
|
|
||||||
- NDK (Side by side)
|
|
||||||
|
|
||||||
Point the ANDROID_NDK variable to the installed NDK directory:
|
|
||||||
|
|
||||||
```
|
|
||||||
export ANDROID_NDK="/home/mark/Android/Sdk/ndk/25.2.9519653/"
|
|
||||||
```
|
|
||||||
|
|
||||||
Add the platform-tools to you PATH to use 'adb' command:
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
export PATH="$PATH:/home/mark/Android/Sdk/platform-tools/"
|
|
||||||
```
|
|
||||||
|
|
||||||
Run the 'adb devices' and 'adb usb' to verify your Android networkService is in development mode and allows connections.
|
|
||||||
|
|
||||||
### MacOS
|
### MacOS
|
||||||
|
|
||||||
See https://docs.gluonhq.com/#platforms_macos
|
TODO
|
||||||
|
|
||||||
Install the xcode command line tools and an iPhone emulator.
|
|
||||||
|
|
||||||
If you have problems with *xcrun* not being able to find *iphoneos*, try:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
sudo xcode-select --switch /Applications/Xcode.app
|
|
||||||
```
|
|
||||||
|
|
||||||
Download and install homebrew, and install the following:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
brew install maven
|
|
||||||
brew install --HEAD libusbmuxd
|
|
||||||
brew install --HEAD libimobiledevice
|
|
||||||
```
|
|
||||||
|
|
||||||
#### iOS (on MacOS)
|
|
||||||
|
|
||||||
See https://docs.gluonhq.com/#prerequisites_ios
|
|
||||||
|
|
133
build.gradle
133
build.gradle
|
@ -1,49 +1,50 @@
|
||||||
import org.apache.tools.ant.filters.ReplaceTokens
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
id 'groovy'
|
||||||
id 'application'
|
id 'application'
|
||||||
id 'org.openjfx.javafxplugin' version '0.0.14'
|
id 'org.openjfx.javafxplugin' version '0.0.14'
|
||||||
id "com.github.johnrengelman.shadow" version "8.1.1"
|
id 'com.google.osdetector' version '1.7.3'
|
||||||
id 'com.gluonhq.gluonfx-gradle-plugin' version '1.0.19'
|
id 'org.beryx.jlink' version '2.26.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
import org.apache.tools.ant.filters.ReplaceTokens
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
maven {
|
|
||||||
url 'https://nexus.gluonhq.com/nexus/content/repositories/releases'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
version = "0.0.1"
|
|
||||||
mainClassName = "biz.nellemann.mdexpl.App"
|
|
||||||
application.mainModule = "biz.nellemann.mdexpl"
|
|
||||||
|
|
||||||
tasks.withType(JavaCompile).configureEach {
|
tasks.withType(JavaCompile).configureEach {
|
||||||
options.encoding = 'UTF-8'
|
options.encoding = 'UTF-8'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
application {
|
||||||
|
mainModule = 'biz.nellemann.mdexpl'
|
||||||
|
mainClass = 'biz.nellemann.mdexpl.App'
|
||||||
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
sourceCompatibility = JavaVersion.VERSION_17
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
targetCompatibility = JavaVersion.VERSION_17
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
modularity.inferModulePath = false
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(17)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is to be able to build with a JDK not bundled with JavaFX */
|
/* This is to be able to build with a JDK not bundled with JavaFX */
|
||||||
javafx {
|
javafx {
|
||||||
version = '21+'
|
version = '17.0.8'
|
||||||
modules = [ 'javafx.controls', 'javafx.fxml' ]
|
modules = [ 'javafx.controls', 'javafx.fxml' ]
|
||||||
// platform("linux-aarch64")
|
// platform("linux-aarch64")
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.gluonhq:charm-glisten:6.2.3'
|
|
||||||
implementation 'com.gluonhq:glisten-afterburner:2.1.0'
|
|
||||||
|
|
||||||
implementation 'org.slf4j:slf4j-api:2.0.7' // Logging API
|
implementation 'org.slf4j:slf4j-api:2.0.7' // Logging API
|
||||||
runtimeOnly 'org.slf4j:slf4j-simple:2.0.7' // Logging API
|
runtimeOnly 'org.slf4j:slf4j-simple:2.0.7' // Logging API
|
||||||
|
|
||||||
|
implementation 'javax.inject:javax.inject:1'
|
||||||
|
implementation 'javax.annotation:javax.annotation-api:1.3.2'
|
||||||
implementation 'org.jmdns:jmdns:3.5.8'
|
implementation 'org.jmdns:jmdns:3.5.8'
|
||||||
|
|
||||||
testImplementation 'org.spockframework:spock-core:2.3-groovy-3.0'
|
testImplementation 'org.spockframework:spock-core:2.3-groovy-3.0'
|
||||||
|
@ -54,64 +55,70 @@ test {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jlink {
|
||||||
|
|
||||||
gluonfx {
|
forceMerge 'slf4j'
|
||||||
verbose = true
|
|
||||||
target = project.hasProperty("target") ? project.getProperty("target") : 'host'
|
|
||||||
//target = 'ios' // Uncomment to enable iOS - see https://docs.gluonhq.com/#prerequisites_ios
|
|
||||||
//target = 'android' // Uncomment to enable Android - see https://docs.gluonhq.com/#prerequisites_android
|
|
||||||
|
|
||||||
attachConfig {
|
options = [
|
||||||
version = "4.0.18"
|
'--strip-debug',
|
||||||
services 'storage', 'display', 'lifecycle', 'statusbar'
|
'--compress', '2',
|
||||||
|
'--no-header-files',
|
||||||
|
'--no-man-pages'
|
||||||
|
]
|
||||||
|
|
||||||
|
launcher {
|
||||||
|
name = 'mDNS-Explorer'
|
||||||
|
noConsole = true
|
||||||
}
|
}
|
||||||
|
|
||||||
reflectionList = [
|
// Only works with Java 14 (and later)
|
||||||
"javafx.fxml.FXMLLoader",
|
jpackage {
|
||||||
"com.gluonhq.charm.glisten.mvc.View",
|
imageName = "mDNS-Explorer"
|
||||||
"com.gluonhq.charm.glisten.control.Icon",
|
skipInstaller = true
|
||||||
"com.gluonhq.charm.glisten.control.DropdownButton",
|
installerName = "mDNS-Explorer-${osdetector.arch}"
|
||||||
"com.gluonhq.charm.glisten.control.BottomNavigation",
|
installerOptions += [
|
||||||
"com.gluonhq.charm.glisten.control.BottomNavigationButton",
|
'--vendor', 'Nellemann Data',
|
||||||
"biz.nellemann.mdexpl.view.AboutPresenter",
|
'--description', 'mDNS Explorer',
|
||||||
"biz.nellemann.mdexpl.view.MainPresenter", "biz.nellemann.mdexpl.model.MainModel",
|
'--copyright', 'Mark Nellemann <mark.nellemann@gmail.com>',
|
||||||
"biz.nellemann.mdexpl.service.DiscoveryService",
|
'--app-version', version
|
||||||
]
|
]
|
||||||
|
|
||||||
compilerArgs = [
|
// Requires: https://wixtoolset.org/ to create installer on Windows
|
||||||
'-Djava.awt.headless=true'
|
if(osdetector.os == 'windows') {
|
||||||
]
|
installerType = 'msi'
|
||||||
|
skipInstaller = false
|
||||||
|
installerOptions += [
|
||||||
|
'--win-per-user-install',
|
||||||
|
'--win-dir-chooser',
|
||||||
|
'--win-menu',
|
||||||
|
// '--icon', 'src/main/resources/icon.ico'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
appIdentifier = 'biz.nellemann.mdexpl'
|
// Requires: xcode-select --install
|
||||||
|
if(osdetector.os == 'osx') {
|
||||||
|
installerType = 'dmg'
|
||||||
|
skipInstaller = false
|
||||||
|
installerOptions += [
|
||||||
|
//'--icon', 'src/main/resources/icon.icns'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
if(osdetector.os == 'linux') {
|
||||||
|
skipInstaller = false
|
||||||
|
installerOptions += [
|
||||||
|
'--linux-menu-group', 'Internet',
|
||||||
|
'--linux-shortcut',
|
||||||
|
'--linux-deb-maintainer', 'mark.nellemann@gmail.com',
|
||||||
|
'--linux-rpm-license-type', 'APACHE-20',
|
||||||
|
'--icon', 'src/main/resources/icon.png',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
release {
|
|
||||||
// Android
|
|
||||||
appLabel = "mDNS Explorer"
|
|
||||||
//versionCode = "1"
|
|
||||||
//versionName = "1.0"
|
|
||||||
//providedKeyStorePath = ""
|
|
||||||
//providedKeyStorePassword = ""
|
|
||||||
//providedKeyAlias = ""
|
|
||||||
//providedKeyAliasPassword = ""
|
|
||||||
// iOS
|
|
||||||
//bundleName = "mDNS Explorer"
|
|
||||||
//bundleVersion = ""
|
|
||||||
//bundleShortVersion = ""
|
|
||||||
//providedSigningIdentity = ""
|
|
||||||
//providedProvisioningProfile = ""
|
|
||||||
//skipSigning = true // Will not run or deploy if not signed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
shadowJar {
|
|
||||||
//archiveBaseName.set("vtd-poc-app")
|
|
||||||
//archiveClassifier.set('all')
|
|
||||||
archiveVersion.set("${System.env.BITBUCKET_BRANCH ?: 'dev' }")
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.build.dependsOn tasks.shadowJar
|
|
||||||
|
|
||||||
tasks.processResources {
|
tasks.processResources {
|
||||||
filesMatching('**/configuration.properties') {
|
filesMatching('**/configuration.properties') {
|
||||||
filter(ReplaceTokens, tokens: [copyright: '2023', version: System.env.BITBUCKET_BUILD_NUMBER ?: 'development' ])
|
filter(ReplaceTokens, tokens: [copyright: '2023', version: System.env.BITBUCKET_BUILD_NUMBER ?: 'development' ])
|
||||||
|
|
BIN
doc/mDNS-Explorer.png
Normal file
BIN
doc/mDNS-Explorer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
12
docker/Dockerfile.deb-txt
Normal file
12
docker/Dockerfile.deb-txt
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
FROM docker.io/debian:stable AS build
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y unzip zip curl dpkg-dev
|
||||||
|
RUN bash -c "curl -s 'https://get.sdkman.io' | bash"
|
||||||
|
RUN bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8-zulu"
|
||||||
|
|
||||||
|
COPY ./src /tmp/prj/src
|
||||||
|
COPY ./gradle /tmp/prj/gradle
|
||||||
|
COPY [ "gradlew", "build.gradle", "gradle.properties", "settings.gradle", "/tmp/prj/" ]
|
||||||
|
|
||||||
|
WORKDIR /tmp/prj
|
||||||
|
RUN bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && ./gradlew --no-daemon clean build jpackage"
|
14
docker/Dockerfile.rpm-txt
Normal file
14
docker/Dockerfile.rpm-txt
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
FROM docker.io/almalinux:8 AS build
|
||||||
|
|
||||||
|
RUN dnf -y install unzip zip rpm-build
|
||||||
|
RUN sh -c "curl -s 'https://get.sdkman.io' | bash"
|
||||||
|
RUN sh -c "source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8-zulu"
|
||||||
|
|
||||||
|
COPY ./src /tmp/prj/src
|
||||||
|
COPY ./gradle /tmp/prj/gradle
|
||||||
|
COPY [ "gradlew", "build.gradle", "gradle.properties", "settings.gradle", "/tmp/prj/" ]
|
||||||
|
|
||||||
|
WORKDIR /tmp/prj
|
||||||
|
RUN sh -c "source $HOME/.sdkman/bin/sdkman-init.sh && ./gradlew --no-daemon clean build jpackage"
|
||||||
|
|
||||||
|
COPY --from=build build/jpackage/* build/
|
1
gradle.properties
Normal file
1
gradle.properties
Normal file
|
@ -0,0 +1 @@
|
||||||
|
version = 0.0.1
|
|
@ -1,9 +1,6 @@
|
||||||
pluginManagement {
|
pluginManagement {
|
||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
maven {
|
|
||||||
url "https://nexus.gluonhq.com/nexus/content/repositories/releases"
|
|
||||||
}
|
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +1,58 @@
|
||||||
package biz.nellemann.mdexpl;
|
package biz.nellemann.mdexpl;
|
||||||
|
|
||||||
import biz.nellemann.mdexpl.view.AppViewManager;
|
|
||||||
import com.gluonhq.charm.glisten.application.AppManager;
|
|
||||||
import com.gluonhq.charm.glisten.visual.Swatch;
|
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
import javafx.stage.WindowEvent;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.awt.Taskbar;
|
||||||
|
import java.awt.Toolkit;
|
||||||
|
import java.awt.Taskbar.Feature;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
|
||||||
public class App extends Application {
|
public class App extends Application {
|
||||||
|
|
||||||
private final AppManager appManager = AppManager.initialize(this::postInit);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() {
|
public void init() {
|
||||||
AppViewManager.registerViewsAndDrawer();
|
Platform.setImplicitExit(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage primaryStage) {
|
public void start(Stage primaryStage) throws IOException {
|
||||||
//System.setProperty(com.gluonhq.attach.util.Constants.ATTACH_DEBUG,"true");
|
|
||||||
appManager.start(primaryStage);
|
// Make all stages close and the app exit when the primary stage is closed
|
||||||
|
primaryStage.setOnCloseRequest(e -> Platform.exit());
|
||||||
|
|
||||||
|
// Set icon on the application bar
|
||||||
|
var appIcon = new Image("/icon.png");
|
||||||
|
primaryStage.getIcons().add(appIcon);
|
||||||
|
|
||||||
|
// Set icon on the taskbar/dock
|
||||||
|
if (Taskbar.isTaskbarSupported()) {
|
||||||
|
var taskbar = Taskbar.getTaskbar();
|
||||||
|
|
||||||
|
if (taskbar.isSupported(Feature.ICON_IMAGE)) {
|
||||||
|
final Toolkit defaultToolkit = Toolkit.getDefaultToolkit();
|
||||||
|
var dockIcon = defaultToolkit.getImage(getClass().getResource("/icon.png"));
|
||||||
|
taskbar.setIconImage(dockIcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource("main.fxml"));
|
||||||
|
Scene scene = new Scene(fxmlLoader.load());
|
||||||
|
primaryStage.setTitle("mDNS Explorer");
|
||||||
|
primaryStage.setScene(scene);
|
||||||
|
primaryStage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void postInit(Scene scene) {
|
@Override
|
||||||
Swatch.GREEN.assignTo(scene);
|
public void stop() {
|
||||||
//scene.getStylesheets().add(App.class.getResource("style.css").toExternalForm());
|
|
||||||
((Stage) scene.getWindow()).getIcons().add(new Image(Objects.requireNonNull(App.class.getResourceAsStream("/icon.png"))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
55
src/main/java/biz/nellemann/mdexpl/MainPresenter.java
Normal file
55
src/main/java/biz/nellemann/mdexpl/MainPresenter.java
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package biz.nellemann.mdexpl;
|
||||||
|
|
||||||
|
import biz.nellemann.mdexpl.model.NetworkService;
|
||||||
|
import biz.nellemann.mdexpl.service.DiscoveryService;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.event.EventHandler;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.ListView;
|
||||||
|
import javafx.scene.layout.BorderPane;
|
||||||
|
import javafx.stage.WindowEvent;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class MainPresenter {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(MainPresenter.class);
|
||||||
|
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private BorderPane view;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ResourceBundle resources;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ListView<NetworkService> listView;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
DiscoveryService discoveryService;
|
||||||
|
|
||||||
|
private final ObservableList<NetworkService> devicesList = FXCollections.observableArrayList();
|
||||||
|
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void initialize() {
|
||||||
|
log.info("initialize()");
|
||||||
|
|
||||||
|
view.addEventFilter(WindowEvent.WINDOW_CLOSE_REQUEST, event -> {
|
||||||
|
log.info("Window closing");
|
||||||
|
discoveryService.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
discoveryService = new DiscoveryService();
|
||||||
|
discoveryService.initialize();
|
||||||
|
discoveryService.setObservableList(devicesList);
|
||||||
|
listView.setItems(devicesList);
|
||||||
|
listView.setCellFactory(p -> new NetworkServiceCell());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -1,40 +1,37 @@
|
||||||
package biz.nellemann.mdexpl;
|
package biz.nellemann.mdexpl;
|
||||||
|
|
||||||
import biz.nellemann.mdexpl.model.NetworkService;
|
import biz.nellemann.mdexpl.model.NetworkService;
|
||||||
import com.gluonhq.attach.util.impl.ClipboardUtils;
|
import javafx.application.Platform;
|
||||||
import com.gluonhq.charm.glisten.control.CharmListCell;
|
import javafx.scene.control.ListCell;
|
||||||
import com.gluonhq.charm.glisten.control.ListTile;
|
|
||||||
import javafx.scene.input.ClipboardContent;
|
import javafx.scene.input.ClipboardContent;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.shape.Rectangle;
|
import javafx.scene.shape.Rectangle;
|
||||||
import javafx.scene.input.Clipboard;
|
import javafx.scene.input.Clipboard;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public class NetworkServiceCell extends CharmListCell<NetworkService> {
|
public class NetworkServiceCell extends ListCell<NetworkService> {
|
||||||
|
|
||||||
private final ListTile tile;
|
|
||||||
private final Rectangle icon;
|
private final Rectangle icon;
|
||||||
|
|
||||||
private final Clipboard clipboard;
|
private final Clipboard clipboard;
|
||||||
private final ClipboardContent clipboardContent;
|
private final ClipboardContent clipboardContent;
|
||||||
|
|
||||||
|
HBox hBox;
|
||||||
|
|
||||||
public NetworkServiceCell() {
|
public NetworkServiceCell() {
|
||||||
clipboard = Clipboard.getSystemClipboard();
|
clipboard = Clipboard.getSystemClipboard();
|
||||||
clipboardContent = new ClipboardContent();
|
clipboardContent = new ClipboardContent();
|
||||||
|
|
||||||
this.tile = new ListTile();
|
|
||||||
//imageView = new ImageView();
|
|
||||||
//imageView.setFitHeight(15);
|
|
||||||
//imageView.setFitWidth(25);
|
|
||||||
//tile.setPrimaryGraphic(imageView);
|
|
||||||
icon = new Rectangle();
|
icon = new Rectangle();
|
||||||
icon.setHeight(25);
|
icon.setHeight(25);
|
||||||
icon.setWidth(25);
|
icon.setWidth(25);
|
||||||
tile.setPrimaryGraphic(icon);
|
this.setGraphic(icon);
|
||||||
tile.setOnMouseClicked(e -> {
|
|
||||||
clipboardContent.putString(itemProperty().get().getUrl());
|
this.setOnMouseClicked(e -> {
|
||||||
clipboard.setContent(clipboardContent);
|
if(itemProperty().get() != null) {
|
||||||
|
clipboardContent.putString(itemProperty().get().getUrl());
|
||||||
|
clipboard.setContent(clipboardContent);
|
||||||
|
};
|
||||||
});
|
});
|
||||||
setText(null);
|
setText(null);
|
||||||
}
|
}
|
||||||
|
@ -43,16 +40,14 @@ public class NetworkServiceCell extends CharmListCell<NetworkService> {
|
||||||
@Override
|
@Override
|
||||||
public void updateItem(NetworkService item, boolean empty) {
|
public void updateItem(NetworkService item, boolean empty) {
|
||||||
super.updateItem(item, empty);
|
super.updateItem(item, empty);
|
||||||
if (item != null && !empty) {
|
if (empty) {
|
||||||
tile.textProperty().setAll(item.getName(),
|
setText(null);
|
||||||
item.getApp() + " (" + item.getSubType() + ") - " + item.getUrl()
|
|
||||||
);
|
|
||||||
icon.setFill(item.getColor());
|
|
||||||
setGraphic(tile);
|
|
||||||
} else {
|
|
||||||
setGraphic(null);
|
setGraphic(null);
|
||||||
|
} else {
|
||||||
|
setText(item.toString());
|
||||||
|
icon.setFill(item.getColor());
|
||||||
|
setGraphic(icon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
package biz.nellemann.mdexpl.model;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
public class MainModel {
|
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(MainModel.class);
|
|
||||||
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void initialize() {
|
|
||||||
log.info("initialize()");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -74,7 +74,7 @@ public class NetworkService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name + " (" + type + "), app=" + app + ", url=" + url;
|
return name + " (" + app + ") " + url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,6 @@ import javafx.scene.paint.Color;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import javax.annotation.PreDestroy;
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import javax.jmdns.JmDNS;
|
import javax.jmdns.JmDNS;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -71,7 +69,6 @@ public class DiscoveryService {
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
log.info("initialize()");
|
log.info("initialize()");
|
||||||
try {
|
try {
|
||||||
|
@ -82,7 +79,6 @@ public class DiscoveryService {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@PreDestroy
|
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
if(jmdns != null) {
|
if(jmdns != null) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
package biz.nellemann.mdexpl.view;
|
|
||||||
|
|
||||||
import com.gluonhq.charm.glisten.application.AppManager;
|
|
||||||
import com.gluonhq.charm.glisten.control.AppBar;
|
|
||||||
import com.gluonhq.charm.glisten.mvc.View;
|
|
||||||
import com.gluonhq.charm.glisten.visual.MaterialDesignIcon;
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import java.util.ResourceBundle;
|
|
||||||
|
|
||||||
public class AboutPresenter {
|
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(AboutPresenter.class);
|
|
||||||
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private ResourceBundle resources;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
View about;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
Label labelInfoWebsite;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
Label labelVersion;
|
|
||||||
|
|
||||||
@Inject String appVersion;
|
|
||||||
@Inject String aboutWebsite;
|
|
||||||
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void initialize() {
|
|
||||||
log.info("initialize()");
|
|
||||||
|
|
||||||
about.showingProperty().addListener((obs, oldValue, newValue) -> {
|
|
||||||
if (newValue) {
|
|
||||||
AppManager appManager = AppManager.getInstance();
|
|
||||||
AppBar appBar = appManager.getAppBar();
|
|
||||||
|
|
||||||
appBar.setNavIcon(MaterialDesignIcon.MENU.button(e ->
|
|
||||||
appManager.getDrawer().open()));
|
|
||||||
|
|
||||||
appBar.setTitleText("About");
|
|
||||||
|
|
||||||
appBar.getActionItems().add(MaterialDesignIcon.CLOSE.button(e -> {
|
|
||||||
appManager.goHome();
|
|
||||||
}));
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
labelVersion.setText(appVersion);
|
|
||||||
labelInfoWebsite.setText(aboutWebsite);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
package biz.nellemann.mdexpl.view;
|
|
||||||
|
|
||||||
import biz.nellemann.mdexpl.App;
|
|
||||||
import com.gluonhq.charm.glisten.afterburner.AppView;
|
|
||||||
import com.gluonhq.charm.glisten.afterburner.AppViewRegistry;
|
|
||||||
import com.gluonhq.charm.glisten.afterburner.Utils;
|
|
||||||
import com.gluonhq.charm.glisten.application.AppManager;
|
|
||||||
import com.gluonhq.charm.glisten.control.Avatar;
|
|
||||||
import com.gluonhq.charm.glisten.control.NavigationDrawer;
|
|
||||||
import com.gluonhq.charm.glisten.visual.MaterialDesignIcon;
|
|
||||||
import javafx.scene.image.Image;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import static com.gluonhq.charm.glisten.afterburner.AppView.Flag.*;
|
|
||||||
|
|
||||||
public class AppViewManager {
|
|
||||||
|
|
||||||
public static final AppViewRegistry REGISTRY = new AppViewRegistry();
|
|
||||||
|
|
||||||
// Shown in drawer
|
|
||||||
public static final AppView PRIMARY_VIEW = view("Explorer", MainPresenter.class, MaterialDesignIcon.HOME, SHOW_IN_DRAWER, HOME_VIEW, SKIP_VIEW_STACK);
|
|
||||||
public static final AppView ABOUT_VIEW = view("About", AboutPresenter.class, MaterialDesignIcon.HELP, SHOW_IN_DRAWER);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static AppView view(String title, Class<?> presenterClass, MaterialDesignIcon menuIcon, AppView.Flag... flags ) {
|
|
||||||
return REGISTRY.createView(name(presenterClass), title, presenterClass, menuIcon, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String name(Class<?> presenterClass) {
|
|
||||||
return presenterClass.getSimpleName().toUpperCase(Locale.ROOT).replace("PRESENTER", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void registerViewsAndDrawer() {
|
|
||||||
for (AppView view : REGISTRY.getViews()) {
|
|
||||||
view.registerView();
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigationDrawer.Header header = new NavigationDrawer.Header("mDNS Explorer",
|
|
||||||
"Multicast DNS Explorer",
|
|
||||||
new Avatar(48, new Image(Objects.requireNonNull(App.class.getResourceAsStream("/icon.png"))))
|
|
||||||
);
|
|
||||||
|
|
||||||
Utils.buildDrawer(AppManager.getInstance().getDrawer(), header, REGISTRY.getViews());
|
|
||||||
|
|
||||||
//NavigationDrawer.Footer footer = new NavigationDrawer.Footer("Bla");
|
|
||||||
//AppManager.getInstance().getDrawer().setFooter(footer);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
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.service.DiscoveryService;
|
|
||||||
import com.gluonhq.charm.glisten.application.AppManager;
|
|
||||||
import com.gluonhq.charm.glisten.control.AppBar;
|
|
||||||
import com.gluonhq.charm.glisten.control.CharmListView;
|
|
||||||
import com.gluonhq.charm.glisten.control.LifecycleEvent;
|
|
||||||
import com.gluonhq.charm.glisten.mvc.View;
|
|
||||||
import com.gluonhq.charm.glisten.visual.MaterialDesignIcon;
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.scene.input.MouseEvent;
|
|
||||||
import javafx.scene.input.TouchEvent;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import java.util.ResourceBundle;
|
|
||||||
|
|
||||||
public class MainPresenter {
|
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(MainPresenter.class);
|
|
||||||
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private View main;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
private MainModel model;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
private DiscoveryService discoveryService;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private ResourceBundle resources;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private CharmListView<NetworkService, Integer> charmListView;
|
|
||||||
|
|
||||||
private final ObservableList<NetworkService> devicesList = FXCollections.observableArrayList();
|
|
||||||
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void initialize() {
|
|
||||||
|
|
||||||
log.info("initialize()");
|
|
||||||
|
|
||||||
main.showingProperty().addListener((obs, oldValue, newValue) -> {
|
|
||||||
if (newValue) {
|
|
||||||
AppManager appManager = AppManager.getInstance();
|
|
||||||
AppBar appBar = appManager.getAppBar();
|
|
||||||
|
|
||||||
appBar.setNavIcon(MaterialDesignIcon.MENU.button(e ->
|
|
||||||
appManager.getDrawer().open()));
|
|
||||||
|
|
||||||
appBar.setTitleText("mDNS Explorer");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
discoveryService.setObservableList(devicesList);
|
|
||||||
charmListView.setItems(devicesList);
|
|
||||||
charmListView.setCellFactory(p -> new NetworkServiceCell());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void onEventShowing(LifecycleEvent lifecycleEvent) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onEventHiding(LifecycleEvent lifecycleEvent) {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
12
src/main/java/module-info.java
Normal file
12
src/main/java/module-info.java
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
module biz.nellemann.mdexpl {
|
||||||
|
requires javafx.controls;
|
||||||
|
requires javafx.fxml;
|
||||||
|
requires javax.jmdns;
|
||||||
|
requires org.slf4j;
|
||||||
|
requires javax.inject;
|
||||||
|
requires java.annotation;
|
||||||
|
requires java.desktop;
|
||||||
|
|
||||||
|
opens biz.nellemann.mdexpl to javafx.fxml;
|
||||||
|
exports biz.nellemann.mdexpl;
|
||||||
|
}
|
12
src/main/resources/biz/nellemann/mdexpl/main.fxml
Normal file
12
src/main/resources/biz/nellemann/mdexpl/main.fxml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.ListView?>
|
||||||
|
<?import javafx.scene.layout.BorderPane?>
|
||||||
|
|
||||||
|
<BorderPane fx:id="view" prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/17.0.8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="biz.nellemann.mdexpl.MainPresenter">
|
||||||
|
|
||||||
|
<center>
|
||||||
|
<ListView fx:id="listView" BorderPane.alignment="CENTER" />
|
||||||
|
</center>
|
||||||
|
|
||||||
|
</BorderPane>
|
|
@ -1,45 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
<?import com.gluonhq.charm.glisten.control.Icon?>
|
|
||||||
<?import com.gluonhq.charm.glisten.mvc.View?>
|
|
||||||
<?import javafx.geometry.Insets?>
|
|
||||||
<?import javafx.scene.control.Label?>
|
|
||||||
<?import javafx.scene.layout.HBox?>
|
|
||||||
<?import javafx.scene.layout.VBox?>
|
|
||||||
<?import javafx.scene.text.Font?>
|
|
||||||
|
|
||||||
<View fx:id="about" xmlns="http://javafx.com/javafx/20.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="biz.nellemann.mdexpl.view.AboutPresenter">
|
|
||||||
|
|
||||||
<VBox alignment="CENTER">
|
|
||||||
<Label alignment="CENTER" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" text="mDNS Explorer">
|
|
||||||
<VBox.margin>
|
|
||||||
<Insets bottom="5.0" />
|
|
||||||
</VBox.margin>
|
|
||||||
<font>
|
|
||||||
<Font size="36.0" />
|
|
||||||
</font>
|
|
||||||
</Label>
|
|
||||||
<Label fx:id="labelVersion" alignment="CENTER" layoutX="10.0" layoutY="234.0" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" text="version" textAlignment="CENTER">
|
|
||||||
<VBox.margin>
|
|
||||||
<Insets bottom="5.0" top="5.0" />
|
|
||||||
</VBox.margin>
|
|
||||||
</Label>
|
|
||||||
|
|
||||||
<HBox alignment="CENTER" spacing="5.0">
|
|
||||||
<children>
|
|
||||||
|
|
||||||
<Icon content="INFO" />
|
|
||||||
<Label fx:id="labelInfoWebsite" alignment="CENTER" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" textAlignment="CENTER">
|
|
||||||
</Label>
|
|
||||||
</children>
|
|
||||||
<VBox.margin>
|
|
||||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
|
||||||
</VBox.margin>
|
|
||||||
</HBox>
|
|
||||||
|
|
||||||
</VBox>
|
|
||||||
<padding>
|
|
||||||
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
|
||||||
</padding>
|
|
||||||
|
|
||||||
</View>
|
|
|
@ -1,16 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
<?import com.gluonhq.charm.glisten.control.BottomNavigation?>
|
|
||||||
<?import com.gluonhq.charm.glisten.control.BottomNavigationButton?>
|
|
||||||
<?import com.gluonhq.charm.glisten.control.CharmListView?>
|
|
||||||
<?import com.gluonhq.charm.glisten.control.Icon?>
|
|
||||||
<?import com.gluonhq.charm.glisten.mvc.View?>
|
|
||||||
<?import javafx.scene.layout.BorderPane?>
|
|
||||||
|
|
||||||
<View fx:id="main" onHiding="#onEventHiding" onShowing="#onEventShowing" xmlns="http://javafx.com/javafx/20.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="biz.nellemann.mdexpl.view.MainPresenter">
|
|
||||||
|
|
||||||
<center>
|
|
||||||
<CharmListView fx:id="charmListView" BorderPane.alignment="CENTER" />
|
|
||||||
</center>
|
|
||||||
|
|
||||||
</View>
|
|
Loading…
Reference in a new issue