Add filter button to messages (#1098)

Adds filtering to channel decoded events panel.
This commit is contained in:
VolFFProCoder
2022-04-30 05:44:57 -04:00
committed by GitHub
parent 1152b13220
commit adcb55ffbc
19 changed files with 660 additions and 433 deletions

View File

@@ -33,6 +33,7 @@ public class DecodeEvent implements IDecodeEvent
private long mTimeStart;
private long mTimeEnd;
private String mEventDescription;
private DecodeEventType mDecodeEventType;
private IdentifierCollection mIdentifierCollection;
private IChannelDescriptor mChannelDescriptor;
private String mDetails;
@@ -131,6 +132,21 @@ public class DecodeEvent implements IDecodeEvent
mEventDescription = description;
}
/**
* {@link DecodeEventType}
*/
@Override
public DecodeEventType getEventType() {
return mDecodeEventType;
}
/**
* Sets the {@link DecodeEventType}
*/
public void setEventType(DecodeEventType eventType) {
this.mDecodeEventType = eventType;
}
/**
* Identifier collection for this event.
*/
@@ -252,6 +268,7 @@ public class DecodeEvent implements IDecodeEvent
protected long mTimeStart;
protected long mDuration;
protected String mEventDescription;
protected DecodeEventType mDecodeEventType;
protected IdentifierCollection mIdentifierCollection;
protected IChannelDescriptor mChannelDescriptor;
protected String mDetails;
@@ -296,6 +313,11 @@ public class DecodeEvent implements IDecodeEvent
return this;
}
public DecodeEventBuilder eventType(DecodeEventType eventType) {
mDecodeEventType = eventType;
return this;
}
/**
* Sets the event description text
* @param description of the event

View File

@@ -24,7 +24,10 @@ package io.github.dsheirer.module.decode.event;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.channel.IChannelDescriptor;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.module.decode.event.filter.DecodeEventFilterSet;
import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.sample.Listener;
import org.slf4j.Logger;
@@ -35,7 +38,7 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class DecodeEventModel extends AbstractTableModel implements Listener<IDecodeEvent>
public class DecodeEventModel extends AbstractTableModel implements Listener<IDecodeEvent>, EventFilterProvider<IDecodeEvent>
{
private static final long serialVersionUID = 1L;
private final static Logger mLog = LoggerFactory.getLogger(DecodeEventModel.class);
@@ -54,6 +57,7 @@ public class DecodeEventModel extends AbstractTableModel implements Listener<IDe
protected int mMaxMessages = 500;
protected List<IDecodeEvent> mEvents = new ArrayList<>();
protected FilterSet<IDecodeEvent> mEventFilterSet = new DecodeEventFilterSet();
protected String[] mHeaders = new String[]{"Time", "Duration", "Event", "From", "Alias", "To", "Alias", "Channel", "Frequency", "Details"};
@@ -105,6 +109,12 @@ public class DecodeEventModel extends AbstractTableModel implements Listener<IDe
}
}
public void clear()
{
mEvents.clear();
fireTableDataChanged();
}
/**
* Clears all events from this model and loads the events argument
*/
@@ -139,6 +149,10 @@ public class DecodeEventModel extends AbstractTableModel implements Listener<IDe
*/
public void receive(final IDecodeEvent event)
{
if (!mEventFilterSet.passes(event))
{
return;
}
if(!mEvents.contains(event))
{
mEvents.add(0, event);
@@ -162,6 +176,16 @@ public class DecodeEventModel extends AbstractTableModel implements Listener<IDe
}
}
@Override
public FilterSet<IDecodeEvent> getFilterSet() {
return mEventFilterSet;
}
@Override
public void setFilterSet(FilterSet<IDecodeEvent> filterSet) {
this.mEventFilterSet = filterSet;
}
@Override
public int getRowCount()
{

View File

@@ -28,12 +28,18 @@ import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.channel.IChannelDescriptor;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.filter.AllPassFilter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.identifier.Form;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.identifier.Role;
import io.github.dsheirer.module.ProcessingChain;
import io.github.dsheirer.module.decode.event.filter.EventClearButton;
import io.github.dsheirer.module.decode.event.filter.EventClearHandler;
import io.github.dsheirer.module.decode.event.filter.EventFilterButton;
import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
@@ -42,15 +48,9 @@ import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.*;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Collections;
@@ -72,6 +72,7 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
private AliasModel mAliasModel;
private UserPreferences mUserPreferences;
private TimestampCellRenderer mTimestampCellRenderer;
private EventManagementPanel mEventManagementPanel;
/**
* View for call event table
@@ -89,9 +90,12 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
mTable = new JTable(mEventModel);
mTable.setAutoCreateRowSorter(true);
mTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
mEventManagementPanel = new EventManagementPanel();
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
updateCellRenderers();
add(mEventManagementPanel, "span,growx");
mEmptyScroller = new JScrollPane(mTable);
add(mEmptyScroller);
}
@@ -148,15 +152,76 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
mCurrentEventHistory = processingChain.getDecodeEventHistory();
mEventModel.clearAndSet(mCurrentEventHistory.getItems());
processingChain.getDecodeEventHistory().addListener(mEventModel);
mEventManagementPanel.enableButtons();
}
else
{
mCurrentEventHistory = null;
mEventModel.clearAndSet(Collections.emptyList());
mEventManagementPanel.disableButtons();
}
});
}
public class EventManagementPanel extends JPanel
{
private static final long serialVersionUID = 1L;
private EventFilterButton<IDecodeEvent> mFilterButton;
private EventClearButton mEventClearButton;
public EventManagementPanel()
{
setLayout(new MigLayout("insets 2 2 5 5", "[]5[left,grow]", ""));
initializeFilterButton();
initializeClearButton();
disableButtons();
add(mFilterButton);
add(mEventClearButton);
}
public void enableButtons()
{
mFilterButton.setEnabled(true);
mEventClearButton.setEnabled(true);
}
public void disableButtons()
{
mFilterButton.setEnabled(false);
mEventClearButton.setEnabled(false);
}
private void initializeFilterButton()
{
EventFilterProvider<IDecodeEvent> filterProvider = (EventFilterProvider<IDecodeEvent>) mTable.getModel();
FilterSet<IDecodeEvent> filterSet = new FilterSet<>(new AllPassFilter<>());
if (filterProvider != null) {
filterSet = filterProvider.getFilterSet();
}
mFilterButton = new EventFilterButton<>("Message Filter Editor", filterSet);
}
private void initializeClearButton() {
mEventClearButton = new EventClearButton(
((DecodeEventModel) mTable.getModel()).getMaxMessageCount()
);
mEventClearButton.setEventClearHandler(new EventClearHandler() {
@Override
public void onHistoryLimitChanged(int newHistoryLimit) {
((DecodeEventModel) mTable.getModel()).setMaxMessageCount(newHistoryLimit);
}
@Override
public void onClearHistoryClicked() {
((DecodeEventModel) mTable.getModel()).clear();
}
});
}
}
/**
* Custom cell renderer for displaying identifiers from an identifier collection
*/

View File

@@ -77,6 +77,11 @@ public interface IDecodeEvent
*/
Protocol getProtocol();
/**
* {@link DecodeEventType} for the event produced
*/
DecodeEventType getEventType();
/**
* Timeslot for the event.
* @return timeslot or default of 0

View File

@@ -0,0 +1,16 @@
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
public class DecodeEventFilterSet extends FilterSet<IDecodeEvent> {
public DecodeEventFilterSet() {
super ("All Messages");
addFilter(new DecodedCallEventFilter());
addFilter(new DecodedCallEncryptedEventFilter());
addFilter(new DecodedDataEventFilter());
addFilter(new DecodedCommandEventFilter());
addFilter(new DecodedRegistrationEventFilter());
}
}

View File

@@ -0,0 +1,19 @@
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import java.util.Arrays;
public class DecodedCallEncryptedEventFilter extends EventFilter
{
public DecodedCallEncryptedEventFilter()
{
super("Voice Calls - Encrypted", Arrays.asList(
DecodeEventType.CALL_ENCRYPTED,
DecodeEventType.CALL_GROUP_ENCRYPTED,
DecodeEventType.CALL_PATCH_GROUP_ENCRYPTED,
DecodeEventType.CALL_INTERCONNECT_ENCRYPTED,
DecodeEventType.CALL_UNIT_TO_UNIT_ENCRYPTED
));
}
}

View File

@@ -0,0 +1,25 @@
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import java.util.Arrays;
public class DecodedCallEventFilter extends EventFilter
{
public DecodedCallEventFilter()
{
super("Voice Calls", Arrays.asList(
DecodeEventType.CALL_GROUP,
DecodeEventType.CALL_PATCH_GROUP,
DecodeEventType.CALL_ALERT,
DecodeEventType.CALL_DETECT,
DecodeEventType.CALL_DO_NOT_MONITOR,
DecodeEventType.CALL_END,
DecodeEventType.CALL_INTERCONNECT,
DecodeEventType.CALL_UNIQUE_ID,
DecodeEventType.CALL_UNIT_TO_UNIT,
DecodeEventType.CALL_NO_TUNER,
DecodeEventType.CALL_TIMEOUT
));
}
}

View File

@@ -0,0 +1,24 @@
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import java.util.Arrays;
public class DecodedCommandEventFilter extends EventFilter
{
public DecodedCommandEventFilter()
{
super("Commands", Arrays.asList(
DecodeEventType.ANNOUNCEMENT,
DecodeEventType.STATION_ID,
DecodeEventType.PAGE,
DecodeEventType.QUERY,
DecodeEventType.RADIO_CHECK,
DecodeEventType.STATUS,
DecodeEventType.COMMAND,
DecodeEventType.EMERGENCY,
DecodeEventType.NOTIFICATION,
DecodeEventType.FUNCTION
));
}
}

View File

@@ -0,0 +1,23 @@
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import java.util.Arrays;
public class DecodedDataEventFilter extends EventFilter
{
public DecodedDataEventFilter()
{
super("Data Calls", Arrays.asList(
DecodeEventType.DATA_CALL,
DecodeEventType.DATA_CALL_ENCRYPTED,
DecodeEventType.DATA_PACKET,
DecodeEventType.GPS,
DecodeEventType.IP_PACKET,
DecodeEventType.UDP_PACKET,
DecodeEventType.SDM,
DecodeEventType.ID_ANI,
DecodeEventType.ID_UNIQUE
));
}
}

View File

@@ -0,0 +1,22 @@
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import java.util.Arrays;
public class DecodedRegistrationEventFilter extends EventFilter
{
public DecodedRegistrationEventFilter()
{
super("Registrations", Arrays.asList(
DecodeEventType.AFFILIATE,
DecodeEventType.AUTOMATIC_REGISTRATION_SERVICE,
DecodeEventType.REGISTER,
DecodeEventType.REGISTER_ESN,
DecodeEventType.DEREGISTER,
DecodeEventType.REQUEST,
DecodeEventType.RESPONSE,
DecodeEventType.RESPONSE_PACKET
));
}
}

View File

@@ -0,0 +1,101 @@
package io.github.dsheirer.module.decode.event.filter;
import com.jidesoft.swing.JideSplitButton;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class EventClearButton extends JideSplitButton
{
private static final long serialVersionUID = 1L;
private EventClearHandler mEventClearHandler;
public EventClearButton(int maxHistoryCount)
{
super("Clear");
JPanel historyPanel = new JPanel();
historyPanel.add(new JLabel("History Entries:"));
final JSlider historySlider = initializeHistorySlider();
historySlider.setValue(maxHistoryCount);
historySlider.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent arg0)
{
if (mEventClearHandler != null)
{
mEventClearHandler.onHistoryLimitChanged(historySlider.getValue());
}
}
});
historyPanel.add(historySlider);
add(historyPanel);
/* This handles the click action on the main button. Clear messages */
addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
if (mEventClearHandler != null)
{
mEventClearHandler.onClearHistoryClicked();
}
}
});
}
public void setEventClearHandler(EventClearHandler eventClearHandler) {
this.mEventClearHandler = eventClearHandler;
}
private JSlider initializeHistorySlider()
{
final JSlider slider = new JSlider();
slider.setMinimum(0);
slider.setMaximum(2000);
slider.setMajorTickSpacing(500);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.addMouseListener(new MouseListener()
{
@Override
public void mouseClicked(MouseEvent arg0)
{
if(SwingUtilities.isLeftMouseButton(arg0) && arg0.getClickCount() == 2)
{
slider.setValue(500); //default
}
}
public void mouseEntered(MouseEvent arg0)
{
}
public void mouseExited(MouseEvent arg0)
{
}
public void mousePressed(MouseEvent arg0)
{
}
public void mouseReleased(MouseEvent arg0)
{
}
});
return slider;
}
}

View File

@@ -0,0 +1,6 @@
package io.github.dsheirer.module.decode.event.filter;
public interface EventClearHandler {
void onHistoryLimitChanged(int newHistoryLimit);
void onClearHistoryClicked();
}

View File

@@ -0,0 +1,56 @@
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
/**
* This is used as a base for {@link IDecodeEvent} types.
* This will take a list of {@link IDecodeEvent}s and generate filters for the list.
* This will default canProcess to TRUE. Override if other value or evaluation is needed.
*
*/
public class EventFilter extends Filter<IDecodeEvent>
{
private Map<DecodeEventType, FilterElement<DecodeEventType>> mElements = new EnumMap<>(DecodeEventType.class);
public EventFilter(String name, List<DecodeEventType> eventTypes)
{
super(name);
for (DecodeEventType eventType : eventTypes) {
mElements.put(eventType, new FilterElement<>(eventType));
}
}
@Override
public boolean passes(IDecodeEvent iDecodeEvent)
{
if (mEnabled && canProcess(iDecodeEvent))
{
if (mElements.containsKey(iDecodeEvent.getEventType()))
{
return mElements.get(iDecodeEvent.getEventType()).isEnabled();
}
}
return false;
}
@Override
public boolean canProcess(IDecodeEvent iDecodeEvent)
{
return true;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<>(mElements.values());
}
}

View File

@@ -0,0 +1,84 @@
package io.github.dsheirer.module.decode.event.filter;
import com.jidesoft.swing.JideButton;
import io.github.dsheirer.filter.FilterEditorPanel;
import io.github.dsheirer.filter.FilterSet;
import net.miginfocom.swing.MigLayout;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class EventFilterButton<T> extends JideButton
{
private static final long serialVersionUID = 1L;
public EventFilterButton(String dialogTitle, FilterSet<T> filterSet)
{
this("Filter", dialogTitle, filterSet);
}
public EventFilterButton(String buttonLabel, String dialogTitle, FilterSet<T> filterSet)
{
super(buttonLabel);
addActionListener(
new EventFilterActionHandler(dialogTitle, filterSet)
);
}
public class EventFilterActionHandler implements ActionListener
{
private String title;
private FilterSet<T> filterSet;
public EventFilterActionHandler( String title, FilterSet<T> filterSet)
{
this.title = title;
this.filterSet = filterSet;
}
@Override
public void actionPerformed(ActionEvent e)
{
final JFrame editor = new JFrame();
editor.setTitle(title);
editor.setLocationRelativeTo(EventFilterButton.this);
editor.setSize(600, 400);
editor.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
editor.setLayout(new MigLayout("", "[grow,fill]",
"[grow,fill][][]"));
FilterEditorPanel<T> panel = new FilterEditorPanel<T>(filterSet);
JScrollPane scroller = new JScrollPane(panel);
scroller.setViewportView(panel);
editor.add(scroller, "wrap");
editor.add(new JLabel("Right-click to select/deselect all nodes"), "wrap");
JButton close = new JButton("Close");
close.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
editor.dispose();
}
});
editor.add(close);
EventQueue.invokeLater(new Runnable()
{
public void run()
{
editor.setVisible(true);
}
});
}
}
}

View File

@@ -0,0 +1,8 @@
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.filter.FilterSet;
public interface EventFilterProvider<T> {
FilterSet<T> getFilterSet();
void setFilterSet(FilterSet<T> filterSet);
}