@@ -36,6 +36,7 @@ import org.json.JSONObject
36
36
import java.lang.Process
37
37
import java.nio.ByteBuffer
38
38
import java.time.Duration
39
+ import java.util.concurrent.atomic.AtomicReference
39
40
import android.os.Process as AndroidProcess
40
41
41
42
/* *
@@ -72,22 +73,40 @@ class RecorderThread(
72
73
private set
73
74
@Volatile private var isCancelled = false
74
75
76
+ enum class KeepState {
77
+ KEEP ,
78
+ DISCARD ,
79
+ DISCARD_TOO_SHORT ,
80
+ }
81
+
75
82
/* *
76
83
* Whether to preserve the recording.
77
84
*
78
85
* This is initially set to null while the [RecordRule]s are being processed. Once computed,
79
86
* this field is set to the computed value. The value can be changed, including from other
80
87
* threads, in case the user wants to override the rules during the middle of the call.
81
88
*/
82
- @Volatile var keepRecording: Boolean? = null
89
+ private val _keepRecording = AtomicReference <KeepState >()
90
+ var keepRecording: KeepState ?
91
+ get() = _keepRecording .get()
83
92
set(value) {
84
93
require(value != null )
85
94
86
- field = value
95
+ _keepRecording .set(value)
96
+ Log .d(tag, " Keep state updated: $value " )
97
+
98
+ listener.onRecordingStateChanged(this )
99
+ }
100
+
101
+ private fun keepRecordingCompareAndSet (expected : KeepState ? , value : KeepState ? ) {
102
+ require(value != null )
103
+
104
+ if (_keepRecording .compareAndSet(expected, value)) {
87
105
Log .d(tag, " Keep state updated: $value " )
88
106
89
107
listener.onRecordingStateChanged(this )
90
108
}
109
+ }
91
110
92
111
/* *
93
112
* Whether the user paused the recording.
@@ -123,6 +142,8 @@ class RecorderThread(
123
142
val outputPath: OutputPath
124
143
get() = outputFilenameGenerator.generate(callMetadataCollector.callMetadata)
125
144
145
+ private val minDuration: Int
146
+
126
147
// Format
127
148
private val format: Format
128
149
private val formatParam: UInt?
@@ -136,6 +157,8 @@ class RecorderThread(
136
157
init {
137
158
Log .i(tag, " Created thread for call: $parentCall " )
138
159
160
+ minDuration = prefs.minDuration
161
+
139
162
val savedFormat = Format .fromPreferences(prefs)
140
163
format = savedFormat.first
141
164
formatParam = savedFormat.second
@@ -165,14 +188,27 @@ class RecorderThread(
165
188
Log .i(tag, " Evaluating record rules for ${numbers.size} phone number(s)" )
166
189
167
190
val rules = prefs.recordRules ? : Preferences .DEFAULT_RECORD_RULES
168
- keepRecording = try {
191
+ val keep = try {
169
192
RecordRule .evaluate(context, rules, numbers)
170
193
} catch (e: Exception ) {
171
194
Log .w(tag, " Failed to evaluate record rules" , e)
172
195
// Err on the side of caution
173
196
true
174
197
}
175
198
199
+ keepRecordingCompareAndSet(
200
+ null ,
201
+ if (keep) {
202
+ if (minDuration > 0 ) {
203
+ KeepState .DISCARD_TOO_SHORT
204
+ } else {
205
+ KeepState .KEEP
206
+ }
207
+ } else {
208
+ KeepState .DISCARD
209
+ },
210
+ )
211
+
176
212
listener.onRecordingStateChanged(this )
177
213
}
178
214
@@ -214,7 +250,7 @@ class RecorderThread(
214
250
callMetadataCollector.update(true )
215
251
val finalPath = outputPath
216
252
217
- if (keepRecording != false ) {
253
+ if (keepRecording == KeepState . KEEP ) {
218
254
dirUtils.tryMoveToOutputDir(
219
255
outputDocFile,
220
256
finalPath.value,
@@ -646,6 +682,11 @@ class RecorderThread(
646
682
" record=${recordElapsed / 1_000_000.0 } ms, " +
647
683
" encode=${encodeElapsed / 1_000_000.0 } ms" )
648
684
}
685
+
686
+ val secondsEncoded = numFramesEncoded / audioRecord.sampleRate / audioRecord.channelCount
687
+ if (secondsEncoded >= minDuration) {
688
+ keepRecordingCompareAndSet(KeepState .DISCARD_TOO_SHORT , KeepState .KEEP )
689
+ }
649
690
}
650
691
651
692
if (wasReadSamplesError) {
0 commit comments