diff --git a/GoogleService-Info.plist b/GoogleService-Info.plist new file mode 100644 index 000000000..c90ada3a3 --- /dev/null +++ b/GoogleService-Info.plist @@ -0,0 +1,36 @@ + + + + + CLIENT_ID + 907754492821-oc9e77svgmmlgouo0op7vv00e1hb8278.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.907754492821-oc9e77svgmmlgouo0op7vv00e1hb8278 + ANDROID_CLIENT_ID + 907754492821-9jakqvathu61e6b8g95i41ssi7o4dnka.apps.googleusercontent.com + API_KEY + AIzaSyAvyBQcOXuO_q851vevqZIufsPRTMCe808 + GCM_SENDER_ID + 907754492821 + PLIST_VERSION + 1 + BUNDLE_ID + com.yunqiinnovation.deepsound + PROJECT_ID + deepsound-e0e3d + STORAGE_BUCKET + deepsound-e0e3d.firebasestorage.app + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:907754492821:ios:246794fc44b23174145c9c + + \ No newline at end of file diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 6879ee469..aeb0fa019 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -1,3 +1,5 @@ +import java.util.Properties +import java.io.FileInputStream repositories { google() mavenCentral() @@ -11,10 +13,16 @@ plugins { id("kotlin-android") // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. id("dev.flutter.flutter-gradle-plugin") + id("com.google.gms.google-services")version "4.3.13" +} + +val keystorePropertiesFile = rootProject.file("app/sign/DeepSound.properties") +val keystoreProperties =Properties().apply { + load(FileInputStream(keystorePropertiesFile)) // 使用 Kotlin 扩展方法 } android { - namespace = "com.example.deep_voice" + namespace = "com.yunqiinnovation.deepsound" compileSdk = 35 ndkVersion = "28.0.13004108" @@ -31,7 +39,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId = "com.example.deep_voice" + applicationId = "com.yunqiinnovation.deepsound" // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. minSdk = 24 // Updated to meet azure_speech_recognition_null_safety requirements @@ -40,11 +48,25 @@ android { versionName = flutter.versionName } + signingConfigs { + create("release") { + keyAlias = keystoreProperties.getProperty("keyAlias") + keyPassword = keystoreProperties.getProperty("keyPassword") + storeFile = file(keystoreProperties.getProperty("storeFile")) + storePassword = keystoreProperties.getProperty("storePassword") + enableV1Signing = true + enableV2Signing = true + } + } + buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig = signingConfigs.getByName("debug") + isMinifyEnabled = false + isShrinkResources = false + signingConfig = signingConfigs.getByName("release") + // signingConfig = signingConfigs.getByName("debug") } } } diff --git a/android/app/google-services.json b/android/app/google-services.json new file mode 100644 index 000000000..4cbe172f9 --- /dev/null +++ b/android/app/google-services.json @@ -0,0 +1,47 @@ +{ + "project_info": { + "project_number": "907754492821", + "project_id": "deepsound-e0e3d", + "storage_bucket": "deepsound-e0e3d.firebasestorage.app" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:907754492821:android:2deffc5aa1037ae2145c9c", + "android_client_info": { + "package_name": "com.yunqiinnovation.deepsound" + } + }, + "oauth_client": [ + { + "client_id": "907754492821-9jakqvathu61e6b8g95i41ssi7o4dnka.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.yunqiinnovation.deepsound", + "certificate_hash": "5a9520045bc16fb4f09fb97668a8b0fc4babd376" + } + }, + { + "client_id": "907754492821-15btgolbd7thaa8fpt7s8hs8hq3jl0lg.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyAj0_-nS3ygc8VFe1azotSE3-HGZYClkTs" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "907754492821-15btgolbd7thaa8fpt7s8hs8hq3jl0lg.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/android/app/sign/DeepSound.properties b/android/app/sign/DeepSound.properties new file mode 100644 index 000000000..96bb7e16a --- /dev/null +++ b/android/app/sign/DeepSound.properties @@ -0,0 +1,4 @@ +storePassword=13071376716 +keyPassword=13071376716 +keyAlias=DeepSound +storeFile=sign/DeepSound.keystore \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 88f1fef92..f120cc1f7 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -29,7 +29,7 @@ - + + + + 3829022037361855 + fb3829022037361855 + 193d0a2b1757c41cfa557ea8e8f21c33 + \ No newline at end of file diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 89176ef44..35365df1a 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -3,6 +3,7 @@ allprojects { google() mavenCentral() } + } val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() diff --git a/firebase.json b/firebase.json new file mode 100644 index 000000000..a2b0f41a5 --- /dev/null +++ b/firebase.json @@ -0,0 +1 @@ +{"flutter":{"platforms":{"android":{"default":{"projectId":"deepsound-e0e3d","appId":"1:907754492821:android:2deffc5aa1037ae2145c9c","fileOutput":"android/app/google-services.json"}},"dart":{"lib/firebase_options.dart":{"projectId":"deepsound-e0e3d","configurations":{"android":"1:907754492821:android:2deffc5aa1037ae2145c9c","ios":"1:907754492821:ios:8c1d7d1b583aec22145c9c","macos":"1:907754492821:ios:8c1d7d1b583aec22145c9c","web":"1:907754492821:web:99451e7efd7d120a145c9c","windows":"1:907754492821:web:e3e8040111db6871145c9c"}}}}}} \ No newline at end of file diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart new file mode 100644 index 000000000..1f8b9593e --- /dev/null +++ b/lib/firebase_options.dart @@ -0,0 +1,92 @@ +// File generated by FlutterFire CLI. +// ignore_for_file: type=lint +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; +import 'package:flutter/foundation.dart' + show defaultTargetPlatform, kIsWeb, TargetPlatform; + +/// Default [FirebaseOptions] for use with your Firebase apps. +/// +/// Example: +/// ```dart +/// import 'firebase_options.dart'; +/// // ... +/// await Firebase.initializeApp( +/// options: DefaultFirebaseOptions.currentPlatform, +/// ); +/// ``` +class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + if (kIsWeb) { + return web; + } + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return android; + case TargetPlatform.iOS: + return ios; + case TargetPlatform.macOS: + return macos; + case TargetPlatform.windows: + return windows; + case TargetPlatform.linux: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for linux - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + default: + throw UnsupportedError( + 'DefaultFirebaseOptions are not supported for this platform.', + ); + } + } + + static const FirebaseOptions web = FirebaseOptions( + apiKey: 'AIzaSyBJXQTXsEmY_XA7ZEOusxG6VF5CaKtjHCA', + appId: '1:907754492821:web:99451e7efd7d120a145c9c', + messagingSenderId: '907754492821', + projectId: 'deepsound-e0e3d', + authDomain: 'deepsound-e0e3d.firebaseapp.com', + storageBucket: 'deepsound-e0e3d.firebasestorage.app', + measurementId: 'G-ETX6DF35J3', + ); + + static const FirebaseOptions android = FirebaseOptions( + apiKey: 'AIzaSyAj0_-nS3ygc8VFe1azotSE3-HGZYClkTs', + appId: '1:907754492821:android:2deffc5aa1037ae2145c9c', + messagingSenderId: '907754492821', + projectId: 'deepsound-e0e3d', + storageBucket: 'deepsound-e0e3d.firebasestorage.app', + ); + + static const FirebaseOptions ios = FirebaseOptions( + apiKey: 'AIzaSyAvyBQcOXuO_q851vevqZIufsPRTMCe808', + appId: '1:907754492821:ios:8c1d7d1b583aec22145c9c', + messagingSenderId: '907754492821', + projectId: 'deepsound-e0e3d', + storageBucket: 'deepsound-e0e3d.firebasestorage.app', + androidClientId: '907754492821-9jakqvathu61e6b8g95i41ssi7o4dnka.apps.googleusercontent.com', + iosClientId: '907754492821-ej6j6pfto66jfhpj47b9bjs9amvo9dde.apps.googleusercontent.com', + iosBundleId: 'com.example.client', + ); + + static const FirebaseOptions macos = FirebaseOptions( + apiKey: 'AIzaSyAvyBQcOXuO_q851vevqZIufsPRTMCe808', + appId: '1:907754492821:ios:8c1d7d1b583aec22145c9c', + messagingSenderId: '907754492821', + projectId: 'deepsound-e0e3d', + storageBucket: 'deepsound-e0e3d.firebasestorage.app', + androidClientId: '907754492821-9jakqvathu61e6b8g95i41ssi7o4dnka.apps.googleusercontent.com', + iosClientId: '907754492821-ej6j6pfto66jfhpj47b9bjs9amvo9dde.apps.googleusercontent.com', + iosBundleId: 'com.example.client', + ); + + static const FirebaseOptions windows = FirebaseOptions( + apiKey: 'AIzaSyBJXQTXsEmY_XA7ZEOusxG6VF5CaKtjHCA', + appId: '1:907754492821:web:e3e8040111db6871145c9c', + messagingSenderId: '907754492821', + projectId: 'deepsound-e0e3d', + authDomain: 'deepsound-e0e3d.firebaseapp.com', + storageBucket: 'deepsound-e0e3d.firebasestorage.app', + measurementId: 'G-SKGXEK5TJM', + ); +} diff --git a/lib/main.dart b/lib/main.dart index 135794747..0808b3423 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -12,6 +12,9 @@ import 'package:window_size/window_size.dart'; import 'core/translations/app_translations.dart'; import 'core/themes/app_theme.dart'; import 'core/themes/theme_service.dart'; +import 'modules/login/controllers/weixinLogin.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'firebase_options.dart'; void main() async { // 确保Flutter绑定初始化 @@ -23,6 +26,26 @@ void main() async { // 初始化环境变量 await dotenv.load(); + FlWX().register( + appSecret: '4cfde33bc22b6cc6b1f8b225dd97d255', + appId: 'wx5bbb7be9324f3e2d', + params: FlWXCallbackParams(onHttp: (url) async { + return ''; + })); + var err = FlWX().isInstalled; + String msg = err.toString() == 'false' ? '请先下载微信app' : '请授权登录'; + print(msg); + if (Firebase.apps.isEmpty) { + await Firebase.initializeApp( + name: 'DeepSound', + options: DefaultFirebaseOptions.currentPlatform, + //demoProjectId:'1:907754492821:android:2deffc5aa1037ae2145c9c' + ).then((value) { + // logger.i('Firebase Initialized:${value.options}'); + print('Firebase Initialized:${value.options}'); + }); + } + // 设置窗口大小(仅在 macOS 下生效) if (Platform.isMacOS) { const double width = 375.0; diff --git a/lib/modules/login/controllers/facebookLogin.dart b/lib/modules/login/controllers/facebookLogin.dart new file mode 100644 index 000000000..d77ae2177 --- /dev/null +++ b/lib/modules/login/controllers/facebookLogin.dart @@ -0,0 +1,39 @@ +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter_facebook_auth/flutter_facebook_auth.dart'; +import 'package:get/get.dart'; + +class Facebook { + String token = ''; + // FaceBook登录 + // 修改后的 signInWithFacebook + Future signInWithFacebook() async { + try { + print("开始 Facebook 登录"); + final LoginResult result = await FacebookAuth.instance.login(); + print("Facebook 登录结果:$result"); + + if (result.status == LoginStatus.success) { + token = result.accessToken!.tokenString; + print("Facebook Token: $token"); + + final credential = FacebookAuthProvider.credential(token); + print("Facebook Token: $credential"); + final userData = await FacebookAuth.instance.getUserData(); + print("user login facebook ${userData..values}"); + + return true; + } else { + print("登录失败,状态:${result.status},消息:${result.message}"); + return false; + } + } catch (e) { + print("Facebook 登录异常:$e"); + return false; + } + } + + // 退出Google登录 + Future signOutFacebook() async { + await FacebookAuth.instance.logOut(); + } +} diff --git a/lib/modules/login/controllers/googleLogin.dart b/lib/modules/login/controllers/googleLogin.dart new file mode 100644 index 000000000..3e118e6f8 --- /dev/null +++ b/lib/modules/login/controllers/googleLogin.dart @@ -0,0 +1,72 @@ +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:google_sign_in/google_sign_in.dart'; +import 'package:get/get.dart'; + +class Google { + String? token = ''; + String? email = ''; + String? displayName = ''; + String? id = ''; + String? accessToken = ''; + + // 登录并返回 + Future signInWithGoogle() async { + try { + // 确保用户已经登录,并获取当前用户的身份验证令牌 + final currentUser = FirebaseAuth.instance.currentUser; + print('Future响应数据: $currentUser'); + if (currentUser != null) { + // 这里处理您需要使用这个JWT令牌的逻辑,例如将它存储到本地存储中作为凭据。 + + token != await currentUser.getIdToken(); + + print('Future响应数据tokenResult: $token'); + + return true; + } else { + UserCredential userCredential = await loginGoogle(); + if (token != "") { + return true; + } else { + return false; + } + + // logger.d("user login google: ${userCredential.user?.displayName}"); + } + } catch (e) { + print("Google 登录异常:$e"); + return false; + } + } + + // Google登录 + Future loginGoogle() async { + print('loginGoogle:'); + final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn(); + email = googleUser?.email; + id = googleUser?.id; + displayName = googleUser?.displayName; + print('loginGoogle: $googleUser'); + // Obtain the auth details from the request + final GoogleSignInAuthentication? googleAuth = + await googleUser?.authentication; + accessToken = googleAuth?.accessToken; + token = googleAuth?.idToken; + print('loginGoogle:googleAuth?.idToken= ${token}'); + print('loginGoogle:googleAuth?.accessToken= ${accessToken}'); + // Create a new credential, + final credential = GoogleAuthProvider.credential( + accessToken: googleAuth?.accessToken, + idToken: googleAuth?.idToken, + ); + + // Once signed in, return the UserCredential + return await FirebaseAuth.instance.signInWithCredential(credential); + } + + // 退出Google登录 + Future signOutGoogle() async { + await FirebaseAuth.instance.signOut(); + await GoogleSignIn().signOut(); + } +} diff --git a/lib/modules/login/controllers/weixinLogin.dart b/lib/modules/login/controllers/weixinLogin.dart new file mode 100644 index 000000000..214c5a663 --- /dev/null +++ b/lib/modules/login/controllers/weixinLogin.dart @@ -0,0 +1,889 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; +import 'package:fluwx/fluwx.dart'; +export 'package:fluwx/fluwx.dart'; +import 'package:http/http.dart' as http; +import 'package:get/get.dart'; + +typedef FlWeChatResponseCallback = void Function(WeChatResponse response); + +/// 支持成功 +typedef FlWeChatPayResponseResultCallback = void Function(); + +/// 支付结果回调 +typedef FlWeChatPayResponseCallback = void Function( + WeChatPaymentResponse response); + +/// LaunchFromWX 回调 +typedef FlWeChatLaunchFromWXRequestCallback = void Function( + WeChatLaunchFromWXRequest response); + +/// OpenBusinessWebView 回调 +typedef FlWeChatOpenBusinessWebViewResponseCallback = void Function( + WeChatOpenBusinessWebviewResponse response); + +/// WeChatOpenBusinessView 回调 +typedef FlWeChatOpenBusinessViewResponseCallback = void Function( + WeChatOpenBusinessViewResponse response); + +/// Share 回调 +typedef FlWeChatShareResponseCallback = void Function( + WeChatShareResponse response); + +/// Auth 回调 +typedef FlWeChatAuthResponseCallback = Function(WeChatAuthResponse response); + +/// Auth token 回调 +typedef FlWeChatAuthResponseTokenCallback = Function( + WeChatAuthResponse response, WXTokenModel token); + +/// Auth userinfo 回调 +typedef FlWeChatAuthResponseUserinfoCallback = Function( + WeChatAuthResponse response, + WXTokenModel token, + WXUserModel userInfo, +); + +/// 显示 toast +typedef FlWXToastCallback = void Function(String msg); + +/// 日志 打印 +typedef FlWXLogCallback = void Function(String msg); + +/// http 请求 +typedef FlWXHTTPCallback = Future Function(String url); + +class FlWX { + factory FlWX() => _singleton ??= FlWX._(); + + FlWX._(); + + static FlWX? _singleton; + + final Fluwx fluwx = Fluwx(); + + String? _appId; + + /// 获取当前设置的 appId + String? get appId => _appId; + + String? _appSecret; + + /// 获取当前设置的 appSecret + String? get appSecret => _appSecret; + + String _appName = ''; + String token = ''; + String refresh_token = ''; + String openid = ''; + // 参数 说明 + // access_token 接口调用凭证 + // expires_in access_token 接口调用凭证超时时间,单位(秒) + // refresh_token 用户刷新 access_token + // openid 授权用户唯一标识 + // scope 用户授权的作用域(snsapi_userinfo) + // unionid 当且仅当该移动应用已获得该用户的 userinfo 授权时,才会出现该字段 + /// callback params + FlWXCallbackParams? _params; + + /// 分享时的缩略图 + Uint8List? _shareThumbnail; + + /// register + bool _isRegister = false; + + Future register({ + required String appId, + required FlWXCallbackParams params, + String? universalLink, + bool doOnIOS = true, + bool doOnAndroid = true, + + /// 用于获取用户信息 + String? appSecret, + + /// app名称(用于分享时使用) + String appName = '', + + /// 分享时的缩略图 + Uint8List? shareThumbnail, + }) { + assert(appId.isNotEmpty); + if (universalLink != null) assert(universalLink.isNotEmpty); + if (appSecret != null) { + assert(appSecret.isNotEmpty); + } + _isRegister = true; + _appId = appId; + _appSecret = appSecret; + _appName = appName; + _shareThumbnail = shareThumbnail; + _params = params; + return fluwx.registerApi( + appId: appId, + doOnAndroid: doOnAndroid, + doOnIOS: doOnIOS, + universalLink: universalLink, + ); + } + + /// 获取微信 用户token + String _getTokenUrl(String code) { + print("_getTokenUrl 数据获取${_appId},${_appSecret}"); + assert( + _appId != null && + _appId!.isNotEmpty && + _appSecret != null && + _appSecret!.isNotEmpty, + ); + return 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=$_appId&secret=$_appSecret&code=$code&grant_type=authorization_code'; + } + + /// 获取微信 用户token + String _getRefreshTokenUrl(String code) { + print("_getRefreshTokenUrl 数据获取${_appId},${_appSecret}"); + assert( + _appId != null && + _appId!.isNotEmpty && + _appSecret != null && + _appSecret!.isNotEmpty, + ); + return 'https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=$_appId&grant_type=refresh_token&&refresh_token=$refresh_token'; + } + + // GET请求示例 + Future> fetchData(String Url) async { + try { + final response = await http.get(Uri.parse(Url)); + + if (response.statusCode == 200) { + final data = jsonDecode(response.body) as Map; + print('GET响应数据: $data'); + + return data; + } else { + print('GET请求失败,状态码: ${response.statusCode}'); + return {}; + } + } catch (e) { + print('GET请求异常: $e'); + return {}; + } + } + + // POST请求示例 + Future postData() async { + try { + final response = await http.post( + Uri.parse('https://jsonplaceholder.typicode.com/posts'), + headers: { + 'Content-Type': 'application/json', + }, // 设置请求头‌:ml-citation{ref="6" data="citationList"} + body: jsonEncode({ + // 编码JSON数据‌:ml-citation{ref="3" data="citationList"} + 'title': 'foo', + 'body': 'bar', + 'userId': 1, + }), + ); + + if (response.statusCode == 201) { + final data = jsonDecode(response.body); + print('POST响应数据: $data'); + return true; + } else { + print('POST请求失败,状态码: ${response.statusCode}'); + return false; + } + } catch (e) { + print('POST请求异常: $e'); + return false; + } + } + + /// 获取微信 用户信息 + String _getUserInfoUrl({required String openId, required String token}) => + 'https://api.weixin.qq.com/sns/userinfo?access_token=$token&openid=$openId'; + //检验授权凭证(access_token)是否有效 + String _getUserTokenEffertive({ + required String openId, + required String token, + }) => + 'https://api.weixin.qq.com/sns/auth?access_token=$token&openid=$openId'; + + /// 是否安装微信 + Future get isInstalled async { + assert(_isRegister, '请先调用 FlWX().register()'); + final installed = await fluwx.isWeChatInstalled; + if (!installed) _params!.onToast?.call('该服务需要先安装微信'); + return installed; + } + + /// isSupportOpenBusinessView + Future get isSupportOpenBusinessView async { + if (!await isInstalled) return false; + return await fluwx.isSupportOpenBusinessView; + } + + /// 添加移除监听 + FluwxCancelable _onListener(FlWeChatResponseCallback onResponse) { + late FluwxCancelable cancelable; + subscriber(response) { + onResponse(response); + Future.delayed(const Duration(milliseconds: 500), () { + cancelable.cancel(); + }); + } + + return cancelable = fluwx.addSubscriber(subscriber); + } + + Future getExtMsg() async { + if (!await isInstalled) return null; + return await fluwx.getExtMsg(); + } + + Future attemptToResumeMsgFromWx({ + Function(WeChatShowMessageFromWXRequest response)? onResponse, + }) async { + if (!await isInstalled) return; + await fluwx.attemptToResumeMsgFromWx(); + if (onResponse != null) { + _onListener((response) { + if (response is WeChatShowMessageFromWXRequest) onResponse(response); + }); + } + } + + /// 调用微信登录 + Future authBy( + AuthType which, { + /// 原始数据回调 + FlWeChatAuthResponseCallback? onResponse, + + /// 获取token回调 + FlWeChatAuthResponseTokenCallback? onToken, + + /// 获取userinfo回调 + FlWeChatAuthResponseUserinfoCallback? onUserinfo, + bool getUserInfo = false, + }) async { + if (!await isInstalled) return false; + final cancelable = _onListener((response) async { + if (response is WeChatAuthResponse) { + onResponse?.call(response); + print( + "用户点击事件${response.isSuccessful},${response.code},${response.lang},${response.state},${response.country}", + ); + if (response.isSuccessful && response.code != null) { + print("数据获取失败${onToken},${onUserinfo}"); + // if (onToken != null || onUserinfo != null) { + try { + var Istoken = await fetchData( + _getTokenUrl(response.code!), + ); // 触发GET请求 + //token有效期是30天,30天后需要重新授权,需要看看API文档 + print("数据获取fetchData${Istoken}"); + + if (Istoken != {}) { + token = Istoken["access_token"].toString(); + refresh_token = Istoken["refresh_token"].toString(); + openid = Istoken["openid"].toString(); + print("数据获取token=${token}"); + print("数据获取openid=${openid}"); + print("数据获取refresh_token=${refresh_token}"); + Istoken = await fetchData(_getRefreshTokenUrl(response.code!)); + print("数据获取_getUserTokenEffertive${Istoken}"); + Istoken = await fetchData( + _getUserTokenEffertive(openId: openid, token: token), + ); // 触发GET请求 + print("数据获取_getUserTokenEffertive${Istoken}"); + Istoken = await fetchData( + _getUserInfoUrl(openId: openid, token: token), + ); // 触发GET请求 + print("数据获取_getUserInfoUrl${Istoken}"); + } + } catch (e) { + _params!.onLog?.call('数据解析失败 $e'); + } + // } + } + } else if (response is WeChatAuthByQRCodeFinishedResponse) {} + }); + final result = await fluwx.authBy(which: which); + print("authBy数据获取${result}"); + if (!result) { + cancelable.cancel(); + _params!.onToast?.call('微信授权失败'); + } + return result; + } + + Future autoDeduct( + AutoDeduct data, { + FlWeChatResponseCallback? onResponse, + }) async { + final cancelable = _onListener((response) { + onResponse?.call(response); + }); + final result = await fluwx.autoDeduct(data: data); + if (!result) { + cancelable.cancel(); + _params!.onToast?.call('微信签约失败'); + } + return result; + } + + /// 调用微信支付 + Future pay( + PayType which, { + FlWeChatPayResponseResultCallback? onSuccess, + FlWeChatPayResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + final cancelable = _onListener((response) { + if (response is WeChatPaymentResponse) { + onResponse?.call(response); + _params!.onLog?.call('微信支付 result code: ${response.errCode} '); + if (response.errCode == 0) { + _params!.onToast?.call('支付成功'); + onSuccess?.call(); + } else if (response.errCode == -2) { + _params!.onToast?.call('已取消支付'); + } else { + _params!.onToast?.call('支付失败'); + } + } + }); + final result = await fluwx.pay(which: which); + if (!result) { + cancelable.cancel(); + _params!.onToast?.call('支付失败'); + } + return result; + } + + /// ****************** open ****************** /// + + Future open(OpenType target) async { + if (!await isInstalled) return false; + return fluwx.open(target: target); + } + + Future openWeChatApp({ + FlWeChatLaunchFromWXRequestCallback? onResponse, + }) async { + if (!await isInstalled) return false; + final cancelable = _onListener((response) { + if (response is WeChatLaunchFromWXRequest) onResponse?.call(response); + }); + final result = await fluwx.open(target: WeChatApp()); + if (!result) cancelable.cancel(); + return result; + } + + Future openBrowser( + String url, { + FlWeChatOpenBusinessWebViewResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + final cancelable = _onListener((response) { + if (response is WeChatOpenBusinessWebviewResponse) { + onResponse?.call(response); + } + }); + final result = await fluwx.open(target: Browser(url)); + if (!result) cancelable.cancel(); + return result; + } + + Future openRankList() async { + if (!await isInstalled) return false; + return fluwx.open(target: RankList()); + } + + Future openBusinessView({ + required String businessType, + required String query, + FlWeChatOpenBusinessViewResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + final cancelable = _onListener((response) { + if (response is WeChatOpenBusinessViewResponse) { + onResponse?.call(response); + } + }); + final result = await fluwx.open( + target: BusinessView(businessType: businessType, query: query), + ); + if (!result) cancelable.cancel(); + return result; + } + + Future openInvoice({ + required String appId, + required String cardType, + String locationId = '', + String cardId = '', + String canMultiSelect = '', + Function(WeChatOpenInvoiceResponse response)? onResponse, + }) async { + if (!await isInstalled) return false; + final cancelable = _onListener((response) { + if (response is WeChatOpenInvoiceResponse) onResponse?.call(response); + }); + final result = await fluwx.open( + target: Invoice( + appId: appId, + cardType: cardType, + locationId: locationId, + cardId: cardId, + canMultiSelect: canMultiSelect, + ), + ); + if (!result) cancelable.cancel(); + return result; + } + + Future openCustomerServiceChat({ + required String corpId, + required String url, + Function(WeChatOpenCustomerServiceChatResponse response)? onResponse, + }) async { + if (!await isInstalled) return false; + final cancelable = _onListener((response) { + if (response is WeChatOpenCustomerServiceChatResponse) { + onResponse?.call(response); + } + }); + final result = await fluwx.open( + target: CustomerServiceChat(corpId: corpId, url: url), + ); + if (!result) cancelable.cancel(); + return result; + } + + Future openMiniProgram( + String username, { + String? path, + Function(WeChatLaunchMiniProgramResponse response)? onResponse, + }) async { + if (!await isInstalled) return false; + final cancelable = _onListener((response) { + if (response is WeChatLaunchMiniProgramResponse) { + onResponse?.call(response); + } + }); + final result = await fluwx.open( + target: MiniProgram(username: username, path: path), + ); + if (!result) { + cancelable.cancel(); + _params?.onToast?.call('小程序打开失败'); + } + return result; + } + + Future openSubscribeMessage({ + required String appId, + required int scene, + required String templateId, + String? reserved, + Function(WeChatSubscribeMsgResponse response)? onResponse, + }) async { + if (!await isInstalled) return false; + final cancelable = _onListener((response) { + if (response is WeChatSubscribeMsgResponse) onResponse?.call(response); + }); + final result = await fluwx.open( + target: SubscribeMessage( + appId: appId, + scene: scene, + templateId: templateId, + reserved: reserved, + ), + ); + if (!result) cancelable.cancel(); + return result; + } + + /// ****************** share ****************** /// + + Future share( + WeChatShareModel what, { + FlWeChatShareResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + return _shareResponse(fluwx.share(what), onResponse: onResponse); + } + + Future shareText( + String source, { + String? title, + String? description, + WeChatScene scene = WeChatScene.session, + String? messageExt, + String? messageAction, + String? mediaTagName, + String? msgSignature, + Uint8List? thumbData, + String? thumbDataHash, + FlWeChatShareResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + return _shareResponse( + fluwx.share( + WeChatShareTextModel( + source, + title: title ?? _appName, + description: description ?? _appName, + scene: scene, + messageExt: messageExt, + messageAction: messageAction, + mediaTagName: mediaTagName, + msgSignature: msgSignature, + thumbData: thumbData ?? _shareThumbnail, + thumbDataHash: thumbDataHash, + ), + ), + onResponse: onResponse, + ); + } + + Future shareMiniProgram( + String webPage, { + required String webPageUrl, + WXMiniProgramType miniProgramType = WXMiniProgramType.release, + required String userName, + String path = "/", + String? title, + String? description, + bool withShareTicket = false, + String? messageExt, + String? messageAction, + String? mediaTagName, + String? msgSignature, + Uint8List? thumbData, + String? thumbDataHash, + FlWeChatShareResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + return _shareResponse( + fluwx.share( + WeChatShareMiniProgramModel( + webPageUrl: webPageUrl, + userName: userName, + title: title ?? _appName, + description: description ?? _appName, + messageExt: messageExt, + messageAction: messageAction, + mediaTagName: mediaTagName, + msgSignature: msgSignature, + thumbData: thumbData ?? _shareThumbnail, + thumbDataHash: thumbDataHash, + ), + ), + onResponse: onResponse, + ); + } + + Future shareImage( + WeChatImageToShare source, { + String? title, + String? description, + WeChatScene scene = WeChatScene.session, + String? messageExt, + String? messageAction, + String? mediaTagName, + String? msgSignature, + Uint8List? thumbData, + String? thumbDataHash, + FlWeChatShareResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + return _shareResponse( + fluwx.share( + WeChatShareImageModel( + source, + title: title ?? _appName, + description: description ?? _appName, + scene: scene, + messageExt: messageExt, + messageAction: messageAction, + mediaTagName: mediaTagName, + msgSignature: msgSignature, + thumbData: thumbData ?? _shareThumbnail, + thumbDataHash: thumbDataHash, + ), + ), + onResponse: onResponse, + ); + } + + Future shareMusic({ + String? musicUrl, + String? musicDataUrl, + String? musicLowBandUrl, + String? musicLowBandDataUrl, + String? title, + String? description, + WeChatScene scene = WeChatScene.session, + String? messageExt, + String? messageAction, + String? mediaTagName, + String? msgSignature, + Uint8List? thumbData, + String? thumbDataHash, + FlWeChatShareResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + return _shareResponse( + fluwx.share( + WeChatShareMusicModel( + musicUrl: musicUrl, + musicDataUrl: musicDataUrl, + musicLowBandUrl: musicLowBandUrl, + musicLowBandDataUrl: musicLowBandDataUrl, + title: title ?? _appName, + description: description ?? _appName, + scene: scene, + messageExt: messageExt, + messageAction: messageAction, + mediaTagName: mediaTagName, + msgSignature: msgSignature, + thumbData: thumbData ?? _shareThumbnail, + thumbDataHash: thumbDataHash, + ), + ), + onResponse: onResponse, + ); + } + + Future shareVideo( + String videoUrl, { + String? title, + String? description, + WeChatScene scene = WeChatScene.session, + String? messageExt, + String? messageAction, + String? mediaTagName, + String? msgSignature, + Uint8List? thumbData, + String? thumbDataHash, + FlWeChatShareResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + return _shareResponse( + fluwx.share( + WeChatShareVideoModel( + title: title ?? _appName, + videoUrl: videoUrl, + description: description ?? _appName, + scene: scene, + messageExt: messageExt, + messageAction: messageAction, + mediaTagName: mediaTagName, + msgSignature: msgSignature, + thumbData: thumbData ?? _shareThumbnail, + thumbDataHash: thumbDataHash, + ), + ), + onResponse: onResponse, + ); + } + + Future shareWebPage( + String webPage, { + String? title, + String? description, + WeChatScene scene = WeChatScene.session, + String? messageExt, + String? messageAction, + String? mediaTagName, + String? msgSignature, + Uint8List? thumbData, + String? thumbDataHash, + FlWeChatShareResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + return _shareResponse( + fluwx.share( + WeChatShareWebPageModel( + webPage, + title: title ?? _appName, + description: description ?? _appName, + scene: scene, + messageExt: messageExt, + messageAction: messageAction, + mediaTagName: mediaTagName, + msgSignature: msgSignature, + thumbData: thumbData ?? _shareThumbnail, + thumbDataHash: thumbDataHash, + ), + ), + onResponse: onResponse, + ); + } + + Future shareFile( + File source, { + required String suffix, + String? title, + String? description, + WeChatScene scene = WeChatScene.session, + String? messageExt, + String? messageAction, + String? mediaTagName, + String? msgSignature, + Uint8List? thumbData, + String? thumbDataHash, + FlWeChatShareResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + return _shareResponse( + fluwx.share( + WeChatShareFileModel( + WeChatFile.file(source, suffix: suffix), + title: title ?? _appName, + description: description ?? _appName, + scene: scene, + messageExt: messageExt, + messageAction: messageAction, + mediaTagName: mediaTagName, + msgSignature: msgSignature, + thumbData: thumbData ?? _shareThumbnail, + thumbDataHash: thumbDataHash, + ), + ), + onResponse: onResponse, + ); + } + + Future _shareResponse( + Future share, { + FlWeChatShareResponseCallback? onResponse, + }) async { + final cancelable = _onListener((response) { + if (response is WeChatShareResponse) onResponse?.call(response); + }); + final result = await share; + if (!result) cancelable.cancel(); + return result; + } +} + +class FlWXCallbackParams { + FlWXCallbackParams({this.onToast, this.onLog, this.onHttp}); + + /// toast 显示 + final FlWXToastCallback? onToast; + + /// 日志 打印 + final FlWXLogCallback? onLog; + + /// http 请求 返回 json + final FlWXHTTPCallback? onHttp; +} + +class WXUserModel { + final String? openid; + final String? nickname; + final int? sex; + final String? language; + final String? city; + final String? province; + final String? country; + final String? headImgUrl; + final List? privilege; + final String? unionId; + + WXUserModel.fromJson(Map json) + : openid = json['openid'] as String?, + nickname = json['nickname'] as String?, + sex = json['sex'] as int?, + language = json['language'] as String?, + city = json['city'] as String?, + province = json['province'] as String?, + country = json['country'] as String?, + headImgUrl = json['headimgurl'] as String?, + privilege = json['privilege'] as List?, + unionId = json['unionid'] as String?; + + Map toMap() => { + 'openid': openid, + 'unionId': unionId, + 'nickname': nickname, + 'sex': sex, + 'headImgUrl': headImgUrl, + 'privilege': privilege, + 'language': language, + 'city': city, + 'province': province, + 'country': country, + }; +} + +class WXTokenModel { + final String? accessToken; + final int? expiresIn; + final String? refreshToken; + final String? openid; + final String? scope; + final String? unionId; + + WXTokenModel.fromJson(Map json) + : accessToken = json['access_token'] as String?, + expiresIn = json['expires_in'] as int?, + refreshToken = json['refresh_token'] as String?, + openid = json['openid'] as String?, + scope = json['scope'] as String?, + unionId = json['unionid'] as String?; + + Map toMap() => { + 'openid': openid, + 'unionId': unionId, + 'accessToken': accessToken, + 'expiresIn': expiresIn, + 'refreshToken': refreshToken, + 'scope': scope, + }; +} + +extension ExtensionMap on Map { + Payment? toPayment() { + try { + final appId = (this['appId'] ?? this['appid']); + final partnerId = (this['partnerId'] ?? + this['partnerid'] ?? + this['partner']) as String?; + final prepayId = (this['prepayId'] ?? this['prepayid'] ?? this['prepay']); + final packageValue = + (this['packageValue'] ?? this['packagevalue'] ?? this['package']); + final nonceStr = (this['nonceStr'] ?? this['noncestr'] ?? this['nonce']); + final timestamp = (this['timestamp'] ?? this['timeStamp']); + final sign = this['sign']; + if (appId != null && + partnerId != null && + prepayId != null && + packageValue != null && + nonceStr != null && + timestamp != null && + sign != null) { + return Payment( + appId: appId.toString(), + partnerId: partnerId.toString(), + prepayId: prepayId.toString(), + packageValue: packageValue.toString(), + nonceStr: nonceStr.toString(), + timestamp: int.parse(timestamp!.toString()), + sign: sign.toString(), + ); + } + } catch (e) { + FlWX()._params?.onLog?.call(e.toString()); + } + return null; + } +} diff --git a/lib/modules/login/views/login_view.dart b/lib/modules/login/views/login_view.dart index cc4faa1c0..c827a1715 100644 --- a/lib/modules/login/views/login_view.dart +++ b/lib/modules/login/views/login_view.dart @@ -3,6 +3,9 @@ import 'package:get/get.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import '../controllers/login_controller.dart'; +import '../controllers/facebookLogin.dart'; +import '../controllers/googleLogin.dart'; +import '../controllers/weixinLogin.dart'; import '../../../core/themes/app_theme.dart'; class LoginView extends GetView { @@ -353,6 +356,9 @@ class LoginView extends GetView { SizedBox(width: 24.w), _buildSocialButton( FontAwesomeIcons.weixin, Colors.green, isDarkMode), + SizedBox(width: 24.w), + _buildSocialButton( + FontAwesomeIcons.facebook, Colors.blue, isDarkMode), ], ), ], @@ -378,16 +384,60 @@ class LoginView extends GetView { ], ), child: IconButton( - icon: FaIcon( - icon, - color: color, - size: 20.sp, - ), - onPressed: () {}, - ), + icon: FaIcon( + icon, + color: color, + size: 20.sp, + ), + onPressed: () => SocialLogin(icon)), ); } + Future SocialLogin(IconData icon) async { + switch (icon) { + case FontAwesomeIcons.facebook: + { + print("数据获取Facebook"); + bool authResult = await Facebook().signInWithFacebook(); + print("数据获取Facebook().token${authResult}"); + if (authResult) { + print("数据获取_Facebook.token${Facebook().token}"); + controller.login(); + } + break; + } + case FontAwesomeIcons.google: + { + print("数据获取_Google()"); + bool authResult = await Google().signInWithGoogle(); + print("数据获取_Google().token${authResult}"); + if (authResult) { + controller.login(); + } + + break; + } + case FontAwesomeIcons.weixin: + { + print("数据获取_FlWX()"); + bool authResult = await FlWX().authBy( + NormalAuth( + scope: "snsapi_userinfo", + state: "wechat_sdk_demo_test", + ), + ); + + if (authResult) { + // 确保 FlWX() 返回的是同一个实例,并且 token 已经被设置 + // 这里假设 FlWX().token 在 authBy 成功后被设置 + print("数据获取_FlWX().token${FlWX().token}"); + controller.login(); + } + break; + } + } + } + // 用户协议和隐私政策 Widget _buildTermsAndPrivacy(bool isDarkMode) { return Column( diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index 24b0c8f03..05e1258a9 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -7,7 +7,7 @@ project(runner LANGUAGES CXX) set(BINARY_NAME "deep_voice") # The unique GTK application identifier for this application. See: # https://wiki.gnome.org/HowDoI/ChooseApplicationID -set(APPLICATION_ID "com.example.deep_voice") +set(APPLICATION_ID "com.yunqiinnovation.deepsound") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index fab21e7ca..f443e8bbe 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,6 +7,7 @@ #include "generated_plugin_registrant.h" #include +#include #include #include #include @@ -15,6 +16,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); file_selector_plugin_register_with_registrar(file_selector_linux_registrar); + g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); + flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); g_autoptr(FlPluginRegistrar) record_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "RecordLinuxPlugin"); record_linux_plugin_register_with_registrar(record_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 6915aceb9..346ccb75e 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST file_selector_linux + flutter_secure_storage_linux record_linux url_launcher_linux window_size diff --git a/pubspec.yaml b/pubspec.yaml index 8f6e1e625..799493343 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -50,7 +50,12 @@ dependencies: lottie: ^3.3.1 app_settings: 5.2.0 speech_to_text: ^7.0.0 - + fluwx: ^5.4.1 + firebase_core: ^3.12.1 + firebase_auth: ^5.5.0 + google_sign_in: ^6.2.2 + flutter_facebook_auth: ^7.1.1 + dev_dependencies: flutter_test: sdk: flutter diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index eb676d5f0..74bd2623b 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,6 +7,9 @@ #include "generated_plugin_registrant.h" #include +#include +#include +#include #include #include #include @@ -17,6 +20,12 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { FileSelectorWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("FileSelectorWindows")); + FirebaseAuthPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FirebaseAuthPluginCApi")); + FirebaseCorePluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); + FlutterSecureStorageWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); FlutterTtsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterTtsPlugin")); PermissionHandlerWindowsPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 47ae460e0..e548eecfe 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,6 +4,9 @@ list(APPEND FLUTTER_PLUGIN_LIST file_selector_windows + firebase_auth + firebase_core + flutter_secure_storage_windows flutter_tts permission_handler_windows record_windows