#90 [WIP] on redesigning the channel processing gui components and backing model. Simplifies channel state by introducing a new Metadata class that tracks all metadata for all decoders.

This commit is contained in:
Denny
2017-01-15 08:42:13 -05:00
parent 7115b4feb1
commit 16b69d4aef
12 changed files with 1256 additions and 757 deletions

View File

@@ -34,8 +34,10 @@ public class AliasedIdentifier
/**
* Resets alias and value to null
*
* Package Private - should only be changed within MutableMetadata
*/
public void reset()
void reset()
{
mIdentifier = null;
mAlias = null;
@@ -51,8 +53,10 @@ public class AliasedIdentifier
/**
* Sets the identifier to the argument value
*
* Package Private - should only be changed within MutableMetadata
*/
public void setIdentifier(String identifier)
void setIdentifier(String identifier)
{
mIdentifier = identifier;
}
@@ -75,8 +79,10 @@ public class AliasedIdentifier
/**
* Sets the alias to the argument
*
* Package Private - should only be changed within MutableMetadata
*/
public void setAlias(Alias alias)
void setAlias(Alias alias)
{
mAlias = alias;
}
@@ -88,4 +94,14 @@ public class AliasedIdentifier
{
return mAlias != null;
}
public AliasedIdentifier copyOf()
{
AliasedIdentifier copy = new AliasedIdentifier();
copy.setAlias(mAlias);
copy.setIdentifier(new String(mIdentifier));
return copy;
}
}

View File

@@ -27,7 +27,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ChannelMetadataModel extends AbstractTableModel implements Listener<MetadataChangeEvent>
public class ChannelMetadataModel extends AbstractTableModel implements Listener<MutableMetadataChangeEvent>
{
public static final int COLUMN_STATE = 0;
public static final int COLUMN_PRIMARY = 1;
@@ -40,10 +40,10 @@ public class ChannelMetadataModel extends AbstractTableModel implements Listener
private static final String[] COLUMNS =
{"State", "Primary", "Secondary", "Message", "Network", "Frequency", "Configuration"};
private List<Metadata> mChannelMetadata = new ArrayList();
private Map<Metadata,Channel> mMetadataChannelMap = new HashMap();
private List<MutableMetadata> mChannelMetadata = new ArrayList();
private Map<MutableMetadata,Channel> mMetadataChannelMap = new HashMap();
public void add(Metadata metadata, Channel channel)
public void add(MutableMetadata metadata, Channel channel)
{
mChannelMetadata.add(metadata);
mMetadataChannelMap.put(metadata, channel);
@@ -55,7 +55,7 @@ public class ChannelMetadataModel extends AbstractTableModel implements Listener
metadata.addListener(this);
}
public void remove(Metadata metadata)
public void remove(MutableMetadata metadata)
{
metadata.removeListener(this);
@@ -88,7 +88,7 @@ public class ChannelMetadataModel extends AbstractTableModel implements Listener
@Override
public Class<?> getColumnClass(int columnIndex)
{
return Metadata.class;
return MutableMetadata.class;
}
@Override
@@ -98,13 +98,13 @@ public class ChannelMetadataModel extends AbstractTableModel implements Listener
}
@Override
public void receive(MetadataChangeEvent metadataChangeEvent)
public void receive(MutableMetadataChangeEvent mutableMetadataChangeEvent)
{
int rowIndex = mChannelMetadata.indexOf(metadataChangeEvent.getMetadata());
int rowIndex = mChannelMetadata.indexOf(mutableMetadataChangeEvent.getMetadata());
if(rowIndex >= 0)
{
switch(metadataChangeEvent.getAttribute())
switch(mutableMetadataChangeEvent.getAttribute())
{
case CHANNEL_CONFIGURATION_LABEL_1:
case CHANNEL_CONFIGURATION_LABEL_2:

View File

@@ -24,7 +24,6 @@ import net.miginfocom.swing.MigLayout;
import javax.swing.*;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import java.awt.*;
import java.text.DecimalFormat;
@@ -120,7 +119,7 @@ public class ChannelMetadataPanel extends JPanel
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column)
{
Metadata metadata = (Metadata)value;
MutableMetadata metadata = (MutableMetadata)value;
if(mAttribute1 != null)
{
@@ -187,7 +186,7 @@ public class ChannelMetadataPanel extends JPanel
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column)
{
Metadata metadata = (Metadata)value;
MutableMetadata metadata = (MutableMetadata)value;
if(metadata.hasChannelFrequency())
{
@@ -198,7 +197,7 @@ public class ChannelMetadataPanel extends JPanel
mLabel1.setText(null);
}
mLabel2.setText(metadata.getChannelID());
mLabel2.setText(metadata.getChannelFrequencyLabel());
setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
@@ -224,7 +223,7 @@ public class ChannelMetadataPanel extends JPanel
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column)
{
Metadata metadata = (Metadata)value;
MutableMetadata metadata = (MutableMetadata)value;
mLabel1.setText(metadata.getState().getDisplayValue());

View File

@@ -0,0 +1,26 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.metadata;
import sample.Listener;
public interface IAttributeChangeRequestListener
{
public Listener<AttributeChangeRequest> getAttributeChangeRequestListener();
}

View File

@@ -0,0 +1,28 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.metadata;
import sample.Listener;
public interface IAttributeChangeRequestProvider
{
public void setAttributeChangeRequestListener(Listener<AttributeChangeRequest> listener);
public void removeAttributeChangeRequestListener();
}

View File

@@ -19,234 +19,187 @@
package channel.metadata;
import alias.Alias;
import alias.id.broadcast.BroadcastChannel;
import alias.id.priority.Priority;
import channel.state.State;
import sample.Broadcaster;
import sample.Listener;
import module.decode.DecoderType;
public class Metadata implements Listener<AttributeChangeRequest>
import java.util.ArrayList;
import java.util.List;
public class Metadata
{
private State mState = State.IDLE;
private String mChannelConfigurationLabel1;
private String mChannelConfigurationLabel2;
private String mChannelID;
private long mChannelFrequency;
private AliasedIdentifier mNetworkID1 = new AliasedIdentifier();
private AliasedIdentifier mNetworkID2 = new AliasedIdentifier();
// Static unique metadata identifier tracking
private static int UNIQUE_METADATA_ID_GENERATOR = 0;
//Temporal attributes
private String mMessage;
private String mMessageType;
private AliasedIdentifier mPrimaryAddressFrom = new AliasedIdentifier();
private AliasedIdentifier mPrimaryAddressTo = new AliasedIdentifier();
private AliasedIdentifier mSecondaryAddressFrom = new AliasedIdentifier();
private AliasedIdentifier mSecondaryAddressTo = new AliasedIdentifier();
private int mMetadataID;
private Broadcaster<MetadataChangeEvent> mMetadataChangeEventBroadcaster = new Broadcaster<>();
protected boolean mUpdated;
protected DecoderType mPrimaryDecoderType;
protected boolean mSelected;
protected State mState = State.IDLE;
protected String mChannelConfigurationLabel1;
protected String mChannelConfigurationLabel2;
protected String mChannelFrequencyLabel;
protected long mChannelFrequency;
protected AliasedIdentifier mNetworkID1 = new AliasedIdentifier();
protected AliasedIdentifier mNetworkID2 = new AliasedIdentifier();
protected String mMessage;
protected String mMessageType;
protected AliasedIdentifier mPrimaryAddressFrom = new AliasedIdentifier();
protected AliasedIdentifier mPrimaryAddressTo = new AliasedIdentifier();
protected AliasedIdentifier mSecondaryAddressFrom = new AliasedIdentifier();
protected AliasedIdentifier mSecondaryAddressTo = new AliasedIdentifier();
//Lazily constructed member variables.
private Integer mAudioPriority;
private Boolean mRecordable;
private List<BroadcastChannel> mBroadcastChannels;
/**
* Channel metadata. Contains all attributes that reflect the state and current attribute values for a channel
* that is currently decoding. This metadata is intended to support any decoding channel gui components to
* graphically convey the current state of a decoding channel.
* graphically convey the current state of a decoding channel and to provide audio metadata
*/
public Metadata()
{
mMetadataID = UNIQUE_METADATA_ID_GENERATOR++;
}
/**
* Sets the state of this metadata
* Protected constructor. Primarily used by the copyOf() method to create a metadata copy with the same ID
*/
public void setState(State state)
protected Metadata(int metadataID)
{
mState = state;
broadcast(Attribute.CHANNEL_STATE);
mMetadataID = metadataID;
}
/**
* Request to change an attribute value for this metadata. This is the primary method for updating all attributes
* in this metadata set.
* Unique string identifier for this metadata that is comprised of the channel ID and the primary TO address.
*
* This identifier can be used to uniquely identify a channel audio stream and the primary communicant.
*/
@Override
public void receive(AttributeChangeRequest request)
public String getUniqueIdentifier()
{
switch(request.getAttribute())
{
case CHANNEL_CONFIGURATION_LABEL_1:
mChannelConfigurationLabel1 = request.getStringValue();
broadcast(Attribute.CHANNEL_CONFIGURATION_LABEL_1);
break;
case CHANNEL_CONFIGURATION_LABEL_2:
mChannelConfigurationLabel2 = request.getStringValue();
broadcast(Attribute.CHANNEL_CONFIGURATION_LABEL_2);
break;
case CHANNEL_FREQUENCY:
if(request.hasValue())
{
mChannelFrequency = request.getLongValue();
}
else
{
mChannelFrequency = 0;
}
broadcast(Attribute.CHANNEL_FREQUENCY);
break;
case CHANNEL_ID:
mChannelID = request.getStringValue();
broadcast(Attribute.CHANNEL_ID);
break;
case MESSAGE:
mMessage = request.getStringValue();
broadcast(Attribute.MESSAGE);
break;
case MESSAGE_TYPE:
mMessageType = request.getStringValue();
broadcast(Attribute.MESSAGE_TYPE);
break;
case NETWORK_ID_1:
mNetworkID1.setIdentifier(request.getStringValue());
mNetworkID1.setAlias(request.getAlias());
broadcast(Attribute.NETWORK_ID_1);
break;
case NETWORK_ID_2:
mNetworkID2.setIdentifier(request.getStringValue());
mNetworkID2.setAlias(request.getAlias());
broadcast(Attribute.NETWORK_ID_2);
break;
case PRIMARY_ADDRESS_FROM:
mPrimaryAddressFrom.setIdentifier(request.getStringValue());
mPrimaryAddressFrom.setAlias(request.getAlias());
broadcast(Attribute.PRIMARY_ADDRESS_FROM);
break;
case PRIMARY_ADDRESS_TO:
mPrimaryAddressTo.setIdentifier(request.getStringValue());
mPrimaryAddressTo.setAlias(request.getAlias());
broadcast(Attribute.PRIMARY_ADDRESS_TO);
break;
case SECONDARY_ADDRESS_FROM:
mSecondaryAddressFrom.setIdentifier(request.getStringValue());
mSecondaryAddressFrom.setAlias(request.getAlias());
broadcast(Attribute.SECONDARY_ADDRESS_FROM);
break;
case SECONDARY_ADDRESS_TO:
mSecondaryAddressTo.setIdentifier(request.getStringValue());
mSecondaryAddressTo.setAlias(request.getAlias());
broadcast(Attribute.SECONDARY_ADDRESS_TO);
break;
default:
throw new IllegalArgumentException("Unrecognized Metadata Attribute: " +
request.getAttribute().name());
}
return "SRC:" + mMetadataID +
" ID:" + (mPrimaryAddressTo.hasIdentifier() ? mPrimaryAddressTo.getIdentifier() : "UNKNOWN");
}
/**
* Broadcasts to registered listeners that an attribute has changed for this metadata
* Indicates if any of the fields of this metadata have been updated since the last time a copy was made from this
* metadata. This method is primarily used by downstream audio playback and audio recording to signal when changes
* are made to the metadata that requires the downstream component to reinspect the metadata.
*
* This flag is reset to false immediately after a copy is made of this metadata via the copyOf() method.
*/
private void broadcast(Attribute attribute)
public boolean isUpdated()
{
mMetadataChangeEventBroadcaster.broadcast(new MetadataChangeEvent(this, attribute));
return mUpdated;
}
/**
* Adds the listener to receive metadata change events for this channel metadata
* Audio Priority as the highest audio priority value from across the primary and secondary identifier aliases.
*
* A default priority will be used if there are no aliased values. If there is a conflict between audio priority
* and 'Do Not Monitor' among the aliases, the highest audio priority will be used (prefer to monitor).
*
* -1 Do Not Monitor
* 0 Channel Selected for immediate monitor
* 1 Highest Priority
* 100 Lowest Priority (default)
*
* @return audio priority
*/
public void addListener(Listener<MetadataChangeEvent> listener)
public int getAudioPriority()
{
mMetadataChangeEventBroadcaster.addListener(listener);
if(isSelected())
{
return Priority.SELECTED_PRIORITY;
}
if(mAudioPriority == null)
{
determineAudioPriority();
}
return mAudioPriority;
}
/**
* Removes the listener from receiving metadata change events for this channel metadata
* Indicates if the audio priority is set to 'Do Not Monitor'
*/
public void removeListener(Listener<MetadataChangeEvent> listener)
public boolean isDoNotMonitor()
{
mMetadataChangeEventBroadcaster.removeListener(listener);
return getAudioPriority() == Priority.DO_NOT_MONITOR;
}
/**
* Resets temporal attributes to null value. This only resets attributes that have a non-ull value in order to
* limit the number of change events that are produced.
* Indicates if any of the primary or secondary TO/FROM aliases are identified as recordable
*/
public void resetTemporalAttributes()
public boolean isRecordable()
{
if(hasMessage())
if(mRecordable == null)
{
mMessage = null;
broadcast(Attribute.MESSAGE);
determineRecordable();
}
if(hasMessageType())
{
mMessageType = null;
broadcast(Attribute.MESSAGE_TYPE);
}
if(mPrimaryAddressFrom.hasIdentifier())
{
mPrimaryAddressFrom.reset();
broadcast(Attribute.PRIMARY_ADDRESS_FROM);
}
if(mPrimaryAddressTo.hasIdentifier())
{
mPrimaryAddressTo.reset();
broadcast(Attribute.PRIMARY_ADDRESS_TO);
}
if(mSecondaryAddressFrom.hasIdentifier())
{
mSecondaryAddressFrom.reset();
broadcast(Attribute.SECONDARY_ADDRESS_FROM);
}
if(mSecondaryAddressTo.hasIdentifier())
{
mSecondaryAddressTo.reset();
broadcast(Attribute.SECONDARY_ADDRESS_TO);
}
return mRecordable;
}
/**
* Resets all attributes to empty values
* Indicates that this metadata has been selected by the user and any audio produced by this channel will be set
* to the highest playback priority (immediate monitor).
*/
public void resetAllAttributes()
public boolean isSelected()
{
resetTemporalAttributes();
return mSelected;
}
if(hasChannelConfigurationLabel1())
/**
* List of de-duplicated broadcast channels aggregated from the primary and secondary TO/FROM aliases.
*/
public List<BroadcastChannel> getBroadcastChannels()
{
if(mBroadcastChannels == null)
{
mChannelConfigurationLabel1 = null;
broadcast(Attribute.CHANNEL_CONFIGURATION_LABEL_1);
determineBroadcastChannels();
}
if(hasChannelConfigurationLabel2())
{
mChannelConfigurationLabel2 = null;
broadcast(Attribute.CHANNEL_CONFIGURATION_LABEL_2);
}
return mBroadcastChannels;
}
if(hasChannelID())
{
mChannelID = null;
broadcast(Attribute.CHANNEL_ID);
}
/**
* Indicates if this metadata is streamable, meaning that it contains one ore more broadcast channels
*/
public boolean isStreamable()
{
return !getBroadcastChannels().isEmpty();
}
if(hasChannelFrequency())
{
mChannelFrequency = 0;
broadcast(Attribute.CHANNEL_FREQUENCY);
}
/**
* Unique identifier for the source channel that produces this metadata
*/
public int getMetadataID()
{
return mMetadataID;
}
if(mNetworkID1.hasIdentifier())
{
mNetworkID1.reset();
broadcast(Attribute.NETWORK_ID_1);
}
/**
* Decoder type (ie protocol) for the primary decoder
*/
public DecoderType getPrimaryDecoderType()
{
return mPrimaryDecoderType;
}
if(mNetworkID2.hasIdentifier())
{
mNetworkID2.reset();
broadcast(Attribute.NETWORK_ID_2);
}
/**
* Indicates if this metadata has a primary decoder type defined
*/
public boolean hasPrimaryDecoderType()
{
return mPrimaryDecoderType != null;
}
/**
@@ -285,7 +238,7 @@ public class Metadata implements Listener<AttributeChangeRequest>
case CHANNEL_CONFIGURATION_LABEL_2:
return mChannelConfigurationLabel2;
case CHANNEL_ID:
return mChannelID;
return mChannelFrequencyLabel;
case CHANNEL_STATE:
return mState.getDisplayValue();
case MESSAGE:
@@ -352,17 +305,17 @@ public class Metadata implements Listener<AttributeChangeRequest>
/**
* Channel ID or Channel Number
*/
public String getChannelID()
public String getChannelFrequencyLabel()
{
return mChannelID;
return mChannelFrequencyLabel;
}
/**
* Indicates if there is a non-null, non-empty channel identifier value
*/
public boolean hasChannelID()
public boolean hasChannelFrequencyLabel()
{
return mChannelID != null && !mChannelID.isEmpty();
return mChannelFrequencyLabel != null && !mChannelFrequencyLabel.isEmpty();
}
/**
@@ -460,4 +413,232 @@ public class Metadata implements Listener<AttributeChangeRequest>
{
return mSecondaryAddressTo;
}
/**
* Determines the audio priority from across the primary and secondary TO/FROM aliases when they exist
*/
private void determineAudioPriority()
{
int priority = Priority.DEFAULT_PRIORITY;
boolean doNotMonitorFound = false;
boolean audioPriorityFound = false;
if(getPrimaryAddressTo().hasAlias())
{
Alias primaryTo = getPrimaryAddressTo().getAlias();
if(primaryTo.hasCallPriority())
{
if(primaryTo.getCallPriority() == Priority.DO_NOT_MONITOR)
{
doNotMonitorFound = true;
}
else
{
audioPriorityFound = true;
if(primaryTo.getCallPriority() < priority)
{
priority = primaryTo.getCallPriority();
}
}
}
}
if(getPrimaryAddressFrom().hasAlias())
{
Alias primaryFrom = getPrimaryAddressFrom().getAlias();
if(primaryFrom.hasCallPriority())
{
if(primaryFrom.getCallPriority() == Priority.DO_NOT_MONITOR)
{
doNotMonitorFound = true;
}
else
{
audioPriorityFound = true;
if(primaryFrom.getCallPriority() < priority)
{
priority = primaryFrom.getCallPriority();
}
}
}
}
if(getSecondaryAddressTo().hasAlias())
{
Alias secondaryTo = getSecondaryAddressTo().getAlias();
if(secondaryTo.hasCallPriority())
{
if(secondaryTo.getCallPriority() == Priority.DO_NOT_MONITOR)
{
doNotMonitorFound = true;
}
else
{
audioPriorityFound = true;
if(secondaryTo.getCallPriority() < priority)
{
priority = secondaryTo.getCallPriority();
}
}
}
}
if(getSecondaryAddressFrom().hasAlias())
{
Alias secondaryFrom = getSecondaryAddressFrom().getAlias();
if(secondaryFrom.hasCallPriority())
{
if(secondaryFrom.getCallPriority() == Priority.DO_NOT_MONITOR)
{
doNotMonitorFound = true;
}
else
{
audioPriorityFound = true;
if(secondaryFrom.getCallPriority() < priority)
{
priority = secondaryFrom.getCallPriority();
}
}
}
}
//If we found a 'Do Not Monitor' and no other audio priority, then set to do not monitor
if(doNotMonitorFound && !audioPriorityFound)
{
mAudioPriority = Priority.DO_NOT_MONITOR;
}
else
{
mAudioPriority = priority;
}
}
/**
* Determines if any of the primary or secondary to/from aliases are recordable.
*/
private void determineRecordable()
{
mRecordable = false;
if(mPrimaryAddressTo.hasAlias() && mPrimaryAddressTo.getAlias().isRecordable())
{
mRecordable = true;
return;
}
if(mPrimaryAddressFrom.hasAlias() && mPrimaryAddressFrom.getAlias().isRecordable())
{
mRecordable = true;
return;
}
if(mSecondaryAddressTo.hasAlias() && mSecondaryAddressTo.getAlias().isRecordable())
{
mRecordable = true;
return;
}
if(mSecondaryAddressFrom.hasAlias() && mSecondaryAddressFrom.getAlias().isRecordable())
{
mRecordable = true;
return;
}
}
/**
* Aggregates a de-duplicated list of broadcast channels from among the primary/secondary to/from aliases
*/
private void determineBroadcastChannels()
{
mBroadcastChannels = new ArrayList<>();
if(mPrimaryAddressTo.hasAlias() && mPrimaryAddressTo.getAlias().isStreamable())
{
for(BroadcastChannel broadcastChannel: mPrimaryAddressTo.getAlias().getBroadcastChannels())
{
if(!mBroadcastChannels.contains(broadcastChannel))
{
mBroadcastChannels.add(broadcastChannel);
}
}
}
if(mPrimaryAddressFrom.hasAlias() && mPrimaryAddressFrom.getAlias().isStreamable())
{
for(BroadcastChannel broadcastChannel: mPrimaryAddressFrom.getAlias().getBroadcastChannels())
{
if(!mBroadcastChannels.contains(broadcastChannel))
{
mBroadcastChannels.add(broadcastChannel);
}
}
}
if(mSecondaryAddressTo.hasAlias() && mSecondaryAddressTo.getAlias().isStreamable())
{
for(BroadcastChannel broadcastChannel: mSecondaryAddressTo.getAlias().getBroadcastChannels())
{
if(!mBroadcastChannels.contains(broadcastChannel))
{
mBroadcastChannels.add(broadcastChannel);
}
}
}
if(mSecondaryAddressFrom.hasAlias() && mSecondaryAddressFrom.getAlias().isStreamable())
{
for(BroadcastChannel broadcastChannel: mSecondaryAddressFrom.getAlias().getBroadcastChannels())
{
if(!mBroadcastChannels.contains(broadcastChannel))
{
mBroadcastChannels.add(broadcastChannel);
}
}
}
}
/**
* Creates a deep/full snapshot copy of this metadata set.
*
* The streamable, recordable, audio priority and broadcast channels are not copied. These are lazily determined
* when initially requested.
*/
public Metadata copyOf()
{
Metadata copy = new Metadata(mMetadataID);
copy.mState = mState;
copy.mPrimaryDecoderType = mPrimaryDecoderType;
copy.mChannelFrequency = mChannelFrequency;
copy.mChannelConfigurationLabel1 = hasChannelConfigurationLabel1() ? new String(mChannelConfigurationLabel1) : null;
copy.mChannelConfigurationLabel2 = hasChannelConfigurationLabel2() ? new String(mChannelConfigurationLabel2) : null;
copy.mChannelFrequencyLabel = hasChannelFrequencyLabel() ? new String(mChannelFrequencyLabel) : null;
copy.mMessage = hasMessage() ? new String(mMessage) : null;
copy.mMessageType = hasMessageType() ? new String(mMessageType) : null;
copy.mNetworkID1 = mNetworkID1.copyOf();
copy.mNetworkID2 = mNetworkID2.copyOf();
copy.mPrimaryAddressFrom = mPrimaryAddressFrom.copyOf();
copy.mPrimaryAddressTo = mPrimaryAddressTo.copyOf();
copy.mSecondaryAddressFrom = mSecondaryAddressFrom.copyOf();
copy.mSecondaryAddressTo = mSecondaryAddressTo.copyOf();
copy.mUpdated = mUpdated;
//Reset the updated flag
mUpdated = false;
return copy;
}
}

View File

@@ -0,0 +1,251 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.metadata;
import channel.state.State;
import module.decode.DecoderType;
import sample.Broadcaster;
import sample.Listener;
public class MutableMetadata extends Metadata implements Listener<AttributeChangeRequest>
{
private Broadcaster<MutableMetadataChangeEvent> mMetadataChangeEventBroadcaster = new Broadcaster<>();
/**
* Mutable channel metadata. Contains all attributes that reflect the state and current attribute values for a
* channel that is currently decoding. This metadata is intended to support any decoding channel gui components to
* graphically convey the current state of a decoding channel and to provide audio metadata
*/
public MutableMetadata()
{
super();
}
/**
* Sets the selected state for this metadata. Selected indicates that the user has selected this metadata for
* immediate monitor in the audio panel. When true, this overrides the audio priority for any audio produced
* by the channel and sets that audio to the highest priority.
*
* Any component that toggles the selected state for this metadata is also responsible for clearing the flag.
*/
public void setSelected(boolean selected)
{
mSelected = selected;
}
/**
* Sets the primary decoder type
*/
public void setPrimaryDecoderType(DecoderType decoderType)
{
mPrimaryDecoderType = decoderType;
}
/**
* Request to change an attribute value for this metadata. This is the primary method for updating all attributes
* in this metadata set.
*/
@Override
public void receive(AttributeChangeRequest request)
{
switch(request.getAttribute())
{
case CHANNEL_CONFIGURATION_LABEL_1:
mChannelConfigurationLabel1 = request.getStringValue();
broadcast(Attribute.CHANNEL_CONFIGURATION_LABEL_1);
break;
case CHANNEL_CONFIGURATION_LABEL_2:
mChannelConfigurationLabel2 = request.getStringValue();
broadcast(Attribute.CHANNEL_CONFIGURATION_LABEL_2);
break;
case CHANNEL_FREQUENCY:
if(request.hasValue())
{
mChannelFrequency = request.getLongValue();
}
else
{
mChannelFrequency = 0;
}
broadcast(Attribute.CHANNEL_FREQUENCY);
break;
case CHANNEL_ID:
mChannelFrequencyLabel = request.getStringValue();
broadcast(Attribute.CHANNEL_ID);
break;
case MESSAGE:
mMessage = request.getStringValue();
broadcast(Attribute.MESSAGE);
break;
case MESSAGE_TYPE:
mMessageType = request.getStringValue();
broadcast(Attribute.MESSAGE_TYPE);
break;
case NETWORK_ID_1:
mNetworkID1.setIdentifier(request.getStringValue());
mNetworkID1.setAlias(request.getAlias());
broadcast(Attribute.NETWORK_ID_1);
break;
case NETWORK_ID_2:
mNetworkID2.setIdentifier(request.getStringValue());
mNetworkID2.setAlias(request.getAlias());
broadcast(Attribute.NETWORK_ID_2);
break;
case PRIMARY_ADDRESS_FROM:
mPrimaryAddressFrom.setIdentifier(request.getStringValue());
mPrimaryAddressFrom.setAlias(request.getAlias());
broadcast(Attribute.PRIMARY_ADDRESS_FROM);
break;
case PRIMARY_ADDRESS_TO:
mPrimaryAddressTo.setIdentifier(request.getStringValue());
mPrimaryAddressTo.setAlias(request.getAlias());
broadcast(Attribute.PRIMARY_ADDRESS_TO);
break;
case SECONDARY_ADDRESS_FROM:
mSecondaryAddressFrom.setIdentifier(request.getStringValue());
mSecondaryAddressFrom.setAlias(request.getAlias());
broadcast(Attribute.SECONDARY_ADDRESS_FROM);
break;
case SECONDARY_ADDRESS_TO:
mSecondaryAddressTo.setIdentifier(request.getStringValue());
mSecondaryAddressTo.setAlias(request.getAlias());
broadcast(Attribute.SECONDARY_ADDRESS_TO);
break;
case CHANNEL_STATE:
mState = (State)request.getValue();
broadcast(Attribute.CHANNEL_STATE);
break;
default:
throw new IllegalArgumentException("Unrecognized Metadata Attribute: " +
request.getAttribute().name());
}
}
/**
* Resets temporal attributes to null value. This only resets attributes that have a non-ull value in order to
* limit the number of change events that are produced.
*/
public void resetTemporalAttributes()
{
if(hasMessage())
{
mMessage = null;
broadcast(Attribute.MESSAGE);
}
if(hasMessageType())
{
mMessageType = null;
broadcast(Attribute.MESSAGE_TYPE);
}
if(mPrimaryAddressFrom.hasIdentifier())
{
mPrimaryAddressFrom.reset();
broadcast(Attribute.PRIMARY_ADDRESS_FROM);
}
if(mPrimaryAddressTo.hasIdentifier())
{
mPrimaryAddressTo.reset();
broadcast(Attribute.PRIMARY_ADDRESS_TO);
}
if(mSecondaryAddressFrom.hasIdentifier())
{
mSecondaryAddressFrom.reset();
broadcast(Attribute.SECONDARY_ADDRESS_FROM);
}
if(mSecondaryAddressTo.hasIdentifier())
{
mSecondaryAddressTo.reset();
broadcast(Attribute.SECONDARY_ADDRESS_TO);
}
}
/**
* Resets all attributes to empty values
*/
public void resetAllAttributes()
{
resetTemporalAttributes();
if(hasChannelConfigurationLabel1())
{
mChannelConfigurationLabel1 = null;
broadcast(Attribute.CHANNEL_CONFIGURATION_LABEL_1);
}
if(hasChannelConfigurationLabel2())
{
mChannelConfigurationLabel2 = null;
broadcast(Attribute.CHANNEL_CONFIGURATION_LABEL_2);
}
if(hasChannelFrequencyLabel())
{
mChannelFrequencyLabel = null;
broadcast(Attribute.CHANNEL_ID);
}
if(hasChannelFrequency())
{
mChannelFrequency = 0;
broadcast(Attribute.CHANNEL_FREQUENCY);
}
if(mNetworkID1.hasIdentifier())
{
mNetworkID1.reset();
broadcast(Attribute.NETWORK_ID_1);
}
if(mNetworkID2.hasIdentifier())
{
mNetworkID2.reset();
broadcast(Attribute.NETWORK_ID_2);
}
}
/**
* Broadcasts to registered listeners that an attribute has changed for this metadata
*/
private void broadcast(Attribute attribute)
{
mMetadataChangeEventBroadcaster.broadcast(new MutableMetadataChangeEvent(this, attribute));
}
/**
* Adds the listener to receive metadata change events for this channel metadata
*/
public void addListener(Listener<MutableMetadataChangeEvent> listener)
{
mMetadataChangeEventBroadcaster.addListener(listener);
}
/**
* Removes the listener from receiving metadata change events for this channel metadata
*/
public void removeListener(Listener<MutableMetadataChangeEvent> listener)
{
mMetadataChangeEventBroadcaster.removeListener(listener);
}
}

View File

@@ -18,9 +18,9 @@
******************************************************************************/
package channel.metadata;
public class MetadataChangeEvent
public class MutableMetadataChangeEvent
{
private Metadata mMetadata;
private MutableMetadata mMetadata;
private Attribute mAttribute;
/**
@@ -29,7 +29,7 @@ public class MetadataChangeEvent
* @param metadata where the change occurred
* @param attribute that changed
*/
public MetadataChangeEvent(Metadata metadata, Attribute attribute)
public MutableMetadataChangeEvent(MutableMetadata metadata, Attribute attribute)
{
mMetadata = metadata;
mAttribute = attribute;
@@ -38,7 +38,7 @@ public class MetadataChangeEvent
/**
* Channel metadata where the change occurred
*/
public Metadata getMetadata()
public MutableMetadata getMetadata()
{
return mMetadata;
}