diff --git a/lib/api/api.dart b/lib/api/api.dart index bcc7a78..8c13f10 100644 --- a/lib/api/api.dart +++ b/lib/api/api.dart @@ -7,6 +7,7 @@ import 'package:starcitizen_doctor/data/app_placard_data.dart'; import 'package:starcitizen_doctor/data/app_torrent_data.dart'; import 'package:starcitizen_doctor/data/app_version_data.dart'; import 'package:starcitizen_doctor/data/countdown_festival_item_data.dart'; +import 'package:starcitizen_doctor/data/input_method_api_data.dart'; import 'package:starcitizen_doctor/data/sc_localization_data.dart'; class Api { @@ -52,6 +53,15 @@ class Api { return l; } + static Future getCommunityInputMethodIndexData() async { + final data = await getCommunityInputMethodData("index.json"); + return InputMethodApiData.fromJson(json.decode(data)); + } + + static Future getCommunityInputMethodData(String file) async { + return getRepoData("input_method", file); + } + static Future> getAppTorrentDataList() async { final data = await getRepoData("sc_doctor", "torrent.json"); final dataJson = json.decode(data); @@ -83,13 +93,15 @@ class Api { } static Future getRepoData(String dir, String name) async { - final r = await RSHttp.getText("${URLConf.apiRepoPath}/$dir/$name",withCustomDns: await isUseInternalDNS()); + final r = await RSHttp.getText("${URLConf.apiRepoPath}/$dir/$name", + withCustomDns: await isUseInternalDNS()); return r; } static Future isUseInternalDNS() async { final userBox = await Hive.openBox("app_conf"); - final isUseInternalDNS = userBox.get("isUseInternalDNS", defaultValue: false); + final isUseInternalDNS = + userBox.get("isUseInternalDNS", defaultValue: false); return isUseInternalDNS; } } diff --git a/lib/data/input_method_api_data.dart b/lib/data/input_method_api_data.dart new file mode 100644 index 0000000..236589d --- /dev/null +++ b/lib/data/input_method_api_data.dart @@ -0,0 +1,52 @@ +class InputMethodApiData { + InputMethodApiData({ + this.enable, + this.languages, + }); + + InputMethodApiData.fromJson(dynamic json) { + enable = json['enable']; + if (json['languages'] != null) { + languages = {}; + json['languages'].forEach((String key, dynamic v) { + languages![key] = InputMethodApiLanguageData.fromJson(v); + }); + } + } + + bool? enable; + Map? languages; + + Map toJson() { + final map = {}; + map['enable'] = enable; + if (languages != null) { + map['languages'] = languages!.map((key, value) { + return MapEntry(key, value.toJson()); + }); + } + return map; + } +} + +class InputMethodApiLanguageData { + InputMethodApiLanguageData({ + this.file, + this.version, + }); + + InputMethodApiLanguageData.fromJson(dynamic json) { + file = json['file']; + version = json['version']; + } + + String? file; + String? version; + + Map toJson() { + final map = {}; + map['file'] = file; + map['version'] = version; + return map; + } +} diff --git a/lib/ui/home/localization/localization_dialog_ui.dart b/lib/ui/home/localization/localization_dialog_ui.dart index dd90677..ec2ab76 100644 --- a/lib/ui/home/localization/localization_dialog_ui.dart +++ b/lib/ui/home/localization/localization_dialog_ui.dart @@ -4,6 +4,7 @@ import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:flutter_tilt/flutter_tilt.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:starcitizen_doctor/common/utils/log.dart'; import 'package:starcitizen_doctor/data/sc_localization_data.dart'; import 'package:starcitizen_doctor/ui/tools/tools_ui_model.dart'; import 'package:starcitizen_doctor/widgets/widgets.dart'; @@ -103,9 +104,21 @@ class LocalizationDialogUI extends HookConsumerWidget { const SizedBox(height: 12), Row( children: [ - Text(S.current.localization_info_installed_version( - "${state.patchStatus?.value ?? ""} ${(state.isInstalledAdvanced ?? false) ? S.current.home_localization_msg_version_advanced : ""}")), - const Spacer(), + Expanded( + child: Row( + children: [ + Text(S.current.localization_info_installed_version( + "${state.patchStatus?.value ?? ""} ${(state.isInstalledAdvanced ?? false) ? S.current.home_localization_msg_version_advanced : ""}")), + SizedBox(width: 24), + if (state + .installedCommunityInputMethodSupportVersion != + null) + Text( + "社区输入法支持:${state.installedCommunityInputMethodSupportVersion}", + ) + ], + ), + ), if (state.patchStatus?.value != S.current.home_action_info_game_built_in) Row( @@ -415,6 +428,8 @@ class LocalizationDialogUI extends HookConsumerWidget { LocalizationUIModel model, MapEntry item, LocalizationUIState state) async { + bool enableCommunityInputMethod = + state.communityInputMethodLanguageData != null; final userOK = await showConfirmDialogs( context, "${item.value.info}", @@ -443,6 +458,7 @@ class LocalizationDialogUI extends HookConsumerWidget { .localization_info_update_time(item.value.updateAt ?? ""), style: TextStyle(color: Colors.white.withOpacity(.6)), ), + const SizedBox(height: 12), ], ), const SizedBox(height: 12), @@ -462,6 +478,29 @@ class LocalizationDialogUI extends HookConsumerWidget { ], ), ), + SizedBox(height: 12), + Row( + children: [ + Text( + "安装社区输入法支持", + ), + Spacer(), + StatefulBuilder( + builder: (BuildContext context, + void Function(void Function()) setState) { + return ToggleSwitch( + checked: enableCommunityInputMethod, + onChanged: state.communityInputMethodLanguageData == null + ? null + : (v) { + enableCommunityInputMethod = v; + setState(() {}); + }, + ); + }, + ) + ], + ) ], ), confirm: S.current.localization_action_install, @@ -471,7 +510,11 @@ class LocalizationDialogUI extends HookConsumerWidget { ); if (userOK) { if (!context.mounted) return; - model.doRemoteInstall(context, item.value)?.call(); + dPrint("doRemoteInstall ${item.value} $enableCommunityInputMethod"); + model + .doRemoteInstall(context, item.value, + isEnableCommunityInputMethod: enableCommunityInputMethod) + ?.call(); } } diff --git a/lib/ui/home/localization/localization_ui_model.dart b/lib/ui/home/localization/localization_ui_model.dart index 1a6c284..b681931 100644 --- a/lib/ui/home/localization/localization_ui_model.dart +++ b/lib/ui/home/localization/localization_ui_model.dart @@ -15,6 +15,7 @@ import 'package:starcitizen_doctor/common/conf/url_conf.dart'; import 'package:starcitizen_doctor/common/io/rs_http.dart'; import 'package:starcitizen_doctor/common/utils/log.dart'; import 'package:starcitizen_doctor/common/utils/provider.dart'; +import 'package:starcitizen_doctor/data/input_method_api_data.dart'; import 'package:starcitizen_doctor/data/sc_localization_data.dart'; import 'package:starcitizen_doctor/generated/no_l10n_strings.dart'; import 'package:starcitizen_doctor/ui/home/home_ui_model.dart'; @@ -29,6 +30,8 @@ part 'localization_ui_model.freezed.dart'; class LocalizationUIState with _$LocalizationUIState { factory LocalizationUIState({ String? selectedLanguage, + String? installedCommunityInputMethodSupportVersion, + InputMethodApiLanguageData? communityInputMethodLanguageData, Map? apiLocalizationData, @Default("") String workingVersion, MapEntry? patchStatus, @@ -83,9 +86,28 @@ class LocalizationUIModel extends _$LocalizationUIModel { final Map> _allVersionLocalizationData = {}; + Future _loadCommunityInputMethodData() async { + try { + final data = await Api.getCommunityInputMethodIndexData(); + if (data.enable ?? false) { + final lang = state.selectedLanguage; + if (lang != null) { + final l = data.languages?[lang]; + if (l != null) { + state = state.copyWith(communityInputMethodLanguageData: l); + dPrint("loadCommunityInputMethodData: ${l.toJson()}"); + } + } + } + } catch (e) { + dPrint("loadCommunityInputMethodData error: $e"); + } + } + Future _loadData() async { _allVersionLocalizationData.clear(); await _updateStatus(); + await _loadCommunityInputMethodData(); for (var lang in languageSupport.keys) { final l = await Api.getScLocalizationData(lang).unwrap(); if (l != null) { @@ -211,8 +233,13 @@ class LocalizationUIModel extends _$LocalizationUIModel { return path.split("\\").last; } - installFormString(StringBuffer globalIni, String versionName, - {bool? advanced}) async { + installFormString( + StringBuffer globalIni, + String versionName, { + bool? advanced, + String? communityInputMethodVersion, + String? communityInputMethodSupportData, + }) async { dPrint("LocalizationUIModel -> installFormString $versionName"); final iniFile = File( "${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini"); @@ -223,6 +250,15 @@ class LocalizationUIModel extends _$LocalizationUIModel { if (advanced ?? false) { globalIni.write("_starcitizen_doctor_localization_advanced=true\n"); } + if (communityInputMethodVersion != null) { + globalIni.write( + "_starcitizen_doctor_localization_community_input_method_version=$communityInputMethodVersion\n"); + } + if (communityInputMethodSupportData != null) { + for (var line in communityInputMethodSupportData.split("\n")) { + globalIni.write("$line\n"); + } + } globalIni .write("_starcitizen_doctor_localization_version=$versionName\n"); } @@ -241,8 +277,8 @@ class LocalizationUIModel extends _$LocalizationUIModel { await _updateStatus(); } - VoidCallback? doRemoteInstall( - BuildContext context, ScLocalizationData value) { + VoidCallback? doRemoteInstall(BuildContext context, ScLocalizationData value, + {bool isEnableCommunityInputMethod = false}) { return () async { AnalyticsApi.touch("install_localization"); @@ -256,13 +292,33 @@ class LocalizationUIModel extends _$LocalizationUIModel { } else { dPrint("use cache $savePath"); } + + final communityInputMethodData = state.communityInputMethodLanguageData; + + String? communityInputMethodSupportData; + + if (isEnableCommunityInputMethod && communityInputMethodData != null) { + final str = await downloadOrGetCachedCommunityInputMethodSupportFile( + communityInputMethodData); + if (str.trim().isNotEmpty) { + communityInputMethodSupportData = str; + } + } + await Future.delayed(const Duration(milliseconds: 300)); // check file final globalIni = await compute(readArchive, savePath.absolute.path); if (globalIni.isEmpty) { throw S.current.localization_info_corrupted_file; } - await installFormString(globalIni, value.versionName ?? ""); + await installFormString( + globalIni, + value.versionName ?? "", + communityInputMethodSupportData: communityInputMethodSupportData, + communityInputMethodVersion: isEnableCommunityInputMethod + ? communityInputMethodData?.version + : null, + ); } catch (e) { if (!context.mounted) return; await showToast( @@ -273,6 +329,21 @@ class LocalizationUIModel extends _$LocalizationUIModel { }; } + Future downloadOrGetCachedCommunityInputMethodSupportFile( + InputMethodApiLanguageData communityInputMethodData) async { + final lang = state.selectedLanguage ?? "_"; + final box = await Hive.openBox("community_input_method_data"); + final cachedVersion = box.get("${lang}_version"); + + if (cachedVersion != communityInputMethodData.version) { + final data = await Api.getCommunityInputMethodData( + communityInputMethodData.file ?? ""); + await box.put("${lang}_data", data); + return data; + } + return box.get("${lang}_data").toString(); + } + Future downloadLocalizationFile( File savePath, ScLocalizationData value) async { dPrint("downloading file to $savePath"); @@ -310,7 +381,8 @@ class LocalizationUIModel extends _$LocalizationUIModel { } void selectLang(String v) async { - state = state.copyWith(selectedLanguage: v); + state = state.copyWith( + selectedLanguage: v, communityInputMethodLanguageData: null); _loadData(); final appConfBox = await Hive.openBox("app_conf"); await appConfBox.put("localization_selectedLanguage", v); @@ -332,14 +404,42 @@ class LocalizationUIModel extends _$LocalizationUIModel { } _updateStatus() async { + final iniPath = + "${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini"; final patchStatus = MapEntry( await _getLangCfgEnableLang(lang: state.selectedLanguage!), - await _getInstalledIniVersion( - "${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini")); - final isInstalledAdvanced = await _checkAdvancedStatus( - "${_scDataDir.absolute.path}\\Localization\\${state.selectedLanguage}\\global.ini"); + await _getInstalledIniVersion(iniPath)); + final isInstalledAdvanced = await _checkAdvancedStatus(iniPath); + final installedCommunityInputMethodSupportVersion = + await getInstalledCommunityInputMethodSupportVersion(iniPath); + + dPrint( + "_updateStatus updateStatus: $patchStatus , isInstalledAdvanced: $isInstalledAdvanced ,installedCommunityInputMethodSupportVersion: $installedCommunityInputMethodSupportVersion"); + state = state.copyWith( - patchStatus: patchStatus, isInstalledAdvanced: isInstalledAdvanced); + patchStatus: patchStatus, + isInstalledAdvanced: isInstalledAdvanced, + installedCommunityInputMethodSupportVersion: + installedCommunityInputMethodSupportVersion, + ); + } + + Future getInstalledCommunityInputMethodSupportVersion( + String path) async { + final iniFile = File(path); + if (!await iniFile.exists()) { + return null; + } + final iniStringSplit = (await iniFile.readAsString()).split("\n"); + for (var i = iniStringSplit.length - 1; i > 0; i--) { + if (iniStringSplit[i].contains( + "_starcitizen_doctor_localization_community_input_method_version=")) { + final v = iniStringSplit[i].trim().split( + "_starcitizen_doctor_localization_community_input_method_version=")[1]; + return v; + } + } + return null; } Future _checkAdvancedStatus(String path) async { diff --git a/lib/ui/home/localization/localization_ui_model.freezed.dart b/lib/ui/home/localization/localization_ui_model.freezed.dart index aac3eb8..6e8fbd1 100644 --- a/lib/ui/home/localization/localization_ui_model.freezed.dart +++ b/lib/ui/home/localization/localization_ui_model.freezed.dart @@ -17,6 +17,10 @@ final _privateConstructorUsedError = UnsupportedError( /// @nodoc mixin _$LocalizationUIState { String? get selectedLanguage => throw _privateConstructorUsedError; + String? get installedCommunityInputMethodSupportVersion => + throw _privateConstructorUsedError; + InputMethodApiLanguageData? get communityInputMethodLanguageData => + throw _privateConstructorUsedError; Map? get apiLocalizationData => throw _privateConstructorUsedError; String get workingVersion => throw _privateConstructorUsedError; @@ -39,6 +43,8 @@ abstract class $LocalizationUIStateCopyWith<$Res> { @useResult $Res call( {String? selectedLanguage, + String? installedCommunityInputMethodSupportVersion, + InputMethodApiLanguageData? communityInputMethodLanguageData, Map? apiLocalizationData, String workingVersion, MapEntry? patchStatus, @@ -62,6 +68,8 @@ class _$LocalizationUIStateCopyWithImpl<$Res, $Val extends LocalizationUIState> @override $Res call({ Object? selectedLanguage = freezed, + Object? installedCommunityInputMethodSupportVersion = freezed, + Object? communityInputMethodLanguageData = freezed, Object? apiLocalizationData = freezed, Object? workingVersion = null, Object? patchStatus = freezed, @@ -73,6 +81,16 @@ class _$LocalizationUIStateCopyWithImpl<$Res, $Val extends LocalizationUIState> ? _value.selectedLanguage : selectedLanguage // ignore: cast_nullable_to_non_nullable as String?, + installedCommunityInputMethodSupportVersion: freezed == + installedCommunityInputMethodSupportVersion + ? _value.installedCommunityInputMethodSupportVersion + : installedCommunityInputMethodSupportVersion // ignore: cast_nullable_to_non_nullable + as String?, + communityInputMethodLanguageData: freezed == + communityInputMethodLanguageData + ? _value.communityInputMethodLanguageData + : communityInputMethodLanguageData // ignore: cast_nullable_to_non_nullable + as InputMethodApiLanguageData?, apiLocalizationData: freezed == apiLocalizationData ? _value.apiLocalizationData : apiLocalizationData // ignore: cast_nullable_to_non_nullable @@ -107,6 +125,8 @@ abstract class _$$LocalizationUIStateImplCopyWith<$Res> @useResult $Res call( {String? selectedLanguage, + String? installedCommunityInputMethodSupportVersion, + InputMethodApiLanguageData? communityInputMethodLanguageData, Map? apiLocalizationData, String workingVersion, MapEntry? patchStatus, @@ -128,6 +148,8 @@ class __$$LocalizationUIStateImplCopyWithImpl<$Res> @override $Res call({ Object? selectedLanguage = freezed, + Object? installedCommunityInputMethodSupportVersion = freezed, + Object? communityInputMethodLanguageData = freezed, Object? apiLocalizationData = freezed, Object? workingVersion = null, Object? patchStatus = freezed, @@ -139,6 +161,16 @@ class __$$LocalizationUIStateImplCopyWithImpl<$Res> ? _value.selectedLanguage : selectedLanguage // ignore: cast_nullable_to_non_nullable as String?, + installedCommunityInputMethodSupportVersion: freezed == + installedCommunityInputMethodSupportVersion + ? _value.installedCommunityInputMethodSupportVersion + : installedCommunityInputMethodSupportVersion // ignore: cast_nullable_to_non_nullable + as String?, + communityInputMethodLanguageData: freezed == + communityInputMethodLanguageData + ? _value.communityInputMethodLanguageData + : communityInputMethodLanguageData // ignore: cast_nullable_to_non_nullable + as InputMethodApiLanguageData?, apiLocalizationData: freezed == apiLocalizationData ? _value._apiLocalizationData : apiLocalizationData // ignore: cast_nullable_to_non_nullable @@ -168,6 +200,8 @@ class __$$LocalizationUIStateImplCopyWithImpl<$Res> class _$LocalizationUIStateImpl implements _LocalizationUIState { _$LocalizationUIStateImpl( {this.selectedLanguage, + this.installedCommunityInputMethodSupportVersion, + this.communityInputMethodLanguageData, final Map? apiLocalizationData, this.workingVersion = "", this.patchStatus, @@ -178,6 +212,10 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState { @override final String? selectedLanguage; + @override + final String? installedCommunityInputMethodSupportVersion; + @override + final InputMethodApiLanguageData? communityInputMethodLanguageData; final Map? _apiLocalizationData; @override Map? get apiLocalizationData { @@ -208,7 +246,7 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState { @override String toString() { - return 'LocalizationUIState(selectedLanguage: $selectedLanguage, apiLocalizationData: $apiLocalizationData, workingVersion: $workingVersion, patchStatus: $patchStatus, isInstalledAdvanced: $isInstalledAdvanced, customizeList: $customizeList)'; + return 'LocalizationUIState(selectedLanguage: $selectedLanguage, installedCommunityInputMethodSupportVersion: $installedCommunityInputMethodSupportVersion, communityInputMethodLanguageData: $communityInputMethodLanguageData, apiLocalizationData: $apiLocalizationData, workingVersion: $workingVersion, patchStatus: $patchStatus, isInstalledAdvanced: $isInstalledAdvanced, customizeList: $customizeList)'; } @override @@ -218,6 +256,14 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState { other is _$LocalizationUIStateImpl && (identical(other.selectedLanguage, selectedLanguage) || other.selectedLanguage == selectedLanguage) && + (identical(other.installedCommunityInputMethodSupportVersion, + installedCommunityInputMethodSupportVersion) || + other.installedCommunityInputMethodSupportVersion == + installedCommunityInputMethodSupportVersion) && + (identical(other.communityInputMethodLanguageData, + communityInputMethodLanguageData) || + other.communityInputMethodLanguageData == + communityInputMethodLanguageData) && const DeepCollectionEquality() .equals(other._apiLocalizationData, _apiLocalizationData) && (identical(other.workingVersion, workingVersion) || @@ -234,6 +280,8 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState { int get hashCode => Object.hash( runtimeType, selectedLanguage, + installedCommunityInputMethodSupportVersion, + communityInputMethodLanguageData, const DeepCollectionEquality().hash(_apiLocalizationData), workingVersion, patchStatus, @@ -253,6 +301,8 @@ class _$LocalizationUIStateImpl implements _LocalizationUIState { abstract class _LocalizationUIState implements LocalizationUIState { factory _LocalizationUIState( {final String? selectedLanguage, + final String? installedCommunityInputMethodSupportVersion, + final InputMethodApiLanguageData? communityInputMethodLanguageData, final Map? apiLocalizationData, final String workingVersion, final MapEntry? patchStatus, @@ -262,6 +312,10 @@ abstract class _LocalizationUIState implements LocalizationUIState { @override String? get selectedLanguage; @override + String? get installedCommunityInputMethodSupportVersion; + @override + InputMethodApiLanguageData? get communityInputMethodLanguageData; + @override Map? get apiLocalizationData; @override String get workingVersion; diff --git a/lib/ui/home/localization/localization_ui_model.g.dart b/lib/ui/home/localization/localization_ui_model.g.dart index 883c01d..89926cf 100644 --- a/lib/ui/home/localization/localization_ui_model.g.dart +++ b/lib/ui/home/localization/localization_ui_model.g.dart @@ -7,7 +7,7 @@ part of 'localization_ui_model.dart'; // ************************************************************************** String _$localizationUIModelHash() => - r'3441a52a0047dcb685c6fdcb25044dae8688210b'; + r'86e433e1901683ad05b81e34d3b37b9b72c4c786'; /// See also [LocalizationUIModel]. @ProviderFor(LocalizationUIModel) diff --git a/lib/ui/settings/settings_ui_model.g.dart b/lib/ui/settings/settings_ui_model.g.dart index 0abfbbb..674d917 100644 --- a/lib/ui/settings/settings_ui_model.g.dart +++ b/lib/ui/settings/settings_ui_model.g.dart @@ -6,7 +6,7 @@ part of 'settings_ui_model.dart'; // RiverpodGenerator // ************************************************************************** -String _$settingsUIModelHash() => r'27193efaa8964e8a097daf8dbabf93bb1fdcab3c'; +String _$settingsUIModelHash() => r'00662390cfeab943929fe4820665de211f9ec8ef'; /// See also [SettingsUIModel]. @ProviderFor(SettingsUIModel)