diff --git a/app/lib/core/component/widget/blend_mask.dart b/app/lib/core/component/widget/blend_mask.dart new file mode 100644 index 000000000..4c1478032 --- /dev/null +++ b/app/lib/core/component/widget/blend_mask.dart @@ -0,0 +1,46 @@ +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; + +// https://stackoverflow.com/a/77039320 +class BlendMask extends SingleChildRenderObjectWidget { + const BlendMask({ + required this.blendMode, + this.opacity = 1.0, + super.key, + super.child, + }); + final BlendMode blendMode; + final double opacity; + + @override + RenderObject createRenderObject(BuildContext context) { + return RenderBlendMask(blendMode, opacity); + } + + @override + void updateRenderObject(BuildContext context, RenderBlendMask renderObject) { + renderObject + ..blendMode = blendMode + ..opacity = opacity; + } +} + +class RenderBlendMask extends RenderProxyBox { + RenderBlendMask(this.blendMode, this.opacity); + BlendMode blendMode; + double opacity; + + @override + void paint(PaintingContext context, Offset offset) { + context.canvas.saveLayer( + offset & size, + Paint() + ..blendMode = blendMode + ..color = Color.fromARGB((opacity * 255).round(), 255, 255, 255), + ); + + super.paint(context, offset); + + context.canvas.restore(); + } +} diff --git a/app/lib/core/extension/double_to_jma_forecast_intensity.dart b/app/lib/core/extension/double_to_jma_forecast_intensity.dart index 937c5fc61..b8c644eac 100644 --- a/app/lib/core/extension/double_to_jma_forecast_intensity.dart +++ b/app/lib/core/extension/double_to_jma_forecast_intensity.dart @@ -15,3 +15,19 @@ extension JmaForecastIntensityDouble on double { _ => JmaForecastIntensity.seven, }; } + +extension JmaForecastIntensityEx on JmaForecastIntensity { + (double min, double max) get toRealtimeValue => switch (this) { + JmaForecastIntensity.zero => (double.negativeInfinity, 0.5), + JmaForecastIntensity.one => (0.5, 1.5), + JmaForecastIntensity.two => (1.5, 2.5), + JmaForecastIntensity.three => (2.5, 3.5), + JmaForecastIntensity.four => (3.5, 4.5), + JmaForecastIntensity.fiveLower => (4.5, 5.0), + JmaForecastIntensity.fiveUpper => (5.0, 5.5), + JmaForecastIntensity.sixLower => (5.5, 6.0), + JmaForecastIntensity.sixUpper => (6.0, 6.5), + JmaForecastIntensity.seven => (6.5, double.infinity), + _ => throw UnimplementedError(), + }; +} diff --git a/app/lib/core/extension/kyoshin_color_map_model.dart b/app/lib/core/extension/kyoshin_color_map_model.dart new file mode 100644 index 000000000..99e841c06 --- /dev/null +++ b/app/lib/core/extension/kyoshin_color_map_model.dart @@ -0,0 +1,6 @@ +import 'package:eqmonitor/core/provider/kmoni/model/kyoshin_color_map_model.dart'; +import 'package:flutter/material.dart'; + +extension KyoshinColorMapModelEx on KyoshinColorMapModel { + Color get color => Color.fromARGB(255, r, g, b); +} diff --git a/app/lib/core/provider/kmoni/data/kyoshin_color_map_data_source.dart b/app/lib/core/provider/kmoni/data/kyoshin_color_map_data_source.dart index f02bc522e..7d2914708 100644 --- a/app/lib/core/provider/kmoni/data/kyoshin_color_map_data_source.dart +++ b/app/lib/core/provider/kmoni/data/kyoshin_color_map_data_source.dart @@ -3,24 +3,13 @@ import 'dart:convert'; import 'package:eqmonitor/core/provider/kmoni/model/kyoshin_color_map_model.dart'; import 'package:eqmonitor/gen/assets.gen.dart'; import 'package:flutter/services.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; -part 'kyoshin_color_map_data_source.g.dart'; - -@Riverpod(keepAlive: true) -KyoshinColorMapDataSource kyoshinColorMapDataSource( - KyoshinColorMapDataSourceRef ref, -) => - KyoshinColorMapDataSource(); - -class KyoshinColorMapDataSource { - Future> getKyoshinColorMap() async { - final str = await rootBundle.loadString(Assets.kyoshinShindoColorMap); - final json = jsonDecode(str) as List; - return json - .map( - (e) => KyoshinColorMapModel.fromJson(e as Map), - ) - .toList(); - } +Future> getKyoshinColorMap() async { + final str = await rootBundle.loadString(Assets.kyoshinShindoColorMap); + final json = jsonDecode(str) as List; + return json + .map( + (e) => KyoshinColorMapModel.fromJson(e as Map), + ) + .toList(); } diff --git a/app/lib/core/provider/kmoni/data/kyoshin_color_map_data_source.g.dart b/app/lib/core/provider/kmoni/data/kyoshin_color_map_data_source.g.dart deleted file mode 100644 index 2d89bf0e7..000000000 --- a/app/lib/core/provider/kmoni/data/kyoshin_color_map_data_source.g.dart +++ /dev/null @@ -1,29 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -// ignore_for_file: type=lint, duplicate_ignore - -part of 'kyoshin_color_map_data_source.dart'; - -// ************************************************************************** -// RiverpodGenerator -// ************************************************************************** - -String _$kyoshinColorMapDataSourceHash() => - r'e3d7e19d0fd08f094ecd01a0b7b80d4969a998ef'; - -/// See also [kyoshinColorMapDataSource]. -@ProviderFor(kyoshinColorMapDataSource) -final kyoshinColorMapDataSourceProvider = - Provider.internal( - kyoshinColorMapDataSource, - name: r'kyoshinColorMapDataSourceProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$kyoshinColorMapDataSourceHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef KyoshinColorMapDataSourceRef = ProviderRef; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, inference_failure_on_uninitialized_variable, inference_failure_on_function_return_type, inference_failure_on_untyped_parameter, deprecated_member_use_from_same_package diff --git a/app/lib/core/provider/kmoni/model/kyoshin_color_map_model.freezed.dart b/app/lib/core/provider/kmoni/model/kyoshin_color_map_model.freezed.dart index 3be064c4a..13987cb6b 100644 --- a/app/lib/core/provider/kmoni/model/kyoshin_color_map_model.freezed.dart +++ b/app/lib/core/provider/kmoni/model/kyoshin_color_map_model.freezed.dart @@ -12,7 +12,7 @@ part of 'kyoshin_color_map_model.dart'; T _$identity(T value) => value; final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); KyoshinColorMapModel _$KyoshinColorMapModelFromJson(Map json) { return _KyoshinColorMapModel.fromJson(json); diff --git a/app/lib/core/provider/kmoni/page/kmoni_settings_page.dart b/app/lib/core/provider/kmoni/page/kmoni_settings_page.dart index 412a8d832..fa0b406b4 100644 --- a/app/lib/core/provider/kmoni/page/kmoni_settings_page.dart +++ b/app/lib/core/provider/kmoni/page/kmoni_settings_page.dart @@ -1,6 +1,6 @@ import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:eqmonitor/core/component/widget/kmoni_caution.dart'; -import 'package:eqmonitor/core/provider/kmoni/viewmodel/kmoni_view_settings.dart'; +import 'package:eqmonitor/core/provider/kmoni/viewmodel/kmoni_settings.dart'; import 'package:eqmonitor/feature/home/component/sheet/sheet_header.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; diff --git a/app/lib/core/provider/kmoni/provider/kmoni_color_provider.dart b/app/lib/core/provider/kmoni/provider/kmoni_color_provider.dart index 06789d671..316b07701 100644 --- a/app/lib/core/provider/kmoni/provider/kmoni_color_provider.dart +++ b/app/lib/core/provider/kmoni/provider/kmoni_color_provider.dart @@ -1,5 +1,4 @@ import 'package:collection/collection.dart'; -import 'package:eqmonitor/core/provider/kmoni/data/kyoshin_color_map_data_source.dart'; import 'package:eqmonitor/core/provider/kmoni/model/kyoshin_color_map_model.dart'; import 'package:flutter/material.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -7,10 +6,10 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'kmoni_color_provider.g.dart'; @Riverpod(keepAlive: true) -Future> kyoshinColorMap( +List kyoshinColorMap( KyoshinColorMapRef ref, ) => - ref.watch(kyoshinColorMapDataSourceProvider).getKyoshinColorMap(); + throw UnimplementedError(); extension IntensityToKyoshinColor on List { Color intensityToColor(double intensity) { diff --git a/app/lib/core/provider/kmoni/provider/kmoni_color_provider.g.dart b/app/lib/core/provider/kmoni/provider/kmoni_color_provider.g.dart index ab3c52eb6..6fee93631 100644 --- a/app/lib/core/provider/kmoni/provider/kmoni_color_provider.g.dart +++ b/app/lib/core/provider/kmoni/provider/kmoni_color_provider.g.dart @@ -8,12 +8,11 @@ part of 'kmoni_color_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$kyoshinColorMapHash() => r'3dacbfefc295ce60e0feec38dedeadb24f1b1404'; +String _$kyoshinColorMapHash() => r'817bc52e91bf85de3fb107575ded49926cb2f735'; /// See also [kyoshinColorMap]. @ProviderFor(kyoshinColorMap) -final kyoshinColorMapProvider = - FutureProvider>.internal( +final kyoshinColorMapProvider = Provider>.internal( kyoshinColorMap, name: r'kyoshinColorMapProvider', debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') @@ -23,6 +22,6 @@ final kyoshinColorMapProvider = allTransitiveDependencies: null, ); -typedef KyoshinColorMapRef = FutureProviderRef>; +typedef KyoshinColorMapRef = ProviderRef>; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, inference_failure_on_uninitialized_variable, inference_failure_on_function_return_type, inference_failure_on_untyped_parameter, deprecated_member_use_from_same_package diff --git a/app/lib/core/provider/kmoni/viewmodel/kmoni_view_settings.dart b/app/lib/core/provider/kmoni/viewmodel/kmoni_settings.dart similarity index 72% rename from app/lib/core/provider/kmoni/viewmodel/kmoni_view_settings.dart rename to app/lib/core/provider/kmoni/viewmodel/kmoni_settings.dart index 32359b13a..354c1e183 100644 --- a/app/lib/core/provider/kmoni/viewmodel/kmoni_view_settings.dart +++ b/app/lib/core/provider/kmoni/viewmodel/kmoni_settings.dart @@ -4,21 +4,17 @@ import 'package:eqmonitor/core/provider/shared_preferences.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; -part 'kmoni_view_settings.freezed.dart'; -part 'kmoni_view_settings.g.dart'; +part 'kmoni_settings.freezed.dart'; +part 'kmoni_settings.g.dart'; @freezed class KmoniSettingsState with _$KmoniSettingsState { const factory KmoniSettingsState({ - // 設定 - /// 震度0以上のみ表示するかどうか - /// 震度0-1: グレーで表示 - /// 震度1-: isShowIntensityIcon が true の場合はアイコンを表示 - /// 震度1-: isShowIntensityIcon が false の場合は色で表示 - @Default(false) bool isUpper0Only, + /// 強震モニタの表示最低リアルタイム震度 + @Default(null) double? minRealtimeShindo, - /// 震度アイコンを表示するかどうか - @Default(false) bool isShowIntensityIcon, + /// スケールを表示するかどうか + @Default(true) bool showRealtimeShindoScale, /// 強震モニタを使用するかどうか @Default(false) bool useKmoni, @@ -63,27 +59,27 @@ class KmoniSettings extends _$KmoniSettings { jsonEncode(state.toJson()), ); - void toggleIsUpper0Only() { + void toggleUseKmoni() { state = state.copyWith( - isUpper0Only: !state.isUpper0Only, + useKmoni: !state.useKmoni, ); } - void toggleIsShowIntensityIcon() { + void setUseKmoni({required bool value}) { state = state.copyWith( - isShowIntensityIcon: !state.isShowIntensityIcon, + useKmoni: value, ); } - void toggleUseKmoni() { + void setMinRealtimeShindo({required double? value}) { state = state.copyWith( - useKmoni: !state.useKmoni, + minRealtimeShindo: value, ); } - void setUseKmoni({required bool value}) { + void setShowRealtimeShindoScale({required bool value}) { state = state.copyWith( - useKmoni: value, + showRealtimeShindoScale: value, ); } } diff --git a/app/lib/core/provider/kmoni/viewmodel/kmoni_view_settings.freezed.dart b/app/lib/core/provider/kmoni/viewmodel/kmoni_settings.freezed.dart similarity index 63% rename from app/lib/core/provider/kmoni/viewmodel/kmoni_view_settings.freezed.dart rename to app/lib/core/provider/kmoni/viewmodel/kmoni_settings.freezed.dart index ee1fe3472..035b5f2fc 100644 --- a/app/lib/core/provider/kmoni/viewmodel/kmoni_view_settings.freezed.dart +++ b/app/lib/core/provider/kmoni/viewmodel/kmoni_settings.freezed.dart @@ -3,7 +3,7 @@ // ignore_for_file: type=lint // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark -part of 'kmoni_view_settings.dart'; +part of 'kmoni_settings.dart'; // ************************************************************************** // FreezedGenerator @@ -12,7 +12,7 @@ part of 'kmoni_view_settings.dart'; T _$identity(T value) => value; final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); KmoniSettingsState _$KmoniSettingsStateFromJson(Map json) { return _KmoniSettingsState.fromJson(json); @@ -20,15 +20,11 @@ KmoniSettingsState _$KmoniSettingsStateFromJson(Map json) { /// @nodoc mixin _$KmoniSettingsState { -// 設定 - /// 震度0以上のみ表示するかどうか - /// 震度0-1: グレーで表示 - /// 震度1-: isShowIntensityIcon が true の場合はアイコンを表示 - /// 震度1-: isShowIntensityIcon が false の場合は色で表示 - bool get isUpper0Only => throw _privateConstructorUsedError; + /// 強震モニタの表示最低リアルタイム震度 + double? get minRealtimeShindo => throw _privateConstructorUsedError; - /// 震度アイコンを表示するかどうか - bool get isShowIntensityIcon => throw _privateConstructorUsedError; + /// スケールを表示するかどうか + bool get showRealtimeShindoScale => throw _privateConstructorUsedError; /// 強震モニタを使用するかどうか bool get useKmoni => throw _privateConstructorUsedError; @@ -45,7 +41,8 @@ abstract class $KmoniSettingsStateCopyWith<$Res> { KmoniSettingsState value, $Res Function(KmoniSettingsState) then) = _$KmoniSettingsStateCopyWithImpl<$Res, KmoniSettingsState>; @useResult - $Res call({bool isUpper0Only, bool isShowIntensityIcon, bool useKmoni}); + $Res call( + {double? minRealtimeShindo, bool showRealtimeShindoScale, bool useKmoni}); } /// @nodoc @@ -61,18 +58,18 @@ class _$KmoniSettingsStateCopyWithImpl<$Res, $Val extends KmoniSettingsState> @pragma('vm:prefer-inline') @override $Res call({ - Object? isUpper0Only = null, - Object? isShowIntensityIcon = null, + Object? minRealtimeShindo = freezed, + Object? showRealtimeShindoScale = null, Object? useKmoni = null, }) { return _then(_value.copyWith( - isUpper0Only: null == isUpper0Only - ? _value.isUpper0Only - : isUpper0Only // ignore: cast_nullable_to_non_nullable - as bool, - isShowIntensityIcon: null == isShowIntensityIcon - ? _value.isShowIntensityIcon - : isShowIntensityIcon // ignore: cast_nullable_to_non_nullable + minRealtimeShindo: freezed == minRealtimeShindo + ? _value.minRealtimeShindo + : minRealtimeShindo // ignore: cast_nullable_to_non_nullable + as double?, + showRealtimeShindoScale: null == showRealtimeShindoScale + ? _value.showRealtimeShindoScale + : showRealtimeShindoScale // ignore: cast_nullable_to_non_nullable as bool, useKmoni: null == useKmoni ? _value.useKmoni @@ -90,7 +87,8 @@ abstract class _$$KmoniSettingsStateImplCopyWith<$Res> __$$KmoniSettingsStateImplCopyWithImpl<$Res>; @override @useResult - $Res call({bool isUpper0Only, bool isShowIntensityIcon, bool useKmoni}); + $Res call( + {double? minRealtimeShindo, bool showRealtimeShindoScale, bool useKmoni}); } /// @nodoc @@ -104,18 +102,18 @@ class __$$KmoniSettingsStateImplCopyWithImpl<$Res> @pragma('vm:prefer-inline') @override $Res call({ - Object? isUpper0Only = null, - Object? isShowIntensityIcon = null, + Object? minRealtimeShindo = freezed, + Object? showRealtimeShindoScale = null, Object? useKmoni = null, }) { return _then(_$KmoniSettingsStateImpl( - isUpper0Only: null == isUpper0Only - ? _value.isUpper0Only - : isUpper0Only // ignore: cast_nullable_to_non_nullable - as bool, - isShowIntensityIcon: null == isShowIntensityIcon - ? _value.isShowIntensityIcon - : isShowIntensityIcon // ignore: cast_nullable_to_non_nullable + minRealtimeShindo: freezed == minRealtimeShindo + ? _value.minRealtimeShindo + : minRealtimeShindo // ignore: cast_nullable_to_non_nullable + as double?, + showRealtimeShindoScale: null == showRealtimeShindoScale + ? _value.showRealtimeShindoScale + : showRealtimeShindoScale // ignore: cast_nullable_to_non_nullable as bool, useKmoni: null == useKmoni ? _value.useKmoni @@ -129,26 +127,22 @@ class __$$KmoniSettingsStateImplCopyWithImpl<$Res> @JsonSerializable() class _$KmoniSettingsStateImpl implements _KmoniSettingsState { const _$KmoniSettingsStateImpl( - {this.isUpper0Only = false, - this.isShowIntensityIcon = false, + {this.minRealtimeShindo = null, + this.showRealtimeShindoScale = true, this.useKmoni = false}); factory _$KmoniSettingsStateImpl.fromJson(Map json) => _$$KmoniSettingsStateImplFromJson(json); -// 設定 - /// 震度0以上のみ表示するかどうか - /// 震度0-1: グレーで表示 - /// 震度1-: isShowIntensityIcon が true の場合はアイコンを表示 - /// 震度1-: isShowIntensityIcon が false の場合は色で表示 + /// 強震モニタの表示最低リアルタイム震度 @override @JsonKey() - final bool isUpper0Only; + final double? minRealtimeShindo; - /// 震度アイコンを表示するかどうか + /// スケールを表示するかどうか @override @JsonKey() - final bool isShowIntensityIcon; + final bool showRealtimeShindoScale; /// 強震モニタを使用するかどうか @override @@ -157,7 +151,7 @@ class _$KmoniSettingsStateImpl implements _KmoniSettingsState { @override String toString() { - return 'KmoniSettingsState(isUpper0Only: $isUpper0Only, isShowIntensityIcon: $isShowIntensityIcon, useKmoni: $useKmoni)'; + return 'KmoniSettingsState(minRealtimeShindo: $minRealtimeShindo, showRealtimeShindoScale: $showRealtimeShindoScale, useKmoni: $useKmoni)'; } @override @@ -165,18 +159,19 @@ class _$KmoniSettingsStateImpl implements _KmoniSettingsState { return identical(this, other) || (other.runtimeType == runtimeType && other is _$KmoniSettingsStateImpl && - (identical(other.isUpper0Only, isUpper0Only) || - other.isUpper0Only == isUpper0Only) && - (identical(other.isShowIntensityIcon, isShowIntensityIcon) || - other.isShowIntensityIcon == isShowIntensityIcon) && + (identical(other.minRealtimeShindo, minRealtimeShindo) || + other.minRealtimeShindo == minRealtimeShindo) && + (identical( + other.showRealtimeShindoScale, showRealtimeShindoScale) || + other.showRealtimeShindoScale == showRealtimeShindoScale) && (identical(other.useKmoni, useKmoni) || other.useKmoni == useKmoni)); } @JsonKey(ignore: true) @override - int get hashCode => - Object.hash(runtimeType, isUpper0Only, isShowIntensityIcon, useKmoni); + int get hashCode => Object.hash( + runtimeType, minRealtimeShindo, showRealtimeShindoScale, useKmoni); @JsonKey(ignore: true) @override @@ -195,23 +190,21 @@ class _$KmoniSettingsStateImpl implements _KmoniSettingsState { abstract class _KmoniSettingsState implements KmoniSettingsState { const factory _KmoniSettingsState( - {final bool isUpper0Only, - final bool isShowIntensityIcon, + {final double? minRealtimeShindo, + final bool showRealtimeShindoScale, final bool useKmoni}) = _$KmoniSettingsStateImpl; factory _KmoniSettingsState.fromJson(Map json) = _$KmoniSettingsStateImpl.fromJson; - @override // 設定 - /// 震度0以上のみ表示するかどうか - /// 震度0-1: グレーで表示 - /// 震度1-: isShowIntensityIcon が true の場合はアイコンを表示 - /// 震度1-: isShowIntensityIcon が false の場合は色で表示 - bool get isUpper0Only; @override - /// 震度アイコンを表示するかどうか - bool get isShowIntensityIcon; + /// 強震モニタの表示最低リアルタイム震度 + double? get minRealtimeShindo; + @override + + /// スケールを表示するかどうか + bool get showRealtimeShindoScale; @override /// 強震モニタを使用するかどうか diff --git a/app/lib/core/provider/kmoni/viewmodel/kmoni_view_settings.g.dart b/app/lib/core/provider/kmoni/viewmodel/kmoni_settings.g.dart similarity index 78% rename from app/lib/core/provider/kmoni/viewmodel/kmoni_view_settings.g.dart rename to app/lib/core/provider/kmoni/viewmodel/kmoni_settings.g.dart index f43755026..aecaef591 100644 --- a/app/lib/core/provider/kmoni/viewmodel/kmoni_view_settings.g.dart +++ b/app/lib/core/provider/kmoni/viewmodel/kmoni_settings.g.dart @@ -2,7 +2,7 @@ // ignore_for_file: type=lint, duplicate_ignore -part of 'kmoni_view_settings.dart'; +part of 'kmoni_settings.dart'; // ************************************************************************** // JsonSerializableGenerator @@ -15,10 +15,10 @@ _$KmoniSettingsStateImpl _$$KmoniSettingsStateImplFromJson( json, ($checkedConvert) { final val = _$KmoniSettingsStateImpl( - isUpper0Only: - $checkedConvert('isUpper0Only', (v) => v as bool? ?? false), - isShowIntensityIcon: $checkedConvert( - 'isShowIntensityIcon', (v) => v as bool? ?? false), + minRealtimeShindo: $checkedConvert( + 'minRealtimeShindo', (v) => (v as num?)?.toDouble() ?? null), + showRealtimeShindoScale: $checkedConvert( + 'showRealtimeShindoScale', (v) => v as bool? ?? true), useKmoni: $checkedConvert('useKmoni', (v) => v as bool? ?? false), ); return val; @@ -28,8 +28,8 @@ _$KmoniSettingsStateImpl _$$KmoniSettingsStateImplFromJson( Map _$$KmoniSettingsStateImplToJson( _$KmoniSettingsStateImpl instance) => { - 'isUpper0Only': instance.isUpper0Only, - 'isShowIntensityIcon': instance.isShowIntensityIcon, + 'minRealtimeShindo': instance.minRealtimeShindo, + 'showRealtimeShindoScale': instance.showRealtimeShindoScale, 'useKmoni': instance.useKmoni, }; @@ -37,7 +37,7 @@ Map _$$KmoniSettingsStateImplToJson( // RiverpodGenerator // ************************************************************************** -String _$kmoniSettingsHash() => r'3a624489de7f8bd258d81cbd5e30d570bbe2951a'; +String _$kmoniSettingsHash() => r'5bfee9dfe90781154d43ff084a82cf74202b8631'; /// See also [KmoniSettings]. @ProviderFor(KmoniSettings) diff --git a/app/lib/core/provider/kmoni/viewmodel/kmoni_view_model.dart b/app/lib/core/provider/kmoni/viewmodel/kmoni_view_model.dart index a2fb8567d..64e0b7577 100644 --- a/app/lib/core/provider/kmoni/viewmodel/kmoni_view_model.dart +++ b/app/lib/core/provider/kmoni/viewmodel/kmoni_view_model.dart @@ -6,7 +6,8 @@ import 'package:dio/dio.dart'; import 'package:eqmonitor/core/provider/app_lifecycle.dart'; import 'package:eqmonitor/core/provider/kmoni/model/kmoni_view_model_state.dart'; import 'package:eqmonitor/core/provider/kmoni/use_case/kmoni_use_case.dart'; -import 'package:eqmonitor/core/provider/kmoni/viewmodel/kmoni_view_settings.dart'; +import 'package:eqmonitor/core/provider/kmoni/viewmodel/kmoni_settings.dart'; +import 'package:eqmonitor/core/provider/kmoni_observation_points/model/kmoni_observation_point.dart'; import 'package:eqmonitor/core/provider/kmoni_observation_points/provider/kyoshin_observation_points_provider.dart'; import 'package:eqmonitor/core/provider/log/talker.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -17,20 +18,25 @@ part 'kmoni_view_model.g.dart'; class KmoniViewModel extends _$KmoniViewModel { @override KmoniViewModelState build() { - ref.listen(appLifeCycleProvider, (_, next) async { - if (next == AppLifecycleState.resumed) { + ref + ..listen(appLifeCycleProvider, (_, next) async { + if (next == AppLifecycleState.resumed) { + state = state.copyWith( + status: KmoniStatus.none, + ); + await syncDelayWithKmoni(); + await _update(); + return; + } + // 停止 state = state.copyWith( - status: KmoniStatus.none, + status: KmoniStatus.stopped, ); - await syncDelayWithKmoni(); - await _update(); - return; - } - // 停止 - state = state.copyWith( - status: KmoniStatus.stopped, + }) + ..listen( + kmoniSettingsProvider.select((v) => v.minRealtimeShindo), + (_, __) => _updateState(), ); - }); return const KmoniViewModelState( isInitialized: false, lastUpdatedAt: null, @@ -74,6 +80,8 @@ class KmoniViewModel extends _$KmoniViewModel { ); } + List? _cache; + /// 画像取得 Future _update() async { if (state.status == KmoniStatus.stopped || @@ -90,13 +98,20 @@ class KmoniViewModel extends _$KmoniViewModel { } try { - final result = await ref.read(kmoniUseCaseProvider).fetchRealtimeShindo( + _cache = await ref.read(kmoniUseCaseProvider).fetchRealtimeShindo( now, obsPoints: ref.read(kyoshinObservationPointsProvider).points, ); + final minRealtimeIntensity = + ref.read(kmoniSettingsProvider).minRealtimeShindo; + final filtered = minRealtimeIntensity == null + ? _cache + : _cache + ?.where((e) => (e.intensityValue ?? -3) >= minRealtimeIntensity) + .toList(); state = state.copyWith( lastUpdatedAt: now, - analyzedPoints: result, + analyzedPoints: filtered, status: KmoniStatus.realtime, ); } on DioException catch (e) { @@ -113,6 +128,22 @@ class KmoniViewModel extends _$KmoniViewModel { } } + /// 状態更新のみ + void _updateState() { + if (_cache != null) { + final minRealtimeIntensity = + ref.read(kmoniSettingsProvider).minRealtimeShindo; + final filtered = minRealtimeIntensity == null + ? _cache + : _cache + ?.where((e) => (e.intensityValue ?? -3) >= minRealtimeIntensity) + .toList(); + state = state.copyWith( + analyzedPoints: filtered, + ); + } + } + Future syncDelayWithKmoni() async { final useCase = ref.read(kmoniUseCaseProvider); final talker = ref.read(talkerProvider); diff --git a/app/lib/core/provider/kmoni/viewmodel/kmoni_view_model.g.dart b/app/lib/core/provider/kmoni/viewmodel/kmoni_view_model.g.dart index 5d713d1eb..bc8b23788 100644 --- a/app/lib/core/provider/kmoni/viewmodel/kmoni_view_model.g.dart +++ b/app/lib/core/provider/kmoni/viewmodel/kmoni_view_model.g.dart @@ -8,7 +8,7 @@ part of 'kmoni_view_model.dart'; // RiverpodGenerator // ************************************************************************** -String _$kmoniViewModelHash() => r'40151be04bc2784875bdfac2710aa8efb6f6b968'; +String _$kmoniViewModelHash() => r'c1b3436e3e4a03524fdfcfab3fa9608454fe6493'; /// See also [KmoniViewModel]. @ProviderFor(KmoniViewModel) diff --git a/app/lib/core/provider/kmoni/widget/kmoni_maintenance_widget.dart b/app/lib/core/provider/kmoni/widget/kmoni_maintenance_widget.dart index 98c3a50e6..4be06c446 100644 --- a/app/lib/core/provider/kmoni/widget/kmoni_maintenance_widget.dart +++ b/app/lib/core/provider/kmoni/widget/kmoni_maintenance_widget.dart @@ -1,7 +1,7 @@ import 'package:eqmonitor/core/component/container/bordered_container.dart'; import 'package:eqmonitor/core/provider/kmoni/model/kmoni_maintenance_message_model.dart'; import 'package:eqmonitor/core/provider/kmoni/viewmodel/kmoni_maintenance_view_model.dart'; -import 'package:eqmonitor/core/provider/kmoni/viewmodel/kmoni_view_settings.dart'; +import 'package:eqmonitor/core/provider/kmoni/viewmodel/kmoni_settings.dart'; import 'package:eqmonitor/feature/home/component/sheet/sheet_header.dart'; import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; diff --git a/app/lib/feature/earthquake_history_details/component/prefecture_intensity.g.dart b/app/lib/feature/earthquake_history_details/component/prefecture_intensity.g.dart index 46fa5a8a6..164b273b9 100644 --- a/app/lib/feature/earthquake_history_details/component/prefecture_intensity.g.dart +++ b/app/lib/feature/earthquake_history_details/component/prefecture_intensity.g.dart @@ -8,7 +8,7 @@ part of 'prefecture_intensity.dart'; // RiverpodGenerator // ************************************************************************** -String _$calculatorHash() => r'8f639dd4d60bda645dfc32e57a8efe6ebb4d1e5e'; +String _$calculatorHash() => r'ec6f3bf8790c9360816212ab548ae098f3b31ad0'; /// Copied from Dart SDK class _SystemHash { diff --git a/app/lib/feature/home/component/kmoni/kmoni_scale.dart b/app/lib/feature/home/component/kmoni/kmoni_scale.dart new file mode 100644 index 000000000..022fe0bc5 --- /dev/null +++ b/app/lib/feature/home/component/kmoni/kmoni_scale.dart @@ -0,0 +1,200 @@ +import 'dart:ui' as ui; + +import 'package:collection/collection.dart'; +import 'package:eqapi_types/eqapi_types.dart'; +import 'package:eqmonitor/core/extension/double_to_jma_forecast_intensity.dart'; +import 'package:eqmonitor/core/extension/kyoshin_color_map_model.dart'; +import 'package:eqmonitor/core/provider/kmoni/model/kyoshin_color_map_model.dart'; +import 'package:eqmonitor/core/provider/kmoni/provider/kmoni_color_provider.dart'; +import 'package:eqmonitor/core/theme/build_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +class KmoniScaleWidget extends ConsumerWidget { + const KmoniScaleWidget({ + super.key, + this.showText = true, + this.markers = const [], + }); + + final bool showText; + final List markers; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final colorMap = ref.watch(kyoshinColorMapProvider); + final theme = Theme.of(context); + final colorScheme = theme.colorScheme; + return CustomPaint( + size: Size.infinite, + painter: _KmoniScalePainter( + colorMap: colorMap, + lineColor: colorScheme.onSurface, + showText: showText, + markers: markers, + ), + ); + } +} + +class _KmoniScalePainter extends CustomPainter { + _KmoniScalePainter({ + required this.colorMap, + this.lineColor = Colors.black, + this.showText = true, + this.markers = const [], + }); + + final List colorMap; + + /// 区切り線の色 + final Color lineColor; + + final bool showText; + final List markers; + + @override + void paint(Canvas canvas, Size size) { + final rect = Offset.zero & size; + + drawBackground(canvas, rect); + drawRealtimeIntensityChange(canvas, rect, drawText: showText); + for (final e in markers) { + drawMarker(canvas, rect, e); + } + } + + // 背景のグラデーションを描画 + void drawBackground(Canvas canvas, Rect rect) => canvas.drawRect( + rect, + Paint() + ..shader = ui.Gradient.linear( + rect.centerLeft, + rect.centerRight, + colorMap.map((e) => Color.fromARGB(255, e.r, e.g, e.b)).toList(), + colorMap.mapIndexed((index, e) => index / colorMap.length).toList(), + ), + ); + + // 震度の切り替わりに線を描画 + void drawIntensityChangeLine(Canvas canvas, Rect rect) { + final paint = Paint() + ..color = Colors.black + ..strokeWidth = 1; + // 震度の切り替わりを描画 + JmaForecastIntensity? lastIntensity; + colorMap.forEachIndexed((index, element) { + final intensity = element.intensity.toJmaForecastIntensity; + if (lastIntensity != null && lastIntensity != intensity) { + final x = rect.width * (index / colorMap.length); + canvas.drawLine( + Offset(x, rect.top), + Offset(x, rect.bottom), + paint, + ); + } + lastIntensity = intensity; + }); + } + + // リアルタイム震度が整数値の場合、その震度を描画 + void drawRealtimeIntensityChange( + Canvas canvas, + Rect rect, { + bool drawText = true, + }) { + final paint = Paint() + ..color = lineColor + ..strokeWidth = 1; + // 震度の切り替わりを描画 + colorMap.forEachIndexed((index, element) { + if (element.intensity % 1 != 0) { + return; + } + final color = + (element.color.computeLuminance() > 0.5 ? Colors.black : Colors.white) + .withOpacity(0.8); + final x = rect.width * (index / colorMap.length) + rect.left; + final width = rect.width / colorMap.length; + canvas.drawLine( + Offset(x + width, rect.top), + Offset(x + width, rect.bottom), + paint..color = color, + ); + + if (drawText) { + final textPainter = TextPainter( + text: TextSpan( + text: element.intensity.toStringAsFixed(0), + style: TextStyle( + color: color, + fontSize: 10, + fontFamily: monoFont, + fontWeight: FontWeight.bold, + ), + ), + textDirection: TextDirection.ltr, + )..layout(); + final offset = Offset( + x + + width / 2 + + switch (element.intensity) { + 7.0 => -textPainter.width, + _ => textPainter.width / 2, + } + + rect.left, + rect.bottom - textPainter.height, + ); + textPainter.paint( + canvas, + offset, + ); + } + }); + } + + // 赤三角 + void drawMarker(Canvas canvas, Rect rect, double intensity) { + final lower = colorMap.lastWhereOrNull((e) => e.intensity <= intensity); + final upper = colorMap.firstWhereOrNull((e) => e.intensity >= intensity); + + final double x; + if (lower == null) { + x = 0; + } else if (upper == null) { + x = 1; + } else { + x = (colorMap.indexOf(lower) + + (intensity - lower.intensity) / + (upper.intensity - lower.intensity)) / + colorMap.length; + } + + final paint = Paint() + ..color = Colors.red + ..style = PaintingStyle.fill; + final outlinePaint = Paint() + ..color = Colors.white + ..style = PaintingStyle.stroke + ..strokeWidth = 0; + + final path = Path() + ..moveTo(rect.width * x - 5, rect.top) + ..lineTo(rect.width * x + 5, rect.top) + ..lineTo(rect.width * x, rect.top + 10) + ..close(); + + canvas + ..drawPath(path, paint) + ..drawPath(path, outlinePaint); + } + + @override + bool shouldRepaint(_KmoniScalePainter oldDelegate) => + oldDelegate.colorMap != colorMap || + oldDelegate.showText != showText || + !const ListEquality().equals(oldDelegate.markers, markers); + + @override + bool shouldRebuildSemantics(_KmoniScalePainter oldDelegate) => false; +} diff --git a/app/lib/feature/home/component/kmoni/kmoni_settings_dialog.dart b/app/lib/feature/home/component/kmoni/kmoni_settings_dialog.dart index 8b8fb4fb5..295162868 100644 --- a/app/lib/feature/home/component/kmoni/kmoni_settings_dialog.dart +++ b/app/lib/feature/home/component/kmoni/kmoni_settings_dialog.dart @@ -1,37 +1,108 @@ -import 'package:eqmonitor/core/provider/kmoni/viewmodel/kmoni_view_settings.dart'; +import 'package:eqmonitor/core/provider/kmoni/provider/kmoni_color_provider.dart'; +import 'package:eqmonitor/core/provider/kmoni/viewmodel/kmoni_settings.dart'; +import 'package:eqmonitor/feature/home/component/kmoni/kmoni_scale.dart'; +import 'package:eqmonitor/feature/settings/component/settings_section_header.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -class KmoniSettingsDialogWidget extends ConsumerWidget { - const KmoniSettingsDialogWidget({super.key}); +class KmoniSettingsWidget extends HookConsumerWidget { + const KmoniSettingsWidget({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final state = ref.watch(kmoniSettingsProvider); - return AlertDialog( - title: const Text('強震モニタの設定'), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - SwitchListTile.adaptive( - title: const Text('震度0以上のみ表示'), - subtitle: const Text('震度0(観測震度0.5)以上の観測点のみ表示します'), - value: state.isUpper0Only, - onChanged: (value) { - ref.read(kmoniSettingsProvider.notifier).toggleIsUpper0Only(); - }, - ), - SwitchListTile.adaptive( - title: const Text('震度アイコンを表示'), - value: state.isShowIntensityIcon, - onChanged: (value) { - ref - .read(kmoniSettingsProvider.notifier) - .toggleIsShowIntensityIcon(); - }, - ), + final colorMap = ref.watch(kyoshinColorMapProvider); + final (min, max) = (colorMap.first, colorMap.last); + final theme = Theme.of(context); + + final barWidget = Container( + margin: const EdgeInsets.symmetric(vertical: 8), + width: 36, + height: 4, + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + color: theme.colorScheme.onBackground, + boxShadow: const [ + BoxShadow(color: Colors.black12, blurRadius: 12), ], ), ); + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + barWidget, + SafeArea( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SettingsSectionHeader( + text: '表示する最低リアルタイム震度', + ), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 4), + child: SizedBox( + height: 30, + child: KmoniScaleWidget( + showText: false, + markers: [ + if (state.minRealtimeShindo != null && + state.minRealtimeShindo != -3.0) + state.minRealtimeShindo!, + ], + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: SliderTheme( + data: theme.sliderTheme.copyWith( + trackShape: _CustomTrackShape(), + ), + child: Slider( + min: min.intensity, + max: max.intensity, + value: state.minRealtimeShindo ?? min.intensity, + onChanged: (value) => ref + .read(kmoniSettingsProvider.notifier) + .setMinRealtimeShindo( + value: value, + ), + ), + ), + ), + SwitchListTile.adaptive( + value: state.showRealtimeShindoScale, + onChanged: (value) => ref + .read(kmoniSettingsProvider.notifier) + .setShowRealtimeShindoScale( + value: value, + ), + title: const Text('リアルタイム震度のスケールを表示'), + ), + ], + ), + ), + ], + ); + } +} + +class _CustomTrackShape extends RoundedRectSliderTrackShape { + @override + Rect getPreferredRect({ + required RenderBox parentBox, + Offset offset = Offset.zero, + required SliderThemeData sliderTheme, + bool isEnabled = false, + bool isDiscrete = false, + }) { + final trackHeight = sliderTheme.trackHeight; + final trackLeft = offset.dx; + final trackTop = offset.dy + (parentBox.size.height - trackHeight!) / 2; + final trackWidth = parentBox.size.width; + return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight); } } diff --git a/app/lib/feature/home/component/sheet/status_widget.dart b/app/lib/feature/home/component/sheet/status_widget.dart index 3e91686cc..ebb2b9ebe 100644 --- a/app/lib/feature/home/component/sheet/status_widget.dart +++ b/app/lib/feature/home/component/sheet/status_widget.dart @@ -1,7 +1,7 @@ import 'package:eqmonitor/core/component/container/bordered_container.dart'; import 'package:eqmonitor/core/provider/kmoni/model/kmoni_view_model_state.dart'; +import 'package:eqmonitor/core/provider/kmoni/viewmodel/kmoni_settings.dart'; import 'package:eqmonitor/core/provider/kmoni/viewmodel/kmoni_view_model.dart'; -import 'package:eqmonitor/core/provider/kmoni/viewmodel/kmoni_view_settings.dart'; import 'package:eqmonitor/core/provider/websocket/websocket_provider.dart'; import 'package:eqmonitor/core/theme/build_theme.dart'; import 'package:flutter/material.dart' hide ConnectionState; diff --git a/app/lib/feature/home/features/map/viewmodel/main_map_viewmodel.dart b/app/lib/feature/home/features/map/viewmodel/main_map_viewmodel.dart index 25a6b7b2d..61c88c741 100644 --- a/app/lib/feature/home/features/map/viewmodel/main_map_viewmodel.dart +++ b/app/lib/feature/home/features/map/viewmodel/main_map_viewmodel.dart @@ -12,8 +12,8 @@ import 'package:eqmonitor/core/provider/config/theme/intensity_color/intensity_c import 'package:eqmonitor/core/provider/config/theme/intensity_color/model/intensity_color_model.dart'; import 'package:eqmonitor/core/provider/eew/eew_alive_telegram.dart'; import 'package:eqmonitor/core/provider/estimated_intensity/provider/estimated_intensity_provider.dart'; +import 'package:eqmonitor/core/provider/kmoni/viewmodel/kmoni_settings.dart'; import 'package:eqmonitor/core/provider/kmoni/viewmodel/kmoni_view_model.dart'; -import 'package:eqmonitor/core/provider/kmoni/viewmodel/kmoni_view_settings.dart'; import 'package:eqmonitor/core/provider/kmoni_observation_points/model/kmoni_observation_point.dart'; import 'package:eqmonitor/core/provider/map/map_style.dart'; import 'package:eqmonitor/core/provider/travel_time/provider/travel_time_provider.dart'; diff --git a/app/lib/feature/home/view/home_view.dart b/app/lib/feature/home/view/home_view.dart index 16c3df347..bfa808b20 100644 --- a/app/lib/feature/home/view/home_view.dart +++ b/app/lib/feature/home/view/home_view.dart @@ -1,5 +1,5 @@ import 'dart:async'; -import 'dart:developer'; +import 'dart:math'; import 'package:collection/collection.dart'; import 'package:eqapi_types/eqapi_types.dart'; @@ -14,11 +14,14 @@ import 'package:eqmonitor/core/provider/capture/intensity_icon_render.dart'; import 'package:eqmonitor/core/provider/config/notification/fcm_topic_manager.dart'; import 'package:eqmonitor/core/provider/config/permission/permission_status_provider.dart'; import 'package:eqmonitor/core/provider/eew/eew_alive_telegram.dart'; +import 'package:eqmonitor/core/provider/kmoni/viewmodel/kmoni_settings.dart'; import 'package:eqmonitor/core/provider/kmoni/viewmodel/kmoni_view_model.dart'; import 'package:eqmonitor/core/provider/kmoni/widget/kmoni_maintenance_widget.dart'; import 'package:eqmonitor/core/provider/ntp/ntp_provider.dart'; import 'package:eqmonitor/core/router/router.dart'; import 'package:eqmonitor/feature/home/component/eew/eew_widget.dart'; +import 'package:eqmonitor/feature/home/component/kmoni/kmoni_scale.dart'; +import 'package:eqmonitor/feature/home/component/kmoni/kmoni_settings_dialog.dart'; import 'package:eqmonitor/feature/home/component/parameter/parameter_loader_widget.dart'; import 'package:eqmonitor/feature/home/component/render/map_components_renderer.dart'; import 'package:eqmonitor/feature/home/component/sheet/earthquake_history_widget.dart'; @@ -179,7 +182,6 @@ class _HomeBodyWidget extends HookConsumerWidget { return true; // ignore: avoid_catches_without_on_clauses } catch (e) { - log('画像のキャッシュ 失敗: $e'); await Future.delayed(const Duration(milliseconds: 1000)); return true; } @@ -194,6 +196,7 @@ class _HomeBodyWidget extends HookConsumerWidget { final child = Stack( children: [ const MainMapView(), + const _KmoniScale(), SheetFloatingActionButtons( controller: sheetController, fab: const [ @@ -281,6 +284,16 @@ class _Fabs extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { return Column( children: [ + FloatingActionButton.small( + heroTag: 'sheet', + tooltip: '強震モニタの設定', + onPressed: () => showModalBottomSheet( + context: context, + builder: (context) => const KmoniSettingsWidget(), + ), + elevation: 0, + child: const Icon(Icons.settings), + ), FloatingActionButton.small( heroTag: 'home', tooltip: '表示領域領域を戻す', @@ -334,3 +347,53 @@ class _Sheet extends StatelessWidget { ); } } + +class _KmoniScale extends ConsumerWidget { + const _KmoniScale(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final state = ref.watch(kmoniSettingsProvider); + final body = Padding( + padding: const EdgeInsets.all(8), + child: Tooltip( + message: '強震モニタ リアルタイム震度のスケール', + child: Card( + clipBehavior: Clip.antiAlias, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + ), + child: InkWell( + child: KmoniScaleWidget( + markers: [ + if (state.minRealtimeShindo != null && + state.minRealtimeShindo != -3.0) + state.minRealtimeShindo!, + ], + ), + onTap: () {}, + ), + ), + ), + ); + + if (!state.showRealtimeShindoScale || !state.useKmoni) { + return const SizedBox.shrink(); + } + + return LayoutBuilder( + builder: (context, constraints) { + // 横幅は 画面2/3 もしくは 300px 以下 + final width = min(constraints.maxWidth * 2 / 3, 300); + return Align( + alignment: Alignment.topRight, + child: SizedBox( + width: width.toDouble(), + height: 50, + child: body, + ), + ); + }, + ); + } +} diff --git a/app/lib/feature/settings/children/config/debug/debugger_page.dart b/app/lib/feature/settings/children/config/debug/debugger_page.dart index 7c99ee391..767bee995 100644 --- a/app/lib/feature/settings/children/config/debug/debugger_page.dart +++ b/app/lib/feature/settings/children/config/debug/debugger_page.dart @@ -64,7 +64,7 @@ class _DebugWidget extends ConsumerWidget { leading: const Icon(Icons.settings), onTap: () => showDialog( context: context, - builder: (context) => const KmoniSettingsDialogWidget(), + builder: (context) => const KmoniSettingsWidget(), ), ), ListTile( diff --git a/app/lib/feature/setup/pages/kmoni_warn.dart b/app/lib/feature/setup/pages/kmoni_warn.dart index 1933bbfea..a7f85be05 100644 --- a/app/lib/feature/setup/pages/kmoni_warn.dart +++ b/app/lib/feature/setup/pages/kmoni_warn.dart @@ -1,6 +1,6 @@ import 'package:eqmonitor/core/component/button/action_button.dart'; import 'package:eqmonitor/core/component/widget/kmoni_caution.dart'; -import 'package:eqmonitor/core/provider/kmoni/viewmodel/kmoni_view_settings.dart'; +import 'package:eqmonitor/core/provider/kmoni/viewmodel/kmoni_settings.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; diff --git a/app/lib/main.dart b/app/lib/main.dart index a0b311a5f..693ddc30d 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -9,6 +9,8 @@ import 'package:eqmonitor/core/provider/application_documents_directory.dart'; import 'package:eqmonitor/core/provider/custom_provider_observer.dart'; import 'package:eqmonitor/core/provider/device_info.dart'; import 'package:eqmonitor/core/provider/jma_code_table_provider.dart'; +import 'package:eqmonitor/core/provider/kmoni/data/kyoshin_color_map_data_source.dart'; +import 'package:eqmonitor/core/provider/kmoni/provider/kmoni_color_provider.dart'; import 'package:eqmonitor/core/provider/kmoni_observation_points/provider/kyoshin_observation_points_provider.dart'; import 'package:eqmonitor/core/provider/log/talker.dart'; import 'package:eqmonitor/core/provider/package_info.dart'; @@ -83,41 +85,43 @@ Future main() async { final deviceInfo = DeviceInfoPlugin(); final results = await ( - SharedPreferences.getInstance(), - loadKmoniObservationPoints(), - PackageInfo.fromPlatform(), - // ignore: prefer_void_to_null - (!kIsWeb && Platform.isAndroid - ? deviceInfo.androidInfo - : Future.value()), - // ignore: prefer_void_to_null - (!kIsWeb && Platform.isIOS ? deviceInfo.iosInfo : Future.value()), + ( + SharedPreferences.getInstance(), + loadKmoniObservationPoints(), + PackageInfo.fromPlatform(), + // ignore: prefer_void_to_null + (!kIsWeb && Platform.isAndroid + ? deviceInfo.androidInfo + : Future.value()), + // ignore: prefer_void_to_null + (!kIsWeb && Platform.isIOS ? deviceInfo.iosInfo : Future.value()), - kIsWeb ? Future.value() : _registerNotificationChannelIfNeeded(), - kIsWeb ? Future.value() : getApplicationDocumentsDirectory(), - loadJmaCodeTable(), - kIsWeb - ? Future.value() - : FlutterLocalNotificationsPlugin().initialize( - const InitializationSettings( - iOS: DarwinInitializationSettings( - requestAlertPermission: false, - requestSoundPermission: false, - requestBadgePermission: false, - ), - android: AndroidInitializationSettings('mipmap/ic_launcher'), - macOS: DarwinInitializationSettings( - requestAlertPermission: false, - requestSoundPermission: false, - requestBadgePermission: false, + kIsWeb ? Future.value() : _registerNotificationChannelIfNeeded(), + kIsWeb ? Future.value() : getApplicationDocumentsDirectory(), + loadJmaCodeTable(), + kIsWeb + ? Future.value() + : FlutterLocalNotificationsPlugin().initialize( + const InitializationSettings( + iOS: DarwinInitializationSettings( + requestAlertPermission: false, + requestSoundPermission: false, + requestBadgePermission: false, + ), + android: AndroidInitializationSettings('mipmap/ic_launcher'), + macOS: DarwinInitializationSettings( + requestAlertPermission: false, + requestSoundPermission: false, + requestBadgePermission: false, + ), ), ), - ), - ).wait; - - await ( - initInAppPurchase(), - initLicenses(), + ).wait, + ( + initInAppPurchase(), + initLicenses(), + kIsWeb ? Future.value() : getKyoshinColorMap(), + ).wait, ).wait; FirebaseMessaging.onBackgroundMessage(onBackgroundMessage); @@ -128,17 +132,19 @@ Future main() async { } container = ProviderContainer( overrides: [ - sharedPreferencesProvider.overrideWithValue(results.$1), - kyoshinObservationPointsProvider.overrideWithValue(results.$2), + sharedPreferencesProvider.overrideWithValue(results.$1.$1), + kyoshinObservationPointsProvider.overrideWithValue(results.$1.$2), talkerProvider.overrideWithValue(talker), - packageInfoProvider.overrideWithValue(results.$3), - if (results.$4 != null) - androidDeviceInfoProvider.overrideWithValue(results.$4!), - if (results.$5 != null) - iosDeviceInfoProvider.overrideWithValue(results.$5!), - if (results.$7 != null) - applicationDocumentsDirectoryProvider.overrideWithValue(results.$7!), - jmaCodeTableProvider.overrideWithValue(results.$8), + packageInfoProvider.overrideWithValue(results.$1.$3), + if (results.$1.$4 != null) + androidDeviceInfoProvider.overrideWithValue(results.$1.$4!), + if (results.$1.$5 != null) + iosDeviceInfoProvider.overrideWithValue(results.$1.$5!), + if (results.$1.$7 != null) + applicationDocumentsDirectoryProvider.overrideWithValue(results.$1.$7!), + jmaCodeTableProvider.overrideWithValue(results.$1.$8), + if (results.$2.$3 != null) + kyoshinColorMapProvider.overrideWithValue(results.$2.$3!), ], observers: [ if (kDebugMode) diff --git a/melos.yaml b/melos.yaml index fd9e7e6d5..25142165a 100644 --- a/melos.yaml +++ b/melos.yaml @@ -1,6 +1,5 @@ name: eqmonitor_workspace repository: https://github.com/YumNumm/EQMonitor -sdkPath: .fvm/flutter_sdk command: version: