fix(android): strengthen user voice indicators
This commit is contained in:
@@ -2197,34 +2197,75 @@ class _SnapshotView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
for (final cl in byChannel[ch.id] ?? const <rust.BridgeClient>[])
|
for (final cl in byChannel[ch.id] ?? const <rust.BridgeClient>[])
|
||||||
Padding(
|
_clientTile(theme, cl, (depthById[ch.id] ?? 0) * indentPerLevel),
|
||||||
padding: EdgeInsets.only(
|
|
||||||
left: ((depthById[ch.id] ?? 0) * indentPerLevel) + 64,
|
|
||||||
),
|
|
||||||
child: ListTile(
|
|
||||||
dense: true,
|
|
||||||
visualDensity: VisualDensity.compact,
|
|
||||||
leading: _clientVoiceStatusIcon(theme, cl),
|
|
||||||
title: Text(
|
|
||||||
cl.name,
|
|
||||||
style: cl.isServerQuery
|
|
||||||
? TextStyle(color: theme.colorScheme.onSurfaceVariant)
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _clientVoiceStatusIcon(ThemeData theme, rust.BridgeClient client) {
|
Widget _clientTile(
|
||||||
|
ThemeData theme,
|
||||||
|
rust.BridgeClient client,
|
||||||
|
double channelIndent,
|
||||||
|
) {
|
||||||
|
final status = _clientVoiceStatusIcon(theme, client);
|
||||||
|
final nameStyle = client.isServerQuery
|
||||||
|
? TextStyle(color: theme.colorScheme.onSurfaceVariant)
|
||||||
|
: status.isSpeaking
|
||||||
|
? TextStyle(
|
||||||
|
color: theme.colorScheme.primary,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
final tile = ListTile(
|
||||||
|
dense: true,
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
leading: status.icon,
|
||||||
|
title: Text(client.name, style: nameStyle),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.only(left: channelIndent + 64, right: 8),
|
||||||
|
child: AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 120),
|
||||||
|
curve: Curves.easeOut,
|
||||||
|
decoration: status.isSpeaking
|
||||||
|
? BoxDecoration(
|
||||||
|
color: theme.colorScheme.primaryContainer.withValues(
|
||||||
|
alpha: 0.45,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(
|
||||||
|
color: theme.colorScheme.primary.withValues(alpha: 0.55),
|
||||||
|
width: 1.2,
|
||||||
|
),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: theme.colorScheme.primary.withValues(alpha: 0.14),
|
||||||
|
blurRadius: 8,
|
||||||
|
spreadRadius: 1,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
child: tile,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
({Widget icon, bool isSpeaking}) _clientVoiceStatusIcon(
|
||||||
|
ThemeData theme,
|
||||||
|
rust.BridgeClient client,
|
||||||
|
) {
|
||||||
final isSelf = client.id == snapshot.ownClientId;
|
final isSelf = client.id == snapshot.ownClientId;
|
||||||
|
final inCurrentChannel = client.channel == currentVoiceChannelId;
|
||||||
final outputMuted = isSelf ? localOutputMuted : client.outputMuted;
|
final outputMuted = isSelf ? localOutputMuted : client.outputMuted;
|
||||||
final inputMuted = isSelf ? localInputMuted : client.inputMuted;
|
final inputMuted = isSelf ? localInputMuted : client.inputMuted;
|
||||||
final speaking = isSelf
|
final rawSpeaking = isSelf
|
||||||
? (audioStats?.pttActive ?? false)
|
? (audioStats?.pttActive ?? false)
|
||||||
: client.isSpeaking;
|
: client.isSpeaking;
|
||||||
|
final speaking = rawSpeaking && !outputMuted && !inputMuted;
|
||||||
|
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
final Color color;
|
final Color color;
|
||||||
@@ -2238,18 +2279,25 @@ class _SnapshotView extends StatelessWidget {
|
|||||||
color = theme.colorScheme.error;
|
color = theme.colorScheme.error;
|
||||||
tooltip = 'Microphone muted';
|
tooltip = 'Microphone muted';
|
||||||
} else if (speaking) {
|
} else if (speaking) {
|
||||||
icon = Icons.volume_up;
|
icon = isSelf ? Icons.mic : Icons.volume_up;
|
||||||
color = theme.colorScheme.primary;
|
color = theme.colorScheme.primary;
|
||||||
tooltip = 'Speaking';
|
tooltip = 'Speaking';
|
||||||
} else {
|
} else if (inCurrentChannel) {
|
||||||
icon = Icons.volume_up_outlined;
|
icon = isSelf ? Icons.mic_none : Icons.volume_up_outlined;
|
||||||
color = theme.colorScheme.onSurfaceVariant;
|
color = theme.colorScheme.onSurfaceVariant;
|
||||||
tooltip = 'Not speaking';
|
tooltip = 'Not speaking';
|
||||||
|
} else {
|
||||||
|
icon = Icons.person_outline;
|
||||||
|
color = theme.colorScheme.onSurfaceVariant;
|
||||||
|
tooltip = 'Outside current channel';
|
||||||
}
|
}
|
||||||
|
|
||||||
return Tooltip(
|
return (
|
||||||
message: tooltip,
|
icon: Tooltip(
|
||||||
child: Icon(icon, size: 18, color: color),
|
message: tooltip,
|
||||||
|
child: Icon(icon, size: 18, color: color),
|
||||||
|
),
|
||||||
|
isSpeaking: speaking,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user