Commit 6650b9effc2c303d0502707c995105fb7e7fa12b

Authored by 权海
1 parent fbfb0bc5

feat(ui):优化主题应用

@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 archiveVersion = 1; 3 archiveVersion = 1;
4 classes = { 4 classes = {
5 }; 5 };
6 - objectVersion = 77; 6 + objectVersion = 54;
7 objects = { 7 objects = {
8 8
9 /* Begin PBXBuildFile section */ 9 /* Begin PBXBuildFile section */
@@ -190,8 +190,7 @@ class CustomWatchThemePreviewController extends GetxController { @@ -190,8 +190,7 @@ class CustomWatchThemePreviewController extends GetxController {
190 WatchThemeSyncDialog( 190 WatchThemeSyncDialog(
191 themeImageUrl: themeItem.energeticImage, 191 themeImageUrl: themeItem.energeticImage,
192 onSync: () async { 192 onSync: () async {
193 - if (device?.isPaired != true ||  
194 - device?.isWatchAppInstalled != true) { 193 + if (device?.isPaired != true || device?.isWatchAppInstalled != true) {
195 Get.back(); 194 Get.back();
196 await Get.dialog<void>( 195 await Get.dialog<void>(
197 const WatchThemeNoWatchDialog(), 196 const WatchThemeNoWatchDialog(),
@@ -217,10 +216,20 @@ class CustomWatchThemePreviewController extends GetxController { @@ -217,10 +216,20 @@ class CustomWatchThemePreviewController extends GetxController {
217 216
218 isApplying.value = true; 217 isApplying.value = true;
219 218
  219 + WatchTransformTheme? downloadedTheme;
  220 + if (themeItem.isDefaultTheme != 1) {
  221 + try {
  222 + downloadedTheme = await _syncPayloadBuilder.build(themeItem);
  223 + } catch (e) {
  224 + AppToast.show(l10n.watchThemeImageDownloadFailed(e.toString()));
  225 + isApplying.value = false;
  226 + return false;
  227 + }
  228 + }
  229 +
220 var syncSuccess = true; 230 var syncSuccess = true;
221 try { 231 try {
222 - final payload = await _syncPayloadBuilder.build(themeItem);  
223 - final success = await WearEngineHostApi().syncWatchTheme(payload); 232 + final success = await WearEngineHostApi().syncWatchTheme(downloadedTheme);
224 if (success) { 233 if (success) {
225 final result = await _themeApi.applyTheme(themeId); 234 final result = await _themeApi.applyTheme(themeId);
226 if (result is! AppSuccess<void>) { 235 if (result is! AppSuccess<void>) {
@@ -161,8 +161,7 @@ class WatchThemePreviewController extends GetxController { @@ -161,8 +161,7 @@ class WatchThemePreviewController extends GetxController {
161 WatchThemeSyncDialog( 161 WatchThemeSyncDialog(
162 themeImageUrl: themeItem.energeticImage, 162 themeImageUrl: themeItem.energeticImage,
163 onSync: () async { 163 onSync: () async {
164 - if (device?.isPaired != true ||  
165 - device?.isWatchAppInstalled != true) { 164 + if (device?.isPaired != true || device?.isWatchAppInstalled != true) {
166 Get.back(); 165 Get.back();
167 await Get.dialog<void>( 166 await Get.dialog<void>(
168 const WatchThemeNoWatchDialog(), 167 const WatchThemeNoWatchDialog(),
@@ -188,10 +187,20 @@ class WatchThemePreviewController extends GetxController { @@ -188,10 +187,20 @@ class WatchThemePreviewController extends GetxController {
188 187
189 isApplying.value = true; 188 isApplying.value = true;
190 189
  190 + WatchTransformTheme? downloadedTheme;
  191 + if (themeItem.isDefaultTheme != 1) {
  192 + try {
  193 + downloadedTheme = await _syncPayloadBuilder.build(themeItem);
  194 + } catch (e) {
  195 + AppToast.show(l10n.watchThemeImageDownloadFailed(e.toString()));
  196 + isApplying.value = false;
  197 + return false;
  198 + }
  199 + }
  200 +
191 var syncSuccess = true; 201 var syncSuccess = true;
192 try { 202 try {
193 - final payload = await _syncPayloadBuilder.build(themeItem);  
194 - final success = await WearEngineHostApi().syncWatchTheme(payload); 203 + final success = await WearEngineHostApi().syncWatchTheme(downloadedTheme);
195 if (success) { 204 if (success) {
196 final result = await _themeApi.applyTheme(themeId); 205 final result = await _themeApi.applyTheme(themeId);
197 if (result is! AppSuccess<void>) { 206 if (result is! AppSuccess<void>) {
1 import 'dart:io'; 1 import 'dart:io';
  2 +import 'dart:typed_data';
2 3
3 import 'package:doublefeel_flutter/app/modules/watch_theme/models/watch_theme_models.dart'; 4 import 'package:doublefeel_flutter/app/modules/watch_theme/models/watch_theme_models.dart';
4 import 'package:doublefeel_flutter/pigeon/wear_engine_api.g.dart'; 5 import 'package:doublefeel_flutter/pigeon/wear_engine_api.g.dart';
5 -import 'package:flutter/foundation.dart';  
6 import 'package:flutter/services.dart'; 6 import 'package:flutter/services.dart';
7 7
8 class WatchThemeSyncPayloadBuilder { 8 class WatchThemeSyncPayloadBuilder {
@@ -46,18 +46,37 @@ class WatchThemeSyncPayloadBuilder { @@ -46,18 +46,37 @@ class WatchThemeSyncPayloadBuilder {
46 } 46 }
47 47
48 Future<Uint8List?> _downloadImage(Uri uri) async { 48 Future<Uint8List?> _downloadImage(Uri uri) async {
49 - final client = HttpClient();  
50 - try {  
51 - final request = await client.getUrl(uri);  
52 - final response = await request.close();  
53 - if (response.statusCode < 200 || response.statusCode >= 300) {  
54 - return null; 49 + const maxAttempts = 3;
  50 + for (var attempt = 0; attempt < maxAttempts; attempt += 1) {
  51 + final client = HttpClient()
  52 + ..connectionTimeout = const Duration(seconds: 15)
  53 + ..idleTimeout = const Duration(seconds: 5);
  54 + try {
  55 + final request = await client.getUrl(uri);
  56 + request.headers.set(HttpHeaders.acceptHeader, 'image/*,*/*');
  57 + request.headers.set(HttpHeaders.connectionHeader, 'close');
  58 + request.headers.set(
  59 + HttpHeaders.userAgentHeader,
  60 + 'doublefeel_flutter/watch_theme',
  61 + );
  62 + final response = await request.close();
  63 + if (response.statusCode < 200 || response.statusCode >= 300) {
  64 + return null;
  65 + }
  66 + final builder = BytesBuilder(copy: false);
  67 + await for (final chunk in response) {
  68 + builder.add(chunk);
  69 + }
  70 + return builder.takeBytes();
  71 + } catch (_) {
  72 + if (attempt == maxAttempts - 1) {
  73 + return null;
  74 + }
  75 + await Future<void>.delayed(Duration(milliseconds: 150 * (attempt + 1)));
  76 + } finally {
  77 + client.close(force: true);
55 } 78 }
56 - return consolidateHttpClientResponseBytes(response);  
57 - } catch (_) {  
58 - return null;  
59 - } finally {  
60 - client.close(force: true);  
61 } 79 }
  80 + return null;
62 } 81 }
63 } 82 }
@@ -659,6 +659,15 @@ @@ -659,6 +659,15 @@
659 "watchThemeAbandonMessage": "Your changes won't be saved if you close this page. Discard them?", 659 "watchThemeAbandonMessage": "Your changes won't be saved if you close this page. Discard them?",
660 "watchThemeContinueEditing": "Continue Editing", 660 "watchThemeContinueEditing": "Continue Editing",
661 "watchThemeImageUploadFailed": "Image upload failed. Please try again", 661 "watchThemeImageUploadFailed": "Image upload failed. Please try again",
  662 + "watchThemeImageDownloadFailed": "Failed to download theme images: {error}",
  663 + "@watchThemeImageDownloadFailed": {
  664 + "description": "Toast shown when watch theme images fail to download",
  665 + "placeholders": {
  666 + "error": {
  667 + "type": "String"
  668 + }
  669 + }
  670 + },
662 "watchThemeCreateFailed": "Failed to create watch face. Please try again", 671 "watchThemeCreateFailed": "Failed to create watch face. Please try again",
663 "watchThemeDeleteTheme": "Delete Theme", 672 "watchThemeDeleteTheme": "Delete Theme",
664 "watchThemeDeleteMessage": "Deleted themes cannot be restored. Delete this theme?", 673 "watchThemeDeleteMessage": "Deleted themes cannot be restored. Delete this theme?",
@@ -1038,6 +1038,15 @@ @@ -1038,6 +1038,15 @@
1038 "watchThemeAbandonMessage": "关闭此页面后,已编辑的内容不会保留,是否放弃编辑?", 1038 "watchThemeAbandonMessage": "关闭此页面后,已编辑的内容不会保留,是否放弃编辑?",
1039 "watchThemeContinueEditing": "继续编辑", 1039 "watchThemeContinueEditing": "继续编辑",
1040 "watchThemeImageUploadFailed": "图片上传失败,请重试", 1040 "watchThemeImageUploadFailed": "图片上传失败,请重试",
  1041 + "watchThemeImageDownloadFailed": "主题图片下载失败:{error}",
  1042 + "@watchThemeImageDownloadFailed": {
  1043 + "description": "表盘主题图片下载失败提示",
  1044 + "placeholders": {
  1045 + "error": {
  1046 + "type": "String"
  1047 + }
  1048 + }
  1049 + },
1041 "watchThemeCreateFailed": "创建表盘失败,请重试", 1050 "watchThemeCreateFailed": "创建表盘失败,请重试",
1042 "watchThemeDeleteTheme": "删除主题", 1051 "watchThemeDeleteTheme": "删除主题",
1043 "watchThemeDeleteMessage": "主题删除后无法恢复,确认删除该主题吗?", 1052 "watchThemeDeleteMessage": "主题删除后无法恢复,确认删除该主题吗?",
@@ -3867,6 +3867,12 @@ abstract class AppLocalizations { @@ -3867,6 +3867,12 @@ abstract class AppLocalizations {
3867 /// **'图片上传失败,请重试'** 3867 /// **'图片上传失败,请重试'**
3868 String get watchThemeImageUploadFailed; 3868 String get watchThemeImageUploadFailed;
3869 3869
  3870 + /// 表盘主题图片下载失败提示
  3871 + ///
  3872 + /// In zh, this message translates to:
  3873 + /// **'主题图片下载失败:{error}'**
  3874 + String watchThemeImageDownloadFailed(String error);
  3875 +
3870 /// No description provided for @watchThemeCreateFailed. 3876 /// No description provided for @watchThemeCreateFailed.
3871 /// 3877 ///
3872 /// In zh, this message translates to: 3878 /// In zh, this message translates to:
@@ -2169,6 +2169,11 @@ class AppLocalizationsEn extends AppLocalizations { @@ -2169,6 +2169,11 @@ class AppLocalizationsEn extends AppLocalizations {
2169 'Image upload failed. Please try again'; 2169 'Image upload failed. Please try again';
2170 2170
2171 @override 2171 @override
  2172 + String watchThemeImageDownloadFailed(String error) {
  2173 + return 'Failed to download theme images: $error';
  2174 + }
  2175 +
  2176 + @override
2172 String get watchThemeCreateFailed => 2177 String get watchThemeCreateFailed =>
2173 'Failed to create watch face. Please try again'; 2178 'Failed to create watch face. Please try again';
2174 2179
@@ -2061,6 +2061,11 @@ class AppLocalizationsZh extends AppLocalizations { @@ -2061,6 +2061,11 @@ class AppLocalizationsZh extends AppLocalizations {
2061 String get watchThemeImageUploadFailed => '图片上传失败,请重试'; 2061 String get watchThemeImageUploadFailed => '图片上传失败,请重试';
2062 2062
2063 @override 2063 @override
  2064 + String watchThemeImageDownloadFailed(String error) {
  2065 + return '主题图片下载失败:$error';
  2066 + }
  2067 +
  2068 + @override
2064 String get watchThemeCreateFailed => '创建表盘失败,请重试'; 2069 String get watchThemeCreateFailed => '创建表盘失败,请重试';
2065 2070
2066 @override 2071 @override