mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-09 15:17:24 -05:00
1723 Adds call event management and deletion by age or directory size, both options in the user preferences dialog.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2024 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
|
||||
@@ -24,19 +24,19 @@ import io.github.dsheirer.alias.Alias;
|
||||
import io.github.dsheirer.alias.action.AliasActionType;
|
||||
import io.github.dsheirer.alias.action.RecurringAction;
|
||||
import io.github.dsheirer.audio.AudioFormats;
|
||||
import io.github.dsheirer.gui.preference.playback.ToneFrequency;
|
||||
import io.github.dsheirer.gui.preference.playback.ToneUtil;
|
||||
import io.github.dsheirer.gui.preference.playback.ToneVolume;
|
||||
import io.github.dsheirer.gui.preference.audio.playback.ToneFrequency;
|
||||
import io.github.dsheirer.gui.preference.audio.playback.ToneUtil;
|
||||
import io.github.dsheirer.gui.preference.audio.playback.ToneVolume;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.Clip;
|
||||
import javax.sound.sampled.DataLine;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
public class BeepAction extends RecurringAction
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
* Copyright (C) 2014-2024 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
|
||||
@@ -27,13 +27,20 @@ import io.github.dsheirer.audio.call.CallRepository;
|
||||
import io.github.dsheirer.audio.call.ICallEventListener;
|
||||
import io.github.dsheirer.audio.playback.AudioPlaybackManager;
|
||||
import io.github.dsheirer.audio.playbackfx.AudioPlaybackController;
|
||||
import io.github.dsheirer.log.LoggingSuppressor;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.preference.retention.AgeUnits;
|
||||
import io.github.dsheirer.preference.retention.RetentionPolicy;
|
||||
import io.github.dsheirer.preference.retention.RetentionPreference;
|
||||
import io.github.dsheirer.record.AudioRecordingManager;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
@@ -44,9 +51,13 @@ import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
@@ -56,6 +67,8 @@ import org.springframework.stereotype.Component;
|
||||
public class AudioManager implements Listener<AudioSegment>
|
||||
{
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AudioManager.class);
|
||||
private static final LoggingSuppressor LOGGING_SUPPRESSOR = new LoggingSuppressor(LOGGER);
|
||||
private static final long GIGABYTES = 1024l * 1024l * 1024l;
|
||||
|
||||
@Resource
|
||||
private AudioPlaybackManager mAudioPlaybackManager;
|
||||
@@ -71,8 +84,10 @@ public class AudioManager implements Listener<AudioSegment>
|
||||
private UserPreferences mUserPreferences;
|
||||
private CallRepository mCallRepository;
|
||||
private List<ICallEventListener> mCallListeners = new CopyOnWriteArrayList<>();
|
||||
private ActiveAudioSegmentProcessor mProcessor = new ActiveAudioSegmentProcessor();
|
||||
private ScheduledFuture<?> mProcessorFuture;
|
||||
private ActiveAudioSegmentProcessor mAudioSegmentProcessor = new ActiveAudioSegmentProcessor();
|
||||
private CallAgeOffProcessor mCallAgeOffProcessor = new CallAgeOffProcessor();
|
||||
private ScheduledFuture<?> mAudioSegmentProcessorFuture;
|
||||
private ScheduledFuture<?> mCallRetentionProcessorFuture;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
@@ -86,7 +101,12 @@ public class AudioManager implements Listener<AudioSegment>
|
||||
@PostConstruct
|
||||
public void postConstruct()
|
||||
{
|
||||
mProcessorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(mProcessor, 100, 500, TimeUnit.MILLISECONDS);
|
||||
LOGGER.info("Post constructing ......");
|
||||
mAudioSegmentProcessorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(mAudioSegmentProcessor, 100,
|
||||
500, TimeUnit.MILLISECONDS);
|
||||
//Delay first run until 1-minute after startup.
|
||||
mCallRetentionProcessorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(mCallAgeOffProcessor,
|
||||
30, 30, TimeUnit.SECONDS);
|
||||
|
||||
//Register call event listeners
|
||||
add(mAudioRecordingManager);
|
||||
@@ -97,13 +117,20 @@ public class AudioManager implements Listener<AudioSegment>
|
||||
@PreDestroy
|
||||
public void preDestroy()
|
||||
{
|
||||
if(mProcessorFuture != null)
|
||||
{
|
||||
mProcessorFuture.cancel(true);
|
||||
mProcessorFuture = null;
|
||||
}
|
||||
LOGGER.info("AudioManager shutting down");
|
||||
mProcessor.shutdown();
|
||||
|
||||
if(mAudioSegmentProcessorFuture != null)
|
||||
{
|
||||
mAudioSegmentProcessorFuture.cancel(true);
|
||||
mAudioSegmentProcessorFuture = null;
|
||||
}
|
||||
mAudioSegmentProcessor.shutdown();
|
||||
|
||||
if(mCallRetentionProcessorFuture != null)
|
||||
{
|
||||
mCallRetentionProcessorFuture.cancel(true);
|
||||
mCallRetentionProcessorFuture = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,8 +210,8 @@ public class AudioManager implements Listener<AudioSegment>
|
||||
//Let the duplicate call detector process the segment first to detect duplicates.
|
||||
audioSegment.addLease(mDuplicateCallDetector.getClass().toString());
|
||||
mDuplicateCallDetector.receive(audioSegment);
|
||||
audioSegment.addLease(mProcessor.getClass().toString());
|
||||
mProcessor.add(audioSegment);
|
||||
audioSegment.addLease(mAudioSegmentProcessor.getClass().toString());
|
||||
mAudioSegmentProcessor.add(audioSegment);
|
||||
|
||||
//TODO: remove for cleanup
|
||||
// audioSegment.addLease(mAudioPlaybackManager.getClass().toString());
|
||||
@@ -314,4 +341,163 @@ public class AudioManager implements Listener<AudioSegment>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call event retention and age-off processor.
|
||||
*/
|
||||
public class CallAgeOffProcessor implements Runnable
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
RetentionPreference retentionPreference = mUserPreferences.getRetentionPreference();
|
||||
RetentionPolicy policy = retentionPreference.getRetentionPolicy();
|
||||
|
||||
try
|
||||
{
|
||||
if(policy.isAgePolicy())
|
||||
{
|
||||
AgeUnits ageUnits = retentionPreference.getAgeUnits();
|
||||
int ageValue = retentionPreference.getAgeValue();
|
||||
long threshold = System.currentTimeMillis() - ageUnits.getDuration(ageValue);
|
||||
PageRequest requestPage1 = PageRequest.of(0, 100);
|
||||
long start = 0;
|
||||
Page<Call> page = mCallRepository.findCallsByEventTimeBetweenOrderByEventTime(start, threshold, requestPage1);
|
||||
|
||||
while(page.getTotalElements() > 0)
|
||||
{
|
||||
List<Call> calls = page.get().collect(Collectors.toList());
|
||||
|
||||
for(Call call: calls)
|
||||
{
|
||||
//Keep adjusting the start time to 1 millisecond more than the current call so that
|
||||
//if there's an error deleting any given call, we don't get stuck processing it every time
|
||||
start = call.getEventTime() + 1;
|
||||
|
||||
//Only evaluate calls that are complete
|
||||
if(call.isComplete())
|
||||
{
|
||||
boolean deleteFailed = false;
|
||||
File file = new File(call.getFile());
|
||||
if(file.exists())
|
||||
{
|
||||
try
|
||||
{
|
||||
File parentDirectory = file.getParentFile();
|
||||
Files.delete(file.toPath());
|
||||
|
||||
//Delete parent directory if it is now empty
|
||||
if(parentDirectory.exists() && parentDirectory.isDirectory())
|
||||
{
|
||||
if(!Files.list(parentDirectory.toPath()).findFirst().isPresent())
|
||||
{
|
||||
Files.delete(parentDirectory.toPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception io)
|
||||
{
|
||||
LOGGING_SUPPRESSOR.error("Call Event Delete", 5,
|
||||
"Error deleting call event recording: " + call.getFile() +
|
||||
" - " + io.getLocalizedMessage());
|
||||
deleteFailed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!deleteFailed)
|
||||
{
|
||||
mCallRepository.delete(call);
|
||||
broadcastDeleted(call);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
page = mCallRepository.findCallsByEventTimeBetweenOrderByEventTime(start, threshold, requestPage1);
|
||||
}
|
||||
}
|
||||
|
||||
if(policy.isSizePolicy())
|
||||
{
|
||||
Path callsDirectory = mUserPreferences.getDirectoryPreference().getDirectoryCalls();
|
||||
|
||||
if(Files.exists(callsDirectory))
|
||||
{
|
||||
long currentSize = FileUtils.sizeOfDirectory(callsDirectory.toFile());
|
||||
long objectiveSize = retentionPreference.getObjectiveDirectorySize();
|
||||
|
||||
if(currentSize >= objectiveSize)
|
||||
{
|
||||
long start = 0;
|
||||
long now = System.currentTimeMillis();
|
||||
PageRequest pageable = PageRequest.of(0, 100);
|
||||
Page<Call> page = mCallRepository.findCallsByEventTimeBetweenOrderByEventTime(start, now, pageable);
|
||||
|
||||
while(currentSize >= objectiveSize && page.getTotalElements() > 0)
|
||||
{
|
||||
List<Call> calls = page.get().collect(Collectors.toList());
|
||||
|
||||
for(Call call: calls)
|
||||
{
|
||||
//Keep adjusting the start time to 1 millisecond more than the current call so that
|
||||
//if there's an error deleting any given call, we don't get stuck processing it every time
|
||||
start = call.getEventTime() + 1;
|
||||
|
||||
if(currentSize < objectiveSize)
|
||||
{
|
||||
start = now - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
//Only evaluate calls that are complete
|
||||
if(call.isComplete())
|
||||
{
|
||||
boolean deleteFailed = false;
|
||||
File file = new File(call.getFile());
|
||||
if(file.exists())
|
||||
{
|
||||
try
|
||||
{
|
||||
File parentDirectory = file.getParentFile();
|
||||
long fileSize = FileUtils.sizeOf(file);
|
||||
Files.delete(file.toPath());
|
||||
currentSize -= fileSize;
|
||||
|
||||
//Delete parent directory if it is now empty
|
||||
if(parentDirectory.exists() && parentDirectory.isDirectory())
|
||||
{
|
||||
if(!Files.list(parentDirectory.toPath()).findFirst().isPresent())
|
||||
{
|
||||
Files.delete(parentDirectory.toPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception io)
|
||||
{
|
||||
LOGGING_SUPPRESSOR.error("Call Event Delete", 5,
|
||||
"Error deleting call event recording: " + call.getFile() +
|
||||
" - " + io.getLocalizedMessage());
|
||||
deleteFailed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!deleteFailed)
|
||||
{
|
||||
mCallRepository.delete(call);
|
||||
broadcastDeleted(call);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
page = mCallRepository.findCallsByEventTimeBetweenOrderByEventTime(start, now, pageable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
LOGGER.error("Unexpected error while deleting call events using retention policy: " + policy, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
* Copyright (C) 2014-2024 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
|
||||
@@ -59,7 +59,6 @@ public class ActiveAudioSegment
|
||||
private static final LoggingSuppressor LOGGING_SUPPRESSOR = new LoggingSuppressor(LOGGER);
|
||||
private static final SimpleDateFormat DIRECTORY_SDF = new SimpleDateFormat("yyyyMMdd");
|
||||
private static final SimpleDateFormat FILE_NAME_SDF = new SimpleDateFormat("yyyyMMdd_HHmmss");
|
||||
private static final String CALLS_DIRECTORY_FOLDER = "calls";
|
||||
private static int sFileNameCounter = 1;
|
||||
|
||||
private AudioSegment mAudioSegment;
|
||||
@@ -152,8 +151,8 @@ public class ActiveAudioSegment
|
||||
{
|
||||
Date now = new Date();
|
||||
String dailyFolder = DIRECTORY_SDF.format(now);
|
||||
Path recordingDirectory = mUserPreferences.getDirectoryPreference().getDirectoryRecording()
|
||||
.resolve(CALLS_DIRECTORY_FOLDER).resolve(dailyFolder);
|
||||
Path recordingDirectory = mUserPreferences.getDirectoryPreference().getDirectoryCalls()
|
||||
.resolve(dailyFolder);
|
||||
|
||||
if(!Files.exists(recordingDirectory))
|
||||
{
|
||||
|
||||
@@ -63,41 +63,41 @@ public class Call
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "id_generator")
|
||||
@SequenceGenerator(name = "id_generator", allocationSize = 1)
|
||||
@Column(name=COLUMN_ID)
|
||||
private long mId;
|
||||
private long id;
|
||||
@Column(name=COLUMN_CALL_TYPE)
|
||||
private String mCallType;
|
||||
private String callType;
|
||||
@Column(name=COLUMN_CHANNEL)
|
||||
private String mChannel;
|
||||
private String channel;
|
||||
@Column(name=COLUMN_DUPLICATE)
|
||||
private boolean mDuplicate;
|
||||
private boolean duplicate;
|
||||
@Column(name=COLUMN_DURATION)
|
||||
private double mDuration;
|
||||
private double duration;
|
||||
@Column(name=COLUMN_EVENT_TIME)
|
||||
private long mEventTime;
|
||||
private long eventTime;
|
||||
@Column(name=COLUMN_FILE)
|
||||
private String mFile;
|
||||
private String file;
|
||||
@Column(name=COLUMN_FREQUENCY)
|
||||
private double mFrequency;
|
||||
private double frequency;
|
||||
@Column(name=COLUMN_FROM_ALIAS)
|
||||
private String mFromAlias;
|
||||
private String fromAlias;
|
||||
@Column(name=COLUMN_FROM_ID)
|
||||
private String mFromId;
|
||||
private String fromId;
|
||||
@Column(name=COLUMN_MONITOR)
|
||||
private int mMonitor;
|
||||
private int monitor;
|
||||
@Column(name=COLUMN_PROTOCOL)
|
||||
private String mProtocol;
|
||||
private String protocol;
|
||||
@Column(name=COLUMN_RECORD)
|
||||
private boolean mRecord;
|
||||
private boolean record;
|
||||
@Column(name=COLUMN_SITE)
|
||||
private String mSite;
|
||||
private String site;
|
||||
@Column(name=COLUMN_STREAM)
|
||||
private boolean mStream;
|
||||
private boolean stream;
|
||||
@Column(name=COLUMN_SYSTEM)
|
||||
private String mSystem;
|
||||
private String system;
|
||||
@Column(name=COLUMN_TO_ALIAS)
|
||||
private String mToAlias;
|
||||
private String toAlias;
|
||||
@Column(name=COLUMN_TO_ID)
|
||||
private String mToId;
|
||||
private String toId;
|
||||
|
||||
@Transient
|
||||
private boolean mComplete = true;
|
||||
@@ -119,7 +119,7 @@ public class Call
|
||||
*/
|
||||
public long getId()
|
||||
{
|
||||
return mId;
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,7 +128,7 @@ public class Call
|
||||
*/
|
||||
public long getEventTime()
|
||||
{
|
||||
return mEventTime;
|
||||
return eventTime;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,7 +137,7 @@ public class Call
|
||||
*/
|
||||
public void setEventTime(long eventTime)
|
||||
{
|
||||
mEventTime = eventTime;
|
||||
this.eventTime = eventTime;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,7 +146,7 @@ public class Call
|
||||
*/
|
||||
public String getFromId()
|
||||
{
|
||||
return mFromId;
|
||||
return fromId;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,7 +155,7 @@ public class Call
|
||||
*/
|
||||
public void setFromId(String fromId)
|
||||
{
|
||||
mFromId = fromId;
|
||||
this.fromId = fromId;
|
||||
updateTimestamp();
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ public class Call
|
||||
*/
|
||||
public String getFromAlias()
|
||||
{
|
||||
return mFromAlias;
|
||||
return fromAlias;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -174,7 +174,7 @@ public class Call
|
||||
*/
|
||||
public void setFromAlias(String fromAlias)
|
||||
{
|
||||
mFromAlias = fromAlias;
|
||||
this.fromAlias = fromAlias;
|
||||
updateTimestamp();
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ public class Call
|
||||
*/
|
||||
public String getToId()
|
||||
{
|
||||
return mToId;
|
||||
return toId;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,7 +193,7 @@ public class Call
|
||||
*/
|
||||
public void setToId(String toId)
|
||||
{
|
||||
mToId = toId;
|
||||
this.toId = toId;
|
||||
updateTimestamp();
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ public class Call
|
||||
*/
|
||||
public String getToAlias()
|
||||
{
|
||||
return mToAlias;
|
||||
return toAlias;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -212,7 +212,7 @@ public class Call
|
||||
*/
|
||||
public void setToAlias(String toAlias)
|
||||
{
|
||||
mToAlias = toAlias;
|
||||
this.toAlias = toAlias;
|
||||
updateTimestamp();
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ public class Call
|
||||
*/
|
||||
public String getSystem()
|
||||
{
|
||||
return mSystem;
|
||||
return system;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -231,7 +231,7 @@ public class Call
|
||||
*/
|
||||
public void setSystem(String system)
|
||||
{
|
||||
mSystem = system;
|
||||
this.system = system;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,7 +240,7 @@ public class Call
|
||||
*/
|
||||
public String getSite()
|
||||
{
|
||||
return mSite;
|
||||
return site;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,7 +249,7 @@ public class Call
|
||||
*/
|
||||
public void setSite(String site)
|
||||
{
|
||||
mSite = site;
|
||||
this.site = site;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,7 +258,7 @@ public class Call
|
||||
*/
|
||||
public String getChannel()
|
||||
{
|
||||
return mChannel;
|
||||
return channel;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,7 +267,7 @@ public class Call
|
||||
*/
|
||||
public void setChannel(String channel)
|
||||
{
|
||||
mChannel = channel;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,7 +276,7 @@ public class Call
|
||||
*/
|
||||
public double getFrequency()
|
||||
{
|
||||
return mFrequency;
|
||||
return frequency;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -285,7 +285,7 @@ public class Call
|
||||
*/
|
||||
public void setFrequency(double frequency)
|
||||
{
|
||||
mFrequency = frequency;
|
||||
this.frequency = frequency;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -294,7 +294,7 @@ public class Call
|
||||
*/
|
||||
public String getFile()
|
||||
{
|
||||
return mFile;
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,7 +303,7 @@ public class Call
|
||||
*/
|
||||
public void setFile(String file)
|
||||
{
|
||||
mFile = file;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -312,7 +312,7 @@ public class Call
|
||||
*/
|
||||
public double getDuration()
|
||||
{
|
||||
return mDuration;
|
||||
return duration;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -321,7 +321,7 @@ public class Call
|
||||
*/
|
||||
public void setDuration(double duration)
|
||||
{
|
||||
mDuration = duration;
|
||||
this.duration = duration;
|
||||
updateTimestamp();
|
||||
}
|
||||
|
||||
@@ -331,7 +331,7 @@ public class Call
|
||||
*/
|
||||
public String getProtocol()
|
||||
{
|
||||
return mProtocol;
|
||||
return protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -340,7 +340,7 @@ public class Call
|
||||
*/
|
||||
public void setProtocol(String protocol)
|
||||
{
|
||||
mProtocol = protocol;
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -368,7 +368,7 @@ public class Call
|
||||
*/
|
||||
public String getCallType()
|
||||
{
|
||||
return mCallType;
|
||||
return callType;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -377,7 +377,7 @@ public class Call
|
||||
*/
|
||||
public void setCallType(String callType)
|
||||
{
|
||||
mCallType = callType;
|
||||
this.callType = callType;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -386,7 +386,7 @@ public class Call
|
||||
*/
|
||||
public boolean isDuplicate()
|
||||
{
|
||||
return mDuplicate;
|
||||
return duplicate;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -395,7 +395,7 @@ public class Call
|
||||
*/
|
||||
public void setDuplicate(boolean duplicate)
|
||||
{
|
||||
mDuplicate = duplicate;
|
||||
this.duplicate = duplicate;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -404,7 +404,7 @@ public class Call
|
||||
*/
|
||||
public int getMonitor()
|
||||
{
|
||||
return mMonitor;
|
||||
return monitor;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -413,7 +413,7 @@ public class Call
|
||||
*/
|
||||
public void setMonitor(int monitor)
|
||||
{
|
||||
mMonitor = monitor;
|
||||
this.monitor = monitor;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -422,7 +422,7 @@ public class Call
|
||||
*/
|
||||
public boolean isRecord()
|
||||
{
|
||||
return mRecord;
|
||||
return record;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -431,7 +431,7 @@ public class Call
|
||||
*/
|
||||
public void setRecord(boolean record)
|
||||
{
|
||||
mRecord = record;
|
||||
this.record = record;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -440,7 +440,7 @@ public class Call
|
||||
*/
|
||||
public boolean isStream()
|
||||
{
|
||||
return mStream;
|
||||
return stream;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -449,7 +449,7 @@ public class Call
|
||||
*/
|
||||
public void setStream(boolean stream)
|
||||
{
|
||||
mStream = stream;
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -485,5 +485,11 @@ public class Call
|
||||
{
|
||||
return mPlaybackChannel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
return obj instanceof Call c && getId() == c.getId();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
* Copyright (C) 2014-2024 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
|
||||
@@ -29,27 +29,35 @@ import org.springframework.data.jpa.repository.Query;
|
||||
*/
|
||||
public interface CallRepository extends JpaRepository<Call,Long>
|
||||
{
|
||||
@Query("SELECT t FROM Call t WHERE t.mToId like ?1 OR t.mToAlias like ?1 OR t.mFromId like ?1 OR t.mFromAlias like ?1")
|
||||
@Query("SELECT t FROM Call t WHERE t.toId like ?1 OR t.toAlias like ?1 OR t.fromId like ?1 OR t.fromAlias like ?1")
|
||||
Page<Call> findByAnyIdWithPagination(String value, Pageable pageable);
|
||||
|
||||
@Query("SELECT t FROM Call t WHERE t.mToId like ?1 OR t.mToAlias like ?1")
|
||||
@Query("SELECT t FROM Call t WHERE t.toId like ?1 OR t.toAlias like ?1")
|
||||
Page<Call> findByToWithPagination(String value, Pageable pageable);
|
||||
|
||||
@Query("SELECT t FROM Call t WHERE t.mFromId like ?1 OR t.mFromAlias like ?1")
|
||||
@Query("SELECT t FROM Call t WHERE t.fromId like ?1 OR t.fromAlias like ?1")
|
||||
Page<Call> findByFromWithPagination(String value, Pageable pageable);
|
||||
|
||||
@Query("SELECT t FROM Call t WHERE t.mCallType like ?1")
|
||||
@Query("SELECT t FROM Call t WHERE t.callType like ?1")
|
||||
Page<Call> findByCallTypeWithPagination(String value, Pageable pageable);
|
||||
|
||||
@Query("SELECT t FROM Call t WHERE t.mChannel like ?1")
|
||||
@Query("SELECT t FROM Call t WHERE t.channel like ?1")
|
||||
Page<Call> findByChannelWithPagination(String value, Pageable pageable);
|
||||
|
||||
@Query("SELECT t FROM Call t WHERE t.mProtocol like ?1")
|
||||
@Query("SELECT t FROM Call t WHERE t.protocol like ?1")
|
||||
Page<Call> findByProtocolWithPagination(String value, Pageable pageable);
|
||||
|
||||
@Query("SELECT t FROM Call t WHERE t.mSite like ?1")
|
||||
@Query("SELECT t FROM Call t WHERE t.site like ?1")
|
||||
Page<Call> findBySiteWithPagination(String value, Pageable pageable);
|
||||
|
||||
@Query("SELECT t FROM Call t WHERE t.mSystem like ?1")
|
||||
@Query("SELECT t FROM Call t WHERE t.system like ?1")
|
||||
Page<Call> findBySystemWithPagination(String value, Pageable pageable);
|
||||
|
||||
/**
|
||||
* Find all calls between the timestamp range arguments.
|
||||
* @param start timestamp
|
||||
* @param end timestamp
|
||||
* @return pageable set of calls.
|
||||
*/
|
||||
Page<Call> findCallsByEventTimeBetweenOrderByEventTime(long start, long end, Pageable pageable);
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ import org.springframework.data.domain.Sort;
|
||||
public class CallView extends VBox implements IPageRequestListener
|
||||
{
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(CallView.class);
|
||||
private static final Sort DEFAULT_SORT = Sort.by("mEventTime").descending();
|
||||
private static final Sort DEFAULT_SORT = Sort.by("eventTime").descending();
|
||||
|
||||
@Resource
|
||||
private AudioManager mAudioManager;
|
||||
@@ -613,7 +613,7 @@ public class CallView extends VBox implements IPageRequestListener
|
||||
|
||||
TableColumn playbackColumn = new TableColumn();
|
||||
playbackColumn.setSortable(false);
|
||||
playbackColumn.setId("mPlayback");
|
||||
playbackColumn.setId("playback");
|
||||
playbackColumn.sortTypeProperty().addListener(mSortOrderListener);
|
||||
IconNode iconNode = new IconNode(Elusive.VOLUME_UP);
|
||||
iconNode.setIconSize(16);
|
||||
@@ -623,7 +623,7 @@ public class CallView extends VBox implements IPageRequestListener
|
||||
playbackColumn.setPrefWidth(40);
|
||||
|
||||
TableColumn eventTimeColumn = new TableColumn();
|
||||
eventTimeColumn.setId("mEventTime");
|
||||
eventTimeColumn.setId("eventTime");
|
||||
eventTimeColumn.sortTypeProperty().addListener(mSortOrderListener);
|
||||
eventTimeColumn.setText("Date/Time");
|
||||
eventTimeColumn.setCellValueFactory(new PropertyValueFactory<Call, Long>(Call.COLUMN_EVENT_TIME));
|
||||
@@ -631,63 +631,63 @@ public class CallView extends VBox implements IPageRequestListener
|
||||
eventTimeColumn.setPrefWidth(160);
|
||||
|
||||
TableColumn durationColumn = new TableColumn();
|
||||
durationColumn.setId("mDuration");
|
||||
durationColumn.setId("duration");
|
||||
durationColumn.sortTypeProperty().addListener(mSortOrderListener);
|
||||
durationColumn.setText("Duration");
|
||||
durationColumn.setCellValueFactory(new PropertyValueFactory<Call, Double>(Call.COLUMN_DURATION));
|
||||
durationColumn.setPrefWidth(70);
|
||||
|
||||
TableColumn callTypeColumn = new TableColumn();
|
||||
callTypeColumn.setId("mCallType");
|
||||
callTypeColumn.setId("callType");
|
||||
callTypeColumn.sortTypeProperty().addListener(mSortOrderListener);
|
||||
callTypeColumn.setText("Type");
|
||||
callTypeColumn.setCellValueFactory(new PropertyValueFactory<Call, String>(Call.COLUMN_CALL_TYPE));
|
||||
callTypeColumn.setPrefWidth(85);
|
||||
|
||||
TableColumn toIdColumn = new TableColumn();
|
||||
toIdColumn.setId("mToId");
|
||||
toIdColumn.setId("toId");
|
||||
toIdColumn.sortTypeProperty().addListener(mSortOrderListener);
|
||||
toIdColumn.setText("To");
|
||||
toIdColumn.setCellValueFactory(new PropertyValueFactory<Call, String>(Call.COLUMN_TO_ID));
|
||||
toIdColumn.setPrefWidth(80);
|
||||
|
||||
TableColumn toAliasColumn = new TableColumn();
|
||||
toAliasColumn.setId("mToAlias");
|
||||
toAliasColumn.setId("toAlias");
|
||||
toAliasColumn.sortTypeProperty().addListener(mSortOrderListener);
|
||||
toAliasColumn.setText("To Alias");
|
||||
toAliasColumn.setCellValueFactory(new PropertyValueFactory<Call, String>(Call.COLUMN_TO_ALIAS));
|
||||
toAliasColumn.setPrefWidth(100);
|
||||
|
||||
TableColumn fromIdColumn = new TableColumn();
|
||||
fromIdColumn.setId("mFromId");
|
||||
fromIdColumn.setId("fromId");
|
||||
fromIdColumn.sortTypeProperty().addListener(mSortOrderListener);
|
||||
fromIdColumn.setText("From");
|
||||
fromIdColumn.setCellValueFactory(new PropertyValueFactory<Call, String>(Call.COLUMN_FROM_ID));
|
||||
fromIdColumn.setPrefWidth(70);
|
||||
|
||||
TableColumn fromAliasColumn = new TableColumn();
|
||||
fromAliasColumn.setId("mFromAlias");
|
||||
fromAliasColumn.setId("fromAlias");
|
||||
fromAliasColumn.sortTypeProperty().addListener(mSortOrderListener);
|
||||
fromAliasColumn.setText("From Alias");
|
||||
fromAliasColumn.setCellValueFactory(new PropertyValueFactory<Call, String>(Call.COLUMN_FROM_ALIAS));
|
||||
fromAliasColumn.setPrefWidth(100);
|
||||
|
||||
TableColumn protocolColumn = new TableColumn();
|
||||
protocolColumn.setId("mProtocol");
|
||||
protocolColumn.setId("protocol");
|
||||
protocolColumn.sortTypeProperty().addListener(mSortOrderListener);
|
||||
protocolColumn.setText("Protocol");
|
||||
protocolColumn.setCellValueFactory(new PropertyValueFactory<Call, String>(Call.COLUMN_PROTOCOL));
|
||||
protocolColumn.setPrefWidth(90);
|
||||
|
||||
TableColumn frequencyColumn = new TableColumn();
|
||||
frequencyColumn.setId("mFrequency");
|
||||
frequencyColumn.setId("frequency");
|
||||
frequencyColumn.sortTypeProperty().addListener(mSortOrderListener);
|
||||
frequencyColumn.setText("Frequency");
|
||||
frequencyColumn.setCellValueFactory(new PropertyValueFactory<Call, Double>(Call.COLUMN_FREQUENCY));
|
||||
frequencyColumn.setPrefWidth(80);
|
||||
|
||||
TableColumn systemColumn = new TableColumn();
|
||||
systemColumn.setId("mSystem");
|
||||
systemColumn.setId("system");
|
||||
systemColumn.sortTypeProperty().addListener(mSortOrderListener);
|
||||
systemColumn.setText("System");
|
||||
systemColumn.setCellValueFactory(new PropertyValueFactory<Call, String>(Call.COLUMN_SYSTEM));
|
||||
@@ -703,7 +703,7 @@ public class CallView extends VBox implements IPageRequestListener
|
||||
|
||||
TableColumn channelColumn = new TableColumn();
|
||||
channelColumn.setVisible(false);
|
||||
channelColumn.setId("mChannel");
|
||||
channelColumn.setId("channel");
|
||||
channelColumn.sortTypeProperty().addListener(mSortOrderListener);
|
||||
channelColumn.setText("Channel");
|
||||
channelColumn.setCellValueFactory(new PropertyValueFactory<Call, String>(Call.COLUMN_CHANNEL));
|
||||
@@ -711,7 +711,7 @@ public class CallView extends VBox implements IPageRequestListener
|
||||
|
||||
TableColumn duplicateColumn = new TableColumn();
|
||||
duplicateColumn.setVisible(false);
|
||||
duplicateColumn.setId("mDuplicate");
|
||||
duplicateColumn.setId("duplicate");
|
||||
duplicateColumn.sortTypeProperty().addListener(mSortOrderListener);
|
||||
duplicateColumn.setText("Duplicate");
|
||||
duplicateColumn.setCellValueFactory(new PropertyValueFactory<Call, Boolean>(Call.COLUMN_DUPLICATE));
|
||||
@@ -719,7 +719,7 @@ public class CallView extends VBox implements IPageRequestListener
|
||||
|
||||
TableColumn recordColumn = new TableColumn();
|
||||
recordColumn.setVisible(false);
|
||||
recordColumn.setId("mRecord");
|
||||
recordColumn.setId("record");
|
||||
recordColumn.sortTypeProperty().addListener(mSortOrderListener);
|
||||
recordColumn.setText("Record");
|
||||
recordColumn.setCellValueFactory(new PropertyValueFactory<Call, Boolean>(Call.COLUMN_RECORD));
|
||||
@@ -727,7 +727,7 @@ public class CallView extends VBox implements IPageRequestListener
|
||||
|
||||
TableColumn streamColumn = new TableColumn();
|
||||
streamColumn.setVisible(false);
|
||||
streamColumn.setId("mStream");
|
||||
streamColumn.setId("stream");
|
||||
streamColumn.sortTypeProperty().addListener(mSortOrderListener);
|
||||
streamColumn.setText("Stream");
|
||||
streamColumn.setCellValueFactory(new PropertyValueFactory<Call, Boolean>(Call.COLUMN_STREAM));
|
||||
@@ -735,7 +735,7 @@ public class CallView extends VBox implements IPageRequestListener
|
||||
|
||||
TableColumn monitorColumn = new TableColumn();
|
||||
monitorColumn.setVisible(false);
|
||||
monitorColumn.setId("mMonitor");
|
||||
monitorColumn.setId("monitor");
|
||||
monitorColumn.sortTypeProperty().addListener(mSortOrderListener);
|
||||
monitorColumn.setText("Monitor");
|
||||
monitorColumn.setCellValueFactory(new PropertyValueFactory<Call, Integer>(Call.COLUMN_MONITOR));
|
||||
@@ -752,6 +752,11 @@ public class CallView extends VBox implements IPageRequestListener
|
||||
{
|
||||
mAudioPlaybackController.replay(MixerChannel.LEFT, selectedCall);
|
||||
}
|
||||
else if(oldValue != null && selectedCall == null)
|
||||
{
|
||||
//We were in replay but now we don't have a selected call
|
||||
mAudioPlaybackController.auto(MixerChannel.LEFT, null, null);
|
||||
}
|
||||
|
||||
AudioPlaybackChannelController left = mAudioPlaybackController.getChannelController(MixerChannel.LEFT);
|
||||
AudioPlaybackChannelController right = mAudioPlaybackController.getChannelController(MixerChannel.RIGHT);
|
||||
@@ -911,6 +916,8 @@ public class CallView extends VBox implements IPageRequestListener
|
||||
{
|
||||
mCalls.remove(call);
|
||||
}
|
||||
|
||||
mCallCount.set(mCallCount.get() - 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.scene.media.Media;
|
||||
import javafx.scene.media.MediaException;
|
||||
import javafx.scene.media.MediaPlayer;
|
||||
|
||||
/**
|
||||
@@ -41,6 +42,7 @@ public class AudioPlaybackChannelController implements ChangeListener<MediaPlaye
|
||||
{
|
||||
private static final String EMPTY = new String("EMPTY");
|
||||
private static final String LOADED = new String("LOADED");
|
||||
private static final String NO_FILE = new String("NO FILE");
|
||||
|
||||
private ObjectProperty<MediaPlayer> mMediaPlayer = new SimpleObjectProperty<>();
|
||||
private StringProperty mFrequency = new SimpleStringProperty();
|
||||
@@ -233,18 +235,31 @@ public class AudioPlaybackChannelController implements ChangeListener<MediaPlaye
|
||||
{
|
||||
if(mCall.getFile() != null)
|
||||
{
|
||||
//Create a new media player
|
||||
mMediaPlayer.set(new MediaPlayer(new Media(new File(mCall.getFile()).toURI().toString())));
|
||||
//Register status listener
|
||||
mMediaPlayer.get().statusProperty().addListener(this);
|
||||
//Register runnable for end-of-media event
|
||||
mMediaPlayer.get().onEndOfMediaProperty().set(mEndOfMediaRunnable);
|
||||
//Set audio channel balance
|
||||
mMediaPlayer.get().balanceProperty().set(mBalance);
|
||||
//Set the media player to auto-start replay
|
||||
mMediaPlayer.get().setAutoPlay(true);
|
||||
File file = new File(mCall.getFile());
|
||||
|
||||
mMediaPlayerStatus.set(LOADED);
|
||||
if(file.exists())
|
||||
{
|
||||
try
|
||||
{
|
||||
MediaPlayer mediaPlayer = new MediaPlayer(new Media(file.toURI().toString()));
|
||||
//Create a new media player
|
||||
mMediaPlayer.set(mediaPlayer);
|
||||
//Register status listener
|
||||
mMediaPlayer.get().statusProperty().addListener(this);
|
||||
//Register runnable for end-of-media event
|
||||
mMediaPlayer.get().onEndOfMediaProperty().set(mEndOfMediaRunnable);
|
||||
//Set audio channel balance
|
||||
mMediaPlayer.get().balanceProperty().set(mBalance);
|
||||
//Set the media player to auto-start replay
|
||||
mMediaPlayer.get().setAutoPlay(true);
|
||||
mMediaPlayerStatus.set(LOADED);
|
||||
}
|
||||
catch(MediaException me)
|
||||
{
|
||||
mMediaPlayer.set(null);
|
||||
mMediaPlayerStatus.set(NO_FILE);
|
||||
}
|
||||
}
|
||||
|
||||
//Register a listener on the call's last updated property for update notifications.
|
||||
mCall.lastUpdatedProperty().addListener(mCallUpdateListener);
|
||||
|
||||
@@ -67,13 +67,13 @@ import io.github.dsheirer.gui.preference.DecodeEventViewPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.TalkgroupFormatPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.UserPreferencesEditor;
|
||||
import io.github.dsheirer.gui.preference.application.ApplicationPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.audio.call.CallManagementPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.audio.playback.PlaybackPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.audio.record.RecordPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.audio.retention.RetentionPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.calibration.VectorCalibrationPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.call.CallManagementPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.decoder.JmbeLibraryPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.directory.DirectoryPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.mp3.MP3PreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.playback.PlaybackPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.record.RecordPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.tuner.TunerPreferenceEditor;
|
||||
import io.github.dsheirer.jmbe.JmbeEditor;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
@@ -618,16 +618,6 @@ public class SpringJavaFxBeanFactory
|
||||
return new CallManagementPreferenceEditor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Application preference editor
|
||||
*/
|
||||
@Bean
|
||||
@Lazy
|
||||
public MP3PreferenceEditor getMp3PreferenceEditor()
|
||||
{
|
||||
return new MP3PreferenceEditor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Playback preference editor
|
||||
*/
|
||||
@@ -648,6 +638,16 @@ public class SpringJavaFxBeanFactory
|
||||
return new RecordPreferenceEditor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Audio/call retention preference editor
|
||||
*/
|
||||
@Bean
|
||||
@Lazy
|
||||
public RetentionPreferenceEditor getRetentionPreferenceEditor()
|
||||
{
|
||||
return new RetentionPreferenceEditor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode event view preference editor
|
||||
*/
|
||||
|
||||
@@ -87,8 +87,9 @@ public class TableViewColumnController implements ListChangeListener
|
||||
public void reset()
|
||||
{
|
||||
apply(mDefaultTableState);
|
||||
|
||||
//Clear the preference since we're at default state
|
||||
mPreferences.put(mPreferencePrefix + PREFERENCE_KEY_TABLE_STATE, null);
|
||||
mPreferences.remove(mPreferencePrefix + PREFERENCE_KEY_TABLE_STATE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,6 +134,12 @@ public class TableViewColumnController implements ListChangeListener
|
||||
}
|
||||
}
|
||||
|
||||
//If the table names got screwed up in the table state, just add the columns all back in.
|
||||
if(mTableView.getColumns().isEmpty())
|
||||
{
|
||||
mTableView.getColumns().addAll(columns);
|
||||
}
|
||||
|
||||
//Clear and restore the sort ordering
|
||||
mTableView.getSortOrder().clear();
|
||||
for(TableState.SortedColumn sortedColumn: tableState.getSort())
|
||||
|
||||
@@ -25,13 +25,13 @@ package io.github.dsheirer.gui.preference;
|
||||
public enum PreferenceEditorType
|
||||
{
|
||||
APPLICATION("Application"),
|
||||
AUDIO_PLAYBACK("Playback"),
|
||||
AUDIO_RECORD("Record"),
|
||||
AUDIO_DUPLICATE_MANAGEMENT("Duplicate/Streaming"),
|
||||
AUDIO_CALL_RETENTION("Call Retention"),
|
||||
CHANNEL_EVENT("Channel Events"),
|
||||
DIRECTORY("Directories"),
|
||||
JMBE_LIBRARY("JMBE Audio Library"),
|
||||
AUDIO_MP3("MP3"),
|
||||
AUDIO_RECORD("Record"),
|
||||
AUDIO_PLAYBACK("Playback/Tones"),
|
||||
AUDIO_CALL_MANAGEMENT("Call Management"),
|
||||
TUNERS("Tuners"),
|
||||
TALKGROUP_FORMAT("Talkgroup & Radio ID"),
|
||||
VECTOR_CALIBRATION("Vector Calibration"),
|
||||
|
||||
@@ -172,8 +172,8 @@ public class UserPreferencesEditor extends BorderPane
|
||||
applicationItem.setExpanded(true);
|
||||
|
||||
TreeItem<String> audioItem = new TreeItem<>("Audio");
|
||||
audioItem.getChildren().add(new TreeItem(PreferenceEditorType.AUDIO_CALL_MANAGEMENT));
|
||||
audioItem.getChildren().add(new TreeItem(PreferenceEditorType.AUDIO_MP3));
|
||||
audioItem.getChildren().add(new TreeItem(PreferenceEditorType.AUDIO_CALL_RETENTION));
|
||||
audioItem.getChildren().add(new TreeItem(PreferenceEditorType.AUDIO_DUPLICATE_MANAGEMENT));
|
||||
audioItem.getChildren().add(new TreeItem(PreferenceEditorType.AUDIO_PLAYBACK));
|
||||
audioItem.getChildren().add(new TreeItem(PreferenceEditorType.AUDIO_RECORD));
|
||||
treeRoot.getChildren().add(audioItem);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
* Copyright (C) 2014-2024 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
|
||||
@@ -17,7 +17,7 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.gui.preference.call;
|
||||
package io.github.dsheirer.gui.preference.audio.call;
|
||||
|
||||
import io.github.dsheirer.audio.broadcast.PatchGroupStreamingOption;
|
||||
import io.github.dsheirer.gui.preference.PreferenceEditor;
|
||||
@@ -71,7 +71,7 @@ public class CallManagementPreferenceEditor extends PreferenceEditor
|
||||
@Override
|
||||
public PreferenceEditorType getPreferenceEditorType()
|
||||
{
|
||||
return PreferenceEditorType.AUDIO_CALL_MANAGEMENT;
|
||||
return PreferenceEditorType.AUDIO_DUPLICATE_MANAGEMENT;
|
||||
}
|
||||
|
||||
private GridPane getEditorPane()
|
||||
@@ -17,7 +17,7 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.gui.preference.playback;
|
||||
package io.github.dsheirer.gui.preference.audio.playback;
|
||||
|
||||
import io.github.dsheirer.audio.AudioFormats;
|
||||
import io.github.dsheirer.audio.playbackfx.PlaybackView;
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
* Copyright (C) 2014-2024 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
|
||||
@@ -17,7 +17,7 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.gui.preference.playback;
|
||||
package io.github.dsheirer.gui.preference.audio.playback;
|
||||
|
||||
public enum ToneFrequency
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2024 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
|
||||
@@ -17,7 +17,7 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.gui.preference.playback;
|
||||
package io.github.dsheirer.gui.preference.audio.playback;
|
||||
|
||||
import io.github.dsheirer.dsp.oscillator.IRealOscillator;
|
||||
import io.github.dsheirer.dsp.oscillator.ScalarRealOscillator;
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
* Copyright (C) 2014-2024 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
|
||||
@@ -17,7 +17,7 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.gui.preference.playback;
|
||||
package io.github.dsheirer.gui.preference.audio.playback;
|
||||
|
||||
public enum ToneVolume
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
* Copyright (C) 2014-2024 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
|
||||
@@ -17,20 +17,25 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.gui.preference.mp3;
|
||||
package io.github.dsheirer.gui.preference.audio.record;
|
||||
|
||||
import io.github.dsheirer.audio.convert.InputAudioFormat;
|
||||
import io.github.dsheirer.audio.convert.MP3Setting;
|
||||
import io.github.dsheirer.gui.preference.PreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.PreferenceEditorType;
|
||||
import io.github.dsheirer.preference.mp3.MP3Preference;
|
||||
import io.github.dsheirer.preference.record.RecordPreference;
|
||||
import io.github.dsheirer.record.RecordFormat;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.ComboBox;
|
||||
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;
|
||||
@@ -39,21 +44,20 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Preference settings for MP3
|
||||
* Preference settings for recording
|
||||
*/
|
||||
public class MP3PreferenceEditor extends PreferenceEditor
|
||||
public class RecordPreferenceEditor extends PreferenceEditor
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(MP3PreferenceEditor.class);
|
||||
private final static Logger mLog = LoggerFactory.getLogger(RecordPreferenceEditor.class);
|
||||
private RecordPreference mRecordPreference;
|
||||
private MP3Preference mMP3Preference;
|
||||
private GridPane mEditorPane;
|
||||
private ComboBox<RecordFormat> mRecordFormatComboBox;
|
||||
private ComboBox<MP3Setting> mMP3SettingComboBox;
|
||||
private ComboBox<InputAudioFormat> mAudioSampleRateComboBox;
|
||||
private CheckBox mNormalizeAudioCheckBox;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*/
|
||||
public MP3PreferenceEditor()
|
||||
public RecordPreferenceEditor()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -61,6 +65,7 @@ public class MP3PreferenceEditor extends PreferenceEditor
|
||||
public void postConstruct()
|
||||
{
|
||||
mMP3Preference = getUserPreferences().getMP3Preference();
|
||||
mRecordPreference = getUserPreferences().getRecordPreference();
|
||||
HBox.setHgrow(getEditorPane(), Priority.ALWAYS);
|
||||
getChildren().add(getEditorPane());
|
||||
}
|
||||
@@ -68,7 +73,7 @@ public class MP3PreferenceEditor extends PreferenceEditor
|
||||
@Override
|
||||
public PreferenceEditorType getPreferenceEditorType()
|
||||
{
|
||||
return PreferenceEditorType.AUDIO_MP3;
|
||||
return PreferenceEditorType.AUDIO_RECORD;
|
||||
}
|
||||
|
||||
private GridPane getEditorPane()
|
||||
@@ -81,15 +86,28 @@ public class MP3PreferenceEditor extends PreferenceEditor
|
||||
mEditorPane.setVgap(10);
|
||||
|
||||
int row = 0;
|
||||
|
||||
Label label = new Label("Audio Recording Format:");
|
||||
mEditorPane.add(label, 0, row);
|
||||
mEditorPane.add(getRecordFormatComboBox(), 1, row);
|
||||
|
||||
row++;
|
||||
|
||||
Separator separator = new Separator();
|
||||
GridPane.setHgrow(separator, Priority.ALWAYS);
|
||||
GridPane.setConstraints(separator, 0, row, 2, 1);
|
||||
mEditorPane.getChildren().add(separator);
|
||||
|
||||
row++;
|
||||
|
||||
Label topLabel = new Label("MP3 Encoder Preferences");
|
||||
mEditorPane.add(topLabel, 0, row++, 2, 1);
|
||||
|
||||
|
||||
mEditorPane.add(getNormalizeAudioCheckBox(), 1, row++);
|
||||
|
||||
Label label = new Label("(LAME) Encoder Setting:");
|
||||
GridPane.setHalignment(label, HPos.RIGHT);
|
||||
mEditorPane.add(label, 0, row);
|
||||
Label lameLabel = new Label("(LAME) Encoder Setting:");
|
||||
GridPane.setHalignment(lameLabel, HPos.RIGHT);
|
||||
mEditorPane.add(lameLabel, 0, row);
|
||||
mEditorPane.add(getMP3SettingComboBox(), 1, row++);
|
||||
|
||||
Label sampleRateLabel = new Label("Input Audio Sample Rate:");
|
||||
@@ -104,6 +122,26 @@ public class MP3PreferenceEditor extends PreferenceEditor
|
||||
return mEditorPane;
|
||||
}
|
||||
|
||||
private ComboBox<RecordFormat> getRecordFormatComboBox()
|
||||
{
|
||||
if(mRecordFormatComboBox == null)
|
||||
{
|
||||
mRecordFormatComboBox = new ComboBox<>();
|
||||
mRecordFormatComboBox.getItems().addAll(RecordFormat.values());
|
||||
mRecordFormatComboBox.getSelectionModel().select(mRecordPreference.getAudioRecordFormat());
|
||||
mRecordFormatComboBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<RecordFormat>()
|
||||
{
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends RecordFormat> observable, RecordFormat oldValue, RecordFormat newValue)
|
||||
{
|
||||
mRecordPreference.setAudioRecordFormat(newValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return mRecordFormatComboBox;
|
||||
}
|
||||
|
||||
private ComboBox<MP3Setting> getMP3SettingComboBox()
|
||||
{
|
||||
if(mMP3SettingComboBox == null)
|
||||
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.gui.preference.audio.retention;
|
||||
|
||||
import io.github.dsheirer.gui.control.IntegerTextField;
|
||||
import io.github.dsheirer.gui.preference.PreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.PreferenceEditorType;
|
||||
import io.github.dsheirer.preference.retention.AgeUnits;
|
||||
import io.github.dsheirer.preference.retention.RetentionPolicy;
|
||||
import io.github.dsheirer.preference.retention.RetentionPreference;
|
||||
import io.github.dsheirer.preference.retention.SizeUnits;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Preference settings for audio/call retention
|
||||
*/
|
||||
public class RetentionPreferenceEditor extends PreferenceEditor
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(RetentionPreferenceEditor.class);
|
||||
private RetentionPreference mRetentionPreference;
|
||||
private GridPane mEditorPane;
|
||||
private ComboBox<RetentionPolicy> mRetentionPolicyComboBox;
|
||||
private ComboBox<AgeUnits> mAgeUnitsComboBox;
|
||||
private ComboBox<SizeUnits> mSizeUnitsComboBox;
|
||||
private IntegerTextField mAgeValueTextField;
|
||||
private IntegerTextField mDirectorySizeTextField;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*/
|
||||
public RetentionPreferenceEditor()
|
||||
{
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void postConstruct()
|
||||
{
|
||||
mRetentionPreference = getUserPreferences().getRetentionPreference();
|
||||
HBox.setHgrow(getEditorPane(), Priority.ALWAYS);
|
||||
getChildren().add(getEditorPane());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreferenceEditorType getPreferenceEditorType()
|
||||
{
|
||||
return PreferenceEditorType.AUDIO_CALL_RETENTION;
|
||||
}
|
||||
|
||||
private GridPane getEditorPane()
|
||||
{
|
||||
if(mEditorPane == null)
|
||||
{
|
||||
mEditorPane = new GridPane();
|
||||
mEditorPane.setPadding(new Insets(10, 10, 10, 10));
|
||||
mEditorPane.setHgap(10);
|
||||
mEditorPane.setVgap(10);
|
||||
|
||||
int row = 0;
|
||||
|
||||
Label label = new Label("Call event retention settings for automatic deletion of call " +
|
||||
"events and related audio. Note: these settings do not apply to alias-based audio recordings.");
|
||||
label.setWrapText(true);
|
||||
mEditorPane.add(label, 0, row, 4, 1);
|
||||
|
||||
Label autoDeleteLabel = new Label("Automatic Call Event Deletion");
|
||||
mEditorPane.add(autoDeleteLabel, 0, ++row, 4, 1);
|
||||
|
||||
Label deleteByLabel = new Label("Delete By:");
|
||||
GridPane.setHalignment(deleteByLabel, HPos.RIGHT);
|
||||
mEditorPane.add(deleteByLabel, 1, ++row);
|
||||
mEditorPane.add(getRetentionPolicyComboBox(), 2, row);
|
||||
|
||||
Label maxAgeLabel = new Label("Maximum Age:");
|
||||
GridPane.setHalignment(maxAgeLabel, HPos.RIGHT);
|
||||
mEditorPane.add(maxAgeLabel, 1, ++row);
|
||||
mEditorPane.add(getAgeValueTextField(), 2, row);
|
||||
mEditorPane.add(getAgeUnitsComboBox(), 3, row);
|
||||
|
||||
Label maxSizeLabel = new Label("Maximum Directory Size:");
|
||||
GridPane.setHalignment(maxSizeLabel, HPos.RIGHT);
|
||||
mEditorPane.add(maxSizeLabel, 1, ++row);
|
||||
mEditorPane.add(getDirectorySizeTextField(), 2, row);
|
||||
mEditorPane.add(getSizeUnitsComboBox(), 3, row);
|
||||
|
||||
updateControls();
|
||||
}
|
||||
|
||||
return mEditorPane;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the enabled state of editor controls whenever the retention policy changes.
|
||||
*/
|
||||
private void updateControls()
|
||||
{
|
||||
RetentionPolicy policy = getRetentionPolicyComboBox().getValue();
|
||||
|
||||
switch(policy)
|
||||
{
|
||||
case AGE:
|
||||
getAgeValueTextField().setDisable(false);
|
||||
getAgeUnitsComboBox().setDisable(false);
|
||||
getDirectorySizeTextField().setDisable(true);
|
||||
break;
|
||||
case SIZE:
|
||||
getAgeValueTextField().setDisable(true);
|
||||
getAgeUnitsComboBox().setDisable(true);
|
||||
getDirectorySizeTextField().setDisable(false);
|
||||
break;
|
||||
default:
|
||||
case AGE_AND_SIZE:
|
||||
getAgeValueTextField().setDisable(false);
|
||||
getAgeUnitsComboBox().setDisable(false);
|
||||
getDirectorySizeTextField().setDisable(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public ComboBox<RetentionPolicy> getRetentionPolicyComboBox()
|
||||
{
|
||||
if(mRetentionPolicyComboBox == null)
|
||||
{
|
||||
mRetentionPolicyComboBox = new ComboBox<>(FXCollections.observableArrayList(RetentionPolicy.values()));
|
||||
mRetentionPolicyComboBox.getSelectionModel().select(mRetentionPreference.getRetentionPolicy());
|
||||
mRetentionPolicyComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if(newValue != null)
|
||||
{
|
||||
mRetentionPreference.setRetentionPolicy(newValue);
|
||||
}
|
||||
|
||||
updateControls();
|
||||
});
|
||||
}
|
||||
|
||||
return mRetentionPolicyComboBox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Age units combo box control.
|
||||
*/
|
||||
public ComboBox<AgeUnits> getAgeUnitsComboBox()
|
||||
{
|
||||
if(mAgeUnitsComboBox == null)
|
||||
{
|
||||
mAgeUnitsComboBox = new ComboBox<>(FXCollections.observableArrayList(AgeUnits.values()));
|
||||
mAgeUnitsComboBox.getSelectionModel().select(mRetentionPreference.getAgeUnits());
|
||||
mAgeUnitsComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if(newValue != null)
|
||||
{
|
||||
mRetentionPreference.setAgeUnits(newValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return mAgeUnitsComboBox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Size units combo box control.
|
||||
*/
|
||||
public ComboBox<SizeUnits> getSizeUnitsComboBox()
|
||||
{
|
||||
if(mSizeUnitsComboBox == null)
|
||||
{
|
||||
mSizeUnitsComboBox = new ComboBox<>(FXCollections.observableArrayList(SizeUnits.values()));
|
||||
mSizeUnitsComboBox.getSelectionModel().select(mRetentionPreference.getSizeUnits());
|
||||
mSizeUnitsComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if(newValue != null)
|
||||
{
|
||||
mRetentionPreference.setSizeUnits(newValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return mSizeUnitsComboBox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Age value text field.
|
||||
*/
|
||||
private IntegerTextField getAgeValueTextField()
|
||||
{
|
||||
if(mAgeValueTextField == null)
|
||||
{
|
||||
mAgeValueTextField = new IntegerTextField();
|
||||
mAgeValueTextField.set(mRetentionPreference.getAgeValue());
|
||||
mAgeValueTextField.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
Integer value = mAgeValueTextField.get();
|
||||
|
||||
if(value != null)
|
||||
{
|
||||
mRetentionPreference.setAgeValue(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
mAgeValueTextField.set(mRetentionPreference.getAgeValue());
|
||||
}
|
||||
});
|
||||
//Monitor field for lost focus and restore a valid value if the user entered value is bad
|
||||
mAgeValueTextField.focusedProperty().addListener((observable, oldValue, isFocused) -> {
|
||||
if(!isFocused)
|
||||
{
|
||||
Integer value = mAgeValueTextField.get();
|
||||
|
||||
if(value == null || value <= 0)
|
||||
{
|
||||
mAgeValueTextField.setText(String.valueOf(mRetentionPreference.getAgeValue()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return mAgeValueTextField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Directory size text field.
|
||||
*/
|
||||
private IntegerTextField getDirectorySizeTextField()
|
||||
{
|
||||
if(mDirectorySizeTextField == null)
|
||||
{
|
||||
mDirectorySizeTextField = new IntegerTextField();
|
||||
mDirectorySizeTextField.set(mRetentionPreference.getMaxDirectorySize());
|
||||
mDirectorySizeTextField.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
Integer value = mDirectorySizeTextField.get();
|
||||
|
||||
if(value != null && value > 0)
|
||||
{
|
||||
mRetentionPreference.setMaxDirectorySize(value);
|
||||
}
|
||||
});
|
||||
//Monitor field for lost focus and restore a valid value if the user entered value is bad
|
||||
mDirectorySizeTextField.focusedProperty().addListener((observable, oldValue, isFocused) -> {
|
||||
if(!isFocused)
|
||||
{
|
||||
Integer value = mDirectorySizeTextField.get();
|
||||
|
||||
if(value == null || value <= 0)
|
||||
{
|
||||
mDirectorySizeTextField.setText(String.valueOf(mRetentionPreference.getMaxDirectorySize()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return mDirectorySizeTextField;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
* Copyright (C) 2014-2024 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
|
||||
@@ -64,6 +64,11 @@ public class DirectoryPreferenceEditor extends PreferenceEditor implements IPref
|
||||
private Button mResetApplicationLogsButton;
|
||||
private Label mApplicationLogsPathLabel;
|
||||
|
||||
private Label mCallLabel;
|
||||
private Button mChangeCallButton;
|
||||
private Button mResetCallButton;
|
||||
private Label mCallPathLabel;
|
||||
|
||||
private Label mConfigurationLabel;
|
||||
private Button mChangeConfigurationButton;
|
||||
private Button mResetConfigurationButton;
|
||||
@@ -172,6 +177,19 @@ public class DirectoryPreferenceEditor extends PreferenceEditor implements IPref
|
||||
mEditorPane.add(getResetApplicationLogsButton(), 3, row++);
|
||||
|
||||
|
||||
GridPane.setMargin(getCallLabel(), new Insets(0, 10, 0, 0));
|
||||
mEditorPane.add(getCallLabel(), 0, row);
|
||||
|
||||
GridPane.setMargin(getCallPathLabel(), new Insets(0, 10, 0, 0));
|
||||
mEditorPane.add(getCallPathLabel(), 1, row);
|
||||
|
||||
GridPane.setMargin(getChangeCallButton(), new Insets(2, 10, 2, 0));
|
||||
mEditorPane.add(getChangeCallButton(), 2, row);
|
||||
|
||||
GridPane.setMargin(getResetCallButton(), new Insets(2, 0, 2, 0));
|
||||
mEditorPane.add(getResetCallButton(), 3, row++);
|
||||
|
||||
|
||||
GridPane.setMargin(getConfigurationLabel(), new Insets(0, 10, 0, 0));
|
||||
mEditorPane.add(getConfigurationLabel(), 0, row);
|
||||
|
||||
@@ -442,6 +460,71 @@ public class DirectoryPreferenceEditor extends PreferenceEditor implements IPref
|
||||
return mApplicationLogsPathLabel;
|
||||
}
|
||||
|
||||
private Label getCallLabel()
|
||||
{
|
||||
if(mCallLabel == null)
|
||||
{
|
||||
mCallLabel = new Label("Calls");
|
||||
}
|
||||
|
||||
return mCallLabel;
|
||||
}
|
||||
|
||||
private Button getChangeCallButton()
|
||||
{
|
||||
if(mChangeCallButton == null)
|
||||
{
|
||||
mChangeCallButton = new Button("Change...");
|
||||
mChangeCallButton.setOnAction(new EventHandler<ActionEvent>()
|
||||
{
|
||||
@Override
|
||||
public void handle(ActionEvent event)
|
||||
{
|
||||
DirectoryChooser directoryChooser = new DirectoryChooser();
|
||||
directoryChooser.setTitle("Select Calls Folder");
|
||||
directoryChooser.setInitialDirectory(mDirectoryPreference.getDirectoryCalls().toFile());
|
||||
Stage stage = (Stage)getChangeCallButton().getScene().getWindow();
|
||||
File selected = directoryChooser.showDialog(stage);
|
||||
|
||||
if(selected != null)
|
||||
{
|
||||
mDirectoryPreference.setDirectoryCalls(selected.toPath());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return mChangeCallButton;
|
||||
}
|
||||
|
||||
private Button getResetCallButton()
|
||||
{
|
||||
if(mResetCallButton == null)
|
||||
{
|
||||
mResetCallButton = new Button("Reset");
|
||||
mResetCallButton.setOnAction(new EventHandler<ActionEvent>()
|
||||
{
|
||||
@Override
|
||||
public void handle(ActionEvent event)
|
||||
{
|
||||
mDirectoryPreference.resetDirectoryCall();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return mResetCallButton;
|
||||
}
|
||||
|
||||
private Label getCallPathLabel()
|
||||
{
|
||||
if(mCallPathLabel == null)
|
||||
{
|
||||
mCallPathLabel = new Label(mDirectoryPreference.getDirectoryCalls().toString());
|
||||
}
|
||||
|
||||
return mCallPathLabel;
|
||||
}
|
||||
|
||||
private Label getConfigurationLabel()
|
||||
{
|
||||
if(mConfigurationLabel == null)
|
||||
@@ -904,6 +987,7 @@ public class DirectoryPreferenceEditor extends PreferenceEditor implements IPref
|
||||
{
|
||||
getApplicationRootPathLabel().setText(mDirectoryPreference.getDirectoryApplicationRoot().toString());
|
||||
getApplicationLogsPathLabel().setText(mDirectoryPreference.getDirectoryApplicationLog().toString());
|
||||
getCallPathLabel().setText(mDirectoryPreference.getDirectoryCalls().toString());
|
||||
getEventLogsPathLabel().setText(mDirectoryPreference.getDirectoryEventLog().toString());
|
||||
getPlaylistPathLabel().setText(mDirectoryPreference.getDirectoryPlaylist().toString());
|
||||
getRecordingPathLabel().setText(mDirectoryPreference.getDirectoryRecording().toString());
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.gui.preference.record;
|
||||
|
||||
import io.github.dsheirer.gui.preference.PreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.PreferenceEditorType;
|
||||
import io.github.dsheirer.preference.record.RecordPreference;
|
||||
import io.github.dsheirer.record.RecordFormat;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Preference settings for recording
|
||||
*/
|
||||
public class RecordPreferenceEditor extends PreferenceEditor
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(RecordPreferenceEditor.class);
|
||||
private RecordPreference mRecordPreference;
|
||||
private GridPane mEditorPane;
|
||||
private ComboBox<RecordFormat> mRecordFormatComboBox;
|
||||
|
||||
public RecordPreferenceEditor()
|
||||
{
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void postConstruct()
|
||||
{
|
||||
mRecordPreference = getUserPreferences().getRecordPreference();
|
||||
HBox.setHgrow(getEditorPane(), Priority.ALWAYS);
|
||||
getChildren().add(getEditorPane());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreferenceEditorType getPreferenceEditorType()
|
||||
{
|
||||
return PreferenceEditorType.AUDIO_RECORD;
|
||||
}
|
||||
|
||||
private GridPane getEditorPane()
|
||||
{
|
||||
if(mEditorPane == null)
|
||||
{
|
||||
mEditorPane = new GridPane();
|
||||
mEditorPane.setPadding(new Insets(10, 10, 10, 10));
|
||||
mEditorPane.setHgap(10);
|
||||
mEditorPane.setVgap(10);
|
||||
|
||||
Label label = new Label("Audio Recording Format:");
|
||||
mEditorPane.add(label, 0, 0);
|
||||
|
||||
mEditorPane.add(getRecordFormatComboBox(), 1, 0);
|
||||
}
|
||||
|
||||
return mEditorPane;
|
||||
}
|
||||
|
||||
private ComboBox<RecordFormat> getRecordFormatComboBox()
|
||||
{
|
||||
if(mRecordFormatComboBox == null)
|
||||
{
|
||||
mRecordFormatComboBox = new ComboBox<>();
|
||||
mRecordFormatComboBox.getItems().addAll(RecordFormat.values());
|
||||
mRecordFormatComboBox.getSelectionModel().select(mRecordPreference.getAudioRecordFormat());
|
||||
mRecordFormatComboBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<RecordFormat>()
|
||||
{
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends RecordFormat> observable, RecordFormat oldValue, RecordFormat newValue)
|
||||
{
|
||||
mRecordPreference.setAudioRecordFormat(newValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return mRecordFormatComboBox;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
* Copyright (C) 2014-2024 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
|
||||
@@ -36,6 +36,7 @@ public enum PreferenceType
|
||||
PLAYBACK,
|
||||
RADIO_REFERENCE,
|
||||
RECORD,
|
||||
RETENTION,
|
||||
TALKGROUP_FORMAT,
|
||||
TUNER;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
* Copyright (C) 2014-2024 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
|
||||
@@ -32,6 +32,7 @@ import io.github.dsheirer.preference.playback.PlaybackPreference;
|
||||
import io.github.dsheirer.preference.playlist.PlaylistPreference;
|
||||
import io.github.dsheirer.preference.radioreference.RadioReferencePreference;
|
||||
import io.github.dsheirer.preference.record.RecordPreference;
|
||||
import io.github.dsheirer.preference.retention.RetentionPreference;
|
||||
import io.github.dsheirer.preference.source.ChannelMultiFrequencyPreference;
|
||||
import io.github.dsheirer.preference.source.TunerPreference;
|
||||
import io.github.dsheirer.preference.swing.SwingPreference;
|
||||
@@ -78,6 +79,7 @@ public class UserPreferences implements Listener<PreferenceType>
|
||||
private PlaylistPreference mPlaylistPreference;
|
||||
private RadioReferencePreference mRadioReferencePreference;
|
||||
private RecordPreference mRecordPreference;
|
||||
private RetentionPreference mRetentionPreference;
|
||||
private SwingPreference mSwingPreference = new SwingPreference();
|
||||
private TalkgroupFormatPreference mTalkgroupFormatPreference;
|
||||
private TunerPreference mTunerPreference;
|
||||
@@ -216,6 +218,14 @@ public class UserPreferences implements Listener<PreferenceType>
|
||||
return mRecordPreference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Audio/call retention preferences
|
||||
*/
|
||||
public RetentionPreference getRetentionPreference()
|
||||
{
|
||||
return mRetentionPreference;
|
||||
}
|
||||
|
||||
/**
|
||||
* MP3 preferences
|
||||
*/
|
||||
@@ -273,6 +283,7 @@ public class UserPreferences implements Listener<PreferenceType>
|
||||
mPlaylistPreference = new PlaylistPreference(this::receive, mDirectoryPreference);
|
||||
mRadioReferencePreference = new RadioReferencePreference(this::receive);
|
||||
mRecordPreference = new RecordPreference(this::receive);
|
||||
mRetentionPreference = new RetentionPreference(this::receive);
|
||||
mTalkgroupFormatPreference = new TalkgroupFormatPreference(this::receive);
|
||||
mTunerPreference = new TunerPreference(this::receive);
|
||||
mVectorCalibrationPreference = new VectorCalibrationPreference(this::receive);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
* Copyright (C) 2014-2024 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
|
||||
@@ -42,6 +42,7 @@ public class DirectoryPreference extends Preference
|
||||
|
||||
private static final String DIRECTORY_APPLICATION_ROOT = "SDRTrunk";
|
||||
private static final String DIRECTORY_APPLICATION_LOG = "logs";
|
||||
private static final String DIRECTORY_CALL = "calls";
|
||||
private static final String DIRECTORY_CONFIGURATION = "configuration";
|
||||
private static final String DIRECTORY_EVENT_LOG = "event_logs";
|
||||
private static final String DIRECTORY_JMBE = "jmbe";
|
||||
@@ -52,6 +53,7 @@ public class DirectoryPreference extends Preference
|
||||
|
||||
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_CALLS = "directory.event.calls";
|
||||
private static final String PREFERENCE_KEY_DIRECTORY_CONFIGURATION = "directory.configuration";
|
||||
private static final String PREFERENCE_KEY_DIRECTORY_EVENT_LOGS = "directory.event.logs";
|
||||
private static final String PREFERENCE_KEY_DIRECTORY_JMBE = "directory.jmbe";
|
||||
@@ -64,6 +66,7 @@ public class DirectoryPreference extends Preference
|
||||
|
||||
private Path mDirectoryApplicationRoot;
|
||||
private Path mDirectoryApplicationLogs;
|
||||
private Path mDirectoryCalls;
|
||||
private Path mDirectoryConfiguration;
|
||||
private Path mDirectoryEventLogs;
|
||||
private Path mDirectoryJmbe;
|
||||
@@ -125,6 +128,7 @@ public class DirectoryPreference extends Preference
|
||||
private void nullifyApplicationChildDirectories()
|
||||
{
|
||||
mDirectoryApplicationLogs = null;
|
||||
mDirectoryCalls = null;
|
||||
mDirectoryEventLogs = null;
|
||||
mDirectoryJmbe = null;
|
||||
mDirectoryPlaylist = null;
|
||||
@@ -234,6 +238,40 @@ public class DirectoryPreference extends Preference
|
||||
notifyPreferenceUpdated();
|
||||
}
|
||||
|
||||
/**
|
||||
* Path to the folder for storing calls
|
||||
*/
|
||||
public Path getDirectoryCalls()
|
||||
{
|
||||
if(mDirectoryCalls == null)
|
||||
{
|
||||
mDirectoryCalls = getPath(PREFERENCE_KEY_DIRECTORY_CALLS, getDefaultCallDirectory());
|
||||
createDirectory(mDirectoryCalls);
|
||||
}
|
||||
|
||||
return mDirectoryCalls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path to the calls folder
|
||||
*/
|
||||
public void setDirectoryCalls(Path path)
|
||||
{
|
||||
mDirectoryCalls = path;
|
||||
mPreferences.put(PREFERENCE_KEY_DIRECTORY_CALLS, path.toString());
|
||||
notifyPreferenceUpdated();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a stored call directory preference so that the default path can be used again
|
||||
*/
|
||||
public void resetDirectoryCall()
|
||||
{
|
||||
mPreferences.remove(PREFERENCE_KEY_DIRECTORY_CALLS);
|
||||
mDirectoryCalls = null;
|
||||
notifyPreferenceUpdated();
|
||||
}
|
||||
|
||||
/**
|
||||
* Path to the folder for storing configuration files
|
||||
*/
|
||||
@@ -488,6 +526,14 @@ public class DirectoryPreference extends Preference
|
||||
return getDirectoryApplicationRoot().resolve(DIRECTORY_APPLICATION_LOG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default calls directory
|
||||
*/
|
||||
public Path getDefaultCallDirectory()
|
||||
{
|
||||
return getDirectoryApplicationRoot().resolve(DIRECTORY_CALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default configuration directory
|
||||
*/
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
package io.github.dsheirer.preference.playback;
|
||||
|
||||
import io.github.dsheirer.audio.playbackfx.PlaybackView;
|
||||
import io.github.dsheirer.gui.preference.playback.ToneFrequency;
|
||||
import io.github.dsheirer.gui.preference.playback.ToneUtil;
|
||||
import io.github.dsheirer.gui.preference.playback.ToneVolume;
|
||||
import io.github.dsheirer.gui.preference.audio.playback.ToneFrequency;
|
||||
import io.github.dsheirer.gui.preference.audio.playback.ToneUtil;
|
||||
import io.github.dsheirer.gui.preference.audio.playback.ToneVolume;
|
||||
import io.github.dsheirer.preference.Preference;
|
||||
import io.github.dsheirer.preference.PreferenceType;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.preference.retention;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* Age units
|
||||
*/
|
||||
public enum AgeUnits
|
||||
{
|
||||
DAYS,
|
||||
WEEKS,
|
||||
MONTHS,
|
||||
YEARS;
|
||||
|
||||
/**
|
||||
* Returns a duration value in milliseconds that represents the value in the current units.
|
||||
* @param value to calculate
|
||||
* @return milliseconds of duration representing the age unit and value.
|
||||
*/
|
||||
public long getDuration(int value)
|
||||
{
|
||||
return switch(this)
|
||||
{
|
||||
case DAYS -> Duration.ofDays(value).toMillis();
|
||||
case WEEKS -> Duration.ofDays(7).toMillis() * value;
|
||||
case MONTHS -> Duration.ofDays(31).toMillis() * value;
|
||||
case YEARS -> Duration.ofDays(365).toMillis() * value;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.preference.retention;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* Retention (ie age-off) policy for managing call events and related audio recording files.
|
||||
*/
|
||||
public enum RetentionPolicy
|
||||
{
|
||||
AGE("Age"),
|
||||
SIZE("Directory Size"),
|
||||
AGE_AND_SIZE("Age & Directory Size");
|
||||
|
||||
private String mLabel;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param label to display for the entry
|
||||
*/
|
||||
RetentionPolicy(String label)
|
||||
{
|
||||
mLabel = label;
|
||||
}
|
||||
|
||||
public static EnumSet<RetentionPolicy> AGE_POLICIES = EnumSet.of(AGE, AGE_AND_SIZE);
|
||||
public static EnumSet<RetentionPolicy> SIZE_POLICIES = EnumSet.of(SIZE, AGE_AND_SIZE);
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the entry is an age-based retention policy.
|
||||
*/
|
||||
public boolean isAgePolicy()
|
||||
{
|
||||
return AGE_POLICIES.contains(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the entry is a size-based retention policy.
|
||||
*/
|
||||
public boolean isSizePolicy()
|
||||
{
|
||||
return SIZE_POLICIES.contains(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.preference.retention;
|
||||
|
||||
import io.github.dsheirer.preference.Preference;
|
||||
import io.github.dsheirer.preference.PreferenceType;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import java.util.prefs.Preferences;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* User preferences for (audio) retention and age-off
|
||||
*/
|
||||
public class RetentionPreference extends Preference
|
||||
{
|
||||
private static final String PREFERENCE_KEY_AUDIO_RETENTION_POLICY = "audio.retention.policy";
|
||||
private static final String PREFERENCE_KEY_AGE_UNITS = "call.age.units";
|
||||
private static final String PREFERENCE_KEY_AGE_VALUE = "call.age.value";
|
||||
private static final String PREFERENCE_KEY_SIZE_UNITS = "call.size.units";
|
||||
private static final String PREFERENCE_KEY_MAX_CALL_DIRECTORY_SIZE = "audio.max.directory.size.gb";
|
||||
private static final long GIGABYTES = 1024l * 1024l * 1024l;
|
||||
private static final long MEGABYTES = 1024l * 1024l;
|
||||
private final static Logger mLog = LoggerFactory.getLogger(RetentionPreference.class);
|
||||
private Preferences mPreferences = Preferences.userNodeForPackage(RetentionPreference.class);
|
||||
private RetentionPolicy mRetentionPolicy;
|
||||
private AgeUnits mAgeUnits;
|
||||
private Integer mAgeValue;
|
||||
private SizeUnits mSizeUnits;
|
||||
private Integer mMaxDirectorySize;
|
||||
|
||||
/**
|
||||
* Constructs this preference with an update listener
|
||||
* @param updateListener to receive notifications whenever these preferences change
|
||||
*/
|
||||
public RetentionPreference(Listener<PreferenceType> updateListener)
|
||||
{
|
||||
super(updateListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreferenceType getPreferenceType()
|
||||
{
|
||||
return PreferenceType.RETENTION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retention policy to use for age-off of call events and related audio recordings.
|
||||
* @return retention policy.
|
||||
*/
|
||||
public RetentionPolicy getRetentionPolicy()
|
||||
{
|
||||
if(mRetentionPolicy == null)
|
||||
{
|
||||
String raw = mPreferences.get(PREFERENCE_KEY_AUDIO_RETENTION_POLICY, null);
|
||||
|
||||
if(raw != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
mRetentionPolicy = RetentionPolicy.valueOf(raw);
|
||||
}
|
||||
catch(Exception e )
|
||||
{
|
||||
//Do nothing ... use the default.
|
||||
}
|
||||
}
|
||||
|
||||
if(mRetentionPolicy == null)
|
||||
{
|
||||
mRetentionPolicy = RetentionPolicy.AGE_AND_SIZE; //Default value
|
||||
}
|
||||
}
|
||||
|
||||
return mRetentionPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the audio/call retention policy.
|
||||
* @param retentionPolicy to use
|
||||
*/
|
||||
public void setRetentionPolicy(RetentionPolicy retentionPolicy)
|
||||
{
|
||||
mRetentionPolicy = retentionPolicy;
|
||||
mPreferences.put(PREFERENCE_KEY_AUDIO_RETENTION_POLICY, retentionPolicy.name());
|
||||
notifyPreferenceUpdated();
|
||||
}
|
||||
|
||||
/**
|
||||
* Age units for call/audio age-off
|
||||
* @return age units.
|
||||
*/
|
||||
public AgeUnits getAgeUnits()
|
||||
{
|
||||
if(mAgeUnits == null)
|
||||
{
|
||||
String raw = mPreferences.get(PREFERENCE_KEY_AGE_UNITS, null);
|
||||
|
||||
if(raw != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
mAgeUnits = AgeUnits.valueOf(raw);
|
||||
}
|
||||
catch(Exception e )
|
||||
{
|
||||
//Do nothing ... use the default.
|
||||
}
|
||||
}
|
||||
|
||||
if(mAgeUnits == null)
|
||||
{
|
||||
mAgeUnits = AgeUnits.DAYS; //Default value
|
||||
}
|
||||
}
|
||||
|
||||
return mAgeUnits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the age units.
|
||||
* @param ageUnits to set
|
||||
*/
|
||||
public void setAgeUnits(AgeUnits ageUnits)
|
||||
{
|
||||
mAgeUnits = ageUnits;
|
||||
mPreferences.put(PREFERENCE_KEY_AGE_UNITS, mAgeUnits.name());
|
||||
notifyPreferenceUpdated();
|
||||
}
|
||||
|
||||
/**
|
||||
* Age value
|
||||
* @return age value.
|
||||
*/
|
||||
public int getAgeValue()
|
||||
{
|
||||
if(mAgeValue == null)
|
||||
{
|
||||
mAgeValue = mPreferences.getInt(PREFERENCE_KEY_AGE_VALUE, 30);
|
||||
}
|
||||
|
||||
return mAgeValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the age value.
|
||||
* @param value to set
|
||||
*/
|
||||
public void setAgeValue(int value)
|
||||
{
|
||||
mPreferences.putInt(PREFERENCE_KEY_AGE_VALUE, value);
|
||||
notifyPreferenceUpdated();
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximum directory size in gigabytes (GB)
|
||||
* @return size
|
||||
*/
|
||||
public int getMaxDirectorySize()
|
||||
{
|
||||
if(mMaxDirectorySize == null)
|
||||
{
|
||||
mMaxDirectorySize = mPreferences.getInt(PREFERENCE_KEY_MAX_CALL_DIRECTORY_SIZE, 5);
|
||||
}
|
||||
|
||||
return mMaxDirectorySize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Objective file system directory size.
|
||||
* Note: value is adjusted for byte-2-kilobyte (1000 vs 1024) conversion.
|
||||
* @return objective directory size.
|
||||
*/
|
||||
public long getObjectiveDirectorySize()
|
||||
{
|
||||
if(getSizeUnits() == SizeUnits.GB)
|
||||
{
|
||||
return getMaxDirectorySize() * GIGABYTES;
|
||||
}
|
||||
else
|
||||
{
|
||||
return getMaxDirectorySize() * MEGABYTES;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum call directory size in gigabytes (GB)
|
||||
* @param sizeGb for the directory.
|
||||
*/
|
||||
public void setMaxDirectorySize(int sizeGb)
|
||||
{
|
||||
mMaxDirectorySize = sizeGb;
|
||||
mPreferences.putInt(PREFERENCE_KEY_MAX_CALL_DIRECTORY_SIZE, sizeGb);
|
||||
notifyPreferenceUpdated();
|
||||
}
|
||||
|
||||
/**
|
||||
* Size units for directory sizing.
|
||||
* @return size units or default of gigabytes.
|
||||
*/
|
||||
public SizeUnits getSizeUnits()
|
||||
{
|
||||
if(mSizeUnits == null)
|
||||
{
|
||||
String raw = mPreferences.get(PREFERENCE_KEY_SIZE_UNITS, null);
|
||||
|
||||
if(raw != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
mSizeUnits = SizeUnits.valueOf(raw);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
//Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
if(mSizeUnits == null)
|
||||
{
|
||||
mSizeUnits = SizeUnits.GB; //default
|
||||
}
|
||||
}
|
||||
|
||||
return mSizeUnits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size units for directory sizing.
|
||||
* @param sizeUnits to set
|
||||
*/
|
||||
public void setSizeUnits(SizeUnits sizeUnits)
|
||||
{
|
||||
mSizeUnits = sizeUnits;
|
||||
mPreferences.put(PREFERENCE_KEY_SIZE_UNITS, mSizeUnits.name());
|
||||
notifyPreferenceUpdated();;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.preference.retention;
|
||||
|
||||
/**
|
||||
* Size units
|
||||
*/
|
||||
public enum SizeUnits
|
||||
{
|
||||
MB,
|
||||
GB;
|
||||
}
|
||||
Reference in New Issue
Block a user