Showing
7 changed files
with
238 additions
and
3 deletions
| @@ -340,6 +340,8 @@ interface PlatformHostApi { | @@ -340,6 +340,8 @@ interface PlatformHostApi { | ||
| 340 | fun getFullUserAgent(): String | 340 | fun getFullUserAgent(): String |
| 341 | /** 是否是测试环境 */ | 341 | /** 是否是测试环境 */ |
| 342 | fun isDebugEnvoriment(): Boolean | 342 | fun isDebugEnvoriment(): Boolean |
| 343 | + fun shareText(text: String, callback: (Result<Boolean>) -> Unit) | ||
| 344 | + fun shareFileData(databytes: ByteArray, callback: (Result<Boolean>) -> Unit) | ||
| 343 | /** | 345 | /** |
| 344 | * 更新用户信息, 有登录态后调用 | 346 | * 更新用户信息, 有登录态后调用 |
| 345 | * jsonString: UserPreferences的序列化string | 347 | * jsonString: UserPreferences的序列化string |
| @@ -421,6 +423,46 @@ interface PlatformHostApi { | @@ -421,6 +423,46 @@ interface PlatformHostApi { | ||
| 421 | } | 423 | } |
| 422 | } | 424 | } |
| 423 | run { | 425 | run { |
| 426 | + val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.shareText$separatedMessageChannelSuffix", codec) | ||
| 427 | + if (api != null) { | ||
| 428 | + channel.setMessageHandler { message, reply -> | ||
| 429 | + val args = message as List<Any?> | ||
| 430 | + val textArg = args[0] as String | ||
| 431 | + api.shareText(textArg) { result: Result<Boolean> -> | ||
| 432 | + val error = result.exceptionOrNull() | ||
| 433 | + if (error != null) { | ||
| 434 | + reply.reply(PlatformApiPigeonUtils.wrapError(error)) | ||
| 435 | + } else { | ||
| 436 | + val data = result.getOrNull() | ||
| 437 | + reply.reply(PlatformApiPigeonUtils.wrapResult(data)) | ||
| 438 | + } | ||
| 439 | + } | ||
| 440 | + } | ||
| 441 | + } else { | ||
| 442 | + channel.setMessageHandler(null) | ||
| 443 | + } | ||
| 444 | + } | ||
| 445 | + run { | ||
| 446 | + val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.shareFileData$separatedMessageChannelSuffix", codec) | ||
| 447 | + if (api != null) { | ||
| 448 | + channel.setMessageHandler { message, reply -> | ||
| 449 | + val args = message as List<Any?> | ||
| 450 | + val databytesArg = args[0] as ByteArray | ||
| 451 | + api.shareFileData(databytesArg) { result: Result<Boolean> -> | ||
| 452 | + val error = result.exceptionOrNull() | ||
| 453 | + if (error != null) { | ||
| 454 | + reply.reply(PlatformApiPigeonUtils.wrapError(error)) | ||
| 455 | + } else { | ||
| 456 | + val data = result.getOrNull() | ||
| 457 | + reply.reply(PlatformApiPigeonUtils.wrapResult(data)) | ||
| 458 | + } | ||
| 459 | + } | ||
| 460 | + } | ||
| 461 | + } else { | ||
| 462 | + channel.setMessageHandler(null) | ||
| 463 | + } | ||
| 464 | + } | ||
| 465 | + run { | ||
| 424 | val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.updateLoginInfo$separatedMessageChannelSuffix", codec) | 466 | val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.updateLoginInfo$separatedMessageChannelSuffix", codec) |
| 425 | if (api != null) { | 467 | if (api != null) { |
| 426 | channel.setMessageHandler { message, reply -> | 468 | channel.setMessageHandler { message, reply -> |
| @@ -370,6 +370,8 @@ protocol PlatformHostApi { | @@ -370,6 +370,8 @@ protocol PlatformHostApi { | ||
| 370 | func getFullUserAgent() throws -> String | 370 | func getFullUserAgent() throws -> String |
| 371 | /// 是否是测试环境 | 371 | /// 是否是测试环境 |
| 372 | func isDebugEnvoriment() throws -> Bool | 372 | func isDebugEnvoriment() throws -> Bool |
| 373 | + func shareText(text: String, completion: @escaping (Result<Bool, Error>) -> Void) | ||
| 374 | + func shareFileData(databytes: FlutterStandardTypedData, completion: @escaping (Result<Bool, Error>) -> Void) | ||
| 373 | /// 更新用户信息, 有登录态后调用 | 375 | /// 更新用户信息, 有登录态后调用 |
| 374 | /// jsonString: UserPreferences的序列化string | 376 | /// jsonString: UserPreferences的序列化string |
| 375 | /// baseUrl: 请求地址, https://api.doublefeel.cn | 377 | /// baseUrl: 请求地址, https://api.doublefeel.cn |
| @@ -439,6 +441,40 @@ class PlatformHostApiSetup { | @@ -439,6 +441,40 @@ class PlatformHostApiSetup { | ||
| 439 | } else { | 441 | } else { |
| 440 | isDebugEnvorimentChannel.setMessageHandler(nil) | 442 | isDebugEnvorimentChannel.setMessageHandler(nil) |
| 441 | } | 443 | } |
| 444 | + let shareTextChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.shareText\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) | ||
| 445 | + if let api = api { | ||
| 446 | + shareTextChannel.setMessageHandler { message, reply in | ||
| 447 | + let args = message as! [Any?] | ||
| 448 | + let textArg = args[0] as! String | ||
| 449 | + api.shareText(text: textArg) { result in | ||
| 450 | + switch result { | ||
| 451 | + case .success(let res): | ||
| 452 | + reply(wrapResult(res)) | ||
| 453 | + case .failure(let error): | ||
| 454 | + reply(wrapError(error)) | ||
| 455 | + } | ||
| 456 | + } | ||
| 457 | + } | ||
| 458 | + } else { | ||
| 459 | + shareTextChannel.setMessageHandler(nil) | ||
| 460 | + } | ||
| 461 | + let shareFileDataChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.shareFileData\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) | ||
| 462 | + if let api = api { | ||
| 463 | + shareFileDataChannel.setMessageHandler { message, reply in | ||
| 464 | + let args = message as! [Any?] | ||
| 465 | + let databytesArg = args[0] as! FlutterStandardTypedData | ||
| 466 | + api.shareFileData(databytes: databytesArg) { result in | ||
| 467 | + switch result { | ||
| 468 | + case .success(let res): | ||
| 469 | + reply(wrapResult(res)) | ||
| 470 | + case .failure(let error): | ||
| 471 | + reply(wrapError(error)) | ||
| 472 | + } | ||
| 473 | + } | ||
| 474 | + } | ||
| 475 | + } else { | ||
| 476 | + shareFileDataChannel.setMessageHandler(nil) | ||
| 477 | + } | ||
| 442 | /// 更新用户信息, 有登录态后调用 | 478 | /// 更新用户信息, 有登录态后调用 |
| 443 | /// jsonString: UserPreferences的序列化string | 479 | /// jsonString: UserPreferences的序列化string |
| 444 | /// baseUrl: 请求地址, https://api.doublefeel.cn | 480 | /// baseUrl: 请求地址, https://api.doublefeel.cn |
| @@ -98,6 +98,26 @@ final class PlatformHostApiImpl: PlatformHostApi { | @@ -98,6 +98,26 @@ final class PlatformHostApiImpl: PlatformHostApi { | ||
| 98 | UIApplication.shared.open(url) | 98 | UIApplication.shared.open(url) |
| 99 | } | 99 | } |
| 100 | } | 100 | } |
| 101 | + | ||
| 102 | + func shareText(text: String, completion: @escaping (Result<Bool, any Error>) -> Void) { | ||
| 103 | + presentShareSheet(items: [text], completion: completion) | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + func shareFileData(databytes: FlutterStandardTypedData, completion: @escaping (Result<Bool, any Error>) -> Void) { | ||
| 107 | + let fileURL = FileManager.default.temporaryDirectory | ||
| 108 | + .appendingPathComponent("doublefeel-share-\(UUID().uuidString)") | ||
| 109 | + .appendingPathExtension("data") | ||
| 110 | + | ||
| 111 | + do { | ||
| 112 | + try databytes.data.write(to: fileURL, options: .atomic) | ||
| 113 | + } catch { | ||
| 114 | + print("[PlatformHostApiImpl.shareFileData] return error: \(error)") | ||
| 115 | + completion(.failure(error)) | ||
| 116 | + return | ||
| 117 | + } | ||
| 118 | + | ||
| 119 | + presentShareSheet(items: [fileURL], cleanupURL: fileURL, completion: completion) | ||
| 120 | + } | ||
| 101 | 121 | ||
| 102 | func uploadFile(filePath: String, resourceType: HResourceType, completion: @escaping (Result<String?, any Error>) -> Void) { | 122 | func uploadFile(filePath: String, resourceType: HResourceType, completion: @escaping (Result<String?, any Error>) -> Void) { |
| 103 | let obsResourceType: HuaweiOBSResourceType | 123 | let obsResourceType: HuaweiOBSResourceType |
| @@ -274,3 +294,73 @@ final class PlatformHostApiImpl: PlatformHostApi { | @@ -274,3 +294,73 @@ final class PlatformHostApiImpl: PlatformHostApi { | ||
| 274 | return userAgent | 294 | return userAgent |
| 275 | } | 295 | } |
| 276 | } | 296 | } |
| 297 | + | ||
| 298 | +//MARK: - 分享 | ||
| 299 | +extension PlatformHostApiImpl{ | ||
| 300 | + private func presentShareSheet( | ||
| 301 | + items: [Any], | ||
| 302 | + cleanupURL: URL? = nil, | ||
| 303 | + completion: @escaping (Result<Bool, any Error>) -> Void | ||
| 304 | + ) { | ||
| 305 | + DispatchQueue.main.async { | ||
| 306 | + guard let viewController = Self.topViewController() else { | ||
| 307 | + if let cleanupURL { | ||
| 308 | + try? FileManager.default.removeItem(at: cleanupURL) | ||
| 309 | + } | ||
| 310 | + let error = NSError( | ||
| 311 | + domain: "PlatformHostApiImpl.Share", | ||
| 312 | + code: 1, | ||
| 313 | + userInfo: [NSLocalizedDescriptionKey: "Unable to find a view controller for sharing."] | ||
| 314 | + ) | ||
| 315 | + print("[PlatformHostApiImpl.share] return error: \(error)") | ||
| 316 | + completion(.failure(error)) | ||
| 317 | + return | ||
| 318 | + } | ||
| 319 | + | ||
| 320 | + let activityController = UIActivityViewController( | ||
| 321 | + activityItems: items, | ||
| 322 | + applicationActivities: nil | ||
| 323 | + ) | ||
| 324 | + activityController.popoverPresentationController?.sourceView = viewController.view | ||
| 325 | + activityController.popoverPresentationController?.sourceRect = CGRect( | ||
| 326 | + x: viewController.view.bounds.midX, | ||
| 327 | + y: viewController.view.bounds.midY, | ||
| 328 | + width: 1, | ||
| 329 | + height: 1 | ||
| 330 | + ) | ||
| 331 | + activityController.completionWithItemsHandler = { _, completed, _, error in | ||
| 332 | + if let cleanupURL { | ||
| 333 | + try? FileManager.default.removeItem(at: cleanupURL) | ||
| 334 | + } | ||
| 335 | + if let error { | ||
| 336 | + print("[PlatformHostApiImpl.share] return error: \(error)") | ||
| 337 | + completion(.failure(error)) | ||
| 338 | + } else { | ||
| 339 | + print("[PlatformHostApiImpl.share] return: \(completed)") | ||
| 340 | + completion(.success(completed)) | ||
| 341 | + } | ||
| 342 | + } | ||
| 343 | + viewController.present(activityController, animated: true) | ||
| 344 | + } | ||
| 345 | + } | ||
| 346 | + | ||
| 347 | + private static func topViewController( | ||
| 348 | + from rootViewController: UIViewController? = UIApplication.shared.connectedScenes | ||
| 349 | + .compactMap { $0 as? UIWindowScene } | ||
| 350 | + .first(where: { $0.activationState == .foregroundActive })? | ||
| 351 | + .windows | ||
| 352 | + .first(where: { $0.isKeyWindow })? | ||
| 353 | + .rootViewController | ||
| 354 | + ) -> UIViewController? { | ||
| 355 | + if let presented = rootViewController?.presentedViewController { | ||
| 356 | + return topViewController(from: presented) | ||
| 357 | + } | ||
| 358 | + if let navigationController = rootViewController as? UINavigationController { | ||
| 359 | + return topViewController(from: navigationController.visibleViewController) | ||
| 360 | + } | ||
| 361 | + if let tabBarController = rootViewController as? UITabBarController { | ||
| 362 | + return topViewController(from: tabBarController.selectedViewController) | ||
| 363 | + } | ||
| 364 | + return rootViewController | ||
| 365 | + } | ||
| 366 | +} |
| @@ -9,6 +9,7 @@ import 'package:doublefeel_flutter/core/error/http_error_handling_policy.dart'; | @@ -9,6 +9,7 @@ import 'package:doublefeel_flutter/core/error/http_error_handling_policy.dart'; | ||
| 9 | import 'package:doublefeel_flutter/core/network/api/friend_api.dart'; | 9 | import 'package:doublefeel_flutter/core/network/api/friend_api.dart'; |
| 10 | import 'package:doublefeel_flutter/core/result/app_result.dart'; | 10 | import 'package:doublefeel_flutter/core/result/app_result.dart'; |
| 11 | import 'package:doublefeel_flutter/core/services/user_state_service.dart'; | 11 | import 'package:doublefeel_flutter/core/services/user_state_service.dart'; |
| 12 | +import 'package:doublefeel_flutter/core/util/app_toast.dart'; | ||
| 12 | import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart'; | 13 | import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart'; |
| 13 | import 'package:doublefeel_flutter/data/models/friend/friend_models.dart'; | 14 | import 'package:doublefeel_flutter/data/models/friend/friend_models.dart'; |
| 14 | import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; | 15 | import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; |
| @@ -64,11 +65,16 @@ class BindPartnerController extends GetxController { | @@ -64,11 +65,16 @@ class BindPartnerController extends GetxController { | ||
| 64 | 65 | ||
| 65 | bool get canSubmit => partnerIdInput.value.isNotEmpty; | 66 | bool get canSubmit => partnerIdInput.value.isNotEmpty; |
| 66 | 67 | ||
| 67 | - void onShareMyCode() { | ||
| 68 | - Share.share("""嘿 ❤️ 我发现了一个叫 DoubleFeel 的 App! | 68 | + void onShareMyCode() async { |
| 69 | + try { | ||
| 70 | + final success = | ||
| 71 | + await PlatformHostApi().shareText("""嘿 ❤️ 我发现了一个叫 DoubleFeel 的 App! | ||
| 69 | 它可以帮我们互相关心 — 记录压力和睡眠、查看 HRV 与身体状态、及时了解彼此的健康状况。 | 72 | 它可以帮我们互相关心 — 记录压力和睡眠、查看 HRV 与身体状态、及时了解彼此的健康状况。 |
| 70 | 快来一起使用吧 👉 https://doublefeel.cn/ | 73 | 快来一起使用吧 👉 https://doublefeel.cn/ |
| 71 | 我的好友id:${myInviteCode.value}"""); | 74 | 我的好友id:${myInviteCode.value}"""); |
| 75 | + } catch (e) { | ||
| 76 | + AppToast.show(e.toString()); | ||
| 77 | + } | ||
| 72 | } | 78 | } |
| 73 | 79 | ||
| 74 | Future<void> onSubmitPartnerId() async { | 80 | Future<void> onSubmitPartnerId() async { |
| @@ -422,6 +422,62 @@ class PlatformHostApi { | @@ -422,6 +422,62 @@ class PlatformHostApi { | ||
| 422 | } | 422 | } |
| 423 | } | 423 | } |
| 424 | 424 | ||
| 425 | + Future<bool> shareText(String text) async { | ||
| 426 | + final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.shareText$pigeonVar_messageChannelSuffix'; | ||
| 427 | + final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>( | ||
| 428 | + pigeonVar_channelName, | ||
| 429 | + pigeonChannelCodec, | ||
| 430 | + binaryMessenger: pigeonVar_binaryMessenger, | ||
| 431 | + ); | ||
| 432 | + final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[text]); | ||
| 433 | + final List<Object?>? pigeonVar_replyList = | ||
| 434 | + await pigeonVar_sendFuture as List<Object?>?; | ||
| 435 | + if (pigeonVar_replyList == null) { | ||
| 436 | + throw _createConnectionError(pigeonVar_channelName); | ||
| 437 | + } else if (pigeonVar_replyList.length > 1) { | ||
| 438 | + throw PlatformException( | ||
| 439 | + code: pigeonVar_replyList[0]! as String, | ||
| 440 | + message: pigeonVar_replyList[1] as String?, | ||
| 441 | + details: pigeonVar_replyList[2], | ||
| 442 | + ); | ||
| 443 | + } else if (pigeonVar_replyList[0] == null) { | ||
| 444 | + throw PlatformException( | ||
| 445 | + code: 'null-error', | ||
| 446 | + message: 'Host platform returned null value for non-null return value.', | ||
| 447 | + ); | ||
| 448 | + } else { | ||
| 449 | + return (pigeonVar_replyList[0] as bool?)!; | ||
| 450 | + } | ||
| 451 | + } | ||
| 452 | + | ||
| 453 | + Future<bool> shareFileData(Uint8List databytes) async { | ||
| 454 | + final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.shareFileData$pigeonVar_messageChannelSuffix'; | ||
| 455 | + final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>( | ||
| 456 | + pigeonVar_channelName, | ||
| 457 | + pigeonChannelCodec, | ||
| 458 | + binaryMessenger: pigeonVar_binaryMessenger, | ||
| 459 | + ); | ||
| 460 | + final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[databytes]); | ||
| 461 | + final List<Object?>? pigeonVar_replyList = | ||
| 462 | + await pigeonVar_sendFuture as List<Object?>?; | ||
| 463 | + if (pigeonVar_replyList == null) { | ||
| 464 | + throw _createConnectionError(pigeonVar_channelName); | ||
| 465 | + } else if (pigeonVar_replyList.length > 1) { | ||
| 466 | + throw PlatformException( | ||
| 467 | + code: pigeonVar_replyList[0]! as String, | ||
| 468 | + message: pigeonVar_replyList[1] as String?, | ||
| 469 | + details: pigeonVar_replyList[2], | ||
| 470 | + ); | ||
| 471 | + } else if (pigeonVar_replyList[0] == null) { | ||
| 472 | + throw PlatformException( | ||
| 473 | + code: 'null-error', | ||
| 474 | + message: 'Host platform returned null value for non-null return value.', | ||
| 475 | + ); | ||
| 476 | + } else { | ||
| 477 | + return (pigeonVar_replyList[0] as bool?)!; | ||
| 478 | + } | ||
| 479 | + } | ||
| 480 | + | ||
| 425 | /// 更新用户信息, 有登录态后调用 | 481 | /// 更新用户信息, 有登录态后调用 |
| 426 | /// jsonString: UserPreferences的序列化string | 482 | /// jsonString: UserPreferences的序列化string |
| 427 | /// baseUrl: 请求地址, https://api.doublefeel.cn | 483 | /// baseUrl: 请求地址, https://api.doublefeel.cn |
| @@ -119,6 +119,11 @@ abstract class PlatformHostApi { | @@ -119,6 +119,11 @@ abstract class PlatformHostApi { | ||
| 119 | /// 是否是测试环境 | 119 | /// 是否是测试环境 |
| 120 | bool isDebugEnvoriment(); | 120 | bool isDebugEnvoriment(); |
| 121 | 121 | ||
| 122 | + @async | ||
| 123 | + bool shareText(String text); | ||
| 124 | + @async | ||
| 125 | + bool shareFileData(Uint8List databytes); | ||
| 126 | + | ||
| 122 | /// 更新用户信息, 有登录态后调用 | 127 | /// 更新用户信息, 有登录态后调用 |
| 123 | /// jsonString: UserPreferences的序列化string | 128 | /// jsonString: UserPreferences的序列化string |
| 124 | /// baseUrl: 请求地址, https://api.doublefeel.cn | 129 | /// baseUrl: 请求地址, https://api.doublefeel.cn |
-
Please register or login to post a comment