diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastModel.java b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastModel.java index 30d482e2..564c1d68 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastModel.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastModel.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2016 Dennis Sheirer + * 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 @@ -14,14 +15,15 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * ***************************************************************************** + */ package io.github.dsheirer.audio.broadcast; import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.alias.id.broadcast.BroadcastChannel; import io.github.dsheirer.icon.IconManager; +import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.properties.SystemProperties; import io.github.dsheirer.sample.Broadcaster; import io.github.dsheirer.sample.Listener; @@ -79,17 +81,15 @@ public class BroadcastModel extends AbstractTableModel implements Listener - * - ******************************************************************************/ + * ***************************************************************************** + */ package io.github.dsheirer.audio.broadcast; +import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.record.AudioRecorder; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.buffer.ReusableAudioPacket; @@ -26,7 +28,6 @@ import io.github.dsheirer.util.TimeStamp; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; @@ -46,7 +47,7 @@ public class StreamManager implements Listener private Listener mAudioRecordingListener; private BroadcastFormat mBroadcastFormat; - private Path mTempDirectory; + private UserPreferences mUserPreferences; private Map mStreamRecorders = new HashMap<>(); private Runnable mRecorderMonitor; private ScheduledFuture mRecorderMonitorFuture; @@ -61,15 +62,14 @@ public class StreamManager implements Listener * Completed streamable audio recordings are nominated to the output listener (for broadcast) upon completion * * @param listener to receive completed audio recordings - * @param tempDirectory where to store temporary audio recordings + * @param broadcastFormat for the stream + * @param userPreferences to obtain the temporary streaming recording directory */ - public StreamManager(Listener listener, BroadcastFormat broadcastFormat, Path tempDirectory) + public StreamManager(Listener listener, BroadcastFormat broadcastFormat, UserPreferences userPreferences) { - assert (tempDirectory != null && Files.isDirectory(tempDirectory)); - mAudioRecordingListener = listener; mBroadcastFormat = broadcastFormat; - mTempDirectory = tempDirectory; + mUserPreferences = userPreferences; } /** @@ -204,7 +204,7 @@ public class StreamManager implements Listener sb.append(TimeStamp.getLongTimeStamp("_")); sb.append(mBroadcastFormat.getFileExtension()); - Path temporaryRecordingPath = mTempDirectory.resolve(sb.toString()); + Path temporaryRecordingPath = mUserPreferences.getDirectoryPreference().getDirectoryStreaming().resolve(sb.toString()); return temporaryRecordingPath; } diff --git a/src/main/java/io/github/dsheirer/gui/SDRTrunk.java b/src/main/java/io/github/dsheirer/gui/SDRTrunk.java index e418e570..a033697f 100644 --- a/src/main/java/io/github/dsheirer/gui/SDRTrunk.java +++ b/src/main/java/io/github/dsheirer/gui/SDRTrunk.java @@ -36,6 +36,7 @@ import io.github.dsheirer.eventbus.MyEventBus; import io.github.dsheirer.gui.preference.PreferenceEditorType; import io.github.dsheirer.gui.preference.PreferenceEditorViewRequest; import io.github.dsheirer.icon.IconManager; +import io.github.dsheirer.log.ApplicationLog; import io.github.dsheirer.map.MapService; import io.github.dsheirer.module.log.EventLogManager; import io.github.dsheirer.playlist.PlaylistManager; @@ -56,7 +57,6 @@ import io.github.dsheirer.spectrum.SpectralDisplayPanel; import io.github.dsheirer.util.ThreadPool; import io.github.dsheirer.util.TimeStamp; import net.miginfocom.swing.MigLayout; -import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -89,6 +89,7 @@ import java.util.List; public class SDRTrunk implements Listener { private final static Logger mLog = LoggerFactory.getLogger(SDRTrunk.class); + private static final String PROPERTY_BROADCAST_STATUS_VISIBLE = "main.broadcast.status.visible"; private boolean mBroadcastStatusVisible; @@ -106,21 +107,14 @@ public class SDRTrunk implements Listener private JideSplitPane mSplitPane; private JavaFxWindowManager mJavaFxWindowManager; private UserPreferences mUserPreferences = new UserPreferences(); + private ApplicationLog mApplicationLog; private String mTitle; public SDRTrunk() { - mLog.info("*******************************************************************"); - mLog.info("**** sdrtrunk: a trunked radio and digital decoding application ***"); - mLog.info("**** website: https://github.com/dsheirer/sdrtrunk ***"); - mLog.info("*******************************************************************"); - mLog.info("Memory Logging Format: [Used/Allocated PercentUsed%]"); - mLog.info("Host OS Name: " + System.getProperty("os.name")); - mLog.info("Host OS Arch: " + System.getProperty("os.arch")); - mLog.info("Host OS Version: " + System.getProperty("os.version")); - mLog.info("Host CPU Cores: " + Runtime.getRuntime().availableProcessors()); - mLog.info("Host Max Java Memory: " + FileUtils.byteCountToDisplaySize(Runtime.getRuntime().maxMemory())); + mApplicationLog = new ApplicationLog(mUserPreferences); + mApplicationLog.start(); //Setup the application home directory Path home = getHomePath(); @@ -151,9 +145,9 @@ public class SDRTrunk implements Listener ChannelMapModel channelMapModel = new ChannelMapModel(); - EventLogManager eventLogManager = new EventLogManager(); + EventLogManager eventLogManager = new EventLogManager(mUserPreferences); - RecorderManager recorderManager = new RecorderManager(aliasModel); + RecorderManager recorderManager = new RecorderManager(aliasModel, mUserPreferences); mJavaFxWindowManager = new JavaFxWindowManager(mUserPreferences); @@ -170,7 +164,7 @@ public class SDRTrunk implements Listener AudioPlaybackManager audioPlaybackManager = new AudioPlaybackManager(mSourceManager.getMixerManager()); - mBroadcastModel = new BroadcastModel(aliasModel, mIconManager); + mBroadcastModel = new BroadcastModel(aliasModel, mIconManager, mUserPreferences); //Audio packets are routed through the audio packet manager for metadata enrichment and then //distributed to the audio packet processors (ie playback, recording, streaming, etc.) @@ -373,47 +367,20 @@ public class SDRTrunk implements Listener { Robot robot = new Robot(); - final BufferedImage image = - robot.createScreenCapture(mMainGui.getBounds()); + final BufferedImage image = robot.createScreenCapture(mMainGui.getBounds()); - SystemProperties props = SystemProperties.getInstance(); + String filename = TimeStamp.getTimeStamp("_") + "_screen_capture.png"; - Path capturePath = props.getApplicationFolder("screen_captures"); + final Path captureFile = mUserPreferences.getDirectoryPreference().getDirectoryScreenCapture().resolve(filename); - if(!Files.exists(capturePath)) - { + EventQueue.invokeLater(() -> { try { - Files.createDirectory(capturePath); + ImageIO.write(image, "png", captureFile.toFile()); } catch(IOException e) { - mLog.error("Couldn't create 'screen_captures' " - + "subdirectory in the " + - "SDRTrunk application directory", e); - } - } - - String filename = TimeStamp.getTimeStamp("_") + - "_screen_capture.png"; - - final Path captureFile = capturePath.resolve(filename); - - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - try - { - ImageIO.write(image, "png", - captureFile.toFile()); - } - catch(IOException e) - { - mLog.error("Couldn't write screen capture to " - + "file [" + captureFile.toString() + "]", e); - } + mLog.error("Couldn't write screen capture to file [" + captureFile.toString() + "]", e); } }); } @@ -442,6 +409,7 @@ public class SDRTrunk implements Listener mLog.info("Releasing tuners ..."); mSourceManager.shutdown(); mLog.info("Shutdown complete."); + mApplicationLog.stop(); } /** diff --git a/src/main/java/io/github/dsheirer/gui/preference/PreferenceEditorFactory.java b/src/main/java/io/github/dsheirer/gui/preference/PreferenceEditorFactory.java index ac8dd70b..1b149851 100644 --- a/src/main/java/io/github/dsheirer/gui/preference/PreferenceEditorFactory.java +++ b/src/main/java/io/github/dsheirer/gui/preference/PreferenceEditorFactory.java @@ -20,6 +20,7 @@ package io.github.dsheirer.gui.preference; +import io.github.dsheirer.gui.preference.directory.DirectoryPreferenceEditor; import io.github.dsheirer.gui.preference.tuner.TunerPreferenceEditor; import io.github.dsheirer.preference.UserPreferences; import javafx.scene.Node; @@ -33,10 +34,12 @@ public class PreferenceEditorFactory { switch(preferenceEditorType) { - case TALKGROUP_FORMAT: - return new TalkgroupFormatPreferenceEditor(userPreferences); case CHANNEL_EVENT: return new DecodeEventViewPreferenceEditor(userPreferences); + case DIRECTORY: + return new DirectoryPreferenceEditor(userPreferences); + case TALKGROUP_FORMAT: + return new TalkgroupFormatPreferenceEditor(userPreferences); case TUNER: return new TunerPreferenceEditor(userPreferences); } diff --git a/src/main/java/io/github/dsheirer/gui/preference/PreferenceEditorType.java b/src/main/java/io/github/dsheirer/gui/preference/PreferenceEditorType.java index 940952a5..b076b7da 100644 --- a/src/main/java/io/github/dsheirer/gui/preference/PreferenceEditorType.java +++ b/src/main/java/io/github/dsheirer/gui/preference/PreferenceEditorType.java @@ -25,9 +25,10 @@ package io.github.dsheirer.gui.preference; */ public enum PreferenceEditorType { - CHANNEL_EVENT("Channel Event"), - TALKGROUP_FORMAT("Talkgroup Display"), - TUNER("Tuner"); + CHANNEL_EVENT("Channel Events"), + DIRECTORY("Directories"), + TALKGROUP_FORMAT("Talkgroups"), + TUNER("Tuners"); private String mLabel; diff --git a/src/main/java/io/github/dsheirer/gui/preference/PreferencesEditor.java b/src/main/java/io/github/dsheirer/gui/preference/PreferencesEditor.java index 3f9018da..babc7061 100644 --- a/src/main/java/io/github/dsheirer/gui/preference/PreferencesEditor.java +++ b/src/main/java/io/github/dsheirer/gui/preference/PreferencesEditor.java @@ -104,7 +104,7 @@ public class PreferencesEditor extends Application public void start(Stage stage) throws Exception { stage.setTitle("Preferences"); - Scene scene = new Scene(getBorderPane(), 800, 500); + Scene scene = new Scene(getBorderPane(), 900, 500); stage.setScene(scene); stage.show(); } @@ -192,10 +192,19 @@ public class PreferencesEditor extends Application TreeItem displayItem = new TreeItem<>("Display"); displayItem.getChildren().add(new TreeItem(PreferenceEditorType.CHANNEL_EVENT)); displayItem.getChildren().add(new TreeItem(PreferenceEditorType.TALKGROUP_FORMAT)); - displayItem.getChildren().add(new TreeItem(PreferenceEditorType.TUNER)); treeRoot.getChildren().add(displayItem); displayItem.setExpanded(true); + TreeItem storageItem = new TreeItem<>("File Storage"); + storageItem.getChildren().add(new TreeItem(PreferenceEditorType.DIRECTORY)); + treeRoot.getChildren().add(storageItem); + storageItem.setExpanded(true); + + TreeItem sourceItem = new TreeItem<>("Source"); + sourceItem.getChildren().add(new TreeItem(PreferenceEditorType.TUNER)); + treeRoot.getChildren().add(sourceItem); + sourceItem.setExpanded(true); + mEditorSelectionTreeView = new TreeView(); mEditorSelectionTreeView.setShowRoot(false); mEditorSelectionTreeView.setRoot(treeRoot); diff --git a/src/main/java/io/github/dsheirer/gui/preference/directory/DirectoryPreferenceEditor.java b/src/main/java/io/github/dsheirer/gui/preference/directory/DirectoryPreferenceEditor.java new file mode 100644 index 00000000..8c9794ec --- /dev/null +++ b/src/main/java/io/github/dsheirer/gui/preference/directory/DirectoryPreferenceEditor.java @@ -0,0 +1,684 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.gui.preference.directory; + +import com.google.common.eventbus.Subscribe; +import io.github.dsheirer.eventbus.MyEventBus; +import io.github.dsheirer.preference.PreferenceType; +import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.preference.directory.DirectoryPreference; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.geometry.Insets; +import javafx.geometry.Orientation; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.Separator; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.stage.DirectoryChooser; +import javafx.stage.Stage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + + +/** + * Preference settings for channel event view + */ +public class DirectoryPreferenceEditor extends HBox +{ + private final static Logger mLog = LoggerFactory.getLogger(DirectoryPreferenceEditor.class); + + private DirectoryPreference mDirectoryPreference; + private GridPane mEditorPane; + + private Label mApplicationRootLabel; + private Button mChangeApplicationRootButton; + private Button mResetApplicationRootButton; + private Label mApplicationRootPathLabel; + + private Label mApplicationLogsLabel; + private Button mChangeApplicationLogsButton; + private Button mResetApplicationLogsButton; + private Label mApplicationLogsPathLabel; + + private Label mEventLogsLabel; + private Button mChangeEventLogsButton; + private Button mResetEventLogsButton; + private Label mEventLogsPathLabel; + + private Label mPlaylistLabel; + private Button mChangePlaylistButton; + private Button mResetPlaylistButton; + private Label mPlaylistPathLabel; + + private Label mRecordingLabel; + private Button mChangeRecordingButton; + private Button mResetRecordingButton; + private Label mRecordingPathLabel; + + private Label mScreenCaptureLabel; + private Button mChangeScreenCaptureButton; + private Button mResetScreenCaptureButton; + private Label mScreenCapturePathLabel; + + private Label mStreamingLabel; + private Button mChangeStreamingButton; + private Button mResetStreamingButton; + private Label mStreamingPathLabel; + + public DirectoryPreferenceEditor(UserPreferences userPreferences) + { + mDirectoryPreference = userPreferences.getDirectoryPreference(); + + //Register to receive directory preference update notifications so we can update the path labels + MyEventBus.getEventBus().register(this); + + HBox.setHgrow(getEditorPane(), Priority.ALWAYS); + getChildren().add(getEditorPane()); + } + + private GridPane getEditorPane() + { + if(mEditorPane == null) + { + mEditorPane = new GridPane(); + mEditorPane.setPadding(new Insets(10, 10, 10, 10)); + + int row = 0; + + Label itemLabel = new Label("Item"); + GridPane.setMargin(itemLabel, new Insets(0, 10, 0, 0)); + mEditorPane.add(itemLabel, 0, row); + + Label directoryLabel = new Label("Directory"); + GridPane.setMargin(directoryLabel, new Insets(0, 10, 0, 0)); + mEditorPane.add(directoryLabel, 1, row++); + + mEditorPane.add(new Separator(Orientation.HORIZONTAL), 0, row++, 4, 1); + + GridPane.setMargin(getApplicationRootLabel(), new Insets(0, 10, 0, 0)); + mEditorPane.add(getApplicationRootLabel(), 0, row); + + GridPane.setMargin(getApplicationRootPathLabel(), new Insets(0, 10, 0, 0)); + mEditorPane.add(getApplicationRootPathLabel(), 1, row); + + GridPane.setMargin(getChangeApplicationRootButton(), new Insets(2, 10, 2, 0)); + mEditorPane.add(getChangeApplicationRootButton(), 2, row); + + GridPane.setMargin(getResetApplicationRootButton(), new Insets(2, 0, 2, 0)); + mEditorPane.add(getResetApplicationRootButton(), 3, row++); + + + GridPane.setMargin(getApplicationLogsLabel(), new Insets(0, 10, 0, 0)); + mEditorPane.add(getApplicationLogsLabel(), 0, row); + + GridPane.setMargin(getApplicationLogsPathLabel(), new Insets(0, 10, 0, 0)); + mEditorPane.add(getApplicationLogsPathLabel(), 1, row); + + GridPane.setMargin(getChangeApplicationLogsButton(), new Insets(2, 10, 2, 0)); + mEditorPane.add(getChangeApplicationLogsButton(), 2, row); + + GridPane.setMargin(getResetApplicationLogsButton(), new Insets(2, 0, 2, 0)); + mEditorPane.add(getResetApplicationLogsButton(), 3, row++); + + + GridPane.setMargin(getEventLogsLabel(), new Insets(0, 10, 0, 0)); + mEditorPane.add(getEventLogsLabel(), 0, row); + + GridPane.setMargin(getEventLogsPathLabel(), new Insets(0, 10, 0, 0)); + mEditorPane.add(getEventLogsPathLabel(), 1, row); + + GridPane.setMargin(getChangeEventLogsButton(), new Insets(2, 10, 2, 0)); + mEditorPane.add(getChangeEventLogsButton(), 2, row); + + GridPane.setMargin(getResetEventLogsButton(), new Insets(2, 0, 2, 0)); + mEditorPane.add(getResetEventLogsButton(), 3, row++); + + + GridPane.setMargin(getPlaylistLabel(), new Insets(0, 10, 0, 0)); + mEditorPane.add(getPlaylistLabel(), 0, row); + + GridPane.setMargin(getPlaylistPathLabel(), new Insets(0, 10, 0, 0)); + mEditorPane.add(getPlaylistPathLabel(), 1, row); + + GridPane.setMargin(getChangePlaylistButton(), new Insets(2, 10, 2, 0)); + mEditorPane.add(getChangePlaylistButton(), 2, row); + + GridPane.setMargin(getResetPlaylistButton(), new Insets(2, 0, 2, 0)); + mEditorPane.add(getResetPlaylistButton(), 3, row++); + + + GridPane.setMargin(getRecordingLabel(), new Insets(0, 10, 0, 0)); + mEditorPane.add(getRecordingLabel(), 0, row); + + GridPane.setMargin(getRecordingPathLabel(), new Insets(0, 10, 0, 0)); + mEditorPane.add(getRecordingPathLabel(), 1, row); + + GridPane.setMargin(getChangeRecordingButton(), new Insets(2, 10, 2, 0)); + mEditorPane.add(getChangeRecordingButton(), 2, row); + + GridPane.setMargin(getResetRecordingButton(), new Insets(2, 0, 2, 0)); + mEditorPane.add(getResetRecordingButton(), 3, row++); + + + GridPane.setMargin(getScreenCaptureLabel(), new Insets(0, 10, 0, 0)); + mEditorPane.add(getScreenCaptureLabel(), 0, row); + + GridPane.setMargin(getScreenCapturePathLabel(), new Insets(0, 10, 0, 0)); + mEditorPane.add(getScreenCapturePathLabel(), 1, row); + + GridPane.setMargin(getChangeScreenCaptureButton(), new Insets(2, 10, 2, 0)); + mEditorPane.add(getChangeScreenCaptureButton(), 2, row); + + GridPane.setMargin(getResetScreenCaptureButton(), new Insets(2, 0, 2, 0)); + mEditorPane.add(getResetScreenCaptureButton(), 3, row++); + + + GridPane.setMargin(getStreamingLabel(), new Insets(0, 10, 0, 0)); + mEditorPane.add(getStreamingLabel(), 0, row); + + GridPane.setMargin(getStreamingPathLabel(), new Insets(0, 10, 0, 0)); + mEditorPane.add(getStreamingPathLabel(), 1, row); + + GridPane.setMargin(getChangeStreamingButton(), new Insets(2, 10, 2, 0)); + mEditorPane.add(getChangeStreamingButton(), 2, row); + + GridPane.setMargin(getResetStreamingButton(), new Insets(2, 0, 2, 0)); + mEditorPane.add(getResetStreamingButton(), 3, row++); + } + + return mEditorPane; + } + + private Label getApplicationRootLabel() + { + if(mApplicationRootLabel == null) + { + mApplicationRootLabel = new Label("Application Root"); + } + + return mApplicationRootLabel; + } + + private Button getChangeApplicationRootButton() + { + if(mChangeApplicationRootButton == null) + { + mChangeApplicationRootButton = new Button("Change..."); + mChangeApplicationRootButton.setOnAction(new EventHandler() + { + @Override + public void handle(ActionEvent event) + { + DirectoryChooser directoryChooser = new DirectoryChooser(); + directoryChooser.setTitle("Select Application Root Folder"); + directoryChooser.setInitialDirectory(mDirectoryPreference.getDirectoryApplicationRoot().toFile()); + Stage stage = (Stage)getChangeApplicationRootButton().getScene().getWindow(); + File selected = directoryChooser.showDialog(stage); + + if(selected != null) + { + mDirectoryPreference.setDirectoryApplicationRoot(selected.toPath()); + } + } + }); + } + + return mChangeApplicationRootButton; + } + + private Button getResetApplicationRootButton() + { + if(mResetApplicationRootButton == null) + { + mResetApplicationRootButton = new Button("Reset"); + mResetApplicationRootButton.setOnAction(new EventHandler() + { + @Override + public void handle(ActionEvent event) + { + mDirectoryPreference.resetDirectoryApplicationRoot(); + } + }); + } + + return mResetApplicationRootButton; + } + + private Label getApplicationRootPathLabel() + { + if(mApplicationRootPathLabel == null) + { + mApplicationRootPathLabel = new Label(mDirectoryPreference.getDirectoryApplicationRoot().toString()); + } + + return mApplicationRootPathLabel; + } + + private Label getApplicationLogsLabel() + { + if(mApplicationLogsLabel == null) + { + mApplicationLogsLabel = new Label("Application Logs"); + } + + return mApplicationLogsLabel; + } + + private Button getChangeApplicationLogsButton() + { + if(mChangeApplicationLogsButton == null) + { + mChangeApplicationLogsButton = new Button("Change..."); + mChangeApplicationLogsButton.setOnAction(new EventHandler() + { + @Override + public void handle(ActionEvent event) + { + DirectoryChooser directoryChooser = new DirectoryChooser(); + directoryChooser.setTitle("Select Application Logs Folder"); + directoryChooser.setInitialDirectory(mDirectoryPreference.getDirectoryApplicationLog().toFile()); + Stage stage = (Stage)getChangeApplicationLogsButton().getScene().getWindow(); + File selected = directoryChooser.showDialog(stage); + + if(selected != null) + { + mDirectoryPreference.setDirectoryApplicationLogs(selected.toPath()); + } + } + }); + } + + return mChangeApplicationLogsButton; + } + + private Button getResetApplicationLogsButton() + { + if(mResetApplicationLogsButton == null) + { + mResetApplicationLogsButton = new Button("Reset"); + mResetApplicationLogsButton.setOnAction(new EventHandler() + { + @Override + public void handle(ActionEvent event) + { + mDirectoryPreference.resetDirectoryApplicationLogs(); + } + }); + } + + return mResetApplicationLogsButton; + } + + private Label getApplicationLogsPathLabel() + { + if(mApplicationLogsPathLabel == null) + { + mApplicationLogsPathLabel = new Label(mDirectoryPreference.getDirectoryApplicationLog().toString()); + } + + return mApplicationLogsPathLabel; + } + + private Label getEventLogsLabel() + { + if(mEventLogsLabel == null) + { + mEventLogsLabel = new Label("Event Logs"); + } + + return mEventLogsLabel; + } + + private Button getChangeEventLogsButton() + { + if(mChangeEventLogsButton == null) + { + mChangeEventLogsButton = new Button("Change..."); + mChangeEventLogsButton.setOnAction(new EventHandler() + { + @Override + public void handle(ActionEvent event) + { + DirectoryChooser directoryChooser = new DirectoryChooser(); + directoryChooser.setTitle("Select Event Logs Folder"); + directoryChooser.setInitialDirectory(mDirectoryPreference.getDirectoryEventLog().toFile()); + Stage stage = (Stage)getChangeEventLogsButton().getScene().getWindow(); + File selected = directoryChooser.showDialog(stage); + + if(selected != null) + { + mDirectoryPreference.setDirectoryEventLogs(selected.toPath()); + } + } + }); + } + + return mChangeEventLogsButton; + } + + private Button getResetEventLogsButton() + { + if(mResetEventLogsButton == null) + { + mResetEventLogsButton = new Button("Reset"); + mResetEventLogsButton.setOnAction(new EventHandler() + { + @Override + public void handle(ActionEvent event) + { + mDirectoryPreference.resetDirectoryEventLogs(); + } + }); + } + + return mResetEventLogsButton; + } + + private Label getEventLogsPathLabel() + { + if(mEventLogsPathLabel == null) + { + mEventLogsPathLabel = new Label(mDirectoryPreference.getDirectoryEventLog().toString()); + } + + return mEventLogsPathLabel; + } + + private Label getPlaylistLabel() + { + if(mPlaylistLabel == null) + { + mPlaylistLabel = new Label("Playlists"); + } + + return mPlaylistLabel; + } + + private Button getChangePlaylistButton() + { + if(mChangePlaylistButton == null) + { + mChangePlaylistButton = new Button("Change..."); + mChangePlaylistButton.setOnAction(new EventHandler() + { + @Override + public void handle(ActionEvent event) + { + DirectoryChooser directoryChooser = new DirectoryChooser(); + directoryChooser.setTitle("Select Playlist Folder"); + directoryChooser.setInitialDirectory(mDirectoryPreference.getDirectoryPlaylist().toFile()); + Stage stage = (Stage)getChangePlaylistButton().getScene().getWindow(); + File selected = directoryChooser.showDialog(stage); + + if(selected != null) + { + mDirectoryPreference.setDirectoryPlaylist(selected.toPath()); + } + } + }); + } + + return mChangePlaylistButton; + } + + private Button getResetPlaylistButton() + { + if(mResetPlaylistButton == null) + { + mResetPlaylistButton = new Button("Reset"); + mResetPlaylistButton.setOnAction(new EventHandler() + { + @Override + public void handle(ActionEvent event) + { + mDirectoryPreference.resetDirectoryPlaylist(); + } + }); + } + + return mResetPlaylistButton; + } + + private Label getPlaylistPathLabel() + { + if(mPlaylistPathLabel == null) + { + mPlaylistPathLabel = new Label(mDirectoryPreference.getDirectoryPlaylist().toString()); + } + + return mPlaylistPathLabel; + } + + private Label getRecordingLabel() + { + if(mRecordingLabel == null) + { + mRecordingLabel = new Label("Recordings"); + } + + return mRecordingLabel; + } + + private Button getChangeRecordingButton() + { + if(mChangeRecordingButton == null) + { + mChangeRecordingButton = new Button("Change..."); + mChangeRecordingButton.setOnAction(new EventHandler() + { + @Override + public void handle(ActionEvent event) + { + DirectoryChooser directoryChooser = new DirectoryChooser(); + directoryChooser.setTitle("Select Recording Folder"); + directoryChooser.setInitialDirectory(mDirectoryPreference.getDirectoryRecording().toFile()); + Stage stage = (Stage)getChangeRecordingButton().getScene().getWindow(); + File selected = directoryChooser.showDialog(stage); + + if(selected != null) + { + mDirectoryPreference.setDirectoryRecording(selected.toPath()); + } + } + }); + } + + return mChangeRecordingButton; + } + + private Button getResetRecordingButton() + { + if(mResetRecordingButton == null) + { + mResetRecordingButton = new Button("Reset"); + mResetRecordingButton.setOnAction(new EventHandler() + { + @Override + public void handle(ActionEvent event) + { + mDirectoryPreference.resetDirectoryRecording(); + } + }); + } + + return mResetRecordingButton; + } + + private Label getRecordingPathLabel() + { + if(mRecordingPathLabel == null) + { + mRecordingPathLabel = new Label(mDirectoryPreference.getDirectoryRecording().toString()); + } + + return mRecordingPathLabel; + } + + private Label getScreenCaptureLabel() + { + if(mScreenCaptureLabel == null) + { + mScreenCaptureLabel = new Label("Screen Captures"); + } + + return mScreenCaptureLabel; + } + + private Button getChangeScreenCaptureButton() + { + if(mChangeScreenCaptureButton == null) + { + mChangeScreenCaptureButton = new Button("Change..."); + mChangeScreenCaptureButton.setOnAction(new EventHandler() + { + @Override + public void handle(ActionEvent event) + { + DirectoryChooser directoryChooser = new DirectoryChooser(); + directoryChooser.setTitle("Select Screen Capture Folder"); + directoryChooser.setInitialDirectory(mDirectoryPreference.getDirectoryScreenCapture().toFile()); + Stage stage = (Stage)getChangeScreenCaptureButton().getScene().getWindow(); + File selected = directoryChooser.showDialog(stage); + + if(selected != null) + { + mDirectoryPreference.setDirectoryScreenCapture(selected.toPath()); + } + } + }); + } + + return mChangeScreenCaptureButton; + } + + private Button getResetScreenCaptureButton() + { + if(mResetScreenCaptureButton == null) + { + mResetScreenCaptureButton = new Button("Reset"); + mResetScreenCaptureButton.setOnAction(new EventHandler() + { + @Override + public void handle(ActionEvent event) + { + mDirectoryPreference.resetDirectoryScreenCapture(); + } + }); + } + + return mResetScreenCaptureButton; + } + + private Label getScreenCapturePathLabel() + { + if(mScreenCapturePathLabel == null) + { + mScreenCapturePathLabel = new Label(mDirectoryPreference.getDirectoryScreenCapture().toString()); + } + + return mScreenCapturePathLabel; + } + + private Label getStreamingLabel() + { + if(mStreamingLabel == null) + { + mStreamingLabel = new Label("Streaming"); + } + + return mStreamingLabel; + } + + private Button getChangeStreamingButton() + { + if(mChangeStreamingButton == null) + { + mChangeStreamingButton = new Button("Change..."); + mChangeStreamingButton.setOnAction(new EventHandler() + { + @Override + public void handle(ActionEvent event) + { + DirectoryChooser directoryChooser = new DirectoryChooser(); + directoryChooser.setTitle("Select Streaming Folder"); + directoryChooser.setInitialDirectory(mDirectoryPreference.getDirectoryStreaming().toFile()); + Stage stage = (Stage)getChangeStreamingButton().getScene().getWindow(); + File selected = directoryChooser.showDialog(stage); + + if(selected != null) + { + mDirectoryPreference.setDirectoryStreaming(selected.toPath()); + } + } + }); + } + + return mChangeStreamingButton; + } + + private Button getResetStreamingButton() + { + if(mResetStreamingButton == null) + { + mResetStreamingButton = new Button("Reset"); + mResetStreamingButton.setOnAction(new EventHandler() + { + @Override + public void handle(ActionEvent event) + { + mDirectoryPreference.resetDirectoryStreaming(); + } + }); + } + + return mResetStreamingButton; + } + + private Label getStreamingPathLabel() + { + if(mStreamingPathLabel == null) + { + mStreamingPathLabel = new Label(mDirectoryPreference.getDirectoryStreaming().toString()); + } + + return mStreamingPathLabel; + } + + @Subscribe + public void preferenceUpdated(PreferenceType preferenceType) + { + if(preferenceType != null && preferenceType == PreferenceType.DIRECTORY) + { + getApplicationRootPathLabel().setText(mDirectoryPreference.getDirectoryApplicationRoot().toString()); + getApplicationLogsPathLabel().setText(mDirectoryPreference.getDirectoryApplicationLog().toString()); + getEventLogsPathLabel().setText(mDirectoryPreference.getDirectoryEventLog().toString()); + getPlaylistPathLabel().setText(mDirectoryPreference.getDirectoryPlaylist().toString()); + getRecordingPathLabel().setText(mDirectoryPreference.getDirectoryRecording().toString()); + getScreenCapturePathLabel().setText(mDirectoryPreference.getDirectoryScreenCapture().toString()); + getStreamingPathLabel().setText(mDirectoryPreference.getDirectoryStreaming().toString()); + } + } +} diff --git a/src/main/java/io/github/dsheirer/gui/preference/tuner/TunerPreferenceEditor.java b/src/main/java/io/github/dsheirer/gui/preference/tuner/TunerPreferenceEditor.java index 3b3c44c2..9e4a04e6 100644 --- a/src/main/java/io/github/dsheirer/gui/preference/tuner/TunerPreferenceEditor.java +++ b/src/main/java/io/github/dsheirer/gui/preference/tuner/TunerPreferenceEditor.java @@ -68,6 +68,7 @@ public class TunerPreferenceEditor extends HBox GridPane.setMargin(getChannelizerLabel(), new Insets(0, 10, 0, 0)); GridPane.setHalignment(getChannelizerLabel(), HPos.LEFT); mEditorPane.add(getChannelizerLabel(), 0, 0); + GridPane.setMargin(getChannelizerTypeChoiceBox(), new Insets(2, 0, 2, 0)); mEditorPane.add(getChannelizerTypeChoiceBox(), 1, 0); mEditorPane.add(new Separator(Orientation.HORIZONTAL), 0, 1, 2, 1); mEditorPane.add(getPolyphaseLabel(), 0, 2, 2, 1); diff --git a/src/main/java/io/github/dsheirer/log/ApplicationLog.java b/src/main/java/io/github/dsheirer/log/ApplicationLog.java new file mode 100644 index 00000000..a4b8bbaf --- /dev/null +++ b/src/main/java/io/github/dsheirer/log/ApplicationLog.java @@ -0,0 +1,166 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.log; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.encoder.PatternLayoutEncoder; +import ch.qos.logback.classic.filter.ThresholdFilter; +import ch.qos.logback.core.rolling.RollingFileAppender; +import ch.qos.logback.core.rolling.TimeBasedRollingPolicy; +import com.google.common.eventbus.Subscribe; +import io.github.dsheirer.eventbus.MyEventBus; +import io.github.dsheirer.preference.PreferenceType; +import io.github.dsheirer.preference.UserPreferences; +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.file.Path; + +/** + * Logback and SLF4j logging implementation. + */ +public class ApplicationLog +{ + private final static Logger mLog = LoggerFactory.getLogger(ApplicationLog.class); + + private static final String APPLICATION_LOG_FILENAME = "sdrtrunk_app.log"; + private static final int APPLICATION_LOG_MAX_HISTORY = 10; + + private UserPreferences mUserPreferences; + private RollingFileAppender mRollingFileAppender; + private Path mApplicationLogPath; + + /** + * Constructs the application log instance. Note: use the start() method to initiate logging. + * @param userPreferences + */ + public ApplicationLog(UserPreferences userPreferences) + { + mUserPreferences = userPreferences; + MyEventBus.getEventBus().register(this); + } + + @Subscribe + public void preferenceUpdated(PreferenceType preferenceType) + { + if(preferenceType == PreferenceType.DIRECTORY && mRollingFileAppender != null && mApplicationLogPath != null) + { + Path applicationLogPath = mUserPreferences.getDirectoryPreference().getDirectoryApplicationLog(); + + //Restart the application log if the path is updated + if(!applicationLogPath.equals(mApplicationLogPath)) + { + mLog.info("Application logging directory has changed [" + applicationLogPath.toString() + " ] - restarting logging"); + stop(); + start(); + } + } + } + + /** + * Starts application logging + */ + public void start() + { + if(mRollingFileAppender == null) + { + mApplicationLogPath = mUserPreferences.getDirectoryPreference().getDirectoryApplicationLog(); + Path logfile = mApplicationLogPath.resolve(APPLICATION_LOG_FILENAME); + mLog.info("Application Log File: " + logfile.toString()); + + LoggerContext loggerContext = (LoggerContext)LoggerFactory.getILoggerFactory(); + + PatternLayoutEncoder encoder = new PatternLayoutEncoder(); + encoder.setContext(loggerContext); + encoder.setPattern("%-25(%d{yyyyMMdd HHmmss.SSS} [%thread]) %-5level %logger{30} - %msg %memory_usage%n"); + encoder.start(); + + mRollingFileAppender = new RollingFileAppender(); + mRollingFileAppender.setContext(loggerContext); + mRollingFileAppender.setAppend(true); + mRollingFileAppender.setName("FILE"); + mRollingFileAppender.setEncoder(encoder); + mRollingFileAppender.setFile(logfile.toString()); + + ThresholdFilter thresholdFilter = new ThresholdFilter(); + thresholdFilter.setLevel(Level.DEBUG.toString()); + thresholdFilter.setContext(loggerContext); + thresholdFilter.setName("sdrtrunk threshold filter"); + thresholdFilter.start(); + + mRollingFileAppender.addFilter(thresholdFilter); + + String pattern = logfile.toString().replace(APPLICATION_LOG_FILENAME, "%d{yyyyMMdd}_" + APPLICATION_LOG_FILENAME); + TimeBasedRollingPolicy rollingPolicy = new TimeBasedRollingPolicy<>(); + rollingPolicy.setContext(loggerContext); + rollingPolicy.setFileNamePattern(pattern); + rollingPolicy.setMaxHistory(APPLICATION_LOG_MAX_HISTORY); + rollingPolicy.setParent(mRollingFileAppender); + rollingPolicy.start(); + + mRollingFileAppender.setRollingPolicy(rollingPolicy); + mRollingFileAppender.start(); + + Logger logger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + ((ch.qos.logback.classic.Logger)logger).setLevel(Level.ALL); + ((ch.qos.logback.classic.Logger)logger).addAppender(mRollingFileAppender); + +// StatusPrinter.print(loggerContext); + + mLog.info(""); + mLog.info("*******************************************************************"); + mLog.info("**** sdrtrunk: a trunked radio and digital decoding application ***"); + mLog.info("**** website: https://github.com/dsheirer/sdrtrunk ***"); + mLog.info("*******************************************************************"); + mLog.info("Memory Logging Format: [Used/Allocated PercentUsed%]"); + mLog.info("Host OS Name: " + System.getProperty("os.name")); + mLog.info("Host OS Arch: " + System.getProperty("os.arch")); + mLog.info("Host OS Version: " + System.getProperty("os.version")); + mLog.info("Host CPU Cores: " + Runtime.getRuntime().availableProcessors()); + mLog.info("Host Max Java Memory: " + FileUtils.byteCountToDisplaySize(Runtime.getRuntime().maxMemory())); + mLog.info("Storage Directories:"); + mLog.info(" Application Root: " + mUserPreferences.getDirectoryPreference().getDirectoryApplicationRoot()); + mLog.info(" Application Log: " + mUserPreferences.getDirectoryPreference().getDirectoryApplicationLog()); + mLog.info(" Event Log: " + mUserPreferences.getDirectoryPreference().getDirectoryEventLog()); + mLog.info(" Playlist: " + mUserPreferences.getDirectoryPreference().getDirectoryPlaylist()); + mLog.info(" Recordings: " + mUserPreferences.getDirectoryPreference().getDirectoryRecording()); + } + + } + + /** + * Stops the log file appender and nullifies it for shutdown or for reinitialization. + */ + public void stop() + { + if(mRollingFileAppender != null) + { + mLog.info("Stopping application logging"); + mRollingFileAppender.stop(); + LoggerContext loggerContext = (LoggerContext)LoggerFactory.getILoggerFactory(); + Logger logger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + ((ch.qos.logback.classic.Logger)logger).detachAppender(mRollingFileAppender); + mRollingFileAppender = null; + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventPanel.java b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventPanel.java index 057b7374..798e0d5d 100644 --- a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventPanel.java +++ b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventPanel.java @@ -26,6 +26,7 @@ import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.channel.IChannelDescriptor; import io.github.dsheirer.eventbus.MyEventBus; import io.github.dsheirer.icon.IconManager; +import io.github.dsheirer.identifier.Form; import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.identifier.IdentifierCollection; import io.github.dsheirer.identifier.Role; @@ -202,7 +203,15 @@ public class DecodeEventPanel extends JPanel implements Listener, IDataUnitDetectListen } catch(BitSetFullException bsfe) { - mLog.debug("Message full exception - unexpected"); +// mLog.debug("Message full exception - unexpected"); //Reset so that we can start over again reset(0); @@ -280,9 +280,9 @@ public class P25MessageFramer2 implements Listener, IDataUnitDetectListen mTrailingDibitsToSuppress = 22; break; default: - mLog.debug("*** MORE THAN 5 PDU BLOCKS DETECTED [" + - mPDUSequence.getHeader().getBlocksToFollowCount() + - "] - DETERMINE TRAILING NULL COUNT TO SUPPRESS AND UPDATE CODE"); +// mLog.debug("*** MORE THAN 5 PDU BLOCKS DETECTED [" + +// mPDUSequence.getHeader().getBlocksToFollowCount() + +// "] - DETERMINE TRAILING NULL COUNT TO SUPPRESS AND UPDATE CODE"); break; } @@ -298,7 +298,7 @@ public class P25MessageFramer2 implements Listener, IDataUnitDetectListen } else { - mLog.error("Received PDU data block with out a preceeding data header"); +// mLog.error("Received PDU data block with out a preceeding data header"); reset(mDataUnitID.getMessageLength()); } break; @@ -489,39 +489,6 @@ public class P25MessageFramer2 implements Listener, IDataUnitDetectListen } }); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180922_040600_9600BPS_CNYICC_Onondaga Simulcast_LCN 09.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180922_042033_9600BPS_CNYICC_Onondaga Simulcast_LCN 10.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_045614_9600BPS_CNYICC_Onondaga Simulcast_LCN 09.bits"); //Has Moto LC Opcode 01 -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_045647_9600BPS_CNYICC_Onondaga Simulcast_LCN 10.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_050325_9600BPS_CNYICC_Onondaga Simulcast_LCN 09.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_050403_9600BPS_CNYICC_Onondaga Simulcast_LCN 10.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_050721_9600BPS_CNYICC_Onondaga Simulcast_LCN 10.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_051057_9600BPS_CNYICC_Onondaga Simulcast_LCN 09.bits"); //ARS PDUs and SNDCP PDUs -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_051808_9600BPS_CNYICC_Onondaga Simulcast_LCN 09_EMERG_AND_PACKETS.bits"); //ARS and SNCDP PDUs -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_052519_9600BPS_CNYICC_Onondaga Simulcast_LCN 09.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181008_091511_9600BPS_CNYICC_Onondaga Simulcast_LCN 15 Control.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181008_091650_9600BPS_CNYICC_Onondaga Simulcast_LCN 15 Control.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_101745_9600BPS_CNYICC_Onondaga Simulcast_LCN 06.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_101850_9600BPS_CNYICC_Onondaga Simulcast_LCN 07.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_101855_9600BPS_CNYICC_Onondaga Simulcast_LCN 08.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_102145_9600BPS_CNYICC_Onondaga Simulcast_LCN 04.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_102451_9600BPS_CNYICC_Onondaga Simulcast_LCN 09_lots_of_PACKET_DATA.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_102456_9600BPS_CNYICC_Onondaga Simulcast_LCN 10.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_102459_9600BPS_CNYICC_Onondaga Simulcast_LCN 11.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_102501_9600BPS_CNYICC_Onondaga Simulcast_LCN 12.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_103208_9600BPS_CNYICC_Onondaga Simulcast_LCN 09.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_140834_9600BPS_CNYICC_Onondaga Simulcast_LCN 06.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_140851_9600BPS_CNYICC_Onondaga Simulcast_LCN 07.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_141547_9600BPS_CNYICC_Onondaga Simulcast_LCN 06.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_141607_9600BPS_CNYICC_Onondaga Simulcast_LCN 07.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_142300_9600BPS_CNYICC_Onondaga Simulcast_LCN 06.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_142319_9600BPS_CNYICC_Onondaga Simulcast_LCN 07.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_143012_9600BPS_CNYICC_Onondaga Simulcast_LCN 06.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_143031_9600BPS_CNYICC_Onondaga Simulcast_LCN 07.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_143723_9600BPS_CNYICC_Onondaga Simulcast_LCN 06.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_143741_9600BPS_CNYICC_Onondaga Simulcast_LCN 07.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181102_100905_9600BPS_CNYICC_Onondaga Simulcast_LCN 08.bits"); -// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181102_101622_9600BPS_CNYICC_Onondaga Simulcast_LCN 08.bits"); // Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181102_102339_9600BPS_CNYICC_Onondaga Simulcast_LCN 08.bits"); // Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181103_134948_9600BPS_CNYICC_Oswego Simulcast_LCN 04.bits"); // Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181103_144312_9600BPS_CNYICC_Oswego Simulcast_LCN 04.bits"); //Interesting UDP port 231 packets (oswego LCN 4) diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25TrafficChannelManager.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25TrafficChannelManager.java index a7bdbb6e..bc08f3fe 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/P25TrafficChannelManager.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25TrafficChannelManager.java @@ -286,6 +286,7 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv break; case OSP_SNDCP_DATA_CHANNEL_GRANT: + case OSP_GROUP_DATA_CHANNEL_GRANT: case OSP_INDIVIDUAL_DATA_CHANNEL_GRANT: type = encrypted ? DecodeEventType.DATA_CALL_ENCRYPTED : DecodeEventType.DATA_CALL; break; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/P25Message.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/P25Message.java index b00bbf5d..ff50b033 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/P25Message.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/P25Message.java @@ -1,6 +1,7 @@ -/******************************************************************************* +/* + * ****************************************************************************** * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * 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 @@ -14,8 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * ***************************************************************************** + */ package io.github.dsheirer.module.decode.p25.message; import io.github.dsheirer.bits.CorrectedBinaryMessage; @@ -88,7 +89,7 @@ public abstract class P25Message extends Message /** * The transmitted binary message */ - protected CorrectedBinaryMessage getMessage() + public CorrectedBinaryMessage getMessage() { return mMessage; } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/Opcode.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/Opcode.java index 959536c1..cd446fd0 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/Opcode.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/Opcode.java @@ -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 + * ***************************************************************************** + */ + package io.github.dsheirer.module.decode.p25.message.tsbk; import io.github.dsheirer.module.decode.p25.reference.Direction; @@ -150,6 +170,7 @@ public enum Opcode MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT(2, "PTCH GRP VCHN GR", "MOTOROLA PATCH GROUP CHANNEL GRANT"), MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT_UPDATE(3, "PTCH GRP VCH UPD", "MOTOROLA PATCH GROUP CHANNEL GRANT UPDATE"), MOTOROLA_OSP_TRAFFIC_CHANNEL_ID(5, "CHANNEL CWID", "CHANNEL CWID"), + MOTOROLA_OSP_OPCODE_7(7, "MOTOROLA OPCODE 7", "MOTOROLA OPCODE 07"), MOTOROLA_OSP_SYSTEM_LOADING(9, "SYSTEM LOADING", "SYSTEM LOADING"), MOTOROLA_OSP_CONTROL_CHANNEL_ID(11, "CCH BASE STAT ID", "CONTROL CHANNEL BASE STATION ID"), MOTOROLA_OSP_CONTROL_CHANNEL_PLANNED_SHUTDOWN(14, "CCH PLND SHUTDWN", "CONTROL CHANNEL PLANNED SHUTDOWN"), @@ -272,6 +293,8 @@ public enum Opcode return MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT_UPDATE; case 0x05: return MOTOROLA_OSP_TRAFFIC_CHANNEL_ID; + case 0x07: + return MOTOROLA_OSP_OPCODE_7; case 0x09: return MOTOROLA_OSP_SYSTEM_LOADING; case 0x0B: diff --git a/src/main/java/io/github/dsheirer/module/log/EventLogManager.java b/src/main/java/io/github/dsheirer/module/log/EventLogManager.java index b552d4eb..5eaf099d 100644 --- a/src/main/java/io/github/dsheirer/module/log/EventLogManager.java +++ b/src/main/java/io/github/dsheirer/module/log/EventLogManager.java @@ -22,7 +22,7 @@ package io.github.dsheirer.module.log; import io.github.dsheirer.controller.channel.Channel; import io.github.dsheirer.module.Module; import io.github.dsheirer.module.log.config.EventLogConfiguration; -import io.github.dsheirer.properties.SystemProperties; +import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.source.config.SourceConfigTuner; import io.github.dsheirer.util.StringUtils; import org.slf4j.Logger; @@ -36,12 +36,11 @@ public class EventLogManager { private final static Logger mLog = LoggerFactory.getLogger(EventLogManager.class); - private Path mDirectory; + private UserPreferences mUserPreferences; - public EventLogManager() + public EventLogManager(UserPreferences userPreferences) { - mDirectory = SystemProperties.getInstance() - .getApplicationFolder("event_logs"); + mUserPreferences = userPreferences; } public List getLoggers(Channel channel) @@ -89,16 +88,18 @@ public class EventLogManager sb.append(eventLogType.getFileSuffix()); sb.append(".log"); + Path eventLogDirectory = mUserPreferences.getDirectoryPreference().getDirectoryEventLog(); + switch(eventLogType) { case CALL_EVENT: - return new DecodeEventLogger(mDirectory, sb.toString(), frequency); + return new DecodeEventLogger(eventLogDirectory, sb.toString(), frequency); case DECODED_MESSAGE: - return new MessageEventLogger(mDirectory, sb.toString(), MessageEventLogger.Type.DECODED, frequency); + return new MessageEventLogger(eventLogDirectory, sb.toString(), MessageEventLogger.Type.DECODED, frequency); case TRAFFIC_CALL_EVENT: - return new DecodeEventLogger(mDirectory, sb.toString(), frequency); + return new DecodeEventLogger(eventLogDirectory, sb.toString(), frequency); case TRAFFIC_DECODED_MESSAGE: - return new MessageEventLogger(mDirectory, sb.toString(), MessageEventLogger.Type.DECODED, frequency); + return new MessageEventLogger(eventLogDirectory, sb.toString(), MessageEventLogger.Type.DECODED, frequency); default: return null; } diff --git a/src/main/java/io/github/dsheirer/playlist/PlaylistManager.java b/src/main/java/io/github/dsheirer/playlist/PlaylistManager.java index 3ee2267a..05755b9a 100644 --- a/src/main/java/io/github/dsheirer/playlist/PlaylistManager.java +++ b/src/main/java/io/github/dsheirer/playlist/PlaylistManager.java @@ -33,7 +33,7 @@ import io.github.dsheirer.controller.channel.ChannelModel; import io.github.dsheirer.controller.channel.map.ChannelMapEvent; import io.github.dsheirer.controller.channel.map.ChannelMapModel; import io.github.dsheirer.preference.UserPreferences; -import io.github.dsheirer.preference.playlist.FilePreferences; +import io.github.dsheirer.preference.playlist.PlaylistPreference; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.util.ThreadPool; import org.slf4j.Logger; @@ -186,7 +186,7 @@ public class PlaylistManager implements Listener */ private void save() { - FilePreferences files = mUserPreferences.getFilePreferences(); + PlaylistPreference files = mUserPreferences.getPlaylistPreference(); PlaylistV2 playlist = new PlaylistV2(); @@ -239,7 +239,7 @@ public class PlaylistManager implements Listener Files.delete(files.getPlaylistLock()); } - mUserPreferences.getFilePreferences().setPlaylistLastAccessedPath(files.getPlaylist()); + mUserPreferences.getPlaylistPreference().setPlaylistLastAccessedPath(files.getPlaylist()); } catch(IOException ioe) { @@ -256,7 +256,7 @@ public class PlaylistManager implements Listener */ public PlaylistV2 load() { - FilePreferences files = mUserPreferences.getFilePreferences(); + PlaylistPreference files = mUserPreferences.getPlaylistPreference(); PlaylistV2 playlist = null; diff --git a/src/main/java/io/github/dsheirer/preference/PreferenceType.java b/src/main/java/io/github/dsheirer/preference/PreferenceType.java index 81e4fa16..5f00a600 100644 --- a/src/main/java/io/github/dsheirer/preference/PreferenceType.java +++ b/src/main/java/io/github/dsheirer/preference/PreferenceType.java @@ -26,7 +26,7 @@ package io.github.dsheirer.preference; public enum PreferenceType { DECODE_EVENT, - FILES, + DIRECTORY, IDENTIFIER, TUNER; } diff --git a/src/main/java/io/github/dsheirer/preference/UserPreferences.java b/src/main/java/io/github/dsheirer/preference/UserPreferences.java index 181c66cd..bef3682a 100644 --- a/src/main/java/io/github/dsheirer/preference/UserPreferences.java +++ b/src/main/java/io/github/dsheirer/preference/UserPreferences.java @@ -21,19 +21,34 @@ package io.github.dsheirer.preference; import io.github.dsheirer.eventbus.MyEventBus; +import io.github.dsheirer.preference.directory.DirectoryPreference; import io.github.dsheirer.preference.event.DecodeEventPreference; import io.github.dsheirer.preference.identifier.TalkgroupFormatPreference; -import io.github.dsheirer.preference.playlist.FilePreferences; +import io.github.dsheirer.preference.playlist.PlaylistPreference; import io.github.dsheirer.preference.tuner.TunerPreference; import io.github.dsheirer.sample.Listener; /** * User Preferences. A collection of preferences that can be accessed by preference type. + * + * Note: user preference updates are broadcast throughout the system using the Google Guava Event Bus. Each component + * can register with the event bus and annotate a method to receive updates: + * + * To register a component to receive events, add this in the constructor: + * MyEventBus.getEventBus().register(this); + * + * To receive preference update notifications, annotate a method with @Subscribe and use a PreferenceType argument: + * + * @Subscribe + * public void preferenceUpdated(PreferenceType preferenceType) + * { + * } */ public class UserPreferences implements Listener { private DecodeEventPreference mDecodeEventPreference; - private FilePreferences mFilePreferences; + private DirectoryPreference mDirectoryPreference; + private PlaylistPreference mPlaylistPreference; private TalkgroupFormatPreference mTalkgroupFormatPreference; private TunerPreference mTunerPreference; @@ -53,12 +68,20 @@ public class UserPreferences implements Listener return mDecodeEventPreference; } + /** + * Directory preferences + */ + public DirectoryPreference getDirectoryPreference() + { + return mDirectoryPreference; + } + /** * Playlist preferences */ - public FilePreferences getFilePreferences() + public PlaylistPreference getPlaylistPreference() { - return mFilePreferences; + return mPlaylistPreference; } /** @@ -83,7 +106,8 @@ public class UserPreferences implements Listener private void loadPreferenceTypes() { mDecodeEventPreference = new DecodeEventPreference(this); - mFilePreferences = new FilePreferences(this::receive); + mDirectoryPreference = new DirectoryPreference(this::receive); + mPlaylistPreference = new PlaylistPreference(this, mDirectoryPreference); mTalkgroupFormatPreference = new TalkgroupFormatPreference(this); mTunerPreference = new TunerPreference(this); } diff --git a/src/main/java/io/github/dsheirer/preference/directory/DirectoryPreference.java b/src/main/java/io/github/dsheirer/preference/directory/DirectoryPreference.java new file mode 100644 index 00000000..77d829fa --- /dev/null +++ b/src/main/java/io/github/dsheirer/preference/directory/DirectoryPreference.java @@ -0,0 +1,444 @@ +/* + * ****************************************************************************** + * 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 + * ***************************************************************************** + */ + +package io.github.dsheirer.preference.directory; + +import io.github.dsheirer.preference.Preference; +import io.github.dsheirer.preference.PreferenceType; +import io.github.dsheirer.sample.Listener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.prefs.Preferences; + +/** + * User preferences for directories + */ +public class DirectoryPreference extends Preference +{ + private final static Logger mLog = LoggerFactory.getLogger(DirectoryPreference.class); + private Preferences mPreferences = Preferences.userNodeForPackage(DirectoryPreference.class); + + private static final String DIRECTORY_APPLICATION_ROOT = "SDRTrunk"; + private static final String DIRECTORY_APPLICATION_LOG = "logs"; + private static final String DIRECTORY_EVENT_LOG = "event_logs"; + private static final String DIRECTORY_PLAYLIST = "playlist"; + private static final String DIRECTORY_RECORDING = "recordings"; + private static final String DIRECTORY_SCREEN_CAPTURE = "screen_captures"; + private static final String DIRECTORY_STREAMING = "streaming"; + + private static final String PREFERENCE_KEY_DIRECTORY_APPLICATION_ROOT = "directory.application.root"; + private static final String PREFERENCE_KEY_DIRECTORY_APPLICATION_LOGS = "directory.application.logs"; + private static final String PREFERENCE_KEY_DIRECTORY_EVENT_LOGS = "directory.event.logs"; + private static final String PREFERENCE_KEY_DIRECTORY_PLAYLIST = "directory.playlist"; + private static final String PREFERENCE_KEY_DIRECTORY_RECORDING = "directory.recording"; + private static final String PREFERENCE_KEY_DIRECTORY_SCREEN_CAPTURE = "directory.screen.capture"; + private static final String PREFERENCE_KEY_DIRECTORY_STREAMING = "directory.streaming"; + + private Path mDirectoryApplicationRoot; + private Path mDirectoryApplicationLogs; + private Path mDirectoryEventLogs; + private Path mDirectoryPlaylist; + private Path mDirectoryRecording; + private Path mDirectoryScreenCapture; + private Path mDirectoryStreaming; + + /** + * Constructs this preference with an update listener + * @param updateListener to receive notifications whenever these preferences change + */ + public DirectoryPreference(Listener updateListener) + { + super(updateListener); + } + + @Override + public PreferenceType getPreferenceType() + { + return PreferenceType.DIRECTORY; + } + + /** + * Path to the application root directory + */ + public Path getDirectoryApplicationRoot() + { + if(mDirectoryApplicationRoot == null) + { + mDirectoryApplicationRoot = getPath(PREFERENCE_KEY_DIRECTORY_APPLICATION_ROOT, getDefaultApplicationDirectory()); + createDirectory(mDirectoryApplicationRoot); + } + + return mDirectoryApplicationRoot; + } + + /** + * Sets the path to the application root directory + */ + public void setDirectoryApplicationRoot(Path path) + { + mDirectoryApplicationRoot = path; + mPreferences.put(PREFERENCE_KEY_DIRECTORY_APPLICATION_ROOT, path.toString()); + + //Set the child paths to null so that they'll be recreated on next access. If the user has explicitly set a + //path override for any of these directories, then it will be retained. + nullifyApplicationChildDirectories(); + + notifyPreferenceUpdated(); + } + + /** + * Nullifies each of the application root child directories so that they can be re-created from application root + * or re-created from a persisted override path. + */ + private void nullifyApplicationChildDirectories() + { + mDirectoryApplicationLogs = null; + mDirectoryEventLogs = null; + mDirectoryPlaylist = null; + mDirectoryRecording = null; + mDirectoryScreenCapture = null; + mDirectoryStreaming = null; + } + + /** + * Removes a stored recording directory preference so that the default path can be used again + */ + public void resetDirectoryApplicationRoot() + { + mDirectoryApplicationRoot = null; + mPreferences.remove(PREFERENCE_KEY_DIRECTORY_APPLICATION_ROOT); + + //Set the child paths to null so that they'll be recreated on next access. If the user has explicitly set a + //path override for any of these directories, then it will be retained. + nullifyApplicationChildDirectories(); + + notifyPreferenceUpdated(); + } + + /** + * Path to the folder for storing application logs + */ + public Path getDirectoryApplicationLog() + { + if(mDirectoryApplicationLogs == null) + { + mDirectoryApplicationLogs = getPath(PREFERENCE_KEY_DIRECTORY_APPLICATION_LOGS, getDefaultApplicationLogsDirectory()); + createDirectory(mDirectoryApplicationLogs); + } + + return mDirectoryApplicationLogs; + } + + /** + * Sets the path to the application logs folder + */ + public void setDirectoryApplicationLogs(Path path) + { + mDirectoryApplicationLogs = path; + mPreferences.put(PREFERENCE_KEY_DIRECTORY_APPLICATION_LOGS, path.toString()); + notifyPreferenceUpdated(); + } + + /** + * Removes a stored application logs directory preference so that the default path can be used again + */ + public void resetDirectoryApplicationLogs() + { + mPreferences.remove(PREFERENCE_KEY_DIRECTORY_APPLICATION_LOGS); + mDirectoryApplicationLogs = null; + notifyPreferenceUpdated(); + } + + /** + * Path to the folder for storing event logs + */ + public Path getDirectoryEventLog() + { + if(mDirectoryEventLogs == null) + { + mDirectoryEventLogs = getPath(PREFERENCE_KEY_DIRECTORY_EVENT_LOGS, getDefaultEventLogsDirectory()); + createDirectory(mDirectoryEventLogs); + } + + return mDirectoryEventLogs; + } + + /** + * Sets the path to the event logs folder + */ + public void setDirectoryEventLogs(Path path) + { + mDirectoryEventLogs = path; + mPreferences.put(PREFERENCE_KEY_DIRECTORY_EVENT_LOGS, path.toString()); + notifyPreferenceUpdated(); + } + + /** + * Removes a stored playlist directory preference so that the default path can be used again + */ + public void resetDirectoryEventLogs() + { + mPreferences.remove(PREFERENCE_KEY_DIRECTORY_EVENT_LOGS); + mDirectoryEventLogs = null; + notifyPreferenceUpdated(); + } + + /** + * Path to the folder for storing playlist + */ + public Path getDirectoryPlaylist() + { + if(mDirectoryPlaylist == null) + { + mDirectoryPlaylist = getPath(PREFERENCE_KEY_DIRECTORY_PLAYLIST, getDefaultPlaylistDirectory()); + createDirectory(mDirectoryPlaylist); + } + + return mDirectoryPlaylist; + } + + /** + * Sets the path to the playlist folder + */ + public void setDirectoryPlaylist(Path path) + { + mDirectoryPlaylist = path; + mPreferences.put(PREFERENCE_KEY_DIRECTORY_PLAYLIST, path.toString()); + notifyPreferenceUpdated(); + } + + /** + * Removes a stored playlist directory preference so that the default path can be used again + */ + public void resetDirectoryPlaylist() + { + mPreferences.remove(PREFERENCE_KEY_DIRECTORY_PLAYLIST); + mDirectoryPlaylist = null; + notifyPreferenceUpdated(); + } + + /** + * Path to the folder for storing recordings + */ + public Path getDirectoryRecording() + { + if(mDirectoryRecording == null) + { + mDirectoryRecording = getPath(PREFERENCE_KEY_DIRECTORY_RECORDING, getDefaultRecordingDirectory()); + createDirectory(mDirectoryRecording); + } + + return mDirectoryRecording; + } + + /** + * Sets the path to the playlist folder + */ + public void setDirectoryRecording(Path path) + { + mDirectoryRecording = path; + mPreferences.put(PREFERENCE_KEY_DIRECTORY_RECORDING, path.toString()); + notifyPreferenceUpdated(); + } + + /** + * Removes a stored recording directory preference so that the default path can be used again + */ + public void resetDirectoryRecording() + { + mPreferences.remove(PREFERENCE_KEY_DIRECTORY_RECORDING); + mDirectoryRecording = null; + notifyPreferenceUpdated(); + } + + /** + * Path to the folder for storing screen capture + */ + public Path getDirectoryScreenCapture() + { + if(mDirectoryScreenCapture == null) + { + mDirectoryScreenCapture = getPath(PREFERENCE_KEY_DIRECTORY_SCREEN_CAPTURE, getDefaultScreenCaptureDirectory()); + createDirectory(mDirectoryScreenCapture); + } + + return mDirectoryScreenCapture; + } + + /** + * Sets the path to the screen capture folder + */ + public void setDirectoryScreenCapture(Path path) + { + mDirectoryScreenCapture = path; + mPreferences.put(PREFERENCE_KEY_DIRECTORY_SCREEN_CAPTURE, path.toString()); + notifyPreferenceUpdated(); + } + + /** + * Removes a stored screen capture directory preference so that the default path can be used again + */ + public void resetDirectoryScreenCapture() + { + mPreferences.remove(PREFERENCE_KEY_DIRECTORY_SCREEN_CAPTURE); + mDirectoryScreenCapture = null; + notifyPreferenceUpdated(); + } + + /** + * Path to the folder for storing streaming temporary recordings + */ + public Path getDirectoryStreaming() + { + if(mDirectoryStreaming == null) + { + mDirectoryStreaming = getPath(PREFERENCE_KEY_DIRECTORY_STREAMING, getDefaultStreamingDirectory()); + createDirectory(mDirectoryStreaming); + } + + return mDirectoryStreaming; + } + + /** + * Sets the path to the streaming folder + */ + public void setDirectoryStreaming(Path path) + { + mDirectoryStreaming = path; + mPreferences.put(PREFERENCE_KEY_DIRECTORY_STREAMING, path.toString()); + notifyPreferenceUpdated(); + } + + /** + * Removes a stored streaming directory preference so that the default path can be used again + */ + public void resetDirectoryStreaming() + { + mPreferences.remove(PREFERENCE_KEY_DIRECTORY_STREAMING); + mDirectoryStreaming = null; + notifyPreferenceUpdated(); + } + + /** + * Default application root directory + */ + private Path getDefaultApplicationDirectory() + { + return Paths.get(System.getProperty("user.home"), DIRECTORY_APPLICATION_ROOT); + } + + /** + * Default event logs directory + */ + public Path getDefaultApplicationLogsDirectory() + { + return getDirectoryApplicationRoot().resolve(DIRECTORY_APPLICATION_LOG); + } + + /** + * Default event logs directory + */ + public Path getDefaultEventLogsDirectory() + { + return getDirectoryApplicationRoot().resolve(DIRECTORY_EVENT_LOG); + } + + /** + * Default playlist directory + */ + public Path getDefaultPlaylistDirectory() + { + return getDirectoryApplicationRoot().resolve(DIRECTORY_PLAYLIST); + } + + /** + * Default playlist directory + */ + public Path getDefaultRecordingDirectory() + { + return getDirectoryApplicationRoot().resolve(DIRECTORY_RECORDING); + } + + /** + * Default screen capture directory + */ + public Path getDefaultScreenCaptureDirectory() + { + return getDirectoryApplicationRoot().resolve(DIRECTORY_SCREEN_CAPTURE); + } + + /** + * Default streaming directory + */ + public Path getDefaultStreamingDirectory() + { + return getDirectoryApplicationRoot().resolve(DIRECTORY_STREAMING); + } + + /** + * Returns the path stored in preferences and referenced by the key argument if it exists, otherwise returns the + * default path. + * + * Note: the default path is not checked for existence. + * + * @param key to the preferences service for the path + * @param defaultPath to use if the preferred path does not exist + * @return requested path + */ + private Path getPath(String key, Path defaultPath) + { + String stringPath = mPreferences.get(key, defaultPath.toString()); + + if(stringPath != null && !stringPath.isEmpty()) + { + Path temp = Paths.get(stringPath); + + if(Files.exists(temp)) + { + return temp; + } + } + + return defaultPath; + } + + /** + * Creates a directory if it does not already exist + */ + private void createDirectory(Path directory) + { + if(!Files.exists(directory)) + { + try + { + Files.createDirectory(directory); + mLog.info("Created directory [" + directory.toString() + "]"); + } + catch(Exception e) + { + mLog.error("Error creating directory [" + directory.toString() + "]"); + } + } + } +} diff --git a/src/main/java/io/github/dsheirer/preference/playlist/FilePreferences.java b/src/main/java/io/github/dsheirer/preference/playlist/PlaylistPreference.java similarity index 71% rename from src/main/java/io/github/dsheirer/preference/playlist/FilePreferences.java rename to src/main/java/io/github/dsheirer/preference/playlist/PlaylistPreference.java index b5e715db..25319e28 100644 --- a/src/main/java/io/github/dsheirer/preference/playlist/FilePreferences.java +++ b/src/main/java/io/github/dsheirer/preference/playlist/PlaylistPreference.java @@ -22,6 +22,7 @@ package io.github.dsheirer.preference.playlist; import io.github.dsheirer.preference.Preference; import io.github.dsheirer.preference.PreferenceType; +import io.github.dsheirer.preference.directory.DirectoryPreference; import io.github.dsheirer.sample.Listener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,39 +35,37 @@ import java.util.prefs.Preferences; /** * User preferences for playlists */ -public class FilePreferences extends Preference +public class PlaylistPreference extends Preference { - private final static Logger mLog = LoggerFactory.getLogger(FilePreferences.class); - private Preferences mPreferences = Preferences.userNodeForPackage(FilePreferences.class); + private final static Logger mLog = LoggerFactory.getLogger(PlaylistPreference.class); + private Preferences mPreferences = Preferences.userNodeForPackage(PlaylistPreference.class); - private static final String FOLDER_APPLICATION_ROOT = "SDRTrunk"; - private static final String FOLDER_PLAYLIST = "playlist"; private static final String FILE_PLAYLIST = "default.xml"; private static final String FILE_LEGACY_PLAYLIST = "playlist_v2.xml"; - private static final String PREFERENCE_KEY_PLAYLIST_FOLDER = "playlist.folder"; private static final String PREFERENCE_KEY_PLAYLIST_LAST_ACCESSED = "playlist.last.accessed"; private static final String PREFERENCE_KEY_PLAYLIST_STARTUP = "playlist.default"; private static final String PREFERENCE_KEY_PLAYLIST_USE_LAST_ACCESSED = "playlist.use.last.accessed"; - private Path mPlaylistFolder; private Path mPlaylistStartupPath; private Path mPlaylistLastAccessedPath; private Boolean mUsePlaylistLastAccessedByDefault = true; + private DirectoryPreference mDirectoryPreference; /** * Constructs this preference with an update listener * @param updateListener to receive notifications whenever these preferences change */ - public FilePreferences(Listener updateListener) + public PlaylistPreference(Listener updateListener, DirectoryPreference directoryPreference) { super(updateListener); + mDirectoryPreference = directoryPreference; } @Override public PreferenceType getPreferenceType() { - return PreferenceType.FILES; + return PreferenceType.DIRECTORY; } @@ -90,7 +89,7 @@ public class FilePreferences extends Preference */ public Path getLegacyPlaylist() { - return getPlaylistFolder().resolve(FILE_LEGACY_PLAYLIST); + return mDirectoryPreference.getDirectoryPlaylist().resolve(FILE_LEGACY_PLAYLIST); } /** @@ -157,29 +156,6 @@ public class FilePreferences extends Preference notifyPreferenceUpdated(); } - /** - * Path to the folder for storing playlist - */ - public Path getPlaylistFolder() - { - if(mPlaylistFolder == null) - { - mPlaylistFolder = getPath(PREFERENCE_KEY_PLAYLIST_FOLDER, getDefaultPlaylistFolder()); - } - - return mPlaylistFolder; - } - - /** - * Sets the path to the playlist folder - */ - public void setPlaylistFolder(Path path) - { - mPlaylistFolder = path; - mPreferences.put(PREFERENCE_KEY_PLAYLIST_FOLDER, path.toString()); - notifyPreferenceUpdated(); - } - /** * Indicates the playlist to load on startup, either the last accessed playlist (true) or the default playlist (false). */ @@ -204,32 +180,12 @@ public class FilePreferences extends Preference } - /** - * Default application root path - */ - private Path getDefaultApplicationFolder() - { - Path defaultApplicationPath = Paths.get(System.getProperty("user.home"), FOLDER_APPLICATION_ROOT); - createDirectory(defaultApplicationPath); - return defaultApplicationPath; - } - - /** - * Default playlist folder - */ - public Path getDefaultPlaylistFolder() - { - Path defaultPlaylistFolder = getDefaultApplicationFolder().resolve(FOLDER_PLAYLIST); - createDirectory(defaultPlaylistFolder); - return defaultPlaylistFolder; - } - /** * Default playlist */ private Path getDefaultPlaylistPath() { - return getDefaultPlaylistFolder().resolve(FILE_PLAYLIST); + return mDirectoryPreference.getDefaultPlaylistDirectory().resolve(FILE_PLAYLIST); } /** @@ -244,7 +200,7 @@ public class FilePreferences extends Preference */ private Path getPath(String key, Path defaultPath) { - String stringPath = mPreferences.get(PREFERENCE_KEY_PLAYLIST_STARTUP, defaultPath.toString()); + String stringPath = mPreferences.get(key, defaultPath.toString()); if(stringPath != null && !stringPath.isEmpty()) { @@ -257,23 +213,4 @@ public class FilePreferences extends Preference return defaultPath; } - - /** - * Creates a directory if it does not already exist - */ - private void createDirectory(Path directory) - { - if(!Files.exists(directory) && Files.isDirectory(directory)) - { - try - { - Files.createDirectory(directory); - mLog.info("Created directory [" + directory.toString() + "]"); - } - catch(Exception e) - { - mLog.error("Error creating directory [" + directory.toString() + "]"); - } - } - } } diff --git a/src/main/java/io/github/dsheirer/record/RecorderManager.java b/src/main/java/io/github/dsheirer/record/RecorderManager.java index 59655c61..4a398a09 100644 --- a/src/main/java/io/github/dsheirer/record/RecorderManager.java +++ b/src/main/java/io/github/dsheirer/record/RecorderManager.java @@ -1,18 +1,22 @@ -/******************************************************************************* - * sdr-trunk +/* + * ****************************************************************************** + * 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 - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * ***************************************************************************** + */ package io.github.dsheirer.record; import io.github.dsheirer.alias.AliasList; @@ -24,7 +28,7 @@ import io.github.dsheirer.identifier.IdentifierCollection; import io.github.dsheirer.identifier.Role; import io.github.dsheirer.identifier.integer.IntegerIdentifier; import io.github.dsheirer.identifier.string.StringIdentifier; -import io.github.dsheirer.properties.SystemProperties; +import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.record.wave.AudioPacketWaveRecorder; import io.github.dsheirer.record.wave.ComplexBufferWaveRecorder; import io.github.dsheirer.record.wave.WaveMetadata; @@ -60,6 +64,7 @@ public class RecorderManager implements Listener private List mAudioPackets = new ArrayList<>(); private ScheduledFuture mBufferProcessorFuture; private AliasModel mAliasModel; + private UserPreferences mUserPreferences; private int mUnknownAudioRecordingIndex = 1; private boolean mCanStartNewRecorders = true; @@ -71,9 +76,10 @@ public class RecorderManager implements Listener * indicating that the call is complete. A separate recording monitor periodically checks for idled recorders to * be stopped for cases when the channel fails to send an end-call audio packet. */ - public RecorderManager(AliasModel aliasModel) + public RecorderManager(AliasModel aliasModel, UserPreferences userPreferences) { mAliasModel = aliasModel; + mUserPreferences = userPreferences; mAudioPacketQueue.setOverflowListener(new IOverflowListener() { @Override @@ -299,10 +305,13 @@ public class RecorderManager implements Listener return getRecordingBasePath().resolve(sbFinal.toString()); } - + /** + * Base path to recordings folder + * @return + */ public Path getRecordingBasePath() { - return SystemProperties.getInstance().getApplicationFolder("recordings"); + return mUserPreferences.getDirectoryPreference().getDirectoryRecording(); } /** diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 34f5140f..e3a78a8d 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -1,6 +1,7 @@ - + ~ ****************************************************************************** + --> - - ${user.home}/SDRTrunk/logs/sdrtrunk_app.log - - - ${user.home}/SDRTrunk/logs/%d{yyyyMMdd}_sdrtrunk_app.log - 10 - - - %-25(%d{yyyyMMdd HHmmss.SSS} [%thread]) %-5level %logger{30} - %msg %memory_usage%n - - - - - - - %-12(%d{HH:mm:ss.SSS}) %-5level %logger{25} - %msg %memory_usage%n