mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-10 23:57:25 -05:00
Compare commits
482 Commits
v0.3.3-bet
...
v0.5.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
775ae00861 | ||
|
|
1b80f5f1b3 | ||
|
|
f9a7bbeddf | ||
|
|
58a0264565 | ||
|
|
6d2af95e9a | ||
|
|
bf46b19dc9 | ||
|
|
7749fd10f1 | ||
|
|
90bc321aca | ||
|
|
bad4fae1b8 | ||
|
|
c3fc435fdc | ||
|
|
2ab0c78728 | ||
|
|
187989f6b8 | ||
|
|
3b83412bcb | ||
|
|
968ea458da | ||
|
|
a9f4fbbdd4 | ||
|
|
6b3b45e9af | ||
|
|
2355dfce07 | ||
|
|
0e8c9591e4 | ||
|
|
54e4771f36 | ||
|
|
b2eb245feb | ||
|
|
e826314d06 | ||
|
|
0eec2e7ab8 | ||
|
|
5e9c239ea3 | ||
|
|
1fd9b0fbcb | ||
|
|
8413343e76 | ||
|
|
2a0427c559 | ||
|
|
5d9d232585 | ||
|
|
f0f0329f9b | ||
|
|
2a45849e6d | ||
|
|
aa89079e28 | ||
|
|
31b9ee5910 | ||
|
|
6a54fe101c | ||
|
|
87b3d34024 | ||
|
|
b8bbe9264a | ||
|
|
0c7dd192b0 | ||
|
|
8b487d79e4 | ||
|
|
cb83a8e870 | ||
|
|
0bc11a07cf | ||
|
|
560cbad5d7 | ||
|
|
87e9bf8749 | ||
|
|
770f58c668 | ||
|
|
4b806ecfde | ||
|
|
7303e06943 | ||
|
|
846b249fb3 | ||
|
|
67f23494e2 | ||
|
|
d7efb627d7 | ||
|
|
08bc0b9963 | ||
|
|
d1ed46860c | ||
|
|
38f694d262 | ||
|
|
75ef3217ac | ||
|
|
2847dff64a | ||
|
|
f0d9ef99ea | ||
|
|
67702096d7 | ||
|
|
88f124af9d | ||
|
|
d34460c4e3 | ||
|
|
f4f3db7436 | ||
|
|
3ebfa90ca1 | ||
|
|
733fdd0d7a | ||
|
|
09893605eb | ||
|
|
57468ef932 | ||
|
|
c6afb1c133 | ||
|
|
05c0b51fde | ||
|
|
de8cfa24f9 | ||
|
|
fe714c69a7 | ||
|
|
ac219cf2ba | ||
|
|
77b1102100 | ||
|
|
5117e6cbe8 | ||
|
|
61c4e13c3e | ||
|
|
f1190b7be3 | ||
|
|
49e7087b9b | ||
|
|
c397b5a601 | ||
|
|
c809dcc609 | ||
|
|
7eec82deee | ||
|
|
b061db1189 | ||
|
|
8715dd2d6b | ||
|
|
63b0aaaa7f | ||
|
|
d20dd1af8c | ||
|
|
e1f32d3d0b | ||
|
|
eaa3433034 | ||
|
|
838016c89f | ||
|
|
030a88f5a3 | ||
|
|
de3ba5529a | ||
|
|
7962758e05 | ||
|
|
0f9d1c0792 | ||
|
|
48be38a2d8 | ||
|
|
207893e5a8 | ||
|
|
e07d6fa854 | ||
|
|
e26698d0d8 | ||
|
|
a8837c8e2a | ||
|
|
7395f02fb8 | ||
|
|
706ada2940 | ||
|
|
d957846ab0 | ||
|
|
bbe90ca74b | ||
|
|
96611fa2c2 | ||
|
|
c94edf05ef | ||
|
|
74adb6f30c | ||
|
|
421abe5768 | ||
|
|
1d003c561e | ||
|
|
b22177ee17 | ||
|
|
a1e8c761ef | ||
|
|
ad194d1bab | ||
|
|
2bb5082943 | ||
|
|
2c3648f8c2 | ||
|
|
3d816c3570 | ||
|
|
e1194d26bd | ||
|
|
8167e9e9f2 | ||
|
|
3b357c4d40 | ||
|
|
89cb54676d | ||
|
|
73c251ff4d | ||
|
|
0d51b39604 | ||
|
|
af0dbf1566 | ||
|
|
01146a5624 | ||
|
|
316cdb4068 | ||
|
|
91cae70fa1 | ||
|
|
2723926f83 | ||
|
|
2aff9ee817 | ||
|
|
c87e6553dc | ||
|
|
5d580544e7 | ||
|
|
08ea0e0f18 | ||
|
|
c8b441dbd4 | ||
|
|
1aa22789e5 | ||
|
|
cff5494db4 | ||
|
|
9bd6507342 | ||
|
|
379461e649 | ||
|
|
cc52ae5bd7 | ||
|
|
39f6d6c831 | ||
|
|
649258bd2b | ||
|
|
de9431f649 | ||
|
|
18f9e38090 | ||
|
|
2e37f1e687 | ||
|
|
5e8f2d0523 | ||
|
|
9fb6c33319 | ||
|
|
fdecac4230 | ||
|
|
85c27a6cca | ||
|
|
57f56d31a2 | ||
|
|
df25675d3f | ||
|
|
277a84702a | ||
|
|
c651296378 | ||
|
|
866d25e86a | ||
|
|
5e62f1b889 | ||
|
|
e9d524ad14 | ||
|
|
158c8b4b8a | ||
|
|
41df207bb9 | ||
|
|
692ffabe82 | ||
|
|
944ad5a571 | ||
|
|
5ca70d3940 | ||
|
|
904752e3dd | ||
|
|
cf61798466 | ||
|
|
fd72f459f9 | ||
|
|
1bc462b300 | ||
|
|
1c6f91f101 | ||
|
|
a7c28f066a | ||
|
|
604c8ab6e5 | ||
|
|
23a3ee39cc | ||
|
|
fa16db129b | ||
|
|
de249c448b | ||
|
|
e1bc56c500 | ||
|
|
d1d115d11f | ||
|
|
aca41b4f6e | ||
|
|
1e3ae996ac | ||
|
|
b3b2e74688 | ||
|
|
44237bec5c | ||
|
|
53b44f44e3 | ||
|
|
9af7a184ad | ||
|
|
9c8d87ec64 | ||
|
|
4c34968d28 | ||
|
|
2d71f5173a | ||
|
|
2e942113c6 | ||
|
|
e20540ccc6 | ||
|
|
8d7e0e9f46 | ||
|
|
cc5d94f58d | ||
|
|
26197e433c | ||
|
|
f54b4dfd0e | ||
|
|
085ba52294 | ||
|
|
b8c24680a5 | ||
|
|
c658582157 | ||
|
|
83e518d503 | ||
|
|
2163d0f822 | ||
|
|
34c920eede | ||
|
|
35b0b5ef9b | ||
|
|
08672c33bd | ||
|
|
e2651d6c7c | ||
|
|
851a4e12d2 | ||
|
|
b7c66ec844 | ||
|
|
7522a3a820 | ||
|
|
6a0402d1cf | ||
|
|
5309af84f7 | ||
|
|
8d8d77fa11 | ||
|
|
284cf07e97 | ||
|
|
16414c7585 | ||
|
|
70c9eed0bf | ||
|
|
f49b1fef17 | ||
|
|
3fe070987a | ||
|
|
00d5610213 | ||
|
|
d3c1a605df | ||
|
|
85df151a96 | ||
|
|
8915797aac | ||
|
|
03a5d71a95 | ||
|
|
4158d4ac10 | ||
|
|
f934676bd0 | ||
|
|
d5b802e794 | ||
|
|
139227e46d | ||
|
|
2394d51a51 | ||
|
|
d9fa2bea5b | ||
|
|
0cb360eeed | ||
|
|
ea472f873f | ||
|
|
8cbdbd527e | ||
|
|
ff0d893bd9 | ||
|
|
7b924d0486 | ||
|
|
712eb07f4b | ||
|
|
d5c3ecc7df | ||
|
|
c4bf089667 | ||
|
|
378b03e06e | ||
|
|
34235382fb | ||
|
|
c05aff0768 | ||
|
|
f0c3e0fd1a | ||
|
|
d601cbda0e | ||
|
|
2653dc6b38 | ||
|
|
48cbbfc656 | ||
|
|
d6892b9203 | ||
|
|
59f0844fcc | ||
|
|
43b285a50a | ||
|
|
4e92a6e350 | ||
|
|
25d387f3cf | ||
|
|
52e3d9d20d | ||
|
|
1e37a63d34 | ||
|
|
e51f485a2e | ||
|
|
9378254a38 | ||
|
|
4ab37735e8 | ||
|
|
85ca66f656 | ||
|
|
d716b0be1c | ||
|
|
70444c1337 | ||
|
|
d90e149021 | ||
|
|
7510664e00 | ||
|
|
5481fb7d4d | ||
|
|
3a39ae76ba | ||
|
|
08ea5ab7cb | ||
|
|
3fa7401a2b | ||
|
|
fce10960fb | ||
|
|
10b6a08624 | ||
|
|
5960343637 | ||
|
|
347a71f097 | ||
|
|
5b5a1431b4 | ||
|
|
efd811407e | ||
|
|
b67d4d92dc | ||
|
|
735fca16d6 | ||
|
|
72fe72c833 | ||
|
|
4a4d1d4b2e | ||
|
|
2ed7074c0d | ||
|
|
b5a895c266 | ||
|
|
b57d7b5c0e | ||
|
|
cb12d314d0 | ||
|
|
eb79d9e037 | ||
|
|
d95c78c0f1 | ||
|
|
aee5ece8d2 | ||
|
|
ba358a7d48 | ||
|
|
09b209496b | ||
|
|
cc209683bd | ||
|
|
2771c28dba | ||
|
|
d752917073 | ||
|
|
28bc02e172 | ||
|
|
0f6044d48b | ||
|
|
58ecb83e2c | ||
|
|
9e1de922e4 | ||
|
|
84d0b827d8 | ||
|
|
a17132be0d | ||
|
|
dae68515a5 | ||
|
|
0aaec8ff54 | ||
|
|
001883edc2 | ||
|
|
335fb51456 | ||
|
|
e94b6ae172 | ||
|
|
785125da4f | ||
|
|
81b8f5a423 | ||
|
|
bf186a7408 | ||
|
|
43fc326f93 | ||
|
|
7cdbb75b42 | ||
|
|
96a56cab68 | ||
|
|
29d4b36cf7 | ||
|
|
b15a3e59a0 | ||
|
|
9950e46e5a | ||
|
|
570321da3e | ||
|
|
bb0bcd3198 | ||
|
|
cb54850f99 | ||
|
|
5bb8c46bd5 | ||
|
|
2a3fd2c5a3 | ||
|
|
d712feb45a | ||
|
|
fbb24a4c05 | ||
|
|
3992f8178c | ||
|
|
8642def77e | ||
|
|
38e6de6cb2 | ||
|
|
994e78f42b | ||
|
|
b8a46af836 | ||
|
|
1c6d19959a | ||
|
|
50bc3088b7 | ||
|
|
09356848eb | ||
|
|
316bfb19c2 | ||
|
|
c045c8f16d | ||
|
|
8946d789ff | ||
|
|
19399d9138 | ||
|
|
9acdf22b91 | ||
|
|
f963d821a1 | ||
|
|
ade0cef50b | ||
|
|
2044c2a2de | ||
|
|
1c312a50b6 | ||
|
|
54f6bd09e2 | ||
|
|
5f8f2ec330 | ||
|
|
d7e018f66e | ||
|
|
c2a232fdee | ||
|
|
c5d2dcd0cf | ||
|
|
8e4e4129ca | ||
|
|
9176ca610f | ||
|
|
97b31e4d9b | ||
|
|
a3e5520d3e | ||
|
|
8c456972cc | ||
|
|
35d81ab31d | ||
|
|
b90b06e600 | ||
|
|
04144ca677 | ||
|
|
d3c7a50918 | ||
|
|
0fd29fa35a | ||
|
|
c00b173064 | ||
|
|
2718367259 | ||
|
|
3f0c13096a | ||
|
|
71ba4adbd7 | ||
|
|
3908e29a2e | ||
|
|
0c47efea03 | ||
|
|
5c0d97d942 | ||
|
|
61ae0d2d83 | ||
|
|
68f2a8da2c | ||
|
|
0d69f11e65 | ||
|
|
b3d5daea8b | ||
|
|
fe34623c4a | ||
|
|
353747cecc | ||
|
|
a8c649ef64 | ||
|
|
8cf50e629e | ||
|
|
20a8f2bb3c | ||
|
|
8c237be682 | ||
|
|
2d5271b849 | ||
|
|
609a1e86c7 | ||
|
|
821e719015 | ||
|
|
104c4f72be | ||
|
|
9e7fa7618d | ||
|
|
ee09520181 | ||
|
|
fa12b840db | ||
|
|
183d7d6d19 | ||
|
|
e2550869ce | ||
|
|
e88944e53d | ||
|
|
0051a1d381 | ||
|
|
f7a873dd0c | ||
|
|
ad560db5ea | ||
|
|
6f1ce79461 | ||
|
|
df1bc1031e | ||
|
|
a621a31aa9 | ||
|
|
19fa256c60 | ||
|
|
7a830077bd | ||
|
|
f4793f3cfd | ||
|
|
2cd3b73633 | ||
|
|
18f50485d3 | ||
|
|
b410cf0dfe | ||
|
|
1fdcb623a6 | ||
|
|
9dda1f95a1 | ||
|
|
a5c8552937 | ||
|
|
e4209c8e4f | ||
|
|
a898be265f | ||
|
|
c13315f731 | ||
|
|
341e740e77 | ||
|
|
cbc228d798 | ||
|
|
488b2e02ce | ||
|
|
e395d737f8 | ||
|
|
7a9f705517 | ||
|
|
dfaf506e23 | ||
|
|
81c9a352a8 | ||
|
|
e6937764c3 | ||
|
|
5bfc5bf456 | ||
|
|
ce83273393 | ||
|
|
95aa9f8803 | ||
|
|
e971ad4582 | ||
|
|
ff9395e4b9 | ||
|
|
d023da9ead | ||
|
|
60ffb18cd9 | ||
|
|
c36f6901af | ||
|
|
bfaacb309d | ||
|
|
c3b405a752 | ||
|
|
3619b480f8 | ||
|
|
830b2a2d34 | ||
|
|
78bb5ffe43 | ||
|
|
5dd1664844 | ||
|
|
8868c1f954 | ||
|
|
646bd78037 | ||
|
|
5af4cb8706 | ||
|
|
45a6c5a0b6 | ||
|
|
b563f2d11b | ||
|
|
148ddd0bd5 | ||
|
|
a0630102e6 | ||
|
|
3d1ac30646 | ||
|
|
dda37a1198 | ||
|
|
eaf7c7d11e | ||
|
|
80c5ad1bc8 | ||
|
|
956bf2d76a | ||
|
|
1406d5576a | ||
|
|
03e39f3343 | ||
|
|
28f351287c | ||
|
|
54227703a2 | ||
|
|
bb078de0a9 | ||
|
|
ec30cd6305 | ||
|
|
5a88ac0200 | ||
|
|
05eea44f7e | ||
|
|
030fc1617c | ||
|
|
594a77a9d0 | ||
|
|
c2fcc9e85d | ||
|
|
28da8b2fa4 | ||
|
|
1c06c4382f | ||
|
|
28cb9d9407 | ||
|
|
96d9d6f750 | ||
|
|
85691edacb | ||
|
|
f2f2c92afe | ||
|
|
fcd3c2fd46 | ||
|
|
e78d2339ff | ||
|
|
673c8a2423 | ||
|
|
5a03a0038c | ||
|
|
e7f7d27a1f | ||
|
|
0cf964fa98 | ||
|
|
b6f282a85a | ||
|
|
32ab0598e8 | ||
|
|
5ae3cb38e1 | ||
|
|
78205bb1bf | ||
|
|
3d9b38bef5 | ||
|
|
d5b377b1af | ||
|
|
a7a0c26b38 | ||
|
|
d4761be00a | ||
|
|
05fa5c25fd | ||
|
|
69e9bed2af | ||
|
|
d2e7f008c8 | ||
|
|
ddad4b6fb0 | ||
|
|
b65dd7e414 | ||
|
|
b076587f30 | ||
|
|
336669f1d5 | ||
|
|
46cf7f4193 | ||
|
|
e9aeb09011 | ||
|
|
236df8f3f4 | ||
|
|
667c50174d | ||
|
|
1c71a37755 | ||
|
|
318e6f3586 | ||
|
|
f38184fd83 | ||
|
|
d2085052f6 | ||
|
|
441b595325 | ||
|
|
57fab2b7ca | ||
|
|
bf65e10286 | ||
|
|
a12aaf7cf3 | ||
|
|
b3e4bd6a9c | ||
|
|
5281679b7f | ||
|
|
a581da56a8 | ||
|
|
0181926fe3 | ||
|
|
2c3be02939 | ||
|
|
a00ec42230 | ||
|
|
14a3532030 | ||
|
|
3be65d5e9a | ||
|
|
c21f3fca23 | ||
|
|
99d81d247a | ||
|
|
27461df7f0 | ||
|
|
9983d2119d | ||
|
|
9e275b7d62 | ||
|
|
f461ed9cdb | ||
|
|
7c6b7494b8 | ||
|
|
ab31b40124 | ||
|
|
ac686f5f64 | ||
|
|
f7cd798f07 | ||
|
|
41d307cdad | ||
|
|
afae1654d5 | ||
|
|
ffc92a8f0e | ||
|
|
6d9e129087 | ||
|
|
0e1da8fb3b | ||
|
|
7d5371db4c | ||
|
|
131290fcd2 | ||
|
|
e2fe06638b | ||
|
|
17c16e16ee | ||
|
|
cd4eaedfcc | ||
|
|
fd748fec79 | ||
|
|
14b844f0bd | ||
|
|
99ef002d07 | ||
|
|
e868428c88 | ||
|
|
d2542d2560 | ||
|
|
9ca87299d5 |
34
.github/workflows/gradle.yml
vendored
Normal file
34
.github/workflows/gradle.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
||||
|
||||
name: Java CI with Gradle
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'liberica'
|
||||
java-package: 'jdk+fx'
|
||||
- name: Build with Gradle
|
||||
uses: gradle/gradle-build-action@937999e9cc2425eddc7fd62d1053baf041147db7
|
||||
with:
|
||||
arguments: clean build
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -17,7 +17,7 @@ out/
|
||||
*.class
|
||||
*.log
|
||||
# Package Files #
|
||||
*.jar
|
||||
#*.jar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
9
.idea/compiler.xml
generated
9
.idea/compiler.xml
generated
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel>
|
||||
<module name="sdr-trunk_main" target="1.8" />
|
||||
<module name="sdr-trunk_test" target="1.8" />
|
||||
</bytecodeTargetLevel>
|
||||
</component>
|
||||
</project>
|
||||
36
.idea/dictionaries/denny.xml
generated
Normal file
36
.idea/dictionaries/denny.xml
generated
Normal file
@@ -0,0 +1,36 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="denny">
|
||||
<words>
|
||||
<w>bahroo</w>
|
||||
<w>basebanded</w>
|
||||
<w>broadcastify</w>
|
||||
<w>correlator</w>
|
||||
<w>deinterleave</w>
|
||||
<w>descrambled</w>
|
||||
<w>dinf</w>
|
||||
<w>esnid</w>
|
||||
<w>fleetsync</w>
|
||||
<w>heterdyned</w>
|
||||
<w>heterodynes</w>
|
||||
<w>icecast</w>
|
||||
<w>ident</w>
|
||||
<w>nutall</w>
|
||||
<w>overflowable</w>
|
||||
<w>polyphase</w>
|
||||
<w>qpsk</w>
|
||||
<w>remez</w>
|
||||
<w>resampler</w>
|
||||
<w>samplerate</w>
|
||||
<w>sdrtrunk</w>
|
||||
<w>sheirer</w>
|
||||
<w>shoutcast</w>
|
||||
<w>streamable</w>
|
||||
<w>talkgroup</w>
|
||||
<w>talkgroups</w>
|
||||
<w>tgid</w>
|
||||
<w>ultravox</w>
|
||||
<w>wacn</w>
|
||||
<w>xtea</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
||||
3
.idea/misc.xml
generated
3
.idea/misc.xml
generated
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="liberica-17.0.2-fx" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
10
.idea/modules.xml
generated
10
.idea/modules.xml
generated
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/sdr-trunk.iml" filepath="$PROJECT_DIR$/.idea/modules/sdr-trunk.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/sdr-trunk_main.iml" filepath="$PROJECT_DIR$/.idea/modules/sdr-trunk_main.iml" group="sdr-trunk" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/sdr-trunk_test.iml" filepath="$PROJECT_DIR$/.idea/modules/sdr-trunk_test.iml" group="sdr-trunk" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
2
.idea/vcs.xml
generated
2
.idea/vcs.xml
generated
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
21
CHANGELOG
21
CHANGELOG
@@ -1,3 +1,24 @@
|
||||
0.4.0 20200404
|
||||
- Release includes embedded Java 14 & JavaFX 13 runtime
|
||||
- Adds Polyphase Channelizer with 2x channel oversampling and redesigned source management
|
||||
- Adds P25 Phase 2 decoder
|
||||
- Adds P25 radio identifier aliasing
|
||||
- JMBE library 1.0.4 with support for AMBE tone metadata
|
||||
- New JavaFX User Preferences dialog
|
||||
- HackRF tuner enhanced (sample rates and saturation issues)
|
||||
- Adds support for Airspy mini tuner
|
||||
- Redesigned audio sub-system that uses AudioSegments
|
||||
- USB tuner bus saturation management/monitoring
|
||||
- Bitstream (.bits) recording
|
||||
- MP3 Audio Recording
|
||||
- Numerous bug fixes
|
||||
|
||||
0.3.4 20180418
|
||||
- New P25 C4FM decoder that uses Differential QPSK decoder instead of FM demodulation with 4FSK.
|
||||
- Adds channel auto-start and ordering capability with startup dialog allowing user to invoke or bypass channel
|
||||
auto-start. Provides user with more control over startup configuration.
|
||||
- Adds preferred tuner attribute to channel configuration allowing user to select a preferred tuner for the channel
|
||||
|
||||
0.3.3 20171225
|
||||
- Changes build system from ant to gradle. Thanks to: Roman Tsypuk https://github.com/tsypuk
|
||||
- Adds LIST and ID3 metadata to wav audio recordings
|
||||
|
||||
107
README.md
107
README.md
@@ -1,54 +1,93 @@
|
||||

|
||||
|
||||
# sdrtrunk
|
||||
A cross-platform java application for decoding, monitoring, recording and streaming trunked mobile and related radio protocols using Software Defined Radios (SDR).
|
||||
|
||||
* [Getting Started](https://github.com/DSheirer/sdrtrunk/wiki/GettingStarted)
|
||||
* [User's Manual Version 0.3.0](https://github.com/DSheirer/sdrtrunk/wiki/UserManual_Version0.3.0)
|
||||
* [Getting Started](https://github.com/DSheirer/sdrtrunk/wiki/GettingStarted_V0.3.0)
|
||||
* [User's Manual Version 0.3.0 and 0.4.0](https://github.com/DSheirer/sdrtrunk/wiki/UserManual_V0.3.0)
|
||||
* [Playlist Editor User Manual Version 0.5.0](https://github.com/DSheirer/sdrtrunk/wiki/Playlist-Editor)
|
||||
* [Download](https://github.com/DSheirer/sdrtrunk/releases)
|
||||
* [Support](https://groups.google.com/forum/#!forum/sdrtrunk)
|
||||
* [Support Group](https://groups.google.com/forum/#!forum/sdrtrunk)
|
||||
* [Discord Support Channel](https://discord.gg/HJQaKYE)
|
||||
|
||||

|
||||

|
||||
**Figure 1:** sdrtrunk **Version 0.3.0** Application Screenshot
|
||||
|
||||
# Instructions:
|
||||
# End User Instructions:
|
||||
|
||||
If you simply want to download and run the program, please follow these instructions.
|
||||
|
||||
## Download the latest sdrtrunk release for your operating system
|
||||
|
||||
All release versions of sdrtrunk are available from the [releases](https://github.com/DSheirer/sdrtrunk/releases) tab.
|
||||
|
||||
* **(alpha)** These versions are under development feature previews and likely to contain bugs and unexpected behavior.
|
||||
* **(beta)** These versions are currently being tested for bugs and functionality prior to final release.
|
||||
* **(final)** These versions have been tested and are the current release version.
|
||||
|
||||
## Unzip the release
|
||||
|
||||
Use 7-zip or any zip utility to unzip the release file
|
||||
|
||||
## Start the application
|
||||
|
||||
Once unzipped, open a command prompt to where you unzipped the release. Change to the **/bin** directory and use the launch script to start the application:
|
||||
* **Windows** sdr-trunk.bat
|
||||
* **Linux/OSX** ./sdr-trunk
|
||||
|
||||
## Optional - P25/DMR Audio
|
||||
If you're using sdrtrunk with a P25 or DMR radio system, you'll need to compile and include the [JMBE](https://github.com/DSheirer/sdrtrunk/wiki/JMBE) digital audio library.
|
||||
|
||||
In Version 0.5.0 and higher, the SDRTrunk application has support for building the JMBE library. On the menu, choose **View >> User Preferences** and then click the **Decoder >> JMBE Audio Library** tab.
|
||||
|
||||
For versions prior to Version 0.5.0, the [JMBE](https://github.com/DSheirer/sdrtrunk/wiki/JMBE) wiki page contains instructions for downloading the JMBE audio library source code and compiling the JMBE library. Once you have compiled the library, launch the sdrtrunk application. From the menu bar, choose **View >> Preferences**. In the **JMBE Audio Codec** section, update the path to where your compiled JMBE library is located. Any channels that are started after you set the path will be able to produce P25 audio.
|
||||
|
||||
## Minimum System Requirements
|
||||
* **Operating System:** Windows (~~32 or~~ 64-bit), Linux (~~32 or~~ 64-bit) or Mac/Linux (64-bit, 10.14 or higher)
|
||||
* **CPU:** 4-core
|
||||
* **RAM:** 8GB or more (preferred). Depending on usage, 4GB may be sufficient.
|
||||
|
||||
# Developer Instructions:
|
||||
|
||||
If you're interested in modifying and/or compiling the source code, please follow these instructions to use gradle to compile the code.
|
||||
|
||||
## Build the project
|
||||
Project uses gradle as build system. You can build it with locally installed gradle or if you do not have/want install gradle you can use preconfigured linux/windows wrapper.
|
||||
### Build with locally installed gradle 4.3.1
|
||||
sdrtrunk uses the gradle build system. This requires **OpenJDK 17** or higher installed on your local computer.
|
||||
|
||||
### OpenJDK
|
||||
There are many vendors producing OpenJDK distributions. A few that I use are:
|
||||
* [Azul Zulu OpenJDK](https://www.azul.com/downloads/?package=jdk)
|
||||
* [Bellsoft Liberica OpenJDK](https://bell-sw.com/pages/downloads/#/java-17-lts%20/%20current)
|
||||
* [SDKMAN](https://sdkman.io/) - Linux Only
|
||||
|
||||
Use the gradle wrapper to build the source code:
|
||||
|
||||
### Linux
|
||||
```
|
||||
gradle clean buildSdr
|
||||
./gradlew clean build
|
||||
```
|
||||
### Build with preconfigured wrapper for linux/windows
|
||||
Linux
|
||||
### Windows
|
||||
```
|
||||
./gradlew clean buildSdr
|
||||
gradlew.bat clean build
|
||||
```
|
||||
Windows
|
||||
|
||||
The **/build/distributions** folder will contain the zip file of the compiled program. Unzip it and launch the program from the scripts in the **/bin** directory.
|
||||
|
||||
## Run the Application (from Gradle)
|
||||
|
||||
### Linux
|
||||
```
|
||||
gradle.bat clean buildSdr
|
||||
./gradlew clean run
|
||||
```
|
||||
This would add gradle, download all project dependencies and build JAR
|
||||
### Windows
|
||||
```
|
||||
gradlew.bat clean run
|
||||
```
|
||||
|
||||
|
||||
## Development
|
||||
All dependencies/versions are controlled from build.gradle.
|
||||
To change the new release version tag of artifact - change property:
|
||||
```
|
||||
version = '0.3.2'
|
||||
version = '0.5.0'
|
||||
```
|
||||
|
||||
## Run SDRTrunk
|
||||
SDRTrunk is packed into single uber-jar file. There is need to have folders with dependecies and
|
||||
adding them with classpath. Just run with java.
|
||||
### Use Java8
|
||||
```
|
||||
java -jar build/libs/sdr-trunk-all-0.3.2.jar
|
||||
```
|
||||
|
||||
## TODO ITEMS
|
||||
|
||||
- [x] Move project to gradle build
|
||||
- [x] Changed folders structure, based on java project
|
||||
- [x] Dependencies are moved to gradle and versions are updated
|
||||
- [x] Create uberJAR with all dependencies
|
||||
- [ ] Some dependencies left in imports folder. Find them and include into gradle
|
||||
- [ ] Move images into resources of project
|
||||
- [ ] Add tests to project. This would guarantee that after refactoring part of functionality is not broken
|
||||
- [ ] Create modular OOP system (radio-module, gui, web-ui, etc). This would allow to use RADIO modules to call from CLI, GUI or Web UI. And use SDRTrunk as dependency for other projects.
|
||||
251
build.gradle
251
build.gradle
@@ -1,28 +1,123 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
}
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
plugins {
|
||||
id 'application'
|
||||
id 'java'
|
||||
id 'idea'
|
||||
id 'org.beryx.runtime' version '1.12.7'
|
||||
}
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'idea'
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
maven { url "https://jitpack.io" }
|
||||
}
|
||||
|
||||
version = '0.3.3-beta.2'
|
||||
sourceCompatibility = 1.8
|
||||
version = '0.5.0-beta2'
|
||||
sourceCompatibility = '17'
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs 'src/main'
|
||||
test.java.srcDirs 'src/test'
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
// JUnit Tests
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation 'org.assertj:assertj-core:3.8.0'
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.0")
|
||||
testImplementation 'org.mockito:mockito-core:3.+'
|
||||
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
|
||||
|
||||
//Jitpack imports
|
||||
implementation 'com.github.dnault:libresample4j:master'
|
||||
implementation 'com.github.DSheirer.jmbe:jmbe-api:1.0.0'
|
||||
implementation 'com.github.nwaldispuehl:java-lame:v3.98.4'
|
||||
|
||||
//mavenCentral/jcenter imports
|
||||
implementation 'ch.qos.logback:logback-classic:1.2.3'
|
||||
implementation 'ch.qos.logback:logback-core:1.2.3'
|
||||
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.8'
|
||||
implementation 'com.fazecast:jSerialComm:2.5.0'
|
||||
implementation 'com.github.jiconfont:jiconfont-font_awesome:4.7.0.1'
|
||||
implementation 'com.github.jiconfont:jiconfont-javafx:1.0.0'
|
||||
implementation 'com.github.jiconfont:jiconfont-swing:1.0.1'
|
||||
implementation 'com.github.wendykierp:JTransforms:3.1'
|
||||
implementation 'com.google.code.gson:gson:2.8.6'
|
||||
implementation 'com.google.guava:guava:27.0.1-jre'
|
||||
implementation 'com.jidesoft:jide-oss:3.6.18'
|
||||
implementation 'com.miglayout:miglayout-swing:5.2'
|
||||
implementation 'com.mpatric:mp3agic:0.9.1'
|
||||
implementation 'commons-io:commons-io:2.7'
|
||||
implementation 'eu.hansolo:charts:1.0.5'
|
||||
implementation 'io.github.dsheirer:radio-reference-api:15.1.8'
|
||||
implementation 'javax.usb:usb-api:1.0.2'
|
||||
implementation 'net.coderazzi:tablefilter-swing:5.4.0'
|
||||
implementation 'org.apache.commons:commons-compress:1.20'
|
||||
implementation 'org.apache.commons:commons-lang3:3.8.1'
|
||||
implementation 'org.apache.commons:commons-math3:3.6.1'
|
||||
implementation 'org.apache.commons:commons-csv:1.9.0'
|
||||
implementation 'org.apache.mina:mina-core:2.1.3'
|
||||
implementation 'org.apache.mina:mina-http:2.1.3'
|
||||
implementation 'org.controlsfx:controlsfx:11.1.0'
|
||||
implementation 'org.rauschig:jarchivelib:1.0.0'
|
||||
implementation 'org.slf4j:slf4j-api:1.7.25'
|
||||
implementation 'org.usb4java:libusb4java:1.3.0'
|
||||
implementation 'org.usb4java:usb4java:1.3.0'
|
||||
implementation 'org.usb4java:usb4java-javax:1.3.0'
|
||||
implementation 'io.github.dsheirer:libusb4java-darwin-aarch64:1.3.0' //usb4java native lib for OSX M1 cpu
|
||||
implementation 'pl.edu.icm:JLargeArrays:1.6'
|
||||
}
|
||||
|
||||
/**
|
||||
* This is needed for the JDK17 vector API ... until it moves out of incubation
|
||||
*/
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs.add("--add-modules=jdk.incubator.vector") //Needed while Panama Vector API remains in incubator
|
||||
}
|
||||
|
||||
application {
|
||||
mainClassName = "io.github.dsheirer.gui.SDRTrunk"
|
||||
applicationDefaultJvmArgs =
|
||||
['--add-exports=javafx.base/com.sun.javafx.event=ALL-UNNAMED', //Needed for controls-fx.jar
|
||||
'--add-modules=jdk.incubator.vector', //Needed while Panama Vector API remains in incubator
|
||||
'--add-exports=java.desktop/com.sun.java.swing.plaf.windows=ALL-UNNAMED'] //Windows Swing - jide-oss library
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes (
|
||||
'Implementation-Title' : 'sdrtrunk project',
|
||||
'Implementation-Version' : archiveVersion,
|
||||
'Created-By' : "Gradle ${gradle.gradleVersion}",
|
||||
'Build-Timestamp' : new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(new Date()),
|
||||
'Build-JDK' : "${System.properties['java.version']} (${System.properties['java.vendor']} ${System.properties['java.vm.version']}",
|
||||
'Build-OS' : "${System.properties['os.name']} (${System.properties['os.arch']} ${System.properties['os.version']}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
idea {
|
||||
module {
|
||||
downloadJavadoc = true
|
||||
@@ -30,44 +125,102 @@ idea {
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile 'org.assertj:assertj-core:3.8.0'
|
||||
compile fileTree(dir: 'imports', include: '*.jar')
|
||||
compile 'org.apache.commons:commons-lang3:3.7'
|
||||
compile 'org.apache.commons:commons-io:1.3.2'
|
||||
compile 'org.slf4j:slf4j-api:1.7.25'
|
||||
compile 'ch.qos.logback:logback-core:1.2.3'
|
||||
compile 'ch.qos.logback:logback-classic:1.2.3'
|
||||
compile 'com.miglayout:miglayout-swing:5.0'
|
||||
/**
|
||||
* Runtime target OS and CPU image names. We append version here so that version details are at end of zip filename.
|
||||
*/
|
||||
String targetLinuxAarch64 = 'linux-aarch64-v' + version
|
||||
String targetLinuxX86_64 = 'linux-x86_64-v' + version
|
||||
String targetOsxAarch64 = 'osx-aarch64-v' + version
|
||||
String targetOsxX86_64 = 'osx-x86_64-v' + version
|
||||
String targetWindowsX86_64 = 'windows-x86_64-v' + version
|
||||
|
||||
compile 'org.usb4java:usb4java-javax:1.2.0'
|
||||
compile 'org.usb4java:usb4java:1.2.0'
|
||||
compile 'org.usb4java:libusb4java:1.2.0'
|
||||
compile 'javax.usb:usb-api:1.0.2'
|
||||
compile 'com.github.wendykierp:JTransforms:3.1'
|
||||
compile 'pl.edu.icm:JLargeArrays:1.6'
|
||||
compile 'net.coderazzi:tablefilter-swing:5.3.1'
|
||||
compile 'com.jidesoft:jide-oss:3.4.3'
|
||||
compile 'org.apache.mina:mina-core:2.0.16'
|
||||
compile 'org.apache.mina:mina-http:2.0.16'
|
||||
compile 'org.apache.axis:axis-jaxrpc:1.4'
|
||||
compile 'org.apache.axis:axis:1.4'
|
||||
compile 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.3'
|
||||
}
|
||||
/**
|
||||
* Optional pre-downloaded Java Development Kit (JDK) for each target OS and CPU. These locations are hard-coded to the
|
||||
* author's development environment, but can be changed to your environment if you want to use this build pattern.
|
||||
*/
|
||||
String jdk_base = '/home/denny/java_jdks/'
|
||||
String jdk_linux_aarch64 = jdk_base + 'linux-arm64/jdk-17.0.2-full'
|
||||
String jdk_linux_x86_64 = jdk_base + 'linux-x64/jdk-17.0.2-full'
|
||||
String jdk_osx_x86_64 = jdk_base + 'osx-x64/jdk-17.0.2-full.jdk'
|
||||
String jdk_osx_aarch64 = jdk_base + 'osx-arm64/jdk-17.0.2-full.jdk'
|
||||
String jdk_windows_x86_64 = jdk_base + 'windows-x64/jdk-17.0.2-full'
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '4.3.1'
|
||||
}
|
||||
boolean hasTargetJdks = file(jdk_linux_x86_64).exists() || file(jdk_linux_aarch64).exists() ||
|
||||
file(jdk_osx_x86_64).exists() || file(jdk_osx_aarch64).exists() || file(jdk_windows_x86_64).exists()
|
||||
|
||||
task buildSdr(type: Jar) {
|
||||
manifest {
|
||||
attributes 'Implementation-Title': 'SdrTrunk project',
|
||||
'Implementation-Version': version,
|
||||
'Main-Class': 'io.github.dsheirer.gui.SDRTrunk',
|
||||
'Class-Path': 'jmbe-0.3.2.jar jmbe-0.3.3.jar'
|
||||
/**
|
||||
* Auto-download target JDKs. Defaults to false, or the user can override via the gradle command line by
|
||||
* including this option: '-PdownloadJdks=true'
|
||||
*/
|
||||
boolean downloadJdks = project.hasProperty('downloadJdks') ? project.getProperty('downloadJdks') : false
|
||||
|
||||
/**
|
||||
* Beryx Runtime plugin for creating OS and CPU targeted build(s) of the software.
|
||||
*
|
||||
* Option 1: Create a single target build using the locally installed JDK, or optionally create target builds for
|
||||
* all OS and CPU targets when pre-downloaded target JDKs are available (see 'jdk_*' properties above).
|
||||
*
|
||||
* >: gradle runtimeZip
|
||||
*
|
||||
* Option 2: Create all OS and CPU target builds by automatically downloading all JDKs needed to build the target images
|
||||
*
|
||||
* >: gradle runtimeZip -PdownloadJdks=true
|
||||
*/
|
||||
runtime {
|
||||
//Build by automatically downloading JDKs for the OS and CPU targets
|
||||
if(downloadJdks) {
|
||||
println("Building all runtime images")
|
||||
println("The [ > jre: ] build phase downloads several target JDKs and this may take a while ... please wait")
|
||||
targetPlatform(targetLinuxAarch64) {
|
||||
jdkHome = jdkDownload("https://download.bell-sw.com/java/17.0.2+9/bellsoft-jdk17.0.2+9-linux-aarch64-full.tar.gz")
|
||||
}
|
||||
targetPlatform(targetLinuxX86_64) {
|
||||
jdkHome = jdkDownload("https://download.bell-sw.com/java/17.0.2+9/bellsoft-jdk17.0.2+9-linux-amd64-full.tar.gz")
|
||||
}
|
||||
targetPlatform(targetOsxAarch64) {
|
||||
jdkHome = jdkDownload("https://download.bell-sw.com/java/17.0.2+9/bellsoft-jdk17.0.2+9-macos-aarch64-full.tar.gz")
|
||||
}
|
||||
targetPlatform(targetOsxX86_64) {
|
||||
jdkHome = jdkDownload("https://download.bell-sw.com/java/17.0.2+9/bellsoft-jdk17.0.2+9-macos-amd64-full.tar.gz")
|
||||
}
|
||||
targetPlatform(targetWindowsX86_64) {
|
||||
jdkHome = jdkDownload("https://download.bell-sw.com/java/17.0.2+9/bellsoft-jdk17.0.2+9-windows-amd64-full.zip")
|
||||
}
|
||||
}
|
||||
baseName = project.name + '-all'
|
||||
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
|
||||
with jar
|
||||
//Build using pre-downloaded JDKs
|
||||
else if(hasTargetJdks) {
|
||||
println("Building all runtime images using pre-downloaded JDKs")
|
||||
if(file(jdk_linux_aarch64).exists()) {
|
||||
targetPlatform(targetLinuxAarch64, jdk_linux_aarch64)
|
||||
}
|
||||
if(file(jdk_linux_x86_64).exists()) {
|
||||
targetPlatform(targetLinuxX86_64, jdk_linux_x86_64)
|
||||
}
|
||||
if(file(jdk_osx_aarch64).exists()) {
|
||||
targetPlatform(targetOsxAarch64, jdk_osx_aarch64)
|
||||
}
|
||||
if(file(jdk_osx_x86_64).exists()) {
|
||||
targetPlatform(targetOsxX86_64, jdk_osx_x86_64)
|
||||
}
|
||||
if(file(jdk_windows_x86_64).exists()) {
|
||||
targetPlatform(targetWindowsX86_64, jdk_windows_x86_64)
|
||||
}
|
||||
}
|
||||
//Build using locally installed JDK only
|
||||
else {
|
||||
println("Building a single runtime image using locally installed JDK")
|
||||
}
|
||||
|
||||
//Additional modules to include with auto-detected modules.
|
||||
modules = ['jdk.crypto.ec'] //jdk.crypto.ec needed for HTTPS connections (broadcastify calls & map tile server)
|
||||
|
||||
//Use auto-detected modules and 'add' any specified modules.
|
||||
additive = true
|
||||
|
||||
//Recommended options for reducing/shrinking runtime target image size
|
||||
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
|
||||
|
||||
//Runtime images have auto appended version in image zip name. Override to include version in single target image.
|
||||
imageZip = (hasTargetJdks | downloadJdks) ? file("$buildDir/image/sdr-trunk.zip") :
|
||||
file("$buildDir/image/sdr-trunk-" + version + ".zip")
|
||||
}
|
||||
|
||||
16
copyright.template
Normal file
16
copyright.template
Normal file
@@ -0,0 +1,16 @@
|
||||
******************************************************************************
|
||||
Copyright (C) 2014-${today.year} Dennis Sheirer
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*****************************************************************************
|
||||
@@ -1,17 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.3.1-bin.zip
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
51
gradlew
vendored
51
gradlew
vendored
@@ -1,5 +1,21 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
@@ -28,7 +44,7 @@ APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
@@ -109,8 +125,8 @@ if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
@@ -138,19 +154,19 @@ if $cygwin ; then
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
@@ -159,14 +175,9 @@ save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
18
gradlew.bat
vendored
18
gradlew.bat
vendored
@@ -1,3 +1,19 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
1023
sdr-trunk.ipr
Normal file
1023
sdr-trunk.ipr
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,20 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
/*
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 Dennis Sheirer
|
||||
* *
|
||||
* * This program is free software: you can redistribute it and/or modify
|
||||
* * it under the terms of the GNU General Public License as published by
|
||||
* * the Free Software Foundation, either version 3 of the License, or
|
||||
* * (at your option) any later version.
|
||||
* *
|
||||
* * This program is distributed in the hope that it will be useful,
|
||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* * GNU General Public License for more details.
|
||||
* *
|
||||
* * You should have received a copy of the GNU General Public License
|
||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
*/
|
||||
package io.github.dsheirer.alias;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
@@ -24,170 +28,474 @@ import io.github.dsheirer.alias.action.AliasAction;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
||||
import io.github.dsheirer.alias.id.nonrecordable.NonRecordable;
|
||||
import io.github.dsheirer.alias.id.priority.Priority;
|
||||
import io.github.dsheirer.alias.id.record.Record;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.FilteredList;
|
||||
import javafx.util.Callback;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.Color;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* Alias provides an aliasing (e.g. name, color, etc) container that is linked to multiple alias identifiers and
|
||||
* provides a corresponding set of actions related to the alias.
|
||||
*/
|
||||
@JacksonXmlRootElement(localName = "alias")
|
||||
public class Alias
|
||||
{
|
||||
private String mList;
|
||||
private String mGroup;
|
||||
private String mName;
|
||||
private int mColor;
|
||||
private String mIconName;
|
||||
private List<AliasID> mAliasIDs = new ArrayList<AliasID>();
|
||||
private List<AliasAction> mAliasActions = new ArrayList<AliasAction>();
|
||||
private final static Logger mLog = LoggerFactory.getLogger(Alias.class);
|
||||
|
||||
public Alias()
|
||||
{
|
||||
}
|
||||
private BooleanProperty mRecordable = new SimpleBooleanProperty();
|
||||
private BooleanProperty mStreamable = new SimpleBooleanProperty();
|
||||
private BooleanProperty mOverlap = new SimpleBooleanProperty();
|
||||
private IntegerProperty mColor = new SimpleIntegerProperty();
|
||||
private IntegerProperty mPriority = new SimpleIntegerProperty(Priority.DEFAULT_PRIORITY);
|
||||
private IntegerProperty mNonAudioIdentifierCount = new SimpleIntegerProperty();
|
||||
private StringProperty mAliasListName = new SimpleStringProperty();
|
||||
private StringProperty mGroup = new SimpleStringProperty();
|
||||
private StringProperty mIconName = new SimpleStringProperty();
|
||||
private StringProperty mName = new SimpleStringProperty();
|
||||
private ObservableList<AliasID> mAliasIDs = FXCollections.observableArrayList();
|
||||
private ObservableList<AliasAction> mAliasActions = FXCollections.observableArrayList();
|
||||
|
||||
/**
|
||||
* Constructs an instance and sets the specified name.
|
||||
* @param name for the alias
|
||||
*/
|
||||
public Alias(String name)
|
||||
{
|
||||
mName = name;
|
||||
mName.set(name);
|
||||
|
||||
//Bind the non-audio identifier count property to the size of a filtered list of alias identifiers
|
||||
mNonAudioIdentifierCount.bind(Bindings.size(new FilteredList<>(mAliasIDs, aliasID -> {
|
||||
return aliasID != null && !aliasID.isAudioIdentifier();
|
||||
})));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*/
|
||||
public Alias()
|
||||
{
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count of non-audio identifiers for this alias
|
||||
*/
|
||||
@JsonIgnore
|
||||
public IntegerProperty nonAudioIdentifierCountProperty()
|
||||
{
|
||||
return mNonAudioIdentifierCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Audio playback priority
|
||||
*/
|
||||
@JsonIgnore
|
||||
public IntegerProperty priorityProperty()
|
||||
{
|
||||
return mPriority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if one or more of the identifiers for this alias overlap with identifiers from another alias
|
||||
* within the same alias list.
|
||||
*/
|
||||
@JsonIgnore
|
||||
public BooleanProperty overlapProperty()
|
||||
{
|
||||
return mOverlap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recordable property
|
||||
*/
|
||||
@JsonIgnore
|
||||
public BooleanProperty recordableProperty()
|
||||
{
|
||||
return mRecordable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Streamable property
|
||||
*/
|
||||
@JsonIgnore
|
||||
public BooleanProperty streamableProperty()
|
||||
{
|
||||
return mStreamable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias list name property
|
||||
*/
|
||||
@JsonIgnore
|
||||
public StringProperty aliasListNameProperty()
|
||||
{
|
||||
return mAliasListName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Group property
|
||||
*/
|
||||
@JsonIgnore
|
||||
public StringProperty groupProperty()
|
||||
{
|
||||
return mGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias name property
|
||||
* @return
|
||||
*/
|
||||
@JsonIgnore
|
||||
public StringProperty nameProperty()
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias color value property
|
||||
*/
|
||||
@JsonIgnore
|
||||
public IntegerProperty colorProperty()
|
||||
{
|
||||
return mColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Icon name property
|
||||
*/
|
||||
@JsonIgnore
|
||||
public StringProperty iconNameProperty()
|
||||
{
|
||||
return mIconName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias identifiers list property
|
||||
*/
|
||||
@JsonIgnore
|
||||
public ObservableList<AliasID> aliasIds()
|
||||
{
|
||||
return mAliasIDs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias actions list property
|
||||
*/
|
||||
@JsonIgnore
|
||||
public ObservableList<AliasAction> aliasActions()
|
||||
{
|
||||
return mAliasActions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses name value for string representation
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
return getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias name
|
||||
*/
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "name")
|
||||
public String getName()
|
||||
{
|
||||
return mName;
|
||||
return mName.get();
|
||||
}
|
||||
|
||||
public void setName(String name)
|
||||
{
|
||||
mName = name;
|
||||
mName.set(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias list name for the alias list that this alias belongs to
|
||||
*/
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "list")
|
||||
public String getList()
|
||||
public String getAliasListName()
|
||||
{
|
||||
return mList;
|
||||
return mAliasListName.get();
|
||||
}
|
||||
|
||||
public void setList(String list)
|
||||
public void setAliasListName(String aliasListName)
|
||||
{
|
||||
mList = list;
|
||||
mAliasListName.set(aliasListName);
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public boolean matchesAliasList(String aliasList)
|
||||
{
|
||||
boolean isEmptyArgument = aliasList == null || aliasList.isEmpty() || aliasList.contentEquals(AliasModel.NO_ALIAS_LIST);
|
||||
boolean isEmptyThis = getAliasListName() == null || getAliasListName().isEmpty();
|
||||
|
||||
if(isEmptyArgument && isEmptyThis)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if(!isEmptyArgument && !isEmptyThis && aliasList.contentEquals(getAliasListName()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this alias has a specified alias list name
|
||||
*/
|
||||
public boolean hasList()
|
||||
{
|
||||
return mList != null;
|
||||
return mAliasListName.get() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grouping tag for this alias.
|
||||
*/
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "group")
|
||||
public String getGroup()
|
||||
{
|
||||
return mGroup;
|
||||
return mGroup.get();
|
||||
}
|
||||
|
||||
public void setGroup(String group)
|
||||
{
|
||||
mGroup = group;
|
||||
mGroup.set(group);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this alias has a grouping tag
|
||||
*/
|
||||
public boolean hasGroup()
|
||||
{
|
||||
return mGroup != null;
|
||||
return mGroup.get() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Color (RGBA) value to use for this alias
|
||||
*/
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "color")
|
||||
public int getColor()
|
||||
{
|
||||
return mColor;
|
||||
return mColor.get();
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public String getColorHex()
|
||||
{
|
||||
return String.format("#%06X", (0xFFFFFF & mColor.get()));
|
||||
}
|
||||
|
||||
public void setColor(int color)
|
||||
{
|
||||
mColor = color;
|
||||
mColor.set(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display color for this alias.
|
||||
*/
|
||||
@JsonIgnore
|
||||
public Color getDisplayColor()
|
||||
{
|
||||
return new Color(getColor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Icon name of the icon to use for this alias.
|
||||
*/
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "iconName")
|
||||
public String getIconName()
|
||||
{
|
||||
return mIconName;
|
||||
return mIconName.get();
|
||||
}
|
||||
|
||||
public void setIconName(String iconName)
|
||||
{
|
||||
mIconName = iconName;
|
||||
mIconName.set(iconName);
|
||||
}
|
||||
|
||||
/**
|
||||
* List of alias identifiers for this alias.
|
||||
*/
|
||||
@JacksonXmlProperty(isAttribute = false, localName = "id")
|
||||
public List<AliasID> getId()
|
||||
public List<AliasID> getAliasIdentifiers()
|
||||
{
|
||||
return mAliasIDs;
|
||||
}
|
||||
|
||||
public void setId(ArrayList<AliasID> id)
|
||||
public void setAliasIdentifiers(List<AliasID> id)
|
||||
{
|
||||
mAliasIDs = id;
|
||||
mAliasIDs.clear();
|
||||
|
||||
for(AliasID aliasID : id)
|
||||
{
|
||||
addAliasID(aliasID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an alias identifier to this alias and updates the recordable and streamable attributes.
|
||||
*/
|
||||
public void addAliasID(AliasID id)
|
||||
{
|
||||
mAliasIDs.add(id);
|
||||
validate();
|
||||
updateOverlapBinding();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an alias identifier from this alias and updates the recordable and streamable attributes.
|
||||
*/
|
||||
public void removeAliasID(AliasID id)
|
||||
{
|
||||
mAliasIDs.remove(id);
|
||||
if(id != null)
|
||||
{
|
||||
mAliasIDs.remove(id);
|
||||
validate();
|
||||
updateOverlapBinding();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List of alias actions associated with this alias.
|
||||
*/
|
||||
@JacksonXmlProperty(isAttribute = false, localName = "action")
|
||||
public List<AliasAction> getAction()
|
||||
public List<AliasAction> getAliasActions()
|
||||
{
|
||||
return mAliasActions;
|
||||
}
|
||||
|
||||
public void setAction(List<AliasAction> actions)
|
||||
public void setAliasActions(List<AliasAction> actions)
|
||||
{
|
||||
mAliasActions = actions;
|
||||
mAliasActions.setAll(actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified alias action to this alias
|
||||
*/
|
||||
public void addAliasAction(AliasAction action)
|
||||
{
|
||||
mAliasActions.add(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified alias action from this alias
|
||||
*/
|
||||
public void removeAliasAction(AliasAction action)
|
||||
{
|
||||
mAliasActions.remove(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this alias has any associated
|
||||
* @return
|
||||
*/
|
||||
public boolean hasActions()
|
||||
{
|
||||
return !mAliasActions.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the playback priority this alias
|
||||
*/
|
||||
private void updatePriority()
|
||||
{
|
||||
for(AliasID aliasID: mAliasIDs)
|
||||
{
|
||||
if(aliasID instanceof Priority)
|
||||
{
|
||||
mPriority.set(((Priority)aliasID).getPriority());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mPriority.set(Priority.DEFAULT_PRIORITY);
|
||||
}
|
||||
|
||||
private void updateOverlapBinding()
|
||||
{
|
||||
mOverlap.unbind();
|
||||
|
||||
BooleanBinding binding = new SimpleBooleanProperty(true).not();
|
||||
|
||||
for(AliasID aliasID: mAliasIDs)
|
||||
{
|
||||
binding = binding.or(aliasID.overlapProperty());
|
||||
}
|
||||
|
||||
mOverlap.bind(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the recordable status for this alias
|
||||
*/
|
||||
private void updateRecordable()
|
||||
{
|
||||
for(AliasID aliasID: mAliasIDs)
|
||||
{
|
||||
if(aliasID instanceof Record)
|
||||
{
|
||||
mRecordable.set(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mRecordable.set(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the streamable status for this alias
|
||||
*/
|
||||
private void updateStreamable()
|
||||
{
|
||||
for(AliasID aliasID: mAliasIDs)
|
||||
{
|
||||
if(aliasID instanceof BroadcastChannel)
|
||||
{
|
||||
mStreamable.set(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mStreamable.set(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform any validation/cleanup actions on this alias.
|
||||
*/
|
||||
public void validate()
|
||||
{
|
||||
updatePriority();
|
||||
updateRecordable();
|
||||
updateStreamable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the priority level of this alias, if defined, or the default priority
|
||||
*/
|
||||
@JsonIgnore
|
||||
public int getCallPriority()
|
||||
public int getPlaybackPriority()
|
||||
{
|
||||
for(AliasID id : mAliasIDs)
|
||||
{
|
||||
@@ -218,22 +526,23 @@ public class Alias
|
||||
*/
|
||||
public void setCallPriority(int priority)
|
||||
{
|
||||
if(priority == Priority.DO_NOT_MONITOR ||
|
||||
(Priority.MIN_PRIORITY <= priority && priority <= Priority.MAX_PRIORITY))
|
||||
{
|
||||
for(AliasID id : mAliasIDs)
|
||||
{
|
||||
if(id.getType() == AliasIDType.PRIORITY)
|
||||
{
|
||||
((Priority)id).setPriority(priority);
|
||||
return;
|
||||
}
|
||||
}
|
||||
AliasID priorityID = null;
|
||||
|
||||
//If we don't find a priority id, create one
|
||||
Priority p = new Priority();
|
||||
p.setPriority(priority);
|
||||
addAliasID(p);
|
||||
for(AliasID aliasID: mAliasIDs)
|
||||
{
|
||||
if(aliasID instanceof Priority)
|
||||
{
|
||||
priorityID = aliasID;
|
||||
}
|
||||
}
|
||||
|
||||
removeAliasID(priorityID);
|
||||
|
||||
//Don't add a priority alias id if the priority is MAX_PRIORITY or DEFAULT_PRIORITY since the getCallPriority()
|
||||
//will return that as the default value
|
||||
if(priority == Priority.DO_NOT_MONITOR || (Priority.MIN_PRIORITY <= priority && priority < Priority.MAX_PRIORITY))
|
||||
{
|
||||
addAliasID(new Priority(priority));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,51 +552,29 @@ public class Alias
|
||||
@JsonIgnore
|
||||
public boolean isRecordable()
|
||||
{
|
||||
for(AliasID id : getId())
|
||||
{
|
||||
if(id.getType() == AliasIDType.NON_RECORDABLE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return mRecordable.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets or removes the non-recordable alias ID for this alias.
|
||||
* Sets or removes the record alias ID for this alias.
|
||||
*/
|
||||
public void setRecordable(boolean recordable)
|
||||
{
|
||||
if(recordable)
|
||||
AliasID recordID = null;
|
||||
|
||||
for(AliasID aliasID: mAliasIDs)
|
||||
{
|
||||
AliasID toRemove = null;
|
||||
|
||||
for(AliasID id : getId())
|
||||
if(aliasID instanceof Record)
|
||||
{
|
||||
if(id.getType() == AliasIDType.NON_RECORDABLE)
|
||||
{
|
||||
toRemove = id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(toRemove != null)
|
||||
{
|
||||
removeAliasID(toRemove);
|
||||
recordID = aliasID;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(AliasID id : getId())
|
||||
{
|
||||
if(id.getType() == AliasIDType.NON_RECORDABLE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
addAliasID(new NonRecordable());
|
||||
removeAliasID(recordID);
|
||||
|
||||
if(recordable)
|
||||
{
|
||||
addAliasID(new Record());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,15 +584,7 @@ public class Alias
|
||||
@JsonIgnore
|
||||
public boolean isStreamable()
|
||||
{
|
||||
for(AliasID id : getId())
|
||||
{
|
||||
if(id.getType() == AliasIDType.BROADCAST_CHANNEL)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return mStreamable.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -316,7 +595,7 @@ public class Alias
|
||||
{
|
||||
Set<BroadcastChannel> broadcastChannels = new TreeSet<>();
|
||||
|
||||
for(AliasID id : getId())
|
||||
for(AliasID id : getAliasIdentifiers())
|
||||
{
|
||||
if(id.getType() == AliasIDType.BROADCAST_CHANNEL)
|
||||
{
|
||||
@@ -337,15 +616,100 @@ public class Alias
|
||||
return false;
|
||||
}
|
||||
|
||||
for(AliasID id : getId())
|
||||
for(AliasID id : getAliasIdentifiers())
|
||||
{
|
||||
if(id.getType() == AliasIDType.BROADCAST_CHANNEL &&
|
||||
((BroadcastChannel)id).getChannelName().contentEquals(channel))
|
||||
if(id instanceof BroadcastChannel)
|
||||
{
|
||||
return true;
|
||||
BroadcastChannel broadcastChannel = (BroadcastChannel)id;
|
||||
|
||||
if(broadcastChannel.getChannelName() != null && broadcastChannel.getChannelName().contentEquals(channel))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes any broadcast channel(s) that match the argument
|
||||
*/
|
||||
public void removeBroadcastChannel(String channel)
|
||||
{
|
||||
if(channel == null || channel.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<AliasID> toRemove = new ArrayList<>();
|
||||
|
||||
for(AliasID aliasID: getAliasIdentifiers())
|
||||
{
|
||||
if(aliasID instanceof BroadcastChannel &&
|
||||
((BroadcastChannel)aliasID).getChannelName().contentEquals(channel))
|
||||
{
|
||||
toRemove.add(aliasID);
|
||||
}
|
||||
}
|
||||
|
||||
for(AliasID aliasID: toRemove)
|
||||
{
|
||||
removeAliasID(aliasID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes (clears) all broadcast channels from this alias
|
||||
*/
|
||||
public void removeAllBroadcastChannels()
|
||||
{
|
||||
List<AliasID> toRemove = new ArrayList<>();
|
||||
for(AliasID aliasID: mAliasIDs)
|
||||
{
|
||||
if(aliasID instanceof BroadcastChannel)
|
||||
{
|
||||
toRemove.add(aliasID);
|
||||
}
|
||||
}
|
||||
|
||||
for(AliasID aliasID: toRemove)
|
||||
{
|
||||
removeAliasID(aliasID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all non audio identifiers to prepare for replacing them from an editor.
|
||||
*/
|
||||
public void removeNonAudioIdentifiers()
|
||||
{
|
||||
Iterator<AliasID> it = mAliasIDs.iterator();
|
||||
|
||||
while(it.hasNext())
|
||||
{
|
||||
if(!it.next().isAudioIdentifier())
|
||||
{
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all alias actions from this alias
|
||||
*/
|
||||
public void removeAllActions()
|
||||
{
|
||||
mAliasActions.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an observable property extractor for use with observable lists to detect changes internal to this object.
|
||||
*/
|
||||
public static Callback<Alias,Observable[]> extractor()
|
||||
{
|
||||
return (Alias a) -> new Observable[] {a.recordableProperty(), a.streamableProperty(), a.colorProperty(),
|
||||
a.aliasListNameProperty(), a.groupProperty(), a.iconNameProperty(), a.nameProperty(), a.aliasIds(),
|
||||
a.aliasActions(), a.nonAudioIdentifierCountProperty(), a.overlapProperty()};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,356 +0,0 @@
|
||||
package io.github.dsheirer.alias;
|
||||
|
||||
import com.jidesoft.swing.JideSplitPane;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastModel;
|
||||
import io.github.dsheirer.gui.editor.Editor;
|
||||
import io.github.dsheirer.icon.IconManager;
|
||||
import net.coderazzi.filters.gui.AutoChoices;
|
||||
import net.coderazzi.filters.gui.TableFilterHeader;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AliasController extends JPanel
|
||||
implements ActionListener, ListSelectionListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private AliasModel mAliasModel;
|
||||
private JTable mAliasTable;
|
||||
private TableFilterHeader mTableFilterHeader;
|
||||
private AliasEditor mAliasEditor;
|
||||
private MultipleAliasEditor mMultipleAliasEditor;
|
||||
private JideSplitPane mSplitPane;
|
||||
|
||||
private static final String NEW_ALIAS = "New";
|
||||
private static final String COPY_ALIAS = "Copy";
|
||||
private static final String DELETE_ALIAS = "Delete";
|
||||
|
||||
private JButton mNewButton = new JButton(NEW_ALIAS);
|
||||
private JButton mCopyButton = new JButton(COPY_ALIAS);
|
||||
private JButton mDeleteButton = new JButton(DELETE_ALIAS);
|
||||
|
||||
private IconCellRenderer mIconCellRenderer;
|
||||
|
||||
public AliasController(AliasModel aliasModel, BroadcastModel broadcastModel, IconManager iconManager)
|
||||
{
|
||||
mAliasModel = aliasModel;
|
||||
|
||||
mAliasEditor = new AliasEditor(mAliasModel, broadcastModel, iconManager);
|
||||
mMultipleAliasEditor = new MultipleAliasEditor(mAliasModel, broadcastModel, iconManager);
|
||||
|
||||
mIconCellRenderer = new IconCellRenderer(iconManager);
|
||||
|
||||
init();
|
||||
|
||||
mAliasModel.addListener(mAliasEditor);
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout(new MigLayout("insets 0 0 0 0",
|
||||
"[grow,fill]",
|
||||
"[grow,fill]"));
|
||||
|
||||
//System Configuration View and Editor
|
||||
mAliasTable = new JTable(mAliasModel);
|
||||
mAliasTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
mAliasTable.getSelectionModel().addListSelectionListener(this);
|
||||
mAliasTable.setAutoCreateRowSorter(true);
|
||||
|
||||
mAliasTable.getColumnModel().getColumn(AliasModel.COLUMN_COLOR)
|
||||
.setCellRenderer(new ColorCellRenderer());
|
||||
|
||||
mAliasTable.getColumnModel().getColumn(AliasModel.COLUMN_ICON)
|
||||
.setCellRenderer(mIconCellRenderer);
|
||||
|
||||
mTableFilterHeader = new TableFilterHeader(mAliasTable, AutoChoices.ENABLED);
|
||||
mTableFilterHeader.setFilterOnUpdates(true);
|
||||
|
||||
JScrollPane tableScroller = new JScrollPane(mAliasTable);
|
||||
|
||||
JPanel buttonsPanel = new JPanel();
|
||||
|
||||
buttonsPanel.setLayout(
|
||||
new MigLayout("insets 0 0 0 0", "[grow,fill][grow,fill][grow,fill]", "[]"));
|
||||
|
||||
mNewButton.addActionListener(this);
|
||||
mNewButton.setToolTipText("Adds a new alias");
|
||||
buttonsPanel.add(mNewButton);
|
||||
|
||||
mCopyButton.addActionListener(this);
|
||||
mCopyButton.setEnabled(false);
|
||||
mCopyButton.setToolTipText("Creates a copy of the currently selected alias and adds it");
|
||||
buttonsPanel.add(mCopyButton);
|
||||
|
||||
mDeleteButton.addActionListener(this);
|
||||
mDeleteButton.setEnabled(false);
|
||||
mDeleteButton.setToolTipText("Deletes the currently selected alias");
|
||||
buttonsPanel.add(mDeleteButton);
|
||||
|
||||
JPanel listAndButtonsPanel = new JPanel();
|
||||
|
||||
listAndButtonsPanel.setLayout(
|
||||
new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill][]"));
|
||||
|
||||
listAndButtonsPanel.add(tableScroller, "wrap");
|
||||
listAndButtonsPanel.add(buttonsPanel);
|
||||
|
||||
mSplitPane = new JideSplitPane(JideSplitPane.HORIZONTAL_SPLIT);
|
||||
mSplitPane.setDividerSize(5);
|
||||
mSplitPane.setShowGripper(true);
|
||||
mSplitPane.add(listAndButtonsPanel);
|
||||
mSplitPane.add(mAliasEditor);
|
||||
|
||||
add(mSplitPane);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the editor argument as the currently visible alias editor
|
||||
*/
|
||||
private void setEditor(Editor<?> editor)
|
||||
{
|
||||
if(mSplitPane.getComponentCount() == 3)
|
||||
{
|
||||
Component component = mSplitPane.getComponent(2);
|
||||
|
||||
if(component instanceof Editor<?> && component != editor)
|
||||
{
|
||||
//Get current divider location so that we can reapply it after
|
||||
//changing out the editors, so that the interface doesn't move
|
||||
int location = mSplitPane.getDividerLocation(0);
|
||||
|
||||
((Editor<?>) component).setItem(null);
|
||||
|
||||
mSplitPane.remove(component);
|
||||
editor.setPreferredSize(new Dimension(100, 100));
|
||||
mSplitPane.add(editor);
|
||||
|
||||
mSplitPane.validate();
|
||||
mSplitPane.setDividerLocation(0, location);
|
||||
mSplitPane.repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Alias getAlias(int selectedRow)
|
||||
{
|
||||
if(selectedRow >= 0)
|
||||
{
|
||||
int index = mAliasTable.convertRowIndexToModel(selectedRow);
|
||||
|
||||
return mAliasModel.getAliasAtIndex(index);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent event)
|
||||
{
|
||||
//Limits event firing to only when selection is complete
|
||||
if(!event.getValueIsAdjusting())
|
||||
{
|
||||
int[] selectedRows = mAliasTable.getSelectedRows();
|
||||
|
||||
if(selectedRows.length == 0)
|
||||
{
|
||||
mAliasEditor.setItem(null);
|
||||
mCopyButton.setEnabled(false);
|
||||
mDeleteButton.setEnabled(false);
|
||||
}
|
||||
else if(selectedRows.length == 1)
|
||||
{
|
||||
setEditor(mAliasEditor);
|
||||
|
||||
Alias selectedAlias = getAlias(selectedRows[0]);
|
||||
|
||||
mAliasEditor.setItem(selectedAlias);
|
||||
|
||||
if(selectedAlias != null)
|
||||
{
|
||||
mCopyButton.setEnabled(true);
|
||||
mDeleteButton.setEnabled(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
mCopyButton.setEnabled(false);
|
||||
mDeleteButton.setEnabled(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setEditor(mMultipleAliasEditor);
|
||||
|
||||
mCopyButton.setEnabled(true);
|
||||
mDeleteButton.setEnabled(true);
|
||||
|
||||
List<Alias> selectedAliases = new ArrayList<Alias>();
|
||||
|
||||
for(int selectedRow : selectedRows)
|
||||
{
|
||||
Alias alias = getAlias(selectedRow);
|
||||
|
||||
selectedAliases.add(alias);
|
||||
}
|
||||
|
||||
mMultipleAliasEditor.setItem(selectedAliases);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the alias to the alias table/model and scrolls the view it
|
||||
*/
|
||||
private void addAlias(Alias alias)
|
||||
{
|
||||
//HACK: when inserting a row to the model, the JTable gets
|
||||
//notified and attempts to tell the coderazzi table filter
|
||||
//adaptive choices filter to refresh before the table filter is
|
||||
//notified of the row additions, causing an index out of bounds
|
||||
//exception. We turn off adaptive choices temporarily, add the
|
||||
//channel, and turn on adaptive choices again.
|
||||
mTableFilterHeader.setAdaptiveChoices(false);
|
||||
|
||||
int index = mAliasModel.addAlias(alias);
|
||||
|
||||
mTableFilterHeader.setAdaptiveChoices(true);
|
||||
|
||||
if(index >= 0)
|
||||
{
|
||||
int translatedIndex = mAliasTable.convertRowIndexToView(index);
|
||||
mAliasTable.setRowSelectionInterval(translatedIndex, translatedIndex);
|
||||
mAliasTable.scrollRectToVisible(
|
||||
new Rectangle(mAliasTable.getCellRect(translatedIndex, 0, true)));
|
||||
}
|
||||
}
|
||||
|
||||
private List<Alias> getSelectedAliases()
|
||||
{
|
||||
List<Alias> selected = new ArrayList<>();
|
||||
|
||||
int[] rows = mAliasTable.getSelectedRows();
|
||||
|
||||
for(int row : rows)
|
||||
{
|
||||
if(row >= 0)
|
||||
{
|
||||
Alias alias = mAliasModel.getAliasAtIndex(mAliasTable.convertRowIndexToModel(row));
|
||||
|
||||
if(alias != null)
|
||||
{
|
||||
selected.add(alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds to New, Copy and Delete Channel button invocations
|
||||
*/
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent event)
|
||||
{
|
||||
switch(event.getActionCommand())
|
||||
{
|
||||
case NEW_ALIAS:
|
||||
addAlias(new Alias("New Alias"));
|
||||
break;
|
||||
case COPY_ALIAS:
|
||||
for(Alias alias : getSelectedAliases())
|
||||
{
|
||||
addAlias(AliasFactory.copyOf(alias));
|
||||
}
|
||||
break;
|
||||
case DELETE_ALIAS:
|
||||
List<Alias> toDelete = getSelectedAliases();
|
||||
|
||||
if(toDelete != null && !toDelete.isEmpty())
|
||||
{
|
||||
String title = toDelete.size() == 1 ? "Delete Alias?" : "Delete Aliases?";
|
||||
String prompt = toDelete.size() == 1 ? "Do you want to delete this alias?" :
|
||||
"Do you want to delete these " + toDelete.size() + " aliases?";
|
||||
|
||||
int choice = JOptionPane.showConfirmDialog(AliasController.this,
|
||||
prompt, title, JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
|
||||
|
||||
if(choice == JOptionPane.YES_OPTION)
|
||||
{
|
||||
for(Alias alias : toDelete)
|
||||
{
|
||||
mAliasModel.removeAlias(alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Colorizes the cell based on the cell's integer value
|
||||
*/
|
||||
public class ColorCellRenderer extends DefaultTableCellRenderer
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table,
|
||||
Object value, boolean isSelected, boolean hasFocus, int row,
|
||||
int column)
|
||||
{
|
||||
JLabel label = (JLabel) super.getTableCellRendererComponent(table,
|
||||
value, isSelected, hasFocus, row, column);
|
||||
|
||||
label.setBackground(new Color((int) value));
|
||||
|
||||
label.setText("");
|
||||
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays cell's icon and name
|
||||
*/
|
||||
public class IconCellRenderer extends DefaultTableCellRenderer
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private IconManager mIconManager;
|
||||
|
||||
public IconCellRenderer(IconManager iconManager)
|
||||
{
|
||||
mIconManager = iconManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table,
|
||||
Object value, boolean isSelected, boolean hasFocus, int row,
|
||||
int column)
|
||||
{
|
||||
JLabel label = (JLabel) super.getTableCellRendererComponent(table,
|
||||
value, isSelected, hasFocus, row, column);
|
||||
|
||||
ImageIcon icon = mIconManager.getIcon(label.getText(), 12);
|
||||
|
||||
if(icon != null)
|
||||
{
|
||||
label.setIcon(icon);
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias;
|
||||
|
||||
import com.jidesoft.swing.JideTabbedPane;
|
||||
import io.github.dsheirer.alias.action.AliasActionEditor;
|
||||
import io.github.dsheirer.alias.id.AliasIdentifierEditor;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastModel;
|
||||
import io.github.dsheirer.gui.editor.Editor;
|
||||
import io.github.dsheirer.icon.IconManager;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
public class AliasEditor extends Editor<Alias>
|
||||
implements ActionListener, Listener<AliasEvent>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String DEFAULT_NAME = "select an alias";
|
||||
|
||||
private AliasModel mAliasModel;
|
||||
|
||||
private JLabel mAliasLabel;
|
||||
private AliasNameEditor mAliasNameEditor;
|
||||
private AliasIdentifierEditor mAliasIdentifierEditor;
|
||||
private AliasActionEditor mAliasActionEditor;
|
||||
|
||||
public AliasEditor(AliasModel aliasModel, BroadcastModel broadcastModel, IconManager iconManager )
|
||||
{
|
||||
mAliasModel = aliasModel;
|
||||
mAliasNameEditor = new AliasNameEditor( mAliasModel, iconManager );
|
||||
mAliasIdentifierEditor = new AliasIdentifierEditor( aliasModel, broadcastModel );
|
||||
mAliasActionEditor = new AliasActionEditor( aliasModel );
|
||||
|
||||
mAliasModel.addListener( this );
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setItem( Alias alias )
|
||||
{
|
||||
super.setItem( alias );
|
||||
|
||||
if( hasItem() )
|
||||
{
|
||||
mAliasLabel.setText( getItem().getName() );
|
||||
}
|
||||
else
|
||||
{
|
||||
mAliasLabel.setText( DEFAULT_NAME );
|
||||
}
|
||||
|
||||
mAliasNameEditor.setItem( alias );
|
||||
mAliasIdentifierEditor.setItem( alias );
|
||||
mAliasActionEditor.setItem( alias );
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 2", "[grow,fill][grow,fill]",
|
||||
"[][][][][][grow,fill]" ) );
|
||||
|
||||
add( new JLabel( "Alias:" ), "align right" );
|
||||
mAliasLabel = new JLabel( DEFAULT_NAME );
|
||||
add( mAliasLabel, "align left" );
|
||||
|
||||
add( new JSeparator( JSeparator.HORIZONTAL ), "span,growx,push" );
|
||||
|
||||
JideTabbedPane tabs = new JideTabbedPane();
|
||||
tabs.setFont( this.getFont() );
|
||||
tabs.setForeground( Color.BLACK );
|
||||
|
||||
tabs.addTab( "Alias", mAliasNameEditor );
|
||||
tabs.addTab( "Audio / Identifier", mAliasIdentifierEditor );
|
||||
tabs.addTab( "Action", mAliasActionEditor );
|
||||
add( tabs, "span,grow" );
|
||||
|
||||
//Playlist management buttons
|
||||
JButton btnSave = new JButton( "Save" );
|
||||
btnSave.setToolTipText( "Save alias changes to the playlist" );
|
||||
btnSave.addActionListener( AliasEditor.this );
|
||||
add( btnSave );
|
||||
|
||||
JButton btnReset = new JButton( "Reset" );
|
||||
btnReset.setToolTipText( "Reset alias changes since last save" );
|
||||
btnReset.addActionListener( AliasEditor.this );
|
||||
add( btnReset, "wrap" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( AliasEvent event )
|
||||
{
|
||||
//If this is the currently displayed alias, reload it
|
||||
if( hasItem() && getItem() == event.getAlias() )
|
||||
{
|
||||
switch( event.getEvent() )
|
||||
{
|
||||
case CHANGE:
|
||||
//Alias changed - reset the editor with changed alias
|
||||
setItem( event.getAlias() );
|
||||
break;
|
||||
case DELETE:
|
||||
setItem( null );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
if( hasItem() )
|
||||
{
|
||||
String command = e.getActionCommand();
|
||||
|
||||
if( command.contentEquals( "Save" ) )
|
||||
{
|
||||
save();
|
||||
}
|
||||
else if( command.contentEquals( "Reset" ) )
|
||||
{
|
||||
reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
mAliasNameEditor.save();
|
||||
mAliasIdentifierEditor.save();
|
||||
mAliasActionEditor.save();
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias;
|
||||
|
||||
public class AliasEvent
|
||||
{
|
||||
private Alias mAlias;
|
||||
private Event mEvent;
|
||||
|
||||
/**
|
||||
* AliasEvent - event describing any changes to an alias
|
||||
* @param alias - alias that changed
|
||||
* @param event - change event
|
||||
*/
|
||||
public AliasEvent( Alias alias, Event event )
|
||||
{
|
||||
mAlias = alias;
|
||||
mEvent = event;
|
||||
}
|
||||
|
||||
public Alias getAlias()
|
||||
{
|
||||
return mAlias;
|
||||
}
|
||||
|
||||
public Event getEvent()
|
||||
{
|
||||
return mEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel events to describe the specific event
|
||||
*/
|
||||
public enum Event
|
||||
{
|
||||
ADD,
|
||||
CHANGE,
|
||||
DELETE;
|
||||
}
|
||||
}
|
||||
@@ -1,304 +1,301 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 Dennis Sheirer
|
||||
* *
|
||||
* * This program is free software: you can redistribute it and/or modify
|
||||
* * it under the terms of the GNU General Public License as published by
|
||||
* * the Free Software Foundation, either version 3 of the License, or
|
||||
* * (at your option) any later version.
|
||||
* *
|
||||
* * This program is distributed in the hope that it will be useful,
|
||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* * GNU General Public License for more details.
|
||||
* *
|
||||
* * You should have received a copy of the GNU General Public License
|
||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
*
|
||||
*/
|
||||
package io.github.dsheirer.alias;
|
||||
|
||||
import io.github.dsheirer.alias.action.AliasAction;
|
||||
import io.github.dsheirer.alias.action.AliasActionType;
|
||||
import io.github.dsheirer.alias.action.beep.BeepAction;
|
||||
import io.github.dsheirer.alias.action.beep.BeepActionEditor;
|
||||
import io.github.dsheirer.alias.action.clip.ClipAction;
|
||||
import io.github.dsheirer.alias.action.clip.ClipActionEditor;
|
||||
import io.github.dsheirer.alias.action.script.ScriptAction;
|
||||
import io.github.dsheirer.alias.action.script.ScriptActionEditor;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannelEditor;
|
||||
import io.github.dsheirer.alias.id.esn.ESNEditor;
|
||||
import io.github.dsheirer.alias.id.esn.Esn;
|
||||
import io.github.dsheirer.alias.id.fleetsync.FleetsyncID;
|
||||
import io.github.dsheirer.alias.id.fleetsync.FleetsyncIDEditor;
|
||||
import io.github.dsheirer.alias.id.legacy.fleetsync.FleetsyncID;
|
||||
import io.github.dsheirer.alias.id.legacy.mdc.MDC1200ID;
|
||||
import io.github.dsheirer.alias.id.legacy.mobileID.Min;
|
||||
import io.github.dsheirer.alias.id.legacy.mpt1327.MPT1327ID;
|
||||
import io.github.dsheirer.alias.id.legacy.nonrecordable.NonRecordable;
|
||||
import io.github.dsheirer.alias.id.legacy.siteID.SiteID;
|
||||
import io.github.dsheirer.alias.id.legacy.talkgroup.LegacyTalkgroupID;
|
||||
import io.github.dsheirer.alias.id.legacy.uniqueID.UniqueID;
|
||||
import io.github.dsheirer.alias.id.lojack.LoJackFunctionAndID;
|
||||
import io.github.dsheirer.alias.id.lojack.LoJackIDEditor;
|
||||
import io.github.dsheirer.alias.id.mdc.MDC1200ID;
|
||||
import io.github.dsheirer.alias.id.mdc.MDC1200IDEditor;
|
||||
import io.github.dsheirer.alias.id.mobileID.MINEditor;
|
||||
import io.github.dsheirer.alias.id.mobileID.Min;
|
||||
import io.github.dsheirer.alias.id.mpt1327.MPT1327ID;
|
||||
import io.github.dsheirer.alias.id.mpt1327.MPT1327IDEditor;
|
||||
import io.github.dsheirer.alias.id.nonrecordable.NonRecordable;
|
||||
import io.github.dsheirer.alias.id.nonrecordable.NonRecordableEditor;
|
||||
import io.github.dsheirer.alias.id.priority.Priority;
|
||||
import io.github.dsheirer.alias.id.priority.PriorityEditor;
|
||||
import io.github.dsheirer.alias.id.siteID.SiteID;
|
||||
import io.github.dsheirer.alias.id.siteID.SiteIDEditor;
|
||||
import io.github.dsheirer.alias.id.status.StatusID;
|
||||
import io.github.dsheirer.alias.id.status.StatusIDEditor;
|
||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupID;
|
||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupIDEditor;
|
||||
import io.github.dsheirer.alias.id.uniqueID.UniqueID;
|
||||
import io.github.dsheirer.alias.id.uniqueID.UniqueIDEditor;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastModel;
|
||||
import io.github.dsheirer.gui.editor.Editor;
|
||||
import io.github.dsheirer.gui.editor.EmptyEditor;
|
||||
import io.github.dsheirer.alias.id.radio.Radio;
|
||||
import io.github.dsheirer.alias.id.radio.RadioRange;
|
||||
import io.github.dsheirer.alias.id.record.Record;
|
||||
import io.github.dsheirer.alias.id.status.UnitStatusID;
|
||||
import io.github.dsheirer.alias.id.status.UserStatusID;
|
||||
import io.github.dsheirer.alias.id.talkgroup.Talkgroup;
|
||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange;
|
||||
import io.github.dsheirer.alias.id.tone.TonesID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AliasFactory
|
||||
{
|
||||
public static AliasID copyOf( AliasID id )
|
||||
{
|
||||
switch( id.getType() )
|
||||
{
|
||||
case BROADCAST_CHANNEL:
|
||||
BroadcastChannel originalBroadcast = (BroadcastChannel)id;
|
||||
BroadcastChannel copyBroadcast = new BroadcastChannel();
|
||||
copyBroadcast.setChannelName(originalBroadcast.getChannelName());
|
||||
return copyBroadcast;
|
||||
case ESN:
|
||||
Esn originalESN = (Esn)id;
|
||||
Esn copyESN = new Esn();
|
||||
copyESN.setEsn( originalESN.getEsn() );
|
||||
return copyESN;
|
||||
case FLEETSYNC:
|
||||
FleetsyncID originalFleetsyncID = (FleetsyncID)id;
|
||||
FleetsyncID copyFleetsyncID = new FleetsyncID();
|
||||
copyFleetsyncID.setIdent( originalFleetsyncID.getIdent() );
|
||||
return copyFleetsyncID;
|
||||
case LTR_NET_UID:
|
||||
UniqueID originalUniqueID = (UniqueID)id;
|
||||
UniqueID copyUniqueID = new UniqueID();
|
||||
copyUniqueID.setUid( originalUniqueID.getUid() );
|
||||
return copyUniqueID;
|
||||
case LOJACK:
|
||||
LoJackFunctionAndID originalLoJackFunctionAndID = (LoJackFunctionAndID)id;
|
||||
LoJackFunctionAndID copyLoJackFunctionAndID = new LoJackFunctionAndID();
|
||||
copyLoJackFunctionAndID.setFunction( originalLoJackFunctionAndID.getFunction() );
|
||||
copyLoJackFunctionAndID.setID( originalLoJackFunctionAndID.getID() );
|
||||
return copyLoJackFunctionAndID;
|
||||
case MDC1200:
|
||||
MDC1200ID originalMDC1200ID = (MDC1200ID)id;
|
||||
MDC1200ID copyMDC1200ID = new MDC1200ID();
|
||||
copyMDC1200ID.setIdent( originalMDC1200ID.getIdent() );
|
||||
return copyMDC1200ID;
|
||||
case MIN:
|
||||
Min originalMin = (Min)id;
|
||||
Min copyMin = new Min();
|
||||
copyMin.setMin( originalMin.getMin() );
|
||||
return copyMin;
|
||||
case MPT1327:
|
||||
MPT1327ID originalMPT1327ID = (MPT1327ID)id;
|
||||
MPT1327ID copyMPT1327ID = new MPT1327ID();
|
||||
copyMPT1327ID.setIdent( originalMPT1327ID.getIdent() );
|
||||
return copyMPT1327ID;
|
||||
case NON_RECORDABLE:
|
||||
return new NonRecordable();
|
||||
case PRIORITY:
|
||||
Priority originalPriority = (Priority)id;
|
||||
Priority copyPriority = new Priority();
|
||||
copyPriority.setPriority( originalPriority.getPriority() );
|
||||
return copyPriority;
|
||||
case SITE:
|
||||
SiteID originalSiteID = (SiteID)id;
|
||||
SiteID copySiteID = new SiteID();
|
||||
copySiteID.setSite( originalSiteID.getSite() );
|
||||
return copySiteID;
|
||||
case TALKGROUP:
|
||||
TalkgroupID originalTalkgroupID = (TalkgroupID)id;
|
||||
TalkgroupID copyTalkgroupID = new TalkgroupID();
|
||||
copyTalkgroupID.setTalkgroup( originalTalkgroupID.getTalkgroup() );
|
||||
return copyTalkgroupID;
|
||||
case STATUS:
|
||||
StatusID originalStatusID = (StatusID)id;
|
||||
StatusID copyStatusID = new StatusID();
|
||||
copyStatusID.setStatus( originalStatusID.getStatus() );
|
||||
return copyStatusID;
|
||||
default:
|
||||
}
|
||||
private static final Logger mLog = LoggerFactory.getLogger(AliasFactory.class);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static AliasAction copyOf( AliasAction action )
|
||||
{
|
||||
if( action instanceof BeepAction )
|
||||
{
|
||||
return new BeepAction();
|
||||
}
|
||||
else if( action instanceof ClipAction)
|
||||
{
|
||||
ClipAction originalClip = (ClipAction)action;
|
||||
ClipAction copyClip = new ClipAction();
|
||||
copyClip.setInterval( originalClip.getInterval() );
|
||||
copyClip.setPath( originalClip.getPath() );
|
||||
copyClip.setPeriod( originalClip.getPeriod() );
|
||||
return copyClip;
|
||||
}
|
||||
else if( action instanceof ScriptAction )
|
||||
{
|
||||
ScriptAction originalScript = (ScriptAction)action;
|
||||
ScriptAction copyScript = new ScriptAction();
|
||||
copyScript.setInterval( originalScript.getInterval() );
|
||||
copyScript.setPeriod( originalScript.getPeriod() );
|
||||
copyScript.setScript( originalScript.getScript() );
|
||||
return copyScript;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Alias copyOf( Alias original )
|
||||
{
|
||||
Alias copy = new Alias( original.getName() );
|
||||
copy.setList( original.getList() );
|
||||
copy.setGroup( original.getGroup() );
|
||||
copy.setColor( original.getColor() );
|
||||
copy.setIconName( original.getIconName() );
|
||||
|
||||
for( AliasID id: original.getId() )
|
||||
{
|
||||
AliasID copyID = copyOf( id );
|
||||
|
||||
if( copyID != null )
|
||||
{
|
||||
copy.addAliasID( copyID );
|
||||
}
|
||||
}
|
||||
public static AliasID copyOf(AliasID id)
|
||||
{
|
||||
switch(id.getType())
|
||||
{
|
||||
case BROADCAST_CHANNEL:
|
||||
BroadcastChannel originalBroadcast = (BroadcastChannel)id;
|
||||
BroadcastChannel copyBroadcast = new BroadcastChannel();
|
||||
copyBroadcast.setChannelName(originalBroadcast.getChannelName());
|
||||
return copyBroadcast;
|
||||
case ESN:
|
||||
Esn originalESN = (Esn)id;
|
||||
Esn copyESN = new Esn();
|
||||
copyESN.setEsn(originalESN.getEsn());
|
||||
return copyESN;
|
||||
case LOJACK:
|
||||
LoJackFunctionAndID originalLoJackFunctionAndID = (LoJackFunctionAndID)id;
|
||||
LoJackFunctionAndID copyLoJackFunctionAndID = new LoJackFunctionAndID();
|
||||
copyLoJackFunctionAndID.setFunction(originalLoJackFunctionAndID.getFunction());
|
||||
copyLoJackFunctionAndID.setID(originalLoJackFunctionAndID.getID());
|
||||
return copyLoJackFunctionAndID;
|
||||
case MIN:
|
||||
Min originalMin = (Min)id;
|
||||
Min copyMin = new Min();
|
||||
copyMin.setMin(originalMin.getMin());
|
||||
return copyMin;
|
||||
case PRIORITY:
|
||||
Priority originalPriority = (Priority)id;
|
||||
Priority copyPriority = new Priority();
|
||||
copyPriority.setPriority(originalPriority.getPriority());
|
||||
return copyPriority;
|
||||
case RADIO_ID:
|
||||
Radio originalRadio = (Radio)id;
|
||||
Radio copyRadio = new Radio(originalRadio.getProtocol(), originalRadio.getValue());
|
||||
copyRadio.setOverlap(originalRadio.overlapProperty().get());
|
||||
return copyRadio;
|
||||
case RADIO_ID_RANGE:
|
||||
RadioRange originalRadioRange = (RadioRange)id;
|
||||
RadioRange copyRadioRange = new RadioRange(originalRadioRange.getProtocol(), originalRadioRange.getMinRadio(),
|
||||
originalRadioRange.getMaxRadio());
|
||||
copyRadioRange.setOverlap(originalRadioRange.overlapProperty().get());
|
||||
return copyRadioRange;
|
||||
case RECORD:
|
||||
return new Record();
|
||||
case SITE:
|
||||
SiteID originalSiteID = (SiteID)id;
|
||||
SiteID copySiteID = new SiteID();
|
||||
copySiteID.setSite(originalSiteID.getSite());
|
||||
return copySiteID;
|
||||
case STATUS:
|
||||
UserStatusID originalUserStatusID = (UserStatusID)id;
|
||||
UserStatusID copyUserStatusID = new UserStatusID();
|
||||
copyUserStatusID.setStatus(originalUserStatusID.getStatus());
|
||||
copyUserStatusID.setOverlap(originalUserStatusID.overlapProperty().get());
|
||||
return copyUserStatusID;
|
||||
case TALKGROUP:
|
||||
Talkgroup originalTalkgroup = (Talkgroup)id;
|
||||
Talkgroup copyTalkgroup = new Talkgroup(originalTalkgroup.getProtocol(), originalTalkgroup.getValue());
|
||||
copyTalkgroup.setOverlap(originalTalkgroup.overlapProperty().get());
|
||||
return copyTalkgroup;
|
||||
case TALKGROUP_RANGE:
|
||||
TalkgroupRange originalRange = (TalkgroupRange)id;
|
||||
TalkgroupRange copyRange = new TalkgroupRange(originalRange.getProtocol(), originalRange.getMinTalkgroup(),
|
||||
originalRange.getMaxTalkgroup());
|
||||
copyRange.setOverlap(originalRange.overlapProperty().get());
|
||||
return copyRange;
|
||||
case TONES:
|
||||
TonesID originalTones = (TonesID)id;
|
||||
TonesID copyTones = new TonesID();
|
||||
copyTones.setToneSequence(originalTones.getToneSequence().copyOf());
|
||||
return copyTones;
|
||||
case UNIT_STATUS:
|
||||
UnitStatusID originalUnitStatus = (UnitStatusID)id;
|
||||
UnitStatusID copyUnitStatusID = new UnitStatusID();
|
||||
copyUnitStatusID.setStatus(originalUnitStatus.getStatus());
|
||||
copyUnitStatusID.setOverlap(originalUnitStatus.overlapProperty().get());
|
||||
return copyUnitStatusID;
|
||||
|
||||
for( AliasAction action: original.getAction() )
|
||||
{
|
||||
AliasAction copyAction = copyOf( action );
|
||||
|
||||
if( copyAction != null )
|
||||
{
|
||||
copy.addAliasAction( copyAction );
|
||||
}
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
//Legacy identifiers ... not supported
|
||||
case FLEETSYNC:
|
||||
case LEGACY_TALKGROUP:
|
||||
case LTR_NET_UID:
|
||||
case MDC1200:
|
||||
case MPT1327:
|
||||
case NON_RECORDABLE:
|
||||
return null;
|
||||
default:
|
||||
mLog.warn("Unrecognized Alias ID Type [" + id.getType() + "] cannot make copy of instance");
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static AliasAction copyOf(AliasAction action)
|
||||
{
|
||||
if(action instanceof BeepAction)
|
||||
{
|
||||
BeepAction original = (BeepAction)action;
|
||||
BeepAction copyBeep = new BeepAction();
|
||||
copyBeep.setInterval(original.getInterval());
|
||||
copyBeep.setPeriod(original.getPeriod());
|
||||
return copyBeep;
|
||||
}
|
||||
else if(action instanceof ClipAction)
|
||||
{
|
||||
ClipAction originalClip = (ClipAction)action;
|
||||
ClipAction copyClip = new ClipAction();
|
||||
copyClip.setInterval(originalClip.getInterval());
|
||||
copyClip.setPath(originalClip.getPath());
|
||||
copyClip.setPeriod(originalClip.getPeriod());
|
||||
return copyClip;
|
||||
}
|
||||
else if(action instanceof ScriptAction)
|
||||
{
|
||||
ScriptAction originalScript = (ScriptAction)action;
|
||||
ScriptAction copyScript = new ScriptAction();
|
||||
copyScript.setInterval(originalScript.getInterval());
|
||||
copyScript.setPeriod(originalScript.getPeriod());
|
||||
copyScript.setScript(originalScript.getScript());
|
||||
return copyScript;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Alias shallowCopyOf(Alias original)
|
||||
{
|
||||
Alias copy = new Alias(original.getName());
|
||||
copy.setAliasListName(original.getAliasListName());
|
||||
copy.setGroup(original.getGroup());
|
||||
copy.setColor(original.getColor());
|
||||
copy.setIconName(original.getIconName());
|
||||
return copy;
|
||||
}
|
||||
|
||||
public static List<AliasID> copyAliasIDs(Alias original)
|
||||
{
|
||||
List<AliasID> aliasIDS = new ArrayList<>();
|
||||
|
||||
for(AliasID id : original.getAliasIdentifiers())
|
||||
{
|
||||
aliasIDS.add(copyOf(id));
|
||||
}
|
||||
|
||||
return aliasIDS;
|
||||
}
|
||||
|
||||
public static List<AliasAction> copyAliasActions(Alias original)
|
||||
{
|
||||
List<AliasAction> actions = new ArrayList<>();
|
||||
|
||||
for(AliasAction aliasAction: original.getAliasActions())
|
||||
{
|
||||
actions.add(copyOf(aliasAction));
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
public static Alias copyOf(Alias original)
|
||||
{
|
||||
Alias copy = shallowCopyOf(original);
|
||||
|
||||
List<AliasID> aliasIDS = copyAliasIDs(original);
|
||||
|
||||
for(AliasID aliasID: aliasIDS)
|
||||
{
|
||||
copy.addAliasID(aliasID);
|
||||
}
|
||||
|
||||
List<AliasAction> actions = copyAliasActions(original);
|
||||
|
||||
for(AliasAction action: actions)
|
||||
{
|
||||
copy.addAliasAction(action);
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
public static AliasID getAliasID(AliasIDType type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case BROADCAST_CHANNEL:
|
||||
return new BroadcastChannel();
|
||||
case ESN:
|
||||
return new Esn();
|
||||
case FLEETSYNC:
|
||||
return new FleetsyncID();
|
||||
case LTR_NET_UID:
|
||||
return new UniqueID();
|
||||
case LOJACK:
|
||||
return new LoJackFunctionAndID();
|
||||
case MDC1200:
|
||||
return new MDC1200ID();
|
||||
case MIN:
|
||||
return new Min();
|
||||
case MPT1327:
|
||||
return new MPT1327ID();
|
||||
case NON_RECORDABLE:
|
||||
return new NonRecordable();
|
||||
case PRIORITY:
|
||||
return new Priority();
|
||||
case RADIO_ID:
|
||||
return new Radio();
|
||||
case RADIO_ID_RANGE:
|
||||
return new RadioRange();
|
||||
case RECORD:
|
||||
return new Record();
|
||||
case TALKGROUP:
|
||||
return new Talkgroup();
|
||||
case TALKGROUP_RANGE:
|
||||
return new TalkgroupRange();
|
||||
case SITE:
|
||||
return new SiteID();
|
||||
case STATUS:
|
||||
return new UserStatusID();
|
||||
case LEGACY_TALKGROUP:
|
||||
return new LegacyTalkgroupID();
|
||||
default:
|
||||
throw new IllegalArgumentException("Unrecognized Alias ID type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
public static AliasAction getAliasAction(AliasActionType type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case BEEP:
|
||||
return new BeepAction();
|
||||
case CLIP:
|
||||
return new ClipAction();
|
||||
case SCRIPT:
|
||||
return new ScriptAction();
|
||||
default:
|
||||
throw new IllegalArgumentException("Unrecognized Alias Action type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
public static Editor<AliasID> getEditor( AliasID aliasID, BroadcastModel broadcastModel )
|
||||
{
|
||||
if( aliasID != null )
|
||||
{
|
||||
switch( aliasID.getType() )
|
||||
{
|
||||
case BROADCAST_CHANNEL:
|
||||
return new BroadcastChannelEditor(aliasID, broadcastModel);
|
||||
case ESN:
|
||||
return new ESNEditor( aliasID );
|
||||
case FLEETSYNC:
|
||||
return new FleetsyncIDEditor( aliasID );
|
||||
case LTR_NET_UID:
|
||||
return new UniqueIDEditor( aliasID );
|
||||
case LOJACK:
|
||||
return new LoJackIDEditor( aliasID );
|
||||
case MDC1200:
|
||||
return new MDC1200IDEditor( aliasID );
|
||||
case MIN:
|
||||
return new MINEditor( aliasID );
|
||||
case MPT1327:
|
||||
return new MPT1327IDEditor( aliasID );
|
||||
case NON_RECORDABLE:
|
||||
return new NonRecordableEditor( aliasID );
|
||||
case PRIORITY:
|
||||
return new PriorityEditor( aliasID );
|
||||
case SITE:
|
||||
return new SiteIDEditor( aliasID );
|
||||
case STATUS:
|
||||
return new StatusIDEditor( aliasID );
|
||||
case TALKGROUP:
|
||||
return new TalkgroupIDEditor( aliasID );
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new EmptyEditor<AliasID>();
|
||||
}
|
||||
|
||||
public static Editor<AliasAction> getEditor( AliasAction aliasAction )
|
||||
{
|
||||
if( aliasAction != null )
|
||||
{
|
||||
switch( aliasAction.getType() )
|
||||
{
|
||||
case BEEP:
|
||||
return new BeepActionEditor( aliasAction );
|
||||
case CLIP:
|
||||
return new ClipActionEditor( aliasAction );
|
||||
case SCRIPT:
|
||||
return new ScriptActionEditor( aliasAction );
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new EmptyEditor<AliasAction>();
|
||||
}
|
||||
|
||||
public static AliasID getAliasID( AliasIDType type )
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case BROADCAST_CHANNEL:
|
||||
return new BroadcastChannel();
|
||||
case ESN:
|
||||
return new Esn();
|
||||
case FLEETSYNC:
|
||||
return new FleetsyncID();
|
||||
case LTR_NET_UID:
|
||||
return new UniqueID();
|
||||
case LOJACK:
|
||||
return new LoJackFunctionAndID();
|
||||
case MDC1200:
|
||||
return new MDC1200ID();
|
||||
case MIN:
|
||||
return new Min();
|
||||
case MPT1327:
|
||||
return new MPT1327ID();
|
||||
case NON_RECORDABLE:
|
||||
return new NonRecordable();
|
||||
case PRIORITY:
|
||||
return new Priority();
|
||||
case SITE:
|
||||
return new SiteID();
|
||||
case STATUS:
|
||||
return new StatusID();
|
||||
case TALKGROUP:
|
||||
return new TalkgroupID();
|
||||
default:
|
||||
throw new IllegalArgumentException( "Unrecognized Alias ID type: " + type );
|
||||
}
|
||||
}
|
||||
|
||||
public static AliasAction getAliasAction( AliasActionType type )
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case BEEP:
|
||||
return new BeepAction();
|
||||
case CLIP:
|
||||
return new ClipAction();
|
||||
case SCRIPT:
|
||||
return new ScriptAction();
|
||||
default:
|
||||
throw new IllegalArgumentException( "Unrecognized Alias Action type: " + type );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,53 +1,70 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
/*
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 Dennis Sheirer
|
||||
* *
|
||||
* * This program is free software: you can redistribute it and/or modify
|
||||
* * it under the terms of the GNU General Public License as published by
|
||||
* * the Free Software Foundation, either version 3 of the License, or
|
||||
* * (at your option) any later version.
|
||||
* *
|
||||
* * This program is distributed in the hope that it will be useful,
|
||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* * GNU General Public License for more details.
|
||||
* *
|
||||
* * You should have received a copy of the GNU General Public License
|
||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
*/
|
||||
package io.github.dsheirer.alias;
|
||||
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.identifier.IdentifierCollection;
|
||||
import io.github.dsheirer.identifier.configuration.AliasListConfigurationIdentifier;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Alias Model
|
||||
* Alias Model contains all aliases and is responsible for creation and management of alias lists. Alias lists are a
|
||||
* set of aliases that all share a common alias list name and can be attached to a decoding channel for aliasing
|
||||
* identifiers produced by channel decoder(s).
|
||||
*/
|
||||
public class AliasModel extends AbstractTableModel
|
||||
public class AliasModel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final int COLUMN_LIST = 0;
|
||||
public static final int COLUMN_GROUP = 1;
|
||||
public static final int COLUMN_NAME = 2;
|
||||
public static final int COLUMN_ICON = 3;
|
||||
public static final int COLUMN_COLOR = 4;
|
||||
|
||||
private List<Alias> mAliases = new CopyOnWriteArrayList<>();
|
||||
private Broadcaster<AliasEvent> mAliasEventBroadcaster = new Broadcaster<>();
|
||||
private final static Logger mLog = LoggerFactory.getLogger(AliasModel.class);
|
||||
public static final String NO_ALIAS_LIST = "(No Alias List)";
|
||||
private ObservableList<Alias> mAliases = FXCollections.observableArrayList(Alias.extractor());
|
||||
private ObservableList<String> mAliasListNames = FXCollections.observableArrayList();
|
||||
private Map<String,AliasList> mAliasListMap = new HashMap<>();
|
||||
|
||||
public AliasModel()
|
||||
{
|
||||
//Register a listener to detect alias changes and broadcast change events to cause playlist save requests
|
||||
mAliases.addListener(new AliasListChangeListener());
|
||||
}
|
||||
|
||||
public ObservableList<Alias> aliasList()
|
||||
{
|
||||
return mAliases;
|
||||
}
|
||||
|
||||
public ObservableList<String> aliasListNames()
|
||||
{
|
||||
return mAliasListNames;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,41 +75,82 @@ public class AliasModel extends AbstractTableModel
|
||||
return Collections.unmodifiableList(mAliases);
|
||||
}
|
||||
|
||||
public Alias getAliasAtIndex(int row)
|
||||
/**
|
||||
* Removes all aliases from the list and broadcasts the alias delete event for each
|
||||
*/
|
||||
public void clear()
|
||||
{
|
||||
if(mAliases.size() >= row)
|
||||
List<Alias> aliasToRemove = new ArrayList<>(mAliases);
|
||||
|
||||
for(Alias alias: aliasToRemove)
|
||||
{
|
||||
return mAliases.get(row);
|
||||
removeAlias(alias);
|
||||
}
|
||||
|
||||
mAliasListNames.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an optional alias list associated with the identifier collection
|
||||
*
|
||||
* @return alias list or null
|
||||
*/
|
||||
public AliasList getAliasList(IdentifierCollection identifierCollection)
|
||||
{
|
||||
if(identifierCollection != null)
|
||||
{
|
||||
return getAliasList(identifierCollection.getAliasListConfiguration());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an alias list specified by the alias list configuration identifier
|
||||
*
|
||||
* @param configurationIdentifier containing the name of an alias list
|
||||
* @return alias list or null.
|
||||
*/
|
||||
public AliasList getAliasList(AliasListConfigurationIdentifier configurationIdentifier)
|
||||
{
|
||||
if(configurationIdentifier != null && configurationIdentifier.isValid())
|
||||
{
|
||||
return getAliasList(configurationIdentifier.getValue());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new alias list containing all aliases that match the alias name, or returns a previously created and
|
||||
* cached alias list. Returned alias list is automatically registered as a listener to this model so that any
|
||||
* updates to the list by the user will automatically be reflected in constructed alias lists.
|
||||
*/
|
||||
public AliasList getAliasList(String name)
|
||||
{
|
||||
if(name != null && mAliasListMap.containsKey(name))
|
||||
if(name == null || name.isEmpty())
|
||||
{
|
||||
return mAliasListMap.get(name);
|
||||
return new AliasList(name);
|
||||
}
|
||||
|
||||
AliasList mapValue = mAliasListMap.get(name);
|
||||
if (mapValue != null)
|
||||
{
|
||||
return mapValue;
|
||||
}
|
||||
|
||||
AliasList aliasList = new AliasList(name);
|
||||
|
||||
if(name != null)
|
||||
for(Alias alias : mAliases)
|
||||
{
|
||||
for(Alias alias : mAliases)
|
||||
if(alias.hasList() && alias.getAliasListName().equalsIgnoreCase(name))
|
||||
{
|
||||
if(alias.hasList() && alias.getList().equalsIgnoreCase(name))
|
||||
{
|
||||
aliasList.addAlias(alias);
|
||||
}
|
||||
aliasList.addAlias(alias);
|
||||
}
|
||||
|
||||
mAliasListMap.put(name, aliasList);
|
||||
|
||||
//Register the new alias list to receive updates from this model
|
||||
addListener(aliasList);
|
||||
}
|
||||
|
||||
mAliasListMap.put(name, aliasList);
|
||||
|
||||
return aliasList;
|
||||
}
|
||||
|
||||
@@ -101,19 +159,7 @@ public class AliasModel extends AbstractTableModel
|
||||
*/
|
||||
public List<String> getListNames()
|
||||
{
|
||||
List<String> listNames = new ArrayList<>();
|
||||
|
||||
for(Alias alias : mAliases)
|
||||
{
|
||||
if(alias.hasList() && !listNames.contains(alias.getList()))
|
||||
{
|
||||
listNames.add(alias.getList());
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(listNames);
|
||||
|
||||
return listNames;
|
||||
return mAliasListNames;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,33 +182,6 @@ public class AliasModel extends AbstractTableModel
|
||||
return groupNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of alias group names for all aliases that have a matching
|
||||
* list name value
|
||||
*/
|
||||
public List<String> getGroupNames(String listName)
|
||||
{
|
||||
List<String> groupNames = new ArrayList<>();
|
||||
|
||||
if(listName != null)
|
||||
{
|
||||
for(Alias alias : mAliases)
|
||||
{
|
||||
if(alias.hasList() &&
|
||||
alias.hasGroup() &&
|
||||
listName.equals(alias.getList()) &&
|
||||
!groupNames.contains(alias.getGroup()))
|
||||
{
|
||||
groupNames.add(alias.getGroup());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(groupNames);
|
||||
|
||||
return groupNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk loading of aliases
|
||||
*/
|
||||
@@ -177,173 +196,177 @@ public class AliasModel extends AbstractTableModel
|
||||
/**
|
||||
* Adds the alias to the model
|
||||
*/
|
||||
public int addAlias(Alias alias)
|
||||
public void addAlias(Alias alias)
|
||||
{
|
||||
if(alias != null)
|
||||
if(mAliases.contains(alias))
|
||||
{
|
||||
mAliases.add(alias);
|
||||
|
||||
int index = mAliases.size() - 1;
|
||||
|
||||
fireTableRowsInserted(index, index);
|
||||
|
||||
broadcast(new AliasEvent(alias, AliasEvent.Event.ADD));
|
||||
|
||||
return index;
|
||||
removeAlias(alias);
|
||||
}
|
||||
|
||||
return -1;
|
||||
mAliases.add(alias);
|
||||
}
|
||||
|
||||
private boolean hasAliasList(String aliasListName)
|
||||
{
|
||||
return aliasListName != null && mAliasListMap.containsKey(aliasListName);
|
||||
}
|
||||
|
||||
public void addAliasList(String aliasListName)
|
||||
{
|
||||
if(aliasListName != null && !aliasListName.isEmpty())
|
||||
{
|
||||
if(!mAliasListNames.contains(aliasListName))
|
||||
{
|
||||
mAliasListNames.add(aliasListName);
|
||||
}
|
||||
}
|
||||
else if(!mAliasListNames.contains(NO_ALIAS_LIST))
|
||||
{
|
||||
//This list allows users to view unassigned aliases so that they can move them to a valid alias list, but
|
||||
// it is not assignable to a channel
|
||||
mAliasListNames.add(NO_ALIAS_LIST);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the channel from the model and broadcasts a channel remove event
|
||||
* Removes the alias from this model and an alias list (if one exists)
|
||||
*/
|
||||
public void removeAlias(Alias alias)
|
||||
{
|
||||
if(alias != null)
|
||||
{
|
||||
int index = mAliases.indexOf(alias);
|
||||
|
||||
mAliases.remove(alias);
|
||||
|
||||
fireTableRowsDeleted(index, index);
|
||||
|
||||
broadcast(new AliasEvent(alias, AliasEvent.Event.DELETE));
|
||||
if(hasAliasList(alias.getAliasListName()))
|
||||
{
|
||||
getAliasList(alias.getAliasListName()).removeAlias(alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames any broadcast channels that have the previous name.
|
||||
*
|
||||
* @param previousName to rename
|
||||
* @param newName to assign to the broadcast channel
|
||||
* Retrieves all aliases that match the alias list and have at least one alias ID of the specified type
|
||||
* @param aliasListName to search
|
||||
* @param type to find
|
||||
* @return list of aliases
|
||||
*/
|
||||
public void renameBroadcastChannel(String previousName, String newName)
|
||||
public List<Alias> getAliases(String aliasListName, AliasIDType type)
|
||||
{
|
||||
if(previousName == null || previousName.isEmpty() || newName == null || newName.isEmpty())
|
||||
List<Alias> aliases = new ArrayList<>();
|
||||
|
||||
for(Alias alias : mAliases)
|
||||
{
|
||||
if(alias.hasList() && alias.getAliasListName().equalsIgnoreCase(aliasListName))
|
||||
{
|
||||
for(AliasID aliasID: alias.getAliasIdentifiers())
|
||||
{
|
||||
if(aliasID.getType() == type)
|
||||
{
|
||||
aliases.add(alias);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that one or more of the aliases managed by this model are configured to stream to the specified
|
||||
* broadcast channel argument.
|
||||
* @param broadcastChannel to check
|
||||
* @return true if the broadcast channel is non-null, non-empty and at least one alias is configured to stream to
|
||||
* the specified stream name.
|
||||
*/
|
||||
public boolean hasAliasesWithBroadcastChannel(String broadcastChannel)
|
||||
{
|
||||
if(broadcastChannel == null || broadcastChannel.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for(Alias alias: mAliases)
|
||||
{
|
||||
if(alias.hasBroadcastChannel(broadcastChannel))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all aliases configured to stream to the previousStreamName with the updatedStreamName
|
||||
* @param previousStreamName to be removed
|
||||
* @param updatedStreamName to be added
|
||||
*/
|
||||
public void updateBroadcastChannel(String previousStreamName, String updatedStreamName)
|
||||
{
|
||||
if(previousStreamName == null || previousStreamName.isEmpty() || updatedStreamName == null || updatedStreamName.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for(Alias alias : mAliases)
|
||||
for(Alias alias: mAliases)
|
||||
{
|
||||
if(alias.hasBroadcastChannel(previousName))
|
||||
if(alias.hasBroadcastChannel(previousStreamName))
|
||||
{
|
||||
for(BroadcastChannel broadcastChannel : alias.getBroadcastChannels())
|
||||
for(BroadcastChannel broadcastChannel: alias.getBroadcastChannels())
|
||||
{
|
||||
if(broadcastChannel.getChannelName().contentEquals(previousName))
|
||||
if(broadcastChannel.getChannelName().contentEquals(previousStreamName))
|
||||
{
|
||||
broadcastChannel.setChannelName(newName);
|
||||
alias.removeAliasID(broadcastChannel);
|
||||
|
||||
if(!alias.hasBroadcastChannel(updatedStreamName))
|
||||
{
|
||||
alias.addAliasID(new BroadcastChannel(updatedStreamName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount()
|
||||
/**
|
||||
* Monitors alias additions and removals and updates alias lists
|
||||
*/
|
||||
public class AliasListChangeListener implements ListChangeListener<Alias>
|
||||
{
|
||||
return mAliases.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount()
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int columnIndex)
|
||||
{
|
||||
switch(columnIndex)
|
||||
@Override
|
||||
public void onChanged(ListChangeListener.Change<? extends Alias> change)
|
||||
{
|
||||
case COLUMN_LIST:
|
||||
return "List";
|
||||
case COLUMN_GROUP:
|
||||
return "Group";
|
||||
case COLUMN_NAME:
|
||||
return "Name";
|
||||
case COLUMN_ICON:
|
||||
return "Icon";
|
||||
case COLUMN_COLOR:
|
||||
return "Color";
|
||||
while(change.next())
|
||||
{
|
||||
if(change.wasAdded())
|
||||
{
|
||||
for(Alias alias: change.getAddedSubList())
|
||||
{
|
||||
addAliasList(alias.getAliasListName());
|
||||
|
||||
if(hasAliasList(alias.getAliasListName()))
|
||||
{
|
||||
getAliasList(alias.getAliasListName()).addAlias(alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(change.wasRemoved())
|
||||
{
|
||||
for(Alias alias: change.getRemoved())
|
||||
{
|
||||
if(hasAliasList(alias.getAliasListName()))
|
||||
{
|
||||
AliasList aliasList = getAliasList(alias.getName());
|
||||
|
||||
if(alias != null)
|
||||
{
|
||||
aliasList.removeAlias(alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex)
|
||||
{
|
||||
if(columnIndex == COLUMN_COLOR)
|
||||
{
|
||||
return Integer.class;
|
||||
}
|
||||
|
||||
return String.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCellEditable(int rowIndex, int columnIndex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex)
|
||||
{
|
||||
Alias alias = mAliases.get(rowIndex);
|
||||
|
||||
switch(columnIndex)
|
||||
{
|
||||
case COLUMN_LIST:
|
||||
return alias.getList();
|
||||
case COLUMN_GROUP:
|
||||
return alias.getGroup();
|
||||
case COLUMN_NAME:
|
||||
return alias.getName();
|
||||
case COLUMN_ICON:
|
||||
return alias.getIconName();
|
||||
case COLUMN_COLOR:
|
||||
return alias.getColor();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAt(Object aValue, int rowIndex, int columnIndex)
|
||||
{
|
||||
throw new IllegalArgumentException("Not yet implemented");
|
||||
}
|
||||
|
||||
public void broadcast(AliasEvent event)
|
||||
{
|
||||
Alias alias = event.getAlias();
|
||||
|
||||
//Validate the alias following a user action that changed the alias or any alias IDs
|
||||
if(alias != null)
|
||||
{
|
||||
alias.validate();
|
||||
}
|
||||
|
||||
if(event.getEvent() == AliasEvent.Event.CHANGE)
|
||||
{
|
||||
int index = mAliases.indexOf(event.getAlias());
|
||||
|
||||
fireTableRowsUpdated(index, index);
|
||||
}
|
||||
|
||||
mAliasEventBroadcaster.broadcast(event);
|
||||
}
|
||||
|
||||
public void addListener(Listener<AliasEvent> listener)
|
||||
{
|
||||
mAliasEventBroadcaster.addListener(listener);
|
||||
}
|
||||
|
||||
public void removeListener(Listener<AliasEvent> listener)
|
||||
{
|
||||
mAliasEventBroadcaster.removeListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,333 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias;
|
||||
|
||||
import io.github.dsheirer.gui.editor.Editor;
|
||||
import io.github.dsheirer.icon.Icon;
|
||||
import io.github.dsheirer.icon.IconListCellRenderer;
|
||||
import io.github.dsheirer.icon.IconManager;
|
||||
import io.github.dsheirer.icon.IconTableModel;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.event.TableModelEvent;
|
||||
import javax.swing.event.TableModelListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.List;
|
||||
|
||||
public class AliasNameEditor extends Editor<Alias>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static ComboBoxModel<String> EMPTY_LIST_MODEL = new DefaultComboBoxModel<>();
|
||||
private static ComboBoxModel<String> EMPTY_GROUP_MODEL = new DefaultComboBoxModel<>();
|
||||
|
||||
private JComboBox<String> mListCombo = new JComboBox<>(EMPTY_LIST_MODEL);
|
||||
private JComboBox<String> mGroupCombo = new JComboBox<>(EMPTY_GROUP_MODEL);
|
||||
private JComboBox<Icon> mIconCombo;
|
||||
private JTextField mName;
|
||||
private JButton mButtonColor;
|
||||
private JButton mBtnIconManager;
|
||||
|
||||
private AliasModel mAliasModel;
|
||||
private IconManager mIconManager;
|
||||
|
||||
public AliasNameEditor(AliasModel aliasModel, IconManager iconManager)
|
||||
{
|
||||
mAliasModel = aliasModel;
|
||||
mIconManager = iconManager;
|
||||
|
||||
mIconManager.getModel().addTableModelListener(new TableModelListener()
|
||||
{
|
||||
@Override
|
||||
public void tableChanged(TableModelEvent e)
|
||||
{
|
||||
refreshIcons();
|
||||
}
|
||||
});
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout(new MigLayout("fill,wrap 2", "[right][grow,fill]", "[][][][][][][grow]"));
|
||||
|
||||
add(new JLabel("Name:"));
|
||||
mName = new JTextField();
|
||||
mName.getDocument().addDocumentListener(new DocumentListener()
|
||||
{
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e)
|
||||
{
|
||||
setModified(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e)
|
||||
{
|
||||
setModified(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e)
|
||||
{
|
||||
}
|
||||
});
|
||||
add(mName, "wrap");
|
||||
|
||||
add(new JLabel("List:"));
|
||||
mListCombo.setEditable(true);
|
||||
mListCombo.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
if(mListCombo.getSelectedItem() != null)
|
||||
{
|
||||
List<String> groups = mAliasModel
|
||||
.getGroupNames((String) mListCombo.getSelectedItem());
|
||||
|
||||
if(groups.isEmpty())
|
||||
{
|
||||
mGroupCombo.setModel(EMPTY_GROUP_MODEL);
|
||||
}
|
||||
else
|
||||
{
|
||||
mGroupCombo.setModel(new DefaultComboBoxModel<String>(
|
||||
groups.toArray(new String[groups.size()])));
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
setModified(true);
|
||||
}
|
||||
});
|
||||
add(mListCombo, "wrap");
|
||||
|
||||
add(new JLabel("Group:"));
|
||||
mGroupCombo.setEditable(true);
|
||||
mGroupCombo.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
setModified(true);
|
||||
}
|
||||
});
|
||||
add(mGroupCombo, "wrap");
|
||||
|
||||
add(new JLabel("Color:"));
|
||||
|
||||
mButtonColor = new JButton("Select ...");
|
||||
mButtonColor.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
Color newColor = JColorChooser.showDialog(
|
||||
AliasNameEditor.this,
|
||||
"Choose color for this alias",
|
||||
(hasItem() ? getItem().getDisplayColor() : null));
|
||||
|
||||
if(newColor != null)
|
||||
{
|
||||
mButtonColor.setForeground(newColor);
|
||||
mButtonColor.setBackground(newColor);
|
||||
|
||||
setModified(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
add(mButtonColor, "wrap");
|
||||
|
||||
add(new JLabel("Icon:"));
|
||||
|
||||
mIconCombo = new JComboBox<Icon>(mIconManager.getIcons());
|
||||
|
||||
IconListCellRenderer renderer = new IconListCellRenderer(mIconManager);
|
||||
renderer.setPreferredSize(new Dimension(200, 30));
|
||||
mIconCombo.setRenderer(renderer);
|
||||
mIconCombo.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
setModified(true);
|
||||
}
|
||||
});
|
||||
|
||||
add(mIconCombo, "wrap");
|
||||
|
||||
//Dummy place holder
|
||||
add(new JLabel());
|
||||
|
||||
mBtnIconManager = new JButton("Icon Manager");
|
||||
mBtnIconManager.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0)
|
||||
{
|
||||
mIconManager.showEditor(AliasNameEditor.this);
|
||||
}
|
||||
});
|
||||
|
||||
add(mBtnIconManager, "span 2,wrap");
|
||||
|
||||
setModified(false);
|
||||
}
|
||||
|
||||
private void refreshIcons()
|
||||
{
|
||||
if(mIconCombo != null)
|
||||
{
|
||||
EventQueue.invokeLater(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
mIconCombo.setModel(new DefaultComboBoxModel<>(mIconManager.getIcons()));
|
||||
|
||||
if(hasItem())
|
||||
{
|
||||
String iconName = getItem().getIconName();
|
||||
Icon aliasIcon = mIconManager.getModel().getIcon(iconName);
|
||||
|
||||
if(aliasIcon != null)
|
||||
{
|
||||
mIconCombo.setSelectedItem(aliasIcon);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(Alias alias)
|
||||
{
|
||||
super.setItem(alias);
|
||||
|
||||
if(hasItem())
|
||||
{
|
||||
mName.setText(alias.getName());
|
||||
|
||||
mIconCombo.setModel(new DefaultComboBoxModel<>(mIconManager.getIcons()));
|
||||
|
||||
List<String> listNames = mAliasModel.getListNames();
|
||||
|
||||
if(listNames.isEmpty())
|
||||
{
|
||||
mListCombo.setModel(EMPTY_LIST_MODEL);
|
||||
}
|
||||
else
|
||||
{
|
||||
mListCombo.setModel(new DefaultComboBoxModel<String>(
|
||||
listNames.toArray(new String[listNames.size()])));
|
||||
;
|
||||
}
|
||||
|
||||
mListCombo.setSelectedItem(alias.getList());
|
||||
|
||||
List<String> groupNames = mAliasModel.getGroupNames(alias.getList());
|
||||
|
||||
if(groupNames.isEmpty())
|
||||
{
|
||||
mGroupCombo.setModel(EMPTY_GROUP_MODEL);
|
||||
}
|
||||
else
|
||||
{
|
||||
mGroupCombo.setModel(new DefaultComboBoxModel<String>(
|
||||
groupNames.toArray(new String[groupNames.size()])));
|
||||
;
|
||||
}
|
||||
|
||||
mGroupCombo.setSelectedItem(alias.getGroup());
|
||||
|
||||
Color color = alias.getDisplayColor();
|
||||
|
||||
mButtonColor.setBackground(color);
|
||||
mButtonColor.setForeground(color);
|
||||
|
||||
String iconName = alias.getIconName();
|
||||
|
||||
if(iconName == null)
|
||||
{
|
||||
iconName = IconTableModel.DEFAULT_ICON;
|
||||
}
|
||||
|
||||
Icon savedIcon = mIconManager.getModel().getIcon(iconName);
|
||||
|
||||
if(savedIcon != null)
|
||||
{
|
||||
mIconCombo.setSelectedItem(savedIcon);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mListCombo.setModel(EMPTY_LIST_MODEL);
|
||||
mGroupCombo.setModel(EMPTY_GROUP_MODEL);
|
||||
mName.setText(null);
|
||||
|
||||
mButtonColor.setBackground(getBackground());
|
||||
mButtonColor.setForeground(getForeground());
|
||||
}
|
||||
|
||||
repaint();
|
||||
|
||||
setModified(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
if(hasItem() && isModified())
|
||||
{
|
||||
Alias alias = getItem();
|
||||
|
||||
if(mListCombo.getSelectedItem() != null)
|
||||
{
|
||||
alias.setList((String) mListCombo.getSelectedItem());
|
||||
}
|
||||
|
||||
if(mGroupCombo.getSelectedItem() != null)
|
||||
{
|
||||
alias.setGroup((String) mGroupCombo.getSelectedItem());
|
||||
}
|
||||
|
||||
alias.setName(mName.getText());
|
||||
|
||||
alias.setColor(mButtonColor.getBackground().getRGB());
|
||||
|
||||
if(mIconCombo.getSelectedItem() != null)
|
||||
{
|
||||
alias.setIconName(((Icon) mIconCombo.getSelectedItem()).getName());
|
||||
}
|
||||
|
||||
setModified(false);
|
||||
|
||||
//Broadcast an alias change event to save the updates
|
||||
mAliasModel.broadcast(new AliasEvent(getItem(), AliasEvent.Event.CHANGE));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentListener;
|
||||
|
||||
public abstract class ComponentEditor<T> extends JPanel implements DocumentListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private boolean mModified;
|
||||
private ComponentModificationListener mListener;
|
||||
protected T mComponent;
|
||||
|
||||
/**
|
||||
* Abstract class for editor with notifications when contents are modified
|
||||
* and methods for saving or resetting the component values with the
|
||||
* contents of the editor.
|
||||
*/
|
||||
public ComponentEditor( T component )
|
||||
{
|
||||
mComponent = component;
|
||||
}
|
||||
|
||||
public ComponentEditor()
|
||||
{
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mComponent = null;
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
public T getComponent()
|
||||
{
|
||||
return mComponent;
|
||||
}
|
||||
|
||||
protected void setModified( boolean modified )
|
||||
{
|
||||
mModified = modified;
|
||||
|
||||
if( modified && mListener != null )
|
||||
{
|
||||
mListener.componentModified();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the contents of the editor have been modified
|
||||
*/
|
||||
public boolean isModified()
|
||||
{
|
||||
return mModified;
|
||||
}
|
||||
|
||||
public void setComponentModificationListener( ComponentModificationListener listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets or resets the editable component
|
||||
*/
|
||||
public abstract void setComponent( T t );
|
||||
|
||||
/**
|
||||
* Saves changed editor values to the component
|
||||
*/
|
||||
public abstract void save();
|
||||
|
||||
/**
|
||||
* Reloads the editor contents from the source component and resets the
|
||||
* modified flag
|
||||
*/
|
||||
public void reset()
|
||||
{
|
||||
setComponent( getComponent() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate contents of other editors against this component editor before
|
||||
* saving. Override this method to allow controller to pass other editors
|
||||
* to this editor for content inspection/validation before indicating that
|
||||
* this editor passes validation. This allows for validating editor contents
|
||||
* that are contingent on settings in other editors.
|
||||
*
|
||||
* @throws ComponentValidationException if the contents of the editor
|
||||
* argument cannot be validated, with the exception containing a detailed
|
||||
* description of the issue.
|
||||
*/
|
||||
public void validate( ComponentEditor<?> editor ) throws ComponentValidationException
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener interface to receive notification that the contents of the
|
||||
* editor have been modified from the original component value and either
|
||||
* need to be saved or reset
|
||||
*/
|
||||
public interface ComponentModificationListener
|
||||
{
|
||||
public void componentModified();
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package io.github.dsheirer.alias;
|
||||
|
||||
public class ComponentValidationException extends Exception
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Component editor contents validation error
|
||||
*/
|
||||
public ComponentValidationException( String message )
|
||||
{
|
||||
super( message );
|
||||
}
|
||||
}
|
||||
@@ -1,466 +0,0 @@
|
||||
package io.github.dsheirer.alias;
|
||||
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
||||
import io.github.dsheirer.alias.id.priority.Priority;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastModel;
|
||||
import io.github.dsheirer.gui.editor.Editor;
|
||||
import io.github.dsheirer.icon.Icon;
|
||||
import io.github.dsheirer.icon.IconListCellRenderer;
|
||||
import io.github.dsheirer.icon.IconManager;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import javax.swing.event.TableModelEvent;
|
||||
import javax.swing.event.TableModelListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.List;
|
||||
|
||||
public class MultipleAliasEditor extends Editor<List<Alias>>
|
||||
implements Listener<AliasEvent>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final static Logger mLog = LoggerFactory.getLogger(MultipleAliasEditor.class);
|
||||
|
||||
private static String HELP_TEXT =
|
||||
"<html>Select attributes below to change for all selected aliases</html>";
|
||||
|
||||
private JLabel mAliasCountLabel;
|
||||
private JLabel mAliasCount;
|
||||
|
||||
private JCheckBox mListCheckBox = new JCheckBox("List");
|
||||
private JComboBox<String> mListCombo = new JComboBox<>();
|
||||
private JCheckBox mGroupCheckBox = new JCheckBox("Group");
|
||||
private JComboBox<String> mGroupCombo = new JComboBox<>();
|
||||
private JCheckBox mIconCheckBox = new JCheckBox("Icon");
|
||||
private JComboBox<Icon> mIconCombo;
|
||||
private JCheckBox mColorCheckBox = new JCheckBox("Color");
|
||||
private JButton mButtonColor;
|
||||
private JButton mBtnIconManager;
|
||||
private JCheckBox mRecordCheckBox = new JCheckBox("Record");
|
||||
private JComboBox<Record> mRecordActionCombo = new JComboBox<>(Record.values());
|
||||
private JCheckBox mPriorityCheckBox = new JCheckBox("Priority");
|
||||
private JSlider mPrioritySlider;
|
||||
private JLabel mPrioritySliderLabel;
|
||||
private JCheckBox mStreamCheckBox = new JCheckBox("Stream");
|
||||
private JComboBox<String> mStreamCombo;
|
||||
|
||||
private AliasModel mAliasModel;
|
||||
private BroadcastModel mBroadcastModel;
|
||||
private IconManager mIconManager;
|
||||
|
||||
public MultipleAliasEditor(AliasModel aliasModel, BroadcastModel broadcastModel, IconManager iconManager)
|
||||
{
|
||||
mAliasModel = aliasModel;
|
||||
mBroadcastModel = broadcastModel;
|
||||
mIconManager = iconManager;
|
||||
|
||||
mAliasModel.addListener(this);
|
||||
mIconManager.getModel().addTableModelListener(new TableModelListener()
|
||||
{
|
||||
@Override
|
||||
public void tableChanged(TableModelEvent e)
|
||||
{
|
||||
refreshIcons();
|
||||
}
|
||||
});
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
public List<Alias> getAliases()
|
||||
{
|
||||
if (hasItem())
|
||||
{
|
||||
return (List<Alias>) getItem();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void init()
|
||||
{
|
||||
setLayout(new MigLayout("fill,wrap 2", "[grow,fill][grow,fill]",
|
||||
"[][][][][][][][][][][][][][grow,fill]"));
|
||||
|
||||
mAliasCountLabel = new JLabel("Alias:");
|
||||
add(mAliasCountLabel);
|
||||
|
||||
mAliasCount = new JLabel("Multiple");
|
||||
add(mAliasCount, "span");
|
||||
|
||||
add(new JSeparator(), "span");
|
||||
|
||||
add(new JLabel(HELP_TEXT), "span");
|
||||
|
||||
add(mListCheckBox);
|
||||
|
||||
mListCombo.setEditable(true);
|
||||
add(mListCombo, "wrap");
|
||||
|
||||
add(mGroupCheckBox);
|
||||
|
||||
mGroupCombo.setEditable(true);
|
||||
add(mGroupCombo, "wrap");
|
||||
|
||||
add(mColorCheckBox);
|
||||
|
||||
mButtonColor = new JButton("Select ...");
|
||||
mButtonColor.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
Color newColor = JColorChooser.showDialog(
|
||||
MultipleAliasEditor.this,
|
||||
"Choose color for this alias", null);
|
||||
|
||||
if (newColor != null)
|
||||
{
|
||||
mButtonColor.setForeground(newColor);
|
||||
mButtonColor.setBackground(newColor);
|
||||
}
|
||||
}
|
||||
});
|
||||
add(mButtonColor, "wrap");
|
||||
|
||||
add(mIconCheckBox);
|
||||
|
||||
mIconCombo = new JComboBox<Icon>(mIconManager.getIcons());
|
||||
|
||||
IconListCellRenderer renderer = new IconListCellRenderer(mIconManager);
|
||||
renderer.setPreferredSize(new Dimension(200, 30));
|
||||
mIconCombo.setRenderer(renderer);
|
||||
add(mIconCombo, "wrap");
|
||||
|
||||
//Dummy place holder
|
||||
add(new JLabel());
|
||||
|
||||
mBtnIconManager = new JButton("Icon Manager");
|
||||
mBtnIconManager.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0)
|
||||
{
|
||||
mIconManager.showEditor(MultipleAliasEditor.this);
|
||||
}
|
||||
});
|
||||
|
||||
add(mBtnIconManager, "span,wrap");
|
||||
|
||||
add(mRecordCheckBox);
|
||||
add(mRecordActionCombo);
|
||||
|
||||
//Placeholder
|
||||
add(new JLabel(" "));
|
||||
mPrioritySliderLabel = new JLabel("Audio Priority: " + Priority.MIN_PRIORITY);
|
||||
add(mPrioritySliderLabel);
|
||||
|
||||
add(mPriorityCheckBox);
|
||||
|
||||
mPrioritySlider = new JSlider(JSlider.HORIZONTAL,
|
||||
Priority.MIN_PRIORITY,
|
||||
Priority.MAX_PRIORITY + 1,
|
||||
Priority.MIN_PRIORITY);
|
||||
|
||||
mPrioritySlider.setMajorTickSpacing(20);
|
||||
mPrioritySlider.setMinorTickSpacing(5);
|
||||
mPrioritySlider.setPaintTicks(true);
|
||||
mPrioritySlider.setLabelTable(mPrioritySlider.createStandardLabels(25, 25));
|
||||
mPrioritySlider.setPaintLabels(true);
|
||||
mPrioritySlider.addChangeListener(new ChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e)
|
||||
{
|
||||
int priority = mPrioritySlider.getValue();
|
||||
|
||||
if (priority > Priority.MAX_PRIORITY)
|
||||
{
|
||||
mPrioritySliderLabel.setText("Audio Priority: Do Not Monitor");
|
||||
}
|
||||
else
|
||||
{
|
||||
mPrioritySliderLabel.setText("Audio Priority: " + mPrioritySlider.getValue());
|
||||
}
|
||||
}
|
||||
});
|
||||
mPrioritySlider.setToolTipText(HELP_TEXT);
|
||||
|
||||
add(mPrioritySlider);
|
||||
|
||||
add(mStreamCheckBox);
|
||||
|
||||
mStreamCombo = new JComboBox<String>();
|
||||
mStreamCombo.setEditable(true);
|
||||
add(mStreamCombo, "wrap");
|
||||
|
||||
JButton saveButton = new JButton("Save");
|
||||
saveButton.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
save();
|
||||
}
|
||||
});
|
||||
add(saveButton);
|
||||
|
||||
JButton resetButton = new JButton("Reset");
|
||||
resetButton.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
});
|
||||
add(resetButton);
|
||||
|
||||
setModified(false);
|
||||
}
|
||||
|
||||
private void refreshIcons()
|
||||
{
|
||||
if(mIconCombo != null)
|
||||
{
|
||||
EventQueue.invokeLater(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
mIconCombo.setModel(new DefaultComboBoxModel<>(mIconManager.getIcons()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(List<Alias> item)
|
||||
{
|
||||
super.setItem(item);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Multiple Selected [ ");
|
||||
|
||||
if (hasItem())
|
||||
{
|
||||
sb.append(String.valueOf(getAliases().size()));
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append("0");
|
||||
}
|
||||
|
||||
sb.append(" ]");
|
||||
|
||||
mAliasCount.setText(sb.toString());
|
||||
|
||||
List<String> listNames = mAliasModel.getListNames();
|
||||
listNames.add(0, "");
|
||||
mListCombo.setModel(new DefaultComboBoxModel<String>(
|
||||
listNames.toArray(new String[listNames.size()])));
|
||||
|
||||
List<String> groupNames = mAliasModel.getGroupNames();
|
||||
groupNames.add(0, "");
|
||||
mGroupCombo.setModel(new DefaultComboBoxModel<String>(
|
||||
groupNames.toArray(new String[groupNames.size()])));
|
||||
|
||||
List<String> streamNames = mBroadcastModel.getBroadcastConfigurationNames();
|
||||
mStreamCombo.setModel(new DefaultComboBoxModel<String>(streamNames.toArray(new String[streamNames.size()])));
|
||||
|
||||
mIconCombo.setModel(new DefaultComboBoxModel<>(mIconManager.getIcons()));
|
||||
|
||||
setCheckBoxesUnselected();
|
||||
|
||||
setModified(false);
|
||||
}
|
||||
|
||||
private void setCheckBoxesUnselected()
|
||||
{
|
||||
mListCheckBox.setSelected(false);
|
||||
mGroupCheckBox.setSelected(false);
|
||||
mColorCheckBox.setSelected(false);
|
||||
mIconCheckBox.setSelected(false);
|
||||
mRecordCheckBox.setSelected(false);
|
||||
mPriorityCheckBox.setSelected(false);
|
||||
mStreamCheckBox.setSelected(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
List<String> listNames = mAliasModel.getListNames();
|
||||
listNames.add(0, "");
|
||||
mListCombo.setModel(new DefaultComboBoxModel<String>(
|
||||
listNames.toArray(new String[listNames.size()])));
|
||||
|
||||
List<String> groupNames = mAliasModel.getGroupNames();
|
||||
groupNames.add(0, "");
|
||||
mGroupCombo.setModel(new DefaultComboBoxModel<String>(
|
||||
groupNames.toArray(new String[groupNames.size()])));
|
||||
|
||||
mButtonColor.setForeground(getForeground());
|
||||
mButtonColor.setBackground(getBackground());
|
||||
|
||||
super.reset();
|
||||
}
|
||||
|
||||
private boolean hasRequestedChanges()
|
||||
{
|
||||
return mListCheckBox.isSelected() ||
|
||||
mGroupCheckBox.isSelected() ||
|
||||
mColorCheckBox.isSelected() ||
|
||||
mIconCheckBox.isSelected() ||
|
||||
mRecordCheckBox.isSelected() ||
|
||||
mPriorityCheckBox.isSelected() ||
|
||||
mStreamCheckBox.isSelected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
if (hasRequestedChanges() && hasItem())
|
||||
{
|
||||
List<Alias> aliases = getAliases();
|
||||
|
||||
for (Alias alias : aliases)
|
||||
{
|
||||
if (mListCheckBox.isSelected())
|
||||
{
|
||||
String list = null;
|
||||
|
||||
if (mListCombo.getSelectedItem() != null)
|
||||
{
|
||||
list = (String) mListCombo.getSelectedItem();
|
||||
}
|
||||
|
||||
alias.setList(list);
|
||||
}
|
||||
|
||||
if (mGroupCheckBox.isSelected())
|
||||
{
|
||||
String group = null;
|
||||
|
||||
if (mGroupCombo.getSelectedItem() != null)
|
||||
{
|
||||
group = (String) mGroupCombo.getSelectedItem();
|
||||
}
|
||||
|
||||
alias.setGroup(group);
|
||||
}
|
||||
|
||||
if (mColorCheckBox.isSelected())
|
||||
{
|
||||
alias.setColor(mButtonColor.getForeground().getRGB());
|
||||
}
|
||||
|
||||
if (mIconCheckBox.isSelected())
|
||||
{
|
||||
if (mIconCombo.getSelectedItem() != null)
|
||||
{
|
||||
alias.setIconName(((Icon)mIconCombo.getSelectedItem()).getName());
|
||||
}
|
||||
else
|
||||
{
|
||||
alias.setIconName(null);
|
||||
}
|
||||
}
|
||||
|
||||
if (mRecordCheckBox.isSelected())
|
||||
{
|
||||
Record action =
|
||||
(Record) mRecordActionCombo.getSelectedItem();
|
||||
|
||||
alias.setRecordable(action == Record.RECORDABLE);
|
||||
}
|
||||
|
||||
if (mPriorityCheckBox.isSelected())
|
||||
{
|
||||
int priority = mPrioritySlider.getValue();
|
||||
|
||||
//This is a work-around -- we use max priority + 1 in the
|
||||
//gui to indicate do not monitor, and change the value here
|
||||
if (priority == Priority.MAX_PRIORITY + 1)
|
||||
{
|
||||
priority = Priority.DO_NOT_MONITOR;
|
||||
}
|
||||
|
||||
alias.setCallPriority(priority);
|
||||
}
|
||||
|
||||
if(mStreamCheckBox.isSelected())
|
||||
{
|
||||
if(mStreamCombo.getSelectedItem() != null)
|
||||
{
|
||||
String streamName = (String)mStreamCombo.getSelectedItem();
|
||||
|
||||
boolean hasChannel = false;
|
||||
|
||||
//Ensure the alias doesn't already have the channel
|
||||
for(BroadcastChannel channel: alias.getBroadcastChannels())
|
||||
{
|
||||
if(channel.getChannelName().equals(streamName))
|
||||
{
|
||||
hasChannel = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if(!hasChannel)
|
||||
{
|
||||
BroadcastChannel channel = (BroadcastChannel)AliasFactory
|
||||
.getAliasID(AliasIDType.BROADCAST_CHANNEL);
|
||||
|
||||
channel.setChannelName(streamName);
|
||||
alias.addAliasID(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Alias alias : aliases)
|
||||
{
|
||||
//Broadcast an alias change event to save the updates
|
||||
mAliasModel.broadcast(new AliasEvent(alias, AliasEvent.Event.CHANGE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(AliasEvent event)
|
||||
{
|
||||
if (event.getEvent() == AliasEvent.Event.DELETE &&
|
||||
hasItem() &&
|
||||
getAliases().contains(event.getAlias()))
|
||||
{
|
||||
getAliases().remove(event.getAlias());
|
||||
setItem(getAliases());
|
||||
}
|
||||
}
|
||||
|
||||
public enum Record
|
||||
{
|
||||
NON_RECORDABLE("Non-Recordable"),
|
||||
RECORDABLE("Recordable");
|
||||
|
||||
private String mLabel;
|
||||
|
||||
private Record(String label)
|
||||
{
|
||||
mLabel = label;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,379 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias;
|
||||
|
||||
import io.github.dsheirer.alias.action.AliasAction;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
||||
import io.github.dsheirer.alias.id.priority.Priority;
|
||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class PatchGroupAlias extends Alias
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(PatchGroupAlias.class);
|
||||
|
||||
private Alias mPatchGroupAlias;
|
||||
private List<Alias> mPatchedAliases = new ArrayList<>();
|
||||
private List<String> mPatchedTalkgroupIDs = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Patch group alias is a single alias for a group of aliases that are temporarily patched together. This will
|
||||
* normally be used for patched talkgroups, but could be used with any type of alias identifiers that can be
|
||||
* temporarily joined to form a patch group.
|
||||
*
|
||||
* Alias properties such as recordable, streamable and call priority are an aggregation from the set of patched
|
||||
* aliases contained in this patch group alias and the patch group ID alias and where applicable, return the highest
|
||||
* value/priority from the group of patched aliases.
|
||||
*/
|
||||
public PatchGroupAlias()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColor()
|
||||
{
|
||||
if(mPatchGroupAlias != null)
|
||||
{
|
||||
return mPatchGroupAlias.getColor();
|
||||
}
|
||||
|
||||
return super.getColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an alias for this patch group ID
|
||||
*/
|
||||
public void setPatchGroupAlias(Alias alias)
|
||||
{
|
||||
mPatchGroupAlias = alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for this patch group
|
||||
*/
|
||||
public Alias getPatchGroupAlias()
|
||||
{
|
||||
return mPatchGroupAlias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this patch group has an alias
|
||||
*/
|
||||
public boolean hasPatchGroupAlias()
|
||||
{
|
||||
return mPatchGroupAlias != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of patched aliases contained in this patch group
|
||||
*/
|
||||
public List<Alias> getPatchedAliases()
|
||||
{
|
||||
return mPatchedAliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes/clears all patched aliases from this patch alias group
|
||||
*/
|
||||
public void clearPatchedAliases()
|
||||
{
|
||||
mPatchedAliases.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the patched alias to this patch alias group
|
||||
*/
|
||||
public void addPatchedAlias(Alias alias)
|
||||
{
|
||||
if(alias == PatchGroupAlias.this)
|
||||
{
|
||||
throw new IllegalArgumentException("Can't add patch group alias to itself - would create an infinite loop");
|
||||
}
|
||||
|
||||
mPatchedAliases.add(alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the patched alias from this patch alias group
|
||||
*/
|
||||
public void removePatchedAlias(Alias alias)
|
||||
{
|
||||
mPatchedAliases.remove(alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of patched talkgroup IDs for this patch group
|
||||
*/
|
||||
public void setPatchedTalkgroupIDs(List<String> talkgroupIDs)
|
||||
{
|
||||
mPatchedTalkgroupIDs = talkgroupIDs;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of patched talkgroup IDs for this patch group
|
||||
*/
|
||||
public List<String> getPatchedTalkgroupIDs()
|
||||
{
|
||||
return mPatchedTalkgroupIDs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
if(hasPatchGroupAlias())
|
||||
{
|
||||
return getPatchGroupAlias().getName();
|
||||
}
|
||||
else
|
||||
{
|
||||
for(AliasID id: getId())
|
||||
{
|
||||
if(id.getType() == AliasIDType.TALKGROUP)
|
||||
{
|
||||
return "PATCH:" + ((TalkgroupID)id).getTalkgroup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "PATCH:****";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AliasAction> getAction()
|
||||
{
|
||||
List<AliasAction> aliasActions = new ArrayList<>();
|
||||
|
||||
if(hasPatchGroupAlias())
|
||||
{
|
||||
aliasActions.addAll(getPatchGroupAlias().getAction());
|
||||
}
|
||||
|
||||
for(Alias alias: mPatchedAliases)
|
||||
{
|
||||
if(!(alias instanceof PatchGroupAlias))
|
||||
{
|
||||
for(AliasAction action: alias.getAction())
|
||||
{
|
||||
if(!aliasActions.contains(action))
|
||||
{
|
||||
aliasActions.add(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return aliasActions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasActions()
|
||||
{
|
||||
for(Alias alias: mPatchedAliases)
|
||||
{
|
||||
if(!(alias instanceof PatchGroupAlias) && alias.hasActions())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return hasPatchGroupAlias() && getPatchGroupAlias().hasActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the highest listenable audio priority defined among the aliases in the group, or returns 'Do Not Monitor'
|
||||
* if that is the only priority defined. Otherwise, returns default (100) priority.
|
||||
*/
|
||||
@Override
|
||||
public int getCallPriority()
|
||||
{
|
||||
boolean hasDoNotMonitor = false;
|
||||
|
||||
int highestPriority = (hasPatchGroupAlias() ? getPatchGroupAlias().getCallPriority() : Priority.DEFAULT_PRIORITY + 1);
|
||||
|
||||
for(Alias alias: mPatchedAliases)
|
||||
{
|
||||
if(!(alias instanceof PatchGroupAlias) && alias.hasCallPriority() && alias.getCallPriority() < highestPriority)
|
||||
{
|
||||
if(alias.getCallPriority() == Priority.DO_NOT_MONITOR)
|
||||
{
|
||||
hasDoNotMonitor = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
highestPriority = alias.getCallPriority();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(highestPriority <= Priority.DEFAULT_PRIORITY)
|
||||
{
|
||||
return highestPriority;
|
||||
}
|
||||
else if(hasDoNotMonitor)
|
||||
{
|
||||
return Priority.DO_NOT_MONITOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Priority.DEFAULT_PRIORITY;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCallPriority()
|
||||
{
|
||||
if(hasPatchGroupAlias() && getPatchGroupAlias().hasCallPriority())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for(Alias alias: mPatchedAliases)
|
||||
{
|
||||
if(!(alias instanceof PatchGroupAlias) && alias.hasCallPriority())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRecordable()
|
||||
{
|
||||
if(hasPatchGroupAlias() && getPatchGroupAlias().isRecordable())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for(Alias alias: mPatchedAliases)
|
||||
{
|
||||
if(!(alias instanceof PatchGroupAlias) && alias.isRecordable())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStreamable()
|
||||
{
|
||||
if(hasPatchGroupAlias() && getPatchGroupAlias().isStreamable())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for(Alias alias: mPatchedAliases)
|
||||
{
|
||||
if(!(alias instanceof PatchGroupAlias) && alias.isStreamable())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<BroadcastChannel> getBroadcastChannels()
|
||||
{
|
||||
Set<BroadcastChannel> broadcastChannels = new TreeSet<>();
|
||||
|
||||
if(hasPatchGroupAlias())
|
||||
{
|
||||
broadcastChannels.addAll(getPatchGroupAlias().getBroadcastChannels());
|
||||
}
|
||||
|
||||
for(Alias alias: mPatchedAliases)
|
||||
{
|
||||
if(!(alias instanceof PatchGroupAlias))
|
||||
{
|
||||
broadcastChannels.addAll(alias.getBroadcastChannels());
|
||||
}
|
||||
}
|
||||
|
||||
return broadcastChannels;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBroadcastChannel(String channel)
|
||||
{
|
||||
if(hasPatchGroupAlias() && getPatchGroupAlias().hasBroadcastChannel(channel))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for(Alias alias: mPatchedAliases)
|
||||
{
|
||||
if(!(alias instanceof PatchGroupAlias) && alias.hasBroadcastChannel(channel))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
Alias a1 = new Alias("Alias 1");
|
||||
a1.addAliasID(new BroadcastChannel("Channel 1"));
|
||||
a1.addAliasID(new BroadcastChannel("Channel 2"));
|
||||
a1.addAliasID(new BroadcastChannel("Channel 3"));
|
||||
a1.addAliasID(new BroadcastChannel("Channel 4"));
|
||||
|
||||
Alias a2 = new Alias("Alias 1");
|
||||
a1.addAliasID(new BroadcastChannel("Channel 1"));
|
||||
a1.addAliasID(new BroadcastChannel("Channel 2"));
|
||||
a1.addAliasID(new BroadcastChannel("Channel 3"));
|
||||
a1.addAliasID(new BroadcastChannel("Channel 4"));
|
||||
|
||||
Alias a3 = new Alias("Alias 1");
|
||||
a1.addAliasID(new BroadcastChannel("Channel 1"));
|
||||
a1.addAliasID(new BroadcastChannel("Channel 2"));
|
||||
a1.addAliasID(new BroadcastChannel("Channel 3"));
|
||||
a1.addAliasID(new BroadcastChannel("Channel 4"));
|
||||
|
||||
Alias a4 = new Alias("Alias 1");
|
||||
a1.addAliasID(new BroadcastChannel("Channel 1"));
|
||||
a1.addAliasID(new BroadcastChannel("Channel 2"));
|
||||
a1.addAliasID(new BroadcastChannel("Channel 3"));
|
||||
a1.addAliasID(new BroadcastChannel("Channel 4"));
|
||||
|
||||
PatchGroupAlias pga = new PatchGroupAlias();
|
||||
pga.setPatchGroupAlias(a1);
|
||||
pga.addPatchedAlias(a2);
|
||||
pga.addPatchedAlias(a3);
|
||||
pga.addPatchedAlias(a4);
|
||||
|
||||
Set<BroadcastChannel> bc = pga.getBroadcastChannels();
|
||||
|
||||
mLog.debug("Channels:" + bc);
|
||||
}
|
||||
}
|
||||
@@ -26,9 +26,10 @@ import io.github.dsheirer.alias.Alias;
|
||||
import io.github.dsheirer.alias.action.beep.BeepAction;
|
||||
import io.github.dsheirer.alias.action.clip.ClipAction;
|
||||
import io.github.dsheirer.alias.action.script.ScriptAction;
|
||||
import io.github.dsheirer.message.Message;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.util.Callback;
|
||||
|
||||
/**
|
||||
* Alias action defines an action to execute when an alias is detected active.
|
||||
@@ -43,10 +44,33 @@ import java.util.concurrent.ScheduledExecutorService;
|
||||
@JacksonXmlRootElement(localName = "action")
|
||||
public abstract class AliasAction
|
||||
{
|
||||
private SimpleStringProperty mValueProperty = new SimpleStringProperty();
|
||||
|
||||
public AliasAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* String property representation of this alias ID
|
||||
*/
|
||||
@JsonIgnore
|
||||
public SimpleStringProperty valueProperty()
|
||||
{
|
||||
return mValueProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the value property for this alias ID. Note: this method is intended to be invoked by all subclasses
|
||||
* each time that any of the subclass member variable values change.
|
||||
*/
|
||||
public void updateValueProperty()
|
||||
{
|
||||
valueProperty().set(toString());
|
||||
|
||||
//Hack: harmless, but the list extractor does not consistently update unless we do this.
|
||||
valueProperty().get();
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public abstract AliasActionType getType();
|
||||
|
||||
@@ -56,10 +80,18 @@ public abstract class AliasAction
|
||||
* action attached. The alias argument is the parent alias containing the
|
||||
* alias action.
|
||||
*/
|
||||
public abstract void execute(ScheduledExecutorService scheduledExecutorService, Alias alias, Message message);
|
||||
public abstract void execute(Alias alias, IMessage message);
|
||||
|
||||
/**
|
||||
* Dismiss a persistent alias action
|
||||
*/
|
||||
public abstract void dismiss(boolean reset);
|
||||
|
||||
/**
|
||||
* Creates an observable property extractor for use with observable lists to detect changes internal to this object.
|
||||
*/
|
||||
public static Callback<AliasAction,Observable[]> extractor()
|
||||
{
|
||||
return (AliasAction aliasAction) -> new Observable[] {aliasAction.valueProperty()};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,339 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.action;
|
||||
|
||||
import io.github.dsheirer.alias.Alias;
|
||||
import io.github.dsheirer.alias.AliasEvent;
|
||||
import io.github.dsheirer.alias.AliasEvent.Event;
|
||||
import io.github.dsheirer.alias.AliasFactory;
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.gui.editor.Editor;
|
||||
import io.github.dsheirer.gui.editor.EmptyEditor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class AliasActionEditor extends Editor<Alias>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static ListModel<AliasAction> EMPTY_MODEL = new DefaultListModel<>();
|
||||
private JList<AliasAction> mAliasActionList = new JList<>( EMPTY_MODEL );
|
||||
private JButton mNewActionButton;
|
||||
private JButton mCloneActionButton;
|
||||
private JButton mDeleteActionButton;
|
||||
private EditorContainer mEditorContainer = new EditorContainer();
|
||||
|
||||
private AliasModel mAliasModel;
|
||||
|
||||
public AliasActionEditor( AliasModel model )
|
||||
{
|
||||
mAliasModel = model;
|
||||
init();
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 3",
|
||||
"[grow,fill][grow,fill][grow,fill]", "[][grow,fill][]" ) );
|
||||
|
||||
mAliasActionList.setVisibleRowCount( 6 );
|
||||
mAliasActionList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
|
||||
mAliasActionList.setLayoutOrientation( JList.VERTICAL );
|
||||
mAliasActionList.addListSelectionListener( new ListSelectionListener()
|
||||
{
|
||||
@Override
|
||||
public void valueChanged( ListSelectionEvent event )
|
||||
{
|
||||
if( !event.getValueIsAdjusting() )
|
||||
{
|
||||
JList<?> list = (JList<?>)event.getSource();
|
||||
|
||||
Object selectedItem = list.getSelectedValue();
|
||||
|
||||
if( selectedItem != null && selectedItem instanceof AliasAction )
|
||||
{
|
||||
AliasAction selected = (AliasAction)selectedItem;
|
||||
|
||||
mEditorContainer.setAliasAction( selected );
|
||||
|
||||
mCloneActionButton.setEnabled( true );
|
||||
mDeleteActionButton.setEnabled( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
mCloneActionButton.setEnabled( false );
|
||||
mDeleteActionButton.setEnabled( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
JScrollPane scroller = new JScrollPane( mAliasActionList );
|
||||
add( scroller, "span,grow" );
|
||||
|
||||
add( mEditorContainer, "span,grow" );
|
||||
|
||||
mNewActionButton = new JButton( "New ..." );
|
||||
mNewActionButton.setToolTipText( "Create a new Alias Action" );
|
||||
mNewActionButton.addMouseListener( new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e )
|
||||
{
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
|
||||
menu.add( new AddAliasActionItem( AliasActionType.BEEP ) );
|
||||
menu.add( new AddAliasActionItem( AliasActionType.CLIP ) );
|
||||
menu.add( new AddAliasActionItem( AliasActionType.SCRIPT ) );
|
||||
|
||||
menu.show( e.getComponent(), e.getX(), e.getY() );
|
||||
}
|
||||
} );
|
||||
|
||||
add( mNewActionButton );
|
||||
|
||||
mCloneActionButton = new JButton( "Clone" );
|
||||
mCloneActionButton.setToolTipText( "Create a copy of the currently selected action" );
|
||||
mCloneActionButton.setEnabled( false );
|
||||
mCloneActionButton.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
AliasAction selected = mAliasActionList.getSelectedValue();
|
||||
|
||||
if( selected != null )
|
||||
{
|
||||
AliasAction clone = AliasFactory.copyOf( selected );
|
||||
|
||||
if( clone != null )
|
||||
{
|
||||
addAliasActionToList( clone );
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
add( mCloneActionButton );
|
||||
|
||||
mDeleteActionButton = new JButton( "Delete" );
|
||||
mDeleteActionButton.setToolTipText( "Delete the currently selected action" );
|
||||
mDeleteActionButton.setEnabled( false );
|
||||
mDeleteActionButton.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
final AliasAction selected = mAliasActionList.getSelectedValue();
|
||||
|
||||
if( selected != null )
|
||||
{
|
||||
int choice = JOptionPane.showConfirmDialog( mDeleteActionButton,
|
||||
"Do you want to delete [" + selected.toString() + "]",
|
||||
"Are you sure?", JOptionPane.YES_NO_OPTION );
|
||||
|
||||
if( choice == JOptionPane.YES_OPTION && hasItem() )
|
||||
{
|
||||
Alias alias = getItem();
|
||||
|
||||
alias.removeAliasAction( selected );
|
||||
|
||||
mAliasModel.broadcast( new AliasEvent( alias, Event.CHANGE ) );
|
||||
|
||||
setItem( alias );
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
add( mDeleteActionButton, "wrap" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem( Alias alias )
|
||||
{
|
||||
super.setItem( alias );
|
||||
|
||||
if( alias == null || alias.getAction().isEmpty() )
|
||||
{
|
||||
mAliasActionList.setModel( EMPTY_MODEL );
|
||||
}
|
||||
else
|
||||
{
|
||||
DefaultListModel<AliasAction> model = new DefaultListModel<>();
|
||||
|
||||
List<AliasAction> actions = alias.getAction();
|
||||
|
||||
Collections.sort( actions, new Comparator<AliasAction>()
|
||||
{
|
||||
@Override
|
||||
public int compare( AliasAction o1, AliasAction o2 )
|
||||
{
|
||||
return o1.toString().compareTo( o2.toString() );
|
||||
}
|
||||
} );
|
||||
|
||||
for( AliasAction action: actions )
|
||||
{
|
||||
model.addElement( action );
|
||||
}
|
||||
|
||||
mAliasActionList.setModel( model );
|
||||
}
|
||||
|
||||
mEditorContainer.setAliasAction( null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
if( isModified() || mEditorContainer.isModified() )
|
||||
{
|
||||
if( mEditorContainer.isModified() )
|
||||
{
|
||||
mEditorContainer.save();
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
|
||||
if( hasItem() )
|
||||
{
|
||||
mAliasModel.broadcast( new AliasEvent( getItem(), Event.CHANGE ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class EditorContainer extends JPanel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Editor<AliasAction> mEditor = new EmptyEditor<>();
|
||||
|
||||
public EditorContainer()
|
||||
{
|
||||
setLayout( new MigLayout( "","[grow,fill]", "[grow,fill]" ) );
|
||||
|
||||
add( mEditor );
|
||||
}
|
||||
|
||||
public boolean isModified()
|
||||
{
|
||||
if( mEditor != null )
|
||||
{
|
||||
return mEditor.isModified();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void save()
|
||||
{
|
||||
if( mEditor != null )
|
||||
{
|
||||
mEditor.save();
|
||||
}
|
||||
}
|
||||
|
||||
public void setAliasAction( AliasAction aliasAction )
|
||||
{
|
||||
if( mEditor != null )
|
||||
{
|
||||
if( mEditor.isModified() )
|
||||
{
|
||||
int option = JOptionPane.showConfirmDialog(
|
||||
EditorContainer.this,
|
||||
"Action settings have changed. Do you want to save these changes?",
|
||||
"Save Changes?",
|
||||
JOptionPane.YES_NO_OPTION,
|
||||
JOptionPane.QUESTION_MESSAGE );
|
||||
|
||||
if( option == JOptionPane.YES_OPTION )
|
||||
{
|
||||
mEditor.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
removeAll();
|
||||
|
||||
//This will always give us an editor
|
||||
mEditor = AliasFactory.getEditor( aliasAction );
|
||||
|
||||
add( mEditor );
|
||||
|
||||
revalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private void addAliasActionToList( final AliasAction action )
|
||||
{
|
||||
if( action != null && hasItem() )
|
||||
{
|
||||
final Alias alias = getItem();
|
||||
|
||||
alias.addAliasAction( action );
|
||||
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
setItem( alias );
|
||||
|
||||
mAliasActionList.setSelectedValue( action, true );
|
||||
|
||||
AliasActionEditor.this.setModified( true );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
public class AddAliasActionItem extends JMenuItem
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private AliasActionType mAliasActionType;
|
||||
|
||||
public AddAliasActionItem( AliasActionType type )
|
||||
{
|
||||
super( type.toString() );
|
||||
|
||||
mAliasActionType = type;
|
||||
|
||||
addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
final AliasAction action =
|
||||
AliasFactory.getAliasAction( mAliasActionType );
|
||||
|
||||
addAliasActionToList( action );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.action;
|
||||
|
||||
import io.github.dsheirer.alias.AliasEvent;
|
||||
|
||||
/**
|
||||
* Interface for receiving alias events. Each listener will receive
|
||||
* notification of all alias events.
|
||||
*/
|
||||
public interface AliasActionListener
|
||||
{
|
||||
/**
|
||||
* Notifies the listener that an alias event has occurred
|
||||
*/
|
||||
public void aliasChanged( AliasEvent event );
|
||||
}
|
||||
@@ -1,69 +1,104 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||
/*
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 Dennis Sheirer
|
||||
* *
|
||||
* * This program is free software: you can redistribute it and/or modify
|
||||
* * it under the terms of the GNU General Public License as published by
|
||||
* * the Free Software Foundation, either version 3 of the License, or
|
||||
* * (at your option) any later version.
|
||||
* *
|
||||
* * This program is distributed in the hope that it will be useful,
|
||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* * GNU General Public License for more details.
|
||||
* *
|
||||
* * You should have received a copy of the GNU General Public License
|
||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
*/
|
||||
package io.github.dsheirer.alias.action;
|
||||
|
||||
import io.github.dsheirer.alias.Alias;
|
||||
import io.github.dsheirer.message.Message;
|
||||
import io.github.dsheirer.alias.AliasList;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.message.IMessageListener;
|
||||
import io.github.dsheirer.module.Module;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Manages all alias action events. Each received message is interrogated for
|
||||
* any alias entries and then each alias is interrogated for any alias actions.
|
||||
* Manages all alias action events. Each received message is interrogated for any identifiers that may be aliasable
|
||||
* and have associated alias actions.
|
||||
*
|
||||
* Each alias action is executed and provided a copy of the source message so
|
||||
* that the contents of the message can be used as part of the action.
|
||||
* Each alias action is executed and provided a copy of the source message so that the contents of the message can be
|
||||
* used as part of the action.
|
||||
*/
|
||||
public class AliasActionManager implements Listener<Message>
|
||||
public class AliasActionManager extends Module implements IMessageListener, Listener<IMessage>
|
||||
{
|
||||
public AliasActionManager()
|
||||
private final static Logger mLog = LoggerFactory.getLogger(AliasActionManager.class);
|
||||
private AliasList mAliasList;
|
||||
|
||||
public AliasActionManager(AliasList aliasList)
|
||||
{
|
||||
mAliasList = aliasList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(Message message)
|
||||
public void receive(IMessage message)
|
||||
{
|
||||
if(message.isValid())
|
||||
if(mAliasList != null && message.isValid())
|
||||
{
|
||||
List<Alias> aliases = message.getAliases();
|
||||
List<Identifier> identifiers = message.getIdentifiers();
|
||||
|
||||
if(aliases != null)
|
||||
for(Identifier identifier: identifiers)
|
||||
{
|
||||
for(Alias alias : aliases)
|
||||
{
|
||||
if(alias.hasActions())
|
||||
{
|
||||
List<AliasAction> actions = alias.getAction();
|
||||
List<Alias> aliases = mAliasList.getAliases(identifier);
|
||||
|
||||
for(AliasAction action : actions)
|
||||
for(Alias alias: aliases)
|
||||
{
|
||||
if(alias != null && alias.hasActions())
|
||||
{
|
||||
for(AliasAction action: alias.getAliasActions())
|
||||
{
|
||||
/* Provide access to the thread pool manager in case the
|
||||
* action needs to setup a timer, and provide the original
|
||||
* message to be used as part of the action (e.g. sending
|
||||
* the message as a text message to a cell phone */
|
||||
action.execute(ThreadPool.SCHEDULED, alias, message);
|
||||
action.execute(alias, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
//No actions needed
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start()
|
||||
{
|
||||
//No actions needed
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop()
|
||||
{
|
||||
//No actions neeeded
|
||||
}
|
||||
|
||||
/**
|
||||
* IMessageListener interface ... delegates to this class implementation of Listener<Message>
|
||||
*/
|
||||
@Override
|
||||
public Listener<IMessage> getMessageListener()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,32 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.alias.action;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.Alias;
|
||||
import io.github.dsheirer.message.Message;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -18,19 +37,15 @@ public abstract class RecurringAction extends AliasAction
|
||||
protected AtomicBoolean mRunning = new AtomicBoolean(false);
|
||||
@JsonIgnore
|
||||
private ScheduledFuture<?> mPerpetualAction;
|
||||
@JsonIgnore
|
||||
protected ScheduledExecutorService mScheduledExecutorService;
|
||||
|
||||
protected Interval mInterval = Interval.ONCE;
|
||||
protected int mPeriod = 5;
|
||||
|
||||
public abstract void performAction(Alias alias, Message message);
|
||||
public abstract void performAction(Alias alias, IMessage message);
|
||||
|
||||
@Override
|
||||
public void execute(ScheduledExecutorService scheduledExecutorService, Alias alias, Message message)
|
||||
public void execute(Alias alias, IMessage message)
|
||||
{
|
||||
mScheduledExecutorService = scheduledExecutorService;
|
||||
|
||||
if(mRunning.compareAndSet(false, true))
|
||||
{
|
||||
switch(mInterval)
|
||||
@@ -41,11 +56,11 @@ public abstract class RecurringAction extends AliasAction
|
||||
break;
|
||||
case DELAYED_RESET:
|
||||
performThreadedAction(alias, message);
|
||||
mScheduledExecutorService.schedule(new ResetTask(), mPeriod, TimeUnit.SECONDS);
|
||||
ThreadPool.SCHEDULED.schedule(new ResetTask(), mPeriod, TimeUnit.SECONDS);
|
||||
break;
|
||||
case UNTIL_DISMISSED:
|
||||
mPerpetualAction = mScheduledExecutorService.scheduleAtFixedRate(
|
||||
new PerformActionTask(alias, message), mPeriod, mPeriod, TimeUnit.SECONDS);
|
||||
mPerpetualAction = ThreadPool.SCHEDULED.scheduleAtFixedRate(
|
||||
new PerformActionTask(alias, message), 0, mPeriod, TimeUnit.SECONDS);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("<html><div width='250'>Alias [");
|
||||
@@ -56,18 +71,13 @@ public abstract class RecurringAction extends AliasAction
|
||||
|
||||
final String text = sb.toString();
|
||||
|
||||
EventQueue.invokeLater(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
JOptionPane.showMessageDialog(null, text,
|
||||
"Alias Alert", JOptionPane.INFORMATION_MESSAGE);
|
||||
EventQueue.invokeLater(() -> {
|
||||
JOptionPane.showMessageDialog(null, text,
|
||||
"Alias Alert", JOptionPane.INFORMATION_MESSAGE);
|
||||
|
||||
dismiss(false);
|
||||
dismiss(false);
|
||||
|
||||
mScheduledExecutorService.schedule(new ResetTask(), 15, TimeUnit.SECONDS);
|
||||
}
|
||||
ThreadPool.SCHEDULED.schedule(new ResetTask(), 15, TimeUnit.SECONDS);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
@@ -79,16 +89,9 @@ public abstract class RecurringAction extends AliasAction
|
||||
* Spawns the performAction() event into a new thread so that it doesn't
|
||||
* delay any decoder actions.
|
||||
*/
|
||||
private void performThreadedAction(final Alias alias, final Message message)
|
||||
private void performThreadedAction(final Alias alias, final IMessage message)
|
||||
{
|
||||
mScheduledExecutorService.schedule(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
performAction(alias, message);
|
||||
}
|
||||
}, 0, TimeUnit.SECONDS);
|
||||
ThreadPool.CACHED.execute(() -> performAction(alias, message));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -116,6 +119,7 @@ public abstract class RecurringAction extends AliasAction
|
||||
public void setPeriod(int period)
|
||||
{
|
||||
mPeriod = period;
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "interval")
|
||||
@@ -127,19 +131,19 @@ public abstract class RecurringAction extends AliasAction
|
||||
public void setInterval(Interval interval)
|
||||
{
|
||||
mInterval = interval;
|
||||
|
||||
mRunning.set(false);
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
public enum Interval
|
||||
{
|
||||
ONCE("Once"),
|
||||
DELAYED_RESET("Once, Reset After Delay"),
|
||||
DELAYED_RESET("Once - Reset After Delay"),
|
||||
UNTIL_DISMISSED("Until Dismissed");
|
||||
|
||||
private String mLabel;
|
||||
|
||||
private Interval(String label)
|
||||
Interval(String label)
|
||||
{
|
||||
mLabel = label;
|
||||
}
|
||||
@@ -153,9 +157,9 @@ public abstract class RecurringAction extends AliasAction
|
||||
public class PerformActionTask implements Runnable
|
||||
{
|
||||
private Alias mAlias;
|
||||
private Message mMessage;
|
||||
private IMessage mMessage;
|
||||
|
||||
public PerformActionTask(Alias alias, Message message)
|
||||
public PerformActionTask(Alias alias, IMessage message)
|
||||
{
|
||||
mAlias = alias;
|
||||
mMessage = message;
|
||||
|
||||
@@ -1,13 +1,65 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.alias.action.beep;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.Alias;
|
||||
import io.github.dsheirer.alias.action.AliasActionType;
|
||||
import io.github.dsheirer.alias.action.RecurringAction;
|
||||
import io.github.dsheirer.message.Message;
|
||||
import io.github.dsheirer.audio.AudioFormats;
|
||||
import io.github.dsheirer.gui.preference.playback.ToneFrequency;
|
||||
import io.github.dsheirer.gui.preference.playback.ToneUtil;
|
||||
import io.github.dsheirer.gui.preference.playback.ToneVolume;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.Clip;
|
||||
import javax.sound.sampled.DataLine;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
public class BeepAction extends RecurringAction
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(BeepAction.class);
|
||||
private byte[] mToneBytes;
|
||||
|
||||
public BeepAction()
|
||||
{
|
||||
setInterval(Interval.ONCE);
|
||||
float[] tone = ToneUtil.getTone(ToneFrequency.F700, ToneVolume.V10, 1000);
|
||||
|
||||
/* Little-endian byte buffer */
|
||||
ByteBuffer buffer = ByteBuffer.allocate(tone.length * 2).order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
ShortBuffer shortBuffer = buffer.asShortBuffer();
|
||||
|
||||
for(float sample : tone)
|
||||
{
|
||||
shortBuffer.put((short) (sample * Short.MAX_VALUE));
|
||||
}
|
||||
|
||||
mToneBytes = buffer.array();
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@Override
|
||||
@@ -17,14 +69,50 @@ public class BeepAction extends RecurringAction
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performAction(Alias alias, Message message )
|
||||
public void performAction(Alias alias, IMessage message )
|
||||
{
|
||||
System.out.println( "\007" );
|
||||
DataLine.Info info = new DataLine.Info(Clip.class, AudioFormats.PCM_SIGNED_8000_HZ_16_BIT_MONO);
|
||||
|
||||
if(!AudioSystem.isLineSupported(info))
|
||||
{
|
||||
mLog.error("Audio clip playback is not supported on this system");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Clip clip = (Clip)AudioSystem.getLine(info);
|
||||
clip.open(AudioFormats.PCM_SIGNED_8000_HZ_16_BIT_MONO, mToneBytes, 0, mToneBytes.length);
|
||||
clip.start();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Error attempting to play audio test tone");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Beep";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Beep");
|
||||
|
||||
if(getInterval() != null)
|
||||
{
|
||||
switch(getInterval())
|
||||
{
|
||||
case ONCE:
|
||||
sb.append(" Once");
|
||||
break;
|
||||
case DELAYED_RESET:
|
||||
sb.append(" Once, Reset After ").append(getPeriod()).append(" Seconds");
|
||||
break;
|
||||
case UNTIL_DISMISSED:
|
||||
sb.append(" Every ").append(getPeriod()).append(" Seconds Until Dismissed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.action.beep;
|
||||
|
||||
import io.github.dsheirer.alias.action.AliasAction;
|
||||
import io.github.dsheirer.alias.action.RecurringAction;
|
||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
public class BeepActionEditor extends DocumentListenerEditor<AliasAction>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String HELP_TEXT =
|
||||
"<html><h3>Beep Action</h3>"
|
||||
+ "This action will sound a beep according to the interval<br>"
|
||||
+ "that you select:<br><br>"
|
||||
+ "<b>Once:</b> Beep the first time the alias is active and<br>"
|
||||
+ "never again<br><br>"
|
||||
+ "<b>Once, Reset After Delay:</b> Beep once and suppress<br>"
|
||||
+ "subsequent beeps for the specified period in seconds.<br>"
|
||||
+ "After the reset period, beep again when the alias is active.<br><br>"
|
||||
+ "<b>Until Dismissed:</b> Beep every period seconds until you<br>"
|
||||
+ "click OK on the dialog that appears. Alerting is suppressed<br>"
|
||||
+ "for 15 seconds after you click OK.<br>"
|
||||
+ "</html>";
|
||||
|
||||
private JComboBox<RecurringAction.Interval> mComboInterval;
|
||||
private JSlider mPeriodSlider;
|
||||
private JLabel mPeriodSliderLabel;
|
||||
|
||||
public BeepActionEditor( AliasAction aliasAction )
|
||||
{
|
||||
init();
|
||||
|
||||
setItem( aliasAction );
|
||||
}
|
||||
|
||||
public BeepAction getBeepAction()
|
||||
{
|
||||
if( getItem() instanceof BeepAction )
|
||||
{
|
||||
return (BeepAction)getItem();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][]" ) );
|
||||
|
||||
add( new JLabel( "Beep Interval:" ) );
|
||||
|
||||
mComboInterval = new JComboBox<RecurringAction.Interval>( RecurringAction.Interval.values() );
|
||||
mComboInterval.setToolTipText( HELP_TEXT );
|
||||
mComboInterval.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
RecurringAction.Interval selected = mComboInterval
|
||||
.getItemAt( mComboInterval.getSelectedIndex() );
|
||||
|
||||
if( selected != null )
|
||||
{
|
||||
/* Enable/disable period spinner based on selection */
|
||||
boolean enabled = selected != RecurringAction.Interval.ONCE;
|
||||
|
||||
mPeriodSlider.setEnabled( enabled );
|
||||
mPeriodSliderLabel.setEnabled( enabled );
|
||||
}
|
||||
|
||||
setModified( true );
|
||||
}
|
||||
});
|
||||
add( mComboInterval, "wrap" );
|
||||
|
||||
final SpinnerModel model = new SpinnerNumberModel( 1, 1, 30, 1 );
|
||||
model.addChangeListener( new ChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void stateChanged( ChangeEvent e )
|
||||
{
|
||||
setModified( true );
|
||||
}
|
||||
} );
|
||||
|
||||
mPeriodSlider = new JSlider( JSlider.HORIZONTAL, 1, 60, 1 );
|
||||
mPeriodSlider.setMajorTickSpacing( 10 );
|
||||
mPeriodSlider.setMinorTickSpacing( 2 );
|
||||
mPeriodSlider.setPaintTicks( true );
|
||||
mPeriodSlider.setLabelTable( mPeriodSlider.createStandardLabels( 10, 10 ) );
|
||||
mPeriodSlider.setPaintLabels( true );
|
||||
mPeriodSlider.addChangeListener( new ChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void stateChanged( ChangeEvent e )
|
||||
{
|
||||
mPeriodSliderLabel.setText( "Period: " + mPeriodSlider.getValue() );
|
||||
setModified( true );
|
||||
}
|
||||
} );
|
||||
mPeriodSlider.setToolTipText( HELP_TEXT );
|
||||
|
||||
mPeriodSliderLabel = new JLabel( "Period: " + mPeriodSlider.getValue() + " " );
|
||||
add( mPeriodSliderLabel );
|
||||
add( mPeriodSlider, "wrap,grow" );
|
||||
|
||||
JLabel help = new JLabel( "Help ..." );
|
||||
help.setForeground( Color.BLUE.brighter() );
|
||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
||||
help.addMouseListener( new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e )
|
||||
{
|
||||
JOptionPane.showMessageDialog( BeepActionEditor.this,
|
||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
||||
}
|
||||
} );
|
||||
add( help, "align left" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem( AliasAction item )
|
||||
{
|
||||
super.setItem( item );
|
||||
|
||||
if( hasItem() )
|
||||
{
|
||||
BeepAction beep = getBeepAction();
|
||||
|
||||
RecurringAction.Interval interval = beep.getInterval();
|
||||
|
||||
mComboInterval.setSelectedItem( interval );
|
||||
|
||||
boolean enabled = interval != RecurringAction.Interval.ONCE;
|
||||
|
||||
mPeriodSliderLabel.setEnabled( enabled );
|
||||
mPeriodSlider.setEnabled( enabled );
|
||||
mPeriodSlider.setValue( beep.getPeriod() );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
if( hasItem() && isModified() )
|
||||
{
|
||||
BeepAction beep = getBeepAction();
|
||||
|
||||
beep.setInterval( (RecurringAction.Interval)mComboInterval.getSelectedItem() );
|
||||
beep.setPeriod( mPeriodSlider.getValue() );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.alias.action.clip;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
@@ -5,7 +25,7 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.Alias;
|
||||
import io.github.dsheirer.alias.action.AliasActionType;
|
||||
import io.github.dsheirer.alias.action.RecurringAction;
|
||||
import io.github.dsheirer.message.Message;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -25,6 +45,7 @@ public class ClipAction extends RecurringAction
|
||||
|
||||
public ClipAction()
|
||||
{
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@@ -42,10 +63,11 @@ public class ClipAction extends RecurringAction
|
||||
public void setPath(String path)
|
||||
{
|
||||
mFilePath = path;
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performAction(Alias alias, Message message)
|
||||
public void performAction(Alias alias, IMessage message)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -67,8 +89,7 @@ public class ClipAction extends RecurringAction
|
||||
{
|
||||
mClip = AudioSystem.getClip();
|
||||
|
||||
AudioInputStream ais =
|
||||
AudioSystem.getAudioInputStream(new File(mFilePath));
|
||||
AudioInputStream ais = AudioSystem.getAudioInputStream(new File(mFilePath));
|
||||
|
||||
mClip.open(ais);
|
||||
}
|
||||
@@ -93,17 +114,33 @@ public class ClipAction extends RecurringAction
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
if(mFilePath == null)
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Play Audio Clip");
|
||||
|
||||
if(getInterval() != null)
|
||||
{
|
||||
return "Play Clip";
|
||||
switch(getInterval())
|
||||
{
|
||||
case ONCE:
|
||||
sb.append(" Once");
|
||||
break;
|
||||
case DELAYED_RESET:
|
||||
sb.append(" Once, Reset After ").append(getPeriod()).append(" Seconds");
|
||||
break;
|
||||
case UNTIL_DISMISSED:
|
||||
sb.append(" Every ").append(getPeriod()).append(" Seconds Until Dismissed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if(getPath() == null)
|
||||
{
|
||||
return "Play Clip: " + mFilePath;
|
||||
sb.append(" - (audio file empty)");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,285 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.action.clip;
|
||||
|
||||
import io.github.dsheirer.alias.action.AliasAction;
|
||||
import io.github.dsheirer.alias.action.RecurringAction;
|
||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.io.File;
|
||||
|
||||
public class ClipActionEditor extends DocumentListenerEditor<AliasAction>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String PLEASE_SELECT_A_FILE = "Please select a file";
|
||||
private static final String HELP_TEXT =
|
||||
"<html><h3>Clip Action</h3>"
|
||||
+ "This action will play an audio clip according to the interval<br>"
|
||||
+ "that you select:<br><br>"
|
||||
+ "<b>Once:</b> play the first time the alias is active and<br>"
|
||||
+ "never again<br><br>"
|
||||
+ "<b>Once, Reset After Delay:</b> play once and suppress<br>"
|
||||
+ "subsequent runs for the specified period in seconds.<br>"
|
||||
+ "After the reset period, play again when the alias is active.<br><br>"
|
||||
+ "<b>Until Dismissed:</b> play every period seconds until you<br>"
|
||||
+ "click OK on the dialog that appears. Alerting is suppressed<br>"
|
||||
+ "for 15 seconds after you click OK.<br>"
|
||||
+ "</html>";
|
||||
|
||||
private JComboBox<RecurringAction.Interval> mComboInterval;
|
||||
private JSlider mPeriodSlider;
|
||||
private JLabel mPeriodSliderLabel;
|
||||
private JTextField mTextFilePath;
|
||||
|
||||
public ClipActionEditor( AliasAction aliasAction )
|
||||
{
|
||||
init();
|
||||
|
||||
setItem( aliasAction );
|
||||
}
|
||||
|
||||
public ClipAction getClipAction()
|
||||
{
|
||||
if( getItem() instanceof ClipAction )
|
||||
{
|
||||
return (ClipAction)getItem();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][]" ) );
|
||||
|
||||
add( new JLabel( "Play Interval:" ) );
|
||||
|
||||
mComboInterval = new JComboBox<RecurringAction.Interval>( RecurringAction.Interval.values() );
|
||||
mComboInterval.setToolTipText( HELP_TEXT );
|
||||
mComboInterval.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
RecurringAction.Interval selected = mComboInterval
|
||||
.getItemAt( mComboInterval.getSelectedIndex() );
|
||||
|
||||
if( selected != null )
|
||||
{
|
||||
/* Enable/disable period slider based on selection */
|
||||
boolean enabled = selected != RecurringAction.Interval.ONCE;
|
||||
|
||||
mPeriodSlider.setEnabled( enabled );
|
||||
mPeriodSliderLabel.setEnabled( enabled );
|
||||
}
|
||||
|
||||
setModified( true );
|
||||
}
|
||||
});
|
||||
add( mComboInterval );
|
||||
|
||||
final SpinnerModel model = new SpinnerNumberModel( 1, 1, 30, 1 );
|
||||
model.addChangeListener( new ChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void stateChanged( ChangeEvent e )
|
||||
{
|
||||
setModified( true );
|
||||
}
|
||||
} );
|
||||
|
||||
mPeriodSlider = new JSlider( JSlider.HORIZONTAL, 1, 60, 1 );
|
||||
mPeriodSlider.setMajorTickSpacing( 10 );
|
||||
mPeriodSlider.setMinorTickSpacing( 2 );
|
||||
mPeriodSlider.setPaintTicks( true );
|
||||
mPeriodSlider.setLabelTable( mPeriodSlider.createStandardLabels( 10, 10 ) );
|
||||
mPeriodSlider.setPaintLabels( true );
|
||||
mPeriodSlider.addChangeListener( new ChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void stateChanged( ChangeEvent e )
|
||||
{
|
||||
mPeriodSliderLabel.setText( "Period: " + mPeriodSlider.getValue() );
|
||||
setModified( true );
|
||||
}
|
||||
} );
|
||||
mPeriodSlider.setToolTipText( HELP_TEXT );
|
||||
|
||||
mPeriodSliderLabel = new JLabel( "Period: " + mPeriodSlider.getValue() + " " );
|
||||
add( mPeriodSliderLabel );
|
||||
add( mPeriodSlider, "wrap,grow" );
|
||||
|
||||
mTextFilePath = new JTextField( PLEASE_SELECT_A_FILE );
|
||||
add( mTextFilePath, "growx,span" );
|
||||
|
||||
JButton fileButton = new JButton( "File" );
|
||||
|
||||
fileButton.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
final JFileChooser chooser = new JFileChooser();
|
||||
|
||||
int value = chooser.showOpenDialog( ClipActionEditor.this );
|
||||
|
||||
if( value == JFileChooser.APPROVE_OPTION )
|
||||
{
|
||||
File file = chooser.getSelectedFile();
|
||||
|
||||
mTextFilePath.setText( file.getAbsolutePath() );
|
||||
|
||||
setModified( true );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
add( fileButton, "grow" );
|
||||
|
||||
JButton testButton = new JButton( "Test" );
|
||||
|
||||
testButton.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
if( hasItem() )
|
||||
{
|
||||
String path = mTextFilePath.getText();
|
||||
|
||||
if( path == null || path.trim().isEmpty() )
|
||||
{
|
||||
JOptionPane.showMessageDialog( ClipActionEditor.this,
|
||||
"Please select an audio file", PLEASE_SELECT_A_FILE,
|
||||
JOptionPane.ERROR_MESSAGE );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( isModified() )
|
||||
{
|
||||
int option = JOptionPane.showConfirmDialog(
|
||||
ClipActionEditor.this,
|
||||
"Settings have changed. Do you want to save these changes?",
|
||||
"Save Changes?",
|
||||
JOptionPane.YES_NO_OPTION,
|
||||
JOptionPane.QUESTION_MESSAGE );
|
||||
|
||||
if( option == JOptionPane.YES_OPTION )
|
||||
{
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
new Thread( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
getClipAction().play();
|
||||
}
|
||||
catch( Exception e1 )
|
||||
{
|
||||
JOptionPane.showMessageDialog( ClipActionEditor.this,
|
||||
"Couldn't play audio clip [" + e1.getMessage() + "]",
|
||||
"Error", JOptionPane.ERROR_MESSAGE );
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
add( testButton, "grow,wrap" );
|
||||
|
||||
JLabel help = new JLabel( "Help ..." );
|
||||
help.setForeground( Color.BLUE.brighter() );
|
||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
||||
help.addMouseListener( new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e )
|
||||
{
|
||||
JOptionPane.showMessageDialog( ClipActionEditor.this,
|
||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
||||
}
|
||||
} );
|
||||
add( help, "align left" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem( AliasAction item )
|
||||
{
|
||||
super.setItem( item );
|
||||
|
||||
if( hasItem() )
|
||||
{
|
||||
ClipAction clip = getClipAction();
|
||||
|
||||
RecurringAction.Interval interval = clip.getInterval();
|
||||
|
||||
mComboInterval.setSelectedItem( interval );
|
||||
|
||||
boolean enabled = interval != RecurringAction.Interval.ONCE;
|
||||
|
||||
mPeriodSliderLabel.setEnabled( enabled );
|
||||
mPeriodSlider.setEnabled( enabled );
|
||||
mPeriodSlider.setValue( clip.getPeriod() );
|
||||
|
||||
String filepath = clip.getPath();
|
||||
|
||||
if( filepath != null && !filepath.isEmpty() )
|
||||
{
|
||||
mTextFilePath.setText( filepath );
|
||||
}
|
||||
else
|
||||
{
|
||||
mTextFilePath.setText( PLEASE_SELECT_A_FILE );
|
||||
}
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
if( hasItem() && isModified() )
|
||||
{
|
||||
ClipAction script = getClipAction();
|
||||
|
||||
script.setInterval( (RecurringAction.Interval)mComboInterval.getSelectedItem() );
|
||||
script.setPeriod( mPeriodSlider.getValue() );
|
||||
script.setPath( mTextFilePath.getText() );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,30 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.alias.action.script;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.Alias;
|
||||
import io.github.dsheirer.alias.action.AliasActionType;
|
||||
import io.github.dsheirer.alias.action.RecurringAction;
|
||||
import io.github.dsheirer.message.Message;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -19,6 +39,7 @@ public class ScriptAction extends RecurringAction
|
||||
|
||||
public ScriptAction()
|
||||
{
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@@ -36,10 +57,11 @@ public class ScriptAction extends RecurringAction
|
||||
public void setScript(String script)
|
||||
{
|
||||
mScript = script;
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performAction(Alias alias, Message message)
|
||||
public void performAction(Alias alias, IMessage message)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -63,8 +85,7 @@ public class ScriptAction extends RecurringAction
|
||||
|
||||
int exitCode = p.waitFor();
|
||||
|
||||
BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(p.getInputStream()));
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
|
||||
|
||||
String line = "";
|
||||
|
||||
@@ -77,8 +98,7 @@ public class ScriptAction extends RecurringAction
|
||||
|
||||
if(exitCode != 0)
|
||||
{
|
||||
throw new RuntimeException("Exit Code: " + exitCode +
|
||||
" Console:" + sb.toString());
|
||||
throw new RuntimeException("Exit Code: " + exitCode + " Console:" + sb.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,13 +106,30 @@ public class ScriptAction extends RecurringAction
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
if(mScript == null)
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Run Script");
|
||||
|
||||
if(getInterval() != null)
|
||||
{
|
||||
return "Run Script";
|
||||
switch(getInterval())
|
||||
{
|
||||
case ONCE:
|
||||
sb.append(" Once");
|
||||
break;
|
||||
case DELAYED_RESET:
|
||||
sb.append(" Once, Reset After ").append(getPeriod()).append(" Seconds");
|
||||
break;
|
||||
case UNTIL_DISMISSED:
|
||||
sb.append(" Every ").append(getPeriod()).append(" Seconds Until Dismissed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if(getScript() == null)
|
||||
{
|
||||
return "Run Script: " + mScript;
|
||||
sb.append(" - (script file empty)");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,281 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.action.script;
|
||||
|
||||
import io.github.dsheirer.alias.action.AliasAction;
|
||||
import io.github.dsheirer.alias.action.RecurringAction;
|
||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.io.File;
|
||||
|
||||
public class ScriptActionEditor extends DocumentListenerEditor<AliasAction>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String PLEASE_SELECT_A_FILE = "Please select a file";
|
||||
private static final String HELP_TEXT =
|
||||
"<html><h3>Script Action</h3>"
|
||||
+ "This action will run a script according to the interval<br>"
|
||||
+ "that you select:<br><br>"
|
||||
+ "<b>Once:</b> run the first time the alias is active and<br>"
|
||||
+ "never again<br><br>"
|
||||
+ "<b>Once, Reset After Delay:</b> run once and suppress<br>"
|
||||
+ "subsequent runs for the specified period in seconds.<br>"
|
||||
+ "After the reset period, run again when the alias is active.<br><br>"
|
||||
+ "<b>Until Dismissed:</b> run every period seconds until you<br>"
|
||||
+ "click OK on the dialog that appears. Alerting is suppressed<br>"
|
||||
+ "for 15 seconds after you click OK.<br>"
|
||||
+ "</html>";
|
||||
|
||||
private JComboBox<RecurringAction.Interval> mComboInterval;
|
||||
private JSlider mPeriodSlider;
|
||||
private JLabel mPeriodSliderLabel;
|
||||
private JTextField mTextFilePath;
|
||||
|
||||
public ScriptActionEditor( AliasAction aliasAction )
|
||||
{
|
||||
init();
|
||||
|
||||
setItem( aliasAction );
|
||||
}
|
||||
|
||||
public ScriptAction getScriptAction()
|
||||
{
|
||||
if( getItem() instanceof ScriptAction )
|
||||
{
|
||||
return (ScriptAction)getItem();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][]" ) );
|
||||
|
||||
add( new JLabel( "Run Interval:" ) );
|
||||
|
||||
mComboInterval = new JComboBox<RecurringAction.Interval>( RecurringAction.Interval.values() );
|
||||
mComboInterval.setToolTipText( HELP_TEXT );
|
||||
mComboInterval.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
RecurringAction.Interval selected = mComboInterval
|
||||
.getItemAt( mComboInterval.getSelectedIndex() );
|
||||
|
||||
if( selected != null )
|
||||
{
|
||||
/* Enable/disable period slider based on selection */
|
||||
boolean enabled = selected != RecurringAction.Interval.ONCE;
|
||||
|
||||
mPeriodSlider.setEnabled( enabled );
|
||||
mPeriodSliderLabel.setEnabled( enabled );
|
||||
}
|
||||
|
||||
setModified( true );
|
||||
}
|
||||
});
|
||||
add( mComboInterval );
|
||||
|
||||
final SpinnerModel model = new SpinnerNumberModel( 1, 1, 30, 1 );
|
||||
model.addChangeListener( new ChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void stateChanged( ChangeEvent e )
|
||||
{
|
||||
setModified( true );
|
||||
}
|
||||
} );
|
||||
|
||||
mPeriodSlider = new JSlider( JSlider.HORIZONTAL, 1, 60, 1 );
|
||||
mPeriodSlider.setMajorTickSpacing( 10 );
|
||||
mPeriodSlider.setMinorTickSpacing( 2 );
|
||||
mPeriodSlider.setPaintTicks( true );
|
||||
mPeriodSlider.setLabelTable( mPeriodSlider.createStandardLabels( 10, 10 ) );
|
||||
mPeriodSlider.setPaintLabels( true );
|
||||
mPeriodSlider.addChangeListener( new ChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void stateChanged( ChangeEvent e )
|
||||
{
|
||||
mPeriodSliderLabel.setText( "Period: " + mPeriodSlider.getValue() );
|
||||
setModified( true );
|
||||
}
|
||||
} );
|
||||
mPeriodSlider.setToolTipText( HELP_TEXT );
|
||||
|
||||
mPeriodSliderLabel = new JLabel( "Period: " + mPeriodSlider.getValue() + " " );
|
||||
add( mPeriodSliderLabel );
|
||||
add( mPeriodSlider, "wrap,grow" );
|
||||
|
||||
mTextFilePath = new JTextField( PLEASE_SELECT_A_FILE );
|
||||
add( mTextFilePath, "growx,span" );
|
||||
|
||||
JButton fileButton = new JButton( "File" );
|
||||
|
||||
fileButton.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
final JFileChooser chooser = new JFileChooser();
|
||||
|
||||
int value = chooser.showOpenDialog( ScriptActionEditor.this );
|
||||
|
||||
if( value == JFileChooser.APPROVE_OPTION )
|
||||
{
|
||||
File file = chooser.getSelectedFile();
|
||||
|
||||
mTextFilePath.setText( file.getAbsolutePath() );
|
||||
|
||||
setModified( true );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
add( fileButton, "grow" );
|
||||
|
||||
JButton testButton = new JButton( "Test" );
|
||||
testButton.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent actionEvent )
|
||||
{
|
||||
String script = mTextFilePath.getText();
|
||||
|
||||
if( script == null || script.trim().isEmpty() )
|
||||
{
|
||||
JOptionPane.showMessageDialog( ScriptActionEditor.this,
|
||||
"Please select a script file", PLEASE_SELECT_A_FILE,
|
||||
JOptionPane.ERROR_MESSAGE );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( isModified() )
|
||||
{
|
||||
int option = JOptionPane.showConfirmDialog(
|
||||
ScriptActionEditor.this,
|
||||
"Settings have changed. Do you want to save these changes?",
|
||||
"Save Changes?",
|
||||
JOptionPane.YES_NO_OPTION,
|
||||
JOptionPane.QUESTION_MESSAGE );
|
||||
|
||||
if( option == JOptionPane.YES_OPTION )
|
||||
{
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
new Thread( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
getScriptAction().play();
|
||||
}
|
||||
catch( Exception e1 )
|
||||
{
|
||||
JOptionPane.showMessageDialog( ScriptActionEditor.this,
|
||||
e1.getMessage(), "Error", JOptionPane.ERROR_MESSAGE );
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
}
|
||||
} );
|
||||
|
||||
add( testButton, "grow,wrap" );
|
||||
|
||||
JLabel help = new JLabel( "Help ..." );
|
||||
help.setForeground( Color.BLUE.brighter() );
|
||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
||||
help.addMouseListener( new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e )
|
||||
{
|
||||
JOptionPane.showMessageDialog( ScriptActionEditor.this,
|
||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
||||
}
|
||||
} );
|
||||
add( help, "align left" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem( AliasAction item )
|
||||
{
|
||||
super.setItem( item );
|
||||
|
||||
if( hasItem() )
|
||||
{
|
||||
ScriptAction script = getScriptAction();
|
||||
|
||||
RecurringAction.Interval interval = script.getInterval();
|
||||
|
||||
mComboInterval.setSelectedItem( interval );
|
||||
|
||||
boolean enabled = interval != RecurringAction.Interval.ONCE;
|
||||
|
||||
mPeriodSliderLabel.setEnabled( enabled );
|
||||
mPeriodSlider.setEnabled( enabled );
|
||||
mPeriodSlider.setValue( script.getPeriod() );
|
||||
|
||||
String filepath = script.getScript();
|
||||
|
||||
if( filepath != null && !filepath.isEmpty() )
|
||||
{
|
||||
mTextFilePath.setText( filepath );
|
||||
}
|
||||
else
|
||||
{
|
||||
mTextFilePath.setText( PLEASE_SELECT_A_FILE );
|
||||
}
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
if( hasItem() && isModified() )
|
||||
{
|
||||
ScriptAction script = getScriptAction();
|
||||
|
||||
script.setInterval( (RecurringAction.Interval)mComboInterval.getSelectedItem() );
|
||||
script.setPeriod( mPeriodSlider.getValue() );
|
||||
script.setScript( mTextFilePath.getText() );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 Dennis Sheirer
|
||||
* *
|
||||
* * This program is free software: you can redistribute it and/or modify
|
||||
* * it under the terms of the GNU General Public License as published by
|
||||
* * the Free Software Foundation, either version 3 of the License, or
|
||||
* * (at your option) any later version.
|
||||
* *
|
||||
* * This program is distributed in the hope that it will be useful,
|
||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* * GNU General Public License for more details.
|
||||
* *
|
||||
* * You should have received a copy of the GNU General Public License
|
||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
*
|
||||
*/
|
||||
package io.github.dsheirer.alias.id;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
@@ -23,46 +27,139 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
|
||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
||||
import io.github.dsheirer.alias.id.esn.Esn;
|
||||
import io.github.dsheirer.alias.id.fleetsync.FleetsyncID;
|
||||
import io.github.dsheirer.alias.id.legacy.fleetsync.FleetsyncID;
|
||||
import io.github.dsheirer.alias.id.legacy.mdc.MDC1200ID;
|
||||
import io.github.dsheirer.alias.id.legacy.mobileID.Min;
|
||||
import io.github.dsheirer.alias.id.legacy.mpt1327.MPT1327ID;
|
||||
import io.github.dsheirer.alias.id.legacy.nonrecordable.NonRecordable;
|
||||
import io.github.dsheirer.alias.id.legacy.siteID.SiteID;
|
||||
import io.github.dsheirer.alias.id.legacy.talkgroup.LegacyTalkgroupID;
|
||||
import io.github.dsheirer.alias.id.legacy.uniqueID.UniqueID;
|
||||
import io.github.dsheirer.alias.id.lojack.LoJackFunctionAndID;
|
||||
import io.github.dsheirer.alias.id.mdc.MDC1200ID;
|
||||
import io.github.dsheirer.alias.id.mobileID.Min;
|
||||
import io.github.dsheirer.alias.id.mpt1327.MPT1327ID;
|
||||
import io.github.dsheirer.alias.id.nonrecordable.NonRecordable;
|
||||
import io.github.dsheirer.alias.id.priority.Priority;
|
||||
import io.github.dsheirer.alias.id.siteID.SiteID;
|
||||
import io.github.dsheirer.alias.id.status.StatusID;
|
||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupID;
|
||||
import io.github.dsheirer.alias.id.uniqueID.UniqueID;
|
||||
import io.github.dsheirer.alias.id.radio.Radio;
|
||||
import io.github.dsheirer.alias.id.radio.RadioRange;
|
||||
import io.github.dsheirer.alias.id.record.Record;
|
||||
import io.github.dsheirer.alias.id.status.UnitStatusID;
|
||||
import io.github.dsheirer.alias.id.status.UserStatusID;
|
||||
import io.github.dsheirer.alias.id.talkgroup.Talkgroup;
|
||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange;
|
||||
import io.github.dsheirer.alias.id.tone.TonesID;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.util.Callback;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include= JsonTypeInfo.As.PROPERTY, property="type")
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
|
||||
@JsonSubTypes({
|
||||
@JsonSubTypes.Type(value=BroadcastChannel.class, name="broadcastChannel"),
|
||||
@JsonSubTypes.Type(value=Esn.class, name="esn"),
|
||||
@JsonSubTypes.Type(value=FleetsyncID.class, name="fleetsyncID"),
|
||||
@JsonSubTypes.Type(value = BroadcastChannel.class, name = "broadcastChannel"),
|
||||
@JsonSubTypes.Type(value = Esn.class, name = "esn"),
|
||||
@JsonSubTypes.Type(value = FleetsyncID.class, name = "fleetsyncID"),
|
||||
@JsonSubTypes.Type(value = LegacyTalkgroupID.class, name = "talkgroupID"),
|
||||
@JsonSubTypes.Type(value = LoJackFunctionAndID.class, name = "loJackFunctionAndID"),
|
||||
@JsonSubTypes.Type(value = MDC1200ID.class, name = "mdc1200ID"),
|
||||
@JsonSubTypes.Type(value = Min.class, name = "min"),
|
||||
@JsonSubTypes.Type(value = MPT1327ID.class, name = "mpt1327ID"),
|
||||
@JsonSubTypes.Type(value = NonRecordable.class, name = "nonRecordable"),
|
||||
@JsonSubTypes.Type(value = Priority.class, name = "priority"),
|
||||
@JsonSubTypes.Type(value = Radio.class, name = "radio"),
|
||||
@JsonSubTypes.Type(value = RadioRange.class, name = "radioRange"),
|
||||
@JsonSubTypes.Type(value = Record.class, name = "record"),
|
||||
@JsonSubTypes.Type(value = SiteID.class, name = "siteID"),
|
||||
@JsonSubTypes.Type(value = StatusID.class, name = "statusID"),
|
||||
@JsonSubTypes.Type(value = TalkgroupID.class, name = "talkgroupID"),
|
||||
@JsonSubTypes.Type(value = Talkgroup.class, name = "talkgroup"),
|
||||
@JsonSubTypes.Type(value = TalkgroupRange.class, name = "talkgroupRange"),
|
||||
@JsonSubTypes.Type(value = TonesID.class, name = "tones"),
|
||||
@JsonSubTypes.Type(value = UserStatusID.class, name = "statusID"),
|
||||
@JsonSubTypes.Type(value = UnitStatusID.class, name = "unitStatusID"),
|
||||
@JsonSubTypes.Type(value = UniqueID.class, name = "uniqueID")
|
||||
})
|
||||
@JacksonXmlRootElement( localName = "id" )
|
||||
@JacksonXmlRootElement(localName = "id")
|
||||
public abstract class AliasID
|
||||
{
|
||||
public AliasID()
|
||||
{
|
||||
}
|
||||
private SimpleStringProperty mValueProperty = new SimpleStringProperty();
|
||||
private BooleanProperty mOverlapProperty = new SimpleBooleanProperty();
|
||||
|
||||
@JsonIgnore
|
||||
public abstract AliasIDType getType();
|
||||
|
||||
public abstract boolean matches( AliasID id );
|
||||
public AliasID()
|
||||
{
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public abstract boolean isValid();
|
||||
/**
|
||||
* String property representation of this alias ID
|
||||
*/
|
||||
@JsonIgnore
|
||||
public SimpleStringProperty valueProperty()
|
||||
{
|
||||
return mValueProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this alias identifier overlaps with any other alias identifier in the same alias list
|
||||
*/
|
||||
@JsonIgnore
|
||||
public BooleanProperty overlapProperty()
|
||||
{
|
||||
return mOverlapProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the overlap flag for this alias id. Overlap indicates that this identifier collides with another
|
||||
* identifier within the same alias list.
|
||||
*/
|
||||
public void setOverlap(boolean overlap)
|
||||
{
|
||||
mOverlapProperty.set(overlap);
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the value property for this alias ID. Note: this method is intended to be invoked by all subclasses
|
||||
* each time that any of the subclass member variable values change.
|
||||
*/
|
||||
public void updateValueProperty()
|
||||
{
|
||||
if(overlapProperty().get())
|
||||
{
|
||||
valueProperty().set(toString() + " - Error: Overlap");
|
||||
}
|
||||
else
|
||||
{
|
||||
valueProperty().set(toString());
|
||||
}
|
||||
|
||||
//Hack: harmless, but the list extractor does not consistently update unless we do this.
|
||||
valueProperty().get();
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public abstract AliasIDType getType();
|
||||
|
||||
public abstract boolean matches(AliasID id);
|
||||
|
||||
/**
|
||||
* Indicates if this alias ID overlaps with another alias ID.
|
||||
* Note: this method is intended to be overridden by certain subclass implementations.
|
||||
*/
|
||||
@JsonIgnore
|
||||
public boolean overlaps(AliasID id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public abstract boolean isValid();
|
||||
|
||||
/**
|
||||
* Indicates if the identifier is an audio identifier (monitor, record, stream)
|
||||
*/
|
||||
@JsonIgnore
|
||||
public abstract boolean isAudioIdentifier();
|
||||
|
||||
/**
|
||||
* Creates an observable property extractor for use with observable lists to detect changes internal to this object.
|
||||
*/
|
||||
public static Callback<AliasID,Observable[]> extractor()
|
||||
{
|
||||
return (AliasID aid) -> new Observable[] {aid.valueProperty(), aid.overlapProperty()};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,49 +1,67 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 Dennis Sheirer
|
||||
* *
|
||||
* * This program is free software: you can redistribute it and/or modify
|
||||
* * it under the terms of the GNU General Public License as published by
|
||||
* * the Free Software Foundation, either version 3 of the License, or
|
||||
* * (at your option) any later version.
|
||||
* *
|
||||
* * This program is distributed in the hope that it will be useful,
|
||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* * GNU General Public License for more details.
|
||||
* *
|
||||
* * You should have received a copy of the GNU General Public License
|
||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
*
|
||||
*/
|
||||
package io.github.dsheirer.alias.id;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
public enum AliasIDType
|
||||
{
|
||||
ESN( "ESN" ),
|
||||
FLEETSYNC( "Fleetsync" ),
|
||||
LOJACK( "LoJack" ),
|
||||
LTR_NET_UID( "LTR-Net UID" ),
|
||||
MDC1200( "MDC-1200" ),
|
||||
MIN( "Passport MIN" ),
|
||||
MPT1327( "MPT-1327" ),
|
||||
SITE( "Site" ),
|
||||
STATUS( "Status" ),
|
||||
TALKGROUP( "Talkgroup" ),
|
||||
BROADCAST_CHANNEL("Audio Broadcast Channel"),
|
||||
ESN("ESN"),
|
||||
INVERT("Audio Inversion"),
|
||||
LOJACK("LoJack"),
|
||||
LTR_NET_UID("LTR-Net UID"),
|
||||
MIN("Passport MIN"),
|
||||
PRIORITY("Audio Priority"),
|
||||
RADIO_ID("Radio ID"),
|
||||
RADIO_ID_RANGE("Radio ID Range"),
|
||||
RECORD("Record"),
|
||||
SITE("Site"),
|
||||
STATUS("User Status"),
|
||||
TONES("Tone Sequence"),
|
||||
UNIT_STATUS("Unit Status"),
|
||||
TALKGROUP("Talkgroup"),
|
||||
TALKGROUP_RANGE("Talkgroup Range"),
|
||||
|
||||
INVERT( "Audio Inversion" ),
|
||||
PRIORITY( "Audio Priority" ),
|
||||
NON_RECORDABLE( "Audio Non-Recordable" ),
|
||||
BROADCAST_CHANNEL( "Audio Broadcast Channel" );
|
||||
|
||||
private String mLabel;
|
||||
|
||||
private AliasIDType( String label )
|
||||
{
|
||||
mLabel = label;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
//Legacy identifier types - no longer used
|
||||
FLEETSYNC("Fleetsync"),
|
||||
LEGACY_TALKGROUP("Legacy Talkgroup"),
|
||||
MDC1200("MDC-1200"),
|
||||
MPT1327("MPT-1327"),
|
||||
NON_RECORDABLE("Audio Non-Recordable");
|
||||
|
||||
private String mLabel;
|
||||
|
||||
AliasIDType(String label)
|
||||
{
|
||||
mLabel = label;
|
||||
}
|
||||
|
||||
//Values used by the View-By alias editor
|
||||
public static EnumSet<AliasIDType> VIEW_BY_VALUES = EnumSet.of(TALKGROUP, TALKGROUP_RANGE, RADIO_ID, RADIO_ID_RANGE,
|
||||
UNIT_STATUS, STATUS, TONES);
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,367 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id;
|
||||
|
||||
import io.github.dsheirer.alias.Alias;
|
||||
import io.github.dsheirer.alias.AliasEvent;
|
||||
import io.github.dsheirer.alias.AliasFactory;
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastModel;
|
||||
import io.github.dsheirer.gui.editor.Editor;
|
||||
import io.github.dsheirer.gui.editor.EmptyEditor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class AliasIdentifierEditor extends Editor<Alias>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static ListModel<AliasID> EMPTY_MODEL = new DefaultListModel<>();
|
||||
private JList<AliasID> mAliasIDList = new JList<>( EMPTY_MODEL );
|
||||
private JButton mNewIDButton;
|
||||
private JButton mCloneIDButton;
|
||||
private JButton mDeleteIDButton;
|
||||
private EditorContainer mEditorContainer = new EditorContainer();
|
||||
|
||||
private AliasModel mAliasModel;
|
||||
private BroadcastModel mBroadcastModel;
|
||||
|
||||
public AliasIdentifierEditor( AliasModel aliasModel, BroadcastModel broadcastModel )
|
||||
{
|
||||
mAliasModel = aliasModel;
|
||||
mBroadcastModel = broadcastModel;
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 3",
|
||||
"[grow,fill][grow,fill][grow,fill]", "[][grow,fill][]" ) );
|
||||
|
||||
mAliasIDList.setVisibleRowCount( 6 );
|
||||
mAliasIDList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
|
||||
mAliasIDList.setLayoutOrientation( JList.VERTICAL );
|
||||
mAliasIDList.addListSelectionListener( new ListSelectionListener()
|
||||
{
|
||||
@Override
|
||||
public void valueChanged( ListSelectionEvent event )
|
||||
{
|
||||
if( !event.getValueIsAdjusting() )
|
||||
{
|
||||
JList<?> list = (JList<?>)event.getSource();
|
||||
|
||||
Object selectedItem = list.getSelectedValue();
|
||||
|
||||
if( selectedItem != null && selectedItem instanceof AliasID )
|
||||
{
|
||||
AliasID selected = (AliasID)selectedItem;
|
||||
|
||||
mEditorContainer.setAliasID( selected );
|
||||
|
||||
mCloneIDButton.setEnabled( true );
|
||||
mDeleteIDButton.setEnabled( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
mCloneIDButton.setEnabled( false );
|
||||
mDeleteIDButton.setEnabled( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
JScrollPane scroller = new JScrollPane( mAliasIDList );
|
||||
add( scroller, "span,grow" );
|
||||
|
||||
add( mEditorContainer, "span,grow" );
|
||||
|
||||
mNewIDButton = new JButton( "New ..." );
|
||||
mNewIDButton.setToolTipText( "Create a new Alias Identifier" );
|
||||
mNewIDButton.addMouseListener( new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e )
|
||||
{
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
|
||||
menu.add( new AddAliasIdentifierItem( AliasIDType.ESN ) );
|
||||
menu.add( new AddAliasIdentifierItem( AliasIDType.FLEETSYNC) );
|
||||
menu.add( new AddAliasIdentifierItem( AliasIDType.LOJACK) );
|
||||
menu.add( new AddAliasIdentifierItem( AliasIDType.LTR_NET_UID) );
|
||||
menu.add( new AddAliasIdentifierItem( AliasIDType.MDC1200 ) );
|
||||
menu.add( new AddAliasIdentifierItem( AliasIDType.MPT1327 ) );
|
||||
menu.add( new AddAliasIdentifierItem( AliasIDType.MIN ) );
|
||||
menu.add( new AddAliasIdentifierItem( AliasIDType.SITE) );
|
||||
menu.add( new AddAliasIdentifierItem( AliasIDType.STATUS) );
|
||||
menu.add( new AddAliasIdentifierItem( AliasIDType.TALKGROUP) );
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
menu.add( new AddAliasIdentifierItem( AliasIDType.BROADCAST_CHANNEL) );
|
||||
|
||||
if( hasItem() )
|
||||
{
|
||||
Alias alias = getItem();
|
||||
|
||||
if( alias.isRecordable() )
|
||||
{
|
||||
menu.add( new AddAliasIdentifierItem( AliasIDType.NON_RECORDABLE) );
|
||||
}
|
||||
|
||||
if( !alias.hasCallPriority() )
|
||||
{
|
||||
menu.add( new AddAliasIdentifierItem( AliasIDType.PRIORITY) );
|
||||
}
|
||||
}
|
||||
|
||||
menu.show( e.getComponent(), e.getX(), e.getY() );
|
||||
}
|
||||
} );
|
||||
|
||||
add( mNewIDButton );
|
||||
|
||||
mCloneIDButton = new JButton( "Clone" );
|
||||
mCloneIDButton.setToolTipText( "Create a copy of the currently selected identifier" );
|
||||
mCloneIDButton.setEnabled( false );
|
||||
mCloneIDButton.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
AliasID selected = mAliasIDList.getSelectedValue();
|
||||
|
||||
if( selected != null )
|
||||
{
|
||||
AliasID clone = AliasFactory.copyOf( selected );
|
||||
|
||||
if( clone != null )
|
||||
{
|
||||
addAliasIDToList( clone );
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
add( mCloneIDButton );
|
||||
|
||||
mDeleteIDButton = new JButton( "Delete" );
|
||||
mDeleteIDButton.setToolTipText( "Delete the currently selected identifier" );
|
||||
mDeleteIDButton.setEnabled( false );
|
||||
mDeleteIDButton.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
final AliasID selected = mAliasIDList.getSelectedValue();
|
||||
|
||||
if( selected != null )
|
||||
{
|
||||
int choice = JOptionPane.showConfirmDialog( mDeleteIDButton,
|
||||
"Do you want to delete [" + selected.toString() + "]",
|
||||
"Are you sure?", JOptionPane.YES_NO_OPTION );
|
||||
|
||||
if( choice == JOptionPane.YES_OPTION && hasItem() )
|
||||
{
|
||||
Alias alias = getItem();
|
||||
|
||||
alias.removeAliasID( selected );
|
||||
|
||||
mAliasModel.broadcast( new AliasEvent( alias, AliasEvent.Event.CHANGE ) );
|
||||
|
||||
setItem( alias );
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
add( mDeleteIDButton, "wrap" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem( Alias alias )
|
||||
{
|
||||
super.setItem( alias );
|
||||
|
||||
if( alias == null || alias.getId().isEmpty() )
|
||||
{
|
||||
mAliasIDList.setModel( EMPTY_MODEL );
|
||||
}
|
||||
else
|
||||
{
|
||||
DefaultListModel<AliasID> model = new DefaultListModel<AliasID>();
|
||||
|
||||
List<AliasID> ids = alias.getId();
|
||||
|
||||
Collections.sort( ids, new Comparator<AliasID>()
|
||||
{
|
||||
@Override
|
||||
public int compare( AliasID o1, AliasID o2 )
|
||||
{
|
||||
return o1.toString().compareTo( o2.toString() );
|
||||
}
|
||||
} );
|
||||
|
||||
for( AliasID id: ids )
|
||||
{
|
||||
model.addElement( id );
|
||||
}
|
||||
|
||||
mAliasIDList.setModel( model );
|
||||
}
|
||||
|
||||
mEditorContainer.setAliasID( null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
if( isModified() || mEditorContainer.isModified() )
|
||||
{
|
||||
if( mEditorContainer.isModified() )
|
||||
{
|
||||
mEditorContainer.save();
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
|
||||
if( hasItem() )
|
||||
{
|
||||
mAliasModel.broadcast( new AliasEvent( getItem(), AliasEvent.Event.CHANGE ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class EditorContainer extends JPanel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Editor<AliasID> mEditor = new EmptyEditor<>();
|
||||
|
||||
public EditorContainer()
|
||||
{
|
||||
setLayout( new MigLayout( "","[grow,fill]", "[grow,fill]" ) );
|
||||
|
||||
add( mEditor );
|
||||
}
|
||||
|
||||
public boolean isModified()
|
||||
{
|
||||
if( mEditor != null )
|
||||
{
|
||||
return mEditor.isModified();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void save()
|
||||
{
|
||||
if( mEditor != null )
|
||||
{
|
||||
mEditor.save();
|
||||
}
|
||||
}
|
||||
|
||||
public void setAliasID( AliasID aliasID )
|
||||
{
|
||||
if( mEditor != null )
|
||||
{
|
||||
if( mEditor.isModified() )
|
||||
{
|
||||
int option = JOptionPane.showConfirmDialog(
|
||||
EditorContainer.this,
|
||||
"Identifier settings have changed. Do you want to save these changes?",
|
||||
"Save Changes?",
|
||||
JOptionPane.YES_NO_OPTION,
|
||||
JOptionPane.QUESTION_MESSAGE );
|
||||
|
||||
if( option == JOptionPane.YES_OPTION )
|
||||
{
|
||||
mEditor.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
removeAll();
|
||||
|
||||
//This will always give us an editor
|
||||
mEditor = AliasFactory.getEditor( aliasID, mBroadcastModel );
|
||||
|
||||
add( mEditor );
|
||||
|
||||
revalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private void addAliasIDToList( final AliasID id )
|
||||
{
|
||||
if( id != null && hasItem() )
|
||||
{
|
||||
final Alias alias = getItem();
|
||||
|
||||
alias.addAliasID( id );
|
||||
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
setItem( alias );
|
||||
|
||||
mAliasIDList.setSelectedValue( id, true );
|
||||
|
||||
AliasIdentifierEditor.this.setModified( true );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
public class AddAliasIdentifierItem extends JMenuItem
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private AliasIDType mAliasIDType;
|
||||
|
||||
public AddAliasIdentifierItem( AliasIDType type )
|
||||
{
|
||||
super( type.toString() );
|
||||
|
||||
mAliasIDType = type;
|
||||
|
||||
addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
final AliasID id = AliasFactory.getAliasID( mAliasIDType );
|
||||
|
||||
addAliasIDToList( id );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class WildcardID implements Comparable<WildcardID>
|
||||
{
|
||||
public static final String WILDCARD = "*";
|
||||
public static final String REGEX_WILDCARD = ".";
|
||||
|
||||
private String mValue;
|
||||
private Pattern mPattern;
|
||||
private int mWeight;
|
||||
|
||||
/**
|
||||
* Wildcard identifier for matching to string identifiers containing single-character (*) wildcard values.
|
||||
*
|
||||
* Supports ordering from most-specific to least-specific using the build-in weighting calculation where identifier
|
||||
* patterns containing fewer wildcards closer to the least significant digit will be weighted more heavily than
|
||||
* identifier patterns containing more wildcards, or wildcard characters in the most significant digits.
|
||||
*
|
||||
* @param value is a string value containing one or more asterisk (*) single character wildcard with a maximum
|
||||
* length of 10 characters.
|
||||
*
|
||||
* @throws AssertionError if the value is null, longer than 10 characters, or doesn't contain at least 1 asterisk
|
||||
* @throws IllegalArgumentException if the value cannot be compiled as a regular expression after converting the
|
||||
* asterisk characters to the regex single-character wildcard value (.)
|
||||
*/
|
||||
public WildcardID(String value)
|
||||
{
|
||||
Validate.isTrue(value != null && value.contains(WILDCARD) && value.length() < 10);
|
||||
|
||||
mValue = value;
|
||||
|
||||
try
|
||||
{
|
||||
mPattern = Pattern.compile(value.replace(WILDCARD, REGEX_WILDCARD));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new IllegalArgumentException("Invalid regex pattern for alias ID value [" + value + "]", e);
|
||||
}
|
||||
|
||||
mWeight = calculateWeight();
|
||||
}
|
||||
|
||||
public String value()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the id matches the internal regex pattern for this wildcard alias ID
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
public boolean matches(String id)
|
||||
{
|
||||
return id != null && mPattern.matcher(id).matches();
|
||||
}
|
||||
|
||||
public int weight()
|
||||
{
|
||||
return mWeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a weighting value for wildcard character quantity and significant digit location
|
||||
*/
|
||||
private int calculateWeight()
|
||||
{
|
||||
int weight = 0;
|
||||
int characterCount = -1;
|
||||
|
||||
for( int x = 0; x < mValue.length(); x++)
|
||||
{
|
||||
if(mValue.substring(x, x + 1).equals(WILDCARD))
|
||||
{
|
||||
weight += (int)(Math.pow(2, mValue.length() - x - 1)); //Position weight
|
||||
characterCount++;
|
||||
}
|
||||
}
|
||||
|
||||
weight += (int)(Math.pow(2, characterCount)) * 1000; //Character count weight
|
||||
|
||||
return weight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(WildcardID otherWildcardID)
|
||||
{
|
||||
return Integer.compare(this.weight(), otherWildcardID.weight());
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
/*
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 Dennis Sheirer
|
||||
* *
|
||||
* * This program is free software: you can redistribute it and/or modify
|
||||
* * it under the terms of the GNU General Public License as published by
|
||||
* * the Free Software Foundation, either version 3 of the License, or
|
||||
* * (at your option) any later version.
|
||||
* *
|
||||
* * This program is distributed in the hope that it will be useful,
|
||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* * GNU General Public License for more details.
|
||||
* *
|
||||
* * You should have received a copy of the GNU General Public License
|
||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
*/
|
||||
package io.github.dsheirer.alias.id.broadcast;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
@@ -31,6 +34,12 @@ public class BroadcastChannel extends AliasID implements Comparable<BroadcastCha
|
||||
//JAXB Constructor
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAudioIdentifier()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(BroadcastChannel other)
|
||||
{
|
||||
@@ -94,6 +103,7 @@ public class BroadcastChannel extends AliasID implements Comparable<BroadcastCha
|
||||
public void setChannelName(String channel)
|
||||
{
|
||||
mChannelName = channel;
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@@ -118,13 +128,6 @@ public class BroadcastChannel extends AliasID implements Comparable<BroadcastCha
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
if(isValid())
|
||||
{
|
||||
return "Broadcast Channel: " + mChannelName;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Broadcast Channel: None Selected";
|
||||
}
|
||||
return isValid() ? mChannelName : "(invalid)";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id.broadcast;
|
||||
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastModel;
|
||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.List;
|
||||
|
||||
public class BroadcastChannelEditor extends DocumentListenerEditor<AliasID>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String HELP_TEXT = "<html>"
|
||||
+ "<h3>Audio Broadcast Channel</h3>"
|
||||
+ "Name of a audio streaming broadcast configuration<br>"
|
||||
+ "to be used for streaming audio to an external<br>"
|
||||
+ "streaming server";
|
||||
|
||||
private JComboBox<String> mBroadcastConfigurations;
|
||||
private BroadcastModel mBroadcastModel;
|
||||
|
||||
public BroadcastChannelEditor(AliasID aliasID, BroadcastModel broadcastModel)
|
||||
{
|
||||
mBroadcastModel = broadcastModel;
|
||||
|
||||
initGUI();
|
||||
setItem( aliasID );
|
||||
}
|
||||
|
||||
private void initGUI()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 1", "[]", "[][]" ) );
|
||||
|
||||
add( new JLabel( "Broadcast Channel" ), "grow" );
|
||||
|
||||
List<String> channelNames = mBroadcastModel.getBroadcastConfigurationNames();
|
||||
|
||||
mBroadcastConfigurations = new JComboBox<String>(channelNames.toArray(new String[channelNames.size()]));
|
||||
mBroadcastConfigurations.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
setModified(true);
|
||||
}
|
||||
});
|
||||
|
||||
add(mBroadcastConfigurations, "grow");
|
||||
|
||||
JLabel help = new JLabel( "Help ..." );
|
||||
help.setForeground( Color.BLUE.brighter() );
|
||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
||||
help.addMouseListener( new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e )
|
||||
{
|
||||
JOptionPane.showMessageDialog( BroadcastChannelEditor.this,
|
||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
||||
}
|
||||
} );
|
||||
|
||||
add( help, "align left" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(AliasID item)
|
||||
{
|
||||
super.setItem(item);
|
||||
|
||||
if(item instanceof BroadcastChannel)
|
||||
{
|
||||
BroadcastChannel channel = (BroadcastChannel)item;
|
||||
|
||||
if(channel.isValid())
|
||||
{
|
||||
mBroadcastConfigurations.setSelectedItem(channel.getChannelName());
|
||||
}
|
||||
}
|
||||
|
||||
setModified(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
String channel = (String)mBroadcastConfigurations.getSelectedItem();
|
||||
|
||||
BroadcastConfiguration selectedConfiguration = mBroadcastModel.getBroadcastConfiguration(channel);
|
||||
|
||||
|
||||
if(selectedConfiguration != null)
|
||||
{
|
||||
((BroadcastChannel)getItem()).setChannelName(selectedConfiguration.getName());
|
||||
}
|
||||
else
|
||||
{
|
||||
((BroadcastChannel)getItem()).setChannelName(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id.esn;
|
||||
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
public class ESNEditor extends DocumentListenerEditor<AliasID>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String HELP_TEXT =
|
||||
"<html><h3>Electronic Serial Number (ESN)</h3>"
|
||||
+ "<b>ESN:</b> hexadecimal 0-9, A-F (e.g. <u>ABCD1234</u> )<br>"
|
||||
+ "<b>Wildcard:</b> use an asterisk (*) to wildcard individual<br>"
|
||||
+ "digits (e.g. <u>ABCD123*</u> or <u>AB**1**4</u>)"
|
||||
+ "</html>";
|
||||
|
||||
private JTextField mTextField;
|
||||
|
||||
public ESNEditor( AliasID aliasID )
|
||||
{
|
||||
initGUI();
|
||||
|
||||
setItem( aliasID );
|
||||
}
|
||||
|
||||
private void initGUI()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) );
|
||||
|
||||
add( new JLabel( "ESN:" ) );
|
||||
mTextField = new JTextField();
|
||||
mTextField.getDocument().addDocumentListener( this );
|
||||
mTextField.setToolTipText( HELP_TEXT );
|
||||
add( mTextField, "growx,push" );
|
||||
|
||||
JLabel help = new JLabel( "Help ..." );
|
||||
help.setForeground( Color.BLUE.brighter() );
|
||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
||||
help.addMouseListener( new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e )
|
||||
{
|
||||
JOptionPane.showMessageDialog( ESNEditor.this,
|
||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
||||
}
|
||||
} );
|
||||
add( help, "align left" );
|
||||
}
|
||||
|
||||
public Esn getEsn()
|
||||
{
|
||||
if( getItem() instanceof Esn )
|
||||
{
|
||||
return (Esn)getItem();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem( AliasID aliasID )
|
||||
{
|
||||
super.setItem( aliasID );
|
||||
|
||||
Esn esn = getEsn();
|
||||
|
||||
if( esn != null )
|
||||
{
|
||||
mTextField.setText( esn.getEsn() );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
Esn esn = getEsn();
|
||||
|
||||
if( esn != null )
|
||||
{
|
||||
esn.setEsn( mTextField.getText() );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,12 @@ public class Esn extends AliasID
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAudioIdentifier()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "esn")
|
||||
public String getEsn()
|
||||
{
|
||||
@@ -44,11 +50,18 @@ public class Esn extends AliasID
|
||||
public void setEsn(String esn )
|
||||
{
|
||||
mEsn = esn;
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return "ESN: " + mEsn;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("ESN: ").append(mEsn != null ? mEsn : "(empty)");
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append(" **NOT VALID**");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id.fleetsync;
|
||||
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.MaskFormatter;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
public class FleetsyncIDEditor extends DocumentListenerEditor<AliasID>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( FleetsyncIDEditor.class );
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String HELP_TEXT = "<html>"
|
||||
+ "<h3>Fleetsync Identifier</h3>"
|
||||
+ "<b>Fleetsync:</b> ggg-uuuu where g=Group and u=Unit (e.g. <u>001-0001</u>)<br>"
|
||||
+ "<b>Wildcard:</b> use an asterisk (*) for each digit (e.g. <u>001-****</u>)"
|
||||
+ "</html>";
|
||||
|
||||
private JTextField mTextField;
|
||||
|
||||
public FleetsyncIDEditor( AliasID aliasID )
|
||||
{
|
||||
initGUI();
|
||||
|
||||
setItem( aliasID );
|
||||
}
|
||||
|
||||
private void initGUI()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) );
|
||||
|
||||
add( new JLabel( "Fleetsync ID:" ) );
|
||||
|
||||
MaskFormatter formatter = null;
|
||||
|
||||
try
|
||||
{
|
||||
//Mask: 3 digits - 4 digits
|
||||
formatter = new MaskFormatter( "###-####" );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
//Do nothing, the mask was invalid
|
||||
}
|
||||
|
||||
mTextField = new JFormattedTextField( formatter );
|
||||
mTextField.getDocument().addDocumentListener( this );
|
||||
mTextField.setToolTipText( HELP_TEXT );
|
||||
|
||||
add( mTextField, "growx,push" );
|
||||
|
||||
JLabel help = new JLabel( "Help ..." );
|
||||
help.setForeground( Color.BLUE.brighter() );
|
||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
||||
help.addMouseListener( new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e )
|
||||
{
|
||||
JOptionPane.showMessageDialog( FleetsyncIDEditor.this,
|
||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
||||
}
|
||||
} );
|
||||
add( help, "align left" );
|
||||
}
|
||||
|
||||
public FleetsyncID getFleetsyncID()
|
||||
{
|
||||
if( getItem() instanceof FleetsyncID )
|
||||
{
|
||||
return (FleetsyncID)getItem();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem( AliasID aliasID )
|
||||
{
|
||||
super.setItem( aliasID );
|
||||
|
||||
FleetsyncID fleetsync = getFleetsyncID();
|
||||
|
||||
if( fleetsync != null )
|
||||
{
|
||||
mTextField.setText( fleetsync.getIdent() );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
FleetsyncID fleetsync = getFleetsyncID();
|
||||
|
||||
if( fleetsync != null )
|
||||
{
|
||||
fleetsync.setIdent( mTextField.getText() );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,23 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id.fleetsync;
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.alias.id.legacy.fleetsync;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
@@ -29,6 +31,12 @@ public class FleetsyncID extends AliasID
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAudioIdentifier()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "ident")
|
||||
public String getIdent()
|
||||
{
|
||||
@@ -48,7 +56,7 @@ public class FleetsyncID extends AliasID
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return "Fleetsync: " + mID;
|
||||
return "Fleetsync: " + (mID != null ? mID : "(empty)") + " **INVALID - USE TALKGROUP INSTEAD";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1,21 +1,23 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 Dennis Sheirer
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id.mdc;
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.alias.id.legacy.mdc;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
@@ -30,6 +32,12 @@ public class MDC1200ID extends AliasID
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAudioIdentifier()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "ident")
|
||||
public String getIdent()
|
||||
{
|
||||
@@ -49,7 +57,7 @@ public class MDC1200ID extends AliasID
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return "MDC-1200: " + mID;
|
||||
return "MDC-1200: " + (mID != null ? mID : "(empty)") + " **INVALID - USE TALKGROUP INSTEAD**";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -15,7 +15,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id.mobileID;
|
||||
package io.github.dsheirer.alias.id.legacy.mobileID;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
@@ -33,6 +33,12 @@ public class Min extends AliasID
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAudioIdentifier()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "min")
|
||||
public String getMin()
|
||||
{
|
||||
@@ -52,7 +58,7 @@ public class Min extends AliasID
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return "MIN: " + mMin;
|
||||
return "MIN: " + (mMin != null ? mMin : "(empty)") + (isValid() ? "" : " **INVALID - USE TALKGROUP INSTEAD");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1,21 +1,23 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 Dennis Sheirer
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id.mpt1327;
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.alias.id.legacy.mpt1327;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
@@ -30,6 +32,12 @@ public class MPT1327ID extends AliasID
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAudioIdentifier()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "ident")
|
||||
public String getIdent()
|
||||
{
|
||||
@@ -49,7 +57,7 @@ public class MPT1327ID extends AliasID
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return "MPT-1327: " + mID;
|
||||
return "MPT-1327: " + (mID != null ? mID : "(empty)") + " **INVALID - USE TALKGROUP INSTEAD**";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.alias.id.legacy.nonrecordable;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
|
||||
public class NonRecordable extends AliasID
|
||||
{
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@Override
|
||||
public AliasIDType getType()
|
||||
{
|
||||
return AliasIDType.NON_RECORDABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAudioIdentifier()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(AliasID id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Audio Non-Recordable - **INVALID - USE RECORD INSTEAD**";
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id.siteID;
|
||||
package io.github.dsheirer.alias.id.legacy.siteID;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
@@ -29,6 +29,12 @@ public class SiteID extends AliasID
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAudioIdentifier()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "site")
|
||||
public String getSite()
|
||||
{
|
||||
@@ -48,7 +54,7 @@ public class SiteID extends AliasID
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return "Site: " + mSite;
|
||||
return "Site: " + (mSite != null ? mSite : "(empty)") + (isValid() ? "" : " **NOT VALID**");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.alias.id.legacy.talkgroup;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
|
||||
public class LegacyTalkgroupID extends AliasID
|
||||
{
|
||||
private String mTalkgroup;
|
||||
|
||||
public LegacyTalkgroupID()
|
||||
{
|
||||
//No arg JAXB constructor
|
||||
}
|
||||
|
||||
public LegacyTalkgroupID(String talkgroup)
|
||||
{
|
||||
mTalkgroup = talkgroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAudioIdentifier()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "talkgroup")
|
||||
public String getTalkgroup()
|
||||
{
|
||||
return mTalkgroup;
|
||||
}
|
||||
|
||||
public void setTalkgroup(String talkgroup)
|
||||
{
|
||||
mTalkgroup = talkgroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
return mTalkgroup != null;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("Legacy Talkgroup:").append(mTalkgroup).append(" *Disabled - Please use Talkgroup instead");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(AliasID id)
|
||||
{
|
||||
boolean retVal = false;
|
||||
|
||||
if(id instanceof LegacyTalkgroupID)
|
||||
{
|
||||
LegacyTalkgroupID tgid = (LegacyTalkgroupID)id;
|
||||
|
||||
//Create a pattern - replace * wildcards with regex single-char wildcard
|
||||
String pattern = mTalkgroup.replace("*", ".?");
|
||||
|
||||
retVal = tgid.getTalkgroup().matches(pattern);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@Override
|
||||
public AliasIDType getType()
|
||||
{
|
||||
return AliasIDType.LEGACY_TALKGROUP;
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id.uniqueID;
|
||||
package io.github.dsheirer.alias.id.legacy.uniqueID;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
@@ -29,6 +29,12 @@ public class UniqueID extends AliasID
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAudioIdentifier()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "uid")
|
||||
public int getUid()
|
||||
{
|
||||
@@ -48,7 +54,7 @@ public class UniqueID extends AliasID
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return "UniqueID: " + mUid;
|
||||
return "UniqueID: " + mUid + " **INVALID - USE TALKGROUP INSTEAD";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -26,12 +26,18 @@ import io.github.dsheirer.module.decode.lj1200.LJ1200Message;
|
||||
public class LoJackFunctionAndID extends AliasID
|
||||
{
|
||||
private LJ1200Message.Function mFunction = LJ1200Message.Function.F0_UNKNOWN;
|
||||
private String mID = "*****";
|
||||
private String mID = null;
|
||||
|
||||
public LoJackFunctionAndID()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAudioIdentifier()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "id")
|
||||
public String getID()
|
||||
{
|
||||
@@ -41,6 +47,7 @@ public class LoJackFunctionAndID extends AliasID
|
||||
public void setID(String id)
|
||||
{
|
||||
mID = id;
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "function")
|
||||
@@ -52,6 +59,7 @@ public class LoJackFunctionAndID extends AliasID
|
||||
public void setFunction(LJ1200Message.Function function)
|
||||
{
|
||||
mFunction = function;
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,7 +70,8 @@ public class LoJackFunctionAndID extends AliasID
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return "LoJack FUNC: " + mFunction.getLabel() + " ID:" + (mID == null ? "" : mID);
|
||||
return "LoJack FUNCTION: " + mFunction.getLabel() +
|
||||
" ID:" + (mID == null ? "" : mID) + (isValid() ? "" : " **NOT VALID**");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id.lojack;
|
||||
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
||||
import io.github.dsheirer.module.decode.lj1200.LJ1200Message;
|
||||
import io.github.dsheirer.module.decode.lj1200.LJ1200Message.Function;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.MaskFormatter;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
public class LoJackIDEditor extends DocumentListenerEditor<AliasID>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private JTextField mTextField;
|
||||
|
||||
private JComboBox<LJ1200Message.Function> mFunctionCombo;
|
||||
|
||||
private static final String HELP_TEXT = "<html>"
|
||||
+ "<h3>LoJack Function and Identifier</h3>"
|
||||
+ "<b>Function Code:</b> <u>1Y-SITE ID</u><br>"
|
||||
+ "<b>ID:</b> 5 numbers or characters (e.g. <u>1BN47</u>)<br>"
|
||||
+ "<br>"
|
||||
+ "<b>Wildcard:</b> use an asterisk (*) to wildcard ID<br>"
|
||||
+ "characters (e.g. <u>AB*CD</u> or <u>***12</u> or <u>*****</u>)<br><br>"
|
||||
+ "The middle character in a reply ID code identifies the<br>"
|
||||
+ "entity. Valid ID middle characters are:<br>"
|
||||
+ "<li><b>Tower:</b> X,Y</li>"
|
||||
+ "<li><b>Transponder:</b> 0-9,A,C-H,J-N,P-W</li>"
|
||||
+ "<li><b>Not Used:</b> B,I,O,Z</li>"
|
||||
+ "</html>";
|
||||
|
||||
public LoJackIDEditor( AliasID aliasID )
|
||||
{
|
||||
initGUI();
|
||||
|
||||
setItem( aliasID );
|
||||
}
|
||||
|
||||
private void initGUI()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][]" ) );
|
||||
|
||||
add( new JLabel( "Function:" ) );
|
||||
mFunctionCombo = new JComboBox<LJ1200Message.Function>( Function.values() );
|
||||
mFunctionCombo.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
setModified( true );
|
||||
}
|
||||
} );
|
||||
mFunctionCombo.setToolTipText( HELP_TEXT );
|
||||
add( mFunctionCombo, "growx, push" );
|
||||
|
||||
add( new JLabel( "ID:" ) );
|
||||
|
||||
MaskFormatter formatter = null;
|
||||
|
||||
try
|
||||
{
|
||||
//Mask: any character or number, 5 places
|
||||
formatter = new MaskFormatter( "AAAAA" );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
//Do nothing, the mask was invalid
|
||||
}
|
||||
|
||||
mTextField = new JFormattedTextField( formatter );
|
||||
mTextField.getDocument().addDocumentListener( this );
|
||||
mTextField.setToolTipText( HELP_TEXT );
|
||||
add( mTextField, "growx,push" );
|
||||
|
||||
JLabel help = new JLabel( "Help ..." );
|
||||
help.setForeground( Color.BLUE.brighter() );
|
||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
||||
help.addMouseListener( new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e )
|
||||
{
|
||||
JOptionPane.showMessageDialog( LoJackIDEditor.this,
|
||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
||||
}
|
||||
} );
|
||||
add( help, "align left" );
|
||||
}
|
||||
|
||||
public LoJackFunctionAndID getLoJackID()
|
||||
{
|
||||
if( getItem() instanceof LoJackFunctionAndID )
|
||||
{
|
||||
return (LoJackFunctionAndID)getItem();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem( AliasID aliasID )
|
||||
{
|
||||
super.setItem( aliasID );
|
||||
|
||||
LoJackFunctionAndID lojack = getLoJackID();
|
||||
|
||||
if( lojack != null )
|
||||
{
|
||||
mFunctionCombo.setSelectedItem( lojack.getFunction() );
|
||||
mTextField.setText( lojack.getID() );
|
||||
}
|
||||
else
|
||||
{
|
||||
mFunctionCombo.setSelectedItem( null );
|
||||
mTextField.setText( null );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
LoJackFunctionAndID lojack = getLoJackID();
|
||||
|
||||
if( lojack != null )
|
||||
{
|
||||
lojack.setID( mTextField.getText() );
|
||||
lojack.setFunction( (Function)mFunctionCombo.getSelectedItem() );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id.mdc;
|
||||
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.MaskFormatter;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
public class MDC1200IDEditor extends DocumentListenerEditor<AliasID>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String HELP_TEXT = "<html>"
|
||||
+ "<h3>MDC-1200 Identifier</h3>"
|
||||
+ "<b>MDC-1200:</b> four digit [0-9] value (e.g. <u>1234</u>)<br>"
|
||||
+ "<b>Wildcard:</b> use an asterisk (*) to wildcard individual<br>"
|
||||
+ "digits (e.g. <u>123*</u> or <u>**34</u>)"
|
||||
+ "</html>";
|
||||
|
||||
private JTextField mTextField;
|
||||
|
||||
public MDC1200IDEditor( AliasID aliasID )
|
||||
{
|
||||
initGUI();
|
||||
|
||||
setItem( aliasID );
|
||||
}
|
||||
|
||||
private void initGUI()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) );
|
||||
|
||||
add( new JLabel( "MDC-1200 ID:" ) );
|
||||
|
||||
MaskFormatter formatter = null;
|
||||
|
||||
try
|
||||
{
|
||||
//Mask: 4 digits
|
||||
formatter = new MaskFormatter( "####" );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
//Do nothing, the mask was invalid
|
||||
}
|
||||
|
||||
mTextField = new JFormattedTextField( formatter );
|
||||
mTextField.getDocument().addDocumentListener( this );
|
||||
mTextField.setToolTipText( HELP_TEXT );
|
||||
add( mTextField, "growx,push" );
|
||||
|
||||
JLabel help = new JLabel( "Help ..." );
|
||||
help.setForeground( Color.BLUE.brighter() );
|
||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
||||
help.addMouseListener( new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e )
|
||||
{
|
||||
JOptionPane.showMessageDialog( MDC1200IDEditor.this,
|
||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
||||
}
|
||||
} );
|
||||
add( help, "align left" );
|
||||
}
|
||||
|
||||
public MDC1200ID getMDC1200ID()
|
||||
{
|
||||
if( getItem() instanceof MDC1200ID )
|
||||
{
|
||||
return (MDC1200ID)getItem();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem( AliasID aliasID )
|
||||
{
|
||||
super.setItem( aliasID );
|
||||
|
||||
MDC1200ID mdc = getMDC1200ID();
|
||||
|
||||
if( mdc != null )
|
||||
{
|
||||
mTextField.setText( mdc.getIdent() );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
MDC1200ID mdc = getMDC1200ID();
|
||||
|
||||
if( mdc != null )
|
||||
{
|
||||
mdc.setIdent( mTextField.getText() );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id.mobileID;
|
||||
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.MaskFormatter;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
public class MINEditor extends DocumentListenerEditor<AliasID>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String HELP_TEXT = "<html>"
|
||||
+ "<h3>Passport Mobile ID (MIN)</h3>"
|
||||
+ "<b>MIN:</b> six character hex value (e.g. <u>AB12CD</u>)<br>"
|
||||
+ "<b>Wildcard:</b> use an asterisk (*) to wildcard individual<br>"
|
||||
+ "digits (e.g. <u>AB**CD</u> or <u>AB12**</u>)"
|
||||
+ "</html>";
|
||||
|
||||
private JTextField mTextField;
|
||||
|
||||
public MINEditor( AliasID aliasID )
|
||||
{
|
||||
initGUI();
|
||||
|
||||
setItem( aliasID );
|
||||
}
|
||||
|
||||
private void initGUI()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) );
|
||||
|
||||
add( new JLabel( "MIN:" ) );
|
||||
|
||||
MaskFormatter formatter = null;
|
||||
|
||||
try
|
||||
{
|
||||
//Mask: 6 hex characters
|
||||
formatter = new MaskFormatter( "HHHHHH" );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
//Do nothing, the mask was invalid
|
||||
}
|
||||
|
||||
mTextField = new JFormattedTextField( formatter );
|
||||
mTextField.getDocument().addDocumentListener( this );
|
||||
mTextField.setToolTipText( HELP_TEXT );
|
||||
add( mTextField, "growx,push" );
|
||||
|
||||
JLabel help = new JLabel( "Help ..." );
|
||||
help.setForeground( Color.BLUE.brighter() );
|
||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
||||
help.addMouseListener( new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e )
|
||||
{
|
||||
JOptionPane.showMessageDialog( MINEditor.this,
|
||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
||||
}
|
||||
} );
|
||||
add( help, "align left" );
|
||||
}
|
||||
|
||||
public Min getMin()
|
||||
{
|
||||
if( getItem() instanceof Min )
|
||||
{
|
||||
return (Min)getItem();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem( AliasID aliasID )
|
||||
{
|
||||
super.setItem( aliasID );
|
||||
|
||||
Min min = getMin();
|
||||
|
||||
if( min != null )
|
||||
{
|
||||
mTextField.setText( min.getMin() );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
Min min = getMin();
|
||||
|
||||
if( min != null )
|
||||
{
|
||||
min.setMin( mTextField.getText() );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id.mpt1327;
|
||||
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.MaskFormatter;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
public class MPT1327IDEditor extends DocumentListenerEditor<AliasID>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String HELP_TEXT = "<html>"
|
||||
+ "<h3>MPT-1327 Identifier</h3>"
|
||||
+ "<b>MPT-1327:</b> decimal (0-9) format ppp-iiii where<br>"
|
||||
+ "p=Prefix and i=Ident (e.g. <u>123-0001</u>)<br>"
|
||||
+ "<b>Wildcard:</b> use an asterisk (*) to wildcard individual<br>"
|
||||
+ "digits (e.g. <u>ABCD123*</u> or <u>AB**1**4</u>)"
|
||||
+ "</html>";
|
||||
|
||||
private JTextField mTextField;
|
||||
|
||||
public MPT1327IDEditor( AliasID aliasID )
|
||||
{
|
||||
initGUI();
|
||||
|
||||
setItem( aliasID );
|
||||
}
|
||||
|
||||
private void initGUI()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) );
|
||||
|
||||
add( new JLabel( "MPT-1327 ID:" ) );
|
||||
|
||||
MaskFormatter formatter = null;
|
||||
|
||||
try
|
||||
{
|
||||
//Mask: 3 digits - 4 digits
|
||||
formatter = new MaskFormatter( "***-****" );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
//Do nothing, the mask was invalid
|
||||
}
|
||||
|
||||
mTextField = new JFormattedTextField( formatter );
|
||||
mTextField.getDocument().addDocumentListener( this );
|
||||
mTextField.setToolTipText( HELP_TEXT );
|
||||
|
||||
add( mTextField, "growx,push" );
|
||||
|
||||
JLabel help = new JLabel( "Help ..." );
|
||||
help.setForeground( Color.BLUE.brighter() );
|
||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
||||
help.addMouseListener( new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e )
|
||||
{
|
||||
JOptionPane.showMessageDialog( MPT1327IDEditor.this,
|
||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
||||
}
|
||||
} );
|
||||
add( help, "align left" );
|
||||
}
|
||||
|
||||
public MPT1327ID getMPT1327ID()
|
||||
{
|
||||
if( getItem() instanceof MPT1327ID )
|
||||
{
|
||||
return (MPT1327ID)getItem();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem( AliasID aliasID )
|
||||
{
|
||||
super.setItem( aliasID );
|
||||
|
||||
MPT1327ID mpt = getMPT1327ID();
|
||||
|
||||
if( mpt != null )
|
||||
{
|
||||
mTextField.setText( mpt.getIdent() );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
MPT1327ID mpt = getMPT1327ID();
|
||||
|
||||
if( mpt != null )
|
||||
{
|
||||
mpt.setIdent( mTextField.getText() );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package io.github.dsheirer.alias.id.nonrecordable;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
|
||||
public class NonRecordable extends AliasID
|
||||
{
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@Override
|
||||
public AliasIDType getType()
|
||||
{
|
||||
return AliasIDType.NON_RECORDABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(AliasID id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Audio Non-Recordable";
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id.nonrecordable;
|
||||
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
public class NonRecordableEditor extends DocumentListenerEditor<AliasID>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String HELP_TEXT = "<html>"
|
||||
+ "<h3>Audio Non-Recordable</h3>"
|
||||
+ "This identifies an alias as non-recordable.<br><br>"
|
||||
+ "The alias can still be monitored but associated audio will<br>"
|
||||
+ "not be recorded.<br><br>"
|
||||
+ "The default behavior when audio recording is enabled for a<br>"
|
||||
+ "channel is to record all aliases except any alias with a<br>"
|
||||
+ "non-recordable identifier";
|
||||
|
||||
public NonRecordableEditor( AliasID aliasID )
|
||||
{
|
||||
initGUI();
|
||||
|
||||
setItem( aliasID );
|
||||
}
|
||||
|
||||
private void initGUI()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 1", "[]", "[][]" ) );
|
||||
|
||||
add( new JLabel( "Non-Recordable" ), "wrap" );
|
||||
|
||||
JLabel help = new JLabel( "Help ..." );
|
||||
help.setForeground( Color.BLUE.brighter() );
|
||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
||||
help.addMouseListener( new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e )
|
||||
{
|
||||
JOptionPane.showMessageDialog( NonRecordableEditor.this,
|
||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
||||
}
|
||||
} );
|
||||
add( help, "align left" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,17 @@ public class Priority extends AliasID
|
||||
{
|
||||
}
|
||||
|
||||
public Priority(int priority)
|
||||
{
|
||||
mPriority = priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAudioIdentifier()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
@@ -63,6 +74,7 @@ public class Priority extends AliasID
|
||||
public void setPriority(int priority)
|
||||
{
|
||||
mPriority = priority;
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
public String toString()
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id.priority;
|
||||
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
public class PriorityEditor extends DocumentListenerEditor<AliasID>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final String DO_NOT_MONITOR = "Do Not Monitor";
|
||||
|
||||
private static final String HELP_TEXT = "<html>"
|
||||
+ "<h3>Call Audio Priority</h3>"
|
||||
+ "Priority determines which calls have priority for playback<br>"
|
||||
+ "over your computer speakers, or designates an alias for<br>"
|
||||
+ "no-monitoring if you don't want to hear calls from an alias.<br><br>"
|
||||
+ "Lower values indicate higher priority levels.<br><br>"
|
||||
+ "<b>Do Not Monitor: </b> slide priority all the way to the right"
|
||||
+ "</html>";
|
||||
|
||||
private JSlider mPrioritySlider;
|
||||
private JLabel mPrioritySliderLabel;
|
||||
|
||||
public PriorityEditor( AliasID aliasID )
|
||||
{
|
||||
initGUI();
|
||||
|
||||
setItem( aliasID );
|
||||
}
|
||||
|
||||
private void initGUI()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) );
|
||||
|
||||
mPrioritySlider = new JSlider( JSlider.HORIZONTAL,
|
||||
Priority.MIN_PRIORITY,
|
||||
Priority.MAX_PRIORITY + 1,
|
||||
Priority.MIN_PRIORITY );
|
||||
|
||||
mPrioritySlider.setMajorTickSpacing( 20 );
|
||||
mPrioritySlider.setMinorTickSpacing( 5 );
|
||||
mPrioritySlider.setPaintTicks( true );
|
||||
mPrioritySlider.setLabelTable( mPrioritySlider.createStandardLabels( 20, 20 ) );
|
||||
mPrioritySlider.setPaintLabels( true );
|
||||
mPrioritySlider.addChangeListener( new ChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void stateChanged( ChangeEvent e )
|
||||
{
|
||||
int priority = mPrioritySlider.getValue();
|
||||
|
||||
if( priority == Priority.MAX_PRIORITY + 1 )
|
||||
{
|
||||
mPrioritySliderLabel.setText( "Priority: Do Not Monitor" );
|
||||
}
|
||||
else
|
||||
{
|
||||
mPrioritySliderLabel.setText( "Priority: " + priority );
|
||||
}
|
||||
|
||||
setModified( true );
|
||||
}
|
||||
} );
|
||||
mPrioritySlider.setToolTipText( HELP_TEXT );
|
||||
|
||||
mPrioritySliderLabel = new JLabel( "Priority: " + mPrioritySlider.getValue() + " " );
|
||||
add( mPrioritySliderLabel, "span,align center" );
|
||||
add( mPrioritySlider, "span,grow" );
|
||||
|
||||
JLabel help = new JLabel( "Help ..." );
|
||||
help.setForeground( Color.BLUE.brighter() );
|
||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
||||
help.addMouseListener( new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e )
|
||||
{
|
||||
JOptionPane.showMessageDialog( PriorityEditor.this,
|
||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
||||
}
|
||||
} );
|
||||
add( help, "align left" );
|
||||
}
|
||||
|
||||
public Priority getPriority()
|
||||
{
|
||||
if( getItem() instanceof Priority )
|
||||
{
|
||||
return (Priority)getItem();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem( AliasID aliasID )
|
||||
{
|
||||
super.setItem( aliasID );
|
||||
|
||||
Priority priority = getPriority();
|
||||
|
||||
if( priority != null )
|
||||
{
|
||||
int value = priority.getPriority();
|
||||
|
||||
if( value == Priority.DO_NOT_MONITOR )
|
||||
{
|
||||
value = Priority.MAX_PRIORITY + 1;
|
||||
}
|
||||
|
||||
mPrioritySlider.setValue( value );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
Priority priority = getPriority();
|
||||
|
||||
if( priority != null )
|
||||
{
|
||||
int value = mPrioritySlider.getValue();
|
||||
|
||||
if( value == Priority.MAX_PRIORITY + 1 )
|
||||
{
|
||||
value = Priority.DO_NOT_MONITOR;
|
||||
}
|
||||
|
||||
priority.setPriority( value );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
}
|
||||
}
|
||||
143
src/main/java/io/github/dsheirer/alias/id/radio/Radio.java
Normal file
143
src/main/java/io/github/dsheirer/alias/id/radio/Radio.java
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 Dennis Sheirer
|
||||
* *
|
||||
* * This program is free software: you can redistribute it and/or modify
|
||||
* * it under the terms of the GNU General Public License as published by
|
||||
* * the Free Software Foundation, either version 3 of the License, or
|
||||
* * (at your option) any later version.
|
||||
* *
|
||||
* * This program is distributed in the hope that it will be useful,
|
||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* * GNU General Public License for more details.
|
||||
* *
|
||||
* * You should have received a copy of the GNU General Public License
|
||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
*
|
||||
*/
|
||||
package io.github.dsheirer.alias.id.radio;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupFormatter;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
|
||||
/**
|
||||
* Integer radio identifier with protocol.
|
||||
*/
|
||||
public class Radio extends AliasID implements Comparable<Radio>
|
||||
{
|
||||
private Protocol mProtocol = Protocol.UNKNOWN;
|
||||
private int mValue;
|
||||
|
||||
public Radio()
|
||||
{
|
||||
//No arg JAXB constructor
|
||||
}
|
||||
|
||||
public Radio(Protocol protocol, int value)
|
||||
{
|
||||
mProtocol = protocol;
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAudioIdentifier()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "value")
|
||||
public int getValue()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public void setValue(int value)
|
||||
{
|
||||
mValue = value;
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "protocol")
|
||||
public Protocol getProtocol()
|
||||
{
|
||||
return mProtocol;
|
||||
}
|
||||
|
||||
public void setProtocol(Protocol protocol)
|
||||
{
|
||||
mProtocol = protocol;
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
if(mProtocol == null || mProtocol == Protocol.UNKNOWN)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RadioFormat radioFormat = RadioFormat.get(mProtocol);
|
||||
return radioFormat.getMinimumValidValue() <= mValue && mValue <= radioFormat.getMaximumValidValue();
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("Radio ID:").append(TalkgroupFormatter.format(mProtocol, mValue));
|
||||
sb.append(" Protocol:").append((mProtocol));
|
||||
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append(" **NOT VALID**");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(AliasID id)
|
||||
{
|
||||
if(id instanceof Radio)
|
||||
{
|
||||
Radio tgid = (Radio)id;
|
||||
return (getProtocol() == tgid.getProtocol()) && (getValue() == tgid.getValue());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@Override
|
||||
public AliasIDType getType()
|
||||
{
|
||||
return AliasIDType.RADIO_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Radio other)
|
||||
{
|
||||
if(other == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(getProtocol().equals(other.getProtocol()))
|
||||
{
|
||||
return Integer.compare(getValue(), other.getValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
return getProtocol().compareTo(other.getProtocol());
|
||||
}
|
||||
}
|
||||
}
|
||||
125
src/main/java/io/github/dsheirer/alias/id/radio/RadioFormat.java
Normal file
125
src/main/java/io/github/dsheirer/alias/id/radio/RadioFormat.java
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 Dennis Sheirer
|
||||
* *
|
||||
* * This program is free software: you can redistribute it and/or modify
|
||||
* * it under the terms of the GNU General Public License as published by
|
||||
* * the Free Software Foundation, either version 3 of the License, or
|
||||
* * (at your option) any later version.
|
||||
* *
|
||||
* * This program is distributed in the hope that it will be useful,
|
||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* * GNU General Public License for more details.
|
||||
* *
|
||||
* * You should have received a copy of the GNU General Public License
|
||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.alias.id.radio;
|
||||
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
|
||||
/**
|
||||
* Radio ID protocol identifier entry masks and min/max valid ranges and valid range descriptions
|
||||
*
|
||||
* Note: the getMask() value is intended for use with a MaskFormatter attached to a JFormattedTextField. The
|
||||
* protocol masks that have a dash (-) use the # (any digit) character and the protocol masks that do not have
|
||||
* a formatting dash use an asterisk (*). Within the editor(s) that use this enumeration, the editor must use
|
||||
* a setValidCharacters() on the mask formatter whenever it detects the mask containing one or more asterisks.
|
||||
*/
|
||||
public enum RadioFormat
|
||||
{
|
||||
APCO25("********", 0, 0xFFFFFF, "0 to 16,777,215",
|
||||
"<html>APCO25 valid range is 0 to 16,777,215"),
|
||||
DMR("********", 0, 0xFFFFFF, "0 to 16,777,215",
|
||||
"<html>DMR unit id valid range is 0 to 16,777,215"),
|
||||
PASSPORT("********", 0, 0x7FFFFF, "0 to 8,388,607",
|
||||
"<html>PASSPORT valid range is 0 to 8,388,607"),
|
||||
UNKNOWN("********", 1, 0xFFFFFF, "1 to 16,777,215",
|
||||
"Unknown protocol valid value range is 1-16,777,215");
|
||||
|
||||
private String mMask;
|
||||
private int mMinimumValue;
|
||||
private int mMaximumValue;
|
||||
private String mValidRangeDescription;
|
||||
private String mValidRangeHelpText;
|
||||
|
||||
RadioFormat(String mask, int minimumValue, int maximumValue, String validRangeDescription, String validRangeHelpText)
|
||||
{
|
||||
mMask = mask;
|
||||
mMinimumValue = minimumValue;
|
||||
mMaximumValue = maximumValue;
|
||||
mValidRangeDescription = validRangeDescription;
|
||||
mValidRangeHelpText = validRangeHelpText;
|
||||
}
|
||||
|
||||
public String getMask()
|
||||
{
|
||||
return mMask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimum valid value for the protocol
|
||||
*/
|
||||
public int getMinimumValidValue()
|
||||
{
|
||||
return mMinimumValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximum valid value for the protocol
|
||||
*/
|
||||
public int getMaximumValidValue()
|
||||
{
|
||||
return mMaximumValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the value is valid for the protocol
|
||||
*/
|
||||
public boolean isValid(int value)
|
||||
{
|
||||
return getMinimumValidValue() <= value && value <= getMaximumValidValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Short description of the valid value range
|
||||
*/
|
||||
public String getValidRangeDescription()
|
||||
{
|
||||
return mValidRangeDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Help text for valid value ranges
|
||||
*/
|
||||
public String getValidRangeHelpText()
|
||||
{
|
||||
return mValidRangeHelpText;
|
||||
}
|
||||
|
||||
public static RadioFormat get(Protocol protocol)
|
||||
{
|
||||
if(protocol == null)
|
||||
{
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
switch(protocol)
|
||||
{
|
||||
case APCO25:
|
||||
return APCO25;
|
||||
case DMR:
|
||||
return DMR;
|
||||
case PASSPORT:
|
||||
return PASSPORT;
|
||||
default:
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 Dennis Sheirer
|
||||
* *
|
||||
* * This program is free software: you can redistribute it and/or modify
|
||||
* * it under the terms of the GNU General Public License as published by
|
||||
* * the Free Software Foundation, either version 3 of the License, or
|
||||
* * (at your option) any later version.
|
||||
* *
|
||||
* * This program is distributed in the hope that it will be useful,
|
||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* * GNU General Public License for more details.
|
||||
* *
|
||||
* * You should have received a copy of the GNU General Public License
|
||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.alias.id.radio;
|
||||
|
||||
import io.github.dsheirer.preference.identifier.IntegerFormat;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.APCO25TalkgroupFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.AbstractIntegerFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.DMRTalkgroupFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.UnknownTalkgroupFormatter;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Protocol-specific formatting of radio id values and parsing of formatted radio id values.
|
||||
*/
|
||||
public class RadioFormatter
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(RadioFormatter.class);
|
||||
private static Map<Protocol,AbstractIntegerFormatter> mFormatterMap = new EnumMap<>(Protocol.class);
|
||||
|
||||
static
|
||||
{
|
||||
mFormatterMap.put(Protocol.APCO25, new APCO25TalkgroupFormatter());
|
||||
mFormatterMap.put(Protocol.DMR, new DMRTalkgroupFormatter());
|
||||
mFormatterMap.put(Protocol.UNKNOWN, new UnknownTalkgroupFormatter());
|
||||
}
|
||||
|
||||
public RadioFormatter()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the formatted value into an integer using a protocol specific formatter
|
||||
*/
|
||||
public static int parse(Protocol protocol, String value) throws ParseException
|
||||
{
|
||||
AbstractIntegerFormatter formatter = mFormatterMap.get(protocol);
|
||||
|
||||
if(formatter == null)
|
||||
{
|
||||
formatter = mFormatterMap.get(Protocol.UNKNOWN);
|
||||
}
|
||||
|
||||
return formatter.parse(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the integer value using a protocol-specific formatter
|
||||
*/
|
||||
public static String format(Protocol protocol, int value)
|
||||
{
|
||||
AbstractIntegerFormatter formatter = mFormatterMap.get(protocol);
|
||||
|
||||
if(formatter == null)
|
||||
{
|
||||
formatter = mFormatterMap.get(Protocol.UNKNOWN);
|
||||
}
|
||||
|
||||
return formatter.format(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the integer value to the specified integer format using the protocol specific formatter
|
||||
*/
|
||||
public static String format(Protocol protocol, int value, IntegerFormat format)
|
||||
{
|
||||
AbstractIntegerFormatter formatter = mFormatterMap.get(protocol);
|
||||
|
||||
if(formatter == null)
|
||||
{
|
||||
formatter = mFormatterMap.get(Protocol.UNKNOWN);
|
||||
}
|
||||
|
||||
return formatter.format(value, format);
|
||||
}
|
||||
}
|
||||
208
src/main/java/io/github/dsheirer/alias/id/radio/RadioRange.java
Normal file
208
src/main/java/io/github/dsheirer/alias/id/radio/RadioRange.java
Normal file
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 Dennis Sheirer
|
||||
* *
|
||||
* * This program is free software: you can redistribute it and/or modify
|
||||
* * it under the terms of the GNU General Public License as published by
|
||||
* * the Free Software Foundation, either version 3 of the License, or
|
||||
* * (at your option) any later version.
|
||||
* *
|
||||
* * This program is distributed in the hope that it will be useful,
|
||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* * GNU General Public License for more details.
|
||||
* *
|
||||
* * You should have received a copy of the GNU General Public License
|
||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
*
|
||||
*/
|
||||
package io.github.dsheirer.alias.id.radio;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
|
||||
/**
|
||||
* Integer radio identifier range of values with protocol.
|
||||
*/
|
||||
public class RadioRange extends AliasID implements Comparable<RadioRange>
|
||||
{
|
||||
private Protocol mProtocol = Protocol.UNKNOWN;
|
||||
private int mMinRadio;
|
||||
private int mMaxRadio;
|
||||
|
||||
public RadioRange()
|
||||
{
|
||||
//No arg JAXB constructor
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAudioIdentifier()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a radio range of from - to radio values (inclusive) for the specified protocol
|
||||
* @param protocol for the radio range
|
||||
* @param minRadio starting or minimum radio value
|
||||
* @param maxRadio ending or maximum radio value
|
||||
*/
|
||||
public RadioRange(Protocol protocol, int minRadio, int maxRadio)
|
||||
{
|
||||
mProtocol = protocol;
|
||||
mMinRadio = minRadio;
|
||||
mMaxRadio = maxRadio;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "min")
|
||||
public int getMinRadio()
|
||||
{
|
||||
return mMinRadio;
|
||||
}
|
||||
|
||||
public void setMinRadio(int minRadio)
|
||||
{
|
||||
mMinRadio = minRadio;
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "max")
|
||||
public int getMaxRadio()
|
||||
{
|
||||
return mMaxRadio;
|
||||
}
|
||||
|
||||
public void setMaxRadio(int maxRadio)
|
||||
{
|
||||
mMaxRadio = maxRadio;
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "protocol")
|
||||
public Protocol getProtocol()
|
||||
{
|
||||
return mProtocol;
|
||||
}
|
||||
|
||||
public void setProtocol(Protocol protocol)
|
||||
{
|
||||
mProtocol = protocol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
if(mProtocol == null || mProtocol == Protocol.UNKNOWN)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RadioFormat radioFormat = RadioFormat.get(mProtocol);
|
||||
return radioFormat.getMinimumValidValue() <= mMinRadio &&
|
||||
mMinRadio <= radioFormat.getMaximumValidValue() &&
|
||||
radioFormat.getMinimumValidValue() <= mMaxRadio &&
|
||||
mMaxRadio <= radioFormat.getMaximumValidValue() &&
|
||||
mMinRadio < mMaxRadio;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("Radio ID Range:").append(RadioFormatter.format(mProtocol, mMinRadio));
|
||||
sb.append(" to ").append(RadioFormatter.format(mProtocol, mMaxRadio));
|
||||
sb.append(" Protocol:").append(mProtocol != null ? mProtocol : "unspecified");
|
||||
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append(" **NOT VALID**");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(AliasID id)
|
||||
{
|
||||
if(id instanceof RadioRange)
|
||||
{
|
||||
RadioRange tgid = (RadioRange)id;
|
||||
return (getProtocol() == tgid.getProtocol()) &&
|
||||
(getMinRadio() == tgid.getMinRadio()) &&
|
||||
(getMaxRadio() == tgid.getMaxRadio());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this radio range contains/includes the value
|
||||
* @param radioValue
|
||||
* @return
|
||||
*/
|
||||
public boolean contains(int radioValue)
|
||||
{
|
||||
return getMinRadio() <= radioValue && radioValue <= getMaxRadio();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean overlaps(AliasID other)
|
||||
{
|
||||
return other instanceof RadioRange && overlaps((RadioRange)other);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indicates if this talkgroup range overlaps the talkgroup range argument.
|
||||
* @param radioRange to check for overlap
|
||||
* @return true if the ranges overlap
|
||||
*/
|
||||
public boolean overlaps(RadioRange radioRange)
|
||||
{
|
||||
return contains(radioRange.getMinRadio()) ||
|
||||
contains(radioRange.getMaxRadio()) ||
|
||||
(getMinRadio() < radioRange.getMinRadio() &&
|
||||
radioRange.getMaxRadio() < getMaxRadio()) ||
|
||||
(radioRange.getMinRadio() < getMinRadio() &&
|
||||
getMaxRadio() < radioRange.getMaxRadio());
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@Override
|
||||
public AliasIDType getType()
|
||||
{
|
||||
return AliasIDType.RADIO_ID_RANGE;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int compareTo(RadioRange other)
|
||||
{
|
||||
if(other == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(getProtocol().equals(other.getProtocol()))
|
||||
{
|
||||
if(getMinRadio() == other.getMinRadio())
|
||||
{
|
||||
return Integer.compare(getMaxRadio(), other.getMaxRadio());
|
||||
}
|
||||
else
|
||||
{
|
||||
return Integer.compare(getMinRadio(), other.getMinRadio());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return getProtocol().compareTo(other.getProtocol());
|
||||
}
|
||||
}
|
||||
}
|
||||
62
src/main/java/io/github/dsheirer/alias/id/record/Record.java
Normal file
62
src/main/java/io/github/dsheirer/alias/id/record/Record.java
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.alias.id.record;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
|
||||
/**
|
||||
* Alias identifier to flag an alias for audio recording
|
||||
*/
|
||||
public class Record extends AliasID
|
||||
{
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@Override
|
||||
public AliasIDType getType()
|
||||
{
|
||||
return AliasIDType.RECORD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAudioIdentifier()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(AliasID id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Record Audio";
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id.siteID;
|
||||
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
public class SiteIDEditor extends DocumentListenerEditor<AliasID>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String HELP_TEXT = "<html>"
|
||||
+ "<h3>Site Identifier</h3>"
|
||||
+ "<b>LTR-Net:</b> decimal (0-9) (e.g. <u>019</u>)<br>"
|
||||
+ "<b>MPT-1327:</b> 5 digits (0-9) (e.g. <u>23619</u>)<br>"
|
||||
+ "<b>Passport:</b> 3 digits (0-9) (e.g. <u>019</u>)<br>"
|
||||
+ "<b>P25:</b> hex (0-9, A-F) format RR-SS where RR = RF Subsystem<br>"
|
||||
+ "and SS = Site Number (e.g. RFSS 1 Site 1F: <u>01-1F</u>)"
|
||||
+ "</html>";
|
||||
|
||||
private JTextField mTextField;
|
||||
|
||||
public SiteIDEditor( AliasID aliasID )
|
||||
{
|
||||
initGUI();
|
||||
|
||||
setItem( aliasID );
|
||||
}
|
||||
|
||||
private void initGUI()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) );
|
||||
|
||||
add( new JLabel( "Site ID:" ) );
|
||||
mTextField = new JTextField();
|
||||
mTextField.getDocument().addDocumentListener( this );
|
||||
mTextField.setToolTipText( HELP_TEXT );
|
||||
add( mTextField, "growx,push" );
|
||||
|
||||
JLabel help = new JLabel( "Help ..." );
|
||||
help.setForeground( Color.BLUE.brighter() );
|
||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
||||
help.addMouseListener( new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e )
|
||||
{
|
||||
JOptionPane.showMessageDialog( SiteIDEditor.this,
|
||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
||||
}
|
||||
} );
|
||||
|
||||
add( help, "align left" );
|
||||
}
|
||||
|
||||
public SiteID getSiteID()
|
||||
{
|
||||
if( getItem() instanceof SiteID )
|
||||
{
|
||||
return (SiteID)getItem();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem( AliasID aliasID )
|
||||
{
|
||||
super.setItem( aliasID );
|
||||
|
||||
SiteID siteID = getSiteID();
|
||||
|
||||
if( siteID != null )
|
||||
{
|
||||
mTextField.setText( siteID.getSite() );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
SiteID siteID = getSiteID();
|
||||
|
||||
if( siteID != null )
|
||||
{
|
||||
siteID.setSite( mTextField.getText() );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id.status;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
|
||||
public class StatusID extends AliasID
|
||||
{
|
||||
private int mStatus;
|
||||
|
||||
public StatusID()
|
||||
{
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "status")
|
||||
public int getStatus()
|
||||
{
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
public void setStatus(int status)
|
||||
{
|
||||
this.mStatus = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return "Status: " + String.format("%03d", mStatus);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(AliasID id)
|
||||
{
|
||||
boolean retVal = false;
|
||||
|
||||
if(id instanceof StatusID)
|
||||
{
|
||||
StatusID statusId = (StatusID)id;
|
||||
|
||||
retVal = (mStatus == statusId.getStatus());
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@Override
|
||||
public AliasIDType getType()
|
||||
{
|
||||
return AliasIDType.STATUS;
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id.status;
|
||||
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.MaskFormatter;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
public class StatusIDEditor extends DocumentListenerEditor<AliasID>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String HELP_TEXT = "<html>"
|
||||
+ "<h3>Status Identifier</h3>"
|
||||
+ "Status numbers are used in some protocols like Fleetsync.<br><br>"
|
||||
+ "The status number is assigned a meaning in the radio. You<br>"
|
||||
+ "can assign a 3 digit status code (use leading zeros) to an<br>"
|
||||
+ "alias where the alias contains the status meaning.<br><br>"
|
||||
+ "<b>Status:</b> <u>001</u> engine start"
|
||||
+ "</html>";
|
||||
|
||||
private JTextField mTextField;
|
||||
|
||||
public StatusIDEditor( AliasID aliasID )
|
||||
{
|
||||
initGUI();
|
||||
|
||||
setItem( aliasID );
|
||||
}
|
||||
|
||||
private void initGUI()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) );
|
||||
|
||||
add( new JLabel( "Status:" ) );
|
||||
|
||||
MaskFormatter formatter = null;
|
||||
|
||||
try
|
||||
{
|
||||
//Mask: 3 digits
|
||||
formatter = new MaskFormatter( "###" );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
//Do nothing, the mask was invalid
|
||||
}
|
||||
|
||||
mTextField = new JFormattedTextField( formatter );
|
||||
mTextField.getDocument().addDocumentListener( this );
|
||||
mTextField.setToolTipText( HELP_TEXT );
|
||||
add( mTextField, "growx,push" );
|
||||
|
||||
JLabel help = new JLabel( "Help ..." );
|
||||
help.setForeground( Color.BLUE.brighter() );
|
||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
||||
help.addMouseListener( new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e )
|
||||
{
|
||||
JOptionPane.showMessageDialog( StatusIDEditor.this,
|
||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
||||
}
|
||||
} );
|
||||
add( help, "align left" );
|
||||
}
|
||||
|
||||
public StatusID getStatusID()
|
||||
{
|
||||
if( getItem() instanceof StatusID )
|
||||
{
|
||||
return (StatusID)getItem();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem( AliasID aliasID )
|
||||
{
|
||||
super.setItem( aliasID );
|
||||
|
||||
StatusID statusID = getStatusID();
|
||||
|
||||
if( statusID != null )
|
||||
{
|
||||
mTextField.setText( String.format( "%03d", statusID.getStatus() ) );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
StatusID statusID = getStatusID();
|
||||
|
||||
if( statusID != null )
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
try
|
||||
{
|
||||
status = Integer.valueOf( mTextField.getText() );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
//Do nothing, we couldn't parse the value
|
||||
}
|
||||
|
||||
statusID.setStatus( status );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.alias.id.status;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
|
||||
/**
|
||||
* Unit Status
|
||||
*/
|
||||
public class UnitStatusID extends AliasID
|
||||
{
|
||||
private int mStatus;
|
||||
|
||||
public UnitStatusID()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAudioIdentifier()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "status")
|
||||
public int getStatus()
|
||||
{
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
public void setStatus(int status)
|
||||
{
|
||||
mStatus = status;
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Unit Status: ").append(String.format("%03d", mStatus));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(AliasID id)
|
||||
{
|
||||
boolean retVal = false;
|
||||
|
||||
if(id instanceof UnitStatusID)
|
||||
{
|
||||
UnitStatusID userStatusId = (UnitStatusID)id;
|
||||
|
||||
retVal = (mStatus == userStatusId.getStatus());
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@Override
|
||||
public AliasIDType getType()
|
||||
{
|
||||
return AliasIDType.UNIT_STATUS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.alias.id.status;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
|
||||
/**
|
||||
* User Status
|
||||
*/
|
||||
public class UserStatusID extends AliasID
|
||||
{
|
||||
private int mStatus;
|
||||
|
||||
public UserStatusID()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAudioIdentifier()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "status")
|
||||
public int getStatus()
|
||||
{
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
public void setStatus(int status)
|
||||
{
|
||||
mStatus = status;
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("User Status: ").append(String.format("%03d", mStatus));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(AliasID id)
|
||||
{
|
||||
boolean retVal = false;
|
||||
|
||||
if(id instanceof UserStatusID)
|
||||
{
|
||||
UserStatusID userStatusId = (UserStatusID)id;
|
||||
|
||||
retVal = (mStatus == userStatusId.getStatus());
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@Override
|
||||
public AliasIDType getType()
|
||||
{
|
||||
return AliasIDType.STATUS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.alias.id.talkgroup;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Integer talkgroup identifier with protocol.
|
||||
*/
|
||||
public class Talkgroup extends AliasID implements Comparable<Talkgroup>
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(Talkgroup.class);
|
||||
private Protocol mProtocol = Protocol.UNKNOWN;
|
||||
private int mValue;
|
||||
|
||||
public Talkgroup()
|
||||
{
|
||||
//No arg JAXB constructor
|
||||
}
|
||||
|
||||
public Talkgroup(Protocol protocol, int value)
|
||||
{
|
||||
mProtocol = protocol;
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAudioIdentifier()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "value")
|
||||
public int getValue()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public void setValue(int value)
|
||||
{
|
||||
mValue = value;
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "protocol")
|
||||
public Protocol getProtocol()
|
||||
{
|
||||
return mProtocol;
|
||||
}
|
||||
|
||||
public void setProtocol(Protocol protocol)
|
||||
{
|
||||
mProtocol = protocol;
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
if(mProtocol == null || mProtocol == Protocol.UNKNOWN)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TalkgroupFormat talkgroupFormat = TalkgroupFormat.get(mProtocol);
|
||||
return talkgroupFormat.getMinimumValidValue() <= mValue && mValue <= talkgroupFormat.getMaximumValidValue();
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
String talkgroup = null;
|
||||
|
||||
try
|
||||
{
|
||||
talkgroup = TalkgroupFormatter.format(mProtocol, mValue);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Error formatting Talkgroup Protocol [" + mProtocol + "] value [" + mValue + "]", e);
|
||||
}
|
||||
|
||||
sb.append("Talkgroup:").append(talkgroup != null ? talkgroup : "error");
|
||||
sb.append(" Protocol:").append((mProtocol));
|
||||
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append(" **NOT VALID**");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(AliasID id)
|
||||
{
|
||||
if(id instanceof Talkgroup)
|
||||
{
|
||||
Talkgroup tgid = (Talkgroup)id;
|
||||
return (getProtocol() == tgid.getProtocol()) && (getValue() == tgid.getValue());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@Override
|
||||
public AliasIDType getType()
|
||||
{
|
||||
return AliasIDType.TALKGROUP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Talkgroup other)
|
||||
{
|
||||
if(other == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(getProtocol().equals(other.getProtocol()))
|
||||
{
|
||||
return Integer.compare(getValue(), other.getValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
return getProtocol().compareTo(other.getProtocol());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 Dennis Sheirer
|
||||
* *
|
||||
* * This program is free software: you can redistribute it and/or modify
|
||||
* * it under the terms of the GNU General Public License as published by
|
||||
* * the Free Software Foundation, either version 3 of the License, or
|
||||
* * (at your option) any later version.
|
||||
* *
|
||||
* * This program is distributed in the hope that it will be useful,
|
||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* * GNU General Public License for more details.
|
||||
* *
|
||||
* * You should have received a copy of the GNU General Public License
|
||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.alias.id.talkgroup;
|
||||
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
|
||||
/**
|
||||
* Talkgroup protocol identifier entry masks and min/max valid ranges and valid range descriptions
|
||||
*
|
||||
* Note: the getMask() value is intended for use with a MaskFormatter attached to a JFormattedTextField. The
|
||||
* protocol masks that have a dash (-) use the # (any digit) character and the protocol masks that do not have
|
||||
* a formatting dash use an asterisk (*). Within the editor(s) that use this enumeration, the editor must use
|
||||
* a setValidCharacters() on the mask formatter whenever it detects the mask containing one or more asterisks.
|
||||
*/
|
||||
public enum TalkgroupFormat
|
||||
{
|
||||
APCO25("********", 0, 0xFFFF, "0 to 65,535",
|
||||
"<html>APCO25 talkgroup valid range is 0 to 65,535"),
|
||||
DMR("********", 0, 0xFFFFFF, "0 to 16,777,215",
|
||||
"<html>DMR talkgroup valid range is 0 to 16,777,215"),
|
||||
FLEETSYNC("###-####", 0, 0x7FFFFF, "001-0001 to 127-8192",
|
||||
"<html>Fleetsync valid ranges are 1-127(prefix)<br>and 1-8192(ident) (ie. 001-0001 to 127-8192)"),
|
||||
LTR("##-###", 0x101, 0x3FFF, "01-001 to 20-255",
|
||||
"<html>LTR valid ranges are 1-20(repeater) and 1-255(talkgroup) (ie. 01-001 to 20-255)"),
|
||||
MDC1200("*****", 1, 0xFFFF, "1 to 65,535",
|
||||
"MDC-1200 valid value range is 1-65,535"),
|
||||
MPT1327("###-####", 1, 0x7FFFFF, "000-0001 to 127-8192",
|
||||
"<html>MPT-1327 valid ranges are 0-127(prefix)<br>and 1-8192(ident) (ie. 000-0001 to 127-8192)"),
|
||||
NBFM("*****", 1, 0xFFFF, "1 to 65,535",
|
||||
"NBFM valid value range is 1-65,535"),
|
||||
PASSPORT("*****", 1, 0xFFFF, "1 to 65,535",
|
||||
"Passport valid value range is 1-65,535"),
|
||||
UNKNOWN("********", 1, 0xFFFFFF, "1 to 16,777,215",
|
||||
"Unknown protocol valid value range is 1-16,777,215");
|
||||
|
||||
private String mMask;
|
||||
private int mMinimumValue;
|
||||
private int mMaximumValue;
|
||||
private String mValidRangeDescription;
|
||||
private String mValidRangeHelpText;
|
||||
|
||||
TalkgroupFormat(String mask, int minimumValue, int maximumValue, String validRangeDescription, String validRangeHelpText)
|
||||
{
|
||||
mMask = mask;
|
||||
mMinimumValue = minimumValue;
|
||||
mMaximumValue = maximumValue;
|
||||
mValidRangeDescription = validRangeDescription;
|
||||
mValidRangeHelpText = validRangeHelpText;
|
||||
}
|
||||
|
||||
public String getMask()
|
||||
{
|
||||
return mMask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimum valid value for the protocol
|
||||
*/
|
||||
public int getMinimumValidValue()
|
||||
{
|
||||
return mMinimumValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximum valid value for the protocol
|
||||
*/
|
||||
public int getMaximumValidValue()
|
||||
{
|
||||
return mMaximumValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the value is valid for the protocol
|
||||
*/
|
||||
public boolean isValid(int value)
|
||||
{
|
||||
return getMinimumValidValue() <= value && value <= getMaximumValidValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Short description of the valid value range
|
||||
*/
|
||||
public String getValidRangeDescription()
|
||||
{
|
||||
return mValidRangeDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Help text for valid value ranges
|
||||
*/
|
||||
public String getValidRangeHelpText()
|
||||
{
|
||||
return mValidRangeHelpText;
|
||||
}
|
||||
|
||||
public static TalkgroupFormat get(Protocol protocol)
|
||||
{
|
||||
if(protocol == null)
|
||||
{
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
switch(protocol)
|
||||
{
|
||||
case APCO25:
|
||||
return APCO25;
|
||||
case FLEETSYNC:
|
||||
return FLEETSYNC;
|
||||
case LTR:
|
||||
case LTR_NET:
|
||||
return LTR;
|
||||
case MDC1200:
|
||||
return MDC1200;
|
||||
case MPT1327:
|
||||
return MPT1327;
|
||||
case NBFM:
|
||||
return NBFM;
|
||||
case PASSPORT:
|
||||
return PASSPORT;
|
||||
default:
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.alias.id.talkgroup;
|
||||
|
||||
import io.github.dsheirer.preference.identifier.IntegerFormat;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.APCO25TalkgroupFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.AbstractIntegerFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.DMRTalkgroupFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.FleetsyncTalkgroupFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.LTRTalkgroupFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.MDC1200TalkgroupFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.MPT1327TalkgroupFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.NBFMTalkgroupFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.PassportTalkgroupFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.UnknownTalkgroupFormatter;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Protocol-specific formatting of talkgroup values and parsing of formatted talkgroup values.
|
||||
*/
|
||||
public class TalkgroupFormatter
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(TalkgroupFormatter.class);
|
||||
private static Map<Protocol,AbstractIntegerFormatter> mFormatterMap = new EnumMap<>(Protocol.class);
|
||||
|
||||
static
|
||||
{
|
||||
mFormatterMap.put(Protocol.APCO25, new APCO25TalkgroupFormatter());
|
||||
mFormatterMap.put(Protocol.DMR, new DMRTalkgroupFormatter());
|
||||
mFormatterMap.put(Protocol.FLEETSYNC, new FleetsyncTalkgroupFormatter());
|
||||
LTRTalkgroupFormatter ltr = new LTRTalkgroupFormatter();
|
||||
mFormatterMap.put(Protocol.LTR, ltr);
|
||||
mFormatterMap.put(Protocol.LTR_NET, ltr);
|
||||
mFormatterMap.put(Protocol.MDC1200, new MDC1200TalkgroupFormatter());
|
||||
mFormatterMap.put(Protocol.MPT1327, new MPT1327TalkgroupFormatter());
|
||||
mFormatterMap.put(Protocol.NBFM, new NBFMTalkgroupFormatter());
|
||||
mFormatterMap.put(Protocol.PASSPORT, new PassportTalkgroupFormatter());
|
||||
mFormatterMap.put(Protocol.UNKNOWN, new UnknownTalkgroupFormatter());
|
||||
}
|
||||
|
||||
public TalkgroupFormatter()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the formatted value into an integer using a protocol specific formatter
|
||||
*/
|
||||
public static int parse(Protocol protocol, String value) throws ParseException
|
||||
{
|
||||
AbstractIntegerFormatter formatter = mFormatterMap.get(protocol);
|
||||
|
||||
if(formatter == null)
|
||||
{
|
||||
formatter = mFormatterMap.get(Protocol.UNKNOWN);
|
||||
}
|
||||
|
||||
return formatter.parse(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the integer value using a protocol-specific formatter
|
||||
*/
|
||||
public static String format(Protocol protocol, int value)
|
||||
{
|
||||
AbstractIntegerFormatter formatter = mFormatterMap.get(protocol);
|
||||
|
||||
if(formatter == null)
|
||||
{
|
||||
formatter = mFormatterMap.get(Protocol.UNKNOWN);
|
||||
}
|
||||
|
||||
return formatter.format(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the integer value to the specified integer format using the protocol specific formatter
|
||||
*/
|
||||
public static String format(Protocol protocol, int value, IntegerFormat format)
|
||||
{
|
||||
AbstractIntegerFormatter formatter = mFormatterMap.get(protocol);
|
||||
|
||||
if(formatter == null)
|
||||
{
|
||||
formatter = mFormatterMap.get(Protocol.UNKNOWN);
|
||||
}
|
||||
|
||||
return formatter.format(value, format);
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id.talkgroup;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
|
||||
public class TalkgroupID extends AliasID
|
||||
{
|
||||
private String mTalkgroup;
|
||||
|
||||
public TalkgroupID()
|
||||
{
|
||||
//No arg JAXB constructor
|
||||
}
|
||||
|
||||
public TalkgroupID(String talkgroup)
|
||||
{
|
||||
mTalkgroup = talkgroup;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "talkgroup")
|
||||
public String getTalkgroup()
|
||||
{
|
||||
return mTalkgroup;
|
||||
}
|
||||
|
||||
public void setTalkgroup(String talkgroup)
|
||||
{
|
||||
mTalkgroup = talkgroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
return mTalkgroup != null;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("Talkgroup: " + mTalkgroup);
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(AliasID id)
|
||||
{
|
||||
boolean retVal = false;
|
||||
|
||||
if(id instanceof TalkgroupID)
|
||||
{
|
||||
TalkgroupID tgid = (TalkgroupID)id;
|
||||
|
||||
//Create a pattern - replace * wildcards with regex single-char wildcard
|
||||
String pattern = mTalkgroup.replace("*", ".?");
|
||||
|
||||
retVal = tgid.getTalkgroup().matches(pattern);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@Override
|
||||
public AliasIDType getType()
|
||||
{
|
||||
return AliasIDType.TALKGROUP;
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id.talkgroup;
|
||||
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
public class TalkgroupIDEditor extends DocumentListenerEditor<AliasID>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String HELP_TEXT =
|
||||
"<html><h3>Talkgroup Identifier</h3>"
|
||||
+ "<b>P25:</b> 4 or 6 hex characters (e.g. <u>AB12</u> or <u>ABC123</u>)<br>"
|
||||
+ "<b>LTR:</b> A-HH-TTT where A=Area H=Home T=Talkgroup (<u>0-01-128</u>)<br>"
|
||||
+ "<b>Passport</b>: 5-digit number (<u>12345</u> or <u>00023</u>)<br>"
|
||||
+ "<br>"
|
||||
+ "<b>Wildcard:</b> use an asterisk (*) in place of each talkgroup digit (e.g. <u>0*1*5</u>)"
|
||||
+ "</html>";
|
||||
|
||||
private JTextField mTextField;
|
||||
|
||||
public TalkgroupIDEditor( AliasID aliasID )
|
||||
{
|
||||
initGUI();
|
||||
|
||||
setItem( aliasID );
|
||||
}
|
||||
|
||||
private void initGUI()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) );
|
||||
|
||||
add( new JLabel( "Talkgroup:" ) );
|
||||
|
||||
mTextField = new JTextField();
|
||||
mTextField.getDocument().addDocumentListener( this );
|
||||
mTextField.setToolTipText( HELP_TEXT );
|
||||
add( mTextField, "growx,push" );
|
||||
|
||||
JLabel help = new JLabel( "Help ..." );
|
||||
help.setForeground( Color.BLUE.brighter() );
|
||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
||||
help.addMouseListener( new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e )
|
||||
{
|
||||
JOptionPane.showMessageDialog( TalkgroupIDEditor.this,
|
||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
||||
}
|
||||
} );
|
||||
add( help, "align left" );
|
||||
}
|
||||
|
||||
public TalkgroupID getTalkgroupID()
|
||||
{
|
||||
if( getItem() instanceof TalkgroupID )
|
||||
{
|
||||
return (TalkgroupID)getItem();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem( AliasID aliasID )
|
||||
{
|
||||
super.setItem( aliasID );
|
||||
|
||||
TalkgroupID talkgroup = getTalkgroupID();
|
||||
|
||||
if( talkgroup != null )
|
||||
{
|
||||
mTextField.setText( talkgroup.getTalkgroup() );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
TalkgroupID talkgroup = getTalkgroupID();
|
||||
|
||||
if( talkgroup != null )
|
||||
{
|
||||
talkgroup.setTalkgroup( mTextField.getText() );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.alias.id.talkgroup;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
|
||||
/**
|
||||
* Integer talkgroup identifier range of values with protocol.
|
||||
*/
|
||||
public class TalkgroupRange extends AliasID implements Comparable<TalkgroupRange>
|
||||
{
|
||||
private Protocol mProtocol = Protocol.UNKNOWN;
|
||||
private int mMinTalkgroup;
|
||||
private int mMaxTalkgroup;
|
||||
|
||||
public TalkgroupRange()
|
||||
{
|
||||
//No arg JAXB constructor
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a talkgroup range of from - to talkgroup values (inclusive) for the specified protocol
|
||||
* @param protocol for the talkgroup range
|
||||
* @param minTalkgroup starting or minimum talkgroup value
|
||||
* @param maxTalkgroup ending or maximum talkgroup value
|
||||
*/
|
||||
public TalkgroupRange(Protocol protocol, int minTalkgroup, int maxTalkgroup)
|
||||
{
|
||||
mProtocol = protocol;
|
||||
mMinTalkgroup = minTalkgroup;
|
||||
mMaxTalkgroup = maxTalkgroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAudioIdentifier()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "min")
|
||||
public int getMinTalkgroup()
|
||||
{
|
||||
return mMinTalkgroup;
|
||||
}
|
||||
|
||||
public void setMinTalkgroup(int minTalkgroup)
|
||||
{
|
||||
mMinTalkgroup = minTalkgroup;
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "max")
|
||||
public int getMaxTalkgroup()
|
||||
{
|
||||
return mMaxTalkgroup;
|
||||
}
|
||||
|
||||
public void setMaxTalkgroup(int maxTalkgroup)
|
||||
{
|
||||
mMaxTalkgroup = maxTalkgroup;
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "protocol")
|
||||
public Protocol getProtocol()
|
||||
{
|
||||
return mProtocol;
|
||||
}
|
||||
|
||||
public void setProtocol(Protocol protocol)
|
||||
{
|
||||
mProtocol = protocol;
|
||||
updateValueProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
if(mProtocol == null || mProtocol == Protocol.UNKNOWN)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TalkgroupFormat talkgroupFormat = TalkgroupFormat.get(mProtocol);
|
||||
return talkgroupFormat.getMinimumValidValue() <= mMinTalkgroup &&
|
||||
mMinTalkgroup <= talkgroupFormat.getMaximumValidValue() &&
|
||||
talkgroupFormat.getMinimumValidValue() <= mMaxTalkgroup &&
|
||||
mMaxTalkgroup <= talkgroupFormat.getMaximumValidValue() &&
|
||||
mMinTalkgroup < mMaxTalkgroup;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("Talkgroup Range:").append(TalkgroupFormatter.format(mProtocol, mMinTalkgroup));
|
||||
sb.append(" to ").append(TalkgroupFormatter.format(mProtocol, mMaxTalkgroup));
|
||||
sb.append(" Protocol:").append(mProtocol != null ? mProtocol : "unspecified");
|
||||
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append(" **NOT VALID**");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(AliasID id)
|
||||
{
|
||||
if(id instanceof TalkgroupRange)
|
||||
{
|
||||
TalkgroupRange tgid = (TalkgroupRange)id;
|
||||
return (getProtocol() == tgid.getProtocol()) &&
|
||||
(getMinTalkgroup() == tgid.getMinTalkgroup()) &&
|
||||
(getMaxTalkgroup() == tgid.getMaxTalkgroup());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this talkgroup range contains/includes the talkgroup value
|
||||
* @param talkgroupValue
|
||||
* @return
|
||||
*/
|
||||
public boolean contains(int talkgroupValue)
|
||||
{
|
||||
return getMinTalkgroup() <= talkgroupValue && talkgroupValue <= getMaxTalkgroup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean overlaps(AliasID other)
|
||||
{
|
||||
return other instanceof TalkgroupRange && overlaps((TalkgroupRange)other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this talkgroup range overlaps the talkgroup range argument.
|
||||
* @param talkgroupRange to check for overlap
|
||||
* @return true if the ranges overlap
|
||||
*/
|
||||
public boolean overlaps(TalkgroupRange talkgroupRange)
|
||||
{
|
||||
return contains(talkgroupRange.getMinTalkgroup()) ||
|
||||
contains(talkgroupRange.getMaxTalkgroup()) ||
|
||||
(getMinTalkgroup() < talkgroupRange.getMinTalkgroup() &&
|
||||
talkgroupRange.getMaxTalkgroup() < getMaxTalkgroup()) ||
|
||||
(talkgroupRange.getMinTalkgroup() < getMinTalkgroup() &&
|
||||
getMaxTalkgroup() < talkgroupRange.getMaxTalkgroup());
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@Override
|
||||
public AliasIDType getType()
|
||||
{
|
||||
return AliasIDType.TALKGROUP_RANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(TalkgroupRange other)
|
||||
{
|
||||
if(other == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(getProtocol().equals(other.getProtocol()))
|
||||
{
|
||||
if(getMinTalkgroup() == other.getMinTalkgroup())
|
||||
{
|
||||
return Integer.compare(getMaxTalkgroup(), other.getMaxTalkgroup());
|
||||
}
|
||||
else
|
||||
{
|
||||
return Integer.compare(getMinTalkgroup(), other.getMinTalkgroup());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return getProtocol().compareTo(other.getProtocol());
|
||||
}
|
||||
}
|
||||
}
|
||||
150
src/main/java/io/github/dsheirer/alias/id/tone/TonesID.java
Normal file
150
src/main/java/io/github/dsheirer/alias/id/tone/TonesID.java
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.alias.id.tone;
|
||||
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
import io.github.dsheirer.identifier.tone.Tone;
|
||||
import io.github.dsheirer.identifier.tone.ToneSequence;
|
||||
import javafx.collections.ListChangeListener;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tone sequence alias identifier. This is used for matching tone sequences produced by the AMBE audio CODEC. Some
|
||||
* examples include DTMF dialed numbers, KNOX tones, 2-Tone, Tone-outs, etc.
|
||||
*/
|
||||
public class TonesID extends AliasID implements ListChangeListener<Tone>
|
||||
{
|
||||
private ToneSequence mToneSequence;
|
||||
|
||||
public TonesID()
|
||||
{
|
||||
setToneSequence(new ToneSequence());
|
||||
//Empty serialization constructor
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance with the specified tone sequence
|
||||
*/
|
||||
public TonesID(ToneSequence toneSequence)
|
||||
{
|
||||
setToneSequence(toneSequence);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AliasIDType getType()
|
||||
{
|
||||
return AliasIDType.TONES;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of tones that define this identifier
|
||||
*/
|
||||
|
||||
public ToneSequence getToneSequence()
|
||||
{
|
||||
return mToneSequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tone sequence(s) for this ID
|
||||
*/
|
||||
public void setToneSequence(ToneSequence toneSequence)
|
||||
{
|
||||
if(mToneSequence != null)
|
||||
{
|
||||
mToneSequence.tonesProperty().removeListener(this);
|
||||
}
|
||||
|
||||
mToneSequence = toneSequence;
|
||||
mToneSequence.tonesProperty().addListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the other id is a tones identifier and has the exact same sequence of tones. However, it does not
|
||||
* compare each of the tone duration value.
|
||||
* @param id to check for match
|
||||
* @return true if the identifiers match
|
||||
*/
|
||||
@Override
|
||||
public boolean matches(AliasID id)
|
||||
{
|
||||
boolean match = true;
|
||||
|
||||
if(mToneSequence.getTones().isEmpty() || !(id instanceof TonesID))
|
||||
{
|
||||
match = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
List<Tone> otherTones = ((TonesID)id).getToneSequence().getTones();
|
||||
List<Tone> thisTones = mToneSequence.getTones();
|
||||
|
||||
if(thisTones.size() == otherTones.size())
|
||||
{
|
||||
for(int x = 0; x < thisTones.size(); x++)
|
||||
{
|
||||
if(thisTones.get(x).getAmbeTone() != otherTones.get(x).getAmbeTone())
|
||||
{
|
||||
match = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
match = false;
|
||||
}
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
return getToneSequence() != null && getToneSequence().hasTones();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAudioIdentifier()
|
||||
{
|
||||
//This is not an audio identifier in the context that this method is used for.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
if(getToneSequence() != null)
|
||||
{
|
||||
return "Tones: " + getToneSequence().toString();
|
||||
}
|
||||
|
||||
return "Tones: (empty)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChanged(Change<? extends Tone> c)
|
||||
{
|
||||
updateValueProperty();
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.alias.id.uniqueID;
|
||||
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
public class UniqueIDEditor extends DocumentListenerEditor<AliasID>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String HELP_TEXT = "<html>"
|
||||
+ "<h3>LTR-Net Unique ID (UID)</h3>"
|
||||
+ "<b>UID:</b> identifier assigned to each radio in<br>"
|
||||
+ "the range <u>1 - 2097152</u>"
|
||||
+ "</html>";
|
||||
|
||||
private JTextField mTextField;
|
||||
|
||||
public UniqueIDEditor( AliasID aliasID )
|
||||
{
|
||||
initGUI();
|
||||
|
||||
setItem( aliasID );
|
||||
}
|
||||
|
||||
private void initGUI()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) );
|
||||
|
||||
add( new JLabel( "Unique ID:" ) );
|
||||
mTextField = new JTextField();
|
||||
mTextField.getDocument().addDocumentListener( this );
|
||||
mTextField.setToolTipText( HELP_TEXT );
|
||||
add( mTextField, "growx,push" );
|
||||
|
||||
JLabel help = new JLabel( "Help ..." );
|
||||
help.setForeground( Color.BLUE.brighter() );
|
||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
||||
help.addMouseListener( new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e )
|
||||
{
|
||||
JOptionPane.showMessageDialog( UniqueIDEditor.this,
|
||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
||||
}
|
||||
} );
|
||||
add( help, "align left" );
|
||||
}
|
||||
|
||||
public UniqueID getUniqueID()
|
||||
{
|
||||
if( getItem() instanceof UniqueID )
|
||||
{
|
||||
return (UniqueID)getItem();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem( AliasID aliasID )
|
||||
{
|
||||
super.setItem( aliasID );
|
||||
|
||||
UniqueID uid = getUniqueID();
|
||||
|
||||
if( uid != null )
|
||||
{
|
||||
mTextField.setText( String.valueOf( uid.getUid() ) );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
UniqueID uid = getUniqueID();
|
||||
|
||||
if( uid != null )
|
||||
{
|
||||
int id = 0;
|
||||
|
||||
try
|
||||
{
|
||||
id = Integer.parseInt( mTextField.getText() );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
//Do nothing, we couldn't parse the value
|
||||
}
|
||||
|
||||
uid.setUid( id );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
}
|
||||
}
|
||||
218
src/main/java/io/github/dsheirer/audio/AbstractAudioModule.java
Normal file
218
src/main/java/io/github/dsheirer/audio/AbstractAudioModule.java
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.audio;
|
||||
|
||||
import io.github.dsheirer.alias.AliasList;
|
||||
import io.github.dsheirer.identifier.IdentifierUpdateListener;
|
||||
import io.github.dsheirer.identifier.IdentifierUpdateNotification;
|
||||
import io.github.dsheirer.identifier.MutableIdentifierCollection;
|
||||
import io.github.dsheirer.module.Module;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Base audio module implementation.
|
||||
*/
|
||||
public abstract class AbstractAudioModule extends Module implements IAudioSegmentProvider, IdentifierUpdateListener
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(AbstractAudioModule.class);
|
||||
public static final long DEFAULT_SEGMENT_AUDIO_SAMPLE_LENGTH = 60 * 8000; // 1 minute @ 8kHz
|
||||
public static final int DEFAULT_TIMESLOT = 0;
|
||||
private final int mMaxSegmentAudioSampleLength;
|
||||
private Listener<AudioSegment> mAudioSegmentListener;
|
||||
protected MutableIdentifierCollection mIdentifierCollection;
|
||||
private Broadcaster<IdentifierUpdateNotification> mIdentifierUpdateNotificationBroadcaster = new Broadcaster<>();
|
||||
private AliasList mAliasList;
|
||||
private AudioSegment mAudioSegment;
|
||||
private int mAudioSampleCount = 0;
|
||||
private boolean mRecordAudioOverride;
|
||||
private int mTimeslot;
|
||||
|
||||
/**
|
||||
* Constructs an abstract audio module
|
||||
*
|
||||
* @param aliasList for aliasing identifiers
|
||||
* @param maxSegmentAudioSampleLength in milliseconds
|
||||
*/
|
||||
public AbstractAudioModule(AliasList aliasList, int timeslot, long maxSegmentAudioSampleLength)
|
||||
{
|
||||
mAliasList = aliasList;
|
||||
mMaxSegmentAudioSampleLength = (int)(maxSegmentAudioSampleLength * 8); //Convert milliseconds to samples
|
||||
mTimeslot = timeslot;
|
||||
mIdentifierCollection = new MutableIdentifierCollection(getTimeslot());
|
||||
mIdentifierUpdateNotificationBroadcaster.addListener(mIdentifierCollection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an abstract audio module with a default maximum audio segment length and a default timeslot 0.
|
||||
*/
|
||||
public AbstractAudioModule(AliasList aliasList)
|
||||
{
|
||||
this(aliasList, DEFAULT_TIMESLOT, DEFAULT_SEGMENT_AUDIO_SAMPLE_LENGTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Timeslot for this audio module
|
||||
*/
|
||||
protected int getTimeslot()
|
||||
{
|
||||
return mTimeslot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the current audio segment
|
||||
*/
|
||||
protected void closeAudioSegment()
|
||||
{
|
||||
synchronized(this)
|
||||
{
|
||||
if(mAudioSegment != null)
|
||||
{
|
||||
mAudioSegment.completeProperty().set(true);
|
||||
mIdentifierUpdateNotificationBroadcaster.removeListener(mAudioSegment);
|
||||
mAudioSegment.decrementConsumerCount();
|
||||
mAudioSegment = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop()
|
||||
{
|
||||
closeAudioSegment();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current audio segment, or creates a new audio segment as necessary and broadcasts it to any registered
|
||||
* listener(s).
|
||||
*/
|
||||
protected AudioSegment getAudioSegment()
|
||||
{
|
||||
synchronized(this)
|
||||
{
|
||||
if(mAudioSegment == null)
|
||||
{
|
||||
mAudioSegment = new AudioSegment(mAliasList, getTimeslot());
|
||||
mAudioSegment.incrementConsumerCount();
|
||||
mAudioSegment.addIdentifiers(mIdentifierCollection.getIdentifiers());
|
||||
mIdentifierUpdateNotificationBroadcaster.addListener(mAudioSegment);
|
||||
|
||||
if(mRecordAudioOverride)
|
||||
{
|
||||
mAudioSegment.recordAudioProperty().set(true);
|
||||
}
|
||||
|
||||
if(mAudioSegmentListener != null)
|
||||
{
|
||||
mAudioSegment.incrementConsumerCount();
|
||||
mAudioSegmentListener.receive(mAudioSegment);
|
||||
}
|
||||
|
||||
mAudioSampleCount = 0;
|
||||
}
|
||||
|
||||
return mAudioSegment;
|
||||
}
|
||||
}
|
||||
|
||||
protected void addAudio(float[] audioBuffer)
|
||||
{
|
||||
AudioSegment audioSegment = getAudioSegment();
|
||||
|
||||
//If the current segment exceeds the max samples length, close it so that a new segment gets generated
|
||||
//and then link the segments together
|
||||
if(mAudioSampleCount >= mMaxSegmentAudioSampleLength)
|
||||
{
|
||||
AudioSegment previous = getAudioSegment();
|
||||
closeAudioSegment();
|
||||
audioSegment = getAudioSegment();
|
||||
audioSegment.linkTo(previous);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
audioSegment.addAudio(audioBuffer);
|
||||
mAudioSampleCount += audioBuffer.length;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
closeAudioSegment();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all audio segments as recordable when the argument is true. Otherwise, defers to the aliased identifiers
|
||||
* from the identifier collection to determine whether to record the audio or not.
|
||||
* @param recordAudio set to true to mark all audio as recordable.
|
||||
*/
|
||||
public void setRecordAudio(boolean recordAudio)
|
||||
{
|
||||
mRecordAudioOverride = recordAudio;
|
||||
|
||||
if(mRecordAudioOverride)
|
||||
{
|
||||
synchronized(this)
|
||||
{
|
||||
if(mAudioSegment != null)
|
||||
{
|
||||
mAudioSegment.recordAudioProperty().set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive updated identifiers from decoder state(s).
|
||||
*/
|
||||
@Override
|
||||
public Listener<IdentifierUpdateNotification> getIdentifierUpdateListener()
|
||||
{
|
||||
return mIdentifierUpdateNotificationBroadcaster;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifier collection containing the current set of identifiers received from the decoder state(s).
|
||||
*/
|
||||
public MutableIdentifierCollection getIdentifierCollection()
|
||||
{
|
||||
return mIdentifierCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an audio segment listener to receive the output from this audio module.
|
||||
*/
|
||||
@Override
|
||||
public void setAudioSegmentListener(Listener<AudioSegment> listener)
|
||||
{
|
||||
mAudioSegmentListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the audio segment listener from receiving audio segments from this module.
|
||||
*/
|
||||
@Override
|
||||
public void removeAudioSegmentListener()
|
||||
{
|
||||
mAudioSegmentListener = null;
|
||||
}
|
||||
}
|
||||
@@ -1,252 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.audio;
|
||||
|
||||
import io.github.dsheirer.alias.Alias;
|
||||
import io.github.dsheirer.audio.output.AudioOutput;
|
||||
import io.github.dsheirer.channel.metadata.Metadata;
|
||||
import io.github.dsheirer.icon.IconManager;
|
||||
import io.github.dsheirer.properties.SystemProperties;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.settings.ColorSetting;
|
||||
import io.github.dsheirer.settings.Setting;
|
||||
import io.github.dsheirer.settings.SettingChangeListener;
|
||||
import io.github.dsheirer.settings.SettingsManager;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class AudioChannelPanel extends JPanel
|
||||
implements Listener<AudioEvent>, SettingChangeListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger mLog = LoggerFactory.getLogger(AudioChannelPanel.class);
|
||||
|
||||
public static final String PROPERTY_PREFIX = "audio.channel.panel.color.";
|
||||
public static final String PROPERTY_COLOR_BACKGROUND = PROPERTY_PREFIX + "background";
|
||||
public static final String PROPERTY_COLOR_LABEL = PROPERTY_PREFIX + "label";
|
||||
public static final String PROPERTY_COLOR_MUTED = PROPERTY_PREFIX + "muted";
|
||||
public static final String PROPERTY_COLOR_VALUE = PROPERTY_PREFIX + "value";
|
||||
|
||||
private Font mFont = new Font(Font.MONOSPACED, Font.PLAIN, 16);
|
||||
|
||||
private Color mBackgroundColor;
|
||||
private Color mLabelColor;
|
||||
private Color mMutedColor;
|
||||
private Color mValueColor;
|
||||
|
||||
private IconManager mIconManager;
|
||||
private SettingsManager mSettingsManager;
|
||||
private AudioOutput mAudioOutput;
|
||||
|
||||
private JLabel mMutedLabel = new JLabel("M");
|
||||
private JLabel mChannelName = new JLabel(" ");
|
||||
private JLabel mToAlias = new JLabel(" ");
|
||||
private JLabel mTo = new JLabel("-----");
|
||||
|
||||
private boolean mConfigured = false;
|
||||
|
||||
public AudioChannelPanel(IconManager iconManager, SettingsManager settingsManager, AudioOutput audioOutput)
|
||||
{
|
||||
mIconManager = iconManager;
|
||||
mSettingsManager = settingsManager;
|
||||
mSettingsManager.addListener(this);
|
||||
|
||||
mAudioOutput = audioOutput;
|
||||
|
||||
if(mAudioOutput != null)
|
||||
{
|
||||
mAudioOutput.addAudioEventListener(this);
|
||||
mAudioOutput.setMetadataListener(new AudioMetadataProcessor());
|
||||
}
|
||||
|
||||
mBackgroundColor = SystemProperties.getInstance().get(PROPERTY_COLOR_BACKGROUND, Color.BLACK);
|
||||
mLabelColor = SystemProperties.getInstance().get(PROPERTY_COLOR_LABEL, Color.LIGHT_GRAY);
|
||||
mMutedColor = SystemProperties.getInstance().get(PROPERTY_COLOR_MUTED, Color.RED);
|
||||
mValueColor = SystemProperties.getInstance().get(PROPERTY_COLOR_VALUE, Color.GREEN);
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout(new MigLayout("align center center, insets 0 0 0 0",
|
||||
"[][][align right]0[grow,fill]", ""));
|
||||
setBackground(mBackgroundColor);
|
||||
|
||||
mMutedLabel.setFont(mFont);
|
||||
mMutedLabel.setForeground(mMutedColor);
|
||||
mMutedLabel.setVisible(false);
|
||||
add(mMutedLabel);
|
||||
|
||||
mChannelName = new JLabel(mAudioOutput != null ? mAudioOutput.getChannelName() : " ");
|
||||
mChannelName.setFont(mFont);
|
||||
mChannelName.setForeground(mLabelColor);
|
||||
add(mChannelName);
|
||||
|
||||
mToAlias.setFont(mFont);
|
||||
mToAlias.setForeground(mValueColor);
|
||||
add(mToAlias);
|
||||
|
||||
mTo.setFont(mFont);
|
||||
mTo.setForeground(mValueColor);
|
||||
add(mTo,"wmin 10lp");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(final AudioEvent audioEvent)
|
||||
{
|
||||
switch(audioEvent.getType())
|
||||
{
|
||||
case AUDIO_STOPPED:
|
||||
EventQueue.invokeLater(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
resetLabels();
|
||||
}
|
||||
});
|
||||
break;
|
||||
case AUDIO_MUTED:
|
||||
case AUDIO_UNMUTED:
|
||||
EventQueue.invokeLater(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
mMutedLabel.setVisible(mAudioOutput.isMuted());
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the from and to labels. Note: this does not happen on the swing
|
||||
* event thread. Only invoke from the swing thread.
|
||||
*/
|
||||
private void resetLabels()
|
||||
{
|
||||
updateLabel(mTo, null, mToAlias, null);
|
||||
|
||||
mConfigured = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the alias label with text and icon from the alias. Note: this
|
||||
* does not occur on the Swing event thread -- wrap any calls to this
|
||||
* method with an event thread call.
|
||||
*/
|
||||
private void updateLabel(JLabel textLabel, String value, JLabel iconLabel, Alias alias)
|
||||
{
|
||||
if(value != null)
|
||||
{
|
||||
|
||||
if(alias != null)
|
||||
{
|
||||
iconLabel.setIcon(mIconManager.getIcon(alias.getIconName(), 18));
|
||||
|
||||
String aliasName = alias.getName();
|
||||
|
||||
//Truncate label if length exceeds 33 characters - hack!
|
||||
if(aliasName.length() > 33)
|
||||
{
|
||||
textLabel.setText(aliasName.substring(0,30) + "...");
|
||||
}
|
||||
else
|
||||
{
|
||||
textLabel.setText(aliasName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
iconLabel.setIcon(null);
|
||||
textLabel.setText(value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
iconLabel.setIcon(null);
|
||||
textLabel.setText("-----");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes audio metadata to update this panel's display values
|
||||
*/
|
||||
public class AudioMetadataProcessor implements Listener<Metadata>
|
||||
{
|
||||
@Override
|
||||
public void receive(final Metadata metadata)
|
||||
{
|
||||
if(metadata.isUpdated() || !mConfigured)
|
||||
{
|
||||
EventQueue.invokeLater(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
updateLabel(mTo, metadata.getPrimaryAddressTo().getIdentifier(),
|
||||
mToAlias, metadata.getPrimaryAddressTo().getAlias());
|
||||
|
||||
mConfigured = true;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void settingChanged(Setting setting)
|
||||
{
|
||||
if(setting instanceof ColorSetting)
|
||||
{
|
||||
ColorSetting colorSetting = (ColorSetting) setting;
|
||||
|
||||
switch(colorSetting.getColorSettingName())
|
||||
{
|
||||
case CHANNEL_STATE_LABEL_DECODER:
|
||||
if(mTo != null)
|
||||
{
|
||||
mTo.setForeground(mLabelColor);
|
||||
}
|
||||
if(mToAlias != null)
|
||||
{
|
||||
mToAlias.setForeground(mLabelColor);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void settingDeleted(Setting setting)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.audio;
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
@@ -5,81 +24,107 @@ import javax.sound.sampled.DataLine;
|
||||
import javax.sound.sampled.Line;
|
||||
import javax.sound.sampled.SourceDataLine;
|
||||
|
||||
/**
|
||||
* Audio formats
|
||||
*/
|
||||
public class AudioFormats
|
||||
{
|
||||
public static final AudioFormat.Encoding IMBE_ENCODING =
|
||||
new AudioFormat.Encoding( "IMBE" );
|
||||
|
||||
public static final boolean LITTLE_ENDIAN = false;
|
||||
public static final boolean BIG_ENDIAN = true;
|
||||
|
||||
public static final float IMBE_FRAME_RATE = 50;
|
||||
public static final float IMBE_SAMPLE_RATE = 50;
|
||||
public static final float PCM_8KHZ_RATE = 8000;
|
||||
public static final float PCM_48KHZ_RATE = 48000;
|
||||
|
||||
public static final int IMBE_FRAME_SIZE_BYTES = 18;
|
||||
public static final int IMBE_SAMPLE_SIZE_BITS = 144;
|
||||
|
||||
public static final float PCM_8_KHZ_RATE = 8000;
|
||||
public static final float PCM_16_KHZ_RATE = 16000;
|
||||
public static final float PCM_22050_HZ_RATE = 22050;
|
||||
public static final float PCM_44100_HZ_RATE = 44100;
|
||||
|
||||
public static final int ONE_CHANNEL = 1;
|
||||
public static final int TWO_CHANNELS = 2;
|
||||
public static final int PCM_SAMPLE_SIZE_BITS = 16;
|
||||
public static final int PCM_FRAME_SIZE_BYTES_MONO = 2;
|
||||
public static final int PCM_FRAME_SIZE_BYTES_STEREO = 4;
|
||||
|
||||
public static AudioFormat IMBE_AUDIO_FORMAT =
|
||||
new AudioFormat( IMBE_ENCODING,
|
||||
IMBE_SAMPLE_RATE,
|
||||
IMBE_SAMPLE_SIZE_BITS,
|
||||
ONE_CHANNEL,
|
||||
IMBE_FRAME_SIZE_BYTES,
|
||||
IMBE_FRAME_RATE,
|
||||
LITTLE_ENDIAN );
|
||||
|
||||
public static AudioFormat PCM_SIGNED_8KHZ_16BITS_MONO =
|
||||
new AudioFormat( AudioFormat.Encoding.PCM_SIGNED,
|
||||
PCM_8KHZ_RATE,
|
||||
PCM_SAMPLE_SIZE_BITS,
|
||||
ONE_CHANNEL,
|
||||
PCM_FRAME_SIZE_BYTES_MONO,
|
||||
PCM_8KHZ_RATE,
|
||||
LITTLE_ENDIAN );
|
||||
public static final int PCM_SAMPLE_SIZE_16_BITS = 16;
|
||||
public static final int PCM_SAMPLE_SIZE_32_BITS = 32;
|
||||
public static final int PCM_FRAME_SIZE_BYTES_16_BIT_MONO = 2;
|
||||
public static final int PCM_FRAME_SIZE_BYTES_32_BIT_MONO = 4;
|
||||
public static final int PCM_FRAME_SIZE_BYTES_16_BIT_STEREO = 4;
|
||||
|
||||
public static AudioFormat PCM_SIGNED_8KHZ_16BITS_STEREO =
|
||||
new AudioFormat( AudioFormat.Encoding.PCM_SIGNED,
|
||||
PCM_8KHZ_RATE,
|
||||
PCM_SAMPLE_SIZE_BITS,
|
||||
TWO_CHANNELS,
|
||||
PCM_FRAME_SIZE_BYTES_STEREO,
|
||||
PCM_8KHZ_RATE,
|
||||
public static AudioFormat PCM_SIGNED_8000_HZ_32_BIT_MONO = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED,
|
||||
PCM_8_KHZ_RATE,
|
||||
PCM_SAMPLE_SIZE_32_BITS,
|
||||
ONE_CHANNEL,
|
||||
PCM_FRAME_SIZE_BYTES_32_BIT_MONO,
|
||||
PCM_8_KHZ_RATE,
|
||||
LITTLE_ENDIAN );
|
||||
|
||||
public static AudioFormat PCM_SIGNED_8000_HZ_16_BIT_MONO = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED,
|
||||
PCM_8_KHZ_RATE,
|
||||
PCM_SAMPLE_SIZE_16_BITS,
|
||||
ONE_CHANNEL,
|
||||
PCM_FRAME_SIZE_BYTES_16_BIT_MONO,
|
||||
PCM_8_KHZ_RATE,
|
||||
LITTLE_ENDIAN );
|
||||
|
||||
public static AudioFormat PCM_SIGNED_16000_HZ_16_BIT_MONO = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED,
|
||||
PCM_16_KHZ_RATE,
|
||||
PCM_SAMPLE_SIZE_16_BITS,
|
||||
ONE_CHANNEL,
|
||||
PCM_FRAME_SIZE_BYTES_16_BIT_MONO,
|
||||
PCM_16_KHZ_RATE,
|
||||
LITTLE_ENDIAN );
|
||||
|
||||
public static AudioFormat PCM_SIGNED_16000_HZ_32_BIT_MONO = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED,
|
||||
PCM_16_KHZ_RATE,
|
||||
PCM_SAMPLE_SIZE_32_BITS,
|
||||
ONE_CHANNEL,
|
||||
PCM_FRAME_SIZE_BYTES_32_BIT_MONO,
|
||||
PCM_16_KHZ_RATE,
|
||||
LITTLE_ENDIAN );
|
||||
|
||||
public static AudioFormat PCM_SIGNED_22050_HZ_16_BIT_MONO = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED,
|
||||
PCM_22050_HZ_RATE,
|
||||
PCM_SAMPLE_SIZE_16_BITS,
|
||||
ONE_CHANNEL,
|
||||
PCM_FRAME_SIZE_BYTES_16_BIT_MONO,
|
||||
PCM_22050_HZ_RATE,
|
||||
LITTLE_ENDIAN );
|
||||
|
||||
public static AudioFormat PCM_SIGNED_22050_HZ_32_BIT_MONO = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED,
|
||||
PCM_22050_HZ_RATE,
|
||||
PCM_SAMPLE_SIZE_32_BITS,
|
||||
ONE_CHANNEL,
|
||||
PCM_FRAME_SIZE_BYTES_32_BIT_MONO,
|
||||
PCM_22050_HZ_RATE,
|
||||
LITTLE_ENDIAN );
|
||||
|
||||
public static AudioFormat PCM_SIGNED_44100_HZ_16_BIT_MONO = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED,
|
||||
PCM_44100_HZ_RATE,
|
||||
PCM_SAMPLE_SIZE_16_BITS,
|
||||
ONE_CHANNEL,
|
||||
PCM_FRAME_SIZE_BYTES_16_BIT_MONO,
|
||||
PCM_44100_HZ_RATE,
|
||||
LITTLE_ENDIAN );
|
||||
|
||||
public static AudioFormat PCM_SIGNED_44100_HZ_32_BIT_MONO = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED,
|
||||
PCM_44100_HZ_RATE,
|
||||
PCM_SAMPLE_SIZE_32_BITS,
|
||||
ONE_CHANNEL,
|
||||
PCM_FRAME_SIZE_BYTES_32_BIT_MONO,
|
||||
PCM_44100_HZ_RATE,
|
||||
LITTLE_ENDIAN );
|
||||
|
||||
public static AudioFormat PCM_SIGNED_8000_HZ_16BITS_STEREO = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED,
|
||||
PCM_8_KHZ_RATE,
|
||||
PCM_SAMPLE_SIZE_16_BITS,
|
||||
TWO_CHANNELS,
|
||||
PCM_FRAME_SIZE_BYTES_16_BIT_STEREO,
|
||||
PCM_8_KHZ_RATE,
|
||||
LITTLE_ENDIAN );
|
||||
|
||||
public static AudioFormat PCM_SIGNED_48KHZ_16BITS_MONO =
|
||||
new AudioFormat( AudioFormat.Encoding.PCM_SIGNED,
|
||||
PCM_48KHZ_RATE,
|
||||
PCM_SAMPLE_SIZE_BITS,
|
||||
ONE_CHANNEL,
|
||||
PCM_FRAME_SIZE_BYTES_MONO,
|
||||
PCM_48KHZ_RATE,
|
||||
LITTLE_ENDIAN );
|
||||
|
||||
public static AudioFormat PCM_SIGNED_48KHZ_16BITS_STEREO =
|
||||
new AudioFormat( AudioFormat.Encoding.PCM_SIGNED,
|
||||
PCM_48KHZ_RATE,
|
||||
PCM_SAMPLE_SIZE_BITS,
|
||||
TWO_CHANNELS,
|
||||
PCM_FRAME_SIZE_BYTES_STEREO,
|
||||
PCM_48KHZ_RATE,
|
||||
LITTLE_ENDIAN );
|
||||
|
||||
/**
|
||||
* Source Data Line Info for a 48 kHz, 16-bits signed PCM one channel
|
||||
*/
|
||||
public static final Line.Info MONO_SOURCE_DATALINE_INFO =
|
||||
new DataLine.Info( SourceDataLine.class, PCM_SIGNED_8KHZ_16BITS_MONO);
|
||||
new DataLine.Info( SourceDataLine.class, PCM_SIGNED_8000_HZ_16_BIT_MONO);
|
||||
|
||||
/**
|
||||
* Source Data Line Info for a 48 kHz, 16-bits signed PCM two channels
|
||||
*/
|
||||
public static final Line.Info STEREO_SOURCE_DATALINE_INFO =
|
||||
new DataLine.Info( SourceDataLine.class, PCM_SIGNED_8KHZ_16BITS_STEREO);
|
||||
new DataLine.Info( SourceDataLine.class, PCM_SIGNED_8000_HZ_16BITS_STEREO);
|
||||
}
|
||||
|
||||
@@ -1,567 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.audio;
|
||||
|
||||
import io.github.dsheirer.audio.output.AudioOutput;
|
||||
import io.github.dsheirer.audio.output.MonoAudioOutput;
|
||||
import io.github.dsheirer.audio.output.StereoAudioOutput;
|
||||
import io.github.dsheirer.properties.SystemProperties;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.source.mixer.MixerChannel;
|
||||
import io.github.dsheirer.source.mixer.MixerChannelConfiguration;
|
||||
import io.github.dsheirer.source.mixer.MixerManager;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.Mixer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class AudioManager implements Listener<AudioPacket>, IAudioController
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(AudioManager.class);
|
||||
|
||||
public static final int AUDIO_TIMEOUT = 1000; //1 second
|
||||
|
||||
public static final String AUDIO_CHANNELS_PROPERTY = "audio.manager.channels";
|
||||
public static final String AUDIO_MIXER_PROPERTY = "audio.manager.mixer";
|
||||
|
||||
public static final AudioEvent CONFIGURATION_CHANGE_STARTED =
|
||||
new AudioEvent(AudioEvent.Type.AUDIO_CONFIGURATION_CHANGE_STARTED, null);
|
||||
|
||||
public static final AudioEvent CONFIGURATION_CHANGE_COMPLETE =
|
||||
new AudioEvent(AudioEvent.Type.AUDIO_CONFIGURATION_CHANGE_COMPLETE, null);
|
||||
|
||||
private LinkedTransferQueue<AudioPacket> mAudioPacketQueue = new LinkedTransferQueue<>();
|
||||
private Map<Integer,AudioOutputConnection> mChannelConnectionMap = new HashMap<>();
|
||||
private List<AudioOutputConnection> mAudioOutputConnections = new ArrayList<>();
|
||||
private AudioOutputConnection mLowestPriorityConnection;
|
||||
private int mAvailableConnectionCount;
|
||||
|
||||
private Map<String,AudioOutput> mAudioOutputMap = new HashMap<>();
|
||||
|
||||
private Broadcaster<AudioEvent> mControllerBroadcaster = new Broadcaster<>();
|
||||
|
||||
private ScheduledFuture<?> mProcessingTask;
|
||||
private MixerManager mMixerManager;
|
||||
private MixerChannelConfiguration mMixerChannelConfiguration;
|
||||
|
||||
|
||||
/**
|
||||
* Processes all audio produced by the decoding channels and routes audio
|
||||
* packets to any combination of outputs based on any alias audio routing
|
||||
* options specified by the user.
|
||||
*/
|
||||
public AudioManager(MixerManager mixerManager)
|
||||
{
|
||||
mMixerManager = mixerManager;
|
||||
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the saved mixer configuration or a default configuration for audio playback.
|
||||
*/
|
||||
private void loadSettings()
|
||||
{
|
||||
MixerChannelConfiguration configuration = null;
|
||||
|
||||
SystemProperties properties = SystemProperties.getInstance();
|
||||
|
||||
Mixer defaultMixer = AudioSystem.getMixer(null);
|
||||
|
||||
String mixer = properties.get(AUDIO_MIXER_PROPERTY, defaultMixer.getMixerInfo().getName());
|
||||
|
||||
String channels = properties.get(AUDIO_CHANNELS_PROPERTY, MixerChannel.MONO.name());
|
||||
|
||||
MixerChannelConfiguration[] mixerConfigurations = mMixerManager.getOutputMixers();
|
||||
|
||||
for(MixerChannelConfiguration mixerConfig : mixerConfigurations)
|
||||
{
|
||||
if(mixerConfig.matches(mixer, channels))
|
||||
{
|
||||
configuration = mixerConfig;
|
||||
}
|
||||
}
|
||||
|
||||
if(configuration == null)
|
||||
{
|
||||
configuration = getDefaultConfiguration();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
setMixerChannelConfiguration(configuration, false);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Couldn't set stored audio mixer/channel configuration - using default", e);
|
||||
|
||||
try
|
||||
{
|
||||
setMixerChannelConfiguration(getDefaultConfiguration());
|
||||
}
|
||||
catch(Exception e2)
|
||||
{
|
||||
mLog.error("Couldn't set default audio mixer/channel configuration - no audio will be available", e2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a default audio playback configuration with a mono audio playback channel.
|
||||
*/
|
||||
private MixerChannelConfiguration getDefaultConfiguration()
|
||||
{
|
||||
/* Use the system default mixer and mono channel as default startup */
|
||||
Mixer defaultMixer = AudioSystem.getMixer(null);
|
||||
|
||||
return new MixerChannelConfiguration(defaultMixer, MixerChannel.MONO);
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
if(mProcessingTask != null)
|
||||
{
|
||||
mProcessingTask.cancel(true);
|
||||
}
|
||||
|
||||
mAudioPacketQueue.clear();
|
||||
|
||||
mProcessingTask = null;
|
||||
|
||||
mChannelConnectionMap.clear();
|
||||
|
||||
for(AudioOutputConnection connection : mAudioOutputConnections)
|
||||
{
|
||||
connection.dispose();
|
||||
}
|
||||
|
||||
mAudioOutputConnections.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary ingest point for audio produced by all decoding channels, for distribution to audio playback devices.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void receive(AudioPacket packet)
|
||||
{
|
||||
mAudioPacketQueue.add(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks each audio channel assignment and disconnects any inactive connections
|
||||
*/
|
||||
private void disconnectInactiveChannelAssignments()
|
||||
{
|
||||
boolean changed = false;
|
||||
|
||||
for(AudioOutputConnection connection : mAudioOutputConnections)
|
||||
{
|
||||
if(connection.isInactive() && mChannelConnectionMap.containsKey(connection.getChannelMetadataID()))
|
||||
{
|
||||
mChannelConnectionMap.remove(connection.getChannelMetadataID());
|
||||
connection.disconnect();
|
||||
mAvailableConnectionCount++;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(changed)
|
||||
{
|
||||
updateLowestPriorityAssignment();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies the lowest priority channel connection where the a higher value indicates a lower priority.
|
||||
*/
|
||||
private void updateLowestPriorityAssignment()
|
||||
{
|
||||
mLowestPriorityConnection = null;
|
||||
|
||||
for(AudioOutputConnection connection : mAudioOutputConnections)
|
||||
{
|
||||
if(connection.isConnected() &&
|
||||
(mLowestPriorityConnection == null || mLowestPriorityConnection.getPriority() < connection.getPriority()))
|
||||
{
|
||||
mLowestPriorityConnection = connection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures audio playback to use the configuration specified in the entry argument.
|
||||
*
|
||||
* @param entry to use in configuring the audio playback setup.
|
||||
* @throws AudioException if there is an error
|
||||
*/
|
||||
@Override
|
||||
public void setMixerChannelConfiguration(MixerChannelConfiguration entry) throws AudioException
|
||||
{
|
||||
setMixerChannelConfiguration(entry, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures audio playback to use the configuration specified in the entry argument.
|
||||
*
|
||||
* @param entry to use in configuring the audio playback setup.
|
||||
* @param saveSettings to save the audio playback configuration settings in the properties file.
|
||||
* @throws AudioException if there is an error
|
||||
*/
|
||||
public void setMixerChannelConfiguration(MixerChannelConfiguration entry, boolean saveSettings) throws AudioException
|
||||
{
|
||||
if(entry != null && (entry.getMixerChannel() == MixerChannel.MONO || entry.getMixerChannel() == MixerChannel.STEREO))
|
||||
{
|
||||
mControllerBroadcaster.broadcast(CONFIGURATION_CHANGE_STARTED);
|
||||
|
||||
if(mProcessingTask != null)
|
||||
{
|
||||
mProcessingTask.cancel(true);
|
||||
}
|
||||
|
||||
disposeCurrentConfiguration();
|
||||
|
||||
switch(entry.getMixerChannel())
|
||||
{
|
||||
case MONO:
|
||||
AudioOutput mono = new MonoAudioOutput(entry.getMixer());
|
||||
mAudioOutputConnections.add(new AudioOutputConnection(mono));
|
||||
mAvailableConnectionCount++;
|
||||
mAudioOutputMap.put(mono.getChannelName(), mono);
|
||||
break;
|
||||
case STEREO:
|
||||
AudioOutput left = new StereoAudioOutput(entry.getMixer(), MixerChannel.LEFT);
|
||||
mAudioOutputConnections.add(new AudioOutputConnection(left));
|
||||
mAvailableConnectionCount++;
|
||||
mAudioOutputMap.put(left.getChannelName(), left);
|
||||
|
||||
AudioOutput right = new StereoAudioOutput(entry.getMixer(), MixerChannel.RIGHT);
|
||||
mAudioOutputConnections.add(new AudioOutputConnection(right));
|
||||
mAvailableConnectionCount++;
|
||||
mAudioOutputMap.put(right.getChannelName(), right);
|
||||
break;
|
||||
default:
|
||||
throw new AudioException("Unsupported mixer channel "
|
||||
+ "configuration: " + entry.getMixerChannel());
|
||||
}
|
||||
|
||||
mProcessingTask = ThreadPool.SCHEDULED.scheduleAtFixedRate(new AudioPacketProcessor(),
|
||||
0, 15, TimeUnit.MILLISECONDS);
|
||||
|
||||
mControllerBroadcaster.broadcast(CONFIGURATION_CHANGE_COMPLETE);
|
||||
|
||||
if(saveSettings)
|
||||
{
|
||||
SystemProperties properties = SystemProperties.getInstance();
|
||||
properties.set(AUDIO_MIXER_PROPERTY, entry.getMixer().getMixerInfo().getName());
|
||||
properties.set(AUDIO_CHANNELS_PROPERTY, entry.getMixerChannel().name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all channel assignments and terminates all audio outputs in preparation for complete shutdown or change
|
||||
* to another mixer/channel configuration
|
||||
*/
|
||||
private void disposeCurrentConfiguration()
|
||||
{
|
||||
mChannelConnectionMap.clear();
|
||||
|
||||
for(AudioOutputConnection connection : mAudioOutputConnections)
|
||||
{
|
||||
connection.dispose();
|
||||
}
|
||||
|
||||
mAvailableConnectionCount = 0;
|
||||
|
||||
mAudioOutputConnections.clear();
|
||||
|
||||
mAudioOutputMap.clear();
|
||||
|
||||
mLowestPriorityConnection = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Current audio playback mixer channel configuration setting.
|
||||
*/
|
||||
@Override
|
||||
public MixerChannelConfiguration getMixerChannelConfiguration() throws AudioException
|
||||
{
|
||||
return mMixerChannelConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of audio outputs available for the current mixer channel configuration
|
||||
*/
|
||||
@Override
|
||||
public List<AudioOutput> getAudioOutputs()
|
||||
{
|
||||
List<AudioOutput> outputs = new ArrayList<>(mAudioOutputMap.values());
|
||||
|
||||
Collections.sort(outputs, new Comparator<AudioOutput>()
|
||||
{
|
||||
@Override
|
||||
public int compare(AudioOutput first, AudioOutput second)
|
||||
{
|
||||
return first.getChannelName().compareTo(second.getChannelName());
|
||||
}
|
||||
});
|
||||
|
||||
return outputs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an audio event listener to receive audio event notifications.
|
||||
*/
|
||||
@Override
|
||||
public void addControllerListener(Listener<AudioEvent> listener)
|
||||
{
|
||||
mControllerBroadcaster.addListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an audio event listener from receiving audio event notifications.
|
||||
*/
|
||||
@Override
|
||||
public void removeControllerListener(Listener<AudioEvent> listener)
|
||||
{
|
||||
mControllerBroadcaster.removeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an audio output connection for the packet if one is available, or overrides an existing lower priority
|
||||
* connection. Returns null if no connection is available for the audio packet.
|
||||
*
|
||||
* @param audioPacket from a decoding channel source
|
||||
* @return an audio output connection or null
|
||||
*/
|
||||
private AudioOutputConnection getConnection(AudioPacket audioPacket)
|
||||
{
|
||||
int channelMetadataID = audioPacket.getMetadata().getMetadataID();
|
||||
|
||||
//Use an existing connection
|
||||
if(mChannelConnectionMap.containsKey(channelMetadataID))
|
||||
{
|
||||
return mChannelConnectionMap.get(channelMetadataID);
|
||||
}
|
||||
|
||||
//Connect to an unused, available connection
|
||||
if(mAvailableConnectionCount > 0)
|
||||
{
|
||||
for(AudioOutputConnection connection : mAudioOutputConnections)
|
||||
{
|
||||
if(connection.isDisconnected())
|
||||
{
|
||||
connection.connect(channelMetadataID, audioPacket.getMetadata().getAudioPriority());
|
||||
mChannelConnectionMap.put(channelMetadataID, connection);
|
||||
mAvailableConnectionCount--;
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
}
|
||||
//Preempt an existing lower priority connection and connect when this is a higher priority packet
|
||||
else
|
||||
{
|
||||
int priority = audioPacket.getMetadata().getAudioPriority();
|
||||
|
||||
AudioOutputConnection connection = mLowestPriorityConnection;
|
||||
|
||||
if(connection != null && priority < connection.getPriority())
|
||||
{
|
||||
mChannelConnectionMap.remove(connection.getChannelMetadataID());
|
||||
|
||||
connection.connect(channelMetadataID, priority);
|
||||
|
||||
mChannelConnectionMap.put(channelMetadataID, connection);
|
||||
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public class AudioPacketProcessor implements Runnable
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
disconnectInactiveChannelAssignments();
|
||||
|
||||
if(mAudioPacketQueue != null)
|
||||
{
|
||||
List<AudioPacket> packets = new ArrayList<AudioPacket>();
|
||||
|
||||
mAudioPacketQueue.drainTo(packets);
|
||||
|
||||
for(AudioPacket packet : packets)
|
||||
{
|
||||
/* Don't process any packet's marked as do not monitor */
|
||||
if(!packet.getMetadata().isDoNotMonitor() && packet.getType() == AudioPacket.Type.AUDIO)
|
||||
{
|
||||
AudioOutputConnection connection = getConnection(packet);
|
||||
|
||||
if(connection != null)
|
||||
{
|
||||
connection.receive(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Encountered error while processing audio packets", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Audio output connection manages a connection between a source and an audio
|
||||
* output and maintains current state information about the audio activity
|
||||
* received form the source.
|
||||
*/
|
||||
public class AudioOutputConnection
|
||||
{
|
||||
private static final int DISCONNECTED = -1;
|
||||
|
||||
private AudioOutput mAudioOutput;
|
||||
private int mPriority = 0;
|
||||
private int mChannelMetadataID = DISCONNECTED;
|
||||
|
||||
public AudioOutputConnection(AudioOutput audioOutput)
|
||||
{
|
||||
mAudioOutput = audioOutput;
|
||||
}
|
||||
|
||||
public void receive(AudioPacket packet)
|
||||
{
|
||||
if(packet.hasMetadata() && packet.getMetadata().getMetadataID() == mChannelMetadataID)
|
||||
{
|
||||
int priority = packet.getMetadata().getAudioPriority();
|
||||
|
||||
if(mPriority != priority)
|
||||
{
|
||||
mPriority = priority;
|
||||
updateLowestPriorityAssignment();
|
||||
}
|
||||
|
||||
if(mAudioOutput != null)
|
||||
{
|
||||
mAudioOutput.receive(packet);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(packet.hasMetadata())
|
||||
{
|
||||
mLog.error("Received audio packet from channel metadata [" + packet.getMetadata().getMetadataID() +
|
||||
"] however this assignment is currently connected to metadata [" + mChannelMetadataID + "]");
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error("Received audio packet with no metadata - cannot route audio packet");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminates the audio output and prepares this connection for disposal
|
||||
*/
|
||||
public void dispose()
|
||||
{
|
||||
mAudioOutput.dispose();
|
||||
mAudioOutput = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this assignment is currently disconnected from a channel source
|
||||
*/
|
||||
public boolean isDisconnected()
|
||||
{
|
||||
return mChannelMetadataID == DISCONNECTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this assignment is currently connected to a channel source
|
||||
*/
|
||||
public boolean isConnected()
|
||||
{
|
||||
return !isDisconnected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects this assignment to the indicated source so that audio
|
||||
* packets from this source can be sent to the audio output
|
||||
*/
|
||||
public void connect(int source, int priority)
|
||||
{
|
||||
mChannelMetadataID = source;
|
||||
mPriority = priority;
|
||||
updateLowestPriorityAssignment();
|
||||
|
||||
mAudioOutput.updateTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Currently connected source or -1 if disconnected
|
||||
*/
|
||||
public int getChannelMetadataID()
|
||||
{
|
||||
return mChannelMetadataID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects this assignment from the source and prevents any audio
|
||||
* from being routed to the audio output until another source is assigned
|
||||
*/
|
||||
public void disconnect()
|
||||
{
|
||||
mChannelMetadataID = DISCONNECTED;
|
||||
mPriority = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if audio output is current inactive, meaning that the
|
||||
* audio output hasn't recently processed any audio packets.
|
||||
*/
|
||||
public boolean isInactive()
|
||||
{
|
||||
return (mAudioOutput.getLastActivityTimestamp() + AUDIO_TIMEOUT) < System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public int getPriority()
|
||||
{
|
||||
return mPriority;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -14,175 +14,139 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.audio;
|
||||
|
||||
import io.github.dsheirer.alias.AliasList;
|
||||
import io.github.dsheirer.audio.squelch.ISquelchStateListener;
|
||||
import io.github.dsheirer.audio.squelch.SquelchState;
|
||||
import io.github.dsheirer.channel.metadata.Metadata;
|
||||
import io.github.dsheirer.audio.squelch.SquelchStateEvent;
|
||||
import io.github.dsheirer.dsp.filter.FilterFactory;
|
||||
import io.github.dsheirer.dsp.filter.design.FilterDesignException;
|
||||
import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification;
|
||||
import io.github.dsheirer.dsp.filter.fir.real.IRealFilter;
|
||||
import io.github.dsheirer.dsp.filter.fir.remez.RemezFIRFilterDesigner;
|
||||
import io.github.dsheirer.dsp.filter.polyphase.PolyphaseFIRDecimatingFilter_RB;
|
||||
import io.github.dsheirer.module.Module;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.real.IFilteredRealBufferListener;
|
||||
import io.github.dsheirer.sample.real.RealBuffer;
|
||||
import io.github.dsheirer.sample.real.IRealBufferListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
/**
|
||||
* Provides packaging of demodulated audio sample buffers into audio packets for
|
||||
* broadcast to registered audio packet listeners. Includes audio packet
|
||||
* metadata in constructed audio packets.
|
||||
* Provides packaging of demodulated audio sample buffers into audio segments for broadcast to registered listeners.
|
||||
* Includes audio packet metadata in constructed audio segments.
|
||||
*
|
||||
* Incorporates audio squelch state listener to control if audio packets are
|
||||
* broadcast or ignored.
|
||||
* Incorporates audio squelch state listener to control if audio packets are broadcast or ignored.
|
||||
*/
|
||||
public class AudioModule extends Module implements IAudioPacketProvider, IFilteredRealBufferListener,
|
||||
ISquelchStateListener, Listener<RealBuffer>
|
||||
public class AudioModule extends AbstractAudioModule implements ISquelchStateListener, IRealBufferListener,
|
||||
Listener<float[]>
|
||||
{
|
||||
protected static final Logger mLog = LoggerFactory.getLogger(AudioModule.class);
|
||||
|
||||
private static float[] mDecimationCoefficients;
|
||||
private static final Logger mLog = LoggerFactory.getLogger(AudioModule.class);
|
||||
private static float[] sHighPassFilterCoefficients;
|
||||
|
||||
static
|
||||
{
|
||||
//Band-pass filter to both decimate (48kHz to 8kHz) and block 0 - 300 Hz (LTR signalling).
|
||||
FIRFilterSpecification specification = FIRFilterSpecification.bandPassBuilder()
|
||||
.sampleRate(48000)
|
||||
.stopFrequency1(150)
|
||||
.passFrequencyBegin(300)
|
||||
.passFrequencyEnd(3500)
|
||||
.stopFrequency2(4000)
|
||||
.stopRipple(0.0004)
|
||||
.passRipple(0.008)
|
||||
FIRFilterSpecification specification = FIRFilterSpecification.highPassBuilder()
|
||||
.sampleRate(8000)
|
||||
.stopBandCutoff(200)
|
||||
.stopBandAmplitude(0.0)
|
||||
.stopBandRipple(0.025)
|
||||
.passBandStart(300)
|
||||
.passBandAmplitude(1.0)
|
||||
.passBandRipple(0.01)
|
||||
.build();
|
||||
|
||||
RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner(specification);
|
||||
|
||||
try
|
||||
{
|
||||
if(!designer.isValid())
|
||||
{
|
||||
throw new FilterDesignException("Couldn't design the audio decimation filter");
|
||||
}
|
||||
RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner(specification);
|
||||
|
||||
mDecimationCoefficients = designer.getImpulseResponse();
|
||||
if(designer.isValid())
|
||||
{
|
||||
sHighPassFilterCoefficients = designer.getImpulseResponse();
|
||||
}
|
||||
}
|
||||
catch(FilterDesignException e)
|
||||
catch(FilterDesignException fde)
|
||||
{
|
||||
mLog.debug("Error designing filter", e);
|
||||
mLog.error("Filter design error", fde);
|
||||
}
|
||||
}
|
||||
|
||||
private IRealFilter mHighPassFilter = FilterFactory.getRealFilter(sHighPassFilterCoefficients);
|
||||
private SquelchStateListener mSquelchStateListener = new SquelchStateListener();
|
||||
private SquelchState mSquelchState = SquelchState.SQUELCH;
|
||||
private Listener<AudioPacket> mAudioPacketListener;
|
||||
private PolyphaseFIRDecimatingFilter_RB mAudioDecimationFilter;
|
||||
private Metadata mMetadata;
|
||||
|
||||
public AudioModule(Metadata metadata)
|
||||
/**
|
||||
* Creates an Audio Module.
|
||||
*
|
||||
* @param aliasList for aliasing identifiers
|
||||
* @param maxAudioSegmentLength in milliseconds
|
||||
*/
|
||||
public AudioModule(AliasList aliasList, int timeslot, long maxAudioSegmentLength)
|
||||
{
|
||||
mMetadata = metadata;
|
||||
super(aliasList, timeslot, maxAudioSegmentLength);
|
||||
}
|
||||
|
||||
mAudioDecimationFilter = new PolyphaseFIRDecimatingFilter_RB(mDecimationCoefficients, 6, 2.0f);
|
||||
mAudioDecimationFilter.setListener(new Listener<RealBuffer>()
|
||||
{
|
||||
@Override
|
||||
public void receive(RealBuffer realBuffer)
|
||||
{
|
||||
if(mAudioPacketListener != null)
|
||||
{
|
||||
AudioPacket packet = new AudioPacket(realBuffer.getSamples(), mMetadata.copyOf());
|
||||
mAudioPacketListener.receive(packet);
|
||||
}
|
||||
}
|
||||
});
|
||||
/**
|
||||
* Creates an Audio Module.
|
||||
*/
|
||||
public AudioModule(AliasList aliasList)
|
||||
{
|
||||
super(aliasList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose()
|
||||
protected int getTimeslot()
|
||||
{
|
||||
mSquelchStateListener = null;
|
||||
mAudioPacketListener = null;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
getIdentifierCollection().clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(ScheduledExecutorService executor)
|
||||
public void start()
|
||||
{
|
||||
/* No start operations provided */
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop()
|
||||
{
|
||||
/* Issue an end audio packet in case a recorder is still rolling */
|
||||
if(mAudioPacketListener != null)
|
||||
{
|
||||
mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes demodulated audio samples into audio packets with current audio
|
||||
* metadata and sends to the registered listener
|
||||
*/
|
||||
@Override
|
||||
public void receive(RealBuffer buffer)
|
||||
{
|
||||
if(mAudioPacketListener != null && mSquelchState == SquelchState.UNSQUELCH)
|
||||
{
|
||||
mAudioDecimationFilter.receive(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<RealBuffer> getFilteredRealBufferListener()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAudioPacketListener(Listener<AudioPacket> listener)
|
||||
{
|
||||
mAudioPacketListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAudioPacketListener()
|
||||
{
|
||||
mAudioPacketListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<SquelchState> getSquelchStateListener()
|
||||
public Listener<SquelchStateEvent> getSquelchStateListener()
|
||||
{
|
||||
return mSquelchStateListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(float[] audioBuffer)
|
||||
{
|
||||
if(mSquelchState == SquelchState.UNSQUELCH)
|
||||
{
|
||||
audioBuffer = mHighPassFilter.filter(audioBuffer);
|
||||
addAudio(audioBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<float[]> getBufferListener()
|
||||
{
|
||||
//Redirect received reusable buffers to the receive(buffer) method
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for squelch state listener
|
||||
*/
|
||||
public class SquelchStateListener implements Listener<SquelchState>
|
||||
public class SquelchStateListener implements Listener<SquelchStateEvent>
|
||||
{
|
||||
@Override
|
||||
public void receive(SquelchState state)
|
||||
public void receive(SquelchStateEvent event)
|
||||
{
|
||||
if(state == SquelchState.SQUELCH && mAudioPacketListener != null)
|
||||
{
|
||||
mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf()));
|
||||
}
|
||||
mSquelchState = event.getSquelchState();
|
||||
|
||||
mSquelchState = state;
|
||||
if(mSquelchState == SquelchState.SQUELCH)
|
||||
{
|
||||
closeAudioSegment();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.audio;
|
||||
|
||||
import io.github.dsheirer.channel.metadata.Metadata;
|
||||
import io.github.dsheirer.sample.real.RealBuffer;
|
||||
|
||||
public class AudioPacket
|
||||
{
|
||||
private Type mType;
|
||||
private RealBuffer mAudioData;
|
||||
private Metadata mMetadata;
|
||||
|
||||
public AudioPacket(Type type, Metadata metadata)
|
||||
{
|
||||
mType = type;
|
||||
mMetadata = metadata;
|
||||
}
|
||||
|
||||
public AudioPacket(float[] audio, Metadata metadata)
|
||||
{
|
||||
this(Type.AUDIO, metadata);
|
||||
|
||||
mAudioData = new RealBuffer(audio);
|
||||
}
|
||||
|
||||
public boolean hasMetadata()
|
||||
{
|
||||
return mMetadata != null;
|
||||
}
|
||||
|
||||
public Metadata getMetadata()
|
||||
{
|
||||
return mMetadata;
|
||||
}
|
||||
|
||||
public void setMetadata(Metadata metadata)
|
||||
{
|
||||
mMetadata = metadata;
|
||||
}
|
||||
|
||||
public Type getType()
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
public RealBuffer getAudioBuffer()
|
||||
{
|
||||
return mAudioData;
|
||||
}
|
||||
|
||||
public boolean hasAudioBuffer()
|
||||
{
|
||||
return mAudioData != null;
|
||||
}
|
||||
|
||||
public enum Type
|
||||
{
|
||||
AUDIO,
|
||||
END;
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package io.github.dsheirer.audio;
|
||||
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class AudioPacketRateCounter implements Listener<AudioPacket>
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( AudioPacketRateCounter.class );
|
||||
|
||||
private Listener<AudioPacket> mListener;
|
||||
private long mCount;
|
||||
private long mStartTime = -1;
|
||||
|
||||
private String mName;
|
||||
|
||||
public AudioPacketRateCounter()
|
||||
{
|
||||
mName = "UNNAMED";
|
||||
}
|
||||
|
||||
public AudioPacketRateCounter( String name )
|
||||
{
|
||||
mName = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( AudioPacket packet )
|
||||
{
|
||||
if( mStartTime == -1 )
|
||||
{
|
||||
mStartTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
mCount += packet.getAudioBuffer().getSamples().length;
|
||||
|
||||
long elapsed = System.currentTimeMillis() - mStartTime;
|
||||
|
||||
if( elapsed != 0 )
|
||||
{
|
||||
mLog.debug( "[" + mName + "] Audio Rate: " + ( mCount / elapsed ) + " kHz" );
|
||||
}
|
||||
|
||||
if( mListener != null )
|
||||
{
|
||||
mListener.receive( packet );
|
||||
}
|
||||
}
|
||||
|
||||
public void setListener( Listener<AudioPacket> listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
}
|
||||
480
src/main/java/io/github/dsheirer/audio/AudioSegment.java
Normal file
480
src/main/java/io/github/dsheirer/audio/AudioSegment.java
Normal file
@@ -0,0 +1,480 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program.
|
||||
* If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
package io.github.dsheirer.audio;
|
||||
|
||||
import io.github.dsheirer.alias.Alias;
|
||||
import io.github.dsheirer.alias.AliasList;
|
||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
||||
import io.github.dsheirer.alias.id.priority.Priority;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.IdentifierCollection;
|
||||
import io.github.dsheirer.identifier.IdentifierUpdateNotification;
|
||||
import io.github.dsheirer.identifier.MutableIdentifierCollection;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Audio segment containing all related metadata and a dynamic collection of audio packets. An audio segment can be
|
||||
* a discrete (ie start/stop) audio event, or it may be a time-constrained portion of an ongoing continuous audio
|
||||
* broadcast. Since the audio segment is held in memory until all consumers have finished processing the segment,
|
||||
* producers should constrain the duration of each audio segment to a reasonable duration.
|
||||
*
|
||||
* Producers can link time-constrained audio segments from a continous broadcast (e.g. FM radio station) so that
|
||||
* consumers can identify audio segments that belong to a continous stream. This linkage presents a memory leak
|
||||
* potential. Therefore, the audio segment uses the consumer counter to trigger an unlinking via the dispose() mehtod.
|
||||
* Accurate accounting of consumer count via the increment/decrementConsumerCount() methods is essential for good
|
||||
* memory management.
|
||||
*
|
||||
* Producers will add audio buffers and update identifiers throughout the life-cycle of an audio segment. The producer
|
||||
* will signal the completion of an audio segment by setting the complete property to true. This allows consumers the
|
||||
* option to process the audio buffers throughout the life-cycle of the segment, or to process all of the buffers once
|
||||
* the segment is complete.
|
||||
*/
|
||||
public class AudioSegment implements Listener<IdentifierUpdateNotification>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(AudioSegment.class);
|
||||
private BooleanProperty mComplete = new SimpleBooleanProperty(false);
|
||||
private BooleanProperty mDuplicate = new SimpleBooleanProperty(false);
|
||||
private BooleanProperty mRecordAudio = new SimpleBooleanProperty(false);
|
||||
private IntegerProperty mMonitorPriority = new SimpleIntegerProperty(Priority.DEFAULT_PRIORITY);
|
||||
private ObservableSet<BroadcastChannel> mBroadcastChannels = FXCollections.observableSet(new HashSet<>());
|
||||
private MutableIdentifierCollection mIdentifierCollection = new MutableIdentifierCollection();
|
||||
private Broadcaster<IdentifierUpdateNotification> mIdentifierUpdateNotificationBroadcaster = new Broadcaster<>();
|
||||
private List<float[]> mAudioBuffers = new CopyOnWriteArrayList();
|
||||
private AtomicInteger mConsumerCount = new AtomicInteger();
|
||||
private AliasList mAliasList;
|
||||
private long mStartTimestamp = System.currentTimeMillis();
|
||||
private long mSampleCount = 0;
|
||||
private boolean mDisposing = false;
|
||||
private AudioSegment mLinkedAudioSegment;
|
||||
private int mTimeslot;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param aliasList for accessing aliases associated with identifiers for this audio segment.
|
||||
*/
|
||||
public AudioSegment(AliasList aliasList, int timeslot)
|
||||
{
|
||||
mAliasList = aliasList;
|
||||
mTimeslot = timeslot;
|
||||
mIdentifierCollection.setTimeslot(timeslot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Timeslot for this audio segment
|
||||
*/
|
||||
public int getTimeslot()
|
||||
{
|
||||
return mTimeslot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creation timestamp for this audio segment
|
||||
* @return long milliseconds since epoch
|
||||
*/
|
||||
public long getStartTimestamp()
|
||||
{
|
||||
return mStartTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* End timestamp as calculated from start timestamp and current sample count
|
||||
*/
|
||||
public long getEndTimestamp()
|
||||
{
|
||||
return mStartTimestamp + getDuration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Current duration of this audio segment. Note: this is a dynamic value until the complete property is set to true.
|
||||
*
|
||||
* @return duration in milliseconds
|
||||
*/
|
||||
public long getDuration()
|
||||
{
|
||||
return (mSampleCount / 8); //8 kHz audio generates 8 samples per millisecond
|
||||
}
|
||||
|
||||
/**
|
||||
* The complete property is used by the audio segment producer to signal that the segment is complete and no
|
||||
* additional audio or identifiers will be added to the segment.
|
||||
*
|
||||
* @return complete property
|
||||
*/
|
||||
public BooleanProperty completeProperty()
|
||||
{
|
||||
return mComplete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate call audio property. This flag is set to true whenever a duplicate call detection function detects
|
||||
* an audio segment is a duplicate.
|
||||
*/
|
||||
public BooleanProperty duplicateProperty()
|
||||
{
|
||||
return mDuplicate;
|
||||
}
|
||||
|
||||
/**
|
||||
* An observable list of broadcast channels for this audio segment. Broadcast channels are added to this segment
|
||||
* across the life-cycle of the segment. As each new alias identifier is added to the segment, any broadcast
|
||||
* channels assigned to the alias are added to this list. The audio segment producer can also add broadcast
|
||||
* channels to this list.
|
||||
*
|
||||
* @return observable set of broadcast channels
|
||||
*/
|
||||
public ObservableSet<BroadcastChannel> broadcastChannelsProperty()
|
||||
{
|
||||
return mBroadcastChannels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set of broadcast channels from identifier associated aliases for this segment.
|
||||
*/
|
||||
public Set<BroadcastChannel> getBroadcastChannels()
|
||||
{
|
||||
return Collections.unmodifiableSet(mBroadcastChannels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this segment has audio streaming broadcast channels specified.
|
||||
*/
|
||||
public boolean hasBroadcastChannels()
|
||||
{
|
||||
return !mBroadcastChannels.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Property to signal that this audio segment should be recorded. This property can either be set by the producer
|
||||
* of the audio segment, or it can be flipped to true by any aliases that are added to this segment that require
|
||||
* audio associated with the identifier to be recorded.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public BooleanProperty recordAudioProperty()
|
||||
{
|
||||
return mRecordAudio;
|
||||
}
|
||||
|
||||
/**
|
||||
* Audio playback/monitor priority specified by identifier associated aliases for this segment.
|
||||
*/
|
||||
public IntegerProperty monitorPriorityProperty()
|
||||
{
|
||||
return mMonitorPriority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if at least one of the identifier associated aliases for this segments specifies Do Not Monitor.
|
||||
*/
|
||||
public boolean isDoNotMonitor()
|
||||
{
|
||||
return mMonitorPriority.get() <= Priority.DO_NOT_MONITOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias list for this audio segment
|
||||
*/
|
||||
public AliasList getAliasList()
|
||||
{
|
||||
return mAliasList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this audio segment is linked to a preceding audio segment.
|
||||
*/
|
||||
public boolean isLinked()
|
||||
{
|
||||
return mLinkedAudioSegment != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this audio segment is linked to the argument audio segment
|
||||
* @param audioSegment to check for linkage
|
||||
* @return true if this segment is linked to the argument
|
||||
*/
|
||||
public boolean isLinkedTo(AudioSegment audioSegment)
|
||||
{
|
||||
return isLinked() && audioSegment != null && mLinkedAudioSegment.equals(audioSegment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional linked audio segment is the audio segment that precedes this audio segment in a continuous audio
|
||||
* broadcast.
|
||||
*
|
||||
* @return linked audio segment or null.
|
||||
*/
|
||||
public AudioSegment getLinkedAudioSegment()
|
||||
{
|
||||
return mLinkedAudioSegment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Links this audio segment to a preceeding/previous audio segment
|
||||
* @param previousAudioSegment to set as the predecessor
|
||||
*/
|
||||
public void linkTo(AudioSegment previousAudioSegment)
|
||||
{
|
||||
mLinkedAudioSegment = previousAudioSegment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Immutable identifier collection for this audio segment
|
||||
*/
|
||||
public IdentifierCollection getIdentifierCollection()
|
||||
{
|
||||
return mIdentifierCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the collection of identifiers to this segment's identifier collection
|
||||
* @param identifiers to pre-load into this audio segment
|
||||
*/
|
||||
void addIdentifiers(Collection<Identifier> identifiers)
|
||||
{
|
||||
for(Identifier identifier: identifiers)
|
||||
{
|
||||
addIdentifier(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmodifiable copy of the list of audio buffers for this segment.
|
||||
*
|
||||
* @return list of audio buffers
|
||||
*/
|
||||
public List<float[]> getAudioBuffers()
|
||||
{
|
||||
return Collections.unmodifiableList(mAudioBuffers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count of audio buffers contained in this segment.
|
||||
*
|
||||
* Note: audio buffers can be added to an audio segment throughout the segment's life-cycle by the audio producer.
|
||||
*/
|
||||
public int getAudioBufferCount()
|
||||
{
|
||||
return mAudioBuffers.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the audio buffer at the specified index
|
||||
* @param index of the buffer to fetch
|
||||
* @return audio buffer
|
||||
* @throws IllegalArgumentException if requested index is not valid
|
||||
*/
|
||||
public float[] getAudioBuffer(int index)
|
||||
{
|
||||
if(0 <= index && index < getAudioBufferCount())
|
||||
{
|
||||
return mAudioBuffers.get(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("Requested audio buffer at index [" + index + "] does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this audio segment has one or more audio buffers
|
||||
*/
|
||||
public boolean hasAudio()
|
||||
{
|
||||
return !mAudioBuffers.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all audio buffers and decrements the user count on each so that the audio buffer can be reclaimed.
|
||||
*/
|
||||
private void dispose()
|
||||
{
|
||||
mDisposing = true;
|
||||
mAudioBuffers.clear();
|
||||
mIdentifierCollection.clear();
|
||||
mIdentifierUpdateNotificationBroadcaster.clear();
|
||||
mLinkedAudioSegment = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the consumer count to indicate that a consumer is currently processing this segment. When the
|
||||
* consumer count returns to zero, this indicates that all consumers are finished with the audio segment and the
|
||||
* resources can be reclaimed.
|
||||
*
|
||||
* Consumer count should only be increased by the producer of the audio segment, or if a consumer distributes the
|
||||
* segment to additional consumers.
|
||||
*/
|
||||
public void incrementConsumerCount()
|
||||
{
|
||||
mConsumerCount.incrementAndGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrements the consumer count. Consumers of this audio segment should invoke this method to signal that they
|
||||
* will no longer need this audio segment. When all consumers are finished with an audio segment, the audio
|
||||
* segment resources will be reclaimed.
|
||||
*/
|
||||
public void decrementConsumerCount()
|
||||
{
|
||||
int count = mConsumerCount.decrementAndGet();
|
||||
|
||||
if(count <= 0)
|
||||
{
|
||||
dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an audio buffer to this segment. Note the producer of the audio buffer should increment the user count
|
||||
* of the buffer prior to adding it to this segment. This segment will decrement the audio buffer user count once
|
||||
* all consumers of this audio segment have de-registered via the decrementConsumerCount() method.
|
||||
*
|
||||
* @param audioBuffer to add to this segment
|
||||
*/
|
||||
public void addAudio(float[] audioBuffer)
|
||||
{
|
||||
if(audioBuffer == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Can't add null audio buffer");
|
||||
}
|
||||
|
||||
if(mDisposing)
|
||||
{
|
||||
throw new IllegalStateException("Can't add audio to an audio segment that is being disposed");
|
||||
}
|
||||
|
||||
mAudioBuffers.add(audioBuffer);
|
||||
mSampleCount += audioBuffer.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener to receive identifier update notifications
|
||||
*/
|
||||
public void addIdentifierUpdateNotificationListener(Listener<IdentifierUpdateNotification> listener)
|
||||
{
|
||||
mIdentifierUpdateNotificationBroadcaster.addListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the identifier update listener.
|
||||
*/
|
||||
public void removeIdentifierUpdateNotificationListener(Listener<IdentifierUpdateNotification> listener)
|
||||
{
|
||||
mIdentifierUpdateNotificationBroadcaster.removeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the identifier to this audio segment and updates record, priority and streaming properties.
|
||||
*
|
||||
* Note: identifiers pass to this method must be checked for timeslot match.
|
||||
*/
|
||||
public void addIdentifier(Identifier identifier)
|
||||
{
|
||||
mIdentifierCollection.update(identifier);
|
||||
|
||||
List<Alias> aliases = mAliasList.getAliases(identifier);
|
||||
|
||||
for(Alias alias: aliases)
|
||||
{
|
||||
if(alias.isRecordable())
|
||||
{
|
||||
mRecordAudio.set(true);
|
||||
}
|
||||
|
||||
//Add all broadcast channels for the alias ... let the set handle duplication.
|
||||
mBroadcastChannels.addAll(alias.getBroadcastChannels());
|
||||
|
||||
//Only assign a playback priority if it is lower priority than the current setting.
|
||||
int playbackPriority = alias.getPlaybackPriority();
|
||||
|
||||
if(playbackPriority < mMonitorPriority.get())
|
||||
{
|
||||
mMonitorPriority.set(playbackPriority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this audio segment has been flagged as a duplicate audio call
|
||||
*/
|
||||
public boolean isDuplicate()
|
||||
{
|
||||
return mDuplicate.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the duplicate audio call flag for this audio segment
|
||||
* @param duplicate true if this is a duplicate audio segment
|
||||
*/
|
||||
public void setDuplicate(boolean duplicate)
|
||||
{
|
||||
mDuplicate.set(duplicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes identifier update notifications and updates audio segment properties using the associated aliases
|
||||
* from the alias list.
|
||||
*
|
||||
* @param identifierUpdateNotification containing an identifier.
|
||||
*/
|
||||
@Override
|
||||
public void receive(IdentifierUpdateNotification identifierUpdateNotification)
|
||||
{
|
||||
//Only process add updates that match this timeslot
|
||||
if(identifierUpdateNotification.getTimeslot() == getTimeslot())
|
||||
{
|
||||
if(identifierUpdateNotification.isAdd() || identifierUpdateNotification.isSilentAdd())
|
||||
{
|
||||
addIdentifier(identifierUpdateNotification.getIdentifier());
|
||||
}
|
||||
|
||||
mIdentifierUpdateNotificationBroadcaster.broadcast(identifierUpdateNotification);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Audio Segment\n Timeslot:").append(getTimeslot()).append("\n");
|
||||
sb.append(" Start Time:").append(new Date(getStartTimestamp()));
|
||||
sb.append(" Linked:").append(isLinked()).append("\n");
|
||||
sb.append(" Recordable:").append(recordAudioProperty().get()).append("\n");
|
||||
sb.append("Do Not Monitor:").append(isDoNotMonitor()).append("\n");
|
||||
sb.append(" Stream To:").append(getBroadcastChannels()).append("\n");
|
||||
sb.append(" Identifiers:\n").append(getIdentifierCollection()).append("\n");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.audio;
|
||||
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class AudioSegmentBroadcaster<T extends AudioSegment> extends Broadcaster<T>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(AudioSegmentBroadcaster.class);
|
||||
|
||||
/**
|
||||
* Increments the consumer count for the reusable complex buffer and then broadcasts the buffer to all registered
|
||||
* listeners.
|
||||
*
|
||||
* The total consumer count is established and applied to the buffer prior to dispatching. If we were to simply
|
||||
* increment the consumer count prior to sending to each consumer, there is a possibility that the consumer could
|
||||
* immediately decrement the consumer count and prematurely signal that the buffer is ready for disposal before we
|
||||
* send the buffer to all consumers.
|
||||
*/
|
||||
@Override
|
||||
public void broadcast(T audioSegment)
|
||||
{
|
||||
for(Listener<T> listener : mListeners)
|
||||
{
|
||||
audioSegment.incrementConsumerCount();
|
||||
listener.receive(audioSegment);
|
||||
}
|
||||
|
||||
//Decrement consumer counter for this broadcaster
|
||||
audioSegment.decrementConsumerCount();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -14,8 +14,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.audio;
|
||||
|
||||
import io.github.dsheirer.sample.ConversionUtils;
|
||||
@@ -34,16 +34,40 @@ public class AudioUtils
|
||||
/**
|
||||
* Converts the audio packets into a byte array of 16-bit, little-endian audio samples
|
||||
*/
|
||||
public static byte[] convertTo16BitSamples(List<AudioPacket> audioPackets)
|
||||
public static byte[] convertTo16BitSamples(List<float[]> buffers)
|
||||
{
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
|
||||
try
|
||||
{
|
||||
for(AudioPacket packet: audioPackets)
|
||||
for(float[] audioBuffer: buffers)
|
||||
{
|
||||
//Converting from 32-bit floats to signed 16-bit samples
|
||||
ByteBuffer buffer = ConversionUtils.convertToSigned16BitSamples(packet.getAudioBuffer().getSamples());
|
||||
ByteBuffer buffer = ConversionUtils.convertToSigned16BitSamples(audioBuffer);
|
||||
stream.write(buffer.array());
|
||||
}
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
mLog.error("Error writing converted PCM bytes to output stream");
|
||||
}
|
||||
|
||||
return stream.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the audio packets into a byte array of 16-bit, little-endian audio samples
|
||||
*/
|
||||
public static byte[] convert(List<float[]> audioBuffers)
|
||||
{
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
|
||||
try
|
||||
{
|
||||
for(float[] audioBuffer: audioBuffers)
|
||||
{
|
||||
//Converting from 32-bit floats to signed 16-bit samples
|
||||
ByteBuffer buffer = ConversionUtils.convertToSigned16BitSamples(audioBuffer);
|
||||
stream.write(buffer.array());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.audio;
|
||||
|
||||
import io.github.dsheirer.identifier.Form;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.IdentifierClass;
|
||||
import io.github.dsheirer.identifier.Role;
|
||||
import io.github.dsheirer.identifier.configuration.SystemConfigurationIdentifier;
|
||||
import io.github.dsheirer.identifier.patch.PatchGroupIdentifier;
|
||||
import io.github.dsheirer.identifier.radio.RadioIdentifier;
|
||||
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.preference.duplicate.DuplicateCallDetectionPreference;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Detects duplicate calls that occur within the same system. This detector is thread safe for the receive() method.
|
||||
*
|
||||
* Note: system in this context refers to the system name value that is used in channel configurations. All decoder
|
||||
* channels must share the same system name for call duplication detection.
|
||||
*/
|
||||
public class DuplicateCallDetector implements Listener<AudioSegment>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(DuplicateCallDetector.class);
|
||||
private DuplicateCallDetectionPreference mDuplicateCallDetectionPreference;
|
||||
private Map<String,SystemDuplicateCallDetector> mDetectorMap = new HashMap();
|
||||
|
||||
public DuplicateCallDetector(UserPreferences userPreferences)
|
||||
{
|
||||
mDuplicateCallDetectionPreference = userPreferences.getDuplicateCallDetectionPreference();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(AudioSegment audioSegment)
|
||||
{
|
||||
if(mDuplicateCallDetectionPreference.isDuplicateCallDetectionEnabled())
|
||||
{
|
||||
Identifier identifier = audioSegment.getIdentifierCollection()
|
||||
.getIdentifier(IdentifierClass.CONFIGURATION, Form.SYSTEM, Role.ANY);
|
||||
|
||||
if(identifier instanceof SystemConfigurationIdentifier)
|
||||
{
|
||||
String system = ((SystemConfigurationIdentifier)identifier).getValue();
|
||||
|
||||
synchronized(mDetectorMap)
|
||||
{
|
||||
SystemDuplicateCallDetector detector = mDetectorMap.get(system);
|
||||
|
||||
if(detector == null)
|
||||
{
|
||||
detector = new SystemDuplicateCallDetector();
|
||||
mDetectorMap.put(system, detector);
|
||||
}
|
||||
|
||||
detector.add(audioSegment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SystemDuplicateCallDetector
|
||||
{
|
||||
private LinkedTransferQueue<AudioSegment> mAudioSegmentQueue = new LinkedTransferQueue<>();
|
||||
private List<AudioSegment> mAudioSegments = new ArrayList<>();
|
||||
private AtomicBoolean mMonitoring = new AtomicBoolean();
|
||||
private ScheduledFuture<?> mProcessorFuture;
|
||||
|
||||
public SystemDuplicateCallDetector()
|
||||
{
|
||||
}
|
||||
|
||||
public void add(AudioSegment audioSegment)
|
||||
{
|
||||
//Block on audio segment queue so that we don't interfere with monitoring shutdown
|
||||
synchronized(mAudioSegmentQueue)
|
||||
{
|
||||
mAudioSegmentQueue.add(audioSegment);
|
||||
startMonitoring();
|
||||
}
|
||||
}
|
||||
|
||||
private void startMonitoring()
|
||||
{
|
||||
if(mMonitoring.compareAndSet(false, true))
|
||||
{
|
||||
mProcessorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(() -> process(),
|
||||
0, 25, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
private void stopMonitoring()
|
||||
{
|
||||
if(mMonitoring.compareAndSet(true, false))
|
||||
{
|
||||
if(mProcessorFuture != null)
|
||||
{
|
||||
mProcessorFuture.cancel(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if either the TO or FROM integer identifiers for the audio segments are the same
|
||||
* @param segment1 to compare
|
||||
* @param segment2 to compare
|
||||
* @return true if integer identifiers are the same
|
||||
*/
|
||||
private boolean isDuplicate(AudioSegment segment1, AudioSegment segment2)
|
||||
{
|
||||
if(mDuplicateCallDetectionPreference.isDuplicateCallDetectionByTalkgroupEnabled())
|
||||
{
|
||||
//Step 1 check for duplicate TO values
|
||||
List<Identifier> to1 = segment1.getIdentifierCollection().getIdentifiers(Role.TO);
|
||||
List<Identifier> to2 = segment2.getIdentifierCollection().getIdentifiers(Role.TO);
|
||||
|
||||
if(isDuplicate(to1, to2))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(mDuplicateCallDetectionPreference.isDuplicateCallDetectionByRadioEnabled())
|
||||
{
|
||||
//Step 2 check for duplicate FROM values
|
||||
List<Identifier> from1 = segment1.getIdentifierCollection().getIdentifiers(Role.FROM);
|
||||
List<Identifier> from2 = segment2.getIdentifierCollection().getIdentifiers(Role.FROM);
|
||||
|
||||
return isDuplicate(from1, from2);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks both lists of identifiers to determine if there are talkgroups or radio identifiers that are
|
||||
* the same in both lists. Note: talkgroup check compares both talkgroups and patch groups.
|
||||
*
|
||||
* @param identifiers1
|
||||
* @param identifiers2
|
||||
* @return
|
||||
*/
|
||||
private boolean isDuplicate(List<Identifier> identifiers1, List<Identifier> identifiers2)
|
||||
{
|
||||
for(Identifier identifier1: identifiers1)
|
||||
{
|
||||
if(identifier1 instanceof TalkgroupIdentifier)
|
||||
{
|
||||
int talkgroup1 = ((TalkgroupIdentifier)identifier1).getValue();
|
||||
|
||||
for(Identifier identifier2: identifiers2)
|
||||
{
|
||||
if(identifier2 instanceof TalkgroupIdentifier &&
|
||||
((TalkgroupIdentifier)identifier2).getValue() == talkgroup1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if(identifier2 instanceof PatchGroupIdentifier &&
|
||||
((PatchGroupIdentifier)identifier2).getValue().getPatchGroup().getValue() == talkgroup1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(identifier1 instanceof PatchGroupIdentifier)
|
||||
{
|
||||
int talkgroup1 = ((PatchGroupIdentifier)identifier1).getValue().getPatchGroup().getValue();
|
||||
|
||||
for(Identifier identifier2: identifiers2)
|
||||
{
|
||||
if(identifier2 instanceof TalkgroupIdentifier &&
|
||||
((TalkgroupIdentifier)identifier2).getValue() == talkgroup1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if(identifier2 instanceof PatchGroupIdentifier &&
|
||||
((PatchGroupIdentifier)identifier2).getValue().getPatchGroup().getValue() == talkgroup1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(identifier1 instanceof RadioIdentifier)
|
||||
{
|
||||
int radio1 = ((RadioIdentifier)identifier1).getValue();
|
||||
|
||||
for(Identifier identifier2: identifiers2)
|
||||
{
|
||||
if(identifier2 instanceof RadioIdentifier && ((RadioIdentifier)identifier2).getValue() == radio1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes audio segments to detect duplicates
|
||||
*/
|
||||
private void process()
|
||||
{
|
||||
try
|
||||
{
|
||||
//Transfer in newly arrived audio segments
|
||||
mAudioSegmentQueue.drainTo(mAudioSegments);
|
||||
|
||||
//Remove any completed audio segments.
|
||||
mAudioSegments.removeIf(audioSegment -> {
|
||||
boolean complete = audioSegment.completeProperty().get();
|
||||
|
||||
if(complete)
|
||||
{
|
||||
audioSegment.decrementConsumerCount();
|
||||
}
|
||||
|
||||
return complete;
|
||||
});
|
||||
|
||||
//Only check for duplicates if there is more than one call
|
||||
if(mAudioSegments.size() > 1)
|
||||
{
|
||||
List<AudioSegment> duplicates = new ArrayList<>();
|
||||
|
||||
int currentIndex = 0;
|
||||
while(currentIndex < mAudioSegments.size() - 1)
|
||||
{
|
||||
AudioSegment current = mAudioSegments.get(currentIndex);
|
||||
|
||||
if(!current.isDuplicate())
|
||||
{
|
||||
int checkIndex = currentIndex + 1;
|
||||
|
||||
while(checkIndex < mAudioSegments.size())
|
||||
{
|
||||
AudioSegment toCheck = mAudioSegments.get(checkIndex);
|
||||
|
||||
if(!toCheck.isDuplicate())
|
||||
{
|
||||
if(isDuplicate(current, toCheck))
|
||||
{
|
||||
toCheck.setDuplicate(true);
|
||||
toCheck.decrementConsumerCount();
|
||||
duplicates.add(toCheck);
|
||||
}
|
||||
}
|
||||
|
||||
checkIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
currentIndex++;
|
||||
}
|
||||
|
||||
mAudioSegments.removeAll(duplicates);
|
||||
}
|
||||
|
||||
//Finally, if the audio segment queue is empty, shutdown montitoring until a new segment arrives
|
||||
if(mAudioSegments.isEmpty())
|
||||
{
|
||||
//Block on the audio segment queue so that we can shutdown before any new segments are added, and
|
||||
//allow the add(segment) to restart monitoring as soon as needed.
|
||||
synchronized(mAudioSegmentQueue)
|
||||
{
|
||||
if(mAudioSegmentQueue.isEmpty())
|
||||
{
|
||||
try
|
||||
{
|
||||
stopMonitoring();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Unexpected error during duplicate audio segment monitoring shutdown", e);
|
||||
//Do nothing, we got interrupted
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
mLog.error("Unknown error while processing audio segments for duplicate call detection. Please report " +
|
||||
"this to the developer.", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.github.dsheirer.audio;
|
||||
|
||||
import io.github.dsheirer.audio.output.AudioOutput;
|
||||
import io.github.dsheirer.audio.playback.AudioOutput;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.source.mixer.MixerChannelConfiguration;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user