Commit 4044a3452f53d1acdc8ab68a4045c8de1ddf6e11

Authored by 权海
1 parent cb34ffd6

feat(ui):增肌google sign in、发送邮件的支持

@@ -128,6 +128,46 @@ data class AppleSignInModel ( @@ -128,6 +128,46 @@ data class AppleSignInModel (
128 } 128 }
129 129
130 /** Generated class from Pigeon that represents data sent in messages. */ 130 /** Generated class from Pigeon that represents data sent in messages. */
  131 +data class GoogleSignInModel (
  132 + val email: String? = null,
  133 + val clientID: String? = null,
  134 + val idToken: String,
  135 + val nickname: String? = null,
  136 + val avatarUrl: String? = null
  137 +)
  138 + {
  139 + companion object {
  140 + fun fromList(pigeonVar_list: List<Any?>): GoogleSignInModel {
  141 + val email = pigeonVar_list[0] as String?
  142 + val clientID = pigeonVar_list[1] as String?
  143 + val idToken = pigeonVar_list[2] as String
  144 + val nickname = pigeonVar_list[3] as String?
  145 + val avatarUrl = pigeonVar_list[4] as String?
  146 + return GoogleSignInModel(email, clientID, idToken, nickname, avatarUrl)
  147 + }
  148 + }
  149 + fun toList(): List<Any?> {
  150 + return listOf(
  151 + email,
  152 + clientID,
  153 + idToken,
  154 + nickname,
  155 + avatarUrl,
  156 + )
  157 + }
  158 + override fun equals(other: Any?): Boolean {
  159 + if (other !is GoogleSignInModel) {
  160 + return false
  161 + }
  162 + if (this === other) {
  163 + return true
  164 + }
  165 + return PlatformApiPigeonUtils.deepEquals(toList(), other.toList()) }
  166 +
  167 + override fun hashCode(): Int = toList().hashCode()
  168 +}
  169 +
  170 +/** Generated class from Pigeon that represents data sent in messages. */
131 data class WatchAppOtherInfo ( 171 data class WatchAppOtherInfo (
132 val userId: Long? = null, 172 val userId: Long? = null,
133 val nickname: String? = null, 173 val nickname: String? = null,
@@ -287,16 +327,21 @@ private open class PlatformApiPigeonCodec : StandardMessageCodec() { @@ -287,16 +327,21 @@ private open class PlatformApiPigeonCodec : StandardMessageCodec() {
287 } 327 }
288 131.toByte() -> { 328 131.toByte() -> {
289 return (readValue(buffer) as? List<Any?>)?.let { 329 return (readValue(buffer) as? List<Any?>)?.let {
290 - WatchAppOtherInfo.fromList(it) 330 + GoogleSignInModel.fromList(it)
291 } 331 }
292 } 332 }
293 132.toByte() -> { 333 132.toByte() -> {
294 return (readValue(buffer) as? List<Any?>)?.let { 334 return (readValue(buffer) as? List<Any?>)?.let {
295 - AppleProductInfo.fromList(it) 335 + WatchAppOtherInfo.fromList(it)
296 } 336 }
297 } 337 }
298 133.toByte() -> { 338 133.toByte() -> {
299 return (readValue(buffer) as? List<Any?>)?.let { 339 return (readValue(buffer) as? List<Any?>)?.let {
  340 + AppleProductInfo.fromList(it)
  341 + }
  342 + }
  343 + 134.toByte() -> {
  344 + return (readValue(buffer) as? List<Any?>)?.let {
300 AppleProductPaymentResult.fromList(it) 345 AppleProductPaymentResult.fromList(it)
301 } 346 }
302 } 347 }
@@ -313,18 +358,22 @@ private open class PlatformApiPigeonCodec : StandardMessageCodec() { @@ -313,18 +358,22 @@ private open class PlatformApiPigeonCodec : StandardMessageCodec() {
313 stream.write(130) 358 stream.write(130)
314 writeValue(stream, value.toList()) 359 writeValue(stream, value.toList())
315 } 360 }
316 - is WatchAppOtherInfo -> { 361 + is GoogleSignInModel -> {
317 stream.write(131) 362 stream.write(131)
318 writeValue(stream, value.toList()) 363 writeValue(stream, value.toList())
319 } 364 }
320 - is AppleProductInfo -> { 365 + is WatchAppOtherInfo -> {
321 stream.write(132) 366 stream.write(132)
322 writeValue(stream, value.toList()) 367 writeValue(stream, value.toList())
323 } 368 }
324 - is AppleProductPaymentResult -> { 369 + is AppleProductInfo -> {
325 stream.write(133) 370 stream.write(133)
326 writeValue(stream, value.toList()) 371 writeValue(stream, value.toList())
327 } 372 }
  373 + is AppleProductPaymentResult -> {
  374 + stream.write(134)
  375 + writeValue(stream, value.toList())
  376 + }
328 else -> super.writeValue(stream, value) 377 else -> super.writeValue(stream, value)
329 } 378 }
330 } 379 }
@@ -371,6 +420,10 @@ interface PlatformHostApi { @@ -371,6 +420,10 @@ interface PlatformHostApi {
371 fun requestUnhandedUrl(): String? 420 fun requestUnhandedUrl(): String?
372 /** 请求苹果登录 */ 421 /** 请求苹果登录 */
373 fun requestAppleSignIn(callback: (Result<AppleSignInModel?>) -> Unit) 422 fun requestAppleSignIn(callback: (Result<AppleSignInModel?>) -> Unit)
  423 + /** google登录 */
  424 + fun requestGoogleSignIn(callback: (Result<GoogleSignInModel?>) -> Unit)
  425 + /** 发送邮件 */
  426 + fun sendEmail(mailTo: String, title: String, content: String, callback: (Result<Boolean>) -> Unit)
374 /** 427 /**
375 * 查询指定id的苹果商品 428 * 查询指定id的苹果商品
376 * productId: 苹果商品id 429 * productId: 苹果商品id
@@ -666,6 +719,46 @@ interface PlatformHostApi { @@ -666,6 +719,46 @@ interface PlatformHostApi {
666 } 719 }
667 } 720 }
668 run { 721 run {
  722 + val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.requestGoogleSignIn$separatedMessageChannelSuffix", codec)
  723 + if (api != null) {
  724 + channel.setMessageHandler { _, reply ->
  725 + api.requestGoogleSignIn{ result: Result<GoogleSignInModel?> ->
  726 + val error = result.exceptionOrNull()
  727 + if (error != null) {
  728 + reply.reply(PlatformApiPigeonUtils.wrapError(error))
  729 + } else {
  730 + val data = result.getOrNull()
  731 + reply.reply(PlatformApiPigeonUtils.wrapResult(data))
  732 + }
  733 + }
  734 + }
  735 + } else {
  736 + channel.setMessageHandler(null)
  737 + }
  738 + }
  739 + run {
  740 + val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.sendEmail$separatedMessageChannelSuffix", codec)
  741 + if (api != null) {
  742 + channel.setMessageHandler { message, reply ->
  743 + val args = message as List<Any?>
  744 + val mailToArg = args[0] as String
  745 + val titleArg = args[1] as String
  746 + val contentArg = args[2] as String
  747 + api.sendEmail(mailToArg, titleArg, contentArg) { result: Result<Boolean> ->
  748 + val error = result.exceptionOrNull()
  749 + if (error != null) {
  750 + reply.reply(PlatformApiPigeonUtils.wrapError(error))
  751 + } else {
  752 + val data = result.getOrNull()
  753 + reply.reply(PlatformApiPigeonUtils.wrapResult(data))
  754 + }
  755 + }
  756 + }
  757 + } else {
  758 + channel.setMessageHandler(null)
  759 + }
  760 + }
  761 + run {
669 val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.requestAppleProductInfo$separatedMessageChannelSuffix", codec) 762 val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.requestAppleProductInfo$separatedMessageChannelSuffix", codec)
670 if (api != null) { 763 if (api != null) {
671 channel.setMessageHandler { message, reply -> 764 channel.setMessageHandler { message, reply ->
@@ -29,6 +29,7 @@ flutter_ios_podfile_setup @@ -29,6 +29,7 @@ flutter_ios_podfile_setup
29 target 'Runner' do 29 target 'Runner' do
30 use_frameworks! 30 use_frameworks!
31 pod 'OBS', :path => './Runner/Library' 31 pod 'OBS', :path => './Runner/Library'
  32 + pod 'GoogleSignIn'
32 33
33 flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 34 flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
34 end 35 end
1 PODS: 1 PODS:
  2 + - AppAuth (2.1.0):
  3 + - AppAuth/Core (= 2.1.0)
  4 + - AppAuth/ExternalUserAgent (= 2.1.0)
  5 + - AppAuth/Core (2.1.0)
  6 + - AppAuth/ExternalUserAgent (2.1.0):
  7 + - AppAuth/Core
  8 + - AppCheckCore (11.3.1):
  9 + - GoogleUtilities/Environment (~> 8.0)
  10 + - GoogleUtilities/UserDefaults (~> 8.0)
  11 + - PromisesObjC (~> 2.4)
  12 + - PromisesSwift (~> 2.4)
  13 + - RecaptchaInterop (~> 101.0)
2 - Flutter (1.0.0) 14 - Flutter (1.0.0)
3 - fluttertoast (0.0.2): 15 - fluttertoast (0.0.2):
4 - Flutter 16 - Flutter
  17 + - GoogleSignIn (9.2.0):
  18 + - AppAuth (~> 2.1)
  19 + - AppCheckCore (~> 11.0)
  20 + - GTMAppAuth (~> 5.0)
  21 + - GTMSessionFetcher/Core (~> 3.3)
  22 + - GoogleUtilities/Environment (8.1.2):
  23 + - GoogleUtilities/Privacy
  24 + - GoogleUtilities/Logger (8.1.2):
  25 + - GoogleUtilities/Environment
  26 + - GoogleUtilities/Privacy
  27 + - GoogleUtilities/Privacy (8.1.2)
  28 + - GoogleUtilities/UserDefaults (8.1.2):
  29 + - GoogleUtilities/Logger
  30 + - GoogleUtilities/Privacy
  31 + - GTMAppAuth (5.0.0):
  32 + - AppAuth/Core (~> 2.0)
  33 + - GTMSessionFetcher/Core (< 4.0, >= 3.3)
  34 + - GTMSessionFetcher/Core (3.5.0)
5 - image_cropper (0.0.4): 35 - image_cropper (0.0.4):
6 - Flutter 36 - Flutter
7 - TOCropViewController (~> 2.7.4) 37 - TOCropViewController (~> 2.7.4)
@@ -25,6 +55,10 @@ PODS: @@ -25,6 +55,10 @@ PODS:
25 - FlutterMacOS 55 - FlutterMacOS
26 - permission_handler_apple (9.3.0): 56 - permission_handler_apple (9.3.0):
27 - Flutter 57 - Flutter
  58 + - PromisesObjC (2.4.1)
  59 + - PromisesSwift (2.4.1):
  60 + - PromisesObjC (= 2.4.1)
  61 + - RecaptchaInterop (101.0.0)
28 - share_plus (0.0.1): 62 - share_plus (0.0.1):
29 - Flutter 63 - Flutter
30 - shared_preferences_foundation (0.0.1): 64 - shared_preferences_foundation (0.0.1):
@@ -65,6 +99,7 @@ PODS: @@ -65,6 +99,7 @@ PODS:
65 DEPENDENCIES: 99 DEPENDENCIES:
66 - Flutter (from `Flutter`) 100 - Flutter (from `Flutter`)
67 - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) 101 - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
  102 + - GoogleSignIn
68 - image_cropper (from `.symlinks/plugins/image_cropper/ios`) 103 - image_cropper (from `.symlinks/plugins/image_cropper/ios`)
69 - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) 104 - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
70 - OBS (from `./Runner/Library`) 105 - OBS (from `./Runner/Library`)
@@ -79,7 +114,16 @@ DEPENDENCIES: @@ -79,7 +114,16 @@ DEPENDENCIES:
79 114
80 SPEC REPOS: 115 SPEC REPOS:
81 trunk: 116 trunk:
  117 + - AppAuth
  118 + - AppCheckCore
  119 + - GoogleSignIn
  120 + - GoogleUtilities
  121 + - GTMAppAuth
  122 + - GTMSessionFetcher
82 - libwebp 123 - libwebp
  124 + - PromisesObjC
  125 + - PromisesSwift
  126 + - RecaptchaInterop
83 - TAThirdParty 127 - TAThirdParty
84 - ThinkingDataCore 128 - ThinkingDataCore
85 - ThinkingSDK 129 - ThinkingSDK
@@ -114,14 +158,23 @@ EXTERNAL SOURCES: @@ -114,14 +158,23 @@ EXTERNAL SOURCES:
114 :path: ".symlinks/plugins/webview_flutter_wkwebview/darwin" 158 :path: ".symlinks/plugins/webview_flutter_wkwebview/darwin"
115 159
116 SPEC CHECKSUMS: 160 SPEC CHECKSUMS:
  161 + AppAuth: ef4da5a3fc2e10b90c09a0a94a9baeaedc0341d5
  162 + AppCheckCore: e215d35177a9cf469927863e69c13e220df32a8b
117 Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 163 Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
118 fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1 164 fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
  165 + GoogleSignIn: e449a40a92e9f2eea56e98b5214d13725dc5a00a
  166 + GoogleUtilities: 766ace00c6b10d8148408f329d10c4f051931850
  167 + GTMAppAuth: 217a876b249c3c585a54fd6f73e6b58c4f5c4238
  168 + GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
119 image_cropper: c4326ea50132b1e1564499e5d32a84f01fb03537 169 image_cropper: c4326ea50132b1e1564499e5d32a84f01fb03537
120 image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a 170 image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
121 libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 171 libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
122 OBS: 4cf1e6db5515aefd6c3c6ae3e90e85ea5f028e83 172 OBS: 4cf1e6db5515aefd6c3c6ae3e90e85ea5f028e83
123 path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 173 path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
124 permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d 174 permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
  175 + PromisesObjC: 752c3227f599e3467650e47ea36f433eeb10c273
  176 + PromisesSwift: 217dea0fd5d2ad65222a109c48698add13cc1c5b
  177 + RecaptchaInterop: 11e0b637842dfb48308d242afc3f448062325aba
125 share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a 178 share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
126 shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 179 shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
127 sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 180 sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
@@ -133,6 +186,6 @@ SPEC CHECKSUMS: @@ -133,6 +186,6 @@ SPEC CHECKSUMS:
133 video_thumbnail: b637e0ad5f588ca9945f6e2c927f73a69a661140 186 video_thumbnail: b637e0ad5f588ca9945f6e2c927f73a69a661140
134 webview_flutter_wkwebview: 1821ceac936eba6f7984d89a9f3bcb4dea99ebb2 187 webview_flutter_wkwebview: 1821ceac936eba6f7984d89a9f3bcb4dea99ebb2
135 188
136 -PODFILE CHECKSUM: 9846d4e0a8198893058beb0df941f3d2ecdcca2c 189 +PODFILE CHECKSUM: fe3137a8e1c8e115fcac0a685d21e651b2d2788b
137 190
138 COCOAPODS: 1.16.2 191 COCOAPODS: 1.16.2
@@ -11,6 +11,7 @@ import Flutter @@ -11,6 +11,7 @@ import Flutter
11 import UserNotifications 11 import UserNotifications
12 import AppTrackingTransparency 12 import AppTrackingTransparency
13 import AdSupport 13 import AdSupport
  14 +import GoogleSignIn
14 15
15 typealias FlutterBridgeMethod = ((_ params: Any?, _ result: FlutterResult) -> Void) 16 typealias FlutterBridgeMethod = ((_ params: Any?, _ result: FlutterResult) -> Void)
16 17
@@ -48,6 +49,9 @@ class AppDelegate: NSObject, UIApplicationDelegate { @@ -48,6 +49,9 @@ class AppDelegate: NSObject, UIApplicationDelegate {
48 open url: URL, 49 open url: URL,
49 options: [UIApplication.OpenURLOptionsKey: Any] = [:] 50 options: [UIApplication.OpenURLOptionsKey: Any] = [:]
50 ) -> Bool { 51 ) -> Bool {
  52 + if GIDSignIn.sharedInstance.handle(url) {
  53 + return true
  54 + }
51 handleFlutterUrl(url.absoluteString) 55 handleFlutterUrl(url.absoluteString)
52 return true 56 return true
53 } 57 }
@@ -11,16 +11,25 @@ import AuthenticationServices @@ -11,16 +11,25 @@ import AuthenticationServices
11 enum PlatformHostApiError: LocalizedError { 11 enum PlatformHostApiError: LocalizedError {
12 case deallocated 12 case deallocated
13 case invalidAppleCredential 13 case invalidAppleCredential
  14 + case invalidGoogleCredential
14 case missingPresentationAnchor 15 case missingPresentationAnchor
  16 + case missingGoogleIDToken
  17 + case mailComposerAlreadyPresented
15 18
16 var errorDescription: String? { 19 var errorDescription: String? {
17 switch self { 20 switch self {
18 case .deallocated: 21 case .deallocated:
19 - return "PlatformHostApiImpl was released before Apple sign-in started." 22 + return "PlatformHostApiImpl was released before the native request started."
20 case .invalidAppleCredential: 23 case .invalidAppleCredential:
21 return "Apple sign-in did not return an Apple ID credential." 24 return "Apple sign-in did not return an Apple ID credential."
  25 + case .invalidGoogleCredential:
  26 + return "Google sign-in did not return a Google user."
22 case .missingPresentationAnchor: 27 case .missingPresentationAnchor:
23 - return "Unable to find a window for Apple sign-in presentation." 28 + return "Unable to find a window for native presentation."
  29 + case .missingGoogleIDToken:
  30 + return "Google sign-in did not return an ID token."
  31 + case .mailComposerAlreadyPresented:
  32 + return "A mail composer is already being presented."
24 } 33 }
25 } 34 }
26 } 35 }
@@ -11,6 +11,14 @@ @@ -11,6 +11,14 @@
11 <key>CFBundleURLTypes</key> 11 <key>CFBundleURLTypes</key>
12 <array> 12 <array>
13 <dict> 13 <dict>
  14 + <key>CFBundleURLSchemes</key>
  15 + <array>
  16 + <string>com.googleusercontent.apps.518385058395-ja7eugmdu7ar8m8juddn7qjkqvjh5hp6</string>
  17 + </array>
  18 + <key>CFBundleTypeRole</key>
  19 + <string>Editor</string>
  20 + </dict>
  21 + <dict>
14 <key>CFBundleTypeRole</key> 22 <key>CFBundleTypeRole</key>
15 <string>Editor</string> 23 <string>Editor</string>
16 <key>CFBundleURLName</key> 24 <key>CFBundleURLName</key>
@@ -26,8 +34,18 @@ @@ -26,8 +34,18 @@
26 <key>NSAllowsArbitraryLoads</key> 34 <key>NSAllowsArbitraryLoads</key>
27 <true/> 35 <true/>
28 </dict> 36 </dict>
  37 + <key>LSApplicationQueriesSchemes</key>
  38 + <array>
  39 + <string>googlegmail</string>
  40 + <string>ms-outlook</string>
  41 + <string>ymail</string>
  42 + <string>readdle-spark</string>
  43 + <string>mqqapi</string>
  44 + </array>
29 <key>NSHealthShareUsageDescription</key> 45 <key>NSHealthShareUsageDescription</key>
30 <string>需要读取 Apple Health 中的心率、HRV、睡眠、步数、活动能量等数据,用于展示健康状态并同步到 Apple Watch。</string> 46 <string>需要读取 Apple Health 中的心率、HRV、睡眠、步数、活动能量等数据,用于展示健康状态并同步到 Apple Watch。</string>
  47 + <key>GIDClientID</key>
  48 + <string>518385058395-ja7eugmdu7ar8m8juddn7qjkqvjh5hp6.apps.googleusercontent.com</string>
31 <key>NSHealthUpdateUsageDescription</key> 49 <key>NSHealthUpdateUsageDescription</key>
32 <string>需要写入少量健康数据权限以保持与旧版 Apple Health 同步流程兼容。</string> 50 <string>需要写入少量健康数据权限以保持与旧版 Apple Health 同步流程兼容。</string>
33 <key>NSPhotoLibraryUsageDescription</key> 51 <key>NSPhotoLibraryUsageDescription</key>
@@ -5,6 +5,12 @@ @@ -5,6 +5,12 @@
5 <key>CFBundleURLTypes</key> 5 <key>CFBundleURLTypes</key>
6 <array> 6 <array>
7 <dict> 7 <dict>
  8 + <key>CFBundleURLSchemes</key>
  9 + <array>
  10 + <string>com.googleusercontent.apps.518385058395-ja7eugmdu7ar8m8juddn7qjkqvjh5hp6</string>
  11 + </array>
  12 + </dict>
  13 + <dict>
8 <key>CFBundleTypeRole</key> 14 <key>CFBundleTypeRole</key>
9 <string>Editor</string> 15 <string>Editor</string>
10 <key>CFBundleURLName</key> 16 <key>CFBundleURLName</key>
@@ -20,6 +26,14 @@ @@ -20,6 +26,14 @@
20 <key>NSAllowsArbitraryLoads</key> 26 <key>NSAllowsArbitraryLoads</key>
21 <true/> 27 <true/>
22 </dict> 28 </dict>
  29 + <key>LSApplicationQueriesSchemes</key>
  30 + <array>
  31 + <string>googlegmail</string>
  32 + <string>ms-outlook</string>
  33 + <string>ymail</string>
  34 + <string>readdle-spark</string>
  35 + <string>mqqapi</string>
  36 + </array>
23 <key>NSHealthShareUsageDescription</key> 37 <key>NSHealthShareUsageDescription</key>
24 <string>需要读取 Apple Health 中的心率、HRV、睡眠、步数、活动能量等数据,用于展示健康状态并同步到 Apple Watch。</string> 38 <string>需要读取 Apple Health 中的心率、HRV、睡眠、步数、活动能量等数据,用于展示健康状态并同步到 Apple Watch。</string>
25 <key>NSHealthUpdateUsageDescription</key> 39 <key>NSHealthUpdateUsageDescription</key>
@@ -32,9 +46,11 @@ @@ -32,9 +46,11 @@
32 <true/> 46 <true/>
33 <key>UIApplicationSupportsIndirectInputEvents</key> 47 <key>UIApplicationSupportsIndirectInputEvents</key>
34 <true/> 48 <true/>
35 - <key>NSCameraUsageDescription</key>  
36 - <string>需要相机拍摄用户头像</string>  
37 - <key>NSMicrophoneUsageDescription</key>  
38 - <string>需要相机拍摄用户头像</string> 49 + <key>NSCameraUsageDescription</key>
  50 + <string>需要相机拍摄用户头像</string>
  51 + <key>GIDClientID</key>
  52 + <string>518385058395-ja7eugmdu7ar8m8juddn7qjkqvjh5hp6.apps.googleusercontent.com</string>
  53 + <key>NSMicrophoneUsageDescription</key>
  54 + <string>需要相机拍摄用户头像</string>
39 </dict> 55 </dict>
40 </plist> 56 </plist>
@@ -155,6 +155,47 @@ struct AppleSignInModel: Hashable { @@ -155,6 +155,47 @@ struct AppleSignInModel: Hashable {
155 } 155 }
156 156
157 /// Generated class from Pigeon that represents data sent in messages. 157 /// Generated class from Pigeon that represents data sent in messages.
  158 +struct GoogleSignInModel: Hashable {
  159 + var email: String? = nil
  160 + var clientID: String? = nil
  161 + var idToken: String
  162 + var nickname: String? = nil
  163 + var avatarUrl: String? = nil
  164 +
  165 +
  166 + // swift-format-ignore: AlwaysUseLowerCamelCase
  167 + static func fromList(_ pigeonVar_list: [Any?]) -> GoogleSignInModel? {
  168 + let email: String? = nilOrValue(pigeonVar_list[0])
  169 + let clientID: String? = nilOrValue(pigeonVar_list[1])
  170 + let idToken = pigeonVar_list[2] as! String
  171 + let nickname: String? = nilOrValue(pigeonVar_list[3])
  172 + let avatarUrl: String? = nilOrValue(pigeonVar_list[4])
  173 +
  174 + return GoogleSignInModel(
  175 + email: email,
  176 + clientID: clientID,
  177 + idToken: idToken,
  178 + nickname: nickname,
  179 + avatarUrl: avatarUrl
  180 + )
  181 + }
  182 + func toList() -> [Any?] {
  183 + return [
  184 + email,
  185 + clientID,
  186 + idToken,
  187 + nickname,
  188 + avatarUrl,
  189 + ]
  190 + }
  191 + static func == (lhs: GoogleSignInModel, rhs: GoogleSignInModel) -> Bool {
  192 + return deepEqualsPlatformApi(lhs.toList(), rhs.toList()) }
  193 + func hash(into hasher: inout Hasher) {
  194 + deepHashPlatformApi(value: toList(), hasher: &hasher)
  195 + }
  196 +}
  197 +
  198 +/// Generated class from Pigeon that represents data sent in messages.
158 struct WatchAppOtherInfo: Hashable { 199 struct WatchAppOtherInfo: Hashable {
159 var userId: Int64? = nil 200 var userId: Int64? = nil
160 var nickname: String? = nil 201 var nickname: String? = nil
@@ -314,10 +355,12 @@ private class PlatformApiPigeonCodecReader: FlutterStandardReader { @@ -314,10 +355,12 @@ private class PlatformApiPigeonCodecReader: FlutterStandardReader {
314 case 130: 355 case 130:
315 return AppleSignInModel.fromList(self.readValue() as! [Any?]) 356 return AppleSignInModel.fromList(self.readValue() as! [Any?])
316 case 131: 357 case 131:
317 - return WatchAppOtherInfo.fromList(self.readValue() as! [Any?]) 358 + return GoogleSignInModel.fromList(self.readValue() as! [Any?])
318 case 132: 359 case 132:
319 - return AppleProductInfo.fromList(self.readValue() as! [Any?]) 360 + return WatchAppOtherInfo.fromList(self.readValue() as! [Any?])
320 case 133: 361 case 133:
  362 + return AppleProductInfo.fromList(self.readValue() as! [Any?])
  363 + case 134:
321 return AppleProductPaymentResult.fromList(self.readValue() as! [Any?]) 364 return AppleProductPaymentResult.fromList(self.readValue() as! [Any?])
322 default: 365 default:
323 return super.readValue(ofType: type) 366 return super.readValue(ofType: type)
@@ -333,15 +376,18 @@ private class PlatformApiPigeonCodecWriter: FlutterStandardWriter { @@ -333,15 +376,18 @@ private class PlatformApiPigeonCodecWriter: FlutterStandardWriter {
333 } else if let value = value as? AppleSignInModel { 376 } else if let value = value as? AppleSignInModel {
334 super.writeByte(130) 377 super.writeByte(130)
335 super.writeValue(value.toList()) 378 super.writeValue(value.toList())
336 - } else if let value = value as? WatchAppOtherInfo { 379 + } else if let value = value as? GoogleSignInModel {
337 super.writeByte(131) 380 super.writeByte(131)
338 super.writeValue(value.toList()) 381 super.writeValue(value.toList())
339 - } else if let value = value as? AppleProductInfo { 382 + } else if let value = value as? WatchAppOtherInfo {
340 super.writeByte(132) 383 super.writeByte(132)
341 super.writeValue(value.toList()) 384 super.writeValue(value.toList())
342 - } else if let value = value as? AppleProductPaymentResult { 385 + } else if let value = value as? AppleProductInfo {
343 super.writeByte(133) 386 super.writeByte(133)
344 super.writeValue(value.toList()) 387 super.writeValue(value.toList())
  388 + } else if let value = value as? AppleProductPaymentResult {
  389 + super.writeByte(134)
  390 + super.writeValue(value.toList())
345 } else { 391 } else {
346 super.writeValue(value) 392 super.writeValue(value)
347 } 393 }
@@ -397,6 +443,10 @@ protocol PlatformHostApi { @@ -397,6 +443,10 @@ protocol PlatformHostApi {
397 func requestUnhandedUrl() throws -> String? 443 func requestUnhandedUrl() throws -> String?
398 /// 请求苹果登录 444 /// 请求苹果登录
399 func requestAppleSignIn(completion: @escaping (Result<AppleSignInModel?, Error>) -> Void) 445 func requestAppleSignIn(completion: @escaping (Result<AppleSignInModel?, Error>) -> Void)
  446 + /// google登录
  447 + func requestGoogleSignIn(completion: @escaping (Result<GoogleSignInModel?, Error>) -> Void)
  448 + /// 发送邮件
  449 + func sendEmail(mailTo: String, title: String, content: String, completion: @escaping (Result<Bool, Error>) -> Void)
400 /// 查询指定id的苹果商品 450 /// 查询指定id的苹果商品
401 /// productId: 苹果商品id 451 /// productId: 苹果商品id
402 /// baseUnit: 除数基础单位(多少个天、周、月) -> 452 /// baseUnit: 除数基础单位(多少个天、周、月) ->
@@ -658,6 +708,42 @@ class PlatformHostApiSetup { @@ -658,6 +708,42 @@ class PlatformHostApiSetup {
658 } else { 708 } else {
659 requestAppleSignInChannel.setMessageHandler(nil) 709 requestAppleSignInChannel.setMessageHandler(nil)
660 } 710 }
  711 + /// google登录
  712 + let requestGoogleSignInChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.requestGoogleSignIn\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
  713 + if let api = api {
  714 + requestGoogleSignInChannel.setMessageHandler { _, reply in
  715 + api.requestGoogleSignIn { result in
  716 + switch result {
  717 + case .success(let res):
  718 + reply(wrapResult(res))
  719 + case .failure(let error):
  720 + reply(wrapError(error))
  721 + }
  722 + }
  723 + }
  724 + } else {
  725 + requestGoogleSignInChannel.setMessageHandler(nil)
  726 + }
  727 + /// 发送邮件
  728 + let sendEmailChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.sendEmail\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
  729 + if let api = api {
  730 + sendEmailChannel.setMessageHandler { message, reply in
  731 + let args = message as! [Any?]
  732 + let mailToArg = args[0] as! String
  733 + let titleArg = args[1] as! String
  734 + let contentArg = args[2] as! String
  735 + api.sendEmail(mailTo: mailToArg, title: titleArg, content: contentArg) { result in
  736 + switch result {
  737 + case .success(let res):
  738 + reply(wrapResult(res))
  739 + case .failure(let error):
  740 + reply(wrapError(error))
  741 + }
  742 + }
  743 + }
  744 + } else {
  745 + sendEmailChannel.setMessageHandler(nil)
  746 + }
661 /// 查询指定id的苹果商品 747 /// 查询指定id的苹果商品
662 /// productId: 苹果商品id 748 /// productId: 苹果商品id
663 /// baseUnit: 除数基础单位(多少个天、周、月) -> 749 /// baseUnit: 除数基础单位(多少个天、周、月) ->
@@ -3,6 +3,8 @@ import AuthenticationServices @@ -3,6 +3,8 @@ import AuthenticationServices
3 import StoreKit 3 import StoreKit
4 import UIKit 4 import UIKit
5 import WebKit 5 import WebKit
  6 +import GoogleSignIn
  7 +import MessageUI
6 8
7 class PriceFormatter{ 9 class PriceFormatter{
8 static func formatPrice(_ price: Decimal, currencyCode: String) -> String { 10 static func formatPrice(_ price: Decimal, currencyCode: String) -> String {
@@ -46,7 +48,9 @@ extension WatchAppOtherInfo{ @@ -46,7 +48,9 @@ extension WatchAppOtherInfo{
46 * 组装完整 User-Agent格式与 Android 端保持一致 48 * 组装完整 User-Agent格式与 Android 端保持一致
47 * `{systemWebViewUA} doublefeel/{versionCode}({versionName})(Apple##Apple##{model}; iOS{osVersion}; {height}x{width})(huawei)` 49 * `{systemWebViewUA} doublefeel/{versionCode}({versionName})(Apple##Apple##{model}; iOS{osVersion}; {height}x{width})(huawei)`
48 */ 50 */
49 -final class PlatformHostApiImpl: PlatformHostApi { 51 +final class PlatformHostApiImpl: NSObject, PlatformHostApi {
  52 + private var sendEmailCompletion: ((Result<Bool, any Error>) -> Void)?
  53 +
50 func isDebugEnvoriment() throws -> Bool { 54 func isDebugEnvoriment() throws -> Bool {
51 #if DEBUG || CI_ENV 55 #if DEBUG || CI_ENV
52 return true 56 return true
@@ -348,6 +352,124 @@ final class PlatformHostApiImpl: PlatformHostApi { @@ -348,6 +352,124 @@ final class PlatformHostApiImpl: PlatformHostApi {
348 } 352 }
349 } 353 }
350 354
  355 + func requestGoogleSignIn(completion: @escaping (Result<GoogleSignInModel?, any Error>) -> Void) {
  356 + DispatchQueue.main.async {
  357 + guard let viewController = Self.topViewController() else {
  358 + print("[PlatformHostApiImpl.requestGoogleSignIn] error: \(PlatformHostApiError.missingPresentationAnchor.localizedDescription)")
  359 + completion(.failure(PlatformHostApiError.missingPresentationAnchor))
  360 + return
  361 + }
  362 +
  363 + GIDSignIn.sharedInstance.signIn(withPresenting: viewController) { signInResult, error in
  364 + if let error {
  365 + let nsError = error as NSError
  366 + if nsError.domain == kGIDSignInErrorDomain, nsError.code == -5 {
  367 + print("[PlatformHostApiImpl.requestGoogleSignIn] return: nil")
  368 + completion(.success(nil))
  369 + } else {
  370 + print("[PlatformHostApiImpl.requestGoogleSignIn] error: \(error.localizedDescription)")
  371 + completion(.failure(error))
  372 + }
  373 + return
  374 + }
  375 +
  376 + guard let user = signInResult?.user else {
  377 + print("[PlatformHostApiImpl.requestGoogleSignIn] error: \(PlatformHostApiError.invalidGoogleCredential.localizedDescription)")
  378 + completion(.failure(PlatformHostApiError.invalidGoogleCredential))
  379 + return
  380 + }
  381 +
  382 + guard let token = user.idToken?.tokenString else {
  383 + print("[PlatformHostApiImpl.requestGoogleSignIn] error: \(PlatformHostApiError.missingGoogleIDToken.localizedDescription)")
  384 + completion(.failure(PlatformHostApiError.missingGoogleIDToken))
  385 + return
  386 + }
  387 +
  388 + let profilePicUrl = user.profile?.imageURL(withDimension: 240)
  389 + let model = GoogleSignInModel(
  390 + email: user.profile?.email,
  391 + clientID: user.configuration.clientID,
  392 + idToken: token,
  393 + nickname: user.profile?.name,
  394 + avatarUrl: profilePicUrl?.absoluteString
  395 + )
  396 + print("[PlatformHostApiImpl.requestGoogleSignIn] return: \(model)")
  397 + completion(.success(model))
  398 + }
  399 + }
  400 + }
  401 +
  402 + func sendEmail(mailTo: String, title: String, content: String, completion: @escaping (Result<Bool, any Error>) -> Void) {
  403 + DispatchQueue.main.async { [weak self] in
  404 + guard let self else {
  405 + print("[PlatformHostApiImpl.sendEmail] error: \(PlatformHostApiError.deallocated.localizedDescription)")
  406 + completion(.failure(PlatformHostApiError.deallocated))
  407 + return
  408 + }
  409 +
  410 + guard MFMailComposeViewController.canSendMail() else {
  411 + UIApplication.openEmailWithSchema(to: mailTo, subject: title, body: content) { opened in
  412 + print("[PlatformHostApiImpl.sendEmail] return: \(opened)")
  413 + completion(.success(opened))
  414 + }
  415 + return
  416 + }
  417 +
  418 + guard sendEmailCompletion == nil else {
  419 + print("[PlatformHostApiImpl.sendEmail] error: \(PlatformHostApiError.mailComposerAlreadyPresented.localizedDescription)")
  420 + completion(.failure(PlatformHostApiError.mailComposerAlreadyPresented))
  421 + return
  422 + }
  423 +
  424 + guard let viewController = Self.topViewController() else {
  425 + print("[PlatformHostApiImpl.sendEmail] error: \(PlatformHostApiError.missingPresentationAnchor.localizedDescription)")
  426 + completion(.failure(PlatformHostApiError.missingPresentationAnchor))
  427 + return
  428 + }
  429 +
  430 + sendEmailCompletion = completion
  431 + let mailVC = MFMailComposeViewController()
  432 + mailVC.mailComposeDelegate = self
  433 + mailVC.setToRecipients([mailTo])
  434 + mailVC.setSubject(title)
  435 + mailVC.setMessageBody(content, isHTML: false)
  436 + viewController.present(mailVC, animated: true)
  437 + }
  438 + }
  439 +}
  440 +
  441 +extension PlatformHostApiImpl: MFMailComposeViewControllerDelegate {
  442 + func mailComposeController(
  443 + _ controller: MFMailComposeViewController,
  444 + didFinishWith result: MFMailComposeResult,
  445 + error: Error?
  446 + ) {
  447 + controller.dismiss(animated: true) { [weak self] in
  448 + guard let self else { return }
  449 + let completion = self.sendEmailCompletion
  450 + self.sendEmailCompletion = nil
  451 +
  452 + if let error {
  453 + print("[PlatformHostApiImpl.sendEmail] error: \(error.localizedDescription)")
  454 + completion?(.failure(error))
  455 + return
  456 + }
  457 +
  458 + let success: Bool
  459 + switch result {
  460 + case .cancelled:
  461 + success = false
  462 + case .saved, .sent:
  463 + success = true
  464 + case .failed:
  465 + success = false
  466 + @unknown default:
  467 + success = false
  468 + }
  469 + print("[PlatformHostApiImpl.sendEmail] return: \(success)")
  470 + completion?(.success(success))
  471 + }
  472 + }
351 } 473 }
352 474
353 //MARK: - 分享 475 //MARK: - 分享
@@ -419,3 +541,48 @@ extension PlatformHostApiImpl{ @@ -419,3 +541,48 @@ extension PlatformHostApiImpl{
419 return rootViewController 541 return rootViewController
420 } 542 }
421 } 543 }
  544 +
  545 +extension UIApplication{
  546 +
  547 + static func openEmailWithSchema(
  548 + to: String,
  549 + subject: String,
  550 + body: String,
  551 + completion: ((Bool) -> Void)? = nil
  552 + ) {
  553 + if let url = createEmailUrl(to: to, subject: subject, body: body),
  554 + UIApplication.shared.canOpenURL(url){
  555 + UIApplication.shared.open(url, completionHandler: completion)
  556 + }else{
  557 + let subjectEncoded = subject.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
  558 + let bodyEncoded = body.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
  559 + let defaultUrl = URL(string: "mailto:\(to)?subject=\(subjectEncoded)&body=\(bodyEncoded)")!
  560 + UIApplication.shared.open(defaultUrl, completionHandler: completion)
  561 + }
  562 + }
  563 +
  564 + static func createEmailUrl(to: String, subject: String, body: String) -> URL? {
  565 + let subjectEncoded = subject.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
  566 + let bodyEncoded = body.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
  567 + let gmailUrl = URL(string: "googlegmail://co?to=\(to)&subject=\(subjectEncoded)&body=\(bodyEncoded)")
  568 + let outlookUrl = URL(string: "ms-outlook://compose?to=\(to)&subject=\(subjectEncoded)")
  569 + let yahooMail = URL(string: "ymail://mail/compose?to=\(to)&subject=\(subjectEncoded)&body=\(bodyEncoded)")
  570 + let sparkUrl = URL(string: "readdle-spark://compose?recipient=\(to)&subject=\(subjectEncoded)&body=\(bodyEncoded)")
  571 + let qqEmail = URL(string: "mqqapi://composeemail/compose?to=\(to)&subject=\(subjectEncoded)&body=\(bodyEncoded)")
  572 +
  573 + let defaultUrl = URL(string: "mailto:\(to)?subject=\(subjectEncoded)&body=\(bodyEncoded)")
  574 +
  575 + if let gmailUrl = gmailUrl, UIApplication.shared.canOpenURL(gmailUrl) {
  576 + return gmailUrl
  577 + } else if let outlookUrl = outlookUrl, UIApplication.shared.canOpenURL(outlookUrl) {
  578 + return outlookUrl
  579 + } else if let yahooMail = yahooMail,UIApplication.shared.canOpenURL(yahooMail) {
  580 + return yahooMail
  581 + } else if let sparkUrl = sparkUrl, UIApplication.shared.canOpenURL(sparkUrl) {
  582 + return sparkUrl
  583 + }else if let qqEmail, UIApplication.shared.canOpenURL(qqEmail) {
  584 + return qqEmail
  585 + }
  586 + return defaultUrl
  587 + }
  588 +}
@@ -91,6 +91,67 @@ class AppleSignInModel { @@ -91,6 +91,67 @@ class AppleSignInModel {
91 ; 91 ;
92 } 92 }
93 93
  94 +class GoogleSignInModel {
  95 + GoogleSignInModel({
  96 + this.email,
  97 + this.clientID,
  98 + required this.idToken,
  99 + this.nickname,
  100 + this.avatarUrl,
  101 + });
  102 +
  103 + String? email;
  104 +
  105 + String? clientID;
  106 +
  107 + String idToken;
  108 +
  109 + String? nickname;
  110 +
  111 + String? avatarUrl;
  112 +
  113 + List<Object?> _toList() {
  114 + return <Object?>[
  115 + email,
  116 + clientID,
  117 + idToken,
  118 + nickname,
  119 + avatarUrl,
  120 + ];
  121 + }
  122 +
  123 + Object encode() {
  124 + return _toList(); }
  125 +
  126 + static GoogleSignInModel decode(Object result) {
  127 + result as List<Object?>;
  128 + return GoogleSignInModel(
  129 + email: result[0] as String?,
  130 + clientID: result[1] as String?,
  131 + idToken: result[2]! as String,
  132 + nickname: result[3] as String?,
  133 + avatarUrl: result[4] as String?,
  134 + );
  135 + }
  136 +
  137 + @override
  138 + // ignore: avoid_equals_and_hash_code_on_mutable_classes
  139 + bool operator ==(Object other) {
  140 + if (other is! GoogleSignInModel || other.runtimeType != runtimeType) {
  141 + return false;
  142 + }
  143 + if (identical(this, other)) {
  144 + return true;
  145 + }
  146 + return _deepEquals(encode(), other.encode());
  147 + }
  148 +
  149 + @override
  150 + // ignore: avoid_equals_and_hash_code_on_mutable_classes
  151 + int get hashCode => Object.hashAll(_toList())
  152 +;
  153 +}
  154 +
94 class WatchAppOtherInfo { 155 class WatchAppOtherInfo {
95 WatchAppOtherInfo({ 156 WatchAppOtherInfo({
96 this.userId, 157 this.userId,
@@ -316,15 +377,18 @@ class _PigeonCodec extends StandardMessageCodec { @@ -316,15 +377,18 @@ class _PigeonCodec extends StandardMessageCodec {
316 } else if (value is AppleSignInModel) { 377 } else if (value is AppleSignInModel) {
317 buffer.putUint8(130); 378 buffer.putUint8(130);
318 writeValue(buffer, value.encode()); 379 writeValue(buffer, value.encode());
319 - } else if (value is WatchAppOtherInfo) { 380 + } else if (value is GoogleSignInModel) {
320 buffer.putUint8(131); 381 buffer.putUint8(131);
321 writeValue(buffer, value.encode()); 382 writeValue(buffer, value.encode());
322 - } else if (value is AppleProductInfo) { 383 + } else if (value is WatchAppOtherInfo) {
323 buffer.putUint8(132); 384 buffer.putUint8(132);
324 writeValue(buffer, value.encode()); 385 writeValue(buffer, value.encode());
325 - } else if (value is AppleProductPaymentResult) { 386 + } else if (value is AppleProductInfo) {
326 buffer.putUint8(133); 387 buffer.putUint8(133);
327 writeValue(buffer, value.encode()); 388 writeValue(buffer, value.encode());
  389 + } else if (value is AppleProductPaymentResult) {
  390 + buffer.putUint8(134);
  391 + writeValue(buffer, value.encode());
328 } else { 392 } else {
329 super.writeValue(buffer, value); 393 super.writeValue(buffer, value);
330 } 394 }
@@ -339,10 +403,12 @@ class _PigeonCodec extends StandardMessageCodec { @@ -339,10 +403,12 @@ class _PigeonCodec extends StandardMessageCodec {
339 case 130: 403 case 130:
340 return AppleSignInModel.decode(readValue(buffer)!); 404 return AppleSignInModel.decode(readValue(buffer)!);
341 case 131: 405 case 131:
342 - return WatchAppOtherInfo.decode(readValue(buffer)!); 406 + return GoogleSignInModel.decode(readValue(buffer)!);
343 case 132: 407 case 132:
344 - return AppleProductInfo.decode(readValue(buffer)!); 408 + return WatchAppOtherInfo.decode(readValue(buffer)!);
345 case 133: 409 case 133:
  410 + return AppleProductInfo.decode(readValue(buffer)!);
  411 + case 134:
346 return AppleProductPaymentResult.decode(readValue(buffer)!); 412 return AppleProductPaymentResult.decode(readValue(buffer)!);
347 default: 413 default:
348 return super.readValueOfType(type, buffer); 414 return super.readValueOfType(type, buffer);
@@ -770,6 +836,59 @@ class PlatformHostApi { @@ -770,6 +836,59 @@ class PlatformHostApi {
770 } 836 }
771 } 837 }
772 838
  839 + /// google登录
  840 + Future<GoogleSignInModel?> requestGoogleSignIn() async {
  841 + final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.requestGoogleSignIn$pigeonVar_messageChannelSuffix';
  842 + final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
  843 + pigeonVar_channelName,
  844 + pigeonChannelCodec,
  845 + binaryMessenger: pigeonVar_binaryMessenger,
  846 + );
  847 + final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);
  848 + final List<Object?>? pigeonVar_replyList =
  849 + await pigeonVar_sendFuture as List<Object?>?;
  850 + if (pigeonVar_replyList == null) {
  851 + throw _createConnectionError(pigeonVar_channelName);
  852 + } else if (pigeonVar_replyList.length > 1) {
  853 + throw PlatformException(
  854 + code: pigeonVar_replyList[0]! as String,
  855 + message: pigeonVar_replyList[1] as String?,
  856 + details: pigeonVar_replyList[2],
  857 + );
  858 + } else {
  859 + return (pigeonVar_replyList[0] as GoogleSignInModel?);
  860 + }
  861 + }
  862 +
  863 + /// 发送邮件
  864 + Future<bool> sendEmail(String mailTo, String title, String content) async {
  865 + final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.sendEmail$pigeonVar_messageChannelSuffix';
  866 + final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
  867 + pigeonVar_channelName,
  868 + pigeonChannelCodec,
  869 + binaryMessenger: pigeonVar_binaryMessenger,
  870 + );
  871 + final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[mailTo, title, content]);
  872 + final List<Object?>? pigeonVar_replyList =
  873 + await pigeonVar_sendFuture as List<Object?>?;
  874 + if (pigeonVar_replyList == null) {
  875 + throw _createConnectionError(pigeonVar_channelName);
  876 + } else if (pigeonVar_replyList.length > 1) {
  877 + throw PlatformException(
  878 + code: pigeonVar_replyList[0]! as String,
  879 + message: pigeonVar_replyList[1] as String?,
  880 + details: pigeonVar_replyList[2],
  881 + );
  882 + } else if (pigeonVar_replyList[0] == null) {
  883 + throw PlatformException(
  884 + code: 'null-error',
  885 + message: 'Host platform returned null value for non-null return value.',
  886 + );
  887 + } else {
  888 + return (pigeonVar_replyList[0] as bool?)!;
  889 + }
  890 + }
  891 +
773 /// 查询指定id的苹果商品 892 /// 查询指定id的苹果商品
774 /// productId: 苹果商品id 893 /// productId: 苹果商品id
775 /// baseUnit: 除数基础单位(多少个天、周、月) -> 894 /// baseUnit: 除数基础单位(多少个天、周、月) ->
@@ -13,6 +13,13 @@ class AppleSignInModel { @@ -13,6 +13,13 @@ class AppleSignInModel {
13 this.identityToken, 13 this.identityToken,
14 }); 14 });
15 } 15 }
  16 +class GoogleSignInModel{
  17 + final String? email;
  18 + final String? clientID;
  19 + final String idToken;
  20 + final String? nickname;
  21 + final String? avatarUrl;
  22 +}
16 23
17 class WatchAppOtherInfo { 24 class WatchAppOtherInfo {
18 final int? userId; 25 final int? userId;
@@ -163,6 +170,13 @@ abstract class PlatformHostApi { @@ -163,6 +170,13 @@ abstract class PlatformHostApi {
163 /// 请求苹果登录 170 /// 请求苹果登录
164 @async 171 @async
165 AppleSignInModel? requestAppleSignIn(); 172 AppleSignInModel? requestAppleSignIn();
  173 + /// google登录
  174 + @async
  175 + GoogleSignInModel? requestGoogleSignIn();
  176 +
  177 + /// 发送邮件
  178 + @async
  179 + bool sendEmail(String mailTo, String title, String content);
166 180
167 /// 查询指定id的苹果商品 181 /// 查询指定id的苹果商品
168 /// productId: 苹果商品id 182 /// productId: 苹果商品id