From 315e5b29082e0f0cacbe6032a4bcf78949e21cfc Mon Sep 17 00:00:00 2001 From: JulianAlcala Date: Tue, 21 Apr 2026 17:58:20 +0200 Subject: [PATCH] fix(volume): use capabilities max per slider and fix 0-10 scale --- .../state/volume_control_view_model.dart | 13 ++++-- .../state/volume_control_view_state.dart | 9 ++-- .../volume_control_view_state.freezed.dart | 45 +++++++++++-------- .../presentation/volume_control_screen.dart | 13 +++--- 4 files changed, 51 insertions(+), 29 deletions(-) diff --git a/modules/legacy/modules/device_management/lib/src/features/volume_control/presentation/state/volume_control_view_model.dart b/modules/legacy/modules/device_management/lib/src/features/volume_control/presentation/state/volume_control_view_model.dart index fa9bfae0..3c0fcadb 100644 --- a/modules/legacy/modules/device_management/lib/src/features/volume_control/presentation/state/volume_control_view_model.dart +++ b/modules/legacy/modules/device_management/lib/src/features/volume_control/presentation/state/volume_control_view_model.dart @@ -31,13 +31,20 @@ class VolumeControlViewModel extends Notifier { if (device == null) return; final volume = device.settings.volume; + final capVolume = device.capabilities?.volume; + final maxMedia = capVolume?.media ?? 10; + final maxRingtone = capVolume?.ringtone ?? 10; + final maxAlarm = capVolume?.alarm ?? 10; state = state.copyWith( isLoading: false, device: device, - media: volume.media, - ringtone: volume.ringtone, - alarm: volume.alarm, + media: volume.media.clamp(0, maxMedia), + ringtone: volume.ringtone.clamp(0, maxRingtone), + alarm: volume.alarm.clamp(0, maxAlarm), + maxMedia: maxMedia, + maxRingtone: maxRingtone, + maxAlarm: maxAlarm, ); } catch (e) { if (!ref.mounted) return; diff --git a/modules/legacy/modules/device_management/lib/src/features/volume_control/presentation/state/volume_control_view_state.dart b/modules/legacy/modules/device_management/lib/src/features/volume_control/presentation/state/volume_control_view_state.dart index b426d5c3..1a019706 100644 --- a/modules/legacy/modules/device_management/lib/src/features/volume_control/presentation/state/volume_control_view_state.dart +++ b/modules/legacy/modules/device_management/lib/src/features/volume_control/presentation/state/volume_control_view_state.dart @@ -9,9 +9,12 @@ abstract class VolumeControlViewState with _$VolumeControlViewState { @Default(true) bool isLoading, @Default(false) bool isComplete, DeviceEntity? device, - @Default(50) int media, - @Default(50) int ringtone, - @Default(50) int alarm, + @Default(5) int media, + @Default(5) int ringtone, + @Default(5) int alarm, + @Default(10) int maxMedia, + @Default(10) int maxRingtone, + @Default(10) int maxAlarm, @Default('') String errorMessage, }) = _VolumeControlViewState; } diff --git a/modules/legacy/modules/device_management/lib/src/features/volume_control/presentation/state/volume_control_view_state.freezed.dart b/modules/legacy/modules/device_management/lib/src/features/volume_control/presentation/state/volume_control_view_state.freezed.dart index 75fe70fc..5ac7a745 100644 --- a/modules/legacy/modules/device_management/lib/src/features/volume_control/presentation/state/volume_control_view_state.freezed.dart +++ b/modules/legacy/modules/device_management/lib/src/features/volume_control/presentation/state/volume_control_view_state.freezed.dart @@ -14,7 +14,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$VolumeControlViewState { - bool get isLoading; bool get isComplete; DeviceEntity? get device; int get media; int get ringtone; int get alarm; String get errorMessage; + bool get isLoading; bool get isComplete; DeviceEntity? get device; int get media; int get ringtone; int get alarm; int get maxMedia; int get maxRingtone; int get maxAlarm; String get errorMessage; /// Create a copy of VolumeControlViewState /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -25,16 +25,16 @@ $VolumeControlViewStateCopyWith get copyWith => _$Volume @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is VolumeControlViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isComplete, isComplete) || other.isComplete == isComplete)&&(identical(other.device, device) || other.device == device)&&(identical(other.media, media) || other.media == media)&&(identical(other.ringtone, ringtone) || other.ringtone == ringtone)&&(identical(other.alarm, alarm) || other.alarm == alarm)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is VolumeControlViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isComplete, isComplete) || other.isComplete == isComplete)&&(identical(other.device, device) || other.device == device)&&(identical(other.media, media) || other.media == media)&&(identical(other.ringtone, ringtone) || other.ringtone == ringtone)&&(identical(other.alarm, alarm) || other.alarm == alarm)&&(identical(other.maxMedia, maxMedia) || other.maxMedia == maxMedia)&&(identical(other.maxRingtone, maxRingtone) || other.maxRingtone == maxRingtone)&&(identical(other.maxAlarm, maxAlarm) || other.maxAlarm == maxAlarm)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)); } @override -int get hashCode => Object.hash(runtimeType,isLoading,isComplete,device,media,ringtone,alarm,errorMessage); +int get hashCode => Object.hash(runtimeType,isLoading,isComplete,device,media,ringtone,alarm,maxMedia,maxRingtone,maxAlarm,errorMessage); @override String toString() { - return 'VolumeControlViewState(isLoading: $isLoading, isComplete: $isComplete, device: $device, media: $media, ringtone: $ringtone, alarm: $alarm, errorMessage: $errorMessage)'; + return 'VolumeControlViewState(isLoading: $isLoading, isComplete: $isComplete, device: $device, media: $media, ringtone: $ringtone, alarm: $alarm, maxMedia: $maxMedia, maxRingtone: $maxRingtone, maxAlarm: $maxAlarm, errorMessage: $errorMessage)'; } @@ -45,7 +45,7 @@ abstract mixin class $VolumeControlViewStateCopyWith<$Res> { factory $VolumeControlViewStateCopyWith(VolumeControlViewState value, $Res Function(VolumeControlViewState) _then) = _$VolumeControlViewStateCopyWithImpl; @useResult $Res call({ - bool isLoading, bool isComplete, DeviceEntity? device, int media, int ringtone, int alarm, String errorMessage + bool isLoading, bool isComplete, DeviceEntity? device, int media, int ringtone, int alarm, int maxMedia, int maxRingtone, int maxAlarm, String errorMessage }); @@ -62,7 +62,7 @@ class _$VolumeControlViewStateCopyWithImpl<$Res> /// Create a copy of VolumeControlViewState /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? isLoading = null,Object? isComplete = null,Object? device = freezed,Object? media = null,Object? ringtone = null,Object? alarm = null,Object? errorMessage = null,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? isLoading = null,Object? isComplete = null,Object? device = freezed,Object? media = null,Object? ringtone = null,Object? alarm = null,Object? maxMedia = null,Object? maxRingtone = null,Object? maxAlarm = null,Object? errorMessage = null,}) { return _then(_self.copyWith( isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable as bool,isComplete: null == isComplete ? _self.isComplete : isComplete // ignore: cast_nullable_to_non_nullable @@ -70,6 +70,9 @@ as bool,device: freezed == device ? _self.device : device // ignore: cast_nullab as DeviceEntity?,media: null == media ? _self.media : media // ignore: cast_nullable_to_non_nullable as int,ringtone: null == ringtone ? _self.ringtone : ringtone // ignore: cast_nullable_to_non_nullable as int,alarm: null == alarm ? _self.alarm : alarm // ignore: cast_nullable_to_non_nullable +as int,maxMedia: null == maxMedia ? _self.maxMedia : maxMedia // ignore: cast_nullable_to_non_nullable +as int,maxRingtone: null == maxRingtone ? _self.maxRingtone : maxRingtone // ignore: cast_nullable_to_non_nullable +as int,maxAlarm: null == maxAlarm ? _self.maxAlarm : maxAlarm // ignore: cast_nullable_to_non_nullable as int,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable as String, )); @@ -168,10 +171,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( bool isLoading, bool isComplete, DeviceEntity? device, int media, int ringtone, int alarm, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( bool isLoading, bool isComplete, DeviceEntity? device, int media, int ringtone, int alarm, int maxMedia, int maxRingtone, int maxAlarm, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _VolumeControlViewState() when $default != null: -return $default(_that.isLoading,_that.isComplete,_that.device,_that.media,_that.ringtone,_that.alarm,_that.errorMessage);case _: +return $default(_that.isLoading,_that.isComplete,_that.device,_that.media,_that.ringtone,_that.alarm,_that.maxMedia,_that.maxRingtone,_that.maxAlarm,_that.errorMessage);case _: return orElse(); } @@ -189,10 +192,10 @@ return $default(_that.isLoading,_that.isComplete,_that.device,_that.media,_that. /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( bool isLoading, bool isComplete, DeviceEntity? device, int media, int ringtone, int alarm, String errorMessage) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( bool isLoading, bool isComplete, DeviceEntity? device, int media, int ringtone, int alarm, int maxMedia, int maxRingtone, int maxAlarm, String errorMessage) $default,) {final _that = this; switch (_that) { case _VolumeControlViewState(): -return $default(_that.isLoading,_that.isComplete,_that.device,_that.media,_that.ringtone,_that.alarm,_that.errorMessage);case _: +return $default(_that.isLoading,_that.isComplete,_that.device,_that.media,_that.ringtone,_that.alarm,_that.maxMedia,_that.maxRingtone,_that.maxAlarm,_that.errorMessage);case _: throw StateError('Unexpected subclass'); } @@ -209,10 +212,10 @@ return $default(_that.isLoading,_that.isComplete,_that.device,_that.media,_that. /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( bool isLoading, bool isComplete, DeviceEntity? device, int media, int ringtone, int alarm, String errorMessage)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( bool isLoading, bool isComplete, DeviceEntity? device, int media, int ringtone, int alarm, int maxMedia, int maxRingtone, int maxAlarm, String errorMessage)? $default,) {final _that = this; switch (_that) { case _VolumeControlViewState() when $default != null: -return $default(_that.isLoading,_that.isComplete,_that.device,_that.media,_that.ringtone,_that.alarm,_that.errorMessage);case _: +return $default(_that.isLoading,_that.isComplete,_that.device,_that.media,_that.ringtone,_that.alarm,_that.maxMedia,_that.maxRingtone,_that.maxAlarm,_that.errorMessage);case _: return null; } @@ -224,7 +227,7 @@ return $default(_that.isLoading,_that.isComplete,_that.device,_that.media,_that. class _VolumeControlViewState implements VolumeControlViewState { - const _VolumeControlViewState({this.isLoading = true, this.isComplete = false, this.device, this.media = 50, this.ringtone = 50, this.alarm = 50, this.errorMessage = ''}); + const _VolumeControlViewState({this.isLoading = true, this.isComplete = false, this.device, this.media = 5, this.ringtone = 5, this.alarm = 5, this.maxMedia = 10, this.maxRingtone = 10, this.maxAlarm = 10, this.errorMessage = ''}); @override@JsonKey() final bool isLoading; @@ -233,6 +236,9 @@ class _VolumeControlViewState implements VolumeControlViewState { @override@JsonKey() final int media; @override@JsonKey() final int ringtone; @override@JsonKey() final int alarm; +@override@JsonKey() final int maxMedia; +@override@JsonKey() final int maxRingtone; +@override@JsonKey() final int maxAlarm; @override@JsonKey() final String errorMessage; /// Create a copy of VolumeControlViewState @@ -245,16 +251,16 @@ _$VolumeControlViewStateCopyWith<_VolumeControlViewState> get copyWith => __$Vol @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _VolumeControlViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isComplete, isComplete) || other.isComplete == isComplete)&&(identical(other.device, device) || other.device == device)&&(identical(other.media, media) || other.media == media)&&(identical(other.ringtone, ringtone) || other.ringtone == ringtone)&&(identical(other.alarm, alarm) || other.alarm == alarm)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _VolumeControlViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isComplete, isComplete) || other.isComplete == isComplete)&&(identical(other.device, device) || other.device == device)&&(identical(other.media, media) || other.media == media)&&(identical(other.ringtone, ringtone) || other.ringtone == ringtone)&&(identical(other.alarm, alarm) || other.alarm == alarm)&&(identical(other.maxMedia, maxMedia) || other.maxMedia == maxMedia)&&(identical(other.maxRingtone, maxRingtone) || other.maxRingtone == maxRingtone)&&(identical(other.maxAlarm, maxAlarm) || other.maxAlarm == maxAlarm)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)); } @override -int get hashCode => Object.hash(runtimeType,isLoading,isComplete,device,media,ringtone,alarm,errorMessage); +int get hashCode => Object.hash(runtimeType,isLoading,isComplete,device,media,ringtone,alarm,maxMedia,maxRingtone,maxAlarm,errorMessage); @override String toString() { - return 'VolumeControlViewState(isLoading: $isLoading, isComplete: $isComplete, device: $device, media: $media, ringtone: $ringtone, alarm: $alarm, errorMessage: $errorMessage)'; + return 'VolumeControlViewState(isLoading: $isLoading, isComplete: $isComplete, device: $device, media: $media, ringtone: $ringtone, alarm: $alarm, maxMedia: $maxMedia, maxRingtone: $maxRingtone, maxAlarm: $maxAlarm, errorMessage: $errorMessage)'; } @@ -265,7 +271,7 @@ abstract mixin class _$VolumeControlViewStateCopyWith<$Res> implements $VolumeCo factory _$VolumeControlViewStateCopyWith(_VolumeControlViewState value, $Res Function(_VolumeControlViewState) _then) = __$VolumeControlViewStateCopyWithImpl; @override @useResult $Res call({ - bool isLoading, bool isComplete, DeviceEntity? device, int media, int ringtone, int alarm, String errorMessage + bool isLoading, bool isComplete, DeviceEntity? device, int media, int ringtone, int alarm, int maxMedia, int maxRingtone, int maxAlarm, String errorMessage }); @@ -282,7 +288,7 @@ class __$VolumeControlViewStateCopyWithImpl<$Res> /// Create a copy of VolumeControlViewState /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? isLoading = null,Object? isComplete = null,Object? device = freezed,Object? media = null,Object? ringtone = null,Object? alarm = null,Object? errorMessage = null,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? isLoading = null,Object? isComplete = null,Object? device = freezed,Object? media = null,Object? ringtone = null,Object? alarm = null,Object? maxMedia = null,Object? maxRingtone = null,Object? maxAlarm = null,Object? errorMessage = null,}) { return _then(_VolumeControlViewState( isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable as bool,isComplete: null == isComplete ? _self.isComplete : isComplete // ignore: cast_nullable_to_non_nullable @@ -290,6 +296,9 @@ as bool,device: freezed == device ? _self.device : device // ignore: cast_nullab as DeviceEntity?,media: null == media ? _self.media : media // ignore: cast_nullable_to_non_nullable as int,ringtone: null == ringtone ? _self.ringtone : ringtone // ignore: cast_nullable_to_non_nullable as int,alarm: null == alarm ? _self.alarm : alarm // ignore: cast_nullable_to_non_nullable +as int,maxMedia: null == maxMedia ? _self.maxMedia : maxMedia // ignore: cast_nullable_to_non_nullable +as int,maxRingtone: null == maxRingtone ? _self.maxRingtone : maxRingtone // ignore: cast_nullable_to_non_nullable +as int,maxAlarm: null == maxAlarm ? _self.maxAlarm : maxAlarm // ignore: cast_nullable_to_non_nullable as int,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable as String, )); diff --git a/modules/legacy/modules/device_management/lib/src/features/volume_control/presentation/volume_control_screen.dart b/modules/legacy/modules/device_management/lib/src/features/volume_control/presentation/volume_control_screen.dart index b25f7419..f3f794ec 100644 --- a/modules/legacy/modules/device_management/lib/src/features/volume_control/presentation/volume_control_screen.dart +++ b/modules/legacy/modules/device_management/lib/src/features/volume_control/presentation/volume_control_screen.dart @@ -47,6 +47,7 @@ class VolumeControlScreen extends ConsumerWidget { _VolumeCard( label: context.translate(I18n.volumeMedia), value: state.media, + max: state.maxMedia, color: primaryColor, onChanged: vm.setMedia, ), @@ -54,6 +55,7 @@ class VolumeControlScreen extends ConsumerWidget { _VolumeCard( label: context.translate(I18n.volumeRingtone), value: state.ringtone, + max: state.maxRingtone, color: primaryColor, onChanged: vm.setRingtone, ), @@ -61,6 +63,7 @@ class VolumeControlScreen extends ConsumerWidget { _VolumeCard( label: context.translate(I18n.volumeAlarm), value: state.alarm, + max: state.maxAlarm, color: primaryColor, onChanged: vm.setAlarm, ), @@ -96,18 +99,18 @@ class VolumeControlScreen extends ConsumerWidget { class _VolumeCard extends StatelessWidget { final String label; final int value; + final int max; final Color color; final ValueChanged onChanged; const _VolumeCard({ required this.label, required this.value, + required this.max, required this.color, required this.onChanged, }); - int get _displayValue => (value / 10).round(); - @override Widget build(BuildContext context) { return Container( @@ -147,8 +150,8 @@ class _VolumeCard extends StatelessWidget { child: Slider( value: value.toDouble(), min: 0, - max: 100, - divisions: 10, + max: max.toDouble(), + divisions: max, onChanged: (v) => onChanged(v.round()), ), ), @@ -156,7 +159,7 @@ class _VolumeCard extends StatelessWidget { SizedBox( width: 24, child: Text( - '$_displayValue', + '$value', textAlign: TextAlign.center, style: TextStyle( fontSize: 15,