Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions src/App/Backlog.Mobile.UI/Components/NoteRecordingBar.razor
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,24 @@

@if (Recording.IsRecording(NoteId))
{
<RecordingBar State="@BarState" Elapsed="@Recording.Elapsed" Level="@Recording.Level"
<RecordingBar State="@BarState" Elapsed="@Recording.Elapsed" Level="@Recording.Level" Notice="@Recording.Notice"
OnPause="PauseAsync" OnResume="ResumeAsync" OnStop="StopAsync" TestId="@TestId" />
}
else if (Recording.LastFailure is { } failure && failure.NoteId == NoteId)
else
{
<RecordingBar State="RecordingBarState.Failed" Elapsed="@failure.Elapsed" Error="@failure.Error"
OnDismiss="() => Recording.DismissFailure(NoteId)" TestId="@TestId" />
@if (Recording.LastFailure is { } failure && failure.NoteId == NoteId)
{
<RecordingBar State="RecordingBarState.Failed" Elapsed="@failure.Elapsed" Error="@failure.Error"
OnDismiss="() => Recording.DismissFailure(NoteId)" TestId="@TestId" />
}

@* The note was deleted elsewhere while it recorded; Stop made a new note for the
recording, and this page is the one place the person still looks for it. *@
@if (Recording.LastMoved is { } moved && moved.FromNoteId == NoteId)
{
<Alert Message="@RecordingNotices.Moved(moved.Title)" Role="status" BaseClass="capture__notice"
TestId="@(string.IsNullOrWhiteSpace(TestId) ? null : $"{TestId}-moved")" />
}
}

@code {
Expand Down
39 changes: 39 additions & 0 deletions src/App/Backlog.Mobile.UI/Recordings/FlatLevelDetector.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
namespace Backlog.Mobile.UI.Recordings;

/// <summary>
/// Notices a recording that hears nothing at all: a level of exactly 0 held for
/// <see cref="Hold"/> while recording. A room is never that quiet — the microphone's own noise
/// moves the level — so a level that stays at 0 means another app has the microphone, on a
/// recorder that cannot say so itself (Android before 10, and the browser).
/// </summary>
/// <remarks>Fed from the recorder's own threads, so every reading is taken under a lock.</remarks>
public sealed class FlatLevelDetector
{
/// <summary>Ten seconds: long enough that a pause in speech never trips it.</summary>
public static readonly TimeSpan Hold = TimeSpan.FromSeconds(10);

private readonly Lock _gate = new();
private DateTimeOffset? _silentSince;

/// <summary>Takes one reading; answers whether the level has been 0 for <see cref="Hold"/>.</summary>
public bool Observe(double level, DateTimeOffset now)
{
lock (_gate)
{
if (level > 0)
{
_silentSince = null;
return false;
}

_silentSince ??= now;
return now - _silentSince.Value >= Hold;
}
}

/// <summary>Forgets the silence so far: a pause or a resume starts the count again.</summary>
public void Reset()
{
lock (_gate) _silentSince = null;
}
}
Loading
Loading